From 05267f20add0a9d5d902b0fb500d7b574146a683 Mon Sep 17 00:00:00 2001 From: Graham McIntire Date: Fri, 18 Jul 2025 14:16:17 -0500 Subject: [PATCH] update --- .../_gleam_artefacts/gleam@@compile.erl | 182 ------------------ build/packages/packages.toml | 2 +- lib/mix/tasks/gleam_compile.ex | 29 +-- mix.exs | 7 +- 4 files changed, 21 insertions(+), 199 deletions(-) delete mode 100644 build/dev/erlang/aprsme/_gleam_artefacts/gleam@@compile.erl diff --git a/build/dev/erlang/aprsme/_gleam_artefacts/gleam@@compile.erl b/build/dev/erlang/aprsme/_gleam_artefacts/gleam@@compile.erl deleted file mode 100644 index 406deee..0000000 --- a/build/dev/erlang/aprsme/_gleam_artefacts/gleam@@compile.erl +++ /dev/null @@ -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. diff --git a/build/packages/packages.toml b/build/packages/packages.toml index ab102e0..c5930d4 100644 --- a/build/packages/packages.toml +++ b/build/packages/packages.toml @@ -1,3 +1,3 @@ [packages] -gleam_stdlib = "0.62.0" gleeunit = "1.6.0" +gleam_stdlib = "0.62.0" diff --git a/lib/mix/tasks/gleam_compile.ex b/lib/mix/tasks/gleam_compile.ex index 46dc255..801c0df 100644 --- a/lib/mix/tasks/gleam_compile.ex +++ b/lib/mix/tasks/gleam_compile.ex @@ -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 diff --git a/mix.exs b/mix.exs index 55b778d..13af81d 100644 --- a/mix.exs +++ b/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