summaryrefslogtreecommitdiff
path: root/src/rebar_app_info.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/rebar_app_info.erl')
-rw-r--r--src/rebar_app_info.erl240
1 files changed, 237 insertions, 3 deletions
diff --git a/src/rebar_app_info.erl b/src/rebar_app_info.erl
index bb5104e..ef8f69d 100644
--- a/src/rebar_app_info.erl
+++ b/src/rebar_app_info.erl
@@ -5,6 +5,7 @@
new/3,
new/4,
new/5,
+ update_opts/3,
discover/1,
name/1,
name/2,
@@ -35,6 +36,13 @@
dir/2,
out_dir/1,
out_dir/2,
+ default/1,
+ default/2,
+ opts/1,
+ opts/2,
+ get/2,
+ get/3,
+ set/3,
resource_type/1,
resource_type/2,
source/1,
@@ -47,9 +55,20 @@
is_checkout/1,
is_checkout/2,
valid/1,
- valid/2]).
+ valid/2,
+
+ has_all_artifacts/1,
+
+ apply_overrides/2,
+ add_to_profile/3,
+ apply_profiles/2,
+ deduplicate/1,
+ do_deduplicate/2,
+ merge_opts/3,
+ merge_opts/2]).
-include("rebar.hrl").
+-include_lib("providers/include/providers.hrl").
-export_type([t/0]).
@@ -64,6 +83,8 @@
applications=[] :: list(),
deps=[] :: list(),
profiles=[default] :: [atom()],
+ default=dict:new() :: rebar_dict(),
+ opts=dict:new() :: rebar_dict(),
dep_level=0 :: integer(),
dir :: file:name(),
out_dir :: file:name(),
@@ -125,6 +146,36 @@ new(Parent, AppName, Vsn, Dir, Deps) ->
out_dir=ec_cnv:to_list(Dir),
deps=Deps}}.
+update_opts(AppInfo, Opts, Config) ->
+ LockDeps = case resource_type(AppInfo) of
+ pkg ->
+ Deps = deps(AppInfo),
+ [{{locks, default}, Deps}, {{deps, default}, Deps}];
+ _ ->
+ deps_from_config(dir(AppInfo), Config)
+ end,
+
+ Plugins = proplists:get_value(plugins, Config, []),
+ Terms = LockDeps++[{{plugins, default}, Plugins} | Config],
+ true = rebar_config:verify_config_format(Terms),
+ LocalOpts = dict:from_list(Terms),
+
+ NewOpts = merge_opts(LocalOpts, Opts),
+
+ AppInfo#app_info_t{opts=NewOpts
+ ,default=NewOpts}.
+
+deps_from_config(Dir, Config) ->
+ case rebar_config:consult_lock_file(filename:join(Dir, ?LOCK_FILE)) of
+ [D] ->
+ %% We want the top level deps only from the lock file.
+ %% This ensures deterministic overrides for configs.
+ Deps = [X || X <- D, element(3, X) =:= 0],
+ [{{locks, default}, D}, {{deps, default}, Deps}];
+ _ ->
+ [{{deps, default}, proplists:get_value(deps, Config, [])}]
+ end.
+
%% @doc discover a complete version of the app info with all fields set.
-spec discover(file:filename_all()) -> {ok, t()} | not_found.
discover(Dir) ->
@@ -151,6 +202,34 @@ config(#app_info_t{config=Config}) ->
config(AppInfo=#app_info_t{}, Config) ->
AppInfo#app_info_t{config=Config}.
+opts(#app_info_t{opts=Opts}) ->
+ Opts.
+
+opts(AppInfo, Opts) ->
+ AppInfo#app_info_t{opts=Opts}.
+
+default(#app_info_t{default=Default}) ->
+ Default.
+
+default(AppInfo, Default) ->
+ AppInfo#app_info_t{default=Default}.
+
+get(AppInfo, Key) ->
+ {ok, Value} = dict:find(Key, AppInfo#app_info_t.opts),
+ Value.
+
+get(AppInfo, Key, Default) ->
+ case dict:find(Key, AppInfo#app_info_t.opts) of
+ {ok, Value} ->
+ Value;
+ error ->
+ Default
+ end.
+
+-spec set(t(), any(), any()) -> t().
+set(AppInfo=#app_info_t{opts=Opts}, Key, Value) ->
+ AppInfo#app_info_t{opts = dict:store(Key, Value, Opts)}.
+
-spec app_file_src(t()) -> file:filename_all() | undefined.
app_file_src(#app_info_t{app_file_src=undefined, dir=Dir, name=Name}) ->
AppFileSrc = filename:join([ec_cnv:to_list(Dir), "src", ec_cnv:to_list(Name)++".app.src"]),
@@ -338,9 +417,9 @@ is_checkout(#app_info_t{is_checkout=IsCheckout}) ->
IsCheckout.
-spec valid(t()) -> boolean().
-valid(AppInfo=#app_info_t{valid=undefined, state=State}) ->
+valid(AppInfo=#app_info_t{valid=undefined}) ->
case rebar_app_utils:validate_application_info(AppInfo) =:= true
- andalso rebar_state:has_all_artifacts(State) =:= true of
+ andalso has_all_artifacts(AppInfo) =:= true of
true ->
true;
_ ->
@@ -352,3 +431,158 @@ valid(#app_info_t{valid=Valid}) ->
-spec valid(t(), boolean()) -> t().
valid(AppInfo=#app_info_t{}, Valid) ->
AppInfo#app_info_t{valid=Valid}.
+
+-spec has_all_artifacts(#app_info_t{}) -> true | {false, file:filename()}.
+has_all_artifacts(AppInfo) ->
+ Artifacts = rebar_app_info:get(AppInfo, artifacts, []),
+ Dir = dir(AppInfo),
+ all(Dir, Artifacts).
+
+all(_, []) ->
+ true;
+all(Dir, [File|Artifacts]) ->
+ case filelib:is_regular(filename:join(Dir, File)) of
+ false ->
+ ?DEBUG("Missing artifact ~s", [filename:join(Dir, File)]),
+ {false, File};
+ true ->
+ all(Dir, Artifacts)
+ end.
+
+%%%%%
+
+apply_overrides(AppInfo, Name) ->
+ Overrides = rebar_app_info:get(AppInfo, overrides, []),
+ %Name = binary_to_atom(AppName, utf8),
+
+ %% Inefficient. We want the order we get here though.
+ AppInfo1 = lists:foldl(fun({override, O}, AppInfoAcc) ->
+ lists:foldl(fun({deps, Value}, AppInfoAcc1) ->
+ rebar_app_info:set(AppInfoAcc1, {deps,default}, Value);
+ ({Key, Value}, AppInfoAcc1) ->
+ rebar_app_info:set(AppInfoAcc1, Key, Value)
+ end, AppInfoAcc, O);
+ (_, AppInfoAcc) ->
+ AppInfoAcc
+ end, AppInfo, Overrides),
+
+ AppInfo2 = lists:foldl(fun({override, N, O}, AppInfoAcc) when N =:= Name ->
+ lists:foldl(fun({deps, Value}, AppInfoAcc1) ->
+ rebar_app_info:set(AppInfoAcc1, {deps,default}, Value);
+ ({Key, Value}, AppInfoAcc1) ->
+ rebar_app_info:set(AppInfoAcc1, Key, Value)
+ end, AppInfoAcc, O);
+ (_, AppInfoAcc) ->
+ AppInfoAcc
+ end, AppInfo1, Overrides),
+
+ AppInfo3 = lists:foldl(fun({add, N, O}, AppInfoAcc) when N =:= Name ->
+ lists:foldl(fun({deps, Value}, AppInfoAcc1) ->
+ OldValue = rebar_app_info:get(AppInfoAcc1, {deps,default}, []),
+ rebar_app_info:set(AppInfoAcc1, {deps,default}, Value++OldValue);
+ ({Key, Value}, AppInfoAcc1) ->
+ OldValue = rebar_app_info:get(AppInfoAcc1, Key, []),
+ rebar_app_info:set(AppInfoAcc1, Key, Value++OldValue)
+ end, AppInfoAcc, O);
+ (_, AppInfoAcc) ->
+ AppInfoAcc
+ end, AppInfo2, Overrides),
+
+ Opts = opts(AppInfo3),
+ AppInfo3#app_info_t{default=Opts}.
+
+add_to_profile(AppInfo, Profile, KVs) when is_atom(Profile), is_list(KVs) ->
+ Profiles = rebar_app_info:get(AppInfo, profiles, []),
+ ProfileOpts = dict:from_list(proplists:get_value(Profile, Profiles, [])),
+ NewOpts = merge_opts(Profile, dict:from_list(KVs), ProfileOpts),
+ NewProfiles = [{Profile, dict:to_list(NewOpts)}|lists:keydelete(Profile, 1, Profiles)],
+ rebar_app_info:set(AppInfo, profiles, NewProfiles).
+
+apply_profiles(AppInfo, Profile) when not is_list(Profile) ->
+ apply_profiles(AppInfo, [Profile]);
+apply_profiles(AppInfo, [default]) ->
+ AppInfo;
+apply_profiles(AppInfo=#app_info_t{default = Defaults, profiles=CurrentProfiles}, Profiles) ->
+ AppliedProfiles = case Profiles of
+ %% Head of list global profile is special, only for use by rebar3
+ %% It does not clash if a user does `rebar3 as global...` but when
+ %% it is the head we must make sure not to prepend `default`
+ [global | _] ->
+ Profiles;
+ _ ->
+ deduplicate(CurrentProfiles ++ Profiles)
+ end,
+
+ ConfigProfiles = rebar_app_info:get(AppInfo, profiles, []),
+
+ NewOpts =
+ lists:foldl(fun(default, OptsAcc) ->
+ OptsAcc;
+ (Profile, OptsAcc) ->
+ case proplists:get_value(Profile, ConfigProfiles, []) of
+ OptsList when is_list(OptsList) ->
+ ProfileOpts = dict:from_list(OptsList),
+ merge_opts(Profile, ProfileOpts, OptsAcc);
+ Other ->
+ throw(?PRV_ERROR({profile_not_list, Profile, Other}))
+ end
+ end, Defaults, AppliedProfiles),
+ AppInfo#app_info_t{profiles = AppliedProfiles, opts=NewOpts}.
+
+deduplicate(Profiles) ->
+ do_deduplicate(lists:reverse(Profiles), []).
+
+do_deduplicate([], Acc) ->
+ Acc;
+do_deduplicate([Head | Rest], Acc) ->
+ case lists:member(Head, Acc) of
+ true -> do_deduplicate(Rest, Acc);
+ false -> do_deduplicate(Rest, [Head | Acc])
+ end.
+
+merge_opts(Profile, NewOpts, OldOpts) ->
+ Opts = merge_opts(NewOpts, OldOpts),
+
+ Opts2 = case dict:find(plugins, NewOpts) of
+ {ok, Value} ->
+ dict:store({plugins, Profile}, Value, Opts);
+ error ->
+ Opts
+ end,
+
+ case dict:find(deps, NewOpts) of
+ {ok, Value2} ->
+ dict:store({deps, Profile}, Value2, Opts2);
+ error ->
+ Opts2
+ end.
+
+merge_opts(NewOpts, OldOpts) ->
+ dict:merge(fun(deps, _NewValue, OldValue) ->
+ OldValue;
+ ({deps, _}, NewValue, _OldValue) ->
+ NewValue;
+ (plugins, NewValue, _OldValue) ->
+ NewValue;
+ ({plugins, _}, NewValue, _OldValue) ->
+ NewValue;
+ (profiles, NewValue, OldValue) ->
+ dict:to_list(merge_opts(dict:from_list(NewValue), dict:from_list(OldValue)));
+ (_Key, NewValue, OldValue) when is_list(NewValue) ->
+ case io_lib:printable_list(NewValue) of
+ true when NewValue =:= [] ->
+ case io_lib:printable_list(OldValue) of
+ true ->
+ NewValue;
+ false ->
+ OldValue
+ end;
+ true ->
+ NewValue;
+ false ->
+ rebar_utils:tup_umerge(rebar_utils:tup_sort(NewValue)
+ ,rebar_utils:tup_sort(OldValue))
+ end;
+ (_Key, NewValue, _OldValue) ->
+ NewValue
+ end, NewOpts, OldOpts).