查看源代码 mix xref (Mix v1.16.2)
打印模块之间的交叉引用信息。
xref
任务期望第一个参数为模式
$ mix xref MODE
所有可用的模式将在下面讨论。
此任务会自动重新启用,因此您可以在同一 Mix 调用中多次打印信息。
mix xref callers MODULE
打印给定模块的所有调用者。示例
$ mix xref callers MyMod
mix xref trace FILE
编译给定的文件,列出同一应用程序内的所有依赖项。它包括每个依赖项的类型和行号。示例
$ mix xref trace lib/my_app/router.ex
--label
选项可以用来只保留特定的跟踪信息(编译、运行时或导出)
$ mix xref trace lib/my_app/router.ex --label compile
如果您有伞形应用程序,我们也建议使用 --include-siblings
标志来查看来自兄弟应用程序的依赖项。 trace
命令目前不支持伞形应用程序的根目录。
示例
假设给定文件为 lib/b.ex
defmodule B do
import A
A.macro()
macro()
A.fun()
fun()
def calls_macro, do: A.macro()
def calls_fun, do: A.fun()
def calls_struct, do: %A{}
end
mix xref trace
将打印
lib/b.ex:2: require A (export)
lib/b.ex:3: call A.macro/0 (compile)
lib/b.ex:4: import A.macro/0 (compile)
lib/b.ex:5: call A.fun/0 (compile)
lib/b.ex:6: call A.fun/0 (compile)
lib/b.ex:6: import A.fun/0 (compile)
lib/b.ex:7: call A.macro/0 (compile)
lib/b.ex:8: call A.fun/0 (runtime)
lib/b.ex:9: struct A (export)
mix xref graph
打印一个文件依赖关系图,其中从 A
到 B
的边表示 A
(源)依赖于 B
(汇点)。
$ mix xref graph --format stats
以下选项被接受
--exclude
- 要排除的路径。可以重复多次以排除多个路径。--label
- 只显示具有给定标签的关系。标签包括“编译”、“导出”和“运行时”。默认情况下,--label
选项只是过滤打印的图,只显示具有给定标签的关系。您可以传递--only-direct
来修剪图,只保留具有标签给定的直接关系的节点。还有一个特殊的标签叫“compile-connected”,它只保留至少有一个传递依赖关系的编译时文件。请参见下面的“依赖关系类型”部分。--group
- 提供以逗号分隔的路径,这些路径被视为一个组。来自该组多个文件以及进入该组多个文件的依赖关系被视为单个依赖关系。组元素之间的依赖关系会被忽略。这在您计算编译和编译连接依赖关系并且希望一系列文件被视为一个文件时非常有用。该组使用第一个路径打印,后缀为+
。可以重复多次以创建多个组。--only-direct
- 只保留具有--label
给定的直接关系的文件--only-nodes
- 只显示节点名称(没有边)。通常与--sink
标志一起使用--source
- 显示给定源文件引用的所有文件(直接或间接)。可以重复多次以显示来自多个源文件的引用。--sink
- 显示引用给定文件的所有文件(直接或间接)。可以重复多次。--min-cycle-size
- 控制像stats
和cycles
这样的格式中的最小循环大小--format
- 可以设置为以下之一pretty
- 使用 Unicode 字符将图打印到终端。每个都打印每个文件,后跟它依赖的文件。这是除 Windows 外的默认设置;plain
- 与 pretty 相同,但使用 ASCII 字符而不是 Unicode 字符。这是 Windows 上的默认设置;stats
- 打印有关图的一般统计信息;cycles
- 打印图中的所有循环;dot
- 在当前目录中的xref_graph.dot
中生成一个 DOT 图描述。警告:这将覆盖任何先前生成的 文件
--output
(从 v1.15.0 开始) - 可以设置为以下之一-
- 将输出打印到标准输出;路径 - 将输出图写入给定路径
默认设置为当前目录中的
xref_graph.dot
。
--source
和 --sink
选项在尝试理解特定文件中的模块如何与整个系统交互时特别有用。您可以将这些选项与 --label
和 --only-nodes
结合使用,以获取所有表现出特定属性的文件,例如
# To show all compile-time relationships
$ mix xref graph --label compile
# To get the tree that depend on lib/foo.ex at compile time
$ mix xref graph --label compile --sink lib/foo.ex
# To get all files that depend on lib/foo.ex at compile time
$ mix xref graph --label compile --sink lib/foo.ex --only-nodes
# To get all paths between two files
$ mix xref graph --source lib/foo.ex --sink lib/bar.ex
# To show general statistics about the graph
$ mix xref graph --format stats
理解打印的图
当 mix xref graph
运行时,它将打印以下格式的树。想象以下代码
# lib/a.ex
defmodule A do
IO.puts B.hello()
end
# lib/b.ex
defmodule B do
def hello, do: C.world()
end
# lib/c.ex
defmodule C do
def world, do: "hello world"
end
它将打印
$ mix xref graph
lib/a.ex
└── lib/b.ex (compile)
lib/b.ex
└── lib/c.ex
lib/c.ex
此树表示 lib/a.ex
在编译时依赖于 lib/b.ex
。而 lib/b.ex
在运行时依赖于 lib/c.ex
。这通常是一个问题,因为如果 lib/c.ex
发生变化,lib/a.ex
也必须重新编译,因为存在这种间接的编译时依赖关系。当您传递 --label compile
时,图只显示编译时依赖关系
$ mix xref graph --label compile
lib/a.ex
└── lib/b.ex (compile)
--label compile
标志删除所有非编译依赖关系。但是,这可能具有误导性,因为拥有直接的编译时依赖关系并不一定是一个问题。正如上面提到的,最大的问题是传递的编译时依赖关系。您可以使用 --label compile-connected
获取导致传递编译时依赖关系的所有编译时依赖关系
$ mix xref graph --label compile-connected
lib/a.ex
└── lib/b.ex (compile)
上面说明了 lib/a.ex
依赖于 lib/b.ex
,并且会导致传递的编译时依赖关系 - 正如我们所知,lib/a.ex
也依赖于 lib/c.ex
。我们可以通过将 lib/b.ex
作为 --source
传递给 mix xref graph
来检索这些传递依赖关系
$ mix xref graph --source lib/b.ex
lib/b.ex
└── lib/c.ex
类似地,您可以使用 --label compile
和 --sink
标志找到当汇点更改时将重新编译的所有编译时依赖关系
$ mix xref graph --label compile --sink lib/c.ex
lib/a.ex
└── lib/b.ex (compile)
如果您有伞形应用程序,我们也建议使用 --include-siblings
标志来查看来自兄弟应用程序的依赖项。当在伞形应用程序的根目录调用时,graph
命令将列出来自所有伞形应用程序子项的所有文件,没有任何命名空间。
依赖关系类型
Elixir 跟踪模块之间的三种依赖关系类型:编译、导出和运行时。如果一个模块对另一个模块具有编译时依赖关系,则调用者模块在被调用者发生更改时必须重新编译。编译时依赖关系通常是在使用宏或在模块主体(函数之外)调用函数时添加的。您可以通过运行 mix xref trace path/to/file.ex
列出文件中的所有依赖关系。
导出依赖关系是针对模块 API 的编译时依赖关系,即结构体及其公共定义。例如,如果您导入一个模块但只使用它的函数,那就是一个导出依赖关系。如果您使用结构体,那也是一个导出依赖关系。只有当模块 API 发生更改时,导出依赖关系才会重新编译。但请注意,编译时依赖关系优先于导出。因此,如果您导入一个模块并使用它的宏,那就是一个编译时依赖关系。
运行时依赖关系是在您在函数内部调用另一个模块时添加的。具有运行时依赖关系的模块在被调用者发生更改时不需要重新编译,除非它们之间存在传递的编译或过时的导出时依赖关系。 --label compile-connected
选项可以用来查找第一种情况。
共享选项
这些选项在所有模式之间共享
--fail-above
- 如果相关指标高于给定阈值,则生成失败。适用于除mix xref graph --format stats
之外的所有模式。--include-siblings
- 在报告中包含在当前项目中将:in_umbrella
设置为 true 的依赖关系。这可以用来查找调用者或分析项目之间的图(它只适用于trace
子命令)--no-compile
- 即使文件需要编译也不进行编译--no-deps-check
- 不检查依赖关系--no-archives-check
- 不检查档案--no-elixir-version-check
- 不检查 mix.exs 中的 Elixir 版本
摘要
函数
返回项目中所有运行时函数调用的信息列表。
函数
@spec calls(keyword()) :: [ %{callee: {module(), atom(), arity()}, line: integer(), file: String.t()} ]
返回项目中所有运行时函数调用的信息列表。
列表中的每个项目都是一个包含以下键的映射
:callee
- 包含调用的模块、函数和元数的元组:line
- 表示调用函数的行号的整数:file
- 表示调用函数的文件的二进制数据:caller_module
- 调用函数的模块
此函数在伞形项目的根目录使用时返回空列表,因为没有编译清单可以从中提取函数调用信息。要获取伞形应用程序中每个子项的函数调用,请在每个单独应用程序的根目录执行该函数。