From a4cda9452449b7ef5c78d838f1315d69f0bdd1c7 Mon Sep 17 00:00:00 2001
From: Todd Gamblin <tgamblin@llnl.gov>
Date: Wed, 25 Dec 2013 17:19:51 -0800
Subject: [PATCH] install() now takes spec AND prefix

---
 lib/spack/spack/directory_layout.py           | 15 +++--
 lib/spack/spack/package.py                    | 30 ++-------
 lib/spack/spack/packages/callpath.py          |  2 +-
 lib/spack/spack/packages/cmake.py             |  2 +-
 lib/spack/spack/packages/dyninst.py           |  2 +-
 lib/spack/spack/packages/libdwarf.py          | 18 ++---
 lib/spack/spack/packages/libelf.py            |  2 +-
 lib/spack/spack/packages/libunwind.py         |  2 +-
 lib/spack/spack/packages/mpich.py             |  2 +-
 lib/spack/spack/packages/mpileaks.py          |  2 +-
 lib/spack/spack/spec.py                       |  6 ++
 .../spack/test/mock_packages/callpath.py      |  2 +-
 lib/spack/spack/test/mock_packages/dyninst.py |  2 +-
 lib/spack/spack/test/mock_packages/fake.py    |  2 +-
 .../spack/test/mock_packages/libdwarf.py      | 18 ++---
 lib/spack/spack/test/mock_packages/libelf.py  |  2 +-
 lib/spack/spack/test/mock_packages/mpich.py   |  2 +-
 lib/spack/spack/test/mock_packages/mpich2.py  |  2 +-
 .../spack/test/mock_packages/mpileaks.py      |  2 +-
 lib/spack/spack/test/mock_packages/zmpi.py    |  2 +-
 lib/spack/spack/util/prefix.py                | 67 +++++++++++++++++++
 21 files changed, 122 insertions(+), 62 deletions(-)
 create mode 100644 lib/spack/spack/util/prefix.py

diff --git a/lib/spack/spack/directory_layout.py b/lib/spack/spack/directory_layout.py
index aed42f4a2c..7f581dfe29 100644
--- a/lib/spack/spack/directory_layout.py
+++ b/lib/spack/spack/directory_layout.py
@@ -9,6 +9,12 @@
 from spack.error import SpackError
 
 
+def _check_concrete(spec):
+    """If the spec is not concrete, raise a ValueError"""
+    if not spec.concrete:
+        raise ValueError('Specs passed to a DirectoryLayout must be concrete!')
+
+
 class DirectoryLayout(object):
     """A directory layout is used to associate unique paths with specs.
        Different installations are going to want differnet layouts for their
@@ -39,7 +45,8 @@ def make_path_for_spec(self, spec):
 
     def path_for_spec(self, spec):
         """Return an absolute path from the root to a directory for the spec."""
-        assert(spec.concrete)
+        _check_concrete(spec)
+
         path = self.relative_path_for_spec(spec)
         assert(not path.startswith(self.root))
         return os.path.join(self.root, path)
@@ -105,7 +112,7 @@ def __init__(self, root, **kwargs):
 
 
     def relative_path_for_spec(self, spec):
-        assert(spec.concrete)
+        _check_concrete(spec)
 
         path = new_path(
             spec.architecture,
@@ -134,7 +141,7 @@ def read_spec(self, path):
 
 
     def make_path_for_spec(self, spec):
-        assert(spec.concrete)
+        _check_concrete(spec)
 
         path = self.path_for_spec(spec)
         spec_file_path = new_path(path, self.spec_file)
@@ -200,5 +207,3 @@ class InstallDirectoryAlreadyExistsError(DirectoryLayoutError):
     def __init__(self, path):
         super(InstallDirectoryAlreadyExistsError, self).__init__(
             "Install path %s already exists!")
-
-
diff --git a/lib/spack/spack/package.py b/lib/spack/spack/package.py
index cb779c4047..acbc7fc927 100644
--- a/lib/spack/spack/package.py
+++ b/lib/spack/spack/package.py
@@ -67,7 +67,7 @@ class Cmake(Package):
            url       = 'http://www.cmake.org/files/v2.8/cmake-2.8.10.2.tar.gz'
            md5       = '097278785da7182ec0aea8769d06860c'
 
-           def install(self, prefix):
+           def install(self, spec, prefix):
                configure('--prefix=%s'   % prefix,
                          '--parallel=%s' % make_jobs)
                make()
@@ -407,27 +407,9 @@ def add_commands_to_module(self):
         m.rmtree     = shutil.rmtree
         m.move       = shutil.move
 
-        # Useful directories within the prefix
+        # Useful directories within the prefix are encapsulated in
+        # a Prefix object.
         m.prefix  = self.prefix
-        m.bin     = new_path(self.prefix, 'bin')
-        m.sbin    = new_path(self.prefix, 'sbin')
-        m.etc     = new_path(self.prefix, 'etc')
-        m.include = new_path(self.prefix, 'include')
-        m.lib     = new_path(self.prefix, 'lib')
-        m.lib64   = new_path(self.prefix, 'lib64')
-        m.libexec = new_path(self.prefix, 'libexec')
-        m.share   = new_path(self.prefix, 'share')
-        m.doc     = new_path(m.share, 'doc')
-        m.info    = new_path(m.share, 'info')
-        m.man     = new_path(m.share, 'man')
-        m.man1    = new_path(m.man, 'man1')
-        m.man2    = new_path(m.man, 'man2')
-        m.man3    = new_path(m.man, 'man3')
-        m.man4    = new_path(m.man, 'man4')
-        m.man5    = new_path(m.man, 'man5')
-        m.man6    = new_path(m.man, 'man6')
-        m.man7    = new_path(m.man, 'man7')
-        m.man8    = new_path(m.man, 'man8')
 
 
     def preorder_traversal(self, visited=None, **kwargs):
@@ -529,7 +511,7 @@ def installed_dependents(self):
     @property
     def prefix(self):
         """Get the prefix into which this package should be installed."""
-        return spack.install_layout.path_for_spec(self.spec)
+        return self.spec.prefix
 
 
     def url_version(self, version):
@@ -620,7 +602,7 @@ def do_install(self):
             # case it needs to add extra files)
             spack.install_layout.make_path_for_spec(self.spec)
 
-            self.install(self.prefix)
+            self.install(self.spec, self.prefix)
             if not os.path.isdir(self.prefix):
                 tty.die("Install failed for %s.  No install dir created." % self.name)
 
@@ -683,7 +665,7 @@ def module(self):
                           fromlist=[self.__class__.__name__])
 
 
-    def install(self, prefix):
+    def install(self, spec, prefix):
         """Package implementations override this with their own build configuration."""
         tty.die("Packages must provide an install method!")
 
diff --git a/lib/spack/spack/packages/callpath.py b/lib/spack/spack/packages/callpath.py
index e5102a5fad..267f4a4e49 100644
--- a/lib/spack/spack/packages/callpath.py
+++ b/lib/spack/spack/packages/callpath.py
@@ -7,7 +7,7 @@ class Callpath(Package):
     depends_on("dyninst")
     depends_on("mpich")
 
-    def install(self, prefix):
+    def install(self, spec, prefix):
         configure("--prefix=%s" % prefix)
         make()
         make("install")
diff --git a/lib/spack/spack/packages/cmake.py b/lib/spack/spack/packages/cmake.py
index d73cdd2c88..3ee294b134 100644
--- a/lib/spack/spack/packages/cmake.py
+++ b/lib/spack/spack/packages/cmake.py
@@ -5,7 +5,7 @@ class Cmake(Package):
     url       = 'http://www.cmake.org/files/v2.8/cmake-2.8.10.2.tar.gz'
     versions  = { '2.8.10.2' : '097278785da7182ec0aea8769d06860c' }
 
-    def install(self, prefix):
+    def install(self, spec, prefix):
         configure('--prefix=%s'   % prefix,
                   '--parallel=%s' % make_jobs)
         make()
diff --git a/lib/spack/spack/packages/dyninst.py b/lib/spack/spack/packages/dyninst.py
index 7648a389ee..187cc2bb5a 100644
--- a/lib/spack/spack/packages/dyninst.py
+++ b/lib/spack/spack/packages/dyninst.py
@@ -11,7 +11,7 @@ class Dyninst(Package):
     depends_on("libelf")
     depends_on("libdwarf")
 
-    def install(self, prefix):
+    def install(self, spec, prefix):
         configure("--prefix=%s" % prefix)
         make()
         make("install")
diff --git a/lib/spack/spack/packages/libdwarf.py b/lib/spack/spack/packages/libdwarf.py
index 928a007193..3102c3d4dd 100644
--- a/lib/spack/spack/packages/libdwarf.py
+++ b/lib/spack/spack/packages/libdwarf.py
@@ -23,21 +23,21 @@ def clean(self):
                     make('clean')
 
 
-    def install(self, prefix):
+    def install(self, spec, prefix):
         # dwarf build does not set arguments for ar properly
         make.add_default_arg('ARFLAGS=rcs')
 
         # Dwarf doesn't provide an install, so we have to do it.
-        mkdirp(bin, include, lib, man1)
+        mkdirp(prefix.bin, prefix.include, prefix.lib, prefix.man1)
 
         with working_dir('libdwarf'):
             configure("--prefix=%s" % prefix, '--enable-shared')
             make()
 
-            install('libdwarf.a',  lib)
-            install('libdwarf.so', lib)
-            install('libdwarf.h',  include)
-            install('dwarf.h',     include)
+            install('libdwarf.a',  prefix.lib)
+            install('libdwarf.so', prefix.lib)
+            install('libdwarf.h',  prefix.include)
+            install('dwarf.h',     prefix.include)
 
         with working_dir('dwarfdump2'):
             configure("--prefix=%s" % prefix)
@@ -46,6 +46,6 @@ def install(self, prefix):
             # cause a race in parallel
             make(parallel=False)
 
-            install('dwarfdump',     bin)
-            install('dwarfdump.conf', lib)
-            install('dwarfdump.1',    man1)
+            install('dwarfdump',      prefix.bin)
+            install('dwarfdump.conf', prefix.lib)
+            install('dwarfdump.1',    prefix.man1)
diff --git a/lib/spack/spack/packages/libelf.py b/lib/spack/spack/packages/libelf.py
index 621444706e..98e8736693 100644
--- a/lib/spack/spack/packages/libelf.py
+++ b/lib/spack/spack/packages/libelf.py
@@ -6,7 +6,7 @@ class Libelf(Package):
 
     versions = { '0.8.13' : '4136d7b4c04df68b686570afa26988ac' }
 
-    def install(self, prefix):
+    def install(self, spec, prefix):
         configure("--prefix=%s" % prefix,
                   "--enable-shared",
                   "--disable-dependency-tracking",
diff --git a/lib/spack/spack/packages/libunwind.py b/lib/spack/spack/packages/libunwind.py
index f53985709e..0366ebefba 100644
--- a/lib/spack/spack/packages/libunwind.py
+++ b/lib/spack/spack/packages/libunwind.py
@@ -6,7 +6,7 @@ class Libunwind(Package):
 
     versions = { '1.1' : 'fb4ea2f6fbbe45bf032cd36e586883ce' }
 
-    def install(self, prefix):
+    def install(self, spec, prefix):
         configure("--prefix=%s" % prefix)
         make()
         make("install")
diff --git a/lib/spack/spack/packages/mpich.py b/lib/spack/spack/packages/mpich.py
index 8cd201f965..a21db55fe8 100644
--- a/lib/spack/spack/packages/mpich.py
+++ b/lib/spack/spack/packages/mpich.py
@@ -13,7 +13,7 @@ class Mpich(Package):
     provides('mpi@:3', when='@3:')
     provides('mpi@:1', when='@1:')
 
-    def install(self, prefix):
+    def install(self, spec, prefix):
         configure("--prefix=%s" % prefix)
         make()
         make("install")
diff --git a/lib/spack/spack/packages/mpileaks.py b/lib/spack/spack/packages/mpileaks.py
index ffeb38ea45..41b7df0587 100644
--- a/lib/spack/spack/packages/mpileaks.py
+++ b/lib/spack/spack/packages/mpileaks.py
@@ -7,7 +7,7 @@ class Mpileaks(Package):
     depends_on("mpich")
     depends_on("callpath")
 
-    def install(self, prefix):
+    def install(self, spec, prefix):
         configure("--prefix=%s" % prefix)
         make()
         make("install")
diff --git a/lib/spack/spack/spec.py b/lib/spack/spack/spec.py
index c27203f05e..3aba17142b 100644
--- a/lib/spack/spack/spec.py
+++ b/lib/spack/spack/spec.py
@@ -82,6 +82,7 @@
 from spack.color import *
 from spack.util.lang import *
 from spack.util.string import *
+from spack.util.prefix import Prefix
 
 
 # Convenient names for color formats so that other things can use them
@@ -439,6 +440,11 @@ def preorder_traversal(self, visited=None, d=0, **kwargs):
                 yield elt
 
 
+    @property
+    def prefix(self):
+        return Prefix(spack.install_layout.path_for_spec(self))
+
+
     def _concretize_helper(self, presets=None, visited=None):
         """Recursive helper function for concretize().
            This concretizes everything bottom-up.  As things are
diff --git a/lib/spack/spack/test/mock_packages/callpath.py b/lib/spack/spack/test/mock_packages/callpath.py
index e9ad344eaa..8d8949942f 100644
--- a/lib/spack/spack/test/mock_packages/callpath.py
+++ b/lib/spack/spack/test/mock_packages/callpath.py
@@ -11,7 +11,7 @@ class Callpath(Package):
     depends_on("dyninst")
     depends_on("mpi")
 
-    def install(self, prefix):
+    def install(self, spec, prefix):
         configure("--prefix=%s" % prefix)
         make()
         make("install")
diff --git a/lib/spack/spack/test/mock_packages/dyninst.py b/lib/spack/spack/test/mock_packages/dyninst.py
index 6940c7788d..c4c779f54c 100644
--- a/lib/spack/spack/test/mock_packages/dyninst.py
+++ b/lib/spack/spack/test/mock_packages/dyninst.py
@@ -12,7 +12,7 @@ class Dyninst(Package):
     depends_on("libelf")
     depends_on("libdwarf")
 
-    def install(self, prefix):
+    def install(self, spec, prefix):
         configure("--prefix=%s" % prefix)
         make()
         make("install")
diff --git a/lib/spack/spack/test/mock_packages/fake.py b/lib/spack/spack/test/mock_packages/fake.py
index 68077f5f82..c1356180e9 100644
--- a/lib/spack/spack/test/mock_packages/fake.py
+++ b/lib/spack/spack/test/mock_packages/fake.py
@@ -5,7 +5,7 @@ class Fake(Package):
     url      = "http://www.fake-spack-example.org/downloads/fake-1.0.tar.gz"
     versions = { '1.0' : 'foobarbaz' }
 
-    def install(self, prefix):
+    def install(self, spec, prefix):
         configure("--prefix=%s" % prefix)
         make()
         make("install")
diff --git a/lib/spack/spack/test/mock_packages/libdwarf.py b/lib/spack/spack/test/mock_packages/libdwarf.py
index ccfff56286..1bb9fbc889 100644
--- a/lib/spack/spack/test/mock_packages/libdwarf.py
+++ b/lib/spack/spack/test/mock_packages/libdwarf.py
@@ -23,21 +23,21 @@ def clean(self):
                     make('clean')
 
 
-    def install(self, prefix):
+    def install(self, spec, prefix):
         # dwarf build does not set arguments for ar properly
         make.add_default_arg('ARFLAGS=rcs')
 
         # Dwarf doesn't provide an install, so we have to do it.
-        mkdirp(bin, include, lib, man1)
+        mkdirp(prefix.bin, prefix.include, prefix.lib, prefix.man1)
 
         with working_dir('libdwarf'):
             configure("--prefix=%s" % prefix, '--enable-shared')
             make()
 
-            install('libdwarf.a',  lib)
-            install('libdwarf.so', lib)
-            install('libdwarf.h',  include)
-            install('dwarf.h',     include)
+            install('libdwarf.a',  prefix.lib)
+            install('libdwarf.so', prefix.lib)
+            install('libdwarf.h',  prefix.include)
+            install('dwarf.h',     prefix.include)
 
         with working_dir('dwarfdump2'):
             configure("--prefix=%s" % prefix)
@@ -46,6 +46,6 @@ def install(self, prefix):
             # cause a race in parallel
             make(parallel=False)
 
-            install('dwarfdump',     bin)
-            install('dwarfdump.conf', lib)
-            install('dwarfdump.1',    man1)
+            install('dwarfdump',      prefix.bin)
+            install('dwarfdump.conf', prefix.lib)
+            install('dwarfdump.1',    prefix.man1)
diff --git a/lib/spack/spack/test/mock_packages/libelf.py b/lib/spack/spack/test/mock_packages/libelf.py
index 75948b26fb..4d6b62b96b 100644
--- a/lib/spack/spack/test/mock_packages/libelf.py
+++ b/lib/spack/spack/test/mock_packages/libelf.py
@@ -8,7 +8,7 @@ class Libelf(Package):
                   '0.8.12' : 'e21f8273d9f5f6d43a59878dc274fec7',
                   '0.8.10' : '9db4d36c283d9790d8fa7df1f4d7b4d9' }
 
-    def install(self, prefix):
+    def install(self, spec, prefix):
         configure("--prefix=%s" % prefix,
                   "--enable-shared",
                   "--disable-dependency-tracking",
diff --git a/lib/spack/spack/test/mock_packages/mpich.py b/lib/spack/spack/test/mock_packages/mpich.py
index c2a479c07a..351d224dc0 100644
--- a/lib/spack/spack/test/mock_packages/mpich.py
+++ b/lib/spack/spack/test/mock_packages/mpich.py
@@ -15,7 +15,7 @@ class Mpich(Package):
     provides('mpi@:3', when='@3:')
     provides('mpi@:1', when='@1:')
 
-    def install(self, prefix):
+    def install(self, spec, prefix):
         configure("--prefix=%s" % prefix)
         make()
         make("install")
diff --git a/lib/spack/spack/test/mock_packages/mpich2.py b/lib/spack/spack/test/mock_packages/mpich2.py
index ecf99925cc..382cac977a 100644
--- a/lib/spack/spack/test/mock_packages/mpich2.py
+++ b/lib/spack/spack/test/mock_packages/mpich2.py
@@ -17,7 +17,7 @@ class Mpich2(Package):
     provides('mpi@:2.1', when='@1.1:')
     provides('mpi@:2.2', when='@1.2:')
 
-    def install(self, prefix):
+    def install(self, spec, prefix):
         configure("--prefix=%s" % prefix)
         make()
         make("install")
diff --git a/lib/spack/spack/test/mock_packages/mpileaks.py b/lib/spack/spack/test/mock_packages/mpileaks.py
index 6f9b143e9d..da71ff65bb 100644
--- a/lib/spack/spack/test/mock_packages/mpileaks.py
+++ b/lib/spack/spack/test/mock_packages/mpileaks.py
@@ -12,7 +12,7 @@ class Mpileaks(Package):
     depends_on("mpi")
     depends_on("callpath")
 
-    def install(self, prefix):
+    def install(self, spec, prefix):
         configure("--prefix=%s" % prefix)
         make()
         make("install")
diff --git a/lib/spack/spack/test/mock_packages/zmpi.py b/lib/spack/spack/test/mock_packages/zmpi.py
index 93b6193eec..ec7e6fcdab 100644
--- a/lib/spack/spack/test/mock_packages/zmpi.py
+++ b/lib/spack/spack/test/mock_packages/zmpi.py
@@ -11,7 +11,7 @@ class Zmpi(Package):
     provides('mpi@:10.0')
     depends_on('fake')
 
-    def install(self, prefix):
+    def install(self, spec, prefix):
         configure("--prefix=%s" % prefix)
         make()
         make("install")
diff --git a/lib/spack/spack/util/prefix.py b/lib/spack/spack/util/prefix.py
new file mode 100644
index 0000000000..f0ab790a17
--- /dev/null
+++ b/lib/spack/spack/util/prefix.py
@@ -0,0 +1,67 @@
+"""
+This file contains utilities to help with installing packages.
+"""
+from spack.util.filesystem import new_path
+
+class Prefix(object):
+    """This class represents an installation prefix, but provides useful
+       attributes for referring to directories inside the prefix.
+
+       For example, you can do something like this::
+
+           prefix = Prefix('/usr')
+           print prefix.lib
+           print prefix.lib64
+           print prefix.bin
+           print prefix.share
+           print prefix.man4
+
+       This program would print:
+
+           /usr/lib
+           /usr/lib64
+           /usr/bin
+           /usr/share
+           /usr/share/man/man4
+
+       In addition, Prefix objects can be added to strings, e.g.:
+
+           print "foobar " + prefix
+
+       This prints 'foobar /usr". All of this is meant to make custom
+       installs easy.
+    """
+
+    def __init__(self, prefix):
+        self.prefix = prefix
+        self.bin     = new_path(self.prefix, 'bin')
+        self.sbin    = new_path(self.prefix, 'sbin')
+        self.etc     = new_path(self.prefix, 'etc')
+        self.include = new_path(self.prefix, 'include')
+        self.lib     = new_path(self.prefix, 'lib')
+        self.lib64   = new_path(self.prefix, 'lib64')
+        self.libexec = new_path(self.prefix, 'libexec')
+        self.share   = new_path(self.prefix, 'share')
+        self.doc     = new_path(self.share, 'doc')
+        self.info    = new_path(self.share, 'info')
+        self.man     = new_path(self.share, 'man')
+        self.man1    = new_path(self.man, 'man1')
+        self.man2    = new_path(self.man, 'man2')
+        self.man3    = new_path(self.man, 'man3')
+        self.man4    = new_path(self.man, 'man4')
+        self.man5    = new_path(self.man, 'man5')
+        self.man6    = new_path(self.man, 'man6')
+        self.man7    = new_path(self.man, 'man7')
+        self.man8    = new_path(self.man, 'man8')
+
+
+    def __str__(self):
+        return self.prefix
+
+
+    def __add__(self, other):
+        return str(self) + other
+
+
+    def __radd__(self, other):
+        return other + str(self)
-- 
GitLab