aboutsummaryrefslogtreecommitdiffstats
path: root/src/rlx_config.erl
diff options
context:
space:
mode:
authorTristan Sloughter <[email protected]>2014-06-15 19:18:14 -0500
committerTristan Sloughter <[email protected]>2014-10-11 11:13:04 -0500
commit0516b405ccb98febcf94a8e94000f4a633569f59 (patch)
tree08eb3c09943ac68a4493ff7e2fc5925842bc4627 /src/rlx_config.erl
parent5096c28c17ea256f48ce049a43d11b172532e03e (diff)
downloadrelx-0516b405ccb98febcf94a8e94000f4a633569f59.tar.gz
relx-0516b405ccb98febcf94a8e94000f4a633569f59.tar.bz2
relx-0516b405ccb98febcf94a8e94000f4a633569f59.zip
provider task dependencies
Diffstat (limited to 'src/rlx_config.erl')
-rw-r--r--src/rlx_config.erl279
1 files changed, 279 insertions, 0 deletions
diff --git a/src/rlx_config.erl b/src/rlx_config.erl
new file mode 100644
index 0000000..d4acb3c
--- /dev/null
+++ b/src/rlx_config.erl
@@ -0,0 +1,279 @@
+%% -*- erlang-indent-level: 4; indent-tabs-mode: nil; fill-column: 80 -*-
+%%% Copyright 2012 Erlware, LLC. All Rights Reserved.
+%%%
+%%% This file is provided to you under the Apache License,
+%%% Version 2.0 (the "License"); you may not use this file
+%%% except in compliance with the License. You may obtain
+%%% a copy of the License at
+%%%
+%%% http://www.apache.org/licenses/LICENSE-2.0
+%%%
+%%% Unless required by applicable law or agreed to in writing,
+%%% software distributed under the License is distributed on an
+%%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+%%% KIND, either express or implied. See the License for the
+%%% specific language governing permissions and limitations
+%%% under the License.
+%%%-------------------------------------------------------------------
+%%% @author Eric Merritt <[email protected]>
+%%% @copyright 2011 Erlware, LLC.
+%%% @doc
+%%% A module that provides config parsing and support to the system
+%%% @end
+%%%-------------------------------------------------------------------
+-module(rlx_config).
+
+%% API
+-export([do/1,
+ format_error/1]).
+
+-include("relx.hrl").
+
+%%%===================================================================
+%%% API
+%%%===================================================================
+
+%% @doc parse all the configs currently specified in the state,
+%% populating the state as a result.
+-spec do(rlx_state:t()) ->{ok, rlx_state:t()} | relx:error().
+do(State) ->
+ case rlx_state:config_file(State) of
+ [] ->
+ search_for_dominating_config(State);
+ undefined ->
+ search_for_dominating_config(State);
+ ConfigFile when erlang:is_list(ConfigFile) ->
+ load_config(ConfigFile, State)
+ end.
+
+-spec format_error(Reason::term()) -> iolist().
+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]).
+
+%%%===================================================================
+%%% Internal Functions
+%%%===================================================================
+search_for_dominating_config({ok, Cwd}) ->
+ ConfigFile = filename:join(Cwd, "relx.config"),
+ case ec_file:exists(ConfigFile) of
+ true ->
+ {ok, ConfigFile};
+ false ->
+ search_for_dominating_config(parent_dir(Cwd))
+ end;
+search_for_dominating_config({error, _}) ->
+ no_config;
+search_for_dominating_config(State0) ->
+ {ok, Cwd} = file:get_cwd(),
+ case search_for_dominating_config({ok, Cwd}) of
+ {ok, Config} ->
+ %% we need to set the root dir on state as well
+ {ok, RootDir} = parent_dir(Config),
+ State1 = rlx_state:root_dir(State0, RootDir),
+ load_config(Config, rlx_state:config_file(State1, Config));
+ no_config ->
+ {ok, State0}
+ end.
+
+%% @doc Given a directory returns the name of the parent directory.
+-spec parent_dir(Filename::string()) ->
+ {ok, DirName::string()} | {error, no_parent_dir}.
+parent_dir(Filename) ->
+ parent_dir(filename:split(Filename), []).
+
+%% @doc Given list of directories, splits the list and returns all dirs but the
+%% last as a path.
+-spec parent_dir([string()], [string()]) ->
+ {ok, DirName::string()} | {error, no_parent_dir}.
+parent_dir([_H], []) ->
+ {error, no_parent_dir};
+parent_dir([], []) ->
+ {error, no_parent_dir};
+parent_dir([_H], Acc) ->
+ {ok, filename:join(lists:reverse(Acc))};
+parent_dir([H | T], Acc) ->
+ parent_dir(T, [H | Acc]).
+
+-spec load_config(file:filename(), rlx_state:t()) ->
+ {ok, rlx_state:t()} | relx:error().
+load_config(ConfigFile, State) ->
+ {ok, CurrentCwd} = file:get_cwd(),
+ ok = file:set_cwd(filename:dirname(ConfigFile)),
+ Result = case file:consult(ConfigFile) of
+ {error, Reason} ->
+ ?RLX_ERROR({consult, ConfigFile, Reason});
+ {ok, Terms} ->
+ CliTerms = rlx_state:cli_args(State),
+ lists:foldl(fun load_terms/2, {ok, State}, merge_configs(CliTerms, Terms))
+ end,
+ ok = file:set_cwd(CurrentCwd),
+ Result.
+
+-spec load_terms(term(), {ok, rlx_state:t()} | relx:error()) ->
+ {ok, rlx_state:t()} | relx:error().
+load_terms({default_release, {RelName, RelVsn}}, {ok, State}) ->
+ NewVsn = parse_vsn(RelVsn),
+ {ok, rlx_state:default_configured_release(State, RelName, NewVsn)};
+load_terms({paths, Paths}, {ok, State}) ->
+ code:add_pathsa([filename:absname(Path) || Path <- Paths]),
+ {ok, State};
+load_terms({default_libs, DefaultLibs}, {ok, State}) ->
+ State2 = rlx_state:put(State,
+ default_libs,
+ DefaultLibs),
+ {ok, State2};
+load_terms({system_libs, SystemLibs}, {ok, State}) ->
+ State2 = rlx_state:put(State,
+ system_libs,
+ SystemLibs),
+ {ok, State2};
+load_terms({lib_dirs, Dirs}, {ok, State}) ->
+ State2 =
+ rlx_state:add_lib_dirs(State,
+ [list_to_binary(Dir) || Dir <- rlx_util:wildcard_paths(Dirs)]),
+ {ok, State2};
+load_terms({hooks, Hooks}, {ok, State0}) ->
+ add_hooks(Hooks, State0);
+load_terms({providers, Providers0}, {ok, State0}) ->
+ Providers1 = gen_providers(Providers0, State0),
+ case Providers1 of
+ {_, E={error, _}} ->
+ E;
+ {Providers3, {ok, State3}} ->
+ {ok, rlx_state:providers(State3, Providers3)}
+ end;
+load_terms({add_providers, Providers0}, {ok, State0}) ->
+ Providers1 = gen_providers(Providers0, State0),
+ case Providers1 of
+ {_, E={error, _}} ->
+ E;
+ {Providers3, {ok, State1}} ->
+ ExistingProviders = rlx_state:providers(State1),
+ {ok, rlx_state:providers(State1, ExistingProviders ++ Providers3)}
+ end;
+load_terms({skip_apps, SkipApps0}, {ok, State0}) ->
+ {ok, rlx_state:skip_apps(State0, SkipApps0)};
+load_terms({overrides, Overrides0}, {ok, State0}) ->
+ {ok, rlx_state:overrides(State0, Overrides0)};
+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)};
+load_terms({upfrom, UpFrom}, {ok, State0}) ->
+ {ok, rlx_state:upfrom(State0, UpFrom)};
+load_terms({include_src, IncludeSrc}, {ok, State0}) ->
+ {ok, rlx_state:include_src(State0, IncludeSrc)};
+load_terms({release, {RelName, Vsn, {extend, RelName2}}, Applications}, {ok, State0}) ->
+ NewVsn = parse_vsn(Vsn),
+ Release0 = rlx_release:new(RelName, NewVsn),
+ 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
+ E={error, _} ->
+ E;
+ {ok, Release1} ->
+ {ok, rlx_state:add_configured_release(State0, Release1)}
+ end;
+load_terms({release, {RelName, Vsn}, Applications}, {ok, State0}) ->
+ NewVsn = parse_vsn(Vsn),
+ Release0 = rlx_release:new(RelName, NewVsn),
+ case rlx_release:goals(Release0, Applications) of
+ E={error, _} ->
+ E;
+ {ok, Release1} ->
+ {ok, rlx_state:add_configured_release(State0, Release1)}
+ end;
+load_terms({release, {RelName, Vsn}, {erts, ErtsVsn},
+ Applications}, {ok, State}) ->
+ NewVsn = parse_vsn(Vsn),
+ Release0 = rlx_release:erts(rlx_release:new(RelName, NewVsn), ErtsVsn),
+ case rlx_release:goals(Release0, Applications) of
+ E={error, _} ->
+ E;
+ {ok, Release1} ->
+ {ok, rlx_state:add_configured_release(State, Release1)}
+ end;
+load_terms({vm_args, VmArgs}, {ok, State}) ->
+ {ok, rlx_state:vm_args(State, filename:absname(VmArgs))};
+load_terms({sys_config, SysConfig}, {ok, State}) ->
+ case rlx_state:sys_config(State) of
+ undefined ->
+ {ok, rlx_state:sys_config(State, filename:absname(SysConfig))};
+ _ ->
+ {ok, State}
+ end;
+load_terms({root_dir, Root}, {ok, State}) ->
+ {ok, rlx_state:root_dir(State, filename:absname(Root))};
+load_terms({output_dir, OutputDir}, {ok, State}) ->
+ {ok, rlx_state:base_output_dir(State, filename:absname(OutputDir))};
+load_terms({overlay_vars, OverlayVars}, {ok, State}) ->
+ CurrentOverlayVars = rlx_state:get(State, overlay_vars),
+ NewOverlayVars0 = list_of_overlay_vars_files(OverlayVars),
+ NewOverlayVars1 = lists:umerge(lists:usort(NewOverlayVars0), lists:usort(CurrentOverlayVars)),
+ {ok, rlx_state:put(State, overlay_vars, NewOverlayVars1)};
+load_terms({Name, Value}, {ok, State})
+ when erlang:is_atom(Name) ->
+ {ok, rlx_state:put(State, Name, Value)};
+load_terms(_, Error={error, _}) ->
+ Error;
+load_terms(InvalidTerm, _) ->
+ ?RLX_ERROR({invalid_term, InvalidTerm}).
+
+-spec gen_providers([module()], rlx_state:t()) ->
+ {[providers:t()], {ok, rlx_state:t()} | relx:error()}.
+gen_providers(Providers, State) ->
+ lists:foldl(fun(ProviderName, {Providers1, {ok, State1}}) ->
+ {Provider, State2} = providers:new(ProviderName, State1),
+ {[Provider | Providers1], State2};
+ (_, E={_, {error, _}}) ->
+ E
+ end, {[], {ok, State}}, Providers).
+
+add_hooks(Hooks, State) ->
+ {ok, lists:foldl(fun({pre, Target, Hook}, StateAcc) ->
+ rlx_state:prepend_hook(StateAcc, Target, Hook);
+ ({post, Target, Hook}, StateAcc) ->
+ rlx_state:append_hook(StateAcc, Target, Hook)
+ end, State, Hooks)}.
+
+list_of_overlay_vars_files(undefined) ->
+ [];
+list_of_overlay_vars_files([]) ->
+ [];
+list_of_overlay_vars_files([H | _]=FileNames) when erlang:is_list(H) ->
+ FileNames;
+list_of_overlay_vars_files(FileName) when is_list(FileName) ->
+ [FileName].
+
+merge_configs([], ConfigTerms) ->
+ ConfigTerms;
+merge_configs([{_Key, undefined} | CliTerms], ConfigTerms) ->
+ merge_configs(CliTerms, ConfigTerms);
+merge_configs([{_Key, []} | CliTerms], ConfigTerms) ->
+ merge_configs(CliTerms, ConfigTerms);
+merge_configs([{Key, Value} | CliTerms], ConfigTerms) ->
+ case Key of
+ X when X =:= lib_dirs
+ ; X =:= goals
+ ; X =:= overlay_vars
+ ; X =:= overrides ->
+ case lists:keyfind(Key, 1, ConfigTerms) of
+ {Key, Value2} ->
+ MergedValue = lists:umerge([Value, Value2]),
+ merge_configs(CliTerms, lists:keyreplace(Key, 1, ConfigTerms, {Key, MergedValue}));
+ false ->
+ merge_configs(CliTerms, [{Key, Value} | ConfigTerms])
+ end;
+ _ ->
+ merge_configs(CliTerms, lists:keystore(Key, 1, ConfigTerms, {Key, Value}))
+ end.
+
+parse_vsn(Vsn) when Vsn =:= semver ; Vsn =:= "semver" ->
+ binary_to_list(ec_git_vsn:vsn([]));
+parse_vsn(Vsn) ->
+ Vsn.