diff options
-rw-r--r-- | .cirrus.yml | 4 | ||||
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | README.md | 1 | ||||
-rw-r--r-- | src/relx.erl | 3 | ||||
-rw-r--r-- | src/rlx_cmd_args.erl | 13 | ||||
-rw-r--r-- | src/rlx_config.erl | 42 | ||||
-rw-r--r-- | src/rlx_depsolver.erl | 85 | ||||
-rw-r--r-- | src/rlx_depsolver_culprit.erl | 2 | ||||
-rw-r--r-- | src/rlx_prv_overlay.erl | 149 | ||||
-rw-r--r-- | src/rlx_prv_release.erl | 8 | ||||
-rw-r--r-- | src/rlx_release.erl | 87 | ||||
-rw-r--r-- | src/rlx_state.erl | 6 | ||||
-rw-r--r-- | test/rlx_command_SUITE.erl | 14 | ||||
-rw-r--r-- | test/rlx_depsolver_tester.erl | 500 | ||||
-rw-r--r-- | test/rlx_depsolver_tests.erl | 2 | ||||
-rw-r--r-- | test/rlx_extended_bin_SUITE.erl | 5 | ||||
-rw-r--r-- | test/rlx_prv_release_alias.erl | 27 | ||||
-rw-r--r-- | test/rlx_release_SUITE.erl | 151 |
18 files changed, 688 insertions, 413 deletions
diff --git a/.cirrus.yml b/.cirrus.yml index b49a2b6..8e693dd 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -8,7 +8,7 @@ test_task: - image: erlang:19 - image: erlang:18 test_script: | - rebar3 ct + rebar3 eunit && rebar3 ct always: junit_artifacts: path: "_build/test/logs/ct_run.*/junit_report.xml" @@ -20,4 +20,4 @@ osx_check_task: test_script: | wget https://s3.amazonaws.com/rebar3/rebar3 chmod +x rebar3 - ./rebar3 ct + ./rebar3 eunit && ./rebar3 ct @@ -17,3 +17,5 @@ _rel/* !.cirrus.yml erl_crash.dump rebar +TEST-* +tags @@ -81,6 +81,7 @@ Options | | --sys_config | string | | Path to a file to use for sys.config | | -d | --dev-mode | boolean | false | Symlink all applications and configuration into the release instead of copying| | -i | --include-erts | boolean/string | true | 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 | +| | --provider | string | | Specify an additional relx provider | Wiki ---- diff --git a/src/relx.erl b/src/relx.erl index 8027fd4..b5c3ec5 100644 --- a/src/relx.erl +++ b/src/relx.erl @@ -214,7 +214,8 @@ opt_spec_list() -> {sys_config, undefined, "sys_config", string, "Path to a file to use for sys.config"}, {system_libs, undefined, "system_libs", string, "Path to dir of Erlang system libs"}, {version, undefined, "version", undefined, "Print relx version"}, - {root_dir, $r, "root", string, "The project root directory"}]. + {root_dir, $r, "root", string, "The project root directory"}, + {provider, undefined, "provider", atom, "Specify an additional relx provider"}]. -spec format_error(Reason::term()) -> string(). format_error({invalid_return_value, Provider, Value}) -> diff --git a/src/rlx_cmd_args.erl b/src/rlx_cmd_args.erl index b20344c..4f5e9da 100644 --- a/src/rlx_cmd_args.erl +++ b/src/rlx_cmd_args.erl @@ -285,6 +285,19 @@ create(include_erts, Opts) -> create(warnings_as_errors, Opts) -> WarningsAsErrors = proplists:get_value(warnings_as_errors, Opts, false), {warnings_as_errors, WarningsAsErrors}; +create(provider, Opts) -> + case proplists:get_all_values(provider, Opts) of + [] -> + []; + Providers -> + {add_providers, Providers} + end; +create(add_providers, Opts) -> + Providers = proplists:get_value(add_providers, Opts, []), + {add_providers, Providers}; +create(providers, Opts) -> + Providers = proplists:get_value(providers, Opts, []), + {providers, Providers}; create(_, _) -> []. diff --git a/src/rlx_config.erl b/src/rlx_config.erl index f86f593..90cfe7c 100644 --- a/src/rlx_config.erl +++ b/src/rlx_config.erl @@ -53,7 +53,11 @@ format_error({consult, ConfigFile, Reason}) -> io_lib:format("Unable to read file ~s: ~s", [ConfigFile, file:format_error(Reason)]); format_error({invalid_term, Term}) -> - io_lib:format("Invalid term in config file: ~p", [Term]). + io_lib:format("Invalid term in config file: ~p", [Term]); +format_error({failed_to_parse, Goal}) -> + io_lib:format("Unable to parse goal ~s", [Goal]); +format_error({invalid_goal, Goal}) -> + io_lib:format("Invalid goal: ~p", [Goal]). %%%=================================================================== %%% Internal Functions @@ -182,7 +186,7 @@ load_terms({overrides, Overrides0}, {ok, State0}) -> load_terms({dev_mode, DevMode}, {ok, State0}) -> {ok, rlx_state:dev_mode(State0, DevMode)}; load_terms({goals, Goals}, {ok, State0}) -> - {ok, rlx_state:goals(State0, Goals)}; + parse_goals(Goals, State0); load_terms({upfrom, UpFrom}, {ok, State0}) -> {ok, rlx_state:upfrom(State0, UpFrom)}; load_terms({include_src, IncludeSrc}, {ok, State0}) -> @@ -193,8 +197,7 @@ load_terms({release, {RelName, Vsn, {extend, RelName2}}, Applications}, {ok, Sta ExtendRelease = rlx_state:get_configured_release(State0, RelName2, NewVsn), Applications1 = rlx_release:goals(ExtendRelease), case rlx_release:goals(Release0, - lists:umerge(lists:usort(Applications), - lists:usort(Applications1))) of + rlx_release:merge_application_goals(Applications, Applications1)) of E={error, _} -> E; {ok, Release1} -> @@ -206,8 +209,7 @@ load_terms({release, {RelName, Vsn, {extend, RelName2}}, Applications, Config}, ExtendRelease = rlx_state:get_configured_release(State0, RelName2, NewVsn), Applications1 = rlx_release:goals(ExtendRelease), case rlx_release:goals(Release0, - lists:umerge(lists:usort(Applications), - lists:usort(Applications1))) of + rlx_release:merge_application_goals(Applications, Applications1)) of E={error, _} -> E; {ok, Release1} -> @@ -299,6 +301,34 @@ add_hooks(Hooks, State) -> rlx_state:append_hook(StateAcc, Target, Hook) end, State, Hooks)}. +parse_goals(Goals0, State) -> + {Goals, Error} = lists:mapfoldl(fun + (Goal, ok) when is_list(Goal); is_binary(Goal) -> + case rlx_goal:parse(Goal) of + {ok, Constraint} -> + {Constraint, ok}; + {fail, _} -> + {[], ?RLX_ERROR({failed_to_parse, Goal})} + end; + (Goal, ok) when is_tuple(Goal); is_atom(Goal) -> + case rlx_depsolver:is_valid_raw_constraint(Goal) of + true -> + {Goal, ok}; + false -> + {[], ?RLX_ERROR({invalid_goal, Goal})} + end; + (_, Err = {error, _}) -> + {[], Err}; + (Goal, _) -> + {[], ?RLX_ERROR({invalid_goal, Goal})} + end, ok, Goals0), + case Error of + ok -> + {ok, rlx_state:goals(State, Goals)}; + _ -> + Error + end. + list_of_overlay_vars_files(undefined) -> []; list_of_overlay_vars_files([]) -> diff --git a/src/rlx_depsolver.erl b/src/rlx_depsolver.erl index 8a0f632..0bde8c7 100644 --- a/src/rlx_depsolver.erl +++ b/src/rlx_depsolver.erl @@ -1,5 +1,5 @@ %% -*- erlang-indent-level: 4; indent-tabs-mode: nil; fill-column: 80 -*- -%% ex: ts=4 sx=4 et +%% ex: ts=4 sw=4 et %% %% Copyright 2012 Opscode, Inc. All Rights Reserved. %% @@ -88,7 +88,7 @@ add_package_version/3, add_package_version/4, parse_version/1, - is_valid_constraint/1, + is_valid_raw_constraint/1, filter_packages/2]). %% Internally Exported API. This should *not* be used outside of the rlx_depsolver @@ -132,7 +132,7 @@ -type raw_constraint() :: pkg_name() | {pkg_name(), raw_vsn()} | {pkg_name(), raw_vsn(), constraint_op()} - | {pkg_name(), raw_vsn(), vsn(), between}. + | {pkg_name(), raw_vsn(), raw_vsn(), between}. -type constraint() :: pkg_name() | {pkg_name(), vsn()} @@ -272,39 +272,14 @@ parse_version(Vsn) when erlang:is_tuple(Vsn) ; erlang:is_atom(Vsn) -> Vsn. -%% @doc check that a specified constraint is a valid constraint. --spec is_valid_constraint(constraint()) -> boolean(). -is_valid_constraint(Pkg) when is_atom(Pkg) orelse is_binary(Pkg) -> - true; -is_valid_constraint({_Pkg, Vsn}) when is_tuple(Vsn) -> - true; -is_valid_constraint({_Pkg, Vsn, '='}) when is_tuple(Vsn) -> - true; -is_valid_constraint({_Pkg, _LVsn, gte}) -> - true; -is_valid_constraint({_Pkg, _LVsn, '>='}) -> - true; -is_valid_constraint({_Pkg, _LVsn, lte}) -> - true; -is_valid_constraint({_Pkg, _LVsn, '<='}) -> - true; -is_valid_constraint({_Pkg, _LVsn, gt}) -> - true; -is_valid_constraint({_Pkg, _LVsn, '>'}) -> - true; -is_valid_constraint({_Pkg, _LVsn, lt}) -> - true; -is_valid_constraint({_Pkg, _LVsn, '<'}) -> - true; -is_valid_constraint({_Pkg, _LVsn, pes}) -> - true; -is_valid_constraint({_Pkg, _LVsn, '~>'}) -> - true; -is_valid_constraint({_Pkg, _LVsn1, _LVsn2, between}) -> - true; -is_valid_constraint(_InvalidConstraint) -> - false. - +-spec is_valid_raw_constraint(raw_constraint()) -> true; (any()) -> false. +is_valid_raw_constraint(RawConstraint) -> + try fix_con(RawConstraint) + of + Constraint -> is_valid_constraint(Constraint) + catch + error:function_clause -> false + end. %% @doc given a list of package name version pairs, and a list of constraints %% return every member of that list that matches all constraints. @@ -357,9 +332,9 @@ format_version(Version) -> rlx_depsolver_culprit:format_version(Version). %% @doc A formatted constraint tuple --spec format_constraint(constraint()) -> iolist(). -format_constraint(Constraint) -> - rlx_depsolver_culprit:format_constraint(Constraint). +-spec format_constraint(raw_constraint()) -> iolist(). +format_constraint(RawConstraint) -> + rlx_depsolver_culprit:format_constraint(fix_con(RawConstraint)). %%==================================================================== %% Internal Functions @@ -470,6 +445,38 @@ dep_pkg({Pkg, _Vsn1, _Vsn2, _}) -> dep_pkg(Pkg) when is_atom(Pkg) orelse is_binary(Pkg) -> Pkg. +-spec is_valid_constraint(constraint()) -> boolean(). +is_valid_constraint(Pkg) when is_atom(Pkg) orelse is_binary(Pkg) -> + true; +is_valid_constraint({_Pkg, Vsn}) when is_tuple(Vsn) -> + true; +is_valid_constraint({_Pkg, Vsn, '='}) when is_tuple(Vsn) -> + true; +is_valid_constraint({_Pkg, _LVsn, gte}) -> + true; +is_valid_constraint({_Pkg, _LVsn, '>='}) -> + true; +is_valid_constraint({_Pkg, _LVsn, lte}) -> + true; +is_valid_constraint({_Pkg, _LVsn, '<='}) -> + true; +is_valid_constraint({_Pkg, _LVsn, gt}) -> + true; +is_valid_constraint({_Pkg, _LVsn, '>'}) -> + true; +is_valid_constraint({_Pkg, _LVsn, lt}) -> + true; +is_valid_constraint({_Pkg, _LVsn, '<'}) -> + true; +is_valid_constraint({_Pkg, _LVsn, pes}) -> + true; +is_valid_constraint({_Pkg, _LVsn, '~>'}) -> + true; +is_valid_constraint({_Pkg, _LVsn1, _LVsn2, between}) -> + true; +is_valid_constraint(_InvalidConstraint) -> + false. + -spec add_constraint(pkg_name(), vsn(), [constraint()],constraint()) -> ordered_constraints(). add_constraint(SrcPkg, SrcVsn, PkgsConstraints, PkgConstraint) -> case is_valid_constraint(PkgConstraint) of diff --git a/src/rlx_depsolver_culprit.erl b/src/rlx_depsolver_culprit.erl index cf6dcb2..6368d24 100644 --- a/src/rlx_depsolver_culprit.erl +++ b/src/rlx_depsolver_culprit.erl @@ -1,5 +1,5 @@ %% -*- erlang-indent-level: 4; indent-tabs-mode: nil; fill-column: 80 -*- -%% ex: ts=4 sx=4 et +%% ex: ts=4 sw=4 et %% %% @author Eric Merritt <[email protected]> %% diff --git a/src/rlx_prv_overlay.erl b/src/rlx_prv_overlay.erl index 516d238..2b91c0b 100644 --- a/src/rlx_prv_overlay.erl +++ b/src/rlx_prv_overlay.erl @@ -82,9 +82,6 @@ format_error({read_template, FileName, Reason}) -> [FileName, file:format_error(Reason)]); format_error({overlay_failed, Errors}) -> [[format_error(rlx_util:error_reason(Error)), "\n"] || Error <- Errors]; -format_error({dir_render_failed, Dir, Error}) -> - io_lib:format("rendering mkdir path failed ~s with ~p", - [Dir, Error]); format_error({unable_to_make_symlink, AppDir, TargetDir, Reason}) -> io_lib:format("Unable to symlink directory ~s to ~s because \n~s~s", [AppDir, TargetDir, rlx_util:indent(2), @@ -312,14 +309,12 @@ do_individual_overlay(State, _Files, OverlayVars, {chmod, Mode, Path}) -> NewMode = case is_integer(Mode) of true -> Mode; - false -> erlang:list_to_integer(erlang:binary_to_list(render_string (OverlayVars, Mode))) + false -> erlang:binary_to_integer(render_string (OverlayVars, Mode)) end, - Root = rlx_state:output_dir(State), file_render_do(OverlayVars, Path, fun(NewPath) -> - Absolute = absolutize(State, - filename:join(Root,erlang:iolist_to_binary (NewPath))), + Absolute = absolute_path_to(State, NewPath), case file:change_mode(Absolute, NewMode) of {error, Error} -> ?RLX_ERROR({unable_to_chmod, NewMode, NewPath, Error}); @@ -327,20 +322,16 @@ do_individual_overlay(State, _Files, OverlayVars, {chmod, Mode, Path}) -> end end); do_individual_overlay(State, _Files, OverlayVars, {mkdir, Dir}) -> - case rlx_util:render(erlang:iolist_to_binary(Dir), OverlayVars) of - {ok, IoList} -> - Absolute = absolutize(State, - filename:join(rlx_state:output_dir(State), - erlang:iolist_to_binary(IoList))), - case rlx_util:mkdir_p(Absolute) of - {error, Error} -> - ?RLX_ERROR({unable_to_make_dir, Absolute, Error}); - ok -> - ok - end; - {error, Error} -> - ?RLX_ERROR({dir_render_failed, Dir, Error}) - end; + file_render_do(OverlayVars, Dir, + fun(Dir0) -> + Absolute = absolute_path_to(State, Dir0), + case rlx_util:mkdir_p(Absolute) of + {error, Error} -> + ?RLX_ERROR({unable_to_make_dir, Absolute, Error}); + ok -> + ok + end + end); do_individual_overlay(State, _Files, OverlayVars, {copy, From, To}) -> file_render_do(OverlayVars, From, fun(FromFile) -> @@ -367,73 +358,65 @@ do_individual_overlay(State, _Files, OverlayVars, {template, From, To}) -> fun(FromFile) -> file_render_do(OverlayVars, To, fun(ToFile) -> - RelativeRoot = get_relative_root(State), - FromFile0 = absolutize(State, - filename:join(RelativeRoot, - erlang:iolist_to_binary(FromFile))), - FromFile1 = erlang:binary_to_list(FromFile0), write_template(OverlayVars, - FromFile1, - absolutize(State, - filename:join(rlx_state:output_dir(State), - erlang:iolist_to_binary(ToFile)))) + absolute_path_from(State, FromFile), + absolute_path_to(State, ToFile)) end) end). --spec copy_to(rlx_state:t(), file:name(), file:name()) -> ok | relx:error(). -copy_to(State, FromFile0, ToFile0) -> - RelativeRoot = get_relative_root(State), - ToFile1 = absolutize(State, filename:join(rlx_state:output_dir(State), - erlang:iolist_to_binary(ToFile0))), - - FromFile1 = absolutize(State, filename:join(RelativeRoot, - erlang:iolist_to_binary(FromFile0))), - ToFile2 = case is_directory(ToFile0, ToFile1) of - false -> - filelib:ensure_dir(ToFile1), - ToFile1; - true -> - rlx_util:mkdir_p(ToFile1), - erlang:iolist_to_binary(filename:join(ToFile1, - filename:basename(FromFile1))) - end, - case ec_file:copy(FromFile1, ToFile2, [recursive, {file_info, [mode, time]}]) of +-spec wildcard_copy(rlx_state:t(), file:filename_all(), file:filename_all(), + fun((file:filename_all(), file:filename_all()) -> ok | {error, term()}), + ErrorTag :: atom()) -> ok | relx:error(). +wildcard_copy(State, FromFile0, ToFile0, CopyFun, ErrorTag) -> + FromFile1 = absolute_path_from(State, FromFile0), + ToFile1 = absolute_path_to(State, ToFile0), + + Res = case is_directory(ToFile0, ToFile1) of + false -> + filelib:ensure_dir(ToFile1), + CopyFun(FromFile1, ToFile1); + true -> + Root = absolute_path_from(State, "."), + FromFiles = if + is_list(FromFile0) -> filelib:wildcard(FromFile0, Root); + true -> [FromFile1] + end, + rlx_util:mkdir_p(ToFile1), + lists:foldl(fun + (_, {error, _} = Error) -> Error; + (FromFile, ok) -> + CopyFun(filename:join(Root, FromFile), filename:join(ToFile1, filename:basename(FromFile))) + end, ok, FromFiles) + end, + + case Res of ok -> ok; {error, Err} -> - ?RLX_ERROR({copy_failed, + ?RLX_ERROR({ErrorTag, FromFile1, ToFile1, Err}) end. + +-spec copy_to(rlx_state:t(), file:name(), file:name()) -> ok | relx:error(). +copy_to(State, FromFile0, ToFile0) -> + wildcard_copy(State, FromFile0, ToFile0, + fun(FromPath, ToPath) -> ec_file:copy(FromPath, ToPath, [recursive, {file_info, [mode, time]}]) end, + copy_failed). + -spec link_to(rlx_state:t(), file:name(), file:name()) -> ok | relx:error(). link_to(State, FromFile0, ToFile0) -> - RelativeRoot = get_relative_root(State), - ToFile1 = absolutize(State, filename:join(rlx_state:output_dir(State), - erlang:iolist_to_binary(ToFile0))), - - FromFile1 = absolutize(State, filename:join(RelativeRoot, - erlang:iolist_to_binary(FromFile0))), - ToFile2 = case is_directory(ToFile0, ToFile1) of - false -> - filelib:ensure_dir(ToFile1), - ToFile1; - true -> - rlx_util:mkdir_p(ToFile1), - erlang:iolist_to_binary(filename:join(ToFile1, - filename:basename(FromFile1))) - end, - case ec_file:is_symlink(ToFile2) of - true -> file:delete(ToFile2); - false -> ec_file:remove(ToFile2, [recursive]) + wildcard_copy(State, FromFile0, ToFile0, + fun make_link/2, + link_failed). + +make_link(FromFile, ToFile) -> + case ec_file:is_symlink(ToFile) of + true -> file:delete(ToFile); + false -> ec_file:remove(ToFile, [recursive]) end, - case file:make_symlink(FromFile1, ToFile2) of - ok -> ok; - {error, Err} -> - ?RLX_ERROR({link_failed, - FromFile1, - ToFile1, Err}) - end. + file:make_symlink(FromFile, ToFile). get_relative_root(State) -> case rlx_state:config_file(State) of @@ -448,6 +431,12 @@ get_relative_root(State) -> end end. +absolute_path_from(State, Path) -> + absolutize(State, filename:join(get_relative_root(State), Path)). + +absolute_path_to(State, Path) -> + absolutize(State, filename:join(rlx_state:output_dir(State), Path)). + -spec is_directory(file:name(), file:name()) -> boolean(). is_directory(ToFile0, ToFile1) -> case re:run(ToFile0, ?DIRECTORY_RE) of @@ -512,16 +501,18 @@ render_string(OverlayVars, Data) -> end. -spec file_render_do(proplists:proplist(), iolist(), - fun((term()) -> {ok, rlx_state:t()} | relx:error())) -> + fun((string() | binary()) -> {ok, rlx_state:t()} | relx:error())) -> {ok, rlx_state:t()} | relx:error(). file_render_do(OverlayVars, File, NextAction) -> + io:format("render ~p~n", [File]), case rlx_util:render(File, OverlayVars) of - {ok, IoList} -> - NextAction(IoList); + {ok, Binary} when is_binary(File) -> + NextAction(Binary); + {ok, Binary} when is_list(File) -> + NextAction(binary_to_list(Binary)); {error, Error} -> ?RLX_ERROR({render_failed, File, Error}) end. absolutize(State, FileName) -> - filename:absname(filename:join(rlx_state:root_dir(State), - erlang:iolist_to_binary(FileName))). + filename:absname(filename:join(rlx_state:root_dir(State), FileName)). diff --git a/src/rlx_prv_release.erl b/src/rlx_prv_release.erl index 8de1a51..9be190e 100644 --- a/src/rlx_prv_release.erl +++ b/src/rlx_prv_release.erl @@ -168,12 +168,14 @@ solve_release(State0, DepGraph, RelName, RelVsn) -> %% get per release config values and override the State with them Config = rlx_release:config(Release), {ok, State1} = lists:foldl(fun rlx_config:load_terms/2, {ok, State0}, Config), - Goals = rlx_release:goals(Release), - case Goals of + Goals = rlx_release:constraints(Release), + GlobalGoals = rlx_state:goals(State1), + MergedGoals = rlx_release:merge_application_goals(Goals, GlobalGoals), + case MergedGoals of [] -> ?RLX_ERROR(no_goals_specified); _ -> - case rlx_depsolver:solve(DepGraph, Goals) of + case rlx_depsolver:solve(DepGraph, MergedGoals) of {ok, Pkgs} -> set_resolved(State1, Release, Pkgs); {error, Error} -> diff --git a/src/rlx_release.erl b/src/rlx_release.erl index ec26e6b..78e5970 100644 --- a/src/rlx_release.erl +++ b/src/rlx_release.erl @@ -30,6 +30,8 @@ erts/1, goals/2, goals/1, + constraints/1, + merge_application_goals/2, name/1, vsn/1, realize/3, @@ -138,7 +140,11 @@ goals(Release, Goals0) -> {ok, Release}, Goals0). -spec goals(t()) -> [application_goal()]. -goals(#release_t{goals=Goals}) -> +goals(#release_t{goals=Goals, annotations=Annots}) -> + [application_goal(Goal, Annots) || Goal <- Goals]. + +-spec constraints(t()) -> [rlx_depsolver:raw_constraint()]. +constraints(#release_t{goals=Goals}) -> Goals. -spec realize(t(), [{app_name(), app_vsn()}], [rlx_app_info:t()]) -> @@ -373,6 +379,14 @@ parse_goal0({Constraint0, Annots, Incls}, {ok, Release}) Error -> Error end; +parse_goal0({Constraint0, Incls}, {ok, Release}) + when erlang:is_list(Incls), Incls == [] orelse is_atom(hd(Incls)) -> + case parse_constraint(Constraint0) of + {ok, Constraint1} -> + parse_goal1(Release, Constraint1, {void, Incls}); + Error -> + Error + end; parse_goal0(Constraint0, {ok, Release}) -> case parse_constraint(Constraint0) of {ok, Constraint1} -> @@ -409,12 +423,11 @@ parse_constraint(Constraint0) parse_constraint(Constraint0) when erlang:is_tuple(Constraint0); erlang:is_atom(Constraint0) -> - Constraint1 = parse_version(Constraint0), - case rlx_depsolver:is_valid_constraint(Constraint1) of + case rlx_depsolver:is_valid_raw_constraint(Constraint0) of false -> ?RLX_ERROR({invalid_constraint, 2, Constraint0}); true -> - {ok, Constraint1} + {ok, Constraint0} end; parse_constraint(Constraint) -> ?RLX_ERROR({invalid_constraint, 3, Constraint}). @@ -432,22 +445,44 @@ get_app_name({AppName, _, _, _}) when erlang:is_atom(AppName) -> get_app_name(V) -> ?RLX_ERROR({invalid_constraint, 4, V}). --spec parse_version(rlx_depsolver:raw_constraint()) -> - rlx_depsolver:constraint(). -parse_version({AppName, Version}) - when erlang:is_binary(Version); - erlang:is_list(Version) -> - {AppName, rlx_depsolver:parse_version(Version)}; -parse_version({AppName, Version, Constraint}) - when erlang:is_binary(Version); - erlang:is_list(Version) -> - {AppName, rlx_depsolver:parse_version(Version), Constraint}; -parse_version({AppName, Version, Constraint0, Constraint1}) - when erlang:is_binary(Version); - erlang:is_list(Version) -> - {AppName, rlx_depsolver:parse_version(Version), Constraint1, Constraint0}; -parse_version(Constraint) -> - Constraint. +-spec get_goal_app_name(application_goal()) -> atom() | relx:error(). +get_goal_app_name({Constraint, Annots}) + when Annots =:= permanent; + Annots =:= transient; + Annots =:= temporary; + Annots =:= load; + Annots =:= none -> + get_app_name(Constraint); +get_goal_app_name({Constraint, Annots, Incls}) + when (Annots =:= permanent orelse + Annots =:= transient orelse + Annots =:= temporary orelse + Annots =:= load orelse + Annots =:= none), + erlang:is_list(Incls) -> + get_app_name(Constraint); +get_goal_app_name({Constraint, Incls}) + when erlang:is_list(Incls), Incls == [] orelse is_atom(hd(Incls)) -> + get_app_name(Constraint); +get_goal_app_name(Constraint) -> + get_app_name(Constraint). + +-spec application_goal(rlx_depsolver:raw_constraint(), annotations()) -> application_goal(). +application_goal(Constraint, Annots) -> + AppName = get_app_name(Constraint), + try ec_dictionary:get(AppName, Annots) of + {void, void} -> + Constraint; + {void, Incls} -> + {Constraint, Incls}; + {Type, void} -> + {Constraint, Type}; + {Type, Incls} -> + {Constraint, Type, Incls} + catch + throw:not_found -> + Constraint + end. to_atom(RelName) when erlang:is_list(RelName) -> @@ -455,3 +490,15 @@ to_atom(RelName) to_atom(Else) when erlang:is_atom(Else) -> Else. + +-spec merge_application_goals([application_goal()], [application_goal()]) -> [application_goal()]. +merge_application_goals(Goals, BaseGoals) -> + Goals ++ lists:foldl(fun filter_goal_by_name/2, BaseGoals, Goals). + +filter_goal_by_name(AppGoal, GoalList) when is_list(GoalList) -> + case get_goal_app_name(AppGoal) of + AppName when is_atom(AppName) -> + lists:filter(fun(Goal) -> get_goal_app_name(Goal) /= AppName end, GoalList); + _Error -> + GoalList + end. diff --git a/src/rlx_state.erl b/src/rlx_state.erl index 5488a41..cab55f6 100644 --- a/src/rlx_state.erl +++ b/src/rlx_state.erl @@ -105,7 +105,7 @@ lib_dirs=[] :: [file:name()], config_file=[] :: file:filename() | undefined, cli_args=[] :: proplists:proplist(), - goals=[] :: [rlx_depsolver:constraint()], + goals=[] :: [rlx_depsolver:raw_constraint()], providers=[] :: [providers:t()], available_apps=[] :: [rlx_app_info:t()], default_configured_release :: {rlx_release:name() | undefined, rlx_release:vsn() |undefined} | undefined, @@ -254,11 +254,11 @@ lib_dirs(#state_t{lib_dirs=LibDir}) -> add_lib_dirs(State=#state_t{lib_dirs=LibDir}, Dirs) -> State#state_t{lib_dirs=lists:umerge(lists:sort(LibDir), lists:sort(Dirs))}. --spec goals(t()) -> [rlx_depsolver:constraint()]. +-spec goals(t()) -> [rlx_depsolver:raw_constraint()]. goals(#state_t{goals=TS}) -> TS. --spec goals(t(), [rlx_depsolver:constraint()]) -> t(). +-spec goals(t(), [rlx_depsolver:raw_constraint()]) -> t(). goals(State, Goals) -> State#state_t{goals=Goals}. diff --git a/test/rlx_command_SUITE.erl b/test/rlx_command_SUITE.erl index 46664ab..8dd2da8 100644 --- a/test/rlx_command_SUITE.erl +++ b/test/rlx_command_SUITE.erl @@ -27,7 +27,8 @@ lib_expansion_case/1, lib_fail_case/1, spec_parse_fail_case/1, - config_fail_case/1]). + config_fail_case/1, + provider_case/1]). -include_lib("common_test/include/ct.hrl"). -include_lib("eunit/include/eunit.hrl"). @@ -42,7 +43,7 @@ end_per_suite(_Config) -> ok. all() -> - [normal_passing_case, lib_expansion_case, lib_fail_case, config_fail_case]. + [normal_passing_case, lib_expansion_case, lib_fail_case, config_fail_case, provider_case]. normal_passing_case(Config) -> DataDir = filename:join(proplists:get_value(priv_dir, Config), ?MODULE), @@ -111,3 +112,12 @@ config_fail_case(_Config) -> {ok, {Opts, Targets}} = getopt:parse(relx:opt_spec_list(), CmdLine), ?assertMatch({error, {_, {invalid_config_file, ConfigFile}}}, rlx_cmd_args:args2state(Opts, Targets)). + +provider_case(_Config) -> + CmdLine = ["--provider", "relx_provider_1", + "--provider", "relx_provider_2"], + {ok, {Opts, Targets}} = getopt:parse(relx:opt_spec_list(), CmdLine), + {ok, State} = rlx_cmd_args:args2state(Opts, Targets), + ?assertEqual( + [relx_provider_1, relx_provider_2], + proplists:get_value(add_providers, rlx_state:cli_args(State))). diff --git a/test/rlx_depsolver_tester.erl b/test/rlx_depsolver_tester.erl index 9defd91..b3bc146 100644 --- a/test/rlx_depsolver_tester.erl +++ b/test/rlx_depsolver_tester.erl @@ -1,5 +1,5 @@ %% -*- erlang-indent-level: 4; indent-tabs-mode: nil; fill-column: 92 -*- -%% ex: ts=4 sx=4 et +%% ex: ts=4 sw=4 et %%------------------------------------------------------------------- %% %% Copyright 2012 Opscode, Inc. All Rights Reserved. @@ -49,153 +49,167 @@ run_log(FileName) -> run_log_file(Device). data1_test() -> - ExpectedResult = versionify([{"app6","0.0.1"}, - {"dep_pkg13","0.0.2"}, - {"app13","0.0.1"}, - {"dep_pkg2","0.0.5"}, - {"dep_pkg1","0.0.2"}, + ExpectedResult = versionify([ + {"app9","0.0.1"}, {"dep_pkg7","0.1.2"}, - {"app9","0.0.1"}]), + {"dep_pkg1","0.0.2"}, + {"dep_pkg2","0.0.5"}, + {"app13","0.0.1"}, + {"dep_pkg13","0.0.2"}, + {"app6","0.0.1"} + ]), ?assertMatch({ok, ExpectedResult}, run_data(fix_rebar_brokenness("data1.txt"))). data2_test() -> - ExpectedResult = versionify([{"app18","0.0.1"}, - {"app4","0.0.7"}, - {"app1","0.0.1"}, - {"app6","0.0.1"}, - {"dep_pkg13","0.0.2"}, - {"app13","0.0.1"}, - {"dep_pkg5","0.0.3"}, - {"dep_pkg1","0.0.2"}, - {"dep_pkg2","0.0.5"}, - {"dep_pkg7","0.1.2"}, + ExpectedResult = versionify([ + {"dep_pkg16","1.0.2"}, {"app9","0.0.1"}, - {"dep_pkg16","1.0.2"}]), + {"dep_pkg7","0.1.2"}, + {"dep_pkg2","0.0.5"}, + {"dep_pkg1","0.0.2"}, + {"dep_pkg5","0.0.3"}, + {"app13","0.0.1"}, + {"dep_pkg13","0.0.2"}, + {"app6","0.0.1"}, + {"app1","0.0.1"}, + {"app4","0.0.7"}, + {"app18","0.0.1"} + ]), ?assertMatch({ok, ExpectedResult}, run_data(fix_rebar_brokenness("data2.txt"))). - + data3_test() -> - ExpectedResult = versionify([{"app68","0.0.1"}, - {"app58","0.0.1"}, - {"app48","0.0.7"}, - {"app38","0.0.1"}, - {"app28","0.0.1"}, - {"app18","0.0.1"}, - {"app4","0.0.7"}, - {"app1","0.0.1"}, - {"app6","0.0.1"}, - {"dep_pkg13","0.0.2"}, - {"app13","0.0.1"}, - {"dep_pkg5","0.0.3"}, - {"dep_pkg1","0.0.2"}, - {"dep_pkg2","0.0.5"}, - {"dep_pkg7","0.1.2"}, + ExpectedResult = versionify([ + {"dep_pkg16","1.0.2"}, {"app9","0.0.1"}, - {"dep_pkg16","1.0.2"}]), + {"dep_pkg7","0.1.2"}, + {"dep_pkg2","0.0.5"}, + {"dep_pkg1","0.0.2"}, + {"dep_pkg5","0.0.3"}, + {"app13","0.0.1"}, + {"dep_pkg13","0.0.2"}, + {"app6","0.0.1"}, + {"app1","0.0.1"}, + {"app4","0.0.7"}, + {"app18","0.0.1"}, + {"app28","0.0.1"}, + {"app38","0.0.1"}, + {"app48","0.0.7"}, + {"app58","0.0.1"}, + {"app68","0.0.1"} + ]), ?assertMatch({ok,ExpectedResult}, run_data(fix_rebar_brokenness("data3.txt"))). data4_test() -> - ExpectedResult = versionify([{"dep_pkg20","0.0.2"}, - {"app78","0.0.1"}, - {"app68","0.0.1"}, - {"app58","0.0.1"}, - {"app48","0.0.7"}, - {"app38","0.0.1"}, - {"app28","0.0.1"}, - {"app18","0.0.1"}, - {"app4","0.0.7"}, - {"app1","0.0.1"}, - {"app6","0.0.1"}, - {"dep_pkg13","0.0.2"}, - {"app13","0.0.1"}, - {"dep_pkg5","0.0.3"}, - {"dep_pkg1","0.0.2"}, - {"dep_pkg2","0.0.5"}, - {"dep_pkg7","0.1.2"}, + ExpectedResult = versionify([ + {"dep_pkg16","1.0.2"}, {"app9","0.0.1"}, - {"dep_pkg16","1.0.2"}]), + {"dep_pkg7","0.1.2"}, + {"dep_pkg2","0.0.5"}, + {"dep_pkg1","0.0.2"}, + {"dep_pkg5","0.0.3"}, + {"app13","0.0.1"}, + {"dep_pkg13","0.0.2"}, + {"app6","0.0.1"}, + {"app1","0.0.1"}, + {"app4","0.0.7"}, + {"app18","0.0.1"}, + {"app28","0.0.1"}, + {"app38","0.0.1"}, + {"app48","0.0.7"}, + {"app58","0.0.1"}, + {"app68","0.0.1"}, + {"app78","0.0.1"}, + {"dep_pkg20","0.0.2"} + ]), ?assertMatch({ok, ExpectedResult}, run_data(fix_rebar_brokenness("data4.txt"))). data5_test() -> - ExpectedResult = versionify([{"dep_pkg14","0.0.2"}, - {"dep_pkg22","0.0.2"}, - {"dep_pkg20","0.0.2"}, - {"app78","0.0.1"}, - {"app68","0.0.1"}, - {"app58","0.0.1"}, - {"app48","0.0.7"}, - {"app38","0.0.1"}, - {"app28","0.0.1"}, - {"app18","0.0.1"}, - {"app4","0.0.7"}, - {"app1","0.0.1"}, - {"app6","0.0.1"}, - {"dep_pkg13","0.0.2"}, - {"app13","0.0.1"}, - {"dep_pkg5","0.0.3"}, - {"dep_pkg1","0.0.2"}, - {"dep_pkg2","0.0.5"}, - {"dep_pkg7","0.1.2"}, + ExpectedResult = versionify([ + {"dep_pkg16","1.0.2"}, {"app9","0.0.1"}, - {"dep_pkg16","1.0.2"}]), + {"dep_pkg7","0.1.2"}, + {"dep_pkg2","0.0.5"}, + {"dep_pkg1","0.0.2"}, + {"dep_pkg5","0.0.3"}, + {"app13","0.0.1"}, + {"dep_pkg13","0.0.2"}, + {"app6","0.0.1"}, + {"app1","0.0.1"}, + {"app4","0.0.7"}, + {"app18","0.0.1"}, + {"app28","0.0.1"}, + {"app38","0.0.1"}, + {"app48","0.0.7"}, + {"app58","0.0.1"}, + {"app68","0.0.1"}, + {"app78","0.0.1"}, + {"dep_pkg20","0.0.2"}, + {"dep_pkg22","0.0.2"}, + {"dep_pkg14","0.0.2"} + ]), ?assertMatch({ok, ExpectedResult}, run_data(fix_rebar_brokenness("data5.txt"))). data6_test() -> - ExpectedResult = versionify([{"app108","0.0.1"}, - {"app98","0.0.1"}, - {"app88","0.0.1"}, - {"dep_pkg14","0.0.2"}, - {"dep_pkg22","0.0.2"}, - {"dep_pkg20","0.0.2"}, - {"app78","0.0.1"}, - {"app68","0.0.1"}, - {"app58","0.0.1"}, - {"app48","0.0.7"}, - {"app38","0.0.1"}, - {"app28","0.0.1"}, - {"app18","0.0.1"}, - {"app4","0.0.7"}, - {"app1","0.0.1"}, - {"app6","0.0.1"}, - {"dep_pkg13","0.0.2"}, - {"app13","0.0.1"}, - {"dep_pkg5","0.0.3"}, - {"dep_pkg1","0.0.2"}, - {"dep_pkg2","0.0.5"}, - {"dep_pkg7","0.1.2"}, + ExpectedResult = versionify([ + {"dep_pkg16","1.0.2"}, {"app9","0.0.1"}, - {"dep_pkg16","1.0.2"}]), + {"dep_pkg7","0.1.2"}, + {"dep_pkg2","0.0.5"}, + {"dep_pkg1","0.0.2"}, + {"dep_pkg5","0.0.3"}, + {"app13","0.0.1"}, + {"dep_pkg13","0.0.2"}, + {"app6","0.0.1"}, + {"app1","0.0.1"}, + {"app4","0.0.7"}, + {"app18","0.0.1"}, + {"app28","0.0.1"}, + {"app38","0.0.1"}, + {"app48","0.0.7"}, + {"app58","0.0.1"}, + {"app68","0.0.1"}, + {"app78","0.0.1"}, + {"dep_pkg20","0.0.2"}, + {"dep_pkg22","0.0.2"}, + {"dep_pkg14","0.0.2"}, + {"app88","0.0.1"}, + {"app98","0.0.1"}, + {"app108","0.0.1"} + ]), ?assertMatch({ok, ExpectedResult}, run_data(fix_rebar_brokenness("data6.txt"))). log_07be9e47_test() -> Data = run_log(fix_rebar_brokenness("log-07be9e47-6f42-4a5d-b8b5-1d2eae1ad83b.txt")), - ExpectedResult = versionify([{"0","0"}, - {"1","0"}, - {"3","0"}, - {"4","0"}, - {"5","0"}, - {"6","0"}, - {"7","0"}, - {"8","0"}, - {"9","0"}, - {"10","0"}, - {"11","0"}, - {"12","0"}, - {"13","0"}, - {"14","0"}, - {"15","0"}, - {"16","0"}, - {"18","0"}, - {"19","0"}, - {"21","0"}, - {"22","0"}, - {"23","0"}, + ExpectedResult = versionify([ + {"25","0"}, {"24","0"}, - {"25","0"}]), + {"23","0"}, + {"22","0"}, + {"21","0"}, + {"19","0"}, + {"18","0"}, + {"16","0"}, + {"15","0"}, + {"14","0"}, + {"13","0"}, + {"12","0"}, + {"11","0"}, + {"10","0"}, + {"9","0"}, + {"8","0"}, + {"7","0"}, + {"6","0"}, + {"5","0"}, + {"4","0"}, + {"3","0"}, + {"1","0"}, + {"0","0"} + ]), ?assertMatch({ok, ExpectedResult}, Data). @@ -206,144 +220,152 @@ log_183998c1_test() -> log_311a15e7_test() -> {ok, Data} = run_log(fix_rebar_brokenness("log-311a15e7-3378-4c5b-beb7-86a1b9cf0ea9.txt")), - ExpectedResult = lists:sort(versionify([{"45", "22"}, - {"40","1"}, - {"3","5"}, - {"9","0"}, - {"8","0"}, - {"7","0"}, - {"6","2"}, - {"1","5"}, - {"0","2"}, - {"61","1"}, - {"60","0"}, - {"35","4"}, - {"39","0"}, - {"38","2"}, - {"37","2"}, - {"36","3"}, - {"32","24"}, - {"30","0"}, - {"19","1"}, - {"18","0"}, - {"17","2"}, - {"16","0"}, - {"15","0"}, - {"14","1"}, - {"13","0"}, - {"12","1"}, - {"11","0"}, - {"10","1"}, - {"59","0"}, - {"58","1"}, - {"57","0"}, - {"56","0"}, - {"55","4"}, - {"29","2"}, - {"27","2"}, - {"26","0"}, - {"25","5"}, - {"24","3"}, - {"23","1"}, - {"22","3"}, + ExpectedResult = lists:sort(versionify([ + {"20","0"}, {"21","2"}, - {"20","0"}])), + {"22","3"}, + {"23","1"}, + {"24","3"}, + {"25","5"}, + {"26","0"}, + {"27","2"}, + {"29","2"}, + {"55","4"}, + {"56","0"}, + {"57","0"}, + {"58","1"}, + {"59","0"}, + {"10","1"}, + {"11","0"}, + {"12","1"}, + {"13","0"}, + {"14","1"}, + {"15","0"}, + {"16","0"}, + {"17","2"}, + {"18","0"}, + {"19","1"}, + {"30","0"}, + {"32","24"}, + {"36","3"}, + {"37","2"}, + {"38","2"}, + {"39","0"}, + {"35","4"}, + {"60","0"}, + {"61","1"}, + {"0","2"}, + {"1","5"}, + {"6","2"}, + {"7","0"}, + {"8","0"}, + {"9","0"}, + {"3","5"}, + {"40","1"}, + {"45", "22"} + ])), ?assertMatch(ExpectedResult, lists:sort(Data)). log_382cfe5b_test() -> {ok, Data} = run_log(fix_rebar_brokenness("log-382cfe5b-0ac2-48b8-83d1-717cb4620990.txt")), - ExpectedResult = lists:sort(versionify([{"18","0"}, - {"17","0"}, - {"15","1"}, - {"14","0"}, - {"10","0"}, - {"7","0"}, - {"6","0"}, - {"5","0"}, - {"4","0"}, - {"3","0"}, - {"2","1"}, + ExpectedResult = lists:sort(versionify([ + {"0","0"}, {"1","0"}, - {"0","0"}])), + {"2","1"}, + {"3","0"}, + {"4","0"}, + {"5","0"}, + {"6","0"}, + {"7","0"}, + {"10","0"}, + {"14","0"}, + {"15","1"}, + {"17","0"}, + {"18","0"} + ])), ?assertMatch(ExpectedResult, lists:sort(Data)). log_d3564ef6_test() -> {ok, Data} = run_log(fix_rebar_brokenness("log-d3564ef6-6437-41e7-90b6-dbdb849551a6_mod.txt")), - ExpectedResult = lists:sort(versionify([{"57","5"}, - {"56","3"}, - {"55","4"}, - {"54","0"}, - {"53","1"}, - {"82","0"}, - {"81","0"}, - {"80","1"}, - {"29","0"}, - {"28","5"}, - {"27","3"}, - {"26","1"}, - {"25","3"}, - {"24","2"}, - {"23","0"}, - {"22","1"}, - {"21","0"}, - {"20","2"}, - {"75","32"}, - {"79","2"}, - {"78","4"}, - {"74","7"}, - {"73","11"}, - {"72","0"}, - {"70","1"}, - {"47","4"}, - {"45","1"}, - {"44","1"}, - {"43","7"}, - {"42","1"}, - {"41","2"}, - {"40","2"}, - {"19","0"}, - {"18","0"}, - {"17","1"}, - {"16","0"}, - {"15","1"}, - {"14","0"}, - {"13","1"}, - {"12","0"}, - {"11","0"}, - {"10","0"}, - {"9","2"}, - {"4","5"}, - {"3","2"}, - {"0","3"}, - {"69","0"}, - {"68","1"}, - {"67","7"}, - {"39","3"}, - {"35","24"}, - {"33","0"}, + ExpectedResult = lists:sort(versionify([ + {"30","2"}, {"32","2"}, - {"30","2"}])), + {"33","0"}, + {"35","24"}, + {"39","3"}, + {"67","7"}, + {"68","1"}, + {"69","0"}, + {"0","3"}, + {"3","2"}, + {"4","5"}, + {"9","2"}, + {"10","0"}, + {"11","0"}, + {"12","0"}, + {"13","1"}, + {"14","0"}, + {"15","1"}, + {"16","0"}, + {"17","1"}, + {"18","0"}, + {"19","0"}, + {"40","2"}, + {"41","2"}, + {"42","1"}, + {"43","7"}, + {"44","1"}, + {"45","1"}, + {"47","4"}, + {"70","1"}, + {"72","0"}, + {"73","11"}, + {"74","7"}, + {"78","4"}, + {"79","2"}, + {"75","32"}, + {"20","2"}, + {"21","0"}, + {"22","1"}, + {"23","0"}, + {"24","2"}, + {"25","3"}, + {"26","1"}, + {"27","3"}, + {"28","5"}, + {"29","0"}, + {"80","1"}, + {"81","0"}, + {"82","0"}, + {"53","1"}, + {"54","0"}, + {"55","4"}, + {"56","3"}, + {"57","5"} + ])), ?assertMatch(ExpectedResult, lists:sort(Data)). log_ea2d264b_test() -> {ok, Data} = run_log(fix_rebar_brokenness("log-ea2d264b-003e-4611-94ed-14efc7732083.txt")), - ExpectedResult = lists:sort(versionify([{"18","1"}, - {"17","0"}, - {"16","0"}, - {"15","0"}, - {"14","0"}, - {"13","1"}, - {"10","1"}, - {"9","1"}, - {"8","2"}, - {"6","0"}, - {"5","0"}, - {"4","0"}, - {"3","0"}, - {"2","0"}, + ExpectedResult = lists:sort(versionify([ + {"0","1"}, {"1","0"}, - {"0","1"}])), + {"2","0"}, + {"3","0"}, + {"4","0"}, + {"5","0"}, + {"6","0"}, + {"8","2"}, + {"9","1"}, + {"10","1"}, + {"13","1"}, + {"14","0"}, + {"15","0"}, + {"16","0"}, + {"17","0"}, + {"18","1"} + ])), ?assertMatch(ExpectedResult, lists:sort(Data)). %%============================================================================ diff --git a/test/rlx_depsolver_tests.erl b/test/rlx_depsolver_tests.erl index 206bad4..b1c8228 100644 --- a/test/rlx_depsolver_tests.erl +++ b/test/rlx_depsolver_tests.erl @@ -1,5 +1,5 @@ %% -*- erlang-indent-level: 4; indent-tabs-mode: nil; fill-column: 80 -*- -%% ex: ts=4 sx=4 et +%% ex: ts=4 sw=4 et %% %%------------------------------------------------------------------- %% Copyright 2012 Opscode, Inc. All Rights Reserved. diff --git a/test/rlx_extended_bin_SUITE.erl b/test/rlx_extended_bin_SUITE.erl index 8444cb4..710d2c4 100644 --- a/test/rlx_extended_bin_SUITE.erl +++ b/test/rlx_extended_bin_SUITE.erl @@ -1298,10 +1298,7 @@ custom_start_script_hooks(Config) -> ]} ]}, {mkdir, "scripts"}, - {overlay, [{copy, "./pre_start", "bin/hooks/pre_start"}, - {copy, "./post_start", "bin/hooks/post_start"}, - {copy, "./pre_stop", "bin/hooks/pre_stop"}, - {copy, "./post_stop", "bin/hooks/post_stop"}]} + {overlay, [{copy, "./{pre,post}_{start,stop}", "bin/hooks/"}]} ]), %% write the hook scripts, each of them will write an erlang term to a file diff --git a/test/rlx_prv_release_alias.erl b/test/rlx_prv_release_alias.erl new file mode 100644 index 0000000..523940c --- /dev/null +++ b/test/rlx_prv_release_alias.erl @@ -0,0 +1,27 @@ +-module(rlx_prv_release_alias). + +-behaviour(provider). + +-export([init/1, do/1, format_error/1]). + +-define(PROVIDER, test_release_alias). +-define(DEPS, [app_discover]). + +%%============================================================================ +%% API +%%============================================================================ + +-spec init(rlx_state:t()) -> {ok, rlx_state:t()}. +init(State) -> + State1 = rlx_state:add_provider(State, providers:create([{name, ?PROVIDER}, + {module, ?MODULE}, + {deps, ?DEPS}])), + {ok, State1}. + +-spec do(rlx_state:t()) -> {ok, rlx_state:t()} | relx:error(). +do(State) -> + rlx_prv_release:do(State). + +-spec format_error(ErrorDetail::term()) -> iolist(). +format_error(ErrorDetail) -> + rlx_prv_release:format_error(ErrorDetail). diff --git a/test/rlx_release_SUITE.erl b/test/rlx_release_SUITE.erl index d4a86be..a582526 100644 --- a/test/rlx_release_SUITE.erl +++ b/test/rlx_release_SUITE.erl @@ -25,7 +25,9 @@ init_per_testcase/2, all/0, providers/1, + providers_via_api_options/1, add_providers/1, + add_providers_via_api_options/1, make_release/1, make_config_release/1, make_extend_release/1, @@ -40,6 +42,7 @@ make_implicit_config_release/1, overlay_release/1, make_goalless_release/1, + make_external_goal_release/1, make_depfree_release/1, make_invalid_config_release/1, make_relup_release/1, @@ -82,19 +85,18 @@ init_per_testcase(_, Config) -> {state, State1} | Config]. all() -> - [providers, add_providers, make_release, make_config_release, - make_extend_release, make_extend_config_release, make_scriptless_release, - make_overridden_release, make_auto_skip_empty_app_release, + [providers, providers_via_api_options, add_providers, add_providers_via_api_options, + make_release, make_config_release, make_extend_release, make_extend_config_release, + make_scriptless_release, make_overridden_release, make_auto_skip_empty_app_release, make_skip_app_release, make_exclude_app_release, make_app_type_none_release, - make_implicit_config_release, make_rerun_overridden_release, - overlay_release, make_goalless_release, make_depfree_release, - make_invalid_config_release, make_relup_release, make_relup_release2, - make_one_app_top_level_release, make_dev_mode_release, make_dev_mode_template_release, - make_config_script_release, make_release_twice, make_release_twice_dev_mode, - make_erts_release, make_erts_config_release, - make_included_nodetool_release, make_not_included_nodetool_release, - make_src_release, make_excluded_src_release, make_exclude_modules_release, - make_release_with_sys_config_vm_args_src]. + make_implicit_config_release, make_rerun_overridden_release, overlay_release, + make_goalless_release, make_external_goal_release, make_depfree_release, make_invalid_config_release, + make_relup_release, make_relup_release2, make_one_app_top_level_release, + make_dev_mode_release, make_dev_mode_template_release, make_config_script_release, + make_release_twice, make_release_twice_dev_mode, make_erts_release, + make_erts_config_release, make_included_nodetool_release, + make_not_included_nodetool_release, make_src_release, make_excluded_src_release, + make_exclude_modules_release, make_release_with_sys_config_vm_args_src]. add_providers(Config) -> LibDir1 = proplists:get_value(lib1, Config), @@ -133,6 +135,52 @@ add_providers(Config) -> ?assert(lists:member({goal_app_2, "0.0.1"}, AppSpecs)), ?assert(lists:member({lib_dep_1, "0.0.1", load}, AppSpecs)). +add_providers_via_api_options(Config) -> + LibDir1 = proplists:get_value(lib1, Config), + + [(fun({Name, Vsn}) -> + rlx_test_utils:create_app(LibDir1, Name, Vsn, [kernel, stdlib], []) + end)(App) + || + App <- + [{rlx_test_utils:create_random_name("lib_app1_"), rlx_test_utils:create_random_vsn()} + || _ <- lists:seq(1, 100)]], + + rlx_test_utils:create_app(LibDir1, "goal_app_1", "0.0.1", [stdlib,kernel,non_goal_1], []), + rlx_test_utils:create_app(LibDir1, "lib_dep_1", "0.0.1", [stdlib,kernel], []), + rlx_test_utils:create_app(LibDir1, "goal_app_2", "0.0.1", [stdlib,kernel,goal_app_1,non_goal_2], []), + rlx_test_utils:create_app(LibDir1, "non_goal_1", "0.0.1", [stdlib,kernel], [lib_dep_1]), + rlx_test_utils:create_app(LibDir1, "non_goal_2", "0.0.1", [stdlib,kernel], []), + + ConfigFile = filename:join([LibDir1, "relx.config"]), + rlx_test_utils:write_config(ConfigFile, + [{release, {foo, "0.0.1"}, + [goal_app_1, + goal_app_2]}]), + OutputDir = filename:join([proplists:get_value(priv_dir, Config), + rlx_test_utils:create_random_name("relx-output")]), + {ok, Cwd} = file:get_cwd(), + Opts = [{relname, undefined}, + {relvsn, undefined}, + {goals, []}, + {overrides, []}, + {output_dir, OutputDir}, + {lib_dirs, [LibDir1]}, + {root_dir, Cwd}, + {log_level, 3}, + {config, ConfigFile}, + {add_providers, [rlx_prv_release_alias]}], + {ok, State} = relx:do(Opts, ["test_release_alias"]), + [{{foo, "0.0.1"}, Release}] = ec_dictionary:to_list(rlx_state:realized_releases(State)), + AppSpecs = rlx_release:applications(Release), + ?assert(lists:keymember(stdlib, 1, AppSpecs)), + ?assert(lists:keymember(kernel, 1, AppSpecs)), + ?assert(lists:member({non_goal_1, "0.0.1"}, AppSpecs)), + ?assert(lists:member({non_goal_2, "0.0.1"}, AppSpecs)), + ?assert(lists:member({goal_app_1, "0.0.1"}, AppSpecs)), + ?assert(lists:member({goal_app_2, "0.0.1"}, AppSpecs)), + ?assert(lists:member({lib_dep_1, "0.0.1", load}, AppSpecs)). + providers(Config) -> LibDir1 = proplists:get_value(lib1, Config), @@ -170,6 +218,52 @@ providers(Config) -> ?assert(lists:member({goal_app_2, "0.0.1"}, AppSpecs)), ?assert(lists:member({lib_dep_1, "0.0.1", load}, AppSpecs)). +providers_via_api_options(Config) -> + LibDir1 = proplists:get_value(lib1, Config), + + [(fun({Name, Vsn}) -> + rlx_test_utils:create_app(LibDir1, Name, Vsn, [kernel, stdlib], []) + end)(App) + || + App <- + [{rlx_test_utils:create_random_name("lib_app1_"), rlx_test_utils:create_random_vsn()} + || _ <- lists:seq(1, 100)]], + + rlx_test_utils:create_app(LibDir1, "goal_app_1", "0.0.1", [stdlib,kernel,non_goal_1], []), + rlx_test_utils:create_app(LibDir1, "lib_dep_1", "0.0.1", [stdlib,kernel], []), + rlx_test_utils:create_app(LibDir1, "goal_app_2", "0.0.1", [stdlib,kernel,goal_app_1,non_goal_2], []), + rlx_test_utils:create_app(LibDir1, "non_goal_1", "0.0.1", [stdlib,kernel], [lib_dep_1]), + rlx_test_utils:create_app(LibDir1, "non_goal_2", "0.0.1", [stdlib,kernel], []), + + ConfigFile = filename:join([LibDir1, "relx.config"]), + rlx_test_utils:write_config(ConfigFile, + [{release, {foo, "0.0.1"}, + [goal_app_1, + goal_app_2]}]), + OutputDir = filename:join([proplists:get_value(priv_dir, Config), + rlx_test_utils:create_random_name("relx-output")]), + {ok, Cwd} = file:get_cwd(), + Opts = [{relname, undefined}, + {relvsn, undefined}, + {goals, []}, + {overrides, []}, + {output_dir, OutputDir}, + {lib_dirs, [LibDir1]}, + {root_dir, Cwd}, + {log_level, 3}, + {config, ConfigFile}, + {providers, [rlx_prv_release_alias]}], + {ok, State} = relx:do(Opts, ["test_release_alias"]), + [{{foo, "0.0.1"}, Release}] = ec_dictionary:to_list(rlx_state:realized_releases(State)), + AppSpecs = rlx_release:applications(Release), + ?assert(lists:keymember(stdlib, 1, AppSpecs)), + ?assert(lists:keymember(kernel, 1, AppSpecs)), + ?assert(lists:member({non_goal_1, "0.0.1"}, AppSpecs)), + ?assert(lists:member({non_goal_2, "0.0.1"}, AppSpecs)), + ?assert(lists:member({goal_app_1, "0.0.1"}, AppSpecs)), + ?assert(lists:member({goal_app_2, "0.0.1"}, AppSpecs)), + ?assert(lists:member({lib_dep_1, "0.0.1", load}, AppSpecs)). + make_release(Config) -> LibDir1 = proplists:get_value(lib1, Config), @@ -671,7 +765,7 @@ overlay_release(Config) -> {mkdir, "{{target_dir}}/{{var_list_dir}}"}, {copy, OverlayVars1, "{{target_dir}}/{{foo_dir}}/vars1.config"}, - {copy, OverlayVars1, + {copy, filename:join([LibDir1, "vars1*.config"]), "{{target_dir}}/{{yahoo}}/"}, {link, OverlayVars4, "{{target_dir}}/{{yahoo}}/vars4.config"}, @@ -784,6 +878,37 @@ make_goalless_release(Config) -> relx:do(undefined, undefined, [], [LibDir1], 3, OutputDir, ConfigFile)). +make_external_goal_release(Config) -> + LibDir1 = proplists:get_value(lib1, Config), + + rlx_test_utils:create_app(LibDir1, "goal_app_1", "0.0.1", [stdlib,kernel,non_goal_1], []), + rlx_test_utils:create_app(LibDir1, "lib_dep_1", "0.0.1", [], []), + rlx_test_utils:create_app(LibDir1, "goal_app_2", "0.0.1", [stdlib,kernel,goal_app_1,non_goal_2], []), + rlx_test_utils:create_app(LibDir1, "non_goal_1", "0.0.1", [stdlib,kernel], [lib_dep_1]), + rlx_test_utils:create_app(LibDir1, "non_goal_2", "0.0.1", [stdlib,kernel], []), + + ConfigFile = filename:join([LibDir1, "relx.config"]), + + ConfigFile = filename:join([LibDir1, "relx.config"]), + rlx_test_utils:write_config(ConfigFile, + [{goals, [{goal_app_2, "0.0.1"}]}, + {release, {foo, "0.0.1"}, + [goal_app_1]}]), + OutputDir = filename:join([proplists:get_value(priv_dir, Config), + rlx_test_utils:create_random_name("relx-output")]), + {ok, State} = relx:do(undefined, undefined, [], [LibDir1], 3, + OutputDir, ConfigFile), + [{{foo, "0.0.1"}, Release}] = ec_dictionary:to_list(rlx_state:realized_releases(State)), + AppSpecs = rlx_release:applications(Release), + ?assert(lists:keymember(stdlib, 1, AppSpecs)), + ?assert(lists:keymember(kernel, 1, AppSpecs)), + ?assert(lists:member({non_goal_1, "0.0.1"}, AppSpecs)), + ?assert(lists:member({non_goal_2, "0.0.1"}, AppSpecs)), + ?assert(lists:member({goal_app_1, "0.0.1"}, AppSpecs)), + ?assert(lists:member({goal_app_2, "0.0.1"}, AppSpecs)), + ?assert(lists:member({lib_dep_1, "0.0.1", load}, AppSpecs)). + + make_depfree_release(Config) -> LibDir1 = proplists:get_value(lib1, Config), |