@@ -81,10 +81,16 @@ defmodule Mix.Tasks.Xref do
81
81
* `--exclude` - paths to exclude
82
82
83
83
* `--label` - only shows relationships with the given label.
84
- By default, it keeps all labels that are transitive.
85
- The labels are "compile", "export" and "runtime" and the label
86
- modifiers called "compile-direct" and "compile-connected".
87
- See "Dependencies types" section below.
84
+ The labels are "compile", "export" and "runtime". By default,
85
+ the `--label` option simply filters the printed graph to show
86
+ only relationships with the given label. If you want to
87
+ effectively filter the graph, you can pass the `--only-direct`
88
+ flag. There is also a special label called "compile-connected"
89
+ that keeps only compile-time files with at least one transitive
90
+ dependency. See "Dependencies types" section below.
91
+
92
+ * `--only-direct` - keeps only files with the direct relationship
93
+ given by `--label`
88
94
89
95
* `--only-nodes` - only shows the node names (no edges).
90
96
Generally useful with the `--sink` flag
@@ -119,8 +125,8 @@ defmodule Mix.Tasks.Xref do
119
125
those options with `--label` and `--only-nodes` to get all files that exhibit a certain
120
126
property, for example:
121
127
122
- # To get all files and their direct compile time dependencies
123
- mix xref graph --label compile-direct
128
+ # To show all compile- time relationships
129
+ mix xref graph --label compile
124
130
125
131
# To get the tree that depend on lib/foo.ex at compile time
126
132
mix xref graph --label compile --sink lib/foo.ex
@@ -166,27 +172,16 @@ defmodule Mix.Tasks.Xref do
166
172
This tree means that `lib/a.ex` depends on `lib/b.ex` at compile
167
173
time. And `lib/b.ex` depends on `lib/c.ex` at runtime. This is often
168
174
problematic because if `lib/c.ex` changes, `lib/a.ex` also has to
169
- recompile due to this indirect compile time dependency. For this reason,
170
- when you pass `--label compile`, the graph remains the same, as `lib/a.ex`
171
- effectively has an indirect compile-time dependency on `lib/c.ex`:
175
+ recompile due to this indirect compile time dependency. When you pass
176
+ `--label compile`, the graph shows only the compile-time dependencies:
172
177
173
178
$ mix xref graph --label compile
174
179
lib/a.ex
175
180
└── lib/b.ex (compile)
176
- lib/b.ex
177
- └── lib/c.ex
178
-
179
- In other words, when using `--label compile`, any dependency on the graph
180
- without the "(compile)" label is a transitive compile time dependency.
181
- If you want to keep only the direct compile time dependencies, you can
182
- use `--label compile-direct`:
183
-
184
- $ mix xref graph --label compile-direct
185
- lib/a.ex
186
- └── lib/b.ex (compile)
187
181
188
- On the other hand, having direct compile time dependencies is not
189
- necessarily an issue. The biggest concern, as mentioned above, are the
182
+ The `--label compile` flag removes all non-compile dependencies. However,
183
+ this can be misleading because having direct compile time dependencies is
184
+ not necessarily an issue. The biggest concern, as mentioned above, are the
190
185
transitive compile time dependencies. You can get all compile time
191
186
dependencies that cause transitive compile time dependencies by using
192
187
`--label compile-connected`:
@@ -195,19 +190,19 @@ defmodule Mix.Tasks.Xref do
195
190
lib/a.ex
196
191
└── lib/b.ex (compile)
197
192
198
- The above says `lib/a.ex` depends on `lib/b.ex` and it has transitive
199
- compile time dependencies. We can retrieve them by passing `lib/b.ex`
200
- as `--source` to `mix xref graph`:
193
+ The above says `lib/a.ex` depends on `lib/b.ex` and that causes transitive
194
+ compile time dependencies - as we know, `lib/a.ex` also depends on `lib/c.ex`.
195
+ We can retrieve those transitive dependencies by passing `lib/b.ex` as
196
+ `--source` to `mix xref graph`:
201
197
202
198
$ mix xref graph --source lib/b.ex
203
199
lib/b.ex
204
200
└── lib/c.ex
205
201
206
- Similarly, you can use the `--label compile-connected` and the `--sink`
207
- flag to find all compile time dependencies that will recompile once
208
- the sink changes:
202
+ Similarly, you can use the `--label compile` and the `--sink` flag to find
203
+ all compile time dependencies that will recompile once the sink changes:
209
204
210
- $ mix xref graph --label compile-connected --sink lib/c.ex
205
+ $ mix xref graph --label compile --sink lib/c.ex
211
206
lib/a.ex
212
207
└── lib/b.ex (compile)
213
208
@@ -235,12 +230,6 @@ defmodule Mix.Tasks.Xref do
235
230
compile or an outdated export time dependency between them. The option
236
231
`--label compile-connected` can be used to find the first case.
237
232
238
- Overall, there are two label modifiers: "compile-connected" and
239
- "compile-direct". The label modifier "compile-connected" can be
240
- used to find compile time dependencies that cause transitive compile
241
- time dependencies. "compile-direct" only shows direct compile time
242
- dependencies, removing the transitive aspect.
243
-
244
233
## Shared options
245
234
246
235
Those options are shared across all modes:
@@ -275,6 +264,7 @@ defmodule Mix.Tasks.Xref do
275
264
include_siblings: :boolean ,
276
265
label: :string ,
277
266
only_nodes: :boolean ,
267
+ only_direct: :boolean ,
278
268
sink: :keep ,
279
269
source: :keep ,
280
270
min_cycle_size: :integer
@@ -490,7 +480,11 @@ defmodule Mix.Tasks.Xref do
490
480
end
491
481
492
482
defp handle_graph ( opts ) do
493
- { direct_filter , transitive_filter } = label_filter ( opts [ :label ] )
483
+ label = label_filter ( opts [ :label ] )
484
+
485
+ { direct_filter , transitive_filter } =
486
+ if opts [ :only_direct ] , do: { label , :all } , else: { :all , label }
487
+
494
488
write_graph ( file_references ( direct_filter , opts ) , transitive_filter , opts )
495
489
end
496
490
@@ -598,14 +592,17 @@ defmodule Mix.Tasks.Xref do
598
592
|> Enum . flat_map ( & [ { & 1 , nil } , { & 1 , :compile } , { & 1 , :export } ] )
599
593
end
600
594
601
- defp label_filter ( nil ) , do: { :all , :all }
602
- defp label_filter ( "compile" ) , do: { :all , :compile }
603
- defp label_filter ( "export" ) , do: { :all , :export }
604
- defp label_filter ( "runtime" ) , do: { :all , nil }
605
- defp label_filter ( "compile-connected" ) , do: { :all , :compile_connected }
606
- defp label_filter ( "compile-direct" ) , do: { :compile , :all }
595
+ defp label_filter ( nil ) , do: :all
596
+ defp label_filter ( "compile" ) , do: :compile
597
+ defp label_filter ( "export" ) , do: :export
598
+ defp label_filter ( "runtime" ) , do: nil
599
+ defp label_filter ( "compile-connected" ) , do: :compile_connected
607
600
defp label_filter ( other ) , do: Mix . raise ( "Unknown --label #{ other } in mix xref graph" )
608
601
602
+ defp file_references ( :compile_connected , _opts ) do
603
+ Mix . raise ( "Cannot use --only-direct with --label=compile-connected" )
604
+ end
605
+
609
606
defp file_references ( filter , opts ) do
610
607
module_sources =
611
608
for manifest_path <- manifests ( opts ) ,
@@ -758,22 +755,18 @@ defmodule Mix.Tasks.Xref do
758
755
Enum . reduce ( file_references , 0 , fn { _ , refs } , total -> total + length ( refs ) end )
759
756
end
760
757
761
- defp filter_fn ( file_references , excluded , _compile_time , :compile_connected ) ,
758
+ defp filter_fn ( file_references , excluded , :compile_connected ) ,
762
759
do: fn { key , type } ->
763
- type == :compile and match? ( [ _ | _ ] , file_references [ key ] -- excluded )
760
+ type == :compile and match? ( [ _ | _ ] , ( file_references [ key ] || [ ] ) -- excluded )
764
761
end
765
762
766
- defp filter_fn ( _file_references , _excluded , compile_time , :compile ) ,
767
- do: fn { key , type } -> type == :compile or Map . has_key? ( compile_time , key ) end
768
-
769
- defp filter_fn ( _file_references , _excluded , _compile_time , filter ) ,
763
+ defp filter_fn ( _file_references , _excluded , filter ) ,
770
764
do: fn { _key , type } -> type == filter end
771
765
772
766
defp filter ( file_references , _excluded , :all ) , do: file_references
773
767
774
768
defp filter ( file_references , excluded , filter ) do
775
- compile_time = compile_time_references ( file_references )
776
- filter_fn = filter_fn ( file_references , excluded , compile_time , filter )
769
+ filter_fn = filter_fn ( file_references , excluded , filter )
777
770
778
771
for { key , children } <- file_references ,
779
772
into: % { } ,
@@ -818,29 +811,6 @@ defmodule Mix.Tasks.Xref do
818
811
end )
819
812
end
820
813
821
- defp compile_time_references ( file_references ) do
822
- acc =
823
- for { _ , children } <- file_references ,
824
- { key , :compile } <- children ,
825
- into: % { } ,
826
- do: { key , true }
827
-
828
- compile_time_references ( file_references , acc )
829
- end
830
-
831
- defp compile_time_references ( file_references , acc ) do
832
- new_acc =
833
- for { key , children } <- file_references ,
834
- acc [ key ] ,
835
- { key , _ } <- children ,
836
- into: acc ,
837
- do: { key , true }
838
-
839
- if map_size ( new_acc ) == map_size ( acc ) ,
840
- do: acc ,
841
- else: compile_time_references ( file_references , new_acc )
842
- end
843
-
844
814
defp print_stats ( references , opts ) do
845
815
with_digraph ( references , fn graph ->
846
816
shell = Mix . shell ( )
0 commit comments