Skip to content

Commit 132ea15

Browse files
author
José Valim
committed
Merge pull request #1040 from ericmj/umbrella-as-dep
Add support for umbrella project as dependency
2 parents 95a30f3 + 54988ae commit 132ea15

File tree

13 files changed

+147
-46
lines changed

13 files changed

+147
-46
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.in_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.in_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+
dep.project != nil
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: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -184,28 +184,54 @@ defmodule Mix.Project do
184184
end
185185

186186
projects = topsort_projects(projects)
187-
old_tasks = Mix.Task.clear
188187
results = Enum.map projects, fn { app, app_path } ->
189-
for_project(app, app_path, fun)
188+
in_project(app, app_path, fun)
190189
end
191190

192-
Mix.Task.set_tasks(old_tasks)
193191
results
194192
end
195193

196-
# Run function with project on the stack
197-
defp for_project(app, app_path, fun) do
194+
@doc """
195+
Run fun in the context of project app and working directory app_path.
196+
Optionally takes a post_config.
197+
"""
198+
def in_project(app, app_path, post_config // [], fun) do
198199
umbrella_path = apps_path
199200

200201
File.cd! app_path, fn ->
201-
Mix.Project.load_project(app)
202-
result = fun.(umbrella_path)
203-
Mix.Project.pop
204-
Mix.Task.clear
202+
load_project(app, post_config)
203+
result = try do
204+
fun.(umbrella_path)
205+
after
206+
Mix.Project.pop
207+
end
205208
result
206209
end
207210
end
208211

212+
@doc """
213+
Returns the paths this project compiles to.
214+
"""
215+
def compile_paths do
216+
if umbrella? do
217+
List.flatten recursive(fn _ -> compile_paths end)
218+
else
219+
[ Path.expand config[:compile_path] ]
220+
end
221+
end
222+
223+
@doc """
224+
Returns all load paths for this project.
225+
"""
226+
def load_paths do
227+
paths = if umbrella? do
228+
List.flatten recursive(fn _ -> load_paths end)
229+
else
230+
Enum.map config[:load_paths], Path.expand(&1)
231+
end
232+
paths ++ compile_paths
233+
end
234+
209235
# Sort projects in dependency order
210236
defp topsort_projects(projects) do
211237
graph = :digraph.new
@@ -215,7 +241,7 @@ defmodule Mix.Project do
215241
end
216242

217243
Enum.each projects, fn { app, app_path } ->
218-
for_project app, app_path, fn apps_path ->
244+
in_project app, app_path, fn apps_path ->
219245
Enum.each Mix.Deps.children, fn dep ->
220246
if Mix.Deps.available?(dep) and Mix.Deps.in_umbrella?(dep, apps_path) do
221247
:digraph.add_edge(graph, dep.app, app)
@@ -246,6 +272,7 @@ defmodule Mix.Project do
246272
elixirc_exts: [:ex],
247273
elixirc_paths: ["lib"],
248274
elixirc_watch_exts: [:ex, :eex, :exs],
275+
load_paths: [],
249276
lockfile: "mix.lock",
250277
erlc_paths: ["src"],
251278
erlc_include_path: "include",

lib/mix/lib/mix/server.ex

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -74,13 +74,7 @@ defmodule Mix.Server do
7474
def handle_call(:output_app?, _from, config) do
7575
# Check that we haven't already outputted app and that we are part of an
7676
# umbrella project
77-
umbrella = case config.projects do
78-
[{ h, conf }|_] when h != nil -> conf[:apps_path] != nil
79-
_ -> false
80-
end
81-
in_umbrella = Enum.any?(config.projects, fn { _, conf } -> conf[:apps_path] != nil end)
82-
output = !config.io_done && !umbrella && in_umbrella
83-
77+
output = not config.io_done and not umbrella?(config) and in_umbrella?(config)
8478
{ :reply, output, config.io_done(true) }
8579
end
8680

@@ -139,4 +133,17 @@ defmodule Mix.Server do
139133
def handle_cast(request, config) do
140134
super(request, config)
141135
end
136+
137+
# Returns if project is part of an umbrella project
138+
defp in_umbrella?(config) do
139+
Enum.any? config.projects, fn { _, conf } -> conf[:apps_path] != nil end
140+
end
141+
142+
# Returns if project is an umbrella project
143+
defp umbrella?(config) do
144+
case config.projects do
145+
[ { h, conf } | _ ] when h != nil -> conf[:apps_path] != nil
146+
_ -> false
147+
end
148+
end
142149
end

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)