@@ -250,7 +250,7 @@ defmodule Mix.Tasks.Xref do
250
250
251
251
* `stats` - prints general statistics about the graph;
252
252
253
- * `cycles` - prints all cycles in the graph;
253
+ * `cycles` - prints all strongly connected cycles in the graph;
254
254
255
255
* `dot` - produces a DOT graph description in `xref_graph.dot` in the
256
256
current directory. Warning: this will override any previously generated file
@@ -366,27 +366,20 @@ defmodule Mix.Tasks.Xref do
366
366
367
367
lib/c.ex
368
368
lib/b.ex
369
- lib/a.ex
370
-
371
- The cycles are given in order: `c.ex` depends on `b.ex` which depends
372
- on `a.ex` which depends on `c.ex`. In particular, you want to avoid
373
- cycles with compile dependencies in there. You can find those cycles
374
- with:
369
+ lib/a.ex (compile)
370
+
371
+ More precisely, `xref` is printing strongly connected cycles, which
372
+ is the largest cycle possible involving these files. For this reason,
373
+ files may have multiple relationships between them, and therefore the
374
+ cycles are not printed in order. The label reflects the highest type
375
+ of relationship between the given file and any other file in the cycle.
376
+ In the example above, it means `lib/a.ex` depends on something else in
377
+ the cycle at compile-time. Those are exactly the type of dependencies
378
+ we want to avoid, and you can ask `mix xref` to only print graphs with
379
+ with compile dependencies in them by passing the `--label` flag:
375
380
376
381
$ mix xref graph --format cycles --label compile-connected
377
382
378
- Which may look like this:
379
-
380
- Cycle of length 3:
381
-
382
- lib/c.ex
383
- lib/b.ex (compile)
384
- lib/a.ex
385
-
386
- This means `c.ex` depends on `b.ex` at compile time. Any compile dependency
387
- in a cycle is by definition a compile-connected dependency, which must be
388
- generally avoided, as explained earlier in the module documentation.
389
-
390
383
## Shared options
391
384
392
385
Those options are shared across all modes:
@@ -1181,8 +1174,7 @@ defmodule Mix.Tasks.Xref do
1181
1174
cycles =
1182
1175
graph
1183
1176
|> :digraph_utils . cyclic_strong_components ( )
1184
- |> Enum . reduce ( [ ] , & inner_cycles ( graph , Enum . sort ( & 1 ) , & 2 ) )
1185
- |> Enum . map ( & { length ( & 1 ) , & 1 } )
1177
+ |> Enum . map ( & { length ( & 1 ) , add_labels ( & 1 , graph ) } )
1186
1178
1187
1179
cycles =
1188
1180
if min = opts [ :min_cycle_size ] do
@@ -1204,20 +1196,22 @@ defmodule Mix.Tasks.Xref do
1204
1196
defp cycle_filter_fn ( :compile_connected ) , do: cycle_filter_fn ( :compile )
1205
1197
defp cycle_filter_fn ( filter ) , do: fn { _node , type } -> type == filter end
1206
1198
1207
- defp inner_cycles ( _graph , [ ] , acc ) , do: acc
1208
-
1209
- defp inner_cycles ( graph , [ v | vertices ] , acc ) do
1210
- cycle = :digraph . get_cycle ( graph , v )
1211
- inner_cycles ( graph , vertices -- cycle , [ label_cycle ( cycle , graph ) | acc ] )
1199
+ defp add_labels ( vertices , graph ) do
1200
+ vertices
1201
+ |> Enum . map ( fn v -> { v , cycle_label ( vertices , graph , v , false ) } end )
1202
+ |> Enum . sort ( )
1212
1203
end
1213
1204
1214
- defp label_cycle ( [ from , to | cycle ] , graph ) do
1215
- { _edge , _v1 , _v2 , label } = :digraph . edge ( graph , { from , to } )
1216
- [ { to , label } | label_cycle ( [ to | cycle ] , graph ) ]
1205
+ defp cycle_label ( [ out | outs ] , graph , v , export? ) do
1206
+ case :digraph . edge ( graph , { v , out } ) do
1207
+ { _ , _ , _ , :compile } -> :compile
1208
+ { _ , _ , _ , :export } -> cycle_label ( outs , graph , v , true )
1209
+ _ -> cycle_label ( outs , graph , v , export? )
1210
+ end
1217
1211
end
1218
1212
1219
- defp label_cycle ( [ _from ] , _graph ) do
1220
- [ ]
1213
+ defp cycle_label ( [ ] , _graph , _v , export? ) do
1214
+ if export? , do: :export , else: nil
1221
1215
end
1222
1216
1223
1217
defp print_cycles ( references , filter , opts ) do
0 commit comments