Fork of daniellemaywood.uk/gleam — Wasm codegen work
2

Configure Feed

Select the types of activity you want to include in your feed.

gleam / compiler-core / templates / gleam@@compile.erl
5.4 kB 157 lines
1#!/usr/bin/env escript 2 3% TODO: Don't concurrently print warnings and errors 4% TODO: Some tests 5 6-record(arguments, {lib = "./", out = "./", modules = []}). 7 8main(Args) -> 9 #arguments{out = Out, lib = Lib, modules = Modules} = parse(Args), 10 IsElixirModule = fun(Module) -> 11 filename:extension(Module) =:= ".ex" 12 end, 13 {ElixirModules, ErlangModules} = lists:partition(IsElixirModule, Modules), 14 ok = configure_logging(), 15 ok = add_lib_to_erlang_path(Lib), 16 ok = filelib:ensure_dir([Out, $/]), 17 {ErlangOk, _ErlangBeams} = compile_erlang(ErlangModules, Out), 18 {ElixirOk, _ElixirBeams} = case ErlangOk of 19 true -> compile_elixir(ElixirModules, Out); 20 false -> {false, []} 21 end, 22 case ErlangOk and ElixirOk of 23 true -> ok; 24 false -> erlang:halt(1) 25 end. 26 27compile_erlang(Modules, Out) -> 28 Workers = start_compiler_workers(Out), 29 ok = producer_loop(Modules, Workers), 30 collect_results({true, []}). 31 32collect_results(Acc = {Result, Beams}) -> 33 receive 34 {compiled, Beam} -> collect_results({Result, [Beam | Beams]}); 35 failed -> collect_results({false, Beams}) 36 after 0 -> Acc 37 end. 38 39producer_loop([], 0) -> 40 ok; 41producer_loop([], Workers) -> 42 receive 43 {work_please, _} -> producer_loop([], Workers - 1) 44 end; 45producer_loop([Module | Modules], Workers) -> 46 receive 47 {work_please, Worker} -> 48 erlang:send(Worker, {module, Module}), 49 producer_loop(Modules, Workers) 50 end. 51 52start_compiler_workers(Out) -> 53 Parent = self(), 54 NumSchedulers = erlang:system_info(schedulers), 55 SpawnWorker = fun(_) -> 56 erlang:spawn_link(fun() -> worker_loop(Parent, Out) end) 57 end, 58 lists:foreach(SpawnWorker, lists:seq(1, NumSchedulers)), 59 NumSchedulers. 60 61worker_loop(Parent, Out) -> 62 Options = [report_errors, report_warnings, debug_info, {outdir, Out}], 63 erlang:send(Parent, {work_please, self()}), 64 receive 65 {module, Module} -> 66 log({compiling, Module}), 67 case compile:file(Module, Options) of 68 {ok, ModuleName} -> 69 Beam = filename:join(Out, ModuleName) ++ ".beam", 70 Message = {compiled, Beam}, 71 log(Message), 72 erlang:send(Parent, Message); 73 error -> 74 log({failed, Module}), 75 erlang:send(Parent, failed) 76 end, 77 worker_loop(Parent, Out) 78 end. 79 80compile_elixir(Modules, Out) -> 81 Error = [ 82 "The program elixir was not found. Is it installed?", 83 $\n, 84 "Documentation for installing Elixir can be viewed here:", 85 $\n, 86 "https://elixir-lang.org/install.html" 87 ], 88 case Modules of 89 [] -> {true, []}; 90 _ -> 91 log({starting, "compiler.app"}), 92 ok = application:start(compiler), 93 log({starting, "elixir.app"}), 94 case application:start(elixir) of 95 ok -> do_compile_elixir(Modules, Out); 96 _ -> 97 io:put_chars(standard_error, [Error, $\n]), 98 {false, []} 99 end 100 end. 101 102do_compile_elixir(Modules, Out) -> 103 ModuleBins = lists:map(fun(Module) -> 104 log({compiling, Module}), 105 list_to_binary(Module) 106 end, Modules), 107 OutBin = list_to_binary(Out), 108 Options = [{dest, OutBin}], 109 % Silence "redefining module" warnings. 110 % Compiled modules in the build directory are added to the code path. 111 % These warnings result from recompiling loaded modules. 112 % TODO: This line can likely be removed if/when the build directory is cleaned before every compilation. 113 'Elixir.Code':compiler_options([{ignore_module_conflict, true}]), 114 case 'Elixir.Kernel.ParallelCompiler':compile_to_path(ModuleBins, OutBin, Options) of 115 {ok, ModuleAtoms, _} -> 116 ToBeam = fun(ModuleAtom) -> 117 Beam = filename:join(Out, atom_to_list(ModuleAtom)) ++ ".beam", 118 log({compiled, Beam}), 119 Beam 120 end, 121 {true, lists:map(ToBeam, ModuleAtoms)}; 122 {error, Errors, _} -> 123 % Log all filenames associated with modules that failed to compile. 124 % Note: The compiler prints compilation errors upon encountering them. 125 ErrorFiles = lists:usort([File || {File, _, _} <- Errors]), 126 Log = fun(File) -> 127 log({failed, binary_to_list(File)}) 128 end, 129 lists:foreach(Log, ErrorFiles), 130 {false, []}; 131 _ -> {false, []} 132 end. 133 134add_lib_to_erlang_path(Lib) -> 135 code:add_paths(filelib:wildcard([Lib, "/*/ebin"])). 136 137parse(Args) -> 138 parse(Args, #arguments{}). 139 140parse([], Arguments) -> 141 Arguments; 142parse(["--lib", Lib | Rest], Arguments) -> 143 parse(Rest, Arguments#arguments{lib = Lib}); 144parse(["--out", Out | Rest], Arguments) -> 145 parse(Rest, Arguments#arguments{out = Out}); 146parse([Module | Rest], Arguments = #arguments{modules = Modules}) -> 147 parse(Rest, Arguments#arguments{modules = [Module | Modules]}). 148 149configure_logging() -> 150 Enabled = os:getenv("GLEAM_LOG") /= false, 151 persistent_term:put(gleam_logging_enabled, Enabled). 152 153log(Term) -> 154 case persistent_term:get(gleam_logging_enabled) of 155 true -> erlang:display(Term), ok; 156 false -> ok 157 end.