25'ten fazla konu seçemezsiniz Konular bir harf veya rakamla başlamalı, kısa çizgiler ('-') içerebilir ve en fazla 35 karakter uzunluğunda olabilir.

194 satır
8.0 KiB

  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. %% TODO: read in ./relx.config if it exists or --config value
  19. RelxConfig = rebar_state:get(State, relx, []),
  20. ProfileString = rebar_dir:profile_dir_name(State),
  21. ExtraOverlays = [{profile_string, ProfileString}],
  22. CurrentProfiles = rebar_state:current_profiles(State),
  23. RelxMode = case lists:member(prod, CurrentProfiles) of
  24. true ->
  25. [{mode, prod}];
  26. false ->
  27. []
  28. end,
  29. DefaultOutputDir = filename:join(rebar_dir:base_dir(State), ?DEFAULT_RELEASE_DIR),
  30. RelxConfig1 = RelxMode ++ [output_dir(DefaultOutputDir, Opts),
  31. {overlay_vars_values, ExtraOverlays},
  32. {overlay_vars, [{base_dir, rebar_dir:base_dir(State)}]}
  33. | merge_overlays(RelxConfig)],
  34. {ok, RelxState} = rlx_config:to_state(RelxConfig1),
  35. Providers = rebar_state:providers(State),
  36. Cwd = rebar_state:dir(State),
  37. rebar_hooks:run_project_and_app_hooks(Cwd, pre, Provider, Providers, State),
  38. Releases = releases_to_build(Provider, Opts, RelxState),
  39. case Provider of
  40. relup ->
  41. ToVsn = proplists:get_value(relvsn, Opts, undefined),
  42. UpFromVsn = proplists:get_value(upfrom, Opts, undefined),
  43. %% hd/1 can't fail because --all is not a valid option to relup
  44. relx:build_relup(hd(Releases), ToVsn, UpFromVsn, RelxState);
  45. _ ->
  46. parallel_run(Provider, Releases, all_apps(State), RelxState)
  47. end,
  48. rebar_hooks:run_project_and_app_hooks(Cwd, post, Provider, Providers, State),
  49. {ok, State}.
  50. -spec format_error(any()) -> iolist().
  51. format_error(all_relup) ->
  52. "Option --all can not be applied to `relup` command";
  53. format_error(Error) ->
  54. io_lib:format("~p", [Error]).
  55. %%
  56. parallel_run(release, [Release], AllApps, RelxState) ->
  57. relx:build_release(Release, AllApps, RelxState);
  58. parallel_run(tar, [Release], AllApps, RelxState) ->
  59. relx:build_tar(Release, AllApps, RelxState);
  60. parallel_run(Provider, Releases, AllApps, RelxState) ->
  61. rebar_parallel:queue(Releases, fun rel_worker/2, [Provider, AllApps, RelxState], fun rel_handler/2, []).
  62. rel_worker(Release, [Provider, Apps, RelxState]) ->
  63. try
  64. case Provider of
  65. release ->
  66. relx:build_release(Release, Apps, RelxState);
  67. tar ->
  68. relx:build_tar(Release, Apps, RelxState)
  69. end
  70. catch
  71. error:Error ->
  72. {Release, Error}
  73. end.
  74. rel_handler({{Name, Vsn}, {error, {Module, Reason}}}, _Args) ->
  75. ?ERROR("Error building release ~ts-~ts:~n~ts~ts", [Name, Vsn, rebar_utils:indent(1),
  76. Module:format_error(Reason)]),
  77. ok;
  78. rel_handler(_, _Args) ->
  79. ok.
  80. releases_to_build(Provider, Opts, RelxState)->
  81. case proplists:get_value(all, Opts, undefined) of
  82. undefined ->
  83. case proplists:get_value(relname, Opts, undefined) of
  84. undefined ->
  85. [undefined];
  86. R ->
  87. case proplists:get_value(relvsn, Opts, undefined) of
  88. undefined ->
  89. [list_to_atom(R)];
  90. RelVsn ->
  91. [{list_to_atom(R), RelVsn}]
  92. end
  93. end;
  94. true when Provider =:= relup ->
  95. erlang:error(?PRV_ERROR(all_relup));
  96. true ->
  97. highest_unique_releases(rlx_state:configured_releases(RelxState))
  98. end.
  99. %% takes a map of relx configured releases and returns a list of the highest
  100. %% version for each unique release name
  101. -spec highest_unique_releases(rlx_state:releases()) -> [{atom(), string() | undefined}].
  102. highest_unique_releases(Releases) ->
  103. Unique = maps:fold(fun({Name, Vsn}, _, Acc) ->
  104. update_map_if_higher(Name, Vsn, Acc)
  105. end, #{}, Releases),
  106. maps:to_list(Unique).
  107. update_map_if_higher(Name, Vsn, Acc) ->
  108. maps:update_with(Name, fun(Vsn1) ->
  109. case rlx_util:parsed_vsn_lte(rlx_util:parse_vsn(Vsn1),
  110. rlx_util:parse_vsn(Vsn)) of
  111. true ->
  112. Vsn;
  113. false ->
  114. Vsn1
  115. end
  116. end, Vsn, Acc).
  117. %% Don't override output_dir if the user passed one on the command line
  118. output_dir(DefaultOutputDir, Options) ->
  119. {output_dir, proplists:get_value(output_dir, Options, DefaultOutputDir)}.
  120. merge_overlays(Config) ->
  121. {Overlays, Others} =
  122. lists:partition(fun(C) when element(1, C) =:= overlay -> true;
  123. (_) -> false
  124. end, Config),
  125. %% Have profile overlay entries come before others to match how profiles work elsewhere
  126. NewOverlay = lists:flatmap(fun({overlay, Overlay}) -> Overlay end, lists:reverse(Overlays)),
  127. [{overlay, NewOverlay} | Others].
  128. %%
  129. %% Returns a map of all apps that are part of the rebar3 project.
  130. %% This means the project apps and dependencies but not OTP libraries.
  131. -spec all_apps(rebar_state:t()) -> #{atom() => rlx_app_info:t()}.
  132. all_apps(State) ->
  133. lists:foldl(fun(AppInfo, Acc) ->
  134. Acc#{binary_to_atom(rebar_app_info:name(AppInfo), utf8)
  135. => rebar_app_info:app_to_map(AppInfo)}
  136. end, #{}, rebar_state:project_apps(State) ++ rebar_state:all_deps(State)).
  137. %%
  138. -spec opt_spec_list() -> [getopt:option_spec()].
  139. opt_spec_list() ->
  140. [{all, undefined, "all", boolean,
  141. "If true runs the command against all configured releases"},
  142. {relname, $n, "relname", string,
  143. "Specify the name for the release that will be generated"},
  144. {relvsn, $v, "relvsn", string, "Specify the version for the release"},
  145. {upfrom, $u, "upfrom", string,
  146. "Only valid with relup target, specify the release to upgrade from"},
  147. {output_dir, $o, "output-dir", string,
  148. "The output directory for the release. This is `./` by default."},
  149. {help, $h, "help", undefined,
  150. "Print usage"},
  151. {lib_dir, $l, "lib-dir", string,
  152. "Additional dir that should be searched for OTP Apps"},
  153. {log_level, $V, "verbose", {integer, 2},
  154. "Verbosity level, maybe between 0 and 3"},
  155. {dev_mode, $d, "dev-mode", boolean,
  156. "Symlink the applications and configuration into the release instead of copying"},
  157. {include_erts, $i, "include-erts", string,
  158. "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"},
  159. {override, $a, "override", string,
  160. "Provide an app name and a directory to override in the form <appname>:<app directory>"},
  161. {config, $c, "config", {string, ""}, "The path to a config file"},
  162. {overlay_vars, undefined, "overlay_vars", string, "Path to a file of overlay variables"},
  163. {vm_args, undefined, "vm_args", string, "Path to a file to use for vm.args"},
  164. {sys_config, undefined, "sys_config", string, "Path to a file to use for sys.config"},
  165. {system_libs, undefined, "system_libs", string, "Boolean or path to dir of Erlang system libs"},
  166. {version, undefined, "version", undefined, "Print relx version"},
  167. {root_dir, $r, "root", string, "The project root directory"}].