update
This commit is contained in:
parent
05d35150c3
commit
05267f20ad
4 changed files with 21 additions and 199 deletions
|
|
@ -1,182 +0,0 @@
|
|||
#!/usr/bin/env escript
|
||||
-mode(compile).
|
||||
|
||||
% TODO: Don't concurrently print warnings and errors
|
||||
% TODO: Some tests
|
||||
|
||||
main(_) ->
|
||||
ok = io:setopts([binary, {encoding, utf8}]),
|
||||
ok = configure_logging(),
|
||||
compile_package_loop().
|
||||
|
||||
compile_package_loop() ->
|
||||
case io:get_line("") of
|
||||
eof -> ok;
|
||||
Line ->
|
||||
Chars = unicode:characters_to_list(Line),
|
||||
{ok, Tokens, _} = erl_scan:string(Chars),
|
||||
{ok, {Lib, Out, Modules}} = erl_parse:parse_term(Tokens),
|
||||
case compile_package(Lib, Out, Modules) of
|
||||
{ok, ModuleNames} ->
|
||||
PrintModuleName = fun(ModuleName) ->
|
||||
io:put_chars("gleam-compile-module:" ++ atom_to_list(ModuleName) ++ "\n")
|
||||
end,
|
||||
lists:map(PrintModuleName, ModuleNames),
|
||||
io:put_chars("gleam-compile-result-ok\n");
|
||||
err ->
|
||||
io:put_chars("gleam-compile-result-error\n")
|
||||
end,
|
||||
compile_package_loop()
|
||||
end.
|
||||
|
||||
compile_package(Lib, Out, Modules) ->
|
||||
IsElixirModule = fun(Module) ->
|
||||
filename:extension(Module) =:= ".ex"
|
||||
end,
|
||||
{ElixirModules, ErlangModules} = lists:partition(IsElixirModule, Modules),
|
||||
ok = filelib:ensure_dir([Out, $/]),
|
||||
ok = add_lib_to_erlang_path(Lib),
|
||||
{ErlangOk, ErlangBeams} = compile_erlang(ErlangModules, Out),
|
||||
{ElixirOk, ElixirBeams} = case ErlangOk of
|
||||
true -> compile_elixir(ElixirModules, Out);
|
||||
false -> {false, []}
|
||||
end,
|
||||
ok = del_lib_from_erlang_path(Lib),
|
||||
case ErlangOk andalso ElixirOk of
|
||||
true ->
|
||||
ModuleNames = proplists:get_keys(ErlangBeams ++ ElixirBeams),
|
||||
{ok, ModuleNames};
|
||||
false ->
|
||||
err
|
||||
end.
|
||||
|
||||
compile_erlang(Modules, Out) ->
|
||||
Workers = start_compiler_workers(Out),
|
||||
ok = producer_loop(Modules, Workers),
|
||||
collect_results({true, []}).
|
||||
|
||||
collect_results(Acc = {Result, Beams}) ->
|
||||
receive
|
||||
{compiled, ModuleName, Beam} -> collect_results({Result, [{ModuleName, Beam} | Beams]});
|
||||
failed -> collect_results({false, Beams})
|
||||
after 0 -> Acc
|
||||
end.
|
||||
|
||||
producer_loop([], 0) ->
|
||||
ok;
|
||||
producer_loop([], Workers) ->
|
||||
receive
|
||||
{work_please, _} -> producer_loop([], Workers - 1)
|
||||
end;
|
||||
producer_loop([Module | Modules], Workers) ->
|
||||
receive
|
||||
{work_please, Worker} ->
|
||||
erlang:send(Worker, {module, Module}),
|
||||
producer_loop(Modules, Workers)
|
||||
end.
|
||||
|
||||
start_compiler_workers(Out) ->
|
||||
Parent = self(),
|
||||
NumSchedulers = erlang:system_info(schedulers),
|
||||
SpawnWorker = fun(_) ->
|
||||
erlang:spawn_link(fun() -> worker_loop(Parent, Out) end)
|
||||
end,
|
||||
lists:foreach(SpawnWorker, lists:seq(1, NumSchedulers)),
|
||||
NumSchedulers.
|
||||
|
||||
worker_loop(Parent, Out) ->
|
||||
Options = [report_errors, report_warnings, debug_info, {outdir, Out}],
|
||||
erlang:send(Parent, {work_please, self()}),
|
||||
receive
|
||||
{module, Module} ->
|
||||
log({compiling, Module}),
|
||||
case compile:file(Module, Options) of
|
||||
{ok, ModuleName} ->
|
||||
Beam = filename:join(Out, ModuleName) ++ ".beam",
|
||||
Message = {compiled, ModuleName, Beam},
|
||||
log(Message),
|
||||
erlang:send(Parent, Message);
|
||||
error ->
|
||||
log({failed, Module}),
|
||||
erlang:send(Parent, failed)
|
||||
end,
|
||||
worker_loop(Parent, Out)
|
||||
end.
|
||||
|
||||
compile_elixir(Modules, Out) ->
|
||||
Error = [
|
||||
"The program elixir was not found. Is it installed?",
|
||||
$\n,
|
||||
"Documentation for installing Elixir can be viewed here:",
|
||||
$\n,
|
||||
"https://elixir-lang.org/install.html"
|
||||
],
|
||||
case Modules of
|
||||
[] -> {true, []};
|
||||
_ ->
|
||||
log({starting, "compiler.app"}),
|
||||
ok = application:start(compiler),
|
||||
log({starting, "elixir.app"}),
|
||||
case application:start(elixir) of
|
||||
ok -> do_compile_elixir(Modules, Out);
|
||||
_ ->
|
||||
io:put_chars(standard_error, [Error, $\n]),
|
||||
{false, []}
|
||||
end
|
||||
end.
|
||||
|
||||
do_compile_elixir(Modules, Out) ->
|
||||
ModuleBins = lists:map(fun(Module) ->
|
||||
log({compiling, Module}),
|
||||
list_to_binary(Module)
|
||||
end, Modules),
|
||||
OutBin = list_to_binary(Out),
|
||||
Options = [{dest, OutBin}],
|
||||
% Silence "redefining module" warnings.
|
||||
% Compiled modules in the build directory are added to the code path.
|
||||
% These warnings result from recompiling loaded modules.
|
||||
% TODO: This line can likely be removed if/when the build directory is cleaned before every compilation.
|
||||
'Elixir.Code':compiler_options([{ignore_module_conflict, true}]),
|
||||
case 'Elixir.Kernel.ParallelCompiler':compile_to_path(ModuleBins, OutBin, Options) of
|
||||
{ok, ModuleAtoms, _} ->
|
||||
ToBeam = fun(ModuleAtom) ->
|
||||
Beam = filename:join(Out, atom_to_list(ModuleAtom)) ++ ".beam",
|
||||
log({compiled, Beam}),
|
||||
{ModuleAtom, Beam}
|
||||
end,
|
||||
{true, lists:map(ToBeam, ModuleAtoms)};
|
||||
{error, Errors, _} ->
|
||||
% Log all filenames associated with modules that failed to compile.
|
||||
% Note: The compiler prints compilation errors upon encountering them.
|
||||
ErrorFiles = lists:usort([File || {File, _, _} <- Errors]),
|
||||
Log = fun(File) ->
|
||||
log({failed, binary_to_list(File)})
|
||||
end,
|
||||
lists:foreach(Log, ErrorFiles),
|
||||
{false, []};
|
||||
_ -> {false, []}
|
||||
end.
|
||||
|
||||
add_lib_to_erlang_path(Lib) ->
|
||||
code:add_paths(expand_lib_paths(Lib)).
|
||||
|
||||
-if(?OTP_RELEASE >= 26).
|
||||
del_lib_from_erlang_path(Lib) ->
|
||||
code:del_paths(expand_lib_paths(Lib)).
|
||||
-else.
|
||||
del_lib_from_erlang_path(Lib) ->
|
||||
lists:foreach(fun code:del_path/1, expand_lib_paths(Lib)).
|
||||
-endif.
|
||||
|
||||
expand_lib_paths(Lib) ->
|
||||
filelib:wildcard([Lib, "/*/ebin"]).
|
||||
|
||||
configure_logging() ->
|
||||
Enabled = os:getenv("GLEAM_LOG") /= false,
|
||||
persistent_term:put(gleam_logging_enabled, Enabled).
|
||||
|
||||
log(Term) ->
|
||||
case persistent_term:get(gleam_logging_enabled) of
|
||||
true -> io:fwrite("~p~n", [Term]), ok;
|
||||
false -> ok
|
||||
end.
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
[packages]
|
||||
gleam_stdlib = "0.62.0"
|
||||
gleeunit = "1.6.0"
|
||||
gleam_stdlib = "0.62.0"
|
||||
|
|
|
|||
|
|
@ -78,12 +78,13 @@ defmodule Mix.Tasks.GleamCompile do
|
|||
else
|
||||
Mix.shell().info("Gleam output directory #{gleam_output} does not exist")
|
||||
end
|
||||
|
||||
|
||||
# Also copy Gleam stdlib files
|
||||
gleam_stdlib_dir = "build/dev/erlang/gleam_stdlib/ebin"
|
||||
|
||||
if File.exists?(gleam_stdlib_dir) do
|
||||
Mix.shell().info("Copying Gleam stdlib files...")
|
||||
|
||||
|
||||
gleam_stdlib_dir
|
||||
|> File.ls!()
|
||||
|> Enum.filter(&String.ends_with?(&1, ".beam"))
|
||||
|
|
@ -92,25 +93,25 @@ defmodule Mix.Tasks.GleamCompile do
|
|||
dest = Path.join(ebin_dir, beam_file)
|
||||
File.copy!(src, dest)
|
||||
end)
|
||||
|
||||
|
||||
Mix.shell().info("Copied Gleam stdlib files")
|
||||
end
|
||||
else
|
||||
# Last resort: Check for pre-compiled BEAM files in priv/gleam
|
||||
Mix.shell().info("No Gleam compiler available, checking for pre-compiled BEAM files...")
|
||||
|
||||
|
||||
priv_gleam = "priv/gleam"
|
||||
ebin_dir = "_build/#{env}/lib/aprsme/ebin"
|
||||
|
||||
|
||||
if File.exists?(priv_gleam) do
|
||||
File.mkdir_p!(ebin_dir)
|
||||
|
||||
|
||||
# Copy all Gleam-related BEAM files
|
||||
priv_gleam
|
||||
|> File.ls!()
|
||||
|> Enum.filter(fn file ->
|
||||
String.ends_with?(file, ".beam") and
|
||||
(String.starts_with?(file, "gleam") or String.starts_with?(file, "aprsme@"))
|
||||
|> Enum.filter(fn file ->
|
||||
String.ends_with?(file, ".beam") and
|
||||
(String.starts_with?(file, "gleam") or String.starts_with?(file, "aprsme@"))
|
||||
end)
|
||||
|> Enum.each(fn beam_file ->
|
||||
src = Path.join(priv_gleam, beam_file)
|
||||
|
|
@ -118,7 +119,7 @@ defmodule Mix.Tasks.GleamCompile do
|
|||
File.copy!(src, dest)
|
||||
Mix.shell().info("Copied pre-compiled #{beam_file} from priv/gleam")
|
||||
end)
|
||||
|
||||
|
||||
# Check if we got the main module
|
||||
if not File.exists?(Path.join(ebin_dir, "aprsme@encoding.beam")) do
|
||||
Mix.shell().error("Pre-compiled aprsme@encoding.beam not found in priv/gleam")
|
||||
|
|
@ -126,10 +127,10 @@ defmodule Mix.Tasks.GleamCompile do
|
|||
else
|
||||
# Try to copy from dev build if available
|
||||
dev_ebin = "_build/dev/lib/aprsme/ebin"
|
||||
|
||||
|
||||
if File.exists?(dev_ebin) and env != :dev do
|
||||
File.mkdir_p!(ebin_dir)
|
||||
|
||||
|
||||
dev_ebin
|
||||
|> File.ls!()
|
||||
|> Enum.filter(&String.starts_with?(&1, "aprsme@"))
|
||||
|
|
@ -140,7 +141,9 @@ defmodule Mix.Tasks.GleamCompile do
|
|||
Mix.shell().info("Copied #{beam_file} from dev build")
|
||||
end)
|
||||
else
|
||||
Mix.shell().error("Unable to find Gleam compiled files. Please ensure Gleam code is compiled before deployment.")
|
||||
Mix.shell().error(
|
||||
"Unable to find Gleam compiled files. Please ensure Gleam code is compiled before deployment."
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
7
mix.exs
7
mix.exs
|
|
@ -147,14 +147,15 @@ defmodule Aprsme.MixProject do
|
|||
# {:aprs, github: "aprsme/aprs", branch: "main"}
|
||||
# end
|
||||
end
|
||||
|
||||
|
||||
# Copy Gleam BEAM files to the release
|
||||
defp copy_gleam_files(release) do
|
||||
app_dir = Path.join([release.path, "lib", "aprsme-#{release.version}", "ebin"])
|
||||
File.mkdir_p!(app_dir)
|
||||
|
||||
|
||||
# Copy from priv/gleam if available
|
||||
priv_gleam = "priv/gleam"
|
||||
|
||||
if File.exists?(priv_gleam) do
|
||||
priv_gleam
|
||||
|> File.ls!()
|
||||
|
|
@ -165,7 +166,7 @@ defmodule Aprsme.MixProject do
|
|||
File.copy!(src, dest)
|
||||
end)
|
||||
end
|
||||
|
||||
|
||||
release
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue