Refactor of core logic to provide better control over recursion and code path mgmt

This commit is contained in:
Dave Smith 2009-12-12 07:34:29 -07:00
parent 726f8e6946
commit 3990f0a076
3 changed files with 133 additions and 163 deletions

View file

@ -24,18 +24,16 @@
{env, [ {env, [
%% Default log level %% Default log level
{log_level, error}, {log_level, error},
%% Key/value list of base/default configuration used by
%% rebar_config during initialization
{default_config, [
{app_modules, [ rebar_protobuffs_compiler,
rebar_erlc_compiler,
rebar_port_compiler,
rebar_otp_app,
rebar_ct,
rebar_eunit]},
{rel_modules, [ rebar_reltool ]} %% Processing modules
]} {modules, [
{app_dir, [ rebar_protobuffs_compiler,
rebar_erlc_compiler,
rebar_port_compiler,
rebar_otp_app,
rebar_ct,
rebar_eunit]},
{rel_dir, [ rebar_reltool ]}
]}
]} ]}
]}. ]}.

View file

@ -24,10 +24,9 @@
%% ------------------------------------------------------------------- %% -------------------------------------------------------------------
-module(rebar_config). -module(rebar_config).
-export([new/1, -export([new/0, new/1,
get_modules/2, get/3, get_list/3,
get_list/3, delete/2,
get/3,
set_global/2, get_global/2]). set_global/2, get_global/2]).
-include("rebar.hrl"). -include("rebar.hrl").
@ -40,45 +39,34 @@
%% Public API %% Public API
%% =================================================================== %% ===================================================================
new(Dir) -> new() ->
{ok, DefaultConfig} = application:get_env(rebar, default_config), #config { dir = rebar_utils:get_cwd(),
BaseDict = orddict:from_list(DefaultConfig), opts = []}.
new(ParentConfig) ->
%% Load terms from rebar.config, if it exists %% Load terms from rebar.config, if it exists
Dir = rebar_utils:get_cwd(),
ConfigFile = filename:join([Dir, "rebar.config"]), ConfigFile = filename:join([Dir, "rebar.config"]),
case file:consult(ConfigFile) of case file:consult(ConfigFile) of
{ok, Terms} -> {ok, Terms} ->
Dict = merge_terms(Terms, BaseDict); Opts = Terms ++ ParentConfig#config.opts;
{error, enoent} -> {error, enoent} ->
Dict = BaseDict; Opts = ParentConfig#config.opts;
Other -> Other ->
Opts = undefined, % Keep erlc happy
?WARN("Failed to load ~s: ~p\n", [ConfigFile, Other]), ?WARN("Failed to load ~s: ~p\n", [ConfigFile, Other]),
?FAIL, ?FAIL
Dict = BaseDict
end, end,
#config { dir = Dir, opts = Dict }. #config { dir = Dir, opts = Opts }.
get_modules(Config, app) ->
get_list(Config, app_modules, []);
get_modules(Config, rel) ->
get_list(Config, rel_modules, []).
get_list(Config, Key, Default) -> get_list(Config, Key, Default) ->
case orddict:find(Key, Config#config.opts) of get(Config, Key, Default).
error ->
Default;
{ok, List} ->
List
end.
get(Config, Key, Default) -> get(Config, Key, Default) ->
case orddict:find(Key, Config#config.opts) of proplists:get_value(Key, Config#config.opts, Default).
error ->
Default; delete(Config, Key) ->
{ok, Value} -> Config#config { opts = proplists:delete(Key, Config#config.opts) }.
Value
end.
set_global(Key, Value) -> set_global(Key, Value) ->
application:set_env(rebar_global, Key, Value). application:set_env(rebar_global, Key, Value).
@ -95,10 +83,3 @@ get_global(Key, Default) ->
%% =================================================================== %% ===================================================================
%% Internal functions %% Internal functions
%% =================================================================== %% ===================================================================
merge_terms([], Dict) ->
Dict;
merge_terms([{Key, Value} | Rest], Dict) ->
merge_terms(Rest, orddict:store(Key, Value, Dict));
merge_terms([_ | Rest], Dict) ->
merge_terms(Rest, Dict).

View file

@ -26,6 +26,8 @@
-export([run/1]). -export([run/1]).
-export([app_dir/1, rel_dir/1]). % Ugh
-include("rebar.hrl"). -include("rebar.hrl").
%% =================================================================== %% ===================================================================
@ -36,30 +38,23 @@ run(Args) ->
%% Filter all the flags (i.e. string of form key=value) from the %% Filter all the flags (i.e. string of form key=value) from the
%% command line arguments. What's left will be the commands to run. %% command line arguments. What's left will be the commands to run.
Commands = filter_flags(Args, []), Commands = filter_flags(Args, []),
% dbg:tracer(),
dbg:p(all, call),
dbg:tpl(rebar_core, []),
dbg:tpl(rebar_erlc_compiler, clean, []),
%% Pre-load the rebar app so that we get default configuration %% Pre-load the rebar app so that we get default configuration
ok = application:load(rebar), ok = application:load(rebar),
%% Initialize logging system %% Initialize logging system
rebar_log:init(), rebar_log:init(),
%% From the current working directory, search recursively and find
%% all the application and release directories. We always terminate the
%% recursion at an application or release directory.
Cwd = rebar_utils:get_cwd(),
case target_type(Cwd) of
undefined ->
Targets = find_targets(Cwd);
{Type, Filename} ->
Targets = [{Type, Cwd, Filename}]
end,
%% Prefix all the app targets to the code path so that inter-app compilation %% Convert command strings to atoms
%% works properly CommandAtoms = [list_to_atom(C) || C <- Commands],
update_code_path(Targets),
%% Finally, apply the specified command to each target %% Load rebar.config, if it exists
apply_commands(Targets, Commands). process_dir(rebar_utils:get_cwd(), rebar_config:new(), CommandAtoms).
%% =================================================================== %% ===================================================================
@ -84,118 +79,89 @@ filter_flags([Item | Rest], Commands) ->
?CONSOLE("Ignoring command line argument: ~p\n", [Other]), ?CONSOLE("Ignoring command line argument: ~p\n", [Other]),
filter_flags(Rest, Commands) filter_flags(Rest, Commands)
end. end.
%% process_dir(Dir, ParentConfig, Commands) ->
%% Recursively find all the targets starting at a root directory
%%
find_targets(Root) ->
{ok, Files} = file:list_dir(Root),
find_targets(Files, Root, [], 1).
find_targets([], _Root, Acc, _Depth) ->
Acc;
find_targets(_Files, _Root, Acc, 10) ->
Acc;
find_targets([F | Rest], Root, Acc, Depth) ->
AbsName = filename:join([Root, F]),
?DEBUG("find_targets ~s ~s\n", [Root, F]),
case target_type(AbsName) of
undefined ->
case filelib:is_dir(AbsName) of
true ->
{ok, SubFiles} = file:list_dir(AbsName),
Acc2 = find_targets(SubFiles, AbsName, Acc, Depth+1);
false ->
Acc2 = Acc
end;
{Type, Filename} ->
Acc2 = [{Type, AbsName, Filename} | Acc]
end,
find_targets(Rest, Root, Acc2, Depth).
%%
%% Determine the target type of a given file: app, rel or undefined
%%
target_type(AbsName) ->
case rebar_app_utils:is_app_dir(AbsName) of
{true, AppFile} ->
{app, AppFile};
false ->
case rebar_rel_utils:is_rel_dir(AbsName) of
{true, ReltoolFile} ->
{rel, ReltoolFile};
false ->
undefined
end
end.
%%
%% Add all application targets to the front of the code path
%%
update_code_path([]) ->
ok;
update_code_path([{app, Dir, _} | Rest]) ->
EbinDir = filename:join([Dir, "ebin"]),
true = code:add_patha(EbinDir),
?DEBUG("Adding ~s to code path\n", [EbinDir]),
update_code_path(Rest);
update_code_path([_ | Rest]) ->
update_code_path(Rest).
apply_commands(_Targets, []) ->
ok;
apply_commands(Targets, [CommandStr | Rest]) ->
%% Convert the command into an atom for convenience
Command = list_to_atom(CommandStr),
case catch(apply_command(Targets, Command)) of
ok ->
apply_commands(Targets, Rest);
Other ->
Other
end.
apply_command([], _Command) ->
ok;
apply_command([{Type, Dir, File} | Rest], Command) ->
ok = file:set_cwd(Dir), ok = file:set_cwd(Dir),
Config = rebar_config:new(Dir), Config = rebar_config:new(ParentConfig),
%% Look for subdirs configuration list -- if it exists, we're going to process those first %% Save the current code path and then update it with
case subdirs(rebar_config:get_list(Config, subdirs, []), []) of %% lib_dirs. Children inherit parents code path, but we
%% also want to ensure that we restore everything to pristine
%% condition after processing this child
CurrentCodePath = update_code_path(Config),
%% If there are any subdirs specified, process those first...
case rebar_config:get(Config, sub_dirs, []) of
[] -> [] ->
ok; ok;
Subdirs -> Subdirs ->
?DEBUG("Subdirs: ~p\n", [Subdirs]), %% Edge case: config is inherited, EXCEPT for sub_dir directives -- filter those out
update_code_path(Subdirs), FilteredConfig = rebar_config:delete(Config, sub_dirs),
case apply_command(Subdirs, Command) of [process_dir(filename:join(Dir, Subdir), FilteredConfig, Commands) || Subdir <- Subdirs],
ok -> ok = file:set_cwd(Dir)
ok = file:set_cwd(Dir);
error ->
?FAIL
end
end, end,
%% Get the list of processing modules and check each one against
%% CWD to see if it's a fit -- if it is, use that set of modules
%% to process this dir.
{ok, AvailModuleSets} = application:get_env(rebar, modules),
case choose_module_set(AvailModuleSets, Dir) of
{ok, Modules, ModuleSetFile} ->
apply_commands(Commands, Modules, Config, ModuleSetFile);
none ->
ok
end,
%% Pull the list of modules that are associated with Type operations. Each module %% Once we're all done processing, reset the code path to whatever
%% will be inspected for a function matching Command and if found, will execute that. %% the parent initialized it to
Modules = select_modules(rebar_config:get_modules(Config, Type), Command, []), restore_code_path(CurrentCodePath),
case Modules of ok.
%%
%% Give a list of module sets from rebar.app and a directory, find
%% the appropriate subset of modules for this directory
%%
choose_module_set([], _Dir) ->
none;
choose_module_set([{Fn, Modules} | Rest], Dir) ->
case ?MODULE:Fn(Dir) of
{true, File} ->
{ok, Modules, File};
false ->
choose_module_set(Rest, Dir)
end.
%%
%% Return .app file if the current directory is an OTP app
%%
app_dir(Dir) ->
rebar_app_utils:is_app_dir(Dir).
%%
%% Return the reltool.config file if the current directory is release directory
%%
rel_dir(Dir) ->
rebar_rel_utils:is_rel_dir(Dir).
apply_commands([], Modules, Config, ModuleFile) ->
ok;
apply_commands([Command | Rest], Modules, Config, ModuleFile) ->
case select_modules(Modules, Command, []) of
[] -> [] ->
%% None of the modules implement the command; move on to next target apply_commands(Rest, Modules, Config, ModuleFile);
apply_command(Rest, Command); TargetModules ->
_ ->
%% Provide some info on where we are %% Provide some info on where we are
Dir = rebar_utils:get_cwd(),
?CONSOLE("==> ~s (~s)\n", [filename:basename(Dir), Command]), ?CONSOLE("==> ~s (~s)\n", [filename:basename(Dir), Command]),
%% Run the available modules %% Run the available modules
case catch(run_modules(Modules, Command, Config, File)) of case catch(run_modules(TargetModules, Command, Config, ModuleFile)) of
ok -> ok ->
apply_command(Rest, Command); apply_commands(Rest, Modules, Config, ModuleFile);
{error, failed} -> {error, failed} ->
error; error;
Other -> Other ->
@ -204,10 +170,33 @@ apply_command([{Type, Dir, File} | Rest], Command) ->
end end
end. end.
update_code_path(Config) ->
case rebar_config:get(Config, lib_dirs, []) of
[] ->
no_change;
Paths ->
OldPath = code:get_path(),
LibPaths = expand_lib_dirs(Paths, rebar_utils:get_cwd(), []),
ok = code:add_pathsa(LibPaths),
{old, OldPath}
end.
restore_code_path(no_change) ->
ok;
restore_code_path({old, Path}) ->
true = code:set_path(Path),
ok.
expand_lib_dirs([], _Root, Acc) ->
Acc;
expand_lib_dirs([Dir | Rest], Root, Acc) ->
Apps = filelib:wildcard(filename:join([Dir, '*', ebin])),
FqApps = [filename:join([Root, A]) || A <- Apps],
expand_lib_dirs(Rest, Root, Apps ++ FqApps).
subdirs(Dirs, Acc) ->
lists:reverse(find_targets(Dirs, rebar_utils:get_cwd(), [], 1)).
select_modules([], _Command, Acc) -> select_modules([], _Command, Acc) ->
lists:reverse(Acc); lists:reverse(Acc);
@ -229,3 +218,5 @@ run_modules([Module | Rest], Command, Config, File) ->
{error, Reason} -> {error, Reason} ->
{error, Reason} {error, Reason}
end. end.