Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add a SEP to extend the spec syntax #5

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 109 additions & 0 deletions seps/sep-0002.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# Extend the spec syntax to allow for edge attributes

## The problem

The current spec syntax allows to express node properties for a DAG but doesn't have any mechanism to:
1. Express edge properties.
2. Express a DAG structure that is not "flat" from a string literal.

So far, this limitation in the spec language didn't have a major impact on which use cases could be addressed whithin Spack's software model, mainly because of the overly strict unification policy we currently employ - _in a DAG there can be only a single spec stemming from a given package_.

The situation will change when we'll introduce in the software model separate concretization of build dependencies and, later, compiler as dependencies.

There issue is that, since _we might have in a DAG two or more different specs for the same package_, we also need a way to express constraints on them from the command line or, more in general, from a string literal. It's essential then that the spec syntax allows building a non-flat representation of a DAG (to distinguish constraints on the same software applied from different nodes) and allows matching edge properties.

The proposal in this SEP tries to extend the spec syntax to allow specifying arbitrary `key=value` attributes on edges and introduces a way to parse non-flat DAGs from a literal string.

## Proposed changes

### Specify edge attributes

To allow specifying edge attributes from a spec string we'll introduce the use of `:` delimiters after the `^` sigil. Within the delimiters we can have an arbitrary number of space separated `key=value` attributes:
```
A ^:foo=bar: B
```
The spec above should parse as:
```
A
|
| foo=bar
|
B
```
The delimiters are needed to avoid ambiguities with anonymous specs with a constraint on a variant. The following for instance:
```
^shared=true
```
is a syntax that can be already employed to match any node that has a shared boolean variant set to "true".

### Parse non-flat DAGs

Currently the spec:
```
A ^B ^C ^D
```
is parsed as:
```
A
/ | \
/ | \
B C D
```
and there is no way to express more structure, e.g. to say that D should be a dependency of C. What we propose here is to be able to use braces `{}` to recursively define a spec. For instance:
```
A ^B ^{C ^D}
```
is parsed as [^1]:
```
A
/ |
B C
|
D
```

[^1]: From the point of view of the concretizer C should probably imply a `root(C)` atom similarly to what is done for environments concretized together.

### Semantic considerations on abstract specs

The structure constructed from an initial parsing can be modified by the concretization process. For instance:
```
A ^B
```
doesn't imply that B will be a direct dependency of A in the concretized spec - the concretization process can push it down to a transitive dependency. Nonetheless it implies that B needs to be present in the final DAG [^2]

Said otherwise, at the moment we have no way to pin a dependency to some root. This ability though will become important especially in light of separate concretization of build dependencies.

When expressing a constraint on a build dependency we are more likely to want that the build dependency is attached to the root, rather than being generically "present in the DAG". Unfortunately the opposite is true for link dependencies.

One way to allow users to express both behaviors is to introduce an edge attribute that has a meaning only for abstract specs [^3] e.g. `floating`. We can then assume that:
- `floating=true` implies that the dependency is present in the DAG and can be pushed down to be a transitive dependency
- `floating=false` implies that the dependency is pinned to the root of the context

Then to account for the different default behaviors we want for `type=build` and `type=link` dependencies we can assume that the implicit value of the `floating` edge attribute is `false` if `type=build` is specified and `true` otherwise.

This means, for instance, that the spec:
```
trilinos ^:type=build: [email protected] ^:type=link: [email protected] ^[email protected]
```
is equivalent to the more verbose:
```
trilinos ^:type=build floating=false: [email protected] ^:type=link floating=true: [email protected] ^:floating=true: [email protected]
```

Finally it seems appropriate to restrict the `^` symbol to indicate a dependency that is in the process space [^4] of the corresponding root, rather than in the entire DAG.

[^2]: At the ASP level this means that a `^` implies a `node(B)` fact, but no `depends_on(A, B)` fact.

[^3]: That likely means that it should not even be present after concretization and is only used to emit the correct set of facts for clingo

[^4]: We indicate the process space of a root as the subdag linked through edges of either `type=link` and / or `type=run` . In the future it may be restricted to just `type=link` edges (and will represent the list of nodes that need to be loaded in the same process space memory by the OS)

### Use cases

TODO: Add significant use cases.

### Modification history

1. Use a pair of `:` delimiters instead of `[]` (issues wwith zsh)
2. Use braces instead of parenthesis (issues wwith shells)