| inherit package |
| |
| def splitdebuginfo(file, dvar, debugdir, debuglibdir, debugappend, 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 |
| import subprocess |
| |
| src = file[len(dvar):] |
| dest = debuglibdir + os.path.dirname(src) + debugdir + "/" + os.path.basename(src) + debugappend |
| debugfile = dvar + dest |
| # Split the file... |
| bb.utils.mkdirhier(os.path.dirname(debugfile)) |
| #bb.note("Split %s -> %s" % (file, debugfile)) |
| # Only store off the hard link reference if we successfully split! |
| |
| dvar = d.getVar('PKGD') |
| objcopy = d.getVar("OBJCOPY") |
| |
| # 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 os.path.exists(file): |
| 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: |
| append_source_info(file, sourcefile, d) |
| |
| bb.utils.mkdirhier(os.path.dirname(debugfile)) |
| |
| if os.path.basename(src) != "udevd" and os.path.basename(src) != "init": |
| subprocess.check_output([objcopy, file, debugfile], stderr=subprocess.STDOUT) |
| |
| # Set the debuglink to have the view of the file path on the target |
| if not cpath.islink(file): |
| subprocess.check_output([objcopy, '--add-gnu-debuglink', debugfile, file], stderr=subprocess.STDOUT) |
| |
| if newmode: |
| os.chmod(file, origmode) |
| |
| return 0 |
| |
| python split_and_strip_files () { |
| import stat, errno |
| import subprocess |
| |
| dvar = d.getVar('PKGD') |
| pn = d.getVar('PN') |
| targetos = d.getVar('TARGET_OS') |
| |
| oldcwd = os.getcwd() |
| os.chdir(dvar) |
| |
| # We default to '.debug' style |
| if d.getVar('PACKAGE_DEBUG_SPLIT_STYLE') == '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') == '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 = "" |
| elif d.getVar('PACKAGE_DEBUG_SPLIT_STYLE') == 'debug-with-srcpkg': |
| debugappend = "" |
| debugdir = "/.debug" |
| debuglibdir = "" |
| debugsrcdir = "/usr/src/debug" |
| 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) |
| |
| # |
| # First lets figure out all of the files we may have to process ... do this only once! |
| # |
| elffiles = {} |
| symlinks = {} |
| kernmods = [] |
| staticlibs = [] |
| inodes = {} |
| libdir = os.path.abspath(dvar + os.sep + d.getVar("libdir")) |
| baselibdir = os.path.abspath(dvar + os.sep + d.getVar("base_libdir")) |
| skipfiles = (d.getVar("INHIBIT_PACKAGE_STRIP_FILES") or "").split() |
| if (d.getVar('INHIBIT_PACKAGE_STRIP') != '1' or \ |
| d.getVar('INHIBIT_PACKAGE_DEBUG_SPLIT') != '1'): |
| checkelf = {} |
| checkelflinks = {} |
| 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 |
| if oe.package.is_static_lib(file): |
| staticlibs.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 |
| |
| if file in skipfiles: |
| 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 executable |
| 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 or ".node" in f)): |
| |
| if cpath.islink(file): |
| checkelflinks[file] = ltarget |
| continue |
| # Use a reference of device ID and inode number to identify files |
| file_reference = "%d_%d" % (s.st_dev, s.st_ino) |
| checkelf[file] = (file, file_reference) |
| |
| results = oe.utils.multiprocess_launch(oe.package.is_elf, checkelflinks.values(), d) |
| results_map = {} |
| for (ltarget, elf_file) in results: |
| results_map[ltarget] = elf_file |
| for file in checkelflinks: |
| ltarget = checkelflinks[file] |
| # If it's a symlink, and points to an ELF file, we capture the readlink target |
| if results_map[ltarget]: |
| target = os.readlink(file) |
| #bb.note("Sym: %s (%d)" % (ltarget, results_map[ltarget])) |
| symlinks[file] = target |
| |
| results = oe.utils.multiprocess_launch(oe.package.is_elf, checkelf.keys(), d) |
| for (file, elf_file) in results: |
| # It's a file (or hardlink), not a link |
| # ...but is it ELF, and is it already stripped? |
| if elf_file & 1: |
| if elf_file & 2: |
| if 'already-stripped' in (d.getVar('INSANE_SKIP_' + pn) 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 identify files |
| file_reference = checkelf[file][1] |
| 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.break_hardlinks(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') != '1'): |
| oe.utils.multiprocess_launch(splitdebuginfo, list(elffiles), d, extraargs=(dvar, debugdir, debuglibdir, debugappend, debugsrcdir, sourcefile, d)) |
| |
| # Add this line to keep link so(like libc.so.6) in dbg-rpm dir. |
| oe.utils.multiprocess_launch(splitdebuginfo, list(symlinks), d, extraargs=(dvar, debugdir, debuglibdir, debugappend, debugsrcdir, sourcefile, d)) |
| |
| if debugsrcdir and not targetos.startswith("mingw"): |
| for file in staticlibs: |
| append_source_info(file, sourcefile, d, fatal=False) |
| |
| # Hardlink our debug symbols to the other hardlink copies |
| for ref in inodes: |
| if len(inodes[ref]) == 1: |
| continue |
| |
| target = inodes[ref][0][len(dvar):] |
| for file in inodes[ref][1:]: |
| src = file[len(dvar):] |
| dest = debuglibdir + os.path.dirname(src) + debugdir + "/" + os.path.basename(target) + debugappend |
| fpath = dvar + dest |
| ftarget = dvar + debuglibdir + os.path.dirname(target) + debugdir + "/" + os.path.basename(target) + debugappend |
| bb.utils.mkdirhier(os.path.dirname(fpath)) |
| # Only one hardlink of separated debug info file in each directory |
| if not os.access(fpath, os.R_OK): |
| #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)) |
| if not os.path.exists(fpath): |
| 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') != '1'): |
| strip = d.getVar("STRIP") |
| 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_launch(oe.package.runstrip, sfiles, d) |
| |
| # |
| # End of strip |
| # |
| os.chdir(oldcwd) |
| } |
| |
| python package_do_shlibs() { |
| import re, pipes |
| import subprocess |
| |
| exclude_shlibs = d.getVar('EXCLUDE_FROM_SHLIBS', False) |
| if exclude_shlibs: |
| bb.note("not generating shlibs") |
| return |
| |
| lib_re = re.compile("^.*\.so") |
| libdir_re = re.compile(".*/%s$" % d.getVar('baselib')) |
| |
| packages = d.getVar('PACKAGES') |
| |
| shlib_pkgs = [] |
| exclusion_list = d.getVar("EXCLUDE_PACKAGES_FROM_SHLIBS") |
| if exclusion_list: |
| for pkg in packages.split(): |
| if pkg not in exclusion_list.split(): |
| shlib_pkgs.append(pkg) |
| else: |
| bb.note("not generating shlibs for %s" % pkg) |
| else: |
| shlib_pkgs = packages.split() |
| |
| targetos = d.getVar('TARGET_OS') |
| |
| workdir = d.getVar('WORKDIR') |
| |
| ver = d.getVar('PKGV') |
| if not ver: |
| msg = "PKGV not defined" |
| package_qa_handle_error("pkgv-undefined", msg, d) |
| return |
| |
| pkgdest = d.getVar('PKGDEST') |
| |
| shlibswork_dir = d.getVar('SHLIBSWORKDIR') |
| |
| def linux_so(file, pkg, pkgver, d): |
| needs_ldconfig = False |
| needed = set() |
| sonames = set() |
| renames = [] |
| if "-dbg" in pkg: |
| return (needs_ldconfig, needed, sonames, renames) |
| |
| ldir = os.path.dirname(file).replace(pkgdest + "/" + pkg, '') |
| cmd = d.getVar('OBJDUMP') + " -p " + pipes.quote(file) + " 2>/dev/null" |
| fd = os.popen(cmd) |
| lines = fd.readlines() |
| fd.close() |
| rpath = tuple() |
| for l in lines: |
| m = re.match("\s+RPATH\s+([^\s]*)", l) |
| if m: |
| rpaths = m.group(1).replace("$ORIGIN", ldir).split(":") |
| rpath = tuple(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: |
| needed.add((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.add(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, needed, sonames, renames) |
| |
| 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') and not pkg.endswith('-src'): |
| # 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.add(prov) |
| if file.endswith('.dylib') or file.endswith('.so'): |
| rpath = [] |
| p = subprocess.Popen([d.expand("${HOST_PREFIX}otool"), '-l', file], stdout=subprocess.PIPE, stderr=subprocess.PIPE) |
| out, err = p.communicate() |
| # If returned successfully, process stdout for results |
| if p.returncode == 0: |
| for l in out.split("\n"): |
| l = l.strip() |
| if l.startswith('path '): |
| rpath.append(l.split()[1]) |
| |
| p = subprocess.Popen([d.expand("${HOST_PREFIX}otool"), '-L', file], stdout=subprocess.PIPE, stderr=subprocess.PIPE) |
| out, err = p.communicate() |
| # If returned successfully, process stdout for results |
| if p.returncode == 0: |
| for l in out.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].add((name, file, tuple())) |
| |
| def mingw_dll(file, needed, sonames, renames, pkgver): |
| if not os.path.exists(file): |
| return |
| |
| if file.endswith(".dll"): |
| # assume all dlls are shared objects provided by the package |
| sonames.add((os.path.basename(file), os.path.dirname(file).replace(pkgdest + "/" + pkg, ''), pkgver)) |
| |
| if (file.endswith(".dll") or file.endswith(".exe")): |
| # use objdump to search for "DLL Name: .*\.dll" |
| p = subprocess.Popen([d.expand("${HOST_PREFIX}objdump"), "-p", file], stdout=subprocess.PIPE, stderr=subprocess.PIPE) |
| out, err = p.communicate() |
| # process the output, grabbing all .dll names |
| if p.returncode == 0: |
| for m in re.finditer("DLL Name: (.*?\.dll)$", out.decode(), re.MULTILINE | re.IGNORECASE): |
| dllname = m.group(1) |
| if dllname: |
| needed[pkg].add((dllname, file, tuple())) |
| |
| if d.getVar('PACKAGE_SNAP_LIB_SYMLINKS') == "1": |
| snap_symlinks = True |
| else: |
| snap_symlinks = False |
| |
| use_ldconfig = bb.utils.contains('DISTRO_FEATURES', 'ldconfig', True, False, d) |
| |
| needed = {} |
| |
| # Take shared lock since we're only reading, not writing |
| lf = bb.utils.lockfile(d.expand("${PACKAGELOCK}"), True) |
| shlib_provider = oe.package.read_shlib_providers(d) |
| bb.utils.unlockfile(lf) |
| |
| for pkg in shlib_pkgs: |
| private_libs = d.getVar('PRIVATE_LIBS_' + pkg) or d.getVar('PRIVATE_LIBS') or "" |
| private_libs = private_libs.split() |
| needs_ldconfig = False |
| bb.debug(2, "calculating shlib provides for %s" % pkg) |
| |
| pkgver = d.getVar('PKGV_' + pkg) |
| if not pkgver: |
| pkgver = d.getVar('PV_' + pkg) |
| if not pkgver: |
| pkgver = ver |
| |
| needed[pkg] = set() |
| sonames = set() |
| renames = [] |
| linuxlist = [] |
| 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 targetos.startswith("mingw"): |
| mingw_dll(file, needed, sonames, renames, pkgver) |
| elif os.access(file, os.X_OK) or lib_re.match(file): |
| linuxlist.append(file) |
| |
| if linuxlist: |
| results = oe.utils.multiprocess_launch(linux_so, linuxlist, d, extraargs=(pkg, pkgver, d)) |
| for r in results: |
| ldconfig = r[0] |
| needed[pkg] |= r[1] |
| sonames |= r[2] |
| renames.extend(r[3]) |
| 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) |
| if not postinst: |
| postinst = '#!/bin/sh\n' |
| postinst += d.getVar('ldconfig_postinst_fragment') |
| d.setVar('pkg_postinst_%s' % pkg, postinst) |
| bb.debug(1, 'LIBNAMES: pkg %s sonames %s' % (pkg, sonames)) |
| |
| assumed_libs = d.getVar('ASSUME_SHLIBS') |
| if assumed_libs: |
| libdir = d.getVar("libdir") |
| 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'), d.getVar('base_libdir')] |
| |
| for pkg in shlib_pkgs: |
| bb.debug(2, "calculating shlib requirements for %s" % pkg) |
| |
| private_libs = d.getVar('PRIVATE_LIBS_' + pkg) or d.getVar('PRIVATE_LIBS') or "" |
| private_libs = private_libs.split() |
| |
| 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 = [] |
| for k in shlib_provider[n[0]].keys(): |
| shlib_provider_path.append(k) |
| match = None |
| for p in list(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() |
| } |
| PACKAGEBUILDPKGD := " \ |
| perform_packagecopy \ |
| ${PACKAGE_PREPROCESS_FUNCS} \ |
| split_and_strip_files \ |
| fixup_perms \ |
| " |