For visual more visual readers, we have diagrams in the docs: Dependencies | Rebar3 (of course knowing where to find the content in the docs is always the problem!):
For a regular dependency tree, such as:
A
/ \
B C
the dependencies A
, B
, and C
will be fetched.
However, for more complex trees, such as:
A
/ \
B C1
|
C2
The dependencies A
, B
, and C1
will be fetched. When Rebar3 will encounter the requirement for C2
, it will instead display the warning: Skipping C2 (from $SOURCE) as an app of the same name has already been fetched
.
Such a message should let the user know which dependency has been skipped.
What about cases where two transitive dependencies have the same name and are on the same level?
A
/ \
B C
| |
D1 D2
In such a case, D1
will take over D2
, because B
lexicographically sorts before C
. It’s an entirely arbitrary rule, but it is at least a rule that ensures repeatable fetches.
In the event users disagree with the outcome, they can bring D2
to the top level and ensure it will be chosen early:
A D2
/ \
B C
| |
D1 D2
Which will yield A
, B
, C
, and D2
.
Rebar3 will perform that same algorithm with packages, and will also detect circular dependencies and error out on these.
Dependencies in the _checkouts
directory will be left untouched, and are treated as top-level OTP applications.
this mechanism of favoring “closest to the root” is something I believe we had decided to borrow from Maven, and given how unreliable version numbers were back then, it worked surprisingly well and still works surprisingly well today at grabbing deps that work together. “Closest to the root” does all the heavy lifting of course, lexicographical sorting is just a deterministic tie-breaker.