Skip to content

Commit ce77fa5

Browse files
author
José Valim
committed
Support build_embedded and consolidate_protocols
1 parent eee921b commit ce77fa5

File tree

16 files changed

+190
-166
lines changed

16 files changed

+190
-166
lines changed

lib/elixir/lib/code.ex

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -51,16 +51,7 @@ defmodule Code do
5151
:code.del_path(to_char_list(Path.expand path))
5252
end
5353

54-
@doc """
55-
Re-add paths given to the command line to keep their position
56-
on the overall code path.
57-
58-
Some tools may change the code path by prepending new items but
59-
still want the paths given by the user to have higher priority.
60-
Calling this function guarantees the paths are re-added on
61-
top of the user given ones.
62-
"""
63-
@spec readd_paths() :: :ok
54+
@doc false
6455
def readd_paths() do
6556
{pa, pz} = :elixir_code_server.call(:paths)
6657
:code.add_pathsa(pa)

lib/mix/lib/mix/project.ex

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,8 @@ defmodule Mix.Project do
8080
# The configuration that is pushed down to dependencies.
8181
@doc false
8282
def deps_config(config \\ config()) do
83-
[build_path: build_path(config),
83+
[build_embedded: config[:build_embedded],
84+
build_path: build_path(config),
8485
build_per_environment: config[:build_per_environment],
8586
deps_path: deps_path(config)]
8687
end
@@ -320,19 +321,30 @@ defmodule Mix.Project do
320321

321322
_ = cond do
322323
opts[:symlink_ebin] ->
323-
_ = Mix.Utils.symlink_or_copy(source, target)
324+
_ = symlink_or_copy(config, source, target)
324325
match?({:ok, _}, :file.read_link(target)) ->
325326
_ = File.rm_rf!(target)
326327
File.mkdir_p!(target)
327328
true ->
328329
File.mkdir_p!(target)
329330
end
330331

331-
_ = Mix.Utils.symlink_or_copy(Path.expand("include"), Path.join(app, "include"))
332-
_ = Mix.Utils.symlink_or_copy(Path.expand("priv"), Path.join(app, "priv"))
332+
_ = symlink_or_copy(config, Path.expand("include"), Path.join(app, "include"))
333+
_ = symlink_or_copy(config, Path.expand("priv"), Path.join(app, "priv"))
333334
:ok
334335
end
335336

337+
defp symlink_or_copy(config, source, target) do
338+
if config[:build_embedded] do
339+
if File.exists?(source) do
340+
File.rm_rf!(target)
341+
File.cp_r!(source, target)
342+
end
343+
else
344+
Mix.Utils.symlink_or_copy(source, target)
345+
end
346+
end
347+
336348
@doc """
337349
Returns all load paths for this project.
338350
"""
@@ -378,6 +390,8 @@ defmodule Mix.Project do
378390
defp default_config do
379391
[aliases: [],
380392
build_per_environment: true,
393+
build_embedded: false,
394+
consolidate_protocols: false,
381395
default_task: "run",
382396
deps: [],
383397
deps_path: "deps",

lib/mix/lib/mix/tasks/app.start.ex

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,35 @@ defmodule Mix.Tasks.App.Start do
99
1010
## Command line options
1111
12-
* `--force` - force compilation regardless of compilation times
13-
* `--no-compile` - do not compile even if files require compilation
12+
* `--force` - force compilation regardless of compilation times
13+
* `--no-compile` - do not compile even if files require compilation
14+
* `--no-protocols` - do not load consolidated protocols
1415
* `--no-deps-check` - do not check dependencies
15-
* `--no-elixir-version-check`
16-
- do not check elixir version
17-
* `--no-start` - do not start applications after compilation
16+
* `--no-elixir-version-check` - do not check elixir version
17+
* `--no-start` - do not start applications after compilation
1818
1919
"""
2020
@spec run(OptionParser.argv) :: :ok
2121
def run(args) do
2222
Mix.Project.get!
23-
Mix.Task.run "loadpaths", ["--no-readd"|args]
23+
Mix.Task.run "loadpaths", args
2424

2525
unless "--no-compile" in args do
26-
Mix.Task.run "compile", ["--no-readd"|args]
26+
Mix.Task.run "compile", args
27+
end
28+
29+
unless "--no-protocols" in args do
30+
path = Path.join(Mix.Project.build_path, "consolidated")
31+
32+
if File.dir?(path) do
33+
Code.prepend_path(path)
34+
35+
Enum.each(File.ls!(path), fn file ->
36+
module = file |> Path.rootname() |> String.to_atom()
37+
:code.purge(module)
38+
:code.delete(module)
39+
end)
40+
end
2741
end
2842

2943
unless "--no-start" in args do
@@ -35,8 +49,6 @@ defmodule Mix.Tasks.App.Start do
3549
if Code.ensure_loaded?(Logger), do: Logger.App.stop()
3650
start(Mix.Project.config[:app])
3751
end
38-
39-
Code.readd_paths()
4052
end
4153

4254
@doc false

lib/mix/lib/mix/tasks/compile.all.ex

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
defmodule Mix.Tasks.Compile.All do
2+
use Mix.Task
3+
4+
@moduledoc false
5+
@recursive true
6+
7+
# This is an internal task used by mix compile which
8+
# is meant to be recursive and be invoked for each child
9+
# project.
10+
11+
def run(args) do
12+
Mix.Project.get!
13+
14+
res =
15+
Enum.map(Mix.Tasks.Compile.compilers(), fn(compiler) ->
16+
Mix.Task.run("compile.#{compiler}", args)
17+
end)
18+
19+
true = Code.prepend_path(Mix.Project.compile_path)
20+
if :ok in res, do: :ok, else: :noop
21+
end
22+
end

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

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@ defmodule Mix.Tasks.Compile do
1515
* `:compilers` - compilers to run, defaults to:
1616
`[:leex, :yeec, :erlang, :elixir, :app]`
1717
18+
* `:consolidate_protocols` - when true, runs protocol
19+
consolidation via the `compile.protocols` task
20+
21+
* `:build_embedded` - when true, activates protocol
22+
consolidation and does not generate symlinks in builds
23+
1824
## Command line options
1925
2026
* `--list` - list all enabled compilers
@@ -47,21 +53,20 @@ defmodule Mix.Tasks.Compile do
4753
shell.info format('mix ~-#{max}s # ~ts', [task, doc])
4854
end
4955

50-
shell.info "\nEnabled compilers: #{Enum.join compilers(), ", "}"
56+
compilers = compilers() ++ if(consolidate_protocols?(), do: [:protocols], else: [])
57+
shell.info "\nEnabled compilers: #{Enum.join compilers, ", "}"
5158
end
5259

5360
def run(args) do
5461
Mix.Project.get!
55-
Mix.Task.run "loadpaths", ["--no-readd"|args]
62+
Mix.Task.run "loadpaths", args
63+
res = Mix.Task.run "compile.all", args
5664

57-
res =
58-
Enum.map(compilers(), fn(compiler) ->
59-
List.wrap Mix.Task.run("compile.#{compiler}", args)
60-
end)
65+
if consolidate_protocols?() do
66+
Mix.Task.run "compile.protocols", args
67+
end
6168

62-
true = Code.prepend_path(Mix.Project.compile_path)
63-
unless "--no-readd" in args, do: Code.readd_paths()
64-
if Enum.any?(res, &(:ok in &1)), do: :ok, else: :noop
69+
res
6570
end
6671

6772
# Loadpaths without checks because compilers may be defined in deps.
@@ -70,6 +75,11 @@ defmodule Mix.Tasks.Compile do
7075
Mix.Task.reenable "loadpaths"
7176
end
7277

78+
defp consolidate_protocols? do
79+
config = Mix.Project.config
80+
config[:build_embedded] || config[:consolidate_protocols]
81+
end
82+
7383
@doc """
7484
Returns all compilers.
7585
"""

lib/mix/lib/mix/tasks/compile.protocols.ex

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,26 @@
11
defmodule Mix.Tasks.Compile.Protocols do
22
use Mix.Task
33

4-
@recursive true
5-
6-
@shortdoc "Consolidates all protocols in all paths"
7-
84
@moduledoc ~S"""
95
Consolidates all protocols in all paths.
106
11-
This module consolidates all protocols in the code path
7+
This task is automatically invoked whenever the project
8+
enables `:consolidate_protocols` or `:build_embedded` in
9+
its configuration.
10+
11+
## Consolidation
12+
13+
Protocol consolidation is useful in production when no
14+
dynamic code loading will happen, effectively optimizing
15+
protocol dispatches by not accounting for code loading.
16+
17+
This task consolidates all protocols in the code path
1218
and output the new binary files to the given directory
13-
(defaults to "consolidated").
19+
(defaults to "_build/MIX_ENV/consolidated").
1420
15-
A new directory will be created with the consolidated
16-
protocol versions in the build directory for the given
17-
environment. Simply add it to your loadpath to make use
18-
of it according to your MIX_ENV:
21+
In case you are manually compiling protocols or building
22+
releases, you need to take the generated protocols into
23+
account. This can be done with:
1924
2025
$ elixir -pa _build/MIX_ENV/consolidated -S mix run
2126

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ defmodule Mix.Tasks.Deps.Compile do
102102
end
103103

104104
try do
105-
res = Mix.Task.run("compile", ["--no-deps", "--no-elixir-version-check", "--no-readd"])
105+
res = Mix.Task.run("compile", ["--no-deps", "--no-elixir-version-check"])
106106
:ok in List.wrap(res)
107107
catch
108108
kind, reason ->

lib/mix/lib/mix/tasks/escript.build.ex

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,6 @@ defmodule Mix.Tasks.Escript.Build do
4848
4949
Defaults to `true` for Elixir projects.
5050
51-
* `:consolidate_protocols` - if `true`, all protocols will be consolidated
52-
before being embedded into the escript.
53-
54-
Defaults to `true` for Elixir projects.
55-
5651
* `:shebang` - shebang interpreter directive used to execute the escript.
5752
Defaults to `"#! /usr/bin/env escript\n"`.
5853
@@ -93,19 +88,13 @@ defmodule Mix.Tasks.Escript.Build do
9388
Mix.Task.run :compile, args
9489
end
9590

96-
project = Mix.Project.config
91+
project = Mix.Project.config
9792
language = Keyword.get(project, :language, :elixir)
98-
should_consolidate =
99-
Keyword.get(project, :consolidate_protocols, language == :elixir)
100-
101-
if should_consolidate do
102-
Mix.Task.run "compile.protocols", []
103-
end
10493

105-
escriptize(project, language, opts[:force], should_consolidate)
94+
escriptize(project, language, opts[:force])
10695
end
10796

108-
defp escriptize(project, language, force, should_consolidate) do
97+
defp escriptize(project, language, force) do
10998
escript_opts = project[:escript] || []
11099

111100
script_name = to_string(escript_opts[:name] || project[:app])
@@ -131,7 +120,7 @@ defmodule Mix.Tasks.Escript.Build do
131120
|> Stream.concat
132121
|> prepare_beam_paths
133122

134-
if should_consolidate do
123+
if project[:build_embedded] || project[:consolidate_protocols] do
135124
beam_paths =
136125
Path.wildcard(consolidated_path <> "/*")
137126
|> prepare_beam_paths(beam_paths)

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

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,6 @@ defmodule Mix.Tasks.Loadpaths do
2929
load_project(config, args)
3030
end
3131

32-
unless "--no-readd" in args do
33-
Code.readd_paths()
34-
end
35-
3632
:ok
3733
end
3834

lib/mix/test/fixtures/dep_with_protocol/lib/proto.ex

Lines changed: 0 additions & 3 deletions
This file was deleted.

0 commit comments

Comments
 (0)