Revision: 10479 http://gar.svn.sourceforge.net/gar/?rev=10479&view=rev Author: wahwah Date: 2010-07-09 09:50:35 +0000 (Fri, 09 Jul 2010)
Log Message: ----------- mGAR checkpkg: support for $ORIGIN in rpath, one-time message support in the messenger class. Modified Paths: -------------- csw/mgar/gar/v2/lib/python/checkpkg.py csw/mgar/gar/v2/lib/python/checkpkg_test.py csw/mgar/gar/v2/lib/python/dependency_checks.py csw/mgar/gar/v2/lib/python/package_checks.py csw/mgar/gar/v2/lib/python/package_checks_test.py Modified: csw/mgar/gar/v2/lib/python/checkpkg.py =================================================================== --- csw/mgar/gar/v2/lib/python/checkpkg.py 2010-07-09 01:27:36 UTC (rev 10478) +++ csw/mgar/gar/v2/lib/python/checkpkg.py 2010-07-09 09:50:35 UTC (rev 10479) @@ -18,6 +18,7 @@ import socket import sqlite3 import sqlobject +import time from sqlobject import sqlbuilder import subprocess import textwrap @@ -523,8 +524,10 @@ return schema_on_disk def IsDatabaseUpToDate(self): - f_mtime = self.GetFileMtime() - d_mtime = self.GetDatabaseMtime() + f_mtime_epoch = self.GetFileMtime() + d_mtime_epoch = self.GetDatabaseMtime() + f_mtime = time.gmtime(int(f_mtime_epoch)) + d_mtime = time.gmtime(int(d_mtime_epoch)) logging.debug("IsDatabaseUpToDate: f_mtime %s, d_time: %s", f_mtime, d_mtime) # Rounding up to integer seconds. There is a race condition: # pkgadd finishes at 100.1 @@ -532,7 +535,7 @@ # new pkgadd runs and finishes at 100.3 # subsequent checkpkg runs won't pick up the last change. # I don't expect pkgadd to run under 1s. - fresh = int(f_mtime) <= int(d_mtime) + fresh = f_mtime <= d_mtime good_version = self.GetDatabaseSchemaVersion() >= DB_SCHEMA_VERSION logging.debug("IsDatabaseUpToDate: good_version=%s, fresh=%s", repr(good_version), repr(fresh)) @@ -562,21 +565,45 @@ """ def __init__(self): self.runpath_expand_cache = {} + self.runpath_origin_expand_cache = {} self.symlink_expand_cache = {} self.symlink64_cache = {} self.runpath_sanitize_cache = {} - def ExpandRunpath(self, runpath, isalist): + def ExpandRunpath(self, runpath, isalist, binary_path): + """Expands a signle runpath element. + + Args: + runpath: e.g. "/opt/csw/lib/$ISALIST" + isalist: isalist elements + binary_path: Necessary to expand $ORIGIN + """ # TODO: Implement $ORIGIN support # Probably not here as it would make caching unusable. key = (runpath, tuple(isalist)) if key not in self.runpath_expand_cache: - # Emulating $ISALIST expansion + origin_present = False + # Emulating $ISALIST and $ORIGIN expansion + if '$ORIGIN' in runpath: + origin_present = True + if origin_present: + key_o = (runpath, tuple(isalist), binary_path) + if key_o in self.runpath_origin_expand_cache: + return self.runpath_origin_expand_cache[key_o] + else: + if not binary_path.startswith("/"): + binary_path = "/" + binary_path + runpath = runpath.replace('$ORIGIN', binary_path) if '$ISALIST' in runpath: expanded_list = [runpath.replace('$ISALIST', isa) for isa in isalist] else: expanded_list = [runpath] - self.runpath_expand_cache[key] = expanded_list + expanded_list = [os.path.abspath(p) for p in expanded_list] + if not origin_present: + self.runpath_expand_cache[key] = expanded_list + else: + self.runpath_origin_expand_cache[key_o] = expanded_list + return self.runpath_origin_expand_cache[key_o] return self.runpath_expand_cache[key] def ExpandSymlink(self, symlink, target, input_path): @@ -624,7 +651,7 @@ return self.runpath_sanitize_cache[runpath] - def ResolveSoname(self, runpath, soname, isalist, path_list): + def ResolveSoname(self, runpath_list, soname, isalist, path_list, binary_path): """Emulates ldd behavior, minimal implementation. runpath: e.g. ["/opt/csw/lib/$ISALIST", "/usr/lib"] @@ -635,9 +662,6 @@ The function returns the one path. """ - runpath = self.SanitizeRunpath(runpath) - runpath_list = self.ExpandRunpath(runpath, isalist) - runpath_list = self.Emulate64BitSymlinks(runpath_list) # Emulating the install time symlinks, for instance, if the prototype contains # /opt/csw/lib/i386/foo.so.0 and /opt/csw/lib/i386 is a symlink to ".", # the shared library ends up in /opt/csw/lib/foo.so.0 and should be @@ -862,11 +886,16 @@ """Class responsible for passing messages from checks to the user.""" def __init__(self): self.messages = [] + self.one_time_messages = {} self.gar_lines = [] def Message(self, m): self.messages.append(m) + def OneTimeMessage(self, key, m): + if key not in self.one_time_messages: + self.one_time_messages[key] = m + def SuggestGarLine(self, m): self.gar_lines.append(m) @@ -934,7 +963,8 @@ function(pkgs_data, check_interface, logger=logger, messenger=messenger) if check_interface.errors: errors = self.SetErrorsToDict(check_interface.errors, errors) - return errors, messenger.messages, messenger.gar_lines + messages = messenger.messages + messenger.one_time_messages.values() + return errors, messages, messenger.gar_lines def Run(self): self._AutoregisterChecks() Modified: csw/mgar/gar/v2/lib/python/checkpkg_test.py =================================================================== --- csw/mgar/gar/v2/lib/python/checkpkg_test.py 2010-07-09 01:27:36 UTC (rev 10478) +++ csw/mgar/gar/v2/lib/python/checkpkg_test.py 2010-07-09 09:50:35 UTC (rev 10479) @@ -45,15 +45,59 @@ isalist = ["foo", "bar"] runpath = "/opt/csw/lib/$ISALIST" expected = ["/opt/csw/lib/foo", "/opt/csw/lib/bar"] - self.assertEquals(expected, self.e.ExpandRunpath(runpath, isalist)) + bin_path = "opt/csw/lib" + self.assertEquals(expected, self.e.ExpandRunpath(runpath, isalist, bin_path)) def testExpandRunpath_2(self): isalist = ["foo", "bar"] runpath = "/opt/csw/mysql5/lib/$ISALIST/mysql" expected = ["/opt/csw/mysql5/lib/foo/mysql", "/opt/csw/mysql5/lib/bar/mysql"] - self.assertEquals(expected, self.e.ExpandRunpath(runpath, isalist)) + bin_path = "opt/csw/lib" + self.assertEquals(expected, self.e.ExpandRunpath(runpath, isalist, bin_path)) + def testExpandRunpath_OriginSimple(self): + isalist = () + runpath = "$ORIGIN" + expected = ["/opt/csw/lib"] + bin_path = "opt/csw/lib" + self.assertEquals(expected, self.e.ExpandRunpath(runpath, isalist, bin_path)) + + def testExpandRunpath_OriginDots(self): + isalist = () + runpath = "$ORIGIN/.." + expected = ["/opt/csw/lib"] + bin_path = "opt/csw/lib/subdir" + self.assertEquals(expected, self.e.ExpandRunpath(runpath, isalist, bin_path)) + + def testExpandRunpath_Caching(self): + """Make sure that the cache doesn't mess it up. + + Two invocations, where the only difference is the binary path. + """ + isalist = () + runpath = "/opt/csw/lib/foo" + expected = ["/opt/csw/lib/foo"] + bin_path = "opt/csw/lib" + self.assertEquals(expected, self.e.ExpandRunpath(runpath, isalist, bin_path)) + expected = ["/opt/csw/lib/foo"] + bin_path = "/opt/csw/lib/foo" + self.assertEquals(expected, self.e.ExpandRunpath(runpath, isalist, bin_path)) + + def testExpandRunpath_OriginCaching(self): + """Make sure that the cache doesn't mess it up. + + Two invocations, where the only difference is the binary path. + """ + isalist = () + runpath = "$ORIGIN" + expected = ["/opt/csw/lib"] + bin_path = "opt/csw/lib" + self.assertEquals(expected, self.e.ExpandRunpath(runpath, isalist, bin_path)) + expected = ["/opt/csw/foo/lib"] + bin_path = "/opt/csw/foo/lib" + self.assertEquals(expected, self.e.ExpandRunpath(runpath, isalist, bin_path)) + def testEmulate64BitSymlinks_1(self): runpath_list = ["/opt/csw/mysql5/lib/foo/mysql/64"] expected = "/opt/csw/mysql5/lib/foo/mysql/amd64" @@ -130,14 +174,14 @@ 'RPATH set': True, 'RUNPATH RPATH the same': True, 'RUNPATH set': True, - 'needed sonames': ['librt.so.1', + 'needed sonames': ('librt.so.1', 'libresolv.so.2', 'libc.so.1', 'libgen.so.1', 'libsocket.so.1', 'libnsl.so.1', 'libm.so.1', - 'libz.so.1'], + 'libz.so.1'), 'runpath': ('/opt/csw/lib/$ISALIST', '/opt/csw/lib', '/opt/csw/mysql5/lib/$ISALIST', @@ -159,14 +203,14 @@ 'RPATH set': True, 'RUNPATH RPATH the same': False, 'RUNPATH set': False, - 'needed sonames': ['librt.so.1', + 'needed sonames': ('librt.so.1', 'libresolv.so.2', 'libc.so.1', 'libgen.so.1', 'libsocket.so.1', 'libnsl.so.1', 'libm.so.1', - 'libz.so.1'], + 'libz.so.1'), 'runpath': ('/opt/csw/lib/$ISALIST', '/opt/csw/lib', '/opt/csw/mysql5/lib/$ISALIST', Modified: csw/mgar/gar/v2/lib/python/dependency_checks.py =================================================================== --- csw/mgar/gar/v2/lib/python/dependency_checks.py 2010-07-09 01:27:36 UTC (rev 10478) +++ csw/mgar/gar/v2/lib/python/dependency_checks.py 2010-07-09 09:50:35 UTC (rev 10479) @@ -1,6 +1,7 @@ # $Id$ import checkpkg +import os.path import re DEPRECATED_LIBRARY_LOCATIONS = ( @@ -17,7 +18,7 @@ r'^opt/csw/lib/python/site-packages.*', ) -def Libraries(pkg_data, error_mgr, logger, path_and_pkg_by_basename): +def Libraries(pkg_data, error_mgr, logger, messenger, path_and_pkg_by_basename): pkgname = pkg_data["basic_stats"]["pkgname"] logger.debug("Libraries(): pkgname = %s", repr(pkgname)) orphan_sonames = [] @@ -25,6 +26,7 @@ isalist = pkg_data["isalist"] ldd_emulator = checkpkg.LddEmulator() for binary_info in pkg_data["binaries_dump_info"]: + binary_path, binary_basename = os.path.split(binary_info["path"]) for soname in binary_info["needed sonames"]: resolved = False path_list = path_and_pkg_by_basename[soname].keys() @@ -35,11 +37,18 @@ path_list) runpath_tuple = (tuple(binary_info["runpath"]) + tuple(checkpkg.SYS_DEFAULT_RUNPATH)) + runpath_history = [] for runpath in runpath_tuple: - resolved_path = ldd_emulator.ResolveSoname(runpath, + runpath = ldd_emulator.SanitizeRunpath(runpath) + runpath_list = ldd_emulator.ExpandRunpath(runpath, isalist, binary_path) + runpath_list = ldd_emulator.Emulate64BitSymlinks(runpath_list) + # To accumulate all the runpaths that we were looking at + runpath_history += runpath_list + resolved_path = ldd_emulator.ResolveSoname(runpath_list, soname, isalist, - path_list) + path_list, + binary_path) if resolved_path: logger.debug("%s needed by %s:", soname, binary_info["path"]) @@ -61,6 +70,10 @@ break if not resolved: orphan_sonames.append((soname, binary_info["path"])) + messenger.Message( + "%s could not be resolved for %s, with rpath %s, expanded to %s, " + "while the file was available at the following paths: %s" + % (soname, binary_info["path"], runpath_tuple, runpath_history, path_list)) orphan_sonames = set(orphan_sonames) for soname, binary_path in orphan_sonames: error_mgr.ReportError( @@ -69,7 +82,7 @@ # TODO: Report orphan sonames here return required_deps -def ByFilename(pkg_data, error_mgr, logger, path_and_pkg_by_basename): +def ByFilename(pkg_data, error_mgr, logger, messenger, path_and_pkg_by_basename): pkgname = pkg_data["basic_stats"]["pkgname"] req_pkgs_reasons = [] dep_regexes = [(re.compile(x), x, y) Modified: csw/mgar/gar/v2/lib/python/package_checks.py =================================================================== --- csw/mgar/gar/v2/lib/python/package_checks.py 2010-07-09 01:27:36 UTC (rev 10478) +++ csw/mgar/gar/v2/lib/python/package_checks.py 2010-07-09 09:50:35 UTC (rev 10479) @@ -298,14 +298,18 @@ # Resolving sonames for each binary for pkg_data in pkgs_data: pkgname = pkg_data["basic_stats"]["pkgname"] - check_args = (pkg_data, error_mgr, logger, path_and_pkg_by_basename) + check_args = (pkg_data, error_mgr, logger, messenger, + path_and_pkg_by_basename) req_pkgs_reasons = depchecks.Libraries(*check_args) req_pkgs_reasons.extend(depchecks.ByFilename(*check_args)) missing_reasons_by_pkg = {} for pkg, reason in req_pkgs_reasons: if pkg not in missing_reasons_by_pkg: - missing_reasons_by_pkg[pkg] = set() - missing_reasons_by_pkg[pkg].add(reason) + missing_reasons_by_pkg[pkg] = list() + if len(missing_reasons_by_pkg[pkg]) < 4: + missing_reasons_by_pkg[pkg].append(reason) + elif len(missing_reasons_by_pkg[pkg]) == 4: + missing_reasons_by_pkg[pkg].append("...and more.") declared_deps = pkg_data["depends"] declared_deps_set = set([x[0] for x in declared_deps]) req_pkgs_set = set([x[0] for x in req_pkgs_reasons]) @@ -876,7 +880,8 @@ metadata["path"], metadata["machine_id"], cpu_type)) - messenger.Message( + messenger.OneTimeMessage( + "binary-placement", "Files compiled for specific architectures must be placed in " "subdirectories that match the architecture. " "For example, a sparcv8+ binary must not be placed under " Modified: csw/mgar/gar/v2/lib/python/package_checks_test.py =================================================================== --- csw/mgar/gar/v2/lib/python/package_checks_test.py 2010-07-09 01:27:36 UTC (rev 10478) +++ csw/mgar/gar/v2/lib/python/package_checks_test.py 2010-07-09 09:50:35 UTC (rev 10479) @@ -339,7 +339,8 @@ binaries_dump_info[0]["runpath"] = tuple(testdata.rpaths.all_rpaths) self.pkg_data["binaries_dump_info"] = binaries_dump_info[0:1] BAD_PATHS = [ - '$ORIGIN/..', + # Whether this is a valid rpath, is debatable. + # '$ORIGIN/..', '$ORIGIN/../../../usr/lib/v9', '$ORIGIN/../../usr/lib', '$ORIGIN/../lib', This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. _______________________________________________ devel mailing list devel@lists.opencsw.org https://lists.opencsw.org/mailman/listinfo/devel