No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.

251 líneas
10 KiB

hace 4 años
hace 4 años
hace 4 años
hace 4 años
hace 4 años
  1. %% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*-
  2. %% ex: ts=4 sw=4 et
  3. -module(rebar_relx).
  4. -export([do/2,
  5. opt_spec_list/0,
  6. format_error/1]).
  7. -ifdef(TEST).
  8. -export([merge_overlays/1]).
  9. -endif.
  10. -include_lib("providers/include/providers.hrl").
  11. -include("rebar.hrl").
  12. %% ===================================================================
  13. %% Public API
  14. %% ===================================================================
  15. -spec do(atom(), rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
  16. do(Provider, State) ->
  17. {Opts, _} = rebar_state:command_parsed_args(State),
  18. RelxConfig = read_relx_config(State, Opts),
  19. ProfileString = rebar_dir:profile_dir_name(State),
  20. ExtraOverlays = [{profile_string, ProfileString}],
  21. CurrentProfiles = rebar_state:current_profiles(State),
  22. RelxMode = case lists:member(prod, CurrentProfiles) of
  23. true ->
  24. [{mode, prod}];
  25. false ->
  26. []
  27. end,
  28. DefaultOutputDir = filename:join(rebar_dir:base_dir(State), ?DEFAULT_RELEASE_DIR),
  29. RelxConfig1 = RelxMode ++ [output_dir(DefaultOutputDir, Opts),
  30. {overlay_vars_values, ExtraOverlays},
  31. {overlay_vars, [{base_dir, rebar_dir:base_dir(State)}]}
  32. | merge_overlays(RelxConfig)],
  33. {ok, RelxState} = rlx_config:to_state(RelxConfig1),
  34. Providers = rebar_state:providers(State),
  35. Cwd = rebar_state:dir(State),
  36. rebar_hooks:run_project_and_app_hooks(Cwd, pre, Provider, Providers, State),
  37. Releases = releases_to_build(Provider, Opts, RelxState),
  38. case Provider of
  39. relup ->
  40. {Release, ToVsn} =
  41. %% hd/1 can't fail because --all is not a valid option to relup
  42. case Releases of
  43. [{Rel,Vsn}|_] when is_atom(Rel) ->
  44. %% This is returned if --relvsn and --relname are given
  45. {Rel, Vsn};
  46. [undefined|_] ->
  47. erlang:error(?PRV_ERROR(unknown_release));
  48. [Rel|_] when is_atom(Rel) ->
  49. erlang:error(?PRV_ERROR(unknown_vsn))
  50. end,
  51. UpFromVsn = proplists:get_value(upfrom, Opts, undefined),
  52. relx:build_relup(Release, ToVsn, UpFromVsn, RelxState);
  53. _ ->
  54. parallel_run(Provider, Releases, all_apps(State), RelxState)
  55. end,
  56. rebar_hooks:run_project_and_app_hooks(Cwd, post, Provider, Providers, State),
  57. {ok, State}.
  58. read_relx_config(State, Options) ->
  59. ConfigFile = proplists:get_value(config, Options, []),
  60. case ConfigFile of
  61. "" ->
  62. ConfigPath = filename:join([rebar_dir:root_dir(State), "relx.config"]),
  63. case {rebar_state:get(State, relx, []), file:consult(ConfigPath)} of
  64. {[], {ok, Config}} ->
  65. ?DEBUG("Configuring releases with relx.config", []),
  66. Config;
  67. {Config, {error, enoent}} ->
  68. ?DEBUG("Configuring releases the {relx, ...} entry"
  69. " from rebar.config", []),
  70. Config;
  71. {_, {error, Reason}} ->
  72. erlang:error(?PRV_ERROR({config_file, "relx.config", Reason}));
  73. {RebarConfig, {ok, _RelxConfig}} ->
  74. ?WARN("Found conflicting relx configs, configuring releases"
  75. " with rebar.config", []),
  76. RebarConfig
  77. end;
  78. ConfigFile ->
  79. case file:consult(ConfigFile) of
  80. {ok, Config} ->
  81. ?DEBUG("Configuring releases with: ~ts", [ConfigFile]),
  82. Config;
  83. {error, Reason} ->
  84. erlang:error(?PRV_ERROR({config_file, ConfigFile, Reason}))
  85. end
  86. end.
  87. -spec format_error(any()) -> iolist().
  88. format_error(unknown_release) ->
  89. "Option --relname is missing";
  90. format_error(unknown_vsn) ->
  91. "Option --relvsn is missing";
  92. format_error(all_relup) ->
  93. "Option --all can not be applied to `relup` command";
  94. format_error({config_file, Filename, Error}) ->
  95. io_lib:format("Failed to read config file ~ts: ~p", [Filename, Error]);
  96. format_error(Error) ->
  97. io_lib:format("~p", [Error]).
  98. %%
  99. parallel_run(release, [Release], AllApps, RelxState) ->
  100. relx:build_release(Release, AllApps, RelxState);
  101. parallel_run(tar, [Release], AllApps, RelxState) ->
  102. relx:build_tar(Release, AllApps, RelxState);
  103. parallel_run(Provider, Releases, AllApps, RelxState) ->
  104. rebar_parallel:queue(Releases, fun rel_worker/2, [Provider, AllApps, RelxState], fun rel_handler/2, []).
  105. rel_worker(Release, [Provider, Apps, RelxState]) ->
  106. try
  107. case Provider of
  108. release ->
  109. relx:build_release(Release, Apps, RelxState);
  110. tar ->
  111. relx:build_tar(Release, Apps, RelxState)
  112. end
  113. catch
  114. error:Error ->
  115. {Release, Error}
  116. end.
  117. rel_handler({{Name, Vsn}, {error, {Module, Reason}}}, _Args) ->
  118. ?ERROR("Error building release ~ts-~ts:~n~ts~ts", [Name, Vsn, rebar_utils:indent(1),
  119. Module:format_error(Reason)]),
  120. ok;
  121. rel_handler(_, _Args) ->
  122. ok.
  123. releases_to_build(Provider, Opts, RelxState)->
  124. case proplists:get_value(all, Opts, undefined) of
  125. undefined ->
  126. case proplists:get_value(relname, Opts, undefined) of
  127. undefined ->
  128. [undefined];
  129. R ->
  130. case proplists:get_value(relvsn, Opts, undefined) of
  131. undefined ->
  132. [list_to_atom(R)];
  133. RelVsn ->
  134. [{list_to_atom(R), RelVsn}]
  135. end
  136. end;
  137. true when Provider =:= relup ->
  138. erlang:error(?PRV_ERROR(all_relup));
  139. true ->
  140. highest_unique_releases(rlx_state:configured_releases(RelxState))
  141. end.
  142. %% takes a map of relx configured releases and returns a list of the highest
  143. %% version for each unique release name
  144. -spec highest_unique_releases(rlx_state:releases()) -> [{atom(), string() | undefined}].
  145. highest_unique_releases(Releases) ->
  146. Unique = maps:fold(fun({Name, Vsn}, _, Acc) ->
  147. update_map_if_higher(Name, Vsn, Acc)
  148. end, #{}, Releases),
  149. maps:to_list(Unique).
  150. update_map_if_higher(Name, Vsn, Acc) ->
  151. maps:update_with(Name, fun(Vsn1) ->
  152. case rlx_util:parsed_vsn_lte(rlx_util:parse_vsn(Vsn1),
  153. rlx_util:parse_vsn(Vsn)) of
  154. true ->
  155. Vsn;
  156. false ->
  157. Vsn1
  158. end
  159. end, Vsn, Acc).
  160. %% Don't override output_dir if the user passed one on the command line
  161. output_dir(DefaultOutputDir, Options) ->
  162. {output_dir, proplists:get_value(output_dir, Options, DefaultOutputDir)}.
  163. merge_overlays(Config) ->
  164. {Overlays, Others} =
  165. lists:partition(fun(C) when element(1, C) =:= overlay -> true;
  166. (_) -> false
  167. end, Config),
  168. %% Have profile overlay entries come before others to match how profiles work elsewhere
  169. NewOverlay = lists:flatmap(fun({overlay, Overlay}) -> Overlay end, lists:reverse(Overlays)),
  170. [{overlay, NewOverlay} | Others].
  171. %%
  172. %% Returns a map of all apps that are part of the rebar3 project.
  173. %% This means the project apps and dependencies but not OTP libraries.
  174. -spec all_apps(rebar_state:t()) -> #{atom() => rlx_app_info:t()}.
  175. all_apps(State) ->
  176. maps:merge(app_infos_to_relx(rebar_state:project_apps(State), project),
  177. app_infos_to_relx(rebar_state:all_deps(State), dep)).
  178. %%
  179. -spec app_infos_to_relx([rlx_app_info:t()], rlx_app_info:app_type()) -> #{atom() => rlx_app_info:t()}.
  180. app_infos_to_relx(AppInfos, AppType) ->
  181. lists:foldl(fun(AppInfo, Acc) ->
  182. Acc#{binary_to_atom(rebar_app_info:name(AppInfo), utf8)
  183. => app_info_to_relx(rebar_app_info:app_to_map(AppInfo), AppType)}
  184. end, #{}, AppInfos).
  185. app_info_to_relx(#{name := Name,
  186. vsn := Vsn,
  187. applications := Applications,
  188. included_applications := IncludedApplications,
  189. dir := Dir,
  190. link := false}, AppType) ->
  191. rlx_app_info:new(Name, Vsn, Dir, Applications, IncludedApplications, AppType).
  192. -spec opt_spec_list() -> [getopt:option_spec()].
  193. opt_spec_list() ->
  194. [{all, undefined, "all", boolean,
  195. "If true runs the command against all configured releases"},
  196. {relname, $n, "relname", string,
  197. "Specify the name for the release that will be generated"},
  198. {relvsn, $v, "relvsn", string, "Specify the version for the release"},
  199. {upfrom, $u, "upfrom", string,
  200. "Only valid with relup target, specify the release to upgrade from"},
  201. {output_dir, $o, "output-dir", string,
  202. "The output directory for the release. This is `./` by default."},
  203. {help, $h, "help", undefined,
  204. "Print usage"},
  205. {lib_dir, $l, "lib-dir", string,
  206. "Additional dir that should be searched for OTP Apps"},
  207. {dev_mode, $d, "dev-mode", boolean,
  208. "Symlink the applications and configuration into the release instead of copying"},
  209. {include_erts, $i, "include-erts", string,
  210. "If true include a copy of erts used to build with, if a path include erts at that path. If false, do not include erts"},
  211. {override, $a, "override", string,
  212. "Provide an app name and a directory to override in the form <appname>:<app directory>"},
  213. {config, $c, "config", {string, ""}, "The path to a config file"},
  214. {overlay_vars, undefined, "overlay_vars", string, "Path to a file of overlay variables"},
  215. {vm_args, undefined, "vm_args", string, "Path to a file to use for vm.args"},
  216. {sys_config, undefined, "sys_config", string, "Path to a file to use for sys.config"},
  217. {system_libs, undefined, "system_libs", string, "Boolean or path to dir of Erlang system libs"},
  218. {version, undefined, "version", undefined, "Print relx version"},
  219. {root_dir, $r, "root", string, "The project root directory"}].