xref: /OK3568_Linux_fs/yocto/poky/meta/classes/package_deb.bbclass (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1#
2# Copyright 2006-2008 OpenedHand Ltd.
3#
4
5inherit package
6
7IMAGE_PKGTYPE ?= "deb"
8
9DPKG_BUILDCMD ??= "dpkg-deb"
10
11DPKG_ARCH ?= "${@debian_arch_map(d.getVar('TARGET_ARCH'), d.getVar('TUNE_FEATURES'))}"
12DPKG_ARCH[vardepvalue] = "${DPKG_ARCH}"
13
14PKGWRITEDIRDEB = "${WORKDIR}/deploy-debs"
15
16APTCONF_TARGET = "${WORKDIR}"
17
18APT_ARGS = "${@['', '--no-install-recommends'][d.getVar("NO_RECOMMENDATIONS") == "1"]}"
19
20def debian_arch_map(arch, tune):
21    tune_features = tune.split()
22    if arch == "allarch":
23        return "all"
24    if arch in ["i586", "i686"]:
25        return "i386"
26    if arch == "x86_64":
27        if "mx32" in tune_features:
28            return "x32"
29        return "amd64"
30    if arch.startswith("mips"):
31        endian = ["el", ""]["bigendian" in tune_features]
32        if "n64" in tune_features:
33            return "mips64" + endian
34        if "n32" in tune_features:
35            return "mipsn32" + endian
36        return "mips" + endian
37    if arch == "powerpc":
38        return arch + ["", "spe"]["spe" in tune_features]
39    if arch == "aarch64":
40        return "arm64"
41    if arch == "arm":
42        return arch + ["el", "hf"]["callconvention-hard" in tune_features]
43    return arch
44
45python do_package_deb () {
46    packages = d.getVar('PACKAGES')
47    if not packages:
48        bb.debug(1, "PACKAGES not defined, nothing to package")
49        return
50
51    tmpdir = d.getVar('TMPDIR')
52    if os.access(os.path.join(tmpdir, "stamps", "DEB_PACKAGE_INDEX_CLEAN"),os.R_OK):
53        os.unlink(os.path.join(tmpdir, "stamps", "DEB_PACKAGE_INDEX_CLEAN"))
54
55    oe.utils.multiprocess_launch(deb_write_pkg, packages.split(), d, extraargs=(d,))
56}
57do_package_deb[vardeps] += "deb_write_pkg"
58do_package_deb[vardepsexclude] = "BB_NUMBER_THREADS"
59
60def deb_write_pkg(pkg, d):
61    import re, copy
62    import textwrap
63    import subprocess
64    import collections
65    import codecs
66
67    outdir = d.getVar('PKGWRITEDIRDEB')
68    pkgdest = d.getVar('PKGDEST')
69
70    def cleanupcontrol(root):
71        for p in ['CONTROL', 'DEBIAN']:
72            p = os.path.join(root, p)
73            if os.path.exists(p):
74                bb.utils.prunedir(p)
75
76    localdata = bb.data.createCopy(d)
77    root = "%s/%s" % (pkgdest, pkg)
78
79    lf = bb.utils.lockfile(root + ".lock")
80    try:
81
82        localdata.setVar('ROOT', '')
83        localdata.setVar('ROOT_%s' % pkg, root)
84        pkgname = localdata.getVar('PKG:%s' % pkg)
85        if not pkgname:
86            pkgname = pkg
87        localdata.setVar('PKG', pkgname)
88
89        localdata.setVar('OVERRIDES', d.getVar("OVERRIDES", False) + ":" + pkg)
90
91        basedir = os.path.join(os.path.dirname(root))
92
93        pkgoutdir = os.path.join(outdir, localdata.getVar('PACKAGE_ARCH'))
94        bb.utils.mkdirhier(pkgoutdir)
95
96        os.chdir(root)
97        cleanupcontrol(root)
98        from glob import glob
99        g = glob('*')
100        if not g and localdata.getVar('ALLOW_EMPTY', False) != "1":
101            bb.note("Not creating empty archive for %s-%s-%s" % (pkg, localdata.getVar('PKGV'), localdata.getVar('PKGR')))
102            return
103
104        controldir = os.path.join(root, 'DEBIAN')
105        bb.utils.mkdirhier(controldir)
106        os.chmod(controldir, 0o755)
107
108        ctrlfile = codecs.open(os.path.join(controldir, 'control'), 'w', 'utf-8')
109
110        fields = []
111        pe = d.getVar('PKGE')
112        if pe and int(pe) > 0:
113            fields.append(["Version: %s:%s-%s\n", ['PKGE', 'PKGV', 'PKGR']])
114        else:
115            fields.append(["Version: %s-%s\n", ['PKGV', 'PKGR']])
116        fields.append(["Description: %s\n", ['DESCRIPTION']])
117        fields.append(["Section: %s\n", ['SECTION']])
118        fields.append(["Priority: %s\n", ['PRIORITY']])
119        fields.append(["Maintainer: %s\n", ['MAINTAINER']])
120        fields.append(["Architecture: %s\n", ['DPKG_ARCH']])
121        fields.append(["OE: %s\n", ['PN']])
122        fields.append(["PackageArch: %s\n", ['PACKAGE_ARCH']])
123        if d.getVar('HOMEPAGE'):
124            fields.append(["Homepage: %s\n", ['HOMEPAGE']])
125
126        # Package, Version, Maintainer, Description - mandatory
127        # Section, Priority, Essential, Architecture, Source, Depends, Pre-Depends, Recommends, Suggests, Conflicts, Replaces, Provides - Optional
128
129
130        def pullData(l, d):
131            l2 = []
132            for i in l:
133                data = d.getVar(i)
134                if data is None:
135                    raise KeyError(i)
136                if i == 'DPKG_ARCH' and d.getVar('PACKAGE_ARCH') == 'all':
137                    data = 'all'
138                elif i == 'PACKAGE_ARCH' or i == 'DPKG_ARCH':
139                   # The params in deb package control don't allow character
140                   # `_', so change the arch's `_' to `-'. Such as `x86_64'
141                   # -->`x86-64'
142                   data = data.replace('_', '-')
143                l2.append(data)
144            return l2
145
146        ctrlfile.write("Package: %s\n" % pkgname)
147        if d.getVar('PACKAGE_ARCH') == "all":
148            ctrlfile.write("Multi-Arch: foreign\n")
149        # check for required fields
150        for (c, fs) in fields:
151            # Special behavior for description...
152            if 'DESCRIPTION' in fs:
153                 summary = localdata.getVar('SUMMARY') or localdata.getVar('DESCRIPTION') or "."
154                 ctrlfile.write('Description: %s\n' % summary)
155                 description = localdata.getVar('DESCRIPTION') or "."
156                 description = textwrap.dedent(description).strip()
157                 if '\\n' in description:
158                     # Manually indent
159                     for t in description.split('\\n'):
160                         ctrlfile.write(' %s\n' % (t.strip() or '.'))
161                 else:
162                     # Auto indent
163                     ctrlfile.write('%s\n' % textwrap.fill(description.strip(), width=74, initial_indent=' ', subsequent_indent=' '))
164
165            else:
166                 ctrlfile.write(c % tuple(pullData(fs, localdata)))
167
168        # more fields
169
170        custom_fields_chunk = get_package_additional_metadata("deb", localdata)
171        if custom_fields_chunk:
172            ctrlfile.write(custom_fields_chunk)
173            ctrlfile.write("\n")
174
175        mapping_rename_hook(localdata)
176
177        def debian_cmp_remap(var):
178            # dpkg does not allow for '(', ')' or ':' in a dependency name
179            # Replace any instances of them with '__'
180            #
181            # In debian '>' and '<' do not mean what it appears they mean
182            #   '<' = less or equal
183            #   '>' = greater or equal
184            # adjust these to the '<<' and '>>' equivalents
185            # Also, "=" specifiers only work if they have the PR in, so 1.2.3 != 1.2.3-r0
186            # so to avoid issues, map this to ">= 1.2.3 << 1.2.3.0"
187            for dep in list(var.keys()):
188                if '(' in dep or '/' in dep:
189                    newdep = re.sub(r'[(:)/]', '__', dep)
190                    if newdep.startswith("__"):
191                        newdep = "A" + newdep
192                    if newdep != dep:
193                        var[newdep] = var[dep]
194                        del var[dep]
195            for dep in var:
196                for i, v in enumerate(var[dep]):
197                    if (v or "").startswith("< "):
198                        var[dep][i] = var[dep][i].replace("< ", "<< ")
199                    elif (v or "").startswith("> "):
200                        var[dep][i] = var[dep][i].replace("> ", ">> ")
201                    elif (v or "").startswith("= ") and "-r" not in v:
202                        ver = var[dep][i].replace("= ", "")
203                        var[dep][i] = var[dep][i].replace("= ", ">= ")
204                        var[dep].append("<< " + ver + ".0")
205
206        rdepends = bb.utils.explode_dep_versions2(localdata.getVar("RDEPENDS") or "")
207        debian_cmp_remap(rdepends)
208        for dep in list(rdepends.keys()):
209                if dep == pkg:
210                        del rdepends[dep]
211                        continue
212                if '*' in dep:
213                        del rdepends[dep]
214        rrecommends = bb.utils.explode_dep_versions2(localdata.getVar("RRECOMMENDS") or "")
215        debian_cmp_remap(rrecommends)
216        for dep in list(rrecommends.keys()):
217                if '*' in dep:
218                        del rrecommends[dep]
219        rsuggests = bb.utils.explode_dep_versions2(localdata.getVar("RSUGGESTS") or "")
220        debian_cmp_remap(rsuggests)
221        # Deliberately drop version information here, not wanted/supported by deb
222        rprovides = dict.fromkeys(bb.utils.explode_dep_versions2(localdata.getVar("RPROVIDES") or ""), [])
223        # Remove file paths if any from rprovides, debian does not support custom providers
224        for key in list(rprovides.keys()):
225            if key.startswith('/'):
226                del rprovides[key]
227        rprovides = collections.OrderedDict(sorted(rprovides.items(), key=lambda x: x[0]))
228        debian_cmp_remap(rprovides)
229        rreplaces = bb.utils.explode_dep_versions2(localdata.getVar("RREPLACES") or "")
230        debian_cmp_remap(rreplaces)
231        rconflicts = bb.utils.explode_dep_versions2(localdata.getVar("RCONFLICTS") or "")
232        debian_cmp_remap(rconflicts)
233        if rdepends:
234            ctrlfile.write("Depends: %s\n" % bb.utils.join_deps(rdepends))
235        if rsuggests:
236            ctrlfile.write("Suggests: %s\n" % bb.utils.join_deps(rsuggests))
237        if rrecommends:
238            ctrlfile.write("Recommends: %s\n" % bb.utils.join_deps(rrecommends))
239        if rprovides:
240            ctrlfile.write("Provides: %s\n" % bb.utils.join_deps(rprovides))
241        if rreplaces:
242            ctrlfile.write("Replaces: %s\n" % bb.utils.join_deps(rreplaces))
243        if rconflicts:
244            ctrlfile.write("Conflicts: %s\n" % bb.utils.join_deps(rconflicts))
245        ctrlfile.close()
246
247        for script in ["preinst", "postinst", "prerm", "postrm"]:
248            scriptvar = localdata.getVar('pkg_%s' % script)
249            if not scriptvar:
250                continue
251            scriptvar = scriptvar.strip()
252            scriptfile = open(os.path.join(controldir, script), 'w')
253
254            if scriptvar.startswith("#!"):
255                pos = scriptvar.find("\n") + 1
256                scriptfile.write(scriptvar[:pos])
257            else:
258                pos = 0
259                scriptfile.write("#!/bin/sh\n")
260
261            # Prevent the prerm/postrm scripts from being run during an upgrade
262            if script in ('prerm', 'postrm'):
263                scriptfile.write('[ "$1" != "upgrade" ] || exit 0\n')
264
265            scriptfile.write(scriptvar[pos:])
266            scriptfile.write('\n')
267            scriptfile.close()
268            os.chmod(os.path.join(controldir, script), 0o755)
269
270        conffiles_str = ' '.join(get_conffiles(pkg, d))
271        if conffiles_str:
272            conffiles = open(os.path.join(controldir, 'conffiles'), 'w')
273            for f in conffiles_str.split():
274                if os.path.exists(oe.path.join(root, f)):
275                    conffiles.write('%s\n' % f)
276            conffiles.close()
277
278        os.chdir(basedir)
279        subprocess.check_output("PATH=\"%s\" %s -b %s %s" % (localdata.getVar("PATH"), localdata.getVar("DPKG_BUILDCMD"),
280                                                             root, pkgoutdir),
281                                stderr=subprocess.STDOUT,
282                                shell=True)
283
284    finally:
285        cleanupcontrol(root)
286        bb.utils.unlockfile(lf)
287
288# Otherwise allarch packages may change depending on override configuration
289deb_write_pkg[vardepsexclude] = "OVERRIDES"
290
291# Have to list any variables referenced as X_<pkg> that aren't in pkgdata here
292DEBEXTRAVARS = "PKGV PKGR PKGV DESCRIPTION SECTION PRIORITY MAINTAINER DPKG_ARCH PN HOMEPAGE PACKAGE_ADD_METADATA_DEB"
293do_package_write_deb[vardeps] += "${@gen_packagevar(d, 'DEBEXTRAVARS')}"
294
295SSTATETASKS += "do_package_write_deb"
296do_package_write_deb[sstate-inputdirs] = "${PKGWRITEDIRDEB}"
297do_package_write_deb[sstate-outputdirs] = "${DEPLOY_DIR_DEB}"
298
299python do_package_write_deb_setscene () {
300    tmpdir = d.getVar('TMPDIR')
301
302    if os.access(os.path.join(tmpdir, "stamps", "DEB_PACKAGE_INDEX_CLEAN"),os.R_OK):
303        os.unlink(os.path.join(tmpdir, "stamps", "DEB_PACKAGE_INDEX_CLEAN"))
304
305    sstate_setscene(d)
306}
307addtask do_package_write_deb_setscene
308
309python () {
310    if d.getVar('PACKAGES') != '':
311        deps = ' dpkg-native:do_populate_sysroot virtual/fakeroot-native:do_populate_sysroot'
312        d.appendVarFlag('do_package_write_deb', 'depends', deps)
313        d.setVarFlag('do_package_write_deb', 'fakeroot', "1")
314}
315
316python do_package_write_deb () {
317    bb.build.exec_func("read_subpackage_metadata", d)
318    bb.build.exec_func("do_package_deb", d)
319}
320do_package_write_deb[dirs] = "${PKGWRITEDIRDEB}"
321do_package_write_deb[cleandirs] = "${PKGWRITEDIRDEB}"
322do_package_write_deb[depends] += "${@oe.utils.build_depends_string(d.getVar('PACKAGE_WRITE_DEPS'), 'do_populate_sysroot')}"
323addtask package_write_deb after do_packagedata do_package do_deploy_source_date_epoch before do_build
324do_build[rdeptask] += "do_package_write_deb"
325
326PACKAGEINDEXDEPS += "dpkg-native:do_populate_sysroot"
327PACKAGEINDEXDEPS += "apt-native:do_populate_sysroot"
328