Skip to content

Commit e24cef0

Browse files
author
José Valim
committed
Add protocol consolidation with mix compile.protocols
1 parent b161070 commit e24cef0

File tree

4 files changed

+102
-1
lines changed

4 files changed

+102
-1
lines changed

lib/elixir/lib/protocol.ex

+9-1
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,14 @@ defmodule Protocol do
211211
end
212212
end
213213

214+
@doc """
215+
Returns true if the protocol was consolidated.
216+
"""
217+
@spec consolidated?(module) :: boolean
218+
def consolidated?(protocol) do
219+
protocol.__info__(:attributes)[:protocol][:consolidated]
220+
end
221+
214222
@doc """
215223
Receives a protocol and a list of implementations and
216224
consolidates the given protocol.
@@ -225,7 +233,7 @@ defmodule Protocol do
225233
to be consolidated or not by analyzing the protocol
226234
attribute:
227235
228-
Enumerable.__info__(:attributes)[:protocol]
236+
Protocol.consolidated?(Enumerable)
229237
230238
If the first element of the tuple is true, it means
231239
the protocol was consolidated.

lib/elixir/test/elixir/protocol_test.exs

+5
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,11 @@ defmodule Protocol.ConsolidationTest do
266266
{:ok, binary} = Protocol.consolidate(WithAny, [Any, ImplStruct, Map])
267267
:code.load_binary(WithAny, 'protocol_test.exs', binary)
268268

269+
test "consolidated?/1" do
270+
assert Protocol.consolidated?(WithAny)
271+
refute Protocol.consolidated?(Enumerable)
272+
end
273+
269274
test "consolidated implementations without any" do
270275
assert nil? Sample.impl_for(:foo)
271276
assert nil? Sample.impl_for(fn(x) -> x end)
+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
defmodule Mix.Tasks.Compile.Protocols do
2+
use Mix.Task
3+
4+
@recursive true
5+
6+
@moduledoc ~S"""
7+
Consolidates all protocols in all paths.
8+
9+
This module consolidates all protocols in the code path
10+
and output the new binary files to the given directory
11+
(defaults to "consolidated").
12+
13+
A new directory will be created with the consolidated
14+
protocol versions. Simply add it to your codepath to
15+
make use of it:
16+
17+
mix run -pa consolidated
18+
19+
You can verify a protocol is consolidated by checking
20+
its attributes:
21+
22+
elixir -pa consolidated -S \
23+
mix run -e "IO.puts Protocol.consolidated?(Enumerable)"
24+
25+
"""
26+
27+
def run(args) do
28+
Mix.Task.run "compile", args
29+
{opts, _, _} = OptionParser.parse(args, switches: [output: :string], aliases: [o: :output])
30+
31+
paths = filter_otp(:code.get_path, :code.lib_dir)
32+
paths
33+
|> Protocol.extract_protocols
34+
|> consolidate(paths, opts[:output] || "consolidated")
35+
36+
:ok
37+
end
38+
39+
defp filter_otp(paths, otp) do
40+
Enum.filter(paths, &(not :lists.prefix(&1, otp)))
41+
end
42+
43+
defp consolidate(protocols, paths, output) do
44+
File.mkdir_p!(output)
45+
46+
for protocol <- protocols do
47+
impls = Protocol.extract_impls(protocol, paths)
48+
maybe_reload(protocol)
49+
{:ok, binary} = Protocol.consolidate(protocol, impls)
50+
File.write!(Path.join(output, "#{protocol}.beam"), binary)
51+
Mix.shell.info "Consolidated #{inspect protocol}"
52+
end
53+
end
54+
55+
defp maybe_reload(module) do
56+
case :code.which(module) do
57+
:non_existing ->
58+
module
59+
file ->
60+
unless Path.extname(file) == ".beam" do
61+
:code.purge(module)
62+
:code.delete(module)
63+
end
64+
end
65+
end
66+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
Code.require_file "../../test_helper.exs", __DIR__
2+
3+
defmodule Mix.Tasks.Compile.ProtocolsTest do
4+
use MixTest.Case
5+
6+
test "compiles and consolidates protocols" do
7+
Mix.Project.push MixTest.Case.Sample
8+
9+
in_fixture "no_mixfile", fn ->
10+
assert Mix.Tasks.Compile.Protocols.run([]) == :ok
11+
assert_received {:mix_shell, :info, ["Consolidated Enumerable"]}
12+
13+
assert File.regular? "consolidated/Elixir.Enumerable.beam"
14+
purge [Enumerable]
15+
16+
Code.prepend_path("consolidated")
17+
assert Protocol.consolidated?(Enumerable)
18+
end
19+
after
20+
purge [Enumerable]
21+
end
22+
end

0 commit comments

Comments
 (0)