@@ -250,7 +250,7 @@ defmodule Mix.Tasks.Xref do
250250
251251 * `stats` - prints general statistics about the graph;
252252
253- * `cycles` - prints all cycles in the graph;
253+ * `cycles` - prints all strongly connected cycles in the graph;
254254
255255 * `dot` - produces a DOT graph description in `xref_graph.dot` in the
256256 current directory. Warning: this will override any previously generated file
@@ -366,27 +366,20 @@ defmodule Mix.Tasks.Xref do
366366
367367 lib/c.ex
368368 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:
375380
376381 $ mix xref graph --format cycles --label compile-connected
377382
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-
390383 ## Shared options
391384
392385 Those options are shared across all modes:
@@ -1181,8 +1174,7 @@ defmodule Mix.Tasks.Xref do
11811174 cycles =
11821175 graph
11831176 |> :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 ) } )
11861178
11871179 cycles =
11881180 if min = opts [ :min_cycle_size ] do
@@ -1204,20 +1196,22 @@ defmodule Mix.Tasks.Xref do
12041196 defp cycle_filter_fn ( :compile_connected ) , do: cycle_filter_fn ( :compile )
12051197 defp cycle_filter_fn ( filter ) , do: fn { _node , type } -> type == filter end
12061198
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 ( )
12121203 end
12131204
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
12171211 end
12181212
1219- defp label_cycle ( [ _from ] , _graph ) do
1220- [ ]
1213+ defp cycle_label ( [ ] , _graph , _v , export? ) do
1214+ if export? , do: :export , else: nil
12211215 end
12221216
12231217 defp print_cycles ( references , filter , opts ) do
0 commit comments