From 73c978ddd99973e29f2ba42078b10455c1de5ca8 Mon Sep 17 00:00:00 2001
From: "Adam J. Stewart" <ajstewart426@gmail.com>
Date: Wed, 15 Aug 2018 11:30:09 -0500
Subject: [PATCH] install_tree, copy_tree can install into existing directory
 structures (#8289)

Replace use of `shutil.copytree` with `copy_tree` and `install_tree` functions in `llnl.util.filesystem`.

- `copy_tree` copies without setting permissions.  It should be used to copy files around in the build directory.
- `install_tree` copies files and sets permissions.  It should be used to copy files into the installation directory.
- `install` and `copy` are analogous single-file functions.
- add more extensive tests for these functions
- update packages to use these functions.
---
 lib/spack/llnl/util/filesystem.py             |  97 +++++++-
 lib/spack/spack/test/llnl/util/filesystem.py  | 209 ++++++++++++++++++
 lib/spack/spack/test/util/filesystem.py       |  61 -----
 .../repos/builtin/packages/alglib/package.py  |   3 +-
 .../builtin/packages/arlecore/package.py      |   3 +-
 .../builtin/packages/atom-dft/package.py      |   4 +-
 .../builtin/packages/autodock-vina/package.py |   8 +-
 .../builtin/packages/bcl2fastq2/package.py    |   3 +-
 .../repos/builtin/packages/bioawk/package.py  |   8 +-
 .../builtin/packages/biopieces/package.py     |   3 +-
 .../repos/builtin/packages/casper/package.py  |   3 +-
 .../repos/builtin/packages/charm/package.py   |   2 +-
 .../repos/builtin/packages/chlorop/package.py |   3 +-
 .../repos/builtin/packages/chombo/package.py  |   5 +-
 .../repos/builtin/packages/clapack/package.py |   5 +-
 .../builtin/packages/codar-cheetah/package.py |   3 +-
 .../repos/builtin/packages/comd/package.py    |   4 +-
 .../repos/builtin/packages/cosp2/package.py   |   3 +-
 .../repos/builtin/packages/cp2k/package.py    |   6 +-
 .../builtin/packages/cppcheck/package.py      |  13 +-
 .../repos/builtin/packages/cudnn/package.py   |   3 +-
 .../builtin/packages/farmhash/package.py      |   5 +-
 .../repos/builtin/packages/fastqc/package.py  |  17 +-
 .../packages/fermisciencetools/package.py     |   3 +-
 .../repos/builtin/packages/fsl/package.py     |   3 +-
 .../repos/builtin/packages/gatk/package.py    |   3 +-
 .../builtin/packages/gaussian/package.py      |   3 +-
 .../repos/builtin/packages/gcc/package.py     |   3 +-
 .../packages/genomefinisher/package.py        |   7 +-
 .../repos/builtin/packages/git/package.py     |   3 +-
 .../repos/builtin/packages/glib/package.py    |   3 +-
 .../builtin/packages/go-bootstrap/package.py  |  13 +-
 .../repos/builtin/packages/go/package.py      |  15 +-
 .../repos/builtin/packages/grackle/package.py |   3 +-
 .../repos/builtin/packages/gradle/package.py  |   3 +-
 .../repos/builtin/packages/gurobi/package.py  |   4 +-
 .../builtin/packages/hdf5-blosc/package.py    |   4 +-
 .../builtin/packages/igvtools/package.py      |   7 +-
 .../repos/builtin/packages/jdk/package.py     |   3 +-
 .../repos/builtin/packages/jmol/package.py    |   3 +-
 .../repos/builtin/packages/kaldi/package.py   |   3 +-
 .../repos/builtin/packages/leveldb/package.py |  21 +-
 .../builtin/packages/libgridxc/package.py     |   4 +-
 .../builtin/packages/libiconv/package.py      |   5 +-
 .../repos/builtin/packages/llvm/package.py    |   3 +-
 .../repos/builtin/packages/masurca/package.py |   3 +-
 .../repos/builtin/packages/maven/package.py   |   3 +-
 .../repos/builtin/packages/mefit/package.py   |   3 +-
 .../repos/builtin/packages/mfem/package.py    |   2 +-
 .../packages/mpix-launch-swift/package.py     |   6 +-
 .../repos/builtin/packages/mrtrix3/package.py |   5 +-
 .../repos/builtin/packages/namd/package.py    |   5 +-
 .../repos/builtin/packages/ncl/package.py     |   5 +-
 .../repos/builtin/packages/ncurses/package.py |   3 +-
 .../repos/builtin/packages/occa/package.py    |   9 +-
 .../repos/builtin/packages/pexsi/package.py   |   3 +-
 .../repos/builtin/packages/picard/package.py  |   7 +-
 .../repos/builtin/packages/pilon/package.py   |   7 +-
 .../repos/builtin/packages/pindel/package.py  |  10 +-
 .../repos/builtin/packages/python/package.py  |   3 +-
 .../repos/builtin/packages/qbox/package.py    |   8 +-
 .../repos/builtin/packages/qorts/package.py   |   3 +-
 var/spack/repos/builtin/packages/r/package.py |   3 +-
 .../builtin/packages/repeatmasker/package.py  |   3 +-
 .../builtin/packages/rna-seqc/package.py      |   5 +-
 .../builtin/packages/rockstar/package.py      |   4 +-
 .../repos/builtin/packages/savanna/package.py |   3 +-
 .../repos/builtin/packages/sbt/package.py     |   5 +-
 .../repos/builtin/packages/scr/package.py     |   3 +-
 .../repos/builtin/packages/sctk/package.py    |   4 +-
 .../repos/builtin/packages/snpeff/package.py  |   7 +-
 .../repos/builtin/packages/snphylo/package.py |   3 +-
 .../repos/builtin/packages/spark/package.py   |   3 +-
 .../builtin/packages/sublime-text/package.py  |   3 +-
 .../builtin/packages/supernova/package.py     |   3 +-
 .../packages/the-platinum-searcher/package.py |   5 +-
 .../repos/builtin/packages/tinyxml/package.py |   5 +-
 .../builtin/packages/trimmomatic/package.py   |  10 +-
 .../repos/builtin/packages/trinity/package.py |   3 +-
 .../builtin/packages/wannier90/package.py     |   3 +-
 .../builtin/packages/workrave/package.py      |   3 +-
 .../repos/builtin/packages/yorick/package.py  |  13 +-
 82 files changed, 422 insertions(+), 344 deletions(-)
 create mode 100644 lib/spack/spack/test/llnl/util/filesystem.py
 delete mode 100644 lib/spack/spack/test/util/filesystem.py

diff --git a/lib/spack/llnl/util/filesystem.py b/lib/spack/llnl/util/filesystem.py
index ace6cfded6..40eb6a7aa8 100644
--- a/lib/spack/llnl/util/filesystem.py
+++ b/lib/spack/llnl/util/filesystem.py
@@ -60,7 +60,9 @@
     'fix_darwin_install_name',
     'force_remove',
     'force_symlink',
+    'copy',
     'install',
+    'copy_tree',
     'install_tree',
     'is_exe',
     'join_path',
@@ -264,27 +266,98 @@ def unset_executable_mode(path):
     os.chmod(path, mode)
 
 
-def install(src, dest):
-    """Manually install a file to a particular location."""
-    tty.debug("Installing %s to %s" % (src, dest))
+def copy(src, dest, _permissions=False):
+    """Copies the file *src* to the file or directory *dest*.
+
+    If *dest* specifies a directory, the file will be copied into *dest*
+    using the base filename from *src*.
+
+    Parameters:
+        src (str): the file to copy
+        dest (str): the destination file or directory
+        _permissions (bool): for internal use only
+    """
+    if _permissions:
+        tty.debug('Installing {0} to {1}'.format(src, dest))
+    else:
+        tty.debug('Copying {0} to {1}'.format(src, dest))
 
     # Expand dest to its eventual full path if it is a directory.
     if os.path.isdir(dest):
         dest = join_path(dest, os.path.basename(src))
 
     shutil.copy(src, dest)
-    set_install_permissions(dest)
-    copy_mode(src, dest)
+
+    if _permissions:
+        set_install_permissions(dest)
+        copy_mode(src, dest)
+
+
+def install(src, dest):
+    """Installs the file *src* to the file or directory *dest*.
+
+    Same as :py:func:`copy` with the addition of setting proper
+    permissions on the installed file.
+
+    Parameters:
+        src (str): the file to install
+        dest (str): the destination file or directory
+    """
+    copy(src, dest, _permissions=True)
 
 
-def install_tree(src, dest, **kwargs):
-    """Manually install a directory tree to a particular location."""
-    tty.debug("Installing %s to %s" % (src, dest))
-    shutil.copytree(src, dest, **kwargs)
+def copy_tree(src, dest, symlinks=True, _permissions=False):
+    """Recursively copy an entire directory tree rooted at *src*.
 
-    for s, d in traverse_tree(src, dest, follow_nonexisting=False):
-        set_install_permissions(d)
-        copy_mode(s, d)
+    If the destination directory *dest* does not already exist, it will
+    be created as well as missing parent directories.
+
+    If *symlinks* is true, symbolic links in the source tree are represented
+    as symbolic links in the new tree and the metadata of the original links
+    will be copied as far as the platform allows; if false, the contents and
+    metadata of the linked files are copied to the new tree.
+
+    Parameters:
+        src (str): the directory to copy
+        dest (str): the destination directory
+        symlinks (bool): whether or not to preserve symlinks
+        _permissions (bool): for internal use only
+    """
+    if _permissions:
+        tty.debug('Installing {0} to {1}'.format(src, dest))
+    else:
+        tty.debug('Copying {0} to {1}'.format(src, dest))
+
+    mkdirp(dest)
+
+    for s, d in traverse_tree(src, dest, order='pre', follow_nonexisting=True):
+        if symlinks and os.path.islink(s):
+            # Note that this won't rewrite absolute links into the old
+            # root to point at the new root. Should we handle that case?
+            target = os.readlink(s)
+            os.symlink(os.path.abspath(target), d)
+        elif os.path.isdir(s):
+            mkdirp(d)
+        else:
+            shutil.copyfile(s, d)
+
+        if _permissions:
+            set_install_permissions(d)
+            copy_mode(s, d)
+
+
+def install_tree(src, dest, symlinks=True):
+    """Recursively install an entire directory tree rooted at *src*.
+
+    Same as :py:func:`copy_tree` with the addition of setting proper
+    permissions on the installed files and directories.
+
+    Parameters:
+        src (str): the directory to install
+        dest (str): the destination directory
+        symlinks (bool): whether or not to preserve symlinks
+    """
+    copy_tree(src, dest, symlinks, _permissions=True)
 
 
 def is_exe(path):
diff --git a/lib/spack/spack/test/llnl/util/filesystem.py b/lib/spack/spack/test/llnl/util/filesystem.py
new file mode 100644
index 0000000000..7701185dbe
--- /dev/null
+++ b/lib/spack/spack/test/llnl/util/filesystem.py
@@ -0,0 +1,209 @@
+##############################################################################
+# Copyright (c) 2013-2018, Lawrence Livermore National Security, LLC.
+# Produced at the Lawrence Livermore National Laboratory.
+#
+# This file is part of Spack.
+# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved.
+# LLNL-CODE-647188
+#
+# For details, see https://github.com/spack/spack
+# Please also see the NOTICE and LICENSE files for our notice and the LGPL.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License (as
+# published by the Free Software Foundation) version 2.1, February 1999.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and
+# conditions of the GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+##############################################################################
+"""Tests for ``llnl/util/filesystem.py``"""
+
+import llnl.util.filesystem as fs
+import os
+import pytest
+
+
+@pytest.fixture()
+def stage(tmpdir_factory):
+    """Creates a stage with the directory structure for the tests."""
+
+    s = tmpdir_factory.mktemp('filesystem_test')
+
+    with s.as_cwd():
+        # Create source file hierarchy
+        fs.touchp('source/1')
+        fs.touchp('source/a/b/2')
+        fs.touchp('source/a/b/3')
+        fs.touchp('source/c/4')
+        fs.touchp('source/c/d/5')
+        fs.touchp('source/c/d/6')
+        fs.touchp('source/c/d/e/7')
+
+        # Create symlink
+        os.symlink(os.path.abspath('source/1'), 'source/2')
+
+        # Create destination directory
+        fs.mkdirp('dest')
+
+    yield s
+
+
+class TestCopy:
+    """Tests for ``filesystem.copy``"""
+
+    def test_file_dest(self, stage):
+        """Test using a filename as the destination."""
+
+        with fs.working_dir(str(stage)):
+            fs.copy('source/1', 'dest/1')
+
+            assert os.path.exists('dest/1')
+            assert os.stat('source/1').st_mode == os.stat('dest/1').st_mode
+
+    def test_dir_dest(self, stage):
+        """Test using a directory as the destination."""
+
+        with fs.working_dir(str(stage)):
+            fs.copy('source/1', 'dest')
+
+            assert os.path.exists('dest/1')
+            assert os.stat('source/1').st_mode == os.stat('dest/1').st_mode
+
+
+class TestInstall:
+    """Tests for ``filesystem.install``"""
+
+    def test_file_dest(self, stage):
+        """Test using a filename as the destination."""
+
+        with fs.working_dir(str(stage)):
+            fs.install('source/1', 'dest/1')
+
+            assert os.path.exists('dest/1')
+            assert os.stat('source/1').st_mode == os.stat('dest/1').st_mode
+
+    def test_dir_dest(self, stage):
+        """Test using a directory as the destination."""
+
+        with fs.working_dir(str(stage)):
+            fs.install('source/1', 'dest')
+
+            assert os.path.exists('dest/1')
+            assert os.stat('source/1').st_mode == os.stat('dest/1').st_mode
+
+
+class TestCopyTree:
+    """Tests for ``filesystem.copy_tree``"""
+
+    def test_existing_dir(self, stage):
+        """Test copying to an existing directory."""
+
+        with fs.working_dir(str(stage)):
+            fs.copy_tree('source', 'dest')
+
+            assert os.path.exists('dest/a/b/2')
+
+    def test_non_existing_dir(self, stage):
+        """Test copying to a non-existing directory."""
+
+        with fs.working_dir(str(stage)):
+            fs.copy_tree('source', 'dest/sub/directory')
+
+            assert os.path.exists('dest/sub/directory/a/b/2')
+
+    def test_symlinks_true(self, stage):
+        """Test copying with symlink preservation."""
+
+        with fs.working_dir(str(stage)):
+            fs.copy_tree('source', 'dest', symlinks=True)
+
+            assert os.path.exists('dest/2')
+            assert os.path.islink('dest/2')
+
+    def test_symlinks_false(self, stage):
+        """Test copying without symlink preservation."""
+
+        with fs.working_dir(str(stage)):
+            fs.copy_tree('source', 'dest', symlinks=False)
+
+            assert os.path.exists('dest/2')
+            assert not os.path.islink('dest/2')
+
+
+class TestInstallTree:
+    """Tests for ``filesystem.install_tree``"""
+
+    def test_existing_dir(self, stage):
+        """Test installing to an existing directory."""
+
+        with fs.working_dir(str(stage)):
+            fs.install_tree('source', 'dest')
+
+            assert os.path.exists('dest/a/b/2')
+
+    def test_non_existing_dir(self, stage):
+        """Test installing to a non-existing directory."""
+
+        with fs.working_dir(str(stage)):
+            fs.install_tree('source', 'dest/sub/directory')
+
+            assert os.path.exists('dest/sub/directory/a/b/2')
+
+    def test_symlinks_true(self, stage):
+        """Test installing with symlink preservation."""
+
+        with fs.working_dir(str(stage)):
+            fs.install_tree('source', 'dest', symlinks=True)
+
+            assert os.path.exists('dest/2')
+            assert os.path.islink('dest/2')
+
+    def test_symlinks_false(self, stage):
+        """Test installing without symlink preservation."""
+
+        with fs.working_dir(str(stage)):
+            fs.install_tree('source', 'dest', symlinks=False)
+
+            assert os.path.exists('dest/2')
+            assert not os.path.islink('dest/2')
+
+
+def test_move_transaction_commit(tmpdir):
+
+    fake_library = tmpdir.mkdir('lib').join('libfoo.so')
+    fake_library.write('Just some fake content.')
+
+    old_md5 = fs.hash_directory(str(tmpdir))
+
+    with fs.replace_directory_transaction(str(tmpdir.join('lib'))):
+        fake_library = tmpdir.mkdir('lib').join('libfoo.so')
+        fake_library.write('Other content.')
+        new_md5 = fs.hash_directory(str(tmpdir))
+
+    assert old_md5 != fs.hash_directory(str(tmpdir))
+    assert new_md5 == fs.hash_directory(str(tmpdir))
+
+
+def test_move_transaction_rollback(tmpdir):
+
+    fake_library = tmpdir.mkdir('lib').join('libfoo.so')
+    fake_library.write('Just some fake content.')
+
+    h = fs.hash_directory(str(tmpdir))
+
+    try:
+        with fs.replace_directory_transaction(str(tmpdir.join('lib'))):
+            assert h != fs.hash_directory(str(tmpdir))
+            fake_library = tmpdir.mkdir('lib').join('libfoo.so')
+            fake_library.write('Other content.')
+            raise RuntimeError('')
+    except RuntimeError:
+        pass
+
+    assert h == fs.hash_directory(str(tmpdir))
diff --git a/lib/spack/spack/test/util/filesystem.py b/lib/spack/spack/test/util/filesystem.py
deleted file mode 100644
index 86c3332bb5..0000000000
--- a/lib/spack/spack/test/util/filesystem.py
+++ /dev/null
@@ -1,61 +0,0 @@
-##############################################################################
-# Copyright (c) 2013-2018, Lawrence Livermore National Security, LLC.
-# Produced at the Lawrence Livermore National Laboratory.
-#
-# This file is part of Spack.
-# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved.
-# LLNL-CODE-647188
-#
-# For details, see https://github.com/spack/spack
-# Please also see the NOTICE and LICENSE files for our notice and the LGPL.
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License (as
-# published by the Free Software Foundation) version 2.1, February 1999.
-#
-# This program is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and
-# conditions of the GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-##############################################################################
-
-import llnl.util.filesystem as fs
-
-
-def test_move_transaction_commit(tmpdir):
-
-    fake_library = tmpdir.mkdir('lib').join('libfoo.so')
-    fake_library.write('Just some fake content.')
-
-    old_md5 = fs.hash_directory(str(tmpdir))
-
-    with fs.replace_directory_transaction(str(tmpdir.join('lib'))):
-        fake_library = tmpdir.mkdir('lib').join('libfoo.so')
-        fake_library.write('Other content.')
-        new_md5 = fs.hash_directory(str(tmpdir))
-
-    assert old_md5 != fs.hash_directory(str(tmpdir))
-    assert new_md5 == fs.hash_directory(str(tmpdir))
-
-
-def test_move_transaction_rollback(tmpdir):
-
-    fake_library = tmpdir.mkdir('lib').join('libfoo.so')
-    fake_library.write('Just some fake content.')
-
-    h = fs.hash_directory(str(tmpdir))
-
-    try:
-        with fs.replace_directory_transaction(str(tmpdir.join('lib'))):
-            assert h != fs.hash_directory(str(tmpdir))
-            fake_library = tmpdir.mkdir('lib').join('libfoo.so')
-            fake_library.write('Other content.')
-            raise RuntimeError('')
-    except RuntimeError:
-        pass
-
-    assert h == fs.hash_directory(str(tmpdir))
diff --git a/var/spack/repos/builtin/packages/alglib/package.py b/var/spack/repos/builtin/packages/alglib/package.py
index f5fa8f32fd..ff280fc747 100644
--- a/var/spack/repos/builtin/packages/alglib/package.py
+++ b/var/spack/repos/builtin/packages/alglib/package.py
@@ -26,7 +26,6 @@
 import glob
 import os
 import sys
-import shutil
 
 
 class Alglib(MakefilePackage):
@@ -48,7 +47,7 @@ def edit(self, spec, prefix):
         make_file_src = join_path(os.path.dirname(self.module.__file__),
                                   'Makefile')
         make_file = join_path(self.stage.source_path, 'src', 'Makefile')
-        shutil.copy(make_file_src, make_file)
+        copy(make_file_src, make_file)
         filter_file(r'so', dso_suffix, make_file)
 
     def install(self, spec, prefix):
diff --git a/var/spack/repos/builtin/packages/arlecore/package.py b/var/spack/repos/builtin/packages/arlecore/package.py
index 97d152d28c..31daa17f85 100644
--- a/var/spack/repos/builtin/packages/arlecore/package.py
+++ b/var/spack/repos/builtin/packages/arlecore/package.py
@@ -23,7 +23,6 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ##############################################################################
 from spack import *
-import distutils.dir_util
 
 
 class Arlecore(Package):
@@ -37,4 +36,4 @@ class Arlecore(Package):
     depends_on('r', type=('build', 'run'))
 
     def install(self, spec, prefix):
-        distutils.dir_util.copy_tree(".", prefix)
+        install_tree('.', prefix)
diff --git a/var/spack/repos/builtin/packages/atom-dft/package.py b/var/spack/repos/builtin/packages/atom-dft/package.py
index d16d3283dc..644b064fb7 100644
--- a/var/spack/repos/builtin/packages/atom-dft/package.py
+++ b/var/spack/repos/builtin/packages/atom-dft/package.py
@@ -22,9 +22,7 @@
 # License along with this program; if not, write to the Free Software
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ##############################################################################
-
 from spack import *
-import shutil
 
 
 class AtomDft(MakefilePackage):
@@ -40,7 +38,7 @@ class AtomDft(MakefilePackage):
     depends_on('xmlf90')
 
     def edit(self, spec, prefix):
-        shutil.copyfile('arch.make.sample', 'arch.make')
+        copy('arch.make.sample', 'arch.make')
 
     @property
     def build_targets(self):
diff --git a/var/spack/repos/builtin/packages/autodock-vina/package.py b/var/spack/repos/builtin/packages/autodock-vina/package.py
index c34376bac1..4907854a73 100644
--- a/var/spack/repos/builtin/packages/autodock-vina/package.py
+++ b/var/spack/repos/builtin/packages/autodock-vina/package.py
@@ -23,8 +23,6 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ##############################################################################
 from spack import *
-from shutil import copyfile
-from shutil import copymode
 import sys
 
 
@@ -66,7 +64,5 @@ def build(self, spec, prefix):
     def install(self, spec, prefix):
         with working_dir(self.build_directory):
             mkdirp(prefix.bin)
-            copyfile("vina", join_path(prefix.bin, "vina"))
-            copymode("vina", join_path(prefix.bin, "vina"))
-            copyfile("vina_split", join_path(prefix.bin, "vina_split"))
-            copymode("vina_split", join_path(prefix.bin, "vina_split"))
+            install('vina', prefix.bin)
+            install('vina_split', prefix.bin)
diff --git a/var/spack/repos/builtin/packages/bcl2fastq2/package.py b/var/spack/repos/builtin/packages/bcl2fastq2/package.py
index b6e476b55c..eb86367fb0 100644
--- a/var/spack/repos/builtin/packages/bcl2fastq2/package.py
+++ b/var/spack/repos/builtin/packages/bcl2fastq2/package.py
@@ -24,7 +24,6 @@
 ##############################################################################
 from spack import *
 import os
-import shutil
 import glob
 import llnl.util.tty as tty
 
@@ -94,7 +93,7 @@ def wrap():
                     tty.msg("cwd sez: {0}".format(os.getcwd()))
                     tarball = glob.glob(join_path('spack-expanded-archive',
                                         'bcl2fastq2*.tar.gz'))[0]
-                    shutil.move(tarball, '.')
+                    copy(tarball, '.')
                     os.rmdir('spack-expanded-archive')
                     tar = which('tar')
                     tarball = os.path.basename(tarball)
diff --git a/var/spack/repos/builtin/packages/bioawk/package.py b/var/spack/repos/builtin/packages/bioawk/package.py
index ae43dff5f5..8ea38bf293 100644
--- a/var/spack/repos/builtin/packages/bioawk/package.py
+++ b/var/spack/repos/builtin/packages/bioawk/package.py
@@ -23,8 +23,6 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ##############################################################################
 from spack import *
-from shutil import copyfile
-from shutil import copymode
 
 
 class Bioawk(MakefilePackage):
@@ -45,7 +43,5 @@ class Bioawk(MakefilePackage):
 
     def install(self, spec, prefix):
         mkdirp(prefix.bin)
-        copyfile("bioawk", join_path(prefix.bin, "bioawk"))
-        copymode("bioawk", join_path(prefix.bin, "bioawk"))
-        copyfile("maketab", join_path(prefix.bin, "maketab"))
-        copymode("maketab", join_path(prefix.bin, "maketab"))
+        install('bioawk',  prefix.bin)
+        install('maketab', prefix.bin)
diff --git a/var/spack/repos/builtin/packages/biopieces/package.py b/var/spack/repos/builtin/packages/biopieces/package.py
index 30435af0e3..c72eba1b68 100644
--- a/var/spack/repos/builtin/packages/biopieces/package.py
+++ b/var/spack/repos/builtin/packages/biopieces/package.py
@@ -23,7 +23,6 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ##############################################################################
 from spack import *
-import distutils.dir_util
 
 
 class Biopieces(Package):
@@ -79,7 +78,7 @@ class Biopieces(Package):
     depends_on('scan-for-matches')
 
     def install(self, spec, prefix):
-        distutils.dir_util.copy_tree(".", prefix)
+        install_tree('.', prefix)
 
     def setup_environment(self, spack_env, run_env):
         # Note: user will need to set environment variables on their own,
diff --git a/var/spack/repos/builtin/packages/casper/package.py b/var/spack/repos/builtin/packages/casper/package.py
index 2a0f04b64d..1469ec68df 100644
--- a/var/spack/repos/builtin/packages/casper/package.py
+++ b/var/spack/repos/builtin/packages/casper/package.py
@@ -23,7 +23,6 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ##############################################################################
 from spack import *
-import distutils.dir_util
 
 
 class Casper(MakefilePackage):
@@ -43,7 +42,7 @@ class Casper(MakefilePackage):
     conflicts('%gcc@7.1.0')
 
     def install(self, spec, prefix):
-        distutils.dir_util.copy_tree(".", prefix)
+        install_tree('.', prefix)
 
     def setup_environment(self, spack_env, run_env):
         run_env.prepend_path('PATH', self.spec.prefix)
diff --git a/var/spack/repos/builtin/packages/charm/package.py b/var/spack/repos/builtin/packages/charm/package.py
index 4135fc8ca7..b4fc48ae9f 100644
--- a/var/spack/repos/builtin/packages/charm/package.py
+++ b/var/spack/repos/builtin/packages/charm/package.py
@@ -249,7 +249,7 @@ def install(self, spec, prefix):
                     tmppath = filepath + ".tmp"
                     # Skip dangling symbolic links
                     try:
-                        shutil.copy2(filepath, tmppath)
+                        copy(filepath, tmppath)
                         os.remove(filepath)
                         os.rename(tmppath, filepath)
                     except (IOError, OSError):
diff --git a/var/spack/repos/builtin/packages/chlorop/package.py b/var/spack/repos/builtin/packages/chlorop/package.py
index 96fdf70be4..96b348c840 100644
--- a/var/spack/repos/builtin/packages/chlorop/package.py
+++ b/var/spack/repos/builtin/packages/chlorop/package.py
@@ -23,7 +23,6 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ##############################################################################
 from spack import *
-import distutils.dir_util
 import os
 
 
@@ -47,7 +46,7 @@ class Chlorop(Package):
 
     def install(self, spec, prefix):
         os.rename('chlorop', 'bin/chlorop')
-        distutils.dir_util.copy_tree(".", prefix)
+        install_tree('.', prefix)
 
     def setup_environment(self, spack_env, run_env):
         run_env.set('CHLOROP', self.prefix)
diff --git a/var/spack/repos/builtin/packages/chombo/package.py b/var/spack/repos/builtin/packages/chombo/package.py
index 74717b88d4..7136680c4b 100644
--- a/var/spack/repos/builtin/packages/chombo/package.py
+++ b/var/spack/repos/builtin/packages/chombo/package.py
@@ -23,7 +23,6 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ##############################################################################
 from spack import *
-from shutil import copyfile
 import glob
 
 
@@ -71,8 +70,8 @@ def edit(self, spec, prefix):
         # Set remaining variables in Make.defs.local
         # Make.defs.local.template.patch ensures lines for USE_TIMER,
         # USE_LAPACK and lapackincflags are present
-        copyfile('./lib/mk/Make.defs.local.template',
-                 './lib/mk/Make.defs.local')
+        copy('./lib/mk/Make.defs.local.template',
+             './lib/mk/Make.defs.local')
 
         defs_file = FileFilter('./lib/mk/Make.defs.local')
 
diff --git a/var/spack/repos/builtin/packages/clapack/package.py b/var/spack/repos/builtin/packages/clapack/package.py
index 73c4414d31..dd0076db43 100644
--- a/var/spack/repos/builtin/packages/clapack/package.py
+++ b/var/spack/repos/builtin/packages/clapack/package.py
@@ -23,7 +23,6 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ##############################################################################
 from spack import *
-from distutils.dir_util import copy_tree
 
 
 class Clapack(MakefilePackage):
@@ -46,7 +45,7 @@ class Clapack(MakefilePackage):
     depends_on('atlas', when='+external-blas')
 
     def edit(self, spec, prefix):
-        install('make.inc.example', 'make.inc')
+        copy('make.inc.example', 'make.inc')
         if '+external-blas' in spec:
             make_inc = FileFilter('make.inc')
             make_inc.filter(r'^BLASLIB.*',
@@ -60,4 +59,4 @@ def build(self, spec, prefix):
         make('lib')
 
     def install(self, spec, prefix):
-        copy_tree('.', prefix)
+        install_tree('.', prefix)
diff --git a/var/spack/repos/builtin/packages/codar-cheetah/package.py b/var/spack/repos/builtin/packages/codar-cheetah/package.py
index c2754f5f8a..de9a12eb6e 100644
--- a/var/spack/repos/builtin/packages/codar-cheetah/package.py
+++ b/var/spack/repos/builtin/packages/codar-cheetah/package.py
@@ -23,7 +23,6 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ##############################################################################
 from spack import *
-from distutils.dir_util import copy_tree
 
 
 class CodarCheetah(Package):
@@ -42,4 +41,4 @@ class CodarCheetah(Package):
     depends_on('savanna')
 
     def install(self, spec, prefix):
-        copy_tree('.', prefix)
+        install_tree('.', prefix)
diff --git a/var/spack/repos/builtin/packages/comd/package.py b/var/spack/repos/builtin/packages/comd/package.py
index 2aed00a924..baa9c89d59 100644
--- a/var/spack/repos/builtin/packages/comd/package.py
+++ b/var/spack/repos/builtin/packages/comd/package.py
@@ -22,9 +22,7 @@
 # License along with this program; if not, write to the Free Software
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ##############################################################################
-
 from spack import *
-import shutil
 
 
 class Comd(MakefilePackage):
@@ -58,7 +56,7 @@ class Comd(MakefilePackage):
 
     def edit(self, spec, prefix):
         with working_dir('src-mpi') or working_dir('src-openmp'):
-            shutil.copy('Makefile.vanilla', 'Makefile')
+            copy('Makefile.vanilla', 'Makefile')
 
     @property
     def build_targets(self):
diff --git a/var/spack/repos/builtin/packages/cosp2/package.py b/var/spack/repos/builtin/packages/cosp2/package.py
index 001206132a..5eb59d53e6 100644
--- a/var/spack/repos/builtin/packages/cosp2/package.py
+++ b/var/spack/repos/builtin/packages/cosp2/package.py
@@ -23,7 +23,6 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ##############################################################################
 from spack import *
-import shutil
 
 
 class Cosp2(MakefilePackage):
@@ -62,7 +61,7 @@ def edit(self, spec, prefix):
             if '+double' in spec:
                 filter_file('DOUBLE_PRECISION = O.*', 'DOUBLE_PRECISION = OFF',
                             'Makefile.vanilla')
-            shutil.copy('Makefile.vanilla', 'Makefile')
+            copy('Makefile.vanilla', 'Makefile')
 
     def install(self, spec, prefix):
         install_tree('bin/', prefix.bin)
diff --git a/var/spack/repos/builtin/packages/cp2k/package.py b/var/spack/repos/builtin/packages/cp2k/package.py
index 4aa79a4592..52eecc61ed 100644
--- a/var/spack/repos/builtin/packages/cp2k/package.py
+++ b/var/spack/repos/builtin/packages/cp2k/package.py
@@ -23,7 +23,6 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ##############################################################################
 import os
-import shutil
 import copy
 
 from spack import *
@@ -266,8 +265,7 @@ def install(self, spec, prefix):
                 lib_dir = join_path('lib', cp2k_architecture, cp2k_version)
                 mkdirp(lib_dir)
                 try:
-                    shutil.copy(env['LIBSMM_PATH'],
-                                join_path(lib_dir, 'libsmm.a'))
+                    copy(env['LIBSMM_PATH'], join_path(lib_dir, 'libsmm.a'))
                 except KeyError:
                     raise KeyError('Point environment variable LIBSMM_PATH to '
                                    'the absolute path of the libsmm.a file')
@@ -315,4 +313,4 @@ def install(self, spec, prefix):
                  'VERSION={0}'.format(cp2k_version))
             env['PWD'] = pwd_backup
         exe_dir = join_path('exe', cp2k_architecture)
-        shutil.copytree(exe_dir, self.prefix.bin)
+        install_tree(exe_dir, self.prefix.bin)
diff --git a/var/spack/repos/builtin/packages/cppcheck/package.py b/var/spack/repos/builtin/packages/cppcheck/package.py
index 6286941b84..dfd2adb4eb 100644
--- a/var/spack/repos/builtin/packages/cppcheck/package.py
+++ b/var/spack/repos/builtin/packages/cppcheck/package.py
@@ -23,11 +23,9 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ##############################################################################
 from spack import *
-import os
-import shutil
 
 
-class Cppcheck(Package):
+class Cppcheck(MakefilePackage):
     """A tool for static C/C++ code analysis."""
     homepage = "http://cppcheck.sourceforge.net/"
     url      = "https://downloads.sourceforge.net/project/cppcheck/cppcheck/1.78/cppcheck-1.78.tar.bz2"
@@ -41,12 +39,13 @@ class Cppcheck(Package):
 
     depends_on('py-pygments', when='+htmlreport', type='run')
 
+    def build(self, spec, prefix):
+        make('CFGDIR={0}'.format(prefix.cfg))
+
     def install(self, spec, prefix):
-        # cppcheck does not have a configure script
-        make("CFGDIR=%s" % os.path.join(prefix, 'cfg'))
-        # manually install the final cppcheck binary
+        # Manually install the final cppcheck binary
         mkdirp(prefix.bin)
         install('cppcheck', prefix.bin)
-        shutil.copytree('cfg', os.path.join(prefix, 'cfg'))
+        install_tree('cfg', prefix.cfg)
         if spec.satisfies('+htmlreport'):
             install('htmlreport/cppcheck-htmlreport', prefix.bin)
diff --git a/var/spack/repos/builtin/packages/cudnn/package.py b/var/spack/repos/builtin/packages/cudnn/package.py
index 2752a1458f..a9958537c3 100644
--- a/var/spack/repos/builtin/packages/cudnn/package.py
+++ b/var/spack/repos/builtin/packages/cudnn/package.py
@@ -23,7 +23,6 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ##############################################################################
 from spack import *
-from distutils.dir_util import copy_tree
 
 
 class Cudnn(Package):
@@ -40,4 +39,4 @@ class Cudnn(Package):
     depends_on('cuda@8:')
 
     def install(self, spec, prefix):
-        copy_tree('.', prefix)
+        install_tree('.', prefix)
diff --git a/var/spack/repos/builtin/packages/farmhash/package.py b/var/spack/repos/builtin/packages/farmhash/package.py
index 23c9af82a4..a8aa8f2c9f 100644
--- a/var/spack/repos/builtin/packages/farmhash/package.py
+++ b/var/spack/repos/builtin/packages/farmhash/package.py
@@ -24,7 +24,6 @@
 ##############################################################################
 from spack import *
 import os.path
-from shutil import copyfile
 
 
 class Farmhash(CMakePackage):
@@ -36,5 +35,5 @@ class Farmhash(CMakePackage):
     version('92e897', commit='92e897b282426729f4724d91a637596c7e2fe28f')
 
     def patch(self):
-        copyfile(join_path(os.path.dirname(__file__), "CMakeLists.txt"),
-                 "CMakeLists.txt")
+        copy(join_path(os.path.dirname(__file__), "CMakeLists.txt"),
+             "CMakeLists.txt")
diff --git a/var/spack/repos/builtin/packages/fastqc/package.py b/var/spack/repos/builtin/packages/fastqc/package.py
index 1b0eb876ae..c793713618 100644
--- a/var/spack/repos/builtin/packages/fastqc/package.py
+++ b/var/spack/repos/builtin/packages/fastqc/package.py
@@ -23,8 +23,6 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ##############################################################################
 from spack import *
-from distutils.dir_util import copy_tree, mkpath
-from distutils.file_util import copy_file
 
 
 class Fastqc(Package):
@@ -43,15 +41,15 @@ class Fastqc(Package):
     patch('fastqc.patch', level=0)
 
     def install(self, spec, prefix):
-        mkpath(self.prefix.bin)
-        mkpath(self.prefix.lib)
-        copy_file('fastqc', self.prefix.bin)
+        mkdir(prefix.bin)
+        mkdir(prefix.lib)
+        install('fastqc', prefix.bin)
         for j in ['cisd-jhdf5.jar', 'jbzip2-0.9.jar', 'sam-1.103.jar']:
-            copy_file(j, self.prefix.lib)
+            install(j, prefix.lib)
         for d in ['Configuration', 'net', 'org', 'Templates', 'uk']:
-            copy_tree(d, join_path(self.prefix.lib, d))
+            install_tree(d, join_path(prefix.lib, d))
         chmod = which('chmod')
-        chmod('+x', join_path(self.prefix.bin, 'fastqc'))
+        chmod('+x', prefix.bin.fastqc)
 
     # In theory the 'run' dependency on 'jdk' above should take
     # care of this for me. In practice, it does not.
@@ -59,5 +57,4 @@ def setup_environment(self, spack_env, run_env):
         """Add <prefix> to the path; the package has a script at the
            top level.
         """
-        run_env.prepend_path('PATH', join_path(self.spec['java'].prefix,
-                             'bin'))
+        run_env.prepend_path('PATH', self.spec['java'].prefix.bin)
diff --git a/var/spack/repos/builtin/packages/fermisciencetools/package.py b/var/spack/repos/builtin/packages/fermisciencetools/package.py
index bdac786766..556123dd09 100644
--- a/var/spack/repos/builtin/packages/fermisciencetools/package.py
+++ b/var/spack/repos/builtin/packages/fermisciencetools/package.py
@@ -23,7 +23,6 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ##############################################################################
 from spack import *
-from distutils.dir_util import copy_tree
 
 
 class Fermisciencetools(Package):
@@ -42,4 +41,4 @@ class Fermisciencetools(Package):
     version('11r5p3', 'cf050ddddfe9251b6ebe8d3fd7de3c3f')
 
     def install(self, spec, prefix):
-        copy_tree('x86_64-unknown-linux-gnu-libc2.17', prefix)
+        install_tree('x86_64-unknown-linux-gnu-libc2.17', prefix)
diff --git a/var/spack/repos/builtin/packages/fsl/package.py b/var/spack/repos/builtin/packages/fsl/package.py
index c14865622d..00d00db220 100644
--- a/var/spack/repos/builtin/packages/fsl/package.py
+++ b/var/spack/repos/builtin/packages/fsl/package.py
@@ -25,7 +25,6 @@
 from spack import *
 from spack.environment import EnvironmentModifications
 import os
-import distutils.dir_util
 
 
 class Fsl(Package):
@@ -68,7 +67,7 @@ def install(self, spec, prefix):
         build = Executable('./build')
         build()
 
-        distutils.dir_util.copy_tree(".", prefix)
+        install_tree('.', prefix)
 
     def setup_environment(self, spack_env, run_env):
         if not self.stage.source_path:
diff --git a/var/spack/repos/builtin/packages/gatk/package.py b/var/spack/repos/builtin/packages/gatk/package.py
index 8ca12f133a..f7ce7c1714 100644
--- a/var/spack/repos/builtin/packages/gatk/package.py
+++ b/var/spack/repos/builtin/packages/gatk/package.py
@@ -23,7 +23,6 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ##############################################################################
 from spack import *
-from shutil import copyfile
 import glob
 import os.path
 import re
@@ -62,7 +61,7 @@ def install(self, spec, prefix):
         # explicitly codes the path for java and the jar file.
         script_sh = join_path(os.path.dirname(__file__), "gatk.sh")
         script = join_path(prefix.bin, "gatk")
-        copyfile(script_sh, script)
+        install(script_sh, script)
         set_executable(script)
 
         # Munge the helper script to explicitly point to java and the
diff --git a/var/spack/repos/builtin/packages/gaussian/package.py b/var/spack/repos/builtin/packages/gaussian/package.py
index e928d475ad..c14c830b0e 100644
--- a/var/spack/repos/builtin/packages/gaussian/package.py
+++ b/var/spack/repos/builtin/packages/gaussian/package.py
@@ -24,7 +24,6 @@
 ##############################################################################
 from spack import *
 import os
-import shutil
 
 
 class Gaussian(Package):
@@ -36,7 +35,7 @@ class Gaussian(Package):
     version('09', '7d4c95b535e68e48af183920df427e4e')
 
     def install(self, spec, prefix):
-        shutil.copytree(os.getcwd(), prefix.bin)
+        install_tree('.', prefix.bin)
         patch_install_files = ['flc',
                                'linda8.2/opteron-linux/bin/flc',
                                'linda8.2/opteron-linux/bin/LindaLauncher',
diff --git a/var/spack/repos/builtin/packages/gcc/package.py b/var/spack/repos/builtin/packages/gcc/package.py
index 0bfcd26e2b..7bf95acb94 100644
--- a/var/spack/repos/builtin/packages/gcc/package.py
+++ b/var/spack/repos/builtin/packages/gcc/package.py
@@ -28,7 +28,6 @@
 
 import glob
 import os
-import shutil
 import sys
 
 
@@ -207,7 +206,7 @@ def patch(self):
                 new_dispatch_dir = join_path(prefix, 'include', 'dispatch')
                 mkdirp(new_dispatch_dir)
                 new_header = join_path(new_dispatch_dir, 'object.h')
-                shutil.copyfile('/usr/include/dispatch/object.h', new_header)
+                install('/usr/include/dispatch/object.h', new_header)
                 filter_file(r'typedef void \(\^dispatch_block_t\)\(void\)',
                             'typedef void* dispatch_block_t',
                             new_header)
diff --git a/var/spack/repos/builtin/packages/genomefinisher/package.py b/var/spack/repos/builtin/packages/genomefinisher/package.py
index 7599c37505..2083c3b22b 100644
--- a/var/spack/repos/builtin/packages/genomefinisher/package.py
+++ b/var/spack/repos/builtin/packages/genomefinisher/package.py
@@ -23,7 +23,6 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ##############################################################################
 from spack import *
-from shutil import copyfile
 import os
 
 
@@ -49,13 +48,13 @@ def install(self, spec, prefix):
         # Set up a helper script to call java on the jar file,
         # explicitly codes the path for java and the jar file.
         script_sh = join_path(os.path.dirname(__file__), "genomefinisher.sh")
-        script = join_path(prefix.bin, "genomefinisher")
-        copyfile(script_sh, script)
+        script = prefix.bin.genomefinisher
+        install(script_sh, script)
         set_executable(script)
 
         # Munge the helper script to explicitly point to java and the jar file
         # jar file.
-        java = join_path(self.spec['jdk'].prefix, 'bin', 'java')
+        java = spec['jdk'].prefix.bin.java
         kwargs = {'ignore_absent': False, 'backup': False, 'string': False}
         filter_file('^java', java, script, **kwargs)
         filter_file(jar_file, join_path(prefix.bin, jar_file),
diff --git a/var/spack/repos/builtin/packages/git/package.py b/var/spack/repos/builtin/packages/git/package.py
index 7c3499bfe1..3e8a11d69e 100644
--- a/var/spack/repos/builtin/packages/git/package.py
+++ b/var/spack/repos/builtin/packages/git/package.py
@@ -24,7 +24,6 @@
 ##############################################################################
 import sys
 from spack import *
-from distutils.dir_util import copy_tree
 
 
 class Git(AutotoolsPackage):
@@ -241,7 +240,7 @@ def check(self):
 
     @run_after('install')
     def install_completions(self):
-        copy_tree('contrib/completion', self.prefix.share)
+        install_tree('contrib/completion', self.prefix.share)
 
     @run_after('install')
     def install_manpages(self):
diff --git a/var/spack/repos/builtin/packages/glib/package.py b/var/spack/repos/builtin/packages/glib/package.py
index 6a004d8e64..87a0286950 100644
--- a/var/spack/repos/builtin/packages/glib/package.py
+++ b/var/spack/repos/builtin/packages/glib/package.py
@@ -25,7 +25,6 @@
 from spack import *
 
 import os.path
-import shutil
 
 
 class Glib(AutotoolsPackage):
@@ -117,7 +116,7 @@ def fix_dtrace_usr_bin_path(self):
         dtrace_copy = join_path(self.dtrace_copy_path, 'dtrace')
 
         with working_dir(self.dtrace_copy_path, create=True):
-            shutil.copy(dtrace, dtrace_copy)
+            copy(dtrace, dtrace_copy)
             filter_file(
                 '^#!/usr/bin/python',
                 '#!/usr/bin/env python',
diff --git a/var/spack/repos/builtin/packages/go-bootstrap/package.py b/var/spack/repos/builtin/packages/go-bootstrap/package.py
index c7794a40e9..730f423d55 100644
--- a/var/spack/repos/builtin/packages/go-bootstrap/package.py
+++ b/var/spack/repos/builtin/packages/go-bootstrap/package.py
@@ -22,9 +22,6 @@
 # License along with this program; if not, write to the Free Software
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ##############################################################################
-import os
-import shutil
-import glob
 from spack import *
 
 # THIS PACKAGE SHOULD NOT EXIST
@@ -75,15 +72,7 @@ def install(self, spec, prefix):
         with working_dir('src'):
             bash('{0}.bash'.format('all' if self.run_tests else 'make'))
 
-        try:
-            os.makedirs(prefix)
-        except OSError:
-            pass
-        for f in glob.glob('*'):
-            if os.path.isdir(f):
-                shutil.copytree(f, os.path.join(prefix, f))
-            else:
-                shutil.copy2(f, os.path.join(prefix, f))
+        install_tree('.', prefix)
 
     def setup_dependent_environment(self, spack_env, run_env, dependent_spec):
         spack_env.set('GOROOT_BOOTSTRAP', self.spec.prefix)
diff --git a/var/spack/repos/builtin/packages/go/package.py b/var/spack/repos/builtin/packages/go/package.py
index e7c96af8af..48055f7722 100644
--- a/var/spack/repos/builtin/packages/go/package.py
+++ b/var/spack/repos/builtin/packages/go/package.py
@@ -23,8 +23,6 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ##############################################################################
 import os
-import shutil
-import glob
 import llnl.util.tty as tty
 from spack import *
 
@@ -101,15 +99,7 @@ def install(self, spec, prefix):
         with working_dir('src'):
             bash('{0}.bash'.format('all' if self.run_tests else 'make'))
 
-        try:
-            os.makedirs(prefix)
-        except OSError:
-            pass
-        for f in glob.glob('*'):
-            if os.path.isdir(f):
-                shutil.copytree(f, os.path.join(prefix, f))
-            else:
-                shutil.copy2(f, os.path.join(prefix, f))
+        install_tree('.', prefix)
 
     def setup_environment(self, spack_env, run_env):
         spack_env.set('GOROOT_FINAL', self.spec.prefix)
@@ -123,10 +113,9 @@ def setup_dependent_package(self, module, dependent_spec):
 
         In most cases, extensions will only need to set GOPATH and use go::
 
-        env = os.environ
         env['GOPATH'] = self.source_path + ':' + env['GOPATH']
         go('get', '<package>', env=env)
-        shutil.copytree('bin', os.path.join(prefix, '/bin'))
+        install_tree('bin', prefix.bin)
         """
         #  Add a go command/compiler for extensions
         module.go = self.spec['go'].command
diff --git a/var/spack/repos/builtin/packages/grackle/package.py b/var/spack/repos/builtin/packages/grackle/package.py
index e88041dd8b..0df3f3bf81 100644
--- a/var/spack/repos/builtin/packages/grackle/package.py
+++ b/var/spack/repos/builtin/packages/grackle/package.py
@@ -23,7 +23,6 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ##############################################################################
 import os.path
-import shutil
 import inspect
 
 from spack import *
@@ -78,7 +77,7 @@ def install(self, spec, prefix):
             'clib',
             'Make.mach.{0}'.format(grackle_architecture)
         )
-        shutil.copy(template, makefile)
+        copy(template, makefile)
         for key, value in substitutions.items():
             filter_file(key, value, makefile)
 
diff --git a/var/spack/repos/builtin/packages/gradle/package.py b/var/spack/repos/builtin/packages/gradle/package.py
index 055aac845f..f7ad851e9f 100644
--- a/var/spack/repos/builtin/packages/gradle/package.py
+++ b/var/spack/repos/builtin/packages/gradle/package.py
@@ -23,7 +23,6 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ##############################################################################
 from spack import *
-from distutils.dir_util import copy_tree
 
 
 class Gradle(Package):
@@ -81,4 +80,4 @@ class Gradle(Package):
     depends_on('java')
 
     def install(self, spec, prefix):
-        copy_tree('.', prefix)
+        install_tree('.', prefix)
diff --git a/var/spack/repos/builtin/packages/gurobi/package.py b/var/spack/repos/builtin/packages/gurobi/package.py
index aebb6bf6c6..9720313b7e 100644
--- a/var/spack/repos/builtin/packages/gurobi/package.py
+++ b/var/spack/repos/builtin/packages/gurobi/package.py
@@ -23,7 +23,6 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ##############################################################################
 from spack import *
-from distutils.dir_util import copy_tree
 import os
 
 
@@ -58,5 +57,4 @@ def setup_environment(self, spack_env, run_env):
         run_env.set('GUROBI_HOME', self.prefix)
 
     def install(self, spec, prefix):
-        with working_dir('linux64'):
-            copy_tree('.', prefix)
+        install_tree('linux64', prefix)
diff --git a/var/spack/repos/builtin/packages/hdf5-blosc/package.py b/var/spack/repos/builtin/packages/hdf5-blosc/package.py
index b0ff9536c7..0e0d6dd352 100644
--- a/var/spack/repos/builtin/packages/hdf5-blosc/package.py
+++ b/var/spack/repos/builtin/packages/hdf5-blosc/package.py
@@ -34,13 +34,13 @@ def _install_shlib(name, src, dst):
     if sys.platform == "darwin":
         shlib0 = name + ".0.dylib"
         shlib = name + ".dylib"
-        shutil.copyfile(join_path(src, shlib0), join_path(dst, shlib0))
+        install(join_path(src, shlib0), join_path(dst, shlib0))
         os.symlink(shlib0, join_path(dst, shlib))
     else:
         shlib000 = name + ".so.0.0.0"
         shlib0 = name + ".so.0"
         shlib = name + ".dylib"
-        shutil.copyfile(join_path(src, shlib000), join_path(dst, shlib000))
+        install(join_path(src, shlib000), join_path(dst, shlib000))
         os.symlink(shlib000, join_path(dst, shlib0))
         os.symlink(shlib0, join_path(dst, shlib))
 
diff --git a/var/spack/repos/builtin/packages/igvtools/package.py b/var/spack/repos/builtin/packages/igvtools/package.py
index 03e8dee653..410a409d63 100644
--- a/var/spack/repos/builtin/packages/igvtools/package.py
+++ b/var/spack/repos/builtin/packages/igvtools/package.py
@@ -23,7 +23,6 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ##############################################################################
 from spack import *
-from shutil import copyfile
 import os
 
 
@@ -47,13 +46,13 @@ def install(self, spec, prefix):
         # Set up a helper script to call java on the jar file,
         # explicitly codes the path for java and the jar file.
         script_sh = join_path(os.path.dirname(__file__), "igvtools.sh")
-        script = join_path(prefix.bin, "igvtools")
-        copyfile(script_sh, script)
+        script = prefix.bin.igvtools
+        install(script_sh, script)
         set_executable(script)
 
         # Munge the helper script to explicitly point to java and the
         # jar file.
-        java = join_path(self.spec['jdk'].prefix, 'bin', 'java')
+        java = spec['jdk'].prefix.bin.java
         kwargs = {'ignore_absent': False, 'backup': False, 'string': False}
         filter_file('^java', java, script, **kwargs)
         filter_file(jar_file, join_path(prefix.bin, jar_file),
diff --git a/var/spack/repos/builtin/packages/jdk/package.py b/var/spack/repos/builtin/packages/jdk/package.py
index b39c32bc07..f6f78b9501 100644
--- a/var/spack/repos/builtin/packages/jdk/package.py
+++ b/var/spack/repos/builtin/packages/jdk/package.py
@@ -25,7 +25,6 @@
 #
 # Author: Justin Too <too1@llnl.gov>
 #
-import distutils.dir_util
 from spack import *
 
 
@@ -72,7 +71,7 @@ def url_for_version(self, version):
         return url.format(version, minor_version)
 
     def install(self, spec, prefix):
-        distutils.dir_util.copy_tree(".", prefix)
+        install_tree('.', prefix)
 
     def setup_environment(self, spack_env, run_env):
         run_env.set('JAVA_HOME', self.spec.prefix)
diff --git a/var/spack/repos/builtin/packages/jmol/package.py b/var/spack/repos/builtin/packages/jmol/package.py
index eda8dbe877..cd9cbf1c83 100644
--- a/var/spack/repos/builtin/packages/jmol/package.py
+++ b/var/spack/repos/builtin/packages/jmol/package.py
@@ -23,7 +23,6 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ##############################################################################
 from spack import *
-from distutils.dir_util import copy_tree
 
 
 class Jmol(Package):
@@ -38,7 +37,7 @@ class Jmol(Package):
     depends_on('java', type='run')
 
     def install(self, spec, prefix):
-        copy_tree('jmol-{0}'.format(self.version), prefix)
+        install_tree('jmol-{0}'.format(self.version), prefix)
 
     def setup_environment(self, spack_env, run_env):
         run_env.prepend_path('PATH', self.prefix)
diff --git a/var/spack/repos/builtin/packages/kaldi/package.py b/var/spack/repos/builtin/packages/kaldi/package.py
index 69981c85ca..7dbe7d9a3b 100644
--- a/var/spack/repos/builtin/packages/kaldi/package.py
+++ b/var/spack/repos/builtin/packages/kaldi/package.py
@@ -23,7 +23,6 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ##############################################################################
 from spack import *
-from distutils.dir_util import copy_tree
 from os.path import join
 from fnmatch import fnmatch
 import os
@@ -105,7 +104,7 @@ def install(self, spec, prefix):
                         install(join(root, name), prefix.bin)
 
             mkdir(prefix.lib)
-            copy_tree('lib', prefix.lib)
+            install_tree('lib', prefix.lib)
 
             for root, dirs, files in os.walk('.'):
                 for name in files:
diff --git a/var/spack/repos/builtin/packages/leveldb/package.py b/var/spack/repos/builtin/packages/leveldb/package.py
index cf66732170..75b7b9411e 100644
--- a/var/spack/repos/builtin/packages/leveldb/package.py
+++ b/var/spack/repos/builtin/packages/leveldb/package.py
@@ -26,7 +26,7 @@
 from spack import *
 
 
-class Leveldb(Package):
+class Leveldb(MakefilePackage):
     """LevelDB is a fast key-value storage library written at Google
     that provides an ordered mapping from string keys to string values."""
 
@@ -39,21 +39,14 @@ class Leveldb(Package):
     depends_on("snappy")
 
     def install(self, spec, prefix):
-        make()
+        mkdirp(prefix.lib.pkgconfig)
 
-        mkdirp(prefix.include)
-        mkdirp(prefix.lib)
-        mkdirp(join_path(prefix.lib, 'pkgconfig'))
+        libraries  = glob.glob('out-shared/libleveldb.*')
+        libraries += glob.glob('out-static/libleveldb.*')
+        for library in libraries:
+            install(library, prefix.lib)
 
-        cp = which('cp')
-
-        # cp --preserve=links libleveldb.* prefix/lib
-        args = glob.glob('out-shared/libleveldb.*') \
-            + glob.glob('out-static/libleveldb.*')
-        args.append(prefix.lib)
-        cp('--preserve=links', *args)
-
-        cp('-r', 'include/leveldb', prefix.include)
+        install_tree('include/leveldb', prefix.include)
 
         with open(join_path(prefix.lib, 'pkgconfig', 'leveldb.pc'), 'w') as f:
             f.write('prefix={0}\n'.format(prefix))
diff --git a/var/spack/repos/builtin/packages/libgridxc/package.py b/var/spack/repos/builtin/packages/libgridxc/package.py
index 42a2a05517..f2db4ab13f 100644
--- a/var/spack/repos/builtin/packages/libgridxc/package.py
+++ b/var/spack/repos/builtin/packages/libgridxc/package.py
@@ -22,9 +22,7 @@
 # License along with this program; if not, write to the Free Software
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ##############################################################################
-
 from spack import *
-import shutil
 
 
 class Libgridxc(Package):
@@ -42,7 +40,7 @@ def configure(self, spec, prefix):
         sh = which('sh')
         with working_dir('build', create=True):
             sh('../src/config.sh')
-            shutil.copyfile('../extra/fortran.mk', 'fortran.mk')
+            copy('../extra/fortran.mk', 'fortran.mk')
 
     def install(self, spec, prefix):
         with working_dir('build'):
diff --git a/var/spack/repos/builtin/packages/libiconv/package.py b/var/spack/repos/builtin/packages/libiconv/package.py
index df57201d7e..d9ff4a251a 100644
--- a/var/spack/repos/builtin/packages/libiconv/package.py
+++ b/var/spack/repos/builtin/packages/libiconv/package.py
@@ -23,7 +23,6 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ##############################################################################
 from spack import *
-import shutil
 
 
 class Libiconv(AutotoolsPackage):
@@ -46,6 +45,6 @@ def configure_args(self):
         args = ['--enable-extra-encodings']
 
         # A hack to patch config.guess in the libcharset sub directory
-        shutil.copyfile('./build-aux/config.guess',
-                        'libcharset/build-aux/config.guess')
+        copy('./build-aux/config.guess',
+             'libcharset/build-aux/config.guess')
         return args
diff --git a/var/spack/repos/builtin/packages/llvm/package.py b/var/spack/repos/builtin/packages/llvm/package.py
index 2ecd61d75a..3be612fecc 100644
--- a/var/spack/repos/builtin/packages/llvm/package.py
+++ b/var/spack/repos/builtin/packages/llvm/package.py
@@ -504,10 +504,9 @@ def check_darwin_lldb_codesign_requirement(self):
         if not self.spec.satisfies('+lldb platform=darwin'):
             return
         codesign = which('codesign')
-        cp = which('cp')
         mkdir('tmp')
         llvm_check_file = join_path('tmp', 'llvm_check')
-        cp('/usr/bin/false', llvm_check_file)
+        copy('/usr/bin/false', llvm_check_file)
 
         try:
             codesign('-f', '-s', 'lldb_codesign', '--dryrun',
diff --git a/var/spack/repos/builtin/packages/masurca/package.py b/var/spack/repos/builtin/packages/masurca/package.py
index 9c35f45657..a1786cd7f3 100644
--- a/var/spack/repos/builtin/packages/masurca/package.py
+++ b/var/spack/repos/builtin/packages/masurca/package.py
@@ -23,7 +23,6 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ##############################################################################
 from spack import *
-import distutils.dir_util
 
 
 class Masurca(Package):
@@ -44,4 +43,4 @@ class Masurca(Package):
     def install(self, spec, prefix):
         installer = Executable('./install.sh')
         installer()
-        distutils.dir_util.copy_tree(".", prefix)
+        install_tree('.', prefix)
diff --git a/var/spack/repos/builtin/packages/maven/package.py b/var/spack/repos/builtin/packages/maven/package.py
index aef6ddeaee..abd4cca16c 100644
--- a/var/spack/repos/builtin/packages/maven/package.py
+++ b/var/spack/repos/builtin/packages/maven/package.py
@@ -23,7 +23,6 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ##############################################################################
 from spack import *
-from distutils.dir_util import copy_tree
 
 
 class Maven(Package):
@@ -39,4 +38,4 @@ class Maven(Package):
 
     def install(self, spec, prefix):
         # install pre-built distribution
-        copy_tree('.', prefix)
+        install_tree('.', prefix)
diff --git a/var/spack/repos/builtin/packages/mefit/package.py b/var/spack/repos/builtin/packages/mefit/package.py
index cbd05eb656..61e74dfbbe 100644
--- a/var/spack/repos/builtin/packages/mefit/package.py
+++ b/var/spack/repos/builtin/packages/mefit/package.py
@@ -23,7 +23,6 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ##############################################################################
 from spack import *
-import distutils.dir_util
 
 
 class Mefit(Package):
@@ -41,7 +40,7 @@ class Mefit(Package):
     depends_on('casper %gcc@4.8.5')
 
     def install(self, spec, prefix):
-        distutils.dir_util.copy_tree(".", prefix)
+        install_tree('.', prefix)
 
     def setup_environment(self, spack_env, run_env):
         run_env.prepend_path('PATH', self.prefix)
diff --git a/var/spack/repos/builtin/packages/mfem/package.py b/var/spack/repos/builtin/packages/mfem/package.py
index 9e26473f21..7d76ee8db9 100644
--- a/var/spack/repos/builtin/packages/mfem/package.py
+++ b/var/spack/repos/builtin/packages/mfem/package.py
@@ -440,7 +440,7 @@ def install(self, spec, prefix):
             # installed shared mfem library:
             with working_dir('config'):
                 os.rename('config.mk', 'config.mk.orig')
-                shutil.copyfile(str(self.config_mk), 'config.mk')
+                copy(str(self.config_mk), 'config.mk')
                 shutil.copystat('config.mk.orig', 'config.mk')
 
         if '+examples' in spec:
diff --git a/var/spack/repos/builtin/packages/mpix-launch-swift/package.py b/var/spack/repos/builtin/packages/mpix-launch-swift/package.py
index f72b0fff4f..57cb59eb20 100644
--- a/var/spack/repos/builtin/packages/mpix-launch-swift/package.py
+++ b/var/spack/repos/builtin/packages/mpix-launch-swift/package.py
@@ -23,10 +23,9 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ##############################################################################
 from spack import *
-from distutils.dir_util import copy_tree
 
 
-class MpixLaunchSwift(Package):
+class MpixLaunchSwift(MakefilePackage):
     """Library that allows a child MPI application to be launched
     inside a subset of processes in a parent MPI application.
     """
@@ -42,5 +41,4 @@ class MpixLaunchSwift(Package):
     depends_on('swig', type='build')
 
     def install(self, spec, prefix):
-        make()
-        copy_tree('.', prefix)
+        install_tree('.', prefix)
diff --git a/var/spack/repos/builtin/packages/mrtrix3/package.py b/var/spack/repos/builtin/packages/mrtrix3/package.py
index a61a19cc4c..26b083daf5 100644
--- a/var/spack/repos/builtin/packages/mrtrix3/package.py
+++ b/var/spack/repos/builtin/packages/mrtrix3/package.py
@@ -23,7 +23,6 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ##############################################################################
 from spack import *
-import distutils.dir_util
 
 
 class Mrtrix3(Package):
@@ -53,9 +52,7 @@ def install(self, spec, prefix):
         build = Executable('./build')
         configure()
         build()
-        # install_tree('.', prefix) does not work since the prefix
-        # directory already exists by this point
-        distutils.dir_util.copy_tree('.', prefix)
+        install_tree('.', prefix)
 
     def setup_environment(self, spac_env, run_env):
         run_env.prepend_path('PATH', self.prefix)
diff --git a/var/spack/repos/builtin/packages/namd/package.py b/var/spack/repos/builtin/packages/namd/package.py
index 837ae1a5c8..6179cacd84 100644
--- a/var/spack/repos/builtin/packages/namd/package.py
+++ b/var/spack/repos/builtin/packages/namd/package.py
@@ -23,7 +23,6 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ##############################################################################
 import platform
-import shutil
 import sys
 import os
 from spack import *
@@ -58,8 +57,8 @@ class Namd(MakefilePackage):
 
     def _copy_arch_file(self, lib):
         config_filename = 'arch/{0}.{1}'.format(self.arch, lib)
-        shutil.copy('arch/Linux-x86_64.{0}'.format(lib),
-                    config_filename)
+        copy('arch/Linux-x86_64.{0}'.format(lib),
+             config_filename)
         if lib == 'tcl':
             filter_file(r'-ltcl8\.5',
                         '-ltcl{0}'.format(self.spec['tcl'].version.up_to(2)),
diff --git a/var/spack/repos/builtin/packages/ncl/package.py b/var/spack/repos/builtin/packages/ncl/package.py
index 573acb664c..218fc31eb9 100644
--- a/var/spack/repos/builtin/packages/ncl/package.py
+++ b/var/spack/repos/builtin/packages/ncl/package.py
@@ -25,7 +25,6 @@
 from spack import *
 import glob
 import os
-import shutil
 import tempfile
 
 
@@ -263,8 +262,8 @@ def prepare_src_tree(self):
             triangle_src = join_path(self.stage.source_path, 'triangle_src')
             triangle_dst = join_path(self.stage.source_path, 'ni', 'src',
                                      'lib', 'hlu')
-            shutil.copy(join_path(triangle_src, 'triangle.h'), triangle_dst)
-            shutil.copy(join_path(triangle_src, 'triangle.c'), triangle_dst)
+            copy(join_path(triangle_src, 'triangle.h'), triangle_dst)
+            copy(join_path(triangle_src, 'triangle.c'), triangle_dst)
 
     @staticmethod
     def delete_files(*filenames):
diff --git a/var/spack/repos/builtin/packages/ncurses/package.py b/var/spack/repos/builtin/packages/ncurses/package.py
index c54cbcd71f..cc1b6a7287 100644
--- a/var/spack/repos/builtin/packages/ncurses/package.py
+++ b/var/spack/repos/builtin/packages/ncurses/package.py
@@ -26,7 +26,6 @@
 from glob import glob
 from os.path import exists, join
 from os import makedirs
-from shutil import copy
 
 
 class Ncurses(AutotoolsPackage):
@@ -109,7 +108,7 @@ def install(self, spec, prefix):
             if not exists(path):
                 makedirs(path)
             for header in headers:
-                copy(header, path)
+                install(header, path)
 
     @property
     def libs(self):
diff --git a/var/spack/repos/builtin/packages/occa/package.py b/var/spack/repos/builtin/packages/occa/package.py
index a27d3c6b34..ff9285518e 100644
--- a/var/spack/repos/builtin/packages/occa/package.py
+++ b/var/spack/repos/builtin/packages/occa/package.py
@@ -23,8 +23,6 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ##############################################################################
 from spack import *
-import os
-import shutil
 
 
 class Occa(Package):
@@ -63,12 +61,7 @@ class Occa(Package):
     def install(self, spec, prefix):
         # The build environment is set by the 'setup_environment' method.
         # Copy the source to the installation directory and build OCCA there.
-        for file in os.listdir('.'):
-            dest = join_path(prefix, os.path.basename(file))
-            if os.path.isdir(file):
-                shutil.copytree(file, dest)
-            else:
-                shutil.copy2(file, dest)
+        install_tree('.', prefix)
         make('-C', prefix)
 
         if self.run_tests:
diff --git a/var/spack/repos/builtin/packages/pexsi/package.py b/var/spack/repos/builtin/packages/pexsi/package.py
index c730d34756..931f951a09 100644
--- a/var/spack/repos/builtin/packages/pexsi/package.py
+++ b/var/spack/repos/builtin/packages/pexsi/package.py
@@ -25,7 +25,6 @@
 
 import inspect
 import os.path
-import shutil
 
 from spack import *
 
@@ -99,7 +98,7 @@ def edit(self, spec, prefix):
             self.stage.source_path,
             'make.inc'
         )
-        shutil.copy(template, makefile)
+        copy(template, makefile)
         for key, value in substitutions:
             filter_file(key, value, makefile)
 
diff --git a/var/spack/repos/builtin/packages/picard/package.py b/var/spack/repos/builtin/packages/picard/package.py
index cca0ae2f7b..6cab04e592 100644
--- a/var/spack/repos/builtin/packages/picard/package.py
+++ b/var/spack/repos/builtin/packages/picard/package.py
@@ -23,7 +23,6 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ##############################################################################
 from spack import *
-from shutil import copyfile
 import glob
 import os.path
 import re
@@ -69,13 +68,13 @@ def install(self, spec, prefix):
         # Set up a helper script to call java on the jar file,
         # explicitly codes the path for java and the jar file.
         script_sh = join_path(os.path.dirname(__file__), "picard.sh")
-        script = join_path(prefix.bin, "picard")
-        copyfile(script_sh, script)
+        script = prefix.bin.picard
+        install(script_sh, script)
         set_executable(script)
 
         # Munge the helper script to explicitly point to java and the
         # jar file.
-        java = join_path(self.spec['java'].prefix, 'bin', 'java')
+        java = self.spec['java'].prefix.bin.java
         kwargs = {'ignore_absent': False, 'backup': False, 'string': False}
         filter_file('^java', java, script, **kwargs)
         filter_file('picard.jar', join_path(prefix.bin, 'picard.jar'),
diff --git a/var/spack/repos/builtin/packages/pilon/package.py b/var/spack/repos/builtin/packages/pilon/package.py
index 8fba2c2376..0fec636fba 100644
--- a/var/spack/repos/builtin/packages/pilon/package.py
+++ b/var/spack/repos/builtin/packages/pilon/package.py
@@ -23,7 +23,6 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ##############################################################################
 from spack import *
-from shutil import copyfile
 import os.path
 
 
@@ -47,13 +46,13 @@ def install(self, spec, prefix):
         # Set up a helper script to call java on the jar file,
         # explicitly codes the path for java and the jar file.
         script_sh = join_path(os.path.dirname(__file__), "pilon.sh")
-        script = join_path(prefix.bin, "pilon")
-        copyfile(script_sh, script)
+        script = prefix.bin.pilon
+        install(script_sh, script)
         set_executable(script)
 
         # Munge the helper script to explicitly point to java and the
         # jar file.
-        java = join_path(self.spec['java'].prefix, 'bin', 'java')
+        java = self.spec['java'].prefix.bin.java
         kwargs = {'ignore_absent': False, 'backup': False, 'string': False}
         filter_file('^java', java, script, **kwargs)
         filter_file('pilon-{0}.jar', join_path(prefix.bin, jar_file),
diff --git a/var/spack/repos/builtin/packages/pindel/package.py b/var/spack/repos/builtin/packages/pindel/package.py
index 479128707b..d0acc28472 100644
--- a/var/spack/repos/builtin/packages/pindel/package.py
+++ b/var/spack/repos/builtin/packages/pindel/package.py
@@ -23,7 +23,6 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ##############################################################################
 from spack import *
-from shutil import copytree, copyfile
 
 
 class Pindel(MakefilePackage):
@@ -51,9 +50,8 @@ class Pindel(MakefilePackage):
     #
 
     def edit(self, spec, prefix):
-        makefile2 = join_path(self.build_directory, 'Makefile2')
-        copyfile(join_path(self.build_directory, 'Makefile'), makefile2)
-        myedit = FileFilter(makefile2)
+        copy('Makefile', 'Makefile2')
+        myedit = FileFilter('Makefile2')
         myedit.filter('-include Makefile.local', '#removed include')
         myedit.filter('@false', '#removed autofailure')
 
@@ -69,6 +67,4 @@ def install(self, spec, prefix):
         install('src/pindel2vcf', prefix.bin)
         install('src/sam2pindel', prefix.bin)
         install('src/pindel2vcf4tcga', prefix.bin)
-        copytree(join_path(self.build_directory, 'demo'),
-                 prefix.doc,
-                 symlinks=True)
+        install_tree('demo', prefix.doc)
diff --git a/var/spack/repos/builtin/packages/python/package.py b/var/spack/repos/builtin/packages/python/package.py
index 94e6b61bd4..5b8c7e94e3 100644
--- a/var/spack/repos/builtin/packages/python/package.py
+++ b/var/spack/repos/builtin/packages/python/package.py
@@ -27,7 +27,6 @@
 import platform
 import re
 import sys
-import shutil
 
 import llnl.util.tty as tty
 from llnl.util.lang import match_predicate
@@ -709,7 +708,7 @@ def add_files_to_view(self, view, merge_map):
             if not path_contains_subdirectory(src, bin_dir):
                 view.link(src, dst)
             elif not os.path.islink(src):
-                shutil.copy2(src, dst)
+                copy(src, dst)
                 if 'script' in get_filetype(src):
                     filter_file(
                         self.spec.prefix, os.path.abspath(view.root), dst)
diff --git a/var/spack/repos/builtin/packages/qbox/package.py b/var/spack/repos/builtin/packages/qbox/package.py
index 19d83773a8..805a567e55 100644
--- a/var/spack/repos/builtin/packages/qbox/package.py
+++ b/var/spack/repos/builtin/packages/qbox/package.py
@@ -22,8 +22,6 @@
 # License along with this program; if not, write to the Free Software
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ##############################################################################
-
-import shutil
 from spack import *
 
 
@@ -88,6 +86,6 @@ def edit(self, spec, prefix):
     def install(self, spec, prefix):
         mkdir(prefix.src)
         install('src/qb', prefix.src)
-        shutil.move('test', prefix)
-        shutil.move('xml', prefix)
-        shutil.move('util', prefix)
+        install_tree('test', prefix)
+        install_tree('xml', prefix)
+        install_tree('util', prefix)
diff --git a/var/spack/repos/builtin/packages/qorts/package.py b/var/spack/repos/builtin/packages/qorts/package.py
index bb71f15b8f..7e745dc304 100644
--- a/var/spack/repos/builtin/packages/qorts/package.py
+++ b/var/spack/repos/builtin/packages/qorts/package.py
@@ -24,7 +24,6 @@
 ##############################################################################
 from spack import *
 import os.path
-from shutil import copyfile
 
 
 class Qorts(RPackage):
@@ -59,7 +58,7 @@ def install_jar(self):
         # explicitly codes the path for java and the jar file.
         script_sh = join_path(os.path.dirname(__file__), "QoRTs.sh")
         script = self.prefix.bin.QoRTs
-        copyfile(script_sh, script)
+        install(script_sh, script)
         set_executable(script)
 
         # Munge the helper script to explicitly point to java and the
diff --git a/var/spack/repos/builtin/packages/r/package.py b/var/spack/repos/builtin/packages/r/package.py
index 6ae13af9f5..52d11f5bbb 100644
--- a/var/spack/repos/builtin/packages/r/package.py
+++ b/var/spack/repos/builtin/packages/r/package.py
@@ -23,7 +23,6 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ##############################################################################
 import os
-import shutil
 
 from spack import *
 
@@ -142,7 +141,7 @@ def copy_makeconf(self):
         # dependencies in Spack.
         src_makeconf = join_path(self.etcdir, 'Makeconf')
         dst_makeconf = join_path(self.etcdir, 'Makeconf.spack')
-        shutil.copy(src_makeconf, dst_makeconf)
+        install(src_makeconf, dst_makeconf)
 
     # ========================================================================
     # Set up environment to make install easy for R extensions.
diff --git a/var/spack/repos/builtin/packages/repeatmasker/package.py b/var/spack/repos/builtin/packages/repeatmasker/package.py
index 72aa492f3d..6177bb5710 100644
--- a/var/spack/repos/builtin/packages/repeatmasker/package.py
+++ b/var/spack/repos/builtin/packages/repeatmasker/package.py
@@ -24,7 +24,6 @@
 ##############################################################################
 from spack import *
 import inspect
-import distutils.dir_util
 
 
 class Repeatmasker(Package):
@@ -78,4 +77,4 @@ def install(self, spec, prefix):
         with open(config_answers_filename, 'r') as f:
             inspect.getmodule(self).perl('configure', input=f)
 
-        distutils.dir_util.copy_tree(".", prefix.bin)
+        install_tree('.', prefix.bin)
diff --git a/var/spack/repos/builtin/packages/rna-seqc/package.py b/var/spack/repos/builtin/packages/rna-seqc/package.py
index 9fe5bcc096..cc58cb7e0e 100644
--- a/var/spack/repos/builtin/packages/rna-seqc/package.py
+++ b/var/spack/repos/builtin/packages/rna-seqc/package.py
@@ -23,7 +23,6 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ##############################################################################
 from spack import *
-from shutil import copyfile
 import os.path
 
 
@@ -51,12 +50,12 @@ def install(self, spec, prefix):
         # explicitly codes the path for java and the jar file.
         script_sh = join_path(os.path.dirname(__file__), "rna-seqc.sh")
         script = join_path(prefix.bin, "rna-seqc")
-        copyfile(script_sh, script)
+        install(script_sh, script)
         set_executable(script)
 
         # Munge the helper script to explicitly point to java and the
         # jar file.
-        java = join_path(self.spec['jdk'].prefix, 'bin', 'java')
+        java = self.spec['jdk'].prefix.bin.java
         kwargs = {'ignore_absent': False, 'backup': False, 'string': False}
         filter_file('^java', java, script, **kwargs)
         filter_file('RNA-SeQC_v{0}.jar', join_path(prefix.bin, jar_file),
diff --git a/var/spack/repos/builtin/packages/rockstar/package.py b/var/spack/repos/builtin/packages/rockstar/package.py
index f77403495f..ba4134f731 100644
--- a/var/spack/repos/builtin/packages/rockstar/package.py
+++ b/var/spack/repos/builtin/packages/rockstar/package.py
@@ -22,10 +22,8 @@
 # License along with this program; if not, write to the Free Software
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ##############################################################################
-
 import os
 from spack import *
-from distutils.dir_util import copy_tree
 
 
 class Rockstar(MakefilePackage):
@@ -59,7 +57,7 @@ def build(self, spec, prefix):
 
     def install(self, spec, prefix):
         # Install all files and directories
-        copy_tree(".", prefix)
+        install_tree('.', prefix)
 
         mkdir(prefix.bin)
         mkdir(prefix.lib)
diff --git a/var/spack/repos/builtin/packages/savanna/package.py b/var/spack/repos/builtin/packages/savanna/package.py
index 1e5ea83f5b..126c79a5ba 100644
--- a/var/spack/repos/builtin/packages/savanna/package.py
+++ b/var/spack/repos/builtin/packages/savanna/package.py
@@ -23,7 +23,6 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ##############################################################################
 from spack import *
-from distutils.dir_util import copy_tree
 
 
 class Savanna(MakefilePackage):
@@ -46,4 +45,4 @@ class Savanna(MakefilePackage):
     depends_on('tau', when='+tau')
 
     def install(self, spec, prefix):
-        copy_tree('.', prefix)
+        install_tree('.', prefix)
diff --git a/var/spack/repos/builtin/packages/sbt/package.py b/var/spack/repos/builtin/packages/sbt/package.py
index d5eecc145c..5ad2bd2745 100644
--- a/var/spack/repos/builtin/packages/sbt/package.py
+++ b/var/spack/repos/builtin/packages/sbt/package.py
@@ -23,7 +23,6 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ##############################################################################
 from spack import *
-import shutil
 
 
 class Sbt(Package):
@@ -40,5 +39,5 @@ class Sbt(Package):
     depends_on('java')
 
     def install(self, spec, prefix):
-        shutil.copytree('bin', join_path(prefix, 'bin'), symlinks=True)
-        shutil.copytree('conf', join_path(prefix, 'conf'), symlinks=True)
+        install_tree('bin',  prefix.bin)
+        install_tree('conf', prefix.conf)
diff --git a/var/spack/repos/builtin/packages/scr/package.py b/var/spack/repos/builtin/packages/scr/package.py
index 7ec0a5351e..8fc0ecd489 100644
--- a/var/spack/repos/builtin/packages/scr/package.py
+++ b/var/spack/repos/builtin/packages/scr/package.py
@@ -25,7 +25,6 @@
 from spack import *
 
 import os
-import shutil
 
 
 class Scr(CMakePackage):
@@ -151,4 +150,4 @@ def copy_config(self):
         if spec.variants['copy_config'].value:
             dest_path = self.get_abs_path_rel_prefix(
                 spec.variants['scr_config'].value)
-            shutil.copyfile(spec.variants['copy_config'].value, dest_path)
+            install(spec.variants['copy_config'].value, dest_path)
diff --git a/var/spack/repos/builtin/packages/sctk/package.py b/var/spack/repos/builtin/packages/sctk/package.py
index 089b8c04fc..97326572ff 100644
--- a/var/spack/repos/builtin/packages/sctk/package.py
+++ b/var/spack/repos/builtin/packages/sctk/package.py
@@ -23,7 +23,6 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ##############################################################################
 from spack import *
-from distutils.dir_util import copy_tree
 
 
 class Sctk(Package):
@@ -49,5 +48,4 @@ def install(self, spec, prefix):
         make('config')
         make('all')
         make('install')
-        mkdirp(prefix.bin)
-        copy_tree('bin', prefix.bin)
+        install_tree('bin', prefix.bin)
diff --git a/var/spack/repos/builtin/packages/snpeff/package.py b/var/spack/repos/builtin/packages/snpeff/package.py
index 44652ccee0..2d8c7900ce 100644
--- a/var/spack/repos/builtin/packages/snpeff/package.py
+++ b/var/spack/repos/builtin/packages/snpeff/package.py
@@ -24,7 +24,6 @@
 ##############################################################################
 from spack import *
 import os.path
-from shutil import copyfile
 
 
 class Snpeff(Package):
@@ -45,13 +44,13 @@ def install(self, spec, prefix):
         # Set up a helper script to call java on the jar file,
         # explicitly codes the path for java and the jar file.
         script_sh = join_path(os.path.dirname(__file__), "snpEff.sh")
-        script = join_path(prefix.bin, "snpEff")
-        copyfile(script_sh, script)
+        script = prefix.bin.snpEff
+        install(script_sh, script)
         set_executable(script)
 
         # Munge the helper script to explicitly point to java and the
         # jar file.
-        java = join_path(self.spec['java'].prefix.bin, 'java')
+        java = self.spec['java'].prefix.bin.java
         kwargs = {'backup': False}
         filter_file('^java', java, script, **kwargs)
         filter_file('snpEff.jar', join_path(prefix.bin, 'snpEff.jar'),
diff --git a/var/spack/repos/builtin/packages/snphylo/package.py b/var/spack/repos/builtin/packages/snphylo/package.py
index 2add84ad26..5066b41e28 100644
--- a/var/spack/repos/builtin/packages/snphylo/package.py
+++ b/var/spack/repos/builtin/packages/snphylo/package.py
@@ -23,7 +23,6 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ##############################################################################
 from spack import *
-import distutils.dir_util
 
 
 class Snphylo(Package):
@@ -51,7 +50,7 @@ def install(self, spec, prefix):
         with open(install_answer_input, 'r') as f:
             bash = which('bash')
             bash('./setup.sh', input=f)
-            distutils.dir_util.copy_tree(".", prefix)
+            install_tree('.', prefix)
 
     def setup_environment(self, spack_env, run_env):
         run_env.prepend_path('PATH', self.spec.prefix)
diff --git a/var/spack/repos/builtin/packages/spark/package.py b/var/spack/repos/builtin/packages/spark/package.py
index 7cdd6b175f..1b318e8c78 100644
--- a/var/spack/repos/builtin/packages/spark/package.py
+++ b/var/spack/repos/builtin/packages/spark/package.py
@@ -23,7 +23,6 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ##############################################################################
 import re
-import shutil
 
 from spack import *
 
@@ -64,7 +63,7 @@ def install_dir(dirname):
         install_dir('yarn')
 
         # required for spark to recognize binary distribution
-        shutil.copy('RELEASE', prefix)
+        install('RELEASE', prefix)
 
     @when('+hadoop')
     def setup_environment(self, spack_env, run_env):
diff --git a/var/spack/repos/builtin/packages/sublime-text/package.py b/var/spack/repos/builtin/packages/sublime-text/package.py
index dad8ca0db4..83dd2f380b 100644
--- a/var/spack/repos/builtin/packages/sublime-text/package.py
+++ b/var/spack/repos/builtin/packages/sublime-text/package.py
@@ -23,7 +23,6 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ##############################################################################
 from spack import *
-from distutils.dir_util import copy_tree
 
 
 class SublimeText(Package):
@@ -57,4 +56,4 @@ def url_for_version(self, version):
 
     def install(self, spec, prefix):
         # Sublime text comes as a pre-compiled binary.
-        copy_tree('.', prefix)
+        install_tree('.', prefix)
diff --git a/var/spack/repos/builtin/packages/supernova/package.py b/var/spack/repos/builtin/packages/supernova/package.py
index 3e6ac97bc2..f358dd4939 100644
--- a/var/spack/repos/builtin/packages/supernova/package.py
+++ b/var/spack/repos/builtin/packages/supernova/package.py
@@ -23,7 +23,6 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ##############################################################################
 from spack import *
-from distutils.dir_util import copy_tree
 import os
 
 
@@ -62,4 +61,4 @@ def install(self, spec, prefix):
         # remove the broken symlinks
         rm('anaconda-cs/2.2.0-anaconda-cs-c7/lib/libtcl.so',
             'anaconda-cs/2.2.0-anaconda-cs-c7/lib/libtk.so')
-        copy_tree('.', prefix, preserve_symlinks=1)
+        install_tree('.', prefix)
diff --git a/var/spack/repos/builtin/packages/the-platinum-searcher/package.py b/var/spack/repos/builtin/packages/the-platinum-searcher/package.py
index e3d7629e1a..858a0d5eba 100644
--- a/var/spack/repos/builtin/packages/the-platinum-searcher/package.py
+++ b/var/spack/repos/builtin/packages/the-platinum-searcher/package.py
@@ -23,8 +23,6 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ##############################################################################
 from spack import *
-import os
-import shutil
 
 
 class ThePlatinumSearcher(Package):
@@ -38,7 +36,6 @@ class ThePlatinumSearcher(Package):
     extends("go", deptypes='build')
 
     def install(self, spec, prefix):
-        env = os.environ
         env['GOPATH'] = self.stage.source_path + ':' + env['GOPATH']
         go('install', self.package, env=env)
-        shutil.copytree('bin', os.path.join(prefix, 'bin'))
+        install_tree('bin', prefix.bin)
diff --git a/var/spack/repos/builtin/packages/tinyxml/package.py b/var/spack/repos/builtin/packages/tinyxml/package.py
index e34315882f..afd3c7f6b5 100644
--- a/var/spack/repos/builtin/packages/tinyxml/package.py
+++ b/var/spack/repos/builtin/packages/tinyxml/package.py
@@ -23,7 +23,6 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ##############################################################################
 from spack import *
-from shutil import copyfile
 import os.path
 
 
@@ -42,8 +41,8 @@ def url_for_version(self, version):
         return url.format(version.dotted, version.underscored)
 
     def patch(self):
-        copyfile(join_path(os.path.dirname(__file__),
-                           "CMakeLists.txt"), "CMakeLists.txt")
+        copy(join_path(os.path.dirname(__file__),
+             "CMakeLists.txt"), "CMakeLists.txt")
 
     def cmake_args(self):
         spec = self.spec
diff --git a/var/spack/repos/builtin/packages/trimmomatic/package.py b/var/spack/repos/builtin/packages/trimmomatic/package.py
index 470ba69def..41459502ae 100644
--- a/var/spack/repos/builtin/packages/trimmomatic/package.py
+++ b/var/spack/repos/builtin/packages/trimmomatic/package.py
@@ -23,8 +23,6 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ##############################################################################
 from spack import *
-from distutils.dir_util import copy_tree
-from shutil import copyfile
 import os.path
 
 
@@ -47,18 +45,18 @@ def install(self, spec, prefix):
         install(jar_file, prefix.bin)
 
         # Put the adapter files someplace sensible
-        copy_tree('adapters', join_path(self.prefix.share, 'adapters'))
+        install_tree('adapters', prefix.share.adapters)
 
         # Set up a helper script to call java on the jar file,
         # explicitly codes the path for java and the jar file.
         script_sh = join_path(os.path.dirname(__file__), "trimmomatic.sh")
-        script = join_path(prefix.bin, "trimmomatic")
-        copyfile(script_sh, script)
+        script = prefix.bin.trimmomatic
+        install(script_sh, script)
         set_executable(script)
 
         # Munge the helper script to explicitly point to java and the
         # jar file.
-        java = join_path(self.spec['java'].prefix, 'bin', 'java')
+        java = self.spec['java'].prefix.bin.java
         kwargs = {'ignore_absent': False, 'backup': False, 'string': False}
         filter_file('^java', java, script, **kwargs)
         filter_file('trimmomatic.jar', join_path(prefix.bin, jar_file),
diff --git a/var/spack/repos/builtin/packages/trinity/package.py b/var/spack/repos/builtin/packages/trinity/package.py
index eab36f9ae4..5daad5e02f 100644
--- a/var/spack/repos/builtin/packages/trinity/package.py
+++ b/var/spack/repos/builtin/packages/trinity/package.py
@@ -23,7 +23,6 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ##############################################################################
 from spack import *
-from distutils.dir_util import copy_tree
 
 
 class Trinity(MakefilePackage):
@@ -59,7 +58,7 @@ def build(self, spec, prefix):
         make("plugins")
 
     def install(self, spec, prefix):
-        copy_tree('.', prefix.bin, preserve_symlinks=1)
+        install_tree('.', prefix.bin)
         force_remove(join_path(prefix.bin, '.gitmodules'))
         force_remove(join_path(prefix.bin, 'Butterfly', '.err'))
         force_remove(join_path(prefix.bin, 'Butterfly', 'src', '.classpath'))
diff --git a/var/spack/repos/builtin/packages/wannier90/package.py b/var/spack/repos/builtin/packages/wannier90/package.py
index 60dcd21fff..f8b7b9adfc 100644
--- a/var/spack/repos/builtin/packages/wannier90/package.py
+++ b/var/spack/repos/builtin/packages/wannier90/package.py
@@ -24,7 +24,6 @@
 ##############################################################################
 import inspect
 import os.path
-import shutil
 
 from spack import *
 
@@ -77,7 +76,7 @@ def edit(self, spec, prefix):
             'make.sys'
         )
 
-        shutil.copy(template, self.makefile_name)
+        copy(template, self.makefile_name)
         for key, value in substitutions.items():
             filter_file(key, value, self.makefile_name)
 
diff --git a/var/spack/repos/builtin/packages/workrave/package.py b/var/spack/repos/builtin/packages/workrave/package.py
index 76434acad3..a11fd9523e 100644
--- a/var/spack/repos/builtin/packages/workrave/package.py
+++ b/var/spack/repos/builtin/packages/workrave/package.py
@@ -23,7 +23,6 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ##############################################################################
 from spack import *
-from shutil import copyfile
 
 
 class Workrave(AutotoolsPackage):
@@ -102,4 +101,4 @@ def extra_m4(self):
         for fname in self.m4files:
             src = '%s/%s/%s.m4' % (self.stage.source_path, fname, fname)
             dest = '%s/m4/%s.m4' % (self.stage.source_path, fname)
-            copyfile(src, dest)
+            copy(src, dest)
diff --git a/var/spack/repos/builtin/packages/yorick/package.py b/var/spack/repos/builtin/packages/yorick/package.py
index 2f4a3a349e..63ba54761e 100644
--- a/var/spack/repos/builtin/packages/yorick/package.py
+++ b/var/spack/repos/builtin/packages/yorick/package.py
@@ -24,8 +24,6 @@
 ##############################################################################
 from spack import *
 import os
-import shutil
-import glob
 
 
 class Yorick(Package):
@@ -74,13 +72,4 @@ def install(self, spec, prefix):
         make()
         make("install")
 
-        try:
-            os.makedirs(prefix)
-        except OSError:
-            pass
-        os.chdir("relocate")
-        for f in glob.glob('*'):
-            if os.path.isdir(f):
-                shutil.copytree(f, os.path.join(prefix, f))
-            else:
-                shutil.copy2(f, os.path.join(prefix, f))
+        install_tree('relocate', prefix)
-- 
GitLab