Merge pull request #268 from tuncer/code-path-regression

Fix #267 (code path regression)
This commit is contained in:
Jared Morrow 2014-04-24 07:15:10 -06:00
commit a838a2e487
16 changed files with 211 additions and 91 deletions

17
.gitignore vendored
View file

@ -1,12 +1,11 @@
*.beam /ebin/*.beam
rebar /rebar
*~ *~
*.orig *.orig
.*.swp .*.swp
rt.work /rt.work
.test /dialyzer_warnings
dialyzer_warnings /rebar.cmd
rebar.cmd /.eunit
.eunit /deps
deps /.rebar
.rebar/*

View file

@ -0,0 +1,19 @@
-module(code_path_no_recurse_rt).
-export([files/0,
run/1]).
files() ->
[
{copy, "../../rebar", "rebar"},
{copy, "rebar.config", "rebar.config"},
{copy, "src", "src"},
{copy, "test", "test"},
{copy, "deps", "deps"}
].
run(_Dir) ->
retest:log(info, "Compile project~n"),
{ok, _} = retest:sh("./rebar -v compile"),
retest:log(info, "Run eunit with referenced deps on the code path~n"),
{ok, _} = retest:sh("./rebar -v eunit"),
ok.

View file

@ -0,0 +1,12 @@
{application, bazdep,
[
{description, ""},
{vsn, "1"},
{registered, []},
{applications, [
kernel,
stdlib
]},
{mod, {bazdep, []}},
{env, []}
]}.

View file

@ -0,0 +1,6 @@
-module(bazdep).
-export([bazdep/0]).
bazdep() ->
bazdep.

View file

@ -0,0 +1,5 @@
-module(bazdep_tests).
-include_lib("eunit/include/eunit.hrl").
bazdep_test() ->
?assert(bazdep:bazdep() =:= bazdep).

View file

@ -0,0 +1 @@
{deps, [{bazdep, "1"}]}.

View file

@ -0,0 +1,12 @@
{application, foodep,
[
{description, ""},
{vsn, "1"},
{registered, []},
{applications, [
kernel,
stdlib
]},
{mod, {foodep, []}},
{env, []}
]}.

View file

@ -0,0 +1,6 @@
-module(foodep).
-export([foodep/0]).
foodep() ->
bazdep:bazdep() =:= bazdep.

View file

@ -0,0 +1,5 @@
-module(foodep_tests).
-include_lib("eunit/include/eunit.hrl").
foodep_test() ->
?assert(foodep:foodep()).

View file

@ -0,0 +1,12 @@
{application, unuseddep,
[
{description, ""},
{vsn, "1"},
{registered, []},
{applications, [
kernel,
stdlib
]},
{mod, {unuseddep, []}},
{env, []}
]}.

View file

@ -0,0 +1,6 @@
-module(unuseddep).
-export([unuseddep/0]).
unuseddep() ->
unuseddep.

View file

@ -0,0 +1 @@
{deps, [{foodep, "1"}]}.

View file

@ -0,0 +1,12 @@
{application, codepath,
[
{description, ""},
{vsn, "1"},
{registered, []},
{applications, [
kernel,
stdlib
]},
{mod, {codepath, []}},
{env, []}
]}.

View file

@ -0,0 +1,6 @@
-module(codepath).
-export([codepath/0]).
codepath() ->
foodep:foodep().

View file

@ -0,0 +1,12 @@
-module(codepath_tests).
-include_lib("eunit/include/eunit.hrl").
codepath_test() ->
?assertEqual({module, codepath}, code:ensure_loaded(codepath)),
?assertEqual({module, foodep}, code:ensure_loaded(foodep)),
?assertEqual({module, bazdep}, code:ensure_loaded(bazdep)),
?assert(codepath:codepath()).
unuseddep_test() ->
?assertEqual(non_existing, code:which(unuseddep)),
?assertEqual({error, nofile}, code:ensure_loaded(unuseddep)).

View file

@ -88,7 +88,7 @@ process_commands([Command | Rest], ParentConfig) ->
%% path from inside a subdirectory. %% path from inside a subdirectory.
true = rebar_utils:expand_code_path(), true = rebar_utils:expand_code_path(),
{ParentConfig2, _DirSet} = process_dir(rebar_utils:get_cwd(), {ParentConfig2, _DirSet} = process_dir(rebar_utils:get_cwd(),
ParentConfig1, Command, Command, ParentConfig1,
sets:new()), sets:new()),
case get_operations(ParentConfig2) of case get_operations(ParentConfig2) of
Operations -> Operations ->
@ -117,18 +117,13 @@ process_commands([Command | Rest], ParentConfig) ->
end, end,
process_commands(Rest, ParentConfig4). process_commands(Rest, ParentConfig4).
process_dir(Dir, ParentConfig, Command, DirSet) -> process_dir(Dir, Command, ParentConfig, DirSet) ->
case filelib:is_dir(Dir) of case filelib:is_dir(Dir) of
false -> false ->
?WARN("Skipping non-existent sub-dir: ~p\n", [Dir]), ?WARN("Skipping non-existent sub-dir: ~p\n", [Dir]),
{ParentConfig, DirSet}; {ParentConfig, DirSet};
true -> true ->
maybe_process_dir(Dir, ParentConfig, Command, DirSet) WouldCd = would_cd_into_dir(Dir, Command, ParentConfig),
end.
maybe_process_dir(Dir, ParentConfig, Command, DirSet) ->
case should_cd_into_dir(Dir, ParentConfig, Command) of
true ->
ok = file:set_cwd(Dir), ok = file:set_cwd(Dir),
Config = maybe_load_local_config(Dir, ParentConfig), Config = maybe_load_local_config(Dir, ParentConfig),
@ -143,21 +138,27 @@ maybe_process_dir(Dir, ParentConfig, Command, DirSet) ->
%% set of modules to process this dir. %% set of modules to process this dir.
{ok, AvailModuleSets} = application:get_env(rebar, modules), {ok, AvailModuleSets} = application:get_env(rebar, modules),
ModuleSet = choose_module_set(AvailModuleSets, Dir), ModuleSet = choose_module_set(AvailModuleSets, Dir),
skip_or_process_dir(ModuleSet, Config, CurrentCodePath, skip_or_process_dir(Dir, Command, Config, DirSet, CurrentCodePath,
Dir, Command, DirSet); ModuleSet, WouldCd)
false ->
{ParentConfig, DirSet}
end. end.
should_cd_into_dir(Dir, Config, Command) -> would_cd_into_dir(Dir, Command, Config) ->
case would_cd_into_dir1(Dir, Command, Config) of
true ->
would_cd;
false ->
would_not_cd
end.
would_cd_into_dir1(Dir, Command, Config) ->
rebar_utils:processing_base_dir(Config, Dir) orelse rebar_utils:processing_base_dir(Config, Dir) orelse
rebar_config:is_recursive(Config) orelse rebar_config:is_recursive(Config) orelse
is_recursive_command(Config, Command) orelse is_recursive_command(Command, Config) orelse
is_generate_in_rel_dir(Command, Dir). is_generate_in_rel_dir(Command, Dir).
%% Check whether the command is part of the built-in (or extended via %% Check whether the command is part of the built-in (or extended via
%% rebar.config) list of default-recursive commands. %% rebar.config) list of default-recursive commands.
is_recursive_command(Config, Command) -> is_recursive_command(Command, Config) ->
{ok, AppCmds} = application:get_env(rebar, recursive_cmds), {ok, AppCmds} = application:get_env(rebar, recursive_cmds),
ConfCmds = rebar_config:get_local(Config, recursive_cmds, []), ConfCmds = rebar_config:get_local(Config, recursive_cmds, []),
RecursiveCmds = AppCmds ++ ConfCmds, RecursiveCmds = AppCmds ++ ConfCmds,
@ -180,46 +181,46 @@ is_generate_in_rel_dir(generate, Dir) ->
is_generate_in_rel_dir(_, _) -> is_generate_in_rel_dir(_, _) ->
false. false.
skip_or_process_dir({[], undefined}=ModuleSet, Config, CurrentCodePath, skip_or_process_dir(Dir, Command, Config, DirSet, CurrentCodePath,
Dir, Command, DirSet) -> {[], undefined}=ModuleSet, WouldCd) ->
process_dir1(Dir, Command, DirSet, Config, CurrentCodePath, ModuleSet); process_dir1(Dir, Command, Config, DirSet, CurrentCodePath, ModuleSet,
skip_or_process_dir({_, ModuleSetFile}=ModuleSet, Config, CurrentCodePath, WouldCd);
Dir, Command, DirSet) -> skip_or_process_dir(Dir, Command, Config, DirSet, CurrentCodePath,
case lists:suffix(".app.src", ModuleSetFile) {_, File}=ModuleSet, WouldCd) ->
orelse lists:suffix(".app", ModuleSetFile) of case lists:suffix(".app.src", File)
orelse lists:suffix(".app", File) of
true -> true ->
%% .app or .app.src file, check if is_skipped_app %% .app or .app.src file, check if is_skipped_app
skip_or_process_dir1(ModuleSetFile, ModuleSet, skip_or_process_dir1(Dir, Command, Config, DirSet, CurrentCodePath,
Config, CurrentCodePath, Dir, ModuleSet, WouldCd, File);
Command, DirSet);
false -> false ->
%% not an app dir, no need to consider apps=/skip_apps= %% not an app dir, no need to consider apps=/skip_apps=
process_dir1(Dir, Command, DirSet, Config, process_dir1(Dir, Command, Config, DirSet, CurrentCodePath,
CurrentCodePath, ModuleSet) ModuleSet, WouldCd)
end. end.
skip_or_process_dir1(AppFile, ModuleSet, Config, CurrentCodePath, skip_or_process_dir1(Dir, Command, Config, DirSet, CurrentCodePath, ModuleSet,
Dir, Command, DirSet) -> WouldCd, AppFile) ->
case rebar_app_utils:is_skipped_app(Config, AppFile) of case rebar_app_utils:is_skipped_app(Config, AppFile) of
{Config1, {true, _SkippedApp}} when Command == 'update-deps' -> {Config1, {true, _SkippedApp}} when Command == 'update-deps' ->
%% update-deps does its own app skipping. Unfortunately there's no %% update-deps does its own app skipping. Unfortunately there's no
%% way to signal this to rebar_core, so we have to explicitly do it %% way to signal this to rebar_core, so we have to explicitly do it
%% here... Otherwise if you use app=, it'll skip the toplevel %% here... Otherwise if you use app=, it'll skip the toplevel
%% directory and nothing will be updated. %% directory and nothing will be updated.
process_dir1(Dir, Command, DirSet, Config1, process_dir1(Dir, Command, Config1, DirSet, CurrentCodePath,
CurrentCodePath, ModuleSet); ModuleSet, WouldCd);
{Config1, {true, SkippedApp}} -> {Config1, {true, SkippedApp}} ->
?DEBUG("Skipping app: ~p~n", [SkippedApp]), ?DEBUG("Skipping app: ~p~n", [SkippedApp]),
Config2 = increment_operations(Config1), {increment_operations(Config1), DirSet};
{Config2, DirSet};
{Config1, false} -> {Config1, false} ->
process_dir1(Dir, Command, DirSet, Config1, process_dir1(Dir, Command, Config1, DirSet, CurrentCodePath,
CurrentCodePath, ModuleSet) ModuleSet, WouldCd)
end. end.
process_dir1(Dir, Command, DirSet, Config, CurrentCodePath, process_dir1(Dir, Command, Config, DirSet, CurrentCodePath,
{DirModules, ModuleSetFile}) -> {DirModules, File}, WouldCd) ->
Config0 = rebar_config:set_xconf(Config, current_command, Command), Config0 = rebar_config:set_xconf(Config, current_command, Command),
%% Get the list of modules for "any dir". This is a catch-all list %% Get the list of modules for "any dir". This is a catch-all list
%% of modules that are processed in addition to modules associated %% of modules that are processed in addition to modules associated
%% with this directory type. These any_dir modules are processed %% with this directory type. These any_dir modules are processed
@ -230,8 +231,7 @@ process_dir1(Dir, Command, DirSet, Config, CurrentCodePath,
%% Invoke 'preprocess' on the modules -- this yields a list of other %% Invoke 'preprocess' on the modules -- this yields a list of other
%% directories that should be processed _before_ the current one. %% directories that should be processed _before_ the current one.
{Config1, Predirs} = acc_modules(Modules, preprocess, Config0, {Config1, Predirs} = acc_modules(Modules, preprocess, Config0, File),
ModuleSetFile),
%% Remember associated pre-dirs (used for plugin lookup) %% Remember associated pre-dirs (used for plugin lookup)
PredirsAssoc = remember_cwd_predirs(Dir, Predirs), PredirsAssoc = remember_cwd_predirs(Dir, Predirs),
@ -239,55 +239,33 @@ process_dir1(Dir, Command, DirSet, Config, CurrentCodePath,
%% Get the list of plug-in modules from rebar.config. These %% Get the list of plug-in modules from rebar.config. These
%% modules may participate in preprocess and postprocess. %% modules may participate in preprocess and postprocess.
{ok, PluginModules} = plugin_modules(Config1, PredirsAssoc), {ok, PluginModules} = plugin_modules(Config1, PredirsAssoc),
AllModules = Modules ++ PluginModules,
{Config2, PluginPredirs} = acc_modules(PluginModules, preprocess, {Config2, PluginPredirs} = acc_modules(PluginModules, preprocess, Config1,
Config1, ModuleSetFile), File),
AllPredirs = Predirs ++ PluginPredirs, AllPredirs = Predirs ++ PluginPredirs,
?DEBUG("Predirs: ~p\n", [AllPredirs]), ?DEBUG("Predirs: ~p\n", [AllPredirs]),
{Config3, DirSet2} = process_each(AllPredirs, Command, Config2, {Config3, DirSet2} = process_each(AllPredirs, Command, Config2, DirSet,
ModuleSetFile, DirSet), File),
%% Make sure the CWD is reset properly; processing the dirs may have %% Make sure the CWD is reset properly; processing the dirs may have
%% caused it to change %% caused it to change
ok = file:set_cwd(Dir), ok = file:set_cwd(Dir),
%% Check that this directory is not on the skip list %% Maybe apply command to Dir
Config7 = case rebar_config:is_skip_dir(Config3, Dir) of Config4 = maybe_execute(Dir, Command, Config3, Modules, PluginModules,
true -> AllModules, File, WouldCd),
%% Do not execute the command on the directory, as some
%% module has requested a skip on it.
?INFO("Skipping ~s in ~s\n", [Command, Dir]),
Config3;
false ->
%% Check for and get command specific environments
{Config4, Env} = setup_envs(Config3, Modules),
%% Execute any before_command plugins on this directory
Config5 = execute_pre(Command, PluginModules,
Config4, ModuleSetFile, Env),
%% Execute the current command on this directory
Config6 = execute(Command, Modules ++ PluginModules,
Config5, ModuleSetFile, Env),
%% Execute any after_command plugins on this directory
execute_post(Command, PluginModules,
Config6, ModuleSetFile, Env)
end,
%% Mark the current directory as processed %% Mark the current directory as processed
DirSet3 = sets:add_element(Dir, DirSet2), DirSet3 = sets:add_element(Dir, DirSet2),
%% Invoke 'postprocess' on the modules. This yields a list of other %% Invoke 'postprocess' on the modules. This yields a list of other
%% directories that should be processed _after_ the current one. %% directories that should be processed _after_ the current one.
{Config8, Postdirs} = acc_modules(Modules ++ PluginModules, postprocess, {Config5, Postdirs} = acc_modules(AllModules, postprocess, Config4, File),
Config7, ModuleSetFile),
?DEBUG("Postdirs: ~p\n", [Postdirs]), ?DEBUG("Postdirs: ~p\n", [Postdirs]),
Res = process_each(Postdirs, Command, Config8, Res = process_each(Postdirs, Command, Config5, DirSet3, File),
ModuleSetFile, DirSet3),
%% Make sure the CWD is reset properly; processing the dirs may have %% Make sure the CWD is reset properly; processing the dirs may have
%% caused it to change %% caused it to change
@ -300,6 +278,33 @@ process_dir1(Dir, Command, DirSet, Config, CurrentCodePath,
%% Return the updated {config, dirset} as result %% Return the updated {config, dirset} as result
Res. Res.
maybe_execute(Dir, Command, Config, Modules, PluginModules, AllModules, File,
would_cd) ->
%% Check that this directory is not on the skip list
case rebar_config:is_skip_dir(Config, Dir) of
true ->
%% Do not execute the command on the directory, as some
%% module has requested a skip on it.
?INFO("Skipping ~s in ~s\n", [Command, Dir]),
Config;
false ->
%% Check for and get command specific environments
{Config1, Env} = setup_envs(Config, Modules),
%% Execute any before_command plugins on this directory
Config2 = execute_pre(Command, PluginModules, Config1, File, Env),
%% Execute the current command on this directory
Config3 = execute(Command, AllModules, Config2, File, Env),
%% Execute any after_command plugins on this directory
execute_post(Command, PluginModules, Config3, File, Env)
end;
maybe_execute(_Dir, _Command, Config, _Modules, _PluginModules, _AllModules,
_File, would_not_cd) ->
Config.
remember_cwd_predirs(Cwd, Predirs) -> remember_cwd_predirs(Cwd, Predirs) ->
Store = fun(Dir, Dict) -> Store = fun(Dir, Dict) ->
case dict:find(Dir, Dict) of case dict:find(Dir, Dict) of
@ -330,21 +335,21 @@ maybe_load_local_config(Dir, ParentConfig) ->
%% Given a list of directories and a set of previously processed directories, %% Given a list of directories and a set of previously processed directories,
%% process each one we haven't seen yet %% process each one we haven't seen yet
%% %%
process_each([], _Command, Config, _ModuleSetFile, DirSet) -> process_each([], _Command, Config, DirSet, _File) ->
%% reset cached (setup_env) envs %% reset cached (setup_env) envs
Config1 = rebar_config:reset_envs(Config), Config1 = rebar_config:reset_envs(Config),
{Config1, DirSet}; {Config1, DirSet};
process_each([Dir | Rest], Command, Config, ModuleSetFile, DirSet) -> process_each([Dir | Rest], Command, Config, DirSet, File) ->
case sets:is_element(Dir, DirSet) of case sets:is_element(Dir, DirSet) of
true -> true ->
?DEBUG("Skipping ~s; already processed!\n", [Dir]), ?DEBUG("Skipping ~s; already processed!\n", [Dir]),
process_each(Rest, Command, Config, ModuleSetFile, DirSet); process_each(Rest, Command, Config, DirSet, File);
false -> false ->
{Config1, DirSet2} = process_dir(Dir, Config, Command, DirSet), {Config1, DirSet2} = process_dir(Dir, Command, Config, DirSet),
Config2 = rebar_config:clean_config(Config, Config1), Config2 = rebar_config:clean_config(Config, Config1),
%% reset cached (setup_env) envs %% reset cached (setup_env) envs
Config3 = rebar_config:reset_envs(Config2), Config3 = rebar_config:reset_envs(Config2),
process_each(Rest, Command, Config3, ModuleSetFile, DirSet2) process_each(Rest, Command, Config3, DirSet2, File)
end. end.
%% %%
@ -378,20 +383,21 @@ execute_post(Command, Modules, Config, ModuleFile, Env) ->
execute_plugin_hook(Hook, Command, Modules, Config, ModuleFile, Env) -> execute_plugin_hook(Hook, Command, Modules, Config, ModuleFile, Env) ->
HookFunction = list_to_atom(Hook ++ atom_to_list(Command)), HookFunction = list_to_atom(Hook ++ atom_to_list(Command)),
execute(HookFunction, Modules, Config, ModuleFile, Env). execute(HookFunction, hook, Modules, Config, ModuleFile, Env).
%% %%
%% Execute a command across all applicable modules %% Execute a command across all applicable modules
%% %%
execute(Command, Modules, Config, ModuleFile, Env) -> execute(Command, Modules, Config, ModuleFile, Env) ->
execute(Command, not_a_hook, Modules, Config, ModuleFile, Env).
execute(Command, Type, Modules, Config, ModuleFile, Env) ->
case select_modules(Modules, Command, []) of case select_modules(Modules, Command, []) of
[] -> [] ->
Cmd = atom_to_list(Command), case Type of
case lists:prefix("pre_", Cmd) hook ->
orelse lists:prefix("post_", Cmd) of
true ->
ok; ok;
false -> not_a_hook ->
?WARN("'~p' command does not apply to directory ~s\n", ?WARN("'~p' command does not apply to directory ~s\n",
[Command, rebar_utils:get_cwd()]) [Command, rebar_utils:get_cwd()])
end, end,