86 lines
2.8 KiB
Elixir
86 lines
2.8 KiB
Elixir
defmodule Kernel.ParallelRequire do
|
|
@moduledoc """
|
|
A module responsible for requiring files in parallel.
|
|
"""
|
|
|
|
@doc """
|
|
Requires the given files.
|
|
|
|
A callback that is invoked every time a file is required
|
|
can be optionally given as argument.
|
|
|
|
Returns the modules generated by each required file.
|
|
"""
|
|
def files(files, callback \\ fn x -> x end) do
|
|
compiler_pid = self()
|
|
:elixir_code_server.cast({:reset_warnings, compiler_pid})
|
|
schedulers = max(:erlang.system_info(:schedulers_online), 2)
|
|
result = spawn_requires(files, [], callback, schedulers, [])
|
|
|
|
# In case --warning-as-errors is enabled and there was a warning,
|
|
# compilation status will be set to error.
|
|
case :elixir_code_server.call({:compilation_status, compiler_pid}) do
|
|
:ok ->
|
|
result
|
|
:error ->
|
|
IO.puts :stderr, "Compilation failed due to warnings while using the --warnings-as-errors option"
|
|
exit({:shutdown, 1})
|
|
end
|
|
end
|
|
|
|
defp spawn_requires([], [], _callback, _schedulers, result), do: result
|
|
|
|
defp spawn_requires([], waiting, callback, schedulers, result) do
|
|
wait_for_messages([], waiting, callback, schedulers, result)
|
|
end
|
|
|
|
defp spawn_requires(files, waiting, callback, schedulers, result) when length(waiting) >= schedulers do
|
|
wait_for_messages(files, waiting, callback, schedulers, result)
|
|
end
|
|
|
|
defp spawn_requires([h|t], waiting, callback, schedulers, result) do
|
|
parent = self()
|
|
{pid, ref} = :erlang.spawn_monitor fn ->
|
|
:erlang.put(:elixir_compiler_pid, parent)
|
|
|
|
exit(try do
|
|
new = Code.require_file(h) || []
|
|
{:required, Enum.map(new, &elem(&1, 0)), h}
|
|
catch
|
|
kind, reason ->
|
|
{:failure, kind, reason, System.stacktrace}
|
|
end)
|
|
end
|
|
|
|
spawn_requires(t, [{pid, ref}|waiting], callback, schedulers, result)
|
|
end
|
|
|
|
defp wait_for_messages(files, waiting, callback, schedulers, result) do
|
|
receive do
|
|
{:DOWN, ref, :process, pid, status} ->
|
|
tuple = {pid, ref}
|
|
if tuple in waiting do
|
|
case status do
|
|
{:required, mods, file} ->
|
|
callback.(file)
|
|
result = mods ++ result
|
|
waiting = List.delete(waiting, tuple)
|
|
{:failure, kind, reason, stacktrace} ->
|
|
:erlang.raise(kind, reason, stacktrace)
|
|
other ->
|
|
:erlang.raise(:exit, other, [])
|
|
end
|
|
end
|
|
spawn_requires(files, waiting, callback, schedulers, result)
|
|
{:module_available, child, ref, _, _, _} ->
|
|
send(child, {ref, :ack})
|
|
spawn_requires(files, waiting, callback, schedulers, result)
|
|
{:struct_available, _} ->
|
|
spawn_requires(files, waiting, callback, schedulers, result)
|
|
{:waiting, :struct, child, ref, _} ->
|
|
send(child, {ref, :release})
|
|
spawn_requires(files, waiting, callback, schedulers, result)
|
|
end
|
|
end
|
|
end
|