Rather than just use d.getVar(X), use the more explict d.getVar(X, False) since at some point in the future, having the default of expansion would be nice. This is the first step towards that. This patch was mostly made using the command: sed -e 's:\(getVar([^,()]*\)\s*):\1, False):g' -i `grep -ril getVar *` (From OE-Core rev: ab7c1d239b122c8e549e8112c88fd46c9e2b061b) Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2087 lines
79 KiB
Text
2087 lines
79 KiB
Text
#
|
|
# Packaging process
|
|
#
|
|
# Executive summary: This class iterates over the functions listed in PACKAGEFUNCS
|
|
# Taking D and splitting it up into the packages listed in PACKAGES, placing the
|
|
# resulting output in PKGDEST.
|
|
#
|
|
# There are the following default steps but PACKAGEFUNCS can be extended:
|
|
#
|
|
# a) package_get_auto_pr - get PRAUTO from remote PR service
|
|
#
|
|
# b) perform_packagecopy - Copy D into PKGD
|
|
#
|
|
# c) package_do_split_locales - Split out the locale files, updates FILES and PACKAGES
|
|
#
|
|
# d) split_and_strip_files - split the files into runtime and debug and strip them.
|
|
# Debug files include debug info split, and associated sources that end up in -dbg packages
|
|
#
|
|
# e) fixup_perms - Fix up permissions in the package before we split it.
|
|
#
|
|
# f) populate_packages - Split the files in PKGD into separate packages in PKGDEST/<pkgname>
|
|
# Also triggers the binary stripping code to put files in -dbg packages.
|
|
#
|
|
# g) package_do_filedeps - Collect perfile run-time dependency metadata
|
|
# The data is stores in FILER{PROVIDES,DEPENDS}_file_pkg variables with
|
|
# a list of affected files in FILER{PROVIDES,DEPENDS}FLIST_pkg
|
|
#
|
|
# h) package_do_shlibs - Look at the shared libraries generated and autotmatically add any
|
|
# depenedencies found. Also stores the package name so anyone else using this library
|
|
# knows which package to depend on.
|
|
#
|
|
# i) package_do_pkgconfig - Keep track of which packages need and provide which .pc files
|
|
#
|
|
# j) read_shlibdeps - Reads the stored shlibs information into the metadata
|
|
#
|
|
# k) package_depchains - Adds automatic dependencies to -dbg and -dev packages
|
|
#
|
|
# l) emit_pkgdata - saves the packaging data into PKGDATA_DIR for use in later
|
|
# packaging steps
|
|
|
|
inherit packagedata
|
|
inherit prserv
|
|
inherit chrpath
|
|
|
|
# Need the package_qa_handle_error() in insane.bbclass
|
|
inherit insane
|
|
|
|
PKGD = "${WORKDIR}/package"
|
|
PKGDEST = "${WORKDIR}/packages-split"
|
|
|
|
LOCALE_SECTION ?= ''
|
|
|
|
ALL_MULTILIB_PACKAGE_ARCHS = "${@all_multilib_tune_values(d, 'PACKAGE_ARCHS')}"
|
|
|
|
# rpm is used for the per-file dependency identification
|
|
PACKAGE_DEPENDS += "rpm-native"
|
|
|
|
def legitimize_package_name(s):
|
|
"""
|
|
Make sure package names are legitimate strings
|
|
"""
|
|
import re
|
|
|
|
def fixutf(m):
|
|
cp = m.group(1)
|
|
if cp:
|
|
return ('\u%s' % cp).decode('unicode_escape').encode('utf-8')
|
|
|
|
# Handle unicode codepoints encoded as <U0123>, as in glibc locale files.
|
|
s = re.sub('<U([0-9A-Fa-f]{1,4})>', fixutf, s)
|
|
|
|
# Remaining package name validity fixes
|
|
return s.lower().replace('_', '-').replace('@', '+').replace(',', '+').replace('/', '-')
|
|
|
|
def do_split_packages(d, root, file_regex, output_pattern, description, postinst=None, recursive=False, hook=None, extra_depends=None, aux_files_pattern=None, postrm=None, allow_dirs=False, prepend=False, match_path=False, aux_files_pattern_verbatim=None, allow_links=False, summary=None):
|
|
"""
|
|
Used in .bb files to split up dynamically generated subpackages of a
|
|
given package, usually plugins or modules.
|
|
|
|
Arguments:
|
|
root -- the path in which to search
|
|
file_regex -- regular expression to match searched files. Use
|
|
parentheses () to mark the part of this expression
|
|
that should be used to derive the module name (to be
|
|
substituted where %s is used in other function
|
|
arguments as noted below)
|
|
output_pattern -- pattern to use for the package names. Must include %s.
|
|
description -- description to set for each package. Must include %s.
|
|
postinst -- postinstall script to use for all packages (as a
|
|
string)
|
|
recursive -- True to perform a recursive search - default False
|
|
hook -- a hook function to be called for every match. The
|
|
function will be called with the following arguments
|
|
(in the order listed):
|
|
f: full path to the file/directory match
|
|
pkg: the package name
|
|
file_regex: as above
|
|
output_pattern: as above
|
|
modulename: the module name derived using file_regex
|
|
extra_depends -- extra runtime dependencies (RDEPENDS) to be set for
|
|
all packages. The default value of None causes a
|
|
dependency on the main package (${PN}) - if you do
|
|
not want this, pass '' for this parameter.
|
|
aux_files_pattern -- extra item(s) to be added to FILES for each
|
|
package. Can be a single string item or a list of
|
|
strings for multiple items. Must include %s.
|
|
postrm -- postrm script to use for all packages (as a string)
|
|
allow_dirs -- True allow directories to be matched - default False
|
|
prepend -- if True, prepend created packages to PACKAGES instead
|
|
of the default False which appends them
|
|
match_path -- match file_regex on the whole relative path to the
|
|
root rather than just the file name
|
|
aux_files_pattern_verbatim -- extra item(s) to be added to FILES for
|
|
each package, using the actual derived module name
|
|
rather than converting it to something legal for a
|
|
package name. Can be a single string item or a list
|
|
of strings for multiple items. Must include %s.
|
|
allow_links -- True to allow symlinks to be matched - default False
|
|
summary -- Summary to set for each package. Must include %s;
|
|
defaults to description if not set.
|
|
|
|
"""
|
|
|
|
dvar = d.getVar('PKGD', True)
|
|
|
|
# If the root directory doesn't exist, don't error out later but silently do
|
|
# no splitting.
|
|
if not os.path.exists(dvar + root):
|
|
return []
|
|
|
|
ml = d.getVar("MLPREFIX", True)
|
|
if ml:
|
|
if not output_pattern.startswith(ml):
|
|
output_pattern = ml + output_pattern
|
|
|
|
newdeps = []
|
|
for dep in (extra_depends or "").split():
|
|
if dep.startswith(ml):
|
|
newdeps.append(dep)
|
|
else:
|
|
newdeps.append(ml + dep)
|
|
if newdeps:
|
|
extra_depends = " ".join(newdeps)
|
|
|
|
|
|
packages = d.getVar('PACKAGES', True).split()
|
|
split_packages = []
|
|
|
|
if postinst:
|
|
postinst = '#!/bin/sh\n' + postinst + '\n'
|
|
if postrm:
|
|
postrm = '#!/bin/sh\n' + postrm + '\n'
|
|
if not recursive:
|
|
objs = os.listdir(dvar + root)
|
|
else:
|
|
objs = []
|
|
for walkroot, dirs, files in os.walk(dvar + root):
|
|
for file in files:
|
|
relpath = os.path.join(walkroot, file).replace(dvar + root + '/', '', 1)
|
|
if relpath:
|
|
objs.append(relpath)
|
|
|
|
if extra_depends == None:
|
|
extra_depends = d.getVar("PN", True)
|
|
|
|
if not summary:
|
|
summary = description
|
|
|
|
for o in sorted(objs):
|
|
import re, stat
|
|
if match_path:
|
|
m = re.match(file_regex, o)
|
|
else:
|
|
m = re.match(file_regex, os.path.basename(o))
|
|
|
|
if not m:
|
|
continue
|
|
f = os.path.join(dvar + root, o)
|
|
mode = os.lstat(f).st_mode
|
|
if not (stat.S_ISREG(mode) or (allow_links and stat.S_ISLNK(mode)) or (allow_dirs and stat.S_ISDIR(mode))):
|
|
continue
|
|
on = legitimize_package_name(m.group(1))
|
|
pkg = output_pattern % on
|
|
split_packages.append(pkg)
|
|
if not pkg in packages:
|
|
if prepend:
|
|
packages = [pkg] + packages
|
|
else:
|
|
packages.append(pkg)
|
|
oldfiles = d.getVar('FILES_' + pkg, True)
|
|
newfile = os.path.join(root, o)
|
|
# These names will be passed through glob() so if the filename actually
|
|
# contains * or ? (rare, but possible) we need to handle that specially
|
|
newfile = newfile.replace('*', '[*]')
|
|
newfile = newfile.replace('?', '[?]')
|
|
if not oldfiles:
|
|
the_files = [newfile]
|
|
if aux_files_pattern:
|
|
if type(aux_files_pattern) is list:
|
|
for fp in aux_files_pattern:
|
|
the_files.append(fp % on)
|
|
else:
|
|
the_files.append(aux_files_pattern % on)
|
|
if aux_files_pattern_verbatim:
|
|
if type(aux_files_pattern_verbatim) is list:
|
|
for fp in aux_files_pattern_verbatim:
|
|
the_files.append(fp % m.group(1))
|
|
else:
|
|
the_files.append(aux_files_pattern_verbatim % m.group(1))
|
|
d.setVar('FILES_' + pkg, " ".join(the_files))
|
|
else:
|
|
d.setVar('FILES_' + pkg, oldfiles + " " + newfile)
|
|
if extra_depends != '':
|
|
d.appendVar('RDEPENDS_' + pkg, ' ' + extra_depends)
|
|
if not d.getVar('DESCRIPTION_' + pkg, True):
|
|
d.setVar('DESCRIPTION_' + pkg, description % on)
|
|
if not d.getVar('SUMMARY_' + pkg, True):
|
|
d.setVar('SUMMARY_' + pkg, summary % on)
|
|
if postinst:
|
|
d.setVar('pkg_postinst_' + pkg, postinst)
|
|
if postrm:
|
|
d.setVar('pkg_postrm_' + pkg, postrm)
|
|
if callable(hook):
|
|
hook(f, pkg, file_regex, output_pattern, m.group(1))
|
|
|
|
d.setVar('PACKAGES', ' '.join(packages))
|
|
return split_packages
|
|
|
|
PACKAGE_DEPENDS += "file-native"
|
|
|
|
python () {
|
|
if d.getVar('PACKAGES', True) != '':
|
|
deps = ""
|
|
for dep in (d.getVar('PACKAGE_DEPENDS', True) or "").split():
|
|
deps += " %s:do_populate_sysroot" % dep
|
|
d.appendVarFlag('do_package', 'depends', deps)
|
|
|
|
# shlibs requires any DEPENDS to have already packaged for the *.list files
|
|
d.appendVarFlag('do_package', 'deptask', " do_packagedata")
|
|
}
|
|
|
|
# Get a list of files from file vars by searching files under current working directory
|
|
# The list contains symlinks, directories and normal files.
|
|
def files_from_filevars(filevars):
|
|
import os,glob
|
|
cpath = oe.cachedpath.CachedPath()
|
|
files = []
|
|
for f in filevars:
|
|
if os.path.isabs(f):
|
|
f = '.' + f
|
|
if not f.startswith("./"):
|
|
f = './' + f
|
|
globbed = glob.glob(f)
|
|
if globbed:
|
|
if [ f ] != globbed:
|
|
files += globbed
|
|
continue
|
|
files.append(f)
|
|
|
|
for f in files:
|
|
if not cpath.islink(f):
|
|
if cpath.isdir(f):
|
|
newfiles = [ os.path.join(f,x) for x in os.listdir(f) ]
|
|
if newfiles:
|
|
files += newfiles
|
|
|
|
return files
|
|
|
|
# Called in package_<rpm,ipk,deb>.bbclass to get the correct list of configuration files
|
|
def get_conffiles(pkg, d):
|
|
pkgdest = d.getVar('PKGDEST', True)
|
|
root = os.path.join(pkgdest, pkg)
|
|
cwd = os.getcwd()
|
|
os.chdir(root)
|
|
|
|
conffiles = d.getVar('CONFFILES_%s' % pkg, True);
|
|
if conffiles == None:
|
|
conffiles = d.getVar('CONFFILES', True)
|
|
if conffiles == None:
|
|
conffiles = ""
|
|
conffiles = conffiles.split()
|
|
conf_orig_list = files_from_filevars(conffiles)
|
|
|
|
# Remove links and directories from conf_orig_list to get conf_list which only contains normal files
|
|
conf_list = []
|
|
for f in conf_orig_list:
|
|
if os.path.isdir(f):
|
|
continue
|
|
if os.path.islink(f):
|
|
continue
|
|
if not os.path.exists(f):
|
|
continue
|
|
conf_list.append(f)
|
|
|
|
# Remove the leading './'
|
|
for i in range(0, len(conf_list)):
|
|
conf_list[i] = conf_list[i][1:]
|
|
|
|
os.chdir(cwd)
|
|
return conf_list
|
|
|
|
def splitdebuginfo(file, debugfile, debugsrcdir, sourcefile, d):
|
|
# Function to split a single file into two components, one is the stripped
|
|
# target system binary, the other contains any debugging information. The
|
|
# two files are linked to reference each other.
|
|
#
|
|
# sourcefile is also generated containing a list of debugsources
|
|
|
|
import stat
|
|
|
|
dvar = d.getVar('PKGD', True)
|
|
objcopy = d.getVar("OBJCOPY", True)
|
|
debugedit = d.expand("${STAGING_LIBDIR_NATIVE}/rpm/bin/debugedit")
|
|
workdir = d.getVar("WORKDIR", True)
|
|
workparentdir = d.getVar("DEBUGSRC_OVERRIDE_PATH", True) or os.path.dirname(os.path.dirname(workdir))
|
|
|
|
# We ignore kernel modules, we don't generate debug info files.
|
|
if file.find("/lib/modules/") != -1 and file.endswith(".ko"):
|
|
return 1
|
|
|
|
newmode = None
|
|
if not os.access(file, os.W_OK) or os.access(file, os.R_OK):
|
|
origmode = os.stat(file)[stat.ST_MODE]
|
|
newmode = origmode | stat.S_IWRITE | stat.S_IREAD
|
|
os.chmod(file, newmode)
|
|
|
|
# We need to extract the debug src information here...
|
|
if debugsrcdir:
|
|
cmd = "'%s' -b '%s' -d '%s' -i -l '%s' '%s'" % (debugedit, workparentdir, debugsrcdir, sourcefile, file)
|
|
(retval, output) = oe.utils.getstatusoutput(cmd)
|
|
if retval:
|
|
bb.fatal("debugedit failed with exit code %s (cmd was %s)%s" % (retval, cmd, ":\n%s" % output if output else ""))
|
|
|
|
bb.utils.mkdirhier(os.path.dirname(debugfile))
|
|
|
|
cmd = "'%s' --only-keep-debug '%s' '%s'" % (objcopy, file, debugfile)
|
|
(retval, output) = oe.utils.getstatusoutput(cmd)
|
|
if retval:
|
|
bb.fatal("objcopy failed with exit code %s (cmd was %s)%s" % (retval, cmd, ":\n%s" % output if output else ""))
|
|
|
|
# Set the debuglink to have the view of the file path on the target
|
|
cmd = "'%s' --add-gnu-debuglink='%s' '%s'" % (objcopy, debugfile, file)
|
|
(retval, output) = oe.utils.getstatusoutput(cmd)
|
|
if retval:
|
|
bb.fatal("objcopy failed with exit code %s (cmd was %s)%s" % (retval, cmd, ":\n%s" % output if output else ""))
|
|
|
|
if newmode:
|
|
os.chmod(file, origmode)
|
|
|
|
return 0
|
|
|
|
def copydebugsources(debugsrcdir, d):
|
|
# The debug src information written out to sourcefile is further procecessed
|
|
# and copied to the destination here.
|
|
|
|
import stat
|
|
|
|
sourcefile = d.expand("${WORKDIR}/debugsources.list")
|
|
if debugsrcdir and os.path.isfile(sourcefile):
|
|
dvar = d.getVar('PKGD', True)
|
|
strip = d.getVar("STRIP", True)
|
|
objcopy = d.getVar("OBJCOPY", True)
|
|
debugedit = d.expand("${STAGING_LIBDIR_NATIVE}/rpm/bin/debugedit")
|
|
workdir = d.getVar("WORKDIR", True)
|
|
workparentdir = os.path.dirname(os.path.dirname(workdir))
|
|
workbasedir = os.path.basename(os.path.dirname(workdir)) + "/" + os.path.basename(workdir)
|
|
|
|
nosuchdir = []
|
|
basepath = dvar
|
|
for p in debugsrcdir.split("/"):
|
|
basepath = basepath + "/" + p
|
|
if not cpath.exists(basepath):
|
|
nosuchdir.append(basepath)
|
|
bb.utils.mkdirhier(basepath)
|
|
cpath.updatecache(basepath)
|
|
|
|
processdebugsrc = "LC_ALL=C ; sort -z -u '%s' | egrep -v -z '(<internal>|<built-in>)$' | "
|
|
# We need to ignore files that are not actually ours
|
|
# we do this by only paying attention to items from this package
|
|
processdebugsrc += "fgrep -zw '%s' | "
|
|
processdebugsrc += "(cd '%s' ; cpio -pd0mlL --no-preserve-owner '%s%s' 2>/dev/null)"
|
|
|
|
cmd = processdebugsrc % (sourcefile, workbasedir, workparentdir, dvar, debugsrcdir)
|
|
(retval, output) = oe.utils.getstatusoutput(cmd)
|
|
# Can "fail" if internal headers/transient sources are attempted
|
|
#if retval:
|
|
# bb.fatal("debug source copy failed with exit code %s (cmd was %s)" % (retval, cmd))
|
|
|
|
# cpio seems to have a bug with -lL together and symbolic links are just copied, not dereferenced.
|
|
# Work around this by manually finding and copying any symbolic links that made it through.
|
|
cmd = "find %s%s -type l -print0 -delete | sed s#%s%s/##g | (cd '%s' ; cpio -pd0mL --no-preserve-owner '%s%s' 2>/dev/null)" % (dvar, debugsrcdir, dvar, debugsrcdir, workparentdir, dvar, debugsrcdir)
|
|
(retval, output) = oe.utils.getstatusoutput(cmd)
|
|
if retval:
|
|
bb.fatal("debugsrc symlink fixup failed with exit code %s (cmd was %s)" % (retval, cmd))
|
|
|
|
# The copy by cpio may have resulted in some empty directories! Remove these
|
|
cmd = "find %s%s -empty -type d -delete" % (dvar, debugsrcdir)
|
|
(retval, output) = oe.utils.getstatusoutput(cmd)
|
|
if retval:
|
|
bb.fatal("empty directory removal failed with exit code %s (cmd was %s)%s" % (retval, cmd, ":\n%s" % output if output else ""))
|
|
|
|
# Also remove debugsrcdir if its empty
|
|
for p in nosuchdir[::-1]:
|
|
if os.path.exists(p) and not os.listdir(p):
|
|
os.rmdir(p)
|
|
|
|
#
|
|
# Package data handling routines
|
|
#
|
|
|
|
def get_package_mapping (pkg, basepkg, d):
|
|
import oe.packagedata
|
|
|
|
data = oe.packagedata.read_subpkgdata(pkg, d)
|
|
key = "PKG_%s" % pkg
|
|
|
|
if key in data:
|
|
# Have to avoid undoing the write_extra_pkgs(global_variants...)
|
|
if bb.data.inherits_class('allarch', d) and data[key] == basepkg:
|
|
return pkg
|
|
return data[key]
|
|
|
|
return pkg
|
|
|
|
def get_package_additional_metadata (pkg_type, d):
|
|
base_key = "PACKAGE_ADD_METADATA"
|
|
for key in ("%s_%s" % (base_key, pkg_type.upper()), base_key):
|
|
if d.getVar(key, False) is None:
|
|
continue
|
|
d.setVarFlag(key, "type", "list")
|
|
if d.getVarFlag(key, "separator") is None:
|
|
d.setVarFlag(key, "separator", "\\n")
|
|
metadata_fields = [field.strip() for field in oe.data.typed_value(key, d)]
|
|
return "\n".join(metadata_fields).strip()
|
|
|
|
def runtime_mapping_rename (varname, pkg, d):
|
|
#bb.note("%s before: %s" % (varname, d.getVar(varname, True)))
|
|
|
|
if bb.data.inherits_class('packagegroup', d):
|
|
return
|
|
|
|
new_depends = {}
|
|
deps = bb.utils.explode_dep_versions2(d.getVar(varname, True) or "")
|
|
for depend in deps:
|
|
new_depend = get_package_mapping(depend, pkg, d)
|
|
new_depends[new_depend] = deps[depend]
|
|
|
|
d.setVar(varname, bb.utils.join_deps(new_depends, commasep=False))
|
|
|
|
#bb.note("%s after: %s" % (varname, d.getVar(varname, True)))
|
|
|
|
#
|
|
# Package functions suitable for inclusion in PACKAGEFUNCS
|
|
#
|
|
|
|
python package_get_auto_pr() {
|
|
import oe.prservice
|
|
import re
|
|
|
|
# Support per recipe PRSERV_HOST
|
|
pn = d.getVar('PN', True)
|
|
host = d.getVar("PRSERV_HOST_" + pn, True)
|
|
if not (host is None):
|
|
d.setVar("PRSERV_HOST", host)
|
|
|
|
pkgv = d.getVar("PKGV", True)
|
|
|
|
# PR Server not active, handle AUTOINC
|
|
if not d.getVar('PRSERV_HOST', True):
|
|
if 'AUTOINC' in pkgv:
|
|
d.setVar("PKGV", pkgv.replace("AUTOINC", "0"))
|
|
return
|
|
|
|
auto_pr = None
|
|
pv = d.getVar("PV", True)
|
|
version = d.getVar("PRAUTOINX", True)
|
|
pkgarch = d.getVar("PACKAGE_ARCH", True)
|
|
checksum = d.getVar("BB_TASKHASH", True)
|
|
|
|
if d.getVar('PRSERV_LOCKDOWN', True):
|
|
auto_pr = d.getVar('PRAUTO_' + version + '_' + pkgarch, True) or d.getVar('PRAUTO_' + version, True) or None
|
|
if auto_pr is None:
|
|
bb.fatal("Can NOT get PRAUTO from lockdown exported file")
|
|
d.setVar('PRAUTO',str(auto_pr))
|
|
return
|
|
|
|
try:
|
|
conn = d.getVar("__PRSERV_CONN", True)
|
|
if conn is None:
|
|
conn = oe.prservice.prserv_make_conn(d)
|
|
if conn is not None:
|
|
if "AUTOINC" in pkgv:
|
|
srcpv = bb.fetch2.get_srcrev(d)
|
|
base_ver = "AUTOINC-%s" % version[:version.find(srcpv)]
|
|
value = conn.getPR(base_ver, pkgarch, srcpv)
|
|
d.setVar("PKGV", pkgv.replace("AUTOINC", str(value)))
|
|
|
|
auto_pr = conn.getPR(version, pkgarch, checksum)
|
|
except Exception as e:
|
|
bb.fatal("Can NOT get PRAUTO, exception %s" % str(e))
|
|
if auto_pr is None:
|
|
bb.fatal("Can NOT get PRAUTO from remote PR service")
|
|
d.setVar('PRAUTO',str(auto_pr))
|
|
}
|
|
|
|
LOCALEBASEPN ??= "${PN}"
|
|
|
|
python package_do_split_locales() {
|
|
if (d.getVar('PACKAGE_NO_LOCALE', True) == '1'):
|
|
bb.debug(1, "package requested not splitting locales")
|
|
return
|
|
|
|
packages = (d.getVar('PACKAGES', True) or "").split()
|
|
|
|
datadir = d.getVar('datadir', True)
|
|
if not datadir:
|
|
bb.note("datadir not defined")
|
|
return
|
|
|
|
dvar = d.getVar('PKGD', True)
|
|
pn = d.getVar('LOCALEBASEPN', True)
|
|
|
|
if pn + '-locale' in packages:
|
|
packages.remove(pn + '-locale')
|
|
|
|
localedir = os.path.join(dvar + datadir, 'locale')
|
|
|
|
if not cpath.isdir(localedir):
|
|
bb.debug(1, "No locale files in this package")
|
|
return
|
|
|
|
locales = os.listdir(localedir)
|
|
|
|
summary = d.getVar('SUMMARY', True) or pn
|
|
description = d.getVar('DESCRIPTION', True) or ""
|
|
locale_section = d.getVar('LOCALE_SECTION', True)
|
|
mlprefix = d.getVar('MLPREFIX', True) or ""
|
|
for l in sorted(locales):
|
|
ln = legitimize_package_name(l)
|
|
pkg = pn + '-locale-' + ln
|
|
packages.append(pkg)
|
|
d.setVar('FILES_' + pkg, os.path.join(datadir, 'locale', l))
|
|
d.setVar('RRECOMMENDS_' + pkg, '%svirtual-locale-%s' % (mlprefix, ln))
|
|
d.setVar('RPROVIDES_' + pkg, '%s-locale %s%s-translation' % (pn, mlprefix, ln))
|
|
d.setVar('SUMMARY_' + pkg, '%s - %s translations' % (summary, l))
|
|
d.setVar('DESCRIPTION_' + pkg, '%s This package contains language translation files for the %s locale.' % (description, l))
|
|
if locale_section:
|
|
d.setVar('SECTION_' + pkg, locale_section)
|
|
|
|
d.setVar('PACKAGES', ' '.join(packages))
|
|
|
|
# Disabled by RP 18/06/07
|
|
# Wildcards aren't supported in debian
|
|
# They break with ipkg since glibc-locale* will mean that
|
|
# glibc-localedata-translit* won't install as a dependency
|
|
# for some other package which breaks meta-toolchain
|
|
# Probably breaks since virtual-locale- isn't provided anywhere
|
|
#rdep = (d.getVar('RDEPENDS_%s' % pn, True) or "").split()
|
|
#rdep.append('%s-locale*' % pn)
|
|
#d.setVar('RDEPENDS_%s' % pn, ' '.join(rdep))
|
|
}
|
|
|
|
python perform_packagecopy () {
|
|
dest = d.getVar('D', True)
|
|
dvar = d.getVar('PKGD', True)
|
|
|
|
# Start by package population by taking a copy of the installed
|
|
# files to operate on
|
|
# Preserve sparse files and hard links
|
|
cmd = 'tar -cf - -C %s -p . | tar -xf - -C %s' % (dest, dvar)
|
|
(retval, output) = oe.utils.getstatusoutput(cmd)
|
|
if retval:
|
|
bb.fatal("file copy failed with exit code %s (cmd was %s)%s" % (retval, cmd, ":\n%s" % output if output else ""))
|
|
|
|
# replace RPATHs for the nativesdk binaries, to make them relocatable
|
|
if bb.data.inherits_class('nativesdk', d) or bb.data.inherits_class('cross-canadian', d):
|
|
rpath_replace (dvar, d)
|
|
}
|
|
perform_packagecopy[cleandirs] = "${PKGD}"
|
|
perform_packagecopy[dirs] = "${PKGD}"
|
|
|
|
# We generate a master list of directories to process, we start by
|
|
# seeding this list with reasonable defaults, then load from
|
|
# the fs-perms.txt files
|
|
python fixup_perms () {
|
|
import pwd, grp
|
|
|
|
# init using a string with the same format as a line as documented in
|
|
# the fs-perms.txt file
|
|
# <path> <mode> <uid> <gid> <walk> <fmode> <fuid> <fgid>
|
|
# <path> link <link target>
|
|
#
|
|
# __str__ can be used to print out an entry in the input format
|
|
#
|
|
# if fs_perms_entry.path is None:
|
|
# an error occured
|
|
# if fs_perms_entry.link, you can retrieve:
|
|
# fs_perms_entry.path = path
|
|
# fs_perms_entry.link = target of link
|
|
# if not fs_perms_entry.link, you can retrieve:
|
|
# fs_perms_entry.path = path
|
|
# fs_perms_entry.mode = expected dir mode or None
|
|
# fs_perms_entry.uid = expected uid or -1
|
|
# fs_perms_entry.gid = expected gid or -1
|
|
# fs_perms_entry.walk = 'true' or something else
|
|
# fs_perms_entry.fmode = expected file mode or None
|
|
# fs_perms_entry.fuid = expected file uid or -1
|
|
# fs_perms_entry_fgid = expected file gid or -1
|
|
class fs_perms_entry():
|
|
def __init__(self, line):
|
|
lsplit = line.split()
|
|
if len(lsplit) == 3 and lsplit[1].lower() == "link":
|
|
self._setlink(lsplit[0], lsplit[2])
|
|
elif len(lsplit) == 8:
|
|
self._setdir(lsplit[0], lsplit[1], lsplit[2], lsplit[3], lsplit[4], lsplit[5], lsplit[6], lsplit[7])
|
|
else:
|
|
msg = "Fixup Perms: invalid config line %s" % line
|
|
package_qa_handle_error("perm-config", msg, d)
|
|
self.path = None
|
|
self.link = None
|
|
|
|
def _setdir(self, path, mode, uid, gid, walk, fmode, fuid, fgid):
|
|
self.path = os.path.normpath(path)
|
|
self.link = None
|
|
self.mode = self._procmode(mode)
|
|
self.uid = self._procuid(uid)
|
|
self.gid = self._procgid(gid)
|
|
self.walk = walk.lower()
|
|
self.fmode = self._procmode(fmode)
|
|
self.fuid = self._procuid(fuid)
|
|
self.fgid = self._procgid(fgid)
|
|
|
|
def _setlink(self, path, link):
|
|
self.path = os.path.normpath(path)
|
|
self.link = link
|
|
|
|
def _procmode(self, mode):
|
|
if not mode or (mode and mode == "-"):
|
|
return None
|
|
else:
|
|
return int(mode,8)
|
|
|
|
# Note uid/gid -1 has special significance in os.lchown
|
|
def _procuid(self, uid):
|
|
if uid is None or uid == "-":
|
|
return -1
|
|
elif uid.isdigit():
|
|
return int(uid)
|
|
else:
|
|
return pwd.getpwnam(uid).pw_uid
|
|
|
|
def _procgid(self, gid):
|
|
if gid is None or gid == "-":
|
|
return -1
|
|
elif gid.isdigit():
|
|
return int(gid)
|
|
else:
|
|
return grp.getgrnam(gid).gr_gid
|
|
|
|
# Use for debugging the entries
|
|
def __str__(self):
|
|
if self.link:
|
|
return "%s link %s" % (self.path, self.link)
|
|
else:
|
|
mode = "-"
|
|
if self.mode:
|
|
mode = "0%o" % self.mode
|
|
fmode = "-"
|
|
if self.fmode:
|
|
fmode = "0%o" % self.fmode
|
|
uid = self._mapugid(self.uid)
|
|
gid = self._mapugid(self.gid)
|
|
fuid = self._mapugid(self.fuid)
|
|
fgid = self._mapugid(self.fgid)
|
|
return "%s %s %s %s %s %s %s %s" % (self.path, mode, uid, gid, self.walk, fmode, fuid, fgid)
|
|
|
|
def _mapugid(self, id):
|
|
if id is None or id == -1:
|
|
return "-"
|
|
else:
|
|
return "%d" % id
|
|
|
|
# Fix the permission, owner and group of path
|
|
def fix_perms(path, mode, uid, gid, dir):
|
|
if mode and not os.path.islink(path):
|
|
#bb.note("Fixup Perms: chmod 0%o %s" % (mode, dir))
|
|
os.chmod(path, mode)
|
|
# -1 is a special value that means don't change the uid/gid
|
|
# if they are BOTH -1, don't bother to lchown
|
|
if not (uid == -1 and gid == -1):
|
|
#bb.note("Fixup Perms: lchown %d:%d %s" % (uid, gid, dir))
|
|
os.lchown(path, uid, gid)
|
|
|
|
# Return a list of configuration files based on either the default
|
|
# files/fs-perms.txt or the contents of FILESYSTEM_PERMS_TABLES
|
|
# paths are resolved via BBPATH
|
|
def get_fs_perms_list(d):
|
|
str = ""
|
|
bbpath = d.getVar('BBPATH', True)
|
|
fs_perms_tables = d.getVar('FILESYSTEM_PERMS_TABLES', True)
|
|
if not fs_perms_tables:
|
|
fs_perms_tables = 'files/fs-perms.txt'
|
|
for conf_file in fs_perms_tables.split():
|
|
str += " %s" % bb.utils.which(bbpath, conf_file)
|
|
return str
|
|
|
|
|
|
|
|
dvar = d.getVar('PKGD', True)
|
|
|
|
fs_perms_table = {}
|
|
|
|
# By default all of the standard directories specified in
|
|
# bitbake.conf will get 0755 root:root.
|
|
target_path_vars = [ 'base_prefix',
|
|
'prefix',
|
|
'exec_prefix',
|
|
'base_bindir',
|
|
'base_sbindir',
|
|
'base_libdir',
|
|
'datadir',
|
|
'sysconfdir',
|
|
'servicedir',
|
|
'sharedstatedir',
|
|
'localstatedir',
|
|
'infodir',
|
|
'mandir',
|
|
'docdir',
|
|
'bindir',
|
|
'sbindir',
|
|
'libexecdir',
|
|
'libdir',
|
|
'includedir',
|
|
'oldincludedir' ]
|
|
|
|
for path in target_path_vars:
|
|
dir = d.getVar(path, True) or ""
|
|
if dir == "":
|
|
continue
|
|
fs_perms_table[dir] = fs_perms_entry(bb.data.expand("%s 0755 root root false - - -" % (dir), d))
|
|
|
|
# Now we actually load from the configuration files
|
|
for conf in get_fs_perms_list(d).split():
|
|
if os.path.exists(conf):
|
|
f = open(conf)
|
|
for line in f:
|
|
if line.startswith('#'):
|
|
continue
|
|
lsplit = line.split()
|
|
if len(lsplit) == 0:
|
|
continue
|
|
if len(lsplit) != 8 and not (len(lsplit) == 3 and lsplit[1].lower() == "link"):
|
|
msg = "Fixup perms: %s invalid line: %s" % (conf, line)
|
|
package_qa_handle_error("perm-line", msg, d)
|
|
continue
|
|
entry = fs_perms_entry(d.expand(line))
|
|
if entry and entry.path:
|
|
fs_perms_table[entry.path] = entry
|
|
f.close()
|
|
|
|
# Debug -- list out in-memory table
|
|
#for dir in fs_perms_table:
|
|
# bb.note("Fixup Perms: %s: %s" % (dir, str(fs_perms_table[dir])))
|
|
|
|
# We process links first, so we can go back and fixup directory ownership
|
|
# for any newly created directories
|
|
for dir in fs_perms_table:
|
|
if not fs_perms_table[dir].link:
|
|
continue
|
|
|
|
origin = dvar + dir
|
|
if not (cpath.exists(origin) and cpath.isdir(origin) and not cpath.islink(origin)):
|
|
continue
|
|
|
|
link = fs_perms_table[dir].link
|
|
if link[0] == "/":
|
|
target = dvar + link
|
|
ptarget = link
|
|
else:
|
|
target = os.path.join(os.path.dirname(origin), link)
|
|
ptarget = os.path.join(os.path.dirname(dir), link)
|
|
if os.path.exists(target):
|
|
msg = "Fixup Perms: Unable to correct directory link, target already exists: %s -> %s" % (dir, ptarget)
|
|
package_qa_handle_error("perm-link", msg, d)
|
|
continue
|
|
|
|
# Create path to move directory to, move it, and then setup the symlink
|
|
bb.utils.mkdirhier(os.path.dirname(target))
|
|
#bb.note("Fixup Perms: Rename %s -> %s" % (dir, ptarget))
|
|
os.rename(origin, target)
|
|
#bb.note("Fixup Perms: Link %s -> %s" % (dir, link))
|
|
os.symlink(link, origin)
|
|
|
|
for dir in fs_perms_table:
|
|
if fs_perms_table[dir].link:
|
|
continue
|
|
|
|
origin = dvar + dir
|
|
if not (cpath.exists(origin) and cpath.isdir(origin)):
|
|
continue
|
|
|
|
fix_perms(origin, fs_perms_table[dir].mode, fs_perms_table[dir].uid, fs_perms_table[dir].gid, dir)
|
|
|
|
if fs_perms_table[dir].walk == 'true':
|
|
for root, dirs, files in os.walk(origin):
|
|
for dr in dirs:
|
|
each_dir = os.path.join(root, dr)
|
|
fix_perms(each_dir, fs_perms_table[dir].mode, fs_perms_table[dir].uid, fs_perms_table[dir].gid, dir)
|
|
for f in files:
|
|
each_file = os.path.join(root, f)
|
|
fix_perms(each_file, fs_perms_table[dir].fmode, fs_perms_table[dir].fuid, fs_perms_table[dir].fgid, dir)
|
|
}
|
|
|
|
python split_and_strip_files () {
|
|
import stat, errno
|
|
|
|
dvar = d.getVar('PKGD', True)
|
|
pn = d.getVar('PN', True)
|
|
|
|
# We default to '.debug' style
|
|
if d.getVar('PACKAGE_DEBUG_SPLIT_STYLE', True) == 'debug-file-directory':
|
|
# Single debug-file-directory style debug info
|
|
debugappend = ".debug"
|
|
debugdir = ""
|
|
debuglibdir = "/usr/lib/debug"
|
|
debugsrcdir = "/usr/src/debug"
|
|
elif d.getVar('PACKAGE_DEBUG_SPLIT_STYLE', True) == 'debug-without-src':
|
|
# Original OE-core, a.k.a. ".debug", style debug info, but without sources in /usr/src/debug
|
|
debugappend = ""
|
|
debugdir = "/.debug"
|
|
debuglibdir = ""
|
|
debugsrcdir = ""
|
|
else:
|
|
# Original OE-core, a.k.a. ".debug", style debug info
|
|
debugappend = ""
|
|
debugdir = "/.debug"
|
|
debuglibdir = ""
|
|
debugsrcdir = "/usr/src/debug"
|
|
|
|
sourcefile = d.expand("${WORKDIR}/debugsources.list")
|
|
bb.utils.remove(sourcefile)
|
|
|
|
os.chdir(dvar)
|
|
|
|
# Return type (bits):
|
|
# 0 - not elf
|
|
# 1 - ELF
|
|
# 2 - stripped
|
|
# 4 - executable
|
|
# 8 - shared library
|
|
# 16 - kernel module
|
|
def isELF(path):
|
|
type = 0
|
|
ret, result = oe.utils.getstatusoutput("file \"%s\"" % path.replace("\"", "\\\""))
|
|
|
|
if ret:
|
|
msg = "split_and_strip_files: 'file %s' failed" % path
|
|
package_qa_handle_error("split-strip", msg, d)
|
|
return type
|
|
|
|
# Not stripped
|
|
if "ELF" in result:
|
|
type |= 1
|
|
if "not stripped" not in result:
|
|
type |= 2
|
|
if "executable" in result:
|
|
type |= 4
|
|
if "shared" in result:
|
|
type |= 8
|
|
return type
|
|
|
|
|
|
#
|
|
# First lets figure out all of the files we may have to process ... do this only once!
|
|
#
|
|
elffiles = {}
|
|
symlinks = {}
|
|
kernmods = []
|
|
inodes = {}
|
|
libdir = os.path.abspath(dvar + os.sep + d.getVar("libdir", True))
|
|
baselibdir = os.path.abspath(dvar + os.sep + d.getVar("base_libdir", True))
|
|
if (d.getVar('INHIBIT_PACKAGE_STRIP', True) != '1'):
|
|
for root, dirs, files in cpath.walk(dvar):
|
|
for f in files:
|
|
file = os.path.join(root, f)
|
|
if file.endswith(".ko") and file.find("/lib/modules/") != -1:
|
|
kernmods.append(file)
|
|
continue
|
|
|
|
# Skip debug files
|
|
if debugappend and file.endswith(debugappend):
|
|
continue
|
|
if debugdir and debugdir in os.path.dirname(file[len(dvar):]):
|
|
continue
|
|
|
|
try:
|
|
ltarget = cpath.realpath(file, dvar, False)
|
|
s = cpath.lstat(ltarget)
|
|
except OSError as e:
|
|
(err, strerror) = e.args
|
|
if err != errno.ENOENT:
|
|
raise
|
|
# Skip broken symlinks
|
|
continue
|
|
if not s:
|
|
continue
|
|
# Check its an excutable
|
|
if (s[stat.ST_MODE] & stat.S_IXUSR) or (s[stat.ST_MODE] & stat.S_IXGRP) or (s[stat.ST_MODE] & stat.S_IXOTH) \
|
|
or ((file.startswith(libdir) or file.startswith(baselibdir)) and ".so" in f):
|
|
# If it's a symlink, and points to an ELF file, we capture the readlink target
|
|
if cpath.islink(file):
|
|
target = os.readlink(file)
|
|
if isELF(ltarget):
|
|
#bb.note("Sym: %s (%d)" % (ltarget, isELF(ltarget)))
|
|
symlinks[file] = target
|
|
continue
|
|
|
|
# It's a file (or hardlink), not a link
|
|
# ...but is it ELF, and is it already stripped?
|
|
elf_file = isELF(file)
|
|
if elf_file & 1:
|
|
if elf_file & 2:
|
|
if 'already-stripped' in (d.getVar('INSANE_SKIP_' + pn, True) or "").split():
|
|
bb.note("Skipping file %s from %s for already-stripped QA test" % (file[len(dvar):], pn))
|
|
else:
|
|
msg = "File '%s' from %s was already stripped, this will prevent future debugging!" % (file[len(dvar):], pn)
|
|
package_qa_handle_error("already-stripped", msg, d)
|
|
continue
|
|
|
|
# At this point we have an unstripped elf file. We need to:
|
|
# a) Make sure any file we strip is not hardlinked to anything else outside this tree
|
|
# b) Only strip any hardlinked file once (no races)
|
|
# c) Track any hardlinks between files so that we can reconstruct matching debug file hardlinks
|
|
|
|
# Use a reference of device ID and inode number to indentify files
|
|
file_reference = "%d_%d" % (s.st_dev, s.st_ino)
|
|
if file_reference in inodes:
|
|
os.unlink(file)
|
|
os.link(inodes[file_reference][0], file)
|
|
inodes[file_reference].append(file)
|
|
else:
|
|
inodes[file_reference] = [file]
|
|
# break hardlink
|
|
bb.utils.copyfile(file, file)
|
|
elffiles[file] = elf_file
|
|
# Modified the file so clear the cache
|
|
cpath.updatecache(file)
|
|
|
|
#
|
|
# First lets process debug splitting
|
|
#
|
|
if (d.getVar('INHIBIT_PACKAGE_DEBUG_SPLIT', True) != '1'):
|
|
for file in elffiles:
|
|
src = file[len(dvar):]
|
|
dest = debuglibdir + os.path.dirname(src) + debugdir + "/" + os.path.basename(src) + debugappend
|
|
fpath = dvar + dest
|
|
|
|
# Split the file...
|
|
bb.utils.mkdirhier(os.path.dirname(fpath))
|
|
#bb.note("Split %s -> %s" % (file, fpath))
|
|
# Only store off the hard link reference if we successfully split!
|
|
splitdebuginfo(file, fpath, debugsrcdir, sourcefile, d)
|
|
|
|
# Hardlink our debug symbols to the other hardlink copies
|
|
for ref in inodes:
|
|
if len(inodes[ref]) == 1:
|
|
continue
|
|
for file in inodes[ref][1:]:
|
|
src = file[len(dvar):]
|
|
dest = debuglibdir + os.path.dirname(src) + debugdir + "/" + os.path.basename(src) + debugappend
|
|
fpath = dvar + dest
|
|
target = inodes[ref][0][len(dvar):]
|
|
ftarget = dvar + debuglibdir + os.path.dirname(target) + debugdir + "/" + os.path.basename(target) + debugappend
|
|
bb.utils.mkdirhier(os.path.dirname(fpath))
|
|
#bb.note("Link %s -> %s" % (fpath, ftarget))
|
|
os.link(ftarget, fpath)
|
|
|
|
# Create symlinks for all cases we were able to split symbols
|
|
for file in symlinks:
|
|
src = file[len(dvar):]
|
|
dest = debuglibdir + os.path.dirname(src) + debugdir + "/" + os.path.basename(src) + debugappend
|
|
fpath = dvar + dest
|
|
# Skip it if the target doesn't exist
|
|
try:
|
|
s = os.stat(fpath)
|
|
except OSError as e:
|
|
(err, strerror) = e.args
|
|
if err != errno.ENOENT:
|
|
raise
|
|
continue
|
|
|
|
ltarget = symlinks[file]
|
|
lpath = os.path.dirname(ltarget)
|
|
lbase = os.path.basename(ltarget)
|
|
ftarget = ""
|
|
if lpath and lpath != ".":
|
|
ftarget += lpath + debugdir + "/"
|
|
ftarget += lbase + debugappend
|
|
if lpath.startswith(".."):
|
|
ftarget = os.path.join("..", ftarget)
|
|
bb.utils.mkdirhier(os.path.dirname(fpath))
|
|
#bb.note("Symlink %s -> %s" % (fpath, ftarget))
|
|
os.symlink(ftarget, fpath)
|
|
|
|
# Process the debugsrcdir if requested...
|
|
# This copies and places the referenced sources for later debugging...
|
|
copydebugsources(debugsrcdir, d)
|
|
#
|
|
# End of debug splitting
|
|
#
|
|
|
|
#
|
|
# Now lets go back over things and strip them
|
|
#
|
|
if (d.getVar('INHIBIT_PACKAGE_STRIP', True) != '1'):
|
|
strip = d.getVar("STRIP", True)
|
|
sfiles = []
|
|
for file in elffiles:
|
|
elf_file = int(elffiles[file])
|
|
#bb.note("Strip %s" % file)
|
|
sfiles.append((file, elf_file, strip))
|
|
for f in kernmods:
|
|
sfiles.append((f, 16, strip))
|
|
|
|
oe.utils.multiprocess_exec(sfiles, oe.package.runstrip)
|
|
|
|
#
|
|
# End of strip
|
|
#
|
|
}
|
|
|
|
python populate_packages () {
|
|
import glob, re
|
|
|
|
workdir = d.getVar('WORKDIR', True)
|
|
outdir = d.getVar('DEPLOY_DIR', True)
|
|
dvar = d.getVar('PKGD', True)
|
|
packages = d.getVar('PACKAGES', True)
|
|
pn = d.getVar('PN', True)
|
|
|
|
bb.utils.mkdirhier(outdir)
|
|
os.chdir(dvar)
|
|
|
|
# Sanity check PACKAGES for duplicates
|
|
# Sanity should be moved to sanity.bbclass once we have the infrastucture
|
|
package_list = []
|
|
|
|
for pkg in packages.split():
|
|
if pkg in package_list:
|
|
msg = "%s is listed in PACKAGES multiple times, this leads to packaging errors." % pkg
|
|
package_qa_handle_error("packages-list", msg, d)
|
|
else:
|
|
package_list.append(pkg)
|
|
d.setVar('PACKAGES', ' '.join(package_list))
|
|
pkgdest = d.getVar('PKGDEST', True)
|
|
|
|
seen = []
|
|
|
|
# os.mkdir masks the permissions with umask so we have to unset it first
|
|
oldumask = os.umask(0)
|
|
|
|
for pkg in package_list:
|
|
root = os.path.join(pkgdest, pkg)
|
|
bb.utils.mkdirhier(root)
|
|
|
|
filesvar = d.getVar('FILES_%s' % pkg, True) or ""
|
|
if "//" in filesvar:
|
|
msg = "FILES variable for package %s contains '//' which is invalid. Attempting to fix this but you should correct the metadata.\n" % pkg
|
|
package_qa_handle_error("files-invalid", msg, d)
|
|
filesvar.replace("//", "/")
|
|
|
|
origfiles = filesvar.split()
|
|
files = files_from_filevars(origfiles)
|
|
|
|
for file in files:
|
|
if (not cpath.islink(file)) and (not cpath.exists(file)):
|
|
continue
|
|
if file in seen:
|
|
continue
|
|
seen.append(file)
|
|
|
|
def mkdir(src, dest, p):
|
|
src = os.path.join(src, p)
|
|
dest = os.path.join(dest, p)
|
|
fstat = cpath.stat(src)
|
|
os.mkdir(dest, fstat.st_mode)
|
|
os.chown(dest, fstat.st_uid, fstat.st_gid)
|
|
if p not in seen:
|
|
seen.append(p)
|
|
cpath.updatecache(dest)
|
|
|
|
def mkdir_recurse(src, dest, paths):
|
|
if cpath.exists(dest + '/' + paths):
|
|
return
|
|
while paths.startswith("./"):
|
|
paths = paths[2:]
|
|
p = "."
|
|
for c in paths.split("/"):
|
|
p = os.path.join(p, c)
|
|
if not cpath.exists(os.path.join(dest, p)):
|
|
mkdir(src, dest, p)
|
|
|
|
if cpath.isdir(file) and not cpath.islink(file):
|
|
mkdir_recurse(dvar, root, file)
|
|
continue
|
|
|
|
mkdir_recurse(dvar, root, os.path.dirname(file))
|
|
fpath = os.path.join(root,file)
|
|
if not cpath.islink(file):
|
|
os.link(file, fpath)
|
|
fstat = cpath.stat(file)
|
|
os.chmod(fpath, fstat.st_mode)
|
|
os.chown(fpath, fstat.st_uid, fstat.st_gid)
|
|
continue
|
|
ret = bb.utils.copyfile(file, fpath)
|
|
if ret is False or ret == 0:
|
|
raise bb.build.FuncFailed("File population failed")
|
|
|
|
os.umask(oldumask)
|
|
os.chdir(workdir)
|
|
|
|
# Handle LICENSE_EXCLUSION
|
|
package_list = []
|
|
for pkg in packages.split():
|
|
if d.getVar('LICENSE_EXCLUSION-' + pkg, True):
|
|
msg = "%s has an incompatible license. Excluding from packaging." % pkg
|
|
package_qa_handle_error("incompatible-license", msg, d)
|
|
else:
|
|
package_list.append(pkg)
|
|
d.setVar('PACKAGES', ' '.join(package_list))
|
|
|
|
unshipped = []
|
|
for root, dirs, files in cpath.walk(dvar):
|
|
dir = root[len(dvar):]
|
|
if not dir:
|
|
dir = os.sep
|
|
for f in (files + dirs):
|
|
path = os.path.join(dir, f)
|
|
if ('.' + path) not in seen:
|
|
unshipped.append(path)
|
|
|
|
if unshipped != []:
|
|
msg = pn + ": Files/directories were installed but not shipped in any package:"
|
|
if "installed-vs-shipped" in (d.getVar('INSANE_SKIP_' + pn, True) or "").split():
|
|
bb.note("Package %s skipping QA tests: installed-vs-shipped" % pn)
|
|
else:
|
|
for f in unshipped:
|
|
msg = msg + "\n " + f
|
|
msg = msg + "\nPlease set FILES such that these items are packaged. Alternatively if they are unneeded, avoid installing them or delete them within do_install."
|
|
package_qa_handle_error("installed-vs-shipped", msg, d)
|
|
}
|
|
populate_packages[dirs] = "${D}"
|
|
|
|
python package_fixsymlinks () {
|
|
import errno
|
|
pkgdest = d.getVar('PKGDEST', True)
|
|
packages = d.getVar("PACKAGES", False).split()
|
|
|
|
dangling_links = {}
|
|
pkg_files = {}
|
|
for pkg in packages:
|
|
dangling_links[pkg] = []
|
|
pkg_files[pkg] = []
|
|
inst_root = os.path.join(pkgdest, pkg)
|
|
for path in pkgfiles[pkg]:
|
|
rpath = path[len(inst_root):]
|
|
pkg_files[pkg].append(rpath)
|
|
rtarget = cpath.realpath(path, inst_root, True, assume_dir = True)
|
|
if not cpath.lexists(rtarget):
|
|
dangling_links[pkg].append(os.path.normpath(rtarget[len(inst_root):]))
|
|
|
|
newrdepends = {}
|
|
for pkg in dangling_links:
|
|
for l in dangling_links[pkg]:
|
|
found = False
|
|
bb.debug(1, "%s contains dangling link %s" % (pkg, l))
|
|
for p in packages:
|
|
if l in pkg_files[p]:
|
|
found = True
|
|
bb.debug(1, "target found in %s" % p)
|
|
if p == pkg:
|
|
break
|
|
if pkg not in newrdepends:
|
|
newrdepends[pkg] = []
|
|
newrdepends[pkg].append(p)
|
|
break
|
|
if found == False:
|
|
bb.note("%s contains dangling symlink to %s" % (pkg, l))
|
|
|
|
for pkg in newrdepends:
|
|
rdepends = bb.utils.explode_dep_versions2(d.getVar('RDEPENDS_' + pkg, True) or "")
|
|
for p in newrdepends[pkg]:
|
|
if p not in rdepends:
|
|
rdepends[p] = []
|
|
d.setVar('RDEPENDS_' + pkg, bb.utils.join_deps(rdepends, commasep=False))
|
|
}
|
|
|
|
|
|
python package_package_name_hook() {
|
|
"""
|
|
A package_name_hook function can be used to rewrite the package names by
|
|
changing PKG. For an example, see debian.bbclass.
|
|
"""
|
|
pass
|
|
}
|
|
|
|
EXPORT_FUNCTIONS package_name_hook
|
|
|
|
|
|
PKGDESTWORK = "${WORKDIR}/pkgdata"
|
|
|
|
python emit_pkgdata() {
|
|
from glob import glob
|
|
import json
|
|
|
|
def write_if_exists(f, pkg, var):
|
|
def encode(str):
|
|
import codecs
|
|
c = codecs.getencoder("string_escape")
|
|
return c(str)[0]
|
|
|
|
val = d.getVar('%s_%s' % (var, pkg), True)
|
|
if val:
|
|
f.write('%s_%s: %s\n' % (var, pkg, encode(val)))
|
|
return val
|
|
val = d.getVar('%s' % (var), True)
|
|
if val:
|
|
f.write('%s: %s\n' % (var, encode(val)))
|
|
return val
|
|
|
|
def write_extra_pkgs(variants, pn, packages, pkgdatadir):
|
|
for variant in variants:
|
|
with open("%s/%s-%s" % (pkgdatadir, variant, pn), 'w') as fd:
|
|
fd.write("PACKAGES: %s\n" % ' '.join(
|
|
map(lambda pkg: '%s-%s' % (variant, pkg), packages.split())))
|
|
|
|
def write_extra_runtime_pkgs(variants, packages, pkgdatadir):
|
|
for variant in variants:
|
|
for pkg in packages.split():
|
|
ml_pkg = "%s-%s" % (variant, pkg)
|
|
subdata_file = "%s/runtime/%s" % (pkgdatadir, ml_pkg)
|
|
with open(subdata_file, 'w') as fd:
|
|
fd.write("PKG_%s: %s" % (ml_pkg, pkg))
|
|
|
|
packages = d.getVar('PACKAGES', True)
|
|
pkgdest = d.getVar('PKGDEST', True)
|
|
pkgdatadir = d.getVar('PKGDESTWORK', True)
|
|
|
|
# Take shared lock since we're only reading, not writing
|
|
lf = bb.utils.lockfile(d.expand("${PACKAGELOCK}"), True)
|
|
|
|
data_file = pkgdatadir + d.expand("/${PN}" )
|
|
f = open(data_file, 'w')
|
|
f.write("PACKAGES: %s\n" % packages)
|
|
f.close()
|
|
|
|
pn = d.getVar('PN', True)
|
|
global_variants = (d.getVar('MULTILIB_GLOBAL_VARIANTS', True) or "").split()
|
|
variants = (d.getVar('MULTILIB_VARIANTS', True) or "").split()
|
|
|
|
if bb.data.inherits_class('kernel', d) or bb.data.inherits_class('module-base', d):
|
|
write_extra_pkgs(variants, pn, packages, pkgdatadir)
|
|
|
|
if (bb.data.inherits_class('allarch', d) and not bb.data.inherits_class('packagegroup', d)):
|
|
write_extra_pkgs(global_variants, pn, packages, pkgdatadir)
|
|
|
|
workdir = d.getVar('WORKDIR', True)
|
|
|
|
for pkg in packages.split():
|
|
pkgval = d.getVar('PKG_%s' % pkg, True)
|
|
if pkgval is None:
|
|
pkgval = pkg
|
|
d.setVar('PKG_%s' % pkg, pkg)
|
|
|
|
pkgdestpkg = os.path.join(pkgdest, pkg)
|
|
files = {}
|
|
total_size = 0
|
|
for f in pkgfiles[pkg]:
|
|
relpth = os.path.relpath(f, pkgdestpkg)
|
|
fstat = os.lstat(f)
|
|
total_size += fstat.st_size
|
|
files[os.sep + relpth] = fstat.st_size
|
|
d.setVar('FILES_INFO', json.dumps(files))
|
|
|
|
subdata_file = pkgdatadir + "/runtime/%s" % pkg
|
|
sf = open(subdata_file, 'w')
|
|
write_if_exists(sf, pkg, 'PN')
|
|
write_if_exists(sf, pkg, 'PE')
|
|
write_if_exists(sf, pkg, 'PV')
|
|
write_if_exists(sf, pkg, 'PR')
|
|
write_if_exists(sf, pkg, 'PKGE')
|
|
write_if_exists(sf, pkg, 'PKGV')
|
|
write_if_exists(sf, pkg, 'PKGR')
|
|
write_if_exists(sf, pkg, 'LICENSE')
|
|
write_if_exists(sf, pkg, 'DESCRIPTION')
|
|
write_if_exists(sf, pkg, 'SUMMARY')
|
|
write_if_exists(sf, pkg, 'RDEPENDS')
|
|
rprov = write_if_exists(sf, pkg, 'RPROVIDES')
|
|
write_if_exists(sf, pkg, 'RRECOMMENDS')
|
|
write_if_exists(sf, pkg, 'RSUGGESTS')
|
|
write_if_exists(sf, pkg, 'RREPLACES')
|
|
write_if_exists(sf, pkg, 'RCONFLICTS')
|
|
write_if_exists(sf, pkg, 'SECTION')
|
|
write_if_exists(sf, pkg, 'PKG')
|
|
write_if_exists(sf, pkg, 'ALLOW_EMPTY')
|
|
write_if_exists(sf, pkg, 'FILES')
|
|
write_if_exists(sf, pkg, 'pkg_postinst')
|
|
write_if_exists(sf, pkg, 'pkg_postrm')
|
|
write_if_exists(sf, pkg, 'pkg_preinst')
|
|
write_if_exists(sf, pkg, 'pkg_prerm')
|
|
write_if_exists(sf, pkg, 'FILERPROVIDESFLIST')
|
|
write_if_exists(sf, pkg, 'FILES_INFO')
|
|
for dfile in (d.getVar('FILERPROVIDESFLIST_' + pkg, True) or "").split():
|
|
write_if_exists(sf, pkg, 'FILERPROVIDES_' + dfile)
|
|
|
|
write_if_exists(sf, pkg, 'FILERDEPENDSFLIST')
|
|
for dfile in (d.getVar('FILERDEPENDSFLIST_' + pkg, True) or "").split():
|
|
write_if_exists(sf, pkg, 'FILERDEPENDS_' + dfile)
|
|
|
|
sf.write('%s_%s: %d\n' % ('PKGSIZE', pkg, total_size))
|
|
sf.close()
|
|
|
|
# Symlinks needed for rprovides lookup
|
|
if rprov:
|
|
for p in rprov.strip().split():
|
|
subdata_sym = pkgdatadir + "/runtime-rprovides/%s/%s" % (p, pkg)
|
|
bb.utils.mkdirhier(os.path.dirname(subdata_sym))
|
|
oe.path.symlink("../../runtime/%s" % pkg, subdata_sym, True)
|
|
|
|
allow_empty = d.getVar('ALLOW_EMPTY_%s' % pkg, True)
|
|
if not allow_empty:
|
|
allow_empty = d.getVar('ALLOW_EMPTY', True)
|
|
root = "%s/%s" % (pkgdest, pkg)
|
|
os.chdir(root)
|
|
g = glob('*')
|
|
if g or allow_empty == "1":
|
|
# Symlinks needed for reverse lookups (from the final package name)
|
|
subdata_sym = pkgdatadir + "/runtime-reverse/%s" % pkgval
|
|
oe.path.symlink("../runtime/%s" % pkg, subdata_sym, True)
|
|
|
|
packagedfile = pkgdatadir + '/runtime/%s.packaged' % pkg
|
|
open(packagedfile, 'w').close()
|
|
|
|
if bb.data.inherits_class('kernel', d) or bb.data.inherits_class('module-base', d):
|
|
write_extra_runtime_pkgs(variants, packages, pkgdatadir)
|
|
|
|
if bb.data.inherits_class('allarch', d) and not bb.data.inherits_class('packagegroup', d):
|
|
write_extra_runtime_pkgs(global_variants, packages, pkgdatadir)
|
|
|
|
bb.utils.unlockfile(lf)
|
|
}
|
|
emit_pkgdata[dirs] = "${PKGDESTWORK}/runtime ${PKGDESTWORK}/runtime-reverse ${PKGDESTWORK}/runtime-rprovides"
|
|
|
|
ldconfig_postinst_fragment() {
|
|
if [ x"$D" = "x" ]; then
|
|
if [ -x /sbin/ldconfig ]; then /sbin/ldconfig ; fi
|
|
fi
|
|
}
|
|
|
|
RPMDEPS = "${STAGING_LIBDIR_NATIVE}/rpm/bin/rpmdeps-oecore --macros ${STAGING_LIBDIR_NATIVE}/rpm/macros --define '_rpmfc_magic_path ${STAGING_DIR_NATIVE}${datadir_native}/misc/magic.mgc' --rpmpopt ${STAGING_LIBDIR_NATIVE}/rpm/rpmpopt"
|
|
|
|
# Collect perfile run-time dependency metadata
|
|
# Output:
|
|
# FILERPROVIDESFLIST_pkg - list of all files w/ deps
|
|
# FILERPROVIDES_filepath_pkg - per file dep
|
|
#
|
|
# FILERDEPENDSFLIST_pkg - list of all files w/ deps
|
|
# FILERDEPENDS_filepath_pkg - per file dep
|
|
|
|
python package_do_filedeps() {
|
|
if d.getVar('SKIP_FILEDEPS', True) == '1':
|
|
return
|
|
|
|
pkgdest = d.getVar('PKGDEST', True)
|
|
packages = d.getVar('PACKAGES', True)
|
|
rpmdeps = d.getVar('RPMDEPS', True)
|
|
|
|
def chunks(files, n):
|
|
return [files[i:i+n] for i in range(0, len(files), n)]
|
|
|
|
pkglist = []
|
|
for pkg in packages.split():
|
|
if d.getVar('SKIP_FILEDEPS_' + pkg, True) == '1':
|
|
continue
|
|
if pkg.endswith('-dbg') or pkg.endswith('-doc') or pkg.find('-locale-') != -1 or pkg.find('-localedata-') != -1 or pkg.find('-gconv-') != -1 or pkg.find('-charmap-') != -1 or pkg.startswith('kernel-module-'):
|
|
continue
|
|
for files in chunks(pkgfiles[pkg], 100):
|
|
pkglist.append((pkg, files, rpmdeps, pkgdest))
|
|
|
|
processed = oe.utils.multiprocess_exec( pkglist, oe.package.filedeprunner)
|
|
|
|
provides_files = {}
|
|
requires_files = {}
|
|
|
|
for result in processed:
|
|
(pkg, provides, requires) = result
|
|
|
|
if pkg not in provides_files:
|
|
provides_files[pkg] = []
|
|
if pkg not in requires_files:
|
|
requires_files[pkg] = []
|
|
|
|
for file in provides:
|
|
provides_files[pkg].append(file)
|
|
key = "FILERPROVIDES_" + file + "_" + pkg
|
|
d.setVar(key, " ".join(provides[file]))
|
|
|
|
for file in requires:
|
|
requires_files[pkg].append(file)
|
|
key = "FILERDEPENDS_" + file + "_" + pkg
|
|
d.setVar(key, " ".join(requires[file]))
|
|
|
|
for pkg in requires_files:
|
|
d.setVar("FILERDEPENDSFLIST_" + pkg, " ".join(requires_files[pkg]))
|
|
for pkg in provides_files:
|
|
d.setVar("FILERPROVIDESFLIST_" + pkg, " ".join(provides_files[pkg]))
|
|
}
|
|
|
|
SHLIBSDIRS = "${PKGDATA_DIR}/${MLPREFIX}shlibs2"
|
|
SHLIBSWORKDIR = "${PKGDESTWORK}/${MLPREFIX}shlibs2"
|
|
|
|
python package_do_shlibs() {
|
|
import re, pipes
|
|
import subprocess as sub
|
|
|
|
exclude_shlibs = d.getVar('EXCLUDE_FROM_SHLIBS', 0)
|
|
if exclude_shlibs:
|
|
bb.note("not generating shlibs")
|
|
return
|
|
|
|
lib_re = re.compile("^.*\.so")
|
|
libdir_re = re.compile(".*/%s$" % d.getVar('baselib', True))
|
|
|
|
packages = d.getVar('PACKAGES', True)
|
|
targetos = d.getVar('TARGET_OS', True)
|
|
|
|
workdir = d.getVar('WORKDIR', True)
|
|
|
|
ver = d.getVar('PKGV', True)
|
|
if not ver:
|
|
msg = "PKGV not defined"
|
|
package_qa_handle_error("pkgv-undefined", msg, d)
|
|
return
|
|
|
|
pkgdest = d.getVar('PKGDEST', True)
|
|
|
|
shlibswork_dir = d.getVar('SHLIBSWORKDIR', True)
|
|
|
|
# Take shared lock since we're only reading, not writing
|
|
lf = bb.utils.lockfile(d.expand("${PACKAGELOCK}"))
|
|
|
|
def linux_so(file, needed, sonames, renames, pkgver):
|
|
needs_ldconfig = False
|
|
ldir = os.path.dirname(file).replace(pkgdest + "/" + pkg, '')
|
|
cmd = d.getVar('OBJDUMP', True) + " -p " + pipes.quote(file) + " 2>/dev/null"
|
|
fd = os.popen(cmd)
|
|
lines = fd.readlines()
|
|
fd.close()
|
|
rpath = []
|
|
for l in lines:
|
|
m = re.match("\s+RPATH\s+([^\s]*)", l)
|
|
if m:
|
|
rpaths = m.group(1).replace("$ORIGIN", ldir).split(":")
|
|
rpath = map(os.path.normpath, rpaths)
|
|
for l in lines:
|
|
m = re.match("\s+NEEDED\s+([^\s]*)", l)
|
|
if m:
|
|
dep = m.group(1)
|
|
if dep not in needed[pkg]:
|
|
needed[pkg].append((dep, file, rpath))
|
|
m = re.match("\s+SONAME\s+([^\s]*)", l)
|
|
if m:
|
|
this_soname = m.group(1)
|
|
prov = (this_soname, ldir, pkgver)
|
|
if not prov in sonames:
|
|
# if library is private (only used by package) then do not build shlib for it
|
|
if not private_libs or this_soname not in private_libs:
|
|
sonames.append(prov)
|
|
if libdir_re.match(os.path.dirname(file)):
|
|
needs_ldconfig = True
|
|
if snap_symlinks and (os.path.basename(file) != this_soname):
|
|
renames.append((file, os.path.join(os.path.dirname(file), this_soname)))
|
|
return needs_ldconfig
|
|
|
|
def darwin_so(file, needed, sonames, renames, pkgver):
|
|
if not os.path.exists(file):
|
|
return
|
|
ldir = os.path.dirname(file).replace(pkgdest + "/" + pkg, '')
|
|
|
|
def get_combinations(base):
|
|
#
|
|
# Given a base library name, find all combinations of this split by "." and "-"
|
|
#
|
|
combos = []
|
|
options = base.split(".")
|
|
for i in range(1, len(options) + 1):
|
|
combos.append(".".join(options[0:i]))
|
|
options = base.split("-")
|
|
for i in range(1, len(options) + 1):
|
|
combos.append("-".join(options[0:i]))
|
|
return combos
|
|
|
|
if (file.endswith('.dylib') or file.endswith('.so')) and not pkg.endswith('-dev') and not pkg.endswith('-dbg'):
|
|
# Drop suffix
|
|
name = os.path.basename(file).rsplit(".",1)[0]
|
|
# Find all combinations
|
|
combos = get_combinations(name)
|
|
for combo in combos:
|
|
if not combo in sonames:
|
|
prov = (combo, ldir, pkgver)
|
|
sonames.append(prov)
|
|
if file.endswith('.dylib') or file.endswith('.so'):
|
|
rpath = []
|
|
p = sub.Popen([d.expand("${HOST_PREFIX}otool"), '-l', file],stdout=sub.PIPE,stderr=sub.PIPE)
|
|
err, out = p.communicate()
|
|
# If returned succesfully, process stderr for results
|
|
if p.returncode == 0:
|
|
for l in err.split("\n"):
|
|
l = l.strip()
|
|
if l.startswith('path '):
|
|
rpath.append(l.split()[1])
|
|
|
|
p = sub.Popen([d.expand("${HOST_PREFIX}otool"), '-L', file],stdout=sub.PIPE,stderr=sub.PIPE)
|
|
err, out = p.communicate()
|
|
# If returned succesfully, process stderr for results
|
|
if p.returncode == 0:
|
|
for l in err.split("\n"):
|
|
l = l.strip()
|
|
if not l or l.endswith(":"):
|
|
continue
|
|
if "is not an object file" in l:
|
|
continue
|
|
name = os.path.basename(l.split()[0]).rsplit(".", 1)[0]
|
|
if name and name not in needed[pkg]:
|
|
needed[pkg].append((name, file, []))
|
|
|
|
if d.getVar('PACKAGE_SNAP_LIB_SYMLINKS', True) == "1":
|
|
snap_symlinks = True
|
|
else:
|
|
snap_symlinks = False
|
|
|
|
if (d.getVar('USE_LDCONFIG', True) or "1") == "1":
|
|
use_ldconfig = True
|
|
else:
|
|
use_ldconfig = False
|
|
|
|
needed = {}
|
|
shlib_provider = oe.package.read_shlib_providers(d)
|
|
|
|
for pkg in packages.split():
|
|
private_libs = d.getVar('PRIVATE_LIBS_' + pkg, True) or d.getVar('PRIVATE_LIBS', True) or ""
|
|
private_libs = private_libs.split()
|
|
needs_ldconfig = False
|
|
bb.debug(2, "calculating shlib provides for %s" % pkg)
|
|
|
|
pkgver = d.getVar('PKGV_' + pkg, True)
|
|
if not pkgver:
|
|
pkgver = d.getVar('PV_' + pkg, True)
|
|
if not pkgver:
|
|
pkgver = ver
|
|
|
|
needed[pkg] = []
|
|
sonames = list()
|
|
renames = list()
|
|
for file in pkgfiles[pkg]:
|
|
soname = None
|
|
if cpath.islink(file):
|
|
continue
|
|
if targetos == "darwin" or targetos == "darwin8":
|
|
darwin_so(file, needed, sonames, renames, pkgver)
|
|
elif os.access(file, os.X_OK) or lib_re.match(file):
|
|
ldconfig = linux_so(file, needed, sonames, renames, pkgver)
|
|
needs_ldconfig = needs_ldconfig or ldconfig
|
|
for (old, new) in renames:
|
|
bb.note("Renaming %s to %s" % (old, new))
|
|
os.rename(old, new)
|
|
pkgfiles[pkg].remove(old)
|
|
|
|
shlibs_file = os.path.join(shlibswork_dir, pkg + ".list")
|
|
if len(sonames):
|
|
fd = open(shlibs_file, 'w')
|
|
for s in sonames:
|
|
if s[0] in shlib_provider and s[1] in shlib_provider[s[0]]:
|
|
(old_pkg, old_pkgver) = shlib_provider[s[0]][s[1]]
|
|
if old_pkg != pkg:
|
|
bb.warn('%s-%s was registered as shlib provider for %s, changing it to %s-%s because it was built later' % (old_pkg, old_pkgver, s[0], pkg, pkgver))
|
|
bb.debug(1, 'registering %s-%s as shlib provider for %s' % (pkg, pkgver, s[0]))
|
|
fd.write(s[0] + ':' + s[1] + ':' + s[2] + '\n')
|
|
if s[0] not in shlib_provider:
|
|
shlib_provider[s[0]] = {}
|
|
shlib_provider[s[0]][s[1]] = (pkg, pkgver)
|
|
fd.close()
|
|
if needs_ldconfig and use_ldconfig:
|
|
bb.debug(1, 'adding ldconfig call to postinst for %s' % pkg)
|
|
postinst = d.getVar('pkg_postinst_%s' % pkg, True)
|
|
if not postinst:
|
|
postinst = '#!/bin/sh\n'
|
|
postinst += d.getVar('ldconfig_postinst_fragment', True)
|
|
d.setVar('pkg_postinst_%s' % pkg, postinst)
|
|
bb.debug(1, 'LIBNAMES: pkg %s sonames %s' % (pkg, sonames))
|
|
|
|
bb.utils.unlockfile(lf)
|
|
|
|
assumed_libs = d.getVar('ASSUME_SHLIBS', True)
|
|
if assumed_libs:
|
|
libdir = d.getVar("libdir", True)
|
|
for e in assumed_libs.split():
|
|
l, dep_pkg = e.split(":")
|
|
lib_ver = None
|
|
dep_pkg = dep_pkg.rsplit("_", 1)
|
|
if len(dep_pkg) == 2:
|
|
lib_ver = dep_pkg[1]
|
|
dep_pkg = dep_pkg[0]
|
|
if l not in shlib_provider:
|
|
shlib_provider[l] = {}
|
|
shlib_provider[l][libdir] = (dep_pkg, lib_ver)
|
|
|
|
libsearchpath = [d.getVar('libdir', True), d.getVar('base_libdir', True)]
|
|
|
|
for pkg in packages.split():
|
|
bb.debug(2, "calculating shlib requirements for %s" % pkg)
|
|
|
|
deps = list()
|
|
for n in needed[pkg]:
|
|
# if n is in private libraries, don't try to search provider for it
|
|
# this could cause problem in case some abc.bb provides private
|
|
# /opt/abc/lib/libfoo.so.1 and contains /usr/bin/abc depending on system library libfoo.so.1
|
|
# but skipping it is still better alternative than providing own
|
|
# version and then adding runtime dependency for the same system library
|
|
if private_libs and n[0] in private_libs:
|
|
bb.debug(2, '%s: Dependency %s covered by PRIVATE_LIBS' % (pkg, n[0]))
|
|
continue
|
|
if n[0] in shlib_provider.keys():
|
|
shlib_provider_path = list()
|
|
for k in shlib_provider[n[0]].keys():
|
|
shlib_provider_path.append(k)
|
|
match = None
|
|
for p in n[2] + shlib_provider_path + libsearchpath:
|
|
if p in shlib_provider[n[0]]:
|
|
match = p
|
|
break
|
|
if match:
|
|
(dep_pkg, ver_needed) = shlib_provider[n[0]][match]
|
|
|
|
bb.debug(2, '%s: Dependency %s requires package %s (used by files: %s)' % (pkg, n[0], dep_pkg, n[1]))
|
|
|
|
if dep_pkg == pkg:
|
|
continue
|
|
|
|
if ver_needed:
|
|
dep = "%s (>= %s)" % (dep_pkg, ver_needed)
|
|
else:
|
|
dep = dep_pkg
|
|
if not dep in deps:
|
|
deps.append(dep)
|
|
continue
|
|
bb.note("Couldn't find shared library provider for %s, used by files: %s" % (n[0], n[1]))
|
|
|
|
deps_file = os.path.join(pkgdest, pkg + ".shlibdeps")
|
|
if os.path.exists(deps_file):
|
|
os.remove(deps_file)
|
|
if len(deps):
|
|
fd = open(deps_file, 'w')
|
|
for dep in deps:
|
|
fd.write(dep + '\n')
|
|
fd.close()
|
|
}
|
|
|
|
python package_do_pkgconfig () {
|
|
import re
|
|
|
|
packages = d.getVar('PACKAGES', True)
|
|
workdir = d.getVar('WORKDIR', True)
|
|
pkgdest = d.getVar('PKGDEST', True)
|
|
|
|
shlibs_dirs = d.getVar('SHLIBSDIRS', True).split()
|
|
shlibswork_dir = d.getVar('SHLIBSWORKDIR', True)
|
|
|
|
pc_re = re.compile('(.*)\.pc$')
|
|
var_re = re.compile('(.*)=(.*)')
|
|
field_re = re.compile('(.*): (.*)')
|
|
|
|
pkgconfig_provided = {}
|
|
pkgconfig_needed = {}
|
|
for pkg in packages.split():
|
|
pkgconfig_provided[pkg] = []
|
|
pkgconfig_needed[pkg] = []
|
|
for file in pkgfiles[pkg]:
|
|
m = pc_re.match(file)
|
|
if m:
|
|
pd = bb.data.init()
|
|
name = m.group(1)
|
|
pkgconfig_provided[pkg].append(name)
|
|
if not os.access(file, os.R_OK):
|
|
continue
|
|
f = open(file, 'r')
|
|
lines = f.readlines()
|
|
f.close()
|
|
for l in lines:
|
|
m = var_re.match(l)
|
|
if m:
|
|
name = m.group(1)
|
|
val = m.group(2)
|
|
pd.setVar(name, pd.expand(val))
|
|
continue
|
|
m = field_re.match(l)
|
|
if m:
|
|
hdr = m.group(1)
|
|
exp = bb.data.expand(m.group(2), pd)
|
|
if hdr == 'Requires':
|
|
pkgconfig_needed[pkg] += exp.replace(',', ' ').split()
|
|
|
|
# Take shared lock since we're only reading, not writing
|
|
lf = bb.utils.lockfile(d.expand("${PACKAGELOCK}"))
|
|
|
|
for pkg in packages.split():
|
|
pkgs_file = os.path.join(shlibswork_dir, pkg + ".pclist")
|
|
if pkgconfig_provided[pkg] != []:
|
|
f = open(pkgs_file, 'w')
|
|
for p in pkgconfig_provided[pkg]:
|
|
f.write('%s\n' % p)
|
|
f.close()
|
|
|
|
# Go from least to most specific since the last one found wins
|
|
for dir in reversed(shlibs_dirs):
|
|
if not os.path.exists(dir):
|
|
continue
|
|
for file in os.listdir(dir):
|
|
m = re.match('^(.*)\.pclist$', file)
|
|
if m:
|
|
pkg = m.group(1)
|
|
fd = open(os.path.join(dir, file))
|
|
lines = fd.readlines()
|
|
fd.close()
|
|
pkgconfig_provided[pkg] = []
|
|
for l in lines:
|
|
pkgconfig_provided[pkg].append(l.rstrip())
|
|
|
|
for pkg in packages.split():
|
|
deps = []
|
|
for n in pkgconfig_needed[pkg]:
|
|
found = False
|
|
for k in pkgconfig_provided.keys():
|
|
if n in pkgconfig_provided[k]:
|
|
if k != pkg and not (k in deps):
|
|
deps.append(k)
|
|
found = True
|
|
if found == False:
|
|
bb.note("couldn't find pkgconfig module '%s' in any package" % n)
|
|
deps_file = os.path.join(pkgdest, pkg + ".pcdeps")
|
|
if len(deps):
|
|
fd = open(deps_file, 'w')
|
|
for dep in deps:
|
|
fd.write(dep + '\n')
|
|
fd.close()
|
|
|
|
bb.utils.unlockfile(lf)
|
|
}
|
|
|
|
def read_libdep_files(d):
|
|
pkglibdeps = {}
|
|
packages = d.getVar('PACKAGES', True).split()
|
|
for pkg in packages:
|
|
pkglibdeps[pkg] = {}
|
|
for extension in ".shlibdeps", ".pcdeps", ".clilibdeps":
|
|
depsfile = d.expand("${PKGDEST}/" + pkg + extension)
|
|
if os.access(depsfile, os.R_OK):
|
|
fd = open(depsfile)
|
|
lines = fd.readlines()
|
|
fd.close()
|
|
for l in lines:
|
|
l.rstrip()
|
|
deps = bb.utils.explode_dep_versions2(l)
|
|
for dep in deps:
|
|
if not dep in pkglibdeps[pkg]:
|
|
pkglibdeps[pkg][dep] = deps[dep]
|
|
return pkglibdeps
|
|
|
|
python read_shlibdeps () {
|
|
pkglibdeps = read_libdep_files(d)
|
|
|
|
packages = d.getVar('PACKAGES', True).split()
|
|
for pkg in packages:
|
|
rdepends = bb.utils.explode_dep_versions2(d.getVar('RDEPENDS_' + pkg, True) or "")
|
|
for dep in pkglibdeps[pkg]:
|
|
# Add the dep if it's not already there, or if no comparison is set
|
|
if dep not in rdepends:
|
|
rdepends[dep] = []
|
|
for v in pkglibdeps[pkg][dep]:
|
|
if v not in rdepends[dep]:
|
|
rdepends[dep].append(v)
|
|
d.setVar('RDEPENDS_' + pkg, bb.utils.join_deps(rdepends, commasep=False))
|
|
}
|
|
|
|
python package_depchains() {
|
|
"""
|
|
For a given set of prefix and postfix modifiers, make those packages
|
|
RRECOMMENDS on the corresponding packages for its RDEPENDS.
|
|
|
|
Example: If package A depends upon package B, and A's .bb emits an
|
|
A-dev package, this would make A-dev Recommends: B-dev.
|
|
|
|
If only one of a given suffix is specified, it will take the RRECOMMENDS
|
|
based on the RDEPENDS of *all* other packages. If more than one of a given
|
|
suffix is specified, its will only use the RDEPENDS of the single parent
|
|
package.
|
|
"""
|
|
|
|
packages = d.getVar('PACKAGES', True)
|
|
postfixes = (d.getVar('DEPCHAIN_POST', True) or '').split()
|
|
prefixes = (d.getVar('DEPCHAIN_PRE', True) or '').split()
|
|
|
|
def pkg_adddeprrecs(pkg, base, suffix, getname, depends, d):
|
|
|
|
#bb.note('depends for %s is %s' % (base, depends))
|
|
rreclist = bb.utils.explode_dep_versions2(d.getVar('RRECOMMENDS_' + pkg, True) or "")
|
|
|
|
for depend in depends:
|
|
if depend.find('-native') != -1 or depend.find('-cross') != -1 or depend.startswith('virtual/'):
|
|
#bb.note("Skipping %s" % depend)
|
|
continue
|
|
if depend.endswith('-dev'):
|
|
depend = depend[:-4]
|
|
if depend.endswith('-dbg'):
|
|
depend = depend[:-4]
|
|
pkgname = getname(depend, suffix)
|
|
#bb.note("Adding %s for %s" % (pkgname, depend))
|
|
if pkgname not in rreclist and pkgname != pkg:
|
|
rreclist[pkgname] = []
|
|
|
|
#bb.note('setting: RRECOMMENDS_%s=%s' % (pkg, ' '.join(rreclist)))
|
|
d.setVar('RRECOMMENDS_%s' % pkg, bb.utils.join_deps(rreclist, commasep=False))
|
|
|
|
def pkg_addrrecs(pkg, base, suffix, getname, rdepends, d):
|
|
|
|
#bb.note('rdepends for %s is %s' % (base, rdepends))
|
|
rreclist = bb.utils.explode_dep_versions2(d.getVar('RRECOMMENDS_' + pkg, True) or "")
|
|
|
|
for depend in rdepends:
|
|
if depend.find('virtual-locale-') != -1:
|
|
#bb.note("Skipping %s" % depend)
|
|
continue
|
|
if depend.endswith('-dev'):
|
|
depend = depend[:-4]
|
|
if depend.endswith('-dbg'):
|
|
depend = depend[:-4]
|
|
pkgname = getname(depend, suffix)
|
|
#bb.note("Adding %s for %s" % (pkgname, depend))
|
|
if pkgname not in rreclist and pkgname != pkg:
|
|
rreclist[pkgname] = []
|
|
|
|
#bb.note('setting: RRECOMMENDS_%s=%s' % (pkg, ' '.join(rreclist)))
|
|
d.setVar('RRECOMMENDS_%s' % pkg, bb.utils.join_deps(rreclist, commasep=False))
|
|
|
|
def add_dep(list, dep):
|
|
if dep not in list:
|
|
list.append(dep)
|
|
|
|
depends = []
|
|
for dep in bb.utils.explode_deps(d.getVar('DEPENDS', True) or ""):
|
|
add_dep(depends, dep)
|
|
|
|
rdepends = []
|
|
for pkg in packages.split():
|
|
for dep in bb.utils.explode_deps(d.getVar('RDEPENDS_' + pkg, True) or ""):
|
|
add_dep(rdepends, dep)
|
|
|
|
#bb.note('rdepends is %s' % rdepends)
|
|
|
|
def post_getname(name, suffix):
|
|
return '%s%s' % (name, suffix)
|
|
def pre_getname(name, suffix):
|
|
return '%s%s' % (suffix, name)
|
|
|
|
pkgs = {}
|
|
for pkg in packages.split():
|
|
for postfix in postfixes:
|
|
if pkg.endswith(postfix):
|
|
if not postfix in pkgs:
|
|
pkgs[postfix] = {}
|
|
pkgs[postfix][pkg] = (pkg[:-len(postfix)], post_getname)
|
|
|
|
for prefix in prefixes:
|
|
if pkg.startswith(prefix):
|
|
if not prefix in pkgs:
|
|
pkgs[prefix] = {}
|
|
pkgs[prefix][pkg] = (pkg[:-len(prefix)], pre_getname)
|
|
|
|
if "-dbg" in pkgs:
|
|
pkglibdeps = read_libdep_files(d)
|
|
pkglibdeplist = []
|
|
for pkg in pkglibdeps:
|
|
for k in pkglibdeps[pkg]:
|
|
add_dep(pkglibdeplist, k)
|
|
# FIXME this should not look at PN once all task recipes inherit from task.bbclass
|
|
dbgdefaultdeps = ((d.getVar('DEPCHAIN_DBGDEFAULTDEPS', True) == '1') or (d.getVar('PN', True) or '').startswith('packagegroup-'))
|
|
|
|
for suffix in pkgs:
|
|
for pkg in pkgs[suffix]:
|
|
if d.getVarFlag('RRECOMMENDS_' + pkg, 'nodeprrecs'):
|
|
continue
|
|
(base, func) = pkgs[suffix][pkg]
|
|
if suffix == "-dev":
|
|
pkg_adddeprrecs(pkg, base, suffix, func, depends, d)
|
|
elif suffix == "-dbg":
|
|
if not dbgdefaultdeps:
|
|
pkg_addrrecs(pkg, base, suffix, func, pkglibdeplist, d)
|
|
continue
|
|
if len(pkgs[suffix]) == 1:
|
|
pkg_addrrecs(pkg, base, suffix, func, rdepends, d)
|
|
else:
|
|
rdeps = []
|
|
for dep in bb.utils.explode_deps(d.getVar('RDEPENDS_' + base, True) or ""):
|
|
add_dep(rdeps, dep)
|
|
pkg_addrrecs(pkg, base, suffix, func, rdeps, d)
|
|
}
|
|
|
|
# Since bitbake can't determine which variables are accessed during package
|
|
# iteration, we need to list them here:
|
|
PACKAGEVARS = "FILES RDEPENDS RRECOMMENDS SUMMARY DESCRIPTION RSUGGESTS RPROVIDES RCONFLICTS PKG ALLOW_EMPTY pkg_postinst pkg_postrm INITSCRIPT_NAME INITSCRIPT_PARAMS DEBIAN_NOAUTONAME ALTERNATIVE PKGE PKGV PKGR USERADD_PARAM GROUPADD_PARAM CONFFILES SYSTEMD_SERVICE LICENSE SECTION pkg_preinst pkg_prerm RREPLACES GROUPMEMS_PARAM SYSTEMD_AUTO_ENABLE"
|
|
|
|
def gen_packagevar(d):
|
|
ret = []
|
|
pkgs = (d.getVar("PACKAGES", True) or "").split()
|
|
vars = (d.getVar("PACKAGEVARS", True) or "").split()
|
|
for p in pkgs:
|
|
for v in vars:
|
|
ret.append(v + "_" + p)
|
|
|
|
# Ensure that changes to INCOMPATIBLE_LICENSE re-run do_package for
|
|
# affected recipes.
|
|
ret.append('LICENSE_EXCLUSION-%s' % p)
|
|
return " ".join(ret)
|
|
|
|
PACKAGE_PREPROCESS_FUNCS ?= ""
|
|
# Functions for setting up PKGD
|
|
PACKAGEBUILDPKGD ?= " \
|
|
perform_packagecopy \
|
|
${PACKAGE_PREPROCESS_FUNCS} \
|
|
split_and_strip_files \
|
|
fixup_perms \
|
|
"
|
|
# Functions which split PKGD up into separate packages
|
|
PACKAGESPLITFUNCS ?= " \
|
|
package_do_split_locales \
|
|
populate_packages"
|
|
# Functions which process metadata based on split packages
|
|
PACKAGEFUNCS += " \
|
|
package_fixsymlinks \
|
|
package_name_hook \
|
|
package_do_filedeps \
|
|
package_do_shlibs \
|
|
package_do_pkgconfig \
|
|
read_shlibdeps \
|
|
package_depchains \
|
|
emit_pkgdata"
|
|
|
|
python do_package () {
|
|
# Change the following version to cause sstate to invalidate the package
|
|
# cache. This is useful if an item this class depends on changes in a
|
|
# way that the output of this class changes. rpmdeps is a good example
|
|
# as any change to rpmdeps requires this to be rerun.
|
|
# PACKAGE_BBCLASS_VERSION = "1"
|
|
|
|
# Init cachedpath
|
|
global cpath
|
|
cpath = oe.cachedpath.CachedPath()
|
|
|
|
###########################################################################
|
|
# Sanity test the setup
|
|
###########################################################################
|
|
|
|
packages = (d.getVar('PACKAGES', True) or "").split()
|
|
if len(packages) < 1:
|
|
bb.debug(1, "No packages to build, skipping do_package")
|
|
return
|
|
|
|
workdir = d.getVar('WORKDIR', True)
|
|
outdir = d.getVar('DEPLOY_DIR', True)
|
|
dest = d.getVar('D', True)
|
|
dvar = d.getVar('PKGD', True)
|
|
pn = d.getVar('PN', True)
|
|
|
|
if not workdir or not outdir or not dest or not dvar or not pn:
|
|
msg = "WORKDIR, DEPLOY_DIR, D, PN and PKGD all must be defined, unable to package"
|
|
package_qa_handle_error("var-undefined", msg, d)
|
|
return
|
|
|
|
bb.build.exec_func("package_get_auto_pr", d)
|
|
|
|
###########################################################################
|
|
# Optimisations
|
|
###########################################################################
|
|
|
|
# Continually expanding complex expressions is inefficient, particularly
|
|
# when we write to the datastore and invalidate the expansion cache. This
|
|
# code pre-expands some frequently used variables
|
|
|
|
def expandVar(x, d):
|
|
d.setVar(x, d.getVar(x, True))
|
|
|
|
for x in 'PN', 'PV', 'BPN', 'TARGET_SYS', 'EXTENDPRAUTO':
|
|
expandVar(x, d)
|
|
|
|
###########################################################################
|
|
# Setup PKGD (from D)
|
|
###########################################################################
|
|
|
|
for f in (d.getVar('PACKAGEBUILDPKGD', True) or '').split():
|
|
bb.build.exec_func(f, d)
|
|
|
|
###########################################################################
|
|
# Split up PKGD into PKGDEST
|
|
###########################################################################
|
|
|
|
cpath = oe.cachedpath.CachedPath()
|
|
|
|
for f in (d.getVar('PACKAGESPLITFUNCS', True) or '').split():
|
|
bb.build.exec_func(f, d)
|
|
|
|
###########################################################################
|
|
# Process PKGDEST
|
|
###########################################################################
|
|
|
|
# Build global list of files in each split package
|
|
global pkgfiles
|
|
pkgfiles = {}
|
|
packages = d.getVar('PACKAGES', True).split()
|
|
pkgdest = d.getVar('PKGDEST', True)
|
|
for pkg in packages:
|
|
pkgfiles[pkg] = []
|
|
for walkroot, dirs, files in cpath.walk(pkgdest + "/" + pkg):
|
|
for file in files:
|
|
pkgfiles[pkg].append(walkroot + os.sep + file)
|
|
|
|
for f in (d.getVar('PACKAGEFUNCS', True) or '').split():
|
|
bb.build.exec_func(f, d)
|
|
}
|
|
|
|
do_package[dirs] = "${SHLIBSWORKDIR} ${PKGDESTWORK} ${D}"
|
|
do_package[vardeps] += "${PACKAGEBUILDPKGD} ${PACKAGESPLITFUNCS} ${PACKAGEFUNCS} ${@gen_packagevar(d)}"
|
|
addtask package after do_install
|
|
|
|
PACKAGELOCK = "${STAGING_DIR}/package-output.lock"
|
|
SSTATETASKS += "do_package"
|
|
do_package[cleandirs] = "${PKGDEST} ${PKGDESTWORK}"
|
|
do_package[sstate-plaindirs] = "${PKGD} ${PKGDEST} ${PKGDESTWORK}"
|
|
do_package[sstate-lockfile-shared] = "${PACKAGELOCK}"
|
|
do_package_setscene[dirs] = "${STAGING_DIR}"
|
|
|
|
python do_package_setscene () {
|
|
sstate_setscene(d)
|
|
}
|
|
addtask do_package_setscene
|
|
|
|
do_packagedata () {
|
|
:
|
|
}
|
|
|
|
addtask packagedata before do_build after do_package
|
|
|
|
SSTATETASKS += "do_packagedata"
|
|
do_packagedata[sstate-inputdirs] = "${PKGDESTWORK}"
|
|
do_packagedata[sstate-outputdirs] = "${PKGDATA_DIR}"
|
|
do_packagedata[sstate-lockfile-shared] = "${PACKAGELOCK}"
|
|
do_packagedata[stamp-extra-info] = "${MACHINE}"
|
|
|
|
python do_packagedata_setscene () {
|
|
sstate_setscene(d)
|
|
}
|
|
addtask do_packagedata_setscene
|
|
|
|
#
|
|
# Helper functions for the package writing classes
|
|
#
|
|
|
|
def mapping_rename_hook(d):
|
|
"""
|
|
Rewrite variables to account for package renaming in things
|
|
like debian.bbclass or manual PKG variable name changes
|
|
"""
|
|
pkg = d.getVar("PKG", True)
|
|
runtime_mapping_rename("RDEPENDS", pkg, d)
|
|
runtime_mapping_rename("RRECOMMENDS", pkg, d)
|
|
runtime_mapping_rename("RSUGGESTS", pkg, d)
|
|
|