Created
November 5, 2017 00:27
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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