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 180 lines
1#!/usr/bin/env escript 2%% SPDX-License-Identifier: Apache-2.0 3%% SPDX-FileCopyrightText: 2022 The Gleam contributors 4 5-mode(compile). 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,elixir.app"}), 118 case application:ensure_all_started([compiler, elixir]) of 119 {ok, _} -> do_compile_elixir(Modules, Out); 120 _ -> 121 io:put_chars(standard_error, [Error, $\n]), 122 {false, []} 123 end 124 end. 125 126do_compile_elixir(Modules, Out) -> 127 ModuleBins = lists:map(fun(Module) -> 128 log({compiling, Module}), 129 list_to_binary(Module) 130 end, Modules), 131 OutBin = list_to_binary(Out), 132 Options = [{dest, OutBin}, {return_diagnostics, true}], 133 % Silence "redefining module" warnings. 134 % Compiled modules in the build directory are added to the code path. 135 % These warnings result from recompiling loaded modules. 136 % TODO: This line can likely be removed if/when the build directory is cleaned before every compilation. 137 'Elixir.Code':compiler_options([{ignore_module_conflict, true}]), 138 case 'Elixir.Kernel.ParallelCompiler':compile_to_path(ModuleBins, OutBin, Options) of 139 {ok, ModuleAtoms, _} -> 140 ToBeam = fun(ModuleAtom) -> 141 Beam = filename:join(Out, atom_to_list(ModuleAtom)) ++ ".beam", 142 log({compiled, Beam}), 143 {ModuleAtom, Beam} 144 end, 145 {true, lists:map(ToBeam, ModuleAtoms)}; 146 {error, Errors, _} -> 147 % Log all filenames associated with modules that failed to compile. 148 % Note: The compiler prints compilation errors upon encountering them. 149 ErrorFiles = lists:usort([File || {File, _, _} <- Errors]), 150 Log = fun(File) -> 151 log({failed, binary_to_list(File)}) 152 end, 153 lists:foreach(Log, ErrorFiles), 154 {false, []}; 155 _ -> {false, []} 156 end. 157 158add_lib_to_erlang_path(Lib) -> 159 code:add_paths(expand_lib_paths(Lib)). 160 161-if(?OTP_RELEASE >= 26). 162del_lib_from_erlang_path(Lib) -> 163 code:del_paths(expand_lib_paths(Lib)). 164-else. 165del_lib_from_erlang_path(Lib) -> 166 lists:foreach(fun code:del_path/1, expand_lib_paths(Lib)). 167-endif. 168 169expand_lib_paths(Lib) -> 170 filelib:wildcard([Lib, "/*/ebin"]). 171 172configure_logging() -> 173 Enabled = os:getenv("GLEAM_LOG") /= false, 174 persistent_term:put(gleam_logging_enabled, Enabled). 175 176log(Term) -> 177 case persistent_term:get(gleam_logging_enabled) of 178 true -> io:fwrite("~p~n", [Term]), ok; 179 false -> ok 180 end.