1# Compress man pages in ${mandir} and info pages in ${infodir} 2# 3# 1. The doc will be compressed to gz format by default. 4# 5# 2. It will automatically correct the compressed doc which is not 6# in ${DOC_COMPRESS} but in ${DOC_COMPRESS_LIST} to the format 7# of ${DOC_COMPRESS} policy 8# 9# 3. It is easy to add a new type compression by editing 10# local.conf, such as: 11# DOC_COMPRESS_LIST:append = ' abc' 12# DOC_COMPRESS = 'abc' 13# DOC_COMPRESS_CMD[abc] = 'abc compress cmd ***' 14# DOC_DECOMPRESS_CMD[abc] = 'abc decompress cmd ***' 15 16# All supported compression policy 17DOC_COMPRESS_LIST ?= "gz xz bz2" 18 19# Compression policy, must be one of ${DOC_COMPRESS_LIST} 20DOC_COMPRESS ?= "gz" 21 22# Compression shell command 23DOC_COMPRESS_CMD[gz] ?= 'gzip -v -9 -n' 24DOC_COMPRESS_CMD[bz2] ?= "bzip2 -v -9" 25DOC_COMPRESS_CMD[xz] ?= "xz -v" 26 27# Decompression shell command 28DOC_DECOMPRESS_CMD[gz] ?= 'gunzip -v' 29DOC_DECOMPRESS_CMD[bz2] ?= "bunzip2 -v" 30DOC_DECOMPRESS_CMD[xz] ?= "unxz -v" 31 32PACKAGE_PREPROCESS_FUNCS += "package_do_compress_doc compress_doc_updatealternatives" 33python package_do_compress_doc() { 34 compress_mode = d.getVar('DOC_COMPRESS') 35 compress_list = (d.getVar('DOC_COMPRESS_LIST') or '').split() 36 if compress_mode not in compress_list: 37 bb.fatal('Compression policy %s not supported (not listed in %s)\n' % (compress_mode, compress_list)) 38 39 dvar = d.getVar('PKGD') 40 compress_cmds = {} 41 decompress_cmds = {} 42 for mode in compress_list: 43 compress_cmds[mode] = d.getVarFlag('DOC_COMPRESS_CMD', mode) 44 decompress_cmds[mode] = d.getVarFlag('DOC_DECOMPRESS_CMD', mode) 45 46 mandir = os.path.abspath(dvar + os.sep + d.getVar("mandir")) 47 if os.path.exists(mandir): 48 # Decompress doc files which format is not compress_mode 49 decompress_doc(mandir, compress_mode, decompress_cmds) 50 compress_doc(mandir, compress_mode, compress_cmds) 51 52 infodir = os.path.abspath(dvar + os.sep + d.getVar("infodir")) 53 if os.path.exists(infodir): 54 # Decompress doc files which format is not compress_mode 55 decompress_doc(infodir, compress_mode, decompress_cmds) 56 compress_doc(infodir, compress_mode, compress_cmds) 57} 58 59def _get_compress_format(file, compress_format_list): 60 for compress_format in compress_format_list: 61 compress_suffix = '.' + compress_format 62 if file.endswith(compress_suffix): 63 return compress_format 64 65 return '' 66 67# Collect hardlinks to dict, each element in dict lists hardlinks 68# which points to the same doc file. 69# {hardlink10: [hardlink11, hardlink12],,,} 70# The hardlink10, hardlink11 and hardlink12 are the same file. 71def _collect_hardlink(hardlink_dict, file): 72 for hardlink in hardlink_dict: 73 # Add to the existed hardlink 74 if os.path.samefile(hardlink, file): 75 hardlink_dict[hardlink].append(file) 76 return hardlink_dict 77 78 hardlink_dict[file] = [] 79 return hardlink_dict 80 81def _process_hardlink(hardlink_dict, compress_mode, shell_cmds, decompress=False): 82 import subprocess 83 for target in hardlink_dict: 84 if decompress: 85 compress_format = _get_compress_format(target, shell_cmds.keys()) 86 cmd = "%s -f %s" % (shell_cmds[compress_format], target) 87 bb.note('decompress hardlink %s' % target) 88 else: 89 cmd = "%s -f %s" % (shell_cmds[compress_mode], target) 90 bb.note('compress hardlink %s' % target) 91 (retval, output) = subprocess.getstatusoutput(cmd) 92 if retval: 93 bb.warn("de/compress file failed %s (cmd was %s)%s" % (retval, cmd, ":\n%s" % output if output else "")) 94 return 95 96 for hardlink_dup in hardlink_dict[target]: 97 if decompress: 98 # Remove compress suffix 99 compress_suffix = '.' + compress_format 100 new_hardlink = hardlink_dup[:-len(compress_suffix)] 101 new_target = target[:-len(compress_suffix)] 102 else: 103 # Append compress suffix 104 compress_suffix = '.' + compress_mode 105 new_hardlink = hardlink_dup + compress_suffix 106 new_target = target + compress_suffix 107 108 bb.note('hardlink %s-->%s' % (new_hardlink, new_target)) 109 if not os.path.exists(new_hardlink): 110 os.link(new_target, new_hardlink) 111 if os.path.exists(hardlink_dup): 112 os.unlink(hardlink_dup) 113 114def _process_symlink(file, compress_format, decompress=False): 115 compress_suffix = '.' + compress_format 116 if decompress: 117 # Remove compress suffix 118 new_linkname = file[:-len(compress_suffix)] 119 new_source = os.readlink(file)[:-len(compress_suffix)] 120 else: 121 # Append compress suffix 122 new_linkname = file + compress_suffix 123 new_source = os.readlink(file) + compress_suffix 124 125 bb.note('symlink %s-->%s' % (new_linkname, new_source)) 126 if not os.path.exists(new_linkname): 127 os.symlink(new_source, new_linkname) 128 129 os.unlink(file) 130 131def _is_info(file): 132 flags = '.info .info-'.split() 133 for flag in flags: 134 if flag in os.path.basename(file): 135 return True 136 137 return False 138 139def _is_man(file): 140 import re 141 142 # It refers MANSECT-var in man(1.6g)'s man.config 143 # ".1:.1p:.8:.2:.3:.3p:.4:.5:.6:.7:.9:.0p:.tcl:.n:.l:.p:.o" 144 # Not start with '.', and contain the above colon-seperate element 145 p = re.compile(r'[^\.]+\.([1-9lnop]|0p|tcl)') 146 if p.search(file): 147 return True 148 149 return False 150 151def _is_compress_doc(file, compress_format_list): 152 compress_format = _get_compress_format(file, compress_format_list) 153 compress_suffix = '.' + compress_format 154 if file.endswith(compress_suffix): 155 # Remove the compress suffix 156 uncompress_file = file[:-len(compress_suffix)] 157 if _is_info(uncompress_file) or _is_man(uncompress_file): 158 return True, compress_format 159 160 return False, '' 161 162def compress_doc(topdir, compress_mode, compress_cmds): 163 import subprocess 164 hardlink_dict = {} 165 for root, dirs, files in os.walk(topdir): 166 for f in files: 167 file = os.path.join(root, f) 168 if os.path.isdir(file): 169 continue 170 171 if _is_info(file) or _is_man(file): 172 # Symlink 173 if os.path.islink(file): 174 _process_symlink(file, compress_mode) 175 # Hardlink 176 elif os.lstat(file).st_nlink > 1: 177 _collect_hardlink(hardlink_dict, file) 178 # Normal file 179 elif os.path.isfile(file): 180 cmd = "%s %s" % (compress_cmds[compress_mode], file) 181 (retval, output) = subprocess.getstatusoutput(cmd) 182 if retval: 183 bb.warn("compress failed %s (cmd was %s)%s" % (retval, cmd, ":\n%s" % output if output else "")) 184 continue 185 bb.note('compress file %s' % file) 186 187 _process_hardlink(hardlink_dict, compress_mode, compress_cmds) 188 189# Decompress doc files which format is not compress_mode 190def decompress_doc(topdir, compress_mode, decompress_cmds): 191 import subprocess 192 hardlink_dict = {} 193 decompress = True 194 for root, dirs, files in os.walk(topdir): 195 for f in files: 196 file = os.path.join(root, f) 197 if os.path.isdir(file): 198 continue 199 200 res, compress_format = _is_compress_doc(file, decompress_cmds.keys()) 201 # Decompress files which format is not compress_mode 202 if res and compress_mode!=compress_format: 203 # Symlink 204 if os.path.islink(file): 205 _process_symlink(file, compress_format, decompress) 206 # Hardlink 207 elif os.lstat(file).st_nlink > 1: 208 _collect_hardlink(hardlink_dict, file) 209 # Normal file 210 elif os.path.isfile(file): 211 cmd = "%s %s" % (decompress_cmds[compress_format], file) 212 (retval, output) = subprocess.getstatusoutput(cmd) 213 if retval: 214 bb.warn("decompress failed %s (cmd was %s)%s" % (retval, cmd, ":\n%s" % output if output else "")) 215 continue 216 bb.note('decompress file %s' % file) 217 218 _process_hardlink(hardlink_dict, compress_mode, decompress_cmds, decompress) 219 220python compress_doc_updatealternatives () { 221 if not bb.data.inherits_class('update-alternatives', d): 222 return 223 224 mandir = d.getVar("mandir") 225 infodir = d.getVar("infodir") 226 compress_mode = d.getVar('DOC_COMPRESS') 227 for pkg in (d.getVar('PACKAGES') or "").split(): 228 old_names = (d.getVar('ALTERNATIVE:%s' % pkg) or "").split() 229 new_names = [] 230 for old_name in old_names: 231 old_link = d.getVarFlag('ALTERNATIVE_LINK_NAME', old_name) 232 old_target = d.getVarFlag('ALTERNATIVE_TARGET_%s' % pkg, old_name) or \ 233 d.getVarFlag('ALTERNATIVE_TARGET', old_name) or \ 234 d.getVar('ALTERNATIVE_TARGET_%s' % pkg) or \ 235 d.getVar('ALTERNATIVE_TARGET') or \ 236 old_link 237 # Sometimes old_target is specified as relative to the link name. 238 old_target = os.path.join(os.path.dirname(old_link), old_target) 239 240 # The updatealternatives used for compress doc 241 if mandir in old_target or infodir in old_target: 242 new_name = old_name + '.' + compress_mode 243 new_link = old_link + '.' + compress_mode 244 new_target = old_target + '.' + compress_mode 245 d.delVarFlag('ALTERNATIVE_LINK_NAME', old_name) 246 d.setVarFlag('ALTERNATIVE_LINK_NAME', new_name, new_link) 247 if d.getVarFlag('ALTERNATIVE_TARGET_%s' % pkg, old_name): 248 d.delVarFlag('ALTERNATIVE_TARGET_%s' % pkg, old_name) 249 d.setVarFlag('ALTERNATIVE_TARGET_%s' % pkg, new_name, new_target) 250 elif d.getVarFlag('ALTERNATIVE_TARGET', old_name): 251 d.delVarFlag('ALTERNATIVE_TARGET', old_name) 252 d.setVarFlag('ALTERNATIVE_TARGET', new_name, new_target) 253 elif d.getVar('ALTERNATIVE_TARGET_%s' % pkg): 254 d.setVar('ALTERNATIVE_TARGET_%s' % pkg, new_target) 255 elif d.getVar('ALTERNATIVE_TARGET'): 256 d.setVar('ALTERNATIVE_TARGET', new_target) 257 258 new_names.append(new_name) 259 260 if new_names: 261 d.setVar('ALTERNATIVE:%s' % pkg, ' '.join(new_names)) 262} 263 264