diff --git a/ebin/rebar.app b/ebin/rebar.app index bca0abc..294ec2b 100644 --- a/ebin/rebar.app +++ b/ebin/rebar.app @@ -24,18 +24,16 @@ {env, [ %% 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 ]} + ]} ]} ]}. diff --git a/src/rebar_config.erl b/src/rebar_config.erl index 199461c..f9813c2 100644 --- a/src/rebar_config.erl +++ b/src/rebar_config.erl @@ -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). diff --git a/src/rebar_core.erl b/src/rebar_core.erl index 7a40200..c76d738 100644 --- a/src/rebar_core.erl +++ b/src/rebar_core.erl @@ -26,6 +26,8 @@ -export([run/1]). +-export([app_dir/1, rel_dir/1]). % Ugh + -include("rebar.hrl"). %% =================================================================== @@ -36,30 +38,23 @@ run(Args) -> %% 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. 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, - %% Prefix all the app targets to the code path so that inter-app compilation - %% works properly - update_code_path(Targets), + %% Convert command strings to atoms + CommandAtoms = [list_to_atom(C) || C <- Commands], - %% 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). %% =================================================================== @@ -84,118 +79,89 @@ filter_flags([Item | Rest], Commands) -> ?CONSOLE("Ignoring command line argument: ~p\n", [Other]), filter_flags(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 -> @@ -204,10 +170,33 @@ apply_command([{Type, Dir, File} | Rest], Command) -> 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) -> lists:reverse(Acc); @@ -229,3 +218,5 @@ run_modules([Module | Rest], Command, Config, File) -> {error, Reason} -> {error, Reason} end. + +