Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 49 additions & 2 deletions lib/mix/lib/mix/tasks/compile.app.ex
Original file line number Diff line number Diff line change
Expand Up @@ -187,11 +187,11 @@ defmodule Mix.Tasks.Compile.App do
|> add_compile_env(current_properties)
|> add_modules(modules, compile_path)

contents = :io_lib.format("~p.~n", [{:application, app, properties}])
contents = to_erl_term({:application, app, properties})
:application.load({:application, app, properties})

Mix.Project.ensure_structure()
File.write!(target, IO.chardata_to_string(contents))
File.write!(target, [contents, ?.])
File.touch!(target, new_mtime)

# If we just created the .app file, it will have touched
Expand All @@ -208,6 +208,53 @@ defmodule Mix.Tasks.Compile.App do
end
end

defp to_erl_term(tuple) when is_tuple(tuple) do
[?{, tuple |> Tuple.to_list() |> to_erl_head(), ?}]
end

defp to_erl_term(list) when is_list(list) do
[?[, to_erl_head(list), ?]]
end

defp to_erl_term(map) when is_map(map) do
inner =
Enum.map_intersperse(
:maps.to_list(:maps.iterator(map, :reversed)),
?,,
fn {key, value} -> [to_erl_term(key), "=>", to_erl_term(value)] end
)

[?#, ?{, inner, ?}]
end

defp to_erl_term(map) when is_map(map) do
inner =
Enum.map_intersperse(
:maps.to_list(:maps.iterator(map, :reversed)),
?,,
fn {key, value} -> [to_erl_term(key), "=>", to_erl_term(value)] end
)

[?#, ?{, inner, ?}]
end

defp to_erl_term(term) when is_function(term) or is_reference(term) or is_pid(term) do
Mix.raise(
"\"def application\" has a term which cannot be written to .app files: #{inspect(term)}"
)
end

defp to_erl_term(term) do
:io_lib.print(term)
end

defp to_erl_head([]), do: []
defp to_erl_head([h | t]), do: [to_erl_term(h) | to_erl_tail(t)]

defp to_erl_tail([h | t]), do: [?,, to_erl_term(h) | to_erl_tail(t)]
defp to_erl_tail([]), do: []
defp to_erl_tail(other), do: [?|, to_erl_term(other)]

defp current_app_properties(target) do
case :file.consult(target) do
{:ok, [{:application, _app, properties}]} -> properties
Expand Down
51 changes: 28 additions & 23 deletions lib/mix/test/mix/tasks/compile.app_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,6 @@ Code.require_file("../../test_helper.exs", __DIR__)
defmodule Mix.Tasks.Compile.AppTest do
use MixTest.Case

defmodule CustomProject do
def project do
[
app: :custom_project,
version: "0.2.0",
description: "Some UTF-8 dëscriptión"
]
end

def application do
[
maxT: :infinity,
applications: [:example_app, mix: :optional],
extra_applications: [:logger, ex_unit: :optional]
]
end
end

defmodule CustomDeps do
def project do
[app: :custom_deps, version: "0.2.0", deps: deps()]
Expand All @@ -51,9 +33,13 @@ defmodule Mix.Tasks.Compile.AppTest do
end
end

defmodule InvalidProject do
defmodule CustomProject do
def project do
[app: :invalid_project, version: "0.3.0"]
[
app: :custom_project,
version: "0.3.0",
description: "Some UTF-8 dëscriptión"
]
end

def application do
Expand Down Expand Up @@ -124,20 +110,30 @@ defmodule Mix.Tasks.Compile.AppTest do
test "uses custom application settings" do
in_fixture("no_mixfile", fn ->
Mix.Project.push(CustomProject)
env = [foo: [:one, "two", 3, 4], bar: [{} | %{foo: :bar}]]

Process.put(:application,
maxT: :infinity,
applications: [:example_app, mix: :optional],
extra_applications: [:logger, ex_unit: :optional],
env: env
)

Mix.Tasks.Compile.Elixir.run([])
Mix.Tasks.Compile.App.run([])

properties = parse_resource_file(:custom_project)
assert Application.spec(:custom_project, :vsn) == ~c"0.2.0"
assert properties[:vsn] == ~c"0.2.0"
assert Application.spec(:custom_project, :vsn) == ~c"0.3.0"
assert properties[:vsn] == ~c"0.3.0"
assert properties[:maxT] == :infinity
assert properties[:optional_applications] == [:ex_unit, :mix]
assert properties[:description] == ~c"Some UTF-8 dëscriptión"

assert properties[:applications] ==
[:kernel, :stdlib, :elixir, :logger, :ex_unit, :example_app, :mix]

assert properties[:env] == [foo: [:one, "two", 3, 4], bar: [{} | %{foo: :bar}]]

refute Keyword.has_key?(properties, :extra_applications)
end)
end
Expand All @@ -160,7 +156,7 @@ defmodule Mix.Tasks.Compile.AppTest do

test "validates properties" do
in_fixture("no_mixfile", fn ->
Mix.Project.push(InvalidProject)
Mix.Project.push(CustomProject)

Process.put(:application, [:not_a_keyword, applications: []])
message = "Application configuration returned from application/0 should be a keyword list"
Expand Down Expand Up @@ -252,6 +248,15 @@ defmodule Mix.Tasks.Compile.AppTest do
assert_raise Mix.Error, message, fn ->
Mix.Tasks.Compile.App.run([])
end

Process.put(:application, env: [foo: make_ref()])

message =
~r"\"def application\" has a term which cannot be written to \.app files: #Reference"

assert_raise Mix.Error, message, fn ->
Mix.Tasks.Compile.App.run([])
end
end)
end

Expand Down