Compare commits

...
23 Commits
Author SHA1 Message Date
José Valim 71c335ac26 Release v1.9.0 2019-06-24 11:20:18 +02:00
Julius Putra Tanu Setiaji 02f5d57871 Fix typespec of Macro.Env.t (#9155) 2019-06-24 07:15:11 +02:00
José Valim 6c16486b4a Clarify the relationship with config/releases.exs, closes #9153 2019-06-21 18:59:33 +02:00
José Valim bfa5d6d23c Do not pass Meta to Erlang AST, closes #9152
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2019-06-21 16:57:49 +02:00
José Valim c30b6d675b Rename :end metadata to less ambiguous :closing
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2019-06-16 00:02:26 +02:00
José Valim b59937b80f Add missing @doc since annotation
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2019-06-14 07:57:47 +02:00
Eksperimental 5b0f17130f Place Version.Requirement module under Basic Types in docs.exs (#9139)
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2019-06-14 07:57:46 +02:00
José Valim 2548965a1e Ensure started/loaded apps do not leak between Mix tests, closes #9137 2019-06-13 13:49:59 +02:00
Fernando Tapia Rico 09c01da205 Fix bad naming on release script for Windows (#9135)
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2019-06-12 19:32:08 +02:00
Andrea Leopardi 9ed78dea24 Improve a comment in env.*.eex for releases (#9134)
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2019-06-12 19:32:06 +02:00
Justin Schneck e5888e7b93 Add RELEASE_BOOT_SCRIPT and RELEASE_BOOT_SCRIPT_CLEAN (#9132)
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2019-06-12 09:35:09 +02:00
Jonatan Männchen 13af842c66 IEx: Sort Types in t helper (#9131) 2019-06-11 23:50:56 +02:00
José Valim a211223810 Keep struct fields ordered in types
Closes https://github.com/elixir-lang/ex_doc/issues/1016
2019-06-11 13:50:07 +02:00
Chris Wögi 8bc3c826b1 Fix redirection to null on windows (#9130)
Concerning generated `bin/release.bat` from `mix release`.
2019-06-11 13:25:54 +02:00
José Valim 7a3d6ec928 Remove timestamps from release, closes #9127 2019-06-11 10:48:30 +02:00
José Valim 9b2e7892ca Do not crash formatter on false positive sigils, closes #9123
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2019-06-10 11:19:09 +02:00
Tristan Sloughter 04794d5dfd --paths= in rebar3 bare compile fixes subcommand splitting on comma bug (#9120)
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2019-06-08 16:00:21 +02:00
José Valim 36c2787fc6 Immediately shutdown the lexical tracker
Otherwise we may have a race condition if the code
is passing __ENV__ to an eval function which may keep
the lexical tracker if it is alive by the time it is
checked.
2019-06-07 12:02:41 +02:00
Justin Schneck 2bafa0b50b Add preferred_cli_target (#9118) 2019-06-07 08:21:43 +02:00
José Valim c953de0036 Enforce atom keys for config 2019-06-04 15:21:39 +02:00
José Valim aad7aa4d22 Release v1.9.0-rc.0 2019-06-04 13:21:07 +02:00
Tobiasz Małecki ebe23614f7 Remove redundant "a" from Inspect.Opts moduledoc (#9114) 2019-06-04 12:00:44 +02:00
José Valim d8d6ab48c8 Prepare v1.9 for release 2019-06-03 17:36:28 +02:00
34 changed files with 262 additions and 157 deletions
+4 -1
View File
@@ -58,7 +58,7 @@ 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.0-dev
## v1.9.0 (2019-06-24)
### 1. Enhancements
@@ -110,6 +110,7 @@ There are many other enhancements. The Elixir CLI got a handful of new options i
* [Mix] Follow XDG base dir specification in Mix for temporary and configuration files
* [Mix.Generator] Add `copy_file/3`, `copy_template/4`, and `overwite?/2`
* [Mix.Project] Add `preferred_cli_target` that works like `preferred_cli_env`
* [mix archive.uninstall] Allow `mix archive.uninstall APP` to uninstall any installed version of APP
* [mix new] No longer generate a `config/` directory for mix new
* [mix release] Add support for releases
@@ -125,6 +126,7 @@ There are many other enhancements. The Elixir CLI got a handful of new options i
#### Elixir
* [Code] Quote `::` in `Code.format_string!/1` to avoid ambiguity
* [Code] Do not crash formatter on false positive sigils
* [Enum] Ensure the first equal entry is returned by `Enum.min/2` and `Enum.max/2`
* [Kernel] Improve error message when string interpolation is used in a guard
* [Kernel] Properly merge and handle docs for callbacks with multiple clauses
@@ -157,6 +159,7 @@ There are many other enhancements. The Elixir CLI got a handful of new options i
* [mix compile] Ensure Erlang-based Mix compilers (erlang, leex, yecc) set valid position on diagnostics
* [mix compile] Ensure compilation halts in an umbrella project if one of the siblings fail to compile
* [mix deps] Raise an error if the umbrella app's dir name and `mix.exs` app name don't match
* [mix deps.compile] Fix subcommand splitting bug in rebar3
* [mix test] Do not consider modules that are no longer cover compiled when computing coverage report, which could lead to flawed reports
### 3. Soft-deprecations (no warnings emitted)
+1 -1
View File
@@ -1,7 +1,7 @@
PREFIX ?= /usr/local
SHARE_PREFIX ?= $(PREFIX)/share
MAN_PREFIX ?= $(SHARE_PREFIX)/man
CANONICAL := master/ # master/ or vMAJOR.MINOR/
CANONICAL := v1.9/ # master/ or vMAJOR.MINOR/
ELIXIRC := bin/elixirc --verbose --ignore-module-conflict $(ELIXIRC_OPTS)
ERLC := erlc -I lib/elixir/include $(ERLC_OPTS)
ERL := erl -I lib/elixir/include -noshell -pa lib/elixir/ebin
+1 -1
View File
@@ -1 +1 @@
1.9.0-dev
1.9.0
+2 -1
View File
@@ -26,7 +26,8 @@
Time,
Tuple,
URI,
Version
Version,
Version.Requirement
],
"Collections & Enumerables": [
Access,
+1
View File
@@ -963,6 +963,7 @@ defmodule Code do
For compiling many files concurrently, see `Kernel.ParallelCompiler.compile/2`.
"""
@doc since: "1.7.0"
@spec compile_file(binary, nil | binary) :: [{module, binary}]
def compile_file(file, relative_to \\ nil) when is_binary(file) do
:elixir_compiler.file(find_file(file, relative_to), fn _, _ -> :ok end)
+20 -20
View File
@@ -1156,7 +1156,7 @@ defmodule Code.Formatter do
{left_doc, _join, state} =
args_to_algebra_with_comments(
left,
Keyword.delete(meta, :end),
Keyword.delete(meta, :closing),
skip_parens?,
:force_comma,
join,
@@ -1365,26 +1365,26 @@ defmodule Code.Formatter do
## Sigils
defp maybe_sigil_to_algebra(fun, meta, args, state) do
case {Atom.to_string(fun), args} do
{<<"sigil_", name>>, [{:<<>>, _, entries}, modifiers]} ->
opening_terminator = Keyword.fetch!(meta, :terminator)
doc = <<?~, name, opening_terminator::binary>>
with <<"sigil_", name>> <- Atom.to_string(fun),
[{:<<>>, _, entries}, modifiers] when is_list(modifiers) <- args,
opening_terminator when not is_nil(opening_terminator) <- Keyword.get(meta, :terminator) do
doc = <<?~, name, opening_terminator::binary>>
if opening_terminator in [@double_heredoc, @single_heredoc] do
closing_terminator = concat(opening_terminator, List.to_string(modifiers))
if opening_terminator in [@double_heredoc, @single_heredoc] do
closing_terminator = concat(opening_terminator, List.to_string(modifiers))
{doc, state} =
entries
|> prepend_heredoc_line()
|> interpolation_to_algebra(:heredoc, state, doc, closing_terminator)
{force_unfit(doc), state}
else
escape = closing_sigil_terminator(opening_terminator)
closing_terminator = concat(escape, List.to_string(modifiers))
interpolation_to_algebra(entries, escape, state, doc, closing_terminator)
end
{doc, state} =
entries
|> prepend_heredoc_line()
|> interpolation_to_algebra(:heredoc, state, doc, closing_terminator)
{force_unfit(doc), state}
else
escape = closing_sigil_terminator(opening_terminator)
closing_terminator = concat(escape, List.to_string(modifiers))
interpolation_to_algebra(entries, escape, state, doc, closing_terminator)
end
else
_ ->
:error
end
@@ -1847,7 +1847,7 @@ defmodule Code.Formatter do
end
defp add_max_line_to_last_clause([{op, meta, args}], max_line) do
[{op, [end: [line: max_line]] ++ meta, args}]
[{op, [closing: [line: max_line]] ++ meta, args}]
end
defp add_max_line_to_last_clause([clause | clauses], max_line) do
@@ -2239,7 +2239,7 @@ defmodule Code.Formatter do
end
defp end_line(meta) do
meta[:end][:line] || @min_line
meta[:closing][:line] || @min_line
end
## Algebra helpers
+31 -17
View File
@@ -4,8 +4,8 @@ defmodule Config do
## Example
This module is most commonly used to define application
configuration, typically in `config/config.exs`:
This module is most commonly used to define application configuration,
typically in `config/config.exs`:
import Config
@@ -18,14 +18,20 @@ defmodule Config do
`import Config` will import the functions `config/2`, `config/3`
and `import_config/1` to help you manage your configuration.
Once Mix starts, it will automatically evaluate the configuration
file and persist it into `:some_app`'s application environment, which
can be accessed in as follows:
`config/2` and `config/3` are used to define key-value configuration
for a given application. Once Mix starts, it will automatically
evaluate the configuration file and persist the configuration above
into `:some_app`'s application environment, which can be accessed in
as follows:
"value1" = Application.fetch_env!(:some_app, :key1)
See `Config.Reader` for evaluating and reading configuration
files.
Finally, the line `import_config "#{Mix.env()}.exs"` will import other
config files, based on the current Mix environment, such as
`config/dev.exs` and `config/test.exs`.
`Config` also provides a low-level API for evaluating and reading
configuration, under the `Config.Reader` module.
**Important:** if you are writing a library to be used by other developers,
it is generally recommended to avoid the application environment, as the
@@ -34,17 +40,16 @@ defmodule Config do
## Migrating from `use Mix.Config`
The `Config` module in Elixir was introduced in v1.9 as a
replacement to `Mix.Config`, which was specific to Mix and
has been deprecated.
The `Config` module in Elixir was introduced in v1.9 as a replacement to
`Mix.Config`, which was specific to Mix and has been deprecated.
You can leverage `Config` instead of `Mix.Config` in two steps.
The first step is to replace `use Mix.Config` at the top of
your config files by `import Config`.
You can leverage `Config` instead of `Mix.Config` in two steps. The first
step is to replace `use Mix.Config` at the top of your config files by
`import Config`.
The second is to make sure your `import_config/1` calls do
not have a wildcard character. If so, you need to perform
the wildcard lookup manually. For example, if you did:
The second is to make sure your `import_config/1` calls do not have a
wildcard character. If so, you need to perform the wildcard lookup
manually. For example, if you did:
import_config "../apps/*/config/config.exs"
@@ -54,6 +59,15 @@ defmodule Config do
import_config config
end
## config/releases.exs
If you are using releases, see `mix release`, there another configuration
file called `config/releases.exs`. While `config/config.exs` and friends
mentioned in the previous section are executed whenever you run a Mix
command, including when you assemble a release, `config/releases.exs` is
execute every time your production system boots. Since Mix is not available
in a production system, `config/releases.exs` must not use any of the
functions from Mix.
"""
@config_key {__MODULE__, :config}
@@ -152,7 +166,7 @@ defmodule Config do
"""
@doc since: "1.9.0"
def config(root_key, key, opts) when is_atom(root_key) do
def config(root_key, key, opts) when is_atom(root_key) and is_atom(key) do
get_config!()
|> __merge__([{root_key, [{key, opts}]}])
|> put_config()
+1 -3
View File
@@ -220,9 +220,7 @@ defmodule Config.Provider do
end
defp write_config!(config, path) do
{date, time} = :erlang.localtime()
args = [date, time, config]
contents = :io_lib.format("%% coding: utf-8~n%% config generated at ~p ~p~n~p.~n", args)
contents = :io_lib.format("%% coding: utf-8~n~p.~n", [config])
case File.write(path, contents) do
:ok ->
+1 -1
View File
@@ -33,7 +33,7 @@ defmodule Inspect.Opts do
* `:printable_limit` - limits the number of characters that are inspected
on printable strings and printable charlists. You can use `String.printable?/1`
and `List.ascii_printable?/1` to check if a a given string or charlist is
and `List.ascii_printable?/1` to check if a given string or charlist is
printable. Defaults to 4096. If you don't want to limit the number of
characters to a particular number, use `:infinity`.
+5 -5
View File
@@ -33,7 +33,7 @@ defmodule Kernel.LexicalTracker do
@doc false
def stop(pid) do
:gen_server.cast(pid, :stop)
:gen_server.call(pid, :stop)
end
@doc false
@@ -146,6 +146,10 @@ defmodule Kernel.LexicalTracker do
{:reply, :maps.get(key, cache), state}
end
def handle_call(:stop, _from, state) do
{:stop, :normal, :ok, state}
end
def handle_cast({:write_cache, key, value}, %{cache: cache} = state) do
{:noreply, %{state | cache: :maps.put(key, value, cache)}}
end
@@ -206,10 +210,6 @@ defmodule Kernel.LexicalTracker do
{:noreply, %{state | directives: add_directive(state.directives, module, line, warn, :alias)}}
end
def handle_cast(:stop, state) do
{:stop, :normal, state}
end
@doc false
def handle_info(_msg, state) do
{:noreply, state}
+1 -1
View File
@@ -557,7 +557,7 @@ defmodule Kernel.Typespec do
types =
:lists.map(
fn {field, _} -> {field, Keyword.get(fields, field, quote(do: term()))} end,
struct
:lists.sort(struct)
)
fun = fn {field, _} ->
+1 -1
View File
@@ -85,7 +85,7 @@ defmodule Macro.Env do
aliases: aliases,
functions: functions,
macros: macros,
macro_aliases: aliases,
macro_aliases: macro_aliases,
context_modules: context_modules,
vars: vars,
unused_vars: unused_vars,
@@ -158,4 +158,4 @@ Version | Deprecated feature | Replaced by (ava
[v1.6]: https://github.com/elixir-lang/elixir/blob/v1.6/CHANGELOG.md#4-deprecations
[v1.7]: https://github.com/elixir-lang/elixir/blob/v1.7/CHANGELOG.md#4-hard-deprecations
[v1.8]: https://github.com/elixir-lang/elixir/blob/v1.8/CHANGELOG.md#4-hard-deprecations
[v1.9]: https://github.com/elixir-lang/elixir/blob/master/CHANGELOG.md#4-hard-deprecations
[v1.9]: https://github.com/elixir-lang/elixir/blob/v1.9/CHANGELOG.md#4-hard-deprecations
+2 -1
View File
@@ -382,10 +382,11 @@ translate_with_else(Meta, [], S) ->
Var = {var, Ann, VarName},
{{clause, Ann, [Var], [], [Var]}, SC};
translate_with_else(Meta, [{else, [{'->', _, [[{Var, VarMeta, Kind}], Clause]}]}], S) when is_atom(Var), is_atom(Kind) ->
Ann = ?ann(?generated(Meta)),
Ctx = elixir_utils:var_context(VarMeta, Kind),
{ElseVarErl, SV} = elixir_erl_var:assign(Meta, Var, Ctx, S),
{TranslatedClause, SC} = elixir_erl_pass:translate(Clause, SV),
{{clause, Meta, [ElseVarErl], [], [TranslatedClause]}, SC};
{{clause, Ann, [ElseVarErl], [], [TranslatedClause]}, SC};
translate_with_else(Meta, [{else, Else}], S) ->
Generated = ?generated(Meta),
ElseVarEx = {else, Generated, ?var_context},
+8 -8
View File
@@ -653,12 +653,12 @@ line_from_location({Line, _Column, _}) -> Line.
is_eol({_, _, Eol}) -> is_integer(Eol) and (Eol > 0).
end_meta(Token) ->
[{format, block}, {'end', meta_from_location(?location(Token))}].
[{format, block}, {closing, meta_from_location(?location(Token))}].
meta_from_token_with_end(Begin, End) ->
meta_from_token_with_closing(Begin, End) ->
case ?formatter_metadata() of
true ->
[{'end', meta_from_location(?location(End))} | meta_from_token(Begin)];
[{closing, meta_from_location(?location(End))} | meta_from_token(Begin)];
false ->
meta_from_token(Begin)
end.
@@ -740,7 +740,7 @@ eol_pair(Left, Right) ->
true ->
[
{eol, is_eol(?location(Left)) and is_eol(?location(Right))},
{'end', meta_from_location(?location(Right))}
{closing, meta_from_location(?location(Right))}
];
false ->
[]
@@ -828,7 +828,7 @@ build_identifier({_, Location, Identifier}, Args) ->
build_fn(Fn, Stab, End) ->
case check_stab(Stab, none) of
stab ->
Meta = eol_op(?location(Fn)) ++ meta_from_token_with_end(Fn, End),
Meta = eol_op(?location(Fn)) ++ meta_from_token_with_closing(Fn, End),
{fn, Meta, collect_stab(Stab, [], [])};
block ->
return_error(meta_from_token(Fn), "expected anonymous functions to be defined with -> inside: ", "'fn'")
@@ -902,7 +902,7 @@ charlist_part({Begin, End, Tokens}) ->
Form = string_tokens_parse(Tokens),
Meta =
case ?formatter_metadata() of
true -> [{'end', meta_from_location(End)} | meta_from_location(Begin)];
true -> [{closing, meta_from_location(End)} | meta_from_location(Begin)];
false -> meta_from_location(Begin)
end,
{{'.', Meta, ['Elixir.Kernel', to_string]}, Meta, [Form]}.
@@ -915,7 +915,7 @@ string_part({Begin, End, Tokens}) ->
Form = string_tokens_parse(Tokens),
Meta =
case ?formatter_metadata() of
true -> [{'end', meta_from_location(End)} | meta_from_location(Begin)];
true -> [{closing, meta_from_location(End)} | meta_from_location(Begin)];
false -> meta_from_location(Begin)
end,
{'::', Meta, [{{'.', Meta, ['Elixir.Kernel', to_string]}, Meta, [Form]}, {binary, Meta, nil}]}.
@@ -943,7 +943,7 @@ build_stab(Stab) ->
build_stab(Before, Stab, After) ->
case build_stab(Stab) of
{'__block__', Meta, Block} ->
{'__block__', Meta ++ meta_from_token_with_end(Before, After), Block};
{'__block__', Meta ++ meta_from_token_with_closing(Before, After), Block};
Other ->
Other
end.
@@ -461,6 +461,14 @@ defmodule Code.Formatter.IntegrationTest do
"""
end
test "false positive sigil" do
assert_same """
def sigil_d(<<year::2-bytes, "-", month::2-bytes, "-", day::2-bytes>>, calendar) do
ymd(year, month, day, calendar)
end
"""
end
test "capture with operators" do
assert_same """
"this works" |> (&String.upcase/1) |> (&String.downcase/1)
+11 -9
View File
@@ -252,12 +252,14 @@ defmodule CodeTest do
assert string_to_quoted.(":one") == {:__block__, [line: 1], [:one]}
args = [[{:__block__, [original: '1', line: 1], [1]}]]
assert string_to_quoted.("[1]") == {:__block__, [eol: false, end: [line: 1], line: 1], args}
assert string_to_quoted.("[1]") ==
{:__block__, [eol: false, closing: [line: 1], line: 1], args}
args = [{{:__block__, [line: 1], [:ok]}, {:__block__, [line: 1], [:test]}}]
assert string_to_quoted.("{:ok, :test}") ==
{:__block__, [eol: false, end: [line: 1], line: 1], args}
{:__block__, [eol: false, closing: [line: 1], line: 1], args}
assert string_to_quoted.(~s("""\nhello\n""")) ==
{:__block__, [format: :bin_heredoc, line: 1], ["hello\n"]}
@@ -265,12 +267,12 @@ defmodule CodeTest do
assert string_to_quoted.("'''\nhello\n'''") ==
{:__block__, [format: :list_heredoc, line: 1], ['hello\n']}
left = {:__block__, [original: '1', line: 1, end: [line: 1], line: 1], [1]}
left = {:__block__, [original: '1', line: 1, closing: [line: 1], line: 1], [1]}
right = {:__block__, [format: :string, line: 1], ["hello"]}
args = [{:->, [line: 1], [[left], right]}]
assert string_to_quoted.(~s[fn (1) -> "hello" end]) ==
{:fn, [end: [line: 1], line: 1], args}
{:fn, [closing: [line: 1], line: 1], args}
end
test "adds newlines information to blocks when :formatter_metadata (private) is given" do
@@ -285,11 +287,11 @@ defmodule CodeTest do
"""
args = [
{:one, [eol: false, end: [line: 1], line: 1], []},
{:two, [newlines: 0, eol: false, end: [line: 1], line: 1], []},
{:three, [newlines: 1, eol: false, end: [line: 2], line: 2], []},
{:four, [newlines: 2, eol: false, end: [line: 4], line: 4], []},
{:five, [newlines: 3, eol: false, end: [line: 7], line: 7], []}
{:one, [eol: false, closing: [line: 1], line: 1], []},
{:two, [newlines: 0, eol: false, closing: [line: 1], line: 1], []},
{:three, [newlines: 1, eol: false, closing: [line: 2], line: 2], []},
{:four, [newlines: 2, eol: false, closing: [line: 4], line: 4], []},
{:five, [newlines: 3, eol: false, closing: [line: 7], line: 7], []}
]
assert Code.string_to_quoted!(file, formatter_metadata: true) == {:__block__, [], args}
+17
View File
@@ -480,6 +480,23 @@ defmodule TypespecTest do
assert {:type, _, :map_field_exact, [{:atom, _, :other}, {:type, _, :term, []}]} = arg2
end
@fields Enum.map(10..42, &{:"f#{&1}", :ok})
test "@type with a large struct" do
bytecode =
test_module do
defstruct unquote(@fields)
@type my_type :: %TypespecSample{unquote_splicing(@fields)}
end
assert [type: {:my_type, type, []}] = types(bytecode)
assert {:type, _, :map, [struct, arg1, arg2 | _]} = type
assert {:type, _, :map_field_exact, struct_args} = struct
assert [{:atom, _, :__struct__}, {:atom, _, TypespecSample}] = struct_args
assert {:type, _, :map_field_exact, [{:atom, _, :f10}, {:atom, _, :ok}]} = arg1
assert {:type, _, :map_field_exact, [{:atom, _, :f11}, {:atom, _, :ok}]} = arg2
end
test "@type with struct does not @enforce_keys" do
bytecode =
test_module do
+29 -18
View File
@@ -644,7 +644,9 @@ defmodule IEx.Introspection do
types_not_found(inspect(module))
{:ok, types} ->
Enum.each(types, &(&1 |> format_type() |> IO.puts()))
types
|> Enum.sort_by(fn {_, {name, _, args}} -> {name, length(args)} end)
|> Enum.each(&(&1 |> format_type() |> IO.puts()))
end
dont_display_result()
@@ -656,15 +658,19 @@ defmodule IEx.Introspection do
no_beam(module)
{:ok, types} ->
printed =
for {kind, {^type, _, args}} = typespec <- types,
kind != :typep do
type_doc(module, type, length(args), typespec)
|> print_typespec()
end
types
|> Enum.filter(&match?({kind, {^type, _, _}} when kind in [:type, :opaque], &1))
|> Enum.sort_by(fn {_, {name, _, args}} -> {name, length(args)} end)
|> case do
[] ->
types_not_found_or_private("#{inspect(module)}.#{type}")
if printed == [] do
types_not_found_or_private("#{inspect(module)}.#{type}")
types ->
Enum.map(types, fn {_, {_, _, args}} = typespec ->
module
|> type_doc(type, length(args), typespec)
|> print_typespec()
end)
end
end
@@ -677,16 +683,21 @@ defmodule IEx.Introspection do
no_beam(module)
{:ok, types} ->
printed =
for {kind, {^type, _, args}} = typespec <- types,
kind != :typep,
length(args) == arity do
type_doc(module, type, arity, typespec)
|> print_typespec()
end
types
|> Enum.find(
&match?(
{kind, {^type, _, args}} when kind in [:type, :opaque] and length(args) == arity,
&1
)
)
|> case do
nil ->
types_not_found_or_private("#{inspect(module)}.#{type}")
if printed == [] do
types_not_found_or_private("#{inspect(module)}.#{type}")
typespec ->
module
|> type_doc(type, arity, typespec)
|> print_typespec()
end
end
+14
View File
@@ -824,6 +824,10 @@ defmodule IEx.HelpersTest do
end) >= 2
end
test "prints private types" do
assert capture_io(fn -> t(Date.Range) end) =~ "@typep iso_days"
end
test "prints type information" do
assert "@type t() ::" <> _ = capture_io(fn -> t(Enum.t()) end)
assert capture_io(fn -> t(Enum.t()) end) == capture_io(fn -> t(Enum.t() / 0) end)
@@ -831,6 +835,16 @@ defmodule IEx.HelpersTest do
assert capture_io(fn -> t(URI.t()) end) == capture_io(fn -> t(URI.t() / 0) end)
end
test "sorts types alphabetically" do
unsorted =
capture_io(fn -> t(Enum) end)
|> String.split("\n")
|> Enum.reject(&(&1 == ""))
|> Enum.map(&String.replace(&1, ~r/@(type|opaque) /, ""))
assert unsorted == Enum.sort(unsorted)
end
test "prints type documentation" do
content = """
defmodule TypeSample do
+24 -11
View File
@@ -29,7 +29,7 @@ defmodule Mix.CLI do
load_mix_exs()
{task, args} = get_task(args)
ensure_hex(task)
change_env(task)
maybe_change_env_and_target(task)
run_task(task, args)
end
@@ -104,26 +104,39 @@ defmodule Mix.CLI do
end
end
defp change_env(task) do
if env = preferred_cli_env(task) do
Mix.env(env)
defp maybe_change_env_and_target(task) do
task = String.to_atom(task)
config = Mix.Project.config()
if project = Mix.Project.pop() do
%{name: name, file: file} = project
Mix.Project.push(name, file)
end
env = preferred_cli_env(task, config)
target = preferred_cli_target(task, config)
env && Mix.env(env)
target && Mix.target(target)
if env || target do
reload_project()
end
end
defp preferred_cli_env(task) do
defp reload_project() do
if project = Mix.Project.pop() do
%{name: name, file: file} = project
Mix.Project.push(name, file)
end
end
defp preferred_cli_env(task, config) do
if System.get_env("MIX_ENV") do
nil
else
task = String.to_atom(task)
Mix.Project.config()[:preferred_cli_env][task] || Mix.Task.preferred_cli_env(task)
config[:preferred_cli_env][task] || Mix.Task.preferred_cli_env(task)
end
end
defp preferred_cli_target(task, config) do
config[:preferred_cli_target][task]
end
defp load_dot_config do
path = Path.join(Mix.Utils.mix_config(), "config.exs")
+5 -1
View File
@@ -67,9 +67,13 @@ defmodule Mix.Project do
* `:preferred_cli_env` - a keyword list of `{task, env}` tuples where `task`
is the task name as an atom (for example, `:"deps.get"`) and `env` is the
preferred environment (for example, `:test`). This option overrides what
specified by the tasks with the `@preferred_cli_env` attribute (see the
is specified by the tasks with the `@preferred_cli_env` attribute (see the
docs for `Mix.Task`). Defaults to `[]`.
* `:preferred_cli_target` - a keyword list of `{task, target}` tuples where
`task` is the task name as an atom (for example, `:test`) and `target`
is the preferred target (for example, `:host`). Defaults to `[]`.
For more options, keep an eye on the documentation for single Mix tasks; good
examples are the `Mix.Tasks.Compile` task and all the specific compiler tasks
(such as `Mix.Tasks.Compile.Elixir` or `Mix.Tasks.Compile.Erlang`).
+6 -9
View File
@@ -318,9 +318,8 @@ defmodule Mix.Release do
{sys_config, runtime?} = merge_provider_config(release, sys_config, config_provider_path)
path = Path.join(release.version_path, "sys.config")
{date, time} = :erlang.localtime()
args = [runtime?, date, time, sys_config]
format = "%% coding: utf-8~n%% RUNTIME_CONFIG=~s~n%% config generated at ~p ~p~n~p.~n"
args = [runtime?, sys_config]
format = "%% coding: utf-8~n%% RUNTIME_CONFIG=~s~n~p.~n"
File.mkdir_p!(Path.dirname(path))
File.write!(path, :io_lib.format(format, args))
@@ -407,7 +406,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", rel_spec))
File.write!(path <> ".rel", consultable(rel_spec))
sys_path = String.to_charlist(path)
@@ -430,7 +429,7 @@ defmodule Mix.Release do
|> prepend_paths_to_script(prepend_paths)
script = {:script, rel_info, instructions}
File.write!(script_path, consultable("script", script))
File.write!(script_path, consultable(script))
:ok = :systools.script2boot(sys_path)
{:error, module, info} ->
@@ -546,10 +545,8 @@ defmodule Mix.Release do
end)
end
defp consultable(kind, term) do
{date, time} = :erlang.localtime()
args = [kind, date, time, term]
:io_lib.format("%% coding: utf-8~n%% ~ts generated at ~p ~p~n~p.~n", args)
defp consultable(term) do
:io_lib.format("%% coding: utf-8~n~p.~n", [term])
end
@doc """
+1 -1
View File
@@ -183,7 +183,7 @@ defmodule Mix.Tasks.Deps.Compile do
lib_path = Path.join(config[:env_path], "lib/*/ebin")
env = [{"REBAR_CONFIG", config_path}, {"TERM", "dumb"}]
cmd = "#{rebar_cmd(dep)} bare compile --paths #{inspect(lib_path)}"
cmd = "#{rebar_cmd(dep)} bare compile --paths=#{inspect(lib_path)}"
File.mkdir_p!(dep_path)
File.write!(config_path, rebar_config(dep))
+12
View File
@@ -667,6 +667,18 @@ defmodule Mix.Tasks.Release do
not running on Erlang/OTP 22 or later, you must set `RELEASE_NODE`
to `RELEASE_NAME@127.0.0.1` with an IP or a known host
* `RELEASE_BOOT_SCRIPT` - the name of the boot script to use when starting
the release. This script is used when running commands such as `start` and
`daemon`. The boot script is expected to be located at the
path `releases/RELEASE_VSN/RELEASE_BOOT_SCRIPT.boot`. Defaults to `start`
* `RELEASE_BOOT_SCRIPT_CLEAN` - the name of the boot script used when
starting the release clean, without your application or its dependencies.
This script is used by commands such as `eval`, `rpc`, and `remote`.
The boot script is expected to be located at the path
`releases/RELEASE_VSN/RELEASE_BOOT_SCRIPT_CLEAN.boot`. Defaults
to `start_clean`
## Umbrellas
Releases are well integrated with umbrella projects, allowing you to
+21 -13
View File
@@ -66,7 +66,9 @@ defmodule Mix.Tasks.Release.Init do
# export ELIXIR_ERL_OPTIONS="-heart"
# fi
# Set the release to work across nodes
# 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
# RELEASE_DISTRIBUTION variable below.
# export RELEASE_DISTRIBUTION=name
# export RELEASE_NODE=<%= @release.name %>@127.0.0.1
"""
@@ -94,6 +96,8 @@ defmodule Mix.Tasks.Release.Init do
export RELEASE_TMP="${RELEASE_TMP:-"$RELEASE_ROOT/tmp"}"
export RELEASE_VM_ARGS="${RELEASE_VM_ARGS:-"$REL_VSN_DIR/vm.args"}"
export RELEASE_DISTRIBUTION="${RELEASE_DISTRIBUTION:-"sname"}"
export RELEASE_BOOT_SCRIPT="${RELEASE_BOOT_SCRIPT:-"start"}"
export RELEASE_BOOT_SCRIPT_CLEAN="${RELEASE_BOOT_SCRIPT_CLEAN:-"start_clean"}"
rand () {
od -t xS -N 2 -A n /dev/urandom | tr -d " \n"
@@ -103,7 +107,7 @@ defmodule Mix.Tasks.Release.Init do
exec "$REL_VSN_DIR/elixir" \
--hidden --cookie "$RELEASE_COOKIE" \
--$RELEASE_DISTRIBUTION "rpc-$(rand)-$RELEASE_NODE" \
--boot "$REL_VSN_DIR/start_clean" \
--boot "$REL_VSN_DIR/$RELEASE_BOOT_SCRIPT_CLEAN" \
--boot-var RELEASE_LIB "$RELEASE_ROOT/lib" \
--rpc-eval "$RELEASE_NODE" "$1"
}
@@ -117,7 +121,7 @@ defmodule Mix.Tasks.Release.Init do
--$RELEASE_DISTRIBUTION "$RELEASE_NODE" \
--erl "-mode $RELEASE_MODE" \
--erl-config "$RELEASE_SYS_CONFIG" \
--boot "$REL_VSN_DIR/start" \
--boot "$REL_VSN_DIR/$RELEASE_BOOT_SCRIPT" \
--boot-var RELEASE_LIB "$RELEASE_ROOT/lib" \
--vm-args "$RELEASE_VM_ARGS" "$@"
}
@@ -164,7 +168,7 @@ defmodule Mix.Tasks.Release.Init do
exec "$REL_VSN_DIR/elixir" \
--cookie "$RELEASE_COOKIE" \
--erl-config "$RELEASE_SYS_CONFIG" \
--boot "$REL_VSN_DIR/start_clean" \
--boot "$REL_VSN_DIR/$RELEASE_BOOT_SCRIPT_CLEAN" \
--boot-var RELEASE_LIB "$RELEASE_ROOT/lib" \
--vm-args "$RELEASE_VM_ARGS" --eval "$2"
;;
@@ -173,7 +177,7 @@ defmodule Mix.Tasks.Release.Init do
exec "$REL_VSN_DIR/iex" \
--werl --hidden --cookie "$RELEASE_COOKIE" \
--$RELEASE_DISTRIBUTION "rem-$(rand)-$RELEASE_NODE" \
--boot "$REL_VSN_DIR/start_clean" \
--boot "$REL_VSN_DIR/$RELEASE_BOOT_SCRIPT_CLEAN" \
--boot-var RELEASE_LIB "$RELEASE_ROOT/lib" \
--remsh "$RELEASE_NODE"
;;
@@ -228,7 +232,9 @@ defmodule Mix.Tasks.Release.Init do
def env_bat_text,
do: ~S"""
@echo off
rem Set the release to work across nodes
rem Set the release to work across nodes. If using the long name format like
rem the one below (my_app@127.0.0.1), you need to also uncomment the
rem RELEASE_DISTRIBUTION variable below.
rem set RELEASE_DISTRIBUTION=name
rem set RELEASE_NODE=<%= @release.name %>@127.0.0.1
"""
@@ -256,6 +262,8 @@ defmodule Mix.Tasks.Release.Init do
if not defined RELEASE_TMP (set RELEASE_TMP=!RELEASE_ROOT!\tmp)
if not defined RELEASE_VM_ARGS (set RELEASE_VM_ARGS=!REL_VSN_DIR!\vm.args)
if not defined RELEASE_DISTRIBUTION (set RELEASE_DISTRIBUTION=sname)
if not defined RELEASE_BOOT_SCRIPT (set RELEASE_BOOT_SCRIPT=start)
if not defined RELEASE_BOOT_SCRIPT_CLEAN (set RELEASE_BOOT_SCRIPT_CLEAN=start_clean)
set RELEASE_SYS_CONFIG=!REL_VSN_DIR!\sys
if "%~1" == "start" (set "REL_EXEC=elixir" && set "REL_EXTRA=--no-halt" && set "REL_GOTO=start")
@@ -270,11 +278,11 @@ defmodule Mix.Tasks.Release.Init do
)
if not "!REL_GOTO!" == "" (
findstr "RUNTIME_CONFIG=true" "!RELEASE_SYS_CONFIG!.config" >nil 2>&1 && (
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!" >nil
copy /y "!REL_VSN_DIR!\sys.config" "!RELEASE_SYS_CONFIG!.config" >nil || (
mkdir "!RELEASE_TMP!" >nul
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
)
@@ -321,7 +329,7 @@ defmodule Mix.Tasks.Release.Init do
--!RELEASE_DISTRIBUTION! "!RELEASE_NODE!" ^
--erl "-mode !RELEASE_MODE!" ^
--erl-config "!RELEASE_SYS_CONFIG!" ^
--boot "!REL_VSN_DIR!\start" ^
--boot "!REL_VSN_DIR!\!RELEASE_BOOT_SCRIPT!" ^
--boot-var RELEASE_LIB "!RELEASE_ROOT!\lib" ^
--vm-args "!RELEASE_VM_ARGS!"
goto end
@@ -331,7 +339,7 @@ defmodule Mix.Tasks.Release.Init do
--eval "%~2" ^
--cookie "!RELEASE_COOKIE!" ^
--erl-config "!RELEASE_SYS_CONFIG!" ^
--boot "!REL_VSN_DIR!\start_clean" ^
--boot "!REL_VSN_DIR!\!RELEASE_BOOT_SCRIPT_CLEAN!" ^
--boot-var RELEASE_LIB "!RELEASE_ROOT!\lib" ^
--vm-args "!RELEASE_VM_ARGS!"
goto end
@@ -340,7 +348,7 @@ defmodule Mix.Tasks.Release.Init do
"!REL_VSN_DIR!\iex.bat" ^
--werl --hidden --cookie "!RELEASE_COOKIE!" ^
--!RELEASE_DISTRIBUTION! "rem-!RANDOM!-!RELEASE_NODE!" ^
--boot "!REL_VSN_DIR!\start_clean" ^
--boot "!REL_VSN_DIR!\!RELEASE_BOOT_SCRIPT_CLEAN!" ^
--boot-var RELEASE_LIB "!RELEASE_ROOT!\lib" ^
--remsh "!RELEASE_NODE!"
goto end
@@ -349,7 +357,7 @@ defmodule Mix.Tasks.Release.Init do
"!REL_VSN_DIR!\elixir.bat" ^
--hidden --cookie "!RELEASE_COOKIE!" ^
--!RELEASE_DISTRIBUTION! "rpc-!RANDOM!-!RELEASE_NODE!" ^
--boot "!REL_VSN_DIR!\start_clean" ^
--boot "!REL_VSN_DIR!\!RELEASE_BOOT_SCRIPT_CLEAN!" ^
--boot-var RELEASE_LIB "!RELEASE_ROOT!\lib" ^
--rpc-eval "!RELEASE_NODE!" "!REL_RPC!"
goto end
+26
View File
@@ -143,6 +143,32 @@ defmodule Mix.CLITest do
System.delete_env("MIX_EXS")
end
test "target config defaults to the user's preferred cli target", context do
in_tmp(context.test, fn ->
File.write!("custom.exs", """
defmodule P do
use Mix.Project
def project, do: [app: :p, version: "0.1.0", preferred_cli_target: [test_task: :other]]
end
defmodule Mix.Tasks.TestTask do
use Mix.Task
def run(args) do
IO.inspect {Mix.target, args}
end
end
""")
System.put_env("MIX_EXS", "custom.exs")
output = mix(["test_task", "a", "b", "c"])
assert output =~ ~s({:other, ["a", "b", "c"]})
end)
after
System.delete_env("MIX_EXS")
end
test "new with tests and cover" do
in_tmp("new_with_tests", fn ->
output = mix(~w[new .])
-10
View File
@@ -19,7 +19,6 @@ defmodule Mix.Tasks.App.StartTest do
end
end
@tag apps: [:app_start_sample]
test "compiles and starts the project" do
Mix.Project.push(AppStartSample)
@@ -50,7 +49,6 @@ defmodule Mix.Tasks.App.StartTest do
end)
end
@tag apps: [:app_start_sample, :app_loaded_sample]
test "start checks for invalid configuration", context do
Mix.Project.push(AppStartSample)
@@ -77,7 +75,6 @@ defmodule Mix.Tasks.App.StartTest do
end)
end
@tag apps: [:error]
test "validates Elixir version requirement", context do
Mix.ProjectStack.post_config(elixir: "~> ~> 0.8.1")
Mix.Project.push(WrongElixirProject)
@@ -89,7 +86,6 @@ defmodule Mix.Tasks.App.StartTest do
end)
end
@tag apps: [:error]
test "validates the Elixir version with requirement", context do
Mix.Project.push(WrongElixirProject)
@@ -100,7 +96,6 @@ defmodule Mix.Tasks.App.StartTest do
end)
end
@tag apps: [:error]
test "does not validate the Elixir version with requirement when disabled", context do
Mix.Project.push(WrongElixirProject)
@@ -136,7 +131,6 @@ defmodule Mix.Tasks.App.StartTest do
def start(_type, return), do: return
end
@tag apps: [:return_sample]
test "start points to report on error", context do
Mix.Project.push(ReturnSample)
@@ -155,7 +149,6 @@ defmodule Mix.Tasks.App.StartTest do
end)
end
@tag apps: [:return_sample]
test "start points to report on exception error", context do
Mix.Project.push(ReturnSample)
@@ -177,7 +170,6 @@ defmodule Mix.Tasks.App.StartTest do
end)
end
@tag apps: [:return_sample]
test "start points to report on bad return", context do
Mix.Project.push(ReturnSample)
@@ -212,7 +204,6 @@ defmodule Mix.Tasks.App.StartTest do
def start(_type, reason), do: exit(reason)
end
@tag apps: [:exit_sample]
test "start points to report on exit", context do
Mix.Project.push(ExitSample)
@@ -230,7 +221,6 @@ defmodule Mix.Tasks.App.StartTest do
end)
end
@tag apps: [:exit_sample]
test "start points to report on normal exit", context do
Mix.Project.push(ExitSample)
@@ -4,10 +4,8 @@ defmodule Mix.Tasks.Profile.CprofTest do
use MixTest.Case
import ExUnit.CaptureIO
alias Mix.Tasks.Profile.Cprof
@moduletag apps: [:sample]
@expr "Enum.each(1..5, &String.Chars.Integer.to_string/1)"
test "profiles evaluated expression", context do
@@ -4,10 +4,8 @@ defmodule Mix.Tasks.Profile.EprofTest do
use MixTest.Case
import ExUnit.CaptureIO
alias Mix.Tasks.Profile.Eprof
@moduletag apps: [:sample]
@expr "Enum.each(1..5, &String.Chars.Integer.to_string/1)"
test "profiles evaluated expression", context do
@@ -4,9 +4,7 @@ defmodule Mix.Tasks.Profile.FprofTest do
use MixTest.Case
import ExUnit.CaptureIO
alias Mix.Tasks.Profile.Fprof
@moduletag apps: [:sample]
test "profiles evaluated expression", context do
in_tmp(context.test, fn ->
-2
View File
@@ -5,8 +5,6 @@ defmodule Mix.Tasks.RunTest do
import ExUnit.CaptureIO
@moduletag apps: [:sample]
setup do
Mix.Project.push(MixTest.Case.Sample)
end
-2
View File
@@ -3,8 +3,6 @@ Code.require_file("../test_helper.exs", __DIR__)
defmodule Mix.UmbrellaTest do
use MixTest.Case
@moduletag apps: [:foo, :bar]
test "apps_paths" do
in_fixture("umbrella_dep/deps/umbrella", fn ->
assert Mix.Project.apps_paths() == nil
+8 -13
View File
@@ -2,6 +2,9 @@ Mix.start()
Mix.shell(Mix.Shell.Process)
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)
@@ -32,12 +35,9 @@ defmodule MixTest.Case do
end
end
setup config do
if apps = config[:apps] do
Logger.remove_backend(:console)
Application.put_env(:logger, :backends, [])
end
@apps Enum.map(Application.loaded_applications(), &elem(&1, 0))
setup do
on_exit(fn ->
Application.start(:logger)
Mix.env(:dev)
@@ -48,14 +48,9 @@ defmodule MixTest.Case do
Mix.ProjectStack.clear_stack()
delete_tmp_paths()
if apps do
for app <- apps do
Application.stop(app)
Application.unload(app)
end
Logger.add_backend(:console, flush: true)
Application.put_env(:logger, :backends, [:console])
for {app, _, _} <- Application.loaded_applications(), app not in @apps do
Application.stop(app)
Application.unload(app)
end
end)