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