
This PR provides complementary 2 features: 1. An augmentation to the package language to express ABI compatibility relationships among packages. 2. An extension to the concretizer that can synthesize splices between ABI compatible packages. 1. The `can_splice` directive and ABI compatibility We augment the package language with a single directive: `can_splice`. Here is an example of a package `Foo` exercising the `can_splice` directive: class Foo(Package): version("1.0") version("1.1") variant("compat", default=True) variant("json", default=False) variant("pic", default=False) can_splice("foo@1.0", when="@1.1") can_splice("bar@1.0", when="@1.0+compat") can_splice("baz@1.0+compat", when="@1.0+compat", match_variants="*") can_splice("quux@1.0", when=@1.1~compat", match_variants="json") Explanations of the uses of each directive: - `can_splice("foo@1.0", when="@1.1")`: If `foo@1.0` is the dependency of an already installed spec and `foo@1.1` could be a valid dependency for the parent spec, then `foo@1.1` can be spliced in for `foo@1.0` in the parent spec. - `can_splice("bar@1.0", when="@1.0+compat")`: If `bar@1.0` is the dependency of an already installed spec and `foo@1.0+compat` could be a valid dependency for the parent spec, then `foo@1.0+compat` can be spliced in for `bar@1.0+compat` in the parent spec - `can_splice("baz@1.0", when="@1.0+compat", match_variants="*")`: If `baz@1.0+compat` is the dependency of an already installed spec and `foo@1.0+compat` could be a valid dependency for the parent spec, then `foo@1.0+compat` can be spliced in for `baz@1.0+compat` in the parent spec, provided that they have the same value for all other variants (regardless of what those values are). - `can_splice("quux@1.0", when=@1.1~compat", match_variants="json")`:If `quux@1.0` is the dependency of an already installed spec and `foo@1.1~compat` could be a valid dependency for the parent spec, then `foo@1.0~compat` can be spliced in for `quux@1.0` in the parent spec, provided that they have the same value for their `json` variant. 2. Augmenting the solver to synthesize splices ### Changes to the hash encoding in `asp.py` Previously, when including concrete specs in the solve, they would have the following form: installed_hash("foo", "xxxyyy") imposed_constraint("xxxyyy", "foo", "attr1", ...) imposed_constraint("xxxyyy", "foo", "attr2", ...) % etc. Concrete specs now have the following form: installed_hash("foo", "xxxyyy") hash_attr("xxxyyy", "foo", "attr1", ...) hash_attr("xxxyyy", "foo", "attr2", ...) This transformation allows us to control which constraints are imposed when we select a hash, to facilitate the splicing of dependencies. 2.1 Compiling `can_splice` directives in `asp.py` Consider the concrete spec: foo@2.72%gcc@11.4 arch=linux-ubuntu22.04-icelake build_system=autotools ^bar ... It will emit the following facts for reuse (below is a subset) installed_hash("foo", "xxxyyy") hash_attr("xxxyyy", "hash", "foo", "xxxyyy") hash_attr("xxxyyy", "version", "foo", "2.72") hash_attr("xxxyyy", "node_os", "ubuntu22.04") hash_attr("xxxyyy", "hash", "bar", "zzzqqq") hash_attr("xxxyyy", "depends_on", "foo", "bar", "link") Rules that derive abi_splice_conditions_hold will be generated from use of the `can_splice` directive. They will have the following form: can_splice("foo@1.0.0+a", when="@1.0.1+a", match_variants=["b"]) ---> abi_splice_conditions_hold(0, node(SID, "foo"), "foo", BaseHash) :- installed_hash("foo", BaseHash), attr("node", node(SID, SpliceName)), attr("node_version_satisfies", node(SID, "foo"), "1.0.1"), hash_attr("hash", "node_version_satisfies", "foo", "1.0.1"), attr("variant_value", node(SID, "foo"), "a", "True"), hash_attr("hash", "variant_value", "foo", "a", "True"), attr("variant_value", node(SID, "foo"), "b", VariVar0), hash_attr("hash", "variant_value", "foo", "b", VariVar0). 2.2 Synthesizing splices in `concretize.lp` and `splices.lp` The ASP solver generates "splice_at_hash" attrs to indicate that a particular node has a splice in one of its immediate dependencies. Splices can be introduced in the dependencies of concrete specs when `splices.lp` is conditionally loaded (based on the config option `concretizer:splice:True`. 2.3 Constructing spliced specs in `asp.py` The method `SpecBuilder._resolve_splices` implements a top-down memoized implementation of hybrid splicing. This is an optimization over the more general `Spec.splice`, since the solver gives a global view of exactly which specs can be shared, to ensure the minimal number of splicing operations. Misc changes to facilitate configuration and benchmarking - Added the method `Solver.solve_with_stats` to expose timers from the public interface for easier benchmarking - Added the boolean config option `concretizer:splice` to conditionally load splicing behavior Co-authored-by: Greg Becker <becker33@llnl.gov>
272 lines
9.9 KiB
ReStructuredText
272 lines
9.9 KiB
ReStructuredText
.. Copyright 2013-2024 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)
|
|
|
|
|
|
.. _concretizer-options:
|
|
|
|
==========================================
|
|
Concretization Settings (concretizer.yaml)
|
|
==========================================
|
|
|
|
The ``concretizer.yaml`` configuration file allows to customize aspects of the
|
|
algorithm used to select the dependencies you install. The default configuration
|
|
is the following:
|
|
|
|
.. literalinclude:: _spack_root/etc/spack/defaults/concretizer.yaml
|
|
:language: yaml
|
|
|
|
--------------------------------
|
|
Reuse already installed packages
|
|
--------------------------------
|
|
|
|
The ``reuse`` attribute controls how aggressively Spack reuses binary packages during concretization. The
|
|
attribute can either be a single value, or an object for more complex configurations.
|
|
|
|
In the former case ("single value") it allows Spack to:
|
|
|
|
1. Reuse installed packages and buildcaches for all the specs to be concretized, when ``true``
|
|
2. Reuse installed packages and buildcaches only for the dependencies of the root specs, when ``dependencies``
|
|
3. Disregard reusing installed packages and buildcaches, when ``false``
|
|
|
|
In case a finer control over which specs are reused is needed, then the value of this attribute can be
|
|
an object, with the following keys:
|
|
|
|
1. ``roots``: if ``true`` root specs are reused, if ``false`` only dependencies of root specs are reused
|
|
2. ``from``: list of sources from which reused specs are taken
|
|
|
|
Each source in ``from`` is itself an object:
|
|
|
|
.. list-table:: Attributes for a source or reusable specs
|
|
:header-rows: 1
|
|
|
|
* - Attribute name
|
|
- Description
|
|
* - type (mandatory, string)
|
|
- Can be ``local``, ``buildcache``, or ``external``
|
|
* - include (optional, list of specs)
|
|
- If present, reusable specs must match at least one of the constraint in the list
|
|
* - exclude (optional, list of specs)
|
|
- If present, reusable specs must not match any of the constraint in the list.
|
|
|
|
For instance, the following configuration:
|
|
|
|
.. code-block:: yaml
|
|
|
|
concretizer:
|
|
reuse:
|
|
roots: true
|
|
from:
|
|
- type: local
|
|
include:
|
|
- "%gcc"
|
|
- "%clang"
|
|
|
|
tells the concretizer to reuse all specs compiled with either ``gcc`` or ``clang``, that are installed
|
|
in the local store. Any spec from remote buildcaches is disregarded.
|
|
|
|
To reduce the boilerplate in configuration files, default values for the ``include`` and
|
|
``exclude`` options can be pushed up one level:
|
|
|
|
.. code-block:: yaml
|
|
|
|
concretizer:
|
|
reuse:
|
|
roots: true
|
|
include:
|
|
- "%gcc"
|
|
from:
|
|
- type: local
|
|
- type: buildcache
|
|
- type: local
|
|
include:
|
|
- "foo %oneapi"
|
|
|
|
In the example above we reuse all specs compiled with ``gcc`` from the local store
|
|
and remote buildcaches, and we also reuse ``foo %oneapi``. Note that the last source of
|
|
specs override the default ``include`` attribute.
|
|
|
|
For one-off concretizations, the are command line arguments for each of the simple "single value"
|
|
configurations. This means a user can:
|
|
|
|
.. code-block:: console
|
|
|
|
% spack install --reuse <spec>
|
|
|
|
to enable reuse for a single installation, or:
|
|
|
|
.. code-block:: console
|
|
|
|
spack install --fresh <spec>
|
|
|
|
to do a fresh install if ``reuse`` is enabled by default.
|
|
|
|
.. seealso::
|
|
|
|
FAQ: :ref:`Why does Spack pick particular versions and variants? <faq-concretizer-precedence>`
|
|
|
|
------------------------------------------
|
|
Selection of the target microarchitectures
|
|
------------------------------------------
|
|
|
|
The options under the ``targets`` attribute control which targets are considered during a solve.
|
|
Currently the options in this section are only configurable from the ``concretizer.yaml`` file
|
|
and there are no corresponding command line arguments to enable them for a single solve.
|
|
|
|
The ``granularity`` option can take two possible values: ``microarchitectures`` and ``generic``.
|
|
If set to:
|
|
|
|
.. code-block:: yaml
|
|
|
|
concretizer:
|
|
targets:
|
|
granularity: microarchitectures
|
|
|
|
Spack will consider all the microarchitectures known to ``archspec`` to label nodes for
|
|
compatibility. If instead the option is set to:
|
|
|
|
.. code-block:: yaml
|
|
|
|
concretizer:
|
|
targets:
|
|
granularity: generic
|
|
|
|
Spack will consider only generic microarchitectures. For instance, when running on an
|
|
Haswell node, Spack will consider ``haswell`` as the best target in the former case and
|
|
``x86_64_v3`` as the best target in the latter case.
|
|
|
|
The ``host_compatible`` option is a Boolean option that determines whether or not the
|
|
microarchitectures considered during the solve are constrained to be compatible with the
|
|
host Spack is currently running on. For instance, if this option is set to ``true``, a
|
|
user cannot concretize for ``target=icelake`` while running on an Haswell node.
|
|
|
|
---------------
|
|
Duplicate nodes
|
|
---------------
|
|
|
|
The ``duplicates`` attribute controls whether the DAG can contain multiple configurations of
|
|
the same package. This is mainly relevant for build dependencies, which may have their version
|
|
pinned by some nodes, and thus be required at different versions by different nodes in the same
|
|
DAG.
|
|
|
|
The ``strategy`` option controls how the solver deals with duplicates. If the value is ``none``,
|
|
then a single configuration per package is allowed in the DAG. This means, for instance, that only
|
|
a single ``cmake`` or a single ``py-setuptools`` version is allowed. The result would be a slightly
|
|
faster concretization, at the expense of making a few specs unsolvable.
|
|
|
|
If the value is ``minimal`` Spack will allow packages tagged as ``build-tools`` to have duplicates.
|
|
This allows, for instance, to concretize specs whose nodes require different, and incompatible, ranges
|
|
of some build tool. For instance, in the figure below the latest `py-shapely` requires a newer `py-setuptools`,
|
|
while `py-numpy` still needs an older version:
|
|
|
|
.. figure:: images/shapely_duplicates.svg
|
|
:scale: 70 %
|
|
:align: center
|
|
|
|
Up to Spack v0.20 ``duplicates:strategy:none`` was the default (and only) behavior. From Spack v0.21 the
|
|
default behavior is ``duplicates:strategy:minimal``.
|
|
|
|
--------
|
|
Splicing
|
|
--------
|
|
|
|
The ``splice`` key covers config attributes for splicing specs in the solver.
|
|
|
|
"Splicing" is a method for replacing a dependency with another spec
|
|
that provides the same package or virtual. There are two types of
|
|
splices, referring to different behaviors for shared dependencies
|
|
between the root spec and the new spec replacing a dependency:
|
|
"transitive" and "intransitive". A "transitive" splice is one that
|
|
resolves all conflicts by taking the dependency from the new node. An
|
|
"intransitive" splice is one that resolves all conflicts by taking the
|
|
dependency from the original root. From a theory perspective, hybrid
|
|
splices are possible but are not modeled by Spack.
|
|
|
|
All spliced specs retain a ``build_spec`` attribute that points to the
|
|
original Spec before any splice occurred. The ``build_spec`` for a
|
|
non-spliced spec is itself.
|
|
|
|
The figure below shows examples of transitive and intransitive splices:
|
|
|
|
.. figure:: images/splices.png
|
|
:align: center
|
|
|
|
The concretizer can be configured to explicitly splice particular
|
|
replacements for a target spec. Splicing will allow the user to make
|
|
use of generically built public binary caches, while swapping in
|
|
highly optimized local builds for performance critical components
|
|
and/or components that interact closely with the specific hardware
|
|
details of the system. The most prominent candidate for splicing is
|
|
MPI providers. MPI packages have relatively well-understood ABI
|
|
characteristics, and most High Performance Computing facilities deploy
|
|
highly optimized MPI packages tailored to their particular
|
|
hardware. The following config block configures Spack to replace
|
|
whatever MPI provider each spec was concretized to use with the
|
|
particular package of ``mpich`` with the hash that begins ``abcdef``.
|
|
|
|
.. code-block:: yaml
|
|
|
|
concretizer:
|
|
splice:
|
|
explicit:
|
|
- target: mpi
|
|
replacement: mpich/abcdef
|
|
transitive: false
|
|
|
|
.. warning::
|
|
|
|
When configuring an explicit splice, you as the user take on the
|
|
responsibility for ensuring ABI compatibility between the specs
|
|
matched by the target and the replacement you provide. If they are
|
|
not compatible, Spack will not warn you and your application will
|
|
fail to run.
|
|
|
|
The ``target`` field of an explicit splice can be any abstract
|
|
spec. The ``replacement`` field must be a spec that includes the hash
|
|
of a concrete spec, and the replacement must either be the same
|
|
package as the target, provide the virtual that is the target, or
|
|
provide a virtual that the target provides. The ``transitive`` field
|
|
is optional -- by default, splices will be transitive.
|
|
|
|
.. note::
|
|
|
|
With explicit splices configured, it is possible for Spack to
|
|
concretize to a spec that does not satisfy the input. For example,
|
|
with the config above ``hdf5 ^mvapich2`` will concretize to user
|
|
``mpich/abcdef`` instead of ``mvapich2`` as the MPI provider. Spack
|
|
will warn the user in this case, but will not fail the
|
|
concretization.
|
|
|
|
.. _automatic_splicing:
|
|
|
|
^^^^^^^^^^^^^^^^^^
|
|
Automatic Splicing
|
|
^^^^^^^^^^^^^^^^^^
|
|
|
|
The Spack solver can be configured to do automatic splicing for
|
|
ABI-compatible packages. Automatic splices are enabled in the concretizer
|
|
config section
|
|
|
|
.. code-block:: yaml
|
|
|
|
concretizer:
|
|
splice:
|
|
automatic: True
|
|
|
|
Packages can include ABI-compatibility information using the
|
|
``can_splice`` directive. See :ref:`the packaging
|
|
guide<abi_compatibility>` for instructions on specifying ABI
|
|
compatibility using the ``can_splice`` directive.
|
|
|
|
.. note::
|
|
|
|
The ``can_splice`` directive is experimental and may be changed in
|
|
future versions.
|
|
|
|
When automatic splicing is enabled, the concretizer will combine any
|
|
number of ABI-compatible specs if possible to reuse installed packages
|
|
and packages available from binary caches. The end result of these
|
|
specs is equivalent to a series of transitive/intransitive splices,
|
|
but the series may be non-obvious.
|