diff options
Diffstat (limited to 'src/rebar_app_info.erl')
-rw-r--r-- | src/rebar_app_info.erl | 240 |
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). |