Compare commits

...
13 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
19 changed files with 134 additions and 33 deletions
+22
View File
@@ -58,6 +58,28 @@ 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
+1 -1
View File
@@ -1 +1 @@
1.9.1
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 -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
+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)
+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
@@ -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) ->
@@ -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
+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) == []
+11 -4
View File
@@ -520,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
{[], []}
+22 -3
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(),
@@ -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)
@@ -409,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
+4 -2
View File
@@ -386,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`.
+3 -2
View File
@@ -285,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
@@ -378,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 (
+15
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))
+2 -1
View File
@@ -281,10 +281,11 @@ defmodule Mix.Tasks.ReleaseTest do
open_port(script, ['start'])
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