Skip to content

Commit eac9929

Browse files
author
José Valim
committed
Move rebar and other deps to _build
1 parent e1aead5 commit eac9929

File tree

10 files changed

+119
-59
lines changed

10 files changed

+119
-59
lines changed

lib/mix/lib/mix/deps.ex

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -342,28 +342,36 @@ defmodule Mix.Deps do
342342
end
343343

344344
@doc """
345-
Returns all load paths for the dependency.
346-
Expects a loaded dependency.
345+
Returns all load paths for the given dependency. Automatically
346+
derived from source paths.
347347
"""
348-
def load_paths(Mix.Dep[manager: :mix, opts: opts]) do
349-
[Path.join(opts[:build], "ebin")]
348+
def load_paths(Mix.Dep[opts: opts] = dep) do
349+
build_path = Path.dirname(opts[:build])
350+
Enum.map source_paths(dep), fn path ->
351+
Path.join [build_path, Path.basename(path), "ebin"]
352+
end
350353
end
351354
352-
def load_paths(Mix.Dep[manager: :rebar, opts: opts, extra: extra]) do
355+
@doc """
356+
Returns all source paths.
357+
358+
Source paths are the directories that contains ebin files for a given
359+
dependency. All managers, except rebar, have only one source path.
360+
"""
361+
def source_paths(Mix.Dep[manager: :rebar, opts: opts, extra: extra]) do
353362
# Add root dir and all sub dirs with ebin/ directory
354363
sub_dirs = Enum.map(extra[:sub_dirs] || [], fn path ->
355364
Path.join(opts[:dest], path)
356365
end)
357366
358-
[ opts[:dest] | sub_dirs ]
359-
|> Enum.map(&Path.wildcard(&1))
360-
|> Enum.concat
361-
|> Enum.map(&Path.join(&1, "ebin"))
362-
|> Enum.filter(&File.dir?(&1))
367+
[opts[:dest] | sub_dirs]
368+
|> Enum.map(&Path.wildcard(&1))
369+
|> Enum.concat
370+
|> Enum.filter(fn p -> p |> Path.join("ebin") |> File.dir? end)
363371
end
364372
365-
def load_paths(Mix.Dep[manager: manager, opts: opts]) when manager in [:make, nil] do
366-
[ Path.join(opts[:dest], "ebin") ]
373+
def source_paths(Mix.Dep[opts: opts]) do
374+
[opts[:dest]]
367375
end
368376
369377
@doc """

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

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -139,22 +139,19 @@ defmodule Mix.Deps.Retriever do
139139

140140
## Fetching
141141

142-
defp mix_dep(Mix.Dep[opts: opts, app: app] = dep, config) do
142+
defp mix_dep(Mix.Dep[opts: opts] = dep, config) do
143143
Mix.Deps.in_dependency(dep, config, fn _ ->
144144
config = Mix.project
145145
umbrella? = Mix.Project.umbrella?
146-
app_path =
147-
if umbrella? do
148-
false
149-
else
150-
Path.join(Mix.Project.compile_path(config), "#{app}.app")
151-
end
146+
147+
if umbrella? do
148+
opts = Keyword.put_new(opts, :app, false)
149+
end
152150

153151
if req = old_elixir_req(config) do
154152
dep = dep.status({ :elixirreq, req })
155153
end
156154

157-
opts = Keyword.put_new(opts, :app, app_path)
158155
{ dep.manager(:mix).opts(opts).extra(umbrella: umbrella?), children }
159156
end)
160157
end
@@ -182,7 +179,7 @@ defmodule Mix.Deps.Retriever do
182179
dep
183180
else
184181
path = if is_binary(opts_app), do: opts_app, else: "ebin/#{app}.app"
185-
path = Path.expand(path, opts[:dest])
182+
path = Path.expand(path, opts[:build])
186183
dep.status app_status(path, app, req)
187184
end
188185
end

lib/mix/lib/mix/deps/umbrella.ex

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,13 @@ defmodule Mix.Deps.Umbrella do
99

1010
if apps_path = config[:apps_path] do
1111
paths = Path.wildcard(Path.join(apps_path, "*"))
12+
build = Mix.Project.build_path
1213

1314
paths
1415
|> Enum.filter(&File.dir?(&1))
1516
|> extract_umbrella
1617
|> filter_umbrella(config[:apps])
17-
|> to_umbrella_dep()
18+
|> to_umbrella_dep(build)
1819
else
1920
[]
2021
end
@@ -49,10 +50,12 @@ defmodule Mix.Deps.Umbrella do
4950
lc { app, _ } = pair inlist pairs, app in apps, do: pair
5051
end
5152

52-
defp to_umbrella_dep(paths) do
53+
defp to_umbrella_dep(paths, build) do
5354
Enum.map paths, fn({ app, path }) ->
55+
opts = [path: path, dest: Path.expand(path),
56+
env: Mix.env, build: Path.join([build, "lib", app])]
5457
Mix.Dep[scm: Mix.SCM.Path, app: app, requirement: nil, manager: :mix,
55-
status: { :ok, nil }, opts: [path: path, dest: Path.expand(path), env: Mix.env]]
58+
status: { :ok, nil }, opts: opts]
5659
end
5760
end
5861
end

lib/mix/lib/mix/project.ex

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -245,18 +245,26 @@ defmodule Mix.Project do
245245
@doc """
246246
Builds the project structure for the current application.
247247
"""
248-
def build_structure(config // config()) do
249-
ebin = compile_path(config)
250-
File.mkdir_p!(ebin)
251-
252-
lib = Path.dirname(ebin)
253-
old = Path.expand("priv")
254-
255-
case :file.make_symlink(old, Path.join(lib, "priv")) do
256-
:ok -> :ok
257-
{ :error, :eexist } -> :ok
258-
{ :error, _ } -> File.cp_r!(old, lib) |> IO.inspect
248+
def build_structure(config // config(), opts // []) do
249+
app = app_path(config)
250+
File.mkdir_p!(app)
251+
252+
source = Path.expand("ebin")
253+
target = Path.join(app, "ebin")
254+
255+
cond do
256+
opts[:symlink_ebin?] ->
257+
Mix.Utils.symlink_or_copy(source, target)
258+
match?({ :ok, _ }, :file.read_link(target)) ->
259+
File.rm_rf!(target)
260+
File.mkdir_p!(target)
261+
true ->
262+
File.mkdir_p!(target)
259263
end
264+
265+
source = Path.expand("priv")
266+
target = Path.join(app, "priv")
267+
Mix.Utils.symlink_or_copy(source, target)
260268
end
261269

262270
@doc """

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

Lines changed: 36 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -48,28 +48,27 @@ defmodule Mix.Tasks.Deps.Compile do
4848
compiled =
4949
Enum.map deps, fn(dep) ->
5050
Mix.Dep[app: app, status: status, opts: opts] = dep
51-
deps_path = opts[:dest]
52-
5351
check_unavailable!(app, status)
52+
5453
unless run_opts[:quiet] || opts[:compile] == false do
5554
shell.info "* Compiling #{app}"
5655
end
5756

5857
compiled = cond do
5958
not nil?(opts[:compile]) ->
60-
do_compile app, deps_path, opts[:compile]
59+
do_compile dep
6160
mix?(dep) ->
6261
do_mix dep, config
6362
rebar?(dep) ->
64-
do_rebar app, deps_path, config[:deps_path]
63+
do_rebar dep, config
6564
make?(dep) ->
66-
do_command app, deps_path, "make"
65+
do_make dep
6766
true ->
6867
shell.error "Could not compile #{app}, no mix.exs, rebar.config or Makefile " <>
6968
"(pass :compile as an option to customize compilation, set it to false to do nothing)"
7069
end
7170

72-
Enum.each(Mix.Deps.load_paths(dep), &Code.prepend_path/1)
71+
unless mix?(dep), do: build_structure(dep, config)
7372
compiled
7473
end
7574

@@ -85,16 +84,12 @@ defmodule Mix.Tasks.Deps.Compile do
8584
:ok
8685
end
8786

88-
defp app_path_for(Mix.Dep[opts: opts]) do
89-
opts[:build]
90-
end
91-
92-
defp do_mix(dep, config) do
87+
defp do_mix(Mix.Dep[opts: opts] = dep, config) do
9388
# Set the app_path to be the one stored in the dependency.
9489
# This is important because the name of application in the
9590
# mix.exs file can be different than the actual name and we
9691
# choose to respect the one in the mix.exs.
97-
config = Keyword.put(config, :app_path, app_path_for(dep))
92+
config = Keyword.put(config, :app_path, opts[:build])
9893

9994
Mix.Deps.in_dependency dep, config, fn _ ->
10095
try do
@@ -111,8 +106,8 @@ defmodule Mix.Tasks.Deps.Compile do
111106
end
112107
end
113108

114-
defp do_rebar(app, deps_path, root_path) do
115-
do_command app, deps_path, rebar_cmd(app), "compile skip_deps=true deps_dir=#{inspect root_path}"
109+
defp do_rebar(Mix.Dep[app: app] = dep, config) do
110+
do_command dep, rebar_cmd(app), "compile skip_deps=true deps_dir=#{inspect config[:deps_path]}"
116111
end
117112

118113
defp rebar_cmd(app) do
@@ -133,22 +128,42 @@ defmodule Mix.Tasks.Deps.Compile do
133128
Mix.Rebar.local_rebar_cmd || raise Mix.Error, message: "rebar instalation failed"
134129
end
135130

136-
defp do_compile(_, _deps_path, false) do
137-
false
131+
defp do_make(dep) do
132+
do_command(dep, "make")
138133
end
139134

140-
defp do_compile(app, deps_path, command) when is_binary(command) do
141-
Mix.shell.info("#{app}: #{command}")
142-
do_command(app, deps_path, command)
135+
defp do_compile(Mix.Dep[app: app, opts: opts] = dep) do
136+
if command = opts[:compile] do
137+
Mix.shell.info("#{app}: #{command}")
138+
do_command(dep, command)
139+
else
140+
false
141+
end
143142
end
144143

145-
defp do_command(app, deps_path, command, extra // "") do
146-
File.cd! deps_path, fn ->
144+
defp do_command(Mix.Dep[app: app, opts: opts], command, extra // "") do
145+
File.cd! opts[:dest], fn ->
147146
if Mix.shell.cmd("#{command} #{extra}") != 0 do
148147
raise Mix.Error, message: "Could not compile dependency #{app}, #{command} command failed. " <>
149148
"If you want to recompile this dependency, please run: mix deps.compile #{app}"
150149
end
151150
end
152151
true
153152
end
153+
154+
defp build_structure(Mix.Dep[opts: opts] = dep, config) do
155+
build_path = Path.dirname(opts[:build])
156+
Enum.each Mix.Deps.source_paths(dep), fn source ->
157+
app = Path.join(build_path, Path.basename(source))
158+
build_structure(source, app, config)
159+
Code.prepend_path(Path.join(app, "ebin"))
160+
end
161+
end
162+
163+
defp build_structure(dest, build, config) do
164+
File.cd! dest, fn ->
165+
config = Keyword.put(config, :app_path, build)
166+
Mix.Project.build_structure(config, symlink_ebin?: true)
167+
end
168+
end
154169
end

lib/mix/lib/mix/utils.ex

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,29 @@ defmodule Mix.Utils do
314314
defp to_lower_char(char) when char in ?A..?Z, do: char + 32
315315
defp to_lower_char(char), do: char
316316

317+
@doc """
318+
Symlink directory `source` to `target` or copy it recursively
319+
in case symlink fails.
320+
"""
321+
def symlink_or_copy(source, target) do
322+
if File.exists?(source) do
323+
case :file.make_symlink(source, target) do
324+
:ok -> :ok
325+
{ :error, :eexist } ->
326+
case :file.read_link(target) do
327+
{ :ok, _ } -> :ok
328+
{ :error, _ } -> do_copy(source, target)
329+
end
330+
{ :error, _ } -> do_copy(source, target)
331+
end
332+
end
333+
end
334+
335+
defp do_copy(source, target) do
336+
File.rm_rf!(target)
337+
File.cp_r!(source, Path.join(target, "."))
338+
end
339+
317340
@doc """
318341
Opens and reads content from either a URL or a local filesystem path.
319342

lib/mix/test/fixtures/deps_status/deps/invalidvsn/ebin/invalidvsn.app renamed to lib/mix/test/fixtures/deps_status/_build/lib/invalidvsn/ebin/invalidvsn.app

File renamed without changes.

lib/mix/test/fixtures/deps_status/deps/invalidvsn/.gitkeep

Whitespace-only changes.

lib/mix/test/mix/tasks/deps_test.exs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ defmodule Mix.Tasks.DepsTest do
5252
assert_received { :mix_shell, :info, ["* invalidapp (deps/invalidapp)"] }
5353
assert_received { :mix_shell, :info, [" the app file at _build/lib/invalidapp/ebin/invalidapp.app is invalid"] }
5454
assert_received { :mix_shell, :info, ["* noappfile (deps/noappfile)"] }
55-
assert_received { :mix_shell, :info, [" could not find an app file at deps/noappfile/ebin/noappfile.app" <> _] }
55+
assert_received { :mix_shell, :info, [" could not find an app file at _build/lib/noappfile/ebin/noappfile.app" <> _] }
5656
assert_received { :mix_shell, :info, ["* uncloned (https://github.com/elixir-lang/uncloned.git)"] }
5757
assert_received { :mix_shell, :info, [" the dependency is not available, run `mix deps.get`"] }
5858
end
@@ -146,7 +146,7 @@ defmodule Mix.Tasks.DepsTest do
146146
assert_received { :mix_shell, :error, ["* invalidapp (deps/invalidapp)"] }
147147
assert_received { :mix_shell, :error, [" the app file at _build/lib/invalidapp/ebin/invalidapp.app is invalid"] }
148148
assert_received { :mix_shell, :error, ["* noappfile (deps/noappfile)"] }
149-
assert_received { :mix_shell, :error, [" could not find an app file at deps/noappfile/ebin/noappfile.app" <> _] }
149+
assert_received { :mix_shell, :error, [" could not find an app file at _build/lib/noappfile/ebin/noappfile.app" <> _] }
150150
assert_received { :mix_shell, :error, ["* uncloned (https://github.com/elixir-lang/uncloned.git)"] }
151151
assert_received { :mix_shell, :error, [" the dependency is not available, run `mix deps.get`"] }
152152
end

lib/mix/test/mix/umbrella_test.exs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,14 @@ defmodule Mix.UmbrellaTest do
4444

4545
in_fixture "umbrella_dep/deps/umbrella", fn ->
4646
File.mkdir_p!("deps/some_dep/ebin")
47+
File.mkdir_p!("_build/lib/some_dep/ebin")
48+
File.mkdir_p!("_build/lib/foo/ebin")
49+
File.mkdir_p!("_build/lib/bar/ebin")
50+
4751
Mix.Task.run "loadpaths", ["--no-deps-check", "--no-elixir-version-check"]
48-
assert Path.expand('deps/some_dep/ebin') in :code.get_path
52+
assert Path.expand('_build/lib/some_dep/ebin') in :code.get_path
53+
assert Path.expand('_build/lib/foo/ebin') in :code.get_path
54+
assert Path.expand('_build/lib/bar/ebin') in :code.get_path
4955
end
5056
after
5157
Mix.Project.pop

0 commit comments

Comments
 (0)