Skip to content

Commit a8000f2

Browse files
authored
Raise on invalid .app files (#14861)
Since regular expressions now have regexes, they can end-up in .app files, and then apps can no longer boot. This changes makes it so they fail loudly.
1 parent 5bcfbd3 commit a8000f2

File tree

2 files changed

+77
-25
lines changed

2 files changed

+77
-25
lines changed

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

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -187,11 +187,11 @@ defmodule Mix.Tasks.Compile.App do
187187
|> add_compile_env(current_properties)
188188
|> add_modules(modules, compile_path)
189189

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

193193
Mix.Project.ensure_structure()
194-
File.write!(target, IO.chardata_to_string(contents))
194+
File.write!(target, [contents, ?.])
195195
File.touch!(target, new_mtime)
196196

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

211+
defp to_erl_term(tuple) when is_tuple(tuple) do
212+
[?{, tuple |> Tuple.to_list() |> to_erl_head(), ?}]
213+
end
214+
215+
defp to_erl_term(list) when is_list(list) do
216+
[?[, to_erl_head(list), ?]]
217+
end
218+
219+
defp to_erl_term(map) when is_map(map) do
220+
inner =
221+
Enum.map_intersperse(
222+
:maps.to_list(:maps.iterator(map, :reversed)),
223+
?,,
224+
fn {key, value} -> [to_erl_term(key), "=>", to_erl_term(value)] end
225+
)
226+
227+
[?#, ?{, inner, ?}]
228+
end
229+
230+
defp to_erl_term(map) when is_map(map) do
231+
inner =
232+
Enum.map_intersperse(
233+
:maps.to_list(:maps.iterator(map, :reversed)),
234+
?,,
235+
fn {key, value} -> [to_erl_term(key), "=>", to_erl_term(value)] end
236+
)
237+
238+
[?#, ?{, inner, ?}]
239+
end
240+
241+
defp to_erl_term(term) when is_function(term) or is_reference(term) or is_pid(term) do
242+
Mix.raise(
243+
"\"def application\" has a term which cannot be written to .app files: #{inspect(term)}"
244+
)
245+
end
246+
247+
defp to_erl_term(term) do
248+
:io_lib.print(term)
249+
end
250+
251+
defp to_erl_head([]), do: []
252+
defp to_erl_head([h | t]), do: [to_erl_term(h) | to_erl_tail(t)]
253+
254+
defp to_erl_tail([h | t]), do: [?,, to_erl_term(h) | to_erl_tail(t)]
255+
defp to_erl_tail([]), do: []
256+
defp to_erl_tail(other), do: [?|, to_erl_term(other)]
257+
211258
defp current_app_properties(target) do
212259
case :file.consult(target) do
213260
{:ok, [{:application, _app, properties}]} -> properties

lib/mix/test/mix/tasks/compile.app_test.exs

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -7,24 +7,6 @@ Code.require_file("../../test_helper.exs", __DIR__)
77
defmodule Mix.Tasks.Compile.AppTest do
88
use MixTest.Case
99

10-
defmodule CustomProject do
11-
def project do
12-
[
13-
app: :custom_project,
14-
version: "0.2.0",
15-
description: "Some UTF-8 dëscriptión"
16-
]
17-
end
18-
19-
def application do
20-
[
21-
maxT: :infinity,
22-
applications: [:example_app, mix: :optional],
23-
extra_applications: [:logger, ex_unit: :optional]
24-
]
25-
end
26-
end
27-
2810
defmodule CustomDeps do
2911
def project do
3012
[app: :custom_deps, version: "0.2.0", deps: deps()]
@@ -51,9 +33,13 @@ defmodule Mix.Tasks.Compile.AppTest do
5133
end
5234
end
5335

54-
defmodule InvalidProject do
36+
defmodule CustomProject do
5537
def project do
56-
[app: :invalid_project, version: "0.3.0"]
38+
[
39+
app: :custom_project,
40+
version: "0.3.0",
41+
description: "Some UTF-8 dëscriptión"
42+
]
5743
end
5844

5945
def application do
@@ -124,20 +110,30 @@ defmodule Mix.Tasks.Compile.AppTest do
124110
test "uses custom application settings" do
125111
in_fixture("no_mixfile", fn ->
126112
Mix.Project.push(CustomProject)
113+
env = [foo: [:one, "two", 3, 4], bar: [{} | %{foo: :bar}]]
114+
115+
Process.put(:application,
116+
maxT: :infinity,
117+
applications: [:example_app, mix: :optional],
118+
extra_applications: [:logger, ex_unit: :optional],
119+
env: env
120+
)
127121

128122
Mix.Tasks.Compile.Elixir.run([])
129123
Mix.Tasks.Compile.App.run([])
130124

131125
properties = parse_resource_file(:custom_project)
132-
assert Application.spec(:custom_project, :vsn) == ~c"0.2.0"
133-
assert properties[:vsn] == ~c"0.2.0"
126+
assert Application.spec(:custom_project, :vsn) == ~c"0.3.0"
127+
assert properties[:vsn] == ~c"0.3.0"
134128
assert properties[:maxT] == :infinity
135129
assert properties[:optional_applications] == [:ex_unit, :mix]
136130
assert properties[:description] == ~c"Some UTF-8 dëscriptión"
137131

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

135+
assert properties[:env] == [foo: [:one, "two", 3, 4], bar: [{} | %{foo: :bar}]]
136+
141137
refute Keyword.has_key?(properties, :extra_applications)
142138
end)
143139
end
@@ -160,7 +156,7 @@ defmodule Mix.Tasks.Compile.AppTest do
160156

161157
test "validates properties" do
162158
in_fixture("no_mixfile", fn ->
163-
Mix.Project.push(InvalidProject)
159+
Mix.Project.push(CustomProject)
164160

165161
Process.put(:application, [:not_a_keyword, applications: []])
166162
message = "Application configuration returned from application/0 should be a keyword list"
@@ -252,6 +248,15 @@ defmodule Mix.Tasks.Compile.AppTest do
252248
assert_raise Mix.Error, message, fn ->
253249
Mix.Tasks.Compile.App.run([])
254250
end
251+
252+
Process.put(:application, env: [foo: make_ref()])
253+
254+
message =
255+
~r"\"def application\" has a term which cannot be written to \.app files: #Reference"
256+
257+
assert_raise Mix.Error, message, fn ->
258+
Mix.Tasks.Compile.App.run([])
259+
end
255260
end)
256261
end
257262

0 commit comments

Comments
 (0)