Skip to content

Commit c46e14d

Browse files
committed
Keep --only-direct for backwards compatibility
1 parent 8161e43 commit c46e14d

File tree

3 files changed

+67
-94
lines changed

3 files changed

+67
-94
lines changed

CHANGELOG.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ In a nutshell, Elixir went from triggering full recompilations whenever any of `
2727

2828
Elixir v1.13 comes with many improvements to `mix xref`, such as:
2929

30-
* `mix xref graph` now supports `--label` to be set to "compile-direct" and "compile-connected", which returns the direct compile-time dependencies ("compile-direct") or just those that generate additional transitive dependencies ("compile-connected")
30+
* `mix xref graph` now supports `--label` to be set to "compile-connected", which returns all compile-time dependencies that lead to additional transitive dependencies.
3131

3232
* A new `mix xref trace FILE` subcommand receives a file and returns all dependencies in said file, including the line and what caused said dependency (a function/macro call, an alias, a struct, etc).
3333

@@ -184,7 +184,6 @@ Finally, the `Code` module has also been augmented with two functions: `Code.str
184184
* [mix xref] Add `trace` subcommand to print compilation dependencies between files
185185
* [mix xref] Add `--fail-above` option to `mix xref`
186186
* [mix xref] Add `--label compile-connected` to `mix xref`
187-
* [mix xref] Add `--label compile-direct` to `mix xref` (instead of `--only-direct`)
188187

189188
### 2. Bug fixes
190189

lib/mix/lib/mix/tasks/xref.ex

Lines changed: 43 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,16 @@ defmodule Mix.Tasks.Xref do
8181
* `--exclude` - paths to exclude
8282
8383
* `--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`
8894
8995
* `--only-nodes` - only shows the node names (no edges).
9096
Generally useful with the `--sink` flag
@@ -119,8 +125,8 @@ defmodule Mix.Tasks.Xref do
119125
those options with `--label` and `--only-nodes` to get all files that exhibit a certain
120126
property, for example:
121127
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
124130
125131
# To get the tree that depend on lib/foo.ex at compile time
126132
mix xref graph --label compile --sink lib/foo.ex
@@ -166,27 +172,16 @@ defmodule Mix.Tasks.Xref do
166172
This tree means that `lib/a.ex` depends on `lib/b.ex` at compile
167173
time. And `lib/b.ex` depends on `lib/c.ex` at runtime. This is often
168174
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:
172177
173178
$ mix xref graph --label compile
174179
lib/a.ex
175180
└── 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)
187181
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
190185
transitive compile time dependencies. You can get all compile time
191186
dependencies that cause transitive compile time dependencies by using
192187
`--label compile-connected`:
@@ -195,19 +190,19 @@ defmodule Mix.Tasks.Xref do
195190
lib/a.ex
196191
└── lib/b.ex (compile)
197192
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`:
201197
202198
$ mix xref graph --source lib/b.ex
203199
lib/b.ex
204200
└── lib/c.ex
205201
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:
209204
210-
$ mix xref graph --label compile-connected --sink lib/c.ex
205+
$ mix xref graph --label compile --sink lib/c.ex
211206
lib/a.ex
212207
└── lib/b.ex (compile)
213208
@@ -235,12 +230,6 @@ defmodule Mix.Tasks.Xref do
235230
compile or an outdated export time dependency between them. The option
236231
`--label compile-connected` can be used to find the first case.
237232
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-
244233
## Shared options
245234
246235
Those options are shared across all modes:
@@ -275,6 +264,7 @@ defmodule Mix.Tasks.Xref do
275264
include_siblings: :boolean,
276265
label: :string,
277266
only_nodes: :boolean,
267+
only_direct: :boolean,
278268
sink: :keep,
279269
source: :keep,
280270
min_cycle_size: :integer
@@ -490,7 +480,11 @@ defmodule Mix.Tasks.Xref do
490480
end
491481

492482
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+
494488
write_graph(file_references(direct_filter, opts), transitive_filter, opts)
495489
end
496490

@@ -598,14 +592,17 @@ defmodule Mix.Tasks.Xref do
598592
|> Enum.flat_map(&[{&1, nil}, {&1, :compile}, {&1, :export}])
599593
end
600594

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
607600
defp label_filter(other), do: Mix.raise("Unknown --label #{other} in mix xref graph")
608601

602+
defp file_references(:compile_connected, _opts) do
603+
Mix.raise("Cannot use --only-direct with --label=compile-connected")
604+
end
605+
609606
defp file_references(filter, opts) do
610607
module_sources =
611608
for manifest_path <- manifests(opts),
@@ -758,22 +755,18 @@ defmodule Mix.Tasks.Xref do
758755
Enum.reduce(file_references, 0, fn {_, refs}, total -> total + length(refs) end)
759756
end
760757

761-
defp filter_fn(file_references, excluded, _compile_time, :compile_connected),
758+
defp filter_fn(file_references, excluded, :compile_connected),
762759
do: fn {key, type} ->
763-
type == :compile and match?([_ | _], file_references[key] -- excluded)
760+
type == :compile and match?([_ | _], (file_references[key] || []) -- excluded)
764761
end
765762

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),
770764
do: fn {_key, type} -> type == filter end
771765

772766
defp filter(file_references, _excluded, :all), do: file_references
773767

774768
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)
777770

778771
for {key, children} <- file_references,
779772
into: %{},
@@ -818,29 +811,6 @@ defmodule Mix.Tasks.Xref do
818811
end)
819812
end
820813

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-
844814
defp print_stats(references, opts) do
845815
with_digraph(references, fn graph ->
846816
shell = Mix.shell()

lib/mix/test/mix/tasks/xref_test.exs

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -460,7 +460,7 @@ defmodule Mix.Tasks.XrefTest do
460460
end
461461

462462
test "only nodes with compile direct label" do
463-
assert_graph(~w[--label compile-direct --only-nodes], """
463+
assert_graph(~w[--label compile --only-direct --only-nodes], """
464464
lib/a.ex
465465
lib/b.ex
466466
lib/c.ex
@@ -472,13 +472,9 @@ defmodule Mix.Tasks.XrefTest do
472472
lib/a.ex
473473
`-- lib/b.ex (compile)
474474
lib/b.ex
475-
|-- lib/a.ex
476-
|-- lib/c.ex
477475
`-- lib/e.ex (compile)
478476
lib/c.ex
479477
`-- lib/d.ex (compile)
480-
lib/d.ex
481-
`-- lib/e.ex
482478
""")
483479
end
484480

@@ -511,8 +507,8 @@ defmodule Mix.Tasks.XrefTest do
511507
end
512508
end
513509

514-
test "filter by compile-direct label" do
515-
assert_graph(~w[--label compile-direct], """
510+
test "filter by compile direct label" do
511+
assert_graph(~w[--label compile --only-direct], """
516512
lib/a.ex
517513
`-- lib/b.ex (compile)
518514
lib/b.ex
@@ -549,16 +545,19 @@ defmodule Mix.Tasks.XrefTest do
549545
assert_graph(~w[--source lib/a.ex --label compile], """
550546
lib/a.ex
551547
`-- lib/b.ex (compile)
552-
|-- lib/a.ex
553-
|-- lib/c.ex
554-
| `-- lib/d.ex (compile)
555-
| `-- lib/e.ex
556548
`-- lib/e.ex (compile)
557549
""")
558550
end
559551

560-
test "source with compile-direct label" do
561-
assert_graph(~w[--source lib/a.ex --label compile-direct], """
552+
test "source with compile-connected label" do
553+
assert_graph(~w[--source lib/a.ex --label compile-connected], """
554+
lib/a.ex
555+
`-- lib/b.ex (compile)
556+
""")
557+
end
558+
559+
test "source with compile direct label" do
560+
assert_graph(~w[--source lib/a.ex --label compile --only-direct], """
562561
lib/a.ex
563562
`-- lib/b.ex (compile)
564563
`-- lib/e.ex (compile)
@@ -591,18 +590,23 @@ defmodule Mix.Tasks.XrefTest do
591590
lib/a.ex
592591
`-- lib/b.ex (compile)
593592
lib/b.ex
594-
|-- lib/a.ex
595-
|-- lib/c.ex
596593
`-- lib/e.ex (compile)
597594
lib/c.ex
598595
`-- lib/d.ex (compile)
599-
lib/d.ex
600-
`-- lib/e.ex
601596
""")
602597
end
603598

604-
test "sink with compile-direct label" do
605-
assert_graph(~w[--sink lib/e.ex --label compile-direct], """
599+
test "sink with compile-connected label" do
600+
assert_graph(~w[--sink lib/e.ex --label compile-connected], """
601+
lib/a.ex
602+
`-- lib/b.ex (compile)
603+
lib/c.ex
604+
`-- lib/d.ex (compile)
605+
""")
606+
end
607+
608+
test "sink with compile direct label" do
609+
assert_graph(~w[--sink lib/e.ex --label compile --only-direct], """
606610
lib/a.ex
607611
`-- lib/b.ex (compile)
608612
lib/b.ex

0 commit comments

Comments
 (0)