xref: /OK3568_Linux_fs/yocto/poky/meta/classes/compress_doc.bbclass (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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