summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMassimiliano Culpo <massimiliano.culpo@gmail.com>2022-10-26 20:17:32 +0200
committerGitHub <noreply@github.com>2022-10-26 20:17:32 +0200
commit30c9ff50dd048573e3cd7a63a8ab9c05c0bee339 (patch)
tree188f4739697adc00669c75b7e91b1d1e769db97e
parent83ee5001086672a45e140d5858eacc0b8ef90941 (diff)
downloadspack-30c9ff50dd048573e3cd7a63a8ab9c05c0bee339.tar.gz
spack-30c9ff50dd048573e3cd7a63a8ab9c05c0bee339.tar.bz2
spack-30c9ff50dd048573e3cd7a63a8ab9c05c0bee339.tar.xz
spack-30c9ff50dd048573e3cd7a63a8ab9c05c0bee339.zip
Allow for packages with multiple build-systems (#30738)
This commit extends the DSL that can be used in packages to allow declaring that a package uses different build-systems under different conditions. It requires each spec to have a `build_system` single valued variant. The variant can be used in many context to query, manipulate or select the build system associated with a concrete spec. The knowledge to build a package has been moved out of the PackageBase hierarchy, into a new Builder hierarchy. Customization of the default behavior for a given builder can be obtained by coding a new derived builder in package.py. The "run_after" and "run_before" decorators are now applied to methods on the builder. They can also incorporate a "when=" argument to specify that a method is run only when certain conditions apply. For packages that do not define their own builder, forwarding logic is added between the builder and package (methods not found in one will be retrieved from the other); this PR is expected to be fully backwards compatible with unmodified packages that use a single build system.
-rw-r--r--lib/spack/docs/build_systems.rst1
-rw-r--r--lib/spack/docs/build_systems/autotoolspackage.rst8
-rw-r--r--lib/spack/docs/build_systems/bundlepackage.rst6
-rw-r--r--lib/spack/docs/build_systems/cmakepackage.rst12
-rw-r--r--lib/spack/docs/build_systems/luapackage.rst10
-rw-r--r--lib/spack/docs/build_systems/makefilepackage.rst8
-rw-r--r--lib/spack/docs/build_systems/mavenpackage.rst8
-rw-r--r--lib/spack/docs/build_systems/mesonpackage.rst8
-rw-r--r--lib/spack/docs/build_systems/multiplepackage.rst350
-rw-r--r--lib/spack/docs/build_systems/octavepackage.rst8
-rw-r--r--lib/spack/docs/build_systems/perlpackage.rst8
-rw-r--r--lib/spack/docs/build_systems/qmakepackage.rst8
-rw-r--r--lib/spack/docs/build_systems/racketpackage.rst8
-rw-r--r--lib/spack/docs/build_systems/rpackage.rst2
-rw-r--r--lib/spack/docs/build_systems/rubypackage.rst8
-rw-r--r--lib/spack/docs/build_systems/sconspackage.rst8
-rw-r--r--lib/spack/docs/build_systems/sippackage.rst8
-rw-r--r--lib/spack/docs/build_systems/wafpackage.rst8
-rw-r--r--lib/spack/docs/conf.py1
-rw-r--r--lib/spack/docs/developer_guide.rst8
-rw-r--r--lib/spack/docs/features.rst32
-rw-r--r--lib/spack/docs/images/adapter.pngbin0 -> 673639 bytes
-rw-r--r--lib/spack/docs/images/builder_package_architecture.pngbin0 -> 460127 bytes
-rw-r--r--lib/spack/docs/images/builder_phases.pngbin0 -> 131216 bytes
-rw-r--r--lib/spack/docs/images/installation_pipeline.pngbin0 -> 129116 bytes
-rw-r--r--lib/spack/docs/images/original_package_architecture.pngbin0 -> 35639 bytes
-rw-r--r--lib/spack/docs/images/packaging.excalidrawlib3092
-rw-r--r--lib/spack/docs/packaging_guide.rst644
-rw-r--r--lib/spack/spack/audit.py35
-rw-r--r--lib/spack/spack/build_environment.py59
-rw-r--r--lib/spack/spack/build_systems/_checks.py124
-rw-r--r--lib/spack/spack/build_systems/aspell_dict.py61
-rw-r--r--lib/spack/spack/build_systems/autotools.py431
-rw-r--r--lib/spack/spack/build_systems/bundle.py31
-rw-r--r--lib/spack/spack/build_systems/cached_cmake.py90
-rw-r--r--lib/spack/spack/build_systems/cmake.py310
-rw-r--r--lib/spack/spack/build_systems/generic.py44
-rw-r--r--lib/spack/spack/build_systems/intel.py31
-rw-r--r--lib/spack/spack/build_systems/lua.py100
-rw-r--r--lib/spack/spack/build_systems/makefile.py137
-rw-r--r--lib/spack/spack/build_systems/maven.py69
-rw-r--r--lib/spack/spack/build_systems/meson.py183
-rw-r--r--lib/spack/spack/build_systems/nmake.py102
-rw-r--r--lib/spack/spack/build_systems/octave.py61
-rw-r--r--lib/spack/spack/build_systems/oneapi.py11
-rw-r--r--lib/spack/spack/build_systems/perl.py87
-rw-r--r--lib/spack/spack/build_systems/python.py434
-rw-r--r--lib/spack/spack/build_systems/qmake.py77
-rw-r--r--lib/spack/spack/build_systems/r.py87
-rw-r--r--lib/spack/spack/build_systems/racket.py70
-rw-r--r--lib/spack/spack/build_systems/ruby.py57
-rw-r--r--lib/spack/spack/build_systems/scons.py77
-rw-r--r--lib/spack/spack/build_systems/sip.py111
-rw-r--r--lib/spack/spack/build_systems/waf.py80
-rw-r--r--lib/spack/spack/builder.py574
-rw-r--r--lib/spack/spack/cmd/info.py4
-rw-r--r--lib/spack/spack/cmd/uninstall.py2
-rw-r--r--lib/spack/spack/detection/common.py2
-rw-r--r--lib/spack/spack/directives.py18
-rw-r--r--lib/spack/spack/installer.py53
-rw-r--r--lib/spack/spack/mixins.py105
-rw-r--r--lib/spack/spack/multimethod.py20
-rw-r--r--lib/spack/spack/package.py10
-rw-r--r--lib/spack/spack/package_base.py316
-rw-r--r--lib/spack/spack/patch.py2
-rw-r--r--lib/spack/spack/test/audit.py2
-rw-r--r--lib/spack/spack/test/build_systems.py24
-rw-r--r--lib/spack/spack/test/builder.py123
-rw-r--r--lib/spack/spack/test/cmd/info.py4
-rw-r--r--lib/spack/spack/test/cmd/install.py2
-rw-r--r--lib/spack/spack/test/cmd/test.py3
-rw-r--r--lib/spack/spack/test/concretize.py2
-rw-r--r--lib/spack/spack/test/concretize_requirements.py3
-rw-r--r--lib/spack/spack/test/config.py5
-rw-r--r--lib/spack/spack/test/graph.py13
-rw-r--r--lib/spack/spack/test/install.py38
-rw-r--r--lib/spack/spack/test/repo.py4
-rw-r--r--lib/spack/spack/util/package_hash.py3
-rw-r--r--lib/spack/spack/util/web.py2
-rw-r--r--lib/spack/spack/variant.py2
-rw-r--r--share/spack/gitlab/cloud_pipelines/stacks/aws-ahug-aarch64/spack.yaml2
-rw-r--r--share/spack/gitlab/cloud_pipelines/stacks/aws-isc-aarch64/spack.yaml2
-rw-r--r--share/spack/gitlab/cloud_pipelines/stacks/aws-isc/spack.yaml2
-rw-r--r--share/spack/gitlab/cloud_pipelines/stacks/build_systems/spack.yaml2
-rw-r--r--share/spack/gitlab/cloud_pipelines/stacks/e4s/spack.yaml2
-rw-r--r--share/spack/gitlab/cloud_pipelines/stacks/tutorial/spack.yaml2
-rw-r--r--var/spack/repos/builder.test/packages/callbacks/package.py45
-rw-r--r--var/spack/repos/builder.test/packages/custom-phases/package.py31
-rw-r--r--var/spack/repos/builder.test/packages/gnuconfig/package.py15
-rw-r--r--var/spack/repos/builder.test/packages/inheritance/package.py26
-rw-r--r--var/spack/repos/builder.test/packages/old-style-autotools/package.py50
-rw-r--r--var/spack/repos/builder.test/packages/old-style-custom-phases/package.py34
-rw-r--r--var/spack/repos/builder.test/packages/old-style-derived/package.py21
-rw-r--r--var/spack/repos/builder.test/repo.yaml2
-rw-r--r--var/spack/repos/builtin.mock/packages/a/package.py12
-rw-r--r--var/spack/repos/builtin.mock/packages/attributes-foo/package.py1
-rw-r--r--var/spack/repos/builtin.mock/packages/canfail/package.py12
-rw-r--r--var/spack/repos/builtin.mock/packages/cmake-client/package.py10
-rw-r--r--var/spack/repos/builtin.mock/packages/cmake/package.py7
-rw-r--r--var/spack/repos/builtin.mock/packages/dev-build-test-dependent/package.py7
-rw-r--r--var/spack/repos/builtin.mock/packages/dev-build-test-install-phases/package.py1
-rw-r--r--var/spack/repos/builtin.mock/packages/dev-build-test-install/package.py9
-rw-r--r--var/spack/repos/builtin.mock/packages/fail-test-audit/package.py21
-rw-r--r--var/spack/repos/builtin.mock/packages/libtool-deletion/package.py13
-rw-r--r--var/spack/repos/builtin.mock/packages/libtool-installation/package.py15
-rw-r--r--var/spack/repos/builtin.mock/packages/nosource-install/package.py3
-rw-r--r--var/spack/repos/builtin.mock/packages/test-build-callbacks/package.py21
-rw-r--r--var/spack/repos/builtin.mock/packages/test-install-callbacks/package.py16
-rw-r--r--var/spack/repos/builtin.mock/packages/transitive-conditional-virtual-dependency/package.py4
-rw-r--r--var/spack/repos/builtin.mock/packages/trivial-smoke-test/package.py3
-rw-r--r--var/spack/repos/builtin/packages/alpgen/package.py119
-rw-r--r--var/spack/repos/builtin/packages/arpack-ng/package.py111
-rw-r--r--var/spack/repos/builtin/packages/binutils/package.py136
-rw-r--r--var/spack/repos/builtin/packages/clara/package.py24
-rw-r--r--var/spack/repos/builtin/packages/flex/package.py29
-rw-r--r--var/spack/repos/builtin/packages/gobject-introspection/package.py32
-rw-r--r--var/spack/repos/builtin/packages/harfbuzz/package.py67
-rw-r--r--var/spack/repos/builtin/packages/ibm-databroker/package.py2
-rw-r--r--var/spack/repos/builtin/packages/intel-tbb/package.py106
-rw-r--r--var/spack/repos/builtin/packages/json-c/package.py23
-rw-r--r--var/spack/repos/builtin/packages/libtree/package.py36
-rw-r--r--var/spack/repos/builtin/packages/libxkbcommon/package.py32
-rw-r--r--var/spack/repos/builtin/packages/lua/package.py9
-rw-r--r--var/spack/repos/builtin/packages/mesa/package.py44
-rw-r--r--var/spack/repos/builtin/packages/metis/gklib_path.patch11
-rw-r--r--var/spack/repos/builtin/packages/metis/package.py258
-rw-r--r--var/spack/repos/builtin/packages/mmg/package.py33
-rw-r--r--var/spack/repos/builtin/packages/nasm/package.py19
-rw-r--r--var/spack/repos/builtin/packages/netlib-lapack/package.py71
-rw-r--r--var/spack/repos/builtin/packages/openmpi/package.py6
-rw-r--r--var/spack/repos/builtin/packages/plasma/package.py55
-rw-r--r--var/spack/repos/builtin/packages/py-dm-tree/package.py13
-rw-r--r--var/spack/repos/builtin/packages/py-onnx-runtime/package.py2
-rw-r--r--var/spack/repos/builtin/packages/py-pillow/package.py15
-rw-r--r--var/spack/repos/builtin/packages/py-pybind11/package.py54
-rw-r--r--var/spack/repos/builtin/packages/py-pykokkos-base/package.py2
-rw-r--r--var/spack/repos/builtin/packages/py-tfdlpack/package.py2
-rw-r--r--var/spack/repos/builtin/packages/quantum-espresso/package.py55
-rw-r--r--var/spack/repos/builtin/packages/racket/package.py59
-rw-r--r--var/spack/repos/builtin/packages/rkt-base/package.py2
-rw-r--r--var/spack/repos/builtin/packages/rkt-cext-lib/package.py1
-rw-r--r--var/spack/repos/builtin/packages/rkt-compiler-lib/package.py2
-rw-r--r--var/spack/repos/builtin/packages/rkt-dynext-lib/package.py1
-rw-r--r--var/spack/repos/builtin/packages/rkt-rackunit-lib/package.py1
-rw-r--r--var/spack/repos/builtin/packages/rkt-scheme-lib/package.py1
-rw-r--r--var/spack/repos/builtin/packages/rkt-testing-util-lib/package.py1
-rw-r--r--var/spack/repos/builtin/packages/rkt-zo-lib/package.py2
-rw-r--r--var/spack/repos/builtin/packages/ruby/package.py133
-rw-r--r--var/spack/repos/builtin/packages/scotch/package.py129
-rw-r--r--var/spack/repos/builtin/packages/superlu/package.py143
-rw-r--r--var/spack/repos/builtin/packages/swig/package.py36
-rw-r--r--var/spack/repos/builtin/packages/sz/package.py125
-rw-r--r--var/spack/repos/builtin/packages/timemory/package.py2
-rw-r--r--var/spack/repos/builtin/packages/uncrustify/package.py35
-rw-r--r--var/spack/repos/builtin/packages/zlib/package.py59
155 files changed, 7465 insertions, 3573 deletions
diff --git a/lib/spack/docs/build_systems.rst b/lib/spack/docs/build_systems.rst
index 12191d29f0..1ce8d6746e 100644
--- a/lib/spack/docs/build_systems.rst
+++ b/lib/spack/docs/build_systems.rst
@@ -65,7 +65,6 @@ on these ideas for each distinct build system that Spack supports:
build_systems/custompackage
build_systems/inteloneapipackage
build_systems/intelpackage
- build_systems/multiplepackage
build_systems/rocmpackage
build_systems/sourceforgepackage
diff --git a/lib/spack/docs/build_systems/autotoolspackage.rst b/lib/spack/docs/build_systems/autotoolspackage.rst
index d341d28d08..88fabc0c5d 100644
--- a/lib/spack/docs/build_systems/autotoolspackage.rst
+++ b/lib/spack/docs/build_systems/autotoolspackage.rst
@@ -5,9 +5,9 @@
.. _autotoolspackage:
-----------------
-AutotoolsPackage
-----------------
+---------
+Autotools
+---------
Autotools is a GNU build system that provides a build-script generator.
By running the platform-independent ``./configure`` script that comes
@@ -17,7 +17,7 @@ with the package, you can generate a platform-dependent Makefile.
Phases
^^^^^^
-The ``AutotoolsPackage`` base class comes with the following phases:
+The ``AutotoolsBuilder`` and ``AutotoolsPackage`` base classes come with the following phases:
#. ``autoreconf`` - generate the configure script
#. ``configure`` - generate the Makefiles
diff --git a/lib/spack/docs/build_systems/bundlepackage.rst b/lib/spack/docs/build_systems/bundlepackage.rst
index 8787dce546..7a826f5e17 100644
--- a/lib/spack/docs/build_systems/bundlepackage.rst
+++ b/lib/spack/docs/build_systems/bundlepackage.rst
@@ -5,9 +5,9 @@
.. _bundlepackage:
--------------
-BundlePackage
--------------
+------
+Bundle
+------
``BundlePackage`` represents a set of packages that are expected to work well
together, such as a collection of commonly used software libraries. The
diff --git a/lib/spack/docs/build_systems/cmakepackage.rst b/lib/spack/docs/build_systems/cmakepackage.rst
index 9544a7df73..7a1db842de 100644
--- a/lib/spack/docs/build_systems/cmakepackage.rst
+++ b/lib/spack/docs/build_systems/cmakepackage.rst
@@ -5,9 +5,9 @@
.. _cmakepackage:
-------------
-CMakePackage
-------------
+-----
+CMake
+-----
Like Autotools, CMake is a widely-used build-script generator. Designed
by Kitware, CMake is the most popular build system for new C, C++, and
@@ -21,7 +21,7 @@ whereas Autotools is Unix-only.
Phases
^^^^^^
-The ``CMakePackage`` base class comes with the following phases:
+The ``CMakeBuilder`` and ``CMakePackage`` base classes come with the following phases:
#. ``cmake`` - generate the Makefile
#. ``build`` - build the package
@@ -130,8 +130,8 @@ Adding flags to cmake
To add additional flags to the ``cmake`` call, simply override the
``cmake_args`` function. The following example defines values for the flags
``WHATEVER``, ``ENABLE_BROKEN_FEATURE``, ``DETECT_HDF5``, and ``THREADS`` with
-and without the :meth:`~spack.build_systems.cmake.CMakePackage.define` and
-:meth:`~spack.build_systems.cmake.CMakePackage.define_from_variant` helper functions:
+and without the :meth:`~spack.build_systems.cmake.CMakeBuilder.define` and
+:meth:`~spack.build_systems.cmake.CMakeBuilder.define_from_variant` helper functions:
.. code-block:: python
diff --git a/lib/spack/docs/build_systems/luapackage.rst b/lib/spack/docs/build_systems/luapackage.rst
index 6332edfc20..fd70f90c49 100644
--- a/lib/spack/docs/build_systems/luapackage.rst
+++ b/lib/spack/docs/build_systems/luapackage.rst
@@ -5,11 +5,11 @@
.. _luapackage:
-------------
-LuaPackage
-------------
+---
+Lua
+---
-LuaPackage is a helper for the common case of Lua packages that provide
+The ``Lua`` build-system is a helper for the common case of Lua packages that provide
a rockspec file. This is not meant to take a rock archive, but to build
a source archive or repository that provides a rockspec, which should cover
most lua packages. In the case a Lua package builds by Make rather than
@@ -19,7 +19,7 @@ luarocks, prefer MakefilePackage.
Phases
^^^^^^
-The ``LuaPackage`` base class comes with the following phases:
+The ``LuaBuilder`` and `LuaPackage`` base classes come with the following phases:
#. ``unpack`` - if using a rock, unpacks the rock and moves into the source directory
#. ``preprocess`` - adjust sources or rockspec to fix build
diff --git a/lib/spack/docs/build_systems/makefilepackage.rst b/lib/spack/docs/build_systems/makefilepackage.rst
index c092432037..5a83d612fa 100644
--- a/lib/spack/docs/build_systems/makefilepackage.rst
+++ b/lib/spack/docs/build_systems/makefilepackage.rst
@@ -5,9 +5,9 @@
.. _makefilepackage:
----------------
-MakefilePackage
----------------
+--------
+Makefile
+--------
The most primitive build system a package can use is a plain Makefile.
Makefiles are simple to write for small projects, but they usually
@@ -18,7 +18,7 @@ variables.
Phases
^^^^^^
-The ``MakefilePackage`` base class comes with 3 phases:
+The ``MakefileBuilder`` and ``MakefilePackage`` base classes come with 3 phases:
#. ``edit`` - edit the Makefile
#. ``build`` - build the project
diff --git a/lib/spack/docs/build_systems/mavenpackage.rst b/lib/spack/docs/build_systems/mavenpackage.rst
index 94ce128d3a..d1237ce34c 100644
--- a/lib/spack/docs/build_systems/mavenpackage.rst
+++ b/lib/spack/docs/build_systems/mavenpackage.rst
@@ -5,9 +5,9 @@
.. _mavenpackage:
-------------
-MavenPackage
-------------
+-----
+Maven
+-----
Apache Maven is a general-purpose build system that does not rely
on Makefiles to build software. It is designed for building and
@@ -17,7 +17,7 @@ managing and Java-based project.
Phases
^^^^^^
-The ``MavenPackage`` base class comes with the following phases:
+The ``MavenBuilder`` and ``MavenPackage`` base classes come with the following phases:
#. ``build`` - compile code and package into a JAR file
#. ``install`` - copy to installation prefix
diff --git a/lib/spack/docs/build_systems/mesonpackage.rst b/lib/spack/docs/build_systems/mesonpackage.rst
index 5ca444dcb1..c32b2241bc 100644
--- a/lib/spack/docs/build_systems/mesonpackage.rst
+++ b/lib/spack/docs/build_systems/mesonpackage.rst
@@ -5,9 +5,9 @@
.. _mesonpackage:
-------------
-MesonPackage
-------------
+-----
+Meson
+-----
Much like Autotools and CMake, Meson is a build system. But it is
meant to be both fast and as user friendly as possible. GNOME's goal
@@ -17,7 +17,7 @@ is to port modules to use the Meson build system.
Phases
^^^^^^
-The ``MesonPackage`` base class comes with the following phases:
+The ``MesonBuilder`` and ``MesonPackage`` base classes come with the following phases:
#. ``meson`` - generate ninja files
#. ``build`` - build the project
diff --git a/lib/spack/docs/build_systems/multiplepackage.rst b/lib/spack/docs/build_systems/multiplepackage.rst
deleted file mode 100644
index 71751f0dbf..0000000000
--- a/lib/spack/docs/build_systems/multiplepackage.rst
+++ /dev/null
@@ -1,350 +0,0 @@
-.. Copyright 2013-2022 Lawrence Livermore National Security, LLC and other
- Spack Project Developers. See the top-level COPYRIGHT file for details.
-
- SPDX-License-Identifier: (Apache-2.0 OR MIT)
-
-.. _multiplepackage:
-
-----------------------
-Multiple Build Systems
-----------------------
-
-Quite frequently, a package will change build systems from one version to the
-next. For example, a small project that once used a single Makefile to build
-may now require Autotools to handle the increased number of files that need to
-be compiled. Or, a package that once used Autotools may switch to CMake for
-Windows support. In this case, it becomes a bit more challenging to write a
-single build recipe for this package in Spack.
-
-There are several ways that this can be handled in Spack:
-
-#. Subclass the new build system, and override phases as needed (preferred)
-#. Subclass ``Package`` and implement ``install`` as needed
-#. Create separate ``*-cmake``, ``*-autotools``, etc. packages for each build system
-#. Rename the old package to ``*-legacy`` and create a new package
-#. Move the old package to a ``legacy`` repository and create a new package
-#. Drop older versions that only support the older build system
-
-Of these options, 1 is preferred, and will be demonstrated in this
-documentation. Options 3-5 have issues with concretization, so shouldn't be
-used. Options 4-5 also don't support more than two build systems. Option 6 only
-works if the old versions are no longer needed. Option 1 is preferred over 2
-because it makes it easier to drop the old build system entirely.
-
-The exact syntax of the package depends on which build systems you need to
-support. Below are a couple of common examples.
-
-^^^^^^^^^^^^^^^^^^^^^
-Makefile -> Autotools
-^^^^^^^^^^^^^^^^^^^^^
-
-Let's say we have the following package:
-
-.. code-block:: python
-
- class Foo(MakefilePackage):
- version("1.2.0", sha256="...")
-
- def edit(self, spec, prefix):
- filter_file("CC=", "CC=" + spack_cc, "Makefile")
-
- def install(self, spec, prefix):
- install_tree(".", prefix)
-
-
-The package subclasses from :ref:`makefilepackage`, which has three phases:
-
-#. ``edit`` (does nothing by default)
-#. ``build`` (runs ``make`` by default)
-#. ``install`` (runs ``make install`` by default)
-
-In this case, the ``install`` phase needed to be overridden because the
-Makefile did not have an install target. We also modify the Makefile to use
-Spack's compiler wrappers. The default ``build`` phase is not changed.
-
-Starting with version 1.3.0, we want to use Autotools to build instead.
-:ref:`autotoolspackage` has four phases:
-
-#. ``autoreconf`` (does not if a configure script already exists)
-#. ``configure`` (runs ``./configure --prefix=...`` by default)
-#. ``build`` (runs ``make`` by default)
-#. ``install`` (runs ``make install`` by default)
-
-If the only version we need to support is 1.3.0, the package would look as
-simple as:
-
-.. code-block:: python
-
- class Foo(AutotoolsPackage):
- version("1.3.0", sha256="...")
-
- def configure_args(self):
- return ["--enable-shared"]
-
-
-In this case, we use the default methods for each phase and only override
-``configure_args`` to specify additional flags to pass to ``./configure``.
-
-If we wanted to write a single package that supports both versions 1.2.0 and
-1.3.0, it would look something like:
-
-.. code-block:: python
-
- class Foo(AutotoolsPackage):
- version("1.3.0", sha256="...")
- version("1.2.0", sha256="...", deprecated=True)
-
- def configure_args(self):
- return ["--enable-shared"]
-
- # Remove the following once version 1.2.0 is dropped
- @when("@:1.2")
- def patch(self):
- filter_file("CC=", "CC=" + spack_cc, "Makefile")
-
- @when("@:1.2")
- def autoreconf(self, spec, prefix):
- pass
-
- @when("@:1.2")
- def configure(self, spec, prefix):
- pass
-
- @when("@:1.2")
- def install(self, spec, prefix):
- install_tree(".", prefix)
-
-
-There are a few interesting things to note here:
-
-* We added ``deprecated=True`` to version 1.2.0. This signifies that version
- 1.2.0 is deprecated and shouldn't be used. However, if a user still relies
- on version 1.2.0, it's still there and builds just fine.
-* We moved the contents of the ``edit`` phase to the ``patch`` function. Since
- ``AutotoolsPackage`` doesn't have an ``edit`` phase, the only way for this
- step to be executed is to move it to the ``patch`` function, which always
- gets run.
-* The ``autoreconf`` and ``configure`` phases become no-ops. Since the old
- Makefile-based build system doesn't use these, we ignore these phases when
- building ``foo@1.2.0``.
-* The ``@when`` decorator is used to override these phases only for older
- versions. The default methods are used for ``foo@1.3:``.
-
-Once a new Spack release comes out, version 1.2.0 and everything below the
-comment can be safely deleted. The result is the same as if we had written a
-package for version 1.3.0 from scratch.
-
-^^^^^^^^^^^^^^^^^^
-Autotools -> CMake
-^^^^^^^^^^^^^^^^^^
-
-Let's say we have the following package:
-
-.. code-block:: python
-
- class Bar(AutotoolsPackage):
- version("1.2.0", sha256="...")
-
- def configure_args(self):
- return ["--enable-shared"]
-
-
-The package subclasses from :ref:`autotoolspackage`, which has four phases:
-
-#. ``autoreconf`` (does not if a configure script already exists)
-#. ``configure`` (runs ``./configure --prefix=...`` by default)
-#. ``build`` (runs ``make`` by default)
-#. ``install`` (runs ``make install`` by default)
-
-In this case, we use the default methods for each phase and only override
-``configure_args`` to specify additional flags to pass to ``./configure``.
-
-Starting with version 1.3.0, we want to use CMake to build instead.
-:ref:`cmakepackage` has three phases:
-
-#. ``cmake`` (runs ``cmake ...`` by default)
-#. ``build`` (runs ``make`` by default)
-#. ``install`` (runs ``make install`` by default)
-
-If the only version we need to support is 1.3.0, the package would look as
-simple as:
-
-.. code-block:: python
-
- class Bar(CMakePackage):
- version("1.3.0", sha256="...")
-
- def cmake_args(self):
- return [self.define("BUILD_SHARED_LIBS", True)]
-
-
-In this case, we use the default methods for each phase and only override
-``cmake_args`` to specify additional flags to pass to ``cmake``.
-
-If we wanted to write a single package that supports both versions 1.2.0 and
-1.3.0, it would look something like:
-
-.. code-block:: python
-
- class Bar(CMakePackage):
- version("1.3.0", sha256="...")
- version("1.2.0", sha256="...", deprecated=True)
-
- def cmake_args(self):
- return [self.define("BUILD_SHARED_LIBS", True)]
-
- # Remove the following once version 1.2.0 is dropped
- def configure_args(self):
- return ["--enable-shared"]
-
- @when("@:1.2")
- def cmake(self, spec, prefix):
- configure("--prefix=" + prefix, *self.configure_args())
-
-
-There are a few interesting things to note here:
-
-* We added ``deprecated=True`` to version 1.2.0. This signifies that version
- 1.2.0 is deprecated and shouldn't be used. However, if a user still relies
- on version 1.2.0, it's still there and builds just fine.
-* Since CMake and Autotools are so similar, we only need to override the
- ``cmake`` phase, we can use the default ``build`` and ``install`` phases.
-* We override ``cmake`` to run ``./configure`` for older versions.
- ``configure_args`` remains the same.
-* The ``@when`` decorator is used to override these phases only for older
- versions. The default methods are used for ``bar@1.3:``.
-
-Once a new Spack release comes out, version 1.2.0 and everything below the
-comment can be safely deleted. The result is the same as if we had written a
-package for version 1.3.0 from scratch.
-
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-Multiple build systems for the same version
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-During the transition from one build system to another, developers often
-support multiple build systems at the same time. Spack can only use a single
-build system for a single version. To decide which build system to use for a
-particular version, take the following things into account:
-
-1. If the developers explicitly state that one build system is preferred over
- another, use that one.
-2. If one build system is considered "experimental" while another is considered
- "stable", use the stable build system.
-3. Otherwise, use the newer build system.
-
-The developer preference for which build system to use can change over time as
-a newer build system becomes stable/recommended.
-
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-Dropping support for old build systems
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-When older versions of a package don't support a newer build system, it can be
-tempting to simply delete them from a package. This significantly reduces
-package complexity and makes the build recipe much easier to maintain. However,
-other packages or Spack users may rely on these older versions. The recommended
-approach is to first support both build systems (as demonstrated above),
-:ref:`deprecate <deprecate>` versions that rely on the old build system, and
-remove those versions and any phases that needed to be overridden in the next
-Spack release.
-
-^^^^^^^^^^^^^^^^^^^^^^^^^^^
-Three or more build systems
-^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-In rare cases, a package may change build systems multiple times. For example,
-a package may start with Makefiles, then switch to Autotools, then switch to
-CMake. The same logic used above can be extended to any number of build systems.
-For example:
-
-.. code-block:: python
-
- class Baz(CMakePackage):
- version("1.4.0", sha256="...") # CMake
- version("1.3.0", sha256="...") # Autotools
- version("1.2.0", sha256="...") # Makefile
-
- def cmake_args(self):
- return [self.define("BUILD_SHARED_LIBS", True)]
-
- # Remove the following once version 1.3.0 is dropped
- def configure_args(self):
- return ["--enable-shared"]
-
- @when("@1.3")
- def cmake(self, spec, prefix):
- configure("--prefix=" + prefix, *self.configure_args())
-
- # Remove the following once version 1.2.0 is dropped
- @when("@:1.2")
- def patch(self):
- filter_file("CC=", "CC=" + spack_cc, "Makefile")
-
- @when("@:1.2")
- def cmake(self, spec, prefix):
- pass
-
- @when("@:1.2")
- def install(self, spec, prefix):
- install_tree(".", prefix)
-
-
-^^^^^^^^^^^^^^^^^^^
-Additional examples
-^^^^^^^^^^^^^^^^^^^
-
-When writing new packages, it often helps to see examples of existing packages.
-Here is an incomplete list of existing Spack packages that have changed build
-systems before:
-
-================ ===================== ================
-Package Previous Build System New Build System
-================ ===================== ================
-amber custom CMake
-arpack-ng Autotools CMake
-atk Autotools Meson
-blast None Autotools
-dyninst Autotools CMake
-evtgen Autotools CMake
-fish Autotools CMake
-gdk-pixbuf Autotools Meson
-glib Autotools Meson
-glog Autotools CMake
-gmt Autotools CMake
-gtkplus Autotools Meson
-hpl Makefile Autotools
-interproscan Perl Maven
-jasper Autotools CMake
-kahip SCons CMake
-kokkos Makefile CMake
-kokkos-kernels Makefile CMake
-leveldb Makefile CMake
-libdrm Autotools Meson
-libjpeg-turbo Autotools CMake
-mesa Autotools Meson
-metis None CMake
-mpifileutils Autotools CMake
-muparser Autotools CMake
-mxnet Makefile CMake
-nest Autotools CMake
-neuron Autotools CMake
-nsimd CMake nsconfig
-opennurbs Makefile CMake
-optional-lite None CMake
-plasma Makefile CMake
-preseq Makefile Autotools
-protobuf Autotools CMake
-py-pygobject Autotools Python
-singularity Autotools Makefile
-span-lite None CMake
-ssht Makefile CMake
-string-view-lite None CMake
-superlu Makefile CMake
-superlu-dist Makefile CMake
-uncrustify Autotools CMake
-================ ===================== ================
-
-Packages that support multiple build systems can be a bit confusing to write.
-Don't hesitate to open an issue or draft pull request and ask for advice from
-other Spack developers!
diff --git a/lib/spack/docs/build_systems/octavepackage.rst b/lib/spack/docs/build_systems/octavepackage.rst
index 9a81671db6..32e8cb61b3 100644
--- a/lib/spack/docs/build_systems/octavepackage.rst
+++ b/lib/spack/docs/build_systems/octavepackage.rst
@@ -5,9 +5,9 @@
.. _octavepackage:
--------------
-OctavePackage
--------------
+------
+Octave
+------
Octave has its own build system for installing packages.
@@ -15,7 +15,7 @@ Octave has its own build system for installing packages.
Phases
^^^^^^
-The ``OctavePackage`` base class has a single phase:
+The ``OctaveBuilder`` and ``OctavePackage`` base classes have a single phase:
#. ``install`` - install the package
diff --git a/lib/spack/docs/build_systems/perlpackage.rst b/lib/spack/docs/build_systems/perlpackage.rst
index be81ca6ce9..4e1f613c3b 100644
--- a/lib/spack/docs/build_systems/perlpackage.rst
+++ b/lib/spack/docs/build_systems/perlpackage.rst
@@ -5,9 +5,9 @@
.. _perlpackage:
------------
-PerlPackage
------------
+----
+Perl
+----
Much like Octave, Perl has its own language-specific
build system.
@@ -16,7 +16,7 @@ build system.
Phases
^^^^^^
-The ``PerlPackage`` base class comes with 3 phases that can be overridden:
+The ``PerlBuilder`` and ``PerlPackage`` base classes come with 3 phases that can be overridden:
#. ``configure`` - configure the package
#. ``build`` - build the package
diff --git a/lib/spack/docs/build_systems/qmakepackage.rst b/lib/spack/docs/build_systems/qmakepackage.rst
index 6e8bcef7cc..215d59536e 100644
--- a/lib/spack/docs/build_systems/qmakepackage.rst
+++ b/lib/spack/docs/build_systems/qmakepackage.rst
@@ -5,9 +5,9 @@
.. _qmakepackage:
-------------
-QMakePackage
-------------
+-----
+QMake
+-----
Much like Autotools and CMake, QMake is a build-script generator
designed by the developers of Qt. In its simplest form, Spack's
@@ -29,7 +29,7 @@ variables or edit ``*.pro`` files to get things working properly.
Phases
^^^^^^
-The ``QMakePackage`` base class comes with the following phases:
+The ``QMakeBuilder`` and ``QMakePackage`` base classes come with the following phases:
#. ``qmake`` - generate Makefiles
#. ``build`` - build the project
diff --git a/lib/spack/docs/build_systems/racketpackage.rst b/lib/spack/docs/build_systems/racketpackage.rst
index 8ba37ceeba..5e09ffca4a 100644
--- a/lib/spack/docs/build_systems/racketpackage.rst
+++ b/lib/spack/docs/build_systems/racketpackage.rst
@@ -5,9 +5,9 @@
.. _racketpackage:
--------------
-RacketPackage
--------------
+------
+Racket
+------
Much like Python, Racket packages and modules have their own special build system.
To learn more about the specifics of Racket package system, please refer to the
@@ -17,7 +17,7 @@ To learn more about the specifics of Racket package system, please refer to the
Phases
^^^^^^
-The ``RacketPackage`` base class provides an ``install`` phase that
+The ``RacketBuilder`` and ``RacketPackage`` base classes provides an ``install`` phase that
can be overridden, corresponding to the use of:
.. code-block:: console
diff --git a/lib/spack/docs/build_systems/rpackage.rst b/lib/spack/docs/build_systems/rpackage.rst
index 671af779b1..ebf2270e8e 100644
--- a/lib/spack/docs/build_systems/rpackage.rst
+++ b/lib/spack/docs/build_systems/rpackage.rst
@@ -19,7 +19,7 @@ new Spack packages for.
Phases
^^^^^^
-The ``RPackage`` base class has a single phase:
+The ``RBuilder`` and ``RPackage`` base classes have a single phase:
#. ``install`` - install the package
diff --git a/lib/spack/docs/build_systems/rubypackage.rst b/lib/spack/docs/build_systems/rubypackage.rst
index b64ac60b2f..5b6ec462a6 100644
--- a/lib/spack/docs/build_systems/rubypackage.rst
+++ b/lib/spack/docs/build_systems/rubypackage.rst
@@ -5,9 +5,9 @@
.. _rubypackage:
------------
-RubyPackage
------------
+----
+Ruby
+----
Like Perl, Python, and R, Ruby has its own build system for
installing Ruby gems.
@@ -16,7 +16,7 @@ installing Ruby gems.
Phases
^^^^^^
-The ``RubyPackage`` base class provides the following phases that
+The ``RubyBuilder`` and ``RubyPackage`` base classes provide the following phases that
can be overridden:
#. ``build`` - build everything needed to install
diff --git a/lib/spack/docs/build_systems/sconspackage.rst b/lib/spack/docs/build_systems/sconspackage.rst
index cea0408651..aea5dacfa7 100644
--- a/lib/spack/docs/build_systems/sconspackage.rst
+++ b/lib/spack/docs/build_systems/sconspackage.rst
@@ -5,9 +5,9 @@
.. _sconspackage:
-------------
-SConsPackage
-------------
+-----
+SCons
+-----
SCons is a general-purpose build system that does not rely on
Makefiles to build software. SCons is written in Python, and handles
@@ -42,7 +42,7 @@ As previously mentioned, SCons allows developers to add subcommands like
$ scons install
-To facilitate this, the ``SConsPackage`` base class provides the
+To facilitate this, the ``SConsBuilder`` and ``SconsPackage`` base classes provide the
following phases:
#. ``build`` - build the package
diff --git a/lib/spack/docs/build_systems/sippackage.rst b/lib/spack/docs/build_systems/sippackage.rst
index 5235015a92..3e77968e80 100644
--- a/lib/spack/docs/build_systems/sippackage.rst
+++ b/lib/spack/docs/build_systems/sippackage.rst
@@ -5,9 +5,9 @@
.. _sippackage:
-----------
-SIPPackage
-----------
+---
+SIP
+---
SIP is a tool that makes it very easy to create Python bindings for C and C++
libraries. It was originally developed to create PyQt, the Python bindings for
@@ -22,7 +22,7 @@ provides support functions to the automatically generated code.
Phases
^^^^^^
-The ``SIPPackage`` base class comes with the following phases:
+The ``SIPBuilder`` and ``SIPPackage`` base classes come with the following phases:
#. ``configure`` - configure the package
#. ``build`` - build the package
diff --git a/lib/spack/docs/build_systems/wafpackage.rst b/lib/spack/docs/build_systems/wafpackage.rst
index 54fcba98d0..f91479ce43 100644
--- a/lib/spack/docs/build_systems/wafpackage.rst
+++ b/lib/spack/docs/build_systems/wafpackage.rst
@@ -5,9 +5,9 @@
.. _wafpackage:
-----------
-WafPackage
-----------
+---
+Waf
+---
Like SCons, Waf is a general-purpose build system that does not rely
on Makefiles to build software.
@@ -16,7 +16,7 @@ on Makefiles to build software.
Phases
^^^^^^
-The ``WafPackage`` base class comes with the following phases:
+The ``WafBuilder`` and ``WafPackage`` base classes come with the following phases:
#. ``configure`` - configure the project
#. ``build`` - build the project
diff --git a/lib/spack/docs/conf.py b/lib/spack/docs/conf.py
index 1bdce87238..4fc321c72d 100644
--- a/lib/spack/docs/conf.py
+++ b/lib/spack/docs/conf.py
@@ -209,6 +209,7 @@ nitpick_ignore = [
# Spack classes that are private and we don't want to expose
("py:class", "spack.provider_index._IndexBase"),
("py:class", "spack.repo._PrependFileLoader"),
+ ("py:class", "spack.build_systems._checks.BaseBuilder"),
# Spack classes that intersphinx is unable to resolve
("py:class", "spack.version.VersionBase"),
]
diff --git a/lib/spack/docs/developer_guide.rst b/lib/spack/docs/developer_guide.rst
index c4d04cb485..6b67ef9f77 100644
--- a/lib/spack/docs/developer_guide.rst
+++ b/lib/spack/docs/developer_guide.rst
@@ -149,11 +149,9 @@ grouped by functionality.
Package-related modules
^^^^^^^^^^^^^^^^^^^^^^^
-:mod:`spack.package`
- Contains the :class:`~spack.package_base.Package` class, which
- is the superclass for all packages in Spack. Methods on ``Package``
- implement all phases of the :ref:`package lifecycle
- <package-lifecycle>` and manage the build process.
+:mod:`spack.package_base`
+ Contains the :class:`~spack.package_base.PackageBase` class, which
+ is the superclass for all packages in Spack.
:mod:`spack.util.naming`
Contains functions for mapping between Spack package names,
diff --git a/lib/spack/docs/features.rst b/lib/spack/docs/features.rst
index 985da967fd..1682616adb 100644
--- a/lib/spack/docs/features.rst
+++ b/lib/spack/docs/features.rst
@@ -98,40 +98,42 @@ For example, this command:
.. code-block:: console
- $ spack create http://www.mr511.de/software/libelf-0.8.13.tar.gz
+ $ spack create https://ftp.osuosl.org/pub/blfs/conglomeration/libelf/libelf-0.8.13.tar.gz
creates a simple python file:
.. code-block:: python
- from spack import *
+ from spack.package import *
- class Libelf(Package):
+ class Libelf(AutotoolsPackage):
"""FIXME: Put a proper description of your package here."""
# FIXME: Add a proper url for your package's homepage here.
- homepage = "http://www.example.com"
- url = "http://www.mr511.de/software/libelf-0.8.13.tar.gz"
+ homepage = "https://www.example.com"
+ url = "https://ftp.osuosl.org/pub/blfs/conglomeration/libelf/libelf-0.8.13.tar.gz"
- version('0.8.13', '4136d7b4c04df68b686570afa26988ac')
+ # FIXME: Add a list of GitHub accounts to
+ # notify when the package is updated.
+ # maintainers = ["github_user1", "github_user2"]
- # FIXME: Add dependencies if required.
- # depends_on('foo')
+ version("0.8.13", sha256="591a9b4ec81c1f2042a97aa60564e0cb79d041c52faa7416acb38bc95bd2c76d")
- def install(self, spec, prefix):
- # FIXME: Modify the configure line to suit your build system here.
- configure('--prefix={0}'.format(prefix))
+ # FIXME: Add dependencies if required.
+ # depends_on("foo")
- # FIXME: Add logic to build and install here.
- make()
- make('install')
+ def configure_args(self):
+ # FIXME: Add arguments other than --prefix
+ # FIXME: If not needed delete this function
+ args = []
+ return args
It doesn't take much python coding to get from there to a working
package:
.. literalinclude:: _spack_root/var/spack/repos/builtin/packages/libelf/package.py
- :lines: 6-
+ :lines: 5-
Spack also provides wrapper functions around common commands like
``configure``, ``make``, and ``cmake`` to make writing packages
diff --git a/lib/spack/docs/images/adapter.png b/lib/spack/docs/images/adapter.png
new file mode 100644
index 0000000000..aa889c2c32
--- /dev/null
+++ b/lib/spack/docs/images/adapter.png
Binary files differ
diff --git a/lib/spack/docs/images/builder_package_architecture.png b/lib/spack/docs/images/builder_package_architecture.png
new file mode 100644
index 0000000000..0c79b1ee7f
--- /dev/null
+++ b/lib/spack/docs/images/builder_package_architecture.png
Binary files differ
diff --git a/lib/spack/docs/images/builder_phases.png b/lib/spack/docs/images/builder_phases.png
new file mode 100644
index 0000000000..e8141651c4
--- /dev/null
+++ b/lib/spack/docs/images/builder_phases.png
Binary files differ
diff --git a/lib/spack/docs/images/installation_pipeline.png b/lib/spack/docs/images/installation_pipeline.png
new file mode 100644
index 0000000000..c1d1e18f1b
--- /dev/null
+++ b/lib/spack/docs/images/installation_pipeline.png
Binary files differ
diff --git a/lib/spack/docs/images/original_package_architecture.png b/lib/spack/docs/images/original_package_architecture.png
new file mode 100644
index 0000000000..9fc21efcc1
--- /dev/null
+++ b/lib/spack/docs/images/original_package_architecture.png
Binary files differ
diff --git a/lib/spack/docs/images/packaging.excalidrawlib b/lib/spack/docs/images/packaging.excalidrawlib
new file mode 100644
index 0000000000..1449c48579
--- /dev/null
+++ b/lib/spack/docs/images/packaging.excalidrawlib
@@ -0,0 +1,3092 @@
+{
+ "type": "excalidrawlib",
+ "version": 2,
+ "source": "https://excalidraw.com",
+ "libraryItems": [
+ {
+ "status": "unpublished",
+ "elements": [
+ {
+ "type": "rectangle",
+ "version": 601,
+ "versionNonce": 158569138,
+ "isDeleted": false,
+ "id": "8MYJkzMoNEhDhGH1FB83g",
+ "fillStyle": "hachure",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 445.75,
+ "y": 129,
+ "strokeColor": "#000000",
+ "backgroundColor": "#ced4da",
+ "width": 736,
+ "height": 651,
+ "seed": 448140078,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1664627195460,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "rectangle",
+ "version": 195,
+ "versionNonce": 1239338030,
+ "isDeleted": false,
+ "id": "2CKbNSYnk0z80hSe6axnR",
+ "fillStyle": "hachure",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 470.25,
+ "y": 164,
+ "strokeColor": "#000000",
+ "backgroundColor": "#228be6",
+ "width": 495,
+ "height": 455,
+ "seed": 566918834,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [
+ {
+ "id": "IU_VoaKHNHswI8HaxNWt5",
+ "type": "arrow"
+ }
+ ],
+ "updated": 1664627105795,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "rectangle",
+ "version": 403,
+ "versionNonce": 56919410,
+ "isDeleted": false,
+ "id": "XUzv2kfpdxMahaSVVS42X",
+ "fillStyle": "solid",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 509.25,
+ "y": 407.5,
+ "strokeColor": "#000000",
+ "backgroundColor": "#ced4da",
+ "width": 396.99999999999994,
+ "height": 112,
+ "seed": 354909550,
+ "groupIds": [
+ "LYqioPcAzrIgJBDV3IaDA",
+ "SsaCg2uTI9sJjhD323wkh"
+ ],
+ "strokeSharpness": "sharp",
+ "boundElements": [
+ {
+ "id": "71z_J7hoepiXas8Fk5x0B",
+ "type": "arrow"
+ },
+ {
+ "id": "IU_VoaKHNHswI8HaxNWt5",
+ "type": "arrow"
+ }
+ ],
+ "updated": 1664627099901,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 300,
+ "versionNonce": 925254318,
+ "isDeleted": false,
+ "id": "lkCxvsSEn-AuBHtfj1N0d",
+ "fillStyle": "solid",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 547.25,
+ "y": 441,
+ "strokeColor": "#000000",
+ "backgroundColor": "#ced4da",
+ "width": 321,
+ "height": 45,
+ "seed": 1361827954,
+ "groupIds": [
+ "LYqioPcAzrIgJBDV3IaDA",
+ "SsaCg2uTI9sJjhD323wkh"
+ ],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1664627099902,
+ "link": null,
+ "locked": false,
+ "fontSize": 36,
+ "fontFamily": 1,
+ "text": "AutotoolsPackage",
+ "baseline": 32,
+ "textAlign": "center",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "AutotoolsPackage"
+ },
+ {
+ "type": "rectangle",
+ "version": 377,
+ "versionNonce": 1733756722,
+ "isDeleted": false,
+ "id": "aCDb2PgRdoFKA8e-GqQzR",
+ "fillStyle": "solid",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 509.25,
+ "y": 200,
+ "strokeColor": "#000000",
+ "backgroundColor": "#ced4da",
+ "width": 396.99999999999994,
+ "height": 112,
+ "seed": 175218606,
+ "groupIds": [
+ "WEeFev8dTdo9KgzR3hPki",
+ "SsaCg2uTI9sJjhD323wkh"
+ ],
+ "strokeSharpness": "sharp",
+ "boundElements": [
+ {
+ "id": "71z_J7hoepiXas8Fk5x0B",
+ "type": "arrow"
+ }
+ ],
+ "updated": 1664627099902,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 161,
+ "versionNonce": 585481454,
+ "isDeleted": false,
+ "id": "fXYOlmw0CV0WFTNUDity0",
+ "fillStyle": "solid",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 627.75,
+ "y": 233.5,
+ "strokeColor": "#000000",
+ "backgroundColor": "#ced4da",
+ "width": 160,
+ "height": 45,
+ "seed": 1186724402,
+ "groupIds": [
+ "WEeFev8dTdo9KgzR3hPki",
+ "SsaCg2uTI9sJjhD323wkh"
+ ],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1664627099902,
+ "link": null,
+ "locked": false,
+ "fontSize": 36,
+ "fontFamily": 1,
+ "text": "ArpackNg",
+ "baseline": 32,
+ "textAlign": "center",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "ArpackNg"
+ },
+ {
+ "type": "arrow",
+ "version": 290,
+ "versionNonce": 890458354,
+ "isDeleted": false,
+ "id": "71z_J7hoepiXas8Fk5x0B",
+ "fillStyle": "hachure",
+ "strokeWidth": 4,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 707.8516807799414,
+ "y": 403,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 0,
+ "height": 85,
+ "seed": 247298542,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1664627099902,
+ "link": null,
+ "locked": false,
+ "startBinding": {
+ "focus": 0.02318227093169459,
+ "gap": 3,
+ "elementId": "XUzv2kfpdxMahaSVVS42X"
+ },
+ "endBinding": {
+ "focus": -0.02318227093169459,
+ "gap": 6,
+ "elementId": "aCDb2PgRdoFKA8e-GqQzR"
+ },
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": "triangle",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ -85
+ ]
+ ]
+ },
+ {
+ "type": "text",
+ "version": 673,
+ "versionNonce": 1429991214,
+ "isDeleted": false,
+ "id": "bsoYa0EVTdXYsTx5nsFJk",
+ "fillStyle": "hachure",
+ "strokeWidth": 4,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 783.25,
+ "y": 518.3821170339361,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 164,
+ "height": 90,
+ "seed": 1633805298,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [
+ {
+ "id": "IU_VoaKHNHswI8HaxNWt5",
+ "type": "arrow"
+ }
+ ],
+ "updated": 1664627099902,
+ "link": null,
+ "locked": false,
+ "fontSize": 36,
+ "fontFamily": 1,
+ "text": "Package \nHierarchy",
+ "baseline": 77,
+ "textAlign": "center",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "Package \nHierarchy"
+ },
+ {
+ "type": "rectangle",
+ "version": 903,
+ "versionNonce": 1712814318,
+ "isDeleted": false,
+ "id": "qRi5xNnAOqg-SFwtYBpoN",
+ "fillStyle": "solid",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 510.25,
+ "y": 657.5,
+ "strokeColor": "#000000",
+ "backgroundColor": "#ced4da",
+ "width": 396.99999999999994,
+ "height": 112,
+ "seed": 1226050606,
+ "groupIds": [
+ "-wCL8N0qNvseDw29hpA8g"
+ ],
+ "strokeSharpness": "sharp",
+ "boundElements": [
+ {
+ "id": "IU_VoaKHNHswI8HaxNWt5",
+ "type": "arrow"
+ }
+ ],
+ "updated": 1664627118807,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 623,
+ "versionNonce": 492299954,
+ "isDeleted": false,
+ "id": "9h25d9NB-Q9Wc79boMEnC",
+ "fillStyle": "solid",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 552.25,
+ "y": 691,
+ "strokeColor": "#000000",
+ "backgroundColor": "#ced4da",
+ "width": 313,
+ "height": 45,
+ "seed": 186946994,
+ "groupIds": [
+ "-wCL8N0qNvseDw29hpA8g"
+ ],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1664627118807,
+ "link": null,
+ "locked": false,
+ "fontSize": 36,
+ "fontFamily": 1,
+ "text": "Builder Forwarder",
+ "baseline": 32,
+ "textAlign": "center",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "Builder Forwarder"
+ },
+ {
+ "type": "text",
+ "version": 1188,
+ "versionNonce": 351671150,
+ "isDeleted": false,
+ "id": "IlomIIocRvEmmYro4MZ68",
+ "fillStyle": "hachure",
+ "strokeWidth": 4,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1002.75,
+ "y": 168.5,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 157,
+ "height": 90,
+ "seed": 1428885362,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1664627188273,
+ "link": null,
+ "locked": false,
+ "fontSize": 36,
+ "fontFamily": 1,
+ "text": "Package\n Wrapper",
+ "baseline": 77,
+ "textAlign": "center",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "Package\n Wrapper"
+ },
+ {
+ "type": "arrow",
+ "version": 832,
+ "versionNonce": 1121332014,
+ "isDeleted": false,
+ "id": "IU_VoaKHNHswI8HaxNWt5",
+ "fillStyle": "hachure",
+ "strokeWidth": 4,
+ "strokeStyle": "dotted",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 707.7778281289579,
+ "y": 653.5,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 7.847537838213611,
+ "height": 130.23576593212783,
+ "seed": 1301783086,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1664627118807,
+ "link": null,
+ "locked": false,
+ "startBinding": {
+ "elementId": "qRi5xNnAOqg-SFwtYBpoN",
+ "focus": 0.013062197564634722,
+ "gap": 4
+ },
+ "endBinding": {
+ "elementId": "XUzv2kfpdxMahaSVVS42X",
+ "focus": 0.056574233332975385,
+ "gap": 3.7642340678721666
+ },
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": "triangle",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ -7.847537838213611,
+ -130.23576593212783
+ ]
+ ]
+ }
+ ],
+ "id": "mulubEO9Lw-HgC00sx7G-",
+ "created": 1664627205632
+ },
+ {
+ "status": "unpublished",
+ "elements": [
+ {
+ "type": "rectangle",
+ "version": 360,
+ "versionNonce": 699609906,
+ "isDeleted": false,
+ "id": "ai3MIBTq8Rkokk4d2NJ_k",
+ "fillStyle": "hachure",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 441.5,
+ "y": 56,
+ "strokeColor": "#000000",
+ "backgroundColor": "#228be6",
+ "width": 479,
+ "height": 642,
+ "seed": 725687342,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1664622926148,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "rectangle",
+ "version": 327,
+ "versionNonce": 1239118706,
+ "isDeleted": false,
+ "id": "7tuXfM91g28UGae9gJkis",
+ "fillStyle": "hachure",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 993.25,
+ "y": 53,
+ "strokeColor": "#000000",
+ "backgroundColor": "#228be6",
+ "width": 479,
+ "height": 642,
+ "seed": 860539570,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [
+ {
+ "id": "F6E1EQxM-PyPeNjQXH6NZ",
+ "type": "arrow"
+ }
+ ],
+ "updated": 1664623054904,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "rectangle",
+ "version": 482,
+ "versionNonce": 616506034,
+ "isDeleted": false,
+ "id": "TmgDkNmbU86sH2Ssf1mL2",
+ "fillStyle": "solid",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1030.75,
+ "y": 503.5,
+ "strokeColor": "#000000",
+ "backgroundColor": "#ced4da",
+ "width": 396.99999999999994,
+ "height": 112,
+ "seed": 329380206,
+ "groupIds": [
+ "rqi4zfKDNJjqgRyIIknBO"
+ ],
+ "strokeSharpness": "sharp",
+ "boundElements": [
+ {
+ "id": "RQl1RtMzUcPE_zXHt8Ldm",
+ "type": "arrow"
+ },
+ {
+ "id": "F6E1EQxM-PyPeNjQXH6NZ",
+ "type": "arrow"
+ },
+ {
+ "id": "Iey2r9ev3NqXShFhDRa3t",
+ "type": "arrow"
+ }
+ ],
+ "updated": 1664623131360,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 377,
+ "versionNonce": 1649618094,
+ "isDeleted": false,
+ "id": "M6LF3AKrGIzDW8p00PLeg",
+ "fillStyle": "solid",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1068.75,
+ "y": 537,
+ "strokeColor": "#000000",
+ "backgroundColor": "#ced4da",
+ "width": 321,
+ "height": 45,
+ "seed": 1690477682,
+ "groupIds": [
+ "rqi4zfKDNJjqgRyIIknBO"
+ ],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1664622926151,
+ "link": null,
+ "locked": false,
+ "fontSize": 36,
+ "fontFamily": 1,
+ "text": "AutotoolsPackage",
+ "baseline": 32,
+ "textAlign": "center",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "AutotoolsPackage"
+ },
+ {
+ "type": "rectangle",
+ "version": 466,
+ "versionNonce": 378147058,
+ "isDeleted": false,
+ "id": "-34MaUc1fQDbeqLTRUx91",
+ "fillStyle": "solid",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1030.625,
+ "y": 296,
+ "strokeColor": "#000000",
+ "backgroundColor": "#ced4da",
+ "width": 396.99999999999994,
+ "height": 112,
+ "seed": 964531118,
+ "groupIds": [
+ "TtAdfrQjw8FIlPZMGmWhX"
+ ],
+ "strokeSharpness": "sharp",
+ "boundElements": [
+ {
+ "id": "RQl1RtMzUcPE_zXHt8Ldm",
+ "type": "arrow"
+ },
+ {
+ "id": "7czUS_PAuM5hdRJoQRDRT",
+ "type": "arrow"
+ },
+ {
+ "id": "Iey2r9ev3NqXShFhDRa3t",
+ "type": "arrow"
+ }
+ ],
+ "updated": 1664623131360,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 250,
+ "versionNonce": 1826973422,
+ "isDeleted": false,
+ "id": "85YHNomCStJoIV17Sp0A6",
+ "fillStyle": "solid",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1093.625,
+ "y": 329.5,
+ "strokeColor": "#000000",
+ "backgroundColor": "#ced4da",
+ "width": 271,
+ "height": 45,
+ "seed": 1436108338,
+ "groupIds": [
+ "TtAdfrQjw8FIlPZMGmWhX"
+ ],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1664622926151,
+ "link": null,
+ "locked": false,
+ "fontSize": 36,
+ "fontFamily": 1,
+ "text": "builtin.ArpackNg",
+ "baseline": 32,
+ "textAlign": "center",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "builtin.ArpackNg"
+ },
+ {
+ "type": "arrow",
+ "version": 476,
+ "versionNonce": 1270564594,
+ "isDeleted": false,
+ "id": "RQl1RtMzUcPE_zXHt8Ldm",
+ "fillStyle": "hachure",
+ "strokeWidth": 4,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1233.8516807799415,
+ "y": 499,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 0,
+ "height": 85,
+ "seed": 1613426158,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1664622926151,
+ "link": null,
+ "locked": false,
+ "startBinding": {
+ "elementId": "TmgDkNmbU86sH2Ssf1mL2",
+ "focus": 0.023182270931695163,
+ "gap": 4.5
+ },
+ "endBinding": {
+ "elementId": "-34MaUc1fQDbeqLTRUx91",
+ "focus": -0.02381199385360952,
+ "gap": 6
+ },
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": "triangle",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ -85
+ ]
+ ]
+ },
+ {
+ "type": "text",
+ "version": 693,
+ "versionNonce": 1438013742,
+ "isDeleted": false,
+ "id": "wSIdF9zegc69r2D38BVMs",
+ "fillStyle": "hachure",
+ "strokeWidth": 4,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1061.75,
+ "y": 632.3821170339361,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 335,
+ "height": 45,
+ "seed": 1052094450,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1664622926151,
+ "link": null,
+ "locked": false,
+ "fontSize": 36,
+ "fontFamily": 1,
+ "text": "Old-style packages",
+ "baseline": 32,
+ "textAlign": "center",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "Old-style packages"
+ },
+ {
+ "type": "rectangle",
+ "version": 556,
+ "versionNonce": 1760787058,
+ "isDeleted": false,
+ "id": "lYxakYKLpAmo_DvzDJ27b",
+ "fillStyle": "solid",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1030.625,
+ "y": 95,
+ "strokeColor": "#000000",
+ "backgroundColor": "#ced4da",
+ "width": 396.99999999999994,
+ "height": 112,
+ "seed": 1302932978,
+ "groupIds": [
+ "-WCCzMWoqGFfWxksMC6LG"
+ ],
+ "strokeSharpness": "sharp",
+ "boundElements": [
+ {
+ "id": "RQl1RtMzUcPE_zXHt8Ldm",
+ "type": "arrow"
+ },
+ {
+ "id": "8Z8HX6DlXqC-qL-63w1ol",
+ "type": "arrow"
+ },
+ {
+ "id": "ia8wHuSmOVJLvGe5blR5g",
+ "type": "arrow"
+ },
+ {
+ "id": "7czUS_PAuM5hdRJoQRDRT",
+ "type": "arrow"
+ }
+ ],
+ "updated": 1664623123836,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 341,
+ "versionNonce": 1412367214,
+ "isDeleted": false,
+ "id": "hF1874wuKYmbBjYAQwrVJ",
+ "fillStyle": "solid",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1088.125,
+ "y": 128.5,
+ "strokeColor": "#000000",
+ "backgroundColor": "#ced4da",
+ "width": 282,
+ "height": 45,
+ "seed": 524182062,
+ "groupIds": [
+ "-WCCzMWoqGFfWxksMC6LG"
+ ],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1664622926152,
+ "link": null,
+ "locked": false,
+ "fontSize": 36,
+ "fontFamily": 1,
+ "text": "myrepo.ArpackNg",
+ "baseline": 32,
+ "textAlign": "center",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "myrepo.ArpackNg"
+ },
+ {
+ "type": "arrow",
+ "version": 593,
+ "versionNonce": 214413938,
+ "isDeleted": false,
+ "id": "8Z8HX6DlXqC-qL-63w1ol",
+ "fillStyle": "hachure",
+ "strokeWidth": 4,
+ "strokeStyle": "dotted",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1226.4453379157953,
+ "y": 297.5,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 2.434529927712447,
+ "height": 84,
+ "seed": 1326581486,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1664622926152,
+ "link": null,
+ "locked": false,
+ "startBinding": null,
+ "endBinding": {
+ "elementId": "lYxakYKLpAmo_DvzDJ27b",
+ "focus": -0.00782655608584947,
+ "gap": 6.5
+ },
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": "triangle",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 2.434529927712447,
+ -84
+ ]
+ ]
+ },
+ {
+ "type": "rectangle",
+ "version": 733,
+ "versionNonce": 390297266,
+ "isDeleted": false,
+ "id": "G4--cV2YGQSrSijvYiNDB",
+ "fillStyle": "solid",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 482.5,
+ "y": 507,
+ "strokeColor": "#000000",
+ "backgroundColor": "#ced4da",
+ "width": 396.99999999999994,
+ "height": 112,
+ "seed": 85080878,
+ "groupIds": [
+ "qZhg7KFANDHKWmTH71Lm0",
+ "FSKOW2oS76ubMa6DTOrDh"
+ ],
+ "strokeSharpness": "sharp",
+ "boundElements": [
+ {
+ "id": "RQl1RtMzUcPE_zXHt8Ldm",
+ "type": "arrow"
+ },
+ {
+ "id": "BkpnKUCjV1uqDGHPNuNZK",
+ "type": "arrow"
+ },
+ {
+ "id": "aQdIO4VQx_J6SzCz-xt64",
+ "type": "arrow"
+ },
+ {
+ "id": "F6E1EQxM-PyPeNjQXH6NZ",
+ "type": "arrow"
+ }
+ ],
+ "updated": 1664623061069,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 577,
+ "versionNonce": 2001681906,
+ "isDeleted": false,
+ "id": "MbNSUrN26Lx1aERuxunnt",
+ "fillStyle": "solid",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 541.5,
+ "y": 540.5,
+ "strokeColor": "#000000",
+ "backgroundColor": "#ced4da",
+ "width": 279,
+ "height": 45,
+ "seed": 950326962,
+ "groupIds": [
+ "qZhg7KFANDHKWmTH71Lm0",
+ "FSKOW2oS76ubMa6DTOrDh"
+ ],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1664622926152,
+ "link": null,
+ "locked": false,
+ "fontSize": 36,
+ "fontFamily": 1,
+ "text": "Default Builder",
+ "baseline": 32,
+ "textAlign": "center",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "Default Builder"
+ },
+ {
+ "type": "rectangle",
+ "version": 722,
+ "versionNonce": 1372930162,
+ "isDeleted": false,
+ "id": "WIS84sS48dCmi8q81Hh9F",
+ "fillStyle": "solid",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 482.5,
+ "y": 99,
+ "strokeColor": "#000000",
+ "backgroundColor": "#ced4da",
+ "width": 396.99999999999994,
+ "height": 112,
+ "seed": 1977410350,
+ "groupIds": [
+ "_CQwHz-xftDZzy8u9u4YO",
+ "FSKOW2oS76ubMa6DTOrDh"
+ ],
+ "strokeSharpness": "sharp",
+ "boundElements": [
+ {
+ "id": "RQl1RtMzUcPE_zXHt8Ldm",
+ "type": "arrow"
+ },
+ {
+ "id": "BkpnKUCjV1uqDGHPNuNZK",
+ "type": "arrow"
+ },
+ {
+ "id": "aQdIO4VQx_J6SzCz-xt64",
+ "type": "arrow"
+ },
+ {
+ "id": "ia8wHuSmOVJLvGe5blR5g",
+ "type": "arrow"
+ }
+ ],
+ "updated": 1664623105535,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 531,
+ "versionNonce": 1851174834,
+ "isDeleted": false,
+ "id": "qIbTXN1LbDYGZzSceYynz",
+ "fillStyle": "solid",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 540.5,
+ "y": 132.5,
+ "strokeColor": "#000000",
+ "backgroundColor": "#ced4da",
+ "width": 281,
+ "height": 45,
+ "seed": 221818546,
+ "groupIds": [
+ "_CQwHz-xftDZzy8u9u4YO",
+ "FSKOW2oS76ubMa6DTOrDh"
+ ],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1664622926152,
+ "link": null,
+ "locked": false,
+ "fontSize": 36,
+ "fontFamily": 1,
+ "text": "Adapter Builder",
+ "baseline": 32,
+ "textAlign": "center",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "Adapter Builder"
+ },
+ {
+ "type": "arrow",
+ "version": 85,
+ "versionNonce": 50141422,
+ "isDeleted": false,
+ "id": "aQdIO4VQx_J6SzCz-xt64",
+ "fillStyle": "solid",
+ "strokeWidth": 4,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 670,
+ "y": 505,
+ "strokeColor": "#000000",
+ "backgroundColor": "#ced4da",
+ "width": 2,
+ "height": 291,
+ "seed": 417372974,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1664622926152,
+ "link": null,
+ "locked": false,
+ "startBinding": {
+ "elementId": "G4--cV2YGQSrSijvYiNDB",
+ "focus": -0.05731267980406219,
+ "gap": 2
+ },
+ "endBinding": {
+ "elementId": "WIS84sS48dCmi8q81Hh9F",
+ "focus": 0.04321344955983103,
+ "gap": 3
+ },
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": "triangle",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 2,
+ -291
+ ]
+ ]
+ },
+ {
+ "type": "arrow",
+ "version": 720,
+ "versionNonce": 1494556718,
+ "isDeleted": false,
+ "id": "ia8wHuSmOVJLvGe5blR5g",
+ "fillStyle": "hachure",
+ "strokeWidth": 4,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 4.706831282597808,
+ "x": 932.4285606227004,
+ "y": 52.69401049592016,
+ "strokeColor": "#c92a2a",
+ "backgroundColor": "transparent",
+ "width": 47.10077935537049,
+ "height": 145.9883132350331,
+ "seed": 314146734,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElements": [],
+ "updated": 1664623039605,
+ "link": null,
+ "locked": false,
+ "startBinding": {
+ "elementId": "WIS84sS48dCmi8q81Hh9F",
+ "focus": 0.6597923311816741,
+ "gap": 2.0985583595166872
+ },
+ "endBinding": {
+ "elementId": "lYxakYKLpAmo_DvzDJ27b",
+ "focus": -0.6857137990945498,
+ "gap": 3.0336827015810286
+ },
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": "triangle",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 45.89517648378751,
+ 72.7218231059162
+ ],
+ [
+ -1.2056028715829825,
+ 145.9883132350331
+ ]
+ ]
+ },
+ {
+ "type": "text",
+ "version": 727,
+ "versionNonce": 549636846,
+ "isDeleted": false,
+ "id": "JRrvIVZ9KAv56BYbRbCLA",
+ "fillStyle": "hachure",
+ "strokeWidth": 4,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 527.5,
+ "y": 633.5,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 295,
+ "height": 45,
+ "seed": 2130028978,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1664622926153,
+ "link": null,
+ "locked": false,
+ "fontSize": 36,
+ "fontFamily": 1,
+ "text": "Builder Hierarchy",
+ "baseline": 32,
+ "textAlign": "center",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "Builder Hierarchy"
+ },
+ {
+ "type": "text",
+ "version": 281,
+ "versionNonce": 777063918,
+ "isDeleted": false,
+ "id": "BBj29IYUUwcEAk0aGGgEe",
+ "fillStyle": "solid",
+ "strokeWidth": 4,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 746,
+ "y": 2,
+ "strokeColor": "#c92a2a",
+ "backgroundColor": "#ced4da",
+ "width": 438,
+ "height": 35,
+ "seed": 344107566,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1664623034966,
+ "link": null,
+ "locked": false,
+ "fontSize": 28,
+ "fontFamily": 1,
+ "text": "Defer to the old-style package",
+ "baseline": 25,
+ "textAlign": "center",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "Defer to the old-style package"
+ },
+ {
+ "type": "arrow",
+ "version": 864,
+ "versionNonce": 353999662,
+ "isDeleted": false,
+ "id": "F6E1EQxM-PyPeNjQXH6NZ",
+ "fillStyle": "hachure",
+ "strokeWidth": 4,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 1.5656833824867196,
+ "x": 932.5276780900645,
+ "y": 511.2079252998286,
+ "strokeColor": "#c92a2a",
+ "backgroundColor": "transparent",
+ "width": 47.10077935537049,
+ "height": 145.9883132350331,
+ "seed": 2119154546,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElements": [],
+ "updated": 1664623061069,
+ "link": null,
+ "locked": false,
+ "startBinding": {
+ "elementId": "TmgDkNmbU86sH2Ssf1mL2",
+ "focus": 0.700636908798286,
+ "gap": 3.7338363313426726
+ },
+ "endBinding": {
+ "elementId": "G4--cV2YGQSrSijvYiNDB",
+ "focus": -0.7137516210459195,
+ "gap": 1.5235945037890133
+ },
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": "triangle",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 45.89517648378751,
+ 72.7218231059162
+ ],
+ [
+ -1.2056028715829825,
+ 145.9883132350331
+ ]
+ ]
+ },
+ {
+ "type": "text",
+ "version": 318,
+ "versionNonce": 1988243186,
+ "isDeleted": false,
+ "id": "VIOq-st9nvReenpiJkr7q",
+ "fillStyle": "solid",
+ "strokeWidth": 4,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 828,
+ "y": 724.5,
+ "strokeColor": "#c92a2a",
+ "backgroundColor": "#ced4da",
+ "width": 274,
+ "height": 70,
+ "seed": 2086072882,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1664623095297,
+ "link": null,
+ "locked": false,
+ "fontSize": 28,
+ "fontFamily": 1,
+ "text": "Fall-back to the \nAdapter base class",
+ "baseline": 60,
+ "textAlign": "center",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "Fall-back to the \nAdapter base class"
+ },
+ {
+ "type": "arrow",
+ "version": 971,
+ "versionNonce": 1844256174,
+ "isDeleted": false,
+ "id": "7czUS_PAuM5hdRJoQRDRT",
+ "fillStyle": "hachure",
+ "strokeWidth": 4,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 6.272294617229998,
+ "x": 1433.5276780900645,
+ "y": 163.20792529982862,
+ "strokeColor": "#c92a2a",
+ "backgroundColor": "transparent",
+ "width": 47.10077935537049,
+ "height": 145.9883132350331,
+ "seed": 142056302,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElements": [],
+ "updated": 1664623123836,
+ "link": null,
+ "locked": false,
+ "startBinding": {
+ "elementId": "lYxakYKLpAmo_DvzDJ27b",
+ "focus": -0.8331982906950285,
+ "gap": 5.098981289624589
+ },
+ "endBinding": {
+ "elementId": "-34MaUc1fQDbeqLTRUx91",
+ "focus": 0.7587321286266477,
+ "gap": 5.483331940596372
+ },
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": "triangle",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 45.89517648378751,
+ 72.7218231059162
+ ],
+ [
+ -1.2056028715829825,
+ 145.9883132350331
+ ]
+ ]
+ },
+ {
+ "type": "arrow",
+ "version": 1075,
+ "versionNonce": 2073112366,
+ "isDeleted": false,
+ "id": "Iey2r9ev3NqXShFhDRa3t",
+ "fillStyle": "hachure",
+ "strokeWidth": 4,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 6.272294617229998,
+ "x": 1434.451400933309,
+ "y": 387.7559332541056,
+ "strokeColor": "#c92a2a",
+ "backgroundColor": "transparent",
+ "width": 47.10077935537049,
+ "height": 145.9883132350331,
+ "seed": 840513518,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElements": [],
+ "updated": 1664623131360,
+ "link": null,
+ "locked": false,
+ "startBinding": {
+ "elementId": "-34MaUc1fQDbeqLTRUx91",
+ "focus": -0.7723329153292293,
+ "gap": 6.037577244264867
+ },
+ "endBinding": {
+ "elementId": "TmgDkNmbU86sH2Ssf1mL2",
+ "focus": 0.808011962769455,
+ "gap": 6.296927895236422
+ },
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": "triangle",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 45.89517648378751,
+ 72.7218231059162
+ ],
+ [
+ -1.2056028715829825,
+ 145.9883132350331
+ ]
+ ]
+ }
+ ],
+ "id": "sJP5ES4-kuhrqaBed7Feh",
+ "created": 1664623142493
+ },
+ {
+ "status": "unpublished",
+ "elements": [
+ {
+ "type": "rectangle",
+ "version": 351,
+ "versionNonce": 94847218,
+ "isDeleted": false,
+ "id": "QfhQQY4Kvx8RLvCd6qXsx",
+ "fillStyle": "hachure",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1011.5,
+ "y": 249,
+ "strokeColor": "#000000",
+ "backgroundColor": "#228be6",
+ "width": 479,
+ "height": 438,
+ "seed": 1024685106,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1664612347442,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "rectangle",
+ "version": 156,
+ "versionNonce": 2082406190,
+ "isDeleted": false,
+ "id": "rMQqqzkSZsBVWvOk137wO",
+ "fillStyle": "hachure",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 511,
+ "y": 247,
+ "strokeColor": "#000000",
+ "backgroundColor": "#228be6",
+ "width": 479,
+ "height": 438,
+ "seed": 250617778,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1664612347443,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "rectangle",
+ "version": 392,
+ "versionNonce": 414601906,
+ "isDeleted": false,
+ "id": "h2lcAgJBn6WsPKAj3vWS8",
+ "fillStyle": "solid",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 545.5,
+ "y": 490.5,
+ "strokeColor": "#000000",
+ "backgroundColor": "#ced4da",
+ "width": 396.99999999999994,
+ "height": 112,
+ "seed": 721668433,
+ "groupIds": [
+ "ETPwHpdW1CXh0DtqZ_2na"
+ ],
+ "strokeSharpness": "sharp",
+ "boundElements": [
+ {
+ "id": "r2Lq0kGXd6aTn5T-ki1aL",
+ "type": "arrow"
+ }
+ ],
+ "updated": 1664612347443,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 293,
+ "versionNonce": 848488814,
+ "isDeleted": false,
+ "id": "eaxk_MzyrjAjXKf0vmFuU",
+ "fillStyle": "solid",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 583.5,
+ "y": 524,
+ "strokeColor": "#000000",
+ "backgroundColor": "#ced4da",
+ "width": 321,
+ "height": 45,
+ "seed": 1324675135,
+ "groupIds": [
+ "ETPwHpdW1CXh0DtqZ_2na"
+ ],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1664612347443,
+ "link": null,
+ "locked": false,
+ "fontSize": 36,
+ "fontFamily": 1,
+ "text": "AutotoolsPackage",
+ "baseline": 32,
+ "textAlign": "center",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "AutotoolsPackage"
+ },
+ {
+ "type": "rectangle",
+ "version": 370,
+ "versionNonce": 595405938,
+ "isDeleted": false,
+ "id": "6TAhmS7GKN_ppUHjSVGLb",
+ "fillStyle": "solid",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 545.5,
+ "y": 283,
+ "strokeColor": "#000000",
+ "backgroundColor": "#ced4da",
+ "width": 396.99999999999994,
+ "height": 112,
+ "seed": 2083634783,
+ "groupIds": [
+ "biKtN87UToAb_UBhyub5I"
+ ],
+ "strokeSharpness": "sharp",
+ "boundElements": [
+ {
+ "id": "r2Lq0kGXd6aTn5T-ki1aL",
+ "type": "arrow"
+ }
+ ],
+ "updated": 1664612347443,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 155,
+ "versionNonce": 1066372014,
+ "isDeleted": false,
+ "id": "xyXchzGRLKRPuMVGo17mr",
+ "fillStyle": "solid",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 664,
+ "y": 316.5,
+ "strokeColor": "#000000",
+ "backgroundColor": "#ced4da",
+ "width": 160,
+ "height": 45,
+ "seed": 2066951921,
+ "groupIds": [
+ "biKtN87UToAb_UBhyub5I"
+ ],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1664612347443,
+ "link": null,
+ "locked": false,
+ "fontSize": 36,
+ "fontFamily": 1,
+ "text": "ArpackNg",
+ "baseline": 32,
+ "textAlign": "center",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "ArpackNg"
+ },
+ {
+ "type": "arrow",
+ "version": 285,
+ "versionNonce": 1807928882,
+ "isDeleted": false,
+ "id": "r2Lq0kGXd6aTn5T-ki1aL",
+ "fillStyle": "hachure",
+ "strokeWidth": 4,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 748.6016807799414,
+ "y": 486,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 0,
+ "height": 85,
+ "seed": 1479060383,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1664612347443,
+ "link": null,
+ "locked": false,
+ "startBinding": {
+ "elementId": "h2lcAgJBn6WsPKAj3vWS8",
+ "focus": 0.02318227093169459,
+ "gap": 3
+ },
+ "endBinding": {
+ "elementId": "6TAhmS7GKN_ppUHjSVGLb",
+ "focus": -0.02318227093169459,
+ "gap": 6
+ },
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": "triangle",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ -85
+ ]
+ ]
+ },
+ {
+ "type": "text",
+ "version": 572,
+ "versionNonce": 1094575598,
+ "isDeleted": false,
+ "id": "pUx1_v_UyKhu5zXISU4-f",
+ "fillStyle": "hachure",
+ "strokeWidth": 4,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 653,
+ "y": 619.3821170339361,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 182,
+ "height": 45,
+ "seed": 1608256017,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1664612347443,
+ "link": null,
+ "locked": false,
+ "fontSize": 36,
+ "fontFamily": 1,
+ "text": "Metadata",
+ "baseline": 32,
+ "textAlign": "center",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "Metadata"
+ },
+ {
+ "type": "rectangle",
+ "version": 734,
+ "versionNonce": 1401317810,
+ "isDeleted": false,
+ "id": "4YBPHTc5sQiOKGM9NOZwg",
+ "fillStyle": "solid",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1045.5,
+ "y": 490.5,
+ "strokeColor": "#000000",
+ "backgroundColor": "#ced4da",
+ "width": 396.99999999999994,
+ "height": 112,
+ "seed": 1687989426,
+ "groupIds": [
+ "lxE4hLtUAF2X7993lwk8q"
+ ],
+ "strokeSharpness": "sharp",
+ "boundElements": [
+ {
+ "id": "M8cWqpsa0-iwN_cVJeXEQ",
+ "type": "arrow"
+ }
+ ],
+ "updated": 1664612347443,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 436,
+ "versionNonce": 1572061806,
+ "isDeleted": false,
+ "id": "P2U0ucf_QPvJcOWlMLp2K",
+ "fillStyle": "solid",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1183,
+ "y": 524,
+ "strokeColor": "#000000",
+ "backgroundColor": "#ced4da",
+ "width": 122,
+ "height": 45,
+ "seed": 276038958,
+ "groupIds": [
+ "lxE4hLtUAF2X7993lwk8q"
+ ],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1664612347443,
+ "link": null,
+ "locked": false,
+ "fontSize": 36,
+ "fontFamily": 1,
+ "text": "Builder",
+ "baseline": 32,
+ "textAlign": "center",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "Builder"
+ },
+ {
+ "type": "arrow",
+ "version": 489,
+ "versionNonce": 1663911086,
+ "isDeleted": false,
+ "id": "M8cWqpsa0-iwN_cVJeXEQ",
+ "fillStyle": "hachure",
+ "strokeWidth": 4,
+ "strokeStyle": "dashed",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 942,
+ "y": 337,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 303,
+ "height": 143,
+ "seed": 1698960686,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1664612347443,
+ "link": null,
+ "locked": false,
+ "startBinding": null,
+ "endBinding": {
+ "elementId": "4YBPHTc5sQiOKGM9NOZwg",
+ "focus": 0.04820781382766574,
+ "gap": 10.5
+ },
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": "dot",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 295,
+ 0
+ ],
+ [
+ 303,
+ 143
+ ]
+ ]
+ },
+ {
+ "type": "text",
+ "version": 841,
+ "versionNonce": 2059173614,
+ "isDeleted": false,
+ "id": "QGyg9pXnTgByg9Lw9oZKC",
+ "fillStyle": "hachure",
+ "strokeWidth": 4,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1043.5,
+ "y": 621.5,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 401,
+ "height": 45,
+ "seed": 1012078510,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1664612347443,
+ "link": null,
+ "locked": false,
+ "fontSize": 36,
+ "fontFamily": 1,
+ "text": "Installation Procedure",
+ "baseline": 32,
+ "textAlign": "center",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "Installation Procedure"
+ }
+ ],
+ "id": "tezI4Q4gBH7mr-Q_us1KO",
+ "created": 1664612353293
+ },
+ {
+ "status": "unpublished",
+ "elements": [
+ {
+ "type": "rectangle",
+ "version": 273,
+ "versionNonce": 1078330865,
+ "isDeleted": false,
+ "id": "h2lcAgJBn6WsPKAj3vWS8",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 545.5,
+ "y": 489,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 396.99999999999994,
+ "height": 112,
+ "seed": 721668433,
+ "groupIds": [
+ "ETPwHpdW1CXh0DtqZ_2na"
+ ],
+ "strokeSharpness": "sharp",
+ "boundElements": [
+ {
+ "id": "r2Lq0kGXd6aTn5T-ki1aL",
+ "type": "arrow"
+ }
+ ],
+ "updated": 1664534889868,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 174,
+ "versionNonce": 1400524191,
+ "isDeleted": false,
+ "id": "eaxk_MzyrjAjXKf0vmFuU",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 583.5,
+ "y": 522.5,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 321,
+ "height": 45,
+ "seed": 1324675135,
+ "groupIds": [
+ "ETPwHpdW1CXh0DtqZ_2na"
+ ],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1664534889868,
+ "link": null,
+ "locked": false,
+ "fontSize": 36,
+ "fontFamily": 1,
+ "text": "AutotoolsPackage",
+ "baseline": 32,
+ "textAlign": "center",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "AutotoolsPackage"
+ },
+ {
+ "type": "text",
+ "version": 108,
+ "versionNonce": 438728849,
+ "isDeleted": false,
+ "id": "xyXchzGRLKRPuMVGo17mr",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 664,
+ "y": 316.5,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 160,
+ "height": 45,
+ "seed": 2066951921,
+ "groupIds": [
+ "1wm7ikIN28k9zdVSKTLKQ"
+ ],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1664540120970,
+ "link": null,
+ "locked": false,
+ "fontSize": 36,
+ "fontFamily": 1,
+ "text": "ArpackNg",
+ "baseline": 32,
+ "textAlign": "center",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "ArpackNg"
+ },
+ {
+ "type": "rectangle",
+ "version": 322,
+ "versionNonce": 1389146591,
+ "isDeleted": false,
+ "id": "6TAhmS7GKN_ppUHjSVGLb",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 545.5,
+ "y": 283,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 396.99999999999994,
+ "height": 112,
+ "seed": 2083634783,
+ "groupIds": [
+ "1wm7ikIN28k9zdVSKTLKQ"
+ ],
+ "strokeSharpness": "sharp",
+ "boundElements": [
+ {
+ "id": "r2Lq0kGXd6aTn5T-ki1aL",
+ "type": "arrow"
+ }
+ ],
+ "updated": 1664534889868,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "arrow",
+ "version": 94,
+ "versionNonce": 787416433,
+ "isDeleted": false,
+ "id": "r2Lq0kGXd6aTn5T-ki1aL",
+ "fillStyle": "hachure",
+ "strokeWidth": 4,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 748.6016807799414,
+ "y": 486,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 0,
+ "height": 85,
+ "seed": 1479060383,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1664534889868,
+ "link": null,
+ "locked": false,
+ "startBinding": {
+ "elementId": "h2lcAgJBn6WsPKAj3vWS8",
+ "focus": 0.02318227093169459,
+ "gap": 3
+ },
+ "endBinding": {
+ "elementId": "6TAhmS7GKN_ppUHjSVGLb",
+ "focus": -0.02318227093169459,
+ "gap": 6
+ },
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": "triangle",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ -85
+ ]
+ ]
+ },
+ {
+ "type": "text",
+ "version": 227,
+ "versionNonce": 117980031,
+ "isDeleted": false,
+ "id": "pUx1_v_UyKhu5zXISU4-f",
+ "fillStyle": "hachure",
+ "strokeWidth": 4,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 969,
+ "y": 386.5,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 442,
+ "height": 90,
+ "seed": 1608256017,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1664534908931,
+ "link": null,
+ "locked": false,
+ "fontSize": 36,
+ "fontFamily": 1,
+ "text": "Metadata \n+ Installation Procedure",
+ "baseline": 77,
+ "textAlign": "center",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "Metadata \n+ Installation Procedure"
+ }
+ ],
+ "id": "_c7AOn60omrTlppZHlLQh",
+ "created": 1664540190548
+ },
+ {
+ "status": "unpublished",
+ "elements": [
+ {
+ "type": "rectangle",
+ "version": 367,
+ "versionNonce": 963584621,
+ "isDeleted": false,
+ "id": "oAei2n-Ha1gpjnYdK7AwC",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 240.5,
+ "y": 642.75,
+ "strokeColor": "#000000",
+ "backgroundColor": "#228be6",
+ "width": 392,
+ "height": 80,
+ "seed": 701868237,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [
+ {
+ "id": "slfbd0bbRqA8648kZ5fns",
+ "type": "text"
+ },
+ {
+ "id": "slfbd0bbRqA8648kZ5fns",
+ "type": "text"
+ },
+ {
+ "type": "text",
+ "id": "slfbd0bbRqA8648kZ5fns"
+ }
+ ],
+ "updated": 1663329462351,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 373,
+ "versionNonce": 1698441027,
+ "isDeleted": false,
+ "id": "slfbd0bbRqA8648kZ5fns",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 245.5,
+ "y": 670.25,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 382,
+ "height": 25,
+ "seed": 1179637379,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1663329462351,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "Execute the installation process",
+ "baseline": 18,
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": "oAei2n-Ha1gpjnYdK7AwC",
+ "originalText": "Execute the installation process"
+ },
+ {
+ "type": "rectangle",
+ "version": 208,
+ "versionNonce": 844908259,
+ "isDeleted": false,
+ "id": "cLwg2WXUit_OTQmXLIdIW",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 815.5,
+ "y": 517.5,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 392,
+ "height": 80,
+ "seed": 557411811,
+ "groupIds": [
+ "D1SCf714tngJFHk8TFX8T"
+ ],
+ "strokeSharpness": "sharp",
+ "boundElements": [
+ {
+ "id": "SpG_8HxzMHjM2HYK6Fgwx",
+ "type": "arrow"
+ }
+ ],
+ "updated": 1663329462351,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 274,
+ "versionNonce": 1704611021,
+ "isDeleted": false,
+ "id": "1r8FMl26VYSKpPKlHA_Oc",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 916.5,
+ "y": 545,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 207,
+ "height": 25,
+ "seed": 961881101,
+ "groupIds": [
+ "D1SCf714tngJFHk8TFX8T"
+ ],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1663329462351,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "CMakeBuilder.cmake()",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "CMakeBuilder.cmake()"
+ },
+ {
+ "type": "rectangle",
+ "version": 264,
+ "versionNonce": 295137923,
+ "isDeleted": false,
+ "id": "CSwjuAw6Nl67sqQ6p21ty",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 815.5,
+ "y": 642.75,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 392,
+ "height": 80,
+ "seed": 1011629069,
+ "groupIds": [
+ "D1SCf714tngJFHk8TFX8T"
+ ],
+ "strokeSharpness": "sharp",
+ "boundElements": [
+ {
+ "id": "SpG_8HxzMHjM2HYK6Fgwx",
+ "type": "arrow"
+ },
+ {
+ "id": "zvmLoAH5oICRD5og-pBvu",
+ "type": "arrow"
+ }
+ ],
+ "updated": 1663329462351,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 466,
+ "versionNonce": 196160301,
+ "isDeleted": false,
+ "id": "WX4axTU0IR7PJb0GkR-jq",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 922.5,
+ "y": 670.25,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 193,
+ "height": 25,
+ "seed": 716117827,
+ "groupIds": [
+ "D1SCf714tngJFHk8TFX8T"
+ ],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1663329462351,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "CMakeBuilder.build()",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "CMakeBuilder.build()"
+ },
+ {
+ "type": "rectangle",
+ "version": 301,
+ "versionNonce": 1545420173,
+ "isDeleted": false,
+ "id": "coUXke3Fv_DpjqG9zgEjQ",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 815.5,
+ "y": 768,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 392,
+ "height": 80,
+ "seed": 1934529891,
+ "groupIds": [
+ "D1SCf714tngJFHk8TFX8T"
+ ],
+ "strokeSharpness": "sharp",
+ "boundElements": [
+ {
+ "type": "text",
+ "id": "yVIbU03yFYvpXnh9xIgET"
+ },
+ {
+ "id": "zvmLoAH5oICRD5og-pBvu",
+ "type": "arrow"
+ }
+ ],
+ "updated": 1663329462351,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 273,
+ "versionNonce": 1837690307,
+ "isDeleted": false,
+ "id": "yVIbU03yFYvpXnh9xIgET",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 820.5,
+ "y": 795.5,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 382,
+ "height": 25,
+ "seed": 1611291683,
+ "groupIds": [
+ "D1SCf714tngJFHk8TFX8T"
+ ],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1663329462351,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "CMakeBuilder.install()",
+ "baseline": 18,
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": "coUXke3Fv_DpjqG9zgEjQ",
+ "originalText": "CMakeBuilder.install()"
+ },
+ {
+ "type": "arrow",
+ "version": 564,
+ "versionNonce": 1041761261,
+ "isDeleted": false,
+ "id": "SpG_8HxzMHjM2HYK6Fgwx",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1209,
+ "y": 558.5,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 96,
+ "height": 109,
+ "seed": 732445197,
+ "groupIds": [
+ "D1SCf714tngJFHk8TFX8T"
+ ],
+ "strokeSharpness": "round",
+ "boundElements": [],
+ "updated": 1663329462351,
+ "link": null,
+ "locked": false,
+ "startBinding": {
+ "elementId": "cLwg2WXUit_OTQmXLIdIW",
+ "focus": -0.7327371048252911,
+ "gap": 1.5
+ },
+ "endBinding": {
+ "elementId": "CSwjuAw6Nl67sqQ6p21ty",
+ "focus": 0.6494341563786008,
+ "gap": 2.5
+ },
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": "triangle",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 96,
+ 54
+ ],
+ [
+ 1,
+ 109
+ ]
+ ]
+ },
+ {
+ "type": "arrow",
+ "version": 642,
+ "versionNonce": 1380728163,
+ "isDeleted": false,
+ "id": "zvmLoAH5oICRD5og-pBvu",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1216,
+ "y": 680,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 98,
+ "height": 124.33745608356844,
+ "seed": 708861581,
+ "groupIds": [
+ "D1SCf714tngJFHk8TFX8T"
+ ],
+ "strokeSharpness": "round",
+ "boundElements": [],
+ "updated": 1663329462351,
+ "link": null,
+ "locked": false,
+ "startBinding": {
+ "elementId": "CSwjuAw6Nl67sqQ6p21ty",
+ "focus": -0.7839018302828619,
+ "gap": 8.5
+ },
+ "endBinding": {
+ "elementId": "coUXke3Fv_DpjqG9zgEjQ",
+ "focus": 0.7841576120638036,
+ "gap": 6.5
+ },
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": "triangle",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 96,
+ 54
+ ],
+ [
+ -2,
+ 124.33745608356844
+ ]
+ ]
+ },
+ {
+ "type": "text",
+ "version": 613,
+ "versionNonce": 909390253,
+ "isDeleted": false,
+ "id": "fAHH1YdSlMq8ioLIj36Of",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 644,
+ "y": 353.7484662576685,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 166,
+ "height": 567.2515337423315,
+ "seed": 1455993539,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1663329499644,
+ "link": null,
+ "locked": false,
+ "fontSize": 493.16790307261914,
+ "fontFamily": 2,
+ "text": "{",
+ "baseline": 454.2515337423315,
+ "textAlign": "center",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "{"
+ }
+ ],
+ "id": "KBV_I9pxrJD2zPuaP6vBc",
+ "created": 1663329511286
+ },
+ {
+ "status": "unpublished",
+ "elements": [
+ {
+ "type": "rectangle",
+ "version": 93,
+ "versionNonce": 42296109,
+ "isDeleted": false,
+ "id": "cLwg2WXUit_OTQmXLIdIW",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 625.5,
+ "y": 298,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 392,
+ "height": 80,
+ "seed": 557411811,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [
+ {
+ "id": "SpG_8HxzMHjM2HYK6Fgwx",
+ "type": "arrow"
+ }
+ ],
+ "updated": 1663324636434,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 99,
+ "versionNonce": 1537897869,
+ "isDeleted": false,
+ "id": "1r8FMl26VYSKpPKlHA_Oc",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 726.5,
+ "y": 325.5,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 179,
+ "height": 25,
+ "seed": 961881101,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1663324636434,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "Fetch source files",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "Fetch source files"
+ },
+ {
+ "type": "rectangle",
+ "version": 149,
+ "versionNonce": 1653290435,
+ "isDeleted": false,
+ "id": "CSwjuAw6Nl67sqQ6p21ty",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 625.5,
+ "y": 423.25,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 392,
+ "height": 80,
+ "seed": 1011629069,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [
+ {
+ "id": "SpG_8HxzMHjM2HYK6Fgwx",
+ "type": "arrow"
+ },
+ {
+ "id": "zvmLoAH5oICRD5og-pBvu",
+ "type": "arrow"
+ }
+ ],
+ "updated": 1663324636434,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 272,
+ "versionNonce": 1195260909,
+ "isDeleted": false,
+ "id": "WX4axTU0IR7PJb0GkR-jq",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 645.5,
+ "y": 450.75,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 352,
+ "height": 25,
+ "seed": 716117827,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1663324636434,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "Expand them in the stage directory",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "Expand them in the stage directory"
+ },
+ {
+ "type": "rectangle",
+ "version": 185,
+ "versionNonce": 2143651171,
+ "isDeleted": false,
+ "id": "coUXke3Fv_DpjqG9zgEjQ",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 625.5,
+ "y": 548.5,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 392,
+ "height": 80,
+ "seed": 1934529891,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [
+ {
+ "type": "text",
+ "id": "yVIbU03yFYvpXnh9xIgET"
+ },
+ {
+ "id": "zvmLoAH5oICRD5og-pBvu",
+ "type": "arrow"
+ },
+ {
+ "id": "5yqrFWV-hhJ4RoVewqAC0",
+ "type": "arrow"
+ }
+ ],
+ "updated": 1663324636434,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 135,
+ "versionNonce": 1833580109,
+ "isDeleted": false,
+ "id": "yVIbU03yFYvpXnh9xIgET",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 630.5,
+ "y": 563.5,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 382,
+ "height": 50,
+ "seed": 1611291683,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1663324636434,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "Set the stage directory as the \ncurrent working directory",
+ "baseline": 43,
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": "coUXke3Fv_DpjqG9zgEjQ",
+ "originalText": "Set the stage directory as the current working directory"
+ },
+ {
+ "type": "rectangle",
+ "version": 253,
+ "versionNonce": 1704770627,
+ "isDeleted": false,
+ "id": "tBTBRiEA6AJABK4wnKF_-",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 625.5,
+ "y": 673.75,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 392,
+ "height": 80,
+ "seed": 1257829773,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [
+ {
+ "id": "GoE9udjDQxUqdsYCUnbVI",
+ "type": "text"
+ },
+ {
+ "type": "text",
+ "id": "GoE9udjDQxUqdsYCUnbVI"
+ },
+ {
+ "id": "5yqrFWV-hhJ4RoVewqAC0",
+ "type": "arrow"
+ },
+ {
+ "id": "v-9Voh5erXQ8iqoQ_9BVO",
+ "type": "arrow"
+ }
+ ],
+ "updated": 1663324636434,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 194,
+ "versionNonce": 1557028205,
+ "isDeleted": false,
+ "id": "GoE9udjDQxUqdsYCUnbVI",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 630.5,
+ "y": 701.25,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 382,
+ "height": 25,
+ "seed": 895792579,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1663324636434,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "Fork a new build environment",
+ "baseline": 18,
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": "tBTBRiEA6AJABK4wnKF_-",
+ "originalText": "Fork a new build environment"
+ },
+ {
+ "type": "rectangle",
+ "version": 321,
+ "versionNonce": 1675770851,
+ "isDeleted": false,
+ "id": "oAei2n-Ha1gpjnYdK7AwC",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 625.5,
+ "y": 799,
+ "strokeColor": "#000000",
+ "backgroundColor": "#228be6",
+ "width": 392,
+ "height": 80,
+ "seed": 701868237,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [
+ {
+ "id": "slfbd0bbRqA8648kZ5fns",
+ "type": "text"
+ },
+ {
+ "id": "slfbd0bbRqA8648kZ5fns",
+ "type": "text"
+ },
+ {
+ "type": "text",
+ "id": "slfbd0bbRqA8648kZ5fns"
+ },
+ {
+ "id": "v-9Voh5erXQ8iqoQ_9BVO",
+ "type": "arrow"
+ }
+ ],
+ "updated": 1663324636434,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 328,
+ "versionNonce": 1868179405,
+ "isDeleted": false,
+ "id": "slfbd0bbRqA8648kZ5fns",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 630.5,
+ "y": 826.5,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 382,
+ "height": 25,
+ "seed": 1179637379,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1663324636434,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "Execute the installation process",
+ "baseline": 18,
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": "oAei2n-Ha1gpjnYdK7AwC",
+ "originalText": "Execute the installation process"
+ },
+ {
+ "type": "arrow",
+ "version": 221,
+ "versionNonce": 1777917731,
+ "isDeleted": false,
+ "id": "SpG_8HxzMHjM2HYK6Fgwx",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1019,
+ "y": 339,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 96,
+ "height": 109,
+ "seed": 732445197,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElements": [],
+ "updated": 1663324636434,
+ "link": null,
+ "locked": false,
+ "startBinding": {
+ "elementId": "cLwg2WXUit_OTQmXLIdIW",
+ "focus": -0.7533277870216306,
+ "gap": 7
+ },
+ "endBinding": {
+ "elementId": "CSwjuAw6Nl67sqQ6p21ty",
+ "focus": 0.7554869684499315,
+ "gap": 6
+ },
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": "triangle",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 96,
+ 54
+ ],
+ [
+ 1,
+ 109
+ ]
+ ]
+ },
+ {
+ "type": "arrow",
+ "version": 299,
+ "versionNonce": 309789379,
+ "isDeleted": false,
+ "id": "zvmLoAH5oICRD5og-pBvu",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1026,
+ "y": 460.5,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 98,
+ "height": 124.33745608356844,
+ "seed": 708861581,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElements": [],
+ "updated": 1663324636435,
+ "link": null,
+ "locked": false,
+ "startBinding": {
+ "elementId": "CSwjuAw6Nl67sqQ6p21ty",
+ "focus": -0.7021630615640598,
+ "gap": 12
+ },
+ "endBinding": {
+ "elementId": "coUXke3Fv_DpjqG9zgEjQ",
+ "focus": 0.8530521262002744,
+ "gap": 12
+ },
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": "triangle",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 96,
+ 54
+ ],
+ [
+ -2,
+ 124.33745608356844
+ ]
+ ]
+ },
+ {
+ "type": "arrow",
+ "version": 301,
+ "versionNonce": 914472685,
+ "isDeleted": false,
+ "id": "5yqrFWV-hhJ4RoVewqAC0",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1019,
+ "y": 586.6789496258876,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 99,
+ "height": 123.78306157234579,
+ "seed": 642378381,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElements": [],
+ "updated": 1663324636435,
+ "link": null,
+ "locked": false,
+ "startBinding": {
+ "elementId": "coUXke3Fv_DpjqG9zgEjQ",
+ "focus": -0.6501663893510815,
+ "gap": 7
+ },
+ "endBinding": {
+ "elementId": "tBTBRiEA6AJABK4wnKF_-",
+ "focus": 0.8705418381344308,
+ "gap": 8
+ },
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": "triangle",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 99,
+ 42.82105037411236
+ ],
+ [
+ 10.000000000000227,
+ 123.78306157234579
+ ]
+ ]
+ },
+ {
+ "type": "arrow",
+ "version": 351,
+ "versionNonce": 984592995,
+ "isDeleted": false,
+ "id": "v-9Voh5erXQ8iqoQ_9BVO",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1031,
+ "y": 714.8662394200408,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 90,
+ "height": 137.7637151210173,
+ "seed": 698547757,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElements": [],
+ "updated": 1663324636435,
+ "link": null,
+ "locked": false,
+ "startBinding": {
+ "elementId": "tBTBRiEA6AJABK4wnKF_-",
+ "focus": -0.6014975041597337,
+ "gap": 10
+ },
+ "endBinding": {
+ "elementId": "oAei2n-Ha1gpjnYdK7AwC",
+ "focus": 0.9573045267489712,
+ "gap": 12
+ },
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": "triangle",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 90,
+ 33.633760579959244
+ ],
+ [
+ 4,
+ 137.7637151210173
+ ]
+ ]
+ }
+ ],
+ "id": "RzNgncGu1938Ma5Teh6qZ",
+ "created": 1663324659550
+ }
+ ]
+} \ No newline at end of file
diff --git a/lib/spack/docs/packaging_guide.rst b/lib/spack/docs/packaging_guide.rst
index 556dde31e8..f7b8cfe35f 100644
--- a/lib/spack/docs/packaging_guide.rst
+++ b/lib/spack/docs/packaging_guide.rst
@@ -34,24 +34,155 @@ ubiquitous in the scientific software community. Second, it's a modern
language and has many powerful features to help make package writing
easy.
----------------------------
-Creating & editing packages
----------------------------
-.. _cmd-spack-create:
+.. _installation_procedure:
-^^^^^^^^^^^^^^^^
-``spack create``
-^^^^^^^^^^^^^^^^
+--------------------------------------
+Overview of the installation procedure
+--------------------------------------
+
+Whenever Spack installs software, it goes through a series of predefined steps:
-The ``spack create`` command creates a directory with the package name and
-generates a ``package.py`` file with a boilerplate package template. If given
-a URL pointing to a tarball or other software archive, ``spack create`` is
-smart enough to determine basic information about the package, including its name
-and build system. In most cases, ``spack create`` plus a few modifications is
-all you need to get a package working.
+.. image:: images/installation_pipeline.png
+ :scale: 60 %
+ :align: center
-Here's an example:
+All these steps are influenced by the metadata in each ``package.py`` and
+by the current Spack configuration.
+Since build systems are different from one another, the execution of the
+last block in the figure is further expanded in a build system specific way.
+An example for ``CMake`` is, for instance:
+
+.. image:: images/builder_phases.png
+ :align: center
+ :scale: 60 %
+
+The predefined steps for each build system are called "phases".
+In general, the name and order in which the phases will be executed can be
+obtained by either reading the API docs at :py:mod:`~.spack.build_systems`, or
+using the ``spack info`` command:
+
+.. code-block:: console
+ :emphasize-lines: 13,14
+
+ $ spack info --phases m4
+ AutotoolsPackage: m4
+ Homepage: https://www.gnu.org/software/m4/m4.html
+
+ Safe versions:
+ 1.4.17 ftp://ftp.gnu.org/gnu/m4/m4-1.4.17.tar.gz
+
+ Variants:
+ Name Default Description
+
+ sigsegv on Build the libsigsegv dependency
+
+ Installation Phases:
+ autoreconf configure build install
+
+ Build Dependencies:
+ libsigsegv
+
+ ...
+
+An extensive list of available build systems and phases is provided in :ref:`installation_process`.
+
+
+------------------------
+Writing a package recipe
+------------------------
+
+Since v0.19, Spack supports two ways of writing a package recipe. The most commonly used is to encode both the metadata
+(directives, etc.) and the build behavior in a single class, like shown in the following example:
+
+.. code-block:: python
+
+ class Openjpeg(CMakePackage):
+ """OpenJPEG is an open-source JPEG 2000 codec written in C language"""
+
+ homepage = "https://github.com/uclouvain/openjpeg"
+ url = "https://github.com/uclouvain/openjpeg/archive/v2.3.1.tar.gz"
+
+ version("2.4.0", sha256="8702ba68b442657f11aaeb2b338443ca8d5fb95b0d845757968a7be31ef7f16d")
+
+ variant("codec", default=False, description="Build the CODEC executables")
+ depends_on("libpng", when="+codec")
+
+ def url_for_version(self, version):
+ if version >= Version("2.1.1"):
+ return super(Openjpeg, self).url_for_version(version)
+ url_fmt = "https://github.com/uclouvain/openjpeg/archive/version.{0}.tar.gz"
+ return url_fmt.format(version)
+
+ def cmake_args(self):
+ args = [
+ self.define_from_variant("BUILD_CODEC", "codec"),
+ self.define("BUILD_MJ2", False),
+ self.define("BUILD_THIRDPARTY", False),
+ ]
+ return args
+
+A package encoded with a single class is backward compatible with versions of Spack
+lower than v0.19, and so are custom repositories containing only recipes of this kind.
+The downside is that *this format doesn't allow packagers to use more than one build system in a single recipe*.
+
+To do that, we have to resort to the second way Spack has of writing packages, which involves writing a
+builder class explicitly. Using the same example as above, this reads:
+
+.. code-block:: python
+
+ class Openjpeg(CMakePackage):
+ """OpenJPEG is an open-source JPEG 2000 codec written in C language"""
+
+ homepage = "https://github.com/uclouvain/openjpeg"
+ url = "https://github.com/uclouvain/openjpeg/archive/v2.3.1.tar.gz"
+
+ version("2.4.0", sha256="8702ba68b442657f11aaeb2b338443ca8d5fb95b0d845757968a7be31ef7f16d")
+
+ variant("codec", default=False, description="Build the CODEC executables")
+ depends_on("libpng", when="+codec")
+
+ def url_for_version(self, version):
+ if version >= Version("2.1.1"):
+ return super(Openjpeg, self).url_for_version(version)
+ url_fmt = "https://github.com/uclouvain/openjpeg/archive/version.{0}.tar.gz"
+ return url_fmt.format(version)
+
+ class CMakeBuilder(spack.build_systems.cmake.CMakeBuilder):
+ def cmake_args(self):
+ args = [
+ self.define_from_variant("BUILD_CODEC", "codec"),
+ self.define("BUILD_MJ2", False),
+ self.define("BUILD_THIRDPARTY", False),
+ ]
+ return args
+
+This way of writing packages allows extending the recipe to support multiple build systems,
+see :ref:`multiple_build_systems` for more details. The downside is that recipes of this kind
+are only understood by Spack since v0.19+. More information on the internal architecture of
+Spack can be found at :ref:`package_class_structure`.
+
+.. note::
+
+ If a builder is implemented in ``package.py``, all build-specific methods must be moved
+ to the builder. This means that if you have a package like
+
+ .. code-block:: python
+
+ class Foo(CmakePackage):
+ def cmake_args(self):
+ ...
+
+ and you add a builder to the ``package.py``, you must move ``cmake_args`` to the builder.
+
+.. _cmd-spack-create:
+
+---------------------
+Creating new packages
+---------------------
+
+To help creating a new package Spack provides a command that generates a ``package.py``
+file in an existing repository, with a boilerplate package template. Here's an example:
.. code-block:: console
@@ -87,23 +218,6 @@ You do not *have* to download all of the versions up front. You can
always choose to download just one tarball initially, and run
:ref:`cmd-spack-checksum` later if you need more versions.
-Let's say you download 3 tarballs:
-
-.. code-block:: console
-
- How many would you like to checksum? (default is 1, q to abort) 3
- ==> Downloading...
- ==> Fetching https://gmplib.org/download/gmp/gmp-6.1.2.tar.bz2
- ######################################################################## 100.0%
- ==> Fetching https://gmplib.org/download/gmp/gmp-6.1.1.tar.bz2
- ######################################################################## 100.0%
- ==> Fetching https://gmplib.org/download/gmp/gmp-6.1.0.tar.bz2
- ######################################################################## 100.0%
- ==> Checksummed 3 versions of gmp:
- ==> This package looks like it uses the autotools build system
- ==> Created template for gmp package
- ==> Created package file: /Users/Adam/spack/var/spack/repos/builtin/packages/gmp/package.py
-
Spack automatically creates a directory in the appropriate repository,
generates a boilerplate template for your package, and opens up the new
``package.py`` in your favorite ``$EDITOR``:
@@ -111,6 +225,14 @@ generates a boilerplate template for your package, and opens up the new
.. code-block:: python
:linenos:
+ # Copyright 2013-2022 Lawrence Livermore National Security, LLC and other
+ # Spack Project Developers. See the top-level COPYRIGHT file for details.
+ #
+ # SPDX-License-Identifier: (Apache-2.0 OR MIT)
+
+ # ----------------------------------------------------------------------------
+ # If you submit this package back to Spack as a pull request,
+ # please first remove this boilerplate and all FIXME comments.
#
# This is a template package file for Spack. We've put "FIXME"
# next to all the things you'll want to change. Once you've handled
@@ -123,9 +245,8 @@ generates a boilerplate template for your package, and opens up the new
# spack edit gmp
#
# See the Spack documentation for more information on packaging.
- # If you submit this package back to Spack as a pull request,
- # please first remove this boilerplate and all FIXME comments.
- #
+ # ----------------------------------------------------------------------------
+ import spack.build_systems.autotools
from spack.package import *
@@ -133,19 +254,17 @@ generates a boilerplate template for your package, and opens up the new
"""FIXME: Put a proper description of your package here."""
# FIXME: Add a proper url for your package's homepage here.
- homepage = "http://www.example.com"
- url = "https://gmplib.org/download/gmp/gmp-6.1.2.tar.bz2"
+ homepage = "https://www.example.com"
+ url = "https://gmplib.org/download/gmp/gmp-6.1.2.tar.bz2"
# FIXME: Add a list of GitHub accounts to
# notify when the package is updated.
- # maintainers = ['github_user1', 'github_user2']
+ # maintainers = ["github_user1", "github_user2"]
- version('6.1.2', '8ddbb26dc3bd4e2302984debba1406a5')
- version('6.1.1', '4c175f86e11eb32d8bf9872ca3a8e11d')
- version('6.1.0', '86ee6e54ebfc4a90b643a65e402c4048')
+ version("6.2.1", sha256="eae9326beb4158c386e39a356818031bd28f3124cf915f8c5b1dc4c7a36b4d7c")
# FIXME: Add dependencies if required.
- # depends_on('foo')
+ # depends_on("foo")
def configure_args(self):
# FIXME: Add arguments other than --prefix
@@ -154,15 +273,16 @@ generates a boilerplate template for your package, and opens up the new
return args
The tedious stuff (creating the class, checksumming archives) has been
-done for you. You'll notice that ``spack create`` correctly detected that
-``gmp`` uses the Autotools build system. It created a new ``Gmp`` package
-that subclasses the ``AutotoolsPackage`` base class. This base class
-provides basic installation methods common to all Autotools packages:
+done for you. Spack correctly detected that ``gmp`` uses the ``autotools``
+build system, so it created a new ``Gmp`` package that subclasses the
+``AutotoolsPackage`` base class.
+
+The default installation procedure for a package subclassing the ``AutotoolsPackage``
+is to go through the typical process of:
.. code-block:: bash
./configure --prefix=/path/to/installation/directory
-
make
make check
make install
@@ -209,12 +329,14 @@ The rest of the tasks you need to do are as follows:
Your new package may require specific flags during ``configure``.
These can be added via ``configure_args``. Specifics will differ
depending on the package and its build system.
- :ref:`Implementing the install method <install-method>` is
+ :ref:`installation_process` is
covered in detail later.
-Passing a URL to ``spack create`` is a convenient and easy way to get
-a basic package template, but what if your software is licensed and
-cannot be downloaded from a URL? You can still create a boilerplate
+^^^^^^^^^^^^^^^^^^^^^^^^^
+Non-downloadable software
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+If your software cannot be downloaded from a URL you can still create a boilerplate
``package.py`` by telling ``spack create`` what name you want to use:
.. code-block:: console
@@ -223,40 +345,23 @@ cannot be downloaded from a URL? You can still create a boilerplate
This will create a simple ``intel`` package with an ``install()``
method that you can craft to install your package.
-
-What if ``spack create <url>`` guessed the wrong name or build system?
-For example, if your package uses the Autotools build system but does
-not come with a ``configure`` script, Spack won't realize it uses
-Autotools. You can overwrite the old package with ``--force`` and specify
-a name with ``--name`` or a build system template to use with ``--template``:
+Likewise, you can force the build system to be used with ``--template`` and,
+in case it's needed, you can overwrite a package already in the repository
+with ``--force``:
.. code-block:: console
$ spack create --name gmp https://gmplib.org/download/gmp/gmp-6.1.2.tar.bz2
$ spack create --force --template autotools https://gmplib.org/download/gmp/gmp-6.1.2.tar.bz2
-.. note::
-
- If you are creating a package that uses the Autotools build system
- but does not come with a ``configure`` script, you'll need to add an
- ``autoreconf`` method to your package that explains how to generate
- the ``configure`` script. You may also need the following dependencies:
-
- .. code-block:: python
-
- depends_on('autoconf', type='build')
- depends_on('automake', type='build')
- depends_on('libtool', type='build')
- depends_on('m4', type='build')
-
A complete list of available build system templates can be found by running
``spack create --help``.
.. _cmd-spack-edit:
-^^^^^^^^^^^^^^
-``spack edit``
-^^^^^^^^^^^^^^
+-------------------------
+Editing existing packages
+-------------------------
One of the easiest ways to learn how to write packages is to look at
existing ones. You can edit a package file by name with the ``spack
@@ -266,10 +371,15 @@ edit`` command:
$ spack edit gmp
-So, if you used ``spack create`` to create a package, then saved and
-closed the resulting file, you can get back to it with ``spack edit``.
-The ``gmp`` package actually lives in
-``$SPACK_ROOT/var/spack/repos/builtin/packages/gmp/package.py``,
+If you used ``spack create`` to create a package, you can get back to
+it later with ``spack edit``. For instance, the ``gmp`` package actually
+lives in:
+
+.. code-block:: console
+
+ $ spack location -p gmp
+ ${SPACK_ROOT}/var/spack/repos/builtin/packages/gmp/package.py
+
but ``spack edit`` provides a much simpler shortcut and saves you the
trouble of typing the full path.
@@ -2422,7 +2532,7 @@ Spack provides a mechanism for dependencies to influence the
environment of their dependents by overriding the
:meth:`setup_dependent_run_environment <spack.package_base.PackageBase.setup_dependent_run_environment>`
or the
-:meth:`setup_dependent_build_environment <spack.package_base.PackageBase.setup_dependent_build_environment>`
+:meth:`setup_dependent_build_environment <spack.builder.Builder.setup_dependent_build_environment>`
methods.
The Qt package, for instance, uses this call:
@@ -3280,67 +3390,91 @@ the Python extensions provided by them: once for ``+python`` and once
for ``~python``. Other than using a little extra disk space, that
solution has no serious problems.
-.. _installation_procedure:
+.. _installation_process:
+
+--------------------------------
+Overriding build system defaults
+--------------------------------
----------------------------------------
-Implementing the installation procedure
----------------------------------------
+.. note::
-The last element of a package is its **installation procedure**. This is
-where the real work of installation happens, and it's the main part of
-the package you'll need to customize for each piece of software.
+ If you code a single class in ``package.py`` all the functions shown in the table below
+ can be implemented with the same signature on the ``*Package`` instead of the corresponding builder.
-Defining an installation procedure means overriding a set of methods or attributes
-that will be called at some point during the installation of the package.
-The package base class, usually specialized for a given build system, determines the
-actual set of entities available for overriding.
-The classes that are currently provided by Spack are:
+
+Most of the time the default implementation of methods or attributes in build system base classes
+is what a packager needs, and just a very few entities need to be overwritten. Typically we just
+need to override methods like ``configure_args``:
+
+.. code-block:: python
+
+ def configure_args(self):
+ args = ["--enable-cxx"] + self.enable_or_disable("libs")
+ if "libs=static" in self.spec:
+ args.append("--with-pic")
+ return args
+
+The actual set of entities available for overriding in ``package.py`` depend on
+the build system. The build systems currently supported by Spack are:
+----------------------------------------------------------+----------------------------------+
-| **Base Class** | **Purpose** |
+| **API docs** | **Description** |
+==========================================================+==================================+
-| :class:`~spack.package_base.Package` | General base class not |
-| | specialized for any build system |
+| :class:`~spack.build_systems.generic` | Generic build system without any |
+| | base implementation |
+----------------------------------------------------------+----------------------------------+
-| :class:`~spack.build_systems.makefile.MakefilePackage` | Specialized class for packages |
-| | built invoking |
+| :class:`~spack.build_systems.makefile` | Specialized build system for |
+| | software built invoking |
| | hand-written Makefiles |
+----------------------------------------------------------+----------------------------------+
-| :class:`~spack.build_systems.autotools.AutotoolsPackage` | Specialized class for packages |
-| | built using GNU Autotools |
+| :class:`~spack.build_systems.autotools` | Specialized build system for |
+| | software built using |
+| | GNU Autotools |
++----------------------------------------------------------+----------------------------------+
+| :class:`~spack.build_systems.cmake` | Specialized build system for |
+| | software built using CMake |
+----------------------------------------------------------+----------------------------------+
-| :class:`~spack.build_systems.cmake.CMakePackage` | Specialized class for packages |
-| | built using CMake |
+| :class:`~spack.build_systems.maven` | Specialized build system for |
+| | software built using Maven |
+----------------------------------------------------------+----------------------------------+
-| :class:`~spack.build_systems.cuda.CudaPackage` | A helper class for packages that |
-| | use CUDA |
+| :class:`~spack.build_systems.meson` | Specialized build system for |
+| | software built using Meson |
+----------------------------------------------------------+----------------------------------+
-| :class:`~spack.build_systems.qmake.QMakePackage` | Specialized class for packages |
-| | built using QMake |
+| :class:`~spack.build_systems.nmake` | Specialized build system for |
+| | software built using NMake |
+----------------------------------------------------------+----------------------------------+
-| :class:`~spack.build_systems.rocm.ROCmPackage` | A helper class for packages that |
-| | use ROCm |
+| :class:`~spack.build_systems.qmake` | Specialized build system for |
+| | software built using QMake |
+----------------------------------------------------------+----------------------------------+
-| :class:`~spack.build_systems.scons.SConsPackage` | Specialized class for packages |
-| | built using SCons |
+| :class:`~spack.build_systems.scons` | Specialized build system for |
+| | software built using SCons |
+----------------------------------------------------------+----------------------------------+
-| :class:`~spack.build_systems.waf.WafPackage` | Specialized class for packages |
-| | built using Waf |
+| :class:`~spack.build_systems.waf` | Specialized build system for |
+| | software built using Waf |
+----------------------------------------------------------+----------------------------------+
-| :class:`~spack.build_systems.r.RPackage` | Specialized class for |
+| :class:`~spack.build_systems.r` | Specialized build system for |
| | R extensions |
+----------------------------------------------------------+----------------------------------+
-| :class:`~spack.build_systems.octave.OctavePackage` | Specialized class for |
+| :class:`~spack.build_systems.octave` | Specialized build system for |
| | Octave packages |
+----------------------------------------------------------+----------------------------------+
-| :class:`~spack.build_systems.python.PythonPackage` | Specialized class for |
+| :class:`~spack.build_systems.python` | Specialized build system for |
| | Python extensions |
+----------------------------------------------------------+----------------------------------+
-| :class:`~spack.build_systems.perl.PerlPackage` | Specialized class for |
+| :class:`~spack.build_systems.perl` | Specialized build system for |
| | Perl extensions |
+----------------------------------------------------------+----------------------------------+
-| :class:`~spack.build_systems.intel.IntelPackage` | Specialized class for licensed |
-| | Intel software |
+| :class:`~spack.build_systems.ruby` | Specialized build system for |
+| | Ruby extensions |
++----------------------------------------------------------+----------------------------------+
+| :class:`~spack.build_systems.intel` | Specialized build system for |
+| | licensed Intel software |
++----------------------------------------------------------+----------------------------------+
+| :class:`~spack.build_systems.oneapi` | Specialized build system for |
+| | Intel onaAPI software |
++----------------------------------------------------------+----------------------------------+
+| :class:`~spack.build_systems.aspell_dict` | Specialized build system for |
+| | Aspell dictionaries |
+----------------------------------------------------------+----------------------------------+
@@ -3353,69 +3487,17 @@ The classes that are currently provided by Spack are:
For example, a Python extension installed with CMake would ``extends('python')`` and
subclass from :class:`~spack.build_systems.cmake.CMakePackage`.
-^^^^^^^^^^^^^^^^^^^^^
-Installation pipeline
-^^^^^^^^^^^^^^^^^^^^^
-
-When a user runs ``spack install``, Spack:
-
-1. Fetches an archive for the correct version of the software.
-2. Expands the archive.
-3. Sets the current working directory to the root directory of the expanded archive.
-
-Then, depending on the base class of the package under consideration, it will execute
-a certain number of **phases** that reflect the way a package of that type is usually built.
-The name and order in which the phases will be executed can be obtained either reading the API
-docs at :py:mod:`~.spack.build_systems`, or using the ``spack info`` command:
-
-.. code-block:: console
- :emphasize-lines: 26-27
-
- $ spack info --phases m4
- AutotoolsPackage: m4
-
- Description:
- GNU M4 is an implementation of the traditional Unix macro processor.
-
- Homepage: https://www.gnu.org/software/m4/m4.html
-
- Preferred version:
- 1.4.19 https://ftpmirror.gnu.org/m4/m4-1.4.19.tar.gz
-
- Safe versions:
- 1.4.19 https://ftpmirror.gnu.org/m4/m4-1.4.19.tar.gz
- 1.4.18 https://ftpmirror.gnu.org/m4/m4-1.4.18.tar.gz
- 1.4.17 https://ftpmirror.gnu.org/m4/m4-1.4.17.tar.gz
-
- Deprecated versions:
- None
-
- Variants:
- Name [Default] When Allowed values Description
- ============== ==== ============== ===============================
-
- sigsegv [on] -- on, off Build the libsigsegv dependency
-
- Installation Phases:
- autoreconf configure build install
-
- Build Dependencies:
- diffutils gnuconfig libsigsegv
-
- Link Dependencies:
- libsigsegv
-
- Run Dependencies:
- None
-
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+Overriding builder methods
+^^^^^^^^^^^^^^^^^^^^^^^^^^
-Typically, phases have default implementations that fit most of the common cases:
+Build-system "phases" have default implementations that fit most of the common cases:
.. literalinclude:: _spack_root/lib/spack/spack/build_systems/autotools.py
- :pyobject: AutotoolsPackage.configure
+ :pyobject: AutotoolsBuilder.configure
:linenos:
-It is thus just sufficient for a packager to override a few
+It is usually sufficient for a packager to override a few
build system specific helper methods or attributes to provide, for instance,
configure arguments:
@@ -3423,31 +3505,31 @@ configure arguments:
:pyobject: M4.configure_args
:linenos:
-.. note::
- Each specific build system has a list of attributes that can be overridden to
- fine-tune the installation of a package without overriding an entire phase. To
- have more information on them the place to go is the API docs of the :py:mod:`~.spack.build_systems`
- module.
+Each specific build system has a list of attributes and methods that can be overridden to
+fine-tune the installation of a package without overriding an entire phase. To
+have more information on them the place to go is the API docs of the :py:mod:`~.spack.build_systems`
+module.
^^^^^^^^^^^^^^^^^^^^^^^^^^
Overriding an entire phase
^^^^^^^^^^^^^^^^^^^^^^^^^^
-In extreme cases it may be necessary to override an entire phase. Regardless
-of the build system, the signature is the same. For example, the signature
-for the install phase is:
+Sometimes it is necessary to override an entire phase. If the ``package.py`` contains
+a single class recipe, see :ref:`package_class_structure`, then the signature for a
+phase is:
.. code-block:: python
- class Foo(Package):
+ class Openjpeg(CMakePackage):
def install(self, spec, prefix):
...
+regardless of the build system. The arguments for the phase are:
+
``self``
- For those not used to Python instance methods, this is the
- package itself. In this case it's an instance of ``Foo``, which
- extends ``Package``. For API docs on Package objects, see
- :py:class:`Package <spack.package_base.Package>`.
+ This is the package object, which extends ``CMakePackage``.
+ For API docs on Package objects, see
+ :py:class:`Package <spack.package_base.PackageBase>`.
``spec``
This is the concrete spec object created by Spack from an
@@ -3462,12 +3544,111 @@ for the install phase is:
The arguments ``spec`` and ``prefix`` are passed only for convenience, as they always
correspond to ``self.spec`` and ``self.spec.prefix`` respectively.
-As mentioned in :ref:`install-environment`, you will usually not need to refer
-to dependencies explicitly in your package file, as the compiler wrappers take care of most of
-the heavy lifting here. There will be times, though, when you need to refer to
-the install locations of dependencies, or when you need to do something different
-depending on the version, compiler, dependencies, etc. that your package is
-built with. These parameters give you access to this type of information.
+If the ``package.py`` encodes builders explicitly, the signature for a phase changes slightly:
+
+.. code-block:: python
+
+ class CMakeBuilder(spack.build_systems.cmake.CMakeBuilder):
+ def install(self, pkg, spec, prefix):
+ ...
+
+In this case the package is passed as the second argument, and ``self`` is the builder instance.
+
+.. _multiple_build_systems:
+
+^^^^^^^^^^^^^^^^^^^^^^
+Multiple build systems
+^^^^^^^^^^^^^^^^^^^^^^
+
+There are cases where a software actively supports two build systems, or changes build systems
+as it evolves, or needs different build systems on different platforms. Spack allows dealing with
+these cases natively, if a recipe is written using builders explicitly.
+
+For instance, software that supports two build systems unconditionally should derive from
+both ``*Package`` base classes, and declare the possible use of multiple build systems using
+a directive:
+
+.. code-block:: python
+
+ class ArpackNg(CMakePackage, AutotoolsPackage):
+
+ build_system("cmake", "autotools", default="cmake")
+
+In this case the software can be built with both ``autotools`` and ``cmake``. Since the package
+supports multiple build systems, it is necessary to declare which one is the default. The ``package.py``
+will likely contain some overriding of default builder methods:
+
+.. code-block:: python
+
+ class CMakeBuilder(spack.build_systems.cmake.CMakeBuilder):
+ def cmake_args(self):
+ pass
+
+ class Autotoolsbuilder(spack.build_systems.autotools.AutotoolsBuilder):
+ def configure_args(self):
+ pass
+
+In more complex cases it might happen that the build system changes according to certain conditions,
+for instance across versions. That can be expressed with conditional variant values:
+
+.. code-block:: python
+
+ class ArpackNg(CMakePackage, AutotoolsPackage):
+
+ build_system(
+ conditional("cmake", when="@0.64:"),
+ conditional("autotools", when="@:0.63"),
+ default="cmake",
+ )
+
+In the example the directive impose a change from ``Autotools`` to ``CMake`` going
+from ``v0.63`` to ``v0.64``.
+
+^^^^^^^^^^^^^^^^^^
+Mixin base classes
+^^^^^^^^^^^^^^^^^^
+
+Besides build systems, there are other cases where common metadata and behavior can be extracted
+and reused by many packages. For instance, packages that depend on ``Cuda`` or ``Rocm``, share
+common dependencies and constraints. To factor these attributes into a single place, Spack provides
+a few mixin classes in the ``spack.build_systems`` module:
+
++---------------------------------------------------------------+----------------------------------+
+| **API docs** | **Description** |
++===============================================================+==================================+
+| :class:`~spack.build_systems.cuda.CudaPackage` | A helper class for packages that |
+| | use CUDA |
++---------------------------------------------------------------+----------------------------------+
+| :class:`~spack.build_systems.rocm.ROCmPackage` | A helper class for packages that |
+| | use ROCm |
++---------------------------------------------------------------+----------------------------------+
+| :class:`~spack.build_systems.gnu.GNUMirrorPackage` | A helper class for GNU packages |
++---------------------------------------------------------------+----------------------------------+
+| :class:`~spack.build_systems.python.PythonExtension` | A helper class for Python |
+| | extensions |
++---------------------------------------------------------------+----------------------------------+
+| :class:`~spack.build_systems.sourceforge.SourceforgePackage` | A helper class for packages |
+| | from sourceforge.org |
++---------------------------------------------------------------+----------------------------------+
+| :class:`~spack.build_systems.sourceware.SourcewarePackage` | A helper class for packages |
+| | from sourceware.org |
++---------------------------------------------------------------+----------------------------------+
+| :class:`~spack.build_systems.xorg.XorgPackage` | A helper class for x.org |
+| | packages |
++---------------------------------------------------------------+----------------------------------+
+
+These classes should be used by adding them to the inheritance tree of the package that needs them,
+for instance:
+
+.. code-block:: python
+
+ class Cp2k(MakefilePackage, CudaPackage):
+ """CP2K is a quantum chemistry and solid state physics software package
+ that can perform atomistic simulations of solid state, liquid, molecular,
+ periodic, material, crystal, and biological systems
+ """
+
+In the example above ``Cp2k`` inherits all the conflicts and variants that ``CudaPackage`` defines.
.. _install-environment:
@@ -6116,3 +6297,82 @@ might write:
DWARF_PREFIX = $(spack location --install-dir libdwarf)
CXXFLAGS += -I$DWARF_PREFIX/include
CXXFLAGS += -L$DWARF_PREFIX/lib
+
+
+.. _package_class_structure:
+
+--------------------------
+Package class architecture
+--------------------------
+
+.. note::
+
+ This section aims to provide a high-level knowledge of how the package class architecture evolved
+ in Spack, and provides some insights on the current design.
+
+Packages in Spack were originally designed to support only a single build system. The overall
+class structure for a package looked like:
+
+.. image:: images/original_package_architecture.png
+ :scale: 60 %
+ :align: center
+
+In this architecture the base class ``AutotoolsPackage`` was responsible for both the metadata
+related to the ``autotools`` build system (e.g. dependencies or variants common to all packages
+using it), and for encoding the default installation procedure.
+
+In reality, a non-negligible number of packages are either changing their build system during the evolution of the
+project, or using different build systems for different platforms. An architecture based on a single class
+requires hacks or other workarounds to deal with these cases.
+
+To support a model more adherent to reality, Spack v0.19 changed its internal design by extracting
+the attributes and methods related to building a software into a separate hierarchy:
+
+.. image:: images/builder_package_architecture.png
+ :scale: 60 %
+ :align: center
+
+In this new format each ``package.py`` contains one ``*Package`` class that gathers all the metadata,
+and one or more ``*Builder`` classes that encode the installation procedure. A specific builder object
+is created just before the software is built, so at a time where Spack knows which build system needs
+to be used for the current installation, and receives a ``package`` object during initialization.
+
+^^^^^^^^^^^^^^^^^^^^^^^^
+``build_system`` variant
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+To allow imposing conditions based on the build system, each package must a have ``build_system`` variant,
+which is usually inherited from base classes. This variant allows for writing metadata that is conditional
+on the build system:
+
+.. code-block:: python
+
+ with when("build_system=cmake"):
+ depends_on("cmake", type="build")
+
+and also for selecting a specific build system from a spec literal, like in the following command:
+
+.. code-block:: console
+
+ $ spack install arpack-ng build_system=autotools
+
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Compatibility with single-class format
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Internally, Spack always uses builders to perform operations related to the installation of a specific software.
+The builders are created in the ``spack.builder.create`` function
+
+.. literalinclude:: _spack_root/lib/spack/spack/builder.py
+ :pyobject: create
+
+To achieve backward compatibility with the single-class format Spack creates in this function a special
+"adapter builder", if no custom builder is detected in the recipe:
+
+.. image:: images/adapter.png
+ :scale: 60 %
+ :align: center
+
+Overall the role of the adapter is to route access to attributes of methods first through the ``*Package``
+hierarchy, and then back to the base class builder. This is schematically shown in the diagram above, where
+the adapter role is to "emulate" a method resolution order like the one represented by the red arrows. \ No newline at end of file
diff --git a/lib/spack/spack/audit.py b/lib/spack/spack/audit.py
index ee974a19b1..de9fc1a05b 100644
--- a/lib/spack/spack/audit.py
+++ b/lib/spack/spack/audit.py
@@ -503,6 +503,33 @@ def _ensure_all_packages_use_sha256_checksums(pkgs, error_cls):
return errors
+@package_properties
+def _ensure_env_methods_are_ported_to_builders(pkgs, error_cls):
+ """Ensure that methods modifying the build environment are ported to builder classes."""
+ errors = []
+ for pkg_name in pkgs:
+ pkg_cls = spack.repo.path.get_pkg_class(pkg_name)
+ buildsystem_variant, _ = pkg_cls.variants["build_system"]
+ buildsystem_names = [getattr(x, "value", x) for x in buildsystem_variant.values]
+ builder_cls_names = [spack.builder.BUILDER_CLS[x].__name__ for x in buildsystem_names]
+ module = pkg_cls.module
+ has_builders_in_package_py = any(
+ getattr(module, name, False) for name in builder_cls_names
+ )
+ if not has_builders_in_package_py:
+ continue
+
+ for method_name in ("setup_build_environment", "setup_dependent_build_environment"):
+ if hasattr(pkg_cls, method_name):
+ msg = (
+ "Package '{}' need to move the '{}' method from the package class to the"
+ " appropriate builder class".format(pkg_name, method_name)
+ )
+ errors.append(error_cls(msg, []))
+
+ return errors
+
+
@package_https_directives
def _linting_package_file(pkgs, error_cls):
"""Check for correctness of links"""
@@ -660,7 +687,13 @@ def _ensure_variant_defaults_are_parsable(pkgs, error_cls):
errors.append(error_cls(error_msg.format(variant_name, pkg_name), []))
continue
- vspec = variant.make_default()
+ try:
+ vspec = variant.make_default()
+ except spack.variant.MultipleValuesInExclusiveVariantError:
+ error_msg = "Cannot create a default value for the variant '{}' in package '{}'"
+ errors.append(error_cls(error_msg.format(variant_name, pkg_name), []))
+ continue
+
try:
variant.validate_or_raise(vspec, pkg_cls=pkg_cls)
except spack.variant.InvalidVariantValueError:
diff --git a/lib/spack/spack/build_environment.py b/lib/spack/spack/build_environment.py
index f8fda997d3..9fe7c1fbb7 100644
--- a/lib/spack/spack/build_environment.py
+++ b/lib/spack/spack/build_environment.py
@@ -52,6 +52,7 @@ from llnl.util.tty.log import MultiProcessFd
import spack.build_systems.cmake
import spack.build_systems.meson
+import spack.builder
import spack.config
import spack.install_test
import spack.main
@@ -558,9 +559,9 @@ def _set_variables_for_single_module(pkg, module):
if sys.platform == "win32":
m.nmake = Executable("nmake")
# Standard CMake arguments
- m.std_cmake_args = spack.build_systems.cmake.CMakePackage._std_args(pkg)
- m.std_meson_args = spack.build_systems.meson.MesonPackage._std_args(pkg)
- m.std_pip_args = spack.build_systems.python.PythonPackage._std_args(pkg)
+ m.std_cmake_args = spack.build_systems.cmake.CMakeBuilder.std_args(pkg)
+ m.std_meson_args = spack.build_systems.meson.MesonBuilder.std_args(pkg)
+ m.std_pip_args = spack.build_systems.python.PythonPipBuilder.std_args(pkg)
# Put spack compiler paths in module scope.
link_dir = spack.paths.build_env_path
@@ -727,38 +728,6 @@ def get_rpaths(pkg):
return list(dedupe(filter_system_paths(rpaths)))
-def get_std_cmake_args(pkg):
- """List of standard arguments used if a package is a CMakePackage.
-
- Returns:
- list: standard arguments that would be used if this
- package were a CMakePackage instance.
-
- Args:
- pkg (spack.package_base.PackageBase): package under consideration
-
- Returns:
- list: arguments for cmake
- """
- return spack.build_systems.cmake.CMakePackage._std_args(pkg)
-
-
-def get_std_meson_args(pkg):
- """List of standard arguments used if a package is a MesonPackage.
-
- Returns:
- list: standard arguments that would be used if this
- package were a MesonPackage instance.
-
- Args:
- pkg (spack.package_base.PackageBase): package under consideration
-
- Returns:
- list: arguments for meson
- """
- return spack.build_systems.meson.MesonPackage._std_args(pkg)
-
-
def parent_class_modules(cls):
"""
Get list of superclass modules that descend from spack.package_base.PackageBase
@@ -819,7 +788,8 @@ def setup_package(pkg, dirty, context="build"):
platform.setup_platform_environment(pkg, env_mods)
if context == "build":
- pkg.setup_build_environment(env_mods)
+ builder = spack.builder.create(pkg)
+ builder.setup_build_environment(env_mods)
if (not dirty) and (not env_mods.is_unset("CPATH")):
tty.debug(
@@ -1015,7 +985,8 @@ def modifications_from_dependencies(
module.__dict__.update(changes.__dict__)
if context == "build":
- dpkg.setup_dependent_build_environment(env, spec)
+ builder = spack.builder.create(dpkg)
+ builder.setup_dependent_build_environment(env, spec)
else:
dpkg.setup_dependent_run_environment(env, spec)
@@ -1117,8 +1088,20 @@ def _setup_pkg_and_run(
pkg.test_suite.stage, spack.install_test.TestSuite.test_log_name(pkg.spec)
)
+ error_msg = str(exc)
+ if isinstance(exc, (spack.multimethod.NoSuchMethodError, AttributeError)):
+ error_msg = (
+ "The '{}' package cannot find an attribute while trying to build "
+ "from sources. This might be due to a change in Spack's package format "
+ "to support multiple build-systems for a single package. You can fix this "
+ "by updating the build recipe, and you can also report the issue as a bug. "
+ "More information at https://spack.readthedocs.io/en/latest/packaging_guide.html#installation-procedure"
+ ).format(pkg.name)
+ error_msg = colorize("@*R{{{}}}".format(error_msg))
+ error_msg = "{}\n\n{}".format(str(exc), error_msg)
+
# make a pickleable exception to send to parent.
- msg = "%s: %s" % (exc_type.__name__, str(exc))
+ msg = "%s: %s" % (exc_type.__name__, error_msg)
ce = ChildError(
msg,
diff --git a/lib/spack/spack/build_systems/_checks.py b/lib/spack/spack/build_systems/_checks.py
new file mode 100644
index 0000000000..73d5bbdb93
--- /dev/null
+++ b/lib/spack/spack/build_systems/_checks.py
@@ -0,0 +1,124 @@
+# Copyright 2013-2022 Lawrence Livermore National Security, LLC and other
+# Spack Project Developers. See the top-level COPYRIGHT file for details.
+#
+# SPDX-License-Identifier: (Apache-2.0 OR MIT)
+import os
+
+import six
+
+import llnl.util.lang
+
+import spack.builder
+import spack.installer
+import spack.relocate
+import spack.store
+
+
+def sanity_check_prefix(builder):
+ """Check that specific directories and files are created after installation.
+
+ The files to be checked are in the ``sanity_check_is_file`` attribute of the
+ package object, while the directories are in the ``sanity_check_is_dir``.
+
+ Args:
+ builder (spack.builder.Builder): builder that installed the package
+ """
+ pkg = builder.pkg
+
+ def check_paths(path_list, filetype, predicate):
+ if isinstance(path_list, six.string_types):
+ path_list = [path_list]
+
+ for path in path_list:
+ abs_path = os.path.join(pkg.prefix, path)
+ if not predicate(abs_path):
+ msg = "Install failed for {0}. No such {1} in prefix: {2}"
+ msg = msg.format(pkg.name, filetype, path)
+ raise spack.installer.InstallError(msg)
+
+ check_paths(pkg.sanity_check_is_file, "file", os.path.isfile)
+ check_paths(pkg.sanity_check_is_dir, "directory", os.path.isdir)
+
+ ignore_file = llnl.util.lang.match_predicate(spack.store.layout.hidden_file_regexes)
+ if all(map(ignore_file, os.listdir(pkg.prefix))):
+ msg = "Install failed for {0}. Nothing was installed!"
+ raise spack.installer.InstallError(msg.format(pkg.name))
+
+
+def apply_macos_rpath_fixups(builder):
+ """On Darwin, make installed libraries more easily relocatable.
+
+ Some build systems (handrolled, autotools, makefiles) can set their own
+ rpaths that are duplicated by spack's compiler wrapper. This fixup
+ interrogates, and postprocesses if necessary, all libraries installed
+ by the code.
+
+ It should be added as a @run_after to packaging systems (or individual
+ packages) that do not install relocatable libraries by default.
+
+ Args:
+ builder (spack.builder.Builder): builder that installed the package
+ """
+ spack.relocate.fixup_macos_rpaths(builder.spec)
+
+
+def ensure_build_dependencies_or_raise(spec, dependencies, error_msg):
+ """Ensure that some build dependencies are present in the concrete spec.
+
+ If not, raise a RuntimeError with a helpful error message.
+
+ Args:
+ spec (spack.spec.Spec): concrete spec to be checked.
+ dependencies (list of spack.spec.Spec): list of abstract specs to be satisfied
+ error_msg (str): brief error message to be prepended to a longer description
+
+ Raises:
+ RuntimeError: when the required build dependencies are not found
+ """
+ assert spec.concrete, "Can ensure build dependencies only on concrete specs"
+ build_deps = [d.name for d in spec.dependencies(deptype="build")]
+ missing_deps = [x for x in dependencies if x not in build_deps]
+
+ if not missing_deps:
+ return
+
+ # Raise an exception on missing deps.
+ msg = (
+ "{0}: missing dependencies: {1}.\n\nPlease add "
+ "the following lines to the package:\n\n".format(error_msg, ", ".join(missing_deps))
+ )
+
+ for dep in missing_deps:
+ msg += " depends_on('{0}', type='build', when='@{1} {2}')\n".format(
+ dep, spec.version, "build_system=autotools"
+ )
+
+ msg += "\nUpdate the version (when='@{0}') as needed.".format(spec.version)
+ raise RuntimeError(msg)
+
+
+def execute_build_time_tests(builder):
+ """Execute the build-time tests prescribed by builder.
+
+ Args:
+ builder (Builder): builder prescribing the test callbacks. The name of the callbacks is
+ stored as a list of strings in the ``build_time_test_callbacks`` attribute.
+ """
+ builder.pkg.run_test_callbacks(builder, builder.build_time_test_callbacks, "build")
+
+
+def execute_install_time_tests(builder):
+ """Execute the install-time tests prescribed by builder.
+
+ Args:
+ builder (Builder): builder prescribing the test callbacks. The name of the callbacks is
+ stored as a list of strings in the ``install_time_test_callbacks`` attribute.
+ """
+ builder.pkg.run_test_callbacks(builder, builder.install_time_test_callbacks, "install")
+
+
+class BaseBuilder(spack.builder.Builder):
+ """Base class for builders to register common checks"""
+
+ # Check that self.prefix is there after installation
+ spack.builder.run_after("install")(sanity_check_prefix)
diff --git a/lib/spack/spack/build_systems/aspell_dict.py b/lib/spack/spack/build_systems/aspell_dict.py
index f1e41cc3df..9de8255e68 100644
--- a/lib/spack/spack/build_systems/aspell_dict.py
+++ b/lib/spack/spack/build_systems/aspell_dict.py
@@ -2,18 +2,36 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
+import llnl.util.filesystem as fs
-# Why doesn't this work for me?
-# from spack import *
-from llnl.util.filesystem import filter_file
+import spack.directives
+import spack.package_base
+import spack.util.executable
-from spack.build_systems.autotools import AutotoolsPackage
-from spack.directives import extends
-from spack.package_base import ExtensionError
-from spack.util.executable import which
+from .autotools import AutotoolsBuilder, AutotoolsPackage
+
+
+class AspellBuilder(AutotoolsBuilder):
+ """The Aspell builder is close enough to an autotools builder to allow
+ specializing the builder class, so to use variables that are specific
+ to the Aspell extensions.
+ """
+
+ def configure(self, pkg, spec, prefix):
+ aspell = spec["aspell"].prefix.bin.aspell
+ prezip = spec["aspell"].prefix.bin.prezip
+ destdir = prefix
+
+ sh = spack.util.executable.which("sh")
+ sh(
+ "./configure",
+ "--vars",
+ "ASPELL={0}".format(aspell),
+ "PREZIP={0}".format(prezip),
+ "DESTDIR={0}".format(destdir),
+ )
-#
# Aspell dictionaries install their bits into their prefix.lib
# and when activated they'll get symlinked into the appropriate aspell's
# dict dir (see aspell's {de,}activate methods).
@@ -23,12 +41,17 @@ from spack.util.executable import which
class AspellDictPackage(AutotoolsPackage):
"""Specialized class for building aspell dictionairies."""
- extends("aspell")
+ spack.directives.extends("aspell", when="build_system=autotools")
+
+ #: Override the default autotools builder
+ AutotoolsBuilder = AspellBuilder
def view_destination(self, view):
aspell_spec = self.spec["aspell"]
if view.get_projection_for_spec(aspell_spec) != aspell_spec.prefix:
- raise ExtensionError("aspell does not support non-global extensions")
+ raise spack.package_base.ExtensionError(
+ "aspell does not support non-global extensions"
+ )
aspell = aspell_spec.command
return aspell("dump", "config", "dict-dir", output=str).strip()
@@ -36,19 +59,5 @@ class AspellDictPackage(AutotoolsPackage):
return self.prefix.lib
def patch(self):
- filter_file(r"^dictdir=.*$", "dictdir=/lib", "configure")
- filter_file(r"^datadir=.*$", "datadir=/lib", "configure")
-
- def configure(self, spec, prefix):
- aspell = spec["aspell"].prefix.bin.aspell
- prezip = spec["aspell"].prefix.bin.prezip
- destdir = prefix
-
- sh = which("sh")
- sh(
- "./configure",
- "--vars",
- "ASPELL={0}".format(aspell),
- "PREZIP={0}".format(prezip),
- "DESTDIR={0}".format(destdir),
- )
+ fs.filter_file(r"^dictdir=.*$", "dictdir=/lib", "configure")
+ fs.filter_file(r"^datadir=.*$", "datadir=/lib", "configure")
diff --git a/lib/spack/spack/build_systems/autotools.py b/lib/spack/spack/build_systems/autotools.py
index 81a572de52..76e6bb27ce 100644
--- a/lib/spack/spack/build_systems/autotools.py
+++ b/lib/spack/spack/build_systems/autotools.py
@@ -6,87 +6,140 @@ import inspect
import os
import os.path
import stat
-from subprocess import PIPE, check_call
+import subprocess
from typing import List # novm
import llnl.util.filesystem as fs
import llnl.util.tty as tty
-from llnl.util.filesystem import force_remove, working_dir
-from spack.build_environment import InstallError
-from spack.directives import conflicts, depends_on
+import spack.build_environment
+import spack.builder
+import spack.package_base
+from spack.directives import build_system, conflicts, depends_on
+from spack.multimethod import when
from spack.operating_systems.mac_os import macos_version
-from spack.package_base import PackageBase, run_after, run_before
from spack.util.executable import Executable
from spack.version import Version
+from ._checks import (
+ BaseBuilder,
+ apply_macos_rpath_fixups,
+ ensure_build_dependencies_or_raise,
+ execute_build_time_tests,
+ execute_install_time_tests,
+)
-class AutotoolsPackage(PackageBase):
- """Specialized class for packages built using GNU Autotools.
- This class provides four phases that can be overridden:
+class AutotoolsPackage(spack.package_base.PackageBase):
+ """Specialized class for packages built using GNU Autotools."""
- 1. :py:meth:`~.AutotoolsPackage.autoreconf`
- 2. :py:meth:`~.AutotoolsPackage.configure`
- 3. :py:meth:`~.AutotoolsPackage.build`
- 4. :py:meth:`~.AutotoolsPackage.install`
+ #: This attribute is used in UI queries that need to know the build
+ #: system base class
+ build_system_class = "AutotoolsPackage"
+
+ #: Legacy buildsystem attribute used to deserialize and install old specs
+ legacy_buildsystem = "autotools"
+
+ build_system("autotools")
+
+ with when("build_system=autotools"):
+ depends_on("gnuconfig", type="build", when="target=ppc64le:")
+ depends_on("gnuconfig", type="build", when="target=aarch64:")
+ depends_on("gnuconfig", type="build", when="target=riscv64:")
+ conflicts("platform=windows")
+
+ def flags_to_build_system_args(self, flags):
+ """Produces a list of all command line arguments to pass specified
+ compiler flags to configure."""
+ # Has to be dynamic attribute due to caching.
+ setattr(self, "configure_flag_args", [])
+ for flag, values in flags.items():
+ if values:
+ values_str = "{0}={1}".format(flag.upper(), " ".join(values))
+ self.configure_flag_args.append(values_str)
+ # Spack's fflags are meant for both F77 and FC, therefore we
+ # additionaly set FCFLAGS if required.
+ values = flags.get("fflags", None)
+ if values:
+ values_str = "FCFLAGS={0}".format(" ".join(values))
+ self.configure_flag_args.append(values_str)
+
+ # Legacy methods (used by too many packages to change them,
+ # need to forward to the builder)
+ def enable_or_disable(self, *args, **kwargs):
+ return self.builder.enable_or_disable(*args, **kwargs)
+
+ def with_or_without(self, *args, **kwargs):
+ return self.builder.with_or_without(*args, **kwargs)
+
+
+@spack.builder.builder("autotools")
+class AutotoolsBuilder(BaseBuilder):
+ """The autotools builder encodes the default way of installing software built
+ with autotools. It has four phases that can be overridden, if need be:
+
+ 1. :py:meth:`~.AutotoolsBuilder.autoreconf`
+ 2. :py:meth:`~.AutotoolsBuilder.configure`
+ 3. :py:meth:`~.AutotoolsBuilder.build`
+ 4. :py:meth:`~.AutotoolsBuilder.install`
+
+ They all have sensible defaults and for many packages the only thing necessary
+ is to override the helper method
+ :meth:`~spack.build_systems.autotools.AutotoolsBuilder.configure_args`.
- They all have sensible defaults and for many packages the only thing
- necessary will be to override the helper method
- :meth:`~spack.build_systems.autotools.AutotoolsPackage.configure_args`.
For a finer tuning you may also override:
+-----------------------------------------------+--------------------+
| **Method** | **Purpose** |
+===============================================+====================+
- | :py:attr:`~.AutotoolsPackage.build_targets` | Specify ``make`` |
+ | :py:attr:`~.AutotoolsBuilder.build_targets` | Specify ``make`` |
| | targets for the |
| | build phase |
+-----------------------------------------------+--------------------+
- | :py:attr:`~.AutotoolsPackage.install_targets` | Specify ``make`` |
+ | :py:attr:`~.AutotoolsBuilder.install_targets` | Specify ``make`` |
| | targets for the |
| | install phase |
+-----------------------------------------------+--------------------+
- | :py:meth:`~.AutotoolsPackage.check` | Run build time |
+ | :py:meth:`~.AutotoolsBuilder.check` | Run build time |
| | tests if required |
+-----------------------------------------------+--------------------+
"""
#: Phases of a GNU Autotools package
- phases = ["autoreconf", "configure", "build", "install"]
- #: This attribute is used in UI queries that need to know the build
- #: system base class
- build_system_class = "AutotoolsPackage"
-
- @property
- def patch_config_files(self):
- """
- Whether or not to update old ``config.guess`` and ``config.sub`` files
- distributed with the tarball. This currently only applies to
- ``ppc64le:``, ``aarch64:``, and ``riscv64`` target architectures. The
- substitutes are taken from the ``gnuconfig`` package, which is
- automatically added as a build dependency for these architectures. In
- case system versions of these config files are required, the
- ``gnuconfig`` package can be marked external with a prefix pointing to
- the directory containing the system ``config.guess`` and ``config.sub``
- files.
- """
- return (
- self.spec.satisfies("target=ppc64le:")
- or self.spec.satisfies("target=aarch64:")
- or self.spec.satisfies("target=riscv64:")
- )
-
- #: Whether or not to update ``libtool``
- #: (currently only for Arm/Clang/Fujitsu/NVHPC compilers)
+ phases = ("autoreconf", "configure", "build", "install")
+
+ #: Names associated with package methods in the old build-system format
+ legacy_methods = (
+ "configure_args",
+ "check",
+ "installcheck",
+ )
+
+ #: Names associated with package attributes in the old build-system format
+ legacy_attributes = (
+ "archive_files",
+ "patch_libtool",
+ "build_targets",
+ "install_targets",
+ "build_time_test_callbacks",
+ "install_time_test_callbacks",
+ "force_autoreconf",
+ "autoreconf_extra_args",
+ "install_libtool_archives",
+ "patch_config_files",
+ "configure_directory",
+ "configure_abs_path",
+ "build_directory",
+ "autoreconf_search_path_args",
+ )
+
+ #: Whether to update ``libtool`` (e.g. for Arm/Clang/Fujitsu/NVHPC compilers)
patch_libtool = True
- #: Targets for ``make`` during the :py:meth:`~.AutotoolsPackage.build`
- #: phase
+ #: Targets for ``make`` during the :py:meth:`~.AutotoolsBuilder.build` phase
build_targets = [] # type: List[str]
- #: Targets for ``make`` during the :py:meth:`~.AutotoolsPackage.install`
- #: phase
+ #: Targets for ``make`` during the :py:meth:`~.AutotoolsBuilder.install` phase
install_targets = ["install"]
#: Callback names for build-time test
@@ -97,24 +150,40 @@ class AutotoolsPackage(PackageBase):
#: Set to true to force the autoreconf step even if configure is present
force_autoreconf = False
+
#: Options to be passed to autoreconf when using the default implementation
autoreconf_extra_args = [] # type: List[str]
- #: If False deletes all the .la files in the prefix folder
- #: after the installation. If True instead it installs them.
+ #: If False deletes all the .la files in the prefix folder after the installation.
+ #: If True instead it installs them.
install_libtool_archives = False
- depends_on("gnuconfig", type="build", when="target=ppc64le:")
- depends_on("gnuconfig", type="build", when="target=aarch64:")
- depends_on("gnuconfig", type="build", when="target=riscv64:")
- conflicts("platform=windows")
+ @property
+ def patch_config_files(self):
+ """Whether to update old ``config.guess`` and ``config.sub`` files
+ distributed with the tarball.
+
+ This currently only applies to ``ppc64le:``, ``aarch64:``, and
+ ``riscv64`` target architectures.
+
+ The substitutes are taken from the ``gnuconfig`` package, which is
+ automatically added as a build dependency for these architectures. In case
+ system versions of these config files are required, the ``gnuconfig`` package
+ can be marked external, with a prefix pointing to the directory containing the
+ system ``config.guess`` and ``config.sub`` files.
+ """
+ return (
+ self.pkg.spec.satisfies("target=ppc64le:")
+ or self.pkg.spec.satisfies("target=aarch64:")
+ or self.pkg.spec.satisfies("target=riscv64:")
+ )
@property
def _removed_la_files_log(self):
- """File containing the list of remove libtool archives"""
+ """File containing the list of removed libtool archives"""
build_dir = self.build_directory
if not os.path.isabs(self.build_directory):
- build_dir = os.path.join(self.stage.path, build_dir)
+ build_dir = os.path.join(self.pkg.stage.path, build_dir)
return os.path.join(build_dir, "removed_la_files.txt")
@property
@@ -125,13 +194,13 @@ class AutotoolsPackage(PackageBase):
files.append(self._removed_la_files_log)
return files
- @run_after("autoreconf")
+ @spack.builder.run_after("autoreconf")
def _do_patch_config_files(self):
- """Some packages ship with older config.guess/config.sub files and
- need to have these updated when installed on a newer architecture.
- In particular, config.guess fails for PPC64LE for version prior
- to a 2013-06-10 build date (automake 1.13.4) and for ARM (aarch64) and
- RISC-V (riscv64).
+ """Some packages ship with older config.guess/config.sub files and need to
+ have these updated when installed on a newer architecture.
+
+ In particular, config.guess fails for PPC64LE for version prior to a
+ 2013-06-10 build date (automake 1.13.4) and for AArch64 and RISC-V.
"""
if not self.patch_config_files:
return
@@ -139,11 +208,11 @@ class AutotoolsPackage(PackageBase):
# TODO: Expand this to select the 'config.sub'-compatible architecture
# for each platform (e.g. 'config.sub' doesn't accept 'power9le', but
# does accept 'ppc64le').
- if self.spec.satisfies("target=ppc64le:"):
+ if self.pkg.spec.satisfies("target=ppc64le:"):
config_arch = "ppc64le"
- elif self.spec.satisfies("target=aarch64:"):
+ elif self.pkg.spec.satisfies("target=aarch64:"):
config_arch = "aarch64"
- elif self.spec.satisfies("target=riscv64:"):
+ elif self.pkg.spec.satisfies("target=riscv64:"):
config_arch = "riscv64"
else:
config_arch = "local"
@@ -155,7 +224,7 @@ class AutotoolsPackage(PackageBase):
args = [script_abs_path] + additional_args.get(script_name, [])
try:
- check_call(args, stdout=PIPE, stderr=PIPE)
+ subprocess.check_call(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
except Exception as e:
tty.debug(e)
return False
@@ -163,7 +232,7 @@ class AutotoolsPackage(PackageBase):
return True
# Get the list of files that needs to be patched
- to_be_patched = fs.find(self.stage.path, files=["config.sub", "config.guess"])
+ to_be_patched = fs.find(self.pkg.stage.path, files=["config.sub", "config.guess"])
to_be_patched = [f for f in to_be_patched if not runs_ok(f)]
# If there are no files to be patched, return early
@@ -171,22 +240,21 @@ class AutotoolsPackage(PackageBase):
return
# Otherwise, require `gnuconfig` to be a build dependency
- self._require_build_deps(
- pkgs=["gnuconfig"], spec=self.spec, err="Cannot patch config files"
+ ensure_build_dependencies_or_raise(
+ spec=self.pkg.spec, dependencies=["gnuconfig"], error_msg="Cannot patch config files"
)
# Get the config files we need to patch (config.sub / config.guess).
to_be_found = list(set(os.path.basename(f) for f in to_be_patched))
- gnuconfig = self.spec["gnuconfig"]
+ gnuconfig = self.pkg.spec["gnuconfig"]
gnuconfig_dir = gnuconfig.prefix
# An external gnuconfig may not not have a prefix.
if gnuconfig_dir is None:
- raise InstallError(
- "Spack could not find substitutes for GNU config "
- "files because no prefix is available for the "
- "`gnuconfig` package. Make sure you set a prefix "
- "path instead of modules for external `gnuconfig`."
+ raise spack.build_environment.InstallError(
+ "Spack could not find substitutes for GNU config files because no "
+ "prefix is available for the `gnuconfig` package. Make sure you set a "
+ "prefix path instead of modules for external `gnuconfig`."
)
candidates = fs.find(gnuconfig_dir, files=to_be_found, recursive=False)
@@ -203,7 +271,7 @@ class AutotoolsPackage(PackageBase):
msg += (
" or the `gnuconfig` package prefix is misconfigured as" " an external package"
)
- raise InstallError(msg)
+ raise spack.build_environment.InstallError(msg)
# Filter working substitutes
candidates = [f for f in candidates if runs_ok(f)]
@@ -228,7 +296,9 @@ To resolve this problem, please try the following:
and set the prefix to the directory containing the `config.guess` and
`config.sub` files.
"""
- raise InstallError(msg.format(", ".join(to_be_found), self.name))
+ raise spack.build_environment.InstallError(
+ msg.format(", ".join(to_be_found), self.name)
+ )
# Copy the good files over the bad ones
for abs_path in to_be_patched:
@@ -238,7 +308,7 @@ To resolve this problem, please try the following:
fs.copy(substitutes[name], abs_path)
os.chmod(abs_path, mode)
- @run_before("configure")
+ @spack.builder.run_before("configure")
def _patch_usr_bin_file(self):
"""On NixOS file is not available in /usr/bin/file. Patch configure
scripts to use file from path."""
@@ -250,7 +320,7 @@ To resolve this problem, please try the following:
with fs.keep_modification_time(*x.filenames):
x.filter(regex="/usr/bin/file", repl="file", string=True)
- @run_before("configure")
+ @spack.builder.run_before("configure")
def _set_autotools_environment_variables(self):
"""Many autotools builds use a version of mknod.m4 that fails when
running as root unless FORCE_UNSAFE_CONFIGURE is set to 1.
@@ -261,11 +331,10 @@ To resolve this problem, please try the following:
Without it, configure just fails halfway through, but it can
still run things *before* this check. Forcing this just removes a
nuisance -- this is not circumventing any real protection.
-
"""
os.environ["FORCE_UNSAFE_CONFIGURE"] = "1"
- @run_before("configure")
+ @spack.builder.run_before("configure")
def _do_patch_libtool_configure(self):
"""Patch bugs that propagate from libtool macros into "configure" and
further into "libtool". Note that patches that can be fixed by patching
@@ -293,7 +362,7 @@ To resolve this problem, please try the following:
# Support Libtool 2.4.2 and older:
x.filter(regex=r'^(\s*test \$p = "-R")(; then\s*)$', repl=r'\1 || test x-l = x"$p"\2')
- @run_after("configure")
+ @spack.builder.run_after("configure")
def _do_patch_libtool(self):
"""If configure generates a "libtool" script that does not correctly
detect the compiler (and patch_libtool is set), patch in the correct
@@ -328,31 +397,33 @@ To resolve this problem, please try the following:
markers[tag] = "LIBTOOL TAG CONFIG: {0}".format(tag.upper())
# Replace empty linker flag prefixes:
- if self.compiler.name == "nag":
+ if self.pkg.compiler.name == "nag":
# Nag is mixed with gcc and g++, which are recognized correctly.
# Therefore, we change only Fortran values:
for tag in ["fc", "f77"]:
marker = markers[tag]
x.filter(
regex='^wl=""$',
- repl='wl="{0}"'.format(self.compiler.linker_arg),
+ repl='wl="{0}"'.format(self.pkg.compiler.linker_arg),
start_at="# ### BEGIN {0}".format(marker),
stop_at="# ### END {0}".format(marker),
)
else:
- x.filter(regex='^wl=""$', repl='wl="{0}"'.format(self.compiler.linker_arg))
+ x.filter(regex='^wl=""$', repl='wl="{0}"'.format(self.pkg.compiler.linker_arg))
# Replace empty PIC flag values:
for cc, marker in markers.items():
x.filter(
regex='^pic_flag=""$',
- repl='pic_flag="{0}"'.format(getattr(self.compiler, "{0}_pic_flag".format(cc))),
+ repl='pic_flag="{0}"'.format(
+ getattr(self.pkg.compiler, "{0}_pic_flag".format(cc))
+ ),
start_at="# ### BEGIN {0}".format(marker),
stop_at="# ### END {0}".format(marker),
)
# Other compiler-specific patches:
- if self.compiler.name == "fj":
+ if self.pkg.compiler.name == "fj":
x.filter(regex="-nostdlib", repl="", string=True)
rehead = r"/\S*/"
for o in [
@@ -365,12 +436,12 @@ To resolve this problem, please try the following:
"crtendS.o",
]:
x.filter(regex=(rehead + o), repl="", string=True)
- elif self.compiler.name == "dpcpp":
+ elif self.pkg.compiler.name == "dpcpp":
# Hack to filter out spurious predep_objects when building with Intel dpcpp
# (see https://github.com/spack/spack/issues/32863):
x.filter(regex=r"^(predep_objects=.*)/tmp/conftest-[0-9A-Fa-f]+\.o", repl=r"\1")
x.filter(regex=r"^(predep_objects=.*)/tmp/a-[0-9A-Fa-f]+\.o", repl=r"\1")
- elif self.compiler.name == "nag":
+ elif self.pkg.compiler.name == "nag":
for tag in ["fc", "f77"]:
marker = markers[tag]
start_at = "# ### BEGIN {0}".format(marker)
@@ -446,11 +517,8 @@ To resolve this problem, please try the following:
@property
def configure_directory(self):
- """Returns the directory where 'configure' resides.
-
- :return: directory where to find configure
- """
- return self.stage.source_path
+ """Return the directory where 'configure' resides."""
+ return self.pkg.stage.source_path
@property
def configure_abs_path(self):
@@ -463,34 +531,12 @@ To resolve this problem, please try the following:
"""Override to provide another place to build the package"""
return self.configure_directory
- @run_before("autoreconf")
+ @spack.builder.run_before("autoreconf")
def delete_configure_to_force_update(self):
if self.force_autoreconf:
- force_remove(self.configure_abs_path)
-
- def _require_build_deps(self, pkgs, spec, err):
- """Require `pkgs` to be direct build dependencies of `spec`. Raises a
- RuntimeError with a helpful error messages when any dep is missing."""
-
- build_deps = [d.name for d in spec.dependencies(deptype="build")]
- missing_deps = [x for x in pkgs if x not in build_deps]
-
- if not missing_deps:
- return
-
- # Raise an exception on missing deps.
- msg = (
- "{0}: missing dependencies: {1}.\n\nPlease add "
- "the following lines to the package:\n\n".format(err, ", ".join(missing_deps))
- )
-
- for dep in missing_deps:
- msg += " depends_on('{0}', type='build', when='@{1}')\n".format(dep, spec.version)
-
- msg += "\nUpdate the version (when='@{0}') as needed.".format(spec.version)
- raise RuntimeError(msg)
+ fs.force_remove(self.configure_abs_path)
- def autoreconf(self, spec, prefix):
+ def autoreconf(self, pkg, spec, prefix):
"""Not needed usually, configure should be already there"""
# If configure exists nothing needs to be done
@@ -498,8 +544,10 @@ To resolve this problem, please try the following:
return
# Else try to regenerate it, which reuquires a few build dependencies
- self._require_build_deps(
- pkgs=["autoconf", "automake", "libtool"], spec=spec, err="Cannot generate configure"
+ ensure_build_dependencies_or_raise(
+ spec=spec,
+ dependencies=["autoconf", "automake", "libtool"],
+ error_msg="Cannot generate configure",
)
tty.msg("Configure script not found: trying to generate it")
@@ -507,8 +555,8 @@ To resolve this problem, please try the following:
tty.warn("* If the default procedure fails, consider implementing *")
tty.warn("* a custom AUTORECONF phase in the package *")
tty.warn("*********************************************************")
- with working_dir(self.configure_directory):
- m = inspect.getmodule(self)
+ with fs.working_dir(self.configure_directory):
+ m = inspect.getmodule(self.pkg)
# This line is what is needed most of the time
# --install, --verbose, --force
autoreconf_args = ["-ivf"]
@@ -524,98 +572,66 @@ To resolve this problem, please try the following:
spack dependencies."""
return _autoreconf_search_path_args(self.spec)
- @run_after("autoreconf")
+ @spack.builder.run_after("autoreconf")
def set_configure_or_die(self):
- """Checks the presence of a ``configure`` file after the
- autoreconf phase. If it is found sets a module attribute
- appropriately, otherwise raises an error.
+ """Ensure the presence of a "configure" script, or raise. If the "configure"
+ is found, a module level attribute is set.
- :raises RuntimeError: if a configure script is not found in
- :py:meth:`~AutotoolsPackage.configure_directory`
+ Raises:
+ RuntimeError: if the "configure" script is not found
"""
- # Check if a configure script is there. If not raise a RuntimeError.
+ # Check if the "configure" script is there. If not raise a RuntimeError.
if not os.path.exists(self.configure_abs_path):
msg = "configure script not found in {0}"
raise RuntimeError(msg.format(self.configure_directory))
# Monkey-patch the configure script in the corresponding module
- inspect.getmodule(self).configure = Executable(self.configure_abs_path)
+ inspect.getmodule(self.pkg).configure = Executable(self.configure_abs_path)
def configure_args(self):
- """Produces a list containing all the arguments that must be passed to
- configure, except ``--prefix`` which will be pre-pended to the list.
-
- :return: list of arguments for configure
+ """Return the list of all the arguments that must be passed to configure,
+ except ``--prefix`` which will be pre-pended to the list.
"""
return []
- def flags_to_build_system_args(self, flags):
- """Produces a list of all command line arguments to pass specified
- compiler flags to configure."""
- # Has to be dynamic attribute due to caching.
- setattr(self, "configure_flag_args", [])
- for flag, values in flags.items():
- if values:
- values_str = "{0}={1}".format(flag.upper(), " ".join(values))
- self.configure_flag_args.append(values_str)
- # Spack's fflags are meant for both F77 and FC, therefore we
- # additionaly set FCFLAGS if required.
- values = flags.get("fflags", None)
- if values:
- values_str = "FCFLAGS={0}".format(" ".join(values))
- self.configure_flag_args.append(values_str)
-
- def configure(self, spec, prefix):
- """Runs configure with the arguments specified in
- :meth:`~spack.build_systems.autotools.AutotoolsPackage.configure_args`
- and an appropriately set prefix.
+ def configure(self, pkg, spec, prefix):
+ """Run "configure", with the arguments specified by the builder and an
+ appropriately set prefix.
"""
- options = getattr(self, "configure_flag_args", [])
+ options = getattr(self.pkg, "configure_flag_args", [])
options += ["--prefix={0}".format(prefix)]
options += self.configure_args()
- with working_dir(self.build_directory, create=True):
- inspect.getmodule(self).configure(*options)
-
- def setup_build_environment(self, env):
- if self.spec.platform == "darwin" and macos_version() >= Version("11"):
- # Many configure files rely on matching '10.*' for macOS version
- # detection and fail to add flags if it shows as version 11.
- env.set("MACOSX_DEPLOYMENT_TARGET", "10.16")
+ with fs.working_dir(self.build_directory, create=True):
+ inspect.getmodule(self.pkg).configure(*options)
- def build(self, spec, prefix):
- """Makes the build targets specified by
- :py:attr:``~.AutotoolsPackage.build_targets``
- """
+ def build(self, pkg, spec, prefix):
+ """Run "make" on the build targets specified by the builder."""
# See https://autotools.io/automake/silent.html
params = ["V=1"]
params += self.build_targets
- with working_dir(self.build_directory):
- inspect.getmodule(self).make(*params)
+ with fs.working_dir(self.build_directory):
+ inspect.getmodule(self.pkg).make(*params)
- def install(self, spec, prefix):
- """Makes the install targets specified by
- :py:attr:``~.AutotoolsPackage.install_targets``
- """
- with working_dir(self.build_directory):
- inspect.getmodule(self).make(*self.install_targets)
+ def install(self, pkg, spec, prefix):
+ """Run "make" on the install targets specified by the builder."""
+ with fs.working_dir(self.build_directory):
+ inspect.getmodule(self.pkg).make(*self.install_targets)
- run_after("build")(PackageBase._run_default_build_time_test_callbacks)
+ spack.builder.run_after("build")(execute_build_time_tests)
def check(self):
- """Searches the Makefile for targets ``test`` and ``check``
- and runs them if found.
- """
- with working_dir(self.build_directory):
- self._if_make_target_execute("test")
- self._if_make_target_execute("check")
+ """Run "make" on the ``test`` and ``check`` targets, if found."""
+ with fs.working_dir(self.build_directory):
+ self.pkg._if_make_target_execute("test")
+ self.pkg._if_make_target_execute("check")
def _activate_or_not(
self, name, activation_word, deactivation_word, activation_value=None, variant=None
):
- """This function contains the current implementation details of
- :meth:`~spack.build_systems.autotools.AutotoolsPackage.with_or_without` and
- :meth:`~spack.build_systems.autotools.AutotoolsPackage.enable_or_disable`.
+ """This function contain the current implementation details of
+ :meth:`~spack.build_systems.autotools.AutotoolsBuilder.with_or_without` and
+ :meth:`~spack.build_systems.autotools.AutotoolsBuilder.enable_or_disable`.
Args:
name (str): name of the option that is being activated or not
@@ -671,7 +687,7 @@ To resolve this problem, please try the following:
Raises:
KeyError: if name is not among known variants
"""
- spec = self.spec
+ spec = self.pkg.spec
args = []
if activation_value == "prefix":
@@ -681,16 +697,16 @@ To resolve this problem, please try the following:
# Defensively look that the name passed as argument is among
# variants
- if variant not in self.variants:
+ if variant not in self.pkg.variants:
msg = '"{0}" is not a variant of "{1}"'
- raise KeyError(msg.format(variant, self.name))
+ raise KeyError(msg.format(variant, self.pkg.name))
if variant not in spec.variants:
return []
# Create a list of pairs. Each pair includes a configuration
# option and whether or not that option is activated
- variant_desc, _ = self.variants[variant]
+ variant_desc, _ = self.pkg.variants[variant]
if set(variant_desc.values) == set((True, False)):
# BoolValuedVariant carry information about a single option.
# Nonetheless, for uniformity of treatment we'll package them
@@ -718,14 +734,18 @@ To resolve this problem, please try the following:
override_name = "{0}_or_{1}_{2}".format(
activation_word, deactivation_word, option_value
)
- line_generator = getattr(self, override_name, None)
+ line_generator = getattr(self, override_name, None) or getattr(
+ self.pkg, override_name, None
+ )
# If not available use a sensible default
if line_generator is None:
def _default_generator(is_activated):
if is_activated:
line = "--{0}-{1}".format(activation_word, option_value)
- if activation_value is not None and activation_value(option_value):
+ if activation_value is not None and activation_value(
+ option_value
+ ): # NOQA=ignore=E501
line += "={0}".format(activation_value(option_value))
return line
return "--{0}-{1}".format(deactivation_word, option_value)
@@ -764,7 +784,7 @@ To resolve this problem, please try the following:
def enable_or_disable(self, name, activation_value=None, variant=None):
"""Same as
- :meth:`~spack.build_systems.autotools.AutotoolsPackage.with_or_without`
+ :meth:`~spack.build_systems.autotools.AutotoolsBuilder.with_or_without`
but substitute ``with`` with ``enable`` and ``without`` with ``disable``.
Args:
@@ -781,19 +801,14 @@ To resolve this problem, please try the following:
"""
return self._activate_or_not(name, "enable", "disable", activation_value, variant)
- run_after("install")(PackageBase._run_default_install_time_test_callbacks)
+ spack.builder.run_after("install")(execute_install_time_tests)
def installcheck(self):
- """Searches the Makefile for an ``installcheck`` target
- and runs it if found.
- """
- with working_dir(self.build_directory):
- self._if_make_target_execute("installcheck")
-
- # Check that self.prefix is there after installation
- run_after("install")(PackageBase.sanity_check_prefix)
+ """Run "make" on the ``installcheck`` target, if found."""
+ with fs.working_dir(self.build_directory):
+ self.pkg._if_make_target_execute("installcheck")
- @run_after("install")
+ @spack.builder.run_after("install")
def remove_libtool_archives(self):
"""Remove all .la files in prefix sub-folders if the package sets
``install_libtool_archives`` to be False.
@@ -803,14 +818,20 @@ To resolve this problem, please try the following:
return
# Remove the files and create a log of what was removed
- libtool_files = fs.find(str(self.prefix), "*.la", recursive=True)
+ libtool_files = fs.find(str(self.pkg.prefix), "*.la", recursive=True)
with fs.safe_remove(*libtool_files):
fs.mkdirp(os.path.dirname(self._removed_la_files_log))
with open(self._removed_la_files_log, mode="w") as f:
f.write("\n".join(libtool_files))
+ def setup_build_environment(self, env):
+ if self.spec.platform == "darwin" and macos_version() >= Version("11"):
+ # Many configure files rely on matching '10.*' for macOS version
+ # detection and fail to add flags if it shows as version 11.
+ env.set("MACOSX_DEPLOYMENT_TARGET", "10.16")
+
# On macOS, force rpaths for shared library IDs and remove duplicate rpaths
- run_after("install")(PackageBase.apply_macos_rpath_fixups)
+ spack.builder.run_after("install", when="platform=darwin")(apply_macos_rpath_fixups)
def _autoreconf_search_path_args(spec):
diff --git a/lib/spack/spack/build_systems/bundle.py b/lib/spack/spack/build_systems/bundle.py
new file mode 100644
index 0000000000..fad0ba4e14
--- /dev/null
+++ b/lib/spack/spack/build_systems/bundle.py
@@ -0,0 +1,31 @@
+# Copyright 2013-2022 Lawrence Livermore National Security, LLC and other
+# Spack Project Developers. See the top-level COPYRIGHT file for details.
+#
+# SPDX-License-Identifier: (Apache-2.0 OR MIT)
+import spack.builder
+import spack.directives
+import spack.package_base
+
+
+class BundlePackage(spack.package_base.PackageBase):
+ """General purpose bundle, or no-code, package class."""
+
+ #: This attribute is used in UI queries that require to know which
+ #: build-system class we are using
+ build_system_class = "BundlePackage"
+
+ #: Legacy buildsystem attribute used to deserialize and install old specs
+ legacy_buildsystem = "bundle"
+
+ #: Bundle packages do not have associated source or binary code.
+ has_code = False
+
+ spack.directives.build_system("bundle")
+
+
+@spack.builder.builder("bundle")
+class BundleBuilder(spack.builder.Builder):
+ phases = ("install",)
+
+ def install(self, pkg, spec, prefix):
+ pass
diff --git a/lib/spack/spack/build_systems/cached_cmake.py b/lib/spack/spack/build_systems/cached_cmake.py
index 9ffd2a82ab..7caf2a1539 100644
--- a/lib/spack/spack/build_systems/cached_cmake.py
+++ b/lib/spack/spack/build_systems/cached_cmake.py
@@ -3,12 +3,14 @@
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import os
+from typing import Tuple
+import llnl.util.filesystem as fs
import llnl.util.tty as tty
-from llnl.util.filesystem import install, mkdirp
-from spack.build_systems.cmake import CMakePackage
-from spack.package_base import run_after
+import spack.builder
+
+from .cmake import CMakeBuilder, CMakePackage
def cmake_cache_path(name, value, comment=""):
@@ -28,44 +30,46 @@ def cmake_cache_option(name, boolean_value, comment=""):
return 'set({0} {1} CACHE BOOL "{2}")\n'.format(name, value, comment)
-class CachedCMakePackage(CMakePackage):
- """Specialized class for packages built using CMake initial cache.
+class CachedCMakeBuilder(CMakeBuilder):
- This feature of CMake allows packages to increase reproducibility,
- especially between Spack- and manual builds. It also allows packages to
- sidestep certain parsing bugs in extremely long ``cmake`` commands, and to
- avoid system limits on the length of the command line."""
+ #: Names associated with package methods in the old build-system format
+ legacy_methods = CMakeBuilder.legacy_methods + (
+ "initconfig_compiler_entries",
+ "initconfig_mpi_entries",
+ "initconfig_hardware_entries",
+ "std_initconfig_entries",
+ "initconfig_package_entries",
+ ) # type: Tuple[str, ...]
- phases = ["initconfig", "cmake", "build", "install"]
+ #: Names associated with package attributes in the old build-system format
+ legacy_attributes = CMakeBuilder.legacy_attributes + (
+ "cache_name",
+ "cache_path",
+ ) # type: Tuple[str, ...]
@property
def cache_name(self):
return "{0}-{1}-{2}@{3}.cmake".format(
- self.name,
- self.spec.architecture,
- self.spec.compiler.name,
- self.spec.compiler.version,
+ self.pkg.name,
+ self.pkg.spec.architecture,
+ self.pkg.spec.compiler.name,
+ self.pkg.spec.compiler.version,
)
@property
def cache_path(self):
- return os.path.join(self.stage.source_path, self.cache_name)
-
- def flag_handler(self, name, flags):
- if name in ("cflags", "cxxflags", "cppflags", "fflags"):
- return (None, None, None) # handled in the cmake cache
- return (flags, None, None)
+ return os.path.join(self.pkg.stage.source_path, self.cache_name)
def initconfig_compiler_entries(self):
# This will tell cmake to use the Spack compiler wrappers when run
# through Spack, but use the underlying compiler when run outside of
# Spack
- spec = self.spec
+ spec = self.pkg.spec
# Fortran compiler is optional
if "FC" in os.environ:
spack_fc_entry = cmake_cache_path("CMAKE_Fortran_COMPILER", os.environ["FC"])
- system_fc_entry = cmake_cache_path("CMAKE_Fortran_COMPILER", self.compiler.fc)
+ system_fc_entry = cmake_cache_path("CMAKE_Fortran_COMPILER", self.pkg.compiler.fc)
else:
spack_fc_entry = "# No Fortran compiler defined in spec"
system_fc_entry = "# No Fortran compiler defined in spec"
@@ -81,8 +85,8 @@ class CachedCMakePackage(CMakePackage):
" " + cmake_cache_path("CMAKE_CXX_COMPILER", os.environ["CXX"]),
" " + spack_fc_entry,
"else()\n",
- " " + cmake_cache_path("CMAKE_C_COMPILER", self.compiler.cc),
- " " + cmake_cache_path("CMAKE_CXX_COMPILER", self.compiler.cxx),
+ " " + cmake_cache_path("CMAKE_C_COMPILER", self.pkg.compiler.cc),
+ " " + cmake_cache_path("CMAKE_CXX_COMPILER", self.pkg.compiler.cxx),
" " + system_fc_entry,
"endif()\n",
]
@@ -126,7 +130,7 @@ class CachedCMakePackage(CMakePackage):
return entries
def initconfig_mpi_entries(self):
- spec = self.spec
+ spec = self.pkg.spec
if not spec.satisfies("^mpi"):
return []
@@ -160,13 +164,13 @@ class CachedCMakePackage(CMakePackage):
mpiexec = os.path.join(spec["mpi"].prefix.bin, "mpiexec")
if not os.path.exists(mpiexec):
- msg = "Unable to determine MPIEXEC, %s tests may fail" % self.name
+ msg = "Unable to determine MPIEXEC, %s tests may fail" % self.pkg.name
entries.append("# {0}\n".format(msg))
tty.warn(msg)
else:
# starting with cmake 3.10, FindMPI expects MPIEXEC_EXECUTABLE
# vs the older versions which expect MPIEXEC
- if self.spec["cmake"].satisfies("@3.10:"):
+ if self.pkg.spec["cmake"].satisfies("@3.10:"):
entries.append(cmake_cache_path("MPIEXEC_EXECUTABLE", mpiexec))
else:
entries.append(cmake_cache_path("MPIEXEC", mpiexec))
@@ -180,7 +184,7 @@ class CachedCMakePackage(CMakePackage):
return entries
def initconfig_hardware_entries(self):
- spec = self.spec
+ spec = self.pkg.spec
entries = [
"#------------------{0}".format("-" * 60),
@@ -212,7 +216,7 @@ class CachedCMakePackage(CMakePackage):
"#------------------{0}".format("-" * 60),
"# !!!! This is a generated file, edit at own risk !!!!",
"#------------------{0}".format("-" * 60),
- "# CMake executable path: {0}".format(self.spec["cmake"].command.path),
+ "# CMake executable path: {0}".format(self.pkg.spec["cmake"].command.path),
"#------------------{0}\n".format("-" * 60),
]
@@ -220,7 +224,8 @@ class CachedCMakePackage(CMakePackage):
"""This method is to be overwritten by the package"""
return []
- def initconfig(self, spec, prefix):
+ @spack.builder.run_before("cmake")
+ def initconfig(self):
cache_entries = (
self.std_initconfig_entries()
+ self.initconfig_compiler_entries()
@@ -236,11 +241,28 @@ class CachedCMakePackage(CMakePackage):
@property
def std_cmake_args(self):
- args = super(CachedCMakePackage, self).std_cmake_args
+ args = super(CachedCMakeBuilder, self).std_cmake_args
args.extend(["-C", self.cache_path])
return args
- @run_after("install")
+ @spack.builder.run_after("install")
def install_cmake_cache(self):
- mkdirp(self.spec.prefix.share.cmake)
- install(self.cache_path, self.spec.prefix.share.cmake)
+ fs.mkdirp(self.pkg.spec.prefix.share.cmake)
+ fs.install(self.cache_path, self.pkg.spec.prefix.share.cmake)
+
+
+class CachedCMakePackage(CMakePackage):
+ """Specialized class for packages built using CMake initial cache.
+
+ This feature of CMake allows packages to increase reproducibility,
+ especially between Spack- and manual builds. It also allows packages to
+ sidestep certain parsing bugs in extremely long ``cmake`` commands, and to
+ avoid system limits on the length of the command line.
+ """
+
+ CMakeBuilder = CachedCMakeBuilder
+
+ def flag_handler(self, name, flags):
+ if name in ("cflags", "cxxflags", "cppflags", "fflags"):
+ return None, None, None # handled in the cmake cache
+ return flags, None, None
diff --git a/lib/spack/spack/build_systems/cmake.py b/lib/spack/spack/build_systems/cmake.py
index 6fb75b6747..8d8bba4778 100644
--- a/lib/spack/spack/build_systems/cmake.py
+++ b/lib/spack/spack/build_systems/cmake.py
@@ -2,23 +2,26 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
-
-
import inspect
import os
import platform
import re
import sys
-from typing import List
+from typing import List, Tuple
import six
+import llnl.util.filesystem as fs
from llnl.util.compat import Sequence
-from llnl.util.filesystem import working_dir
import spack.build_environment
-from spack.directives import conflicts, depends_on, variant
-from spack.package_base import InstallError, PackageBase, run_after
+import spack.builder
+import spack.package_base
+import spack.util.path
+from spack.directives import build_system, depends_on, variant
+from spack.multimethod import when
+
+from ._checks import BaseBuilder, execute_build_time_tests
# Regex to extract the primary generator from the CMake generator
# string.
@@ -34,56 +37,141 @@ def _extract_primary_generator(generator):
return primary_generator
-class CMakePackage(PackageBase):
+class CMakePackage(spack.package_base.PackageBase):
"""Specialized class for packages built using CMake
For more information on the CMake build system, see:
https://cmake.org/cmake/help/latest/
+ """
+
+ #: This attribute is used in UI queries that need to know the build
+ #: system base class
+ build_system_class = "CMakePackage"
- This class provides three phases that can be overridden:
+ #: Legacy buildsystem attribute used to deserialize and install old specs
+ legacy_buildsystem = "cmake"
- 1. :py:meth:`~.CMakePackage.cmake`
- 2. :py:meth:`~.CMakePackage.build`
- 3. :py:meth:`~.CMakePackage.install`
+ build_system("cmake")
+
+ with when("build_system=cmake"):
+ # https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html
+ variant(
+ "build_type",
+ default="RelWithDebInfo",
+ description="CMake build type",
+ values=("Debug", "Release", "RelWithDebInfo", "MinSizeRel"),
+ )
+ # CMAKE_INTERPROCEDURAL_OPTIMIZATION only exists for CMake >= 3.9
+ # https://cmake.org/cmake/help/latest/variable/CMAKE_INTERPROCEDURAL_OPTIMIZATION.html
+ variant(
+ "ipo",
+ default=False,
+ when="^cmake@3.9:",
+ description="CMake interprocedural optimization",
+ )
+ depends_on("cmake", type="build")
+ depends_on("ninja", type="build", when="platform=windows")
+
+ def flags_to_build_system_args(self, flags):
+ """Return a list of all command line arguments to pass the specified
+ compiler flags to cmake. Note CMAKE does not have a cppflags option,
+ so cppflags will be added to cflags, cxxflags, and fflags to mimic the
+ behavior in other tools.
+ """
+ # Has to be dynamic attribute due to caching
+ setattr(self, "cmake_flag_args", [])
+
+ flag_string = "-DCMAKE_{0}_FLAGS={1}"
+ langs = {"C": "c", "CXX": "cxx", "Fortran": "f"}
+
+ # Handle language compiler flags
+ for lang, pre in langs.items():
+ flag = pre + "flags"
+ # cmake has no explicit cppflags support -> add it to all langs
+ lang_flags = " ".join(flags.get(flag, []) + flags.get("cppflags", []))
+ if lang_flags:
+ self.cmake_flag_args.append(flag_string.format(lang, lang_flags))
+
+ # Cmake has different linker arguments for different build types.
+ # We specify for each of them.
+ if flags["ldflags"]:
+ ldflags = " ".join(flags["ldflags"])
+ ld_string = "-DCMAKE_{0}_LINKER_FLAGS={1}"
+ # cmake has separate linker arguments for types of builds.
+ for type in ["EXE", "MODULE", "SHARED", "STATIC"]:
+ self.cmake_flag_args.append(ld_string.format(type, ldflags))
+
+ # CMake has libs options separated by language. Apply ours to each.
+ if flags["ldlibs"]:
+ libs_flags = " ".join(flags["ldlibs"])
+ libs_string = "-DCMAKE_{0}_STANDARD_LIBRARIES={1}"
+ for lang in langs:
+ self.cmake_flag_args.append(libs_string.format(lang, libs_flags))
+
+ # Legacy methods (used by too many packages to change them,
+ # need to forward to the builder)
+ def define(self, *args, **kwargs):
+ return self.builder.define(*args, **kwargs)
+
+ def define_from_variant(self, *args, **kwargs):
+ return self.builder.define_from_variant(*args, **kwargs)
+
+
+@spack.builder.builder("cmake")
+class CMakeBuilder(BaseBuilder):
+ """The cmake builder encodes the default way of building software with CMake. IT
+ has three phases that can be overridden:
+
+ 1. :py:meth:`~.CMakeBuilder.cmake`
+ 2. :py:meth:`~.CMakeBuilder.build`
+ 3. :py:meth:`~.CMakeBuilder.install`
They all have sensible defaults and for many packages the only thing
- necessary will be to override :py:meth:`~.CMakePackage.cmake_args`.
+ necessary will be to override :py:meth:`~.CMakeBuilder.cmake_args`.
+
For a finer tuning you may also override:
+-----------------------------------------------+--------------------+
| **Method** | **Purpose** |
+===============================================+====================+
- | :py:meth:`~.CMakePackage.root_cmakelists_dir` | Location of the |
+ | :py:meth:`~.CMakeBuilder.root_cmakelists_dir` | Location of the |
| | root CMakeLists.txt|
+-----------------------------------------------+--------------------+
- | :py:meth:`~.CMakePackage.build_directory` | Directory where to |
+ | :py:meth:`~.CMakeBuilder.build_directory` | Directory where to |
| | build the package |
+-----------------------------------------------+--------------------+
-
- The generator used by CMake can be specified by providing the
- generator attribute. Per
+ The generator used by CMake can be specified by providing the ``generator``
+ attribute. Per
https://cmake.org/cmake/help/git-master/manual/cmake-generators.7.html,
- the format is: [<secondary-generator> - ]<primary_generator>. The
- full list of primary and secondary generators supported by CMake may
- be found in the documentation for the version of CMake used;
- however, at this time Spack supports only the primary generators
- "Unix Makefiles" and "Ninja." Spack's CMake support is agnostic with
- respect to primary generators. Spack will generate a runtime error
- if the generator string does not follow the prescribed format, or if
+ the format is: [<secondary-generator> - ]<primary_generator>.
+
+ The full list of primary and secondary generators supported by CMake may be found
+ in the documentation for the version of CMake used; however, at this time Spack
+ supports only the primary generators "Unix Makefiles" and "Ninja." Spack's CMake
+ support is agnostic with respect to primary generators. Spack will generate a
+ runtime error if the generator string does not follow the prescribed format, or if
the primary generator is not supported.
"""
#: Phases of a CMake package
- phases = ["cmake", "build", "install"]
- #: This attribute is used in UI queries that need to know the build
- #: system base class
- build_system_class = "CMakePackage"
-
- build_targets = [] # type: List[str]
- install_targets = ["install"]
-
- build_time_test_callbacks = ["check"]
+ phases = ("cmake", "build", "install")
+
+ #: Names associated with package methods in the old build-system format
+ legacy_methods = ("cmake_args", "check") # type: Tuple[str, ...]
+
+ #: Names associated with package attributes in the old build-system format
+ legacy_attributes = (
+ "generator",
+ "build_targets",
+ "install_targets",
+ "build_time_test_callbacks",
+ "archive_files",
+ "root_cmakelists_dir",
+ "std_cmake_args",
+ "build_dirname",
+ "build_directory",
+ ) # type: Tuple[str, ...]
#: The build system generator to use.
#:
@@ -93,27 +181,14 @@ class CMakePackage(PackageBase):
#:
#: See https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html
#: for more information.
+ generator = "Ninja" if sys.platform == "win32" else "Unix Makefiles"
- generator = "Unix Makefiles"
-
- if sys.platform == "win32":
- generator = "Ninja"
- depends_on("ninja")
-
- # https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html
- variant(
- "build_type",
- default="RelWithDebInfo",
- description="CMake build type",
- values=("Debug", "Release", "RelWithDebInfo", "MinSizeRel"),
- )
-
- # https://cmake.org/cmake/help/latest/variable/CMAKE_INTERPROCEDURAL_OPTIMIZATION.html
- variant("ipo", default=False, description="CMake interprocedural optimization")
- # CMAKE_INTERPROCEDURAL_OPTIMIZATION only exists for CMake >= 3.9
- conflicts("+ipo", when="^cmake@:3.8", msg="+ipo is not supported by CMake < 3.9")
-
- depends_on("cmake", type="build")
+ #: Targets to be used during the build phase
+ build_targets = [] # type: List[str]
+ #: Targets to be used during the install phase
+ install_targets = ["install"]
+ #: Callback names for build-time test
+ build_time_test_callbacks = ["check"]
@property
def archive_files(self):
@@ -126,40 +201,30 @@ class CMakePackage(PackageBase):
This path is relative to the root of the extracted tarball,
not to the ``build_directory``. Defaults to the current directory.
-
- :return: directory containing CMakeLists.txt
"""
- return self.stage.source_path
+ return self.pkg.stage.source_path
@property
def std_cmake_args(self):
"""Standard cmake arguments provided as a property for
convenience of package writers
-
- :return: standard cmake arguments
"""
# standard CMake arguments
- std_cmake_args = CMakePackage._std_args(self)
- std_cmake_args += getattr(self, "cmake_flag_args", [])
+ std_cmake_args = CMakeBuilder.std_args(self.pkg, generator=self.generator)
+ std_cmake_args += getattr(self.pkg, "cmake_flag_args", [])
return std_cmake_args
@staticmethod
- def _std_args(pkg):
+ def std_args(pkg, generator=None):
"""Computes the standard cmake arguments for a generic package"""
-
- try:
- generator = pkg.generator
- except AttributeError:
- generator = CMakePackage.generator
-
- # Make sure a valid generator was chosen
+ generator = generator or "Unix Makefiles"
valid_primary_generators = ["Unix Makefiles", "Ninja"]
primary_generator = _extract_primary_generator(generator)
if primary_generator not in valid_primary_generators:
msg = "Invalid CMake generator: '{0}'\n".format(generator)
msg += "CMakePackage currently supports the following "
msg += "primary generators: '{0}'".format("', '".join(valid_primary_generators))
- raise InstallError(msg)
+ raise spack.package_base.InstallError(msg)
try:
build_type = pkg.spec.variants["build_type"].value
@@ -171,7 +236,7 @@ class CMakePackage(PackageBase):
except KeyError:
ipo = False
- define = CMakePackage.define
+ define = CMakeBuilder.define
args = [
"-G",
generator,
@@ -251,7 +316,7 @@ class CMakePackage(PackageBase):
of ``cmake_var``.
This utility function is similar to
- :meth:`~spack.build_systems.autotools.AutotoolsPackage.with_or_without`.
+ :meth:`~spack.build_systems.autotools.AutotoolsBuilder.with_or_without`.
Examples:
@@ -291,122 +356,75 @@ class CMakePackage(PackageBase):
if variant is None:
variant = cmake_var.lower()
- if variant not in self.variants:
- raise KeyError('"{0}" is not a variant of "{1}"'.format(variant, self.name))
+ if variant not in self.pkg.variants:
+ raise KeyError('"{0}" is not a variant of "{1}"'.format(variant, self.pkg.name))
- if variant not in self.spec.variants:
+ if variant not in self.pkg.spec.variants:
return ""
- value = self.spec.variants[variant].value
+ value = self.pkg.spec.variants[variant].value
if isinstance(value, (tuple, list)):
# Sort multi-valued variants for reproducibility
value = sorted(value)
return self.define(cmake_var, value)
- def flags_to_build_system_args(self, flags):
- """Produces a list of all command line arguments to pass the specified
- compiler flags to cmake. Note CMAKE does not have a cppflags option,
- so cppflags will be added to cflags, cxxflags, and fflags to mimic the
- behavior in other tools."""
- # Has to be dynamic attribute due to caching
- setattr(self, "cmake_flag_args", [])
-
- flag_string = "-DCMAKE_{0}_FLAGS={1}"
- langs = {"C": "c", "CXX": "cxx", "Fortran": "f"}
-
- # Handle language compiler flags
- for lang, pre in langs.items():
- flag = pre + "flags"
- # cmake has no explicit cppflags support -> add it to all langs
- lang_flags = " ".join(flags.get(flag, []) + flags.get("cppflags", []))
- if lang_flags:
- self.cmake_flag_args.append(flag_string.format(lang, lang_flags))
-
- # Cmake has different linker arguments for different build types.
- # We specify for each of them.
- if flags["ldflags"]:
- ldflags = " ".join(flags["ldflags"])
- ld_string = "-DCMAKE_{0}_LINKER_FLAGS={1}"
- # cmake has separate linker arguments for types of builds.
- for type in ["EXE", "MODULE", "SHARED", "STATIC"]:
- self.cmake_flag_args.append(ld_string.format(type, ldflags))
-
- # CMake has libs options separated by language. Apply ours to each.
- if flags["ldlibs"]:
- libs_flags = " ".join(flags["ldlibs"])
- libs_string = "-DCMAKE_{0}_STANDARD_LIBRARIES={1}"
- for lang in langs:
- self.cmake_flag_args.append(libs_string.format(lang, libs_flags))
-
@property
def build_dirname(self):
- """Returns the directory name to use when building the package
-
- :return: name of the subdirectory for building the package
- """
- return "spack-build-%s" % self.spec.dag_hash(7)
+ """Directory name to use when building the package."""
+ return "spack-build-%s" % self.pkg.spec.dag_hash(7)
@property
def build_directory(self):
- """Returns the directory to use when building the package
-
- :return: directory where to build the package
- """
- return os.path.join(self.stage.path, self.build_dirname)
+ """Full-path to the directory to use when building the package."""
+ return os.path.join(self.pkg.stage.path, self.build_dirname)
def cmake_args(self):
- """Produces a list containing all the arguments that must be passed to
- cmake, except:
+ """List of all the arguments that must be passed to cmake, except:
* CMAKE_INSTALL_PREFIX
* CMAKE_BUILD_TYPE
* BUILD_TESTING
which will be set automatically.
-
- :return: list of arguments for cmake
"""
return []
- def cmake(self, spec, prefix):
+ def cmake(self, pkg, spec, prefix):
"""Runs ``cmake`` in the build directory"""
options = self.std_cmake_args
options += self.cmake_args()
options.append(os.path.abspath(self.root_cmakelists_dir))
- with working_dir(self.build_directory, create=True):
- inspect.getmodule(self).cmake(*options)
+ with fs.working_dir(self.build_directory, create=True):
+ inspect.getmodule(self.pkg).cmake(*options)
- def build(self, spec, prefix):
+ def build(self, pkg, spec, prefix):
"""Make the build targets"""
- with working_dir(self.build_directory):
+ with fs.working_dir(self.build_directory):
if self.generator == "Unix Makefiles":
- inspect.getmodule(self).make(*self.build_targets)
+ inspect.getmodule(self.pkg).make(*self.build_targets)
elif self.generator == "Ninja":
self.build_targets.append("-v")
- inspect.getmodule(self).ninja(*self.build_targets)
+ inspect.getmodule(self.pkg).ninja(*self.build_targets)
- def install(self, spec, prefix):
+ def install(self, pkg, spec, prefix):
"""Make the install targets"""
- with working_dir(self.build_directory):
+ with fs.working_dir(self.build_directory):
if self.generator == "Unix Makefiles":
- inspect.getmodule(self).make(*self.install_targets)
+ inspect.getmodule(self.pkg).make(*self.install_targets)
elif self.generator == "Ninja":
- inspect.getmodule(self).ninja(*self.install_targets)
+ inspect.getmodule(self.pkg).ninja(*self.install_targets)
- run_after("build")(PackageBase._run_default_build_time_test_callbacks)
+ spack.builder.run_after("build")(execute_build_time_tests)
def check(self):
- """Searches the CMake-generated Makefile for the target ``test``
- and runs it if found.
+ """Search the CMake-generated files for the targets ``test`` and ``check``,
+ and runs them if found.
"""
- with working_dir(self.build_directory):
+ with fs.working_dir(self.build_directory):
if self.generator == "Unix Makefiles":
- self._if_make_target_execute("test", jobs_env="CTEST_PARALLEL_LEVEL")
- self._if_make_target_execute("check")
+ self.pkg._if_make_target_execute("test", jobs_env="CTEST_PARALLEL_LEVEL")
+ self.pkg._if_make_target_execute("check")
elif self.generator == "Ninja":
- self._if_ninja_target_execute("test", jobs_env="CTEST_PARALLEL_LEVEL")
- self._if_ninja_target_execute("check")
-
- # Check that self.prefix is there after installation
- run_after("install")(PackageBase.sanity_check_prefix)
+ self.pkg._if_ninja_target_execute("test", jobs_env="CTEST_PARALLEL_LEVEL")
+ self.pkg._if_ninja_target_execute("check")
diff --git a/lib/spack/spack/build_systems/generic.py b/lib/spack/spack/build_systems/generic.py
new file mode 100644
index 0000000000..628af6f2d4
--- /dev/null
+++ b/lib/spack/spack/build_systems/generic.py
@@ -0,0 +1,44 @@
+# Copyright 2013-2022 Lawrence Livermore National Security, LLC and other
+# Spack Project Developers. See the top-level COPYRIGHT file for details.
+#
+# SPDX-License-Identifier: (Apache-2.0 OR MIT)
+from typing import Tuple
+
+import spack.builder
+import spack.directives
+import spack.package_base
+
+from ._checks import BaseBuilder, apply_macos_rpath_fixups
+
+
+class Package(spack.package_base.PackageBase):
+ """General purpose class with a single ``install`` phase that needs to be
+ coded by packagers.
+ """
+
+ #: This attribute is used in UI queries that require to know which
+ #: build-system class we are using
+ build_system_class = "Package"
+ #: Legacy buildsystem attribute used to deserialize and install old specs
+ legacy_buildsystem = "generic"
+
+ spack.directives.build_system("generic")
+
+
+@spack.builder.builder("generic")
+class GenericBuilder(BaseBuilder):
+ """A builder for a generic build system, that require packagers
+ to implement an "install" phase.
+ """
+
+ #: A generic package has only the "install" phase
+ phases = ("install",)
+
+ #: Names associated with package methods in the old build-system format
+ legacy_methods = () # type: Tuple[str, ...]
+
+ #: Names associated with package attributes in the old build-system format
+ legacy_attributes = ("archive_files",) # type: Tuple[str, ...]
+
+ # On macOS, force rpaths for shared library IDs and remove duplicate rpaths
+ spack.builder.run_after("install", when="platform=darwin")(apply_macos_rpath_fixups)
diff --git a/lib/spack/spack/build_systems/intel.py b/lib/spack/spack/build_systems/intel.py
index 133b5030de..4abed70f21 100644
--- a/lib/spack/spack/build_systems/intel.py
+++ b/lib/spack/spack/build_systems/intel.py
@@ -2,8 +2,6 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
-
-
import glob
import inspect
import os
@@ -26,12 +24,14 @@ from llnl.util.filesystem import (
import spack.error
from spack.build_environment import dso_suffix
-from spack.package_base import InstallError, PackageBase, run_after
+from spack.package_base import InstallError
from spack.util.environment import EnvironmentModifications
from spack.util.executable import Executable
from spack.util.prefix import Prefix
from spack.version import Version, ver
+from .generic import Package
+
# A couple of utility functions that might be useful in general. If so, they
# should really be defined elsewhere, unless deemed heretical.
# (Or na"ive on my part).
@@ -86,7 +86,7 @@ def _expand_fields(s):
return s
-class IntelPackage(PackageBase):
+class IntelPackage(Package):
"""Specialized class for licensed Intel software.
This class provides two phases that can be overridden:
@@ -99,9 +99,6 @@ class IntelPackage(PackageBase):
to set the appropriate environment variables.
"""
- #: Phases of an Intel package
- phases = ["configure", "install"]
-
#: This attribute is used in UI queries that need to know the build
#: system base class
build_system_class = "IntelPackage"
@@ -1184,12 +1181,13 @@ class IntelPackage(PackageBase):
debug_print(license_type)
return license_type
- def configure(self, spec, prefix):
+ @spack.builder.run_before("install")
+ def configure(self):
"""Generates the silent.cfg file to pass to installer.sh.
See https://software.intel.com/en-us/articles/configuration-file-format
"""
-
+ prefix = self.prefix
# Both tokens AND values of the configuration file are validated during
# the run of the underlying binary installer. Any unknown token or
# unacceptable value will cause that installer to fail. Notably, this
@@ -1270,7 +1268,7 @@ class IntelPackage(PackageBase):
for f in glob.glob("%s/intel*log" % tmpdir):
install(f, dst)
- @run_after("install")
+ @spack.builder.run_after("install")
def validate_install(self):
# Sometimes the installer exits with an error but doesn't pass a
# non-zero exit code to spack. Check for the existence of a 'bin'
@@ -1278,7 +1276,7 @@ class IntelPackage(PackageBase):
if not os.path.exists(self.prefix.bin):
raise InstallError("The installer has failed to install anything.")
- @run_after("install")
+ @spack.builder.run_after("install")
def configure_rpath(self):
if "+rpath" not in self.spec:
return
@@ -1296,7 +1294,7 @@ class IntelPackage(PackageBase):
with open(compiler_cfg, "w") as fh:
fh.write("-Xlinker -rpath={0}\n".format(compilers_lib_dir))
- @run_after("install")
+ @spack.builder.run_after("install")
def configure_auto_dispatch(self):
if self._has_compilers:
if "auto_dispatch=none" in self.spec:
@@ -1320,7 +1318,7 @@ class IntelPackage(PackageBase):
with open(compiler_cfg, "a") as fh:
fh.write("-ax{0}\n".format(",".join(ad)))
- @run_after("install")
+ @spack.builder.run_after("install")
def filter_compiler_wrappers(self):
if ("+mpi" in self.spec or self.provides("mpi")) and "~newdtags" in self.spec:
bin_dir = self.component_bin_dir("mpi")
@@ -1328,7 +1326,7 @@ class IntelPackage(PackageBase):
f = os.path.join(bin_dir, f)
filter_file("-Xlinker --enable-new-dtags", " ", f, string=True)
- @run_after("install")
+ @spack.builder.run_after("install")
def uninstall_ism(self):
# The "Intel(R) Software Improvement Program" [ahem] gets installed,
# apparently regardless of PHONEHOME_SEND_USAGE_DATA.
@@ -1360,7 +1358,7 @@ class IntelPackage(PackageBase):
debug_print(d)
return d
- @run_after("install")
+ @spack.builder.run_after("install")
def modify_LLVMgold_rpath(self):
"""Add libimf.so and other required libraries to the RUNPATH of LLVMgold.so.
@@ -1391,6 +1389,3 @@ class IntelPackage(PackageBase):
]
)
patchelf("--set-rpath", rpath, lib)
-
- # Check that self.prefix is there after installation
- run_after("install")(PackageBase.sanity_check_prefix)
diff --git a/lib/spack/spack/build_systems/lua.py b/lib/spack/spack/build_systems/lua.py
index c0d4321097..48fa106881 100644
--- a/lib/spack/spack/build_systems/lua.py
+++ b/lib/spack/spack/build_systems/lua.py
@@ -2,59 +2,79 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
-
-
import os
from llnl.util.filesystem import find
-from spack.directives import depends_on, extends
+import spack.builder
+import spack.package_base
+import spack.util.executable
+from spack.directives import build_system, depends_on, extends
from spack.multimethod import when
-from spack.package_base import PackageBase
-from spack.util.executable import Executable
-class LuaPackage(PackageBase):
+class LuaPackage(spack.package_base.PackageBase):
"""Specialized class for lua packages"""
- phases = ["unpack", "generate_luarocks_config", "preprocess", "install"]
#: This attribute is used in UI queries that need to know the build
#: system base class
build_system_class = "LuaPackage"
list_depth = 1 # LuaRocks requires at least one level of spidering to find versions
- depends_on("lua-lang")
- extends("lua", when="^lua")
- with when("^lua-luajit"):
- extends("lua-luajit")
- depends_on("luajit")
- depends_on("lua-luajit+lualinks")
- with when("^lua-luajit-openresty"):
- extends("lua-luajit-openresty")
- depends_on("luajit")
- depends_on("lua-luajit-openresty+lualinks")
-
- def unpack(self, spec, prefix):
- if os.path.splitext(self.stage.archive_file)[1] == ".rock":
- directory = self.luarocks("unpack", self.stage.archive_file, output=str)
+
+ build_system("lua")
+
+ with when("build_system=lua"):
+ depends_on("lua-lang")
+ extends("lua", when="^lua")
+ with when("^lua-luajit"):
+ extends("lua-luajit")
+ depends_on("luajit")
+ depends_on("lua-luajit+lualinks")
+ with when("^lua-luajit-openresty"):
+ extends("lua-luajit-openresty")
+ depends_on("luajit")
+ depends_on("lua-luajit-openresty+lualinks")
+
+ @property
+ def lua(self):
+ return spack.util.executable.Executable(self.spec["lua-lang"].prefix.bin.lua)
+
+ @property
+ def luarocks(self):
+ lr = spack.util.executable.Executable(self.spec["lua-lang"].prefix.bin.luarocks)
+ return lr
+
+
+@spack.builder.builder("lua")
+class LuaBuilder(spack.builder.Builder):
+ phases = ("unpack", "generate_luarocks_config", "preprocess", "install")
+
+ #: Names associated with package methods in the old build-system format
+ legacy_methods = ("luarocks_args",)
+
+ #: Names associated with package attributes in the old build-system format
+ legacy_attributes = ()
+
+ def unpack(self, pkg, spec, prefix):
+ if os.path.splitext(pkg.stage.archive_file)[1] == ".rock":
+ directory = pkg.luarocks("unpack", pkg.stage.archive_file, output=str)
dirlines = directory.split("\n")
# TODO: figure out how to scope this better
os.chdir(dirlines[2])
- def _generate_tree_line(self, name, prefix):
+ @staticmethod
+ def _generate_tree_line(name, prefix):
return """{{ name = "{name}", root = "{prefix}" }};""".format(
name=name,
prefix=prefix,
)
- def _luarocks_config_path(self):
- return os.path.join(self.stage.source_path, "spack_luarocks.lua")
-
- def generate_luarocks_config(self, spec, prefix):
- spec = self.spec
+ def generate_luarocks_config(self, pkg, spec, prefix):
+ spec = self.pkg.spec
table_entries = []
for d in spec.traverse(deptypes=("build", "run"), deptype_query="run"):
- if d.package.extends(self.extendee_spec):
+ if d.package.extends(self.pkg.extendee_spec):
table_entries.append(self._generate_tree_line(d.name, d.prefix))
path = self._luarocks_config_path()
@@ -71,30 +91,24 @@ class LuaPackage(PackageBase):
)
return path
- def setup_build_environment(self, env):
- env.set("LUAROCKS_CONFIG", self._luarocks_config_path())
-
- def preprocess(self, spec, prefix):
+ def preprocess(self, pkg, spec, prefix):
"""Override this to preprocess source before building with luarocks"""
pass
- @property
- def lua(self):
- return Executable(self.spec["lua-lang"].prefix.bin.lua)
-
- @property
- def luarocks(self):
- lr = Executable(self.spec["lua-lang"].prefix.bin.luarocks)
- return lr
-
def luarocks_args(self):
return []
- def install(self, spec, prefix):
+ def install(self, pkg, spec, prefix):
rock = "."
specs = find(".", "*.rockspec", recursive=False)
if specs:
rock = specs[0]
rocks_args = self.luarocks_args()
rocks_args.append(rock)
- self.luarocks("--tree=" + prefix, "make", *rocks_args)
+ self.pkg.luarocks("--tree=" + prefix, "make", *rocks_args)
+
+ def _luarocks_config_path(self):
+ return os.path.join(self.pkg.stage.source_path, "spack_luarocks.lua")
+
+ def setup_build_environment(self, env):
+ env.set("LUAROCKS_CONFIG", self._luarocks_config_path())
diff --git a/lib/spack/spack/build_systems/makefile.py b/lib/spack/spack/build_systems/makefile.py
index e2bb8c0c26..b826144258 100644
--- a/lib/spack/spack/build_systems/makefile.py
+++ b/lib/spack/spack/build_systems/makefile.py
@@ -2,62 +2,85 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
-
-
import inspect
from typing import List # novm
-import llnl.util.tty as tty
-from llnl.util.filesystem import working_dir
+import llnl.util.filesystem as fs
+
+import spack.builder
+import spack.package_base
+from spack.directives import build_system, conflicts
-from spack.directives import conflicts
-from spack.package_base import PackageBase, run_after
+from ._checks import (
+ BaseBuilder,
+ apply_macos_rpath_fixups,
+ execute_build_time_tests,
+ execute_install_time_tests,
+)
-class MakefilePackage(PackageBase):
- """Specialized class for packages that are built using editable Makefiles
+class MakefilePackage(spack.package_base.PackageBase):
+ """Specialized class for packages built using a Makefiles."""
- This class provides three phases that can be overridden:
+ #: This attribute is used in UI queries that need to know the build
+ #: system base class
+ build_system_class = "MakefilePackage"
+ #: Legacy buildsystem attribute used to deserialize and install old specs
+ legacy_buildsystem = "makefile"
- 1. :py:meth:`~.MakefilePackage.edit`
- 2. :py:meth:`~.MakefilePackage.build`
- 3. :py:meth:`~.MakefilePackage.install`
+ build_system("makefile")
+ conflicts("platform=windows", when="build_system=makefile")
+
+
+@spack.builder.builder("makefile")
+class MakefileBuilder(BaseBuilder):
+ """The Makefile builder encodes the most common way of building software with
+ Makefiles. It has three phases that can be overridden, if need be:
+
+ 1. :py:meth:`~.MakefileBuilder.edit`
+ 2. :py:meth:`~.MakefileBuilder.build`
+ 3. :py:meth:`~.MakefileBuilder.install`
+
+ It is usually necessary to override the :py:meth:`~.MakefileBuilder.edit`
+ phase (which is by default a no-op), while the other two have sensible defaults.
- It is usually necessary to override the :py:meth:`~.MakefilePackage.edit`
- phase, while :py:meth:`~.MakefilePackage.build` and
- :py:meth:`~.MakefilePackage.install` have sensible defaults.
For a finer tuning you may override:
+-----------------------------------------------+--------------------+
| **Method** | **Purpose** |
+===============================================+====================+
- | :py:attr:`~.MakefilePackage.build_targets` | Specify ``make`` |
+ | :py:attr:`~.MakefileBuilder.build_targets` | Specify ``make`` |
| | targets for the |
| | build phase |
+-----------------------------------------------+--------------------+
- | :py:attr:`~.MakefilePackage.install_targets` | Specify ``make`` |
+ | :py:attr:`~.MakefileBuilder.install_targets` | Specify ``make`` |
| | targets for the |
| | install phase |
+-----------------------------------------------+--------------------+
- | :py:meth:`~.MakefilePackage.build_directory` | Directory where the|
+ | :py:meth:`~.MakefileBuilder.build_directory` | Directory where the|
| | Makefile is located|
+-----------------------------------------------+--------------------+
"""
- #: Phases of a package that is built with an hand-written Makefile
- phases = ["edit", "build", "install"]
- #: This attribute is used in UI queries that need to know the build
- #: system base class
- build_system_class = "MakefilePackage"
+ phases = ("edit", "build", "install")
+
+ #: Names associated with package methods in the old build-system format
+ legacy_methods = ("check", "installcheck")
- #: Targets for ``make`` during the :py:meth:`~.MakefilePackage.build`
- #: phase
+ #: Names associated with package attributes in the old build-system format
+ legacy_attributes = (
+ "build_targets",
+ "install_targets",
+ "build_time_test_callbacks",
+ "install_time_test_callbacks",
+ "build_directory",
+ )
+
+ #: Targets for ``make`` during the :py:meth:`~.MakefileBuilder.build` phase
build_targets = [] # type: List[str]
- #: Targets for ``make`` during the :py:meth:`~.MakefilePackage.install`
- #: phase
+ #: Targets for ``make`` during the :py:meth:`~.MakefileBuilder.install` phase
install_targets = ["install"]
- conflicts("platform=windows")
#: Callback names for build-time test
build_time_test_callbacks = ["check"]
@@ -66,53 +89,39 @@ class MakefilePackage(PackageBase):
@property
def build_directory(self):
- """Returns the directory containing the main Makefile
+ """Return the directory containing the main Makefile."""
+ return self.pkg.stage.source_path
- :return: build directory
- """
- return self.stage.source_path
+ def edit(self, pkg, spec, prefix):
+ """Edit the Makefile before calling make. The default is a no-op."""
+ pass
- def edit(self, spec, prefix):
- """Edits the Makefile before calling make. This phase cannot
- be defaulted.
- """
- tty.msg("Using default implementation: skipping edit phase.")
+ def build(self, pkg, spec, prefix):
+ """Run "make" on the build targets specified by the builder."""
+ with fs.working_dir(self.build_directory):
+ inspect.getmodule(self.pkg).make(*self.build_targets)
- def build(self, spec, prefix):
- """Calls make, passing :py:attr:`~.MakefilePackage.build_targets`
- as targets.
- """
- with working_dir(self.build_directory):
- inspect.getmodule(self).make(*self.build_targets)
-
- def install(self, spec, prefix):
- """Calls make, passing :py:attr:`~.MakefilePackage.install_targets`
- as targets.
- """
- with working_dir(self.build_directory):
- inspect.getmodule(self).make(*self.install_targets)
+ def install(self, pkg, spec, prefix):
+ """Run "make" on the install targets specified by the builder."""
+ with fs.working_dir(self.build_directory):
+ inspect.getmodule(self.pkg).make(*self.install_targets)
- run_after("build")(PackageBase._run_default_build_time_test_callbacks)
+ spack.builder.run_after("build")(execute_build_time_tests)
def check(self):
- """Searches the Makefile for targets ``test`` and ``check``
- and runs them if found.
- """
- with working_dir(self.build_directory):
- self._if_make_target_execute("test")
- self._if_make_target_execute("check")
+ """Run "make" on the ``test`` and ``check`` targets, if found."""
+ with fs.working_dir(self.build_directory):
+ self.pkg._if_make_target_execute("test")
+ self.pkg._if_make_target_execute("check")
- run_after("install")(PackageBase._run_default_install_time_test_callbacks)
+ spack.builder.run_after("install")(execute_install_time_tests)
def installcheck(self):
"""Searches the Makefile for an ``installcheck`` target
and runs it if found.
"""
- with working_dir(self.build_directory):
- self._if_make_target_execute("installcheck")
-
- # Check that self.prefix is there after installation
- run_after("install")(PackageBase.sanity_check_prefix)
+ with fs.working_dir(self.build_directory):
+ self.pkg._if_make_target_execute("installcheck")
# On macOS, force rpaths for shared library IDs and remove duplicate rpaths
- run_after("install")(PackageBase.apply_macos_rpath_fixups)
+ spack.builder.run_after("install", when="platform=darwin")(apply_macos_rpath_fixups)
diff --git a/lib/spack/spack/build_systems/maven.py b/lib/spack/spack/build_systems/maven.py
index 1ff1882e13..66680b5b6c 100644
--- a/lib/spack/spack/build_systems/maven.py
+++ b/lib/spack/spack/build_systems/maven.py
@@ -2,60 +2,73 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
+import llnl.util.filesystem as fs
-
-from llnl.util.filesystem import install_tree, working_dir
-
-from spack.directives import depends_on
-from spack.package_base import PackageBase, run_after
+import spack.builder
+import spack.package_base
+from spack.directives import build_system, depends_on
+from spack.multimethod import when
from spack.util.executable import which
+from ._checks import BaseBuilder
+
-class MavenPackage(PackageBase):
+class MavenPackage(spack.package_base.PackageBase):
"""Specialized class for packages that are built using the
Maven build system. See https://maven.apache.org/index.html
for more information.
-
- This class provides the following phases that can be overridden:
-
- * build
- * install
"""
- # Default phases
- phases = ["build", "install"]
-
# To be used in UI queries that require to know which
# build-system class we are using
build_system_class = "MavenPackage"
- depends_on("java", type=("build", "run"))
- depends_on("maven", type="build")
+ #: Legacy buildsystem attribute used to deserialize and install old specs
+ legacy_buildsystem = "maven"
+
+ build_system("maven")
+
+ with when("build_system=maven"):
+ depends_on("java", type=("build", "run"))
+ depends_on("maven", type="build")
+
+
+@spack.builder.builder("maven")
+class MavenBuilder(BaseBuilder):
+ """The Maven builder encodes the default way to build software with Maven.
+ It has two phases that can be overridden, if need be:
+
+ 1. :py:meth:`~.MavenBuilder.build`
+ 2. :py:meth:`~.MavenBuilder.install`
+ """
+
+ phases = ("build", "install")
+
+ #: Names associated with package methods in the old build-system format
+ legacy_methods = ("build_args",)
+
+ #: Names associated with package attributes in the old build-system format
+ legacy_attributes = ("build_directory",)
@property
def build_directory(self):
"""The directory containing the ``pom.xml`` file."""
- return self.stage.source_path
+ return self.pkg.stage.source_path
def build_args(self):
"""List of args to pass to build phase."""
return []
- def build(self, spec, prefix):
+ def build(self, pkg, spec, prefix):
"""Compile code and package into a JAR file."""
-
- with working_dir(self.build_directory):
+ with fs.working_dir(self.build_directory):
mvn = which("mvn")
- if self.run_tests:
+ if self.pkg.run_tests:
mvn("verify", *self.build_args())
else:
mvn("package", "-DskipTests", *self.build_args())
- def install(self, spec, prefix):
+ def install(self, pkg, spec, prefix):
"""Copy to installation prefix."""
-
- with working_dir(self.build_directory):
- install_tree(".", prefix)
-
- # Check that self.prefix is there after installation
- run_after("install")(PackageBase.sanity_check_prefix)
+ with fs.working_dir(self.build_directory):
+ fs.install_tree(".", prefix)
diff --git a/lib/spack/spack/build_systems/meson.py b/lib/spack/spack/build_systems/meson.py
index cafd613808..6ab760cfb9 100644
--- a/lib/spack/spack/build_systems/meson.py
+++ b/lib/spack/spack/build_systems/meson.py
@@ -2,76 +2,104 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
-
-
import inspect
import os
from typing import List # novm
-from llnl.util.filesystem import working_dir
+import llnl.util.filesystem as fs
-from spack.directives import depends_on, variant
-from spack.package_base import PackageBase, run_after
+import spack.builder
+import spack.package_base
+from spack.directives import build_system, depends_on, variant
+from spack.multimethod import when
+from ._checks import BaseBuilder, execute_build_time_tests
-class MesonPackage(PackageBase):
- """Specialized class for packages built using Meson
- For more information on the Meson build system, see:
- https://mesonbuild.com/
+class MesonPackage(spack.package_base.PackageBase):
+ """Specialized class for packages built using Meson. For more information
+ on the Meson build system, see https://mesonbuild.com/
+ """
- This class provides three phases that can be overridden:
+ #: This attribute is used in UI queries that need to know the build
+ #: system base class
+ build_system_class = "MesonPackage"
- 1. :py:meth:`~.MesonPackage.meson`
- 2. :py:meth:`~.MesonPackage.build`
- 3. :py:meth:`~.MesonPackage.install`
+ #: Legacy buildsystem attribute used to deserialize and install old specs
+ legacy_buildsystem = "meson"
+
+ build_system("meson")
+
+ with when("build_system=meson"):
+ variant(
+ "buildtype",
+ default="debugoptimized",
+ description="Meson build type",
+ values=("plain", "debug", "debugoptimized", "release", "minsize"),
+ )
+ variant(
+ "default_library",
+ default="shared",
+ values=("shared", "static"),
+ multi=True,
+ description="Build shared libs, static libs or both",
+ )
+ variant("strip", default=False, description="Strip targets on install")
+ depends_on("meson", type="build")
+ depends_on("ninja", type="build")
+
+ def flags_to_build_system_args(self, flags):
+ """Produces a list of all command line arguments to pass the specified
+ compiler flags to meson."""
+ # Has to be dynamic attribute due to caching
+ setattr(self, "meson_flag_args", [])
+
+
+@spack.builder.builder("meson")
+class MesonBuilder(BaseBuilder):
+ """The Meson builder encodes the default way to build software with Meson.
+ The builder has three phases that can be overridden, if need be:
+
+ 1. :py:meth:`~.MesonBuilder.meson`
+ 2. :py:meth:`~.MesonBuilder.build`
+ 3. :py:meth:`~.MesonBuilder.install`
They all have sensible defaults and for many packages the only thing
- necessary will be to override :py:meth:`~.MesonPackage.meson_args`.
+ necessary will be to override :py:meth:`~.MesonBuilder.meson_args`.
+
For a finer tuning you may also override:
+-----------------------------------------------+--------------------+
| **Method** | **Purpose** |
+===============================================+====================+
- | :py:meth:`~.MesonPackage.root_mesonlists_dir` | Location of the |
+ | :py:meth:`~.MesonBuilder.root_mesonlists_dir` | Location of the |
| | root MesonLists.txt|
+-----------------------------------------------+--------------------+
- | :py:meth:`~.MesonPackage.build_directory` | Directory where to |
+ | :py:meth:`~.MesonBuilder.build_directory` | Directory where to |
| | build the package |
+-----------------------------------------------+--------------------+
+ """
+ phases = ("meson", "build", "install")
- """
+ #: Names associated with package methods in the old build-system format
+ legacy_methods = ("meson_args", "check")
- #: Phases of a Meson package
- phases = ["meson", "build", "install"]
- #: This attribute is used in UI queries that need to know the build
- #: system base class
- build_system_class = "MesonPackage"
+ #: Names associated with package attributes in the old build-system format
+ legacy_attributes = (
+ "build_targets",
+ "install_targets",
+ "build_time_test_callbacks",
+ "root_mesonlists_dir",
+ "std_meson_args",
+ "build_directory",
+ )
build_targets = [] # type: List[str]
install_targets = ["install"]
build_time_test_callbacks = ["check"]
- variant(
- "buildtype",
- default="debugoptimized",
- description="Meson build type",
- values=("plain", "debug", "debugoptimized", "release", "minsize"),
- )
- variant(
- "default_library",
- default="shared",
- values=("shared", "static"),
- multi=True,
- description="Build shared libs, static libs or both",
- )
- variant("strip", default=False, description="Strip targets on install")
-
- depends_on("meson", type="build")
- depends_on("ninja", type="build")
-
@property
def archive_files(self):
"""Files to archive for packages based on Meson"""
@@ -79,31 +107,26 @@ class MesonPackage(PackageBase):
@property
def root_mesonlists_dir(self):
- """The relative path to the directory containing meson.build
+ """Relative path to the directory containing meson.build
This path is relative to the root of the extracted tarball,
not to the ``build_directory``. Defaults to the current directory.
-
- :return: directory containing meson.build
"""
- return self.stage.source_path
+ return self.pkg.stage.source_path
@property
def std_meson_args(self):
- """Standard meson arguments provided as a property for
- convenience of package writers
-
- :return: standard meson arguments
+ """Standard meson arguments provided as a property for convenience
+ of package writers.
"""
# standard Meson arguments
- std_meson_args = MesonPackage._std_args(self)
+ std_meson_args = MesonBuilder.std_args(self.pkg)
std_meson_args += getattr(self, "meson_flag_args", [])
return std_meson_args
@staticmethod
- def _std_args(pkg):
- """Computes the standard meson arguments for a generic package"""
-
+ def std_args(pkg):
+ """Standard meson arguments for a generic package."""
try:
build_type = pkg.spec.variants["buildtype"].value
except KeyError:
@@ -132,31 +155,18 @@ class MesonPackage(PackageBase):
return args
- def flags_to_build_system_args(self, flags):
- """Produces a list of all command line arguments to pass the specified
- compiler flags to meson."""
- # Has to be dynamic attribute due to caching
- setattr(self, "meson_flag_args", [])
-
@property
def build_dirname(self):
- """Returns the directory name to use when building the package
-
- :return: name of the subdirectory for building the package
- """
- return "spack-build-%s" % self.spec.dag_hash(7)
+ """Returns the directory name to use when building the package."""
+ return "spack-build-{}".format(self.spec.dag_hash(7))
@property
def build_directory(self):
- """Returns the directory to use when building the package
-
- :return: directory where to build the package
- """
- return os.path.join(self.stage.path, self.build_dirname)
+ """Directory to use when building the package."""
+ return os.path.join(self.pkg.stage.path, self.build_dirname)
def meson_args(self):
- """Produces a list containing all the arguments that must be passed to
- meson, except:
+ """List of arguments that must be passed to meson, except:
* ``--prefix``
* ``--libdir``
@@ -165,40 +175,33 @@ class MesonPackage(PackageBase):
* ``--default_library``
which will be set automatically.
-
- :return: list of arguments for meson
"""
return []
- def meson(self, spec, prefix):
- """Runs ``meson`` in the build directory"""
+ def meson(self, pkg, spec, prefix):
+ """Run ``meson`` in the build directory"""
options = [os.path.abspath(self.root_mesonlists_dir)]
options += self.std_meson_args
options += self.meson_args()
- with working_dir(self.build_directory, create=True):
- inspect.getmodule(self).meson(*options)
+ with fs.working_dir(self.build_directory, create=True):
+ inspect.getmodule(self.pkg).meson(*options)
- def build(self, spec, prefix):
+ def build(self, pkg, spec, prefix):
"""Make the build targets"""
options = ["-v"]
options += self.build_targets
- with working_dir(self.build_directory):
- inspect.getmodule(self).ninja(*options)
+ with fs.working_dir(self.build_directory):
+ inspect.getmodule(self.pkg).ninja(*options)
- def install(self, spec, prefix):
+ def install(self, pkg, spec, prefix):
"""Make the install targets"""
- with working_dir(self.build_directory):
- inspect.getmodule(self).ninja(*self.install_targets)
+ with fs.working_dir(self.build_directory):
+ inspect.getmodule(self.pkg).ninja(*self.install_targets)
- run_after("build")(PackageBase._run_default_build_time_test_callbacks)
+ spack.builder.run_after("build")(execute_build_time_tests)
def check(self):
- """Searches the Meson-generated file for the target ``test``
- and runs it if found.
- """
- with working_dir(self.build_directory):
+ """Search Meson-generated files for the target ``test`` and run it if found."""
+ with fs.working_dir(self.build_directory):
self._if_ninja_target_execute("test")
self._if_ninja_target_execute("check")
-
- # Check that self.prefix is there after installation
- run_after("install")(PackageBase.sanity_check_prefix)
diff --git a/lib/spack/spack/build_systems/nmake.py b/lib/spack/spack/build_systems/nmake.py
new file mode 100644
index 0000000000..bf36895160
--- /dev/null
+++ b/lib/spack/spack/build_systems/nmake.py
@@ -0,0 +1,102 @@
+# Copyright 2013-2022 Lawrence Livermore National Security, LLC and other
+# Spack Project Developers. See the top-level COPYRIGHT file for details.
+#
+# SPDX-License-Identifier: (Apache-2.0 OR MIT)
+import inspect
+from typing import List # novm
+
+import llnl.util.filesystem as fs
+
+import spack.builder
+import spack.package_base
+from spack.directives import build_system, conflicts
+
+from ._checks import BaseBuilder
+
+
+class NMakePackage(spack.package_base.PackageBase):
+ """Specialized class for packages built using a Makefiles."""
+
+ #: This attribute is used in UI queries that need to know the build
+ #: system base class
+ build_system_class = "NmakePackage"
+
+ build_system("nmake")
+ conflicts("platform=linux", when="build_system=nmake")
+ conflicts("platform=darwin", when="build_system=nmake")
+ conflicts("platform=cray", when="build_system=nmake")
+
+
+@spack.builder.builder("nmake")
+class NMakeBuilder(BaseBuilder):
+ """The NMake builder encodes the most common way of building software with
+ NMake on Windows. It has three phases that can be overridden, if need be:
+
+ 1. :py:meth:`~.NMakeBuilder.edit`
+ 2. :py:meth:`~.NMakeBuilder.build`
+ 3. :py:meth:`~.NMakeBuilder.install`
+
+ It is usually necessary to override the :py:meth:`~.NMakeBuilder.edit`
+ phase (which is by default a no-op), while the other two have sensible defaults.
+
+ For a finer tuning you may override:
+
+ +--------------------------------------------+--------------------+
+ | **Method** | **Purpose** |
+ +============================================+====================+
+ | :py:attr:`~.NMakeBuilder.build_targets` | Specify ``nmake`` |
+ | | targets for the |
+ | | build phase |
+ +--------------------------------------------+--------------------+
+ | :py:attr:`~.NMakeBuilder.install_targets` | Specify ``nmake`` |
+ | | targets for the |
+ | | install phase |
+ +--------------------------------------------+--------------------+
+ | :py:meth:`~.NMakeBuilder.build_directory` | Directory where the|
+ | | Makefile is located|
+ +--------------------------------------------+--------------------+
+ """
+
+ phases = ("edit", "build", "install")
+
+ #: Names associated with package methods in the old build-system format
+ legacy_methods = ("check", "installcheck")
+
+ #: Names associated with package attributes in the old build-system format
+ legacy_attributes = (
+ "build_targets",
+ "install_targets",
+ "build_time_test_callbacks",
+ "install_time_test_callbacks",
+ "build_directory",
+ )
+
+ #: Targets for ``make`` during the :py:meth:`~.NMakeBuilder.build` phase
+ build_targets = [] # type: List[str]
+ #: Targets for ``make`` during the :py:meth:`~.NMakeBuilder.install` phase
+ install_targets = ["install"]
+
+ #: Callback names for build-time test
+ build_time_test_callbacks = ["check"]
+
+ #: Callback names for install-time test
+ install_time_test_callbacks = ["installcheck"]
+
+ @property
+ def build_directory(self):
+ """Return the directory containing the main Makefile."""
+ return self.pkg.stage.source_path
+
+ def edit(self, pkg, spec, prefix):
+ """Edit the Makefile before calling make. The default is a no-op."""
+ pass
+
+ def build(self, pkg, spec, prefix):
+ """Run "make" on the build targets specified by the builder."""
+ with fs.working_dir(self.build_directory):
+ inspect.getmodule(self.pkg).nmake(*self.build_targets)
+
+ def install(self, pkg, spec, prefix):
+ """Run "make" on the install targets specified by the builder."""
+ with fs.working_dir(self.build_directory):
+ inspect.getmodule(self.pkg).nmake(*self.install_targets)
diff --git a/lib/spack/spack/build_systems/octave.py b/lib/spack/spack/build_systems/octave.py
index 9916c319b0..5b2456f098 100644
--- a/lib/spack/spack/build_systems/octave.py
+++ b/lib/spack/spack/build_systems/octave.py
@@ -2,51 +2,62 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
-
import inspect
-from spack.directives import extends
-from spack.package_base import PackageBase, run_after
+import spack.builder
+import spack.package_base
+from spack.directives import build_system, extends
+from spack.multimethod import when
+
+from ._checks import BaseBuilder
-class OctavePackage(PackageBase):
+class OctavePackage(spack.package_base.PackageBase):
"""Specialized class for Octave packages. See
https://www.gnu.org/software/octave/doc/v4.2.0/Installing-and-Removing-Packages.html
for more information.
-
- This class provides the following phases that can be overridden:
-
- 1. :py:meth:`~.OctavePackage.install`
-
"""
- # Default phases
- phases = ["install"]
-
# To be used in UI queries that require to know which
# build-system class we are using
build_system_class = "OctavePackage"
+ #: Legacy buildsystem attribute used to deserialize and install old specs
+ legacy_buildsystem = "octave"
- extends("octave")
+ build_system("octave")
+
+ with when("build_system=octave"):
+ extends("octave")
- def setup_build_environment(self, env):
- # octave does not like those environment variables to be set:
- env.unset("CC")
- env.unset("CXX")
- env.unset("FC")
- def install(self, spec, prefix):
+@spack.builder.builder("octave")
+class OctaveBuilder(BaseBuilder):
+ """The octave builder provides the following phases that can be overridden:
+
+ 1. :py:meth:`~.OctaveBuilder.install`
+ """
+
+ phases = ("install",)
+
+ #: Names associated with package methods in the old build-system format
+ legacy_methods = ()
+
+ #: Names associated with package attributes in the old build-system format
+ legacy_attributes = ()
+
+ def install(self, pkg, spec, prefix):
"""Install the package from the archive file"""
- inspect.getmodule(self).octave(
+ inspect.getmodule(self.pkg).octave(
"--quiet",
"--norc",
"--built-in-docstrings-file=/dev/null",
"--texi-macros-file=/dev/null",
"--eval",
- "pkg prefix %s; pkg install %s" % (prefix, self.stage.archive_file),
+ "pkg prefix %s; pkg install %s" % (prefix, self.pkg.stage.archive_file),
)
- # Testing
-
- # Check that self.prefix is there after installation
- run_after("install")(PackageBase.sanity_check_prefix)
+ def setup_build_environment(self, env):
+ # octave does not like those environment variables to be set:
+ env.unset("CC")
+ env.unset("CXX")
+ env.unset("FC")
diff --git a/lib/spack/spack/build_systems/oneapi.py b/lib/spack/spack/build_systems/oneapi.py
index 669c66fe8f..1cb79b9901 100644
--- a/lib/spack/spack/build_systems/oneapi.py
+++ b/lib/spack/spack/build_systems/oneapi.py
@@ -2,11 +2,7 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
-
-"""Common utilities for managing intel oneapi packages.
-
-"""
-
+"""Common utilities for managing intel oneapi packages."""
import getpass
import platform
import shutil
@@ -14,18 +10,17 @@ from os.path import basename, dirname, isdir
from llnl.util.filesystem import find_headers, find_libraries, join_path
-from spack.package_base import Package
from spack.util.environment import EnvironmentModifications
from spack.util.executable import Executable
+from .generic import Package
+
class IntelOneApiPackage(Package):
"""Base class for Intel oneAPI packages."""
homepage = "https://software.intel.com/oneapi"
- phases = ["install"]
-
# oneAPI license does not allow mirroring outside of the
# organization (e.g. University/Company).
redistribute_source = False
diff --git a/lib/spack/spack/build_systems/perl.py b/lib/spack/spack/build_systems/perl.py
index 1f354beece..a100b89bfd 100644
--- a/lib/spack/spack/build_systems/perl.py
+++ b/lib/spack/spack/build_systems/perl.py
@@ -2,73 +2,87 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
-
-
import inspect
import os
from llnl.util.filesystem import filter_file
-from spack.directives import extends
-from spack.package_base import PackageBase, run_after
+import spack.builder
+import spack.package_base
+from spack.directives import build_system, extends
+from spack.package_base import PackageBase
from spack.util.executable import Executable
+from ._checks import BaseBuilder, execute_build_time_tests
+
class PerlPackage(PackageBase):
- """Specialized class for packages that are built using Perl.
+ """Specialized class for packages that are built using Perl."""
+
+ #: This attribute is used in UI queries that need to know the build
+ #: system base class
+ build_system_class = "PerlPackage"
+ #: Legacy buildsystem attribute used to deserialize and install old specs
+ legacy_buildsystem = "perl"
+
+ build_system("perl")
+
+ extends("perl", when="build_system=perl")
- This class provides four phases that can be overridden if required:
- 1. :py:meth:`~.PerlPackage.configure`
- 2. :py:meth:`~.PerlPackage.build`
- 3. :py:meth:`~.PerlPackage.check`
- 4. :py:meth:`~.PerlPackage.install`
+@spack.builder.builder("perl")
+class PerlBuilder(BaseBuilder):
+ """The perl builder provides four phases that can be overridden, if required:
+
+ 1. :py:meth:`~.PerlBuilder.configure`
+ 2. :py:meth:`~.PerlBuilder.build`
+ 3. :py:meth:`~.PerlBuilder.check`
+ 4. :py:meth:`~.PerlBuilder.install`
The default methods use, in order of preference:
(1) Makefile.PL,
(2) Build.PL.
- Some packages may need to override
- :py:meth:`~.PerlPackage.configure_args`,
- which produces a list of arguments for
- :py:meth:`~.PerlPackage.configure`.
+ Some packages may need to override :py:meth:`~.PerlBuilder.configure_args`,
+ which produces a list of arguments for :py:meth:`~.PerlBuilder.configure`.
+
Arguments should not include the installation base directory.
"""
#: Phases of a Perl package
- phases = ["configure", "build", "install"]
+ phases = ("configure", "build", "install")
- #: This attribute is used in UI queries that need to know the build
- #: system base class
- build_system_class = "PerlPackage"
+ #: Names associated with package methods in the old build-system format
+ legacy_methods = ("configure_args", "check")
+
+ #: Names associated with package attributes in the old build-system format
+ legacy_attributes = ()
#: Callback names for build-time test
build_time_test_callbacks = ["check"]
- extends("perl")
-
def configure_args(self):
- """Produces a list containing the arguments that must be passed to
- :py:meth:`~.PerlPackage.configure`. Arguments should not include
- the installation base directory, which is prepended automatically.
+ """List of arguments passed to :py:meth:`~.PerlBuilder.configure`.
- :return: list of arguments for Makefile.PL or Build.PL
+ Arguments should not include the installation base directory, which
+ is prepended automatically.
"""
return []
- def configure(self, spec, prefix):
- """Runs Makefile.PL or Build.PL with arguments consisting of
+ def configure(self, pkg, spec, prefix):
+ """Run Makefile.PL or Build.PL with arguments consisting of
an appropriate installation base directory followed by the
- list returned by :py:meth:`~.PerlPackage.configure_args`.
+ list returned by :py:meth:`~.PerlBuilder.configure_args`.
- :raise RuntimeError: if neither Makefile.PL or Build.PL exist
+ Raises:
+ RuntimeError: if neither Makefile.PL nor Build.PL exist
"""
if os.path.isfile("Makefile.PL"):
self.build_method = "Makefile.PL"
- self.build_executable = inspect.getmodule(self).make
+ self.build_executable = inspect.getmodule(self.pkg).make
elif os.path.isfile("Build.PL"):
self.build_method = "Build.PL"
- self.build_executable = Executable(os.path.join(self.stage.source_path, "Build"))
+ self.build_executable = Executable(os.path.join(self.pkg.stage.source_path, "Build"))
else:
raise RuntimeError("Unknown build_method for perl package")
@@ -78,33 +92,30 @@ class PerlPackage(PackageBase):
options = ["Build.PL", "--install_base", prefix]
options += self.configure_args()
- inspect.getmodule(self).perl(*options)
+ inspect.getmodule(self.pkg).perl(*options)
# It is possible that the shebang in the Build script that is created from
# Build.PL may be too long causing the build to fail. Patching the shebang
# does not happen until after install so set '/usr/bin/env perl' here in
# the Build script.
- @run_after("configure")
+ @spack.builder.run_after("configure")
def fix_shebang(self):
if self.build_method == "Build.PL":
pattern = "#!{0}".format(self.spec["perl"].command.path)
repl = "#!/usr/bin/env perl"
filter_file(pattern, repl, "Build", backup=False)
- def build(self, spec, prefix):
+ def build(self, pkg, spec, prefix):
"""Builds a Perl package."""
self.build_executable()
# Ensure that tests run after build (if requested):
- run_after("build")(PackageBase._run_default_build_time_test_callbacks)
+ spack.builder.run_after("build")(execute_build_time_tests)
def check(self):
"""Runs built-in tests of a Perl package."""
self.build_executable("test")
- def install(self, spec, prefix):
+ def install(self, pkg, spec, prefix):
"""Installs a Perl package."""
self.build_executable("install")
-
- # Check that self.prefix is there after installation
- run_after("install")(PackageBase.sanity_check_prefix)
diff --git a/lib/spack/spack/build_systems/python.py b/lib/spack/spack/build_systems/python.py
index d1cecdac63..0f84ac7f8a 100644
--- a/lib/spack/spack/build_systems/python.py
+++ b/lib/spack/spack/build_systems/python.py
@@ -8,93 +8,22 @@ import re
import shutil
from typing import Optional
+import llnl.util.filesystem as fs
+import llnl.util.lang as lang
import llnl.util.tty as tty
-from llnl.util.filesystem import (
- filter_file,
- find,
- find_all_headers,
- find_libraries,
- is_nonsymlink_exe_with_shebang,
- path_contains_subdirectory,
- same_path,
- working_dir,
-)
-from llnl.util.lang import classproperty, match_predicate
-
-from spack.directives import depends_on, extends
+
+import spack.builder
+import spack.multimethod
+import spack.package_base
+from spack.directives import build_system, depends_on, extends
from spack.error import NoHeadersError, NoLibrariesError, SpecError
-from spack.package_base import PackageBase, run_after
from spack.version import Version
+from ._checks import BaseBuilder, execute_install_time_tests
-class PythonPackage(PackageBase):
- """Specialized class for packages that are built using pip."""
-
- #: Package name, version, and extension on PyPI
- pypi = None # type: Optional[str]
-
- maintainers = ["adamjstewart", "pradyunsg"]
-
- # Default phases
- phases = ["install"]
-
- # To be used in UI queries that require to know which
- # build-system class we are using
- build_system_class = "PythonPackage"
-
- #: Callback names for install-time test
- install_time_test_callbacks = ["test"]
-
- extends("python")
- depends_on("py-pip", type="build")
- # FIXME: technically wheel is only needed when building from source, not when
- # installing a downloaded wheel, but I don't want to add wheel as a dep to every
- # package manually
- depends_on("py-wheel", type="build")
-
- py_namespace = None # type: Optional[str]
-
- @staticmethod
- def _std_args(cls):
- return [
- # Verbose
- "-vvv",
- # Disable prompting for input
- "--no-input",
- # Disable the cache
- "--no-cache-dir",
- # Don't check to see if pip is up-to-date
- "--disable-pip-version-check",
- # Install packages
- "install",
- # Don't install package dependencies
- "--no-deps",
- # Overwrite existing packages
- "--ignore-installed",
- # Use env vars like PYTHONPATH
- "--no-build-isolation",
- # Don't warn that prefix.bin is not in PATH
- "--no-warn-script-location",
- # Ignore the PyPI package index
- "--no-index",
- ]
-
- @classproperty
- def homepage(cls):
- if cls.pypi:
- name = cls.pypi.split("/")[0]
- return "https://pypi.org/project/" + name + "/"
-
- @classproperty
- def url(cls):
- if cls.pypi:
- return "https://files.pythonhosted.org/packages/source/" + cls.pypi[0] + "/" + cls.pypi
- @classproperty
- def list_url(cls):
- if cls.pypi:
- name = cls.pypi.split("/")[0]
- return "https://pypi.org/simple/" + name + "/"
+class PythonExtension(spack.package_base.PackageBase):
+ maintainers = ["adamjstewart"]
@property
def import_modules(self):
@@ -124,7 +53,7 @@ class PythonPackage(PackageBase):
# Some Python libraries are packages: collections of modules
# distributed in directories containing __init__.py files
- for path in find(root, "__init__.py", recursive=True):
+ for path in fs.find(root, "__init__.py", recursive=True):
modules.append(
path.replace(root + os.sep, "", 1)
.replace(os.sep + "__init__.py", "")
@@ -133,7 +62,7 @@ class PythonPackage(PackageBase):
# Some Python libraries are modules: individual *.py files
# found in the site-packages directory
- for path in find(root, "*.py", recursive=False):
+ for path in fs.find(root, "*.py", recursive=False):
modules.append(
path.replace(root + os.sep, "", 1).replace(".py", "").replace("/", ".")
)
@@ -160,6 +89,208 @@ class PythonPackage(PackageBase):
"""
return []
+ def view_file_conflicts(self, view, merge_map):
+ """Report all file conflicts, excepting special cases for python.
+ Specifically, this does not report errors for duplicate
+ __init__.py files for packages in the same namespace.
+ """
+ conflicts = list(dst for src, dst in merge_map.items() if os.path.exists(dst))
+
+ if conflicts and self.py_namespace:
+ ext_map = view.extensions_layout.extension_map(self.extendee_spec)
+ namespaces = set(x.package.py_namespace for x in ext_map.values())
+ namespace_re = r"site-packages/{0}/__init__.py".format(self.py_namespace)
+ find_namespace = lang.match_predicate(namespace_re)
+ if self.py_namespace in namespaces:
+ conflicts = list(x for x in conflicts if not find_namespace(x))
+
+ return conflicts
+
+ def add_files_to_view(self, view, merge_map, skip_if_exists=True):
+ bin_dir = self.spec.prefix.bin
+ python_prefix = self.extendee_spec.prefix
+ python_is_external = self.extendee_spec.external
+ global_view = fs.same_path(python_prefix, view.get_projection_for_spec(self.spec))
+ for src, dst in merge_map.items():
+ if os.path.exists(dst):
+ continue
+ elif global_view or not fs.path_contains_subdirectory(src, bin_dir):
+ view.link(src, dst)
+ elif not os.path.islink(src):
+ shutil.copy2(src, dst)
+ is_script = fs.is_nonsymlink_exe_with_shebang(src)
+ if is_script and not python_is_external:
+ fs.filter_file(
+ python_prefix,
+ os.path.abspath(view.get_projection_for_spec(self.spec)),
+ dst,
+ )
+ else:
+ orig_link_target = os.path.realpath(src)
+ new_link_target = os.path.abspath(merge_map[orig_link_target])
+ view.link(new_link_target, dst)
+
+ def remove_files_from_view(self, view, merge_map):
+ ignore_namespace = False
+ if self.py_namespace:
+ ext_map = view.extensions_layout.extension_map(self.extendee_spec)
+ remaining_namespaces = set(
+ spec.package.py_namespace for name, spec in ext_map.items() if name != self.name
+ )
+ if self.py_namespace in remaining_namespaces:
+ namespace_init = lang.match_predicate(
+ r"site-packages/{0}/__init__.py".format(self.py_namespace)
+ )
+ ignore_namespace = True
+
+ bin_dir = self.spec.prefix.bin
+ global_view = self.extendee_spec.prefix == view.get_projection_for_spec(self.spec)
+
+ to_remove = []
+ for src, dst in merge_map.items():
+ if ignore_namespace and namespace_init(dst):
+ continue
+
+ if global_view or not fs.path_contains_subdirectory(src, bin_dir):
+ to_remove.append(dst)
+ else:
+ os.remove(dst)
+
+ view.remove_files(to_remove)
+
+ def test(self):
+ """Attempts to import modules of the installed package."""
+
+ # Make sure we are importing the installed modules,
+ # not the ones in the source directory
+ for module in self.import_modules:
+ self.run_test(
+ inspect.getmodule(self).python.path,
+ ["-c", "import {0}".format(module)],
+ purpose="checking import of {0}".format(module),
+ work_dir="spack-test",
+ )
+
+
+class PythonPackage(PythonExtension):
+ """Specialized class for packages that are built using pip."""
+
+ #: Package name, version, and extension on PyPI
+ pypi = None # type: Optional[str]
+
+ maintainers = ["adamjstewart", "pradyunsg"]
+
+ # To be used in UI queries that require to know which
+ # build-system class we are using
+ build_system_class = "PythonPackage"
+ #: Legacy buildsystem attribute used to deserialize and install old specs
+ legacy_buildsystem = "python_pip"
+
+ #: Callback names for install-time test
+ install_time_test_callbacks = ["test"]
+
+ build_system("python_pip")
+
+ with spack.multimethod.when("build_system=python_pip"):
+ extends("python")
+ depends_on("py-pip", type="build")
+ # FIXME: technically wheel is only needed when building from source, not when
+ # installing a downloaded wheel, but I don't want to add wheel as a dep to every
+ # package manually
+ depends_on("py-wheel", type="build")
+
+ py_namespace = None # type: Optional[str]
+
+ @lang.classproperty
+ def homepage(cls):
+ if cls.pypi:
+ name = cls.pypi.split("/")[0]
+ return "https://pypi.org/project/" + name + "/"
+
+ @lang.classproperty
+ def url(cls):
+ if cls.pypi:
+ return "https://files.pythonhosted.org/packages/source/" + cls.pypi[0] + "/" + cls.pypi
+
+ @lang.classproperty
+ def list_url(cls):
+ if cls.pypi:
+ name = cls.pypi.split("/")[0]
+ return "https://pypi.org/simple/" + name + "/"
+
+ @property
+ def headers(self):
+ """Discover header files in platlib."""
+
+ # Headers may be in either location
+ include = self.prefix.join(self.spec["python"].package.include)
+ platlib = self.prefix.join(self.spec["python"].package.platlib)
+ headers = fs.find_all_headers(include) + fs.find_all_headers(platlib)
+
+ if headers:
+ return headers
+
+ msg = "Unable to locate {} headers in {} or {}"
+ raise NoHeadersError(msg.format(self.spec.name, include, platlib))
+
+ @property
+ def libs(self):
+ """Discover libraries in platlib."""
+
+ # Remove py- prefix in package name
+ library = "lib" + self.spec.name[3:].replace("-", "?")
+ root = self.prefix.join(self.spec["python"].package.platlib)
+
+ for shared in [True, False]:
+ libs = fs.find_libraries(library, root, shared=shared, recursive=True)
+ if libs:
+ return libs
+
+ msg = "Unable to recursively locate {} libraries in {}"
+ raise NoLibrariesError(msg.format(self.spec.name, root))
+
+
+@spack.builder.builder("python_pip")
+class PythonPipBuilder(BaseBuilder):
+ phases = ("install",)
+
+ #: Names associated with package methods in the old build-system format
+ legacy_methods = ("test",)
+
+ #: Same as legacy_methods, but the signature is different
+ legacy_long_methods = ("install_options", "global_options", "config_settings")
+
+ #: Names associated with package attributes in the old build-system format
+ legacy_attributes = ("build_directory", "install_time_test_callbacks")
+
+ #: Callback names for install-time test
+ install_time_test_callbacks = ["test"]
+
+ @staticmethod
+ def std_args(cls):
+ return [
+ # Verbose
+ "-vvv",
+ # Disable prompting for input
+ "--no-input",
+ # Disable the cache
+ "--no-cache-dir",
+ # Don't check to see if pip is up-to-date
+ "--disable-pip-version-check",
+ # Install packages
+ "install",
+ # Don't install package dependencies
+ "--no-deps",
+ # Overwrite existing packages
+ "--ignore-installed",
+ # Use env vars like PYTHONPATH
+ "--no-build-isolation",
+ # Don't warn that prefix.bin is not in PATH
+ "--no-warn-script-location",
+ # Ignore the PyPI package index
+ "--no-index",
+ ]
+
@property
def build_directory(self):
"""The root directory of the Python package.
@@ -170,11 +301,10 @@ class PythonPackage(PackageBase):
* ``setup.cfg``
* ``setup.py``
"""
- return self.stage.source_path
+ return self.pkg.stage.source_path
def config_settings(self, spec, prefix):
"""Configuration settings to be passed to the PEP 517 build backend.
-
Requires pip 22.1+, which requires Python 3.7+.
Args:
@@ -211,10 +341,10 @@ class PythonPackage(PackageBase):
"""
return []
- def install(self, spec, prefix):
+ def install(self, pkg, spec, prefix):
"""Install everything from build directory."""
- args = PythonPackage._std_args(self) + ["--prefix=" + prefix]
+ args = PythonPipBuilder.std_args(pkg) + ["--prefix=" + prefix]
for key, value in self.config_settings(spec, prefix).items():
if spec["py-pip"].version < Version("22.1"):
@@ -223,137 +353,21 @@ class PythonPackage(PackageBase):
"pip 22.1+. Add the following line to the package to fix this:\n\n"
' depends_on("py-pip@22.1:", type="build")'.format(spec.name)
)
+
args.append("--config-settings={}={}".format(key, value))
+
for option in self.install_options(spec, prefix):
args.append("--install-option=" + option)
for option in self.global_options(spec, prefix):
args.append("--global-option=" + option)
- if self.stage.archive_file and self.stage.archive_file.endswith(".whl"):
- args.append(self.stage.archive_file)
+ if pkg.stage.archive_file and pkg.stage.archive_file.endswith(".whl"):
+ args.append(pkg.stage.archive_file)
else:
args.append(".")
- pip = inspect.getmodule(self).pip
- with working_dir(self.build_directory):
+ pip = inspect.getmodule(pkg).pip
+ with fs.working_dir(self.build_directory):
pip(*args)
- @property
- def headers(self):
- """Discover header files in platlib."""
-
- # Headers may be in either location
- include = self.prefix.join(self.spec["python"].package.include)
- platlib = self.prefix.join(self.spec["python"].package.platlib)
- headers = find_all_headers(include) + find_all_headers(platlib)
-
- if headers:
- return headers
-
- msg = "Unable to locate {} headers in {} or {}"
- raise NoHeadersError(msg.format(self.spec.name, include, platlib))
-
- @property
- def libs(self):
- """Discover libraries in platlib."""
-
- # Remove py- prefix in package name
- library = "lib" + self.spec.name[3:].replace("-", "?")
- root = self.prefix.join(self.spec["python"].package.platlib)
-
- for shared in [True, False]:
- libs = find_libraries(library, root, shared=shared, recursive=True)
- if libs:
- return libs
-
- msg = "Unable to recursively locate {} libraries in {}"
- raise NoLibrariesError(msg.format(self.spec.name, root))
-
- # Testing
-
- def test(self):
- """Attempts to import modules of the installed package."""
-
- # Make sure we are importing the installed modules,
- # not the ones in the source directory
- for module in self.import_modules:
- self.run_test(
- inspect.getmodule(self).python.path,
- ["-c", "import {0}".format(module)],
- purpose="checking import of {0}".format(module),
- work_dir="spack-test",
- )
-
- run_after("install")(PackageBase._run_default_install_time_test_callbacks)
-
- # Check that self.prefix is there after installation
- run_after("install")(PackageBase.sanity_check_prefix)
-
- def view_file_conflicts(self, view, merge_map):
- """Report all file conflicts, excepting special cases for python.
- Specifically, this does not report errors for duplicate
- __init__.py files for packages in the same namespace.
- """
- conflicts = list(dst for src, dst in merge_map.items() if os.path.exists(dst))
-
- if conflicts and self.py_namespace:
- ext_map = view.extensions_layout.extension_map(self.extendee_spec)
- namespaces = set(x.package.py_namespace for x in ext_map.values())
- namespace_re = r"site-packages/{0}/__init__.py".format(self.py_namespace)
- find_namespace = match_predicate(namespace_re)
- if self.py_namespace in namespaces:
- conflicts = list(x for x in conflicts if not find_namespace(x))
-
- return conflicts
-
- def add_files_to_view(self, view, merge_map, skip_if_exists=True):
- bin_dir = self.spec.prefix.bin
- python_prefix = self.extendee_spec.prefix
- python_is_external = self.extendee_spec.external
- global_view = same_path(python_prefix, view.get_projection_for_spec(self.spec))
- for src, dst in merge_map.items():
- if os.path.exists(dst):
- continue
- elif global_view or not path_contains_subdirectory(src, bin_dir):
- view.link(src, dst)
- elif not os.path.islink(src):
- shutil.copy2(src, dst)
- is_script = is_nonsymlink_exe_with_shebang(src)
- if is_script and not python_is_external:
- filter_file(
- python_prefix,
- os.path.abspath(view.get_projection_for_spec(self.spec)),
- dst,
- )
- else:
- orig_link_target = os.path.realpath(src)
- new_link_target = os.path.abspath(merge_map[orig_link_target])
- view.link(new_link_target, dst)
-
- def remove_files_from_view(self, view, merge_map):
- ignore_namespace = False
- if self.py_namespace:
- ext_map = view.extensions_layout.extension_map(self.extendee_spec)
- remaining_namespaces = set(
- spec.package.py_namespace for name, spec in ext_map.items() if name != self.name
- )
- if self.py_namespace in remaining_namespaces:
- namespace_init = match_predicate(
- r"site-packages/{0}/__init__.py".format(self.py_namespace)
- )
- ignore_namespace = True
-
- bin_dir = self.spec.prefix.bin
- global_view = self.extendee_spec.prefix == view.get_projection_for_spec(self.spec)
-
- to_remove = []
- for src, dst in merge_map.items():
- if ignore_namespace and namespace_init(dst):
- continue
-
- if global_view or not path_contains_subdirectory(src, bin_dir):
- to_remove.append(dst)
- else:
- os.remove(dst)
-
- view.remove_files(to_remove)
+ spack.builder.run_after("install")(execute_install_time_tests)
diff --git a/lib/spack/spack/build_systems/qmake.py b/lib/spack/spack/build_systems/qmake.py
index c2af684592..f18bd9812f 100644
--- a/lib/spack/spack/build_systems/qmake.py
+++ b/lib/spack/spack/build_systems/qmake.py
@@ -2,82 +2,85 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
-
-
import inspect
from llnl.util.filesystem import working_dir
-from spack.directives import depends_on
-from spack.package_base import PackageBase, run_after
+import spack.builder
+import spack.package_base
+from spack.directives import build_system, depends_on
+
+from ._checks import BaseBuilder, execute_build_time_tests
-class QMakePackage(PackageBase):
+class QMakePackage(spack.package_base.PackageBase):
"""Specialized class for packages built using qmake.
For more information on the qmake build system, see:
http://doc.qt.io/qt-5/qmake-manual.html
+ """
+
+ #: This attribute is used in UI queries that need to know the build
+ #: system base class
+ build_system_class = "QMakePackage"
+ #: Legacy buildsystem attribute used to deserialize and install old specs
+ legacy_buildsystem = "qmake"
+
+ build_system("qmake")
+
+ depends_on("qt", type="build", when="build_system=qmake")
+
- This class provides three phases that can be overridden:
+@spack.builder.builder("qmake")
+class QMakeBuilder(BaseBuilder):
+ """The qmake builder provides three phases that can be overridden:
- 1. :py:meth:`~.QMakePackage.qmake`
- 2. :py:meth:`~.QMakePackage.build`
- 3. :py:meth:`~.QMakePackage.install`
+ 1. :py:meth:`~.QMakeBuilder.qmake`
+ 2. :py:meth:`~.QMakeBuilder.build`
+ 3. :py:meth:`~.QMakeBuilder.install`
They all have sensible defaults and for many packages the only thing
- necessary will be to override :py:meth:`~.QMakePackage.qmake_args`.
+ necessary will be to override :py:meth:`~.QMakeBuilder.qmake_args`.
"""
- #: Phases of a qmake package
- phases = ["qmake", "build", "install"]
+ phases = ("qmake", "build", "install")
- #: This attribute is used in UI queries that need to know the build
- #: system base class
- build_system_class = "QMakePackage"
+ #: Names associated with package methods in the old build-system format
+ legacy_methods = ("qmake_args", "check")
+
+ #: Names associated with package attributes in the old build-system format
+ legacy_attributes = ("build_directory", "build_time_test_callbacks")
#: Callback names for build-time test
build_time_test_callbacks = ["check"]
- depends_on("qt", type="build")
-
@property
def build_directory(self):
"""The directory containing the ``*.pro`` file."""
return self.stage.source_path
def qmake_args(self):
- """Produces a list containing all the arguments that must be passed to
- qmake
- """
+ """List of arguments passed to qmake."""
return []
- def qmake(self, spec, prefix):
+ def qmake(self, pkg, spec, prefix):
"""Run ``qmake`` to configure the project and generate a Makefile."""
-
with working_dir(self.build_directory):
- inspect.getmodule(self).qmake(*self.qmake_args())
+ inspect.getmodule(self.pkg).qmake(*self.qmake_args())
- def build(self, spec, prefix):
+ def build(self, pkg, spec, prefix):
"""Make the build targets"""
-
with working_dir(self.build_directory):
- inspect.getmodule(self).make()
+ inspect.getmodule(self.pkg).make()
- def install(self, spec, prefix):
+ def install(self, pkg, spec, prefix):
"""Make the install targets"""
-
with working_dir(self.build_directory):
- inspect.getmodule(self).make("install")
-
- # Tests
+ inspect.getmodule(self.pkg).make("install")
def check(self):
- """Searches the Makefile for a ``check:`` target and runs it if found."""
-
+ """Search the Makefile for a ``check:`` target and runs it if found."""
with working_dir(self.build_directory):
self._if_make_target_execute("check")
- run_after("build")(PackageBase._run_default_build_time_test_callbacks)
-
- # Check that self.prefix is there after installation
- run_after("install")(PackageBase.sanity_check_prefix)
+ spack.builder.run_after("build")(execute_build_time_tests)
diff --git a/lib/spack/spack/build_systems/r.py b/lib/spack/spack/build_systems/r.py
index 450cae733b..c62baa3555 100644
--- a/lib/spack/spack/build_systems/r.py
+++ b/lib/spack/spack/build_systems/r.py
@@ -3,29 +3,63 @@
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import inspect
-from typing import Optional
+from typing import Optional, Tuple
import llnl.util.lang as lang
from spack.directives import extends
-from spack.package_base import PackageBase, run_after
+from .generic import GenericBuilder, Package
-class RPackage(PackageBase):
- """Specialized class for packages that are built using R.
-
- For more information on the R build system, see:
- https://stat.ethz.ch/R-manual/R-devel/library/utils/html/INSTALL.html
- This class provides a single phase that can be overridden:
+class RBuilder(GenericBuilder):
+ """The R builder provides a single phase that can be overridden:
- 1. :py:meth:`~.RPackage.install`
+ 1. :py:meth:`~.RBuilder.install`
It has sensible defaults, and for many packages the only thing
- necessary will be to add dependencies
+ necessary will be to add dependencies.
"""
- phases = ["install"]
+ #: Names associated with package methods in the old build-system format
+ legacy_methods = (
+ "configure_args",
+ "configure_vars",
+ ) + GenericBuilder.legacy_methods # type: Tuple[str, ...]
+
+ def configure_args(self):
+ """Arguments to pass to install via ``--configure-args``."""
+ return []
+
+ def configure_vars(self):
+ """Arguments to pass to install via ``--configure-vars``."""
+ return []
+
+ def install(self, pkg, spec, prefix):
+ """Installs an R package."""
+
+ config_args = self.configure_args()
+ config_vars = self.configure_vars()
+
+ args = ["--vanilla", "CMD", "INSTALL"]
+
+ if config_args:
+ args.append("--configure-args={0}".format(" ".join(config_args)))
+
+ if config_vars:
+ args.append("--configure-vars={0}".format(" ".join(config_vars)))
+
+ args.extend(["--library={0}".format(self.pkg.module.r_lib_dir), self.stage.source_path])
+
+ inspect.getmodule(self.pkg).R(*args)
+
+
+class RPackage(Package):
+ """Specialized class for packages that are built using R.
+
+ For more information on the R build system, see:
+ https://stat.ethz.ch/R-manual/R-devel/library/utils/html/INSTALL.html
+ """
# package attributes that can be expanded to set the homepage, url,
# list_url, and git values
@@ -35,6 +69,8 @@ class RPackage(PackageBase):
# For Bioconductor packages
bioc = None # type: Optional[str]
+ GenericBuilder = RBuilder
+
maintainers = ["glennpj"]
#: This attribute is used in UI queries that need to know the build
@@ -70,32 +106,3 @@ class RPackage(PackageBase):
def git(self):
if self.bioc:
return "https://git.bioconductor.org/packages/" + self.bioc
-
- def configure_args(self):
- """Arguments to pass to install via ``--configure-args``."""
- return []
-
- def configure_vars(self):
- """Arguments to pass to install via ``--configure-vars``."""
- return []
-
- def install(self, spec, prefix):
- """Installs an R package."""
-
- config_args = self.configure_args()
- config_vars = self.configure_vars()
-
- args = ["--vanilla", "CMD", "INSTALL"]
-
- if config_args:
- args.append("--configure-args={0}".format(" ".join(config_args)))
-
- if config_vars:
- args.append("--configure-vars={0}".format(" ".join(config_vars)))
-
- args.extend(["--library={0}".format(self.module.r_lib_dir), self.stage.source_path])
-
- inspect.getmodule(self).R(*args)
-
- # Check that self.prefix is there after installation
- run_after("install")(PackageBase.sanity_check_prefix)
diff --git a/lib/spack/spack/build_systems/racket.py b/lib/spack/spack/build_systems/racket.py
index 7b37d85cf2..889cc07931 100644
--- a/lib/spack/spack/build_systems/racket.py
+++ b/lib/spack/spack/build_systems/racket.py
@@ -3,14 +3,15 @@
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import os
-from typing import Optional
+from typing import Optional, Tuple
+import llnl.util.filesystem as fs
import llnl.util.lang as lang
import llnl.util.tty as tty
-from llnl.util.filesystem import working_dir
+import spack.builder
from spack.build_environment import SPACK_NO_PARALLEL_MAKE, determine_number_of_jobs
-from spack.directives import extends
+from spack.directives import build_system, extends
from spack.package_base import PackageBase
from spack.util.environment import env_flag
from spack.util.executable import Executable, ProcessError
@@ -19,34 +20,52 @@ from spack.util.executable import Executable, ProcessError
class RacketPackage(PackageBase):
"""Specialized class for packages that are built using Racket's
`raco pkg install` and `raco setup` commands.
-
- This class provides the following phases that can be overridden:
-
- * install
- * setup
"""
#: Package name, version, and extension on PyPI
maintainers = ["elfprince13"]
-
- # Default phases
- phases = ["install"]
-
# To be used in UI queries that require to know which
# build-system class we are using
build_system_class = "RacketPackage"
+ #: Legacy buildsystem attribute used to deserialize and install old specs
+ legacy_buildsystem = "racket"
- extends("racket")
+ build_system("racket")
+
+ extends("racket", when="build_system=racket")
- pkgs = False
- subdirectory = None # type: Optional[str]
racket_name = None # type: Optional[str]
parallel = True
@lang.classproperty
def homepage(cls):
- if cls.pkgs:
+ if cls.racket_name:
return "https://pkgs.racket-lang.org/package/{0}".format(cls.racket_name)
+ return None
+
+
+@spack.builder.builder("racket")
+class RacketBuilder(spack.builder.Builder):
+ """The Racket builder provides an ``install`` phase that can be overridden."""
+
+ phases = ("install",)
+
+ #: Names associated with package methods in the old build-system format
+ legacy_methods = tuple() # type: Tuple[str, ...]
+
+ #: Names associated with package attributes in the old build-system format
+ legacy_attributes = ("build_directory", "build_time_test_callbacks", "subdirectory")
+
+ #: Callback names for build-time test
+ build_time_test_callbacks = ["check"]
+
+ racket_name = None # type: Optional[str]
+
+ @property
+ def subdirectory(self):
+ if self.racket_name:
+ return "pkgs/{0}".format(self.pkg.racket_name)
+ return None
@property
def build_directory(self):
@@ -55,25 +74,25 @@ class RacketPackage(PackageBase):
ret = os.path.join(ret, self.subdirectory)
return ret
- def install(self, spec, prefix):
+ def install(self, pkg, spec, prefix):
"""Install everything from build directory."""
raco = Executable("raco")
- with working_dir(self.build_directory):
- allow_parallel = self.parallel and (not env_flag(SPACK_NO_PARALLEL_MAKE))
+ with fs.working_dir(self.build_directory):
+ parallel = self.pkg.parallel and (not env_flag(SPACK_NO_PARALLEL_MAKE))
args = [
"pkg",
"install",
"-t",
"dir",
"-n",
- self.racket_name,
+ self.pkg.racket_name,
"--deps",
"fail",
"--ignore-implies",
"--copy",
"-i",
"-j",
- str(determine_number_of_jobs(allow_parallel)),
+ str(determine_number_of_jobs(parallel)),
"--",
os.getcwd(),
]
@@ -82,9 +101,8 @@ class RacketPackage(PackageBase):
except ProcessError:
args.insert(-2, "--skip-installed")
raco(*args)
- tty.warn(
- (
- "Racket package {0} was already installed, uninstalling via "
- "Spack may make someone unhappy!"
- ).format(self.racket_name)
+ msg = (
+ "Racket package {0} was already installed, uninstalling via "
+ "Spack may make someone unhappy!"
)
+ tty.warn(msg.format(self.pkg.racket_name))
diff --git a/lib/spack/spack/build_systems/ruby.py b/lib/spack/spack/build_systems/ruby.py
index fcc071f19e..ef29f164ab 100644
--- a/lib/spack/spack/build_systems/ruby.py
+++ b/lib/spack/spack/build_systems/ruby.py
@@ -2,35 +2,49 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
-
import glob
import inspect
-from spack.directives import extends
-from spack.package_base import PackageBase, run_after
-
+import spack.builder
+import spack.package_base
+from spack.directives import build_system, extends
-class RubyPackage(PackageBase):
- """Specialized class for building Ruby gems.
+from ._checks import BaseBuilder
- This class provides two phases that can be overridden if required:
- #. :py:meth:`~.RubyPackage.build`
- #. :py:meth:`~.RubyPackage.install`
- """
+class RubyPackage(spack.package_base.PackageBase):
+ """Specialized class for building Ruby gems."""
maintainers = ["Kerilk"]
- #: Phases of a Ruby package
- phases = ["build", "install"]
-
#: This attribute is used in UI queries that need to know the build
#: system base class
build_system_class = "RubyPackage"
+ #: Legacy buildsystem attribute used to deserialize and install old specs
+ legacy_buildsystem = "ruby"
+
+ build_system("ruby")
+
+ extends("ruby", when="build_system=ruby")
+
- extends("ruby")
+@spack.builder.builder("ruby")
+class RubyBuilder(BaseBuilder):
+ """The Ruby builder provides two phases that can be overridden if required:
- def build(self, spec, prefix):
+ #. :py:meth:`~.RubyBuilder.build`
+ #. :py:meth:`~.RubyBuilder.install`
+ """
+
+ phases = ("build", "install")
+
+ #: Names associated with package methods in the old build-system format
+ legacy_methods = ()
+
+ #: Names associated with package attributes in the old build-system format
+ legacy_attributes = ()
+
+ def build(self, pkg, spec, prefix):
"""Build a Ruby gem."""
# ruby-rake provides both rake.gemspec and Rakefile, but only
@@ -38,15 +52,15 @@ class RubyPackage(PackageBase):
gemspecs = glob.glob("*.gemspec")
rakefiles = glob.glob("Rakefile")
if gemspecs:
- inspect.getmodule(self).gem("build", "--norc", gemspecs[0])
+ inspect.getmodule(self.pkg).gem("build", "--norc", gemspecs[0])
elif rakefiles:
- jobs = inspect.getmodule(self).make_jobs
- inspect.getmodule(self).rake("package", "-j{0}".format(jobs))
+ jobs = inspect.getmodule(self.pkg).make_jobs
+ inspect.getmodule(self.pkg).rake("package", "-j{0}".format(jobs))
else:
# Some Ruby packages only ship `*.gem` files, so nothing to build
pass
- def install(self, spec, prefix):
+ def install(self, pkg, spec, prefix):
"""Install a Ruby gem.
The ruby package sets ``GEM_HOME`` to tell gem where to install to."""
@@ -56,9 +70,6 @@ class RubyPackage(PackageBase):
# if --install-dir is not used, GEM_PATH is deleted from the
# environement, and Gems required to build native extensions will
# not be found. Those extensions are built during `gem install`.
- inspect.getmodule(self).gem(
+ inspect.getmodule(self.pkg).gem(
"install", "--norc", "--ignore-dependencies", "--install-dir", prefix, gems[0]
)
-
- # Check that self.prefix is there after installation
- run_after("install")(PackageBase.sanity_check_prefix)
diff --git a/lib/spack/spack/build_systems/scons.py b/lib/spack/spack/build_systems/scons.py
index 5470091121..f1ae955917 100644
--- a/lib/spack/spack/build_systems/scons.py
+++ b/lib/spack/spack/build_systems/scons.py
@@ -2,63 +2,75 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
-
-
import inspect
-from spack.directives import depends_on
-from spack.package_base import PackageBase, run_after
+import spack.builder
+import spack.package_base
+from spack.directives import build_system, depends_on
+
+from ._checks import BaseBuilder, execute_build_time_tests
-class SConsPackage(PackageBase):
+class SConsPackage(spack.package_base.PackageBase):
"""Specialized class for packages built using SCons.
See http://scons.org/documentation.html for more information.
-
- This class provides the following phases that can be overridden:
-
- 1. :py:meth:`~.SConsPackage.build`
- 2. :py:meth:`~.SConsPackage.install`
-
- Packages that use SCons as a build system are less uniform than packages
- that use other build systems. Developers can add custom subcommands or
- variables that control the build. You will likely need to override
- :py:meth:`~.SConsPackage.build_args` to pass the appropriate variables.
"""
- #: Phases of a SCons package
- phases = ["build", "install"]
-
#: To be used in UI queries that require to know which
#: build-system class we are using
build_system_class = "SConsPackage"
#: Callback names for build-time test
build_time_test_callbacks = ["build_test"]
+ #: Legacy buildsystem attribute used to deserialize and install old specs
+ legacy_buildsystem = "scons"
+
+ build_system("scons")
+
+ depends_on("scons", type="build", when="build_system=scons")
+
- depends_on("scons", type="build")
+@spack.builder.builder("scons")
+class SConsBuilder(BaseBuilder):
+ """The Scons builder provides the following phases that can be overridden:
- def build_args(self, spec, prefix):
+ 1. :py:meth:`~.SConsBuilder.build`
+ 2. :py:meth:`~.SConsBuilder.install`
+
+ Packages that use SCons as a build system are less uniform than packages that use
+ other build systems. Developers can add custom subcommands or variables that
+ control the build. You will likely need to override
+ :py:meth:`~.SConsBuilder.build_args` to pass the appropriate variables.
+ """
+
+ #: Phases of a SCons package
+ phases = ("build", "install")
+
+ #: Names associated with package methods in the old build-system format
+ legacy_methods = ("build_args", "install_args", "build_test")
+
+ #: Names associated with package attributes in the old build-system format
+ legacy_attributes = ()
+
+ def build_args(self):
"""Arguments to pass to build."""
return []
- def build(self, spec, prefix):
+ def build(self, pkg, spec, prefix):
"""Build the package."""
- args = self.build_args(spec, prefix)
-
- inspect.getmodule(self).scons(*args)
+ args = self.build_args()
+ inspect.getmodule(self.pkg).scons(*args)
- def install_args(self, spec, prefix):
+ def install_args(self):
"""Arguments to pass to install."""
return []
- def install(self, spec, prefix):
+ def install(self, pkg, spec, prefix):
"""Install the package."""
- args = self.install_args(spec, prefix)
+ args = self.install_args()
- inspect.getmodule(self).scons("install", *args)
-
- # Testing
+ inspect.getmodule(self.pkg).scons("install", *args)
def build_test(self):
"""Run unit tests after build.
@@ -68,7 +80,4 @@ class SConsPackage(PackageBase):
"""
pass
- run_after("build")(PackageBase._run_default_build_time_test_callbacks)
-
- # Check that self.prefix is there after installation
- run_after("install")(PackageBase.sanity_check_prefix)
+ spack.builder.run_after("build")(execute_build_time_tests)
diff --git a/lib/spack/spack/build_systems/sip.py b/lib/spack/spack/build_systems/sip.py
index 4d16f6731e..b129ca4e0a 100644
--- a/lib/spack/spack/build_systems/sip.py
+++ b/lib/spack/spack/build_systems/sip.py
@@ -2,7 +2,6 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
-
import inspect
import os
import re
@@ -10,28 +9,20 @@ import re
import llnl.util.tty as tty
from llnl.util.filesystem import find, join_path, working_dir
-from spack.directives import depends_on, extends
-from spack.package_base import PackageBase, run_after
+import spack.builder
+import spack.package_base
+from spack.directives import build_system, depends_on, extends
+from spack.multimethod import when
+
+from ._checks import BaseBuilder, execute_install_time_tests
-class SIPPackage(PackageBase):
+class SIPPackage(spack.package_base.PackageBase):
"""Specialized class for packages that are built using the
SIP build system. See https://www.riverbankcomputing.com/software/sip/intro
for more information.
-
- This class provides the following phases that can be overridden:
-
- * configure
- * build
- * install
-
- The configure phase already adds a set of default flags. To see more
- options, run ``python configure.py --help``.
"""
- # Default phases
- phases = ["configure", "build", "install"]
-
# To be used in UI queries that require to know which
# build-system class we are using
build_system_class = "SIPPackage"
@@ -41,11 +32,15 @@ class SIPPackage(PackageBase):
#: Callback names for install-time test
install_time_test_callbacks = ["test"]
+ #: Legacy buildsystem attribute used to deserialize and install old specs
+ legacy_buildsystem = "sip"
- extends("python")
+ build_system("sip")
- depends_on("qt")
- depends_on("py-sip")
+ with when("build_system=sip"):
+ extends("python")
+ depends_on("qt")
+ depends_on("py-sip")
@property
def import_modules(self):
@@ -95,11 +90,51 @@ class SIPPackage(PackageBase):
"""The python ``Executable``."""
inspect.getmodule(self).python(*args, **kwargs)
+ def test(self):
+ """Attempts to import modules of the installed package."""
+
+ # Make sure we are importing the installed modules,
+ # not the ones in the source directory
+ for module in self.import_modules:
+ self.run_test(
+ inspect.getmodule(self).python.path,
+ ["-c", "import {0}".format(module)],
+ purpose="checking import of {0}".format(module),
+ work_dir="spack-test",
+ )
+
+
+@spack.builder.builder("sip")
+class SIPBuilder(BaseBuilder):
+ """The SIP builder provides the following phases that can be overridden:
+
+ * configure
+ * build
+ * install
+
+ The configure phase already adds a set of default flags. To see more
+ options, run ``python configure.py --help``.
+ """
+
+ phases = ("configure", "build", "install")
+
+ #: Names associated with package methods in the old build-system format
+ legacy_methods = ("configure_file", "configure_args", "build_args", "install_args")
+
+ #: Names associated with package attributes in the old build-system format
+ legacy_attributes = (
+ "build_targets",
+ "install_targets",
+ "build_time_test_callbacks",
+ "install_time_test_callbacks",
+ "build_directory",
+ )
+
def configure_file(self):
"""Returns the name of the configure file to use."""
return "configure.py"
- def configure(self, spec, prefix):
+ def configure(self, pkg, spec, prefix):
"""Configure the package."""
configure = self.configure_file()
@@ -118,7 +153,7 @@ class SIPPackage(PackageBase):
"--bindir",
prefix.bin,
"--destdir",
- inspect.getmodule(self).python_platlib,
+ inspect.getmodule(self.pkg).python_platlib,
]
)
@@ -128,53 +163,35 @@ class SIPPackage(PackageBase):
"""Arguments to pass to configure."""
return []
- def build(self, spec, prefix):
+ def build(self, pkg, spec, prefix):
"""Build the package."""
args = self.build_args()
- inspect.getmodule(self).make(*args)
+ inspect.getmodule(self.pkg).make(*args)
def build_args(self):
"""Arguments to pass to build."""
return []
- def install(self, spec, prefix):
+ def install(self, pkg, spec, prefix):
"""Install the package."""
args = self.install_args()
- inspect.getmodule(self).make("install", parallel=False, *args)
+ inspect.getmodule(self.pkg).make("install", parallel=False, *args)
def install_args(self):
"""Arguments to pass to install."""
return []
- # Testing
-
- def test(self):
- """Attempts to import modules of the installed package."""
-
- # Make sure we are importing the installed modules,
- # not the ones in the source directory
- for module in self.import_modules:
- self.run_test(
- inspect.getmodule(self).python.path,
- ["-c", "import {0}".format(module)],
- purpose="checking import of {0}".format(module),
- work_dir="spack-test",
- )
-
- run_after("install")(PackageBase._run_default_install_time_test_callbacks)
-
- # Check that self.prefix is there after installation
- run_after("install")(PackageBase.sanity_check_prefix)
+ spack.builder.run_after("install")(execute_install_time_tests)
- @run_after("install")
+ @spack.builder.run_after("install")
def extend_path_setup(self):
# See github issue #14121 and PR #15297
- module = self.spec["py-sip"].variants["module"].value
+ module = self.pkg.spec["py-sip"].variants["module"].value
if module != "sip":
module = module.split(".")[0]
- with working_dir(inspect.getmodule(self).python_platlib):
+ with working_dir(inspect.getmodule(self.pkg).python_platlib):
with open(os.path.join(module, "__init__.py"), "a") as f:
f.write("from pkgutil import extend_path\n")
f.write("__path__ = extend_path(__path__, __name__)\n")
diff --git a/lib/spack/spack/build_systems/waf.py b/lib/spack/spack/build_systems/waf.py
index 3571ffd525..f972d4e286 100644
--- a/lib/spack/spack/build_systems/waf.py
+++ b/lib/spack/spack/build_systems/waf.py
@@ -2,21 +2,38 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
-
-
import inspect
from llnl.util.filesystem import working_dir
-from spack.directives import depends_on
-from spack.package_base import PackageBase, run_after
+import spack.builder
+import spack.package_base
+from spack.directives import build_system, depends_on
+
+from ._checks import BaseBuilder, execute_build_time_tests, execute_install_time_tests
-class WafPackage(PackageBase):
+class WafPackage(spack.package_base.PackageBase):
"""Specialized class for packages that are built using the
Waf build system. See https://waf.io/book/ for more information.
+ """
+
+ # To be used in UI queries that require to know which
+ # build-system class we are using
+ build_system_class = "WafPackage"
+ #: Legacy buildsystem attribute used to deserialize and install old specs
+ legacy_buildsystem = "waf"
+
+ build_system("waf")
+ # Much like AutotoolsPackage does not require automake and autoconf
+ # to build, WafPackage does not require waf to build. It only requires
+ # python to run the waf build script.
+ depends_on("python@2.5:", type="build", when="build_system=waf")
- This class provides the following phases that can be overridden:
+
+@spack.builder.builder("waf")
+class WafBuilder(BaseBuilder):
+ """The WAF builder provides the following phases that can be overridden:
* configure
* build
@@ -40,12 +57,25 @@ class WafPackage(PackageBase):
function, which passes ``--prefix=/path/to/installation/prefix``.
"""
- # Default phases
- phases = ["configure", "build", "install"]
-
- # To be used in UI queries that require to know which
- # build-system class we are using
- build_system_class = "WafPackage"
+ phases = ("configure", "build", "install")
+
+ #: Names associated with package methods in the old build-system format
+ legacy_methods = (
+ "build_test",
+ "install_test",
+ "configure_args",
+ "build_args",
+ "install_args",
+ "build_test",
+ "install_test",
+ )
+
+ #: Names associated with package attributes in the old build-system format
+ legacy_attributes = (
+ "build_time_test_callbacks",
+ "build_time_test_callbacks",
+ "build_directory",
+ )
# Callback names for build-time test
build_time_test_callbacks = ["build_test"]
@@ -53,11 +83,6 @@ class WafPackage(PackageBase):
# Callback names for install-time test
install_time_test_callbacks = ["install_test"]
- # Much like AutotoolsPackage does not require automake and autoconf
- # to build, WafPackage does not require waf to build. It only requires
- # python to run the waf build script.
- depends_on("python@2.5:", type="build")
-
@property
def build_directory(self):
"""The directory containing the ``waf`` file."""
@@ -65,18 +90,18 @@ class WafPackage(PackageBase):
def python(self, *args, **kwargs):
"""The python ``Executable``."""
- inspect.getmodule(self).python(*args, **kwargs)
+ inspect.getmodule(self.pkg).python(*args, **kwargs)
def waf(self, *args, **kwargs):
"""Runs the waf ``Executable``."""
- jobs = inspect.getmodule(self).make_jobs
+ jobs = inspect.getmodule(self.pkg).make_jobs
with working_dir(self.build_directory):
self.python("waf", "-j{0}".format(jobs), *args, **kwargs)
- def configure(self, spec, prefix):
+ def configure(self, pkg, spec, prefix):
"""Configures the project."""
- args = ["--prefix={0}".format(self.prefix)]
+ args = ["--prefix={0}".format(self.pkg.prefix)]
args += self.configure_args()
self.waf("configure", *args)
@@ -85,7 +110,7 @@ class WafPackage(PackageBase):
"""Arguments to pass to configure."""
return []
- def build(self, spec, prefix):
+ def build(self, pkg, spec, prefix):
"""Executes the build."""
args = self.build_args()
@@ -95,7 +120,7 @@ class WafPackage(PackageBase):
"""Arguments to pass to build."""
return []
- def install(self, spec, prefix):
+ def install(self, pkg, spec, prefix):
"""Installs the targets on the system."""
args = self.install_args()
@@ -105,8 +130,6 @@ class WafPackage(PackageBase):
"""Arguments to pass to install."""
return []
- # Testing
-
def build_test(self):
"""Run unit tests after build.
@@ -115,7 +138,7 @@ class WafPackage(PackageBase):
"""
pass
- run_after("build")(PackageBase._run_default_build_time_test_callbacks)
+ spack.builder.run_after("build")(execute_build_time_tests)
def install_test(self):
"""Run unit tests after install.
@@ -125,7 +148,4 @@ class WafPackage(PackageBase):
"""
pass
- run_after("install")(PackageBase._run_default_install_time_test_callbacks)
-
- # Check that self.prefix is there after installation
- run_after("install")(PackageBase.sanity_check_prefix)
+ spack.builder.run_after("install")(execute_install_time_tests)
diff --git a/lib/spack/spack/builder.py b/lib/spack/spack/builder.py
new file mode 100644
index 0000000000..6baa321eea
--- /dev/null
+++ b/lib/spack/spack/builder.py
@@ -0,0 +1,574 @@
+# Copyright 2013-2022 Lawrence Livermore National Security, LLC and other
+# Spack Project Developers. See the top-level COPYRIGHT file for details.
+#
+# SPDX-License-Identifier: (Apache-2.0 OR MIT)
+import collections
+import copy
+import functools
+import inspect
+from typing import List, Optional, Tuple
+
+import six
+
+import llnl.util.compat
+
+import spack.build_environment
+
+#: Builder classes, as registered by the "builder" decorator
+BUILDER_CLS = {}
+
+#: An object of this kind is a shared global state used to collect callbacks during
+#: class definition time, and is flushed when the class object is created at the end
+#: of the class definition
+#:
+#: Args:
+#: attribute_name (str): name of the attribute that will be attached to the builder
+#: callbacks (list): container used to temporarily aggregate the callbacks
+CallbackTemporaryStage = collections.namedtuple(
+ "CallbackTemporaryStage", ["attribute_name", "callbacks"]
+)
+
+#: Shared global state to aggregate "@run_before" callbacks
+_RUN_BEFORE = CallbackTemporaryStage(attribute_name="run_before_callbacks", callbacks=[])
+#: Shared global state to aggregate "@run_after" callbacks
+_RUN_AFTER = CallbackTemporaryStage(attribute_name="run_after_callbacks", callbacks=[])
+
+#: Map id(pkg) to a builder, to avoid creating multiple
+#: builders for the same package object.
+_BUILDERS = {}
+
+
+def builder(build_system_name):
+ """Class decorator used to register the default builder
+ for a given build-system.
+
+ Args:
+ build_system_name (str): name of the build-system
+ """
+
+ def _decorator(cls):
+ cls.build_system = build_system_name
+ BUILDER_CLS[build_system_name] = cls
+ return cls
+
+ return _decorator
+
+
+def create(pkg):
+ """Given a package object with an associated concrete spec,
+ return the builder object that can install it.
+
+ Args:
+ pkg (spack.package_base.PackageBase): package for which we want the builder
+ """
+ if id(pkg) not in _BUILDERS:
+ _BUILDERS[id(pkg)] = _create(pkg)
+ return _BUILDERS[id(pkg)]
+
+
+class _PhaseAdapter(object):
+ def __init__(self, builder, phase_fn):
+ self.builder = builder
+ self.phase_fn = phase_fn
+
+ def __call__(self, spec, prefix):
+ return self.phase_fn(self.builder.pkg, spec, prefix)
+
+
+def _create(pkg):
+ """Return a new builder object for the package object being passed as argument.
+
+ The function inspects the build-system used by the package object and try to:
+
+ 1. Return a custom builder, if any is defined in the same ``package.py`` file.
+ 2. Return a customization of more generic builders, if any is defined in the
+ class hierarchy (look at AspellDictPackage for an example of that)
+ 3. Return a run-time generated adapter builder otherwise
+
+ The run-time generated adapter builder is capable of adapting an old-style package
+ to the new architecture, where the installation procedure has been extracted from
+ the ``*Package`` hierarchy into a ``*Builder`` hierarchy. This means that the
+ adapter looks for attribute or method overrides preferably in the ``*Package``
+ before using the default builder implementation.
+
+ Note that in case a builder is explicitly coded in ``package.py``, no attempt is made
+ to look for build-related methods in the ``*Package``.
+
+ Args:
+ pkg (spack.package_base.PackageBase): package object for which we need a builder
+ """
+ package_module = inspect.getmodule(pkg)
+ package_buildsystem = buildsystem_name(pkg)
+ default_builder_cls = BUILDER_CLS[package_buildsystem]
+ builder_cls_name = default_builder_cls.__name__
+ builder_cls = getattr(package_module, builder_cls_name, None)
+ if builder_cls:
+ return builder_cls(pkg)
+
+ # Specialized version of a given buildsystem can subclass some
+ # base classes and specialize certain phases or methods or attributes.
+ # In that case they can store their builder class as a class level attribute.
+ # See e.g. AspellDictPackage as an example.
+ base_cls = getattr(pkg, builder_cls_name, default_builder_cls)
+
+ # From here on we define classes to construct a special builder that adapts to the
+ # old, single class, package format. The adapter forwards any call or access to an
+ # attribute related to the installation procedure to a package object wrapped in
+ # a class that falls-back on calling the base builder if no override is found on the
+ # package. The semantic should be the same as the method in the base builder were still
+ # present in the base class of the package.
+
+ class _ForwardToBaseBuilder(object):
+ def __init__(self, wrapped_pkg_object, root_builder):
+ self.wrapped_package_object = wrapped_pkg_object
+ self.root_builder = root_builder
+
+ package_cls = type(wrapped_pkg_object)
+ wrapper_cls = type(self)
+ bases = (package_cls, wrapper_cls)
+ new_cls_name = package_cls.__name__ + "Wrapper"
+ new_cls = type(new_cls_name, bases, {})
+ new_cls.__module__ = package_cls.__module__
+ self.__class__ = new_cls
+ self.__dict__.update(wrapped_pkg_object.__dict__)
+
+ def __getattr__(self, item):
+ result = getattr(super(type(self.root_builder), self.root_builder), item)
+ if item in super(type(self.root_builder), self.root_builder).phases:
+ result = _PhaseAdapter(self.root_builder, result)
+ return result
+
+ def forward_method_to_getattr(fn_name):
+ def __forward(self, *args, **kwargs):
+ return self.__getattr__(fn_name)(*args, **kwargs)
+
+ return __forward
+
+ # Add fallback methods for the Package object to refer to the builder. If a method
+ # with the same name is defined in the Package, it will override this definition
+ # (when _ForwardToBaseBuilder is initialized)
+ for method_name in (
+ base_cls.phases
+ + base_cls.legacy_methods
+ + getattr(base_cls, "legacy_long_methods", tuple())
+ + ("setup_build_environment", "setup_dependent_build_environment")
+ ):
+ setattr(_ForwardToBaseBuilder, method_name, forward_method_to_getattr(method_name))
+
+ def forward_property_to_getattr(property_name):
+ def __forward(self):
+ return self.__getattr__(property_name)
+
+ return __forward
+
+ for attribute_name in base_cls.legacy_attributes:
+ setattr(
+ _ForwardToBaseBuilder,
+ attribute_name,
+ property(forward_property_to_getattr(attribute_name)),
+ )
+
+ class Adapter(six.with_metaclass(_PackageAdapterMeta, base_cls)):
+ def __init__(self, pkg):
+ # Deal with custom phases in packages here
+ if hasattr(pkg, "phases"):
+ self.phases = pkg.phases
+ for phase in self.phases:
+ setattr(Adapter, phase, _PackageAdapterMeta.phase_method_adapter(phase))
+
+ # Attribute containing the package wrapped in dispatcher with a `__getattr__`
+ # method that will forward certain calls to the default builder.
+ self.pkg_with_dispatcher = _ForwardToBaseBuilder(pkg, root_builder=self)
+ super(Adapter, self).__init__(pkg)
+
+ # These two methods don't follow the (self, spec, prefix) signature of phases nor
+ # the (self) signature of methods, so they are added explicitly to avoid using a
+ # catch-all (*args, **kwargs)
+ def setup_build_environment(self, env):
+ return self.pkg_with_dispatcher.setup_build_environment(env)
+
+ def setup_dependent_build_environment(self, env, dependent_spec):
+ return self.pkg_with_dispatcher.setup_dependent_build_environment(env, dependent_spec)
+
+ return Adapter(pkg)
+
+
+def buildsystem_name(pkg):
+ """Given a package object with an associated concrete spec,
+ return the name of its build system.
+
+ Args:
+ pkg (spack.package_base.PackageBase): package for which we want
+ the build system name
+ """
+ try:
+ return pkg.spec.variants["build_system"].value
+ except KeyError:
+ # We are reading an old spec without the build_system variant
+ return pkg.legacy_buildsystem
+
+
+class PhaseCallbacksMeta(type):
+ """Permit to register arbitrary functions during class definition and run them
+ later, before or after a given install phase.
+
+ Each method decorated with ``run_before`` or ``run_after`` gets temporarily
+ stored in a global shared state when a class being defined is parsed by the Python
+ interpreter. At class definition time that temporary storage gets flushed and a list
+ of callbacks is attached to the class being defined.
+ """
+
+ def __new__(mcs, name, bases, attr_dict):
+ for temporary_stage in (_RUN_BEFORE, _RUN_AFTER):
+ staged_callbacks = temporary_stage.callbacks
+
+ # We don't have callbacks in this class, move on
+ if not staged_callbacks:
+ continue
+
+ # If we are here we have callbacks. To get a complete list, get first what
+ # was attached to parent classes, then prepend what we have registered here.
+ #
+ # The order should be:
+ # 1. Callbacks are registered in order within the same class
+ # 2. Callbacks defined in derived classes precede those defined in base
+ # classes
+ for base in bases:
+ callbacks_from_base = getattr(base, temporary_stage.attribute_name, None)
+ if callbacks_from_base:
+ break
+ callbacks_from_base = callbacks_from_base or []
+
+ # Set the callbacks in this class and flush the temporary stage
+ attr_dict[temporary_stage.attribute_name] = staged_callbacks[:] + callbacks_from_base
+ del temporary_stage.callbacks[:]
+
+ return super(PhaseCallbacksMeta, mcs).__new__(mcs, name, bases, attr_dict)
+
+ @staticmethod
+ def run_after(phase, when=None):
+ """Decorator to register a function for running after a given phase.
+
+ Args:
+ phase (str): phase after which the function must run.
+ when (str): condition under which the function is run (if None, it is always run).
+ """
+
+ def _decorator(fn):
+ key = (phase, when)
+ item = (key, fn)
+ _RUN_AFTER.callbacks.append(item)
+ return fn
+
+ return _decorator
+
+ @staticmethod
+ def run_before(phase, when=None):
+ """Decorator to register a function for running before a given phase.
+
+ Args:
+ phase (str): phase before which the function must run.
+ when (str): condition under which the function is run (if None, it is always run).
+ """
+
+ def _decorator(fn):
+ key = (phase, when)
+ item = (key, fn)
+ _RUN_BEFORE.callbacks.append(item)
+ return fn
+
+ return _decorator
+
+
+class BuilderMeta(PhaseCallbacksMeta, type(llnl.util.compat.Sequence)): # type: ignore
+ pass
+
+
+class _PackageAdapterMeta(BuilderMeta):
+ """Metaclass to adapt old-style packages to the new architecture based on builders
+ for the installation phase.
+
+ This class does the necessary mangling to function argument so that a call to a
+ builder object can delegate to a package object.
+ """
+
+ @staticmethod
+ def phase_method_adapter(phase_name):
+ def _adapter(self, pkg, spec, prefix):
+ phase_fn = getattr(self.pkg_with_dispatcher, phase_name)
+ return phase_fn(spec, prefix)
+
+ return _adapter
+
+ @staticmethod
+ def legacy_long_method_adapter(method_name):
+ def _adapter(self, spec, prefix):
+ bind_method = getattr(self.pkg_with_dispatcher, method_name)
+ return bind_method(spec, prefix)
+
+ return _adapter
+
+ @staticmethod
+ def legacy_method_adapter(method_name):
+ def _adapter(self):
+ bind_method = getattr(self.pkg_with_dispatcher, method_name)
+ return bind_method()
+
+ return _adapter
+
+ @staticmethod
+ def legacy_attribute_adapter(attribute_name):
+ def _adapter(self):
+ return getattr(self.pkg_with_dispatcher, attribute_name)
+
+ return property(_adapter)
+
+ @staticmethod
+ def combine_callbacks(pipeline_attribute_name):
+ """This function combines callbacks from old-style packages with callbacks that might
+ be registered for the default builder.
+
+ It works by:
+ 1. Extracting the callbacks from the old-style package
+ 2. Transforming those callbacks by adding an adapter that receives a builder as argument
+ and calls the wrapped function with ``builder.pkg``
+ 3. Combining the list of transformed callbacks with those that might be present in the
+ default builder
+ """
+
+ def _adapter(self):
+ def unwrap_pkg(fn):
+ @functools.wraps(fn)
+ def _wrapped(builder):
+ return fn(builder.pkg_with_dispatcher)
+
+ return _wrapped
+
+ # Concatenate the current list with the one from package
+ callbacks_from_package = getattr(self.pkg, pipeline_attribute_name, [])
+ callbacks_from_package = [(key, unwrap_pkg(x)) for key, x in callbacks_from_package]
+ callbacks_from_builder = getattr(super(type(self), self), pipeline_attribute_name, [])
+ return callbacks_from_package + callbacks_from_builder
+
+ return property(_adapter)
+
+ def __new__(mcs, name, bases, attr_dict):
+ # Add ways to intercept methods and attribute calls and dispatch
+ # them first to a package object
+ default_builder_cls = bases[0]
+ for phase_name in default_builder_cls.phases:
+ attr_dict[phase_name] = _PackageAdapterMeta.phase_method_adapter(phase_name)
+
+ for method_name in default_builder_cls.legacy_methods:
+ attr_dict[method_name] = _PackageAdapterMeta.legacy_method_adapter(method_name)
+
+ # These exist e.g. for Python, see discussion in https://github.com/spack/spack/pull/32068
+ for method_name in getattr(default_builder_cls, "legacy_long_methods", []):
+ attr_dict[method_name] = _PackageAdapterMeta.legacy_long_method_adapter(method_name)
+
+ for attribute_name in default_builder_cls.legacy_attributes:
+ attr_dict[attribute_name] = _PackageAdapterMeta.legacy_attribute_adapter(
+ attribute_name
+ )
+
+ combine_callbacks = _PackageAdapterMeta.combine_callbacks
+ attr_dict[_RUN_BEFORE.attribute_name] = combine_callbacks(_RUN_BEFORE.attribute_name)
+ attr_dict[_RUN_AFTER.attribute_name] = combine_callbacks(_RUN_AFTER.attribute_name)
+
+ return super(_PackageAdapterMeta, mcs).__new__(mcs, name, bases, attr_dict)
+
+
+class InstallationPhase(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 arbitrary callbacks before and after
+ the phase.
+ """
+
+ def __init__(self, name, builder):
+ self.name = name
+ self.builder = builder
+ self.phase_fn = self._select_phase_fn()
+ self.run_before = self._make_callbacks(_RUN_BEFORE.attribute_name)
+ self.run_after = self._make_callbacks(_RUN_AFTER.attribute_name)
+
+ def _make_callbacks(self, callbacks_attribute):
+ result = []
+ callbacks = getattr(self.builder, callbacks_attribute, [])
+ for (phase, condition), fn in callbacks:
+ # Same if it is for another phase
+ if phase != self.name:
+ continue
+
+ # If we have no condition or the callback satisfies a condition, register it
+ if condition is None or self.builder.pkg.spec.satisfies(condition):
+ result.append(fn)
+ return result
+
+ def __str__(self):
+ msg = '{0}: executing "{1}" phase'
+ return msg.format(self.builder, self.name)
+
+ def execute(self):
+ pkg = self.builder.pkg
+ self._on_phase_start(pkg)
+
+ for callback in self.run_before:
+ callback(self.builder)
+
+ self.phase_fn(pkg, pkg.spec, pkg.prefix)
+
+ for callback in self.run_after:
+ callback(self.builder)
+
+ self._on_phase_exit(pkg)
+
+ def _select_phase_fn(self):
+ phase_fn = getattr(self.builder, self.name, None)
+
+ if not phase_fn:
+ msg = (
+ 'unexpected error: package "{0.fullname}" must implement an '
+ '"{1}" phase for the "{2}" build system'
+ )
+ raise RuntimeError(msg.format(self.builder.pkg, self.name, self.builder.build_system))
+
+ return phase_fn
+
+ def _on_phase_start(self, instance):
+ # If a phase has a matching stop_before_phase attribute,
+ # stop the installation process raising a StopPhase
+ if getattr(instance, "stop_before_phase", None) == self.name:
+ raise spack.build_environment.StopPhase(
+ "Stopping before '{0}' phase".format(self.name)
+ )
+
+ def _on_phase_exit(self, instance):
+ # If a phase has a matching last_phase attribute,
+ # stop the installation process raising a StopPhase
+ if getattr(instance, "last_phase", None) == self.name:
+ raise spack.build_environment.StopPhase("Stopping at '{0}' phase".format(self.name))
+
+ def copy(self):
+ return copy.deepcopy(self)
+
+
+class Builder(six.with_metaclass(BuilderMeta, llnl.util.compat.Sequence)):
+ """A builder is a class that, given a package object (i.e. associated with
+ concrete spec), knows how to install it.
+
+ The builder behaves like a sequence, and when iterated over return the
+ "phases" of the installation in the correct order.
+
+ Args:
+ pkg (spack.package_base.PackageBase): package object to be built
+ """
+
+ #: Sequence of phases. Must be defined in derived classes
+ phases = None # type: Optional[Tuple[str, ...]]
+ #: Build system name. Must also be defined in derived classes.
+ build_system = None # type: Optional[str]
+
+ legacy_methods = () # type: Tuple[str, ...]
+ legacy_attributes = () # type: Tuple[str, ...]
+
+ #: List of glob expressions. Each expression must either be
+ #: absolute or relative to the package source path.
+ #: Matching artifacts found at the end of the build process will be
+ #: copied in the same directory tree as _spack_build_logfile and
+ #: _spack_build_envfile.
+ archive_files = [] # type: List[str]
+
+ def __init__(self, pkg):
+ self.pkg = pkg
+ self.callbacks = {}
+ for phase in self.phases:
+ self.callbacks[phase] = InstallationPhase(phase, self)
+
+ @property
+ def spec(self):
+ return self.pkg.spec
+
+ @property
+ def stage(self):
+ return self.pkg.stage
+
+ @property
+ def prefix(self):
+ return self.pkg.prefix
+
+ def test(self):
+ # Defer tests to virtual and concrete packages
+ pass
+
+ def setup_build_environment(self, env):
+ """Sets up the build environment for a package.
+
+ This method will be called before the current package prefix exists in
+ Spack's store.
+
+ Args:
+ env (spack.util.environment.EnvironmentModifications): environment
+ modifications to be applied when the package is built. Package authors
+ can call methods on it to alter the build environment.
+ """
+ if not hasattr(super(Builder, self), "setup_build_environment"):
+ return
+ super(Builder, self).setup_build_environment(env)
+
+ def setup_dependent_build_environment(self, env, dependent_spec):
+ """Sets up the build environment of packages that depend on this one.
+
+ This is similar to ``setup_build_environment``, but it is used to
+ modify the build environments of packages that *depend* on this one.
+
+ This gives packages like Python and others that follow the extension
+ model a way to implement common environment or compile-time settings
+ for dependencies.
+
+ This method will be called before the dependent package prefix exists
+ in Spack's store.
+
+ Examples:
+ 1. Installing python modules generally requires ``PYTHONPATH``
+ to point to the ``lib/pythonX.Y/site-packages`` directory in the
+ module's install prefix. This method could be used to set that
+ variable.
+
+ Args:
+ env (spack.util.environment.EnvironmentModifications): environment
+ modifications to be applied when the dependent package is built.
+ Package authors can call methods on it to alter the build environment.
+
+ dependent_spec (spack.spec.Spec): the spec of the dependent package
+ about to be built. This allows the extendee (self) to query
+ the dependent's state. Note that *this* package's spec is
+ available as ``self.spec``
+ """
+ if not hasattr(super(Builder, self), "setup_dependent_build_environment"):
+ return
+ super(Builder, self).setup_dependent_build_environment(env, dependent_spec)
+
+ def __getitem__(self, idx):
+ key = self.phases[idx]
+ return self.callbacks[key]
+
+ def __len__(self):
+ return len(self.phases)
+
+ def __repr__(self):
+ msg = "{0}({1})"
+ return msg.format(type(self).__name__, self.pkg.spec.format("{name}/{hash:7}"))
+
+ def __str__(self):
+ msg = '"{0}" builder for "{1}"'
+ return msg.format(type(self).build_system, self.pkg.spec.format("{name}/{hash:7}"))
+
+
+# Export these names as standalone to be used in packages
+run_after = PhaseCallbacksMeta.run_after
+run_before = PhaseCallbacksMeta.run_before
diff --git a/lib/spack/spack/cmd/info.py b/lib/spack/spack/cmd/info.py
index ce69d98dbb..6a16a153ea 100644
--- a/lib/spack/spack/cmd/info.py
+++ b/lib/spack/spack/cmd/info.py
@@ -210,11 +210,11 @@ def print_maintainers(pkg):
def print_phases(pkg):
"""output installation phases"""
- if hasattr(pkg, "phases") and pkg.phases:
+ if hasattr(pkg.builder, "phases") and pkg.builder.phases:
color.cprint("")
color.cprint(section_title("Installation Phases:"))
phase_str = ""
- for phase in pkg.phases:
+ for phase in pkg.builder.phases:
phase_str += " {0}".format(phase)
color.cprint(phase_str)
diff --git a/lib/spack/spack/cmd/uninstall.py b/lib/spack/spack/cmd/uninstall.py
index be4b74c54a..af85b64188 100644
--- a/lib/spack/spack/cmd/uninstall.py
+++ b/lib/spack/spack/cmd/uninstall.py
@@ -228,7 +228,7 @@ def do_uninstall(env, specs, force):
except spack.repo.UnknownEntityError:
# The package.py file has gone away -- but still
# want to uninstall.
- spack.package_base.Package.uninstall_by_spec(item, force=True)
+ spack.package_base.PackageBase.uninstall_by_spec(item, force=True)
# A package is ready to be uninstalled when nothing else references it,
# unless we are requested to force uninstall it.
diff --git a/lib/spack/spack/detection/common.py b/lib/spack/spack/detection/common.py
index d79b62b872..b9cd871fc2 100644
--- a/lib/spack/spack/detection/common.py
+++ b/lib/spack/spack/detection/common.py
@@ -228,7 +228,7 @@ def compute_windows_program_path_for_package(pkg):
program files location, return list of best guesses
Args:
- pkg (spack.package_base.Package): package for which
+ pkg (spack.package_base.PackageBase): package for which
Program Files location is to be computed
"""
if not is_windows:
diff --git a/lib/spack/spack/directives.py b/lib/spack/spack/directives.py
index c7491861eb..7c49158c79 100644
--- a/lib/spack/spack/directives.py
+++ b/lib/spack/spack/directives.py
@@ -17,6 +17,7 @@ definition to modify the package, for example:
The available directives are:
+ * ``build_system``
* ``conflicts``
* ``depends_on``
* ``extends``
@@ -59,13 +60,15 @@ __all__ = [
"patch",
"variant",
"resource",
+ "build_system",
]
#: These are variant names used by Spack internally; packages can't use them
reserved_names = ["patches", "dev_path"]
-#: Names of possible directives. This list is populated elsewhere in the file.
-directive_names = []
+#: Names of possible directives. This list is mostly populated using the @directive decorator.
+#: Some directives leverage others and in that case are not automatically added.
+directive_names = ["build_system"]
_patch_order_index = 0
@@ -758,6 +761,17 @@ def resource(**kwargs):
return _execute_resource
+def build_system(*values, **kwargs):
+ default = kwargs.get("default", None) or values[0]
+ return variant(
+ "build_system",
+ values=tuple(values),
+ description="Build systems supported by the package",
+ default=default,
+ multi=False,
+ )
+
+
class DirectiveError(spack.error.SpackError):
"""This is raised when something is wrong with a package directive."""
diff --git a/lib/spack/spack/installer.py b/lib/spack/spack/installer.py
index 411c076aa8..620f489c77 100644
--- a/lib/spack/spack/installer.py
+++ b/lib/spack/spack/installer.py
@@ -112,14 +112,15 @@ def _check_last_phase(pkg):
Raises:
``BadInstallPhase`` if stop_before or last phase is invalid
"""
- if pkg.stop_before_phase and pkg.stop_before_phase not in pkg.phases:
+ phases = pkg.builder.phases
+ if pkg.stop_before_phase and pkg.stop_before_phase not in phases:
raise BadInstallPhase(pkg.name, pkg.stop_before_phase)
- if pkg.last_phase and pkg.last_phase not in pkg.phases:
+ if pkg.last_phase and pkg.last_phase not in phases:
raise BadInstallPhase(pkg.name, pkg.last_phase)
# If we got a last_phase, make sure it's not already last
- if pkg.last_phase and pkg.last_phase == pkg.phases[-1]:
+ if pkg.last_phase and pkg.last_phase == phases[-1]:
pkg.last_phase = None
@@ -129,7 +130,7 @@ def _handle_external_and_upstream(pkg, explicit):
database if it is external package.
Args:
- pkg (spack.package_base.Package): the package whose installation is under
+ pkg (spack.package_base.PackageBase): the package whose installation is under
consideration
explicit (bool): the package was explicitly requested by the user
Return:
@@ -559,7 +560,7 @@ def log(pkg):
Copy provenance into the install directory on success
Args:
- pkg (spack.package_base.Package): the package that was built and installed
+ pkg (spack.package_base.PackageBase): the package that was built and installed
"""
packages_dir = spack.store.layout.build_packages_path(pkg.spec)
@@ -596,7 +597,7 @@ def log(pkg):
errors = six.StringIO()
target_dir = os.path.join(spack.store.layout.metadata_path(pkg.spec), "archived-files")
- for glob_expr in pkg.archive_files:
+ for glob_expr in pkg.builder.archive_files:
# Check that we are trying to copy things that are
# in the stage tree (not arbitrary files)
abs_expr = os.path.realpath(glob_expr)
@@ -810,7 +811,7 @@ class PackageInstaller(object):
Creates and queus the initial build task for the package.
Args:
- pkg (spack.package_base.Package): the package to be built and installed
+ pkg (spack.package_base.PackageBase): the package to be built and installed
request (BuildRequest or None): the associated install request
where ``None`` can be used to indicate the package was
explicitly requested by the user
@@ -1404,7 +1405,7 @@ class PackageInstaller(object):
Write a small metadata file with the current spack environment.
Args:
- pkg (spack.package_base.Package): the package to be built and installed
+ pkg (spack.package_base.PackageBase): the package to be built and installed
"""
if not os.path.exists(pkg.spec.prefix):
path = spack.util.path.debug_padded_filter(pkg.spec.prefix)
@@ -1477,8 +1478,8 @@ class PackageInstaller(object):
known dependents.
Args:
- pkg (spack.package_base.Package): Package that has been installed locally,
- externally or upstream
+ pkg (spack.package_base.PackageBase): Package that has been installed
+ locally, externally or upstream
dependent_ids (list or None): list of the package's
dependent ids, or None if the dependent ids are limited to
those maintained in the package (dependency DAG)
@@ -1562,11 +1563,7 @@ class PackageInstaller(object):
return InstallAction.OVERWRITE
def install(self):
- """
- Install the requested package(s) and or associated dependencies.
-
- Args:
- pkg (spack.package_base.Package): the package to be built and installed"""
+ """Install the requested package(s) and or associated dependencies."""
self._init_queue()
fail_fast_err = "Terminating after first install failure"
@@ -1951,6 +1948,8 @@ class BuildProcessInstaller(object):
fs.install_tree(pkg.stage.source_path, src_target)
def _real_install(self):
+ import spack.builder
+
pkg = self.pkg
# Do the real install in the source directory.
@@ -1981,13 +1980,11 @@ class BuildProcessInstaller(object):
# Spawn a daemon that reads from a pipe and redirects
# everything to log_path, and provide the phase for logging
- for i, (phase_name, phase_attr) in enumerate(
- zip(pkg.phases, pkg._InstallPhase_phases)
- ):
-
+ builder = spack.builder.create(pkg)
+ for i, phase_fn in enumerate(builder):
# Keep a log file for each phase
log_dir = os.path.dirname(pkg.log_path)
- log_file = "spack-build-%02d-%s-out.txt" % (i + 1, phase_name.lower())
+ log_file = "spack-build-%02d-%s-out.txt" % (i + 1, phase_fn.name.lower())
log_file = os.path.join(log_dir, log_file)
try:
@@ -2005,20 +2002,20 @@ class BuildProcessInstaller(object):
with logger.force_echo():
inner_debug_level = tty.debug_level()
tty.set_debug(debug_level)
- tty.msg("{0} Executing phase: '{1}'".format(self.pre, phase_name))
+ msg = "{0} Executing phase: '{1}'"
+ tty.msg(msg.format(self.pre, phase_fn.name))
tty.set_debug(inner_debug_level)
# Redirect stdout and stderr to daemon pipe
- phase = getattr(pkg, phase_attr)
- self.timer.phase(phase_name)
+ self.timer.phase(phase_fn.name)
# Catch any errors to report to logging
- phase(pkg.spec, pkg.prefix)
- spack.hooks.on_phase_success(pkg, phase_name, log_file)
+ phase_fn.execute()
+ spack.hooks.on_phase_success(pkg, phase_fn.name, log_file)
except BaseException:
combine_phase_logs(pkg.phase_log_files, pkg.log_path)
- spack.hooks.on_phase_error(pkg, phase_name, log_file)
+ spack.hooks.on_phase_error(pkg, phase_fn.name, log_file)
# phase error indicates install error
spack.hooks.on_install_failure(pkg.spec)
@@ -2094,7 +2091,7 @@ class BuildTask(object):
Instantiate a build task for a package.
Args:
- pkg (spack.package_base.Package): the package to be built and installed
+ pkg (spack.package_base.PackageBase): the package to be built and installed
request (BuildRequest or None): the associated install request
where ``None`` can be used to indicate the package was
explicitly requested by the user
@@ -2310,7 +2307,7 @@ class BuildRequest(object):
Instantiate a build request for a package.
Args:
- pkg (spack.package_base.Package): the package to be built and installed
+ pkg (spack.package_base.PackageBase): the package to be built and installed
install_args (dict): the install arguments associated with ``pkg``
"""
# Ensure dealing with a package that has a concrete spec
diff --git a/lib/spack/spack/mixins.py b/lib/spack/spack/mixins.py
index ace3681e52..b43b85aa02 100644
--- a/lib/spack/spack/mixins.py
+++ b/lib/spack/spack/mixins.py
@@ -6,10 +6,9 @@
"""This module contains additional behavior that can be attached to any given
package.
"""
-import collections
import os
import sys
-from typing import Callable, DefaultDict, Dict, List # novm
+from typing import Callable, DefaultDict, List # novm
if sys.version_info >= (3, 5):
CallbackDict = DefaultDict[str, List[Callable]]
@@ -18,105 +17,7 @@ else:
import llnl.util.filesystem
-__all__ = [
- "filter_compiler_wrappers",
- "PackageMixinsMeta",
-]
-
-
-class PackageMixinsMeta(type):
- """This metaclass serves the purpose of implementing a declarative syntax
- for package mixins.
-
- Mixins are implemented below in the form of a function. Each one of them
- needs to register a callable that takes a single argument to be run
- before or after a certain phase. This callable is basically a method that
- gets implicitly attached to the package class by calling the mixin.
- """
-
- _methods_to_be_added = {} # type: Dict[str, Callable]
- _add_method_before = collections.defaultdict(list) # type: CallbackDict
- _add_method_after = collections.defaultdict(list) # type: CallbackDict
-
- @staticmethod
- def register_method_before(fn, phase): # type: (Callable, str) -> None
- """Registers a method to be run before a certain phase.
-
- Args:
- fn: function taking a single argument (self)
- phase (str): phase before which fn must run
- """
- PackageMixinsMeta._methods_to_be_added[fn.__name__] = fn
- PackageMixinsMeta._add_method_before[phase].append(fn)
-
- @staticmethod
- def register_method_after(fn, phase): # type: (Callable, str) -> None
- """Registers a method to be run after a certain phase.
-
- Args:
- fn: function taking a single argument (self)
- phase (str): phase after which fn must run
- """
- PackageMixinsMeta._methods_to_be_added[fn.__name__] = fn
- PackageMixinsMeta._add_method_after[phase].append(fn)
-
- def __init__(cls, name, bases, attr_dict):
-
- # Add the methods to the class being created
- if PackageMixinsMeta._methods_to_be_added:
- attr_dict.update(PackageMixinsMeta._methods_to_be_added)
- PackageMixinsMeta._methods_to_be_added.clear()
-
- attr_fmt = "_InstallPhase_{0}"
-
- # Copy the phases that needs it to the most derived classes
- # in order not to interfere with other packages in the hierarchy
- phases_to_be_copied = list(PackageMixinsMeta._add_method_before.keys())
- phases_to_be_copied += list(PackageMixinsMeta._add_method_after.keys())
-
- for phase in phases_to_be_copied:
-
- attr_name = attr_fmt.format(phase)
-
- # Here we want to get the attribute directly from the class (not
- # from the instance), so that we can modify it and add the mixin
- # method to the pipeline.
- phase = getattr(cls, attr_name)
-
- # Due to MRO, we may have taken a method from a parent class
- # and modifying it may influence other packages in unwanted
- # manners. Solve the problem by copying the phase into the most
- # derived class.
- setattr(cls, attr_name, phase.copy())
-
- # Insert the methods in the appropriate position
- # in the installation pipeline.
-
- for phase in PackageMixinsMeta._add_method_before:
-
- attr_name = attr_fmt.format(phase)
- phase_obj = getattr(cls, attr_name)
- fn_list = PackageMixinsMeta._add_method_after[phase]
-
- for f in fn_list:
- phase_obj.run_before.append(f)
-
- # Flush the dictionary for the next class
- PackageMixinsMeta._add_method_before.clear()
-
- for phase in PackageMixinsMeta._add_method_after:
-
- attr_name = attr_fmt.format(phase)
- phase_obj = getattr(cls, attr_name)
- fn_list = PackageMixinsMeta._add_method_after[phase]
-
- for f in fn_list:
- phase_obj.run_after.append(f)
-
- # Flush the dictionary for the next class
- PackageMixinsMeta._add_method_after.clear()
-
- super(PackageMixinsMeta, cls).__init__(name, bases, attr_dict)
+import spack.builder
def filter_compiler_wrappers(*files, **kwargs):
@@ -216,4 +117,4 @@ def filter_compiler_wrappers(*files, **kwargs):
if self.compiler.name == "nag":
x.filter("-Wl,--enable-new-dtags", "", **filter_kwargs)
- PackageMixinsMeta.register_method_after(_filter_compiler_wrappers_impl, after)
+ spack.builder.run_after(after)(_filter_compiler_wrappers_impl)
diff --git a/lib/spack/spack/multimethod.py b/lib/spack/spack/multimethod.py
index 2c6a56db1a..66f7a887f3 100644
--- a/lib/spack/spack/multimethod.py
+++ b/lib/spack/spack/multimethod.py
@@ -120,32 +120,36 @@ class SpecMultiMethod(object):
return method
return self.default or None
- def __call__(self, package_self, *args, **kwargs):
+ def __call__(self, package_or_builder_self, *args, **kwargs):
"""Find the first method with a spec that matches the
package's spec. If none is found, call the default
or if there is none, then raise a NoSuchMethodError.
"""
- spec_method = self._get_method_by_spec(package_self.spec)
+ spec_method = self._get_method_by_spec(package_or_builder_self.spec)
if spec_method:
- return spec_method(package_self, *args, **kwargs)
+ return spec_method(package_or_builder_self, *args, **kwargs)
# Unwrap the MRO of `package_self by hand. Note that we can't
# use `super()` here, because using `super()` recursively
# requires us to know the class of `package_self`, as well as
# its superclasses for successive calls. We don't have that
# information within `SpecMultiMethod`, because it is not
# associated with the package class.
- for cls in inspect.getmro(package_self.__class__)[1:]:
+ for cls in inspect.getmro(package_or_builder_self.__class__)[1:]:
superself = cls.__dict__.get(self.__name__, None)
+
if isinstance(superself, SpecMultiMethod):
# Check parent multimethod for method for spec.
- superself_method = superself._get_method_by_spec(package_self.spec)
+ superself_method = superself._get_method_by_spec(package_or_builder_self.spec)
if superself_method:
- return superself_method(package_self, *args, **kwargs)
+ return superself_method(package_or_builder_self, *args, **kwargs)
elif superself:
- return superself(package_self, *args, **kwargs)
+ return superself(package_or_builder_self, *args, **kwargs)
raise NoSuchMethodError(
- type(package_self), self.__name__, package_self.spec, [m[0] for m in self.method_list]
+ type(package_or_builder_self),
+ self.__name__,
+ package_or_builder_self.spec,
+ [m[0] for m in self.method_list],
)
diff --git a/lib/spack/spack/package.py b/lib/spack/spack/package.py
index 65e699bda4..46c9da4844 100644
--- a/lib/spack/spack/package.py
+++ b/lib/spack/spack/package.py
@@ -17,6 +17,7 @@ from llnl.util.filesystem import *
import spack.util.executable
from spack.build_systems.aspell_dict import AspellDictPackage
from spack.build_systems.autotools import AutotoolsPackage
+from spack.build_systems.bundle import BundlePackage
from spack.build_systems.cached_cmake import (
CachedCMakePackage,
cmake_cache_option,
@@ -25,12 +26,14 @@ from spack.build_systems.cached_cmake import (
)
from spack.build_systems.cmake import CMakePackage
from spack.build_systems.cuda import CudaPackage
+from spack.build_systems.generic import Package
from spack.build_systems.gnu import GNUMirrorPackage
from spack.build_systems.intel import IntelPackage
from spack.build_systems.lua import LuaPackage
from spack.build_systems.makefile import MakefilePackage
from spack.build_systems.maven import MavenPackage
from spack.build_systems.meson import MesonPackage
+from spack.build_systems.nmake import NMakePackage
from spack.build_systems.octave import OctavePackage
from spack.build_systems.oneapi import (
IntelOneApiLibraryPackage,
@@ -38,7 +41,7 @@ from spack.build_systems.oneapi import (
IntelOneApiStaticLibraryList,
)
from spack.build_systems.perl import PerlPackage
-from spack.build_systems.python import PythonPackage
+from spack.build_systems.python import PythonExtension, PythonPackage
from spack.build_systems.qmake import QMakePackage
from spack.build_systems.r import RPackage
from spack.build_systems.racket import RacketPackage
@@ -50,6 +53,7 @@ from spack.build_systems.sourceforge import SourceforgePackage
from spack.build_systems.sourceware import SourcewarePackage
from spack.build_systems.waf import WafPackage
from spack.build_systems.xorg import XorgPackage
+from spack.builder import run_after, run_before
from spack.dependency import all_deptypes
from spack.directives import *
from spack.install_test import get_escaped_text_output
@@ -62,17 +66,13 @@ from spack.installer import (
from spack.mixins import filter_compiler_wrappers
from spack.multimethod import when
from spack.package_base import (
- BundlePackage,
DependencyConflictError,
- Package,
build_system_flags,
env_flags,
flatten_dependencies,
inject_flags,
install_dependency_symlinks,
on_package_attributes,
- run_after,
- run_before,
)
from spack.spec import InvalidSpecDetected, Spec
from spack.util.executable import *
diff --git a/lib/spack/spack/package_base.py b/lib/spack/spack/package_base.py
index f05c0ff868..3c3bcaa84d 100644
--- a/lib/spack/spack/package_base.py
+++ b/lib/spack/spack/package_base.py
@@ -33,7 +33,7 @@ import six
import llnl.util.filesystem as fsys
import llnl.util.tty as tty
-from llnl.util.lang import classproperty, match_predicate, memoized, nullcontext
+from llnl.util.lang import classproperty, memoized, nullcontext
from llnl.util.link_tree import LinkTree
import spack.compilers
@@ -104,7 +104,7 @@ def deprecated_version(pkg, version):
"""Return True if the version is deprecated, False otherwise.
Arguments:
- pkg (Package): The package whose version is to be checked.
+ pkg (PackageBase): The package whose version is to be checked.
version (str or spack.version.VersionBase): The version being checked
"""
if not isinstance(version, VersionBase):
@@ -122,7 +122,7 @@ def preferred_version(pkg):
Returns a sorted list of the preferred versions of the package.
Arguments:
- pkg (Package): The package whose versions are to be assessed.
+ pkg (PackageBase): The package whose versions are to be assessed.
"""
# Here we sort first on the fact that a version is marked
# as preferred in the package, then on the fact that the
@@ -131,77 +131,6 @@ def preferred_version(pkg):
return sorted(pkg.versions, key=key_fn).pop()
-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 arbitrary callbacks before and after
- the phase.
- """
-
- def __init__(self, name):
- self.name = name
- self.run_before = []
- self.run_after = []
-
- 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 callback in self.run_before:
- callback(instance)
- phase(spec, prefix)
- # Execute phase sanity_checks,
- # and give them the chance to fail
- for callback in self.run_after:
- callback(instance)
- # Check instance attributes at the end of a phase
- self._on_phase_exit(instance)
-
- return phase_wrapper
-
- def _on_phase_start(self, instance):
- # If a phase has a matching stop_before_phase attribute,
- # stop the installation process raising a StopPhase
- if getattr(instance, "stop_before_phase", None) == self.name:
- from spack.build_environment import StopPhase
-
- raise StopPhase("Stopping before '{0}' phase".format(self.name))
-
- def _on_phase_exit(self, instance):
- # If a phase has a matching last_phase attribute,
- # stop the installation process raising a StopPhase
- if getattr(instance, "last_phase", None) == self.name:
- from spack.build_environment import StopPhase
-
- raise StopPhase("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.run_before.extend(self.run_before)
- other.run_after.extend(self.run_after)
- return other
-
-
class WindowsRPathMeta(object):
"""Collection of functionality surrounding Windows RPATH specific features
@@ -368,23 +297,18 @@ class DetectablePackageMeta(object):
class PackageMeta(
+ spack.builder.PhaseCallbacksMeta,
DetectablePackageMeta,
spack.directives.DirectiveMeta,
- spack.mixins.PackageMixinsMeta,
spack.multimethod.MultiMethodMeta,
):
"""
Package metaclass for supporting directives (e.g., depends_on) and phases
"""
- phase_fmt = "_InstallPhase_{0}"
-
- # These are accessed only through getattr, by name
- _InstallPhase_run_before = {} # type: Dict[str, List[Callable]]
- _InstallPhase_run_after = {} # type: Dict[str, List[Callable]]
-
def __new__(cls, name, bases, attr_dict):
"""
+ FIXME: REWRITE
Instance creation is preceded by phase attribute transformations.
Conveniently transforms attributes to permit extensible phases by
@@ -392,70 +316,10 @@ class PackageMeta(
InstallPhase attributes in the class that will be initialized in
__init__.
"""
- if "phases" in attr_dict:
- # Turn the strings in 'phases' into InstallPhase instances
- # and add them as private attributes
- _InstallPhase_phases = [PackageMeta.phase_fmt.format(x) for x in attr_dict["phases"]]
- for phase_name, callback_name in zip(_InstallPhase_phases, attr_dict["phases"]):
- attr_dict[phase_name] = InstallPhase(callback_name)
- attr_dict["_InstallPhase_phases"] = _InstallPhase_phases
-
- def _flush_callbacks(check_name):
- # Name of the attribute I am going to check it exists
- check_attr = PackageMeta.phase_fmt.format(check_name)
- checks = getattr(cls, check_attr)
- if checks:
- for phase_name, funcs in checks.items():
- phase_attr = PackageMeta.phase_fmt.format(phase_name)
- try:
- # Search for the phase in the attribute dictionary
- phase = attr_dict[phase_attr]
- 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, phase_attr, None)
- if phase is not None:
- break
-
- phase = attr_dict[phase_attr] = phase.copy()
- getattr(phase, check_name).extend(funcs)
- # Clear the attribute for the next class
- setattr(cls, check_attr, {})
-
- _flush_callbacks("run_before")
- _flush_callbacks("run_after")
-
- # Reset names for packages that inherit from another
- # package with a different name
attr_dict["_name"] = None
return super(PackageMeta, cls).__new__(cls, name, bases, attr_dict)
- @staticmethod
- def register_callback(check_type, *phases):
- def _decorator(func):
- attr_name = PackageMeta.phase_fmt.format(check_type)
- check_list = getattr(PackageMeta, attr_name)
- for item in phases:
- checks = check_list.setdefault(item, [])
- checks.append(func)
- setattr(PackageMeta, attr_name, check_list)
- return func
-
- return _decorator
-
-
-def run_before(*phases):
- """Registers a method of a package to be run before a given phase"""
- return PackageMeta.register_callback("run_before", *phases)
-
-
-def run_after(*phases):
- """Registers a method of a package to be run after a given phase"""
- return PackageMeta.register_callback("run_after", *phases)
-
def on_package_attributes(**attr_dict):
"""Decorator: executes instance function only if object has attr valuses.
@@ -475,7 +339,9 @@ def on_package_attributes(**attr_dict):
has_all_attributes = all([hasattr(instance, key) for key in attr_dict])
if has_all_attributes:
has_the_right_values = all(
- [getattr(instance, key) == value for key, value in attr_dict.items()]
+ [
+ getattr(instance, key) == value for key, value in attr_dict.items()
+ ] # NOQA: ignore=E501
)
if has_the_right_values:
func(instance, *args, **kwargs)
@@ -687,13 +553,6 @@ class PackageBase(six.with_metaclass(PackageMeta, WindowsRPathMeta, PackageViewM
#: directories, sanity checks will fail.
sanity_check_is_dir = [] # type: List[str]
- #: List of glob expressions. Each expression must either be
- #: absolute or relative to the package source path.
- #: Matching artifacts found at the end of the build process will be
- #: copied in the same directory tree as _spack_build_logfile and
- #: _spack_build_envfile.
- archive_files = [] # type: List[str]
-
#: Boolean. Set to ``True`` for packages that require a manual download.
#: This is currently used by package sanity tests and generation of a
#: more meaningful fetch failure error.
@@ -1038,7 +897,7 @@ class PackageBase(six.with_metaclass(PackageMeta, WindowsRPathMeta, PackageViewM
version (spack.version.Version): the version for which a URL is sought
"""
uf = None
- if type(self).url_for_version != Package.url_for_version:
+ if type(self).url_for_version != PackageBase.url_for_version:
uf = self.url_for_version
return self._implement_all_urls_for_version(version, uf)
@@ -1959,9 +1818,9 @@ class PackageBase(six.with_metaclass(PackageMeta, WindowsRPathMeta, PackageViewM
even with exceptions.
restage (bool): Force spack to restage the package source.
skip_patch (bool): Skip patch stage of build if True.
- stop_before (InstallPhase): stop execution before this
+ stop_before (str): stop execution before this
installation phase (or None)
- stop_at (InstallPhase): last installation phase to be executed
+ stop_at (str): last installation phase to be executed
(or None)
tests (bool or list or set): False to run no tests, True to test
all packages, or a list of package names to run tests for some
@@ -2191,46 +2050,6 @@ class PackageBase(six.with_metaclass(PackageMeta, WindowsRPathMeta, PackageViewM
"""
return True
- def sanity_check_prefix(self):
- """This function checks whether install succeeded."""
-
- def check_paths(path_list, filetype, predicate):
- if isinstance(path_list, six.string_types):
- path_list = [path_list]
-
- for path in path_list:
- abs_path = os.path.join(self.prefix, path)
- if not predicate(abs_path):
- raise InstallError(
- "Install failed for %s. No such %s in prefix: %s"
- % (self.name, filetype, path)
- )
-
- check_paths(self.sanity_check_is_file, "file", os.path.isfile)
- check_paths(self.sanity_check_is_dir, "directory", os.path.isdir)
-
- ignore_file = match_predicate(spack.store.layout.hidden_file_regexes)
- if all(map(ignore_file, os.listdir(self.prefix))):
- raise InstallError("Install failed for %s. Nothing was installed!" % self.name)
-
- def apply_macos_rpath_fixups(self):
- """On Darwin, make installed libraries more easily relocatable.
-
- Some build systems (handrolled, autotools, makefiles) can set their own
- rpaths that are duplicated by spack's compiler wrapper. This fixup
- interrogates, and postprocesses if necessary, all libraries installed
- by the code.
-
- It should be added as a @run_after to packaging systems (or individual
- packages) that do not install relocatable libraries by default.
- """
- if "platform=darwin" not in self.spec:
- return
-
- from spack.relocate import fixup_macos_rpaths
-
- fixup_macos_rpaths(self.spec)
-
@property
def build_log_path(self):
"""
@@ -2268,19 +2087,6 @@ class PackageBase(six.with_metaclass(PackageMeta, WindowsRPathMeta, PackageViewM
"""
return None, None, flags
- def setup_build_environment(self, env):
- """Sets up the build environment for a package.
-
- This method will be called before the current package prefix exists in
- Spack's store.
-
- Args:
- env (spack.util.environment.EnvironmentModifications): environment
- modifications to be applied when the package is built. Package authors
- can call methods on it to alter the build environment.
- """
- pass
-
def setup_run_environment(self, env):
"""Sets up the run environment for a package.
@@ -2291,37 +2097,6 @@ class PackageBase(six.with_metaclass(PackageMeta, WindowsRPathMeta, PackageViewM
"""
pass
- def setup_dependent_build_environment(self, env, dependent_spec):
- """Sets up the build environment of packages that depend on this one.
-
- This is similar to ``setup_build_environment``, but it is used to
- modify the build environments of packages that *depend* on this one.
-
- This gives packages like Python and others that follow the extension
- model a way to implement common environment or compile-time settings
- for dependencies.
-
- This method will be called before the dependent package prefix exists
- in Spack's store.
-
- Examples:
- 1. Installing python modules generally requires ``PYTHONPATH``
- to point to the ``lib/pythonX.Y/site-packages`` directory in the
- module's install prefix. This method could be used to set that
- variable.
-
- Args:
- env (spack.util.environment.EnvironmentModifications): environment
- modifications to be applied when the dependent package is built.
- Package authors can call methods on it to alter the build environment.
-
- dependent_spec (spack.spec.Spec): the spec of the dependent package
- about to be built. This allows the extendee (self) to query
- the dependent's state. Note that *this* package's spec is
- available as ``self.spec``
- """
- pass
-
def setup_dependent_run_environment(self, env, dependent_spec):
"""Sets up the run environment of packages that depend on this one.
@@ -2508,7 +2283,7 @@ class PackageBase(six.with_metaclass(PackageMeta, WindowsRPathMeta, PackageViewM
def do_uninstall(self, force=False):
"""Uninstall this package by spec."""
# delegate to instance-less method.
- Package.uninstall_by_spec(self.spec, force)
+ PackageBase.uninstall_by_spec(self.spec, force)
def do_deprecate(self, deprecator, link_fn):
"""Deprecate this package in favor of deprecator spec"""
@@ -2560,7 +2335,7 @@ class PackageBase(six.with_metaclass(PackageMeta, WindowsRPathMeta, PackageViewM
deprecated.package.do_deprecate(deprecator, link_fn)
# Now that we've handled metadata, uninstall and replace with link
- Package.uninstall_by_spec(spec, force=True, deprecator=deprecator)
+ PackageBase.uninstall_by_spec(spec, force=True, deprecator=deprecator)
link_fn(deprecator.prefix, spec.prefix)
def _check_extendable(self):
@@ -2811,21 +2586,25 @@ class PackageBase(six.with_metaclass(PackageMeta, WindowsRPathMeta, PackageViewM
"""
return " ".join("-Wl,-rpath,%s" % p for p in self.rpath)
- def _run_test_callbacks(self, method_names, callback_type="install"):
+ @property
+ def builder(self):
+ return spack.builder.create(self)
+
+ @staticmethod
+ def run_test_callbacks(builder, method_names, callback_type="install"):
"""Tries to call all of the listed methods, returning immediately
if the list is None."""
- if method_names is None:
+ if not builder.pkg.run_tests or method_names is None:
return
fail_fast = spack.config.get("config:fail_fast", False)
-
- with self._setup_test(verbose=False, externals=False) as logger:
+ with builder.pkg._setup_test(verbose=False, externals=False) as logger:
# Report running each of the methods in the build log
print_test_message(logger, "Running {0}-time tests".format(callback_type), True)
for name in method_names:
try:
- fn = getattr(self, name)
+ fn = getattr(builder, name)
msg = ("RUN-TESTS: {0}-time tests [{1}]".format(callback_type, name),)
print_test_message(logger, msg, True)
@@ -2835,27 +2614,13 @@ class PackageBase(six.with_metaclass(PackageMeta, WindowsRPathMeta, PackageViewM
msg = ("RUN-TESTS: method not implemented [{0}]".format(name),)
print_test_message(logger, msg, True)
- self.test_failures.append((e, msg))
+ builder.pkg.test_failures.append((e, msg))
if fail_fast:
break
# Raise any collected failures here
- if self.test_failures:
- raise TestFailure(self.test_failures)
-
- @on_package_attributes(run_tests=True)
- def _run_default_build_time_test_callbacks(self):
- """Tries to call all the methods that are listed in the attribute
- ``build_time_test_callbacks`` if ``self.run_tests is True``.
- """
- self._run_test_callbacks(self.build_time_test_callbacks, "build")
-
- @on_package_attributes(run_tests=True)
- def _run_default_install_time_test_callbacks(self):
- """Tries to call all the methods that are listed in the attribute
- ``install_time_test_callbacks`` if ``self.run_tests is True``.
- """
- self._run_test_callbacks(self.install_time_test_callbacks, "install")
+ if builder.pkg.test_failures:
+ raise TestFailure(builder.pkg.test_failures)
def has_test_method(pkg):
@@ -2979,37 +2744,6 @@ env_flags = PackageBase.env_flags
build_system_flags = PackageBase.build_system_flags
-class BundlePackage(PackageBase):
- """General purpose bundle, or no-code, package class."""
-
- #: There are no phases by default but the property is required to support
- #: post-install hooks (e.g., for module generation).
- phases = [] # type: List[str]
- #: This attribute is used in UI queries that require to know which
- #: build-system class we are using
- build_system_class = "BundlePackage"
-
- #: Bundle packages do not have associated source or binary code.
- has_code = False
-
-
-class Package(PackageBase):
- """General purpose class with a single ``install``
- phase that needs to be coded by packagers.
- """
-
- #: The one and only phase
- phases = ["install"]
- #: This attribute is 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
- run_after("install")(PackageBase.sanity_check_prefix)
- # On macOS, force rpaths for shared library IDs and remove duplicate rpaths
- run_after("install")(PackageBase.apply_macos_rpath_fixups)
-
-
def install_dependency_symlinks(pkg, spec, prefix):
"""
Execute a dummy install and flatten dependencies.
diff --git a/lib/spack/spack/patch.py b/lib/spack/spack/patch.py
index 553b04fda9..06841fa73f 100644
--- a/lib/spack/spack/patch.py
+++ b/lib/spack/spack/patch.py
@@ -352,7 +352,7 @@ class PatchCache(object):
Arguments:
sha256 (str): sha256 hash to look up
- pkg (spack.package_base.Package): Package object to get patch for.
+ pkg (spack.package_base.PackageBase): Package object to get patch for.
We build patch objects lazily because building them requires that
we have information about the package's location in its repo.
diff --git a/lib/spack/spack/test/audit.py b/lib/spack/spack/test/audit.py
index eded1a92f2..bc5dd3edca 100644
--- a/lib/spack/spack/test/audit.py
+++ b/lib/spack/spack/test/audit.py
@@ -22,7 +22,7 @@ import spack.config
# This package has a GitHub patch URL without full_index=1
(["invalid-github-patch-url"], ["PKG-DIRECTIVES", "PKG-PROPERTIES"]),
# This package has a stand-alone 'test' method in build-time callbacks
- (["test-build-callbacks"], ["PKG-DIRECTIVES", "PKG-PROPERTIES"]),
+ (["fail-test-audit"], ["PKG-DIRECTIVES", "PKG-PROPERTIES"]),
# This package has no issues
(["mpileaks"], None),
# This package has a conflict with a trigger which cannot constrain the constraint
diff --git a/lib/spack/spack/test/build_systems.py b/lib/spack/spack/test/build_systems.py
index f660596bb7..c4255eef49 100644
--- a/lib/spack/spack/test/build_systems.py
+++ b/lib/spack/spack/test/build_systems.py
@@ -13,10 +13,11 @@ import pytest
import llnl.util.filesystem as fs
import spack.build_systems.autotools
+import spack.build_systems.cmake
import spack.environment
import spack.platforms
import spack.repo
-from spack.build_environment import ChildError, get_std_cmake_args, setup_package
+from spack.build_environment import ChildError, setup_package
from spack.spec import Spec
from spack.util.executable import which
@@ -144,7 +145,7 @@ class TestAutotoolsPackage(object):
# Assert the libtool archive is not there and we have
# a log of removed files
- assert not os.path.exists(s.package.libtool_archive_file)
+ assert not os.path.exists(s.package.builder.libtool_archive_file)
search_directory = os.path.join(s.prefix, ".spack")
libtool_deletion_log = fs.find(search_directory, "removed_la_files.txt", recursive=True)
assert libtool_deletion_log
@@ -155,11 +156,11 @@ class TestAutotoolsPackage(object):
# Install a package that creates a mock libtool archive,
# patch its package to preserve the installation
s = Spec("libtool-deletion").concretized()
- monkeypatch.setattr(s.package, "install_libtool_archives", True)
+ monkeypatch.setattr(type(s.package.builder), "install_libtool_archives", True)
s.package.do_install(explicit=True)
# Assert libtool archives are installed
- assert os.path.exists(s.package.libtool_archive_file)
+ assert os.path.exists(s.package.builder.libtool_archive_file)
def test_autotools_gnuconfig_replacement(self, mutable_database):
"""
@@ -253,22 +254,23 @@ class TestCMakePackage(object):
def test_cmake_std_args(self):
# Call the function on a CMakePackage instance
s = Spec("cmake-client").concretized()
- assert s.package.std_cmake_args == get_std_cmake_args(s.package)
+ expected = spack.build_systems.cmake.CMakeBuilder.std_args(s.package)
+ assert s.package.builder.std_cmake_args == expected
# Call it on another kind of package
s = Spec("mpich").concretized()
- assert get_std_cmake_args(s.package)
+ assert spack.build_systems.cmake.CMakeBuilder.std_args(s.package)
- def test_cmake_bad_generator(self):
+ def test_cmake_bad_generator(self, monkeypatch):
s = Spec("cmake-client").concretized()
- s.package.generator = "Yellow Sticky Notes"
+ monkeypatch.setattr(type(s.package), "generator", "Yellow Sticky Notes", raising=False)
with pytest.raises(spack.package_base.InstallError):
- get_std_cmake_args(s.package)
+ s.package.builder.std_cmake_args
def test_cmake_secondary_generator(config, mock_packages):
s = Spec("cmake-client").concretized()
s.package.generator = "CodeBlocks - Unix Makefiles"
- assert get_std_cmake_args(s.package)
+ assert s.package.builder.std_cmake_args
def test_define(self):
s = Spec("cmake-client").concretized()
@@ -361,7 +363,7 @@ def test_autotools_args_from_conditional_variant(config, mock_packages):
is not met. When this is the case, the variant is not set in the spec."""
s = Spec("autotools-conditional-variants-test").concretized()
assert "example" not in s.variants
- assert len(s.package._activate_or_not("example", "enable", "disable")) == 0
+ assert len(s.package.builder._activate_or_not("example", "enable", "disable")) == 0
def test_autoreconf_search_path_args_multiple(config, mock_packages, tmpdir):
diff --git a/lib/spack/spack/test/builder.py b/lib/spack/spack/test/builder.py
new file mode 100644
index 0000000000..bda72c8b49
--- /dev/null
+++ b/lib/spack/spack/test/builder.py
@@ -0,0 +1,123 @@
+# Copyright 2013-2022 Lawrence Livermore National Security, LLC and other
+# Spack Project Developers. See the top-level COPYRIGHT file for details.
+#
+# SPDX-License-Identifier: (Apache-2.0 OR MIT)
+import os.path
+
+import pytest
+
+import spack.paths
+
+
+@pytest.fixture()
+def builder_test_repository():
+ builder_test_path = os.path.join(spack.paths.repos_path, "builder.test")
+ with spack.repo.use_repositories(builder_test_path) as mock_repo:
+ yield mock_repo
+
+
+@pytest.mark.parametrize(
+ "spec_str,expected_values",
+ [
+ (
+ "callbacks@2.0",
+ [
+ ("BEFORE_INSTALL_1_CALLED", "1"),
+ ("BEFORE_INSTALL_2_CALLED", "1"),
+ ("CALLBACKS_INSTALL_CALLED", "1"),
+ ("AFTER_INSTALL_1_CALLED", "1"),
+ ("TEST_VALUE", "3"),
+ ("INSTALL_VALUE", "CALLBACKS"),
+ ],
+ ),
+ # The last callback is conditional on "@1.0", check it's being executed
+ (
+ "callbacks@1.0",
+ [
+ ("BEFORE_INSTALL_1_CALLED", "1"),
+ ("BEFORE_INSTALL_2_CALLED", "1"),
+ ("CALLBACKS_INSTALL_CALLED", "1"),
+ ("AFTER_INSTALL_1_CALLED", "1"),
+ ("AFTER_INSTALL_2_CALLED", "1"),
+ ("TEST_VALUE", "4"),
+ ("INSTALL_VALUE", "CALLBACKS"),
+ ],
+ ),
+ # The package below adds to "callbacks" using inheritance, test that using super()
+ # works with builder hierarchies
+ (
+ "inheritance@1.0",
+ [
+ ("DERIVED_BEFORE_INSTALL_CALLED", "1"),
+ ("BEFORE_INSTALL_1_CALLED", "1"),
+ ("BEFORE_INSTALL_2_CALLED", "1"),
+ ("CALLBACKS_INSTALL_CALLED", "1"),
+ ("INHERITANCE_INSTALL_CALLED", "1"),
+ ("AFTER_INSTALL_1_CALLED", "1"),
+ ("AFTER_INSTALL_2_CALLED", "1"),
+ ("TEST_VALUE", "4"),
+ ("INSTALL_VALUE", "INHERITANCE"),
+ ],
+ ),
+ # Generate custom phases using a GenericBuilder
+ (
+ "custom-phases",
+ [
+ ("CONFIGURE_CALLED", "1"),
+ ("INSTALL_CALLED", "1"),
+ ("LAST_PHASE", "INSTALL"),
+ ],
+ ),
+ # Old-style package, with phase defined in base builder
+ (
+ "old-style-autotools@1.0",
+ [
+ ("AFTER_AUTORECONF_1_CALLED", "1"),
+ ],
+ ),
+ (
+ "old-style-autotools@2.0",
+ [
+ ("AFTER_AUTORECONF_2_CALLED", "1"),
+ ],
+ ),
+ (
+ "old-style-custom-phases",
+ [
+ ("AFTER_CONFIGURE_CALLED", "1"),
+ ("TEST_VALUE", "0"),
+ ],
+ ),
+ ],
+)
+@pytest.mark.usefixtures("builder_test_repository", "config")
+@pytest.mark.disable_clean_stage_check
+def test_callbacks_and_installation_procedure(spec_str, expected_values, working_env):
+ """Test the correct execution of callbacks and installation procedures for packages."""
+ s = spack.spec.Spec(spec_str).concretized()
+ builder = spack.builder.create(s.package)
+ for phase_fn in builder:
+ phase_fn.execute()
+
+ # Check calls have produced the expected side effects
+ for var_name, expected in expected_values:
+ assert os.environ[var_name] == expected, os.environ
+
+
+@pytest.mark.usefixtures("builder_test_repository", "config")
+@pytest.mark.parametrize(
+ "spec_str,method_name,expected",
+ [
+ # Call a function defined on the package, which calls the same function defined
+ # on the super(builder)
+ ("old-style-autotools", "configure_args", ["--with-foo"]),
+ # Call a function defined on the package, which calls the same function defined on the
+ # super(pkg), which calls the same function defined in the super(builder)
+ ("old-style-derived", "configure_args", ["--with-bar", "--with-foo"]),
+ ],
+)
+def test_old_style_compatibility_with_super(spec_str, method_name, expected):
+ s = spack.spec.Spec(spec_str).concretized()
+ builder = spack.builder.create(s.package)
+ value = getattr(builder, method_name)()
+ assert value == expected
diff --git a/lib/spack/spack/test/cmd/info.py b/lib/spack/spack/test/cmd/info.py
index 55b875e022..2bbf4d3a7f 100644
--- a/lib/spack/spack/test/cmd/info.py
+++ b/lib/spack/spack/test/cmd/info.py
@@ -43,7 +43,7 @@ def test_it_just_runs(pkg):
def test_info_noversion(mock_packages, print_buffer):
- """Check that a mock package with no versions or variants outputs None."""
+ """Check that a mock package with no versions outputs None."""
info("noversion")
line_iter = iter(print_buffer)
@@ -52,7 +52,7 @@ def test_info_noversion(mock_packages, print_buffer):
has = [desc in line for desc in ["Preferred", "Safe", "Deprecated"]]
if not any(has):
continue
- elif "Variants" not in line:
+ else:
continue
assert "None" in next(line_iter).strip()
diff --git a/lib/spack/spack/test/cmd/install.py b/lib/spack/spack/test/cmd/install.py
index 8c37ea3e98..41de6565a1 100644
--- a/lib/spack/spack/test/cmd/install.py
+++ b/lib/spack/spack/test/cmd/install.py
@@ -1087,7 +1087,7 @@ def test_install_empty_env(
("test-install-callbacks", "undefined-install-test"),
],
)
-def test_install_callbacks_fail(install_mockery, mock_fetch, name, method):
+def test_installation_fail_tests(install_mockery, mock_fetch, name, method):
output = install("--test=root", "--no-cache", name, fail_on_error=False)
assert output.count(method) == 2
diff --git a/lib/spack/spack/test/cmd/test.py b/lib/spack/spack/test/cmd/test.py
index e842dbc465..fac639309d 100644
--- a/lib/spack/spack/test/cmd/test.py
+++ b/lib/spack/spack/test/cmd/test.py
@@ -237,8 +237,7 @@ def test_test_list_all(mock_packages):
"simple-standalone-test",
"test-error",
"test-fail",
- "test-build-callbacks",
- "test-install-callbacks",
+ "fail-test-audit",
]
)
diff --git a/lib/spack/spack/test/concretize.py b/lib/spack/spack/test/concretize.py
index 4d15d48dbe..ccdc19a174 100644
--- a/lib/spack/spack/test/concretize.py
+++ b/lib/spack/spack/test/concretize.py
@@ -228,7 +228,7 @@ class TestConcretize(object):
check_concretize(spec)
def test_concretize_mention_build_dep(self):
- spec = check_concretize("cmake-client ^cmake@3.4.3")
+ spec = check_concretize("cmake-client ^cmake@3.21.3")
# Check parent's perspective of child
to_dependencies = spec.edges_to_dependencies(name="cmake")
diff --git a/lib/spack/spack/test/concretize_requirements.py b/lib/spack/spack/test/concretize_requirements.py
index 50cc6baa9a..e43203b969 100644
--- a/lib/spack/spack/test/concretize_requirements.py
+++ b/lib/spack/spack/test/concretize_requirements.py
@@ -7,6 +7,7 @@ import sys
import pytest
+import spack.build_systems.generic
import spack.config
import spack.repo
import spack.util.spack_yaml as syaml
@@ -102,7 +103,7 @@ def fake_installs(monkeypatch, tmpdir):
stage_path = str(tmpdir.ensure("fake-stage", dir=True))
universal_unused_stage = spack.stage.DIYStage(stage_path)
monkeypatch.setattr(
- spack.package_base.Package, "_make_stage", MakeStage(universal_unused_stage)
+ spack.build_systems.generic.Package, "_make_stage", MakeStage(universal_unused_stage)
)
diff --git a/lib/spack/spack/test/config.py b/lib/spack/spack/test/config.py
index f4d0b570fd..f5f91f039d 100644
--- a/lib/spack/spack/test/config.py
+++ b/lib/spack/spack/test/config.py
@@ -18,6 +18,7 @@ from llnl.util.filesystem import getuid, join_path, mkdirp, touch, touchp
import spack.config
import spack.environment as ev
import spack.main
+import spack.package_base
import spack.paths
import spack.repo
import spack.schema.compilers
@@ -1170,13 +1171,13 @@ def test_license_dir_config(mutable_config, mock_packages):
"""Ensure license directory is customizable"""
expected_dir = spack.paths.default_license_dir
assert spack.config.get("config:license_dir") == expected_dir
- assert spack.package.Package.global_license_dir == expected_dir
+ assert spack.package_base.PackageBase.global_license_dir == expected_dir
assert spack.repo.path.get_pkg_class("a").global_license_dir == expected_dir
rel_path = os.path.join(os.path.sep, "foo", "bar", "baz")
spack.config.set("config:license_dir", rel_path)
assert spack.config.get("config:license_dir") == rel_path
- assert spack.package.Package.global_license_dir == rel_path
+ assert spack.package_base.PackageBase.global_license_dir == rel_path
assert spack.repo.path.get_pkg_class("a").global_license_dir == rel_path
diff --git a/lib/spack/spack/test/graph.py b/lib/spack/spack/test/graph.py
index e7aafe4b0d..60d041d60b 100644
--- a/lib/spack/spack/test/graph.py
+++ b/lib/spack/spack/test/graph.py
@@ -104,6 +104,19 @@ o dyninst
|/
o libelf
"""
+ or graph_str
+ == r"""o mpileaks
+|\
+o | callpath
+|\|
+| o mpich
+|
+o dyninst
+|\
+o | libdwarf
+|/
+o libelf
+"""
)
diff --git a/lib/spack/spack/test/install.py b/lib/spack/spack/test/install.py
index 9093593bed..e8b6f415d3 100644
--- a/lib/spack/spack/test/install.py
+++ b/lib/spack/spack/test/install.py
@@ -144,13 +144,12 @@ class MockStage(object):
return getattr(self.wrapped_stage, attr)
-def test_partial_install_delete_prefix_and_stage(install_mockery, mock_fetch):
+def test_partial_install_delete_prefix_and_stage(install_mockery, mock_fetch, working_env):
s = Spec("canfail").concretized()
instance_rm_prefix = s.package.remove_prefix
try:
- s.package.succeed = False
s.package.remove_prefix = mock_remove_prefix
with pytest.raises(MockInstallError):
s.package.do_install()
@@ -161,7 +160,7 @@ def test_partial_install_delete_prefix_and_stage(install_mockery, mock_fetch):
# must clear failure markings for the package before re-installing it
spack.store.db.clear_failure(s, True)
- s.package.succeed = True
+ s.package.set_install_succeed()
s.package.stage = MockStage(s.package.stage)
s.package.do_install(restage=True)
@@ -174,18 +173,20 @@ def test_partial_install_delete_prefix_and_stage(install_mockery, mock_fetch):
@pytest.mark.disable_clean_stage_check
-def test_failing_overwrite_install_should_keep_previous_installation(mock_fetch, install_mockery):
+def test_failing_overwrite_install_should_keep_previous_installation(
+ mock_fetch, install_mockery, working_env
+):
"""
Make sure that whenever `spack install --overwrite` fails, spack restores
the original install prefix instead of cleaning it.
"""
# Do a successful install
s = Spec("canfail").concretized()
- s.package.succeed = True
+ s.package.set_install_succeed()
# Do a failing overwrite install
s.package.do_install()
- s.package.succeed = False
+ s.package.set_install_fail()
kwargs = {"overwrite": [s.dag_hash()]}
with pytest.raises(Exception):
@@ -238,13 +239,11 @@ def test_install_dependency_symlinks_pkg(install_mockery, mock_fetch, mutable_mo
def test_install_times(install_mockery, mock_fetch, mutable_mock_repo):
"""Test install times added."""
- spec = Spec("dev-build-test-install-phases")
- spec.concretize()
- pkg = spec.package
- pkg.do_install()
+ spec = Spec("dev-build-test-install-phases").concretized()
+ spec.package.do_install()
# Ensure dependency directory exists after the installation.
- install_times = os.path.join(pkg.prefix, ".spack", "install_times.json")
+ install_times = os.path.join(spec.package.prefix, ".spack", "install_times.json")
assert os.path.isfile(install_times)
# Ensure the phases are included
@@ -346,12 +345,11 @@ def test_installed_upstream(install_upstream, mock_fetch):
@pytest.mark.disable_clean_stage_check
-def test_partial_install_keep_prefix(install_mockery, mock_fetch, monkeypatch):
+def test_partial_install_keep_prefix(install_mockery, mock_fetch, monkeypatch, working_env):
s = Spec("canfail").concretized()
# If remove_prefix is called at any point in this test, that is an error
- s.package.succeed = False # make the build fail
- monkeypatch.setattr(spack.package_base.Package, "remove_prefix", mock_remove_prefix)
+ monkeypatch.setattr(spack.package_base.PackageBase, "remove_prefix", mock_remove_prefix)
with pytest.raises(spack.build_environment.ChildError):
s.package.do_install(keep_prefix=True)
assert os.path.exists(s.package.prefix)
@@ -359,7 +357,7 @@ def test_partial_install_keep_prefix(install_mockery, mock_fetch, monkeypatch):
# must clear failure markings for the package before re-installing it
spack.store.db.clear_failure(s, True)
- s.package.succeed = True # make the build succeed
+ s.package.set_install_succeed()
s.package.stage = MockStage(s.package.stage)
s.package.do_install(keep_prefix=True)
assert s.package.spec.installed
@@ -368,14 +366,14 @@ def test_partial_install_keep_prefix(install_mockery, mock_fetch, monkeypatch):
def test_second_install_no_overwrite_first(install_mockery, mock_fetch, monkeypatch):
s = Spec("canfail").concretized()
- monkeypatch.setattr(spack.package_base.Package, "remove_prefix", mock_remove_prefix)
+ monkeypatch.setattr(spack.package_base.PackageBase, "remove_prefix", mock_remove_prefix)
- s.package.succeed = True
+ s.package.set_install_succeed()
s.package.do_install()
assert s.package.spec.installed
# If Package.install is called after this point, it will fail
- s.package.succeed = False
+ s.package.set_install_fail()
s.package.do_install()
@@ -589,7 +587,9 @@ def test_log_install_with_build_files(install_mockery, monkeypatch):
source = spec.package.stage.source_path
config = os.path.join(source, "config.log")
fs.touchp(config)
- spec.package.archive_files = ["missing", "..", config]
+ monkeypatch.setattr(
+ type(spec.package), "archive_files", ["missing", "..", config], raising=False
+ )
spack.installer.log(spec.package)
diff --git a/lib/spack/spack/test/repo.py b/lib/spack/spack/test/repo.py
index fcd009edec..402723d226 100644
--- a/lib/spack/spack/test/repo.py
+++ b/lib/spack/spack/test/repo.py
@@ -113,14 +113,14 @@ def test_absolute_import_spack_packages_as_python_modules(mock_packages):
assert hasattr(spack.pkg.builtin.mock, "mpileaks")
assert hasattr(spack.pkg.builtin.mock.mpileaks, "Mpileaks")
assert isinstance(spack.pkg.builtin.mock.mpileaks.Mpileaks, spack.package_base.PackageMeta)
- assert issubclass(spack.pkg.builtin.mock.mpileaks.Mpileaks, spack.package_base.Package)
+ assert issubclass(spack.pkg.builtin.mock.mpileaks.Mpileaks, spack.package_base.PackageBase)
def test_relative_import_spack_packages_as_python_modules(mock_packages):
from spack.pkg.builtin.mock.mpileaks import Mpileaks
assert isinstance(Mpileaks, spack.package_base.PackageMeta)
- assert issubclass(Mpileaks, spack.package_base.Package)
+ assert issubclass(Mpileaks, spack.package_base.PackageBase)
def test_all_virtual_packages_have_default_providers():
diff --git a/lib/spack/spack/util/package_hash.py b/lib/spack/spack/util/package_hash.py
index 4877748338..f4435fbba4 100644
--- a/lib/spack/spack/util/package_hash.py
+++ b/lib/spack/spack/util/package_hash.py
@@ -64,7 +64,7 @@ class RemoveDirectives(ast.NodeTransformer):
# list of URL attributes and metadata attributes
# these will be removed from packages.
self.metadata_attrs = [s.url_attr for s in spack.fetch_strategy.all_strategies]
- self.metadata_attrs += spack.package_base.Package.metadata_attrs
+ self.metadata_attrs += spack.package_base.PackageBase.metadata_attrs
self.spec = spec
self.in_classdef = False # used to avoid nested classdefs
@@ -158,6 +158,7 @@ class TagMultiMethods(ast.NodeVisitor):
def visit_FunctionDef(self, func):
conditions = []
+
for dec in func.decorator_list:
if isinstance(dec, ast.Call) and dec.func.id == "when":
try:
diff --git a/lib/spack/spack/util/web.py b/lib/spack/spack/util/web.py
index fa4119f917..939ec669c0 100644
--- a/lib/spack/spack/util/web.py
+++ b/lib/spack/spack/util/web.py
@@ -784,7 +784,7 @@ def find_versions_of_archive(
list_depth (int): max depth to follow links on list_url pages.
Defaults to 0.
concurrency (int): maximum number of concurrent requests
- reference_package (spack.package_base.Package or None): a spack package
+ reference_package (spack.package_base.PackageBase or None): a spack package
used as a reference for url detection. Uses the url_for_version
method on the package to produce reference urls which, if found,
are preferred.
diff --git a/lib/spack/spack/variant.py b/lib/spack/spack/variant.py
index 864ea26446..92e512219b 100644
--- a/lib/spack/spack/variant.py
+++ b/lib/spack/spack/variant.py
@@ -96,7 +96,7 @@ class Variant(object):
Args:
vspec (Variant): instance to be validated
- pkg_cls (spack.package_base.Package): the package class
+ pkg_cls (spack.package_base.PackageBase): the package class
that required the validation, if available
Raises:
diff --git a/share/spack/gitlab/cloud_pipelines/stacks/aws-ahug-aarch64/spack.yaml b/share/spack/gitlab/cloud_pipelines/stacks/aws-ahug-aarch64/spack.yaml
index 488309b294..5ede7b882c 100644
--- a/share/spack/gitlab/cloud_pipelines/stacks/aws-ahug-aarch64/spack.yaml
+++ b/share/spack/gitlab/cloud_pipelines/stacks/aws-ahug-aarch64/spack.yaml
@@ -24,6 +24,8 @@ spack:
- openmpi
- mpich
variants: +mpi
+ tbb:
+ require: "intel-tbb"
binutils:
variants: +ld +gold +headers +libiberty ~nls
version:
diff --git a/share/spack/gitlab/cloud_pipelines/stacks/aws-isc-aarch64/spack.yaml b/share/spack/gitlab/cloud_pipelines/stacks/aws-isc-aarch64/spack.yaml
index 61eb974e70..262de722a5 100644
--- a/share/spack/gitlab/cloud_pipelines/stacks/aws-isc-aarch64/spack.yaml
+++ b/share/spack/gitlab/cloud_pipelines/stacks/aws-isc-aarch64/spack.yaml
@@ -24,6 +24,8 @@ spack:
- openmpi
- mpich
variants: +mpi
+ tbb:
+ require: "intel-tbb"
binutils:
variants: +ld +gold +headers +libiberty ~nls
version:
diff --git a/share/spack/gitlab/cloud_pipelines/stacks/aws-isc/spack.yaml b/share/spack/gitlab/cloud_pipelines/stacks/aws-isc/spack.yaml
index ed2f205d42..c71b514c6e 100644
--- a/share/spack/gitlab/cloud_pipelines/stacks/aws-isc/spack.yaml
+++ b/share/spack/gitlab/cloud_pipelines/stacks/aws-isc/spack.yaml
@@ -24,6 +24,8 @@ spack:
- openmpi
- mpich
variants: +mpi
+ tbb:
+ require: "intel-tbb"
binutils:
variants: +ld +gold +headers +libiberty ~nls
version:
diff --git a/share/spack/gitlab/cloud_pipelines/stacks/build_systems/spack.yaml b/share/spack/gitlab/cloud_pipelines/stacks/build_systems/spack.yaml
index ee6c9457c3..b3b4e8697c 100644
--- a/share/spack/gitlab/cloud_pipelines/stacks/build_systems/spack.yaml
+++ b/share/spack/gitlab/cloud_pipelines/stacks/build_systems/spack.yaml
@@ -14,6 +14,8 @@ spack:
definitions:
- default_specs:
+ - 'uncrustify build_system=autotools'
+ - 'uncrustify build_system=cmake'
- lz4 # MakefilePackage
- mpich~fortran # AutotoolsPackage
- py-setuptools # PythonPackage
diff --git a/share/spack/gitlab/cloud_pipelines/stacks/e4s/spack.yaml b/share/spack/gitlab/cloud_pipelines/stacks/e4s/spack.yaml
index 8d6669252a..4d7b875772 100644
--- a/share/spack/gitlab/cloud_pipelines/stacks/e4s/spack.yaml
+++ b/share/spack/gitlab/cloud_pipelines/stacks/e4s/spack.yaml
@@ -21,6 +21,8 @@ spack:
mpi: [mpich]
target: [x86_64]
variants: +mpi
+ tbb:
+ require: "intel-tbb"
binutils:
variants: +ld +gold +headers +libiberty ~nls
cuda:
diff --git a/share/spack/gitlab/cloud_pipelines/stacks/tutorial/spack.yaml b/share/spack/gitlab/cloud_pipelines/stacks/tutorial/spack.yaml
index ea34141ba1..0090fd2f83 100644
--- a/share/spack/gitlab/cloud_pipelines/stacks/tutorial/spack.yaml
+++ b/share/spack/gitlab/cloud_pipelines/stacks/tutorial/spack.yaml
@@ -18,6 +18,8 @@ spack:
packages:
all:
target: [x86_64]
+ tbb:
+ require: 'intel-tbb'
definitions:
- gcc_system_packages:
diff --git a/var/spack/repos/builder.test/packages/callbacks/package.py b/var/spack/repos/builder.test/packages/callbacks/package.py
new file mode 100644
index 0000000000..ea6787d9b5
--- /dev/null
+++ b/var/spack/repos/builder.test/packages/callbacks/package.py
@@ -0,0 +1,45 @@
+# Copyright 2013-2022 Lawrence Livermore National Security, LLC and other
+# Spack Project Developers. See the top-level COPYRIGHT file for details.
+#
+# SPDX-License-Identifier: (Apache-2.0 OR MIT)
+import os
+
+import spack.build_systems.generic
+from spack.package import *
+
+
+class Callbacks(Package):
+ """Package used to verify that callbacks on phases work correctly, including conditions"""
+
+ homepage = "http://www.example.com"
+ url = "http://www.example.com/a-1.0.tar.gz"
+
+ version("2.0", "abcdef0123456789abcdef0123456789")
+ version("1.0", "0123456789abcdef0123456789abcdef")
+
+
+class GenericBuilder(spack.build_systems.generic.GenericBuilder):
+ def install(self, pkg, spec, prefix):
+ os.environ["CALLBACKS_INSTALL_CALLED"] = "1"
+ os.environ["INSTALL_VALUE"] = "CALLBACKS"
+ mkdirp(prefix.bin)
+
+ @run_before("install")
+ def before_install_1(self):
+ os.environ["BEFORE_INSTALL_1_CALLED"] = "1"
+ os.environ["TEST_VALUE"] = "1"
+
+ @run_before("install")
+ def before_install_2(self):
+ os.environ["BEFORE_INSTALL_2_CALLED"] = "1"
+ os.environ["TEST_VALUE"] = "2"
+
+ @run_after("install")
+ def after_install_1(self):
+ os.environ["AFTER_INSTALL_1_CALLED"] = "1"
+ os.environ["TEST_VALUE"] = "3"
+
+ @run_after("install", when="@1.0")
+ def after_install_2(self):
+ os.environ["AFTER_INSTALL_2_CALLED"] = "1"
+ os.environ["TEST_VALUE"] = "4"
diff --git a/var/spack/repos/builder.test/packages/custom-phases/package.py b/var/spack/repos/builder.test/packages/custom-phases/package.py
new file mode 100644
index 0000000000..37b26e37d0
--- /dev/null
+++ b/var/spack/repos/builder.test/packages/custom-phases/package.py
@@ -0,0 +1,31 @@
+# Copyright 2013-2022 Lawrence Livermore National Security, LLC and other
+# Spack Project Developers. See the top-level COPYRIGHT file for details.
+#
+# SPDX-License-Identifier: (Apache-2.0 OR MIT)
+import os
+
+import spack.build_systems.generic
+from spack.package import *
+
+
+class CustomPhases(Package):
+ """Package used to verify that we can set custom phases on builders"""
+
+ homepage = "http://www.example.com"
+ url = "http://www.example.com/a-1.0.tar.gz"
+
+ version("2.0", "abcdef0123456789abcdef0123456789")
+ version("1.0", "0123456789abcdef0123456789abcdef")
+
+
+class GenericBuilder(spack.build_systems.generic.GenericBuilder):
+ phases = ["configure", "install"]
+
+ def configure(self, pkg, spec, prefix):
+ os.environ["CONFIGURE_CALLED"] = "1"
+ os.environ["LAST_PHASE"] = "CONFIGURE"
+
+ def install(self, pkg, spec, prefix):
+ os.environ["INSTALL_CALLED"] = "1"
+ os.environ["LAST_PHASE"] = "INSTALL"
+ mkdirp(prefix.bin)
diff --git a/var/spack/repos/builder.test/packages/gnuconfig/package.py b/var/spack/repos/builder.test/packages/gnuconfig/package.py
new file mode 100644
index 0000000000..53f8a10705
--- /dev/null
+++ b/var/spack/repos/builder.test/packages/gnuconfig/package.py
@@ -0,0 +1,15 @@
+# Copyright 2013-2022 Lawrence Livermore National Security, LLC and other
+# Spack Project Developers. See the top-level COPYRIGHT file for details.
+#
+# SPDX-License-Identifier: (Apache-2.0 OR MIT)
+from spack.package import *
+
+
+class Gnuconfig(Package):
+ """This package is needed to allow mocking AutotoolsPackage objects"""
+
+ homepage = "http://www.example.com"
+ url = "http://www.example.com/a-1.0.tar.gz"
+
+ version("2.0", "abcdef0123456789abcdef0123456789")
+ version("1.0", "0123456789abcdef0123456789abcdef")
diff --git a/var/spack/repos/builder.test/packages/inheritance/package.py b/var/spack/repos/builder.test/packages/inheritance/package.py
new file mode 100644
index 0000000000..307d93ca80
--- /dev/null
+++ b/var/spack/repos/builder.test/packages/inheritance/package.py
@@ -0,0 +1,26 @@
+# Copyright 2013-2022 Lawrence Livermore National Security, LLC and other
+# Spack Project Developers. See the top-level COPYRIGHT file for details.
+#
+# SPDX-License-Identifier: (Apache-2.0 OR MIT)
+import os
+
+import spack.pkg.builder.test.callbacks
+from spack.package import *
+
+
+class Inheritance(spack.pkg.builder.test.callbacks.Callbacks):
+ """Package used to verify that inheritance among packages work as expected"""
+
+ pass
+
+
+class GenericBuilder(spack.pkg.builder.test.callbacks.GenericBuilder):
+ def install(self, pkg, spec, prefix):
+ super(GenericBuilder, self).install(pkg, spec, prefix)
+ os.environ["INHERITANCE_INSTALL_CALLED"] = "1"
+ os.environ["INSTALL_VALUE"] = "INHERITANCE"
+
+ @run_before("install")
+ def derived_before_install(self):
+ os.environ["DERIVED_BEFORE_INSTALL_CALLED"] = "1"
+ os.environ["TEST_VALUE"] = "0"
diff --git a/var/spack/repos/builder.test/packages/old-style-autotools/package.py b/var/spack/repos/builder.test/packages/old-style-autotools/package.py
new file mode 100644
index 0000000000..56213d7158
--- /dev/null
+++ b/var/spack/repos/builder.test/packages/old-style-autotools/package.py
@@ -0,0 +1,50 @@
+# Copyright 2013-2022 Lawrence Livermore National Security, LLC and other
+# Spack Project Developers. See the top-level COPYRIGHT file for details.
+#
+# SPDX-License-Identifier: (Apache-2.0 OR MIT)
+import os
+
+from spack.package import *
+
+
+class OldStyleAutotools(AutotoolsPackage):
+ """Package used to verify that old-style packages work correctly when executing the
+ installation procedure.
+ """
+
+ homepage = "http://www.example.com"
+ url = "http://www.example.com/a-1.0.tar.gz"
+
+ version("2.0", "abcdef0123456789abcdef0123456789")
+ version("1.0", "0123456789abcdef0123456789abcdef")
+
+ def configure(self, spec, prefix):
+ pass
+
+ def build(self, spec, prefix):
+ pass
+
+ def install(self, spec, prefix):
+ mkdirp(prefix.bin)
+
+ def configure_args(self):
+ """This override a function in the builder and construct the result using a method
+ defined in this class and a super method defined in the builder.
+ """
+ return [self.foo()] + super(OldStyleAutotools, self).configure_args()
+
+ def foo(self):
+ return "--with-foo"
+
+ @run_before("autoreconf")
+ def create_configure(self):
+ mkdirp(self.configure_directory)
+ touch(self.configure_abs_path)
+
+ @run_after("autoreconf", when="@1.0")
+ def after_autoreconf_1(self):
+ os.environ["AFTER_AUTORECONF_1_CALLED"] = "1"
+
+ @run_after("autoreconf", when="@2.0")
+ def after_autoreconf_2(self):
+ os.environ["AFTER_AUTORECONF_2_CALLED"] = "1"
diff --git a/var/spack/repos/builder.test/packages/old-style-custom-phases/package.py b/var/spack/repos/builder.test/packages/old-style-custom-phases/package.py
new file mode 100644
index 0000000000..afa5b52d70
--- /dev/null
+++ b/var/spack/repos/builder.test/packages/old-style-custom-phases/package.py
@@ -0,0 +1,34 @@
+# Copyright 2013-2022 Lawrence Livermore National Security, LLC and other
+# Spack Project Developers. See the top-level COPYRIGHT file for details.
+#
+# SPDX-License-Identifier: (Apache-2.0 OR MIT)
+import os
+
+from spack.package import *
+
+
+class OldStyleCustomPhases(AutotoolsPackage):
+ """Package used to verify that old-style packages work correctly when defining custom
+ phases (though it's not recommended for packagers to do so).
+ """
+
+ homepage = "http://www.example.com"
+ url = "http://www.example.com/a-1.0.tar.gz"
+
+ version("2.0", "abcdef0123456789abcdef0123456789")
+ version("1.0", "0123456789abcdef0123456789abcdef")
+
+ phases = ["configure"]
+
+ def configure(self, spec, prefix):
+ mkdirp(prefix.bin)
+
+ @run_after("configure")
+ def after_configure(self):
+ os.environ["AFTER_CONFIGURE_CALLED"] = "1"
+ os.environ["TEST_VALUE"] = "0"
+
+ @run_after("install")
+ def after_install(self):
+ os.environ["AFTER_INSTALL_CALLED"] = "1"
+ os.environ["TEST_VALUE"] = "1"
diff --git a/var/spack/repos/builder.test/packages/old-style-derived/package.py b/var/spack/repos/builder.test/packages/old-style-derived/package.py
new file mode 100644
index 0000000000..a7dd026217
--- /dev/null
+++ b/var/spack/repos/builder.test/packages/old-style-derived/package.py
@@ -0,0 +1,21 @@
+# Copyright 2013-2022 Lawrence Livermore National Security, LLC and other
+# Spack Project Developers. See the top-level COPYRIGHT file for details.
+#
+# SPDX-License-Identifier: (Apache-2.0 OR MIT)
+import spack.pkg.builder.test.old_style_autotools
+from spack.package import *
+
+
+class OldStyleDerived(spack.pkg.builder.test.old_style_autotools.OldStyleAutotools):
+ """Package used to verify that old-style packages work correctly when executing the
+ installation procedure.
+ """
+
+ homepage = "http://www.example.com"
+ url = "http://www.example.com/a-1.0.tar.gz"
+
+ version("2.0", "abcdef0123456789abcdef0123456789")
+ version("1.0", "0123456789abcdef0123456789abcdef")
+
+ def configure_args(self):
+ return ["--with-bar"] + super(OldStyleDerived, self).configure_args()
diff --git a/var/spack/repos/builder.test/repo.yaml b/var/spack/repos/builder.test/repo.yaml
new file mode 100644
index 0000000000..a9031afe21
--- /dev/null
+++ b/var/spack/repos/builder.test/repo.yaml
@@ -0,0 +1,2 @@
+repo:
+ namespace: builder.test
diff --git a/var/spack/repos/builtin.mock/packages/a/package.py b/var/spack/repos/builtin.mock/packages/a/package.py
index b556fbf06f..5dbcd1f9c2 100644
--- a/var/spack/repos/builtin.mock/packages/a/package.py
+++ b/var/spack/repos/builtin.mock/packages/a/package.py
@@ -2,7 +2,7 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
-
+import spack.build_systems.autotools
from spack.package import *
@@ -32,21 +32,23 @@ class A(AutotoolsPackage):
parallel = False
+
+class AutotoolsBuilder(spack.build_systems.autotools.AutotoolsBuilder):
def with_or_without_fee(self, activated):
if not activated:
return "--no-fee"
return "--fee-all-the-time"
- def autoreconf(self, spec, prefix):
+ def autoreconf(self, pkg, spec, prefix):
pass
- def configure(self, spec, prefix):
+ def configure(self, pkg, spec, prefix):
pass
- def build(self, spec, prefix):
+ def build(self, pkg, spec, prefix):
pass
- def install(self, spec, prefix):
+ def install(self, pkg, spec, prefix):
# sanity_check_prefix requires something in the install directory
# Test requires overriding the one provided by `AutotoolsPackage`
mkdirp(prefix.bin)
diff --git a/var/spack/repos/builtin.mock/packages/attributes-foo/package.py b/var/spack/repos/builtin.mock/packages/attributes-foo/package.py
index 9010c52958..83b41b98ac 100644
--- a/var/spack/repos/builtin.mock/packages/attributes-foo/package.py
+++ b/var/spack/repos/builtin.mock/packages/attributes-foo/package.py
@@ -8,7 +8,6 @@ from spack.package import *
class AttributesFoo(BundlePackage):
- phases = ["install"]
version("1.0")
provides("bar")
diff --git a/var/spack/repos/builtin.mock/packages/canfail/package.py b/var/spack/repos/builtin.mock/packages/canfail/package.py
index eb35fdec56..75bb66df25 100644
--- a/var/spack/repos/builtin.mock/packages/canfail/package.py
+++ b/var/spack/repos/builtin.mock/packages/canfail/package.py
@@ -2,6 +2,7 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
+import os
from spack.package import *
@@ -14,7 +15,16 @@ class Canfail(Package):
version("1.0", "0123456789abcdef0123456789abcdef")
- succeed = False
+ def set_install_succeed(self):
+ os.environ["CANFAIL_SUCCEED"] = "1"
+
+ def set_install_fail(self):
+ os.environ.pop("CANFAIL_SUCCEED", None)
+
+ @property
+ def succeed(self):
+ result = True if "CANFAIL_SUCCEED" in os.environ else False
+ return result
def install(self, spec, prefix):
if not self.succeed:
diff --git a/var/spack/repos/builtin.mock/packages/cmake-client/package.py b/var/spack/repos/builtin.mock/packages/cmake-client/package.py
index 54842fd2d8..ed3998e550 100644
--- a/var/spack/repos/builtin.mock/packages/cmake-client/package.py
+++ b/var/spack/repos/builtin.mock/packages/cmake-client/package.py
@@ -15,7 +15,7 @@ def check(condition, msg):
class CmakeClient(CMakePackage):
- """A dumy package that uses cmake."""
+ """A dummy package that uses cmake."""
homepage = "https://www.example.com"
url = "https://www.example.com/cmake-client-1.0.tar.gz"
@@ -38,14 +38,16 @@ class CmakeClient(CMakePackage):
did_something = False
@run_after("cmake")
- @run_before("cmake", "build", "install")
+ @run_before("cmake")
+ @run_before("build")
+ @run_before("install")
def increment(self):
- self.callback_counter += 1
+ CmakeClient.callback_counter += 1
@run_after("cmake")
@on_package_attributes(run_this=True, check_this_is_none=None)
def flip(self):
- self.flipped = True
+ CmakeClient.flipped = True
@run_after("cmake")
@on_package_attributes(does_not_exist=None)
diff --git a/var/spack/repos/builtin.mock/packages/cmake/package.py b/var/spack/repos/builtin.mock/packages/cmake/package.py
index 30c3647df2..dac2c19875 100644
--- a/var/spack/repos/builtin.mock/packages/cmake/package.py
+++ b/var/spack/repos/builtin.mock/packages/cmake/package.py
@@ -18,12 +18,17 @@ def check(condition, msg):
class Cmake(Package):
- """A dumy package for the cmake build system."""
+ """A dummy package for the cmake build system."""
homepage = "https://www.cmake.org"
url = "https://cmake.org/files/v3.4/cmake-3.4.3.tar.gz"
version(
+ "3.23.1",
+ "4cb3ff35b2472aae70f542116d616e63",
+ url="https://cmake.org/files/v3.4/cmake-3.4.3.tar.gz",
+ )
+ version(
"3.4.3",
"4cb3ff35b2472aae70f542116d616e63",
url="https://cmake.org/files/v3.4/cmake-3.4.3.tar.gz",
diff --git a/var/spack/repos/builtin.mock/packages/dev-build-test-dependent/package.py b/var/spack/repos/builtin.mock/packages/dev-build-test-dependent/package.py
index 44374cd1a7..a5ac04d282 100644
--- a/var/spack/repos/builtin.mock/packages/dev-build-test-dependent/package.py
+++ b/var/spack/repos/builtin.mock/packages/dev-build-test-dependent/package.py
@@ -7,14 +7,12 @@
from spack.package import *
-class DevBuildTestDependent(Package):
+class DevBuildTestDependent(MakefilePackage):
homepage = "example.com"
url = "fake.com"
version("0.0.0", sha256="0123456789abcdef0123456789abcdef")
- phases = ["edit", "install"]
-
filename = "dev-build-test-file.txt"
original_string = "This file should be edited"
replacement_string = "This file has been edited"
@@ -28,5 +26,8 @@ class DevBuildTestDependent(Package):
f.truncate()
f.write(self.replacement_string)
+ def build(self, spec, prefix):
+ pass
+
def install(self, spec, prefix):
install(self.filename, prefix)
diff --git a/var/spack/repos/builtin.mock/packages/dev-build-test-install-phases/package.py b/var/spack/repos/builtin.mock/packages/dev-build-test-install-phases/package.py
index fa0f6b794e..916156c1f5 100644
--- a/var/spack/repos/builtin.mock/packages/dev-build-test-install-phases/package.py
+++ b/var/spack/repos/builtin.mock/packages/dev-build-test-install-phases/package.py
@@ -29,4 +29,5 @@ class DevBuildTestInstallPhases(Package):
print("Three locomoco")
def install(self, spec, prefix):
+ mkdirp(prefix.bin)
print("install")
diff --git a/var/spack/repos/builtin.mock/packages/dev-build-test-install/package.py b/var/spack/repos/builtin.mock/packages/dev-build-test-install/package.py
index 185fe5552c..ba0b1400a3 100644
--- a/var/spack/repos/builtin.mock/packages/dev-build-test-install/package.py
+++ b/var/spack/repos/builtin.mock/packages/dev-build-test-install/package.py
@@ -2,19 +2,15 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
-
-
from spack.package import *
-class DevBuildTestInstall(Package):
+class DevBuildTestInstall(MakefilePackage):
homepage = "example.com"
url = "fake.com"
version("0.0.0", sha256="0123456789abcdef0123456789abcdef")
- phases = ["edit", "install"]
-
filename = "dev-build-test-file.txt"
original_string = "This file should be edited"
replacement_string = "This file has been edited"
@@ -26,5 +22,8 @@ class DevBuildTestInstall(Package):
f.truncate()
f.write(self.replacement_string)
+ def build(self, spec, prefix):
+ pass
+
def install(self, spec, prefix):
install(self.filename, prefix)
diff --git a/var/spack/repos/builtin.mock/packages/fail-test-audit/package.py b/var/spack/repos/builtin.mock/packages/fail-test-audit/package.py
new file mode 100644
index 0000000000..1e290724d1
--- /dev/null
+++ b/var/spack/repos/builtin.mock/packages/fail-test-audit/package.py
@@ -0,0 +1,21 @@
+# Copyright 2013-2022 Lawrence Livermore National Security, LLC and other
+# Spack Project Developers. See the top-level COPYRIGHT file for details.
+#
+# SPDX-License-Identifier: (Apache-2.0 OR MIT)
+from spack.package import *
+
+
+class FailTestAudit(MakefilePackage):
+ """Simple package with one optional dependency"""
+
+ homepage = "http://www.example.com"
+ url = "http://www.example.com/a-1.0.tar.gz"
+
+ version("1.0", "0123456789abcdef0123456789abcdef")
+ version("2.0", "abcdef0123456789abcdef0123456789")
+
+ build_time_test_callbacks = ["test"]
+
+ def test(self):
+ print("test: test-install-callbacks")
+ print("PASSED")
diff --git a/var/spack/repos/builtin.mock/packages/libtool-deletion/package.py b/var/spack/repos/builtin.mock/packages/libtool-deletion/package.py
index a169a78d2e..8ab87d2064 100644
--- a/var/spack/repos/builtin.mock/packages/libtool-deletion/package.py
+++ b/var/spack/repos/builtin.mock/packages/libtool-deletion/package.py
@@ -4,6 +4,7 @@
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import os.path
+import spack.build_systems.autotools
from spack.package import *
@@ -19,17 +20,21 @@ class LibtoolDeletion(AutotoolsPackage):
def do_stage(self):
mkdirp(self.stage.source_path)
- def autoreconf(self, spec, prefix):
+
+class AutotoolsBuilder(spack.build_systems.autotools.AutotoolsBuilder):
+ install_libtool_archives = False
+
+ def autoreconf(self, pkg, spec, prefix):
mkdirp(os.path.dirname(self.configure_abs_path))
touch(self.configure_abs_path)
- def configure(self, spec, prefix):
+ def configure(self, pkg, spec, prefix):
pass
- def build(self, spec, prefix):
+ def build(self, pkg, spec, prefix):
pass
- def install(self, spec, prefix):
+ def install(self, pkg, spec, prefix):
mkdirp(os.path.dirname(self.libtool_archive_file))
touch(self.libtool_archive_file)
diff --git a/var/spack/repos/builtin.mock/packages/libtool-installation/package.py b/var/spack/repos/builtin.mock/packages/libtool-installation/package.py
new file mode 100644
index 0000000000..72883b9042
--- /dev/null
+++ b/var/spack/repos/builtin.mock/packages/libtool-installation/package.py
@@ -0,0 +1,15 @@
+# Copyright 2013-2022 Lawrence Livermore National Security, LLC and other
+# Spack Project Developers. See the top-level COPYRIGHT file for details.
+#
+# SPDX-License-Identifier: (Apache-2.0 OR MIT)
+from spack.package import *
+from spack.pkg.builtin.mock.libtool_deletion import AutotoolsBuilder as BuilderBase
+from spack.pkg.builtin.mock.libtool_deletion import LibtoolDeletion
+
+
+class LibtoolInstallation(LibtoolDeletion, AutotoolsPackage):
+ """Mock AutotoolsPackage to check proper installation of libtool archives."""
+
+
+class AutotoolsBuilder(BuilderBase):
+ install_libtool_archives = True
diff --git a/var/spack/repos/builtin.mock/packages/nosource-install/package.py b/var/spack/repos/builtin.mock/packages/nosource-install/package.py
index a1ca724448..5166480dd4 100644
--- a/var/spack/repos/builtin.mock/packages/nosource-install/package.py
+++ b/var/spack/repos/builtin.mock/packages/nosource-install/package.py
@@ -16,9 +16,6 @@ class NosourceInstall(BundlePackage):
depends_on("dependency-install")
- # The install phase must be specified.
- phases = ["install"]
-
# The install method must also be present.
def install(self, spec, prefix):
touch(join_path(self.prefix, "install.txt"))
diff --git a/var/spack/repos/builtin.mock/packages/test-build-callbacks/package.py b/var/spack/repos/builtin.mock/packages/test-build-callbacks/package.py
index 4b4b74e9b3..d45f0d295b 100644
--- a/var/spack/repos/builtin.mock/packages/test-build-callbacks/package.py
+++ b/var/spack/repos/builtin.mock/packages/test-build-callbacks/package.py
@@ -2,9 +2,9 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
-
+import spack.build_systems._checks as checks
+import spack.build_systems.generic
from spack.package import *
-from spack.package_base import run_after
class TestBuildCallbacks(Package):
@@ -15,17 +15,16 @@ class TestBuildCallbacks(Package):
version("1.0", "0123456789abcdef0123456789abcdef")
+
+class GenericBuilder(spack.build_systems.generic.GenericBuilder):
phases = ["build", "install"]
- # Include undefined method (runtime failure) and 'test' (audit failure)
- build_time_test_callbacks = ["undefined-build-test", "test"]
- run_after("build")(Package._run_default_build_time_test_callbacks)
- def build(self, spec, prefix):
+ # Include undefined method (runtime failure)
+ build_time_test_callbacks = ["undefined-build-test"]
+ run_after("build")(checks.execute_build_time_tests)
+
+ def build(self, pkg, spec, prefix):
pass
- def install(self, spec, prefix):
+ def install(self, pkg, spec, prefix):
mkdirp(prefix.bin)
-
- def test(self):
- print("test: running test-build-callbacks")
- print("PASSED")
diff --git a/var/spack/repos/builtin.mock/packages/test-install-callbacks/package.py b/var/spack/repos/builtin.mock/packages/test-install-callbacks/package.py
index 27a31227c3..0d348c0d67 100644
--- a/var/spack/repos/builtin.mock/packages/test-install-callbacks/package.py
+++ b/var/spack/repos/builtin.mock/packages/test-install-callbacks/package.py
@@ -2,9 +2,9 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
-
+import spack.build_systems._checks as checks
+import spack.build_systems.generic
from spack.package import *
-from spack.package_base import run_after
class TestInstallCallbacks(Package):
@@ -15,13 +15,11 @@ class TestInstallCallbacks(Package):
version("1.0", "0123456789abcdef0123456789abcdef")
+
+class GenericBuilder(spack.build_systems.generic.GenericBuilder):
# Include an undefined callback method
- install_time_test_callbacks = ["undefined-install-test", "test"]
- run_after("install")(Package._run_default_install_time_test_callbacks)
+ install_time_test_callbacks = ["undefined-install-test"]
+ run_after("install")(checks.execute_install_time_tests)
- def install(self, spec, prefix):
+ def install(self, pkg, spec, prefix):
mkdirp(prefix.bin)
-
- def test(self):
- print("test: test-install-callbacks")
- print("PASSED")
diff --git a/var/spack/repos/builtin.mock/packages/transitive-conditional-virtual-dependency/package.py b/var/spack/repos/builtin.mock/packages/transitive-conditional-virtual-dependency/package.py
index a7c4658810..9078d63b4d 100644
--- a/var/spack/repos/builtin.mock/packages/transitive-conditional-virtual-dependency/package.py
+++ b/var/spack/repos/builtin.mock/packages/transitive-conditional-virtual-dependency/package.py
@@ -5,12 +5,10 @@
from spack.package import *
-class TransitiveConditionalVirtualDependency(Package):
+class TransitiveConditionalVirtualDependency(BundlePackage):
"""Depends on a package with a conditional virtual dependency."""
homepage = "https://dev.null"
- has_code = False
- phases = []
version("1.0")
depends_on("conditional-virtual-dependency")
diff --git a/var/spack/repos/builtin.mock/packages/trivial-smoke-test/package.py b/var/spack/repos/builtin.mock/packages/trivial-smoke-test/package.py
index ec80640d21..35983a18f4 100644
--- a/var/spack/repos/builtin.mock/packages/trivial-smoke-test/package.py
+++ b/var/spack/repos/builtin.mock/packages/trivial-smoke-test/package.py
@@ -16,6 +16,9 @@ class TrivialSmokeTest(Package):
test_source_filename = "cached_file.in"
+ def install(self, spec, prefix):
+ pass
+
@run_before("install")
def create_extra_test_source(self):
mkdirp(self.install_test_root)
diff --git a/var/spack/repos/builtin/packages/alpgen/package.py b/var/spack/repos/builtin/packages/alpgen/package.py
index 717b513cc3..e0816eeca7 100644
--- a/var/spack/repos/builtin/packages/alpgen/package.py
+++ b/var/spack/repos/builtin/packages/alpgen/package.py
@@ -2,15 +2,16 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
-
import os
+import spack.build_systems.makefile
from spack.package import *
-class Alpgen(MakefilePackage):
- """A collection of codes for the generation of
- multi-parton processes in hadronic collisions."""
+class Alpgen(CMakePackage, MakefilePackage):
+ """A collection of codes for the generation of multi-parton processes
+ in hadronic collisions.
+ """
homepage = "http://mlm.home.cern.ch/mlm/alpgen/"
url = "http://mlm.home.cern.ch/mlm/alpgen/V2.1/v214.tgz"
@@ -18,102 +19,44 @@ class Alpgen(MakefilePackage):
maintainers = ["iarspider"]
tags = ["hep"]
- patch("alpgen-214.patch", when="recipe=cms")
- patch("alpgen-214-Darwin-x86_84-gfortran.patch", when="platform=darwin recipe=cms")
- patch("alpgen-2.1.4-sft.patch", when="recipe=sft", level=0)
+ version("2.1.4", sha256="2f43f7f526793fe5f81a3a3e1adeffe21b653a7f5851efc599ed69ea13985c5e")
- depends_on("cmake", type="build", when="recipe=sft")
+ build_system("makefile", "cmake", default="makefile")
variant(
"recipe",
- values=("cms", "sft"),
+ values=(
+ conditional("cms", when="build_system=makefile"),
+ conditional("sft", when="build_system=cmake"),
+ ),
default="sft",
- description="Select build recipe: CMS for CMS experiment, " + "SFT for ATLAS/LHCb/others.",
+ description="CMS for CMS experiment, SFT for ATLAS/LHCb/others.",
)
- version("2.1.4", sha256="2f43f7f526793fe5f81a3a3e1adeffe21b653a7f5851efc599ed69ea13985c5e")
-
- phases = ["cmake", "build", "install"]
-
- # copied from CMakePackage
- @property
- def build_dirname(self):
- """Returns the directory name to use when building the package
-
- :return: name of the subdirectory for building the package
- """
- return "spack-build-%s" % self.spec.dag_hash(7)
-
- @property
- def build_directory(self):
- """Returns the directory to use when building the package
-
- :return: directory where to build the package
- """
- return os.path.join(self.stage.path, self.build_dirname)
-
- @property
- def root_cmakelists_dir(self):
- """The relative path to the directory containing CMakeLists.txt
-
- This path is relative to the root of the extracted tarball,
- not to the ``build_directory``. Defaults to the current directory.
-
- :return: directory containing CMakeLists.txt
- """
- return self.stage.source_path
-
- def cmake_args(self):
- """Produces a list containing all the arguments that must be passed to
- cmake, except:
-
- * CMAKE_INSTALL_PREFIX
- * CMAKE_BUILD_TYPE
-
- which will be set automatically.
-
- :return: list of arguments for cmake
- """
- return []
-
- @property
- def std_cmake_args(self):
- """Standard cmake arguments provided as a property for
- convenience of package writers
-
- :return: standard cmake arguments
- """
- # standard CMake arguments
- std_cmake_args = CMakePackage._std_args(self)
- std_cmake_args += getattr(self, "cmake_flag_args", [])
- return std_cmake_args
-
- # end
+ patch("alpgen-214.patch", when="recipe=cms")
+ patch("alpgen-214-Darwin-x86_84-gfortran.patch", when="platform=darwin recipe=cms")
+ patch("alpgen-2.1.4-sft.patch", when="recipe=sft", level=0)
def url_for_version(self, version):
root = self.url.rsplit("/", 2)[0]
return "{0}/V{1}/v{2}.tgz".format(root, version.up_to(2), version.joined)
def patch(self):
- if self.spec.satisfies("recipe=sft"):
+ if self.spec.satisfies("build_system=cmake"):
copy(join_path(os.path.dirname(__file__), "CMakeLists.txt"), "CMakeLists.txt")
- if self.spec.satisfies("recipe=cms"):
+ if self.spec.satisfies("build_system=makefile"):
filter_file("-fno-automatic", "-fno-automatic -std=legacy", "compile.mk")
copy(join_path(os.path.dirname(__file__), "cms_build.sh"), "cms_build.sh")
copy(join_path(os.path.dirname(__file__), "cms_install.sh"), "cms_install.sh")
- @when("recipe=cms")
- def cmake(self, spec, prefix):
- return
- @when("recipe=cms")
- def build(self, spec, prefix):
+class MakefileBuilder(spack.build_systems.makefile.MakefileBuilder):
+ def build(self, pkg, spec, prefix):
bash = which("bash")
bash("./cms_build.sh")
- @when("recipe=cms")
- def install(self, spec, prefix):
+ def install(self, pkg, spec, prefix):
bash = which("bash")
bash("./cms_install.sh", prefix)
@@ -121,25 +64,3 @@ class Alpgen(MakefilePackage):
set_install_permissions(root)
for file in files:
set_install_permissions(join_path(root, file))
-
- @when("recipe=sft")
- def cmake(self, spec, prefix):
- """Runs ``cmake`` in the build directory"""
- options = self.std_cmake_args
- options += self.cmake_args()
- options.append(os.path.abspath(self.root_cmakelists_dir))
- with working_dir(self.build_directory, create=True):
- cmake_x = which("cmake")
- cmake_x(*options)
-
- @when("recipe=sft")
- def build(self, spec, prefix):
- """Make the build targets"""
- with working_dir(self.build_directory):
- make()
-
- @when("recipe=sft")
- def install(self, spec, prefix):
- """Make the install targets"""
- with working_dir(self.build_directory):
- make("install")
diff --git a/var/spack/repos/builtin/packages/arpack-ng/package.py b/var/spack/repos/builtin/packages/arpack-ng/package.py
index 70b1b4820c..49feb262cb 100644
--- a/var/spack/repos/builtin/packages/arpack-ng/package.py
+++ b/var/spack/repos/builtin/packages/arpack-ng/package.py
@@ -2,11 +2,12 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
-
+import spack.build_systems.autotools
+import spack.build_systems.cmake
from spack.package import *
-class ArpackNg(Package):
+class ArpackNg(CMakePackage, AutotoolsPackage):
"""ARPACK-NG is a collection of Fortran77 subroutines designed to solve
large scale eigenvalue problems.
@@ -38,6 +39,8 @@ class ArpackNg(Package):
url = "https://github.com/opencollab/arpack-ng/archive/3.3.0.tar.gz"
git = "https://github.com/opencollab/arpack-ng.git"
+ build_system("cmake", "autotools", default="cmake")
+
version("develop", branch="master")
version("3.8.0", sha256="ada5aeb3878874383307239c9235b716a8a170c6d096a6625bfd529844df003d")
version("3.7.0", sha256="972e3fc3cd0b9d6b5a737c9bf6fd07515c0d6549319d4ffb06970e64fa3cc2d6")
@@ -74,13 +77,14 @@ class ArpackNg(Package):
depends_on("blas")
depends_on("lapack")
- depends_on("automake", when="@3.3.0", type="build")
- depends_on("autoconf", when="@3.3.0", type="build")
- depends_on("libtool@2.4.2:", when="@3.3.0", type="build")
- depends_on("cmake@2.8.6:", when="@3.4.0:", type="build")
-
depends_on("mpi", when="+mpi")
+ with when("build_system=autotools"):
+ depends_on("automake", type="build")
+ depends_on("autoconf", type="build")
+ depends_on("libtool@2.4.2:", type="build")
+ depends_on("pkgconfig", type="build")
+
def flag_handler(self, name, flags):
spec = self.spec
iflags = []
@@ -105,36 +109,26 @@ class ArpackNg(Package):
return find_libraries(libraries, root=self.prefix, shared=True, recursive=True)
- @when("@:3.7.0 %gcc@10:")
- def setup_build_environment(self, env):
- # version up to and including 3.7.0 are not ported to gcc 10
- # https://github.com/opencollab/arpack-ng/issues/242
- env.set("FFLAGS", "-fallow-argument-mismatch")
-
- @when("@3.4.0:")
- def install(self, spec, prefix):
- options = ["-DEXAMPLES=ON"]
- options.extend(std_cmake_args)
- options.append("-DCMAKE_INSTALL_NAME_DIR:PATH=%s/lib" % prefix)
-
- # Make sure we use Spack's blas/lapack:
+class CMakeBuilder(spack.build_systems.cmake.CMakeBuilder):
+ def cmake_args(self):
+ spec = self.spec
lapack_libs = spec["lapack"].libs.joined(";")
blas_libs = spec["blas"].libs.joined(";")
- options.extend(
- [
- "-DLAPACK_FOUND=true",
- "-DLAPACK_INCLUDE_DIRS={0}".format(spec["lapack"].prefix.include),
- "-DLAPACK_LIBRARIES={0}".format(lapack_libs),
- "-DBLAS_FOUND=true",
- "-DBLAS_INCLUDE_DIRS={0}".format(spec["blas"].prefix.include),
- "-DBLAS_LIBRARIES={0}".format(blas_libs),
- ]
- )
-
- if "+mpi" in spec:
- options.append("-DMPI=ON")
+ options = [
+ self.define("EXAMPLES", "ON"),
+ self.define("CMAKE_INSTALL_NAME_DIR", self.prefix.lib),
+ self.define("LAPACK_FOUND", True),
+ self.define("LAPACK_INCLUDE_DIRS", spec["lapack"].prefix.include),
+ self.define("LAPACK_LIBRARIES", lapack_libs),
+ self.define("BLAS_FOUND", True),
+ self.define("BLAS_INCLUDE_DIRS", spec["blas"].prefix.include),
+ self.define("BLAS_LIBRARIES", blas_libs),
+ self.define_from_variant("MPI", "mpi"),
+ self.define_from_variant("BUILD_SHARED_LIBS", "shared"),
+ self.define("CMAKE_POSITION_INDEPENDENT_CODE", True),
+ ]
# If 64-bit BLAS is used:
if (
@@ -144,41 +138,28 @@ class ArpackNg(Package):
):
options.append("-DINTERFACE64=1")
- if "+shared" in spec:
- options.append("-DBUILD_SHARED_LIBS=ON")
- else:
- options.append("-DBUILD_SHARED_LIBS=OFF")
- options.append("-DCMAKE_POSITION_INDEPENDENT_CODE:BOOL=true")
-
- cmake(".", *options)
- make()
- if self.run_tests:
- make("test")
- make("install")
-
- @when("@3.3.0")
- def install(self, spec, prefix):
- # Apparently autotools are not bootstrapped
- which("libtoolize")()
- bootstrap = Executable("./bootstrap")
-
- options = ["--prefix=%s" % prefix]
+ return options
- if "+mpi" in spec:
- options.extend(["--enable-mpi", "F77=%s" % spec["mpi"].mpif77])
- options.extend(
- [
+class AutotoolsBuilder(spack.build_systems.autotools.AutotoolsBuilder):
+ def configure_args(self):
+ spec = self.spec
+ options = (
+ self.enable_or_disable("mpi")
+ + [
"--with-blas={0}".format(spec["blas"].libs.ld_flags),
"--with-lapack={0}".format(spec["lapack"].libs.ld_flags),
]
+ + self.enable_or_disable("shared")
)
- if "+shared" not in spec:
- options.append("--enable-shared=no")
-
- bootstrap()
- configure(*options)
- make()
- if self.run_tests:
- make("check")
- make("install")
+
+ if "+mpi" in spec:
+ options.append("F77={0}".format(spec["mpi"].mpif77))
+
+ return options
+
+ @when("@:3.7.0 %gcc@10:")
+ def setup_build_environment(self, env):
+ # version up to and including 3.7.0 are not ported to gcc 10
+ # https://github.com/opencollab/arpack-ng/issues/242
+ env.set("FFLAGS", "-fallow-argument-mismatch")
diff --git a/var/spack/repos/builtin/packages/binutils/package.py b/var/spack/repos/builtin/packages/binutils/package.py
index 9ba963b019..f5a9ae7fd9 100644
--- a/var/spack/repos/builtin/packages/binutils/package.py
+++ b/var/spack/repos/builtin/packages/binutils/package.py
@@ -4,6 +4,7 @@
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import re
+import spack.build_systems.autotools
from spack.package import *
@@ -40,13 +41,21 @@ class Binutils(AutotoolsPackage, GNUMirrorPackage):
version("2.20.1", sha256="71d37c96451333c5c0b84b170169fdcb138bbb27397dc06281905d9717c8ed64")
variant("plugins", default=True, description="enable plugins, needed for gold linker")
- variant("gold", default=False, description="build the gold linker")
+ # When you build ld.gold you automatically get ld, even when you add the
+ # --disable-ld flag
+ variant("gold", default=False, when="+ld", description="build the gold linker")
variant("libiberty", default=False, description="Also install libiberty.")
variant("nls", default=True, description="Enable Native Language Support")
variant("headers", default=False, description="Install extra headers (e.g. ELF)")
variant("lto", default=False, description="Enable lto.")
variant("ld", default=False, description="Enable ld.")
- variant("gas", default=False, description="Enable as assembler.")
+ # When you build binutils with ~ld and +gas and load it in your PATH, you
+ # may end up with incompatibilities between a potentially older system ld
+ # and a recent assembler. For instance the linker on ubuntu 16.04 from
+ # binutils 2.26 and the assembler from binutils 2.36.1 will result in:
+ # "unable to initialize decompress status for section .debug_info"
+ # when compiling with debug symbols on gcc.
+ variant("gas", default=False, when="+ld", description="Enable as assembler.")
variant("interwork", default=False, description="Enable interwork.")
variant(
"libs",
@@ -81,83 +90,12 @@ class Binutils(AutotoolsPackage, GNUMirrorPackage):
conflicts("+gold", when="platform=darwin", msg="Binutils cannot build linkers on macOS")
- # When you build binutils with ~ld and +gas and load it in your PATH, you
- # may end up with incompatibilities between a potentially older system ld
- # and a recent assembler. For instance the linker on ubuntu 16.04 from
- # binutils 2.26 and the assembler from binutils 2.36.1 will result in:
- # "unable to initialize decompress status for section .debug_info"
- # when compiling with debug symbols on gcc.
- conflicts("+gas", "~ld", msg="Assembler not always compatible with system ld")
-
- # When you build ld.gold you automatically get ld, even when you add the
- # --disable-ld flag
- conflicts("~ld", "+gold")
-
@classmethod
def determine_version(cls, exe):
output = Executable(exe)("--version", output=str, error=str)
match = re.search(r"GNU (nm|readelf).* (\S+)", output)
return Version(match.group(2)).dotted.up_to(3) if match else None
- def setup_build_environment(self, env):
-
- if self.spec.satisfies("%cce"):
- env.append_flags("LDFLAGS", "-Wl,-z,muldefs")
-
- if "+nls" in self.spec:
- env.append_flags("LDFLAGS", "-lintl")
-
- def configure_args(self):
- spec = self.spec
-
- args = [
- "--disable-dependency-tracking",
- "--disable-werror",
- "--enable-multilib",
- "--enable-64-bit-bfd",
- "--enable-targets=all",
- "--with-system-zlib",
- "--with-sysroot=/",
- ]
-
- args += self.enable_or_disable("libs")
- args += self.enable_or_disable("lto")
- args += self.enable_or_disable("ld")
- args += self.enable_or_disable("gas")
- args += self.enable_or_disable("interwork")
- args += self.enable_or_disable("gold")
- args += self.enable_or_disable("plugins")
-
- if "+libiberty" in spec:
- args.append("--enable-install-libiberty")
- else:
- args.append("--disable-install-libiberty")
-
- if "+nls" in spec:
- args.append("--enable-nls")
- else:
- args.append("--disable-nls")
-
- # To avoid namespace collisions with Darwin/BSD system tools,
- # prefix executables with "g", e.g., gar, gnm; see Homebrew
- # https://github.com/Homebrew/homebrew-core/blob/master/Formula/binutils.rb
- if spec.satisfies("platform=darwin"):
- args.append("--program-prefix=g")
-
- return args
-
- @run_after("install")
- def install_headers(self):
- # some packages (like TAU) need the ELF headers, so install them
- # as a subdirectory in include/extras
- if "+headers" in self.spec:
- extradir = join_path(self.prefix.include, "extra")
- mkdirp(extradir)
- # grab the full binutils set of headers
- install_tree("include", extradir)
- # also grab the headers from the bfd directory
- install(join_path(self.build_directory, "bfd", "*.h"), extradir)
-
def flag_handler(self, name, flags):
spec = self.spec
# Use a separate variable for injecting flags. This way, installing
@@ -204,3 +142,55 @@ class Binutils(AutotoolsPackage, GNUMirrorPackage):
self.run_test(
exe, "--version", expected, installed=True, purpose=reason, skip_missing=True
)
+
+
+class AutotoolsBuilder(spack.build_systems.autotools.AutotoolsBuilder):
+ def configure_args(self):
+ args = [
+ "--disable-dependency-tracking",
+ "--disable-werror",
+ "--enable-multilib",
+ "--enable-64-bit-bfd",
+ "--enable-targets=all",
+ "--with-system-zlib",
+ "--with-sysroot=/",
+ ]
+ args += self.enable_or_disable("libs")
+ args += self.enable_or_disable("lto")
+ args += self.enable_or_disable("ld")
+ args += self.enable_or_disable("gas")
+ args += self.enable_or_disable("interwork")
+ args += self.enable_or_disable("gold")
+ args += self.enable_or_disable("nls")
+ args += self.enable_or_disable("plugins")
+
+ if "+libiberty" in self.spec:
+ args.append("--enable-install-libiberty")
+ else:
+ args.append("--disable-install-libiberty")
+
+ # To avoid namespace collisions with Darwin/BSD system tools,
+ # prefix executables with "g", e.g., gar, gnm; see Homebrew
+ # https://github.com/Homebrew/homebrew-core/blob/master/Formula/binutils.rb
+ if self.spec.satisfies("platform=darwin"):
+ args.append("--program-prefix=g")
+
+ return args
+
+ @run_after("install", when="+headers")
+ def install_headers(self):
+ # some packages (like TAU) need the ELF headers, so install them
+ # as a subdirectory in include/extras
+ extradir = join_path(self.prefix.include, "extra")
+ mkdirp(extradir)
+ # grab the full binutils set of headers
+ install_tree("include", extradir)
+ # also grab the headers from the bfd directory
+ install(join_path(self.build_directory, "bfd", "*.h"), extradir)
+
+ def setup_build_environment(self, env):
+ if self.spec.satisfies("%cce"):
+ env.append_flags("LDFLAGS", "-Wl,-z,muldefs")
+
+ if "+nls" in self.spec:
+ env.append_flags("LDFLAGS", "-lintl")
diff --git a/var/spack/repos/builtin/packages/clara/package.py b/var/spack/repos/builtin/packages/clara/package.py
index 3eaea61ce5..fadec16835 100644
--- a/var/spack/repos/builtin/packages/clara/package.py
+++ b/var/spack/repos/builtin/packages/clara/package.py
@@ -2,19 +2,26 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
-
+import spack.build_systems.generic
from spack.package import *
-class Clara(CMakePackage):
+class Clara(CMakePackage, Package):
"""A simple to use, composable, command line parser for C++ 11
- and beyond."""
+ and beyond.
+ """
homepage = "https://github.com/catchorg/Clara"
url = "https://github.com/catchorg/Clara/archive/v1.1.5.tar.gz"
maintainers = ["bvanessen"]
+ build_system(
+ conditional("generic", when="+single_header"),
+ conditional("cmake", when="~single_header"),
+ default="generic",
+ )
+
variant("single_header", default=True, description="Install a single header only.")
version("1.1.5", sha256="767dc1718e53678cbea00977adcd0a8a195802a505aec3c537664cf25a173142")
@@ -24,15 +31,8 @@ class Clara(CMakePackage):
version("1.1.1", sha256="10915a49a94d371f05af360d40e9cc9615ab86f200d261edf196a8ddd7efa7f8")
version("1.1.0", sha256="29ca29d843150aabad702356f79009f5b30dda05ac9674a064362b7edcba5477")
- @when("+single_header")
- def cmake(self, spec, prefix):
- pass
-
- @when("+single_header")
- def build(self, spec, prefix):
- pass
- @when("+single_header")
- def install(self, spec, prefix):
+class GenericBuilder(spack.build_systems.generic.GenericBuilder):
+ def install(self, pkg, spec, prefix):
mkdirp(prefix.include)
install_tree("single_include", prefix.include)
diff --git a/var/spack/repos/builtin/packages/flex/package.py b/var/spack/repos/builtin/packages/flex/package.py
index 8f3d496bb5..f0754ec86d 100644
--- a/var/spack/repos/builtin/packages/flex/package.py
+++ b/var/spack/repos/builtin/packages/flex/package.py
@@ -113,20 +113,19 @@ class Flex(AutotoolsPackage):
args += self.enable_or_disable("nls")
return args
- @run_after("install")
+ @run_after("install", when="+lex")
def symlink_lex(self):
"""Install symlinks for lex compatibility."""
- if self.spec.satisfies("+lex"):
- dso = dso_suffix
- for dir, flex, lex in (
- (self.prefix.bin, "flex", "lex"),
- (self.prefix.lib, "libfl.a", "libl.a"),
- (self.prefix.lib, "libfl." + dso, "libl." + dso),
- (self.prefix.lib64, "libfl.a", "libl.a"),
- (self.prefix.lib64, "libfl." + dso, "libl." + dso),
- ):
-
- if os.path.isdir(dir):
- with working_dir(dir):
- if os.path.isfile(flex) and not os.path.lexists(lex):
- symlink(flex, lex)
+ dso = dso_suffix
+ for dir, flex, lex in (
+ (self.prefix.bin, "flex", "lex"),
+ (self.prefix.lib, "libfl.a", "libl.a"),
+ (self.prefix.lib, "libfl." + dso, "libl." + dso),
+ (self.prefix.lib64, "libfl.a", "libl.a"),
+ (self.prefix.lib64, "libfl." + dso, "libl." + dso),
+ ):
+
+ if os.path.isdir(dir):
+ with working_dir(dir):
+ if os.path.isfile(flex) and not os.path.lexists(lex):
+ symlink(flex, lex)
diff --git a/var/spack/repos/builtin/packages/gobject-introspection/package.py b/var/spack/repos/builtin/packages/gobject-introspection/package.py
index 9ea90684e8..624d0941f8 100644
--- a/var/spack/repos/builtin/packages/gobject-introspection/package.py
+++ b/var/spack/repos/builtin/packages/gobject-introspection/package.py
@@ -2,15 +2,16 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
-
+import spack.build_systems.autotools
import spack.hooks.sbang as sbang
from spack.package import *
-class GobjectIntrospection(MesonPackage):
+class GobjectIntrospection(MesonPackage, AutotoolsPackage):
"""The GObject Introspection is used to describe the program APIs and
collect them in a uniform, machine readable format.Cairo is a 2D graphics
- library with support for multiple output"""
+ library with support for multiple output
+ """
homepage = "https://wiki.gnome.org/Projects/GObjectIntrospection"
url = "https://download.gnome.org/sources/gobject-introspection/1.72/gobject-introspection-1.72.0.tar.xz"
@@ -22,6 +23,12 @@ class GobjectIntrospection(MesonPackage):
version("1.49.2", sha256="73d59470ba1a546b293f54d023fd09cca03a951005745d86d586b9e3a8dde9ac")
version("1.48.0", sha256="fa275aaccdbfc91ec0bc9a6fd0562051acdba731e7d584b64a277fec60e75877")
+ build_system(
+ conditional("autotools", when="@:1.60"),
+ conditional("meson", when="@1.61:"),
+ default="meson",
+ )
+
depends_on("pkgconfig", type="build")
depends_on("bison", type="build")
depends_on("flex", type="build")
@@ -94,22 +101,9 @@ class GobjectIntrospection(MesonPackage):
def parallel(self):
return not self.spec.satisfies("%fj")
- def meson_args(self):
- return []
- @when("@:1.60")
- def meson(self, spec, prefix):
- """Run the AutotoolsPackage configure phase"""
- configure("--prefix={0}".format(prefix))
-
- @when("@:1.60")
- def build(self, spec, prefix):
- """Run the AutotoolsPackage build phase"""
+class AutotoolsBuilderPackage(spack.build_systems.autotools.AutotoolsBuilder):
+ @run_before("build")
+ def filter_file_to_avoid_overly_long_shebangs(self):
# we need to filter this file to avoid an overly long hashbang line
filter_file("#!/usr/bin/env @PYTHON@", "#!@PYTHON@", "tools/g-ir-tool-template.in")
- make()
-
- @when("@:1.60")
- def install(self, spec, prefix):
- """Run the AutotoolsPackage install phase"""
- make("install", parallel=False)
diff --git a/var/spack/repos/builtin/packages/harfbuzz/package.py b/var/spack/repos/builtin/packages/harfbuzz/package.py
index 0400c0a21a..e746ed6fbf 100644
--- a/var/spack/repos/builtin/packages/harfbuzz/package.py
+++ b/var/spack/repos/builtin/packages/harfbuzz/package.py
@@ -2,11 +2,12 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
-
+import spack.build_systems.autotools
+import spack.build_systems.meson
from spack.package import *
-class Harfbuzz(MesonPackage):
+class Harfbuzz(MesonPackage, AutotoolsPackage):
"""The Harfbuzz package contains an OpenType text shaping engine."""
homepage = "https://github.com/harfbuzz/harfbuzz"
@@ -14,6 +15,10 @@ class Harfbuzz(MesonPackage):
git = "https://github.com/harfbuzz/harfbuzz.git"
version("5.1.0", sha256="2edb95db668781aaa8d60959d21be2ff80085f31b12053cdd660d9a50ce84f05")
+ build_system(
+ conditional("autotools", when="@:2.9"), conditional("meson", when="@3:"), default="meson"
+ )
+
version("4.2.1", sha256="bd17916513829aeff961359a5ccebba6de2f4bf37a91faee3ac29c120e3d7ee1")
version("4.1.0", sha256="f7984ff4241d4d135f318a93aa902d910a170a8265b7eaf93b5d9a504eed40c8")
version("4.0.1", sha256="98f68777272db6cd7a3d5152bac75083cd52a26176d87bc04c8b3929d33bce49")
@@ -99,31 +104,33 @@ class Harfbuzz(MesonPackage):
def setup_run_environment(self, env):
env.prepend_path("GI_TYPELIB_PATH", join_path(self.prefix.lib, "girepository-1.0"))
- def setup_dependent_build_environment(self, env, dependent_spec):
+ def setup_dependent_run_environment(self, env, dependent_spec):
env.prepend_path("XDG_DATA_DIRS", self.prefix.share)
env.prepend_path("GI_TYPELIB_PATH", join_path(self.prefix.lib, "girepository-1.0"))
- def setup_dependent_run_environment(self, env, dependent_spec):
+ def patch(self):
+ change_sed_delimiter("@", ";", "src/Makefile.in")
+
+
+class SetupEnvironment(object):
+ def setup_dependent_build_environment(self, env, dependent_spec):
env.prepend_path("XDG_DATA_DIRS", self.prefix.share)
env.prepend_path("GI_TYPELIB_PATH", join_path(self.prefix.lib, "girepository-1.0"))
- def meson_args(self):
- args = []
-
- # disable building of gtk-doc files following #9885 and #9771
- args.append("-Ddocs=disabled")
- args.append(
- "-Dgraphite2=" + ("enabled" if self.spec.satisfies("+graphite2") else "disabled")
- )
- if "+coretext" in self.spec:
- args.append("-Dcoretext=enabled")
- elif "~coretext" in self.spec:
- args.append("-Dcoretext=disabled")
+class MesonBuilder(spack.build_systems.meson.MesonBuilder, SetupEnvironment):
+ def meson_args(self):
+ graphite2 = "enabled" if self.pkg.spec.satisfies("+graphite2") else "disabled"
+ coretext = "enabled" if self.pkg.spec.satisfies("+coretext") else "disabled"
+ return [
+ # disable building of gtk-doc files following #9885 and #9771
+ "-Ddocs=disabled",
+ "-Dgraphite2={0}".format(graphite2),
+ "-Dcoretext={0}".format(coretext),
+ ]
- return args
- @when("@:2.9")
+class AutotoolsBuilder(spack.build_systems.autotools.AutotoolsBuilder, SetupEnvironment):
def configure_args(self):
args = []
@@ -135,28 +142,6 @@ class Harfbuzz(MesonPackage):
args.append("GTKDOC_MKPDF={0}".format(true))
args.append("GTKDOC_REBASE={0}".format(true))
args.extend(self.with_or_without("graphite2"))
-
- if "+coretext" in self.spec:
- args.append("--with-coretext")
- elif "~coretext" in self.spec:
- args.append("--without-coretext")
+ args.extend(self.with_or_without("coretext"))
return args
-
- def patch(self):
- change_sed_delimiter("@", ";", "src/Makefile.in")
-
- @when("@:2.9")
- def meson(self, spec, prefix):
- """Run the AutotoolsPackage configure phase"""
- configure("--prefix=" + prefix, *self.configure_args())
-
- @when("@:2.9")
- def build(self, spec, prefix):
- """Run the AutotoolsPackage build phase"""
- make()
-
- @when("@:2.9")
- def install(self, spec, prefix):
- """Run the AutotoolsPackage install phase"""
- make("install")
diff --git a/var/spack/repos/builtin/packages/ibm-databroker/package.py b/var/spack/repos/builtin/packages/ibm-databroker/package.py
index ae80f92439..9c0516844b 100644
--- a/var/spack/repos/builtin/packages/ibm-databroker/package.py
+++ b/var/spack/repos/builtin/packages/ibm-databroker/package.py
@@ -7,7 +7,7 @@
from spack.package import *
-class IbmDatabroker(CMakePackage, PythonPackage):
+class IbmDatabroker(CMakePackage, PythonExtension):
"""The Data Broker (DBR) is a distributed, in-memory container of key-value
stores enabling applications in a workflow to exchange data through one or
more shared namespaces. Thanks to a small set of primitives, applications
diff --git a/var/spack/repos/builtin/packages/intel-tbb/package.py b/var/spack/repos/builtin/packages/intel-tbb/package.py
index 1bc13ac1a3..8921604b08 100644
--- a/var/spack/repos/builtin/packages/intel-tbb/package.py
+++ b/var/spack/repos/builtin/packages/intel-tbb/package.py
@@ -8,10 +8,12 @@ import inspect
import platform
import sys
+import spack.build_systems.cmake
+import spack.build_systems.makefile
from spack.package import *
-class IntelTbb(CMakePackage):
+class IntelTbb(CMakePackage, MakefilePackage):
"""Widely used C++ template library for task parallelism.
Intel Threading Building Blocks (Intel TBB) lets you easily write parallel
C++ programs that take full advantage of multicore performance, that are
@@ -74,6 +76,12 @@ class IntelTbb(CMakePackage):
version("4.4.1", sha256="05737bf6dd220b31aad63d77ca59c742271f81b4cc6643aa6f93d37450ae32b5")
version("4.4", sha256="93c74b6054c69c86fa49d0fce7c50061fc907cb198a7237b8dd058298fd40c0e")
+ build_system(
+ conditional("makefile", when="@:2020.3"),
+ conditional("cmake", when="@2021:"),
+ default="cmake",
+ )
+
provides("tbb")
# Clang builds incorrectly determine GCC version which in turn incorrectly
@@ -161,12 +169,54 @@ class IntelTbb(CMakePackage):
name = "{0}".format(version)
return url.format(name)
+ @property
+ def libs(self):
+ shared = True if "+shared" in self.spec else False
+ return find_libraries("libtbb*", root=self.prefix, shared=shared, recursive=True)
+
+
+class SetupEnvironment(object):
# We set OS here in case the user has it set to something else
# that TBB doesn't expect.
def setup_build_environment(self, env):
env.set("OS", platform.system())
- @when("@:2020.3")
+
+class CMakeBuilder(spack.build_systems.cmake.CMakeBuilder, SetupEnvironment):
+ def cmake_args(self):
+ spec = self.spec
+ options = [
+ self.define("CMAKE_HWLOC_2_INCLUDE_PATH", spec["hwloc"].prefix.include),
+ self.define("CMAKE_HWLOC_2_LIBRARY_PATH", spec["hwloc"].libs),
+ self.define("-DTBB_CPF", True),
+ self.define("TBB_STRICT", False),
+ ]
+ if spec.variants["cxxstd"].value != "default":
+ options.append(self.define("CMAKE_CXX_STANDARD", spec.variants["cxxstd"].value))
+ return options
+
+ @run_after("install")
+ def install_pkgconfig(self):
+ # pkg-config generation is introduced in May 5, 2021.
+ # It must not be overwritten by spack-generated tbb.pc.
+ # https://github.com/oneapi-src/oneTBB/commit/478de5b1887c928e52f029d706af6ea640a877be
+ if self.spec.satisfies("@:2021.2.0", strict=True):
+ mkdirp(self.prefix.lib.pkgconfig)
+
+ with open(join_path(self.prefix.lib.pkgconfig, "tbb.pc"), "w") as f:
+ f.write("prefix={0}\n".format(self.prefix))
+ f.write("exec_prefix=${prefix}\n")
+ f.write("libdir={0}\n".format(self.prefix.lib))
+ f.write("includedir={0}\n".format(self.prefix.include))
+ f.write("\n")
+ f.write("Name: Threading Building Blocks\n")
+ f.write("Description: Intel's parallelism library for C++\n")
+ f.write("Version: {0}\n".format(self.spec.version))
+ f.write("Cflags: -I${includedir}\n")
+ f.write("Libs: -L${libdir} -ltbb -latomic\n")
+
+
+class MakefileBuilder(spack.build_systems.makefile.MakefileBuilder, SetupEnvironment):
def coerce_to_spack(self, tbb_build_subdir):
for compiler in ["icc", "gcc", "clang"]:
fs = glob.glob(join_path(tbb_build_subdir, "*.%s.inc" % compiler))
@@ -183,16 +233,7 @@ class IntelTbb(CMakePackage):
else:
of.write(lin)
- @when("@:2020.3")
- def cmake(self, spec, prefix):
- return
-
- @when("@:2020.3")
- def cmake_args(self):
- return
-
- @when("@:2020.3")
- def build(self, spec, prefix):
+ def build(self, pkg, spec, prefix):
# Deactivate use of RTM with GCC when on an OS with a very old
# assembler.
if (
@@ -239,8 +280,7 @@ class IntelTbb(CMakePackage):
make_opts.append("compiler={0}".format(tbb_compiler))
make(*make_opts)
- @when("@:2020.3")
- def install(self, spec, prefix):
+ def install(self, pkg, spec, prefix):
mkdirp(prefix)
mkdirp(prefix.lib)
@@ -267,46 +307,8 @@ class IntelTbb(CMakePackage):
with working_dir(join_path(self.stage.source_path, "cmake")):
inspect.getmodule(self).cmake(*cmake_args)
- @when("@:2020.3")
@run_after("install")
def darwin_fix(self):
# Replace @rpath in ids with full path
if sys.platform == "darwin":
fix_darwin_install_name(self.prefix.lib)
-
- @property
- def libs(self):
- shared = True if "+shared" in self.spec else False
- return find_libraries("libtbb*", root=self.prefix, shared=shared, recursive=True)
-
- @when("@2021.1.1:")
- def cmake_args(self):
- spec = self.spec
- options = []
- options.append("-DCMAKE_HWLOC_2_INCLUDE_PATH=%s" % spec["hwloc"].prefix.include)
- options.append("-DCMAKE_HWLOC_2_LIBRARY_PATH=%s" % spec["hwloc"].libs)
- options.append("-DTBB_CPF=ON")
- options.append("-DTBB_STRICT=OFF")
- if spec.variants["cxxstd"].value != "default":
- options.append("-DCMAKE_CXX_STANDARD=%s" % spec.variants["cxxstd"].value)
- return options
-
- @run_after("install")
- def install_pkgconfig(self):
- # pkg-config generation is introduced in May 5, 2021.
- # It must not be overwritten by spack-generated tbb.pc.
- # https://github.com/oneapi-src/oneTBB/commit/478de5b1887c928e52f029d706af6ea640a877be
- if self.spec.satisfies("@:2021.2.0", strict=True):
- mkdirp(self.prefix.lib.pkgconfig)
-
- with open(join_path(self.prefix.lib.pkgconfig, "tbb.pc"), "w") as f:
- f.write("prefix={0}\n".format(self.prefix))
- f.write("exec_prefix=${prefix}\n")
- f.write("libdir={0}\n".format(self.prefix.lib))
- f.write("includedir={0}\n".format(self.prefix.include))
- f.write("\n")
- f.write("Name: Threading Building Blocks\n")
- f.write("Description: Intel's parallelism library for C++\n")
- f.write("Version: {0}\n".format(self.spec.version))
- f.write("Cflags: -I${includedir}\n")
- f.write("Libs: -L${libdir} -ltbb -latomic\n")
diff --git a/var/spack/repos/builtin/packages/json-c/package.py b/var/spack/repos/builtin/packages/json-c/package.py
index 559039f21d..a62e0323ea 100644
--- a/var/spack/repos/builtin/packages/json-c/package.py
+++ b/var/spack/repos/builtin/packages/json-c/package.py
@@ -6,7 +6,7 @@
from spack.package import *
-class JsonC(CMakePackage):
+class JsonC(CMakePackage, AutotoolsPackage):
"""A JSON implementation in C."""
homepage = "https://github.com/json-c/json-c/wiki"
@@ -20,7 +20,13 @@ class JsonC(CMakePackage):
version("0.12", sha256="000c01b2b3f82dcb4261751eb71f1b084404fb7d6a282f06074d3c17078b9f3f")
version("0.11", sha256="28dfc65145dc0d4df1dfe7701ac173c4e5f9347176c8983edbfac9149494448c")
- depends_on("autoconf", when="@:0.13.1", type="build")
+ build_system(
+ conditional("cmake", when="@0.14:"),
+ conditional("autotools", when="@:0.13.1"),
+ default="cmake",
+ )
+
+ depends_on("autoconf", when="build_system=autotools", type="build")
parallel = False
@@ -32,19 +38,6 @@ class JsonC(CMakePackage):
"Makefile.in",
)
- @when("@:0.13.1")
- def cmake(self, spec, prefix):
- configure_args = ["--prefix=" + prefix]
- configure(*configure_args)
-
- @when("@:0.13.1")
- def build(self, spec, prefix):
- make()
-
- @when("@:0.13.1")
- def install(self, spec, prefix):
- make("install")
-
@when("%cce@11.0.3:")
def patch(self):
filter_file("-Werror", "", "CMakeLists.txt")
diff --git a/var/spack/repos/builtin/packages/libtree/package.py b/var/spack/repos/builtin/packages/libtree/package.py
index 572f459b43..a1c97dfd76 100644
--- a/var/spack/repos/builtin/packages/libtree/package.py
+++ b/var/spack/repos/builtin/packages/libtree/package.py
@@ -2,11 +2,12 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
-
+import spack.build_systems.cmake
+import spack.build_systems.makefile
from spack.package import *
-class Libtree(MakefilePackage):
+class Libtree(MakefilePackage, CMakePackage):
"""ldd as a tree"""
homepage = "https://github.com/haampie/libtree"
@@ -35,6 +36,10 @@ class Libtree(MakefilePackage):
version("1.0.4", sha256="b15a54b6f388b8bd8636e288fcb581029f1e65353660387b0096a554ad8e9e45")
version("1.0.3", sha256="67ce886c191d50959a5727246cdb04af38872cd811c9ed4e3822f77a8f40b20b")
+ build_system(
+ conditional("cmake", when="@:2"), conditional("makefile", when="@3:"), default="makefile"
+ )
+
def url_for_version(self, version):
if version < Version("2.0.0"):
return (
@@ -45,13 +50,8 @@ class Libtree(MakefilePackage):
return "https://github.com/haampie/libtree/archive/refs/tags/v{0}.tar.gz".format(version)
- # Version 3.x (Makefile)
- @when("@3:")
- def install(self, spec, prefix):
- make("install", "PREFIX=" + prefix)
-
# Version 2.x and earlier (CMake)
- with when("@:2"):
+ with when("build_system=cmake"):
variant("chrpath", default=False, description="Use chrpath for deployment")
variant("strip", default=False, description="Use binutils strip for deployment")
variant(
@@ -70,23 +70,19 @@ class Libtree(MakefilePackage):
depends_on("cxxopts", when="@2.0.0:2", type="build")
depends_on("elfio@:3.9", when="@2.0.0:2", type="build")
+
+class CMakeBuilder(spack.build_systems.cmake.CMakeBuilder):
def cmake_args(self):
- tests_enabled = "ON" if self.run_tests else "OFF"
+ tests_enabled = "ON" if self.pkg.run_tests else "OFF"
if self.spec.satisfies("@2.0:"):
tests_define = "LIBTREE_BUILD_TESTS"
else:
tests_define = "BUILD_TESTING"
- return [CMakePackage.define(tests_define, tests_enabled)]
+ return [self.define(tests_define, tests_enabled)]
- @when("@:2")
- def edit(self, spec, prefix):
- options = CMakePackage._std_args(self) + self.cmake_args()
- options.append(self.stage.source_path)
- with working_dir(self.build_directory):
- cmake(*options)
- @when("@:2")
- def check(self):
- with working_dir(self.build_directory):
- ctest("--output-on-failure")
+class MakefileBuilder(spack.build_systems.makefile.MakefileBuilder):
+ @property
+ def install_targets(self):
+ return ["install", "PREFIX=" + self.prefix]
diff --git a/var/spack/repos/builtin/packages/libxkbcommon/package.py b/var/spack/repos/builtin/packages/libxkbcommon/package.py
index b7003ab5a9..054f05e9bd 100644
--- a/var/spack/repos/builtin/packages/libxkbcommon/package.py
+++ b/var/spack/repos/builtin/packages/libxkbcommon/package.py
@@ -2,11 +2,12 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
-
+import spack.build_systems.autotools
+import spack.build_systems.meson
from spack.package import *
-class Libxkbcommon(MesonPackage):
+class Libxkbcommon(MesonPackage, AutotoolsPackage):
"""xkbcommon is a library to handle keyboard descriptions, including
loading them from disk, parsing them and handling their state. It's mainly
meant for client toolkits, window systems, and other system
@@ -15,6 +16,10 @@ class Libxkbcommon(MesonPackage):
homepage = "https://xkbcommon.org/"
url = "https://xkbcommon.org/download/libxkbcommon-0.8.2.tar.xz"
+ build_system(
+ conditional("meson", when="@0.9:"), conditional("autotools", when="@:0.8"), default="meson"
+ )
+
version("1.4.0", sha256="106cec5263f9100a7e79b5f7220f889bc78e7d7ffc55d2b6fdb1efefb8024031")
version(
"0.8.2",
@@ -44,6 +49,8 @@ class Libxkbcommon(MesonPackage):
depends_on("wayland@1.2.0:", when="+wayland")
depends_on("wayland-protocols@1.7:", when="+wayland")
+
+class MesonBuilder(spack.build_systems.meson.MesonBuilder):
def meson_args(self):
return [
"-Dxkb-config-root={0}".format(self.spec["xkbdata"].prefix),
@@ -51,26 +58,11 @@ class Libxkbcommon(MesonPackage):
"-Denable-wayland=" + str(self.spec.satisfies("+wayland")),
]
- @when("@:0.8")
+
+class AutotoolsBuilder(spack.build_systems.autotools.AutotoolsBuilder):
def configure_args(self):
"""Configure arguments are passed using meson_args functions"""
return [
"--with-xkb-config-root={0}".format(self.spec["xkbdata"].prefix),
"--disable-docs",
- "--" + ("en" if self.spec.satisfies("+wayland") else "dis") + "able-wayland",
- ]
-
- @when("@:0.8")
- def meson(self, spec, prefix):
- """Run the AutotoolsPackage configure phase in source_path"""
- configure("--prefix=" + prefix, *self.configure_args())
-
- @when("@:0.8")
- def build(self, spec, prefix):
- """Run the AutotoolsPackage build phase in source_path"""
- make()
-
- @when("@:0.8")
- def install(self, spec, prefix):
- """Run the AutotoolsPackage install phase in source_path"""
- make("install")
+ ] + self.enable_or_disable("wayland")
diff --git a/var/spack/repos/builtin/packages/lua/package.py b/var/spack/repos/builtin/packages/lua/package.py
index cd314f3df5..4894ab2b91 100644
--- a/var/spack/repos/builtin/packages/lua/package.py
+++ b/var/spack/repos/builtin/packages/lua/package.py
@@ -26,11 +26,6 @@ class LuaImplPackage(MakefilePackage):
description="Fetcher to use in the LuaRocks package manager",
)
- phases = MakefilePackage.phases + ["add_luarocks"]
- #: This attribute is used in UI queries that need to know the build
- #: system base class
- build_system_class = "LuaImplPackage"
-
lua_version_override = None
def __init__(self, *args, **kwargs):
@@ -105,7 +100,9 @@ class LuaImplPackage(MakefilePackage):
)
symlink(real_lib, "liblua" + ext)
- def add_luarocks(self, spec, prefix):
+ @run_after("install")
+ def add_luarocks(self):
+ prefix = self.spec.prefix
with working_dir(os.path.join("luarocks", "luarocks")):
configure("--prefix=" + prefix, "--with-lua=" + prefix)
make("build")
diff --git a/var/spack/repos/builtin/packages/mesa/package.py b/var/spack/repos/builtin/packages/mesa/package.py
index e82bdc3764..caa47f8d64 100644
--- a/var/spack/repos/builtin/packages/mesa/package.py
+++ b/var/spack/repos/builtin/packages/mesa/package.py
@@ -2,9 +2,9 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
-
import sys
+import spack.build_systems.meson
from spack.package import *
@@ -158,6 +158,28 @@ class Mesa(MesonPackage):
flags.append("-std=c99")
return super(Mesa, self).flag_handler(name, flags)
+ @property
+ def libglx_headers(self):
+ return find_headers("GL/glx", root=self.spec.prefix.include, recursive=False)
+
+ @property
+ def libglx_libs(self):
+ return find_libraries("libGL", root=self.spec.prefix, recursive=True)
+
+ @property
+ def libosmesa_headers(self):
+ return find_headers("GL/osmesa", root=self.spec.prefix.include, recursive=False)
+
+ @property
+ def libosmesa_libs(self):
+ if "platform=windows" in self.spec:
+ lib_name = "osmesa"
+ else:
+ lib_name = "libOSMesa"
+ return find_libraries(lib_name, root=self.spec.prefix, recursive=True)
+
+
+class MesonBuilder(spack.build_systems.meson.MesonBuilder):
def meson_args(self):
spec = self.spec
args = [
@@ -274,23 +296,3 @@ class Mesa(MesonPackage):
args.append("-Ddri-drivers=" + ",".join(args_dri_drivers))
return args
-
- @property
- def libglx_headers(self):
- return find_headers("GL/glx", root=self.spec.prefix.include, recursive=False)
-
- @property
- def libglx_libs(self):
- return find_libraries("libGL", root=self.spec.prefix, recursive=True)
-
- @property
- def libosmesa_headers(self):
- return find_headers("GL/osmesa", root=self.spec.prefix.include, recursive=False)
-
- @property
- def libosmesa_libs(self):
- if "platform=windows" in self.spec:
- lib_name = "osmesa"
- else:
- lib_name = "libOSMesa"
- return find_libraries(lib_name, root=self.spec.prefix, recursive=True)
diff --git a/var/spack/repos/builtin/packages/metis/gklib_path.patch b/var/spack/repos/builtin/packages/metis/gklib_path.patch
new file mode 100644
index 0000000000..2cee12d044
--- /dev/null
+++ b/var/spack/repos/builtin/packages/metis/gklib_path.patch
@@ -0,0 +1,11 @@
+--- a/CMakeLists.txt 2022-07-20 21:17:20.352231603 +0200
++++ b/CMakeLists.txt 2022-07-20 21:19:28.998269385 +0200
+@@ -1,7 +1,7 @@
+ cmake_minimum_required(VERSION 2.8)
+ project(METIS)
+
+-set(GKLIB_PATH "GKlib" CACHE PATH "path to GKlib")
++set(GKLIB_PATH "${CMAKE_SOURCE_DIR}/GKlib" CACHE PATH "path to GKlib")
+ set(SHARED FALSE CACHE BOOL "build a shared library")
+
+ if(MSVC)
diff --git a/var/spack/repos/builtin/packages/metis/package.py b/var/spack/repos/builtin/packages/metis/package.py
index edf99be5ee..da6e798783 100644
--- a/var/spack/repos/builtin/packages/metis/package.py
+++ b/var/spack/repos/builtin/packages/metis/package.py
@@ -2,20 +2,22 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
-
-
import os
import sys
+import spack.build_systems.cmake
+import spack.build_systems.makefile
from spack.package import *
-class Metis(Package):
+class Metis(CMakePackage, MakefilePackage):
"""METIS is a set of serial programs for partitioning graphs, partitioning
finite element meshes, and producing fill reducing orderings for sparse
- matrices. The algorithms implemented in METIS are based on the
- multilevel recursive-bisection, multilevel k-way, and multi-constraint
- partitioning schemes."""
+ matrices.
+
+ The algorithms implemented in METIS are based on the multilevel
+ recursive-bisection, multilevel k-way, and multi-constraint partitioning schemes.
+ """
homepage = "http://glaros.dtc.umn.edu/gkhome/metis/metis/overview"
url = "http://glaros.dtc.umn.edu/gkhome/fetch/sw/metis/metis-5.1.0.tar.gz"
@@ -27,52 +29,29 @@ class Metis(Package):
version("5.1.0", sha256="76faebe03f6c963127dbb73c13eab58c9a3faeae48779f049066a21c087c5db2")
version("4.0.3", sha256="5efa35de80703c1b2c4d0de080fafbcf4e0d363a21149a1ad2f96e0144841a55")
- variant("shared", default=True, description="Enables the build of shared libraries.")
- variant("gdb", default=False, description="Enables gdb support (version 5+).")
- variant("int64", default=False, description="Sets the bit width of METIS's index type to 64.")
- variant("real64", default=False, description="Sets the bit width of METIS's real type to 64.")
-
- # For Metis version 5:, the build system is CMake, provide the
- # `build_type` variant.
- variant(
- "build_type",
- default="Release",
- description="The build type for the installation (only Debug or"
- " Release allowed for version 4).",
- values=("Debug", "Release", "RelWithDebInfo", "MinSizeRel"),
+ build_system(
+ conditional("cmake", when="@5:"), conditional("makefile", when="@:4"), default="cmake"
)
+ variant("shared", default=True, description="Build shared libraries")
+ with when("build_system=cmake"):
+ variant("gdb", default=False, description="Enable gdb support")
+ variant("int64", default=False, description="Use index type of 64 bit")
+ variant("real64", default=False, description="Use real type of 64 bit")
+
+ # Use the correct path to GKLIB when building out of source
+ patch("gklib_path.patch")
+ # Install both gklib_defs.h and gklib_rename.h
+ patch("install_gklib_defs_rename.patch")
+ # Disable the "misleading indentation" warning when compiling
+ patch("gklib_nomisleadingindentation_warning.patch", when="%gcc@6:")
+
+ with when("build_system=makefile"):
+ variant("debug", default=False, description="Compile in debug mode")
- # Prior to version 5, the (non-cmake) build system only knows about
- # 'build_type=Debug|Release'.
- conflicts("@:4", when="build_type=RelWithDebInfo")
- conflicts("@:4", when="build_type=MinSizeRel")
- conflicts("@:4", when="+gdb")
- conflicts("@:4", when="+int64")
- conflicts("@:4", when="+real64")
-
- depends_on("cmake@2.8:", when="@5:", type="build")
-
- patch("install_gklib_defs_rename.patch", when="@5:")
- patch("gklib_nomisleadingindentation_warning.patch", when="@5: %gcc@6:")
-
- def setup_build_environment(self, env):
- # Ignore warnings/errors re unrecognized omp pragmas on %intel
- if "%intel@14:" in self.spec:
- env.append_flags("CFLAGS", "-diag-disable 3180")
- # Ignore some warnings to get it to compile with %nvhpc
- # 111: statement is unreachable
- # 177: variable "foo" was declared but never referenced
- # 188: enumerated type mixed with another type
- # 550: variable "foo" was set but never used
- if "%nvhpc" in self.spec:
- env.append_flags("CFLAGS", "--display_error_number")
- env.append_flags("CFLAGS", "--diag_suppress 111")
- env.append_flags("CFLAGS", "--diag_suppress 177")
- env.append_flags("CFLAGS", "--diag_suppress 188")
- env.append_flags("CFLAGS", "--diag_suppress 550")
-
- @when("@5:")
def patch(self):
+ if not self.spec.satisfies("build_system=cmake"):
+ return
+
source_path = self.stage.source_path
metis_header = FileFilter(join_path(source_path, "include", "metis.h"))
@@ -96,18 +75,38 @@ class Metis(Package):
join_path(source_path, "GKlib", "error.c"),
)
- @when("@:4")
- def install(self, spec, prefix):
- # Process library spec and options
+
+class SetupEnvironment(object):
+ def setup_build_environment(self, env):
+ # Ignore warnings/errors re unrecognized omp pragmas on %intel
+ if "%intel@14:" in self.spec:
+ env.append_flags("CFLAGS", "-diag-disable 3180")
+ # Ignore some warnings to get it to compile with %nvhpc
+ # 111: statement is unreachable
+ # 177: variable "foo" was declared but never referenced
+ # 188: enumerated type mixed with another type
+ # 550: variable "foo" was set but never used
+ if "%nvhpc" in self.spec:
+ env.append_flags("CFLAGS", "--display_error_number")
+ env.append_flags("CFLAGS", "--diag_suppress 111")
+ env.append_flags("CFLAGS", "--diag_suppress 177")
+ env.append_flags("CFLAGS", "--diag_suppress 188")
+ env.append_flags("CFLAGS", "--diag_suppress 550")
+
+
+class MakefileBuilder(spack.build_systems.makefile.MakefileBuilder, SetupEnvironment):
+ @property
+ def build_targets(self):
options = []
- if "+shared" in spec:
- options.append("COPTIONS={0}".format(self.compiler.cc_pic_flag))
- if spec.variants["build_type"].value == "Debug":
+ if "+shared" in self.spec:
+ options.append("COPTIONS={0}".format(self.pkg.compiler.cc_pic_flag))
+ if "+debug" in self.spec:
options.append("OPTFLAGS=-g -O0")
- make(*options)
+ return options
+ def install(self, pkg, spec, prefix):
# Compile and install library files
- ccompile = Executable(self.compiler.cc)
+ ccompile = Executable(pkg.compiler.cc)
mkdir(prefix.bin)
binfiles = (
@@ -140,7 +139,7 @@ class Metis(Package):
install(sharefile, prefix.share)
if "+shared" in spec:
- shared_flags = [self.compiler.cc_pic_flag, "-shared"]
+ shared_flags = [pkg.compiler.cc_pic_flag, "-shared"]
if sys.platform == "darwin":
shared_suffix = "dylib"
shared_flags.extend(["-Wl,-all_load", "libmetis.a"])
@@ -157,7 +156,7 @@ class Metis(Package):
ccompile(
"-I%s" % prefix.include,
"-L%s" % prefix.lib,
- (self.compiler.cc_rpath_arg + prefix.lib if "+shared" in spec else ""),
+ (pkg.compiler.cc_rpath_arg + prefix.lib if "+shared" in spec else ""),
join_path("Programs", "io.o"),
join_path("Test", "mtest.c"),
"-o",
@@ -166,58 +165,32 @@ class Metis(Package):
"-lm",
)
- if self.run_tests:
- test_bin = lambda testname: join_path(prefix.bin, testname)
- test_graph = lambda graphname: join_path(prefix.share, graphname)
-
- graph = test_graph("4elt.graph")
- os.system("%s %s" % (test_bin("mtest"), graph))
- os.system("%s %s 40" % (test_bin("kmetis"), graph))
- os.system("%s %s" % (test_bin("onmetis"), graph))
- graph = test_graph("test.mgraph")
- os.system("%s %s 2" % (test_bin("pmetis"), graph))
- os.system("%s %s 2" % (test_bin("kmetis"), graph))
- os.system("%s %s 5" % (test_bin("kmetis"), graph))
- graph = test_graph("metis.mesh")
- os.system("%s %s 10" % (test_bin("partnmesh"), graph))
- os.system("%s %s 10" % (test_bin("partdmesh"), graph))
- os.system("%s %s" % (test_bin("mesh2dual"), graph))
-
- # FIXME: The following code should replace the testing code in the
- # block above since it causes installs to fail when one or more of
- # the Metis tests fail, but it currently doesn't work because the
- # 'mtest', 'onmetis', and 'partnmesh' tests return error codes that
- # trigger false positives for failure.
- """
- Executable(test_bin('mtest'))(test_graph('4elt.graph'))
- Executable(test_bin('kmetis'))(test_graph('4elt.graph'), '40')
- Executable(test_bin('onmetis'))(test_graph('4elt.graph'))
-
- Executable(test_bin('pmetis'))(test_graph('test.mgraph'), '2')
- Executable(test_bin('kmetis'))(test_graph('test.mgraph'), '2')
- Executable(test_bin('kmetis'))(test_graph('test.mgraph'), '5')
-
- Executable(test_bin('partnmesh'))(test_graph('metis.mesh'), '10')
- Executable(test_bin('partdmesh'))(test_graph('metis.mesh'), '10')
- Executable(test_bin('mesh2dual'))(test_graph('metis.mesh'))
- """
-
- @when("@5:")
- def install(self, spec, prefix):
- source_directory = self.stage.source_path
- build_directory = join_path(self.stage.path, "build")
-
- options = CMakePackage._std_args(self)
- options.append("-DGKLIB_PATH:PATH=%s/GKlib" % source_directory)
-
- # Normally this is available via the 'CMakePackage' object, but metis
- # IS-A 'Package' (not a 'CMakePackage') to support non-cmake metis@:5.
- build_type = spec.variants["build_type"].value
- options.extend(["-DCMAKE_BUILD_TYPE:STRING={0}".format(build_type)])
-
- if "+shared" in spec:
- options.append("-DSHARED:BOOL=ON")
- else:
+ def check(self):
+ test_bin = lambda testname: join_path(prefix.bin, testname)
+ test_graph = lambda graphname: join_path(prefix.share, graphname)
+
+ graph = test_graph("4elt.graph")
+ os.system("%s %s" % (test_bin("mtest"), graph))
+ os.system("%s %s 40" % (test_bin("kmetis"), graph))
+ os.system("%s %s" % (test_bin("onmetis"), graph))
+ graph = test_graph("test.mgraph")
+ os.system("%s %s 2" % (test_bin("pmetis"), graph))
+ os.system("%s %s 2" % (test_bin("kmetis"), graph))
+ os.system("%s %s 5" % (test_bin("kmetis"), graph))
+ graph = test_graph("metis.mesh")
+ os.system("%s %s 10" % (test_bin("partnmesh"), graph))
+ os.system("%s %s 10" % (test_bin("partdmesh"), graph))
+ os.system("%s %s" % (test_bin("mesh2dual"), graph))
+
+
+class CMakeBuilder(spack.build_systems.cmake.CMakeBuilder, SetupEnvironment):
+ def cmake_args(self):
+ options = [
+ self.define_from_variant("SHARED", "shared"),
+ self.define_from_variant("GDB", "gdb"),
+ ]
+
+ if self.spec.satisfies("~shared"):
# Remove all RPATH options
# (RPATHxxx options somehow trigger cmake to link dynamically)
rpath_options = []
@@ -226,40 +199,41 @@ class Metis(Package):
rpath_options.append(o)
for o in rpath_options:
options.remove(o)
- if "+gdb" in spec:
- options.append("-DGDB:BOOL=ON")
- with working_dir(build_directory, create=True):
- cmake(source_directory, *options)
- make()
- make("install")
+ return options
+ @run_after("install")
+ def install_headers(self):
+ with working_dir(self.build_directory):
# install all headers, which will be needed for ParMETIS and other programs
- subdirs = ["GKlib", "libmetis", "programs"]
- for subd in subdirs:
- inc_dist = join_path(prefix.include, subd)
+ directories = ["GKlib", "libmetis", "programs"]
+ for directory in directories:
+ inc_dist = join_path(self.prefix.include, directory)
mkdirp(inc_dist)
- install(join_path(source_directory, subd, "*.h"), inc_dist)
-
- if self.run_tests:
- # FIXME: On some systems, the installed binaries for METIS cannot
- # be executed without first being read.
- ls = which("ls")
- ls("-a", "-l", prefix.bin)
-
- for f in ["4elt", "copter2", "mdual"]:
- graph = join_path(source_directory, "graphs", "%s.graph" % f)
- Executable(join_path(prefix.bin, "graphchk"))(graph)
- Executable(join_path(prefix.bin, "gpmetis"))(graph, "2")
- Executable(join_path(prefix.bin, "ndmetis"))(graph)
-
- graph = join_path(source_directory, "graphs", "test.mgraph")
- Executable(join_path(prefix.bin, "gpmetis"))(graph, "2")
- graph = join_path(source_directory, "graphs", "metis.mesh")
- Executable(join_path(prefix.bin, "mpmetis"))(graph, "2")
-
- @run_after("install")
+ install(join_path(self.stage.source_path, directory, "*.h"), inc_dist)
+
+ def check(self):
+ # On some systems, the installed binaries for METIS cannot
+ # be executed without first being read.
+ ls = which("ls")
+ ls("-a", "-l", self.prefix.bin)
+
+ graphchk = Executable(join_path(self.prefix.bin, "graphchk"))
+ gpmetis = Executable(join_path(self.prefix.bin, "gpmetis"))
+ ndmetis = Executable(join_path(self.prefix.bin, "ndmetis"))
+ mpmetis = Executable(join_path(self.prefix.bin, "mpmetis"))
+ for f in ["4elt", "copter2", "mdual"]:
+ graph = join_path(self.stage.source_path, "graphs", "%s.graph" % f)
+ graphchk(graph)
+ gpmetis(graph, "2")
+ ndmetis(graph)
+
+ graph = join_path(self.stage.source_path, "graphs", "test.mgraph")
+ gpmetis(graph, "2")
+ graph = join_path(self.stage.source_path, "graphs", "metis.mesh")
+ mpmetis(graph, "2")
+
+ @run_after("install", when="+shared platform=darwin")
def darwin_fix(self):
# The shared library is not installed correctly on Darwin; fix this
- if (sys.platform == "darwin") and ("+shared" in self.spec):
- fix_darwin_install_name(prefix.lib)
+ fix_darwin_install_name(prefix.lib)
diff --git a/var/spack/repos/builtin/packages/mmg/package.py b/var/spack/repos/builtin/packages/mmg/package.py
index f0269ca430..3cd579dff1 100644
--- a/var/spack/repos/builtin/packages/mmg/package.py
+++ b/var/spack/repos/builtin/packages/mmg/package.py
@@ -5,6 +5,7 @@
import os
+import spack.build_systems.cmake
from spack.package import *
from spack.util.executable import which
@@ -43,24 +44,22 @@ class Mmg(CMakePackage):
depends_on("doxygen", when="+doc")
depends_on("vtk", when="+vtk")
- def cmake_args(self):
- args = []
-
- args.append(self.define_from_variant("USE_SCOTCH", "scotch"))
- args.append(self.define_from_variant("USE_VTK", "vtk"))
- if "+shared" in self.spec:
- args.append("-DLIBMMG3D_SHARED=ON")
- args.append("-DLIBMMG2D_SHARED=ON")
- args.append("-DLIBMMGS_SHARED=ON")
- args.append("-DLIBMMG_SHARED=ON")
- else:
- args.append("-DLIBMMG3D_STATIC=ON")
- args.append("-DLIBMMG2D_STATIC=ON")
- args.append("-DLIBMMGS_STATIC=ON")
- args.append("-DLIBMMG_STATIC=ON")
-
- return args
+class CMakeBuilder(spack.build_systems.cmake.CMakeBuilder):
+ def cmake_args(self):
+ shared_active = self.spec.satisfies("+shared")
+ return [
+ self.define_from_variant("USE_SCOTCH", "scotch"),
+ self.define_from_variant("USE_VTK", "vtk"),
+ self.define("LIBMMG3D_SHARED", shared_active),
+ self.define("LIBMMG2D_SHARED", shared_active),
+ self.define("LIBMMGS_SHARED", shared_active),
+ self.define("LIBMMG_SHARED", shared_active),
+ self.define("LIBMMG3D_STATIC", not shared_active),
+ self.define("LIBMMG2D_STATIC", not shared_active),
+ self.define("LIBMMGS_STATIC", not shared_active),
+ self.define("LIBMMG_STATIC", not shared_active),
+ ]
# parmmg requires this for its build
@run_after("install")
diff --git a/var/spack/repos/builtin/packages/nasm/package.py b/var/spack/repos/builtin/packages/nasm/package.py
index e7d76b1d8f..5514b26b5e 100644
--- a/var/spack/repos/builtin/packages/nasm/package.py
+++ b/var/spack/repos/builtin/packages/nasm/package.py
@@ -8,7 +8,7 @@ import os
from spack.package import *
-class Nasm(Package):
+class Nasm(AutotoolsPackage):
"""NASM (Netwide Assembler) is an 80x86 assembler designed for
portability and modularity. It includes a disassembler as well."""
@@ -17,6 +17,8 @@ class Nasm(Package):
list_url = "https://www.nasm.us/pub/nasm/releasebuilds"
list_depth = 1
+ build_system("autotools", conditional("generic", when="platform=windows"), default="autotools")
+
version("2.15.05", sha256="9182a118244b058651c576baa9d0366ee05983c4d4ae1d9ddd3236a9f2304997")
version("2.14.02", sha256="b34bae344a3f2ed93b2ca7bf25f1ed3fb12da89eeda6096e3551fd66adeae9fc")
version("2.13.03", sha256="23e1b679d64024863e2991e5c166e19309f0fe58a9765622b35bd31be5b2cc99")
@@ -31,11 +33,11 @@ class Nasm(Package):
when="@2.13.03 %gcc@8:",
)
- patch("msvc.mak.patch", when="@2.15.05 platform=windows")
-
- conflicts("%intel@:14", when="@2.14:", msg="Intel 14 has immature C11 support")
+ with when("platform=windows"):
+ depends_on("perl")
+ patch("msvc.mak.patch", when="@2.15.05")
- depends_on("perl", when="platform=windows")
+ conflicts("%intel@:14", when="@2.14:", msg="Intel <= 14 lacks support for C11")
def patch(self):
# Remove flags not recognized by the NVIDIA compiler
@@ -51,13 +53,8 @@ class Nasm(Package):
"configure",
)
- def install(self, spec, prefix):
- with working_dir(self.stage.source_path, create=True):
- configure(*["--prefix={0}".format(self.prefix)])
- make("V=1")
- make("install")
- @when("platform=windows")
+class GenericBuilder(spack.build_systems.generic.GenericBuilder):
def install(self, spec, prefix):
with working_dir(self.stage.source_path, create=True):
# build NASM with nmake
diff --git a/var/spack/repos/builtin/packages/netlib-lapack/package.py b/var/spack/repos/builtin/packages/netlib-lapack/package.py
index 4908fc1b9d..387657d1ca 100644
--- a/var/spack/repos/builtin/packages/netlib-lapack/package.py
+++ b/var/spack/repos/builtin/packages/netlib-lapack/package.py
@@ -2,7 +2,7 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
-
+import spack.build_systems.cmake
from spack.package import *
@@ -55,6 +55,8 @@ class NetlibLapack(CMakePackage):
# netlib-lapack is the reference implementation of LAPACK
for ver in [
+ "3.10.1",
+ "3.10.0",
"3.9.1",
"3.9.0",
"3.8.0",
@@ -182,82 +184,53 @@ class NetlibLapack(CMakePackage):
lapacke_h = join_path(include_dir, "lapacke.h")
return HeaderList([cblas_h, lapacke_h])
- @property
- def build_directory(self):
- return join_path(
- self.stage.source_path,
- "spack-build-shared" if self._building_shared else "spack-build-static",
- )
+class CMakeBuilder(spack.build_systems.cmake.CMakeBuilder):
def cmake_args(self):
- args = ["-DBUILD_SHARED_LIBS:BOOL=" + ("ON" if self._building_shared else "OFF")]
-
- if self.spec.satisfies("+lapacke"):
- args.extend(["-DLAPACKE:BOOL=ON", "-DLAPACKE_WITH_TMG:BOOL=ON"])
- else:
- args.extend(["-DLAPACKE:BOOL=OFF", "-DLAPACKE_WITH_TMG:BOOL=OFF"])
-
- if self.spec.satisfies("@3.6.0:"):
- args.append("-DCBLAS=ON") # always build CBLAS
+ args = [
+ self.define_from_variant("BUILD_SHARED_LIBS", "shared"),
+ self.define_from_variant("LAPACKE", "lapacke"),
+ self.define_from_variant("LAPACKE_WITH_TMG", "lapacke"),
+ self.define("CBLAS", self.spec.satisfies("@3.6.0:")),
+ ]
if self.spec.satisfies("%intel"):
# Intel compiler finds serious syntax issues when trying to
# build CBLAS and LapackE
- args.extend(["-DCBLAS=OFF", "-DLAPACKE:BOOL=OFF"])
+ args.extend([self.define("CBLAS", False), self.define("LAPACKE", False)])
if self.spec.satisfies("%xl") or self.spec.satisfies("%xl_r"):
# use F77 compiler if IBM XL
args.extend(
[
- "-DCMAKE_Fortran_COMPILER=" + self.compiler.f77,
- "-DCMAKE_Fortran_FLAGS="
- + (" ".join(self.spec.compiler_flags["fflags"]))
- + " -O3 -qnohot",
+ self.define("CMAKE_Fortran_COMPILER", self.compiler.f77),
+ self.define(
+ "CMAKE_Fortran_FLAGS",
+ " ".join(self.spec.compiler_flags["fflags"]) + " -O3 -qnohot",
+ ),
]
)
# deprecated routines are commonly needed by, for example, suitesparse
# Note that OpenBLAS spack is built with deprecated routines
- args.append("-DBUILD_DEPRECATED:BOOL=ON")
+ args.append(self.define("BUILD_DEPRECATED", True))
if self.spec.satisfies("+external-blas"):
args.extend(
[
- "-DUSE_OPTIMIZED_BLAS:BOOL=ON",
- "-DBLAS_LIBRARIES:PATH=" + self.spec["blas"].libs.joined(";"),
+ self.define("USE_OPTIMIZED_BLAS", True),
+ self.define("BLAS_LIBRARIES:PATH", self.spec["blas"].libs.joined(";")),
]
)
if self.spec.satisfies("+xblas"):
args.extend(
[
- "-DXBLAS_INCLUDE_DIR=" + self.spec["netlib-xblas"].prefix.include,
- "-DXBLAS_LIBRARY=" + self.spec["netlib-xblas"].libs.joined(";"),
+ self.define("XBLAS_INCLUDE_DIR", self.spec["netlib-xblas"].prefix.include),
+ self.define("XBLAS_LIBRARY", self.spec["netlib-xblas"].libs.joined(";")),
]
)
- args.append("-DBUILD_TESTING:BOOL=" + ("ON" if self.run_tests else "OFF"))
+ args.append(self.define("BUILD_TESTING", self.pkg.run_tests))
return args
-
- # Build, install, and check both static and shared versions of the
- # libraries when +shared
- @when("+shared")
- def cmake(self, spec, prefix):
- for self._building_shared in (False, True):
- super(NetlibLapack, self).cmake(spec, prefix)
-
- @when("+shared")
- def build(self, spec, prefix):
- for self._building_shared in (False, True):
- super(NetlibLapack, self).build(spec, prefix)
-
- @when("+shared")
- def install(self, spec, prefix):
- for self._building_shared in (False, True):
- super(NetlibLapack, self).install(spec, prefix)
-
- @when("+shared")
- def check(self):
- for self._building_shared in (False, True):
- super(NetlibLapack, self).check()
diff --git a/var/spack/repos/builtin/packages/openmpi/package.py b/var/spack/repos/builtin/packages/openmpi/package.py
index e1a34ac6b3..9405951c35 100644
--- a/var/spack/repos/builtin/packages/openmpi/package.py
+++ b/var/spack/repos/builtin/packages/openmpi/package.py
@@ -1053,8 +1053,7 @@ class Openmpi(AutotoolsPackage, CudaPackage):
return config_args
- @when("+wrapper-rpath")
- @run_after("install")
+ @run_after("install", when="+wrapper-rpath")
def filter_rpaths(self):
def filter_lang_rpaths(lang_tokens, rpath_arg):
if self.compiler.cc_rpath_arg == rpath_arg:
@@ -1086,8 +1085,7 @@ class Openmpi(AutotoolsPackage, CudaPackage):
filter_lang_rpaths(["c++", "CC", "cxx"], self.compiler.cxx_rpath_arg)
filter_lang_rpaths(["fort", "f77", "f90"], self.compiler.fc_rpath_arg)
- @when("@:3.0.4+wrapper-rpath")
- @run_after("install")
+ @run_after("install", when="@:3.0.4+wrapper-rpath")
def filter_pc_files(self):
files = find(self.spec.prefix.lib.pkgconfig, "*.pc")
x = FileFilter(*[f for f in files if not os.path.islink(f)])
diff --git a/var/spack/repos/builtin/packages/plasma/package.py b/var/spack/repos/builtin/packages/plasma/package.py
index 5e32b5af31..f6f32d0629 100644
--- a/var/spack/repos/builtin/packages/plasma/package.py
+++ b/var/spack/repos/builtin/packages/plasma/package.py
@@ -2,7 +2,8 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
-
+import spack.build_systems.cmake
+import spack.build_systems.makefile
from spack.package import *
@@ -38,6 +39,12 @@ class Plasma(CMakePackage):
url="https://github.com/icl-utk-edu/plasma/releases/download/17.01/plasma-17.01.tar.gz",
)
+ build_system(
+ conditional("makefile", when="@:17.1"),
+ conditional("cmake", when="@18.9:"),
+ default="cmake",
+ )
+
variant("shared", default=True, description="Build shared library (disables static library)")
variant("lua", default=False, description="Build Lua support for tuning tile sizes")
@@ -82,18 +89,15 @@ class Plasma(CMakePackage):
def patch(self):
python("tools/generate_precisions.py")
- @when("@18.9.0:")
- def cmake_args(self):
- options = list()
-
- options.extend(
- [
- "-DBLAS_LIBRARIES=%s" % self.spec["blas"].libs.joined(";"),
- "-DLAPACK_LIBRARIES=%s" % self.spec["lapack"].libs.joined(";"),
- ]
- )
- options += ["-DBUILD_SHARED_LIBS=%s" % ("ON" if ("+shared" in self.spec) else "OFF")]
+class CMakeBuilder(spack.build_systems.cmake.CMakeBuilder):
+ def cmake_args(self):
+ options = [
+ self.define("BLAS_LIBRARIES", self.spec["blas"].libs.joined(";")),
+ self.define("LAPACK_LIBRARIES", self.spec["lapack"].libs.joined(";")),
+ self.define_from_variant("BUILD_SHARED_LIBS", "shared"),
+ self.define_from_variant("PLASMA_DETECT_LUA", "lua"),
+ ]
for package, provider in (
("openblas", "openblas"),
@@ -102,32 +106,13 @@ class Plasma(CMakePackage):
):
if package in self.spec:
for lib in ("CBLAS", "LAPACKE"):
- options.append("-D%s_PROVIDER=%s" % (lib, provider))
-
- if "lua" in self.spec:
- options.append("-DPLASMA_DETECT_LUA=TRUE")
+ options.append(self.define("{}_PROVIDER".format(lib), provider))
return options
- # Before 18.9.0 it was an Makefile package
- @when("@:17.1")
- def cmake(self, spec, prefix):
- pass
-
- # Before 18.9.0 it was an Makefile package
- @when("@:17.1")
- def build(self, spec, prefix):
- pass
-
- # Before 18.9.0 it was an Makefile package
- @when("@:17.1")
- def install(self, spec, prefix):
- self.edit(spec, prefix)
- make()
- make("install")
-
- @when("@:17.1")
- def edit(self, spec, prefix):
+
+class MakefileBuilder(spack.build_systems.makefile.MakefileBuilder):
+ def edit(self, pkg, spec, prefix):
# copy "make.inc.mkl-gcc" provided by default into "make.inc"
open("make.inc", "w").write(open("make.inc.mkl-gcc").read())
diff --git a/var/spack/repos/builtin/packages/py-dm-tree/package.py b/var/spack/repos/builtin/packages/py-dm-tree/package.py
index 36dcdc910f..1c8b7eca36 100644
--- a/var/spack/repos/builtin/packages/py-dm-tree/package.py
+++ b/var/spack/repos/builtin/packages/py-dm-tree/package.py
@@ -26,20 +26,23 @@ class PyDmTree(PythonPackage):
depends_on("bazel", type="build")
depends_on("py-six@1.12.0:", type=("build", "run"))
+ # This is set later
+ tmp_path = None
+
@run_after("install")
def clean(self):
- remove_linked_tree(self.tmp_path)
+ remove_linked_tree(PyDmTree.tmp_path)
def patch(self):
- self.tmp_path = tempfile.mkdtemp(prefix="spack")
- env["TEST_TMPDIR"] = self.tmp_path
- env["HOME"] = self.tmp_path
+ PyDmTree.tmp_path = tempfile.mkdtemp(prefix="spack")
+ env["TEST_TMPDIR"] = PyDmTree.tmp_path
+ env["HOME"] = PyDmTree.tmp_path
args = [
# Don't allow user or system .bazelrc to override build settings
"'--nohome_rc',\n",
"'--nosystem_rc',\n",
# Bazel does not work properly on NFS, switch to /tmp
- "'--output_user_root={0}',\n".format(self.tmp_path),
+ "'--output_user_root={0}',\n".format(PyDmTree.tmp_path),
"'build',\n",
# Spack logs don't handle colored output well
"'--color=no',\n",
diff --git a/var/spack/repos/builtin/packages/py-onnx-runtime/package.py b/var/spack/repos/builtin/packages/py-onnx-runtime/package.py
index 62ea531395..fa956086b8 100644
--- a/var/spack/repos/builtin/packages/py-onnx-runtime/package.py
+++ b/var/spack/repos/builtin/packages/py-onnx-runtime/package.py
@@ -6,7 +6,7 @@
from spack.package import *
-class PyOnnxRuntime(CMakePackage, PythonPackage):
+class PyOnnxRuntime(CMakePackage, PythonExtension):
"""ONNX Runtime is a performance-focused complete scoring
engine for Open Neural Network Exchange (ONNX) models, with
an open extensible architecture to continually address the
diff --git a/var/spack/repos/builtin/packages/py-pillow/package.py b/var/spack/repos/builtin/packages/py-pillow/package.py
index 8f3488fce3..aa98037062 100644
--- a/var/spack/repos/builtin/packages/py-pillow/package.py
+++ b/var/spack/repos/builtin/packages/py-pillow/package.py
@@ -15,6 +15,18 @@ class PyPillowBase(PythonPackage):
# These defaults correspond to Pillow defaults
# https://pillow.readthedocs.io/en/stable/installation.html#external-libraries
+ VARIANTS_IN_SETUP_CFG = (
+ "zlib",
+ "jpeg",
+ "tiff",
+ "freetype",
+ "lcms",
+ "webp",
+ "webpmux",
+ "jpeg2000",
+ "imagequant",
+ "xcb",
+ )
variant("zlib", default=True, description="Compressed PNG functionality")
variant("jpeg", default=True, description="JPEG functionality")
variant("tiff", default=False, description="Compressed TIFF functionality")
@@ -79,8 +91,7 @@ class PyPillowBase(PythonPackage):
with open("setup.cfg", "a") as setup:
setup.write("[build_ext]\n")
- variants = list(self.spec.variants)
- for variant in variants:
+ for variant in self.VARIANTS_IN_SETUP_CFG:
setup.write(variant_to_cfg(variant))
setup.write("rpath={0}\n".format(":".join(self.rpath)))
diff --git a/var/spack/repos/builtin/packages/py-pybind11/package.py b/var/spack/repos/builtin/packages/py-pybind11/package.py
index b8a234c668..d50bdeef8e 100644
--- a/var/spack/repos/builtin/packages/py-pybind11/package.py
+++ b/var/spack/repos/builtin/packages/py-pybind11/package.py
@@ -2,13 +2,14 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
-
import os
+import spack.build_systems.cmake
+import spack.build_systems.python
from spack.package import *
-class PyPybind11(CMakePackage, PythonPackage):
+class PyPybind11(CMakePackage, PythonExtension):
"""pybind11 -- Seamless operability between C++11 and Python.
pybind11 is a lightweight header-only library that exposes C++ types in
@@ -16,7 +17,8 @@ class PyPybind11(CMakePackage, PythonPackage):
code. Its goals and syntax are similar to the excellent Boost.Python
library by David Abrahams: to minimize boilerplate code in traditional
extension modules by inferring type information using compile-time
- introspection."""
+ introspection.
+ """
homepage = "https://pybind11.readthedocs.io"
url = "https://github.com/pybind/pybind11/archive/v2.6.2.tar.gz"
@@ -26,6 +28,7 @@ class PyPybind11(CMakePackage, PythonPackage):
version("master", branch="master")
version("2.10.0", sha256="eacf582fa8f696227988d08cfc46121770823839fe9e301a20fbce67e7cd70ec")
+ version("2.9.2", sha256="6bd528c4dbe2276635dc787b6b1f2e5316cf6b49ee3e150264e455a0d68d19c1")
version("2.9.1", sha256="c6160321dc98e6e1184cc791fbeadd2907bb4a0ce0e447f2ea4ff8ab56550913")
version("2.9.0", sha256="057fb68dafd972bc13afb855f3b0d8cf0fa1a78ef053e815d9af79be7ff567cb")
version("2.8.1", sha256="f1bcc07caa568eb312411dde5308b1e250bd0e1bc020fae855bf9f43209940cc")
@@ -45,30 +48,25 @@ class PyPybind11(CMakePackage, PythonPackage):
version("2.1.1", sha256="f2c6874f1ea5b4ad4ffffe352413f7d2cd1a49f9050940805c2a082348621540")
version("2.1.0", sha256="2860f2b8d0c9f65f0698289a161385f59d099b7ead1bf64e8993c486f2b93ee0")
- depends_on("ninja", type="build")
depends_on("py-setuptools@42:", type="build")
depends_on("py-pytest", type="test")
depends_on("python@2.7:2.8,3.5:", type=("build", "run"))
depends_on("python@3.6:", when="@2.10.0:", type=("build", "run"))
- depends_on("cmake@3.13:", type="build")
- depends_on("cmake@3.18:", type="build", when="@2.6.0:")
+
+ depends_on("py-pip", type="build")
+ depends_on("py-wheel", type="build")
+ extends("python")
+
+ with when("build_system=cmake"):
+ depends_on("ninja", type="build")
+ depends_on("cmake@3.13:", type="build")
+ depends_on("cmake@3.18:", type="build", when="@2.6.0:")
# compiler support
conflicts("%gcc@:4.7")
conflicts("%clang@:3.2")
conflicts("%intel@:16")
- build_directory = "."
-
- def cmake_args(self):
- args = []
- args.append("-DPYTHON_EXECUTABLE:FILEPATH=%s" % self.spec["python"].command.path)
- args += [self.define("PYBIND11_TEST", self.run_tests)]
- return args
-
- def setup_build_environment(self, env):
- env.set("PYBIND11_USE_CMAKE", 1)
-
# https://github.com/pybind/pybind11/pull/1995
@when("@:2.4")
def patch(self):
@@ -80,13 +78,27 @@ class PyPybind11(CMakePackage, PythonPackage):
string=True,
)
- def install(self, spec, prefix):
- CMakePackage.install(self, spec, prefix)
- PythonPackage.install(self, spec, prefix)
+
+class CMakeBuilder(spack.build_systems.cmake.CMakeBuilder):
+ def cmake_args(self):
+ return [
+ self.define("PYTHON_EXECUTABLE:FILEPATH", self.spec["python"].command.path),
+ self.define("PYBIND11_TEST", self.pkg.run_tests),
+ ]
+
+ def install(self, pkg, spec, prefix):
+ super(CMakeBuilder, self).install(pkg, spec, prefix)
+ python_builder = spack.build_systems.python.PythonPipBuilder(pkg)
+ python_builder.install(pkg, spec, prefix)
+
+ def setup_build_environment(self, env):
+ env.set("PYBIND11_USE_CMAKE", 1)
@run_after("install")
- @on_package_attributes(run_tests=True)
def install_test(self):
+ if not self.pkg.run_tests:
+ return
+
with working_dir("spack-test", create=True):
# test include helper points to right location
python = self.spec["python"].command
diff --git a/var/spack/repos/builtin/packages/py-pykokkos-base/package.py b/var/spack/repos/builtin/packages/py-pykokkos-base/package.py
index 5d3427b4bb..f9b02c9ae9 100644
--- a/var/spack/repos/builtin/packages/py-pykokkos-base/package.py
+++ b/var/spack/repos/builtin/packages/py-pykokkos-base/package.py
@@ -8,7 +8,7 @@
from spack.package import *
-class PyPykokkosBase(CMakePackage, PythonPackage):
+class PyPykokkosBase(CMakePackage, PythonExtension):
"""Minimal set of bindings for Kokkos interoperability with Python
(initialize, finalize, View, DynRankView, Kokkos-tools)"""
diff --git a/var/spack/repos/builtin/packages/py-tfdlpack/package.py b/var/spack/repos/builtin/packages/py-tfdlpack/package.py
index 2bc5186d92..1304184c2e 100644
--- a/var/spack/repos/builtin/packages/py-tfdlpack/package.py
+++ b/var/spack/repos/builtin/packages/py-tfdlpack/package.py
@@ -7,7 +7,7 @@
from spack.package import *
-class PyTfdlpack(CMakePackage, PythonPackage):
+class PyTfdlpack(CMakePackage, PythonExtension):
"""Tensorflow plugin for DLPack."""
homepage = "https://github.com/VoVAllen/tf-dlpack"
diff --git a/var/spack/repos/builtin/packages/quantum-espresso/package.py b/var/spack/repos/builtin/packages/quantum-espresso/package.py
index acc07692cf..47fb80e6f9 100644
--- a/var/spack/repos/builtin/packages/quantum-espresso/package.py
+++ b/var/spack/repos/builtin/packages/quantum-espresso/package.py
@@ -2,11 +2,12 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
-
+import spack.build_systems.cmake
+import spack.build_systems.generic
from spack.package import *
-class QuantumEspresso(CMakePackage):
+class QuantumEspresso(CMakePackage, Package):
"""Quantum ESPRESSO is an integrated suite of Open-Source computer codes
for electronic-structure calculations and materials modeling at the
nanoscale. It is based on density-functional theory, plane waves, and
@@ -19,6 +20,8 @@ class QuantumEspresso(CMakePackage):
maintainers = ["ye-luo", "danielecesarini", "bellenlau"]
+ build_system(conditional("cmake", when="@6.8:"), "generic", default="cmake")
+
version("develop", branch="develop")
version("7.1", sha256="d56dea096635808843bd5a9be2dee3d1f60407c01dbeeda03f8256a3bcfc4eb6")
version("7.0", sha256="85beceb1aaa1678a49e774c085866d4612d9d64108e0ac49b23152c8622880ee")
@@ -56,10 +59,8 @@ class QuantumEspresso(CMakePackage):
destination=".",
)
- variant("cmake", default=True, description="Builds via CMake")
- with when("+cmake"):
+ with when("build_system=cmake"):
depends_on("cmake@3.14.0:", type="build")
- conflicts("@:6.7", msg="+cmake works since QE v6.8")
variant("libxc", default=False, description="Uses libxc")
depends_on("libxc@5.1.2:", when="+libxc")
@@ -93,7 +94,7 @@ class QuantumEspresso(CMakePackage):
msg="bugs with NVHPCSDK from v21.11 to v22.3, OpenMP and GPU",
)
# only cmake is supported
- conflicts("~cmake", msg="Only CMake supported for GPU-enabled version")
+ conflicts("build_system=generic", msg="Only CMake supported for GPU-enabled version")
# NVTX variant for profiling
# requires linking to CUDA runtime APIs , handled by CMake
@@ -121,9 +122,10 @@ class QuantumEspresso(CMakePackage):
with when("+elpa"):
# CMake builds only support elpa without openmp
- depends_on("elpa~openmp", when="+cmake")
- depends_on("elpa+openmp", when="+openmp~cmake")
- depends_on("elpa~openmp", when="~openmp~cmake")
+ depends_on("elpa~openmp", when="build_system=cmake")
+ with when("build_system=generic"):
+ depends_on("elpa+openmp", when="+openmp")
+ depends_on("elpa~openmp", when="~openmp")
# Elpa is formally supported by @:5.4.0, but QE configure searches
# for it in the wrong folders (or tries to download it within
# the build directory). Instead of patching Elpa to provide the
@@ -181,12 +183,14 @@ class QuantumEspresso(CMakePackage):
with when("@7.0.1:"):
# when QE doesn't use hdf5 library, the converter plugin still needs it
depends_on("hdf5@1.8.16:+hl~mpi", when="hdf5=none")
- conflicts("~cmake", msg="QE-to-QMCPACK wave function converter requires cmake")
+ conflicts(
+ "build_system=generic", msg="QE-to-QMCPACK wave function converter requires cmake"
+ )
# Enables building Electron-phonon Wannier 'epw.x' executable
# http://epw.org.uk/Main/About
- variant("epw", default=False, description="Builds Electron-phonon Wannier executable")
- conflicts("~epw", when="+cmake", msg="epw cannot be turned off when using CMake")
+ variant("epw", default=True, description="Builds Electron-phonon Wannier executable")
+ conflicts("~epw", when="build_system=cmake", msg="epw cannot be turned off when using CMake")
with when("+epw"):
# The first version of Q-E to feature integrated EPW is 6.0.0,
@@ -198,8 +202,10 @@ class QuantumEspresso(CMakePackage):
# Constraints may be relaxed as successful reports
# of different compiler+mpi combinations arrive
- # TODO: enable building EPW when ~mpi and ~cmake
- conflicts("~mpi", when="~cmake", msg="EPW needs MPI when ~cmake")
+ # TODO: enable building EPW when ~mpi and build_system=generic
+ conflicts(
+ "~mpi", when="build_system=generic", msg="EPW needs MPI when build_system=generic"
+ )
# EPW doesn't gets along well with OpenMPI 2.x.x
conflicts("^openmpi@2.0.0:2", msg="OpenMPI version incompatible with EPW")
@@ -212,19 +218,19 @@ class QuantumEspresso(CMakePackage):
variant(
"environ",
default=False,
+ when="build_system=generic",
description="Enables support for introducing environment effects "
"into atomistic first-principles simulations."
"See http://quantum-environ.org/about.html",
)
- conflicts("+environ", when="+cmake", msg="environ doesn't work with CMake")
variant(
"gipaw",
default=False,
+ when="build_system=generic",
description="Builds Gauge-Including Projector Augmented-Waves executable",
)
with when("+gipaw"):
- conflicts("+cmake", msg="gipaw doesn't work with CMake")
conflicts(
"@:6.3", msg="gipaw standard support available for QE 6.3 or grater version only"
)
@@ -370,6 +376,8 @@ class QuantumEspresso(CMakePackage):
# extlibs_makefile updated to work with fujitsu compilers
patch("fj-fox.patch", when="+patch %fj")
+
+class CMakeBuilder(spack.build_systems.cmake.CMakeBuilder):
def cmake_args(self):
spec = self.spec
@@ -400,18 +408,9 @@ class QuantumEspresso(CMakePackage):
return cmake_args
- @when("~cmake")
- def cmake(self, spec, prefix):
- print("Bypass cmake stage when building via configure")
-
- @when("~cmake")
- def build(self, spec, prefix):
- print("Bypass build stage when building via configure")
-
- @when("~cmake")
- def install(self, spec, prefix):
- print("Override install stage when building via configure")
+class GenericBuilder(spack.build_systems.generic.GenericBuilder):
+ def install(self, pkg, spec, prefix):
prefix_path = prefix.bin if "@:5.4.0" in spec else prefix
options = ["-prefix={0}".format(prefix_path)]
@@ -577,7 +576,7 @@ class QuantumEspresso(CMakePackage):
# can't be applied to the '+qmcpack' variant
if spec.variants["hdf5"].value != "none":
if spec.satisfies("@6.1.0:6.4.0") or (spec.satisfies("@6.4.1") and "+qmcpack" in spec):
- make_inc = join_path(self.stage.source_path, "make.inc")
+ make_inc = join_path(self.pkg.stage.source_path, "make.inc")
zlib_libs = spec["zlib"].prefix.lib + " -lz"
filter_file(zlib_libs, format(spec["zlib"].libs.ld_flags), make_inc)
diff --git a/var/spack/repos/builtin/packages/racket/package.py b/var/spack/repos/builtin/packages/racket/package.py
index cc9e1ba25b..7031e234ea 100644
--- a/var/spack/repos/builtin/packages/racket/package.py
+++ b/var/spack/repos/builtin/packages/racket/package.py
@@ -2,13 +2,11 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
-
-import sys
-
+import spack.build_systems.makefile
from spack.package import *
-class Racket(Package):
+class Racket(MakefilePackage):
"""The Racket programming language."""
homepage = "https://www.racket-lang.org"
@@ -21,13 +19,6 @@ class Racket(Package):
depends_on("patchutils")
depends_on("libtool", type=("build"))
- phases = ["configure", "build", "install"]
-
- def url_for_version(self, version):
- return "https://mirror.racket-lang.org/installers/{0}/racket-minimal-{0}-src-builtpkgs.tgz".format(
- version
- )
-
variant("cs", default=True, description="Build Racket CS (new ChezScheme VM)")
variant("bc", default=False, description="Build Racket BC (old MZScheme VM)")
variant("shared", default=True, description="Enable shared")
@@ -36,12 +27,22 @@ class Racket(Package):
parallel = False
extendable = True
+ def url_for_version(self, version):
+ return "https://mirror.racket-lang.org/installers/{0}/racket-minimal-{0}-src-builtpkgs.tgz".format(
+ version
+ )
+
+
+class MakefileBuilder(spack.build_systems.makefile.MakefileBuilder):
+
+ build_directory = "src"
+
def toggle(self, spec, variant):
toggle_text = "enable" if spec.variants[variant].value else "disable"
return "--{0}-{1}".format(toggle_text, variant)
- def configure(self, spec, prefix):
- with working_dir("src"):
+ def edit(self, pkg, spec, prefix):
+ with working_dir(self.build_directory):
configure = Executable("./configure")
configure_args = [
self.toggle(spec, "cs"),
@@ -49,7 +50,7 @@ class Racket(Package):
self.toggle(spec, "jit"),
]
toggle_shared = self.toggle(spec, "shared")
- if sys.platform == "darwin":
+ if spec.satisfies("platform=darwin"):
configure_args += ["--enable-macprefix"]
if "+xonx" in spec:
configure_args += ["--enable-xonx", toggle_shared]
@@ -58,16 +59,20 @@ class Racket(Package):
configure_args += ["--prefix={0}".format(prefix)]
configure(*configure_args)
- def build(self, spec, prefix):
- with working_dir("src"):
- if spec.variants["bc"].value:
- make("bc")
- if spec.variants["cs"].value:
- make("cs")
-
- def install(self, spec, prefix):
- with working_dir("src"):
- if spec.variants["bc"].value:
- make("install-bc")
- if spec.variants["cs"].value:
- make("install-cs")
+ @property
+ def build_targets(self):
+ result = []
+ if "+bc" in self.spec:
+ result.append("bc")
+ if "+cs" in self.spec:
+ result.append("cs")
+ return result
+
+ @property
+ def install_targets(self):
+ result = []
+ if "+bc" in self.spec:
+ result.append("install-bc")
+ if "+cs" in self.spec:
+ result.append("install-cs")
+ return result
diff --git a/var/spack/repos/builtin/packages/rkt-base/package.py b/var/spack/repos/builtin/packages/rkt-base/package.py
index 256f90106e..3eb21e485d 100644
--- a/var/spack/repos/builtin/packages/rkt-base/package.py
+++ b/var/spack/repos/builtin/packages/rkt-base/package.py
@@ -18,5 +18,3 @@ class RktBase(RacketPackage):
depends_on("racket@8.3", type=("build", "run"), when="@8.3")
racket_name = "base"
- pkgs = True
- subdirectory = "pkgs/{0}".format(racket_name)
diff --git a/var/spack/repos/builtin/packages/rkt-cext-lib/package.py b/var/spack/repos/builtin/packages/rkt-cext-lib/package.py
index 8d9b257c05..daa1ef5226 100644
--- a/var/spack/repos/builtin/packages/rkt-cext-lib/package.py
+++ b/var/spack/repos/builtin/packages/rkt-cext-lib/package.py
@@ -21,5 +21,4 @@ class RktCextLib(RacketPackage):
depends_on("rkt-scheme-lib@8.3", type=("build", "run"), when="@8.3")
racket_name = "cext-lib"
- pkgs = True
subdirectory = racket_name
diff --git a/var/spack/repos/builtin/packages/rkt-compiler-lib/package.py b/var/spack/repos/builtin/packages/rkt-compiler-lib/package.py
index fee9b3ea9a..6a21c774cb 100644
--- a/var/spack/repos/builtin/packages/rkt-compiler-lib/package.py
+++ b/var/spack/repos/builtin/packages/rkt-compiler-lib/package.py
@@ -21,5 +21,3 @@ class RktCompilerLib(RacketPackage):
depends_on("rkt-zo-lib@1.3", type=("build", "run"), when="@8.3")
racket_name = "compiler-lib"
- pkgs = True
- subdirectory = "pkgs/{0}".format(racket_name)
diff --git a/var/spack/repos/builtin/packages/rkt-dynext-lib/package.py b/var/spack/repos/builtin/packages/rkt-dynext-lib/package.py
index 81f7ca811f..9164571ed3 100644
--- a/var/spack/repos/builtin/packages/rkt-dynext-lib/package.py
+++ b/var/spack/repos/builtin/packages/rkt-dynext-lib/package.py
@@ -17,5 +17,4 @@ class RktDynextLib(RacketPackage):
depends_on("rkt-base@8.3", type=("build", "run"), when="@8.3")
racket_name = "dynext-lib"
- pkgs = True
subdirectory = racket_name
diff --git a/var/spack/repos/builtin/packages/rkt-rackunit-lib/package.py b/var/spack/repos/builtin/packages/rkt-rackunit-lib/package.py
index 783aebe306..3391050258 100644
--- a/var/spack/repos/builtin/packages/rkt-rackunit-lib/package.py
+++ b/var/spack/repos/builtin/packages/rkt-rackunit-lib/package.py
@@ -18,5 +18,4 @@ class RktRackunitLib(RacketPackage):
depends_on("rkt-testing-util-lib@8.3", type=("build", "run"), when="@8.3")
racket_name = "rackunit-lib"
- pkgs = True
subdirectory = racket_name
diff --git a/var/spack/repos/builtin/packages/rkt-scheme-lib/package.py b/var/spack/repos/builtin/packages/rkt-scheme-lib/package.py
index 75346eac70..689d3af7c5 100644
--- a/var/spack/repos/builtin/packages/rkt-scheme-lib/package.py
+++ b/var/spack/repos/builtin/packages/rkt-scheme-lib/package.py
@@ -17,4 +17,3 @@ class RktSchemeLib(RacketPackage):
depends_on("rkt-base@8.3", type=("build", "run"), when="@8.3")
racket_name = "scheme-lib"
- pkgs = True
diff --git a/var/spack/repos/builtin/packages/rkt-testing-util-lib/package.py b/var/spack/repos/builtin/packages/rkt-testing-util-lib/package.py
index 9186845fc9..aa1aafda4e 100644
--- a/var/spack/repos/builtin/packages/rkt-testing-util-lib/package.py
+++ b/var/spack/repos/builtin/packages/rkt-testing-util-lib/package.py
@@ -17,5 +17,4 @@ class RktTestingUtilLib(RacketPackage):
depends_on("rkt-base@8.3:", type=("build", "run"), when="@8.3")
racket_name = "testing-util-lib"
- pkgs = True
subdirectory = racket_name
diff --git a/var/spack/repos/builtin/packages/rkt-zo-lib/package.py b/var/spack/repos/builtin/packages/rkt-zo-lib/package.py
index 66f3d498af..a6b70cd075 100644
--- a/var/spack/repos/builtin/packages/rkt-zo-lib/package.py
+++ b/var/spack/repos/builtin/packages/rkt-zo-lib/package.py
@@ -17,5 +17,3 @@ class RktZoLib(RacketPackage):
depends_on("rkt-base@8.3:", type=("build", "run"), when="@1.3")
racket_name = "zo-lib"
- pkgs = True
- subdirectory = "pkgs/{0}".format(racket_name)
diff --git a/var/spack/repos/builtin/packages/ruby/package.py b/var/spack/repos/builtin/packages/ruby/package.py
index 9cc7f92151..a472509e08 100644
--- a/var/spack/repos/builtin/packages/ruby/package.py
+++ b/var/spack/repos/builtin/packages/ruby/package.py
@@ -2,19 +2,17 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
-
import re
-import sys
-from typing import List
+import spack.build_systems.autotools
+import spack.build_systems.nmake
from spack.package import *
-is_windows = sys.platform == "win32"
-
-class Ruby(Package):
+class Ruby(AutotoolsPackage, NMakePackage):
"""A dynamic, open source programming language with a focus on
- simplicity and productivity."""
+ simplicity and productivity.
+ """
maintainers = ["Kerilk"]
@@ -33,24 +31,25 @@ class Ruby(Package):
version("2.5.3", sha256="9828d03852c37c20fa333a0264f2490f07338576734d910ee3fd538c9520846c")
version("2.2.0", sha256="7671e394abfb5d262fbcd3b27a71bf78737c7e9347fa21c39e58b0bb9c4840fc")
- if not is_windows:
- variant("openssl", default=True, description="Enable OpenSSL support")
- variant("readline", default=False, description="Enable Readline support")
- depends_on("pkgconfig", type=("build"))
- depends_on("libffi")
- depends_on("libx11", when="@:2.3")
- depends_on("tcl", when="@:2.3")
- depends_on("tk", when="@:2.3")
- depends_on("readline", when="+readline")
- depends_on("zlib")
- with when("+openssl"):
- depends_on("openssl@:1")
- depends_on("openssl@:1.0", when="@:2.3")
+ build_system("autotools", "nmake", default="autotools")
+
+ for _platform_condition in ("platform=linux", "platform=darwin", "platform=cray"):
+ with when(_platform_condition):
+ variant("openssl", default=True, description="Enable OpenSSL support")
+ variant("readline", default=False, description="Enable Readline support")
+ depends_on("pkgconfig", type="build")
+ depends_on("libffi")
+ depends_on("libx11", when="@:2.3")
+ depends_on("tcl", when="@:2.3")
+ depends_on("tk", when="@:2.3")
+ depends_on("readline", when="+readline")
+ depends_on("zlib")
+ with when("+openssl"):
+ depends_on("openssl@:1")
+ depends_on("openssl@:1.0", when="@:2.3")
extendable = True
- phases = ["configure", "build", "install"]
- build_targets = [] # type: List[str]
- install_targets = ["install"]
+
# Known build issues when Avira antivirus software is running:
# https://github.com/rvm/rvm/issues/4313#issuecomment-374020379
# TODO: add check for this and warn user
@@ -82,28 +81,6 @@ class Ruby(Package):
url = "https://cache.ruby-lang.org/pub/ruby/{0}/ruby-{1}.tar.gz"
return url.format(version.up_to(2), version)
- def configure_args(self):
- args = []
- if "+openssl" in self.spec:
- args.append("--with-openssl-dir=%s" % self.spec["openssl"].prefix)
- if "+readline" in self.spec:
- args.append("--with-readline-dir=%s" % self.spec["readline"].prefix)
- if "^tk" in self.spec:
- args.append("--with-tk=%s" % self.spec["tk"].prefix)
- if self.spec.satisfies("%fj"):
- args.append("--disable-dtrace")
- return args
-
- def setup_dependent_build_environment(self, env, dependent_spec):
- # TODO: do this only for actual extensions.
- # Set GEM_PATH to include dependent gem directories
- for d in dependent_spec.traverse(deptype=("build", "run", "test"), root=True):
- if d.package.extends(self.spec):
- env.prepend_path("GEM_PATH", d.prefix)
-
- # The actual installation path for this gem
- env.set("GEM_HOME", dependent_spec.prefix)
-
def setup_dependent_run_environment(self, env, dependent_spec):
for d in dependent_spec.traverse(deptype=("run"), root=True):
if d.package.extends(self.spec):
@@ -122,31 +99,31 @@ class Ruby(Package):
module.gem = Executable(self.prefix.bin.gem)
module.rake = Executable(self.prefix.bin.rake)
- def configure(self, spec, prefix):
- with working_dir(self.stage.source_path, create=True):
- if is_windows:
- Executable("win32\\configure.bat")("--prefix=%s" % self.prefix)
- else:
- options = getattr(self, "configure_flag_args", [])
- options += ["--prefix={0}".format(prefix)]
- options += self.configure_args()
- configure(*options)
-
- def build(self, spec, prefix):
- with working_dir(self.stage.source_path):
- if is_windows:
- nmake()
- else:
- params = ["V=1"]
- params += self.build_targets
- make(*params)
-
- def install(self, spec, prefix):
- with working_dir(self.stage.source_path):
- if is_windows:
- nmake("install")
- else:
- make(*self.install_targets)
+
+class SetupEnvironment(object):
+ def setup_dependent_build_environment(self, env, dependent_spec):
+ # TODO: do this only for actual extensions.
+ # Set GEM_PATH to include dependent gem directories
+ for d in dependent_spec.traverse(deptype=("build", "run", "test"), root=True):
+ if d.package.extends(self.spec):
+ env.prepend_path("GEM_PATH", d.prefix)
+
+ # The actual installation path for this gem
+ env.set("GEM_HOME", dependent_spec.prefix)
+
+
+class AutotoolsBuilder(spack.build_systems.autotools.AutotoolsBuilder, SetupEnvironment):
+ def configure_args(self):
+ args = []
+ if "+openssl" in self.spec:
+ args.append("--with-openssl-dir=%s" % self.spec["openssl"].prefix)
+ if "+readline" in self.spec:
+ args.append("--with-readline-dir=%s" % self.spec["readline"].prefix)
+ if "^tk" in self.spec:
+ args.append("--with-tk=%s" % self.spec["tk"].prefix)
+ if self.spec.satisfies("%fj"):
+ args.append("--disable-dtrace")
+ return args
@run_after("install")
def post_install(self):
@@ -158,7 +135,7 @@ class Ruby(Package):
"""
if self.spec.satisfies("+openssl"):
rubygems_updated_cert_path = join_path(
- self.stage.source_path, "rubygems-updated-ssl-cert", "GlobalSignRootCA.pem"
+ self.pkg.stage.source_path, "rubygems-updated-ssl-cert", "GlobalSignRootCA.pem"
)
rubygems_certs_path = join_path(
self.spec.prefix.lib,
@@ -171,11 +148,19 @@ class Ruby(Package):
rbconfig = find(self.prefix, "rbconfig.rb")[0]
filter_file(
- r'^(\s*CONFIG\["CXX"\]\s*=\s*).*', r'\1"{0}"'.format(self.compiler.cxx), rbconfig
+ r'^(\s*CONFIG\["CXX"\]\s*=\s*).*', r'\1"{0}"'.format(self.pkg.compiler.cxx), rbconfig
)
filter_file(
- r'^(\s*CONFIG\["CC"\]\s*=\s*).*', r'\1"{0}"'.format(self.compiler.cc), rbconfig
+ r'^(\s*CONFIG\["CC"\]\s*=\s*).*', r'\1"{0}"'.format(self.pkg.compiler.cc), rbconfig
)
filter_file(
- r'^(\s*CONFIG\["MJIT_CC"\]\s*=\s*).*', r'\1"{0}"'.format(self.compiler.cc), rbconfig
+ r'^(\s*CONFIG\["MJIT_CC"\]\s*=\s*).*',
+ r'\1"{0}"'.format(self.pkg.compiler.cc),
+ rbconfig,
)
+
+
+class NMakeBuilder(spack.build_systems.nmake.NMakeBuilder, SetupEnvironment):
+ def edit(self, pkg, spec, prefix):
+ with working_dir(self.pkg.stage.source_path, create=True):
+ Executable("win32\\configure.bat")("--prefix=%s" % self.prefix)
diff --git a/var/spack/repos/builtin/packages/scotch/package.py b/var/spack/repos/builtin/packages/scotch/package.py
index 166455876e..b957db31bf 100644
--- a/var/spack/repos/builtin/packages/scotch/package.py
+++ b/var/spack/repos/builtin/packages/scotch/package.py
@@ -3,12 +3,15 @@
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
+import spack.build_systems.cmake
+import spack.build_systems.makefile
from spack.package import *
-class Scotch(CMakePackage):
+class Scotch(CMakePackage, MakefilePackage):
"""Scotch is a software package for graph and mesh/hypergraph
- partitioning, graph clustering, and sparse matrix ordering."""
+ partitioning, graph clustering, and sparse matrix ordering.
+ """
homepage = "https://gitlab.inria.fr/scotch/scotch"
git = "https://gitlab.inria.fr/scotch/scotch.git"
@@ -32,13 +35,10 @@ class Scotch(CMakePackage):
version("6.0.0", sha256="8206127d038bda868dda5c5a7f60ef8224f2e368298fbb01bf13fa250e378dd4")
version("5.1.10b", sha256="54c9e7fafefd49d8b2017d179d4f11a655abe10365961583baaddc4eeb6a9add")
- variant("mpi", default=True, description="Activate the compilation of parallel libraries")
- variant(
- "compression", default=True, description="Activate the posibility to use compressed files"
- )
- variant(
- "esmumps", default=False, description="Activate the compilation of esmumps needed by mumps"
- )
+ build_system(conditional("cmake", when="@7:"), "makefile", default="cmake")
+ variant("mpi", default=True, description="Compile parallel libraries")
+ variant("compression", default=True, description="May use compressed files")
+ variant("esmumps", default=False, description="Compile esmumps (needed by mumps)")
variant("shared", default=True, description="Build a shared version of the library")
variant(
"metis", default=False, description="Expose vendored METIS/ParMETIS libraries and wrappers"
@@ -70,8 +70,6 @@ class Scotch(CMakePackage):
conflicts("^metis", when="+metis")
conflicts("^parmetis", when="+metis")
- # NOTE: In cross-compiling environment parallel build
- # produces weird linker errors.
parallel = False
# NOTE: Versions of Scotch up to version 6.0.0 don't include support for
@@ -107,16 +105,30 @@ class Scotch(CMakePackage):
return scotchlibs + zlibs
- @when("@:6")
- def patch(self):
- self.configure()
- # NOTE: Configuration of Scotch is achieved by writing a 'Makefile.inc'
- # file that contains all of the configuration variables and their desired
- # values for the installation. This function writes this file based on
- # the given installation variants.
- @when("@:6")
- def configure(self):
+class CMakeBuilder(spack.build_systems.cmake.CMakeBuilder):
+ def cmake_args(self):
+ spec = self.spec
+ args = [
+ self.define_from_variant("BUILD_LIBSCOTCHMETIS", "metis"),
+ self.define_from_variant("INSTALL_METIS_HEADERS", "metis"),
+ self.define_from_variant("BUILD_LIBESMUMPS", "esmumps"),
+ self.define_from_variant("BUILD_SHARED_LIBS", "shared"),
+ self.define_from_variant("BUILD_PTSCOTCH", "mpi"),
+ ]
+
+ # TODO should we enable/disable THREADS?
+
+ if "+int64" in spec:
+ args.append("-DINTSIZE=64")
+
+ return args
+
+
+class MakefileBuilder(spack.build_systems.makefile.MakefileBuilder):
+ build_directory = "src"
+
+ def edit(self, pkg, spec, prefix):
makefile_inc = []
cflags = [
"-O3",
@@ -147,7 +159,7 @@ class Scotch(CMakePackage):
makefile_inc.extend(
[
"LIB = .dylib",
- "CLIBFLAGS = -dynamiclib {0}".format(self.compiler.cc_pic_flag),
+ "CLIBFLAGS = -dynamiclib {0}".format(pkg.compiler.cc_pic_flag),
"RANLIB = echo",
"AR = $(CC)",
(
@@ -162,13 +174,13 @@ class Scotch(CMakePackage):
makefile_inc.extend(
[
"LIB = .so",
- "CLIBFLAGS = -shared {0}".format(self.compiler.cc_pic_flag),
+ "CLIBFLAGS = -shared {0}".format(pkg.compiler.cc_pic_flag),
"RANLIB = echo",
"AR = $(CC)",
"ARFLAGS = -shared $(LDFLAGS) -o",
]
)
- cflags.append(self.compiler.cc_pic_flag)
+ cflags.append(pkg.compiler.cc_pic_flag)
else:
makefile_inc.extend(
[
@@ -182,9 +194,9 @@ class Scotch(CMakePackage):
# Compiler-Specific Options #
- if self.compiler.name == "gcc":
+ if pkg.compiler.name == "gcc":
cflags.append("-Drestrict=__restrict")
- elif self.compiler.name == "intel":
+ elif pkg.compiler.name == "intel":
cflags.append("-Drestrict=")
mpicc_path = self.spec["mpi"].mpicc if "+mpi" in self.spec else "mpicc"
@@ -237,8 +249,8 @@ class Scotch(CMakePackage):
with open("Makefile.inc", "w") as fh:
fh.write("\n".join(makefile_inc))
- @when("@:6")
- def install(self, spec, prefix):
+ @property
+ def build_targets(self):
targets = ["scotch"]
if "+mpi" in self.spec:
targets.append("ptscotch")
@@ -248,65 +260,4 @@ class Scotch(CMakePackage):
targets.append("esmumps")
if "+mpi" in self.spec:
targets.append("ptesmumps")
-
- with working_dir("src"):
- for target in targets:
- # It seems that building ptesmumps in parallel fails, for
- # version prior to 6.0.0 there is no separated targets force
- # ptesmumps, this library is built by the ptscotch target. This
- # should explain the test for the can_make_parallel variable
- can_make_parallel = not (
- target == "ptesmumps"
- or (self.spec.version < Version("6.0.0") and target == "ptscotch")
- )
- make(target, parallel=can_make_parallel)
-
- lib_ext = dso_suffix if "+shared" in self.spec else "a"
- # It seems easier to remove metis wrappers from the folder that will be
- # installed than to tweak the Makefiles
- if "+metis" not in self.spec:
- with working_dir("lib"):
- force_remove("libscotchmetis.{0}".format(lib_ext))
- force_remove("libptscotchparmetis.{0}".format(lib_ext))
-
- with working_dir("include"):
- force_remove("metis.h")
- force_remove("parmetis.h")
-
- if "~esmumps" in self.spec and self.spec.version < Version("6.0.0"):
- with working_dir("lib"):
- force_remove("libesmumps.{0}".format(lib_ext))
- force_remove("libptesmumps.{0}".format(lib_ext))
-
- with working_dir("include"):
- force_remove("esmumps.h")
-
- install_tree("bin", prefix.bin)
- install_tree("lib", prefix.lib)
- install_tree("include", prefix.include)
- install_tree("man/man1", prefix.share.man.man1)
-
- @when("@:6")
- def cmake(self, spec, prefix):
- self.configure()
-
- @when("@:6")
- def build(self, spec, prefix):
- pass
-
- def cmake_args(self):
- spec = self.spec
- args = [
- self.define_from_variant("BUILD_LIBSCOTCHMETIS", "metis"),
- self.define_from_variant("INSTALL_METIS_HEADERS", "metis"),
- self.define_from_variant("BUILD_LIBESMUMPS", "esmumps"),
- self.define_from_variant("BUILD_SHARED_LIBS", "shared"),
- self.define_from_variant("BUILD_PTSCOTCH", "mpi"),
- ]
-
- # TODO should we enable/disable THREADS?
-
- if "+int64" in spec:
- args.append("-DINTSIZE=64")
-
- return args
+ return targets
diff --git a/var/spack/repos/builtin/packages/superlu/package.py b/var/spack/repos/builtin/packages/superlu/package.py
index 8f20f90ea4..e8bd7c38f6 100644
--- a/var/spack/repos/builtin/packages/superlu/package.py
+++ b/var/spack/repos/builtin/packages/superlu/package.py
@@ -2,15 +2,16 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
-
import os
from llnl.util import tty
+import spack.build_systems.cmake
+import spack.build_systems.generic
from spack.package import *
-class Superlu(CMakePackage):
+class Superlu(CMakePackage, Package):
"""SuperLU is a general purpose library for the direct solution of large,
sparse, nonsymmetric systems of linear equations on high performance
machines. SuperLU is designed for sequential machines."""
@@ -36,9 +37,12 @@ class Superlu(CMakePackage):
url="https://crd-legacy.lbl.gov/~xiaoye/SuperLU/superlu_4.2.tar.gz",
)
+ build_system(
+ conditional("cmake", when="@5:"), conditional("autotools", when="@:4"), default="cmake"
+ )
+
variant("pic", default=True, description="Build with position independent code")
- depends_on("cmake", when="@5:", type="build")
depends_on("blas")
conflicts(
"@:5.2.1",
@@ -48,76 +52,7 @@ class Superlu(CMakePackage):
test_requires_compiler = True
- # CMake installation method
- def cmake_args(self):
- if self.version > Version("5.2.1"):
- _blaslib_key = "enable_internal_blaslib"
- else:
- _blaslib_key = "enable_blaslib"
- args = [
- self.define(_blaslib_key, False),
- self.define("CMAKE_INSTALL_LIBDIR", self.prefix.lib),
- self.define_from_variant("CMAKE_POSITION_INDEPENDENT_CODE", "pic"),
- self.define("enable_tests", self.run_tests),
- ]
-
- return args
-
# Pre-cmake installation method
- @when("@:4")
- def cmake(self, spec, prefix):
- """Use autotools before version 5"""
- config = []
-
- # Define make.inc file
- config.extend(
- [
- "PLAT = _x86_64",
- "SuperLUroot = %s" % self.stage.source_path,
- # 'SUPERLULIB = $(SuperLUroot)/lib/libsuperlu$(PLAT).a',
- "SUPERLULIB = $(SuperLUroot)/lib/libsuperlu_{0}.a".format(self.spec.version),
- "BLASDEF = -DUSE_VENDOR_BLAS",
- "BLASLIB = {0}".format(spec["blas"].libs.ld_flags),
- # or BLASLIB = -L/usr/lib64 -lblas
- "TMGLIB = libtmglib.a",
- "LIBS = $(SUPERLULIB) $(BLASLIB)",
- "ARCH = ar",
- "ARCHFLAGS = cr",
- "RANLIB = {0}".format("ranlib" if which("ranlib") else "echo"),
- "CC = {0}".format(env["CC"]),
- "FORTRAN = {0}".format(env["FC"]),
- "LOADER = {0}".format(env["CC"]),
- "CDEFS = -DAdd_",
- ]
- )
-
- if "+pic" in spec:
- config.extend(
- [
- # Use these lines instead when pic_flag capability arrives
- "CFLAGS = -O3 {0}".format(self.compiler.cc_pic_flag),
- "NOOPTS = {0}".format(self.compiler.cc_pic_flag),
- "FFLAGS = -O2 {0}".format(self.compiler.f77_pic_flag),
- "LOADOPTS = {0}".format(self.compiler.cc_pic_flag),
- ]
- )
- else:
- config.extend(
- ["CFLAGS = -O3", "NOOPTS = ", "FFLAGS = -O2", "LOADOPTS = "]
- )
-
- # Write configuration options to make.inc file
- with open("make.inc", "w") as inc:
- for option in config:
- inc.write("{0}\n".format(option))
-
- make(parallel=False)
-
- # Install manually
- install_tree("lib", prefix.lib)
- mkdir(prefix.include)
- install(join_path("SRC", "*.h"), prefix.include)
-
examples_src_dir = "EXAMPLE"
make_hdr_file = "make.inc"
@@ -221,3 +156,67 @@ class Superlu(CMakePackage):
return
self.run_superlu_test(test_dir, exe, args)
+
+
+class CMakeBuilder(spack.build_systems.cmake.CMakeBuilder):
+ def cmake_args(self):
+ if self.pkg.version > Version("5.2.1"):
+ _blaslib_key = "enable_internal_blaslib"
+ else:
+ _blaslib_key = "enable_blaslib"
+ args = [
+ self.define(_blaslib_key, False),
+ self.define("CMAKE_INSTALL_LIBDIR", self.prefix.lib),
+ self.define_from_variant("CMAKE_POSITION_INDEPENDENT_CODE", "pic"),
+ self.define("enable_tests", self.pkg.run_tests),
+ ]
+ return args
+
+
+class GenericBuilder(spack.build_systems.generic.GenericBuilder):
+ def install(self, pkg, spec, prefix):
+ """Use autotools before version 5"""
+ # Define make.inc file
+ config = [
+ "PLAT = _x86_64",
+ "SuperLUroot = %s" % self.pkg.stage.source_path,
+ # 'SUPERLULIB = $(SuperLUroot)/lib/libsuperlu$(PLAT).a',
+ "SUPERLULIB = $(SuperLUroot)/lib/libsuperlu_{0}.a".format(self.pkg.spec.version),
+ "BLASDEF = -DUSE_VENDOR_BLAS",
+ "BLASLIB = {0}".format(spec["blas"].libs.ld_flags),
+ # or BLASLIB = -L/usr/lib64 -lblas
+ "TMGLIB = libtmglib.a",
+ "LIBS = $(SUPERLULIB) $(BLASLIB)",
+ "ARCH = ar",
+ "ARCHFLAGS = cr",
+ "RANLIB = {0}".format("ranlib" if which("ranlib") else "echo"),
+ "CC = {0}".format(env["CC"]),
+ "FORTRAN = {0}".format(env["FC"]),
+ "LOADER = {0}".format(env["CC"]),
+ "CDEFS = -DAdd_",
+ ]
+
+ if "+pic" in spec:
+ config.extend(
+ [
+ # Use these lines instead when pic_flag capability arrives
+ "CFLAGS = -O3 {0}".format(self.pkg.compiler.cc_pic_flag),
+ "NOOPTS = {0}".format(self.pkg.compiler.cc_pic_flag),
+ "FFLAGS = -O2 {0}".format(self.pkg.compiler.f77_pic_flag),
+ "LOADOPTS = {0}".format(self.pkg.compiler.cc_pic_flag),
+ ]
+ )
+ else:
+ config.extend(
+ ["CFLAGS = -O3", "NOOPTS = ", "FFLAGS = -O2", "LOADOPTS = "]
+ )
+
+ with open("make.inc", "w") as inc:
+ for option in config:
+ inc.write("{0}\n".format(option))
+
+ make(parallel=False)
+
+ install_tree("lib", prefix.lib)
+ mkdir(prefix.include)
+ install(join_path("SRC", "*.h"), prefix.include)
diff --git a/var/spack/repos/builtin/packages/swig/package.py b/var/spack/repos/builtin/packages/swig/package.py
index d1a760ffec..2bb9853526 100644
--- a/var/spack/repos/builtin/packages/swig/package.py
+++ b/var/spack/repos/builtin/packages/swig/package.py
@@ -2,10 +2,10 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
-
import os
import re
+import spack.build_systems.autotools
from spack.package import *
@@ -57,10 +57,10 @@ class Swig(AutotoolsPackage, SourceforgePackage):
depends_on("pcre")
- _autoconf_versions = ["@master", "@fortran", "@4.0.2-fortran", "@4.1.dev1-fortran"]
+ AUTOCONF_VERSIONS = ["@master", "@fortran", "@4.0.2-fortran", "@4.1.dev1-fortran"]
# Git releases do *not* include configure script
- for _version in _autoconf_versions:
+ for _version in AUTOCONF_VERSIONS:
depends_on("autoconf", type="build", when=_version)
depends_on("automake", type="build", when=_version)
depends_on("libtool", type="build", when=_version)
@@ -70,8 +70,6 @@ class Swig(AutotoolsPackage, SourceforgePackage):
depends_on("automake@1.15:", type="build", when="target={0}:".format(_target))
depends_on("pkgconfig", type="build")
- build_directory = "spack-build"
-
conflicts("%nvhpc", when="@:4.0.2")
@classmethod
@@ -80,18 +78,6 @@ class Swig(AutotoolsPackage, SourceforgePackage):
match = re.search(r"SWIG\s+Version\s+(\S+)", output)
return match.group(1) if match else None
- @run_after("install")
- def create_symlink(self):
- # CMake compatibility: see https://github.com/spack/spack/pull/6240
- with working_dir(self.prefix.bin):
- os.symlink("swig", "swig{0}".format(self.spec.version.up_to(2)))
-
- for _version in _autoconf_versions:
-
- @when(_version)
- def autoreconf(self, spec, prefix):
- which("sh")("./autogen.sh")
-
@property
def _installed_exe(self):
return join_path(self.prefix, "bin", "swig")
@@ -134,3 +120,19 @@ class Swig(AutotoolsPackage, SourceforgePackage):
def test(self):
self._test_version()
self._test_swiglib()
+
+
+class AutotoolsBuilder(spack.build_systems.autotools.AutotoolsBuilder):
+ build_directory = "spack-build"
+
+ @run_after("install")
+ def create_symlink(self):
+ # CMake compatibility: see https://github.com/spack/spack/pull/6240
+ with working_dir(self.prefix.bin):
+ os.symlink("swig", "swig{0}".format(self.spec.version.up_to(2)))
+
+ for _version in Swig.AUTOCONF_VERSIONS:
+
+ @when(_version)
+ def autoreconf(self, pkg, spec, prefix):
+ which("sh")("./autogen.sh")
diff --git a/var/spack/repos/builtin/packages/sz/package.py b/var/spack/repos/builtin/packages/sz/package.py
index e16657a6b6..d83e0a52d5 100644
--- a/var/spack/repos/builtin/packages/sz/package.py
+++ b/var/spack/repos/builtin/packages/sz/package.py
@@ -2,11 +2,12 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
-
+import spack.build_systems.autotools
+import spack.build_systems.cmake
from spack.package import *
-class Sz(CMakePackage):
+class Sz(CMakePackage, AutotoolsPackage):
"""Error-bounded Lossy Compressor for HPC Data"""
homepage = "https://szcompressor.org"
@@ -43,6 +44,12 @@ class Sz(CMakePackage):
version("1.4.10.0", sha256="cf23cf1ffd7c69c3d3128ae9c356b6acdc03a38f92c02db5d9bfc04f3fabc506")
version("1.4.9.2", sha256="9dc785274d068d04c2836955fc93518a9797bfd409b46fea5733294b7c7c18f8")
+ build_system(
+ conditional("autotools", when="@:2.1.8.0"),
+ conditional("cmake", when="@2.1.8.1:"),
+ default="cmake",
+ )
+
variant("python", default=False, description="builds the python wrapper")
variant("netcdf", default=False, description="build the netcdf reader")
variant("hdf5", default=False, description="build the hdf5 filter")
@@ -71,86 +78,6 @@ class Sz(CMakePackage):
patch("ctags-only-if-requested.patch", when="@2.1.8.1:2.1.8.3")
- @property
- def build_directory(self):
- """autotools needs a different build directory to work"""
- if self.version >= Version("2.1.8.1"):
- return "spack-build"
- else:
- return "."
-
- @when("@:2.1.8.0")
- def cmake(self, spec, prefix):
- """use autotools before 2.1.8.1"""
- configure_args = ["--prefix=" + prefix]
- if "+fortran" in spec:
- configure_args.append("--enable-fortran")
- else:
- configure_args.append("--disable-fortran")
- configure(*configure_args)
- # at least the v2.0.2.0 tarball contains object files
- # which need to be cleaned out
- make("clean")
-
- def cmake_args(self):
- """configure the package with CMake for version 2.1.8.1 and later"""
- args = []
-
- if "+python" in self.spec:
- args.append("-DBUILD_PYTHON_WRAPPER=ON")
- args.append("-DSZ_PYTHON_SITELIB={0}".format(python_platlib))
- else:
- args.append("-DBUILD_PYTHON_WRAPPER=OFF")
-
- if "+netcdf" in self.spec:
- args.append("-DBUILD_NETCDF_READER=ON")
- else:
- args.append("-DBUILD_NETCDF_READER=OFF")
-
- if "+hdf5" in self.spec:
- args.append("-DBUILD_HDF5_FILTER=ON")
- else:
- args.append("-DBUILD_HDF5_FILTER=OFF")
-
- if "+pastri" in self.spec:
- args.append("-DBUILD_PASTRI=ON")
- else:
- args.append("-DBUILD_PASTRI=OFF")
-
- if "+time_compression" in self.spec:
- args.append("-DBUILD_TIMECMPR=ON")
- else:
- args.append("-DBUILD_TIMECMPR=OFF")
-
- if "+random_access" in self.spec:
- args.append("-DBUILD_RANDOMACCESS=ON")
- else:
- args.append("-DBUILD_RANDOMACCESS=OFF")
-
- if "+fortran" in self.spec:
- args.append("-DBUILD_FORTRAN=ON")
- else:
- args.append("-DBUILD_FORTRAN=OFF")
-
- if "+shared" in self.spec:
- args.append("-DBUILD_SHARED_LIBS=ON")
- else:
- args.append("-DBUILD_SHARED_LIBS=OFF")
-
- if "+stats" in self.spec:
- args.append("-DBUILD_STATS=ON")
- else:
- args.append("-DBUILD_STATS=OFF")
-
- args.append(self.define("BUILD_TESTS", self.run_tests))
-
- return args
-
- @run_after("build")
- @on_package_attributes(run_tests=True)
- def test_build(self):
- make("test")
-
def _test_2d_float(self):
"""This test performs simple 2D compression/decompression (float)"""
test_data_dir = self.test_suite.current_test_data_dir
@@ -232,3 +159,37 @@ class Sz(CMakePackage):
self._test_2d_float()
# run 3D compression and decompression (float)
self._test_3d_float()
+
+
+class AutotoolsBuilder(spack.build_systems.autotools.AutotoolsBuilder):
+ build_directory = "."
+
+ def configure_args(self):
+ return self.enable_or_disable("fortran")
+
+ @run_before("build")
+ def make_clean(self):
+ # at least the v2.0.2.0 tarball contains object files
+ # which need to be cleaned out
+ make("clean")
+
+
+class CMakeBuilder(spack.build_systems.cmake.CMakeBuilder):
+ def cmake_args(self):
+ result = [
+ self.define_from_variant("BUILD_NETCDF_READER", "netcdf"),
+ self.define_from_variant("BUILD_HDF5_FILTER", "hdf5"),
+ self.define_from_variant("BUILD_PASTRI", "pastri"),
+ self.define_from_variant("BUILD_TIMECPR", "time_compression"),
+ self.define_from_variant("BUILD_RANDOMACCESS", "random_access"),
+ self.define_from_variant("BUILD_FORTRAN", "fortran"),
+ self.define_from_variant("BUILD_SHARED_LIBS", "shared"),
+ self.define_from_variant("BUILD_STATS", "stats"),
+ self.define("BUILD_TESTS", self.pkg.run_tests),
+ self.define_from_variant("BUILD_PYTHON_WRAPPER", "python"),
+ ]
+
+ if "+python" in self.spec:
+ result.append(self.define("SZ_PYTHON_SITELIB", python_platlib))
+
+ return result
diff --git a/var/spack/repos/builtin/packages/timemory/package.py b/var/spack/repos/builtin/packages/timemory/package.py
index 759a7437d1..f30e578779 100644
--- a/var/spack/repos/builtin/packages/timemory/package.py
+++ b/var/spack/repos/builtin/packages/timemory/package.py
@@ -8,7 +8,7 @@
from spack.package import *
-class Timemory(CMakePackage, PythonPackage):
+class Timemory(CMakePackage, PythonExtension):
"""Modular profiling toolkit and suite of libraries and tools
for C/C++/Fortran/CUDA/Python"""
diff --git a/var/spack/repos/builtin/packages/uncrustify/package.py b/var/spack/repos/builtin/packages/uncrustify/package.py
index a9148c01e5..b528302ff6 100644
--- a/var/spack/repos/builtin/packages/uncrustify/package.py
+++ b/var/spack/repos/builtin/packages/uncrustify/package.py
@@ -6,7 +6,7 @@
from spack.package import *
-class Uncrustify(Package):
+class Uncrustify(CMakePackage, AutotoolsPackage):
"""Source Code Beautifier for C, C++, C#, ObjectiveC, Java, and others."""
homepage = "http://uncrustify.sourceforge.net/"
@@ -31,28 +31,15 @@ class Uncrustify(Package):
version("0.62", commit="5987f2")
version("0.61", sha256="1df0e5a2716e256f0a4993db12f23d10195b3030326fdf2e07f8e6421e172df9")
- depends_on("cmake", type="build", when="@0.64:")
- depends_on("automake", type="build", when="@0.63")
- depends_on("autoconf", type="build", when="@0.63")
-
- @when("@0.64:")
- def install(self, spec, prefix):
- with working_dir("spack-build", create=True):
- cmake("..", *std_cmake_args)
- make()
- make("install")
-
- @when("@0.63")
- def install(self, spec, prefix):
- which("bash")("autogen.sh")
- configure("--prefix={0}".format(self.prefix))
- make()
- make("install")
-
- @when("@:0.62")
- def install(self, spec, prefix):
- configure("--prefix={0}".format(self.prefix))
- make()
- make("install")
+ build_system(
+ conditional("cmake", when="@0.64:"),
+ conditional("autotools", when="@:0.63"),
+ default="cmake",
+ )
+
+ with when("build_system=autotools"):
+ depends_on("automake", type="build")
+ depends_on("autoconf", type="build")
+ depends_on("libtool", type="build", when="@0.63")
patch("uncrustify-includes.patch", when="@0.73")
diff --git a/var/spack/repos/builtin/packages/zlib/package.py b/var/spack/repos/builtin/packages/zlib/package.py
index 76add72552..58db86f6b2 100644
--- a/var/spack/repos/builtin/packages/zlib/package.py
+++ b/var/spack/repos/builtin/packages/zlib/package.py
@@ -9,10 +9,12 @@
import glob
import os
+import spack.build_systems.generic
+import spack.build_systems.makefile
from spack.package import *
-class Zlib(Package):
+class Zlib(MakefilePackage, Package):
"""A free, general-purpose, legally unencumbered lossless
data-compression library.
"""
@@ -43,10 +45,14 @@ class Zlib(Package):
deprecated=True,
)
+ build_system("makefile", conditional("generic", when="platform=windows"), default="makefile")
+
variant("pic", default=True, description="Produce position-independent code (for shared libs)")
variant("shared", default=True, description="Enables the build of shared libraries.")
variant("optimize", default=True, description="Enable -O2 for a more optimized lib")
+ conflicts("build_system=makefile", when="platform=windows")
+
patch("w_patch.patch", when="@1.2.11%cce")
patch("configure-cc.patch", when="@1.2.12")
@@ -55,11 +61,31 @@ class Zlib(Package):
shared = "+shared" in self.spec
return find_libraries(["libz"], root=self.prefix, recursive=True, shared=shared)
- def win_install(self):
- build_dir = self.stage.source_path
- install_tree = {}
- install_tree["bin"] = glob.glob(os.path.join(build_dir, "*.dll"))
- install_tree["lib"] = glob.glob(os.path.join(build_dir, "*.lib"))
+
+class SetupEnvironment(object):
+ def setup_build_environment(self, env):
+ if "+pic" in self.spec:
+ env.append_flags("CFLAGS", self.pkg.compiler.cc_pic_flag)
+ if "+optimize" in self.spec:
+ env.append_flags("CFLAGS", "-O2")
+
+
+class MakefileBuilder(spack.build_systems.makefile.MakefileBuilder, SetupEnvironment):
+ def edit(self, pkg, spec, prefix):
+ config_args = []
+ if "~shared" in self.spec:
+ config_args.append("--static")
+ configure("--prefix={0}".format(prefix), *config_args)
+
+
+class GenericBuilder(spack.build_systems.generic.GenericBuilder, SetupEnvironment):
+ def install(self, spec, prefix):
+ nmake("-f" "win32\\Makefile.msc")
+ build_dir = self.pkg.stage.source_path
+ install_tree = {
+ "bin": glob.glob(os.path.join(build_dir, "*.dll")),
+ "lib": glob.glob(os.path.join(build_dir, "*.lib")),
+ }
compose_src_path = lambda x: os.path.join(build_dir, x)
install_tree["include"] = [compose_src_path("zlib.h"), compose_src_path("zconf.h")]
# Windows path seps are fine here as this method is Windows specific.
@@ -76,24 +102,3 @@ class Zlib(Package):
install(file, install_dst)
installtree(self.prefix, install_tree)
-
- def setup_build_environment(self, env):
- if "+pic" in self.spec:
- env.append_flags("CFLAGS", self.compiler.cc_pic_flag)
- if "+optimize" in self.spec:
- env.append_flags("CFLAGS", "-O2")
-
- def install(self, spec, prefix):
- if "platform=windows" in self.spec:
- nmake("-f" "win32\\Makefile.msc")
- self.win_install()
- else:
- config_args = []
- if "~shared" in spec:
- config_args.append("--static")
- configure("--prefix={0}".format(prefix), *config_args)
-
- make()
- if self.run_tests:
- make("check")
- make("install")