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