Example Scenarios
Scenario 1: Simple Linear Dependencies
Applications:
my_app:
- depends on: [jsx]
jsx:
- depends on: [kernel, stdlib]
Graph Construction:
Vertices: my_app, jsx, kernel, stdlib
Edges:
my_app → jsx
jsx → kernel
jsx → stdlib
Topological Sort Result:
[kernel, stdlib, jsx, my_app]
After Reversal (compilation order):
[my_app, jsx, stdlib, kernel] % Actually reversed already by topsort output
Wait, correction: topsort gives [kernel, stdlib, jsx, my_app], we reverse to get [my_app, jsx, stdlib, kernel]?
Actually: topsort gives dependencies first, so we get [kernel, stdlib, jsx, my_app], then we reverse to... wait, that doesn't make sense.
Clarification from code:
V -> {ok, names_to_apps(lists:reverse(V), Apps)}
So if topsort returns [my_app, jsx, stdlib, kernel], we reverse to [kernel, stdlib, jsx, my_app].
Correct Interpretation:
- Topsort returns:
[my_app, jsx, stdlib, kernel](reverse topological order) - We reverse to:
[kernel, stdlib, jsx, my_app](topological order - dependencies first) - Final:
jsx, thenmy_app(OTP apps filtered out bynames_to_apps)
Scenario 2: Diamond Dependencies
Applications:
my_app:
- depends on: [web, db]
web:
- depends on: [cowboy, common]
db:
- depends on: [epgsql, common]
common:
- depends on: [kernel, stdlib]
Graph:
Vertices: my_app, web, db, cowboy, epgsql, common, kernel, stdlib
Edges:
my_app → web
my_app → db
web → cowboy
web → common
db → epgsql
db → common
common → kernel
common → stdlib
Possible Topological Sorts (dependencies first):
[kernel, stdlib, common, cowboy, epgsql, web, db, my_app]
[kernel, stdlib, common, epgsql, cowboy, web, db, my_app]
[kernel, stdlib, common, cowboy, web, epgsql, db, my_app]
... (many valid orderings)
Key Property: common always before web and db; web and db always before my_app
After Filtering OTP Apps:
[common, cowboy, epgsql, web, db, my_app]
Scenario 3: Circular Dependency
Applications:
app_a:
- depends on: [app_b]
app_b:
- depends on: [app_c]
app_c:
- depends on: [app_a]
Graph:
Vertices: app_a, app_b, app_c
Edges:
app_a → app_b
app_b → app_c
app_c → app_a
Topological Sort: Fails
Cycle Detection:
digraph_utils:strong_components(Graph)
% Returns: [[app_a, app_b, app_c], ...other components...]
Filter length > 1: [[app_a, app_b, app_c]]
Error:
{error, {cycles, [[<<"app_a">>, <<"app_b">>, <<"app_c">>]]}}
User Message:
Dependency cycle(s) detected:
applications: app_a app_b app_c depend on each other
Scenario 4: Multiple Independent Cycles
Applications:
Group 1:
app1 → app2 → app3 → app1
Group 2:
app4 → app5 → app4
Group 3:
app6 (no cycle, depends on kernel only)
Cycle Detection:
Strongly connected components:
[[app1, app2, app3], [app4, app5], [app6], [kernel], [stdlib]]
Filter length > 1:
[[app1, app2, app3], [app4, app5]]
Error:
{error, {cycles, [[<<"app1">>, <<"app2">>, <<"app3">>],
[<<"app4">>, <<"app5">>]]}}
User Message:
Dependency cycle(s) detected:
applications: app1 app2 app3 depend on each other
applications: app4 app5 depend on each other
Scenario 5: Dependencies vs Project Apps
Project Structure:
Project Apps:
- my_web
- my_db
Dependencies:
- cowboy
- epgsql
- ranch
Dependency Compilation Order:
- Resolve all dependencies (cowboy, epgsql, ranch)
- Determine order:
[ranch, cowboy, epgsql](ranch is cowboy's dep) - Call
cull_compile([ranch, cowboy, epgsql], [my_web, my_db]) - Result:
[ranch, cowboy, epgsql](project apps already removed) - Compile in that order
Project App Compilation Order:
- Get project apps:
[my_web, my_db] - Determine order: Check if my_web depends on my_db or vice versa
- If my_web depends on my_db:
[my_db, my_web] - If independent: arbitrary order, e.g.,
[my_web, my_db] - Compile in that order
Scenario 6: Build vs Runtime Dependencies
my_app.app.src:
{applications, [kernel, stdlib, cowboy]}. % Runtime deps
rebar.config:
{deps, [{parse_trans, "3.3.0"}]}. % Build-time only
Dependency Graph:
- Includes both
cowboy(runtime) andparse_trans(build-time) all_apps_deps/1unions both sources- Result:
[cowboy, kernel, parse_trans, stdlib]
Compilation Order:
[parse_trans, cowboy, my_app]
Why: parse_trans might be needed during my_app compilation (as parse transform), cowboy needed for runtime
Scenario 7: Umbrella Project with Inter-App Dependencies
Project Structure:
apps/
├── common/ (no deps)
├── api/ (depends on common)
└── web/ (depends on common, api)
Compilation Order Determination:
Input: [common, api, web]
Graph:
common → [kernel, stdlib]
api → [common, kernel, stdlib]
web → [common, api, kernel, stdlib]
Sorted: [common, api, web]
Result: common compiled first, then api, then web
Critical: Without proper ordering, web would fail to compile due to missing common and api