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