diff options
Diffstat (limited to 'src/rebar_app_info.erl')
-rw-r--r-- | src/rebar_app_info.erl | 317 |
1 files changed, 239 insertions, 78 deletions
diff --git a/src/rebar_app_info.erl b/src/rebar_app_info.erl index cf3b82e..d051a15 100644 --- a/src/rebar_app_info.erl +++ b/src/rebar_app_info.erl @@ -7,6 +7,8 @@ new/4, new/5, update_opts/3, + update_opts/2, + update_opts_deps/2, discover/1, name/1, name/2, @@ -22,7 +24,6 @@ parent/2, original_vsn/1, original_vsn/2, - ebin_dir/1, priv_dir/1, applications/1, applications/2, @@ -36,6 +37,8 @@ dir/2, out_dir/1, out_dir/2, + ebin_dir/1, + ebin_dir/2, default/1, default/2, opts/1, @@ -43,16 +46,18 @@ get/2, get/3, set/3, - resource_type/1, - resource_type/2, source/1, source/2, + project_type/1, + project_type/2, is_lock/1, is_lock/2, is_checkout/1, is_checkout/2, valid/1, valid/2, + is_available/1, + is_available/2, verify_otp_vsn/1, has_all_artifacts/1, @@ -66,13 +71,16 @@ -include("rebar.hrl"). -include_lib("providers/include/providers.hrl"). --export_type([t/0]). +-export_type([t/0, + project_type/0]). + +-type project_type() :: rebar3 | mix | undefined. --record(app_info_t, {name :: binary(), +-record(app_info_t, {name :: binary() | undefined, app_file_src :: file:filename_all() | undefined, app_file_src_script:: file:filename_all() | undefined, app_file :: file:filename_all() | undefined, - original_vsn :: binary() | string() | undefined, + original_vsn :: binary() | undefined, parent=root :: binary() | root, app_details=[] :: list(), applications=[] :: list(), @@ -83,11 +91,13 @@ dep_level=0 :: integer(), dir :: file:name(), out_dir :: file:name(), - resource_type :: pkg | src, + ebin_dir :: file:name(), source :: string() | tuple() | checkout | undefined, is_lock=false :: boolean(), is_checkout=false :: boolean(), - valid :: boolean()}). + valid :: boolean() | undefined, + project_type :: project_type(), + is_available=false :: boolean()}). %%============================================================================ %% types @@ -103,54 +113,64 @@ new() -> #app_info_t{}. +%% @doc Build a new app info value with only the app name set. -spec new(atom() | binary() | string()) -> {ok, t()}. new(AppName) -> - {ok, #app_info_t{name=ec_cnv:to_binary(AppName)}}. + {ok, #app_info_t{name=rebar_utils:to_binary(AppName)}}. +%% @doc Build a new app info value with only the name and version set. -spec new(atom() | binary() | string(), binary() | string()) -> {ok, t()}. new(AppName, Vsn) -> - {ok, #app_info_t{name=ec_cnv:to_binary(AppName), + {ok, #app_info_t{name=rebar_utils:to_binary(AppName), original_vsn=Vsn}}. %% @doc build a complete version of the app info with all fields set. -spec new(atom() | binary() | string(), binary() | string(), file:name()) -> {ok, t()}. new(AppName, Vsn, Dir) -> - {ok, #app_info_t{name=ec_cnv:to_binary(AppName), + {ok, #app_info_t{name=rebar_utils:to_binary(AppName), original_vsn=Vsn, - dir=ec_cnv:to_list(Dir), - out_dir=ec_cnv:to_list(Dir)}}. + dir=rebar_utils:to_list(Dir), + out_dir=rebar_utils:to_list(Dir), + ebin_dir=filename:join(rebar_utils:to_list(Dir), "ebin")}}. %% @doc build a complete version of the app info with all fields set. -spec new(atom() | binary() | string(), binary() | string(), file:name(), list()) -> {ok, t()}. new(AppName, Vsn, Dir, Deps) -> - {ok, #app_info_t{name=ec_cnv:to_binary(AppName), + {ok, #app_info_t{name=rebar_utils:to_binary(AppName), original_vsn=Vsn, - dir=ec_cnv:to_list(Dir), - out_dir=ec_cnv:to_list(Dir), + dir=rebar_utils:to_list(Dir), + out_dir=rebar_utils:to_list(Dir), + ebin_dir=filename:join(rebar_utils:to_list(Dir), "ebin"), deps=Deps}}. %% @doc build a complete version of the app info with all fields set. -spec new(atom() | binary(), atom() | binary() | string(), binary() | string(), file:name(), list()) -> {ok, t()}. new(Parent, AppName, Vsn, Dir, Deps) -> - {ok, #app_info_t{name=ec_cnv:to_binary(AppName), + {ok, #app_info_t{name=rebar_utils:to_binary(AppName), parent=Parent, original_vsn=Vsn, - dir=ec_cnv:to_list(Dir), - out_dir=ec_cnv:to_list(Dir), + dir=rebar_utils:to_list(Dir), + out_dir=rebar_utils:to_list(Dir), + ebin_dir=filename:join(rebar_utils:to_list(Dir), "ebin"), deps=Deps}}. +%% @doc update the opts based on the contents of a config +%% file for the app +-spec update_opts(t(), rebar_dict(), [any()]) -> t(). update_opts(AppInfo, Opts, Config) -> - LockDeps = case resource_type(AppInfo) of - pkg -> - Deps = deps(AppInfo), - [{{locks, default}, Deps}, {{deps, default}, Deps}]; + LockDeps = case source(AppInfo) of + Tuple when is_tuple(Tuple) andalso element(1, Tuple) =:= pkg -> + %% Deps are set separate for packages + %% instead of making it seem we have no deps + %% don't set anything here. + []; _ -> - deps_from_config(dir(AppInfo), Config) + deps_from_config(dir(AppInfo), proplists:get_value(deps, Config, [])) end, Plugins = proplists:get_value(plugins, Config, []), @@ -160,13 +180,32 @@ update_opts(AppInfo, Opts, Config) -> NewOpts = rebar_opts:merge_opts(LocalOpts, Opts), - AppInfo#app_info_t{opts=NewOpts - ,default=NewOpts}. + AppInfo#app_info_t{opts=NewOpts, + default=NewOpts}. -deps_from_config(Dir, Config) -> +%% @doc update current app info opts by merging in a new dict of opts +-spec update_opts(t(), rebar_dict()) -> t(). +update_opts(AppInfo=#app_info_t{opts=LocalOpts}, Opts) -> + NewOpts = rebar_opts:merge_opts(LocalOpts, Opts), + AppInfo#app_info_t{opts=NewOpts, + default=NewOpts}. + +%% @doc update the opts based on new deps, usually from an app's hex registry metadata +-spec update_opts_deps(t(), [any()]) -> t(). +update_opts_deps(AppInfo=#app_info_t{opts=Opts}, Deps) -> + LocalOpts = dict:from_list([{{locks, default}, Deps}, {{deps, default}, Deps}]), + NewOpts = rebar_opts:merge_opts(LocalOpts, Opts), + AppInfo#app_info_t{opts=NewOpts, + default=NewOpts, + deps=Deps}. + + +%% @private extract the deps for an app in `Dir' based on its config file data +-spec deps_from_config(file:filename(), [any()]) -> [{tuple(), any()}, ...]. +deps_from_config(Dir, ConfigDeps) -> case rebar_config:consult_lock_file(filename:join(Dir, ?LOCK_FILE)) of [] -> - [{{deps, default}, proplists:get_value(deps, Config, [])}]; + [{{deps, default}, ConfigDeps}]; D -> %% We want the top level deps only from the lock file. %% This ensures deterministic overrides for configs. @@ -184,30 +223,48 @@ discover(Dir) -> not_found end. +%% @doc get the name of the app. -spec name(t()) -> binary(). name(#app_info_t{name=Name}) -> Name. +%% @doc set the name of the app. -spec name(t(), atom() | binary() | string()) -> t(). name(AppInfo=#app_info_t{}, AppName) -> - AppInfo#app_info_t{name=ec_cnv:to_binary(AppName)}. + AppInfo#app_info_t{name=rebar_utils:to_binary(AppName)}. +%% @doc get the dictionary of options for the app. +-spec opts(t()) -> rebar_dict(). opts(#app_info_t{opts=Opts}) -> Opts. +%% @doc set the dictionary of options for the app. +-spec opts(t(), rebar_dict()) -> t(). opts(AppInfo, Opts) -> AppInfo#app_info_t{opts=Opts}. +%% @doc get the dictionary of options under the default profile. +%% Represents a root set prior to applying other profiles. +-spec default(t()) -> rebar_dict(). default(#app_info_t{default=Default}) -> Default. +%% @doc set the dictionary of options under the default profile. +%% Useful when re-applying profile. +-spec default(t(), rebar_dict()) -> t(). default(AppInfo, Default) -> AppInfo#app_info_t{default=Default}. +%% @doc look up a value in the dictionary of options; fails if +%% the key for it does not exist. +-spec get(t(), term()) -> term(). get(AppInfo, Key) -> {ok, Value} = dict:find(Key, AppInfo#app_info_t.opts), Value. +%% @doc look up a value in the dictionary of options; returns +%% a `Default' value otherwise. +-spec get(t(), term(), term()) -> term(). get(AppInfo, Key, Default) -> case dict:find(Key, AppInfo#app_info_t.opts) of {ok, Value} -> @@ -216,31 +273,35 @@ get(AppInfo, Key, Default) -> Default end. +%% @doc sets a given value in the dictionary of options for the app. -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)}. +%% @doc finds the .app.src file for an app, if any. -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"]), - case filelib:is_file(AppFileSrc) of - true -> - AppFileSrc; - false -> - undefined +app_file_src(#app_info_t{app_file_src=undefined, dir=Dir, name=Name, opts=Opts}) -> + CandidatePaths = [filename:join([rebar_utils:to_list(Dir), Src, rebar_utils:to_list(Name)++".app.src"]) + || Src <- rebar_opts:get(Opts, src_dirs, ["src"])], + case lists:dropwhile(fun(Path) -> not filelib:is_file(Path) end, CandidatePaths) of + [] -> undefined; + [AppFileSrc|_] -> AppFileSrc end; app_file_src(#app_info_t{app_file_src=AppFileSrc}) -> - ec_cnv:to_list(AppFileSrc). + rebar_utils:to_list(AppFileSrc). +%% @doc sets the .app.src file for an app. An app without such a file +%% can explicitly be set with `undefined'. -spec app_file_src(t(), file:filename_all() | undefined) -> t(). app_file_src(AppInfo=#app_info_t{}, undefined) -> AppInfo#app_info_t{app_file_src=undefined}; app_file_src(AppInfo=#app_info_t{}, AppFileSrc) -> - AppInfo#app_info_t{app_file_src=ec_cnv:to_list(AppFileSrc)}. + AppInfo#app_info_t{app_file_src=rebar_utils:to_list(AppFileSrc)}. +%% @doc finds the .app.src.script file for an app, if any. -spec app_file_src_script(t()) -> file:filename_all() | undefined. app_file_src_script(#app_info_t{app_file_src_script=undefined, dir=Dir, name=Name}) -> - AppFileSrcScript = filename:join([ec_cnv:to_list(Dir), "src", ec_cnv:to_list(Name)++".app.src.script"]), + AppFileSrcScript = filename:join([rebar_utils:to_list(Dir), "src", rebar_utils:to_list(Name)++".app.src.script"]), case filelib:is_file(AppFileSrcScript) of true -> AppFileSrcScript; @@ -248,17 +309,20 @@ app_file_src_script(#app_info_t{app_file_src_script=undefined, dir=Dir, name=Nam undefined end; app_file_src_script(#app_info_t{app_file_src_script=AppFileSrcScript}) -> - ec_cnv:to_list(AppFileSrcScript). + rebar_utils:to_list(AppFileSrcScript). +%% @doc sets the .app.src.script file for an app. An app without such a file +%% can explicitly be set with `undefined'. -spec app_file_src_script(t(), file:filename_all()) -> t(). app_file_src_script(AppInfo=#app_info_t{}, undefined) -> AppInfo#app_info_t{app_file_src_script=undefined}; app_file_src_script(AppInfo=#app_info_t{}, AppFileSrcScript) -> - AppInfo#app_info_t{app_file_src_script=ec_cnv:to_list(AppFileSrcScript)}. + AppInfo#app_info_t{app_file_src_script=rebar_utils:to_list(AppFileSrcScript)}. +%% @doc finds the .app file for an app, if any. -spec app_file(t()) -> file:filename_all() | undefined. app_file(#app_info_t{app_file=undefined, out_dir=Dir, name=Name}) -> - AppFile = filename:join([ec_cnv:to_list(Dir), "ebin", ec_cnv:to_list(Name)++".app"]), + AppFile = filename:join([rebar_utils:to_list(Dir), "ebin", rebar_utils:to_list(Name)++".app"]), case filelib:is_file(AppFile) of true -> AppFile; @@ -268,136 +332,209 @@ app_file(#app_info_t{app_file=undefined, out_dir=Dir, name=Name}) -> app_file(#app_info_t{app_file=AppFile}) -> AppFile. --spec app_file(t(), file:filename_all()) -> t(). +%% @doc sets the .app file for an app. +-spec app_file(t(), file:filename_all() | undefined) -> t(). app_file(AppInfo=#app_info_t{}, AppFile) -> AppInfo#app_info_t{app_file=AppFile}. +%% @doc returns the information stored in the app's app file, +%% or if none, from the .app.src file. -spec app_details(t()) -> list(). app_details(AppInfo=#app_info_t{app_details=[]}) -> case app_file(AppInfo) of undefined -> - rebar_file_utils:try_consult(app_file_src(AppInfo)); + try rebar_config:consult_app_file(app_file_src(AppInfo)) of + [] -> []; + [{application, _Name, AppDetails}] -> AppDetails + catch + _:_ -> + [] + end; AppFile -> - try - rebar_file_utils:try_consult(AppFile) + try rebar_file_utils:try_consult(AppFile) of + [] -> []; + [{application, _Name, AppDetails}] -> AppDetails catch throw:{error, {Module, Reason}} -> - ?DEBUG("Warning, falling back to .app.src because of: ~s", + ?DEBUG("Warning, falling back to .app.src because of: ~ts", [Module:format_error(Reason)]), - rebar_file_utils:try_consult(app_file_src(AppInfo)) + case rebar_config:consult_app_file(app_file_src(AppInfo)) of + [] -> []; + [{application, _Name, AppDetails}] -> AppDetails + end end end; app_details(#app_info_t{app_details=AppDetails}) -> AppDetails. +%% @doc stores the information that would be returned from the +%% app file, when reading from `app_details/1'. -spec app_details(t(), list()) -> t(). app_details(AppInfo=#app_info_t{}, AppDetails) -> AppInfo#app_info_t{app_details=AppDetails}. +%% @doc returns the app's parent in the dep tree. +-spec parent(t()) -> root | binary(). parent(#app_info_t{parent=Parent}) -> Parent. +%% @doc sets the app's parent. -spec parent(t(), binary() | root) -> t(). parent(AppInfo=#app_info_t{}, Parent) -> AppInfo#app_info_t{parent=Parent}. --spec original_vsn(t()) -> string(). +%% @doc returns the original version of the app (unevaluated if +%% asking for a semver) +-spec original_vsn(t()) -> binary(). original_vsn(#app_info_t{original_vsn=Vsn}) -> Vsn. --spec original_vsn(t(), string()) -> t(). +%% @doc stores the original version of the app (unevaluated if +%% asking for a semver) +-spec original_vsn(t(), binary() | string()) -> t(). original_vsn(AppInfo=#app_info_t{}, Vsn) -> AppInfo#app_info_t{original_vsn=Vsn}. +%% @doc returns the list of applications the app depends on. -spec applications(t()) -> list(). applications(#app_info_t{applications=Applications}) -> Applications. +%% @doc sets the list of applications the app depends on. +%% Should be obtained from the app file. -spec applications(t(), list()) -> t(). applications(AppInfo=#app_info_t{}, Applications) -> AppInfo#app_info_t{applications=Applications}. +%% @doc returns the list of active profiles -spec profiles(t()) -> list(). profiles(#app_info_t{profiles=Profiles}) -> Profiles. +%% @doc sets the list of active profiles -spec profiles(t(), list()) -> t(). profiles(AppInfo=#app_info_t{}, Profiles) -> AppInfo#app_info_t{profiles=Profiles}. +%% @doc returns the list of dependencies -spec deps(t()) -> list(). deps(#app_info_t{deps=Deps}) -> Deps. +%% @doc sets the list of dependencies. -spec deps(t(), list()) -> t(). deps(AppInfo=#app_info_t{}, Deps) -> AppInfo#app_info_t{deps=Deps}. -dep_level(AppInfo=#app_info_t{}, Level) -> - AppInfo#app_info_t{dep_level=Level}. - +%% @doc returns the level the app has in the lock files or in the +%% dep tree. +-spec dep_level(t()) -> non_neg_integer(). dep_level(#app_info_t{dep_level=Level}) -> Level. +%% @doc sets the level the app has in the lock files or in the +%% dep tree. +-spec dep_level(t(), non_neg_integer()) -> t(). +dep_level(AppInfo=#app_info_t{}, Level) -> + AppInfo#app_info_t{dep_level=Level}. + +%% @doc returns the directory that contains the app. -spec dir(t()) -> file:name(). dir(#app_info_t{dir=Dir}) -> Dir. +%% @doc sets the directory that contains the app. -spec dir(t(), file:name()) -> t(). dir(AppInfo=#app_info_t{out_dir=undefined}, Dir) -> - AppInfo#app_info_t{dir=ec_cnv:to_list(Dir), - out_dir=ec_cnv:to_list(Dir)}; + AppInfo#app_info_t{dir=rebar_utils:to_list(Dir), + out_dir=rebar_utils:to_list(Dir)}; dir(AppInfo=#app_info_t{}, Dir) -> - AppInfo#app_info_t{dir=ec_cnv:to_list(Dir)}. + AppInfo#app_info_t{dir=rebar_utils:to_list(Dir)}. +%% @doc returns the directory where build artifacts for the app +%% should go -spec out_dir(t()) -> file:name(). out_dir(#app_info_t{out_dir=OutDir}) -> OutDir. +%% @doc sets the directory where build artifacts for the app +%% should go -spec out_dir(t(), file:name()) -> t(). out_dir(AppInfo=#app_info_t{}, OutDir) -> - AppInfo#app_info_t{out_dir=ec_cnv:to_list(OutDir)}. + AppInfo#app_info_t{out_dir=rebar_utils:to_list(OutDir), + ebin_dir=filename:join(rebar_utils:to_list(OutDir), "ebin")}. +%% @doc gets the directory where ebin files for the app should go -spec ebin_dir(t()) -> file:name(). -ebin_dir(#app_info_t{out_dir=OutDir}) -> - ec_cnv:to_list(filename:join(OutDir, "ebin")). - +ebin_dir(#app_info_t{ebin_dir=undefined, + out_dir=OutDir}) -> + filename:join(rebar_utils:to_list(OutDir), "ebin"); +ebin_dir(#app_info_t{ebin_dir=EbinDir}) -> + EbinDir. + +%% @doc sets the directory where beam files should go +-spec ebin_dir(t(), file:name()) -> t(). +ebin_dir(AppInfo, EbinDir) -> + AppInfo#app_info_t{ebin_dir=EbinDir}. + +%% @doc gets the directory where private files for the app should go -spec priv_dir(t()) -> file:name(). priv_dir(#app_info_t{out_dir=OutDir}) -> - ec_cnv:to_list(filename:join(OutDir, "priv")). + rebar_utils:to_list(filename:join(OutDir, "priv")). --spec resource_type(t(), pkg | src) -> t(). -resource_type(AppInfo=#app_info_t{}, Type) -> - AppInfo#app_info_t{resource_type=Type}. - --spec resource_type(t()) -> pkg | src. -resource_type(#app_info_t{resource_type=ResourceType}) -> - ResourceType. +%% @doc finds the source specification for the app +-spec source(t()) -> string() | tuple(). +source(#app_info_t{source=Source}) -> + Source. +%% @doc sets the source specification for the app -spec source(t(), string() | tuple() | checkout) -> t(). source(AppInfo=#app_info_t{}, Source) -> AppInfo#app_info_t{source=Source}. --spec source(t()) -> string() | tuple(). -source(#app_info_t{source=Source}) -> - Source. +%% @doc returns the lock status for the app +-spec is_lock(t()) -> boolean(). +is_lock(#app_info_t{is_lock=IsLock}) -> + IsLock. +%% @doc sets the lock status for the app -spec is_lock(t(), boolean()) -> t(). is_lock(AppInfo=#app_info_t{}, IsLock) -> AppInfo#app_info_t{is_lock=IsLock}. --spec is_lock(t()) -> boolean(). -is_lock(#app_info_t{is_lock=IsLock}) -> - IsLock. +%% @doc returns whether the app is a checkout app or not +-spec is_checkout(t()) -> boolean(). +is_checkout(#app_info_t{is_checkout=IsCheckout}) -> + IsCheckout. +%% @doc sets whether the app is a checkout app or not -spec is_checkout(t(), boolean()) -> t(). is_checkout(AppInfo=#app_info_t{}, IsCheckout) -> AppInfo#app_info_t{is_checkout=IsCheckout}. --spec is_checkout(t()) -> boolean(). -is_checkout(#app_info_t{is_checkout=IsCheckout}) -> - IsCheckout. +%% @doc returns whether the app source exists in the deps dir +-spec is_available(t()) -> boolean(). +is_available(#app_info_t{is_available=IsAvailable}) -> + IsAvailable. + +%% @doc sets whether the app's source is available +%% only set if the app's source is found in the expected dep directory +-spec is_available(t(), boolean()) -> t(). +is_available(AppInfo=#app_info_t{}, IsAvailable) -> + AppInfo#app_info_t{is_available=IsAvailable}. + +%% @doc +-spec project_type(t()) -> atom(). +project_type(#app_info_t{project_type=ProjectType}) -> + ProjectType. + +%% @doc +-spec project_type(t(), atom()) -> t(). +project_type(AppInfo=#app_info_t{}, ProjectType) -> + AppInfo#app_info_t{project_type=ProjectType}. + +%% @doc returns whether the app is valid (built) or not -spec valid(t()) -> boolean(). valid(AppInfo=#app_info_t{valid=undefined}) -> case rebar_app_utils:validate_application_info(AppInfo) =:= true @@ -410,14 +547,22 @@ valid(AppInfo=#app_info_t{valid=undefined}) -> valid(#app_info_t{valid=Valid}) -> Valid. +%% @doc sets whether the app is valid (built) or not. If left unset, +%% rebar3 will do the detection of the status itself. -spec valid(t(), boolean()) -> t(). valid(AppInfo=#app_info_t{}, Valid) -> AppInfo#app_info_t{valid=Valid}. +%% @doc checks whether the app can be built with the current +%% Erlang/OTP version. If the check fails, the function raises +%% an exception and displays an error. +-spec verify_otp_vsn(t()) -> ok | no_return(). 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, [])). +%% @doc checks whether all the build artifacts for an app to be considered +%% valid are present. -spec has_all_artifacts(#app_info_t{}) -> true | {false, file:filename()}. has_all_artifacts(AppInfo) -> Artifacts = rebar_app_info:get(AppInfo, artifacts, []), @@ -427,13 +572,17 @@ has_all_artifacts(AppInfo) -> ,{out_dir, OutDir}], all(OutDir, Context, Artifacts). +%% @private checks that all files/artifacts in the directory are found. +%% Template evaluation must happen and a bbmustache context needs to +%% be provided. +-spec all(file:filename(), term(), [string()]) -> true | {false, string()}. all(_, _, []) -> true; all(Dir, Context, [File|Artifacts]) -> FilePath = filename:join(Dir, rebar_templater:render(File, Context)), case filelib:is_regular(FilePath) of false -> - ?DEBUG("Missing artifact ~s", [FilePath]), + ?DEBUG("Missing artifact ~ts", [FilePath]), {false, File}; true -> all(Dir, Context, Artifacts) @@ -441,15 +590,23 @@ all(Dir, Context, [File|Artifacts]) -> %%%%% +%% @doc given a set of override rules, modify the app info accordingly +-spec apply_overrides(list(), t()) -> t(). 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}. +%% @doc adds a new profile with its own config to the app data +-spec add_to_profile(t(), atom(), [{_,_}]) -> t(). 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}. +%% @doc applies and merges the profile configuration in the specified order +%% of profiles (or for a single profile) and returns an app info record +%% with the resulting configuration +-spec apply_profiles(t(), atom() | [atom(),...]) -> t(). apply_profiles(AppInfo, Profile) when not is_list(Profile) -> apply_profiles(AppInfo, [Profile]); apply_profiles(AppInfo, [default]) -> @@ -481,9 +638,13 @@ apply_profiles(AppInfo=#app_info_t{default = Defaults, profiles=CurrentProfiles} end, Defaults, AppliedProfiles), AppInfo#app_info_t{profiles = AppliedProfiles, opts=NewOpts}. +%% @private drops duplicated profile definitions +-spec deduplicate(list()) -> list(). deduplicate(Profiles) -> do_deduplicate(lists:reverse(Profiles), []). +%% @private drops duplicated profile definitions +-spec do_deduplicate(list(), list()) -> list(). do_deduplicate([], Acc) -> Acc; do_deduplicate([Head | Rest], Acc) -> |