src/modules/flavor/elf.py
changeset 1544 e0ce5081236a
parent 1516 8c950a3b4171
child 1580 2763f36d0eda
--- a/src/modules/flavor/elf.py	Thu Dec 03 18:22:02 2009 -0600
+++ b/src/modules/flavor/elf.py	Thu Dec 03 17:44:38 2009 -0800
@@ -49,16 +49,19 @@
         """Exception that is used for elf dependencies which have a dynamic
         token in their path that we're unable to decode."""
 
-        def __init__(self, file_path, run_path, token):
+        def __init__(self, proto_path, installed_path, run_path, token):
                 base.DependencyAnalysisError.__init__(self)
-                self.fp = file_path
+                self.pp = proto_path
+                self.ip = installed_path
                 self.rp = run_path
                 self.tok = token
 
         def __str__(self):
-                return  _("%s had this token, %s, in its run path:%s. We are "
-                    "unable to handle this token at this time.") % \
-                    (self.fp, self.tok, self.rp)
+                return  _("%s (which will be installed at %s) had this token, "
+                    "%s, in its run path:%s.  It is not currently possible to "
+                    "automatically expand this token. Please specify its value "
+                    "on the command line.") % \
+                    (self.pp, self.ip, self.tok, self.rp)
 
 
 class ElfDependency(base.MultiplePathDependency):
@@ -103,15 +106,62 @@
                 return "ElfDep(%s, %s, %s, %s)" % (self.action, self.base_name,
                     self.run_paths, self.pkg_vars)
 
-def process_elf_dependencies(action, proto_dir, pkg_vars, **kwargs):
-        """Given a file action and proto directory, produce the elf dependencies
-        for that file."""
+def expand_variables(paths, dyn_tok_conv):
+        """Replace dynamic tokens, such as $PLATFORM, in the paths in the
+        paramter 'paths' with the values for that token provided in the
+        dictionary 'dyn_tok_conv.'
+        """
+
+        res = []
+        elist = []
+        for p in paths:
+                tok_start = p.find("$")
+                if tok_start > -1:
+                        tok = p[tok_start:]
+                        tok_end = tok.find("/")
+                        if tok_end > -1:
+                                tok = tok[:tok_end]
+                        if tok not in dyn_tok_conv:
+                                elist.append((p, tok))
+                        else:
+                                np = [
+                                    p[:tok_start] + dc + \
+                                    p[tok_start + len(tok):]
+                                    for dc in dyn_tok_conv[tok]
+                                ]
+                                # The first dynamic token has been replaced, but
+                                # more may remain so process the path again.
+                                rec_res, rec_elist = expand_variables(np,
+                                    dyn_tok_conv)
+                                res.extend(rec_res)
+                                elist.extend(rec_elist)
+                else:
+                        res.append(p)
+        return res, elist
+        
+def process_elf_dependencies(action, proto_dir, pkg_vars, dyn_tok_conv,
+    kernel_paths, **kwargs):
+        """Produce the elf dependencies for the file delivered in the action
+        provided.
+
+        'action' is the file action to analyze.
+
+        'pkg_vars' is the list of variants against which the package delivering
+        the action was published.
+
+        'proto_dir' is the proto area where the file the action delivers lives.
+
+        'dyn_tok_conv' is the dictionary which maps the dynamic tokens, like
+        $PLATFORM, to the values they should be expanded to.
+
+        'kernel_paths' contains the run paths which kernel modules should use.
+        """
 
         if not action.name == "file":
                 return []
 
         installed_path = action.attrs[action.key_attr]
-        
+
         proto_file = action.attrs[PD_LOCAL_PATH]
 
         if not os.path.exists(proto_file):
@@ -133,11 +183,8 @@
         if len(rp) == 1 and rp[0] == "":
                 rp = []
 
-        rp = [
-            os.path.normpath(p.replace("$ORIGIN",
-                os.path.join("/", os.path.dirname(installed_path))))
-            for p in rp
-        ]
+        dyn_tok_conv["$ORIGIN"] = [os.path.join("/",
+            os.path.dirname(installed_path))]
 
         kernel64 = None
 
@@ -156,7 +203,7 @@
                         rp.append("/platform/%s/kernel" %
                             installed_path.split("/")[1]) 
                 # Default kernel search path
-                rp.extend(("/kernel", "/usr/kernel"))
+                rp.extend(kernel_paths)
                 # What subdirectory should we look in for 64-bit kernel modules?
                 if ei["bits"] == 64:
                         if ei["arch"] == "i386":
@@ -172,20 +219,45 @@
                 if "/usr/lib" not in rp:
                         rp.append("/usr/lib")
 
+        rp, elist = expand_variables(rp, dyn_tok_conv)
+
+        elist = [
+            UnsupportedDynamicToken(proto_file, installed_path, p, tok)
+            for p, tok in elist
+        ]
+
         res = []
-        elist = []
 
-        for p in rp:
-                if "$" in p:
-                        tok = p[p.find("$"):]
-                        if "/" in tok:
-                                tok = tok[:tok.find("/")]
-                        elist.append(UnsupportedDynamicToken(installed_path, p,
-                            tok))
-
-        rp = [p for p in rp[:] if "$" not in p]
-
-        return [
-            ElfDependency(action, d, rp, pkg_vars, proto_dir)
-            for d in deps
-        ], elist
+        for d in deps:
+                pn, fn = os.path.split(d)
+                pathlist = []
+                for p in rp:
+                        if kernel64:
+                                # Find 64-bit modules the way krtld does.
+                                # XXX We don't resolve dependencies found in
+                                # /platform, since we don't know where under
+                                # /platform to look.
+                                deppath = os.path.join(p, pn, kernel64, fn)[1:]
+                        else:
+                                # This is a hack for when a runpath uses the 64
+                                # symlink to the actual 64-bit directory.
+                                # Better would be to see if the runpath was a
+                                # link, and if so, use its resolution, but
+                                # extracting that information from used list is
+                                # a pain, especially because you potentially
+                                # have to resolve symlinks at all levels of the
+                                # path.
+                                if p.endswith("/64"):
+                                        if ei["arch"] == "i386":
+                                                p = p[:-2] + "amd64"
+                                        elif ei["arch"] == "sparc":
+                                                p = p[:-2] + "sparcv9"
+                                deppath = os.path.join(p, d).lstrip(os.path.sep)
+                        # deppath includes filename; remove that.
+                        head, tail = os.path.split(deppath)
+                        if head:
+                                pathlist.append(head)
+                res.append(ElfDependency(action, fn, pathlist, pkg_vars,
+                    proto_dir))
+        del dyn_tok_conv["$ORIGIN"]
+        return res, elist