Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 12 commits into from
Jan 16, 2017
52 changes: 32 additions & 20 deletions lib/spack/spack/build_systems/autotools.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,23 +31,25 @@
from subprocess import check_call

import llnl.util.tty as tty
from llnl.util.filesystem import working_dir
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

* autoreconf
* configure
* build
* install

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
phases by overriding `build_targets` and `install_targets`
phases by overriding ``build_targets`` and ``install_targets``
"""
phases = ['autoreconf', 'configure', 'build', 'install']
# To be used in UI queries that require to know which
Expand Down Expand Up @@ -124,6 +126,10 @@ def do_patch_config_guess(self):

return False

def build_directory(self):
"""Override to provide another place to build the package"""
return self.stage.source_path

def patch(self):
"""Perform any required patches."""

Expand All @@ -138,39 +144,44 @@ def autoreconf(self, spec, prefix):

@PackageBase.sanity_check('autoreconf')
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"""
if not os.path.exists('configure'):
raise RuntimeError(
'configure script not found in {0}'.format(os.getcwd()))
with working_dir(self.build_directory()):
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
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`
"""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)

with working_dir(self.build_directory()):
inspect.getmodule(self).configure(*options)

def build(self, spec, prefix):
"""Make the build targets"""
inspect.getmodule(self).make(*self.build_targets)
with working_dir(self.build_directory()):
inspect.getmodule(self).make(*self.build_targets)

def install(self, spec, prefix):
"""Make the install targets"""
inspect.getmodule(self).make(*self.install_targets)
with working_dir(self.build_directory()):
inspect.getmodule(self).make(*self.install_targets)

@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
"""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.
"""
try:
Expand All @@ -181,11 +192,12 @@ def _run_default_function(self):
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`
"""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')
with working_dir(self.build_directory()):
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)
40 changes: 23 additions & 17 deletions lib/spack/spack/build_systems/cmake.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
##############################################################################

import inspect
import os
import platform

import llnl.util.tty as tty
Expand All @@ -35,21 +34,28 @@


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:
- cmake
- build
- install

* cmake
* build
* install

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']
# To be used in UI queries that require to know which
# build-system class we are using
build_system_class = 'CMakePackage'

build_targets = []
install_targets = ['install']

depends_on('cmake', type='build')

def build_type(self):
Expand Down Expand Up @@ -97,35 +103,35 @@ def build_directory(self):
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

* 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):
with working_dir(self.build_directory(), create=True):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@adamjstewart Will this work in case the build directory already exists?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I believe so. I'll have to check how we define working_dir, but if I remember correctly it only creates the directory if it doesn't exist.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like it calls mkdirp, which does indeed check to see whether or not the directory exists first, so we can set create=True and not worry.

inspect.getmodule(self).cmake(*options)

def build(self, spec, prefix):
"""The usual `make` after cmake"""
"""Make the build targets"""
with working_dir(self.build_directory()):
inspect.getmodule(self).make()
inspect.getmodule(self).make(*self.build_targets)

def install(self, spec, prefix):
"""...and the final `make install` after cmake"""
"""Make the install targets"""
with working_dir(self.build_directory()):
inspect.getmodule(self).make('install')
inspect.getmodule(self).make(*self.install_targets)

@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
"""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.
"""
try:
Expand All @@ -136,7 +142,7 @@ def _run_default_function(self):
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`
"""Default test: search the Makefile for the target ``test``
and run them if found.
"""
with working_dir(self.build_directory()):
Expand Down
11 changes: 6 additions & 5 deletions lib/spack/spack/build_systems/makefile.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,10 @@ 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

* edit
* build
* install

It is necessary to override the 'edit' phase, while 'build' and 'install'
have sensible defaults.
Expand All @@ -58,12 +59,12 @@ def edit(self, spec, prefix):
tty.msg('Using default implementation: skipping edit phase.')

def build(self, spec, prefix):
"""Default build phase : call make passing build_args"""
"""Make the build targets"""
with working_dir(self.build_directory()):
inspect.getmodule(self).make(*self.build_targets)

def install(self, spec, prefix):
"""Default install phase : call make passing install_args"""
"""Make the install targets"""
with working_dir(self.build_directory()):
inspect.getmodule(self).make(*self.install_targets)

Expand Down
1 change: 1 addition & 0 deletions lib/spack/spack/build_systems/r.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class RPackage(PackageBase):
"""Specialized class for packages that are built using R

This class provides a single phase that can be overridden:

* install

It has sensible defaults and for many packages the only thing
Expand Down
21 changes: 12 additions & 9 deletions var/spack/repos/builtin/packages/espressopp/package.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class Espressopp(CMakePackage):
url = "https://github.com/espressopp/espressopp/tarball/v1.9.4.1"

version('develop', git='https://github.com/espressopp/espressopp.git', branch='master')
version('1.9.4.1', '0da74a6d4e1bfa6a2a24fca354245a4f')
version('1.9.4.1', '0da74a6d4e1bfa6a2a24fca354245a4f')
version('1.9.4', 'f2a27993a83547ad014335006eea74ea')

variant('debug', default=False, description='Build debug version')
Expand All @@ -54,20 +54,23 @@ class Espressopp(CMakePackage):
depends_on("fftw")
depends_on("py-sphinx", when="+ug", 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("doxygen", when="+dg", type='build')

def cmake_args(self):
def build_type(self):
spec = self.spec
options = []
options.extend(['-DEXTERNAL_MPI4PY=ON', '-DEXTERNAL_BOOST=ON'])
if '+debug' in spec:
options.extend(['-DCMAKE_BUILD_TYPE:STRING=Debug'])
return 'Debug'
else:
options.extend(['-DCMAKE_BUILD_TYPE:STRING=Release'])

return options

return 'Release'

def cmake_args(self):
return ['-DEXTERNAL_MPI4PY=ON', '-DEXTERNAL_BOOST=ON']

def build(self, spec, prefix):
with working_dir(self.build_directory()):
make()
Expand Down
14 changes: 7 additions & 7 deletions var/spack/repos/builtin/packages/tcl/package.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
from spack import *


class Tcl(Package):
class Tcl(AutotoolsPackage):
"""Tcl (Tool Command Language) is a very powerful but easy to
learn dynamic programming language, suitable for a very wide
range of uses, including web and desktop applications,
Expand All @@ -52,10 +52,10 @@ def setup_environment(self, spack_env, env):
env.set('TCL_LIBRARY', join_path(self.prefix.lib, 'tcl{0}'.format(
self.spec.version.up_to(2))))

def install(self, spec, prefix):
with working_dir('unix'):
configure("--prefix={0}".format(prefix))
make()
make("install")
with working_dir(prefix.bin):
def build_directory(self):
return 'unix'

@AutotoolsPackage.sanity_check('install')
def symlink_tclsh(self):
with working_dir(self.prefix.bin):
symlink('tclsh{0}'.format(self.version.up_to(2)), 'tclsh')
22 changes: 11 additions & 11 deletions var/spack/repos/builtin/packages/tk/package.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
from spack import *


class Tk(Package):
class Tk(AutotoolsPackage):
"""Tk is a graphical user interface toolkit that takes developing
desktop applications to a higher level than conventional
approaches. Tk is the standard GUI not only for Tcl, but for
Expand All @@ -46,15 +46,15 @@ def url_for_version(self, version):
base_url = "http://prdownloads.sourceforge.net/tcl"
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
# 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(
self.spec.version.up_to(2))))

def install(self, spec, prefix):
with working_dir('unix'):
configure("--prefix={0}".format(prefix),
"--with-tcl={0}".format(spec['tcl'].prefix.lib))
make()
make("install")
run_env.set('TK_LIBRARY', join_path(self.prefix.lib, 'tk{0}'.format(
self.spec.version.up_to(2))))

def build_directory(self):
return 'unix'

def configure_args(self):
spec = self.spec
return ['--with-tcl={0}'.format(spec['tcl'].prefix.lib)]
13 changes: 4 additions & 9 deletions var/spack/repos/builtin/packages/zlib/package.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##############################################################################
from spack import *
from os import environ


class Zlib(AutotoolsPackage):
Expand All @@ -35,17 +34,13 @@ class Zlib(AutotoolsPackage):

version('1.2.10', 'd9794246f853d15ce0fcbf79b9a3cf13')
# author had this to say about 1.2.9....
# Due to the bug fixes, any installations of 1.2.9 should be immediately
# Due to the bug fixes, any installations of 1.2.9 should be immediately
# replaced with 1.2.10.
version('1.2.8', '44d667c142d7cda120332623eab69f40')

variant('pic', default=True,
description='Produce position-independent code (for shared libs)')

def configure(self, spec, prefix):

if '+pic' in spec:
environ['CFLAGS'] = self.compiler.pic_flag

config_args = ['--prefix', prefix]
configure(*config_args)
def setup_environment(self, spack_env, run_env):
if '+pic' in self.spec:
spack_env.set('CFLAGS', self.compiler.pic_flag)