(1) Added "spack spconfig" command.

(2) Neatened up the spconfig.py auto-generated file.
This commit is contained in:
citibeth 2016-03-13 00:13:00 -05:00
parent 90bb855ffa
commit 4236157823
2 changed files with 197 additions and 33 deletions

View File

@ -0,0 +1,97 @@
##############################################################################
# Copyright (c) 2016, Lawrence Livermore National Security, LLC.
# Produced at the Lawrence Livermore National Laboratory.
#
# This file is part of Spack.
# Written by Elizabeth Fischer
# LLNL-CODE-647188
#
# For details, see https://github.com/llnl/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
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 = "Create a configuration script and module, but don't build."
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(
'-q', '--quiet', action='store_true', dest='quiet',
help="Do not display verbose build output while installing.")
subparser.add_argument(
'spec', nargs=argparse.REMAINDER,
help="specs to use for install. Must contain package AND verison.")
def spconfig(self, args):
if not args.spec:
tty.die("spack spconfig requires a package spec argument.")
specs = spack.cmd.parse_specs(args.spec)
if len(specs) > 1:
tty.die("spack spconfig only takes one spec.")
# Take a write lock before checking for existence.
with spack.installed_db.write_transaction():
spec = specs[0]
if not spack.repo.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, spack.repo.first_repo(), None, True)
return
print('spec', spec)
if not spec.version.concrete:
tty.die("spack spconfig spec must have a single, concrete version.")
spec.concretize()
package = spack.repo.get(spec)
# It's OK if the package is already installed.
#if package.installed:
# tty.error("Already installed in %s" % package.prefix)
# tty.msg("Uninstall or try adding a version suffix for this SPCONFIG 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=True, # Don't remove install directory, even if you think you should
ignore_deps=args.ignore_deps,
verbose=not args.quiet,
keep_stage=True, # don't remove source dir for SPCONFIG.
install_phases = {'spconfig', 'provenance'})

View File

@ -45,6 +45,9 @@
from urlparse import urlparse, urljoin from urlparse import urlparse, urljoin
import textwrap import textwrap
from StringIO import StringIO from StringIO import StringIO
import shutil
import sys
import string
import llnl.util.tty as tty import llnl.util.tty as tty
from llnl.util.tty.log import log_output from llnl.util.tty.log import log_output
@ -68,6 +71,7 @@
from spack.util.compression import allowed_archive, extension from spack.util.compression import allowed_archive, extension
from spack.util.executable import ProcessError, which from spack.util.executable import ProcessError, which
from spack.util.environment import dump_environment from spack.util.environment import dump_environment
from spack import directory_layout
"""Allowed URL schemes for spack packages.""" """Allowed URL schemes for spack packages."""
_ALLOWED_URL_SCHEMES = ["http", "https", "ftp", "file", "git"] _ALLOWED_URL_SCHEMES = ["http", "https", "ftp", "file", "git"]
@ -827,7 +831,7 @@ def _resource_stage(self, resource):
def do_install(self, def do_install(self,
keep_prefix=False, keep_stage=False, ignore_deps=False, keep_prefix=False, keep_stage=False, ignore_deps=False,
skip_patch=False, verbose=False, make_jobs=None, fake=False, skip_patch=False, verbose=False, make_jobs=None, fake=False,
install_phases = {'spconfig', 'configure', 'build', 'install'}): install_phases = {'configure', 'build', 'install', 'provenance'}):
"""Called by commands to install a package and its dependencies. """Called by commands to install a package and its dependencies.
Package implementations should override install() to describe Package implementations should override install() to describe
@ -853,7 +857,7 @@ def do_install(self,
return return
# Ensure package is not already installed # Ensure package is not already installed
if spack.install_layout.check_installed(self.spec): if 'install' in install_phases and spack.install_layout.check_installed(self.spec):
tty.msg("%s is already installed in %s" % (self.name, self.prefix)) tty.msg("%s is already installed in %s" % (self.name, self.prefix))
return return
@ -895,35 +899,46 @@ def build_process():
self.do_fake_install() self.do_fake_install()
else: else:
# Do the real install in the source directory. # Do the real install in the source directory.
self.stage.chdir_to_source() self.stage.chdir_to_source()
# Save the build environment in a file before building. # Save the build environment in a file before building.
env_path = join_path(os.getcwd(), 'spack-build.env') env_path = join_path(os.getcwd(), 'spack-build.env')
try: try:
# Redirect I/O to a build log (and optionally to the terminal) # Redirect I/O to a build log (and optionally to the terminal)
log_path = join_path(os.getcwd(), 'spack-build.out') log_path = join_path(os.getcwd(), 'spack-build.out')
log_file = open(log_path, 'w') log_file = open(log_path, 'w')
with log_output(log_file, verbose, sys.stdout.isatty(), True): with log_output(log_file, verbose, sys.stdout.isatty(), True):
dump_environment(env_path) dump_environment(env_path)
self.install(self.spec, self.prefix) self.install(self.spec, self.prefix)
except ProcessError as e: except ProcessError as e:
# Annotate ProcessErrors with the location of the build log. # Annotate ProcessErrors with the location of the build log.
e.build_log = log_path e.build_log = log_path
raise e raise e
# Ensure that something was actually installed. # Ensure that something was actually installed.
self._sanity_check_install() if 'install' in self.install_phases:
self._sanity_check_install()
# Copy provenance into the install directory on success
log_install_path = spack.install_layout.build_log_path(self.spec)
env_install_path = spack.install_layout.build_env_path(self.spec)
packages_dir = spack.install_layout.build_packages_path(self.spec)
install(log_path, log_install_path) # Copy provenance into the install directory on success
install(env_path, env_install_path) if 'provenance' in self.install_phases:
dump_packages(self.spec, packages_dir)
log_install_path = spack.install_layout.build_log_path(self.spec)
env_install_path = spack.install_layout.build_env_path(self.spec)
packages_dir = spack.install_layout.build_packages_path(self.spec)
# Remove first if we're overwriting another build
# (can happen with spack spconfig)
try:
shutil.rmtree(packages_dir) # log_install_path and env_install_path are inside this
except:
pass
install(log_path, log_install_path)
install(env_path, env_install_path)
dump_packages(self.spec, packages_dir)
# Stop timer. # Stop timer.
self._total_time = time.time() - start_time self._total_time = time.time() - start_time
@ -937,16 +952,29 @@ def build_process():
try: try:
# Create the install prefix and fork the build process. # Create the install prefix and fork the build process.
spack.install_layout.create_install_directory(self.spec) spack.install_layout.create_install_directory(self.spec)
except directory_layout.InstallDirectoryAlreadyExistsError:
if 'install' in install_phases:
# Abort install if install directory exists.
# But do NOT remove it (you'd be overwriting someon else's stuff)
tty.warn("Keeping existing install prefix in place.")
raise
else:
# We're not installing anyway, so don't worry if someone
# else has already written in the install directory
pass
try:
spack.build_environment.fork(self, build_process) spack.build_environment.fork(self, build_process)
except: except:
# remove the install prefix if anything went wrong during install. # remove the install prefix if anything went wrong during install.
if not keep_prefix: if keep_prefix:
self.remove_prefix()
else:
tty.warn("Keeping install prefix in place despite error.", tty.warn("Keeping install prefix in place despite error.",
"Spack will think this package is installed. " + "Spack will think this package is installed. " +
"Manually remove this directory to fix:", "Manually remove this directory to fix:",
self.prefix, wrap=True) self.prefix, wrap=True)
else:
self.remove_prefix()
raise raise
# note: PARENT of the build process adds the new package to # note: PARENT of the build process adds the new package to
@ -1333,6 +1361,12 @@ def install(self, spec, prefix):
with open(os.path.join(prefix, 'dummy'), 'w') as fout: with open(os.path.join(prefix, 'dummy'), 'w') as fout:
pass pass
# stackoverflow.com/questions/12791997/how-do-you-do-a-simple-chmod-x-from-within-python
def make_executable(path):
mode = os.stat(path).st_mode
mode |= (mode & 0o444) >> 2 # copy R bits to X
os.chmod(path, mode)
class CMakePackage(StagedPackage): class CMakePackage(StagedPackage):
@ -1364,11 +1398,44 @@ def install_spconfig(self):
env['CMAKE_TRANSITIVE_INCLUDE_PATH'] = self.cmake_transitive_include_path() env['CMAKE_TRANSITIVE_INCLUDE_PATH'] = self.cmake_transitive_include_path()
env['CMAKE_PREFIX_PATH'] = os.environ['CMAKE_PREFIX_PATH'] env['CMAKE_PREFIX_PATH'] = os.environ['CMAKE_PREFIX_PATH']
with open('spconfig.py', 'w') as fout: spconfig_fname = 'spconfig.py'
fout.write('import sys\nimport os\nimport subprocess\n') with open(spconfig_fname, 'w') as fout:
fout.write('env = {}\n'.format(repr(env))) fout.write(\
fout.write('cmd = {} + sys.argv[1:]\n'.format(repr(cmd))) r"""#!{}
fout.write('proc = subprocess.Popen(cmd, env=env)\nproc.wait()\n') #
import sys
import os
import subprocess
def cmdlist(str):
return list(x.strip().replace("'",'') for x in str.split('\n') if x)
env = dict()
""".format(sys.executable))
env_vars = sorted(list(env.keys()))
for name in env_vars:
val = env[name]
if string.find(name, 'PATH') < 0:
fout.write('env[{}] = {}\n'.format(repr(name),repr(val)))
else:
if name == 'CMAKE_TRANSITIVE_INCLUDE_PATH':
sep = ';'
else:
sep = ':'
fout.write('env[{}] = "{}".join(cmdlist("""\n'.format(repr(name),sep))
for part in string.split(val, sep):
fout.write(' {}\n'.format(part))
fout.write('"""))\n')
fout.write('\ncmd = cmdlist("""\n')
fout.write('{}\n'.format(cmd[0]))
for arg in cmd[1:]:
fout.write(' {}\n'.format(arg))
fout.write('""") + sys.argv[1:]\n')
fout.write('\nproc = subprocess.Popen(cmd, env=env)\nproc.wait()\n')
make_executable(spconfig_fname)
def install_configure(self): def install_configure(self):