Merge pull request #588 from lrascao/fix/look_for_ct_spec_files_only_in_test_dir

Look for ct .spec files in the ct_dir that was specified
diff --git a/THANKS b/THANKS
index 630502a..ab7935c 100644
--- a/THANKS
+++ b/THANKS
@@ -148,3 +148,5 @@
 Sebastien Serre
 John Daily
 Yury Gargay
+Frank Hunleth
+Matwey Kornilov
diff --git a/inttest/cross1/c_src/test1.c b/inttest/cross1/c_src/test1.c
new file mode 100644
index 0000000..4073ed6
--- /dev/null
+++ b/inttest/cross1/c_src/test1.c
@@ -0,0 +1 @@
+#include "test1.h"
diff --git a/inttest/cross1/c_src/test1.h b/inttest/cross1/c_src/test1.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/inttest/cross1/c_src/test1.h
diff --git a/inttest/cross1/cross1_rt.erl b/inttest/cross1/cross1_rt.erl
new file mode 100644
index 0000000..297f353
--- /dev/null
+++ b/inttest/cross1/cross1_rt.erl
@@ -0,0 +1,66 @@
+%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*-
+%% ex: ts=4 sw=4 et
+%% -------------------------------------------------------------------
+%%
+%% rebar: Erlang Build Tools
+%%
+%% Copyright (c) 2016 Luis Rascao
+%%
+%% Permission is hereby granted, free of charge, to any person obtaining a copy
+%% of this software and associated documentation files (the "Software"), to deal
+%% in the Software without restriction, including without limitation the rights
+%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+%% copies of the Software, and to permit persons to whom the Software is
+%% furnished to do so, subject to the following conditions:
+%%
+%% The above copyright notice and this permission notice shall be included in
+%% all copies or substantial portions of the Software.
+%%
+%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+%% THE SOFTWARE.
+%% -------------------------------------------------------------------
+
+-module(cross1_rt).
+-export([files/0,
+         run/1]).
+
+-include_lib("eunit/include/eunit.hrl").
+
+setup([Target]) ->
+  retest_utils:load_module(filename:join(Target, "inttest_utils.erl")),
+  ok.
+
+files() ->
+    [
+     {copy, "c_src", "c_src"},
+     {create, "ebin/foo.app", app(foo, [])}
+    ] ++ inttest_utils:rebar_setup().
+
+run(_Dir) ->
+    %% we will now determine where gcc is located
+    %% create a symlink to it in the cwd and change the
+    %% rebar arch so that rebar is fooled into believing
+    %% it's doing cross compilation.
+    {ok, [_, GccLocation]} = retest_sh:run("which gcc", []),
+    {ok, _} = retest_sh:run(io_lib:format("ln -s ~s unknown-unknown-linux-gnu-gcc", [GccLocation]),
+                            []),
+    ?assertMatch({ok, _}, retest_sh:run("./rebar compile -vvv",
+                                        [{env, [{"PATH", "./:" ++ os:getenv("PATH")},
+                                                {"REBAR_TARGET_ARCH", "unknown-unknown-linux-gnu"}]}])).
+
+%%
+%% Generate the contents of a simple .app file
+%%
+app(Name, Modules) ->
+    App = {application, Name,
+           [{description, atom_to_list(Name)},
+            {vsn, "1"},
+            {modules, Modules},
+            {registered, []},
+            {applications, [kernel, stdlib]}]},
+    io_lib:format("~p.\n", [App]).
diff --git a/inttest/tdeps_prefer/a.erl b/inttest/tdeps_prefer/a.erl
new file mode 100644
index 0000000..bf413a6
--- /dev/null
+++ b/inttest/tdeps_prefer/a.erl
@@ -0,0 +1,4 @@
+-module({{module}}).
+
+-include_lib("b/include/b.hrl").
+-include_lib("c/include/c.hrl").
diff --git a/inttest/tdeps_prefer/a.rebar.config b/inttest/tdeps_prefer/a.rebar.config
new file mode 100644
index 0000000..41fe9bc
--- /dev/null
+++ b/inttest/tdeps_prefer/a.rebar.config
@@ -0,0 +1,4 @@
+{deps, [
+	{c, "1", {git, "../repo/c"}},
+	{b, "1", {git, "../repo/b"}}
+]}.
diff --git a/inttest/tdeps_prefer/b.hrl b/inttest/tdeps_prefer/b.hrl
new file mode 100644
index 0000000..9f02fab
--- /dev/null
+++ b/inttest/tdeps_prefer/b.hrl
@@ -0,0 +1 @@
+-define(HELLO, hello).
diff --git a/inttest/tdeps_prefer/c.hrl b/inttest/tdeps_prefer/c.hrl
new file mode 100644
index 0000000..152a99f
--- /dev/null
+++ b/inttest/tdeps_prefer/c.hrl
@@ -0,0 +1 @@
+-define(WORLD, world).
diff --git a/inttest/tdeps_prefer/root.rebar.config b/inttest/tdeps_prefer/root.rebar.config
new file mode 100644
index 0000000..d1c3793
--- /dev/null
+++ b/inttest/tdeps_prefer/root.rebar.config
@@ -0,0 +1 @@
+{sub_dirs, ["apps/a"]}.
diff --git a/inttest/tdeps_prefer/tdeps_prefer_rt.erl b/inttest/tdeps_prefer/tdeps_prefer_rt.erl
new file mode 100644
index 0000000..94c9b8f
--- /dev/null
+++ b/inttest/tdeps_prefer/tdeps_prefer_rt.erl
@@ -0,0 +1,68 @@
+%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*-
+%% ex: ts=4 sw=4 et
+-module(tdeps_prefer_rt).
+
+-compile(export_all).
+
+setup([Target]) ->
+    retest_utils:load_module(filename:join(Target, "inttest_utils.erl")),
+    ok.
+
+%% Test REBAR_DEPS_FORCE_LIB
+%% A -> [B, C]
+%% where B should be found globally and C should be found locally
+files() ->
+    [
+    %% A application
+    {create, "apps/a/ebin/a.app", app(a, [a])},
+    {copy, "a.rebar.config", "apps/a/rebar.config"},
+    {template, "a.erl", "apps/a/src/a.erl", dict:from_list([{module, a}])},
+
+    %% B application
+    {create, "libs/b-1/ebin/b.app", app(b, [])},
+    {copy, "b.hrl", "libs/b-1/include/b.hrl"},
+
+    %% C application
+    {create, "repo/c/ebin/c.app", app(c, [])},
+    {copy, "c.hrl", "repo/c/include/c.hrl"},
+
+    {copy, "root.rebar.config", "rebar.config"}
+    ] ++ inttest_utils:rebar_setup().
+
+apply_cmds([], _Params) ->
+    ok;
+apply_cmds([Cmd | Rest], Params) ->
+    io:format("Running: ~s (~p)\n", [Cmd, Params]),
+    {ok, _} = retest_sh:run(Cmd, Params),
+    apply_cmds(Rest, Params).
+
+run(Dir) ->
+    %% Initialize the c apps as git repos so that dependencies pull
+    %% properly
+    GitCmds = ["git init",
+               "git add -A",
+               "git config user.email 'tdeps@example.com'",
+               "git config user.name 'tdeps'",
+               "git commit -a -m \"Initial Commit\""],
+    ErlLibs = filename:join(Dir, "libs"),
+    ok = apply_cmds(GitCmds, [{dir, "repo/c"}]),
+    Env = [
+        {"REBAR_DEPS_PREFER_LIBS", "1"},
+        {"ERL_LIBS", ErlLibs}
+    ],
+
+    {ok, _} = retest_sh:run("./rebar -v get-deps", [{env, Env}]),
+    {ok, _} = retest_sh:run("./rebar -v compile", [{env, Env}]),
+    ok.
+
+%%
+%% Generate the contents of a simple .app file
+%%
+app(Name, Modules) ->
+    App = {application, Name,
+           [{description, atom_to_list(Name)},
+            {vsn, "1"},
+            {modules, Modules},
+            {registered, []},
+            {applications, [kernel, stdlib]}]},
+    io_lib:format("~p.\n", [App]).
diff --git a/inttest/vsn_cache/main.erl b/inttest/vsn_cache/main.erl
new file mode 100644
index 0000000..67b6465
--- /dev/null
+++ b/inttest/vsn_cache/main.erl
@@ -0,0 +1,13 @@
+-module(main).
+-behaviour(application).
+     
+-export([start/0,start/1,start/2,stop/1]).
+
+start() ->
+	start(permanent).
+start(_Restart) ->
+	ok.
+start(_Type,_Args) ->
+	ok.
+stop(_State) ->
+	ok.
diff --git a/inttest/vsn_cache/vsn_cache_rt.erl b/inttest/vsn_cache/vsn_cache_rt.erl
new file mode 100644
index 0000000..b482888
--- /dev/null
+++ b/inttest/vsn_cache/vsn_cache_rt.erl
@@ -0,0 +1,90 @@
+%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*-
+%% ex: ts=4 sw=4 et
+-module(vsn_cache_rt).
+
+-compile(export_all).
+
+setup([Target]) ->
+    retest_utils:load_module(filename:join(Target, "inttest_utils.erl")),
+    ok.
+
+files() ->
+    [
+    %% Cache save check
+    {create, "save/src/save.app.src", app(save, [main])},
+    {create, "save/vsn_cache_file", ""},
+    {copy, "main.erl", "save/src/main.erl"},
+
+    %% Cache load check
+    {create, "load/src/load.app.src", app(load, [main])},
+    {copy, "main.erl", "load/src/main.erl"}
+    ] ++ inttest_utils:rebar_setup().
+
+apply_cmds([], _Params) ->
+    ok;
+apply_cmds([Cmd | Rest], Params) ->
+    io:format("Running: ~s (~p)\n", [Cmd, Params]),
+    {ok, _} = retest_sh:run(Cmd, Params),
+    apply_cmds(Rest, Params).
+
+run_save(Dir) ->
+    GitCmds = ["git init",
+               "git add -A",
+               "git config user.email 'vsn_cache@example.com'",
+               "git config user.name 'vsn_cache'",
+               "git commit -a -m \"Initial Commit\""],
+    AppDir = filename:join(Dir, "save"),
+    EbinDir = filename:join(AppDir, "ebin"),
+    AppFile = filename:join(EbinDir, "save.app"),
+    VsnCacheFile = filename:join(AppDir, "vsn_cache_file"),
+    Env = [{"REBAR_VSN_CACHE_FILE", VsnCacheFile}],
+
+    %% Initialize test git repository 
+    ok = apply_cmds(GitCmds, [{dir, AppDir}]),
+    %% Compile test project with vsn cache enabled
+    {ok, _} = retest_sh:run("../rebar -v compile", [{env, Env}, {dir, AppDir}]),
+    %% Vsn cache file has an entry
+    {ok, [{{git, AppDir}, Hash}]} = file:consult(VsnCacheFile),
+    %% This vsn entry must coincide with entry from ebin/save.app
+    {ok, [{application, save, PropList}]} = file:consult(AppFile),
+    Hash = proplists:get_value(vsn, PropList),
+    ok.
+
+run_load(Dir) ->
+    AppDir = filename:join(Dir, "load"),
+    EbinDir = filename:join(AppDir, "ebin"),
+    AppFile = filename:join(EbinDir, "load.app"),
+    VsnCacheFile = filename:join(AppDir, "vsn_cache_file"),
+    Hash = "deadbeef",
+    CacheEntries = [{{git, AppDir}, Hash}],
+    Env = [{"REBAR_VSN_CACHE_FILE", VsnCacheFile}],
+
+    %% Initialize dummy vsn cache file
+    vsn_cache_file(VsnCacheFile, CacheEntries),
+    %% Compile test project with vsn cache enabled
+    {ok, _} = retest_sh:run("../rebar -v compile", [{env, Env}, {dir, AppDir}]),
+    %% This vsn entry in cache file must coincide with entry in ebin/load.app
+    {ok, [{application, load, PropList}]} = file:consult(AppFile),
+    Hash = proplists:get_value(vsn, PropList),
+    ok.
+
+run(Dir) ->
+    run_save(Dir),
+    run_load(Dir),
+    ok.
+
+%%
+%% Generate the contents of a simple .app file
+%%
+app(Name, Modules) ->
+    App = {application, Name,
+           [{description, atom_to_list(Name)},
+            {vsn, git},
+            {modules, Modules},
+            {registered, []},
+            {applications, [kernel, stdlib]}]},
+    io_lib:format("~p.\n", [App]).
+
+vsn_cache_file(Name, Entries) ->
+    file:write_file(Name,
+        [io_lib:format("~p.~n", [X]) || X <- Entries]).
diff --git a/src/rebar.erl b/src/rebar.erl
index 2fceb19..5a809d2 100644
--- a/src/rebar.erl
+++ b/src/rebar.erl
@@ -143,7 +143,7 @@
     %% Keep track of how many operations we do, so we can detect bad commands
     BaseConfig1 = rebar_config:set_xconf(BaseConfig, operations, 0),
     %% Initialize vsn cache
-    rebar_config:set_xconf(BaseConfig1, vsn_cache, dict:new()).
+    rebar_utils:init_vsn_cache(BaseConfig1).
 
 init_config1(BaseConfig) ->
     %% Determine the location of the rebar executable; important for pulling
@@ -284,7 +284,12 @@
                       {"freebsd", compile, "c_src/freebsd_tweaks.sh"},
                       {eunit, "touch file2.out"},
                       {compile, "touch postcompile.out"}]}
-       ]).
+       ]),
+    ?CONSOLE(
+       "Environment variables:~n"
+       "  REBAR_DEPS_PREFER_LIBS to look for dependecies in system libs prior fetching.~n"
+       "  REBAR_VSN_CACHE_FILE to load vsn cache from and save to specified file.~n"
+       "~n", []).
 
 %%
 %% Parse command line arguments using getopt and also filtering out any
diff --git a/src/rebar_core.erl b/src/rebar_core.erl
index 0650430..6cc8d38 100644
--- a/src/rebar_core.erl
+++ b/src/rebar_core.erl
@@ -106,7 +106,7 @@
                                                       ParentConfig2),
             %% Wipe out vsn cache to avoid invalid hits when
             %% dependencies are updated
-            rebar_config:set_xconf(ParentConfig3, vsn_cache, dict:new())
+            rebar_utils:init_vsn_cache(ParentConfig3)
         catch
             throw:rebar_abort ->
                 case rebar_config:get_xconf(ParentConfig1, keep_going, false) of
diff --git a/src/rebar_deps.erl b/src/rebar_deps.erl
index a6e5b27..995ce97 100644
--- a/src/rebar_deps.erl
+++ b/src/rebar_deps.erl
@@ -58,16 +58,19 @@
     %% used globally since it will be set on the first time through here
     Config1 = set_shared_deps_dir(Config, get_shared_deps_dir(Config, [])),
 
+    %% Check whether user forced deps resolution via system wide libs
+    Config2 = set_deps_prefer_libs(Config1, get_deps_prefer_libs(Config1, undefined)),
+
     %% Get the list of deps for the current working directory and identify those
     %% deps that are available/present.
-    Deps = rebar_config:get_local(Config1, deps, []),
-    {Config2, {AvailableDeps, MissingDeps}} = find_deps(Config1, find, Deps),
+    Deps = rebar_config:get_local(Config2, deps, []),
+    {Config3, {AvailableDeps, MissingDeps}} = find_deps(Config2, find, Deps),
 
     ?DEBUG("Available deps: ~p\n", [AvailableDeps]),
     ?DEBUG("Missing deps  : ~p\n", [MissingDeps]),
 
     %% Add available deps to code path
-    Config3 = update_deps_code_path(Config2, AvailableDeps),
+    Config4 = update_deps_code_path(Config3, AvailableDeps),
 
     %% Filtering out 'raw' dependencies so that no commands other than
     %% deps-related can be executed on their directories.
@@ -83,8 +86,8 @@
                           fun(D, Acc) ->
                                   rebar_config:set_skip_dir(Acc, D#dep.dir)
                           end,
-                          Config3,
-                          collect_deps(rebar_utils:get_cwd(), Config3)),
+                          Config4,
+                          collect_deps(rebar_utils:get_cwd(), Config4)),
             %% Return the empty list, as we don't want anything processed before
             %% us.
             {ok, NewConfig, []};
@@ -97,12 +100,12 @@
             %% Also, if skip_deps=comma,separated,app,list, then only the given
             %% dependencies are skipped.
             NewConfig =
-                case rebar_config:get_global(Config3, skip_deps, false) of
+                case rebar_config:get_global(Config4, skip_deps, false) of
                     "true" ->
                         lists:foldl(
                           fun(#dep{dir = Dir}, C) ->
                                   rebar_config:set_skip_dir(C, Dir)
-                          end, Config3, AvailableDeps);
+                          end, Config4, AvailableDeps);
                     Apps when is_list(Apps) ->
                         SkipApps = [list_to_atom(App) ||
                                        App <- string:tokens(Apps, ",")],
@@ -112,9 +115,9 @@
                                       true -> rebar_config:set_skip_dir(C, Dir);
                                       false -> C
                                   end
-                          end, Config3, AvailableDeps);
+                          end, Config4, AvailableDeps);
                     _ ->
-                        Config3
+                        Config4
                 end,
 
             %% Return all the available dep directories for process
@@ -254,7 +257,9 @@
        "  ~p~n"
        "  ~p~n"
        "Valid command line options:~n"
-       "  deps_dir=\"deps\" (override default or rebar.config deps_dir)~n",
+       "  deps_dir=\"deps\" (override default or rebar.config deps_dir)~n"
+       "Environment variables:~n"
+       "  REBAR_DEPS_PREFER_LIBS to look for dependecies in system libs prior fetching.~n",
        [
         Description,
         {deps_dir, "deps"},
@@ -302,6 +307,18 @@
 get_shared_deps_dir(Config, Default) ->
     rebar_config:get_xconf(Config, deps_dir, Default).
 
+set_deps_prefer_libs(Config, undefined) ->
+    DepsPreferLibs = case os:getenv("REBAR_DEPS_PREFER_LIBS") of
+        false -> false;
+        _ -> true
+    end,
+    rebar_config:set_xconf(Config, deps_prefer_libs, DepsPreferLibs);
+set_deps_prefer_libs(Config, _DepsPreferLibs) ->
+    Config.
+
+get_deps_prefer_libs(Config, Default) ->
+    rebar_config:get_xconf(Config, deps_prefer_libs, Default).
+
 get_deps_dir(Config) ->
     get_deps_dir(Config, "").
 
@@ -378,9 +395,15 @@
     %% e.g. {git, "https://github.com/mochi/mochiweb.git", "HEAD"}
     %% Deps with a source must be found (or fetched) locally.
     %% Those without a source may be satisfied from lib dir (get_lib_dir).
-    find_dep(Config, Dep, Dep#dep.source).
+    DepsPreferLibs = get_deps_prefer_libs(Config, false),
+    Mode = case {Dep#dep.source, DepsPreferLibs} of
+        {undefined, _DepsPreferLibs} -> maybe_in_lib;
+        {_DepSource, true} -> maybe_in_lib;
+        {_DepSource, false} -> local_only
+    end,
+    find_dep(Config, Dep, Mode).
 
-find_dep(Config, Dep, undefined) ->
+find_dep(Config, Dep, maybe_in_lib) ->
     %% 'source' is undefined.  If Dep is not satisfied locally,
     %% go ahead and find it amongst the lib_dir's.
     case find_dep_in_dir(Config, Dep, get_deps_dir(Config, Dep#dep.app)) of
@@ -389,7 +412,7 @@
         {Config1, {missing, _}} ->
             find_dep_in_dir(Config1, Dep, get_lib_dir(Dep#dep.app))
     end;
-find_dep(Config, Dep, _Source) ->
+find_dep(Config, Dep, local_only) ->
     %% _Source is defined.  Regardless of what it is, we must find it
     %% locally satisfied or fetch it from the original source
     %% into the project's deps
diff --git a/src/rebar_utils.erl b/src/rebar_utils.erl
index 64595a2..c3ebfe5 100644
--- a/src/rebar_utils.erl
+++ b/src/rebar_utils.erl
@@ -67,7 +67,9 @@
          processing_base_dir/1,
          processing_base_dir/2,
          patch_env/2,
-         cleanup_code_path/1
+         cleanup_code_path/1,
+         init_vsn_cache/1,
+         save_vsn_cache/1
         ]).
 
 %% for internal use only
@@ -268,6 +270,23 @@
             re:replace(InStr, RegEx, [VarValue, "\\2"], ReOpts)
     end.
 
+init_vsn_cache(Config) ->
+    init_vsn_cache(Config, os:getenv("REBAR_VSN_CACHE_FILE")).
+init_vsn_cache(Config, false) ->
+    rebar_config:set_xconf(Config, vsn_cache, dict:new());
+init_vsn_cache(Config, CacheFile) ->
+    {ok, CacheList} = file:consult(CacheFile),
+    CacheDict = dict:from_list(CacheList),
+    rebar_config:set_xconf(Config, vsn_cache, CacheDict).
+
+save_vsn_cache(Config) ->
+    save_vsn_cache(Config, os:getenv("REBAR_VSN_CACHE_FILE")).
+save_vsn_cache(_Config, false) ->
+    ok;
+save_vsn_cache(Config, CacheFile) ->
+    file:write_file(CacheFile,
+        [io_lib:format("~p.~n", [X]) || X <- dict:to_list(rebar_config:get_xconf(Config, vsn_cache))]).
+
 vcs_vsn(Config, Vsn, Dir) ->
     Key = {Vsn, Dir},
     Cache = rebar_config:get_xconf(Config, vsn_cache),
@@ -276,6 +295,7 @@
             VsnString = vcs_vsn_1(Vsn, Dir),
             Cache1 = dict:store(Key, VsnString, Cache),
             Config1 = rebar_config:set_xconf(Config, vsn_cache, Cache1),
+            save_vsn_cache(Config1),
             {Config1, VsnString};
         {ok, VsnString} ->
             {Config, VsnString}
@@ -760,7 +780,7 @@
                          >>),
     Cmd = Compiler ++ " -DTYPE=\""++Type++"\" " ++ TempFile,
     ShOpts = [{use_stdout, false}, return_on_error],
-    {ok, Res} = sh(Cmd, ShOpts),
+    {error, {_,Res}} = sh(Cmd, ShOpts),
     ok = file:delete(TempFile),
     case string:tokens(Res, ":") of
         [_, Ln | _] ->