Merge pull request #1186 from epfl-scitas/features/install_with_phases

do_install : allow for an arbitrary number of phases
This commit is contained in:
Todd Gamblin
2016-10-24 17:13:49 -07:00
committed by GitHub
26 changed files with 1248 additions and 678 deletions

View File

@@ -24,11 +24,11 @@
##############################################################################
"""Utility classes for logging the output of blocks of code.
"""
import sys
import multiprocessing
import os
import re
import select
import inspect
import sys
import llnl.util.tty as tty
import llnl.util.tty.color as color
@@ -100,25 +100,29 @@ def __exit__(self, exc_type, exception, traceback):
class log_output(object):
"""Redirects output and error of enclosed block to a file.
"""Spawns a daemon that reads from a pipe and writes to a file
Usage:
with log_output(open('logfile.txt', 'w')):
# do things ... output will be logged.
# Spawns the daemon
with log_output('logfile.txt', 'w') as log_redirection:
# do things ... output is not redirected
with log_redirection:
# do things ... output will be logged
or:
with log_output(open('logfile.txt', 'w'), echo=True):
# do things ... output will be logged
# and also printed to stdout.
with log_output('logfile.txt', echo=True) as log_redirection:
# do things ... output is not redirected
with log_redirection:
# do things ... output will be logged
# and also printed to stdout.
Closes the provided stream when done with the block.
If echo is True, also prints the output to stdout.
Opens a stream in 'w' mode at daemon spawning and closes it at
daemon joining. If echo is True, also prints the output to stdout.
"""
def __init__(self, stream, echo=False, force_color=False, debug=False):
self.stream = stream
# various output options
def __init__(self, filename, echo=False, force_color=False, debug=False):
self.filename = filename
# Various output options
self.echo = echo
self.force_color = force_color
self.debug = debug
@@ -126,70 +130,81 @@ def __init__(self, stream, echo=False, force_color=False, debug=False):
# Default is to try file-descriptor reassignment unless the system
# out/err streams do not have an associated file descriptor
self.directAssignment = False
self.read, self.write = os.pipe()
def trace(self, frame, event, arg):
"""Jumps to __exit__ on the child process."""
raise _SkipWithBlock()
# Sets a daemon that writes to file what it reads from a pipe
self.p = multiprocessing.Process(
target=self._spawn_writing_daemon,
args=(self.read,),
name='logger_daemon'
)
self.p.daemon = True
# Needed to un-summon the daemon
self.parent_pipe, self.child_pipe = multiprocessing.Pipe()
def __enter__(self):
"""Redirect output from the with block to a file.
self.p.start()
return log_output.OutputRedirection(self)
This forks the with block as a separate process, with stdout
and stderr redirected back to the parent via a pipe. If
echo is set, also writes to standard out.
def __exit__(self, exc_type, exc_val, exc_tb):
self.parent_pipe.send(True)
self.p.join(60.0) # 1 minute to join the child
"""
# remember these values for later.
self._force_color = color._force_color
self._debug = tty._debug
def _spawn_writing_daemon(self, read):
# Parent: read from child, skip the with block.
read_file = os.fdopen(read, 'r', 0)
with open(self.filename, 'w') as log_file:
with keyboard_input(sys.stdin):
while True:
rlist, _, _ = select.select([read_file, sys.stdin], [], [])
if not rlist:
break
read, write = os.pipe()
# Allow user to toggle echo with 'v' key.
# Currently ignores other chars.
if sys.stdin in rlist:
if sys.stdin.read(1) == 'v':
self.echo = not self.echo
self.pid = os.fork()
if self.pid:
# Parent: read from child, skip the with block.
os.close(write)
read_file = os.fdopen(read, 'r', 0)
with self.stream as log_file:
with keyboard_input(sys.stdin):
while True:
rlist, w, x = select.select(
[read_file, sys.stdin], [], [])
if not rlist:
# Handle output from the with block process.
if read_file in rlist:
line = read_file.readline()
if not line:
# For some reason we never reach this point...
break
# Allow user to toggle echo with 'v' key.
# Currently ignores other chars.
if sys.stdin in rlist:
if sys.stdin.read(1) == 'v':
self.echo = not self.echo
# Echo to stdout if requested.
if self.echo:
sys.stdout.write(line)
# handle output from the with block process.
if read_file in rlist:
line = read_file.readline()
if not line:
break
# Stripped output to log file.
log_file.write(_strip(line))
log_file.flush()
# Echo to stdout if requested.
if self.echo:
sys.stdout.write(line)
if self.child_pipe.poll():
break
# Stripped output to log file.
log_file.write(_strip(line))
def __del__(self):
"""Closes the pipes"""
os.close(self.write)
os.close(self.read)
read_file.flush()
read_file.close()
class OutputRedirection(object):
# Set a trace function to skip the with block.
sys.settrace(lambda *args, **keys: None)
frame = inspect.currentframe(1)
frame.f_trace = self.trace
def __init__(self, other):
self.__dict__.update(other.__dict__)
else:
# Child: redirect output, execute the with block.
os.close(read)
def __enter__(self):
"""Redirect output from the with block to a file.
Hijacks stdout / stderr and writes to the pipe
connected to the logger daemon
"""
# remember these values for later.
self._force_color = color._force_color
self._debug = tty._debug
# Redirect this output to a pipe
write = self.write
try:
# Save old stdout and stderr
self._stdout = os.dup(sys.stdout.fileno())
@@ -205,53 +220,26 @@ def __enter__(self):
output_redirect = os.fdopen(write, 'w')
sys.stdout = output_redirect
sys.stderr = output_redirect
if self.force_color:
color._force_color = True
if self.debug:
tty._debug = True
def __exit__(self, exc_type, exception, traceback):
"""Exits on child, handles skipping the with block on parent."""
# Child should just exit here.
if self.pid == 0:
def __exit__(self, exc_type, exception, traceback):
"""Plugs back the original file descriptors
for stdout and stderr
"""
# Flush the log to disk.
sys.stdout.flush()
sys.stderr.flush()
if exception:
# Restore stdout on the child if there's an exception,
# and let it be raised normally.
#
# This assumes that even if the exception is caught,
# the child will exit with a nonzero return code. If
# it doesn't, the child process will continue running.
#
# TODO: think about how this works outside install.
# TODO: ideally would propagate exception to parent...
if self.directAssignment:
sys.stdout = self._stdout
sys.stderr = self._stderr
else:
os.dup2(self._stdout, sys.stdout.fileno())
os.dup2(self._stderr, sys.stderr.fileno())
return False
if self.directAssignment:
# We seem to need this only to pass test/install.py
sys.stdout = self._stdout
sys.stderr = self._stderr
else:
# Die quietly if there was no exception.
os._exit(0)
os.dup2(self._stdout, sys.stdout.fileno())
os.dup2(self._stderr, sys.stderr.fileno())
else:
# If the child exited badly, parent also should exit.
pid, returncode = os.waitpid(self.pid, 0)
if returncode != 0:
os._exit(1)
# restore output options.
color._force_color = self._force_color
tty._debug = self._debug
# Suppresses exception if it's our own.
return exc_type is _SkipWithBlock
# restore output options.
color._force_color = self._force_color
tty._debug = self._debug

View File

@@ -186,10 +186,19 @@
# packages should live. This file is overloaded for spack core vs.
# for packages.
#
__all__ = ['Package', 'StagedPackage', 'CMakePackage',
'Version', 'when', 'ver', 'alldeps', 'nolink']
__all__ = ['Package',
'CMakePackage',
'AutotoolsPackage',
'MakefilePackage',
'Version',
'when',
'ver',
'alldeps',
'nolink']
from spack.package import Package, ExtensionConflictError
from spack.package import StagedPackage, CMakePackage
from spack.build_systems.makefile import MakefilePackage
from spack.build_systems.autotools import AutotoolsPackage
from spack.build_systems.cmake import CMakePackage
from spack.version import Version, ver
from spack.spec import DependencySpec, alldeps, nolink
from spack.multimethod import when

View File

@@ -51,16 +51,14 @@
Skimming this module is a nice way to get acquainted with the types of
calls you can make from within the install() function.
"""
import os
import sys
import shutil
import multiprocessing
import platform
import os
import shutil
import sys
import llnl.util.tty as tty
from llnl.util.filesystem import *
import spack
from llnl.util.filesystem import *
from spack.environment import EnvironmentModifications, validate
from spack.util.environment import *
from spack.util.executable import Executable, which
@@ -351,8 +349,8 @@ def set_module_variables_for_package(pkg, module):
m.cmake = Executable('cmake')
m.ctest = Executable('ctest')
# standard CMake arguments
m.std_cmake_args = get_std_cmake_args(pkg)
# Standard CMake arguments
m.std_cmake_args = spack.CMakePackage._std_args(pkg)
# Put spack compiler paths in module scope.
link_dir = spack.build_env_path
@@ -522,41 +520,26 @@ def child_fun():
carries on.
"""
try:
pid = os.fork()
except OSError as e:
raise InstallError("Unable to fork build process: %s" % e)
if pid == 0:
# Give the child process the package's build environment.
setup_package(pkg, dirty=dirty)
def child_execution(child_connection):
try:
# call the forked function.
setup_package(pkg, dirty=dirty)
function()
child_connection.send([None, None, None])
except Exception as e:
child_connection.send([type(e), e, None])
finally:
child_connection.close()
# Use os._exit here to avoid raising a SystemExit exception,
# which interferes with unit tests.
os._exit(0)
except spack.error.SpackError as e:
e.die()
except:
# Child doesn't raise or return to main spack code.
# Just runs default exception handler and exits.
sys.excepthook(*sys.exc_info())
os._exit(1)
else:
# Parent process just waits for the child to complete. If the
# child exited badly, assume it already printed an appropriate
# message. Just make the parent exit with an error code.
pid, returncode = os.waitpid(pid, 0)
if returncode != 0:
message = "Installation process had nonzero exit code : {code}"
strcode = str(returncode)
raise InstallError(message.format(code=strcode))
parent_connection, child_connection = multiprocessing.Pipe()
p = multiprocessing.Process(
target=child_execution,
args=(child_connection,)
)
p.start()
exc_type, exception, traceback = parent_connection.recv()
p.join()
if exception is not None:
raise exception
class InstallError(spack.error.SpackError):

View File

@@ -0,0 +1,106 @@
##############################################################################
# Copyright (c) 2013-2016, 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/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 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 inspect
import os.path
import llnl.util.tty as tty
from spack.package import PackageBase
class AutotoolsPackage(PackageBase):
"""Specialized class for packages that are built using GNU Autotools
This class provides four phases that can be overridden:
- autoreconf
- configure
- build
- install
They all have sensible defaults and for many packages the only thing
necessary will be to override `configure_args`
"""
phases = ['autoreconf', 'configure', 'build', 'install']
# To be used in UI queries that require to know which
# build-system class we are using
build_system_class = 'AutotoolsPackage'
def autoreconf(self, spec, prefix):
"""Not needed usually, configure should be already there"""
pass
@PackageBase.sanity_check('autoreconf')
def is_configure_or_die(self):
"""Checks the presence of a `configure` file after the
autoreconf phase"""
if not os.path.exists('configure'):
raise RuntimeError(
'configure script not found in {0}'.format(os.getcwd()))
def configure_args(self):
"""Method to be overridden. Should return an iterable containing
all the arguments that must be passed to configure, except --prefix
"""
return []
def configure(self, spec, prefix):
"""Runs configure with the arguments specified in `configure_args`
and an appropriately set prefix
"""
options = ['--prefix={0}'.format(prefix)] + self.configure_args()
inspect.getmodule(self).configure(*options)
def build(self, spec, prefix):
"""The usual `make` after configure"""
inspect.getmodule(self).make()
def install(self, spec, prefix):
"""...and the final `make install` after configure"""
inspect.getmodule(self).make('install')
@PackageBase.sanity_check('build')
@PackageBase.on_package_attributes(run_tests=True)
def _run_default_function(self):
"""This function is run after build if self.run_tests == True
It will search for a method named `check` and run it. A sensible
default is provided in the base class.
"""
try:
fn = getattr(self, 'check')
tty.msg('Trying default sanity checks [check]')
fn()
except AttributeError:
tty.msg('Skipping default sanity checks [method `check` not implemented]') # NOQA: ignore=E501
def check(self):
"""Default test : search the Makefile for targets `test` and `check`
and run them if found.
"""
self._if_make_target_execute('test')
self._if_make_target_execute('check')
# Check that self.prefix is there after installation
PackageBase.sanity_check('install')(PackageBase.sanity_check_prefix)

View File

@@ -0,0 +1,143 @@
##############################################################################
# Copyright (c) 2013-2016, 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/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 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 inspect
import os
import platform
import llnl.util.tty as tty
import spack.build_environment
from llnl.util.filesystem import working_dir, join_path
from spack.package import PackageBase
class CMakePackage(PackageBase):
"""Specialized class for packages that are built using cmake
This class provides three phases that can be overridden:
- cmake
- build
- install
They all have sensible defaults and for many packages the only thing
necessary will be to override `cmake_args`
"""
phases = ['cmake', 'build', 'install']
# To be used in UI queries that require to know which
# build-system class we are using
build_system_class = 'CMakePackage'
def build_type(self):
"""Override to provide the correct build_type in case a complex
logic is needed
"""
return 'RelWithDebInfo'
def root_cmakelists_dir(self):
"""Directory where to find the root CMakeLists.txt"""
return self.stage.source_path
@property
def std_cmake_args(self):
"""Standard cmake arguments provided as a property for
convenience of package writers
"""
# standard CMake arguments
return CMakePackage._std_args(self)
@staticmethod
def _std_args(pkg):
"""Computes the standard cmake arguments for a generic package"""
try:
build_type = pkg.build_type()
except AttributeError:
build_type = 'RelWithDebInfo'
args = ['-DCMAKE_INSTALL_PREFIX:PATH={0}'.format(pkg.prefix),
'-DCMAKE_BUILD_TYPE:STRING={0}'.format(build_type),
'-DCMAKE_VERBOSE_MAKEFILE:BOOL=ON']
if platform.mac_ver()[0]:
args.append('-DCMAKE_FIND_FRAMEWORK:STRING=LAST')
# Set up CMake rpath
args.append('-DCMAKE_INSTALL_RPATH_USE_LINK_PATH:BOOL=FALSE')
rpaths = ':'.join(spack.build_environment.get_rpaths(pkg))
args.append('-DCMAKE_INSTALL_RPATH:STRING={0}'.format(rpaths))
return args
def build_directory(self):
"""Override to provide another place to build the package"""
return join_path(self.stage.source_path, 'spack-build')
def cmake_args(self):
"""Method to be overridden. Should return an iterable containing
all the arguments that must be passed to configure, except:
- CMAKE_INSTALL_PREFIX
- CMAKE_BUILD_TYPE
"""
return []
def cmake(self, spec, prefix):
"""Run cmake in the build directory"""
options = [self.root_cmakelists_dir()] + self.std_cmake_args + \
self.cmake_args()
create = not os.path.exists(self.build_directory())
with working_dir(self.build_directory(), create=create):
inspect.getmodule(self).cmake(*options)
def build(self, spec, prefix):
"""The usual `make` after cmake"""
with working_dir(self.build_directory()):
inspect.getmodule(self).make()
def install(self, spec, prefix):
"""...and the final `make install` after cmake"""
with working_dir(self.build_directory()):
inspect.getmodule(self).make('install')
@PackageBase.sanity_check('build')
@PackageBase.on_package_attributes(run_tests=True)
def _run_default_function(self):
"""This function is run after build if self.run_tests == True
It will search for a method named `check` and run it. A sensible
default is provided in the base class.
"""
try:
fn = getattr(self, 'check')
tty.msg('Trying default build sanity checks [check]')
fn()
except AttributeError:
tty.msg('Skipping default build sanity checks [method `check` not implemented]') # NOQA: ignore=E501
def check(self):
"""Default test : search the Makefile for the target `test`
and run them if found.
"""
with working_dir(self.build_directory()):
self._if_make_target_execute('test')
# Check that self.prefix is there after installation
PackageBase.sanity_check('install')(PackageBase.sanity_check_prefix)

View File

@@ -0,0 +1,77 @@
##############################################################################
# Copyright (c) 2013-2016, 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/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 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 inspect
from llnl.util.filesystem import working_dir
from spack.package import PackageBase
class MakefilePackage(PackageBase):
"""Specialized class for packages that are built using editable Makefiles
This class provides three phases that can be overridden:
- edit
- build
- install
It is necessary to override the 'edit' phase, while 'build' and 'install'
have sensible defaults.
"""
phases = ['edit', 'build', 'install']
# To be used in UI queries that require to know which
# build-system class we are using
build_system_class = 'MakefilePackage'
def build_directory(self):
"""Directory where the main Makefile is located"""
return self.stage.source_path
def build_args(self):
"""List of arguments that should be passed to make at build time"""
return []
def install_args(self):
"""List of arguments that should be passed to make at install time"""
return []
def edit(self, spec, prefix):
"""This phase cannot be defaulted for obvious reasons..."""
raise NotImplementedError('\'edit\' function not implemented')
def build(self, spec, prefix):
"""Default build phase : call make passing build_args"""
args = self.build_args()
with working_dir(self.build_directory()):
inspect.getmodule(self).make(*args)
def install(self, spec, prefix):
"""Default install phase : call make passing install_args"""
args = self.install_args() + ['install']
with working_dir(self.build_directory()):
inspect.getmodule(self).make(*args)
# Check that self.prefix is there after installation
PackageBase.sanity_check('install')(PackageBase.sanity_check_prefix)

View File

@@ -0,0 +1,43 @@
##############################################################################
# Copyright (c) 2013-2016, 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/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 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 spack.cmd.configure as cfg
from spack import *
description = 'Stops at build stage when installing a package, if possible'
build_system_to_phase = {
CMakePackage: 'build',
AutotoolsPackage: 'build'
}
def setup_parser(subparser):
cfg.setup_parser(subparser)
def build(parser, args):
cfg._stop_at_phase_during_install(args, build, build_system_to_phase)

View File

@@ -0,0 +1,90 @@
##############################################################################
# Copyright (c) 2013-2016, 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/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 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 argparse
import llnl.util.tty as tty
import spack.cmd
import spack.cmd.install as inst
from spack import *
description = 'Stops at configuration stage when installing a package, if possible' # NOQA: ignore=E501
build_system_to_phase = {
CMakePackage: 'cmake',
AutotoolsPackage: 'configure'
}
def setup_parser(subparser):
subparser.add_argument(
'package',
nargs=argparse.REMAINDER,
help="spec of the package to install"
)
subparser.add_argument(
'-v', '--verbose',
action='store_true',
help="Print additional output during builds"
)
def _stop_at_phase_during_install(args, calling_fn, phase_mapping):
if not args.package:
tty.die("configure requires at least one package argument")
# TODO: to be refactored with code in install
specs = spack.cmd.parse_specs(args.package, concretize=True)
if len(specs) != 1:
tty.error('only one spec can be installed at a time.')
spec = specs.pop()
pkg = spec.package
try:
key = [cls for cls in phase_mapping if isinstance(pkg, cls)].pop()
phase = phase_mapping[key]
# Install package dependencies if needed
parser = argparse.ArgumentParser()
inst.setup_parser(parser)
tty.msg('Checking dependencies for {0}'.format(args.package))
cli_args = ['-v'] if args.verbose else []
install_args = parser.parse_args(cli_args + ['--only=dependencies'])
install_args.package = args.package
inst.install(parser, install_args)
# Install package and stop at the given phase
cli_args = ['-v'] if args.verbose else []
install_args = parser.parse_args(cli_args + ['--only=package'])
install_args.package = args.package
inst.install(parser, install_args, stop_at=phase)
except IndexError:
tty.error(
'Package {0} has no {1} phase, or its {1} phase is not separated from install'.format( # NOQA: ignore=E501
spec.name, calling_fn.__name__)
)
def configure(parser, args):
_stop_at_phase_during_install(args, configure, build_system_to_phase)

View File

@@ -22,25 +22,24 @@
# 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 string
from __future__ import print_function
import os
import re
import string
from ordereddict_backport import OrderedDict
import llnl.util.tty as tty
from llnl.util.filesystem import mkdirp
import spack
import spack.cmd
import spack.cmd.checksum
import spack.url
import spack.util.web
from spack.spec import Spec
from spack.util.naming import *
from llnl.util.filesystem import mkdirp
from ordereddict_backport import OrderedDict
from spack.repository import Repo, RepoError
from spack.spec import Spec
from spack.util.executable import which
from spack.util.naming import *
description = "Create a new package file from an archive URL"
@@ -87,7 +86,7 @@
from spack import *
class ${class_name}(Package):
class ${class_name}(${base_class_name}):
""\"FIXME: Put a proper description of your package here.""\"
# FIXME: Add a proper url for your package's homepage here.
@@ -98,109 +97,160 @@ class ${class_name}(Package):
${dependencies}
def install(self, spec, prefix):
${install}
${body}
""")
# Build dependencies and extensions
dependencies_dict = {
'autotools': """\
class DefaultGuess(object):
"""Provides the default values to be used for the package file template"""
base_class_name = 'Package'
dependencies = """\
# FIXME: Add dependencies if required.
# depends_on('foo')""",
# depends_on('foo')"""
'cmake': """\
body = """\
def install(self, spec, prefix):
# FIXME: Unknown build system
make()
make('install')"""
def __init__(self, name, url, version_hash_tuples):
self.name = name
self.class_name = mod_to_class(name)
self.url = url
self.version_hash_tuples = version_hash_tuples
@property
def versions(self):
"""Adds a version() call to the package for each version found."""
max_len = max(len(str(v)) for v, h in self.version_hash_tuples)
format = " version(%%-%ds, '%%s')" % (max_len + 2)
return '\n'.join(
format % ("'%s'" % v, h) for v, h in self.version_hash_tuples
)
class AutotoolsGuess(DefaultGuess):
"""Provides appropriate overrides for autotools-based packages"""
base_class_name = 'AutotoolsPackage'
dependencies = """\
# FIXME: Add dependencies if required.
# depends_on('m4', type='build')
# depends_on('autoconf', type='build')
# depends_on('automake', type='build')
# depends_on('libtool', type='build')
# depends_on('foo')"""
body = """\
def configure_args(self):
# FIXME: Add arguments other than --prefix
# FIXME: If not needed delete the function
args = []
return args"""
class CMakeGuess(DefaultGuess):
"""Provides appropriate overrides for cmake-based packages"""
base_class_name = 'CMakePackage'
dependencies = """\
# FIXME: Add additional dependencies if required.
depends_on('cmake', type='build')""",
depends_on('cmake', type='build')"""
'scons': """\
body = """\
def cmake_args(self):
# FIXME: Add arguments other than
# FIXME: CMAKE_INSTALL_PREFIX and CMAKE_BUILD_TYPE
# FIXME: If not needed delete the function
args = []
return args"""
class SconsGuess(DefaultGuess):
"""Provides appropriate overrides for scons-based packages"""
dependencies = """\
# FIXME: Add additional dependencies if required.
depends_on('scons', type='build')""",
depends_on('scons', type='build')"""
'bazel': """\
body = """\
def install(self, spec, prefix):
# FIXME: Add logic to build and install here.
scons('prefix={0}'.format(prefix))
scons('install')"""
class BazelGuess(DefaultGuess):
"""Provides appropriate overrides for bazel-based packages"""
dependencies = """\
# FIXME: Add additional dependencies if required.
depends_on('bazel', type='build')""",
depends_on('bazel', type='build')"""
'python': """\
body = """\
def install(self, spec, prefix):
# FIXME: Add logic to build and install here.
bazel()"""
class PythonGuess(DefaultGuess):
"""Provides appropriate overrides for python extensions"""
dependencies = """\
extends('python')
# FIXME: Add additional dependencies if required.
# depends_on('py-setuptools', type='build')
# depends_on('py-foo', type=nolink)""",
# depends_on('py-foo', type=nolink)"""
'R': """\
body = """\
def install(self, spec, prefix):
# FIXME: Add logic to build and install here.
setup_py('install', '--prefix={0}'.format(prefix))"""
def __init__(self, name, *args):
name = 'py-{0}'.format(name)
super(PythonGuess, self).__init__(name, *args)
class RGuess(DefaultGuess):
"""Provides appropriate overrides for R extensions"""
dependencies = """\
extends('R')
# FIXME: Add additional dependencies if required.
# depends_on('r-foo', type=nolink)""",
# depends_on('r-foo', type=nolink)"""
'octave': """\
body = """\
def install(self, spec, prefix):
# FIXME: Add logic to build and install here.
R('CMD', 'INSTALL', '--library={0}'.format(self.module.r_lib_dir),
self.stage.source_path)"""
def __init__(self, name, *args):
name = 'r-{0}'.format(name)
super(RGuess, self).__init__(name, *args)
class OctaveGuess(DefaultGuess):
"""Provides appropriate overrides for octave packages"""
dependencies = """\
extends('octave')
# FIXME: Add additional dependencies if required.
# depends_on('octave-foo', type=nolink)""",
# depends_on('octave-foo', type=nolink)"""
'unknown': """\
# FIXME: Add dependencies if required.
# depends_on('foo')"""
}
# Default installation instructions
install_dict = {
'autotools': """\
# FIXME: Modify the configure line to suit your build system here.
configure('--prefix={0}'.format(prefix))
# FIXME: Add logic to build and install here.
make()
make('install')""",
'cmake': """\
with working_dir('spack-build', create=True):
# FIXME: Modify the cmake line to suit your build system here.
cmake('..', *std_cmake_args)
# FIXME: Add logic to build and install here.
make()
make('install')""",
'scons': """\
# FIXME: Add logic to build and install here.
scons('prefix={0}'.format(prefix))
scons('install')""",
'bazel': """\
# FIXME: Add logic to build and install here.
bazel()""",
'python': """\
# FIXME: Add logic to build and install here.
setup_py('install', '--prefix={0}'.format(prefix))""",
'R': """\
# FIXME: Add logic to build and install here.
R('CMD', 'INSTALL', '--library={0}'.format(self.module.r_lib_dir),
self.stage.source_path)""",
'octave': """\
body = """\
def install(self, spec, prefix):
# FIXME: Add logic to build and install here.
octave('--quiet', '--norc',
'--built-in-docstrings-file=/dev/null',
'--texi-macros-file=/dev/null',
'--eval', 'pkg prefix {0}; pkg install {1}'.format(
prefix, self.stage.archive_file))""",
prefix, self.stage.archive_file))"""
'unknown': """\
# FIXME: Unknown build system
make()
make('install')"""
}
def make_version_calls(ver_hash_tuples):
"""Adds a version() call to the package for each version found."""
max_len = max(len(str(v)) for v, h in ver_hash_tuples)
format = " version(%%-%ds, '%%s')" % (max_len + 2)
return '\n'.join(format % ("'%s'" % v, h) for v, h in ver_hash_tuples)
def __init__(self, name, *args):
name = 'octave-{0}'.format(name)
super(OctaveGuess, self).__init__(name, *args)
def setup_parser(subparser):
@@ -227,6 +277,16 @@ def setup_parser(subparser):
class BuildSystemGuesser(object):
_choices = {
'autotools': AutotoolsGuess,
'cmake': CMakeGuess,
'scons': SconsGuess,
'bazel': BazelGuess,
'python': PythonGuess,
'R': RGuess,
'octave': OctaveGuess
}
def __call__(self, stage, url):
"""Try to guess the type of build system used by a project based on
the contents of its archive or the URL it was downloaded from."""
@@ -275,6 +335,10 @@ def __call__(self, stage, url):
self.build_system = build_system
def make_guess(self, name, url, ver_hash_tuples):
cls = self._choiches.get(self.build_system, DefaultGuess)
return cls(name, url, ver_hash_tuples)
def guess_name_and_version(url, args):
# Try to deduce name and version of the new package from the URL
@@ -348,7 +412,7 @@ def fetch_tarballs(url, name, version):
tty.msg("Found %s versions of %s:" % (len(versions), name),
*spack.cmd.elide_list(
["%-10s%s" % (v, u) for v, u in versions.iteritems()]))
print
print('')
archives_to_fetch = tty.get_number(
"Include how many checksums in the package file?",
default=5, abort='q')
@@ -389,16 +453,10 @@ def create(parser, args):
if not ver_hash_tuples:
tty.die("Could not fetch any tarballs for %s" % name)
# Add prefix to package name if it is an extension.
if guesser.build_system == 'python':
name = 'py-{0}'.format(name)
if guesser.build_system == 'R':
name = 'r-{0}'.format(name)
if guesser.build_system == 'octave':
name = 'octave-{0}'.format(name)
guess = guesser.make_guess(name, url, ver_hash_tuples)
# Create a directory for the new package.
pkg_path = repo.filename_for_package_name(name)
pkg_path = repo.filename_for_package_name(guess.name)
if os.path.exists(pkg_path) and not args.force:
tty.die("%s already exists." % pkg_path)
else:
@@ -408,12 +466,15 @@ def create(parser, args):
with open(pkg_path, "w") as pkg_file:
pkg_file.write(
package_template.substitute(
name=name,
class_name=mod_to_class(name),
url=url,
versions=make_version_calls(ver_hash_tuples),
dependencies=dependencies_dict[guesser.build_system],
install=install_dict[guesser.build_system]))
name=guess.name,
class_name=guess.class_name,
base_class_name=guess.base_class_name,
url=guess.url,
versions=guess.versions,
dependencies=guess.dependencies,
body=guess.body
)
)
# If everything checks out, go ahead and edit.
spack.editor(pkg_path)

View File

@@ -48,8 +48,11 @@ def setup_parser(subparser):
def print_text_info(pkg):
"""Print out a plain text description of a package."""
print "Package: ", pkg.name
print "Homepage: ", pkg.homepage
header = "{0}: ".format(pkg.build_system_class)
print header, pkg.name
whitespaces = ''.join([' '] * (len(header) - len("Homepage: ")))
print "Homepage:", whitespaces, pkg.homepage
print
print "Safe versions: "
@@ -84,6 +87,13 @@ def print_text_info(pkg):
print " " + fmt % (name, default, desc)
print
print "Installation Phases:"
phase_str = ''
for phase in pkg.phases:
phase_str += " {0}".format(phase)
print phase_str
for deptype in ('build', 'link', 'run'):
print
print "%s Dependencies:" % deptype.capitalize()
@@ -94,7 +104,7 @@ def print_text_info(pkg):
print " None"
print
print "Virtual packages: "
print "Virtual Packages: "
if pkg.provided:
for spec, when in pkg.provided.items():
print " %s provides %s" % (when, spec)

View File

@@ -22,7 +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
##############################################################################
from __future__ import print_function
import argparse
import llnl.util.tty as tty
@@ -75,7 +74,7 @@ def setup_parser(subparser):
help="Run tests during installation of a package.")
def install(parser, args):
def install(parser, args, **kwargs):
if not args.package:
tty.die("install requires at least one package argument")
@@ -88,7 +87,7 @@ def install(parser, args):
# Parse cli arguments and construct a dictionary
# that will be passed to Package.do_install API
kwargs = {
kwargs.update({
'keep_prefix': args.keep_prefix,
'keep_stage': args.keep_stage,
'install_deps': 'dependencies' in args.things_to_install,
@@ -97,7 +96,7 @@ def install(parser, args):
'verbose': args.verbose,
'fake': args.fake,
'dirty': args.dirty
}
})
# Spec from cli
specs = spack.cmd.parse_specs(args.package, concretize=True)

View File

@@ -22,16 +22,18 @@
# 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 os
import string
import sys
import llnl.util.tty as tty
import spack
import spack.cmd
from spack import which
from spack.cmd.edit import edit_package
from spack.stage import DIYStage
from llnl.util.filesystem import set_executable
description = "Create a configuration script and module, but don't build."
@@ -51,6 +53,72 @@ def setup_parser(subparser):
help="Install a package *without* cleaning the environment.")
def spack_transitive_include_path():
return ';'.join(
os.path.join(dep, 'include')
for dep in os.environ['SPACK_DEPENDENCIES'].split(os.pathsep)
)
def write_spconfig(package):
# Set-up the environment
spack.build_environment.setup_package(package)
cmd = [str(which('cmake'))] + package.std_cmake_args + package.cmake_args()
env = dict()
paths = os.environ['PATH'].split(':')
paths = [item for item in paths if 'spack/env' not in item]
env['PATH'] = ':'.join(paths)
env['SPACK_TRANSITIVE_INCLUDE_PATH'] = spack_transitive_include_path()
env['CMAKE_PREFIX_PATH'] = os.environ['CMAKE_PREFIX_PATH']
env['CC'] = os.environ['SPACK_CC']
env['CXX'] = os.environ['SPACK_CXX']
env['FC'] = os.environ['SPACK_FC']
setup_fname = 'spconfig.py'
with open(setup_fname, 'w') as fout:
fout.write(
r"""#!%s
#
import sys
import os
import subprocess
def cmdlist(str):
return list(x.strip().replace("'",'') for x in str.split('\n') if x)
env = dict(os.environ)
""" % 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[%s] = %s\n' % (repr(name), repr(val)))
else:
if name == 'SPACK_TRANSITIVE_INCLUDE_PATH':
sep = ';'
else:
sep = ':'
fout.write(
'env[%s] = "%s".join(cmdlist("""\n' % (repr(name), sep))
for part in string.split(val, sep):
fout.write(' %s\n' % part)
fout.write('"""))\n')
fout.write("env['CMAKE_TRANSITIVE_INCLUDE_PATH'] = env['SPACK_TRANSITIVE_INCLUDE_PATH'] # Deprecated\n") # NOQA: ignore=E501
fout.write('\ncmd = cmdlist("""\n')
fout.write('%s\n' % cmd[0])
for arg in cmd[1:]:
fout.write(' %s\n' % arg)
fout.write('""") + sys.argv[1:]\n')
fout.write('\nproc = subprocess.Popen(cmd, env=env)\nproc.wait()\n')
set_executable(setup_fname)
def setup(self, args):
if not args.spec:
tty.die("spack setup requires a package spec argument.")
@@ -80,6 +148,12 @@ def setup(self, args):
spec.concretize()
package = spack.repo.get(spec)
if not isinstance(package, spack.CMakePackage):
tty.die(
'Support for {0} derived packages not yet implemented'.format(
package.build_system_class
)
)
# It's OK if the package is already installed.
@@ -89,10 +163,4 @@ def setup(self, args):
# TODO: make this an argument, not a global.
spack.do_checksum = False
package.do_install(
keep_prefix=True, # Don't remove install directory
install_deps=not args.ignore_deps,
verbose=args.verbose,
keep_stage=True, # don't remove source dir for SETUP.
install_phases=set(['setup', 'provenance']),
dirty=args.dirty)
write_spconfig(package)

View File

@@ -26,6 +26,7 @@
import sys
import llnl.util.tty as tty
import spack
import inspect
class SpackError(Exception):
@@ -49,7 +50,7 @@ def die(self):
else:
tty.error(self.message)
if self.long_message:
print self.long_message
print(self.long_message)
os._exit(1)
def __str__(self):
@@ -58,6 +59,16 @@ def __str__(self):
msg += "\n %s" % self._long_message
return msg
def __repr__(self):
args = [repr(self.message), repr(self.long_message)]
args = ','.join(args)
qualified_name = inspect.getmodule(
self).__name__ + '.' + type(self).__name__
return qualified_name + '(' + args + ')'
def __reduce__(self):
return type(self), (self.message, self.long_message)
class UnsupportedPlatformError(SpackError):
"""Raised by packages when a platform is not supported"""

View File

@@ -33,24 +33,20 @@
rundown on spack and how it differs from homebrew, look at the
README.
"""
import contextlib
import copy
import functools
import inspect
import os
import sys
import re
import sys
import textwrap
import time
import string
import contextlib
from StringIO import StringIO
import llnl.util.lock
import llnl.util.tty as tty
from llnl.util.filesystem import *
from llnl.util.lang import *
from llnl.util.link_tree import LinkTree
from llnl.util.tty.log import log_output
import spack
import spack.build_environment
import spack.compilers
import spack.directives
import spack.error
@@ -60,20 +56,188 @@
import spack.repository
import spack.url
import spack.util.web
from llnl.util.filesystem import *
from llnl.util.lang import *
from llnl.util.link_tree import LinkTree
from llnl.util.tty.log import log_output
from spack import directory_layout
from spack.stage import Stage, ResourceStage, StageComposite
from spack.util.crypto import bit_length
from spack.util.environment import dump_environment
from spack.util.executable import ProcessError, which
from spack.util.executable import ProcessError
from spack.version import *
from spack import directory_layout
"""Allowed URL schemes for spack packages."""
_ALLOWED_URL_SCHEMES = ["http", "https", "ftp", "file", "git"]
class Package(object):
class InstallPhase(object):
"""Manages a single phase of the installation
This descriptor stores at creation time the name of the method it should
search for execution. The method is retrieved at __get__ time, so that
it can be overridden by subclasses of whatever class declared the phases.
It also provides hooks to execute prerequisite and sanity checks.
"""
def __init__(self, name):
self.name = name
self.preconditions = []
self.sanity_checks = []
def __get__(self, instance, owner):
# The caller is a class that is trying to customize
# my behavior adding something
if instance is None:
return self
# If instance is there the caller wants to execute the
# install phase, thus return a properly set wrapper
phase = getattr(instance, self.name)
@functools.wraps(phase)
def phase_wrapper(spec, prefix):
# Check instance attributes at the beginning of a phase
self._on_phase_start(instance)
# Execute phase pre-conditions,
# and give them the chance to fail
for check in self.preconditions:
# Do something sensible at some point
check(instance)
phase(spec, prefix)
# Execute phase sanity_checks,
# and give them the chance to fail
for check in self.sanity_checks:
check(instance)
# Check instance attributes at the end of a phase
self._on_phase_exit(instance)
return phase_wrapper
def _on_phase_start(self, instance):
pass
def _on_phase_exit(self, instance):
# If a phase has a matching last_phase attribute,
# stop the installation process raising a StopIteration
if getattr(instance, 'last_phase', None) == self.name:
raise StopIteration('Stopping at \'{0}\' phase'.format(self.name))
def copy(self):
try:
return copy.deepcopy(self)
except TypeError:
# This bug-fix was not back-ported in Python 2.6
# http://bugs.python.org/issue1515
other = InstallPhase(self.name)
other.preconditions.extend(self.preconditions)
other.sanity_checks.extend(self.sanity_checks)
return other
class PackageMeta(type):
"""Conveniently transforms attributes to permit extensible phases
Iterates over the attribute 'phases' and creates / updates private
InstallPhase attributes in the class that is being initialized
"""
phase_fmt = '_InstallPhase_{0}'
_InstallPhase_sanity_checks = {}
_InstallPhase_preconditions = {}
def __new__(meta, name, bases, attr_dict):
# Check if phases is in attr dict, then set
# install phases wrappers
if 'phases' in attr_dict:
_InstallPhase_phases = [PackageMeta.phase_fmt.format(x) for x in attr_dict['phases']] # NOQA: ignore=E501
for phase_name, callback_name in zip(_InstallPhase_phases, attr_dict['phases']): # NOQA: ignore=E501
attr_dict[phase_name] = InstallPhase(callback_name)
attr_dict['_InstallPhase_phases'] = _InstallPhase_phases
def _append_checks(check_name):
# Name of the attribute I am going to check it exists
attr_name = PackageMeta.phase_fmt.format(check_name)
checks = getattr(meta, attr_name)
if checks:
for phase_name, funcs in checks.items():
try:
# Search for the phase in the attribute dictionary
phase = attr_dict[
PackageMeta.phase_fmt.format(phase_name)]
except KeyError:
# If it is not there it's in the bases
# and we added a check. We need to copy
# and extend
for base in bases:
phase = getattr(
base,
PackageMeta.phase_fmt.format(phase_name),
None
)
attr_dict[PackageMeta.phase_fmt.format(
phase_name)] = phase.copy()
phase = attr_dict[
PackageMeta.phase_fmt.format(phase_name)]
getattr(phase, check_name).extend(funcs)
# Clear the attribute for the next class
setattr(meta, attr_name, {})
@classmethod
def _register_checks(cls, check_type, *args):
def _register_sanity_checks(func):
attr_name = PackageMeta.phase_fmt.format(check_type)
check_list = getattr(meta, attr_name)
for item in args:
checks = check_list.setdefault(item, [])
checks.append(func)
setattr(meta, attr_name, check_list)
return func
return _register_sanity_checks
@staticmethod
def on_package_attributes(**attrs):
def _execute_under_condition(func):
@functools.wraps(func)
def _wrapper(instance):
# If all the attributes have the value we require, then
# execute
if all([getattr(instance, key, None) == value for key, value in attrs.items()]): # NOQA: ignore=E501
func(instance)
return _wrapper
return _execute_under_condition
@classmethod
def precondition(cls, *args):
return cls._register_checks('preconditions', *args)
@classmethod
def sanity_check(cls, *args):
return cls._register_checks('sanity_checks', *args)
if all([not hasattr(x, '_register_checks') for x in bases]):
attr_dict['_register_checks'] = _register_checks
if all([not hasattr(x, 'sanity_check') for x in bases]):
attr_dict['sanity_check'] = sanity_check
if all([not hasattr(x, 'precondition') for x in bases]):
attr_dict['precondition'] = precondition
if all([not hasattr(x, 'on_package_attributes') for x in bases]):
attr_dict['on_package_attributes'] = on_package_attributes
# Preconditions
_append_checks('preconditions')
# Sanity checks
_append_checks('sanity_checks')
return super(PackageMeta, meta).__new__(meta, name, bases, attr_dict)
def __init__(cls, name, bases, dict):
type.__init__(cls, name, bases, dict)
spack.directives.ensure_dicts(cls)
class PackageBase(object):
"""This is the superclass for all spack packages.
***The Package class***
@@ -309,7 +473,7 @@ class SomePackage(Package):
Package creators override functions like install() (all of them do this),
clean() (some of them do this), and others to provide custom behavior.
"""
__metaclass__ = PackageMeta
#
# These are default values for instance variables.
#
@@ -344,12 +508,6 @@ class SomePackage(Package):
"""Per-process lock objects for each install prefix."""
prefix_locks = {}
class __metaclass__(type):
"""Ensure attributes required by Spack directives are present."""
def __init__(cls, name, bases, dict):
type.__init__(cls, name, bases, dict)
spack.directives.ensure_dicts(cls)
def __init__(self, spec):
# this determines how the package should be built.
self.spec = spec
@@ -429,6 +587,8 @@ def __init__(self, spec):
if self.is_extension:
spack.repo.get(self.extendee_spec)._check_extendable()
self.extra_args = {}
def possible_dependencies(self, visited=None):
"""Return set of possible transitive dependencies of this package."""
if visited is None:
@@ -886,12 +1046,34 @@ def namespace(self):
return namespace
def do_fake_install(self):
"""Make a fake install directory contaiing a 'fake' file in bin."""
"""Make a fake install directory containing a 'fake' file in bin."""
# FIXME : Make this part of the 'install' behavior ?
mkdirp(self.prefix.bin)
touch(join_path(self.prefix.bin, 'fake'))
mkdirp(self.prefix.lib)
mkdirp(self.prefix.man1)
def _if_make_target_execute(self, target):
try:
# Check if we have a makefile
file = [x for x in ('Makefile', 'makefile') if os.path.exists(x)]
file = file.pop()
except IndexError:
tty.msg('No Makefile found in the build directory')
return
# Check if 'target' is in the makefile
regex = re.compile('^' + target + ':')
with open(file, 'r') as f:
matches = [line for line in f.readlines() if regex.match(line)]
if not matches:
tty.msg('Target \'' + target + ':\' not found in Makefile')
return
# Execute target
inspect.getmodule(self).make(target)
def _get_needed_resources(self):
resources = []
# Select the resources that are needed for this build
@@ -925,13 +1107,10 @@ def _prefix_write_lock(self):
finally:
self.prefix_lock.release_write()
install_phases = set(['configure', 'build', 'install', 'provenance'])
def do_install(self,
keep_prefix=False,
keep_stage=False,
install_deps=True,
install_self=True,
skip_patch=False,
verbose=False,
make_jobs=None,
@@ -939,7 +1118,7 @@ def do_install(self,
fake=False,
explicit=False,
dirty=False,
install_phases=install_phases):
**kwargs):
"""Called by commands to install a package and its dependencies.
Package implementations should override install() to describe
@@ -975,9 +1154,7 @@ def do_install(self,
# Ensure package is not already installed
layout = spack.install_layout
with self._prefix_read_lock():
if ('install' in install_phases and
layout.check_installed(self.spec)):
if layout.check_installed(self.spec):
tty.msg(
"%s is already installed in %s" % (self.name, self.prefix))
rec = spack.installed_db.get_record(self.spec)
@@ -987,6 +1164,8 @@ def do_install(self,
rec.explicit = True
return
self._do_install_pop_kwargs(kwargs)
tty.msg("Installing %s" % self.name)
# First, install dependencies recursively.
@@ -1009,7 +1188,6 @@ def do_install(self,
# Set parallelism before starting build.
self.make_jobs = make_jobs
# ------------------- BEGIN def build_process()
# Then install the package itself.
def build_process():
"""Forked for each build. Has its own process and python
@@ -1022,112 +1200,129 @@ def build_process():
else:
self.do_stage()
tty.msg("Building %s" % self.name)
tty.msg(
'Building {0} [{1}]'.format(self.name, self.build_system_class)
)
self.stage.keep = keep_stage
self.install_phases = install_phases
self.build_directory = join_path(self.stage.path, 'spack-build')
self.source_directory = self.stage.source_path
with contextlib.nested(self.stage, self._prefix_write_lock()):
# Run the pre-install hook in the child process after
# the directory is created.
spack.hooks.pre_install(self)
if fake:
self.do_fake_install()
else:
# Do the real install in the source directory.
self.stage.chdir_to_source()
# Save the build environment in a file before building.
env_path = join_path(os.getcwd(), 'spack-build.env')
try:
try:
with contextlib.nested(self.stage, self._prefix_write_lock()):
# Run the pre-install hook in the child process after
# the directory is created.
spack.hooks.pre_install(self)
if fake:
self.do_fake_install()
else:
# Do the real install in the source directory.
self.stage.chdir_to_source()
# Save the build environment in a file before building.
env_path = join_path(os.getcwd(), 'spack-build.env')
# Redirect I/O to a build log (and optionally to
# the terminal)
log_path = join_path(os.getcwd(), 'spack-build.out')
log_file = open(log_path, 'w')
with log_output(log_file, verbose, sys.stdout.isatty(),
True):
dump_environment(env_path)
self.install(self.spec, self.prefix)
# FIXME : refactor this assignment
self.log_path = log_path
self.env_path = env_path
dump_environment(env_path)
# Spawn a daemon that reads from a pipe and redirects
# everything to log_path
redirection_context = log_output(
log_path, verbose,
sys.stdout.isatty(),
True
)
with redirection_context as log_redirection:
for phase_name, phase in zip(self.phases, self._InstallPhase_phases): # NOQA: ignore=E501
tty.msg(
'Executing phase : \'{0}\''.format(phase_name) # NOQA: ignore=E501
)
# Redirect stdout and stderr to daemon pipe
with log_redirection:
getattr(self, phase)(
self.spec, self.prefix)
self.log()
# Run post install hooks before build stage is removed.
spack.hooks.post_install(self)
except ProcessError as e:
# Annotate ProcessErrors with the location of
# the build log
e.build_log = log_path
raise e
# Stop timer.
self._total_time = time.time() - start_time
build_time = self._total_time - self._fetch_time
# Ensure that something was actually installed.
if 'install' in self.install_phases:
self.sanity_check_prefix()
tty.msg("Successfully installed %s" % self.name,
"Fetch: %s. Build: %s. Total: %s." %
(_hms(self._fetch_time), _hms(build_time),
_hms(self._total_time)))
print_pkg(self.prefix)
# Copy provenance into the install directory on success
if 'provenance' in self.install_phases:
log_install_path = layout.build_log_path(self.spec)
env_install_path = layout.build_env_path(self.spec)
packages_dir = layout.build_packages_path(self.spec)
# Remove first if we're overwriting another build
# (can happen with spack setup)
try:
# log_install_path and env_install_path are here
shutil.rmtree(packages_dir)
except:
pass
install(log_path, log_install_path)
install(env_path, env_install_path)
dump_packages(self.spec, packages_dir)
# Run post install hooks before build stage is removed.
spack.hooks.post_install(self)
# Stop timer.
self._total_time = time.time() - start_time
build_time = self._total_time - self._fetch_time
tty.msg("Successfully installed %s" % self.name,
"Fetch: %s. Build: %s. Total: %s." %
(_hms(self._fetch_time), _hms(build_time),
_hms(self._total_time)))
print_pkg(self.prefix)
# ------------------- END def build_process()
except ProcessError as e:
# Annotate ProcessErrors with the location of
# the build log
e.build_log = log_path
raise e
try:
# Create the install prefix and fork the build process.
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 someone's data)
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:
# Fork a child to do the actual installation
spack.build_environment.fork(self, build_process, dirty=dirty)
except:
# remove the install prefix if anything went wrong during install.
# If we installed then we should keep the prefix
keep_prefix = True if self.last_phase is None else keep_prefix
# note: PARENT of the build process adds the new package to
# the database, so that we don't need to re-read from file.
spack.installed_db.add(
self.spec, spack.install_layout, explicit=explicit
)
except directory_layout.InstallDirectoryAlreadyExistsError:
# Abort install if install directory exists.
# But do NOT remove it (you'd be overwriting someone else's stuff)
tty.warn("Keeping existing install prefix in place.")
raise
except StopIteration as e:
# A StopIteration exception means that do_install
# was asked to stop early from clients
tty.msg(e.message)
tty.msg(
'Package stage directory : {0}'.format(self.stage.source_path)
)
finally:
# Remove the install prefix if anything went wrong during install.
if not keep_prefix:
self.remove_prefix()
else:
tty.warn("Keeping install prefix in place despite error.",
"Spack will think this package is installed. " +
"Manually remove this directory to fix:",
self.prefix,
wrap=False)
raise
# Parent of the build process adds the new package to
# the database, so that we don't need to re-read from file.
# NOTE: add() implicitly acquires a write-lock
spack.installed_db.add(
self.spec, spack.install_layout, explicit=explicit)
def _do_install_pop_kwargs(self, kwargs):
"""Pops kwargs from do_install before starting the installation
Args:
kwargs:
'stop_at': last installation phase to be executed (or None)
"""
self.last_phase = kwargs.pop('stop_at', None)
if self.last_phase is not None and self.last_phase not in self.phases:
tty.die('\'{0.last_phase}\' is not among the allowed phases for package {0.name}'.format(self)) # NOQA: ignore=E501
def log(self):
# 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)
# Remove first if we're overwriting another build
# (can happen with spack setup)
try:
# log_install_path and env_install_path are inside this
shutil.rmtree(packages_dir)
except Exception:
# FIXME : this potentially catches too many things...
pass
install(self.log_path, log_install_path)
install(self.env_path, env_install_path)
dump_packages(self.spec, packages_dir)
def sanity_check_prefix(self):
"""This function checks whether install succeeded."""
@@ -1281,13 +1476,6 @@ def setup_dependent_package(self, module, dependent_spec):
"""
pass
def install(self, spec, prefix):
"""
Package implementations override this with their own configuration
"""
raise InstallError("Package %s provides no install method!" %
self.name)
def do_uninstall(self, force=False):
if not self.installed:
# prefix may not exist, but DB may be inconsistent. Try to fix by
@@ -1498,6 +1686,16 @@ def rpath_args(self):
return " ".join("-Wl,-rpath,%s" % p for p in self.rpath)
class Package(PackageBase):
phases = ['install']
# To be used in UI queries that require to know which
# build-system class we are using
build_system_class = 'Package'
# This will be used as a registration decorator in user
# packages, if need be
PackageBase.sanity_check('install')(PackageBase.sanity_check_prefix)
def install_dependency_symlinks(pkg, spec, prefix):
"""Execute a dummy install and flatten dependencies"""
flatten_dependencies(spec, prefix)
@@ -1598,166 +1796,6 @@ def _hms(seconds):
return ' '.join(parts)
class StagedPackage(Package):
"""A Package subclass where the install() is split up into stages."""
def install_setup(self):
"""Creates a spack_setup.py script to configure the package later."""
raise InstallError(
"Package %s provides no install_setup() method!" % self.name)
def install_configure(self):
"""Runs the configure process."""
raise InstallError(
"Package %s provides no install_configure() method!" % self.name)
def install_build(self):
"""Runs the build process."""
raise InstallError(
"Package %s provides no install_build() method!" % self.name)
def install_install(self):
"""Runs the install process."""
raise InstallError(
"Package %s provides no install_install() method!" % self.name)
def install(self, spec, prefix):
if 'setup' in self.install_phases:
self.install_setup()
if 'configure' in self.install_phases:
self.install_configure()
if 'build' in self.install_phases:
self.install_build()
if 'install' in self.install_phases:
self.install_install()
else:
# Create a dummy file so the build doesn't fail.
# That way, the module file will also be created.
with open(os.path.join(prefix, 'dummy'), 'w'):
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):
def make_make(self):
import multiprocessing
# number of jobs spack will to build with.
jobs = multiprocessing.cpu_count()
if not self.parallel:
jobs = 1
elif self.make_jobs:
jobs = self.make_jobs
make = spack.build_environment.MakeExecutable('make', jobs)
return make
def configure_args(self):
"""Returns package-specific arguments to be provided to
the configure command.
"""
return list()
def configure_env(self):
"""Returns package-specific environment under which the
configure command should be run.
"""
return dict()
def transitive_inc_path(self):
return ';'.join(
os.path.join(dep, 'include')
for dep in os.environ['SPACK_DEPENDENCIES'].split(os.pathsep)
)
def install_setup(self):
cmd = [str(which('cmake'))]
cmd += spack.build_environment.get_std_cmake_args(self)
cmd += ['-DCMAKE_INSTALL_PREFIX=%s' % os.environ['SPACK_PREFIX'],
'-DCMAKE_C_COMPILER=%s' % os.environ['SPACK_CC'],
'-DCMAKE_CXX_COMPILER=%s' % os.environ['SPACK_CXX'],
'-DCMAKE_Fortran_COMPILER=%s' % os.environ['SPACK_FC']]
cmd += self.configure_args()
env = {
'PATH': os.environ['PATH'],
'SPACK_TRANSITIVE_INCLUDE_PATH': self.transitive_inc_path(),
'CMAKE_PREFIX_PATH': os.environ['CMAKE_PREFIX_PATH']
}
setup_fname = 'spconfig.py'
with open(setup_fname, 'w') as fout:
fout.write(r"""#!%s
#
import sys
import os
import subprocess
def cmdlist(str):
return list(x.strip().replace("'",'') for x in str.split('\n') if x)
env = dict(os.environ)
""" % 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[%s] = %s\n' % (repr(name), repr(val)))
else:
if name == 'SPACK_TRANSITIVE_INCLUDE_PATH':
sep = ';'
else:
sep = ':'
fout.write('env[%s] = "%s".join(cmdlist("""\n'
% (repr(name), sep))
for part in string.split(val, sep):
fout.write(' %s\n' % part)
fout.write('"""))\n')
fout.write("env['CMAKE_TRANSITIVE_INCLUDE_PATH'] = "
"env['SPACK_TRANSITIVE_INCLUDE_PATH'] # Deprecated\n")
fout.write('\ncmd = cmdlist("""\n')
fout.write('%s\n' % cmd[0])
for arg in cmd[1:]:
fout.write(' %s\n' % arg)
fout.write('""") + sys.argv[1:]\n')
fout.write('\nproc = subprocess.Popen(cmd, env=env)\n')
fout.write('proc.wait()\n')
make_executable(setup_fname)
def install_configure(self):
cmake = which('cmake')
with working_dir(self.build_directory, create=True):
env = os.environ
env.update(self.configure_env())
env['SPACK_TRANSITIVE_INCLUDE_PATH'] = self.transitive_inc_path()
options = self.configure_args()
options += spack.build_environment.get_std_cmake_args(self)
cmake(self.source_directory, *options)
def install_build(self):
make = self.make_make()
with working_dir(self.build_directory, create=False):
make()
def install_install(self):
make = self.make_make()
with working_dir(self.build_directory, create=False):
make('install')
class FetchError(spack.error.SpackError):
"""Raised when something goes wrong during fetch."""

View File

@@ -25,28 +25,24 @@
from spack import *
class Astyle(Package):
class Astyle(MakefilePackage):
"""A Free, Fast, and Small Automatic Formatter for C, C++, C++/CLI,
Objective-C, C#, and Java Source Code.
"""
homepage = "http://astyle.sourceforge.net/"
url = "http://downloads.sourceforge.net/project/astyle/astyle/astyle%202.04/astyle_2.04_linux.tar.gz"
url = "http://downloads.sourceforge.net/project/astyle/astyle/astyle%202.04/astyle_2.04_linux.tar.gz"
version('2.04', '30b1193a758b0909d06e7ee8dd9627f6')
def install(self, spec, prefix):
parallel = False
with working_dir('src'):
# we need to edit the makefile in place to set compiler:
make_file = join_path(self.stage.source_path,
'build', 'gcc', 'Makefile')
filter_file(r'^CXX\s*=.*', 'CXX=%s' % spack_cxx, make_file)
def build_directory(self):
return join_path(self.stage.source_path, 'build', self.compiler.name)
make('-f',
make_file,
parallel=False)
def edit(self, spec, prefix):
makefile = join_path(self.build_directory(), 'Makefile')
filter_file(r'^CXX\s*=.*', 'CXX=%s' % spack_cxx, makefile)
mkdirp(self.prefix.bin)
install(join_path(self.stage.source_path, 'src', 'bin', 'astyle'),
self.prefix.bin)
def install_args(self):
return ['prefix={0}'.format(prefix)]

View File

@@ -25,10 +25,8 @@
from spack import *
class Autoconf(Package):
"""
Autoconf -- system configuration part of autotools
"""
class Autoconf(AutotoolsPackage):
"""Autoconf -- system configuration part of autotools"""
homepage = 'https://www.gnu.org/software/autoconf/'
url = 'http://ftp.gnu.org/gnu/autoconf/autoconf-2.69.tar.gz'
@@ -54,8 +52,3 @@ def setup_dependent_package(self, module, dependent_spec):
'ifnames']
for name in executables:
setattr(module, name, self._make_executable(name))
def install(self, spec, prefix):
configure("--prefix=%s" % prefix)
make()
make("install")

View File

@@ -25,16 +25,9 @@
from spack import *
class Blitz(Package):
class Blitz(AutotoolsPackage):
"""N-dimensional arrays for C++"""
homepage = "http://github.com/blitzpp/blitz"
url = "https://github.com/blitzpp/blitz/tarball/1.0.0"
url = "https://github.com/blitzpp/blitz/tarball/1.0.0"
version('1.0.0', '9f040b9827fe22228a892603671a77af')
# No dependencies
def install(self, spec, prefix):
configure('--prefix=%s' % prefix)
make()
make("install")

View File

@@ -25,12 +25,12 @@
from spack import *
class Gmp(Package):
"""GMP is a free library for arbitrary precision arithmetic, operating
on signed integers, rational numbers, and floating-point numbers."""
class Gmp(AutotoolsPackage):
"""GMP is a free library for arbitrary precision arithmetic,
operating on signed integers, rational numbers, and
floating-point numbers."""
homepage = "https://gmplib.org"
url = "https://gmplib.org/download/gmp/gmp-6.0.0a.tar.bz2"
url = "https://gmplib.org/download/gmp/gmp-6.0.0a.tar.bz2"
version('6.1.1', '4c175f86e11eb32d8bf9872ca3a8e11d')
version('6.1.0', '86ee6e54ebfc4a90b643a65e402c4048')
@@ -39,16 +39,10 @@ class Gmp(Package):
depends_on('m4', type='build')
def install(self, spec, prefix):
config_args = ['--prefix=' + prefix,
'--enable-cxx']
def configure_args(self):
args = ['--enable-cxx']
# We need this flag if we want all the following checks to pass.
if spec.compiler.name == 'intel':
config_args.append('CXXFLAGS=-no-ftz')
args.append('CXXFLAGS=-no-ftz')
configure(*config_args)
make()
make('check')
make('install')
return args

View File

@@ -27,10 +27,10 @@
import shutil
class Hdf5(Package):
class Hdf5(AutotoolsPackage):
"""HDF5 is a data model, library, and file format for storing and managing
data. It supports an unlimited variety of datatypes, and is designed for
flexible and efficient I/O and for high volume and complex data.
data. It supports an unlimited variety of datatypes, and is designed for
flexible and efficient I/O and for high volume and complex data.
"""
homepage = "http://www.hdfgroup.org/HDF5/"
@@ -58,17 +58,19 @@ class Hdf5(Package):
variant('threadsafe', default=False,
description='Enable thread-safe capabilities')
depends_on("mpi", when='+mpi')
depends_on("szip", when='+szip')
depends_on("zlib@1.1.2:")
depends_on('mpi', when='+mpi')
depends_on('szip', when='+szip')
depends_on('zlib@1.1.2:')
def validate(self, spec):
@AutotoolsPackage.precondition('configure')
def validate(self):
"""
Checks if incompatible variants have been activated at the same time
:param spec: spec of the package
:raises RuntimeError: in case of inconsistencies
"""
spec = self.spec
if '+fortran' in spec and not self.compiler.fc:
msg = 'cannot build a fortran variant without a fortran compiler'
raise RuntimeError(msg)
@@ -77,8 +79,8 @@ def validate(self, spec):
msg = 'cannot use variant +threadsafe with either +cxx or +fortran'
raise RuntimeError(msg)
def install(self, spec, prefix):
self.validate(spec)
def configure_args(self):
spec = self.spec
# Handle compilation after spec validation
extra_args = []
@@ -139,21 +141,13 @@ def install(self, spec, prefix):
'--disable-hl',
])
configure(
"--prefix=%s" % prefix,
"--with-zlib=%s" % spec['zlib'].prefix,
*extra_args)
make()
return ["--with-zlib=%s" % spec['zlib'].prefix] + extra_args
if self.run_tests:
make("check")
make("install")
self.check_install(spec)
def check_install(self, spec):
"Build and run a small program to test the installed HDF5 library"
print "Checking HDF5 installation..."
def check(self):
super(Hdf5, self).check()
# Build and run a small program to test the installed HDF5 library
spec = self.spec
print("Checking HDF5 installation...")
checkdir = "spack-check"
with working_dir(checkdir, create=True):
source = r"""
@@ -190,15 +184,15 @@ def check_install(self, spec):
output = ""
success = output == expected
if not success:
print "Produced output does not match expected output."
print "Expected output:"
print '-' * 80
print expected
print '-' * 80
print "Produced output:"
print '-' * 80
print output
print '-' * 80
print("Produced output does not match expected output.")
print("Expected output:")
print('-' * 80)
print(expected)
print('-' * 80)
print("Produced output:")
print('-' * 80)
print(output)
print('-' * 80)
raise RuntimeError("HDF5 install check failed")
shutil.rmtree(checkdir)

View File

@@ -43,7 +43,7 @@ class Ibmisc(CMakePackage):
depends_on('cmake', type='build')
depends_on('doxygen', type='build')
def configure_args(self):
def cmake_args(self):
spec = self.spec
return [
'-DUSE_EVERYTRACE=%s' % ('YES' if '+everytrace' in spec else 'NO'),

View File

@@ -25,7 +25,7 @@
from spack import *
class Lzo(Package):
class Lzo(AutotoolsPackage):
"""Real-time data compression library"""
homepage = 'https://www.oberhumer.com/opensource/lzo/'
@@ -37,15 +37,8 @@ class Lzo(Package):
version('2.06', '95380bd4081f85ef08c5209f4107e9f8')
version('2.05', 'c67cda5fa191bab761c7cb06fe091e36')
def install(self, spec, prefix):
configure_args = [
'--prefix={0}'.format(prefix),
def configure_args(self):
return [
'--disable-dependency-tracking',
'--enable-shared'
]
configure(*configure_args)
make()
if self.run_tests:
make('check')
make('test') # more exhaustive test
make('install')

View File

@@ -25,8 +25,9 @@
from spack import *
class Openjpeg(Package):
class Openjpeg(CMakePackage):
"""OpenJPEG is an open-source JPEG 2000 codec written in C language.
It has been developed in order to promote the use of JPEG 2000, a
still-image compression standard from the Joint Photographic
Experts Group (JPEG).
@@ -35,7 +36,7 @@ class Openjpeg(Package):
"""
homepage = "https://github.com/uclouvain/openjpeg"
url = "https://github.com/uclouvain/openjpeg/archive/version.2.1.tar.gz"
url = "https://github.com/uclouvain/openjpeg/archive/version.2.1.tar.gz"
version('2.1', '3e1c451c087f8462955426da38aa3b3d')
version('2.0.1', '105876ed43ff7dbb2f90b41b5a43cfa5')
@@ -44,9 +45,3 @@ class Openjpeg(Package):
version('1.5.1', 'd774e4b5a0db5f0f171c4fc0aabfa14e')
depends_on('cmake', type='build')
def install(self, spec, prefix):
cmake('.', *std_cmake_args)
make()
make("install")

View File

@@ -25,7 +25,7 @@
from spack import *
class Qhull(Package):
class Qhull(CMakePackage):
"""Qhull computes the convex hull, Delaunay triangulation, Voronoi
diagram, halfspace intersection about a point, furt hest-site
Delaunay triangulation, and furthest-site Voronoi diagram. The
@@ -44,9 +44,3 @@ class Qhull(Package):
url="http://www.qhull.org/download/qhull-2012.1-src.tgz")
depends_on('cmake@2.6:', type='build')
def install(self, spec, prefix):
with working_dir('spack-build', create=True):
cmake('..', *std_cmake_args)
make()
make("install")

View File

@@ -26,7 +26,7 @@
import llnl.util.tty as tty
class Swiftsim(Package):
class Swiftsim(AutotoolsPackage):
"""SPH With Inter-dependent Fine-grained Tasking (SWIFT) provides
astrophysicists with a state of the art framework to perform
particle based simulations.
@@ -58,20 +58,15 @@ def setup_environment(self, spack_env, run_env):
tty.warn('This is needed to clone SWIFT repository')
spack_env.set('GIT_SSL_NO_VERIFY', 1)
def install(self, spec, prefix):
# Generate configure from configure.ac
# and Makefile.am
def autoreconf(self, spec, prefix):
libtoolize()
aclocal()
autoconf()
autogen = Executable('./autogen.sh')
autogen()
# Configure and install
options = ['--prefix=%s' % prefix,
'--enable-mpi' if '+mpi' in spec else '--disable-mpi',
'--with-metis={0}'.format(spec['metis'].prefix),
'--enable-optimization']
configure(*options)
make()
make("install")
def configure_args(self):
return ['--prefix=%s' % self.prefix,
'--enable-mpi' if '+mpi' in self.spec else '--disable-mpi',
'--with-metis={0}'.format(self.spec['metis'].prefix),
'--enable-optimization']

View File

@@ -25,24 +25,21 @@
from spack import *
class Szip(Package):
"""An implementation of the extended-Rice lossless compression algorithm.
It provides lossless compression of scientific data, and is provided
with HDF software products.
class Szip(AutotoolsPackage):
"""Szip is an implementation of the extended-Rice lossless
compression algorithm.
It provides lossless compression of scientific data, and is
provided with HDF software products.
"""
homepage = "https://www.hdfgroup.org/doc_resource/SZIP/"
url = "http://www.hdfgroup.org/ftp/lib-external/szip/2.1/src/szip-2.1.tar.gz"
url = "http://www.hdfgroup.org/ftp/lib-external/szip/2.1/src/szip-2.1.tar.gz"
version('2.1', '902f831bcefb69c6b635374424acbead')
def install(self, spec, prefix):
configure('--prefix=%s' % prefix,
'--enable-production',
'--enable-shared',
'--enable-static',
'--enable-encoding')
make()
make("install")
def configure_args(self):
return ['--enable-production',
'--enable-shared',
'--enable-static',
'--enable-encoding']