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