Added customization for make targets in 'build' and 'install' phases for CMakePackage (#2742)

* Added customization for make targets in 'build' and 'install' phases for CMakePackage

* Use rst in build system docs so that Sphinx generates nice API docs

* Allow AutotoolsPackages to be built in a different directory

* Flake8

* Fix missing import

* Allow configure to be located in different directory

* Update espressopp to use build targets

* Flake8

* Sphinx fix, lists must be a new paragraph

* Back out change that allowed a configure script in a different directory than build_directory

* Add missing deps, build in parallel

* Missing space for rst list
This commit is contained in:
Adam J. Stewart 2017-01-15 18:23:16 -06:00 committed by Todd Gamblin
parent 4b7b595e3c
commit f480e3449e
8 changed files with 95 additions and 77 deletions

View File

@ -31,6 +31,7 @@
from subprocess import check_call from subprocess import check_call
import llnl.util.tty as tty import llnl.util.tty as tty
from llnl.util.filesystem import working_dir
from spack.package import PackageBase from spack.package import PackageBase
@ -38,16 +39,17 @@ class AutotoolsPackage(PackageBase):
"""Specialized class for packages that are built using GNU Autotools """Specialized class for packages that are built using GNU Autotools
This class provides four phases that can be overridden: This class provides four phases that can be overridden:
- autoreconf
- configure * autoreconf
- build * configure
- install * build
* install
They all have sensible defaults and for many packages the only thing They all have sensible defaults and for many packages the only thing
necessary will be to override `configure_args` necessary will be to override ``configure_args``
Additionally, you may specify make targets for build and install Additionally, you may specify make targets for build and install
phases by overriding `build_targets` and `install_targets` phases by overriding ``build_targets`` and ``install_targets``
""" """
phases = ['autoreconf', 'configure', 'build', 'install'] phases = ['autoreconf', 'configure', 'build', 'install']
# To be used in UI queries that require to know which # To be used in UI queries that require to know which
@ -124,6 +126,10 @@ def do_patch_config_guess(self):
return False return False
def build_directory(self):
"""Override to provide another place to build the package"""
return self.stage.source_path
def patch(self): def patch(self):
"""Perform any required patches.""" """Perform any required patches."""
@ -138,39 +144,44 @@ def autoreconf(self, spec, prefix):
@PackageBase.sanity_check('autoreconf') @PackageBase.sanity_check('autoreconf')
def is_configure_or_die(self): def is_configure_or_die(self):
"""Checks the presence of a `configure` file after the """Checks the presence of a ``configure`` file after the
autoreconf phase""" autoreconf phase"""
with working_dir(self.build_directory()):
if not os.path.exists('configure'): if not os.path.exists('configure'):
raise RuntimeError( raise RuntimeError(
'configure script not found in {0}'.format(os.getcwd())) 'configure script not found in {0}'.format(os.getcwd()))
def configure_args(self): def configure_args(self):
"""Method to be overridden. Should return an iterable containing """Method to be overridden. Should return an iterable containing
all the arguments that must be passed to configure, except --prefix all the arguments that must be passed to configure, except ``--prefix``
""" """
return [] return []
def configure(self, spec, prefix): def configure(self, spec, prefix):
"""Runs configure with the arguments specified in `configure_args` """Runs configure with the arguments specified in ``configure_args``
and an appropriately set prefix and an appropriately set prefix
""" """
options = ['--prefix={0}'.format(prefix)] + self.configure_args() options = ['--prefix={0}'.format(prefix)] + self.configure_args()
with working_dir(self.build_directory()):
inspect.getmodule(self).configure(*options) inspect.getmodule(self).configure(*options)
def build(self, spec, prefix): def build(self, spec, prefix):
"""Make the build targets""" """Make the build targets"""
with working_dir(self.build_directory()):
inspect.getmodule(self).make(*self.build_targets) inspect.getmodule(self).make(*self.build_targets)
def install(self, spec, prefix): def install(self, spec, prefix):
"""Make the install targets""" """Make the install targets"""
with working_dir(self.build_directory()):
inspect.getmodule(self).make(*self.install_targets) inspect.getmodule(self).make(*self.install_targets)
@PackageBase.sanity_check('build') @PackageBase.sanity_check('build')
@PackageBase.on_package_attributes(run_tests=True) @PackageBase.on_package_attributes(run_tests=True)
def _run_default_function(self): def _run_default_function(self):
"""This function is run after build if self.run_tests == True """This function is run after build if ``self.run_tests == True``
It will search for a method named `check` and run it. A sensible It will search for a method named ``check`` and run it. A sensible
default is provided in the base class. default is provided in the base class.
""" """
try: try:
@ -181,9 +192,10 @@ def _run_default_function(self):
tty.msg('Skipping default sanity checks [method `check` not implemented]') # NOQA: ignore=E501 tty.msg('Skipping default sanity checks [method `check` not implemented]') # NOQA: ignore=E501
def check(self): def check(self):
"""Default test : search the Makefile for targets `test` and `check` """Default test: search the Makefile for targets ``test`` and ``check``
and run them if found. and run them if found.
""" """
with working_dir(self.build_directory()):
self._if_make_target_execute('test') self._if_make_target_execute('test')
self._if_make_target_execute('check') self._if_make_target_execute('check')

View File

@ -24,7 +24,6 @@
############################################################################## ##############################################################################
import inspect import inspect
import os
import platform import platform
import llnl.util.tty as tty import llnl.util.tty as tty
@ -35,21 +34,28 @@
class CMakePackage(PackageBase): class CMakePackage(PackageBase):
"""Specialized class for packages that are built using cmake """Specialized class for packages that are built using CMake
This class provides three phases that can be overridden: This class provides three phases that can be overridden:
- cmake
- build * cmake
- install * build
* install
They all have sensible defaults and for many packages the only thing They all have sensible defaults and for many packages the only thing
necessary will be to override `cmake_args` necessary will be to override ``cmake_args``
Additionally, you may specify make targets for build and install
phases by overriding ``build_targets`` and ``install_targets``
""" """
phases = ['cmake', 'build', 'install'] phases = ['cmake', 'build', 'install']
# To be used in UI queries that require to know which # To be used in UI queries that require to know which
# build-system class we are using # build-system class we are using
build_system_class = 'CMakePackage' build_system_class = 'CMakePackage'
build_targets = []
install_targets = ['install']
depends_on('cmake', type='build') depends_on('cmake', type='build')
def build_type(self): def build_type(self):
@ -97,8 +103,9 @@ def build_directory(self):
def cmake_args(self): def cmake_args(self):
"""Method to be overridden. Should return an iterable containing """Method to be overridden. Should return an iterable containing
all the arguments that must be passed to configure, except: all the arguments that must be passed to configure, except:
- CMAKE_INSTALL_PREFIX
- CMAKE_BUILD_TYPE * CMAKE_INSTALL_PREFIX
* CMAKE_BUILD_TYPE
""" """
return [] return []
@ -106,26 +113,25 @@ def cmake(self, spec, prefix):
"""Run cmake in the build directory""" """Run cmake in the build directory"""
options = [self.root_cmakelists_dir()] + self.std_cmake_args + \ options = [self.root_cmakelists_dir()] + self.std_cmake_args + \
self.cmake_args() self.cmake_args()
create = not os.path.exists(self.build_directory()) with working_dir(self.build_directory(), create=True):
with working_dir(self.build_directory(), create=create):
inspect.getmodule(self).cmake(*options) inspect.getmodule(self).cmake(*options)
def build(self, spec, prefix): def build(self, spec, prefix):
"""The usual `make` after cmake""" """Make the build targets"""
with working_dir(self.build_directory()): with working_dir(self.build_directory()):
inspect.getmodule(self).make() inspect.getmodule(self).make(*self.build_targets)
def install(self, spec, prefix): def install(self, spec, prefix):
"""...and the final `make install` after cmake""" """Make the install targets"""
with working_dir(self.build_directory()): with working_dir(self.build_directory()):
inspect.getmodule(self).make('install') inspect.getmodule(self).make(*self.install_targets)
@PackageBase.sanity_check('build') @PackageBase.sanity_check('build')
@PackageBase.on_package_attributes(run_tests=True) @PackageBase.on_package_attributes(run_tests=True)
def _run_default_function(self): def _run_default_function(self):
"""This function is run after build if self.run_tests == True """This function is run after build if ``self.run_tests == True``
It will search for a method named `check` and run it. A sensible It will search for a method named ``check`` and run it. A sensible
default is provided in the base class. default is provided in the base class.
""" """
try: try:
@ -136,7 +142,7 @@ def _run_default_function(self):
tty.msg('Skipping default build sanity checks [method `check` not implemented]') # NOQA: ignore=E501 tty.msg('Skipping default build sanity checks [method `check` not implemented]') # NOQA: ignore=E501
def check(self): def check(self):
"""Default test : search the Makefile for the target `test` """Default test: search the Makefile for the target ``test``
and run them if found. and run them if found.
""" """
with working_dir(self.build_directory()): with working_dir(self.build_directory()):

View File

@ -34,9 +34,10 @@ class MakefilePackage(PackageBase):
"""Specialized class for packages that are built using editable Makefiles """Specialized class for packages that are built using editable Makefiles
This class provides three phases that can be overridden: This class provides three phases that can be overridden:
- edit
- build * edit
- install * build
* install
It is necessary to override the 'edit' phase, while 'build' and 'install' It is necessary to override the 'edit' phase, while 'build' and 'install'
have sensible defaults. have sensible defaults.
@ -58,12 +59,12 @@ def edit(self, spec, prefix):
tty.msg('Using default implementation: skipping edit phase.') tty.msg('Using default implementation: skipping edit phase.')
def build(self, spec, prefix): def build(self, spec, prefix):
"""Default build phase : call make passing build_args""" """Make the build targets"""
with working_dir(self.build_directory()): with working_dir(self.build_directory()):
inspect.getmodule(self).make(*self.build_targets) inspect.getmodule(self).make(*self.build_targets)
def install(self, spec, prefix): def install(self, spec, prefix):
"""Default install phase : call make passing install_args""" """Make the install targets"""
with working_dir(self.build_directory()): with working_dir(self.build_directory()):
inspect.getmodule(self).make(*self.install_targets) inspect.getmodule(self).make(*self.install_targets)

View File

@ -33,6 +33,7 @@ class RPackage(PackageBase):
"""Specialized class for packages that are built using R """Specialized class for packages that are built using R
This class provides a single phase that can be overridden: This class provides a single phase that can be overridden:
* install * install
It has sensible defaults and for many packages the only thing It has sensible defaults and for many packages the only thing

View File

@ -54,19 +54,22 @@ class Espressopp(CMakePackage):
depends_on("fftw") depends_on("fftw")
depends_on("py-sphinx", when="+ug", type='build') depends_on("py-sphinx", when="+ug", type='build')
depends_on("py-sphinx", when="+pdf", type='build') depends_on("py-sphinx", when="+pdf", type='build')
depends_on('py-numpy', when="+ug", type='build')
depends_on('py-numpy', when="+pdf", type='build')
depends_on('py-matplotlib', when="+ug", type='build')
depends_on('py-matplotlib', when="+pdf", type='build')
depends_on("texlive", when="+pdf", type='build') depends_on("texlive", when="+pdf", type='build')
depends_on("doxygen", when="+dg", type='build') depends_on("doxygen", when="+dg", type='build')
def cmake_args(self): def build_type(self):
spec = self.spec spec = self.spec
options = []
options.extend(['-DEXTERNAL_MPI4PY=ON', '-DEXTERNAL_BOOST=ON'])
if '+debug' in spec: if '+debug' in spec:
options.extend(['-DCMAKE_BUILD_TYPE:STRING=Debug']) return 'Debug'
else: else:
options.extend(['-DCMAKE_BUILD_TYPE:STRING=Release']) return 'Release'
return options def cmake_args(self):
return ['-DEXTERNAL_MPI4PY=ON', '-DEXTERNAL_BOOST=ON']
def build(self, spec, prefix): def build(self, spec, prefix):
with working_dir(self.build_directory()): with working_dir(self.build_directory()):

View File

@ -25,7 +25,7 @@
from spack import * from spack import *
class Tcl(Package): class Tcl(AutotoolsPackage):
"""Tcl (Tool Command Language) is a very powerful but easy to """Tcl (Tool Command Language) is a very powerful but easy to
learn dynamic programming language, suitable for a very wide learn dynamic programming language, suitable for a very wide
range of uses, including web and desktop applications, range of uses, including web and desktop applications,
@ -52,10 +52,10 @@ def setup_environment(self, spack_env, env):
env.set('TCL_LIBRARY', join_path(self.prefix.lib, 'tcl{0}'.format( env.set('TCL_LIBRARY', join_path(self.prefix.lib, 'tcl{0}'.format(
self.spec.version.up_to(2)))) self.spec.version.up_to(2))))
def install(self, spec, prefix): def build_directory(self):
with working_dir('unix'): return 'unix'
configure("--prefix={0}".format(prefix))
make() @AutotoolsPackage.sanity_check('install')
make("install") def symlink_tclsh(self):
with working_dir(prefix.bin): with working_dir(self.prefix.bin):
symlink('tclsh{0}'.format(self.version.up_to(2)), 'tclsh') symlink('tclsh{0}'.format(self.version.up_to(2)), 'tclsh')

View File

@ -25,7 +25,7 @@
from spack import * from spack import *
class Tk(Package): class Tk(AutotoolsPackage):
"""Tk is a graphical user interface toolkit that takes developing """Tk is a graphical user interface toolkit that takes developing
desktop applications to a higher level than conventional desktop applications to a higher level than conventional
approaches. Tk is the standard GUI not only for Tcl, but for approaches. Tk is the standard GUI not only for Tcl, but for
@ -46,15 +46,15 @@ def url_for_version(self, version):
base_url = "http://prdownloads.sourceforge.net/tcl" base_url = "http://prdownloads.sourceforge.net/tcl"
return "{0}/tk{1}-src.tar.gz".format(base_url, version) return "{0}/tk{1}-src.tar.gz".format(base_url, version)
def setup_environment(self, spack_env, env): def setup_environment(self, spack_env, run_env):
# When using Tkinter from within spack provided python+tk, python # When using Tkinter from within spack provided python+tk, python
# will not be able to find Tcl/Tk unless TK_LIBRARY is set. # will not be able to find Tcl/Tk unless TK_LIBRARY is set.
env.set('TK_LIBRARY', join_path(self.prefix.lib, 'tk{0}'.format( run_env.set('TK_LIBRARY', join_path(self.prefix.lib, 'tk{0}'.format(
self.spec.version.up_to(2)))) self.spec.version.up_to(2))))
def install(self, spec, prefix): def build_directory(self):
with working_dir('unix'): return 'unix'
configure("--prefix={0}".format(prefix),
"--with-tcl={0}".format(spec['tcl'].prefix.lib)) def configure_args(self):
make() spec = self.spec
make("install") return ['--with-tcl={0}'.format(spec['tcl'].prefix.lib)]

View File

@ -23,7 +23,6 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
############################################################################## ##############################################################################
from spack import * from spack import *
from os import environ
class Zlib(AutotoolsPackage): class Zlib(AutotoolsPackage):
@ -42,10 +41,6 @@ class Zlib(AutotoolsPackage):
variant('pic', default=True, variant('pic', default=True,
description='Produce position-independent code (for shared libs)') description='Produce position-independent code (for shared libs)')
def configure(self, spec, prefix): def setup_environment(self, spack_env, run_env):
if '+pic' in self.spec:
if '+pic' in spec: spack_env.set('CFLAGS', self.compiler.pic_flag)
environ['CFLAGS'] = self.compiler.pic_flag
config_args = ['--prefix', prefix]
configure(*config_args)