aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTristan Sloughter <[email protected]>2019-05-20 10:29:09 -0600
committerGitHub <[email protected]>2019-05-20 10:29:09 -0600
commit407b0250ce73c8e2071ea2526e01ecfaf4fe841d (patch)
tree097769798d2d6d00fe3e2ff8a17a04034bf52a8f
parentcca2a6cade4d97b5b36ca7c055d25677860bdc5d (diff)
parent670a602f657eeb6ac17589407df8e43cf4d1be89 (diff)
downloadrelx-407b0250ce73c8e2071ea2526e01ecfaf4fe841d.tar.gz
relx-407b0250ce73c8e2071ea2526e01ecfaf4fe841d.tar.bz2
relx-407b0250ce73c8e2071ea2526e01ecfaf4fe841d.zip
Merge pull request #668 from ElectronicRU/goals_option
Make {goals, ...} option add goals to release depsolver.
-rw-r--r--src/rlx_config.erl42
-rw-r--r--src/rlx_depsolver.erl83
-rw-r--r--src/rlx_prv_release.erl8
-rw-r--r--src/rlx_release.erl87
-rw-r--r--src/rlx_state.erl6
-rw-r--r--test/rlx_release_SUITE.erl34
6 files changed, 189 insertions, 71 deletions
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 08b81a3..0bde8c7 100644
--- a/src/rlx_depsolver.erl
+++ b/src/rlx_depsolver.erl
@@ -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_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 a183043..e22fdd4 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()]) ->
@@ -364,6 +370,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} ->
@@ -400,12 +414,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}).
@@ -423,22 +436,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) ->
@@ -446,3 +481,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_release_SUITE.erl b/test/rlx_release_SUITE.erl
index d9685ea..3d77732 100644
--- a/test/rlx_release_SUITE.erl
+++ b/test/rlx_release_SUITE.erl
@@ -42,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,
@@ -89,7 +90,7 @@ all() ->
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_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,
@@ -877,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),