Skip to content

Commit dd9ba46

Browse files
committed
Add support for umbrella project as dependency
Closes #1031
1 parent 6f26ff3 commit dd9ba46

File tree

12 files changed

+124
-31
lines changed

12 files changed

+124
-31
lines changed

lib/mix/lib/mix/deps.ex

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,4 +168,54 @@ defmodule Mix.Deps do
168168

169169
"#{app} #{version}#{inspect scm.format(opts)}"
170170
end
171+
172+
@doc """
173+
Returns all compile paths for the dependency.
174+
"""
175+
def compile_paths(Mix.Dep[app: app, opts: opts] = dep) do
176+
if mix?(dep) do
177+
Mix.Project.for_project app, opts[:dest], fn _ ->
178+
Mix.Project.compile_paths
179+
end
180+
else
181+
[ Path.join(opts[:dest], "ebin") ]
182+
end
183+
end
184+
185+
@doc """
186+
Returns all load paths for the dependency.
187+
"""
188+
def load_paths(Mix.Dep[app: app, opts: opts] = dep) do
189+
if mix?(dep) do
190+
paths = Mix.Project.for_project app, opts[:dest], fn _ ->
191+
Mix.Project.load_paths
192+
end
193+
Enum.uniq paths
194+
else
195+
[ Path.join(opts[:dest], "ebin") ]
196+
end
197+
end
198+
199+
@doc """
200+
Returns if dependency is a mix project.
201+
"""
202+
def mix?(dep) do
203+
File.regular? Path.join(dep.opts[:dest], "mix.exs")
204+
end
205+
206+
@doc """
207+
Returns if dependency is a rebar project.
208+
"""
209+
def rebar?(dep) do
210+
Enum.any? ["rebar.config", "rebar.config.script"], fn file ->
211+
File.regular? Path.join(dep.opts[:dest], file)
212+
end
213+
end
214+
215+
@doc """
216+
Returns if dependency is a make project.
217+
"""
218+
def make?(dep) do
219+
File.regular? Path.join(dep.opts[:dest], "Makefile")
220+
end
171221
end

lib/mix/lib/mix/project.ex

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -193,8 +193,10 @@ defmodule Mix.Project do
193193
results
194194
end
195195

196-
# Run function with project on the stack
197-
defp for_project(app, app_path, fun) do
196+
@doc """
197+
Run fun with project app on top of the stack.
198+
"""
199+
def for_project(app, app_path, fun) do
198200
umbrella_path = apps_path
199201

200202
File.cd! app_path, fn ->
@@ -206,6 +208,29 @@ defmodule Mix.Project do
206208
end
207209
end
208210

211+
@doc """
212+
Returns the paths this project compiles to.
213+
"""
214+
def compile_paths do
215+
if umbrella? do
216+
List.flatten recursive(fn _ -> compile_paths end)
217+
else
218+
[ Path.expand config[:compile_path] ]
219+
end
220+
end
221+
222+
@doc """
223+
Returns all load paths for this project.
224+
"""
225+
def load_paths do
226+
paths = if umbrella? do
227+
List.flatten recursive(fn _ -> load_paths end)
228+
else
229+
Enum.map config[:load_paths], Path.expand(&1)
230+
end
231+
paths ++ compile_paths
232+
end
233+
209234
# Sort projects in dependency order
210235
defp topsort_projects(projects) do
211236
graph = :digraph.new
@@ -246,6 +271,7 @@ defmodule Mix.Project do
246271
elixirc_exts: [:ex],
247272
elixirc_paths: ["lib"],
248273
elixirc_watch_exts: [:ex, :eex, :exs],
274+
load_paths: [],
249275
lockfile: "mix.lock",
250276
erlc_paths: ["src"],
251277
erlc_include_path: "include",

lib/mix/lib/mix/tasks/deps.compile.ex

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ defmodule Mix.Tasks.Deps.Compile do
2424
2525
"""
2626

27-
import Mix.Deps, only: [all: 0, available?: 1, by_name!: 1, format_dep: 1]
27+
import Mix.Deps, only: [ all: 0, available?: 1, by_name!: 1, compile_paths: 1,
28+
format_dep: 1, make?: 1, mix?: 1, rebar?: 1 ]
2829

2930
def run(args) do
3031
case OptionParser.parse(args) do
@@ -45,10 +46,6 @@ defmodule Mix.Tasks.Deps.Compile do
4546
shell.info "* Compiling #{app}"
4647

4748
deps_path = opts[:dest]
48-
ebin = Path.join(deps_path, "ebin") |> binary_to_list
49-
50-
# Avoid compilation conflicts
51-
:code.del_path(ebin |> Path.expand)
5249

5350
root_path = Path.expand(Mix.project[:deps_path])
5451

@@ -57,27 +54,26 @@ defmodule Mix.Tasks.Deps.Compile do
5754
root_lockfile: Path.expand(Mix.project[:lockfile])
5855
]
5956

60-
File.cd! deps_path, fn ->
61-
cond do
62-
opts[:compile] -> do_compile app, opts[:compile]
63-
mix? -> do_mix dep, config
64-
rebar? -> do_rebar app, root_path
65-
make? -> do_command app, "make"
66-
true -> shell.error "Could not compile #{app}, no mix.exs, rebar.config or Makefile " <>
67-
"(pass :compile as an option to customize compilation, set it to :noop to do nothing)"
68-
end
57+
ebins = Enum.map compile_paths(dep), binary_to_list(&1)
58+
59+
# Avoid compilation conflicts
60+
Enum.each ebins, fn ebin -> :code.del_path(ebin |> Path.expand) end
61+
62+
cond do
63+
opts[:compile] -> File.cd! deps_path, fn -> do_compile app, opts[:compile] end
64+
mix?(dep) -> File.cd! deps_path, fn -> do_mix dep, config end
65+
rebar?(dep) -> File.cd! deps_path, fn -> do_rebar app, root_path end
66+
make?(dep) -> File.cd! deps_path, fn -> do_command app, "make" end
67+
true -> shell.error "Could not compile #{app}, no mix.exs, rebar.config or Makefile " <>
68+
"(pass :compile as an option to customize compilation, set it to :noop to do nothing)"
6969
end
7070

71-
Code.prepend_path ebin
71+
Enum.each ebins, Code.prepend_path &1
7272
end
7373

7474
Mix.Deps.Lock.touch
7575
end
7676

77-
defp mix?, do: File.regular?("mix.exs")
78-
defp rebar?, do: Enum.any? ["rebar.config", "rebar.config.script"], File.regular?(&1)
79-
defp make?, do: File.regular?("Makefile")
80-
8177
defp check_unavailable!(app, { :unavailable, _ }) do
8278
raise Mix.Error, message: "Cannot compile dependency #{app} because " <>
8379
"it isn't available, run `mix deps.get` first"

lib/mix/lib/mix/tasks/deps.loadpaths.ex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
defmodule Mix.Tasks.Deps.Loadpaths do
22
use Mix.Task
33

4-
import Mix.Deps, only: [all: 0, ok?: 1]
4+
import Mix.Deps, only: [all: 0, load_paths: 1, ok?: 1]
55

66
@hidden true
77
@shortdoc "Load all dependencies paths"
@@ -19,7 +19,7 @@ defmodule Mix.Tasks.Deps.Loadpaths do
1919
end
2020

2121
lc dep inlist all, ok?(dep) do
22-
Code.prepend_path Path.join(dep.opts[:dest], "ebin")
22+
Enum.each load_paths(dep), Code.prepend_path(&1)
2323
end
2424
end
2525
end

lib/mix/lib/mix/tasks/loadpaths.ex

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,6 @@ defmodule Mix.Tasks.Loadpaths do
2121
Mix.Task.run "deps.loadpaths", args
2222
end
2323

24-
paths = Mix.project[:load_paths] || []
25-
Enum.each paths, Code.prepend_path(&1)
26-
27-
ebin = Mix.project[:compile_path] || "ebin"
28-
Code.prepend_path(ebin)
24+
Enum.each Mix.Project.load_paths, Code.prepend_path(&1)
2925
end
30-
end
26+
end
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
defmodule UmbrellaDep.Mixfile do
2+
use Mix.Project
3+
4+
def project do
5+
[ app: :umbrella_dep,
6+
deps: deps ]
7+
end
8+
9+
defp deps do
10+
[ { :umbrella, path: "deps/umbrella", app: false } ]
11+
end
12+
end

lib/mix/test/mix/umbrella_test.exs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ defmodule Mix.UmbrellaTest do
44
use MixTest.Case
55

66
test "compile umbrella" do
7-
in_fixture "umbrella", fn ->
7+
in_fixture "umbrella_dep/deps/umbrella", fn ->
88
Mix.Project.load_project(:umbrella)
99
Mix.Task.run "compile"
1010

@@ -17,6 +17,19 @@ defmodule Mix.UmbrellaTest do
1717
end
1818
after
1919
Mix.Project.pop
20-
purge [Umbrella.Mixfile]
20+
purge [Umbrella.Mixfile, Foo, Foo.Mix, Bar, Bar.Mix]
21+
end
22+
23+
test "umbrella as dependency" do
24+
in_fixture "umbrella_dep", fn ->
25+
Mix.Project.load_project(:umbrella_dep)
26+
Mix.Tasks.Deps.Compile.run []
27+
Mix.Tasks.Deps.Loadpaths.run []
28+
29+
assert "hello world" == Bar.bar
30+
end
31+
after
32+
Mix.Project.pop
33+
purge [UmbrellaDep.Mixfile, Umbrella.Mixfile, Foo, Foo.Mix, Bar, Bar.Mix]
2134
end
2235
end

0 commit comments

Comments
 (0)