summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorBrett Viren <bv@bnl.gov>2016-04-29 15:08:54 -0400
committerBrett Viren <bv@bnl.gov>2016-04-29 15:12:09 -0400
commita2b9a000dcb18868ea9f1c110632a251fbea92f3 (patch)
treeabca138900d14e73fbf6494f65bc13c5c4d59a05 /lib
parent49956faab97e2dea52371755d0fcb9b013567ac7 (diff)
downloadspack-a2b9a000dcb18868ea9f1c110632a251fbea92f3.tar.gz
spack-a2b9a000dcb18868ea9f1c110632a251fbea92f3.tar.bz2
spack-a2b9a000dcb18868ea9f1c110632a251fbea92f3.tar.xz
spack-a2b9a000dcb18868ea9f1c110632a251fbea92f3.zip
Add add removal and status actions in addition to link and add various ways to filter what is done.
Diffstat (limited to 'lib')
-rw-r--r--lib/spack/spack/cmd/view.py130
1 files changed, 107 insertions, 23 deletions
diff --git a/lib/spack/spack/cmd/view.py b/lib/spack/spack/cmd/view.py
index c7f1c21f67..dd698350b2 100644
--- a/lib/spack/spack/cmd/view.py
+++ b/lib/spack/spack/cmd/view.py
@@ -1,3 +1,8 @@
+'''
+Produce a file-system "view" of a Spack DAG.
+
+Concept from Nix, implemented by brett.viren@gmail.com ca 2016.
+'''
##############################################################################
# Copyright (c) 2013, Lawrence Livermore National Security, LLC.
# Produced at the Lawrence Livermore National Laboratory.
@@ -22,7 +27,9 @@
# 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 re
import argparse
import spack
@@ -34,8 +41,15 @@ description = "Produce a single-rooted directory view of a spec."
def setup_parser(subparser):
setup_parser.parser = subparser
- subparser.add_argument('prefix', nargs=1,
- help="Path to top-level directory for the view.")
+ subparser.add_argument('-e','--exclude', action='append', default=[],
+ help="exclude any packages which the given re pattern")
+ subparser.add_argument('-a','--action', default='link',
+ choices=['link','remove','status'], # names match action_*() functions below
+ help="what action to perform on the view")
+ subparser.add_argument('--no-dependencies', action='store_true', default=False,
+ help="just operate on named packages and do not follow dependencies")
+ subparser.add_argument('-p','--prefix', required=True,
+ help="Path to a top-level directory to receive the view.")
subparser.add_argument('specs', nargs=argparse.REMAINDER,
help="specs of packages to expose in the view.")
@@ -45,64 +59,134 @@ def assuredir(path):
os.makedirs(path)
def relative_to(prefix, path):
+ 'Return end of `path` relative to `prefix`'
assert 0 == path.find(prefix)
reldir = path[len(prefix):]
if reldir.startswith('/'):
reldir = reldir[1:]
return reldir
-def view_one(prefix, spec):
- print spec.name,spec.prefix
-
+
+def transform_path(spec, path, prefix=None):
+ 'Return the a relative path corresponding to given path spec.prefix'
+ if os.path.isabs(path):
+ path = relative_to(spec.prefix, path)
+ subdirs = path.split(os.path.sep)
+ if subdirs[0] == '.spack':
+ lst = ['.spack',spec.name]+subdirs[1:]
+ path = os.path.join(*lst)
+ if prefix:
+ path = os.path.join(prefix, path)
+ return path
+
+def action_status(spec, prefix):
+ 'Check status of view in prefix against spec'
dotspack = os.path.join(prefix, '.spack', spec.name)
-
if os.path.exists(os.path.join(dotspack)):
- tty.warn("Skipping previously added package %s"%spec.name)
+ tty.info("Package added: %s"%spec.name)
return
+ tty.info("Package missing: %s"%spec.name)
+ return
+def action_remove(spec, prefix):
+ 'Remove any files found in spec from prefix and purge empty directories.'
+
+ if not os.path.exists(prefix):
+ return
+
+ dotspack = transform_path(spec, '.spack', prefix)
+ if not os.path.exists(dotspack):
+ tty.info("Skipping nonexistent package %s"%spec.name)
+ return
for dirpath,dirnames,filenames in os.walk(spec.prefix):
if not filenames:
- continue # avoid explicitly making empty dirs
+ continue
- reldir = relative_to(spec.prefix, dirpath)
+ targdir = transform_path(spec, dirpath, prefix)
+ for fname in filenames:
+ src = os.path.join(dirpath, fname)
+ dst = os.path.join(targdir, fname)
+ if not os.path.exists(dst):
+ #tty.warn("Skipping nonexistent file for view: %s" % dst)
+ continue
+ os.unlink(dst)
- # fixme: assumes .spack has no subdirs
- if dirpath.endswith('.spack'):
- targdir = dotspack
- else:
- targdir = os.path.join(prefix, reldir)
+def action_link(spec, prefix):
+ 'Symlink all files in `spec` into directory `prefix`.'
+
+ dotspack = transform_path(spec, '.spack', prefix)
+ if os.path.exists(dotspack):
+ tty.warn("Skipping previously added package %s"%spec.name)
+ return
+
+ for dirpath,dirnames,filenames in os.walk(spec.prefix):
+ if not filenames:
+ continue # avoid explicitly making empty dirs
+
+ targdir = transform_path(spec, dirpath, prefix)
assuredir(targdir)
- print '\t%s...'%reldir,
- nlinks = 0
for fname in filenames:
src = os.path.join(dirpath, fname)
dst = os.path.join(targdir, fname)
if os.path.exists(dst):
+ if '.spack' in dst.split(os.path.sep):
+ continue # silence these
tty.warn("Skipping existing file for view: %s" % dst)
continue
os.symlink(src,dst)
- nlinks += 1
- print nlinks
-
+
+
+
+def purge_empty_directories(path):
+ 'Ascend up from the leaves accessible from `path` and remove empty directories.'
+ for dirpath, subdirs, files in os.walk(path, topdown=False):
+ for sd in subdirs:
+ sdp = os.path.join(dirpath,sd)
+ try:
+ os.rmdir(sdp)
+ except OSError:
+ tty.warn("Not removing directory with contents: %s" % sdp)
+
def view(parser, args):
- # if not args.specs:
- # tty.die("view creation requires at least one spec argument")
+ 'The view command.'
+ to_exclude = [re.compile(e) for e in args.exclude]
+ def exclude(spec):
+ for e in to_exclude:
+ if e.match(spec.name):
+ return True
+ return False
specs = spack.cmd.parse_specs(args.specs, normalize=True, concretize=True)
if not specs:
setup_parser.parser.print_help()
return 1
- prefix = args.prefix[0]
+ prefix = args.prefix
assuredir(prefix)
flat = set()
for spec in specs:
+ if args.no_dependencies:
+ flat.add(spec)
+ continue
flat.update(spec.normalized().traverse())
+ action = args.action.lower()
+ method = eval('action_' + action)
+
for spec in flat:
- view_one(prefix, spec)
+ if exclude(spec):
+ tty.info('Skipping excluded package: "%s"' % spec.name)
+ continue
+ if not os.path.exists(spec.prefix):
+ tty.warn('Skipping unknown package: %s in %s' % (spec.name, spec.prefix))
+ continue
+ tty.info("%s %s" % (action, spec.name))
+ method(spec, prefix)
+
+ if action in ['remove']:
+ purge_empty_directories(prefix)