Compare commits

...
39 Commits
Author SHA1 Message Date
Dmitry Kakurin 762989b39f Fix Path.absname/1 to correctly handle UNC paths on Windows (#9689) 2020-01-06 21:44:27 +01:00
Lasse Skindstad Ebert 1767df4747 Fix release tar when include_erts is false (#9570)
Now excludes files that does not exist on the file system. This could
happen e.g. if the release is built with `include_erts: false`.

Otherwise building the tar would fail with `:enoent`.
2019-11-22 11:20:54 +01:00
José Valim 055526057a Release v1.9.4 2019-11-05 16:29:34 +01:00
José Valim 5af52898d3 Release v1.9.3 2019-11-05 13:42:49 +01:00
José Valim 34dc2466e4 Remove warnings from rebar3 safe install 2019-11-05 12:42:33 +01:00
Bram Verburg fad48c401f Warn when using unsafe URL local installs 2019-11-05 11:53:35 +01:00
Michał Kalbarczyk 215229c28f Make release's boot scripts deterministic (#9387) 2019-11-03 18:02:23 +01:00
José Valim 1ea09243c5 Clarify escaping rules in sigils, closes #9471 2019-11-03 11:14:39 +01:00
Chris de Graaf 96c9500afd Use default_release option when name is not given (#9158) 2019-10-21 18:27:19 +02:00
Gary Rennie b6ea714339 Add :tar option for releases to create a tarball (#9290) 2019-10-16 17:37:26 +02:00
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
46 changed files with 776 additions and 166 deletions
+110
View File
@@ -58,6 +58,115 @@ 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.4 (2019-11-05)
### 1. Bug fixes
#### Mix
* [mix local.hex] Remove invalid deprecation warning on `mix local.hex` command
## v1.9.3 (2019-11-05)
Note this release deprecates the use of URLs on `mix archive.install`, `mix escript.install`, and `mix local.rebar`. Support for passing URLs to said commands will be fully removed on Elixir v1.10, as they are unsafe. Thanks to Bram Verburg for the report and for providing a fix.
The alternative is straight-forward: you can simply download the artifact via the command line and then invoke the command with a file system path. For example, instead of:
$ mix archive.install https://example.org/installer.ez
You can execute on Unix (Linux, MacOS X):
$ wget https://example.org/installer.ez
$ mix archive.install installer.ez
or
$ curl -o installer.ez https://example.org/installer.ez
$ mix archive.install installer.ez
On Windows (Win7 or later):
> powershell -Command "Invoke-WebRequest https://example.org/installer.ez -OutFile installer.ez"
> mix archive.install installer.ez
or
> powershell -Command "(New-Object Net.WebClient).DownloadFile('https://example.org/installer.ez', 'installer.ez')"
> mix archive.install installer.ez
Note that, if you are a library author, consider providing installable escripts and archives through Hex, such as Phoenix:
$ mix archive.install hex phx_new
Installations through Hex are always safe and they come with version management and all other benefits from Hex too.
### 1. Enhancements
#### Mix
* [mix release] Add :tar option for releases to create a tarball
### 2. Bug fixes
#### Mix
* [mix release] Use `default_release` option when name is not given
* [mix release] Make release's boot script contents deterministic
### 3. Deprecations
#### Mix
* [mix archive.install] Warn when installing from URI
* [mix escript.install] Warn when installing from URI
* [mix local.rebar] Warn when installing from URI
## 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 +244,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.4
+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
+29 -13
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"
)
@@ -4880,16 +4884,24 @@ defmodule Kernel do
@doc ~S"""
Handles the sigil `~S` for strings.
It simply returns a string without escaping characters and without
interpolations.
It returns a string without interpolations and without escape
characters, except for the escaping of the closing sigil character
itself.
## Examples
iex> ~S(foo)
"foo"
iex> ~S(f#{o}o)
"f\#{o}o"
iex> ~S(\o/)
"\\o/"
However, if you want to re-use the sigil character itself on
the string, you need to escape it:
iex> ~S((\))
"()"
"""
defmacro sigil_S(term, modifiers)
@@ -4926,8 +4938,9 @@ defmodule Kernel do
@doc ~S"""
Handles the sigil `~C` for charlists.
It simply returns a charlist without escaping characters and without
interpolations.
It returns a charlist without interpolations and without escape
characters, except for the escaping of the closing sigil character
itself.
## Examples
@@ -5007,8 +5020,10 @@ defmodule Kernel do
@doc ~S"""
Handles the sigil `~R` for regular expressions.
It returns a regular expression pattern without escaping
nor interpreting interpolations.
It returns a regular expression pattern without interpolations and
without escape characters. Note it still supports escape of Regex
tokens (such as escaping `+` or `?`) and it also requires you to
escape the closing sigil character itself if it appears on the Regex.
More information on regexes can be found in the `Regex` module.
@@ -5171,8 +5186,9 @@ defmodule Kernel do
@doc ~S"""
Handles the sigil `~W` for list of words.
It returns a list of "words" split by whitespace without escaping nor
interpreting interpolations.
It returns a list of "words" split by whitespace without interpolations
and without escape characters, except for the escaping of the closing
sigil character itself.
## Modifiers
+7 -2
View File
@@ -95,6 +95,8 @@ defmodule Path do
absname(absname_join(name), cwd)
end
@slash [?/, ?\\]
# Joins a list
defp absname_join([name1, name2 | rest]), do: absname_join([absname_join(name1, name2) | rest])
@@ -110,6 +112,11 @@ defmodule Path do
do_absname_join(rest, relativename, [?:, uc_letter + ?a - ?A], :win32)
end
defp do_absname_join(<<c1, c2, rest::binary>>, relativename, [], :win32)
when c1 in @slash and c2 in @slash do
do_absname_join(rest, relativename, '//', :win32)
end
defp do_absname_join(<<?\\, rest::binary>>, relativename, result, :win32),
do: do_absname_join(<<?/, rest::binary>>, relativename, result, :win32)
@@ -254,8 +261,6 @@ defmodule Path do
defp unix_pathtype([list | rest]) when is_list(list), do: unix_pathtype(list ++ rest)
defp unix_pathtype(relative), do: {:relative, relative}
@slash [?/, ?\\]
defp win32_pathtype([list | rest]) when is_list(list), do: win32_pathtype(list ++ rest)
defp win32_pathtype([char, list | rest]) when is_list(list),
+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)
+12
View File
@@ -39,6 +39,13 @@ defmodule PathTest do
describe "Windows" do
@describetag :windows
test "absname/1" do
assert Path.absname("//host/path") == "//host/path"
assert Path.absname("\\\\host\\path") == "//host/path"
assert Path.absname("\\/host\\path") == "//host/path"
assert Path.absname("/\\host\\path") == "//host/path"
end
test "relative/1" do
assert Path.relative("C:/usr/local/bin") == "usr/local/bin"
assert Path.relative("C:\\usr\\local\\bin") == "usr\\local\\bin"
@@ -67,6 +74,11 @@ defmodule PathTest do
assert Path.type("/usr/local/bin") == :volumerelative
assert Path.type('usr/local/bin') == :relative
assert Path.type("../usr/local/bin") == :relative
assert Path.type("//host/path") == :absolute
assert Path.type("\\\\host\\path") == :absolute
assert Path.type("/\\host\\path") == :absolute
assert Path.type("\\/host\\path") == :absolute
end
test "split/1" do
+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
{[], []}
+2 -2
View File
@@ -107,7 +107,7 @@ defmodule Mix.Local.Installer do
module.install(basename, binary, previous_files)
:badpath ->
Mix.raise("Expected #{inspect(src)} to be a URL or a local file path")
Mix.raise("Expected #{inspect(src)} to be a local file path")
{:local, message} ->
Mix.raise(message)
@@ -164,7 +164,7 @@ defmodule Mix.Local.Installer do
cond do
local_path?(url_or_path) -> {:local, url_or_path}
file_url?(url_or_path) -> {:url, url_or_path}
true -> {:error, "Expected #{inspect(url_or_path)} to be a URL or a local file path"}
true -> {:error, "Expected #{inspect(url_or_path)} to be a local file path"}
end
end
+100 -32
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
@@ -24,7 +25,8 @@ defmodule Mix.Release do
and `term` is the value given to it on `c:Config.Provider.init/1`
* `:options` - a keyword list with all other user supplied release options
* `:steps` - a list of functions that receive the release and returns a release.
Must also contain the atom `:assemble` which is the internal assembling step
Must also contain the atom `:assemble` which is the internal assembling step.
May also contain the atom `:tar` to create a tarball of the release.
"""
defstruct [
@@ -47,7 +49,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 +85,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 +114,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)
@@ -158,10 +178,16 @@ defmodule Mix.Release do
{name, opts}
[_ | _] ->
Mix.raise(
"\"mix release\" was invoked without a name but there are multiple releases. " <>
"Please call \"mix release NAME\" or set :default_release in your project configuration"
)
case Keyword.get(config, :default_release) do
nil ->
Mix.raise(
"\"mix release\" was invoked without a name but there are multiple releases. " <>
"Please call \"mix release NAME\" or set :default_release in your project configuration"
)
name ->
lookup_release(name, config)
end
end
end
@@ -228,36 +254,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)}")
@@ -266,13 +318,19 @@ defmodule Mix.Release do
defp build_start_boot(all_apps, specified_apps) do
specified_apps ++
for(
{app, _props} <- all_apps,
not List.keymember?(specified_apps, app, 0),
do: {app, :permanent}
Enum.sort(
for(
{app, props} <- all_apps,
not List.keymember?(specified_apps, app, 0),
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)
@@ -280,12 +338,14 @@ defmodule Mix.Release do
end
defp validate_steps!(steps) do
if not is_list(steps) or Enum.any?(steps, &(&1 != :assemble and not is_function(&1, 1))) do
valid_atoms = [:assemble, :tar]
if not is_list(steps) or Enum.any?(steps, &(&1 not in valid_atoms and not is_function(&1, 1))) do
Mix.raise("""
The :steps option must be a list of:
* anonymous function that receives one argument
* the atom :assemble
* the atom :assemble or :tar
Got: #{inspect(steps)}
""")
@@ -295,6 +355,14 @@ defmodule Mix.Release do
Mix.raise("The :steps option must contain the atom :assemble once, got: #{inspect(steps)}")
end
if :assemble in Enum.drop_while(steps, &(&1 != :tar)) do
Mix.raise("The :tar step must come after :assemble")
end
if Enum.count(steps, &(&1 == :tar)) > 1 do
Mix.raise("The :steps option can only contain the atom :tar once")
end
:ok
end
@@ -319,9 +387,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 +447,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 +474,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 +497,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 +614,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 """
+20 -13
View File
@@ -12,7 +12,7 @@ defmodule Mix.Tasks.Archive.Install do
mix do archive.build, archive.install
If an argument is provided, it should be a local path or a URL to a
If an argument is provided, it should be a local path to a
prebuilt archive, a Git repository, a GitHub repository, or a Hex
package.
@@ -35,13 +35,12 @@ defmodule Mix.Tasks.Archive.Install do
mix some_task
Note that installing via Git, GitHub, or Hex fetches the source
of the archive and builds it, while using a URL or a local path
fetches a pre-built archive.
of the archive and builds it, while using local path uses a pre-built archive.
## Command line options
* `--sha512` - checks the archive matches the given SHA-512 checksum. Only
applies to installations via URL or local path
applies to installations via a local path
* `--force` - forces installation without a shell prompt; primarily
intended for automation in build systems like Make
@@ -55,9 +54,6 @@ defmodule Mix.Tasks.Archive.Install do
* `--organization` - specifies an organization to use if fetching the package
from a private Hex repository
* `--timeout` - sets a request timeout in milliseconds for fetching
archives from URLs. Default is 60 seconds
"""
@behaviour Mix.Local.Installer
@@ -77,17 +73,28 @@ defmodule Mix.Tasks.Archive.Install do
end
@impl true
def check_install_spec({local_or_url, path_or_url} = _install_spec, _opts)
when local_or_url in [:local, :url] do
if Path.extname(path_or_url) == ".ez" do
:ok
else
{:error, "Expected a local file path or a file URL ending in \".ez\"."}
def check_install_spec({:local, path} = _install_spec, _opts) do
check_extname(path)
end
def check_install_spec({:url, url} = _install_spec, opts) do
unless opts[:sha512] do
Mix.Utils.warn_install_over_http_deprecated("archive.install", url)
end
check_extname(url)
end
def check_install_spec(_, _), do: :ok
defp check_extname(path_or_url) do
if Path.extname(path_or_url) == ".ez" do
:ok
else
{:error, "Expected a local file path ending in \".ez\"."}
end
end
@impl true
def find_previous_versions(src) do
app =
+10 -6
View File
@@ -12,12 +12,11 @@ defmodule Mix.Tasks.Escript.Install do
mix do escript.build, escript.install
If an argument is provided, it should be a local path or a URL to a prebuilt escript,
If an argument is provided, it should be a local path to a prebuilt escript,
a Git repository, a GitHub repository, or a Hex package.
mix escript.install escript
mix escript.install path/to/escript
mix escript.install https://example.com/my_escript
mix escript.install git https://path/to/git/repo
mix escript.install git https://path/to/git/repo branch git_branch
mix escript.install git https://path/to/git/repo tag git_tag
@@ -40,7 +39,7 @@ defmodule Mix.Tasks.Escript.Install do
## Command line options
* `--sha512` - checks the escript matches the given SHA-512 checksum. Only
applies to installations via URL or local path
applies to installations via a local path
* `--force` - forces installation without a shell prompt; primarily
intended for automation in build systems like Make
@@ -54,9 +53,6 @@ defmodule Mix.Tasks.Escript.Install do
* `--organization` - specifies an organization to use if fetching the package
from a private Hex repository
* `--timeout` - sets a request timeout in milliseconds for fetching
archives from URLs. Default is 60 seconds
"""
@behaviour Mix.Local.Installer
@@ -79,6 +75,14 @@ defmodule Mix.Tasks.Escript.Install do
end
@impl true
def check_install_spec({:url, url} = _install_spec, opts) do
unless opts[:sha512] do
Mix.Utils.warn_install_over_http_deprecated("escript.install", url)
end
:ok
end
def check_install_spec(_, _), do: :ok
@impl true
+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
+42 -3
View File
@@ -25,9 +25,9 @@ defmodule Mix.Tasks.Local.Rebar do
## Command line options
* `rebar PATH` - specifies a path or URL for `rebar`
* `rebar PATH` - specifies a path for `rebar`
* `rebar3 PATH` - specifies a path or URL for `rebar3`
* `rebar3 PATH` - specifies a path for `rebar3`
* `--sha512` - checks the archive matches the given SHA-512 checksum
@@ -48,9 +48,11 @@ defmodule Mix.Tasks.Local.Rebar do
case argv do
["rebar", path | _] ->
if file_url?(path), do: warn_install_over_http_deprecated(:rebar, path)
install_from_path(:rebar, path, opts)
["rebar3", path | _] ->
if file_url?(path), do: warn_install_over_http_deprecated(:rebar3, path)
install_from_path(:rebar3, path, opts)
[] ->
@@ -77,7 +79,7 @@ defmodule Mix.Tasks.Local.Rebar do
Mix.shell().info([:green, "* creating ", :reset, Path.relative_to_cwd(local)])
:badpath ->
Mix.raise("Expected #{inspect(path)} to be a URL or a local file path")
Mix.raise("Expected #{inspect(path)} to be a local file path")
{:local, message} ->
Mix.raise(message)
@@ -100,6 +102,10 @@ defmodule Mix.Tasks.Local.Rebar do
true
end
defp file_url?(url_or_path) do
URI.parse(url_or_path).scheme in ["http", "https"]
end
defp install_from_s3(manager, list_url, escript_url, opts) do
hex_mirror = Mix.Hex.mirror()
list_url = hex_mirror <> list_url
@@ -114,4 +120,37 @@ defmodule Mix.Tasks.Local.Rebar do
install_from_path(manager, url, Keyword.put(opts, :sha512, sha512))
end
defp warn_install_over_http_deprecated(manager, url) do
shell = Mix.shell()
basename = Path.basename(url)
shell.error("""
Warning: the use of HTTP/HTTPS URLs with `mix local.rebar` is deprecated")
Run `mix help local.rebar` for details on installing #{manager} from Hex's CDN.
Alternatively you can fetch the file using an external HTTP client and then
install it locally:
Unix (Linux, MacOS X):
$ wget #{url}
$ mix local.rebar #{manager} #{basename}
or
$ curl -o #{basename} #{url}
$ mix local.rebar #{manager} #{basename}
Windows (Win7 or later):
> powershell -Command "Invoke-WebRequest #{url} -OutFile #{basename}"
> mix local.rebar #{manager} #{basename}
or
> powershell -Command "(New-Object Net.WebClient).DownloadFile('#{url}', '#{basename}')"
> mix local.rebar #{manager} #{basename}
""")
end
end
+69 -7
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
@@ -465,7 +491,9 @@ defmodule Mix.Tasks.Release do
can pass anonymous functions before and after the `:assemble` to
customize your release assembling pipeline. Those anonymous functions
will receive a `Mix.Release` struct and must return the same or
an updated `Mix.Release` struct.
an updated `Mix.Release` struct. It is also possible to build a tarball
of the release by passing the `:tar` step anywhere after `:assemble`.
The tarball is created in `_build/MIX_ENV/rel/RELEASE_NAME-RELEASE_VSN.tar.gz`
See `Mix.Release` for more documentation on the struct and which
fields can be modified. Note that `:steps` field itself can be
@@ -927,6 +955,10 @@ defmodule Mix.Tasks.Release do
end
end
defp run_steps(%{steps: [:tar | steps]} = release) do
%{release | steps: steps} |> make_tar() |> run_steps()
end
defp run_steps(%{steps: [:assemble | steps]} = release) do
%{release | steps: steps} |> assemble() |> run_steps()
end
@@ -980,6 +1012,36 @@ defmodule Mix.Tasks.Release do
release
end
defp make_tar(release) do
tar_filename = "#{release.name}-#{release.version}.tar.gz"
out_path = Path.join([release.path, "..", "..", tar_filename]) |> Path.expand()
info(release, [:green, "* building ", :reset, out_path])
lib_dirs =
Enum.reduce(release.applications, [], fn {name, app_config}, acc ->
vsn = Keyword.fetch!(app_config, :vsn)
[Path.join("lib", "#{name}-#{vsn}") | acc]
end)
release_files =
for basename <- File.ls!(Path.join(release.path, "releases")),
not File.dir?(Path.join([release.path, "releases", basename])),
do: Path.join("releases", basename)
dirs =
["bin", Path.join("releases", release.version), "erts-#{release.erts_version}"] ++
lib_dirs ++ release_files
files =
dirs
|> Enum.filter(&File.exists?(Path.join(release.path, &1)))
|> Enum.map(&{String.to_charlist(&1), String.to_charlist(Path.join(release.path, &1))})
File.rm(out_path)
:ok = :erl_tar.create(String.to_charlist(out_path), files, [:dereference, :compressed])
release
end
# build_rel
defp build_rel(release, config) do
+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 (
+34
View File
@@ -676,4 +676,38 @@ defmodule Mix.Utils do
[proxy_auth: {user, pass}]
end
def warn_install_over_http_deprecated(task_name, url) do
basename = Path.basename(url)
shell = Mix.shell()
shell.error("""
Warning: the use of HTTP/HTTPS URLs with `mix #{task_name}` is deprecated
Run `mix help #{task_name}` for details on the available alternatives, using
hex, git or github. Alternatively you can fetch the file using an external HTTP
client and then install it locally:
Unix (Linux, MacOS X):
$ wget #{url}
$ mix #{task_name} #{basename}
or
$ curl -o #{basename} #{url}
$ mix #{task_name} #{basename}
Windows (Win7 or later):
> powershell -Command "Invoke-WebRequest #{url} -OutFile #{basename}"
> mix #{task_name} #{basename}
or
> powershell -Command "(New-Object Net.WebClient).DownloadFile('#{url}', '#{basename}')"
> mix #{task_name} #{basename}
""")
end
end
+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)
+80 -1
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))
@@ -80,7 +95,7 @@ defmodule Mix.ReleaseTest do
test "uses chosen release via the default_release" do
release =
from_config!(
:bar,
nil,
config(
default_release: :bar,
releases: [foo: [version: "0.2.0"], bar: [version: "0.3.0"]]
@@ -153,6 +168,14 @@ defmodule Mix.ReleaseTest do
~r"The :steps option must contain the atom :assemble once",
fn -> release(steps: [:assemble, :assemble]) end
assert_raise Mix.Error,
~r"The :tar step must come after :assemble",
fn -> release(steps: [:tar, :assemble]) end
assert_raise Mix.Error,
~r"The :steps option can only contain the atom :tar once",
fn -> release(steps: [:assemble, :tar, :tar]) end
assert_raise Mix.Error,
~r"The :steps option must be",
fn -> release(steps: [:foo]) end
@@ -437,6 +460,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 +600,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
+3 -1
View File
@@ -82,6 +82,8 @@ defmodule Mix.Tasks.ArchiveTest do
# Try to override it with URL
send(self(), {:mix_shell_input, :yes?, false})
Mix.Tasks.Archive.Install.run(["https://example.com/archive-0.1.0?hello.ez"])
assert_received {:mix_shell, :error, ["Warning: the use of HTTP/HTTPS URLs" <> _]}
assert_received {:mix_shell, :yes?, ["Found existing entry: " <> _]}
# Loading the archive should emit warning again
@@ -116,7 +118,7 @@ defmodule Mix.Tasks.ArchiveTest do
end
test "archive install missing file" do
message = ~r[Expected "./unlikely-to-exist-0.1.0.ez" to be a URL or a local file path]
message = ~r[Expected "./unlikely-to-exist-0.1.0.ez" to be a local file path]
assert_raise Mix.Error, message, fn ->
Mix.Tasks.Archive.Install.run(["./unlikely-to-exist-0.1.0.ez"])
+7
View File
@@ -242,6 +242,13 @@ defmodule Mix.Tasks.EscriptTest do
assert_received {:mix_shell, :info, ["* escript_test"]}
refute_received {:mix_shell, :info, ["* escript_test.bat"]}
# Try to override it with URL
send(self(), {:mix_shell_input, :yes?, false})
Mix.Tasks.Escript.Install.run(["https://example.com/escript_test"])
assert_received {:mix_shell, :error, ["Warning: the use of HTTP/HTTPS URLs" <> _]}
assert_received {:mix_shell, :yes?, ["Found existing entry: " <> _]}
# check uninstall confirmation
send(self(), {:mix_shell_input, :yes?, false})
Mix.Tasks.Escript.Uninstall.run(["escript_test"])
+101 -18
View File
@@ -36,6 +36,74 @@ defmodule Mix.Tasks.ReleaseTest do
end)
end
test "tar" do
in_fixture("release_test", fn ->
config = [releases: [demo: [steps: [:assemble, :tar]]]]
Mix.Project.in_project(:release_test, ".", config, fn _ ->
root = Path.absname("_build/#{Mix.env()}/rel/demo")
ignored_app_path = Path.join([root, "lib", "ignored_app-0.1.0", "ebin"])
File.mkdir_p!(ignored_app_path)
File.touch(Path.join(ignored_app_path, "ignored_app.app"))
ignored_release_path = Path.join([root, "releases", "ignored_dir"])
File.mkdir_p!(ignored_release_path)
File.touch(Path.join(ignored_release_path, "ignored"))
Mix.Task.run("release")
tar_path = Path.expand(Path.join([root, "..", "..", "demo-0.1.0.tar.gz"]))
message = "* building #{tar_path}"
assert_received {:mix_shell, :info, [^message]}
assert File.exists?(tar_path)
{:ok, files} = String.to_charlist(tar_path) |> :erl_tar.table([:compressed])
files = Enum.map(files, &to_string/1)
files_with_versions = File.ls!(Path.join(root, "lib"))
assert "bin/demo" in files
assert "releases/0.1.0/sys.config" in files
assert "releases/0.1.0/vm.args" in files
assert "releases/COOKIE" in files
assert "releases/start_erl.data" in files
assert Enum.any?(files, &(&1 =~ "erts"))
assert Enum.any?(files, &(&1 =~ "stdlib"))
for dir <- files_with_versions -- ["ignored_app-0.1.0"] do
[name | _] = String.split(dir, "-")
assert "lib/#{dir}/ebin/#{name}.app" in files
end
refute "lib/ignored_app-0.1.0/ebin/ignored_app.app" in files
refute "releases/ignored_dir/ignored" in files
end)
end)
end
test "tar without ERTS" do
in_fixture("release_test", fn ->
config = [releases: [demo: [include_erts: false, steps: [:assemble, :tar]]]]
Mix.Project.in_project(:release_test, ".", config, fn _ ->
root = Path.absname("_build/#{Mix.env()}/rel/demo")
Mix.Task.run("release")
tar_path = Path.expand(Path.join([root, "..", "..", "demo-0.1.0.tar.gz"]))
message = "* building #{tar_path}"
assert_received {:mix_shell, :info, [^message]}
assert File.exists?(tar_path)
{:ok, files} = String.to_charlist(tar_path) |> :erl_tar.table([:compressed])
files = Enum.map(files, &to_string/1)
assert "bin/demo" in files
refute Enum.any?(files, &(&1 =~ "erts"))
refute Enum.any?(files, &(&1 =~ "stdlib"))
end)
end)
end
test "steps" do
in_fixture("release_test", fn ->
last_step = fn release ->
@@ -140,8 +208,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 +238,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 +268,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 +321,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 +336,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 +347,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 +397,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 +414,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 +480,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...")