Skip to content

Instantly share code, notes, and snippets.

@atomkirk
Created November 5, 2017 00:27
Show Gist options
  • Save atomkirk/b6d583b1a7ba63664802fb1b27ca5aee to your computer and use it in GitHub Desktop.
Save atomkirk/b6d583b1a7ba63664802fb1b27ca5aee to your computer and use it in GitHub Desktop.
a mix task to help figure out why elixir is recompiling every file
defmodule Mix.Tasks.AnalyzeCompile do
use Mix.Task
@shortdoc "Looks for problems in compile graph"
@moduledoc """
This is where we would put any long form documentation or doctests.
"""
def run(_args) do
graph =
File.read!("xref_graph.dot")
|> String.trim
|> String.splitter("\n")
|> Enum.reduce(%{}, fn line, graph ->
line
|> String.split("\"")
|> case do
[" ", file, _] ->
case graph[file] do
nil -> Map.put(graph, file, [])
_list -> graph
end
[" ", file, " -> ", depends_on, _] ->
add_to_graph(graph, depends_on, file, false)
[" ", file, " -> ", depends_on, " [label=", "(compile)", "]"] ->
add_to_graph(graph, depends_on, file, true)
_ -> graph
end
end)
# worst offenders
graph
|> Enum.map(fn {file, deps} ->
{file, all_deps(graph, deps) |> Enum.uniq}
end)
|> Enum.sort_by(fn {_file, list} -> length(list) end)
|> Enum.reverse
|> Enum.take(100)
|> Enum.each(fn {file, list} ->
IO.puts("#{length(list) + 1}: #{file}")
end)
# find cycles
graph
|> Enum.map(fn {file, deps} ->
{file, find_cycle(graph, deps)}
end)
|> Enum.find(fn
{_file, []} -> false
{file, list} -> IO.puts("#{file}: #{Poison.encode! list, pretty: true}")
# {file, list} -> IO.puts("#{file}: #{Kernel.inspect list}")
end)
end
defp add_to_graph(graph, file, depends_on, compile) do
case graph[file] do
nil ->
Map.put(graph, file, [%{
compile: compile,
file: depends_on,
}])
list ->
Map.put(graph, file, list ++ [%{
compile: compile,
file: depends_on,
}])
end
end
def all_deps(graph, deps, acc \\ [], compile_in_tree \\ false)
def all_deps(_graph, [], acc, _), do: acc
def all_deps(graph, [%{file: file, compile: compile} | rest], acc, compile_in_tree) do
if Enum.member?(acc, file) do
acc
else
track = compile_in_tree || compile
# if track do
all_deps(graph, rest, acc ++ [file], track) ++ all_deps(graph, graph[file], acc ++ [file], track)
# else
# acc
# end
end
end
def find_cycle(graph, deps, breadcrumb \\ [], compile_in_tree \\ false) do
Enum.find_value(deps, fn %{file: file, compile: compile} ->
track = compile || compile_in_tree
if Enum.member?(breadcrumb, file) do
if track do
breadcrumb
else
[]
end
else
find_cycle(graph, graph[file], breadcrumb ++ [file], track)
end
end) || []
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment