Features¶
Features enable automatic dependency resolution between components. A feature is a plain string that can be associated with a component using the provides
keyword. Other components can depend on a feature using the requires
keyword, or indicate that they are mutually exclusive using the conflicts
keyword.
When a component requires a feature, it is up to the dependency resolver run during project generation to find another component that provides the feature. In the case where multiple components provide the same feature, the choice is deferred to the user, and the user records their preferred choice in the .slcp file.
Components cannot directly depend on each other by id
, they must provide and require features. This allows additional components from the project or SDK extensions to replace the functionality of a component, and in general allows for multiple implementations of APIs for generic drivers.
A component may associate a condition
with each of its provides, requires and conflicts. When guarded by a condition, the feature only comes into play when the condition matches a list of features provided by the project or its component dependencies. All features listed must be present for the condition to be active.
Dependency Resolution¶
A project is typically seeded with a list of components using the component
keyword. These components typically require additional features, and dependency resolution is needed.
Dependency resolution can be considered a step-by-step process according to the following algorithm:
- The project contains a set of components
C
. - The set of requirements
R
, providesP
, and conflictsK
are extracted from the set of components. - The set of unsatisfied requirements
U = R - P
is computed. For each unsatisfied requirement:- Find all components in the SDK that provide the unsatisfied requirement, while not providing anything in
K
.- For this purpose, a component can be considered to provide a feature if all its conditions are satisfied by the set
R + P
, rather than just consideringP
. This is an optimization that takes advantage of the fact that outstanding requirements will be provided at some point, else dependency resolution will have failed.
- For this purpose, a component can be considered to provide a feature if all its conditions are satisfied by the set
- If only one new component is found, add it to
C
.
- Find all components in the SDK that provide the unsatisfied requirement, while not providing anything in
- If at least one component was added in step 3, continue from step 2.
- Dependency resolution is unable to add more components, and is considered finished:
- If all of the following conditions are true, the project has been resolved successfully.
R
is a subset ofP
- No two components provide the same feature (unless the
allow_multiple
flag is set on all instances of the provide) K
is disjoint fromP
- If not, dependency resolution has failed.
- If all of the following conditions are true, the project has been resolved successfully.
If dependency resolution fails, the project generation tool shall present the failure and possible avenues of fixing it. This may include listing options when multiple components provide the same feature.
Recommendations¶
If dependency resolution fails because a required feature is provided by
multiple candidate components, any component present in the partially
resolved project may provide hints to further dependency resolution. Components
may use the recommends
key to recommend specific components by id
. A
recommended component is only considered for inclusion in the project if it
provides a feature in the set of unsatisfied requirements U
, and if its
provided features are disjoint from those of all other recommended components.
If the set of provided features is not disjoint from another recommended
component, it is considered a conflict at the same level as if neither component
was recommended, but the project generation tool may highlight these components
when presenting a fix to the user, indicating that both may be better suited
than a third component that wasn't recommended at all.
Given the list of recommended components considered for inclusion, only one
recommendation is added to C
at a time, re-starting the dependency resolution
process after each addition. If multiple candidate components are considered for
inclusion (each satisfying a different requirement), the first candiate
component as sorted alphabetically by id
is always selected.
A recommendation may specify one or more instance names for instantiable components. When added to the project, all instances are added. If different components recommend different instance names for the same component, the choice of what instance name to use must be given to the user in the same way as the choice between competing components for a feature.
If the user adds an explicitly named instance of a component to the project itself, this necessarily means that the automatically recommended instance is no longer in play, since the recommendation is no longer considered during dependency resolution. GUI tools shall attempt to notice when this happens, and prompt the user whether they desire to also make the recommended instances explicit, or continue with removing them. Alternatively, GUI tools may automatically make the recommended instances explicit without user interaction. This behavior is only possible when using a tool that watches the project and remembers the state of the previous project generation, and is therefore not required for CLI tools.