diff --git a/lib/spack/spack/cmd/diy.py b/lib/spack/spack/cmd/diy.py new file mode 100644 index 0000000000..6e7f10fba6 --- /dev/null +++ b/lib/spack/spack/cmd/diy.py @@ -0,0 +1,93 @@ +############################################################################## +# Copyright (c) 2013, Lawrence Livermore National Security, LLC. +# Produced at the Lawrence Livermore National Laboratory. +# +# This file is part of Spack. +# Written by Todd Gamblin, tgamblin@llnl.gov, All rights reserved. +# LLNL-CODE-647188 +# +# For details, see https://scalability-llnl.github.io/spack +# Please also see the LICENSE file 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 General Public License (as published by +# the Free Software Foundation) version 2.1 dated 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 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 sys +import os +from external import argparse + +import llnl.util.tty as tty + +import spack +import spack.cmd +from spack.cmd.edit import edit_package +from spack.stage import DIYStage + +description = "Do-It-Yourself: build from an existing source directory." + +def setup_parser(subparser): + subparser.add_argument( + '-i', '--ignore-dependencies', action='store_true', dest='ignore_deps', + help="Do not try to install dependencies of requested packages.") + subparser.add_argument( + '--keep-prefix', action='store_true', + help="Don't remove the install prefix if installation fails.") + subparser.add_argument( + '--skip-patch', action='store_true', + help="Skip patching for the DIY build.") + subparser.add_argument( + 'spec', nargs=argparse.REMAINDER, + help="specs to use for install. Must contain package AND verison.") + + +def diy(self, args): + if not args.spec: + tty.die("spack diy requires a package spec argument.") + + specs = spack.cmd.parse_specs(args.spec) + if len(specs) > 1: + tty.die("spack diy only takes one spec.") + + spec = specs[0] + if not spack.db.exists(spec.name): + tty.warn("No such package: %s" % spec.name) + create = tty.get_yes_or_no("Create this package?", default=False) + if not create: + tty.msg("Exiting without creating.") + sys.exit(1) + else: + tty.msg("Running 'spack edit -f %s'" % spec.name) + edit_package(spec.name, True) + return + + if not spec.version.concrete: + tty.die("spack diy spec must have a single, concrete version.") + + spec.concretize() + package = spack.db.get(spec) + + if package.installed: + tty.error("Already installed in %s" % package.prefix) + tty.msg("Uninstall or try adding a version suffix for this DIY build.") + sys.exit(1) + + # Forces the build to run out of the current directory. + package.stage = DIYStage(os.getcwd()) + + # TODO: make this an argument, not a global. + spack.do_checksum = False + + package.do_install( + keep_prefix=args.keep_prefix, + ignore_deps=args.ignore_deps, + keep_stage=True) # don't remove source dir for DIY. diff --git a/lib/spack/spack/cmd/edit.py b/lib/spack/spack/cmd/edit.py index 842dcd7faf..c4225a7dd3 100644 --- a/lib/spack/spack/cmd/edit.py +++ b/lib/spack/spack/cmd/edit.py @@ -24,7 +24,6 @@ ############################################################################## import os import string -from contextlib import closing import llnl.util.tty as tty from llnl.util.filesystem import mkdirp, join_path @@ -54,6 +53,27 @@ def install(self, spec, prefix): """) +def edit_package(name, force=False): + path = spack.db.filename_for_package_name(name) + + if os.path.exists(path): + if not os.path.isfile(path): + tty.die("Something's wrong. '%s' is not a file!" % path) + if not os.access(path, os.R_OK|os.W_OK): + tty.die("Insufficient permissions on '%s'!" % path) + elif not force: + tty.die("No package '%s'. Use spack create, or supply -f/--force " + "to edit a new file." % name) + else: + mkdirp(os.path.dirname(path)) + with open(path, "w") as pkg_file: + pkg_file.write( + package_template.substitute( + name=name, class_name=mod_to_class(name))) + + spack.editor(path) + + def setup_parser(subparser): subparser.add_argument( '-f', '--force', dest='force', action='store_true', @@ -80,22 +100,7 @@ def edit(parser, args): # By default open the directory where packages or commands live. if not name: path = spack.packages_path + spack.editor(path) else: - path = spack.db.filename_for_package_name(name) + edit_package(name, args.force) - if os.path.exists(path): - if not os.path.isfile(path): - tty.die("Something's wrong. '%s' is not a file!" % path) - if not os.access(path, os.R_OK|os.W_OK): - tty.die("Insufficient permissions on '%s'!" % path) - elif not args.force: - tty.die("No package '%s'. Use spack create, or supply -f/--force " - "to edit a new file." % name) - else: - mkdirp(os.path.dirname(path)) - with closing(open(path, "w")) as pkg_file: - pkg_file.write( - package_template.substitute(name=name, class_name=mod_to_class(name))) - - # If everything checks out, go ahead and edit. - spack.editor(path) diff --git a/lib/spack/spack/cmd/install.py b/lib/spack/spack/cmd/install.py index 11b50f8fcf..820444a404 100644 --- a/lib/spack/spack/cmd/install.py +++ b/lib/spack/spack/cmd/install.py @@ -22,7 +22,6 @@ # along with this program; if not, write to the Free Software Foundation, # Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ############################################################################## -import sys from external import argparse import llnl.util.tty as tty @@ -64,7 +63,7 @@ def install(parser, args): tty.die("The -j option must be a positive integer!") if args.no_checksum: - spack.do_checksum = False + spack.do_checksum = False # TODO: remove this global. specs = spack.cmd.parse_specs(args.packages, concretize=True) for spec in specs: diff --git a/lib/spack/spack/package.py b/lib/spack/spack/package.py index 908fd86a87..7d9eca5077 100644 --- a/lib/spack/spack/package.py +++ b/lib/spack/spack/package.py @@ -481,6 +481,12 @@ def stage(self): return self._stage + @stage.setter + def stage(self, stage): + """Allow a stage object to be set to override the default.""" + self._stage = stage + + @property def fetcher(self): if not self.spec.versions.concrete: @@ -787,6 +793,7 @@ def do_install(self, **kwargs): keep_stage = kwargs.get('keep_stage', False) ignore_deps = kwargs.get('ignore_deps', False) fake_install = kwargs.get('fake', False) + skip_patch = kwargs.get('skip_patch', False) # Override builtin number of make jobs. self.make_jobs = kwargs.get('make_jobs', None) @@ -805,7 +812,10 @@ def do_install(self, **kwargs): start_time = time.time() if not fake_install: - self.do_patch() + if not skip_patch: + self.do_patch() + else: + self.do_stage() # create the install directory. The install layout # handles this in case so that it can use whatever diff --git a/lib/spack/spack/stage.py b/lib/spack/spack/stage.py index 84454c9d2c..d451743508 100644 --- a/lib/spack/spack/stage.py +++ b/lib/spack/spack/stage.py @@ -309,6 +309,39 @@ def destroy(self): os.chdir(os.path.dirname(self.path)) +class DIYStage(object): + """Simple class that allows any directory to be a spack stage.""" + def __init__(self, path): + self.archive_file = None + self.path = path + self.source_path = path + + def chdir(self): + if os.path.isdir(self.path): + os.chdir(self.path) + else: + tty.die("Setup failed: no such directory: " + self.path) + + def chdir_to_source(self): + self.chdir() + + def fetch(self): + tty.msg("No need to fetch for DIY.") + + def check(self): + tty.msg("No checksum needed for DIY.") + + def expand_archive(self): + tty.msg("Using source directory: %s" % self.source_path) + + def restage(self): + tty.die("Cannot restage DIY stage.") + + def destroy(self): + # No need to destroy DIY stage. + pass + + def _get_mirrors(): """Get mirrors from spack configuration.""" config = spack.config.get_config()