/ otp_patches / compile.escript
compile.escript
1 #!/usr/bin/env escript 2 %% -*- mode:erlang; erlang-indent-level: 4; indent-tabs-mode: nil -*- 3 %%! -smp enable 4 -mode(compile). 5 6 main([]) -> 7 Dir = filename:dirname(escript:script_name()), 8 in_dir(fun compile_patches/0, Dir). 9 10 in_dir(F, D) -> 11 {ok, Cwd} = file:get_cwd(), 12 try 13 file:set_cwd(D), 14 try F() of 15 [] -> halt(0); 16 Other -> fatal("~p~n", [Other]) 17 catch 18 error:_ -> halt(1) 19 end 20 after 21 file:set_cwd(Cwd) 22 end. 23 24 compile_patches() -> 25 SubDir = determine_subdir(), 26 Patches = filelib:wildcard(filename:join(SubDir, "*.patch")), 27 info("Patches = ~p~n", [Patches]), 28 [R || R <- [patch_and_compile(P) || P <- Patches], R =/= ok]. 29 30 -spec patch_and_compile(any()) -> ok | {error, any()}. 31 patch_and_compile(P) -> 32 Base = filename:basename(P, ".patch"), 33 Mod = filename:basename(Base, ".erl"), 34 Beam = Mod ++ ".beam", 35 file:delete(filename:join("./ebin", filename:basename(Beam))), 36 case code:where_is_file(Beam) of 37 non_existing -> 38 {error, {source_not_found, Base}}; 39 BeamFile -> 40 case filelib:find_source(BeamFile) of 41 {ok, SrcFile} -> 42 case patch_source(P, SrcFile) of 43 {ok, NewF} -> 44 compile(NewF, SrcFile); 45 {error, _} = Error1 -> 46 info("Error patching source ~p: ~p~n", [SrcFile, Error1]), 47 Error1 48 end; 49 {error, _} = Error -> 50 info("Error finding source for ~p: ~p~n", [BeamFile, Error]), 51 Error 52 end 53 end. 54 55 patch_source(Patch, Src) -> 56 New = filename:join(cur_dir(), filename:basename(Src)), 57 NewPath = transform_path(New), 58 PatchPath = transform_path(filename:join(cur_dir(), Patch)), 59 _DelRes = file:delete(New), 60 SrcPath = transform_path(Src), 61 Cmd = patch_cmd(PatchPath, NewPath, SrcPath), 62 info("Executing patch cmd: ~s~n", [Cmd]), 63 case nonl(os:cmd(Cmd)) of 64 [] -> 65 {ok, New}; 66 Res -> 67 {error, {patch_error, {Res, PatchPath}}} 68 end. 69 70 nonl([$\n]) -> []; 71 nonl([]) -> []; 72 nonl([H|T]) -> [H|nonl(T)]. 73 74 compile(F, Src) -> 75 SrcDir = filename:dirname(Src), 76 IncDir = filename:join(filename:dirname(SrcDir), "include"), 77 ok = filelib:ensure_dir("./ebin/foo"), 78 case compile:file(F, [report_errors, report_warnings, debug_info, 79 {i, SrcDir}, {i, IncDir}, {outdir, "ebin"}]) of 80 {ok, _Mod} -> 81 ok; 82 error -> 83 {error, {compile_error, F}} 84 end. 85 86 determine_subdir() -> 87 case filelib:wildcard("*/versions.eterm") of 88 [] -> 89 "."; 90 [_|_] = Vsns -> 91 match_versions(Vsns) 92 end. 93 94 match_versions([H|T]) -> 95 case file:consult(H) of 96 {ok, Apps} -> 97 case lists:all(fun match_vsn/1, Apps) of 98 true -> 99 filename:dirname(H); 100 false -> 101 match_versions(T) 102 end; 103 {error, Reason} -> 104 fatal("Cannot read ~p: ~p~n", [H, Reason]) 105 end; 106 match_versions([]) -> 107 ".". 108 109 110 match_vsn({App, Re}) -> 111 case code:lib_dir(App) of 112 {error, _} -> 113 false; 114 Dir -> 115 re_match(filename:basename(Dir), Re) 116 end. 117 118 re_match(Str, Re) -> 119 case re:run(Str, Re) of 120 {match, _} -> true; 121 nomatch -> false 122 end. 123 124 125 cur_dir() -> 126 {ok, Cwd} = file:get_cwd(), 127 Cwd. 128 129 info(Fmt, Args) -> 130 try rebar_api:info(Fmt, Args) 131 catch 132 error:_ -> 133 io:fwrite(Fmt, Args) 134 end. 135 136 fatal(Fmt, Args) -> 137 io:fwrite("ERROR: " ++ Fmt, Args), 138 halt(1). 139 140 % We need to transform paths from c:/ to /c/ when running in msys on win32 141 transform_path(Path) -> 142 IsMsys = lists:member(os:getenv("MSYSTEM"), ["MSYS", "MINGW64"]), 143 HasCygpath = os:find_executable("cygpath"), 144 if 145 IsMsys and (HasCygpath =/= false) -> 146 Cmd = "cygpath \"" ++ Path ++ "\"", 147 Res = os:cmd(Cmd), 148 string:trim(Res, both, [$\r, $\n]); 149 true -> 150 Path 151 end. 152 153 patch_cmd(PatchPath, NewPath, SrcPath) -> 154 Arch = erlang:system_info(system_architecture), 155 case string:find(Arch, "apple-darwin") of 156 nomatch -> 157 "patch -s --read-only=ignore -i \"" ++ PatchPath ++ "\" -o \"" ++ NewPath ++ "\" \"" ++ SrcPath ++ "\""; 158 _ -> 159 % The patch binary on macOS is outdated and doesn't support ignoring 160 % read-only files. However, that should not be the case on macOS 161 % anyway. 162 "patch -s -i \"" ++ PatchPath ++ "\" -o \"" ++ NewPath ++ "\" \"" ++ SrcPath ++ "\"" 163 end.