diff --git a/lib/spack/llnl/util/filesystem.py b/lib/spack/llnl/util/filesystem.py
index 576aeb16bd36749356a2ddcc72d3137aa6810db2..1fdd25f60874ca0c6e0b6696bb44b2170c1fc61a 100644
--- a/lib/spack/llnl/util/filesystem.py
+++ b/lib/spack/llnl/util/filesystem.py
@@ -22,9 +22,10 @@
 # along with this program; if not, write to the Free Software Foundation,
 # Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ##############################################################################
-__all__ = ['set_install_permissions', 'install', 'expand_user', 'working_dir',
-           'touch', 'touchp', 'mkdirp', 'force_remove', 'join_path', 'ancestor',
-           'can_access', 'filter_file', 'change_sed_delimiter', 'is_exe']
+__all__ = ['set_install_permissions', 'install', 'install_tree', 'traverse_tree',
+           'expand_user', 'working_dir', 'touch', 'touchp', 'mkdirp',
+           'force_remove', 'join_path', 'ancestor', 'can_access', 'filter_file',
+           'change_sed_delimiter', 'is_exe', 'force_symlink']
 
 import os
 import sys
@@ -140,12 +141,7 @@ def set_install_permissions(path):
         os.chmod(path, 0644)
 
 
-def install(src, dest):
-    """Manually install a file to a particular location."""
-    tty.info("Installing %s to %s" % (src, dest))
-    shutil.copy(src, dest)
-    set_install_permissions(dest)
-
+def copy_mode(src, dest):
     src_mode = os.stat(src).st_mode
     dest_mode = os.stat(dest).st_mode
     if src_mode | stat.S_IXUSR: dest_mode |= stat.S_IXUSR
@@ -154,6 +150,24 @@ def install(src, dest):
     os.chmod(dest, dest_mode)
 
 
+def install(src, dest):
+    """Manually install a file to a particular location."""
+    tty.info("Installing %s to %s" % (src, dest))
+    shutil.copy(src, dest)
+    set_install_permissions(dest)
+    copy_mode(src, dest)
+
+
+def install_tree(src, dest, **kwargs):
+    """Manually install a file to a particular location."""
+    tty.info("Installing %s to %s" % (src, dest))
+    shutil.copytree(src, dest, **kwargs)
+
+    for s, d in traverse_tree(src, dest, follow_nonexisting=False):
+        set_install_permissions(d)
+        copy_mode(s, d)
+
+
 def is_exe(path):
     """True if path is an executable file."""
     return os.path.isfile(path) and os.access(path, os.X_OK)
@@ -210,6 +224,14 @@ def touchp(path):
     touch(path)
 
 
+def force_symlink(src, dest):
+    try:
+        os.symlink(src, dest)
+    except OSError, e:
+        os.remove(dest)
+        os.symlink(src, dest)
+
+
 def join_path(prefix, *args):
     path = str(prefix)
     for elt in args:
@@ -228,3 +250,84 @@ def ancestor(dir, n=1):
 def can_access(file_name):
     """True if we have read/write access to the file."""
     return os.access(file_name, os.R_OK|os.W_OK)
+
+
+def traverse_tree(source_root, dest_root, rel_path='', **kwargs):
+    """Traverse two filesystem trees simultaneously.
+
+    Walks the LinkTree directory in pre or post order.  Yields each
+    file in the source directory with a matching path from the dest
+    directory, along with whether the file is a directory.
+    e.g., for this tree::
+
+        root/
+          a/
+            file1
+            file2
+          b/
+            file3
+
+    When called on dest, this yields::
+
+        ('root',         'dest')
+        ('root/a',       'dest/a')
+        ('root/a/file1', 'dest/a/file1')
+        ('root/a/file2', 'dest/a/file2')
+        ('root/b',       'dest/b')
+        ('root/b/file3', 'dest/b/file3')
+
+    Optional args:
+
+    order=[pre|post] -- Whether to do pre- or post-order traveral.
+
+    ignore=<predicate> -- Predicate indicating which files to ignore.
+
+    follow_nonexisting -- Whether to descend into directories in
+                          src that do not exit in dest. Default True.
+
+    follow_links -- Whether to descend into symlinks in src.
+
+    """
+    follow_nonexisting = kwargs.get('follow_nonexisting', True)
+    follow_links = kwargs.get('follow_link', False)
+
+    # Yield in pre or post order?
+    order  = kwargs.get('order', 'pre')
+    if order not in ('pre', 'post'):
+        raise ValueError("Order must be 'pre' or 'post'.")
+
+    # List of relative paths to ignore under the src root.
+    ignore = kwargs.get('ignore', lambda filename: False)
+
+    # Don't descend into ignored directories
+    if ignore(rel_path):
+        return
+
+    source_path = os.path.join(source_root, rel_path)
+    dest_path   = os.path.join(dest_root, rel_path)
+
+    # preorder yields directories before children
+    if order == 'pre':
+        yield (source_path, dest_path)
+
+    for f in os.listdir(source_path):
+        source_child = os.path.join(source_path, f)
+        dest_child   = os.path.join(dest_path, f)
+        rel_child    = os.path.join(rel_path, f)
+
+        # Treat as a directory
+        if os.path.isdir(source_child) and (
+            follow_links or not os.path.islink(source_child)):
+
+            # When follow_nonexisting isn't set, don't descend into dirs
+            # in source that do not exist in dest
+            if follow_nonexisting or os.path.exists(dest_child):
+                tuples = traverse_tree(source_root, dest_root, rel_child, **kwargs)
+                for t in tuples: yield t
+
+        # Treat as a file.
+        elif not ignore(os.path.join(rel_path, f)):
+            yield (source_child, dest_child)
+
+    if order == 'post':
+        yield (source_path, dest_path)
diff --git a/lib/spack/llnl/util/link_tree.py b/lib/spack/llnl/util/link_tree.py
index 4d778eca1e378cb537f977e66d881e388b563015..583f077b7948f90221ae9ba8b75c5a693e14c6cf 100644
--- a/lib/spack/llnl/util/link_tree.py
+++ b/lib/spack/llnl/util/link_tree.py
@@ -32,88 +32,6 @@
 empty_file_name = '.spack-empty'
 
 
-def traverse_tree(source_root, dest_root, rel_path='', **kwargs):
-    """Traverse two filesystem trees simultaneously.
-
-    Walks the LinkTree directory in pre or post order.  Yields each
-    file in the source directory with a matching path from the dest
-    directory, along with whether the file is a directory.
-    e.g., for this tree::
-
-        root/
-          a/
-            file1
-            file2
-          b/
-            file3
-
-    When called on dest, this yields::
-
-        ('root',         'dest')
-        ('root/a',       'dest/a')
-        ('root/a/file1', 'dest/a/file1')
-        ('root/a/file2', 'dest/a/file2')
-        ('root/b',       'dest/b')
-        ('root/b/file3', 'dest/b/file3')
-
-    Optional args:
-
-    order=[pre|post] -- Whether to do pre- or post-order traveral.
-
-    ignore=<predicate> -- Predicate indicating which files to ignore.
-
-    follow_nonexisting -- Whether to descend into directories in
-                          src that do not exit in dest. Default True.
-
-    follow_links -- Whether to descend into symlinks in src.
-
-    """
-    follow_nonexisting = kwargs.get('follow_nonexisting', True)
-    follow_links = kwargs.get('follow_link', False)
-
-    # Yield in pre or post order?
-    order  = kwargs.get('order', 'pre')
-    if order not in ('pre', 'post'):
-        raise ValueError("Order must be 'pre' or 'post'.")
-
-    # List of relative paths to ignore under the src root.
-    ignore = kwargs.get('ignore', lambda filename: False)
-
-    # Don't descend into ignored directories
-    if ignore(rel_path):
-        return
-
-    source_path = os.path.join(source_root, rel_path)
-    dest_path   = os.path.join(dest_root, rel_path)
-
-    # preorder yields directories before children
-    if order == 'pre':
-        yield (source_path, dest_path)
-
-    for f in os.listdir(source_path):
-        source_child = os.path.join(source_path, f)
-        dest_child   = os.path.join(dest_path, f)
-        rel_child    = os.path.join(rel_path, f)
-
-        # Treat as a directory
-        if os.path.isdir(source_child) and (
-            follow_links or not os.path.islink(source_child)):
-
-            # When follow_nonexisting isn't set, don't descend into dirs
-            # in source that do not exist in dest
-            if follow_nonexisting or os.path.exists(dest_child):
-                tuples = traverse_tree(source_root, dest_root, rel_child, **kwargs)
-                for t in tuples: yield t
-
-        # Treat as a file.
-        elif not ignore(os.path.join(rel_path, f)):
-            yield (source_child, dest_child)
-
-    if order == 'post':
-        yield (source_path, dest_path)
-
-
-
 class LinkTree(object):
     """Class to create trees of symbolic links from a source directory.
 
diff --git a/lib/spack/spack/build_environment.py b/lib/spack/spack/build_environment.py
index 59b25d96e7ce31d485cfa3ad4e314c69789692f2..d11e2e270c133aaae8e1364a6d8fb78f9c721fda 100644
--- a/lib/spack/spack/build_environment.py
+++ b/lib/spack/spack/build_environment.py
@@ -189,18 +189,19 @@ def set_module_variables_for_package(pkg):
     m.std_cmake_args.append('-DCMAKE_INSTALL_RPATH=%s' % ":".join(get_rpaths(pkg)))
 
     # Emulate some shell commands for convenience
-    m.pwd        = os.getcwd
-    m.cd         = os.chdir
-    m.mkdir      = os.mkdir
-    m.makedirs   = os.makedirs
-    m.remove     = os.remove
-    m.removedirs = os.removedirs
-    m.symlink    = os.symlink
-
-    m.mkdirp     = mkdirp
-    m.install    = install
-    m.rmtree     = shutil.rmtree
-    m.move       = shutil.move
+    m.pwd          = os.getcwd
+    m.cd           = os.chdir
+    m.mkdir        = os.mkdir
+    m.makedirs     = os.makedirs
+    m.remove       = os.remove
+    m.removedirs   = os.removedirs
+    m.symlink      = os.symlink
+
+    m.mkdirp       = mkdirp
+    m.install      = install
+    m.install_tree = install_tree
+    m.rmtree       = shutil.rmtree
+    m.move         = shutil.move
 
     # Useful directories within the prefix are encapsulated in
     # a Prefix object.