mix xref (Mix v1.13.0-dev) View Source
Prints cross reference information between modules.
xref task expects a mode as first argument:
mix xref MODE
All available modes are discussed below.
This task is automatically reenabled, so you can print information multiple times in the same Mix invocation.
Prints all callers of the given
mix xref callers MyMod
Prints a file dependency graph where an edge from
A (source) depends on
mix xref graph --format stats
The following options are accepted:
--exclude- paths to exclude
--label- only shows relationships with the given label. By default, it keeps all labels that are transitive. The labels are "compile", "export" and "runtime" and there are two additional label modifiers "compile-direct" and "compile-connected". See "Dependencies types" section below.
--only-nodes- only shows the node names (no edges). Generally useful with the
--source- displays all files that the given source file references (directly or indirectly)
--sink- displays all files that reference the given file (directly or indirectly)
--min-cycle-size- controls the minimum cycle size on formats like
--format- can be set to one of:
pretty- prints the graph to the terminal using Unicode characters. Each prints each file followed by the files it depends on. This is the default except on Windows;
plain- the same as pretty except ASCII characters are used instead of Unicode characters. This is the default on Windows;
stats- prints general statistics about the graph;
cycles- prints all cycles in the graph;
dot- produces a DOT graph description in
xref_graph.dotin the current directory. Warning: this will override any previously generated file
--sink options are particularly useful when trying to understand
how the modules in a particular file interact with the whole system. You can combine
those options with
--only-nodes to get all files that exhibit a certain
property, for example:
# To get all files and their direct compile time dependencies mix xref graph --label compile-direct # 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 # To limit statistics only to certain labels mix xref graph --format stats --label compile
mix xref graph runs, it will print a tree of the following
format. Imagine the following code:
# 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
It will print:
$ mix xref graph lib/a.ex `-- lib/b.ex (compile) `-- lib/c.ex
This tree means that
lib/a.ex depends on
lib/b.ex at compile
time which then depends on
lib/c.ex at runtime. This is often
problematic because if
lib/a.ex also has to
recompile due to this indirect compile time dependency.
--sink filters the graph but does not
ultimately change how you read it. For example, if we use the
--sink lib/c.ex flag, we would see the same tree:
$ mix xref graph --sink lib/c.ex lib/a.ex `-- lib/b.ex (compile) `-- lib/c.ex
--label compile flag is given with
won't be shown, because no module has a compile time dependency on
lib/a.ex still has an indirect compile time dependency
$ mix xref graph --sink lib/c.ex --label compile lib/a.ex `-- lib/b.ex (compile)
Therefore, using a combination of
--label is useful to
find all files that will change once the sink changes, alongside the
transitive dependencies that will cause said recompilations.
Elixir tracks three types of dependencies between modules: compile, exports, and runtime. If a module has a compile time dependency on another module, the caller module has to be recompiled whenever the callee changes. Compile-time dependencies are typically added when using macros or when invoking functions in the module body (outside of functions).
Exports dependencies are compile time dependencies on the module API, namely structs and its public definitions. For example, if you import a module but only use its functions, it is an export dependency. If you use a struct, it is an export dependency too. Export dependencies are only recompiled if the module API changes. Note, however, that compile time dependencies have higher precedence than exports. Therefore if you import a module and use its macros, it is a compile time dependency.
Runtime dependencies are added whenever you invoke another module
inside a function. Modules with runtime dependencies do not have
to be compiled when the callee changes, unless there is a transitive
compile or export time dependency between them. The option
--label compile-connected can be used to find such cases.
Overall, there are two label modifiers: "compile-connected" and "compile-direct". The label modifier "compile-connected" can be used to find files that have at least one compile dependency between them, excluding the compile time dependency itself. "compile-direct" only shows direct compile time dependencies, removing the transitive aspect.
Those options are shared across all modes:
--fail-above- generates a failure if the relevant metric is above the given threshold. This metric is the number of references, except for
--format cycleswhere it is the number of cycles, and
--format statswhich has none.
--include-siblings- includes dependencies that have
:in_umbrellaset to true in the current project in the reports. This can be used to find callers or to analyze graphs between projects
--no-compile- does not compile even if files require compilation
--no-deps-check- does not check dependencies
--no-archives-check- does not check archives
--no-elixir-version-check- does not check the Elixir version from mix.exs
Link to this section Summary
Link to this section Functions
Returns a list of information of all the runtime function calls in the project.
Each item in the list is a map with the following keys:
:callee- a tuple containing the module, function, and arity of the call
:line- an integer representing the line where the function is called
:file- a binary representing the file where the function is called
:caller_module- the module where the function is called
This function returns an empty list when used at the root of an umbrella project because there is no compile manifest to extract the function call information from. To get the function calls of each child in an umbrella, execute the function at the root of each individual application.