diff options
Diffstat (limited to 'src/rebar_app_info.erl')
-rw-r--r-- | src/rebar_app_info.erl | 192 |
1 files changed, 159 insertions, 33 deletions
diff --git a/src/rebar_app_info.erl b/src/rebar_app_info.erl index bb5104e..92706f3 100644 --- a/src/rebar_app_info.erl +++ b/src/rebar_app_info.erl @@ -1,15 +1,15 @@ -module(rebar_app_info). --export([new/1, +-export([new/0, + new/1, new/2, new/3, new/4, new/5, + update_opts/3, discover/1, name/1, name/2, - config/1, - config/2, app_file_src/1, app_file_src/2, app_file_src_script/1, @@ -35,21 +35,35 @@ 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, source/2, - state/1, - state/2, - state_or_new/2, is_lock/1, is_lock/2, is_checkout/1, is_checkout/2, valid/1, - valid/2]). + valid/2, + + verify_otp_vsn/1, + has_all_artifacts/1, + + apply_overrides/2, + add_to_profile/3, + apply_profiles/2, + deduplicate/1, + do_deduplicate/2]). -include("rebar.hrl"). +-include_lib("providers/include/providers.hrl"). -export_type([t/0]). @@ -57,19 +71,19 @@ app_file_src :: file:filename_all() | undefined, app_file_src_script:: file:filename_all() | undefined, app_file :: file:filename_all() | undefined, - config :: rebar_state:t() | undefined, original_vsn :: binary() | string() | undefined, parent=root :: binary() | root, app_details=[] :: list(), 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(), resource_type :: pkg | src, source :: string() | tuple() | undefined, - state :: rebar_state:t() | undefined, is_lock=false :: boolean(), is_checkout=false :: boolean(), valid :: boolean()}). @@ -84,6 +98,10 @@ %% ============================================================================ %% @doc Build a new, empty, app info value. This is not of a lot of use and you %% probably wont be doing this much. +-spec new() -> t(). +new() -> + #app_info_t{}. + -spec new(atom() | binary() | string()) -> {ok, t()}. new(AppName) -> @@ -125,6 +143,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 = rebar_opts: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) -> @@ -143,13 +191,33 @@ name(#app_info_t{name=Name}) -> name(AppInfo=#app_info_t{}, AppName) -> AppInfo#app_info_t{name=ec_cnv:to_binary(AppName)}. --spec config(t()) -> rebar_state:t(). -config(#app_info_t{config=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 config(t(), rebar_state:t()) -> t(). -config(AppInfo=#app_info_t{}, Config) -> - AppInfo#app_info_t{config=Config}. +-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}) -> @@ -305,22 +373,6 @@ source(AppInfo=#app_info_t{}, Source) -> source(#app_info_t{source=Source}) -> Source. --spec state(t(), rebar_state:t() | undefined) -> t(). -state(AppInfo=#app_info_t{}, State) -> - AppInfo#app_info_t{state=State}. - --spec state(t()) -> rebar_state:t() | undefined. -state(#app_info_t{state=State}) -> - State. - --spec state_or_new(rebar_state:t(), t()) -> rebar_state:t(). -state_or_new(State, AppInfo=#app_info_t{state=undefined}) -> - AppDir = dir(AppInfo), - C = rebar_config:consult(AppDir), - rebar_state:new(State, C, AppInfo); -state_or_new(_State, #app_info_t{state=State}) -> - State. - -spec is_lock(t(), boolean()) -> t(). is_lock(AppInfo=#app_info_t{}, IsLock) -> AppInfo#app_info_t{is_lock=IsLock}. @@ -338,9 +390,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 +404,77 @@ valid(#app_info_t{valid=Valid}) -> -spec valid(t(), boolean()) -> t(). valid(AppInfo=#app_info_t{}, Valid) -> AppInfo#app_info_t{valid=Valid}. + +verify_otp_vsn(AppInfo) -> + rebar_utils:check_min_otp_version(rebar_app_info:get(AppInfo, minimum_otp_vsn, undefined)), + rebar_utils:check_blacklisted_otp_versions(rebar_app_info:get(AppInfo, blacklisted_otp_vsns, [])). + +-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(Overrides, AppInfo) -> + Name = binary_to_atom(rebar_app_info:name(AppInfo), utf8), + Opts = rebar_opts:apply_overrides(opts(AppInfo), Name, Overrides), + AppInfo#app_info_t{default=Opts, opts=Opts}. + +add_to_profile(AppInfo, Profile, KVs) when is_atom(Profile), is_list(KVs) -> + Opts = rebar_opts:add_to_profile(opts(AppInfo), Profile, KVs), + AppInfo#app_info_t{opts=Opts}. + +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), + rebar_opts: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. |