-
Notifications
You must be signed in to change notification settings - Fork 2.4k
Spec syntax: semantics for % (percent) and ^ (caret) for Spack v1.0 #44379
Description
Summary
Currently the caret sigil ^ is used to indicate a transitive dependency of any type, while the percent sigil % is a node attribute that denotes a "compiler". This semantic need to change when compilers will turn into dependencies. Below is a sketched description of the proposed new semantic, with some discussion on backward compatibility.
Semantic: ^ transitive vs. % direct
When compilers are turned into nodes, we should use:
^to denote transitive dependencies%to denote direct dependencies
We can distinguish the edge types with the [] syntax:
# bar is a direct run dependency of foo
foo %[deptypes=run] bar
# bar is in the transitive link graph of foo
foo ^[deptypes=link] bar
If no type is specified, we should use these defaults:
# % without types is a direct build dependency
foo %bar -> foo %[deptypes=build] bar
# ^ without types is either a transitive link/run dependency, or a direct build dependency
foo ^bar -> foo ^[deptypes=link,run] bar + foo %[deptypes=build] bar
The second default allows us to say that ^cmake must be in the condition_set of its root, i.e. it is either a transitive link/run dependency, or a direct build dependency.
Specify dependencies with more structure
All the specs after a ^ or a % sigil refer to the root spec of the context. We need some symbols (() or {}) to allow opening and closing new contexts:
# bar is a link,run transitive dependency of foo
foo^bar
# like above, and baz is a transitive link,run dep of bar
foo^{bar ^baz}
All these symbols can be used with satisfies or intersects:
# Was the spec built using cmake 3.14 or later?
spec.satisfies("%[email protected]:")
# Is the spec transitively linked to a foo built with cmake?
spec.satisfies("^[type=link]{foo %[email protected]:}")
Considerations on backward compatibility
The current spec syntax established a few idioms that we need to respect, if we don't want to break each and every user.
% without parens should accept only a name and an optional version
Currently the % sigil in a spec denotes a node attribute, and admits only a name and an optional version specified after it. Very frequently this node attribute is inter-mixed with variants that refer to the root spec, for instance:
foo @X.Y %gcc@13 +bar +baz
In the spec above +bar and +baz refer to foo, not to gcc.
In order to avoid breakage, if % is not followed by parens, we need to accept only a name and an optional version even when gcc will be a node in the DAG. Specifying a variant on the gcc node can be done like:
foo +bar +baz %{gcc +binutils}
Need to introduce custom aliases for specs
With the proposed changes usage in spec literals has a similar meaning in most cases, for "pure" toolchains. For instance:
foo %gcc@13
doesn't need any modifications, and still denotes a foo built with gcc@13.
The only exceptions are compilers associated with specs having a different name:
foo %clang -> foo %llvm+clang
foo %oneapi -> foo %intel-oneapi-compilers
...
To avoid breakage in those cases we can introduce aliases for specs, and ship with these defaults:
packages:
aliases:
clang: "llvm+clang"
oneapi: "intel-oneapi-compilers"In this way specs will be translated automatically, and people could continue to write %oneapi or %clang.
Note that this is particularly important for clang, which is provided by llvm+clang and would need this cumbersome syntax otherwise:
foo %{llvm+clang}
due to the fact that % without parens associate following variants to the root spec.
There's no way to allow mixed toolchain with the same syntax
Using mixed toolchains will be requiring more boilerplate from the command line:
foo %clang %[virtuals=fortran] gcc
but on the other hand, it can be easily configured:
packages:
fortran:
require:
- gcc
c:
require:
- llvm+clangUsing a ^ for direct build dependencies works, because of defaults
We might have usage in directives or in constraints of expressions using ^ and a build type dependency. For instance:
conflicts("~hdf5", when="@0.6.0 ^cmake@:3.26")
patch("linux-gcc-cmakev3.11-plus.patch", when="@:1.9.1%gcc^[email protected]:")
if spec.satisfies("^[email protected]:3.21.2"):
...This works because the default behavior for ^ includes direct build dependencies.
The same applies for input specs like:
foo ^cmake@3
In this case we can add an additional constraint that the cmake must be in the condition_set of foo.
For specs like:
foo %cmake@3
we can emit additional constraints for the solver, like:
depends_on(node(0, foo), node(0, cmake), "build).
Metadata
Metadata
Assignees
Labels
Type
Projects
Status