|
|
- #!/usr/bin/env escript
- %% -*- erlang -*
- %%!
-
- -include_lib("kernel/include/file.hrl").
-
- main(Args) ->
- case lists:member("--help", Args) of
- true ->
- usage(),
- halt(0);
- false ->
- ok
- end,
-
- %% Get a string repr of build time
- BuiltTime = getTimeStr(),
-
- %% Get a string repr of first matching VCS changeset
- VcsInfo = vcsInfo([{git, ".git", "git describe --always --tags", "git status -s"}]),
-
- %% Check for force=1 flag to force a rebuild
- case lists:member("force=1", Args) of
- true ->
- rm("ebin/*.beam");
- false ->
- case filelib:is_file("ebin/npRMain.beam") of
- true -> rm("ebin/npRMain.beam");
- false -> io:fwrite("No beam files found.~n")
- end
- end,
-
- %% Add check for debug flag
- DebugFlag =
- case lists:member("debug", Args) of
- true -> debug_info;
- false -> undefined
- end,
-
- OtpInfo = string:strip(erlang:system_info(otp_release), both, $\n),
-
- %% Types dict:dict() and digraph:digraph() have been introduced in
- %% Erlang 17.
- %% At the same time, their counterparts dict() and digraph() are to be
- %% deprecated in Erlang 18. namespaced_types option is used to select
- %% proper type name depending on the OTP version used.
- %% Extract the system info of the version of OTP we use to compile npRMain
- %% NamespacedTypes =
- %% case is_otp(OtpInfo, "^[0-9]+") of
- %% true -> {d, namespaced_types};
- %% false -> undefined
- %% end,
-
- %% Compile all src/*.erl to ebin
- %% To not accidentally try to compile files like Mac OS X resource forks,
- %% we only look for npRMain source files that start with a letter.
- Opts = [
- DebugFlag,
- {outdir, "ebin"},
- {i, "include"},
- {d, 'BUILD_TIME', BuiltTime},
- {d, 'VCS_INFO', VcsInfo},
- {d, 'OTP_INFO', OtpInfo}
- ],
- case make:files(filelib:wildcard("src/*.erl"), Opts) of
- up_to_date ->
- ok;
- error ->
- io:format("Failed to compile eNpc files!\n"),
- halt(1)
- end,
-
- %% Make sure file:consult can parse the .app file
- case file:consult("ebin/eNpc.app") of
- {ok, _} ->
- ok;
- {error, Reason} ->
- io:format("Invalid syntax in ebin/eNpc.app: ~p\n", [Reason]),
- halt(1)
- end,
-
- %% Add ebin/ to our path
- true = code:add_path("ebin"),
-
- %% Run npRMain compile to do proper .app validation etc.
- %% and npRMain escriptize to create the npRMain script
- %% RebarArgs = Args -- ["debug"], %% Avoid trying to run 'debug' command
- % npRMain:main(["compile"] ++ RebarArgs),
-
- escriptize(),
-
- %% Finally, update executable perms for our script on *nix,
- %% or write out script files on win32.
- case os:type() of
- {unix, _} ->
- [] = os:cmd("chmod u+x eNpc"),
- ok;
- {win32, _} ->
- writeWindowsScripts(),
- ok;
- _ ->
- ok
- end,
-
- %% Add a helpful message
- io:format(<<"Congratulations! You now have a self-contained script called"
- " \"eNpc\" in\n"
- "your current working directory. "
- "Place this script anywhere in your path\n"
- "and you can use eNpc to build native code for Erlang\n">>).
-
- usage() ->
- io:format(<<"Usage: bootstrap [OPTION]...~n">>),
- io:format(<<" force=1 unconditional build~n">>),
- io:format(<<" debug add debug information~n">>).
-
- %% is_otp(OtpInfo, Regex) ->
- %% case re:run(OtpInfo, Regex, [{capture, none}]) of
- %% match -> true;
- %% nomatch -> false
- %% end.
-
- rm(Path) ->
- NativePath = filename:nativename(Path),
- Cmd = case os:type() of
- {unix, _} -> "rm -f ";
- {win32, _} -> "del /q "
- end,
- [] = os:cmd(Cmd ++ NativePath),
- ok.
-
- getTimeStr() ->
- {{Y, M, D}, {H, Min, S}} = erlang:localtime(),
- lists:flatten(io_lib:format("~B_~2.10.0B_~2.10.0B ~B:~2.10.0B:~2.10.0B", [Y, M, D, H, Min, S])).
-
- vcsInfo([]) ->
- "No VCS info available.";
- vcsInfo([{Id, Dir, VsnCmd, StatusCmd} | Rest]) ->
- case filelib:is_dir(Dir) of
- true ->
- Vsn = string:strip(os:cmd(VsnCmd), both, $\n),
- Status =
- case string:strip(os:cmd(StatusCmd), both, $\n) of
- [] ->
- "";
- _ ->
- "-dirty"
- end,
- lists:concat([Id, " ", Vsn, Status]);
- false ->
- vcsInfo(Rest)
- end.
-
- writeWindowsScripts() ->
- CmdScript =
- "@echo off\r\n"
- "setlocal\r\n"
- "set rebarscript=%~f0\r\n"
- "escript.exe \"%rebarscript:.cmd=%\" %*\r\n",
- ok = file:write_file("eNpc.cmd", CmdScript).
-
-
- escriptize() ->
- AppName = "eNpc",
- ScriptName = "eNpc",
-
- Files = loadEScriptFiles(AppName, "ebin", "*"),
-
- {ok, {"mem", ZipBin}} = zip:create("mem", Files, [memory]),
- Shebang = "#!/usr/bin/env escript\n",
- Comment = "%%\n",
- EmuArgs = io_lib:format("%%! -pa ~s/~s/ebin\n", [AppName, AppName]),
- Script = iolist_to_binary([Shebang, Comment, EmuArgs, ZipBin]),
- ok = file:write_file(ScriptName, Script),
-
- %% Finally, update executable perms for our script
- {ok, #file_info{mode = Mode}} = file:read_file_info(ScriptName),
- ok = file:change_mode(ScriptName, Mode bor 8#00111).
-
- loadEScriptFiles(AppName, Path, WildCard) ->
- FileNames = filelib:wildcard(WildCard, Path),
- Entries =
- lists:flatmap(
- fun(FN) ->
- FPath = filename:join(Path, FN),
- {ok, Contents} = file:read_file(FPath),
- ZipPath = filename:join(AppName, FPath),
- [{ZipPath, Contents} | dirEntries(ZipPath)]
- end,
- FileNames),
- usort(Entries).
-
- %% Given a filename, return zip archive dir entries for each sub-dir.
- %% Required to work around issues fixed in OTP-10071.
- dirEntries(File) ->
- Dirs = dirs(File),
- [{Dir ++ "/", <<>>} || Dir <- Dirs].
-
- %% Given "foo/bar/baz", return ["foo", "foo/bar", "foo/bar/baz"].
- dirs(Dir) ->
- dirs1(filename:split(Dir), "", []).
-
- dirs1([], _, Acc) ->
- lists:reverse(Acc);
- dirs1([H | T], "", []) ->
- dirs1(T, H, [H]);
- dirs1([H | T], Last, Acc) ->
- Dir = filename:join(Last, H),
- dirs1(T, Dir, [Dir | Acc]).
-
- usort(List) ->
- lists:ukeysort(1, lists:flatten(List)).
|