@@ -44,12 +44,22 @@ defmodule Mix.Tasks.Xref do
4444 └── lib/a.ex
4545
4646 Because you have a compile-time dependency, any of the files `lib/a.ex`,
47- `lib/b.ex`, and `lib/c.ex` depend on will cause the whole cycle to
48- recompile. Therefore, your first priority to reduce compile times is
49- to remove such cycles. You can spot them by running:
47+ `lib/b.ex`, and `lib/c.ex` depend on will cause `lib/a.ex` to recompile.
48+ In other words, whenever you have a cycle, **a change to any file in the
49+ cycle will cause all compile-time deps to recompile**. Therefore, your
50+ first priority to reduce constant recompilations is to remve them.
51+ You can spot them by running:
5052
5153 $ mix xref graph --format cycles --label compile-connected
5254
55+ > #### Use the --label option
56+ >
57+ > The job of `mix xref` is to explore relationships between files
58+ > and it is expected that most of your files are either directly
59+ > or indirectly connected. For this reason, it is strongly advised
60+ > to pass the `--label` option to filter the amount of data,
61+ > specifically with `compile-connected` (or `compile`) as values.
62+
5363 Whenever you find a compile-time dependency, such as `lib/a.ex` pointing
5464 to `lib/b.ex`, there are two ways to remove them:
5565
@@ -208,13 +218,11 @@ defmodule Mix.Tasks.Xref do
208218 * `--exclude` - path to exclude. Can be repeated to exclude multiple paths.
209219
210220 * `--label` - only shows relationships with the given label.
211- The labels are "compile", "export" and "runtime". By default, the `--label`
212- option does not change how the graph is computed, it simply filters the
213- printed graph to show only relationships with the given label. However,
214- you can pass `--only-direct` to trim the graph to only the nodes that
215- have the direct relationship given by label. There is also a special
216- label called "compile-connected" that keeps only compile-time files with
217- at least one transitive dependency. See "Dependency types" section below.
221+ The labels are "compile-connected", "compile", "export" and "runtime".
222+ By default, the `--label` option does not change how the graph is computed,
223+ it simply filters the printed graph to show only relationships with the given
224+ label. However, you can pass `--only-direct` to trim the graph to only the
225+ nodes that have the direct relationship given by label.
218226
219227 * `--group` - provide comma-separated paths to consider as a group. Dependencies
220228 from and into multiple files of the group are considered a single dependency.
@@ -239,6 +247,9 @@ defmodule Mix.Tasks.Xref do
239247 * `--min-cycle-size` - controls the minimum cycle size on formats
240248 like `stats` and `cycles`
241249
250+ * `--min-cycle-label` - controls the minimum number of dependencies
251+ with the given `--label` on a cycle
252+
242253 * `--format` - can be set to one of:
243254
244255 * `pretty` - prints the graph to the terminal using Unicode characters.
@@ -435,6 +446,7 @@ defmodule Mix.Tasks.Xref do
435446 sink: :keep ,
436447 source: :keep ,
437448 min_cycle_size: :integer ,
449+ min_cycle_label: :integer ,
438450 output: :string
439451 ]
440452
@@ -1218,9 +1230,22 @@ defmodule Mix.Tasks.Xref do
12181230 cycles
12191231 end
12201232
1221- # :compile_connected is the same
1233+ min_cycle_label =
1234+ if integer = opts [ :min_cycle_label ] do
1235+ if filter == :all do
1236+ Mix . raise ( "--min-cycle-label requires the --label option to be given" )
1237+ end
1238+
1239+ integer
1240+ else
1241+ 1
1242+ end
1243+
1244+ # :compile_connected is the same as :compile
12221245 if cycle_fn = cycle_filter_fn ( filter ) do
1223- Enum . filter ( cycles , fn { _length , cycle } -> Enum . any? ( cycle , cycle_fn ) end )
1246+ Enum . filter ( cycles , fn { _length , cycle } ->
1247+ Enum . count_until ( cycle , cycle_fn , min_cycle_label ) == min_cycle_label
1248+ end )
12241249 else
12251250 cycles
12261251 end
@@ -1269,11 +1294,35 @@ defmodule Mix.Tasks.Xref do
12691294 shell . info ( "#{ length ( cycles ) } cycles found. Showing them in decreasing size:\n " )
12701295
12711296 for { length , cycle } <- cycles do
1272- shell . info ( "Cycle of length #{ length } :\n " )
1297+ meta =
1298+ cycle
1299+ |> Enum . reduce ( { 0 , 0 } , fn
1300+ { _ , :compile } , { compile , export } -> { compile + 1 , export }
1301+ { _ , :export } , { compile , export } -> { compile , export + 1 }
1302+ { _ , _ } , { compile , export } -> { compile , export }
1303+ end )
1304+ |> case do
1305+ { 0 , 0 } ->
1306+ ""
1307+
1308+ { compile , export } ->
1309+ info =
1310+ if ( compile > 0 , do: [ "#{ compile } compile" ] , else: [ ] ) ++
1311+ if ( export > 0 , do: [ "#{ export } export" ] , else: [ ] )
1312+
1313+ " (" <> Enum . join ( info , ", " ) <> ")"
1314+ end
1315+
1316+ shell . info ( "Cycle of length #{ length } #{ meta } :\n " )
12731317
12741318 for { node , type } <- cycle do
1275- type = if type , do: " (#{ type } )" , else: ""
1276- shell . info ( " " <> node <> type )
1319+ shell . info (
1320+ case type do
1321+ :compile -> [ :red , " #{ node } (compile)" ]
1322+ :export -> " #{ node } (export)"
1323+ _ -> " #{ node } "
1324+ end
1325+ )
12771326 end
12781327
12791328 shell . info ( "" )
0 commit comments