Handling of circular dependencies
We had a couple of cases where we came to the conclusion that these would impeed a circular dependency between dune modules.
Example
One example would be higher order geometries in dune-geometry
. The canonical way to implement these should be based on dune-localfunctions
. A geometry describes a particular type of function in local coordinates. The issue is that dune-localfunctions
already depends on dune-geometry
. This would (potentially) lead to the following setup:
Module: dune-localfunctions
Depends: dune-geometry
and
Module: dune-geometry
Depends: dune-common
Suggests: dune-localfunctions
Current state
The current state is, that his might even work, although not officially supported.
dunecontrol
simply starts building a graph of all dependencies and suggesttions and when ever a module is already in the list, it will not add it again. The only difference between Suggests
and Depends
is that missing dependencies are fatal, while missing suggestions are OK.
So depending on the order in which dunecontrol
finds the modules, it might create one of two possible DAGS of the core modules (as an example). Here follow the original graph and the two possible DAGS and their respective build-order:
graph RL
subgraph AA[Original Graph]
A:common(dune-common)
A:geometry(dune-geometry)
A:istl(dune-istl)
A:localfunctions(dune-localfunctions)
A:grid(dune-grid)
A:geometry --> A:common
A:istl --> A:common
A:geometry -.-> A:localfunctions
A:localfunctions --> A:geometry
A:localfunctions --> A:common
A:grid --> A:geometry
end
graph RL
subgraph D1[DAG variant 1]
B:common(dune-common)
B:geometry(dune-geometry)
B:istl(dune-istl)
B:localfunctions(dune-localfunctions)
B:grid(dune-grid)
B:geometry --> B:common
B:istl --> B:common
B:localfunctions --> B:geometry
B:localfunctions --> B:common
B:grid --> B:geometry
end
graph RL
subgraph D2[DAG variant 2]
C:common(dune-common)
C:geometry(dune-geometry)
C:istl(dune-istl)
C:localfunctions(dune-localfunctions)
C:grid(dune-grid)
C:geometry --> C:common
C:localfunctions --> C:common
C:istl --> C:common
C:grid --> C:geometry
C:geometry -.-> C:localfunctions
end
graph LR
subgraph B1[Build order 1]
D:common(dune-common)
D:geometry(dune-geometry)
D:istl(dune-istl)
D:localfunctions(dune-localfunctions)
D:grid(dune-grid)
D:common --> D:istl
D:istl --> D:geometry
D:grid --> D:localfunctions
D:geometry --> D:grid
end
graph LR
subgraph B2[Build order 2]
E:common(dune-common)
E:geometry(dune-geometry)
E:istl(dune-istl)
E:localfunctions(dune-localfunctions)
E:grid(dune-grid)
E:common --> E:istl
E:istl --> E:localfunctions
E:localfunctions -.-> E:geometry
E:geometry --> E:grid
end
As the circular dependency is only weak in the sense, that dune-localfunctions
only requires the geometry headers but no lib, it is possible to compile this setup without problems.
Note that there might still be some issues with cmake
though...
Proposal
In order to fix this problem and be sufficiently flexible to handle such weak suggestions and weak dependencies I suggest to introduce two additional key words Requires
and Recommends
.
The semantics are then as follows:
-
Depends
: requirements that need to be built first (e.g. we might require libs built in these modules). -
Suggests
: same asdepends
, but we are still functional without these modules. -
Requires
: we only require the headers, but these relations don't impact the construction of a DAG (in particular we don't require any libs or other compiled artifacts from these modules). -
Recommends
: same asRequires
, but we are still functional without these modules.
Alternative names might also be Weak-Depends
and Weak-Suggests
... in general I'm open for suggestions.