Compare commits

...
29 Commits
Author SHA1 Message Date
José Valim ffe7a577cc Release v1.9.2 2019-10-12 00:21:23 +02:00
José Valim a2f14bd007 Consider options when running regexes on the fly
Closes #9343.
2019-10-11 17:45:12 +02:00
Fernando Tapia Rico 66ac6a3d8a Ensure reproducible builds (boostrapping issue) (#9385)
During bootstrap, the generated AST for the `defexception` macro
does not include import metadata when calling to Kernel functions
without using the qualified name. That's not the case when the
Kernel is later recompiled.

When compiling the standard library, exceptions like
`FunctionClauseError` were generating different ASTs (different
metadata) depending on if they were compiled with the bootstrapped
Kernel or the later compiled one.
2019-10-09 19:12:24 +02:00
José Valim 50caa25d41 Ensure compilation works for a variable named super, closes #9390 2019-10-09 19:12:05 +02:00
José Valim 92af3fdf0f Use Base.encode32 when generating cookie to avoid unsafe chars, closes #9328 2019-09-06 09:57:18 +02:00
José Valim c443cdee36 Move specification of env variables to their own block 2019-08-23 16:07:29 +02:00
José Valim 8ca3876b10 Fixes for release install command on Windows, closes #9310 2019-08-23 15:57:28 +02:00
José Valim c7e822345b Ask user to manually clean manifests if we can't do it, closes #9308 2019-08-22 16:25:30 +02:00
Gary Rennie b43a6a923e Allow {:from_app, app_name} as a version for releases (#9280)
Sometimes it is desireable to lookup the version from another
application to use as the version for a release. This is true in the
case of umbrella applications, where a particular app may be targetted
for a release.

Using `{:from_app, :my_app}` will allow the version returned from
`Application.spec(:my_app, :vsn)` to be used as the version.
2019-08-09 12:35:58 +02:00
Wouter Klijn e60fe36740 Fix release RPC tests (#9253) 2019-07-30 22:17:27 +02:00
Derrick Zhang f5735eb697 Make the wildcard example equivalent with Mix.Config in an umbrella project (#9251) 2019-07-30 09:49:21 +02:00
José Valim 7002554a47 Ensure local captures work correctly on macro expansion
Closes #9245
2019-07-27 11:30:34 +02:00
José Valim 660a09b3af Quote executable path on Windows, closes #9242 (#9243) 2019-07-26 16:02:04 +02:00
José Valim 79388035f5 Release v1.9.1 2019-07-18 12:17:13 +02:00
José Valim edc204f0b2 Make sure contents are not empty before decoding
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2019-07-18 11:41:37 +02:00
Jason Axelson 7d2cee20f6 Increase understandability of the tuple documentation (#9226)
[ci skip]

Since Elixir data structures are immutable, emphasize that creating a new tuple
is required. Also add a "result" binding to make it more clear that the last
line is being used as a result for later.
2019-07-18 10:29:09 +02:00
José Valim a58a924e10 :start cannot be customized on use
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2019-07-18 10:29:00 +02:00
José Valim e0a9b4b476 Preserve UTF8 encoding in release config files
Closes #9225

Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2019-07-18 10:28:42 +02:00
José Valim eb8121c790 Print relative paths in the formatter error 2019-07-15 13:26:47 +02:00
José Valim 3f0608bdc3 Raise readable error for mismatched sources, closes #9218 2019-07-15 13:09:45 +02:00
José Valim 9797a466fc Only run tests if epmd is available, closes #9167 2019-07-11 00:15:17 +02:00
Sven Gehring 418c277dfb Fix formatter removing nested parens in call on call (#9211) 2019-07-10 20:19:54 +02:00
José Valim 580bd764f7 Clarify potentially breaking bug fix, closes #9191 2019-07-04 16:45:46 +02:00
José Valim 49dec48926 Improve docs for releases
Closes #9187.
2019-07-04 09:39:02 +02:00
José Valim c8c7663c83 Run the formatter 2019-07-02 21:35:22 +02:00
José Valim 4e6261a392 Support included applications, closes #9163 2019-07-02 21:20:55 +02:00
José Valim f10cf8bdc8 Make sure locals tracker can be stopped 2019-06-27 10:12:20 +02:00
José Valim 75313ababc Add catch all for bad supervisor names
Otherwise if a different supervisor implementation,
such as a `ConsumerSupervisor` in GenStage, pass a
different name by accident, logging would fail.

Closes #8126.
2019-06-26 14:32:30 +02:00
Nathan Long 570d44b502 More details about the :native time unit, from the Erlang docs (#9157)
[ci skip]
2019-06-25 14:13:42 +02:00
37 changed files with 413 additions and 119 deletions
+47
View File
@@ -58,6 +58,52 @@ Another important change related to configuration is that `mix new` will no long
There are many other enhancements. The Elixir CLI got a handful of new options in order to best support releases. `Logger` now computes its sync/async/discard thresholds in a decentralized fashion, reducing contention. `EEx` templates support more complex expressions than before. Finally, there is a new `~U` sigil for working with UTC DateTimes as well as new functions in the `File`, `Registry`, and `System` modules.
## v1.9.2 (2019-10-12)
### 1. Enhancements
#### Mix
* [mix release] Allow `{:from_app, app_name}` as a version for releases
### 2. Bug fixes
#### Elixir
* [Kernel] Ensure compilation works for a variable named `super`
* [Kernel] Ensure capture operator of a local function expands correctly inside a macro
* [Regex] Ensure dynamic recompilation of regexes considers options. This fixes an issue where parsing the protocol in `URI.parse/1` seemingly looked case sensitive when running Elixir precompiled on another machine
#### Mix
* [mix release] Use `Base.encode32` when generating cookie to avoid unsafe chars
* [mix release] Fix `install` command on Windows
* [mix release] Quote executable path on Windows to ensure it works on directories with spaces
## v1.9.1 (2019-07-18)
### 1. Enhancements
#### Mix
* [mix format] Print relative paths in `--check-formatted` output
* [mix release] Support included applications
### 2. Bug fixes
#### Elixir
* [Code] Fix formatter wrongly removing nested parens in nested calls
#### Logger
* [Logger] Do not crash translator on poorly formatted supervisor names
#### Mix
* [mix compile] Raise readable error for mismatched sources during compilation
* [mix release] Preserve UTF8 encoding in release config files
## v1.9.0 (2019-06-24)
### 1. Enhancements
@@ -135,6 +181,7 @@ There are many other enhancements. The Elixir CLI got a handful of new options i
* [Kernel] Type variables starting with an underscore (`_foo`) should not raise compile error
* [Kernel] Keep order of elements when macro `in/2` is expanded with a literal list on the right-hand side
* [Kernel] Print proper location on undefined function error from dynamically generated functions
* [Kernel] **Potentially breaking** Do not leak aliases when nesting module definitions that are fully namespaced modules. If you defined `defmodule Elixir.Foo.Bar` inside `defmodule Foo`, previous Elixir versions would automatically define an alias, but fully namespaced modules such as `Elixir.Foo.Bar` should never define or require an alias. If you were accidentally relying on this broken behaviour, your code may no longer work
* [System] Make sure `:init.get_status/0` is set to `{:started, :started}` once the system starts
* [Path] Do not expand `~` in `Path.expand/2` when not followed by a path separator
* [Protocol] Ensure `debug_info` is kept in protocols
+1 -1
View File
@@ -1 +1 @@
1.9.0
1.9.2
+2 -2
View File
@@ -156,9 +156,9 @@ if not !runMode! == "iex" (
set beforeExtra=-noshell -s elixir start_cli !beforeExtra!
)
if defined useWerl (
start !ERTS_BIN!werl.exe !ext_libs! !ELIXIR_ERL_OPTIONS! !parsErlang! !beforeExtra! -extra !parsElixir!
start "" "!ERTS_BIN!werl.exe" !ext_libs! !ELIXIR_ERL_OPTIONS! !parsErlang! !beforeExtra! -extra !parsElixir!
) else (
!ERTS_BIN!erl.exe !ext_libs! !ELIXIR_ERL_OPTIONS! !parsErlang! !beforeExtra! -extra !parsElixir!
"!ERTS_BIN!erl.exe" !ext_libs! !ELIXIR_ERL_OPTIONS! !parsErlang! !beforeExtra! -extra !parsElixir!
)
:end
endlocal
-1
View File
@@ -123,7 +123,6 @@ defmodule Agent do
The generated `child_spec/1` can be customized with the following options:
* `:id` - the child specification identifier, defaults to the current module
* `:start` - how to start the child process (defaults to calling `__MODULE__.start_link/1`)
* `:restart` - when the child should be restarted, defaults to `:permanent`
* `:shutdown` - how to shut down the child, either immediately or by giving it time to shut down
+13 -7
View File
@@ -1075,7 +1075,7 @@ defmodule Code.Formatter do
defp local_to_algebra(fun, meta, args, context, state) when is_atom(fun) do
skip_parens =
cond do
not Keyword.get(meta, :no_parens, false) -> :required
not Keyword.get(meta, :no_parens, false) -> :skip_if_only_do_end
local_without_parens?(fun, args, state) -> :skip_unless_many_args
true -> :skip_if_do_end
end
@@ -1093,9 +1093,10 @@ defmodule Code.Formatter do
{doc, state}
end
# parens may one of:
# parens may be one of:
#
# * :skip_unless_many_args - skips parens unless we are the argument context
# * :skip_if_only_do_end - skip parens if we are do-end and the only arg
# * :skip_if_do_end - skip parens if we are do-end
# * :required - never skip parens
#
@@ -1111,11 +1112,16 @@ defmodule Code.Formatter do
if blocks = do_end_blocks(last, state) do
{call_doc, state} =
if rest == [] do
{" do", state}
else
no_parens? = parens != :required
call_args_to_algebra_no_blocks(meta, rest, no_parens?, list_to_keyword?, " do", state)
case rest do
[] when parens == :required ->
{"() do", state}
[] ->
{" do", state}
_ ->
no_parens? = parens not in [:required, :skip_if_only_do_end]
call_args_to_algebra_no_blocks(meta, rest, no_parens?, list_to_keyword?, " do", state)
end
{blocks_doc, state} = do_end_blocks_to_algebra(blocks, state)
+1 -1
View File
@@ -55,7 +55,7 @@ defmodule Config do
It has to be replaced by:
for config <- "apps/*/config/config.exs" |> Path.expand() |> Path.wildcard() do
for config <- "../apps/*/config/config.exs" |> Path.expand(__DIR__) |> Path.wildcard() do
import_config config
end
+2 -2
View File
@@ -220,9 +220,9 @@ defmodule Config.Provider do
end
defp write_config!(config, path) do
contents = :io_lib.format("%% coding: utf-8~n~p.~n", [config])
contents = :io_lib.format("%% coding: utf-8~n~tw.~n", [config])
case File.write(path, contents) do
case File.write(path, contents, [:utf8]) do
:ok ->
:ok
+1 -1
View File
@@ -208,7 +208,7 @@ defmodule Exception do
{_, kind, _, clauses} <- List.keyfind(defs, {function, arity}, 0) do
clauses =
for {meta, ex_args, guards, _block} <- clauses do
scope = :elixir_erl.scope(meta)
scope = :elixir_erl.scope(meta, true)
{erl_args, scope} =
:elixir_erl_clauses.match(&:elixir_erl_pass.translate_args/2, ex_args, scope)
-1
View File
@@ -142,7 +142,6 @@ defmodule GenServer do
The generated `child_spec/1` can be customized with the following options:
* `:id` - the child specification identifier, defaults to the current module
* `:start` - how to start the child process (defaults to calling `__MODULE__.start_link/1`)
* `:restart` - when the child should be restarted, defaults to `:permanent`
* `:shutdown` - how to shut down the child, either immediately or by giving it time to shut down
+8 -4
View File
@@ -4433,14 +4433,18 @@ defmodule Kernel do
defoverridable message: 1
@impl true
def exception(msg) when is_binary(msg) do
def exception(msg) when Kernel.is_binary(msg) do
exception(message: msg)
end
end
# TODO: Change the implementation on v2.0 to simply call Kernel.struct!/2
# Calls to Kernel functions must be fully-qualified to ensure
# reproducible builds; otherwise, this macro will generate ASTs
# with different metadata (:import, :context) depending on if
# it is the bootstrapped version or not.
@impl true
def exception(args) when is_list(args) do
def exception(args) when Kernel.is_list(args) do
struct = __struct__()
{valid, invalid} = Enum.split_with(args, fn {k, _} -> Map.has_key?(struct, k) end)
@@ -4451,9 +4455,9 @@ defmodule Kernel do
_ ->
IO.warn(
"the following fields are unknown when raising " <>
"#{inspect(__MODULE__)}: #{inspect(invalid)}. " <>
"#{Kernel.inspect(__MODULE__)}: #{Kernel.inspect(invalid)}. " <>
"Please make sure to only give known fields when raising " <>
"or redefine #{inspect(__MODULE__)}.exception/1 to " <>
"or redefine #{Kernel.inspect(__MODULE__)}.exception/1 to " <>
"discard unknown fields. Future Elixir versions will raise on " <>
"unknown fields given to raise/2"
)
+2 -2
View File
@@ -461,13 +461,13 @@ defmodule Regex do
end
defp safe_run(
%Regex{re_pattern: compiled, source: source, re_version: version},
%Regex{re_pattern: compiled, source: source, re_version: version, opts: compile_opts},
string,
options
) do
case version() do
^version -> :re.run(string, compiled, options)
_ -> :re.run(string, source, options)
_ -> :re.run(string, source, translate_options(compile_opts, options))
end
end
-1
View File
@@ -306,7 +306,6 @@ defmodule Supervisor do
following options:
* `:id` - the child specification identifier, defaults to the current module
* `:start` - how to start the child process (defaults to calling `__MODULE__.start_link/1`)
* `:restart` - when the supervisor should be restarted, defaults to `:permanent`
The `@doc` annotation immediately preceding `use Supervisor` will be
+8 -4
View File
@@ -910,10 +910,14 @@ defmodule System do
`convert_time_unit/3` accepts an additional time unit (other than the
ones in the `t:time_unit/0` type) called `:native`. `:native` is the time
unit used by the Erlang runtime system. It's determined when the runtime
starts and stays the same until the runtime is stopped. To determine what
the `:native` unit amounts to in a system, you can call this function to
convert 1 second to the `:native` time unit (i.e.,
`System.convert_time_unit(1, :second, :native)`).
starts and stays the same until the runtime is stopped, but could differ
the next time the runtime is started on the same machine. For this reason,
you should use this function to convert `:native` time units to a predictable
unit before you display them to humans.
To determine how many seconds the `:native` unit represents in your current
runtime, you can can call this function to convert 1 second to the `:native`
time unit: `System.convert_time_unit(1, :second, :native)`.
"""
@spec convert_time_unit(integer, time_unit | :native, time_unit | :native) :: integer
def convert_time_unit(time, from_unit, to_unit) do
-1
View File
@@ -97,7 +97,6 @@ defmodule Task do
`child_spec/1` can be customized with the following options:
* `:id` - the child specification identifier, defaults to the current module
* `:start` - how to start the child process (defaults to calling `__MODULE__.start_link/1`)
* `:restart` - when the child should be restarted, defaults to `:temporary`
* `:shutdown` - how to shut down the child, either immediately or by giving it time to shut down
+4 -3
View File
@@ -31,16 +31,17 @@ defmodule Tuple do
The functions in this module that add and remove elements from tuples are
rarely used in practice, as they typically imply tuples are being used as
collections. To append to a tuple, it is preferable to use pattern matching:
collections. To append to a tuple, it is preferable to extract the elements
from the old tuple with pattern matching, and then create a new tuple:
tuple = {:ok, :example}
# Avoid
Tuple.insert_at(tuple, 2, %{})
result = Tuple.insert_at(tuple, 2, %{})
# Prefer
{:ok, atom} = tuple
{:ok, atom, %{}}
result = {:ok, atom, %{}}
"""
+1
View File
@@ -13,6 +13,7 @@
backup_vars=nil, %% a copy of vars to be used on ^var
extra_guards=[], %% extra guards from args expansion
counter=#{}, %% a map counting the variables defined
expand_captures=false, %% a boolean to control if captures should be expanded
stacktrace=false %% holds information about the stacktrace variable
}).
+7 -7
View File
@@ -1,7 +1,7 @@
%% Compiler backend to Erlang.
-module(elixir_erl).
-export([elixir_to_erl/1, definition_to_anonymous/4, compile/1, consolidate/3,
get_ann/1, debug_info/4, scope/1, format_error/1]).
get_ann/1, debug_info/4, scope/2, format_error/1]).
-include("elixir.hrl").
-define(typespecs, 'Elixir.Kernel.Typespec').
@@ -48,7 +48,7 @@ get_ann([], Gen, Line) -> erl_anno:set_generated(Gen, Line).
%% Converts an Elixir definition to an anonymous function.
definition_to_anonymous(Module, Kind, Meta, Clauses) ->
ErlClauses = [translate_clause(Kind, Clause) || Clause <- Clauses],
ErlClauses = [translate_clause(Kind, Clause, true) || Clause <- Clauses],
Fun = {'fun', ?ann(Meta), {clauses, ErlClauses}},
LocalHandler = fun(LocalName, LocalArgs) -> invoke_local(Module, LocalName, LocalArgs) end,
{value, Result, _Binding} = erl_eval:expr(Fun, [], {value, LocalHandler}),
@@ -117,8 +117,8 @@ elixir_to_erl_cons(T) -> elixir_to_erl(T).
%% Returns a scope for translation.
scope(_Meta) ->
#elixir_erl{}.
scope(_Meta, ExpandCaptures) ->
#elixir_erl{expand_captures=ExpandCaptures}.
%% Static compilation hook, used in protocol consolidation
@@ -220,15 +220,15 @@ add_definition(Meta, Body, {Head, Tail}) ->
end.
translate_definition(Kind, Meta, {Name, Arity}, Clauses) ->
ErlClauses = [translate_clause(Kind, Clause) || Clause <- Clauses],
ErlClauses = [translate_clause(Kind, Clause, false) || Clause <- Clauses],
case is_macro(Kind) of
true -> {function, ?ann(Meta), elixir_utils:macro_name(Name), Arity + 1, ErlClauses};
false -> {function, ?ann(Meta), Name, Arity, ErlClauses}
end.
translate_clause(Kind, {Meta, Args, Guards, Body}) ->
S = scope(Meta),
translate_clause(Kind, {Meta, Args, Guards, Body}, ExpandCaptures) ->
S = scope(Meta, ExpandCaptures),
{TClause, TS} = elixir_erl_clauses:clause(Meta,
fun elixir_erl_pass:translate_args/2, Args, Body, Guards, S),
+10 -2
View File
@@ -69,7 +69,7 @@ translate({'__CALLER__', Meta, Atom}, S) when is_atom(Atom) ->
translate({'__STACKTRACE__', Meta, Atom}, S = #elixir_erl{stacktrace={Var, _}}) when is_atom(Atom) ->
{{var, ?ann(Meta), Var}, S#elixir_erl{stacktrace={Var, true}}};
translate({'super', Meta, Args}, S) ->
translate({'super', Meta, Args}, S) when is_list(Args) ->
%% In the expanded AST, super is used to invoke a function
%% in the current module originated from a default clause
%% or a super call.
@@ -96,7 +96,15 @@ translate({'&', Meta, [{'/', _, [{{'.', _, [Remote, Fun]}, _, []}, Arity]}]}, S)
{{'fun', Ann, {function, TRemote, TFun, TArity}}, SR};
translate({'&', Meta, [{'/', _, [{Fun, _, Atom}, Arity]}]}, S)
when is_atom(Fun), is_atom(Atom), is_integer(Arity) ->
{{'fun', ?ann(Meta), {function, Fun, Arity}}, S};
case S of
#elixir_erl{expand_captures=true} ->
Vars = [{list_to_atom("arg" ++ integer_to_list(Counter)), [], ?MODULE}
|| Counter <- tl(lists:seq(0, Arity))],
translate({'fn', Meta, [{'->', Meta, [Vars, {Fun, Meta, Vars}]}]}, S);
#elixir_erl{expand_captures=false} ->
{{'fun', ?ann(Meta), {function, Fun, Arity}}, S}
end;
translate({fn, Meta, Clauses}, S) ->
Transformer = fun({'->', CMeta, [ArgsWithGuards, Expr]}, Acc) ->
+15 -5
View File
@@ -10,14 +10,21 @@
-include("elixir.hrl").
-define(cache, {elixir, cache_env}).
-define(locals, {elixir, locals}).
-define(tracker, 'Elixir.Module.LocalsTracker').
setup({DataSet, _DataBag}) ->
ets:insert(DataSet, {?cache, 0, nil}),
case elixir_config:get(bootstrap) of
false -> ets:insert(DataSet, {?locals, true});
true -> ok
end,
ok.
stop({_DataSet, _DataBag}) ->
ok.
stop({DataSet, _DataBag}) ->
ets:delete(DataSet, ?locals).
yank(Tuple, Module) ->
if_tracker(Module, fun(Tracker) -> ?tracker:yank(Tracker, Tuple) end).
@@ -44,9 +51,12 @@ if_tracker(Module, Callback) ->
if_tracker(Module, ok, Callback).
if_tracker(Module, Default, Callback) ->
try {elixir_config:get(bootstrap), elixir_module:data_tables(Module)} of
{true, _Tracker} -> Default;
{false, Tracker} -> Callback(Tracker)
try
{DataSet, _} = Tables = elixir_module:data_tables(Module),
{ets:member(DataSet, ?locals), Tables}
of
{true, Tracker} -> Callback(Tracker);
{false, _} -> Default
catch
error:badarg -> Default
end.
@@ -449,6 +449,12 @@ defmodule Code.Formatter.CallsTest do
assert_same "unquote(call)()"
assert_same "unquote(call)(one, two)"
assert_same """
unquote(call)() do
:ok
end
"""
assert_same """
unquote(call)(one, two) do
:ok
@@ -668,6 +674,11 @@ defmodule Code.Formatter.CallsTest do
assert_same "foo.bar(call)()"
assert_same "foo.bar(call)(one, two)"
assert_same """
foo.bar(call)() do
end
"""
assert_same """
foo.bar(call)(one, two) do
:ok
@@ -18,7 +18,7 @@ defmodule Config.ProviderTest do
File.rm_rf(@tmp_path)
File.mkdir_p!(@tmp_path)
File.write!(@sys_config, :io_lib.format("~p.~n", [context[:sys_config] || []]))
File.write!(@sys_config, :io_lib.format("~tw.~n", [context[:sys_config] || []]), [:utf8])
on_exit(fn ->
Application.delete_env(@config_app, :config_providers)
@@ -50,14 +50,16 @@ defmodule Config.ProviderTest do
assert config[@config_app] == [config_providers: :booted]
end
test "writes extra config" do
init_and_assert_boot(extra_config: [my_app: [key: :old_value, sys_key: :sys_value]])
assert consult(@sys_config)[:my_app] == [sys_key: :sys_value, key: :value]
@tag sys_config: [my_app: [encoding: {:"£", "£", '£'}]]
test "writes sys_config with encoding" do
init_and_assert_boot()
config = consult(@sys_config)
assert config[:my_app][:encoding] == {:"£", "£", '£'}
end
@tag sys_config: [my_app: [key: :old_value, sys_key: :sys_value, extra_config: :old_value]]
test "overrides sys_config" do
init_and_assert_boot(extra_config: [my_app: [extra_config: :value]])
test "writes extra config with overrides" do
init_and_assert_boot(extra_config: [my_app: [key: :old_extra_value, extra_config: :value]])
assert consult(@sys_config)[:my_app] ==
[sys_key: :sys_value, extra_config: :value, key: :value]
@@ -34,6 +34,10 @@ defmodule Kernel.MacrosTest do
quote(do: 1 + unquote(value))
end
defmacro my_macro_with_capture(value) do
Enum.map(value, &by_two/1)
end
test "require" do
assert Kernel.MacrosTest.Nested.value() == 1
end
@@ -42,7 +46,7 @@ defmodule Kernel.MacrosTest do
assert Nested.value() == 1
end
test "local but private macro" do
test "local with private macro" do
assert my_private_macro() == 4
end
@@ -54,6 +58,10 @@ defmodule Kernel.MacrosTest do
assert my_macro_with_local(4) == 17
end
test "local with capture" do
assert my_macro_with_capture([1, 2, 3]) == [2, 4, 6]
end
test "macros cannot be called dynamically" do
x = Nested
assert_raise UndefinedFunctionError, fn -> x.value end
@@ -254,6 +254,11 @@ defmodule Kernel.OverridableTest do
assert Overridable.capture_super_macro(5) == [1, 2, 3, 4, 5, 1, 2, 3, 4, 5]
end
test "super as a variable" do
super = :ok
assert super == :ok
end
test "overridable with many clauses" do
assert Overridable.many_clauses(0) == 11
assert Overridable.many_clauses(1) == 13
+1 -1
View File
@@ -5,7 +5,7 @@ defmodule NodeTest do
doctest Node
@tag :unix
@tag :epmd
test "start/3 and stop/0" do
assert Node.stop() == {:error, :not_found}
assert {:ok, _} = Node.start(:hello, :shortnames, 15000)
+8
View File
@@ -194,6 +194,10 @@ defmodule RegexTest do
assert Regex.run(@re_19_3_little, "abcd abce", capture: :all_names) == ["d"]
end
test "run/3 with regexes with options compiled in different systems" do
assert Regex.run(%{~r/foo/i | re_version: "bad version"}, "FOO") == ["FOO"]
end
test "scan/2" do
assert Regex.scan(~r"c(d|e)", "abcd abce") == [["cd", "d"], ["ce", "e"]]
assert Regex.scan(~r"c(?:d|e)", "abcd abce") == [["cd"], ["ce"]]
@@ -213,6 +217,10 @@ defmodule RegexTest do
assert Regex.scan(@re_19_3_little, "abcd abce", capture: :all_names) == [["d"], ["e"]]
end
test "scan/2 with regexes with options compiled in different systems" do
assert Regex.scan(%{~r/foo/i | re_version: "bad version"}, "FOO") == [["FOO"]]
end
test "split/2,3" do
assert Regex.split(~r",", "") == [""]
assert Regex.split(~r",", "", trim: true) == []
+3 -2
View File
@@ -84,10 +84,11 @@ defmodule CodeFormatterHelpers do
end
assert_timeout = String.to_integer(System.get_env("ELIXIR_ASSERT_TIMEOUT") || "500")
exclude = if PathHelpers.windows?(), do: [unix: true], else: [windows: true]
epmd_exclude = if match?({_, 0}, System.cmd("epmd", ["-daemon"])), do: [], else: [epmd: true]
os_exclude = if PathHelpers.windows?(), do: [unix: true], else: [windows: true]
ExUnit.start(
trace: "--trace" in System.argv(),
assert_receive_timeout: assert_timeout,
exclude: exclude
exclude: epmd_exclude ++ os_exclude
)
+1
View File
@@ -332,6 +332,7 @@ defmodule Logger.Translator do
defp sup_name({:global, name}), do: inspect(name)
defp sup_name({:via, _mod, name}), do: inspect(name)
defp sup_name({pid, mod}), do: [inspect(pid), " (", inspect(mod), ?)]
defp sup_name(unknown_name), do: inspect(unknown_name)
defp sup_context(:start_error), do: "failed to start"
defp sup_context(:child_terminated), do: "terminated"
+17 -5
View File
@@ -264,7 +264,12 @@ defmodule Mix.Compilers.Elixir do
nil -> {[file], false}
end
{source, sources} = List.keytake(sources, file, source(:source))
{source, sources} =
List.keytake(sources, file, source(:source)) ||
Mix.raise(
"Could not find source for #{inspect(file)}. Make sure the :elixirc_paths configuration " <>
"is a list of relative paths to the current project or absolute paths to external directories"
)
source =
source(
@@ -515,10 +520,17 @@ defmodule Mix.Compilers.Elixir do
split_manifest(data, compile_path)
[v | data] when is_integer(v) ->
for module <- data, is_record(module, :module) do
File.rm(Path.join(compile_path, module(module, :beam)))
:code.purge(module(module, :module))
:code.delete(module(module, :module))
try do
for module <- data, is_record(module, :module) do
File.rm(Path.join(compile_path, module(module, :beam)))
:code.purge(module(module, :module))
:code.delete(module(module, :module))
end
rescue
_ ->
Mix.raise(
"Cannot clean-up stale manifest, please run \"mix clean --deps\" manually before proceeding"
)
end
{[], []}
+72 -23
View File
@@ -7,7 +7,8 @@ defmodule Mix.Release do
The Mix.Release struct has the following read-only fields:
* `:name` - the name of the release as an atom
* `:version` - the version of the release as a string
* `:version` - the version of the release as a string or
`{:from_app, app_name}
* `:path` - the path to the release root
* `:version_path` - the path to the release version inside the release
* `:applications` - a map of application with their definitions
@@ -47,7 +48,7 @@ defmodule Mix.Release do
name: atom(),
version: String.t(),
path: String.t(),
version_path: String.t(),
version_path: String.t() | {:from_app, application()},
applications: %{application() => keyword()},
boot_scripts: %{atom() => [{application(), mode()}]},
erts_version: charlist(),
@@ -83,14 +84,14 @@ defmodule Mix.Release do
{include_erts, opts} = Keyword.pop(opts, :include_erts, true)
{erts_source, erts_lib_dir, erts_version} = erts_data(include_erts)
loaded_apps = apps |> Keyword.keys() |> load_apps(%{}, erts_lib_dir)
loaded_apps = apps |> Keyword.keys() |> load_apps(%{}, erts_lib_dir, :maybe)
# Make sure IEx is either an active part of the release or add it as none.
{loaded_apps, apps} =
if Map.has_key?(loaded_apps, :iex) do
{loaded_apps, apps}
else
{load_apps([:iex], loaded_apps, erts_lib_dir), apps ++ [iex: :none]}
{load_apps([:iex], loaded_apps, erts_lib_dir, :maybe), apps ++ [iex: :none]}
end
start_boot = build_start_boot(loaded_apps, apps)
@@ -112,6 +113,24 @@ defmodule Mix.Release do
)
end)
version =
case version do
{:from_app, app} ->
Application.load(app)
version = Application.spec(app, :vsn)
if !version do
Mix.raise(
"Could not find version for #{inspect(app)}, please make sure the application exists"
)
end
to_string(version)
_ ->
version
end
{config_providers, opts} = Keyword.pop(opts, :config_providers, [])
{steps, opts} = Keyword.pop(opts, :steps, [:assemble])
validate_steps!(steps)
@@ -228,36 +247,62 @@ defmodule Mix.Release do
end
end
defp load_apps(apps, seen, otp_root) do
for app <- apps,
not Map.has_key?(seen, app),
reduce: seen do
seen -> load_app(app, seen, otp_root)
defp load_apps(apps, seen, otp_root, included) do
for app <- apps, reduce: seen do
seen ->
if reentrant_seen = reentrant(seen, app, included) do
reentrant_seen
else
load_app(app, seen, otp_root, included)
end
end
end
defp load_app(app, seen, otp_root) do
defp reentrant(seen, app, included) do
properties = seen[app]
cond do
is_nil(properties) ->
nil
included != :maybe and properties[:included] != included ->
if properties[:included] == :maybe do
put_in(seen[app][:included], included)
else
Mix.raise(
"#{inspect(app)} is listed both as a regular application and as an included application"
)
end
true ->
seen
end
end
defp load_app(app, seen, otp_root, included) do
path = Path.join(otp_root, "#{app}-*")
case Path.wildcard(path) do
[] ->
case :code.lib_dir(app) do
{:error, :bad_name} -> Mix.raise("Could not find application #{inspect(app)}")
path -> do_load_app(app, path, seen, otp_root, false)
path -> do_load_app(app, path, seen, otp_root, false, included)
end
paths ->
path = paths |> Enum.sort() |> List.last()
do_load_app(app, to_charlist(path), seen, otp_root, true)
do_load_app(app, to_charlist(path), seen, otp_root, true, included)
end
end
defp do_load_app(app, path, seen, otp_root, otp_app?) do
defp do_load_app(app, path, seen, otp_root, otp_app?, included) do
case :file.consult(Path.join(path, "ebin/#{app}.app")) do
{:ok, terms} ->
[{:application, ^app, properties}] = terms
seen = Map.put(seen, app, [path: path, otp_app?: otp_app?] ++ properties)
load_apps(Keyword.get(properties, :applications, []), seen, otp_root)
value = [path: path, otp_app?: otp_app?, included: included] ++ properties
seen = Map.put(seen, app, value)
seen = load_apps(Keyword.get(properties, :applications, []), seen, otp_root, false)
load_apps(Keyword.get(properties, :included_applications, []), seen, otp_root, true)
{:error, reason} ->
Mix.raise("Could not load #{app}.app. Reason: #{inspect(reason)}")
@@ -267,12 +312,16 @@ defmodule Mix.Release do
defp build_start_boot(all_apps, specified_apps) do
specified_apps ++
for(
{app, _props} <- all_apps,
{app, props} <- all_apps,
not List.keymember?(specified_apps, app, 0),
do: {app, :permanent}
do: {app, default_mode(props)}
)
end
defp default_mode(props) do
if props[:included] == true, do: :load, else: :permanent
end
defp build_start_clean_boot(boot) do
for({app, _mode} <- boot, do: {app, :none})
|> Keyword.put(:stdlib, :permanent)
@@ -319,9 +368,9 @@ defmodule Mix.Release do
path = Path.join(release.version_path, "sys.config")
args = [runtime?, sys_config]
format = "%% coding: utf-8~n%% RUNTIME_CONFIG=~s~n~p.~n"
format = "%% coding: utf-8~n%% RUNTIME_CONFIG=~s~n~tw.~n"
File.mkdir_p!(Path.dirname(path))
File.write!(path, :io_lib.format(format, args))
File.write!(path, :io_lib.format(format, args), [:utf8])
case :file.consult(path) do
{:ok, _} ->
@@ -379,7 +428,7 @@ defmodule Mix.Release do
end
end
defp random_cookie, do: Base.url_encode64(:crypto.strong_rand_bytes(40))
defp random_cookie, do: Base.encode32(:crypto.strong_rand_bytes(32))
@doc """
Makes the start_erl.data file with the
@@ -406,7 +455,7 @@ defmodule Mix.Release do
:ok | {:error, String.t()}
def make_boot_script(release, path, modes, prepend_paths \\ []) do
with {:ok, rel_spec} <- build_release_spec(release, modes) do
File.write!(path <> ".rel", consultable(rel_spec))
File.write!(path <> ".rel", consultable(rel_spec), [:utf8])
sys_path = String.to_charlist(path)
@@ -429,7 +478,7 @@ defmodule Mix.Release do
|> prepend_paths_to_script(prepend_paths)
script = {:script, rel_info, instructions}
File.write!(script_path, consultable(script))
File.write!(script_path, consultable(script), [:utf8])
:ok = :systools.script2boot(sys_path)
{:error, module, info} ->
@@ -546,7 +595,7 @@ defmodule Mix.Release do
end
defp consultable(term) do
:io_lib.format("%% coding: utf-8~n~p.~n", [term])
:io_lib.format("%% coding: utf-8~n~tp.~n", [term])
end
@doc """
+1 -1
View File
@@ -498,7 +498,7 @@ defmodule Mix.Tasks.Format do
end
defp to_bullet_list(files) do
Enum.map_join(files, "\n", &" * #{&1}")
Enum.map_join(files, "\n", &" * #{&1 |> to_string() |> Path.relative_to_cwd()}")
end
defp equivalent?(input, output) do
+32 -6
View File
@@ -114,7 +114,15 @@ defmodule Mix.Tasks.Release do
distribution. For example, if you need to do some prep work before
running the actual system, like migrating your database, `eval` can
be a good fit. Just keep in mind any application you may use during
eval has to be explicitly started.
eval has to be explicitly loaded and/or started.
You can start an application by calling `Application.ensure_all_started/1`.
However, if for some reason you cannot start an application, maybe
cause it will run other services you do not want, you must at least
load the application by calling `Application.load/1`. If you don't
load the application, any attempt at reading its environment or
configuration may fail. Note that if you start an application,
it is automatically loaded before started.
Another way to run commands is with `rpc`, which will connect to the
system currently running and instruct it to execute the given
@@ -133,7 +141,9 @@ defmodule Mix.Tasks.Release do
defmodule MyApp.ReleaseTasks do
def eval_purge_stale_data() do
# Eval commands needs to start the app before
# Or Application.load(:my_app) if you can't start it
Application.ensure_all_started(:my_app)
# Code that purges stale data
...
end
@@ -376,8 +386,10 @@ defmodule Mix.Tasks.Release do
* `:path` - the path the release should be installed to.
Defaults to `"_build/MIX_ENV/rel/RELEASE_NAME"`.
* `:version` - the release version as a string. Defaults to the current
application version.
* `:version` - the release version as a string or `{:from_app, app_name}`.
Defaults to the current application version. The `{:from_app, app_name}` format
can be used to easily reference the application version from another application.
This is particularly useful in umbrella applications.
* `:quiet` - a boolean that controls if releases should write to the standard
output its steps. Defaults to `false`.
@@ -443,11 +455,25 @@ defmodule Mix.Tasks.Release do
to make sure the Erlang Distribution listens only on a given port
known at runtime, you can set the following:
export ELIXIR_ERL_OPTIONS="-kernel inet_dist_listen_min $BEAM_PORT inet_dist_listen_max $BEAM_PORT"
case $RELEASE_COMMAND in
start*|daemon*)
ELIXIR_ERL_OPTIONS="-kernel inet_dist_listen_min $BEAM_PORT inet_dist_listen_max $BEAM_PORT"
export ELIXIR_ERL_OPTIONS
;;
*)
;;
esac
Or for Windows, in your `env.bat`:
Note we only set the port on start/daemon commands. If you also limit
the port on other commands, such as `rpc`, then you will be unable
to establish a remote connection as the port will already be in use
by the node.
set ELIXIR_ERL_OPTIONS="-kernel inet_dist_listen_min %BEAM_PORT% inet_dist_listen_max %BEAM_PORT%"
On Windows, your `env.bat` would look like this:
IF NOT %RELEASE_COMMAND:start=%==%RELEASE_COMMAND% (
set ELIXIR_ERL_OPTIONS="-kernel inet_dist_listen_min %BEAM_PORT% inet_dist_listen_max %BEAM_PORT%"
)
### Steps
+12 -7
View File
@@ -60,11 +60,15 @@ defmodule Mix.Tasks.Release.Init do
#!/bin/sh
# Sets and enables heart (recommended only in daemon mode)
# if [ "$RELEASE_COMMAND" = "daemon" ] || [ "$RELEASE_COMMAND" = "daemon_iex" ]; then
# HEART_COMMAND="$RELEASE_ROOT/bin/$RELEASE_NAME $RELEASE_COMMAND"
# export HEART_COMMAND
# export ELIXIR_ERL_OPTIONS="-heart"
# fi
# case $RELEASE_COMMAND in
# daemon*)
# HEART_COMMAND="$RELEASE_ROOT/bin/$RELEASE_NAME $RELEASE_COMMAND"
# export HEART_COMMAND
# export ELIXIR_ERL_OPTIONS="-heart"
# ;;
# *)
# ;;
# esac
# Set the release to work across nodes. If using the long name format like
# the one below (my_app@127.0.0.1), you need to also uncomment the
@@ -281,7 +285,7 @@ defmodule Mix.Tasks.Release.Init do
findstr "RUNTIME_CONFIG=true" "!RELEASE_SYS_CONFIG!.config" >nul 2>&1 && (
for /f "skip=1" %%X in ('wmic os get localdatetime') do if not defined TIMESTAMP set TIMESTAMP=%%X
set RELEASE_SYS_CONFIG=!RELEASE_TMP!\!RELEASE_NAME!-!RELEASE_VSN!-!TIMESTAMP:~0,11!-!RANDOM!.runtime
mkdir "!RELEASE_TMP!" >nul
mkdir "!RELEASE_TMP!" >nul 2>&1
copy /y "!REL_VSN_DIR!\sys.config" "!RELEASE_SYS_CONFIG!.config" >nul || (
echo Cannot start release because it could not write to "!RELEASE_SYS_CONFIG!.config"
goto end
@@ -374,7 +378,8 @@ defmodule Mix.Tasks.Release.Init do
)
!ERLSRV! add !RELEASE_NAME!_!RELEASE_NAME! ^
-name "!RELEASE_NODE!" ^
-!RELEASE_DISTRIBUTION! "!RELEASE_NODE!" ^
-env RELEASE_ROOT=!RELEASE_ROOT! -env RELEASE_NAME=!RELEASE_NAME! -env RELEASE_VSN=!RELEASE_VSN! -env RELEASE_COOKIE=!RELEASE_COOKIE! -env RELEASE_NODE=!RELEASE_NODE! -env RELEASE_VM_ARGS=!RELEASE_VM_ARGS! -env RELEASE_TMP=!RELEASE_TMP! -env RELEASE_SYS_CONFIG=!RELEASE_SYS_CONFIG! ^
-args "-setcookie !RELEASE_COOKIE! -config !RELEASE_SYS_CONFIG! -mode !RELEASE_MODE! -boot !REL_VSN_DIR!\start -boot_var RELEASE_LIB !RELEASE_ROOT!\lib -args_file !REL_VSN_DIR!\vm.args"
if %ERRORLEVEL% EQU 0 (
+1
View File
@@ -1,3 +1,4 @@
use Mix.Config
config :release_test, :static, :was_set
config :release_test, :encoding, {:"£", "£", '£'}
+3 -2
View File
@@ -19,11 +19,12 @@ defmodule ReleaseTest do
static_config: Application.fetch_env(:release_test, :static),
runtime_config: Application.fetch_env(:release_test, :runtime),
sys_config_init: to_string(sys_config),
sys_config_env: System.get_env("RELEASE_SYS_CONFIG")
sys_config_env: System.get_env("RELEASE_SYS_CONFIG"),
encoding: Application.get_env(:release_test, :encoding)
}
path = Path.join(System.get_env("RELEASE_ROOT"), "RELEASE_BOOTED")
File.write!(path, inspect(info))
File.write!(path, :erlang.term_to_binary(info))
if System.get_env("RELEASE_NAME") =~ "permanent" do
Supervisor.start_link([], strategy: :one_for_one)
+71
View File
@@ -50,6 +50,21 @@ defmodule Mix.ReleaseTest do
assert release.options[:quiet]
end
test "allows specifying the version from an application" do
overrides = [version: {:from_app, :elixir}]
release = from_config!(nil, config(), overrides)
assert release.version == to_string(Application.spec(:elixir, :vsn))
end
test "raises when :from_app is used with an app that doesn't exist" do
overrides = [version: {:from_app, :not_valid}]
assert_raise Mix.Error,
~r"Could not find version for :not_valid, please make sure the application exists",
fn -> from_config!(nil, config(), overrides) end
end
test "includes applications" do
release = from_config!(nil, config(), [])
assert release.applications.mix[:path] == to_charlist(Application.app_dir(:mix))
@@ -437,6 +452,14 @@ defmodule Mix.ReleaseTest do
assert contents =~ "[{foo,[{bar,baz}]}]."
end
test "writes sys_config with encoding" do
assert make_sys_config(release([]), [encoding: {:"£", "£", '£'}], "unused/runtime/path") ==
:ok
{:ok, contents} = :file.consult(@sys_config)
assert contents == [[encoding: {:"£", "£", '£'}]]
end
test "writes the given sys_config with config providers" do
release = release(config_providers: @providers)
assert make_sys_config(release, [kernel: [key: :value]], "/foo/bar/bat") == :ok
@@ -569,6 +592,54 @@ defmodule Mix.ReleaseTest do
end
end
describe "included applications" do
test "are included in the release", context do
in_tmp(context.test, fn ->
app =
{:application, :my_sample1,
applications: [:kernel, :stdlib, :elixir],
description: 'my_sample1',
modules: [],
vsn: '1.0.0',
included_applications: [:runtime_tools]}
File.mkdir_p!("my_sample1/ebin")
Code.prepend_path("my_sample1/ebin")
format = :io_lib.format("%% coding: utf-8~n~p.~n", [app])
File.write!("my_sample1/ebin/my_sample1.app", format)
release = release(applications: [my_sample1: :permanent])
assert release.applications.runtime_tools[:included]
assert release.boot_scripts.start[:runtime_tools] == :load
release = release(applications: [my_sample1: :permanent, runtime_tools: :none])
assert release.applications.runtime_tools[:included]
assert release.boot_scripts.start[:runtime_tools] == :none
end)
end
test "raise on conflict", context do
in_tmp(context.test, fn ->
app =
{:application, :my_sample2,
applications: [:kernel, :stdlib, :elixir, :runtime_tools],
description: 'my_sample',
modules: [],
vsn: '1.0.0',
included_applications: [:runtime_tools]}
File.mkdir_p!("my_sample2/ebin")
Code.prepend_path("my_sample2/ebin")
format = :io_lib.format("%% coding: utf-8~n~p.~n", [app])
File.write!("my_sample2/ebin/my_sample2.app", format)
assert_raise Mix.Error,
":runtime_tools is listed both as a regular application and as an included application",
fn -> release(applications: [my_sample2: :permanent]) end
end)
end
end
defp size!(path) do
File.stat!(path).size
end
+33 -18
View File
@@ -140,8 +140,9 @@ defmodule Mix.Tasks.ReleaseTest do
static_config: {:ok, :was_set},
runtime_config: :error,
sys_config_env: sys_config_env,
sys_config_init: sys_config_init
} = wait_until_evaled(Path.join(root, "RELEASE_BOOTED"))
sys_config_init: sys_config_init,
encoding: {:"£", "£", '£'}
} = wait_until_decoded(Path.join(root, "RELEASE_BOOTED"))
if match?({:win32, _}, :os.type()) do
# `RELEAS~1` is the DOS path name (8 character) for the `release_test` directory
@@ -169,6 +170,7 @@ defmodule Mix.Tasks.ReleaseTest do
File.write!("config/releases.exs", """
import Config
config :release_test, :runtime, :was_set
config :release_test, :encoding, {:runtime, :"£", "£", '£'}
""")
root = Path.absname("_build/dev/rel/runtime_config")
@@ -198,8 +200,9 @@ defmodule Mix.Tasks.ReleaseTest do
static_config: {:ok, :was_set},
runtime_config: {:ok, :was_set},
sys_config_env: sys_config_env,
sys_config_init: sys_config_init
} = wait_until_evaled(Path.join(root, "RELEASE_BOOTED"))
sys_config_init: sys_config_init,
encoding: {:runtime, :"£", "£", '£'}
} = wait_until_decoded(Path.join(root, "RELEASE_BOOTED"))
if match?({:win32, _}, :os.type()) do
assert sys_config_env =~ "tmp\\runtime_config-0.1.0"
@@ -250,7 +253,7 @@ defmodule Mix.Tasks.ReleaseTest do
root_dir: root_dir,
static_config: {:ok, :was_set},
runtime_config: :error
} = wait_until_evaled(Path.join(root, "RELEASE_BOOTED"))
} = wait_until_decoded(Path.join(root, "RELEASE_BOOTED"))
if match?({:win32, _}, :os.type()) do
assert String.ends_with?(app_dir, "demo/lib/release_test-0.1.0")
@@ -265,6 +268,7 @@ defmodule Mix.Tasks.ReleaseTest do
end)
end
@tag :epmd
test "executes rpc instructions" do
in_fixture("release_test", fn ->
config = [releases: [permanent1: [include_erts: false]]]
@@ -275,12 +279,13 @@ defmodule Mix.Tasks.ReleaseTest do
script = Path.join(root, "bin/permanent1")
open_port(script, ['start'])
wait_until_evaled(Path.join(root, "RELEASE_BOOTED"))
wait_until_decoded(Path.join(root, "RELEASE_BOOTED"))
assert System.cmd(script, ["rpc", "ReleaseTest.hello_world"]) == {"hello world\n", 0}
assert System.cmd(script, ["stop"]) == {"", 0}
assert {pid, 0} = System.cmd(script, ["pid"])
assert pid != "\n"
assert System.cmd(script, ["stop"]) == {"", 0}
end)
end)
end
@@ -324,7 +329,7 @@ defmodule Mix.Tasks.ReleaseTest do
release_vsn: "0.1.0",
static_config: {:ok, :was_set},
runtime_config: {:ok, :was_set}
} = wait_until_evaled(Path.join(root, "RELEASE_BOOTED"))
} = wait_until_decoded(Path.join(root, "RELEASE_BOOTED"))
end)
end)
end
@@ -341,22 +346,27 @@ defmodule Mix.Tasks.ReleaseTest do
script = Path.join(root, "bin/permanent2")
open_port(script, ['daemon_iex'])
assert wait_until_evaled(Path.join(root, "RELEASE_BOOTED")) == %{
app_dir: Path.join(root, "lib/release_test-0.1.0"),
assert %{
app_dir: app_dir,
cookie_env: "abcdefghij",
mode: :embedded,
node: :"permanent2@#{@hostname}",
node: release_node("permanent2"),
protocols_consolidated?: true,
release_name: "permanent2",
release_node: "permanent2",
release_root: root,
release_root: ^root,
release_vsn: "0.1.0",
root_dir: :code.root_dir() |> to_string(),
root_dir: root_dir,
static_config: {:ok, :was_set},
runtime_config: :error,
sys_config_env: Path.join(root, "releases/0.1.0/sys"),
sys_config_init: Path.join(root, "releases/0.1.0/sys")
}
sys_config_env: sys_config_env,
sys_config_init: sys_config_init
} = wait_until_decoded(Path.join(root, "RELEASE_BOOTED"))
assert app_dir == Path.join(root, "lib/release_test-0.1.0")
assert root_dir == :code.root_dir() |> to_string()
assert sys_config_env == Path.join(root, "releases/0.1.0/sys")
assert sys_config_init == Path.join(root, "releases/0.1.0/sys")
assert wait_until(fn ->
File.read!(Path.join(root, "tmp/log/erlang.log.1")) =~
@@ -402,8 +412,13 @@ defmodule Mix.Tasks.ReleaseTest do
Port.open({:spawn_executable, to_charlist(command)}, [:hide, args: args])
end
defp wait_until_evaled(file) do
wait_until(fn -> File.exists?(file) && Code.eval_file(file) |> elem(0) end)
defp wait_until_decoded(file) do
wait_until(fn ->
case File.read(file) do
{:ok, bin} when byte_size(bin) > 0 -> :erlang.binary_to_term(bin)
_ -> nil
end
end)
end
defp wait_until(fun) do
+3 -2
View File
@@ -5,8 +5,9 @@ Application.put_env(:mix, :colors, enabled: false)
Logger.remove_backend(:console)
Application.put_env(:logger, :backends, [])
exclude = if match?({:win32, _}, :os.type()), do: [unix: true], else: [windows: true]
ExUnit.start(trace: "--trace" in System.argv(), exclude: exclude)
os_exclude = if match?({:win32, _}, :os.type()), do: [unix: true], else: [windows: true]
epmd_exclude = if match?({_, 0}, System.cmd("epmd", ["-daemon"])), do: [], else: [epmd: true]
ExUnit.start(trace: "--trace" in System.argv(), exclude: epmd_exclude ++ os_exclude)
unless {1, 7, 4} <= Mix.SCM.Git.git_version() do
IO.puts(:stderr, "Skipping tests with git sparse checkouts...")