From d983beafe93a0f58272d3351f12329984541878f Mon Sep 17 00:00:00 2001
From: Andrew Thompson <andrew@hijacked.us>
Date: Wed, 16 Oct 2013 19:48:00 -0400
Subject: Don't over-aggressively clean the code path in the presence of
 lib_dir directives

Rebar, when it encounters a lib_dir directive, caches the current code
path, adds the libdir(s) and returns the cached copy of the path. When
rebar has finished processing that directory, it restores the cached
path. This is problematic in the below scenario:

        /(lib_dir)->G
  A -> B -> C -> D -> E
   \-> F -> D -> E

When rebar is finished processing B, it restores the code path to what
it was before it processed B, removing C, D, E and G from the code path.
This means when it comes to process F, neither D or E are in the code
path, so any header includes, rebar plugins or parse transforms will not
be in the code path. Without the lib_dir directive, rebar does no code
path cleanups, so everything works fine.

This change makes rebar only remove the explicit lib_dir code paths it
added and adds an inttest that replicates the above scenario.
---
 inttest/tdeps3/a.erl             |  3 ++
 inttest/tdeps3/a.rebar.config    |  4 ++
 inttest/tdeps3/b.hrl             |  1 +
 inttest/tdeps3/b.rebar.config    |  5 +++
 inttest/tdeps3/c.hrl             |  1 +
 inttest/tdeps3/c.rebar.config    |  1 +
 inttest/tdeps3/d.hrl             |  1 +
 inttest/tdeps3/d.rebar.config    |  1 +
 inttest/tdeps3/e.hrl             |  1 +
 inttest/tdeps3/f.hrl             |  1 +
 inttest/tdeps3/root.rebar.config |  1 +
 inttest/tdeps3/tdeps3_rt.erl     | 89 ++++++++++++++++++++++++++++++++++++++++
 12 files changed, 109 insertions(+)
 create mode 100644 inttest/tdeps3/a.erl
 create mode 100644 inttest/tdeps3/a.rebar.config
 create mode 100644 inttest/tdeps3/b.hrl
 create mode 100644 inttest/tdeps3/b.rebar.config
 create mode 100644 inttest/tdeps3/c.hrl
 create mode 100644 inttest/tdeps3/c.rebar.config
 create mode 100644 inttest/tdeps3/d.hrl
 create mode 100644 inttest/tdeps3/d.rebar.config
 create mode 100644 inttest/tdeps3/e.hrl
 create mode 100644 inttest/tdeps3/f.hrl
 create mode 100644 inttest/tdeps3/root.rebar.config
 create mode 100644 inttest/tdeps3/tdeps3_rt.erl

(limited to 'inttest')

diff --git a/inttest/tdeps3/a.erl b/inttest/tdeps3/a.erl
new file mode 100644
index 0000000..5a387eb
--- /dev/null
+++ b/inttest/tdeps3/a.erl
@@ -0,0 +1,3 @@
+-module({{module}}).
+
+-include_lib("{{dep}}/include/{{dep}}.hrl").
diff --git a/inttest/tdeps3/a.rebar.config b/inttest/tdeps3/a.rebar.config
new file mode 100644
index 0000000..19b8ef8
--- /dev/null
+++ b/inttest/tdeps3/a.rebar.config
@@ -0,0 +1,4 @@
+{deps, [
+	{b, "1", {git, "../repo/b"}},
+	{f, "1", {git, "../repo/f"}}
+]}.
diff --git a/inttest/tdeps3/b.hrl b/inttest/tdeps3/b.hrl
new file mode 100644
index 0000000..efbeab1
--- /dev/null
+++ b/inttest/tdeps3/b.hrl
@@ -0,0 +1 @@
+-include_lib("c/include/c.hrl").
diff --git a/inttest/tdeps3/b.rebar.config b/inttest/tdeps3/b.rebar.config
new file mode 100644
index 0000000..d1ccae2
--- /dev/null
+++ b/inttest/tdeps3/b.rebar.config
@@ -0,0 +1,5 @@
+{deps, [
+	{c, "1", {git, "../repo/c"}}
+]}.
+
+{lib_dirs, [apps]}.
diff --git a/inttest/tdeps3/c.hrl b/inttest/tdeps3/c.hrl
new file mode 100644
index 0000000..cc87fff
--- /dev/null
+++ b/inttest/tdeps3/c.hrl
@@ -0,0 +1 @@
+-include_lib("d/include/d.hrl").
diff --git a/inttest/tdeps3/c.rebar.config b/inttest/tdeps3/c.rebar.config
new file mode 100644
index 0000000..b590771
--- /dev/null
+++ b/inttest/tdeps3/c.rebar.config
@@ -0,0 +1 @@
+{deps, [{d, "1", {git, "../repo/d"}}]}.
diff --git a/inttest/tdeps3/d.hrl b/inttest/tdeps3/d.hrl
new file mode 100644
index 0000000..02f8088
--- /dev/null
+++ b/inttest/tdeps3/d.hrl
@@ -0,0 +1 @@
+-include_lib("e/include/e.hrl").
diff --git a/inttest/tdeps3/d.rebar.config b/inttest/tdeps3/d.rebar.config
new file mode 100644
index 0000000..4c7cd54
--- /dev/null
+++ b/inttest/tdeps3/d.rebar.config
@@ -0,0 +1 @@
+{deps, [{e, "1", {git, "../repo/e"}}]}.
diff --git a/inttest/tdeps3/e.hrl b/inttest/tdeps3/e.hrl
new file mode 100644
index 0000000..9f02fab
--- /dev/null
+++ b/inttest/tdeps3/e.hrl
@@ -0,0 +1 @@
+-define(HELLO, hello).
diff --git a/inttest/tdeps3/f.hrl b/inttest/tdeps3/f.hrl
new file mode 100644
index 0000000..02f8088
--- /dev/null
+++ b/inttest/tdeps3/f.hrl
@@ -0,0 +1 @@
+-include_lib("e/include/e.hrl").
diff --git a/inttest/tdeps3/root.rebar.config b/inttest/tdeps3/root.rebar.config
new file mode 100644
index 0000000..d1c3793
--- /dev/null
+++ b/inttest/tdeps3/root.rebar.config
@@ -0,0 +1 @@
+{sub_dirs, ["apps/a"]}.
diff --git a/inttest/tdeps3/tdeps3_rt.erl b/inttest/tdeps3/tdeps3_rt.erl
new file mode 100644
index 0000000..da87d43
--- /dev/null
+++ b/inttest/tdeps3/tdeps3_rt.erl
@@ -0,0 +1,89 @@
+-module(tdeps3_rt).
+
+-compile(export_all).
+
+%% Exercise transitive dependencies where there are multiple files
+%% depending on the same set of deps as well as lib_dir directives
+%% A -> B -> C -> D -> E
+%%      |--> G(via lib_dir)
+%% |--> F -> D -> E
+
+files() ->
+    [
+     %% A1 application
+     {create, "ebin/a.app", app(a, [a])},
+     {template, "a.erl", "src/a.erl", dict:from_list([{module, a}, {dep, b}])},
+
+     {copy, "a.rebar.config", "rebar.config"},
+     {copy, "../../rebar", "rebar"},
+
+     %% B application
+     {create, "repo/b/ebin/b.app", app(b, [b])},
+     {template, "a.erl", "repo/b/src/b.erl", dict:from_list([{module, b}, {dep, b}])},
+     {copy, "b.rebar.config", "repo/b/rebar.config"},
+     {copy, "b.hrl", "repo/b/include/b.hrl"},
+
+     %% C application
+     {create, "repo/c/ebin/c.app", app(c, [c])},
+     {template, "a.erl", "repo/c/src/c.erl", dict:from_list([{module, c}, {dep, d}])},
+     {copy, "c.rebar.config", "repo/c/rebar.config"},
+     {copy, "c.hrl", "repo/c/include/c.hrl"},
+
+     %% D application
+     {create, "repo/d/ebin/d.app", app(d, [d])},
+     {template, "a.erl", "repo/d/src/d.erl", dict:from_list([{module, d}, {dep, e}])},
+     {copy, "d.rebar.config", "repo/d/rebar.config"},
+     {copy, "d.hrl", "repo/d/include/d.hrl"},
+
+     %% E application
+     {create, "repo/e/ebin/e.app", app(e, [])},
+     {copy, "e.hrl", "repo/e/include/e.hrl"},
+
+
+     %% F application
+     {create, "repo/f/ebin/f.app", app(f, [f])},
+     {template, "a.erl", "repo/f/src/f.erl", dict:from_list([{module, f}, {dep, d}])},
+     {copy, "c.rebar.config", "repo/f/rebar.config"},
+     {copy, "f.hrl", "repo/f/include/f.hrl"},
+
+     %% G application, which is part of the B repo, in a lib_dir
+     {create, "repo/b/apps/g/ebin/g.app", app(g, [])},
+     {copy, "e.hrl", "repo/b/apps/g/include/g.hrl"}
+
+    ].
+
+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 b/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'"],
+    ok = apply_cmds(GitCmds, [{dir, "repo/b"}]),
+    ok = apply_cmds(GitCmds, [{dir, "repo/c"}]),
+    ok = apply_cmds(GitCmds, [{dir, "repo/d"}]),
+    ok = apply_cmds(GitCmds, [{dir, "repo/e"}]),
+    ok = apply_cmds(GitCmds, [{dir, "repo/f"}]),
+
+    {ok, _} = retest_sh:run("./rebar -v get-deps compile", []),
+    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]).
-- 
cgit v1.1