Current section
Files
Jump to
Current section
Files
lib/archeometer/collect/xref.ex
defmodule Archeometer.Collect.XRef do
@moduledoc """
Module for collecting cross references between modules defined in the same
project.
"""
def compile_xrefs(extra_deps \\ [], opts \\ []) do
full_deps = compile_full_refs(opts, extra_deps)
local_mods =
extra_deps
|> Archeometer.Collect.Project.local_modules()
|> MapSet.new()
full_deps
|> Enum.filter(&(&1.callee in local_mods))
end
defp compile_full_refs(opts, extra_deps) do
tracer = Keyword.get(opts, :tracer, __MODULE__)
ets = :ets.new(__MODULE__, [:named_table, :duplicate_bag, :public])
try do
deps = Mix.Dep.filter_by_name(extra_deps, Mix.Dep.load_and_cache())
Enum.each(deps, &compile_dep(&1, tracer))
compile_task(tracer, opts[:clear?])
:ets.tab2list(__MODULE__)
|> Enum.map(fn {_caller, ref} -> ref end)
after
:ets.delete(ets)
end
end
defp compile_task(tracer, true) do
Mix.Task.clear()
Mix.Task.run("compile", ["--force", "--tracer", tracer])
end
defp compile_task(tracer, _),
do: Mix.Task.run("compile", ["--force", "--tracer", tracer])
defp compile_dep(dep, tracer) do
Mix.Dep.in_dependency(dep, [env: Mix.env()], fn _ ->
Mix.Task.run("compile", ["--force", "--tracer", tracer])
end)
end
def trace({:require, _meta, module, _opts}, env),
do: save_event({:require, module}, env)
def trace({:struct_expansion, _meta, module, _keys}, env),
do: save_event({:struct, module}, env)
def trace({:remote_function, _meta, module, _function, _arity}, env),
do: save_event({:remote_function, module}, env)
def trace({:remote_macro, _meta, module, _function, _arity}, env),
do: save_event({:remote_macro, module}, env)
def trace({:imported_function, _meta, module, _function, _arity}, env),
do: save_event({:imported_function, module}, env)
def trace({:imported_macro, _meta, module, _function, _arity}, env),
do: save_event({:imported_macro, module}, env)
def trace(_event, _env),
do: :ok
def save_event({type, callee}, env) do
dep = %{
caller: env.module,
callee: callee,
type: type,
file: env.file,
line: env.line
}
:ets.insert(__MODULE__, {env.module, dep})
:ok
end
end