Skip to content

Commit f06f151

Browse files
committed
Clean up code, add tests, fix bugs found in tests
1 parent 0cbf50c commit f06f151

File tree

10 files changed

+227
-65
lines changed

10 files changed

+227
-65
lines changed

lib/mix/lib/mix/deps.ex

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,9 @@ defmodule Mix.Deps do
9393
changing the current working directory and loading the given
9494
project into the project stack.
9595
"""
96-
def in_dependency(Mix.Dep[app: app, opts: opts], post_config // [], fun) do
96+
def in_dependency(dep, post_config // [], fun)
97+
98+
def in_dependency(Mix.Dep[app: app, opts: opts, rebar: nil], post_config, fun) do
9799
env = opts[:env] || :prod
98100
old_env = Mix.env
99101

@@ -105,6 +107,17 @@ defmodule Mix.Deps do
105107
end
106108
end
107109

110+
def in_dependency(Mix.Dep[opts: opts], post_config, fun) do
111+
# Use post_config for rebar deps
112+
Mix.Project.post_config(post_config)
113+
Mix.Project.push(Mix.Rebar.Mixproject)
114+
try do
115+
File.cd!(opts[:dest], fn -> fun.(nil) end)
116+
after
117+
Mix.Project.pop
118+
end
119+
end
120+
108121
@doc """
109122
Formats the status of a dependency.
110123
"""
@@ -229,11 +242,11 @@ defmodule Mix.Deps do
229242
Enum.uniq paths
230243
rebar?(dep) ->
231244
# Add root dir and all sub dirs with ebin/ directory
232-
[ opts[:dest] | dep.rebar[:sub_dirs] ]
233-
|> Enum.map(Path.wildcard(&1))
234-
|> List.concat
235-
|> Enum.map(fn path -> Path.join([opts[:dest], path, "ebin"]) end)
236-
|> Enum.filter(File.dir?(&1))
245+
[ opts[:dest] | (dep.rebar[:sub_dirs] || []) ]
246+
|> Enum.map(Path.wildcard(&1))
247+
|> List.concat
248+
|> Enum.map(fn path -> Path.join([opts[:dest], path, "ebin"]) end)
249+
|> Enum.filter(File.dir?(&1))
237250
true ->
238251
[ Path.join(opts[:dest], "ebin") ]
239252
end

lib/mix/lib/mix/deps/retriever.ex

Lines changed: 28 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ defmodule Mix.Deps.Retriever do
1414
provides a dependency in the wrong format.
1515
"""
1616
def all(post_config // []) do
17-
{ deps, _ } = all(nil, children, post_config, fn(dep, acc) -> { dep, acc } end)
17+
{ deps, _ } = all(nil, children(post_config), post_config, fn(dep, acc) -> { dep, acc } end)
1818
deps
1919
end
2020

@@ -24,7 +24,7 @@ defmodule Mix.Deps.Retriever do
2424
in case some processing is done.
2525
"""
2626
def all(rest, post_config // [], callback) do
27-
all(rest, children, post_config, callback)
27+
all(rest, children(post_config), post_config, callback)
2828
end
2929

3030
defp all(rest, childs, post_config, callback) do
@@ -34,7 +34,7 @@ defmodule Mix.Deps.Retriever do
3434
cond do
3535
Mix.Deps.available?(dep) and mixfile?(dep) ->
3636
Mix.Deps.in_dependency(dep, post_config, fn project ->
37-
{ deps, rest } = all(rest, children, [], callback)
37+
{ deps, rest } = all(rest, children(post_config), post_config, callback)
3838

3939
# We need to call with_mix_project once again
4040
# here in case the dependency was not available
@@ -44,10 +44,12 @@ defmodule Mix.Deps.Retriever do
4444
end)
4545

4646
Mix.Deps.available?(dep) and rebarconfig?(dep) ->
47-
dir = dep.opts[:dest]
48-
config = Mix.Rebar.load_config(dir)
49-
{ deps, rest } = all(rest, rebar_children(dir), [], callback)
50-
{ dep.rebar(config).deps(deps), rest }
47+
dep = rebar_dep(dep)
48+
49+
Mix.Deps.in_dependency(dep, post_config, fn _ ->
50+
{ deps, rest } = all(rest, rebar_children("."), post_config, callback)
51+
{ dep.deps(deps), rest }
52+
end)
5153

5254
true ->
5355
{ dep, rest }
@@ -60,8 +62,8 @@ defmodule Mix.Deps.Retriever do
6062
as a `Mix.Dep` record. Unlike with `all` the `deps`
6163
field is not populated.
6264
"""
63-
def children() do
64-
Mix.Project.recur(fn _ ->
65+
def children(post_config // []) do
66+
Mix.Project.recur(post_config, fn _ ->
6567
(Mix.project[:deps] || []) |> setup_deps
6668
end) |> List.concat
6769
end
@@ -87,22 +89,20 @@ defmodule Mix.Deps.Retriever do
8789
Enum.map deps, fn dep ->
8890
dep = with_scm_and_status(dep, scms)
8991

90-
cond do
91-
Mix.Deps.available?(dep) and mixfile?(dep) ->
92-
Mix.Deps.in_dependency(dep, fn project ->
93-
with_mix_project(dep, project)
94-
end)
95-
Mix.Deps.available?(dep) and rebarconfig?(dep) ->
96-
config = Mix.Rebar.load_config(dep.opts[:dest])
97-
with_rebar_config(dep, config)
98-
true ->
99-
dep
92+
if Mix.Deps.available?(dep) and mixfile?(dep) do
93+
dep = Mix.Deps.in_dependency(dep, fn project ->
94+
with_mix_project(dep, project)
95+
end)
10096
end
97+
98+
if Mix.Deps.available?(dep) and rebarconfig?(dep) do
99+
dep = rebar_dep(dep)
100+
end
101+
102+
dep
101103
end
102104
end
103105

104-
defp with_rebar_config(dep, _config), do: dep
105-
106106
defp with_mix_project(Mix.Dep[project: nil] = dep, project) do
107107
if match?({ :noappfile, _ }, dep.status) and Mix.Project.umbrella? do
108108
dep = dep.update_opts(Keyword.put(&1, :app, false))
@@ -113,6 +113,13 @@ defmodule Mix.Deps.Retriever do
113113

114114
defp with_mix_project(dep, _project), do: dep
115115

116+
defp rebar_dep(Mix.Dep[rebar: nil, opts: opts] = dep) do
117+
config = Mix.Rebar.load_config(opts[:dest])
118+
dep.rebar(config)
119+
end
120+
121+
defp rebar_dep(dep), do: dep
122+
116123
defp with_scm_and_status({ app, opts }, scms) when is_atom(app) and is_list(opts) do
117124
with_scm_and_status({ app, nil, opts }, scms)
118125
end

lib/mix/lib/mix/project.ex

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ defmodule Mix.Project do
149149
use this function to transparently go through the project, regardless
150150
if it is an umbrella project or not.
151151
"""
152-
def recur(fun) do
152+
def recur(post_config // [], fun) do
153153
if apps_path = config[:apps_path] do
154154
paths = Path.wildcard(Path.join(apps_path, "*"))
155155
paths = Enum.filter(paths, File.dir?(&1))
@@ -163,11 +163,12 @@ defmodule Mix.Project do
163163
projects = topsort_projects(projects, Path.expand(apps_path))
164164

165165
results = Enum.map projects, fn { app, app_path } ->
166-
in_project(app, app_path, fun)
166+
in_project(app, app_path, post_config, fun)
167167
end
168168

169169
results
170170
else
171+
# Note that post_config isnt used for this case
171172
[fun.(get)]
172173
end
173174
end

lib/mix/lib/mix/rebar.ex

Lines changed: 53 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -31,29 +31,8 @@ defmodule Mix.Rebar do
3131
"""
3232
def deps(config) do
3333
if deps = config[:deps] do
34-
Enum.map(deps, fn({ app, req, source }) ->
35-
scm = case source do
36-
{ scm, url } -> [{ scm, to_binary(url) }]
37-
{ scm, url, _ } -> [{ scm, to_binary(url) }]
38-
end
39-
40-
ref = case source do
41-
{ _, _, "" } -> [branch: "HEAD"]
42-
{ _, _, { :branch, branch } } -> [branch: to_binary(branch)]
43-
{ _, _, { :tag, tag } } -> [tag: to_binary(tag)]
44-
{ _, _, ref } -> [ref: to_binary(ref)]
45-
_ -> []
46-
end
47-
48-
raw = case source do
49-
{ _, _, _, [:raw] } -> [app: false]
50-
{ _, _, _, [raw: true] } -> [app: false]
51-
_ -> []
52-
end
53-
54-
opts = scm ++ ref ++ raw
55-
{ app, to_binary(req), opts }
56-
end)
34+
deps_dir = config[:deps_dir] || "deps"
35+
Enum.map(deps, parse_dep(&1, deps_dir))
5736
else
5837
[]
5938
end
@@ -67,10 +46,10 @@ defmodule Mix.Rebar do
6746
config = load_config(dir)
6847

6948
if sub_dirs = config[:sub_dirs] do
70-
sub_dirs = sub_dirs
71-
|> Enum.map(Path.wildcard(&1))
72-
|> List.concat
73-
|> Enum.filter(File.dir?(&1))
49+
sub_dirs = sub_dirs
50+
|> Enum.map(Path.wildcard(&1))
51+
|> List.concat
52+
|> Enum.filter(File.dir?(&1))
7453

7554
Enum.map(sub_dirs, fn(dir) ->
7655
recur(dir, fun)
@@ -80,8 +59,46 @@ defmodule Mix.Rebar do
8059
[File.cd!(dir, fn -> fun.(config) end)]
8160
end
8261

62+
defp parse_dep({ app, req }, deps_dir) do
63+
{ app, compile_req(req), [path: Path.join(deps_dir, app)]}
64+
end
65+
66+
defp parse_dep({ app, req, source }, _deps_dir) do
67+
[ scm, url | source ] = tuple_to_list(source)
68+
69+
{ ref, source } = case source do
70+
[""|s] -> { [branch: "HEAD"], s }
71+
[{ :branch, branch }|s] -> { [branch: to_binary(branch)], s }
72+
[{ :tag, tag }|s] -> { [tag: to_binary(tag)], s }
73+
[ref|s] -> { [ref: to_binary(ref)], s }
74+
_ -> { [], [] }
75+
end
76+
77+
raw = case source do
78+
[[:raw]|_] -> [app: false]
79+
[[raw: true]|_] -> [app: false]
80+
_ -> []
81+
end
82+
83+
opts = [{ scm, to_binary(url) }] ++ ref ++ raw
84+
{ app, compile_req(req), opts }
85+
end
86+
87+
defp parse_dep(app, deps_dir) do
88+
parse_dep({ app, ".*" }, deps_dir)
89+
end
90+
91+
defp compile_req(req) do
92+
case to_binary(req) |> Regex.compile do
93+
{ :ok, re } ->
94+
re
95+
{ :error, reason } ->
96+
raise Mix.Error, message: "Unable to compile version regex: \"#{req}\", #{reason}"
97+
end
98+
end
99+
83100
defp eval_script(path, config) do
84-
script = Path.basename(path)
101+
script = Path.basename(path) |> binary_to_list
85102
case :file.script(path, eval_binds(CONFIG: config, SCRIPT: script)) do
86103
{ :ok, config } ->
87104
config
@@ -97,3 +114,11 @@ defmodule Mix.Rebar do
97114
end)
98115
end
99116
end
117+
118+
# Used when pushing a rebar dependency to the project stack
119+
defmodule Mix.Rebar.Mixproject do
120+
@moduledoc false
121+
122+
@doc false
123+
def project, do: []
124+
end

lib/mix/test/fixtures/.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
git_repo
2-
deps_on_git_repo
2+
deps_on_git_repo
3+
git_rebar
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{ sub_dirs, ["apps/*"] }.
2+
3+
{ deps, [
4+
{ git_rebar, "0.1..*", { git, "../git_rebar", master } }
5+
] }.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
CONFIG ++ [{'SCRIPT', SCRIPT}].

lib/mix/test/mix/rebar_test.exs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
Code.require_file "../test_helper.exs", __DIR__
2+
3+
defmodule Mix.RebarTest do
4+
use MixTest.Case
5+
6+
defmodule RebarAsDep do
7+
def project do
8+
[ app: :rebar_as_dep,
9+
version: "0.1.0",
10+
deps: [
11+
{ :rebar_dep, %r/.*/, path: MixTest.Case.fixture_path("rebar_dep"), app: false }
12+
]
13+
]
14+
end
15+
end
16+
17+
test "load rebar config" do
18+
path = MixTest.Case.fixture_path("rebar_dep")
19+
config = Mix.Rebar.load_config(path)
20+
assert config[:sub_dirs] == ['apps/*']
21+
assert config[:SCRIPT] == 'rebar.config.script'
22+
end
23+
24+
test "rebar sub_dirs" do
25+
path = MixTest.Case.fixture_path("rebar_dep")
26+
assert Mix.Rebar.recur(path, fn _ -> File.cwd end)
27+
[path, Path.join([path, "apps", "rebar_sub_dep"])]
28+
end
29+
30+
test "parse rebar dependencies" do
31+
Mix.Project.push(RebarAsDep)
32+
33+
in_tmp "parse rebar dependencies", fn ->
34+
deps = Mix.Deps.all
35+
assert Enum.find(deps, match?(Mix.Dep[app: :rebar_dep], &1))
36+
37+
assert Enum.find(deps, fn dep ->
38+
Mix.Dep[app: app, opts: opts] = dep
39+
if app == :git_rebar do
40+
assert Enum.find(opts, match?({:git, "../git_rebar"}, &1))
41+
assert Enum.find(opts, match?({:ref, "master"}, &1))
42+
true
43+
end
44+
end)
45+
end
46+
after
47+
Mix.Project.pop
48+
end
49+
50+
test "get and compile dependencies for rebar" do
51+
Mix.Project.push(RebarAsDep)
52+
53+
in_tmp "dependencies for rebar", fn ->
54+
Mix.Tasks.Deps.Get.run []
55+
assert_received { :mix_shell, :info, ["* Getting git_rebar [git: \"../git_rebar\"]"] }
56+
57+
Mix.Tasks.Compile.run []
58+
assert_received { :mix_shell, :info, ["* Compiling git_rebar"] }
59+
assert_received { :mix_shell, :info, ["* Compiling rebar_dep"] }
60+
assert :git_rebar.any_function == :ok
61+
end
62+
after
63+
Mix.Project.pop
64+
end
65+
end

lib/mix/test/mix/umbrella_test.exs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,18 @@ defmodule Mix.UmbrellaTest do
1919
purge [Umbrella.Mixfile, Foo, Foo.Mix, Bar, Bar.Mix]
2020
end
2121

22+
test "dependency in umbrella" do
23+
in_fixture "umbrella_dep/deps/umbrella", fn ->
24+
Mix.Project.in_project(:umbrella, ".", fn _ ->
25+
Mix.Task.run "deps"
26+
assert_received { :mix_shell, :info, ["==> bar"] }
27+
assert_received { :mix_shell, :info, ["* foo [path: \"../foo\"]"] }
28+
end)
29+
end
30+
after
31+
purge [Umbrella.Mixfile, Foo.Mix, Bar.Mix]
32+
end
33+
2234
test "umbrella as dependency" do
2335
in_fixture "umbrella_dep", fn ->
2436
Mix.Project.in_project(:umbrella_dep, ".", fn _ ->

0 commit comments

Comments
 (0)