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

@ -25,17 +25,15 @@
%% Default log level
{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).
-export([new/1,
get_modules/2,
get_list/3,
get/3,
-export([new/0, new/1,
get/3, get_list/3,
delete/2,
set_global/2, get_global/2]).
-include("rebar.hrl").
@ -40,45 +39,34 @@
%% Public API
%% ===================================================================
new(Dir) ->
{ok, DefaultConfig} = application:get_env(rebar, default_config),
BaseDict = orddict:from_list(DefaultConfig),
new() ->
#config { dir = rebar_utils:get_cwd(),
opts = []}.
new(ParentConfig) ->
%% Load terms from rebar.config, if it exists
Dir = rebar_utils:get_cwd(),
ConfigFile = filename:join([Dir, "rebar.config"]),
case file:consult(ConfigFile) of
{ok, Terms} ->
Dict = merge_terms(Terms, BaseDict);
Opts = Terms ++ ParentConfig#config.opts;
{error, enoent} ->
Dict = BaseDict;
Opts = ParentConfig#config.opts;
Other ->
Opts = undefined, % Keep erlc happy
?WARN("Failed to load ~s: ~p\n", [ConfigFile, Other]),
?FAIL,
Dict = BaseDict
?FAIL
end,
#config { dir = Dir, opts = Dict }.
get_modules(Config, app) ->
get_list(Config, app_modules, []);
get_modules(Config, rel) ->
get_list(Config, rel_modules, []).
#config { dir = Dir, opts = Opts }.
get_list(Config, Key, Default) ->
case orddict:find(Key, Config#config.opts) of
error ->
Default;
{ok, List} ->
List
end.
get(Config, Key, Default).
get(Config, Key, Default) ->
case orddict:find(Key, Config#config.opts) of
error ->
Default;
{ok, Value} ->
Value
end.
proplists:get_value(Key, Config#config.opts, Default).
delete(Config, Key) ->
Config#config { opts = proplists:delete(Key, Config#config.opts) }.
set_global(Key, Value) ->
application:set_env(rebar_global, Key, Value).
@ -95,10 +83,3 @@ get_global(Key, Default) ->
%% ===================================================================
%% 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([app_dir/1, rel_dir/1]). % Ugh
-include("rebar.hrl").
%% ===================================================================
@ -37,29 +39,22 @@ run(Args) ->
%% command line arguments. What's left will be the commands to run.
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
ok = application:load(rebar),
%% Initialize logging system
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,
%% Convert command strings to atoms
CommandAtoms = [list_to_atom(C) || C <- Commands],
%% Prefix all the app targets to the code path so that inter-app compilation
%% works properly
update_code_path(Targets),
%% Finally, apply the specified command to each target
apply_commands(Targets, Commands).
%% Load rebar.config, if it exists
process_dir(rebar_utils:get_cwd(), rebar_config:new(), CommandAtoms).
%% ===================================================================
@ -86,116 +81,87 @@ filter_flags([Item | Rest], Commands) ->
end.
%%
%% 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) ->
process_dir(Dir, ParentConfig, Commands) ->
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
case subdirs(rebar_config:get_list(Config, subdirs, []), []) of
%% Save the current code path and then update it with
%% 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;
Subdirs ->
?DEBUG("Subdirs: ~p\n", [Subdirs]),
update_code_path(Subdirs),
case apply_command(Subdirs, Command) of
ok ->
ok = file:set_cwd(Dir);
error ->
?FAIL
end
%% Edge case: config is inherited, EXCEPT for sub_dir directives -- filter those out
FilteredConfig = rebar_config:delete(Config, sub_dirs),
[process_dir(filename:join(Dir, Subdir), FilteredConfig, Commands) || Subdir <- Subdirs],
ok = file:set_cwd(Dir)
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
%% will be inspected for a function matching Command and if found, will execute that.
Modules = select_modules(rebar_config:get_modules(Config, Type), Command, []),
case Modules of
%% Once we're all done processing, reset the code path to whatever
%% the parent initialized it to
restore_code_path(CurrentCodePath),
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_command(Rest, Command);
_ ->
apply_commands(Rest, Modules, Config, ModuleFile);
TargetModules ->
%% Provide some info on where we are
Dir = rebar_utils:get_cwd(),
?CONSOLE("==> ~s (~s)\n", [filename:basename(Dir), Command]),
%% Run the available modules
case catch(run_modules(Modules, Command, Config, File)) of
case catch(run_modules(TargetModules, Command, Config, ModuleFile)) of
ok ->
apply_command(Rest, Command);
apply_commands(Rest, Modules, Config, ModuleFile);
{error, failed} ->
error;
Other ->
@ -205,9 +171,32 @@ apply_command([{Type, Dir, File} | Rest], Command) ->
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) ->
lists:reverse(Acc);
@ -229,3 +218,5 @@ run_modules([Module | Rest], Command, Config, File) ->
{error, Reason} ->
{error, Reason}
end.