Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

197 рядки
7.2 KiB

13 роки тому
13 роки тому
15 роки тому
13 роки тому
14 роки тому
14 роки тому
13 роки тому
13 роки тому
13 роки тому
13 роки тому
14 роки тому
13 роки тому
13 роки тому
  1. %% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*-
  2. %% ex: ts=4 sw=4 et
  3. %% -------------------------------------------------------------------
  4. %%
  5. %% rebar: Erlang Build Tools
  6. %%
  7. %% Copyright (c) 2009 Dave Smith (dizzyd@dizzyd.com)
  8. %%
  9. %% Permission is hereby granted, free of charge, to any person obtaining a copy
  10. %% of this software and associated documentation files (the "Software"), to deal
  11. %% in the Software without restriction, including without limitation the rights
  12. %% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  13. %% copies of the Software, and to permit persons to whom the Software is
  14. %% furnished to do so, subject to the following conditions:
  15. %%
  16. %% The above copyright notice and this permission notice shall be included in
  17. %% all copies or substantial portions of the Software.
  18. %%
  19. %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  20. %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  21. %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  22. %% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  23. %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  24. %% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  25. %% THE SOFTWARE.
  26. %% -------------------------------------------------------------------
  27. -module(rebar_escripter).
  28. -export([escriptize/2,
  29. clean/2]).
  30. %% for internal use only
  31. -export([info/2]).
  32. -include("rebar.hrl").
  33. -include_lib("kernel/include/file.hrl").
  34. %% ===================================================================
  35. %% Public API
  36. %% ===================================================================
  37. escriptize(Config0, AppFile) ->
  38. %% Extract the application name from the archive -- this is the default
  39. %% name of the generated script
  40. {Config, AppName} = rebar_app_utils:app_name(Config0, AppFile),
  41. AppNameStr = atom_to_list(AppName),
  42. %% Get the output filename for the escript -- this may include dirs
  43. Filename = rebar_config:get_local(Config, escript_name, AppName),
  44. ok = filelib:ensure_dir(Filename),
  45. %% Look for a list of other applications (dependencies) to include
  46. %% in the output file. We then use the .app files for each of these
  47. %% to pull in all the .beam files.
  48. InclBeams = get_app_beams(
  49. rebar_config:get_local(Config, escript_incl_apps, []), []),
  50. %% Look for a list of extra files to include in the output file.
  51. %% For internal rebar-private use only. Do not use outside rebar.
  52. InclExtra = get_extra(Config),
  53. %% Construct the archive of everything in ebin/ dir -- put it on the
  54. %% top-level of the zip file so that code loading works properly.
  55. EbinPrefix = filename:join(AppNameStr, "ebin"),
  56. EbinFiles = usort(load_files(EbinPrefix, "*", "ebin")),
  57. ExtraFiles = usort(InclBeams ++ InclExtra),
  58. Files = EbinFiles ++ ExtraFiles,
  59. case zip:create("mem", Files, [memory]) of
  60. {ok, {"mem", ZipBin}} ->
  61. %% Archive was successfully created. Prefix that binary with our
  62. %% header and write to our escript file
  63. Shebang = rebar_config:get(Config, escript_shebang,
  64. "#!/usr/bin/env escript\n"),
  65. Comment = rebar_config:get(Config, escript_comment, "%%\n"),
  66. DefaultEmuArgs = ?FMT("%%! -pa ~s/~s/ebin\n",
  67. [AppNameStr, AppNameStr]),
  68. EmuArgs = rebar_config:get(Config, escript_emu_args,
  69. DefaultEmuArgs),
  70. Script = iolist_to_binary([Shebang, Comment, EmuArgs, ZipBin]),
  71. case file:write_file(Filename, Script) of
  72. ok ->
  73. ok;
  74. {error, WriteError} ->
  75. ?ERROR("Failed to write ~p script: ~p\n",
  76. [AppName, WriteError]),
  77. ?FAIL
  78. end;
  79. {error, ZipError} ->
  80. ?ERROR("Failed to construct ~p escript: ~p\n",
  81. [AppName, ZipError]),
  82. ?FAIL
  83. end,
  84. %% Finally, update executable perms for our script
  85. {ok, #file_info{mode = Mode}} = file:read_file_info(Filename),
  86. ok = file:change_mode(Filename, Mode bor 8#00111),
  87. {ok, Config}.
  88. clean(Config0, AppFile) ->
  89. %% Extract the application name from the archive -- this is the default
  90. %% name of the generated script
  91. {Config, AppName} = rebar_app_utils:app_name(Config0, AppFile),
  92. %% Get the output filename for the escript -- this may include dirs
  93. Filename = rebar_config:get_local(Config, escript_name, AppName),
  94. rebar_file_utils:delete_each([Filename]),
  95. {ok, Config}.
  96. %% ===================================================================
  97. %% Internal functions
  98. %% ===================================================================
  99. info(help, escriptize) ->
  100. info_help("Generate escript archive");
  101. info(help, clean) ->
  102. info_help("Delete generated escript archive").
  103. info_help(Description) ->
  104. ?CONSOLE(
  105. "~s.~n"
  106. "~n"
  107. "Valid rebar.config options:~n"
  108. " ~p~n"
  109. " ~p~n"
  110. " ~p~n"
  111. " ~p~n"
  112. " ~p~n",
  113. [
  114. Description,
  115. {escript_name, "application"},
  116. {escript_incl_apps, []},
  117. {escript_shebang, "#!/usr/bin/env escript\n"},
  118. {escript_comment, "%%\n"},
  119. {escript_emu_args, "%%! -pa application/application/ebin\n"}
  120. ]).
  121. get_app_beams([], Acc) ->
  122. Acc;
  123. get_app_beams([App | Rest], Acc) ->
  124. case code:lib_dir(App, ebin) of
  125. {error, bad_name} ->
  126. ?ABORT("Failed to get ebin/ directory for "
  127. "~p escript_incl_apps.", [App]);
  128. Path ->
  129. Prefix = filename:join(atom_to_list(App), "ebin"),
  130. Acc2 = load_files(Prefix, "*", Path),
  131. get_app_beams(Rest, Acc2 ++ Acc)
  132. end.
  133. get_extra(Config) ->
  134. Extra = rebar_config:get_local(Config, escript_incl_extra, []),
  135. lists:foldl(fun({Wildcard, Dir}, Files) ->
  136. load_files(Wildcard, Dir) ++ Files
  137. end, [], Extra).
  138. load_files(Wildcard, Dir) ->
  139. load_files("", Wildcard, Dir).
  140. load_files(Prefix, Wildcard, Dir) ->
  141. [read_file(Prefix, Filename, Dir)
  142. || Filename <- filelib:wildcard(Wildcard, Dir)].
  143. read_file(Prefix, Filename, Dir) ->
  144. Filename1 = case Prefix of
  145. "" ->
  146. Filename;
  147. _ ->
  148. filename:join([Prefix, Filename])
  149. end,
  150. [dir_entries(filename:dirname(Filename1)),
  151. {Filename1, file_contents(filename:join(Dir, Filename))}].
  152. file_contents(Filename) ->
  153. {ok, Bin} = file:read_file(Filename),
  154. Bin.
  155. %% Given a filename, return zip archive dir entries for each sub-dir.
  156. %% Required to work around issues fixed in OTP-10071.
  157. dir_entries(File) ->
  158. Dirs = dirs(File),
  159. [{Dir ++ "/", <<>>} || Dir <- Dirs].
  160. %% Given "foo/bar/baz", return ["foo", "foo/bar", "foo/bar/baz"].
  161. dirs(Dir) ->
  162. dirs1(filename:split(Dir), "", []).
  163. dirs1([], _, Acc) ->
  164. lists:reverse(Acc);
  165. dirs1([H|T], "", []) ->
  166. dirs1(T, H, [H]);
  167. dirs1([H|T], Last, Acc) ->
  168. Dir = filename:join(Last, H),
  169. dirs1(T, Dir, [Dir|Acc]).
  170. usort(List) ->
  171. lists:ukeysort(1, lists:flatten(List)).