Compare commits
37
Commits
v1.12.0-rc.1
...
v1.0.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
89460154bb | ||
|
|
b026e8877a | ||
|
|
597381b2e0 | ||
|
|
df70729b14 | ||
|
|
61b116c1a5 | ||
|
|
d37a10bbd0 | ||
|
|
5d75c5c861 | ||
|
|
db4089cc71 | ||
|
|
f9f4f14edf | ||
|
|
58946ce879 | ||
|
|
de1d5ff793 | ||
|
|
a4be8fb260 | ||
|
|
031a27f5fa | ||
|
|
f3c1931cac | ||
|
|
2e48d81c20 | ||
|
|
e7ef3fc95d | ||
|
|
2d01aa1c6a | ||
|
|
ae07124550 | ||
|
|
1f744f4dac | ||
|
|
ad88ac642f | ||
|
|
f7956e9d80 | ||
|
|
da6892aa96 | ||
|
|
7a3bd20804 | ||
|
|
f7015166dc | ||
|
|
c6f346a355 | ||
|
|
911a3ba97f | ||
|
|
a1668a6b28 | ||
|
|
a4cb74a83f | ||
|
|
bbf9552186 | ||
|
|
e621b5828e | ||
|
|
68e282c50a | ||
|
|
403a50fbcd | ||
|
|
8e98cd4f41 | ||
|
|
499c385000 | ||
|
|
aecafc099d | ||
|
|
785c7d700a | ||
|
|
cecc8a0f95 |
@@ -1,5 +1,25 @@
|
||||
# Changelog
|
||||
|
||||
## v1.0.1 (2014-10-07)
|
||||
|
||||
* Enhancements
|
||||
* [Mix] Add MIX_ENV output to archive.build (as on escript.build)
|
||||
|
||||
* Bug fixes
|
||||
* [CLI] Fix Elixir CLI when running on Cygwin
|
||||
* [ExUnit] Include stacktraces in ExUnit timeouts
|
||||
* [Mix] Load dependencies before `deps.check` compiles dependencies
|
||||
* [Mix] Ensure `Mix.Config` is deep merged
|
||||
* [Mix] Change version requirement in new apps to `~> MAJOR.MINOR`
|
||||
* [Mix] Report correct location if `local.hex` fails and give higher priority to powershell install on Windows
|
||||
* [Mix] Ensure `--elixirc-paths` option for `compile.elixir` is a subset of the project `:elixirc_paths` configuration and it does not remove skipped entries from the manifest
|
||||
* [Mix] Ensure `compile.elixir` writes to manifest when files are removed
|
||||
* [Mix] Ensure `compile.elixir` purges and deletes modules before compiling
|
||||
* [Mix] Do not crash on malformed proxy env var
|
||||
* [Stream] Ensure `chunk/4` works correctly when halted
|
||||
* [System] Do not allow relative paths in `System.cmd/3` as documented
|
||||
* [System] Fix `:stderr_to_stdout` option in `System.cmd/3`
|
||||
|
||||
## v1.0.0 (2014-09-10)
|
||||
|
||||
* Enhancements
|
||||
|
||||
@@ -146,8 +146,8 @@ release_zip: compile
|
||||
|
||||
release_docs: docs
|
||||
cd ../docs
|
||||
rm -rf ../docs/master
|
||||
mv docs ../docs/master
|
||||
rm -rf ../docs/stable
|
||||
mv docs ../docs/stable
|
||||
|
||||
#==> Tests tasks
|
||||
|
||||
|
||||
+23
-11
@@ -1,28 +1,40 @@
|
||||
# Release process
|
||||
|
||||
## All releases
|
||||
|
||||
This document simply outlines the release process:
|
||||
|
||||
1. Remove all `-dev` extension from versions (see below for all files)
|
||||
1. Ensure you are running on the oldest supported Erlang version
|
||||
|
||||
2. Ensure CHANGELOG is updated and add current date
|
||||
2. Remove all `-dev` extension from versions (see below for all files)
|
||||
|
||||
3. Commit changes above with title "Release vVERSION" and generate new tag
|
||||
3. Ensure CHANGELOG is updated and add current date
|
||||
|
||||
4. Run `make clean test` to ensure all tests pass from scratch and the CI is green
|
||||
4. Commit changes above with title "Release vVERSION" and generate new tag
|
||||
|
||||
5. Push master and the new tag
|
||||
5. Run `make clean test` to ensure all tests pass from scratch and the CI is green
|
||||
|
||||
6. Release new docs with `make release_docs`, move docs to `docs/stable`
|
||||
6. Ensure minimum supported Hex works with new release (instructions upcoming)
|
||||
|
||||
7. Release new zip with `make release_zip`, push new zip to GitHub Releases, name it `Precompiled.zip`
|
||||
7. Push master and the new tag
|
||||
|
||||
8. Fast-forward merge master into stable branch with `git merge master --ff` and push it
|
||||
8. Release new docs with `make release_docs`, move docs to `docs/stable` and push them
|
||||
|
||||
9. After release, bump versions, add `-dev` back and commit
|
||||
9. Release new zip with `make release_zip`, push `Precompiled.zip` to GitHub Releases
|
||||
|
||||
10. `make release_docs` once again and push it to `elixir-lang/docs`
|
||||
10. Add the release to `elixir.csv` file in `elixir-lang/elixir-lang.github.com`
|
||||
|
||||
11. Add the release to `elixir.csv` file in `elixir-lang/elixir-lang.github.com`
|
||||
## New vMAJOR.MINOR releases
|
||||
|
||||
11. Create a new branch "vMAJOR.MINOR" release
|
||||
|
||||
12. Move stable docs to `docs/vOLD-MAJOR.OLD-MINOR`
|
||||
|
||||
13. Move master docs to `docs/stable`
|
||||
|
||||
14. Bump versions, start new CHANGELOG, add `-dev` back and commit "Start vVERSION+1"
|
||||
|
||||
15. `make release_docs` and push it to `elixir-lang/docs`
|
||||
|
||||
## Places where version is mentioned
|
||||
|
||||
|
||||
+1
-1
@@ -81,7 +81,7 @@ done
|
||||
SELF=$(readlink_f "$0")
|
||||
SCRIPT_PATH=$(dirname "$SELF")
|
||||
|
||||
if [ "$OSTYPE" = "cygwin" ]; then SCRIPT_PATH=$(cygpath -m "SCRIPT_PATH"); fi
|
||||
if [ "$OSTYPE" = "cygwin" ]; then SCRIPT_PATH=$(cygpath -m "$SCRIPT_PATH"); fi
|
||||
if [ "$MODE" != "iex" ]; then ERL="-noshell -s elixir start_cli $ERL"; fi
|
||||
|
||||
# Check for terminal support
|
||||
|
||||
@@ -260,7 +260,7 @@ defmodule Code do
|
||||
Accepts `relative_to` as an argument to tell where the file is located.
|
||||
|
||||
While `load_file` loads a file and returns the loaded modules and their
|
||||
byte code, `eval_file` simply evalutes the file contents and returns the
|
||||
byte code, `eval_file` simply evaluates the file contents and returns the
|
||||
evaluation result and its bindings.
|
||||
"""
|
||||
def eval_file(file, relative_to \\ nil) do
|
||||
|
||||
+15
-18
@@ -188,7 +188,7 @@ defmodule GenEvent do
|
||||
@type manager :: pid | name | {atom, node}
|
||||
|
||||
@typedoc "Supported values for new handlers"
|
||||
@type handler :: atom | {atom, term} | pid
|
||||
@type handler :: atom | {atom, term} | {pid, reference}
|
||||
|
||||
@doc false
|
||||
defmacro __using__(_) do
|
||||
@@ -601,11 +601,7 @@ defmodule GenEvent do
|
||||
reply(tag, reply)
|
||||
loop(parent, name, handlers, debug, hib)
|
||||
{_from, tag, {:add_handler, handler, args}} ->
|
||||
{hib, reply, handlers} = server_add_handler(handler, args, handlers, nil)
|
||||
reply(tag, reply)
|
||||
loop(parent, name, handlers, debug, hib)
|
||||
{_from, tag, {:add_handler, handler, args, notify}} ->
|
||||
{hib, reply, handlers} = server_add_handler(handler, args, handlers, notify)
|
||||
{hib, reply, handlers} = server_add_handler(handler, args, handlers)
|
||||
reply(tag, reply)
|
||||
loop(parent, name, handlers, debug, hib)
|
||||
{_from, tag, {:add_mon_handler, handler, args, notify}} ->
|
||||
@@ -739,13 +735,13 @@ defmodule GenEvent do
|
||||
IO.puts dev, "*DBG* #{inspect name}: #{inspect dbg}"
|
||||
end
|
||||
|
||||
defp server_add_handler({module, id}, args, handlers, notify) do
|
||||
handler = handler(module: module, id: {module, id}, pid: notify)
|
||||
defp server_add_handler({module, id}, args, handlers) do
|
||||
handler = handler(module: module, id: {module, id})
|
||||
do_add_handler(module, handler, args, handlers, :ok)
|
||||
end
|
||||
|
||||
defp server_add_handler(module, args, handlers, notify) do
|
||||
handler = handler(module: module, id: module, pid: notify)
|
||||
defp server_add_handler(module, args, handlers) do
|
||||
handler = handler(module: module, id: module)
|
||||
do_add_handler(module, handler, args, handlers, :ok)
|
||||
end
|
||||
|
||||
@@ -763,7 +759,7 @@ defmodule GenEvent do
|
||||
|
||||
defp server_add_process_handler(pid, handlers, notify) do
|
||||
ref = Process.monitor(pid)
|
||||
handler = handler(module: GenEvent.Stream, id: pid,
|
||||
handler = handler(module: GenEvent.Stream, id: {self(), ref},
|
||||
pid: notify, ref: ref)
|
||||
do_add_handler(GenEvent.Stream, handler, {pid, ref}, handlers, {self(), ref})
|
||||
end
|
||||
@@ -779,7 +775,7 @@ defmodule GenEvent do
|
||||
if sup do
|
||||
server_add_mon_handler(module2, {args2, state}, handlers, sup)
|
||||
else
|
||||
server_add_handler(module2, {args2, state}, handlers, nil)
|
||||
server_add_handler(module2, {args2, state}, handlers)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -796,7 +792,7 @@ defmodule GenEvent do
|
||||
|
||||
defp server_split_process_handlers(mode, event, [handler|t], handlers, streams) do
|
||||
case handler(handler, :id) do
|
||||
pid when is_pid(pid) ->
|
||||
{pid, _ref} when is_pid(pid) ->
|
||||
server_process_notify(mode, event, handler)
|
||||
server_split_process_handlers(mode, event, t, handlers, [handler|streams])
|
||||
_ ->
|
||||
@@ -858,16 +854,17 @@ defmodule GenEvent do
|
||||
end
|
||||
|
||||
defp server_collect_process_handlers(mode, event, [handler|t], handlers, name) when mode in [:sync, :ack] do
|
||||
handler(ref: ref) = handler
|
||||
handler(ref: ref, id: id) = handler
|
||||
|
||||
receive do
|
||||
{^ref, :ok} ->
|
||||
server_collect_process_handlers(mode, event, t, [handler|handlers], name)
|
||||
{^ref, :done} ->
|
||||
do_terminate(handler, :remove_handler, event, name, :normal)
|
||||
{_from, tag, {:delete_handler, ^id, args}} ->
|
||||
do_terminate(handler, args, :remove, name, :normal)
|
||||
reply(tag, :ok)
|
||||
server_collect_process_handlers(mode, event, t, handlers, name)
|
||||
{:DOWN, ^ref, _, _, reason} ->
|
||||
do_terminate(handler, {:stop, reason}, :remove, name, :shutdown)
|
||||
do_terminate(handler, {:stop, reason}, :DOWN, name, :shutdown)
|
||||
server_collect_process_handlers(mode, event, t, handlers, name)
|
||||
end
|
||||
end
|
||||
@@ -939,7 +936,7 @@ defmodule GenEvent do
|
||||
case :lists.keyfind(ref, handler(:ref) + 1, handlers) do
|
||||
false -> :error
|
||||
handler ->
|
||||
do_terminate(handler, {:stop, reason}, :remove, name, :shutdown)
|
||||
do_terminate(handler, {:stop, reason}, :DOWN, name, :shutdown)
|
||||
{:ok, :lists.keydelete(ref, handler(:ref) + 1, handlers)}
|
||||
end
|
||||
end
|
||||
|
||||
@@ -80,7 +80,8 @@ defimpl Enumerable, for: GenEvent.Stream do
|
||||
|
||||
defp start(%{manager: manager} = stream) do
|
||||
try do
|
||||
{:ok, {pid, ref}} = :gen.call(manager, self(), {:add_process_handler, self(), self()}, :infinity)
|
||||
{:ok, {pid, ref}} = :gen.call(manager, self(),
|
||||
{:add_process_handler, self(), self()}, :infinity)
|
||||
mon_ref = Process.monitor(pid)
|
||||
{pid, ref, mon_ref}
|
||||
catch
|
||||
@@ -92,19 +93,6 @@ defimpl Enumerable, for: GenEvent.Stream do
|
||||
self = self()
|
||||
|
||||
receive do
|
||||
# The handler was removed. Stop iteration, resolve the
|
||||
# event later. We need to demonitor now, otherwise DOWN
|
||||
# appears with higher priority in the shutdown process.
|
||||
{:gen_event_EXIT, ^self, _reason} = event ->
|
||||
Process.demonitor(mon_ref, [:flush])
|
||||
send(self, event)
|
||||
{:halt, {:removed, acc}}
|
||||
|
||||
# The manager died. Stop iteration, resolve the event later.
|
||||
{:DOWN, ^mon_ref, _, _, _} = event ->
|
||||
send(self, event)
|
||||
{:halt, {:removed, acc}}
|
||||
|
||||
# Got an async event.
|
||||
{_from, {^pid, ^ref}, {:notify, event}} ->
|
||||
{[{:async, pid, ref, event}], acc}
|
||||
@@ -116,6 +104,19 @@ defimpl Enumerable, for: GenEvent.Stream do
|
||||
# Got an ack event.
|
||||
{_from, {^pid, ^ref}, {:ack_notify, event}} ->
|
||||
{[{:ack, pid, ref, event}], acc}
|
||||
|
||||
# The handler was removed. Stop iteration, resolve the
|
||||
# event later. We need to demonitor now, otherwise DOWN
|
||||
# appears with higher priority in the shutdown process.
|
||||
{:gen_event_EXIT, {^pid, ^ref}, _reason} = event ->
|
||||
Process.demonitor(mon_ref, [:flush])
|
||||
send(self, event)
|
||||
{:halt, {:removed, acc}}
|
||||
|
||||
# The manager died. Stop iteration, resolve the event later.
|
||||
{:DOWN, ^mon_ref, _, _, _} = event ->
|
||||
send(self, event)
|
||||
{:halt, {:removed, acc}}
|
||||
after
|
||||
timeout ->
|
||||
exit({:timeout, {__MODULE__, :next, [stream, acc]}})
|
||||
@@ -135,26 +136,20 @@ defimpl Enumerable, for: GenEvent.Stream do
|
||||
|
||||
# If we reach this branch, the handler was not removed yet,
|
||||
# so we trigger a request for doing so.
|
||||
defp stop(stream, {pid, _, _} = acc) do
|
||||
parent = self()
|
||||
_ = Task.start(fn -> GenEvent.remove_handler(pid, parent, :shutdown) end)
|
||||
defp stop(stream, {pid, ref, _} = acc) do
|
||||
_ = GenEvent.remove_handler(pid, {pid, ref}, :shutdown)
|
||||
stop(stream, {:removed, acc})
|
||||
end
|
||||
|
||||
defp wait_for_handler_removal(pid, ref, mon_ref) do
|
||||
self = self()
|
||||
|
||||
receive do
|
||||
{_from, {^pid, ^ref}, {notify, _event}} when notify in [:ack_notify, :sync_notify] ->
|
||||
send pid, {ref, :done}
|
||||
wait_for_handler_removal(pid, ref, mon_ref)
|
||||
{:gen_event_EXIT, ^self, reason}
|
||||
{:gen_event_EXIT, {^pid, ^ref}, reason}
|
||||
when reason == :normal
|
||||
when reason == :shutdown
|
||||
when tuple_size(reason) == 3 and elem(reason, 0) == :swapped ->
|
||||
Process.demonitor(mon_ref, [:flush])
|
||||
:ok
|
||||
{:gen_event_EXIT, ^self, reason} ->
|
||||
{:gen_event_EXIT, {^pid, ^ref}, reason} ->
|
||||
Process.demonitor(mon_ref, [:flush])
|
||||
{:error, reason}
|
||||
{:DOWN, ^mon_ref, _, _, reason} ->
|
||||
@@ -164,7 +159,7 @@ defimpl Enumerable, for: GenEvent.Stream do
|
||||
|
||||
defp flush_events(ref) do
|
||||
receive do
|
||||
{_from, {_pid, ^ref}, {:notify, _event}} ->
|
||||
{_from, {_pid, ^ref}, {notify, _event}} when notify in [:notify, :ack_notify, :sync_notify] ->
|
||||
flush_events(ref)
|
||||
after
|
||||
0 -> :ok
|
||||
|
||||
@@ -435,7 +435,7 @@ defmodule Inspect.Algebra do
|
||||
iex> Inspect.Algebra.format(doc, 20) |> IO.iodata_to_binary
|
||||
"[1! 2! 3! ...]"
|
||||
"""
|
||||
@spec surround_many(binary, [any], binary, integer | :infinity, (term -> t), binary) :: t
|
||||
@spec surround_many(binary, [any], binary, Inspect.Opts.t, (term, Inspect.Opts.t -> t), binary) :: t
|
||||
def surround_many(left, docs, right, opts, fun, separator \\ @surround_separator) do
|
||||
do_surround_many(left, docs, right, opts.limit, opts, fun, separator)
|
||||
end
|
||||
|
||||
@@ -180,7 +180,7 @@ defmodule IO do
|
||||
Inspects and writes the given argument to the device.
|
||||
|
||||
It enables pretty printing by default with width of
|
||||
80 characters. Th width can be changed by explicitly
|
||||
80 characters. The width can be changed by explicitly
|
||||
passing the `:width` option.
|
||||
|
||||
## Examples
|
||||
|
||||
@@ -1813,7 +1813,9 @@ defmodule Kernel do
|
||||
end
|
||||
|
||||
defp unnest({{:., _, [expr, key]}, _, []}, acc, kind)
|
||||
when is_tuple(expr) and elem(expr, 0) != :__aliases__ and elem(expr, 0) != :__MODULE__ do
|
||||
when is_tuple(expr) and
|
||||
:erlang.element(1, expr) != :__aliases__ and
|
||||
:erlang.element(1, expr) != :__MODULE__ do
|
||||
unnest(expr, [{:map, key}|acc], kind)
|
||||
end
|
||||
|
||||
@@ -1834,8 +1836,8 @@ defmodule Kernel do
|
||||
|
||||
defp proper_start?({{:., _, [expr, _]}, _, _args})
|
||||
when is_atom(expr)
|
||||
when elem(expr, 0) == :__aliases__
|
||||
when elem(expr, 0) == :__MODULE__, do: true
|
||||
when :erlang.element(1, expr) == :__aliases__
|
||||
when :erlang.element(1, expr) == :__MODULE__, do: true
|
||||
|
||||
defp proper_start?({atom, _, _args})
|
||||
when is_atom(atom), do: true
|
||||
@@ -2057,7 +2059,7 @@ defmodule Kernel do
|
||||
end
|
||||
|
||||
defp warn_info([entry|_]) do
|
||||
opts = elem(entry, tuple_size(entry) - 1)
|
||||
opts = :erlang.element(tuple_size(entry), entry)
|
||||
Exception.format_file_line(Keyword.get(opts, :file), Keyword.get(opts, :line)) <> " "
|
||||
end
|
||||
|
||||
|
||||
@@ -113,7 +113,8 @@ defmodule Kernel.CLI do
|
||||
IO.puts :stderr, Exception.format(kind, reason, prune_stacktrace(trace))
|
||||
end
|
||||
|
||||
@elixir_internals [:elixir_compiler, :elixir_module, :elixir_translator, :elixir_expand]
|
||||
@elixir_internals [:elixir, :elixir_exp, :elixir_compiler, :elixir_module, :elixir_translator,
|
||||
:elixir_expand, :elixir_lexical]
|
||||
|
||||
defp prune_stacktrace([{mod, _, _, _}|t]) when mod in @elixir_internals do
|
||||
prune_stacktrace(t)
|
||||
|
||||
@@ -215,7 +215,8 @@ defmodule Kernel.ParallelCompiler do
|
||||
IO.puts Exception.format(:exit, reason, [])
|
||||
end
|
||||
|
||||
@elixir_internals [:elixir_compiler, :elixir_module, :elixir_translator, :elixir_expand]
|
||||
@elixir_internals [:elixir, :elixir_exp, :elixir_compiler, :elixir_module, :elixir_translator,
|
||||
:elixir_expand, :elixir_lexical]
|
||||
|
||||
defp prune_stacktrace([{mod, _, _, _}|t]) when mod in @elixir_internals do
|
||||
prune_stacktrace(t)
|
||||
|
||||
+31
-26
@@ -156,18 +156,22 @@ defmodule Stream do
|
||||
@spec chunk(Enumerable.t, non_neg_integer, non_neg_integer, Enumerable.t | nil) :: Enumerable.t
|
||||
def chunk(enum, n, step, pad \\ nil) when n > 0 and step > 0 do
|
||||
limit = :erlang.max(n, step)
|
||||
lazy enum, {[], 0},
|
||||
fn(f1) -> R.chunk(n, step, limit, f1) end,
|
||||
fn(f1) -> &do_chunk(&1, n, pad, f1) end
|
||||
if is_nil(pad) do
|
||||
lazy enum, {[], 0}, fn(f1) -> R.chunk(n, step, limit, f1) end
|
||||
else
|
||||
lazy enum, {[], 0},
|
||||
fn(f1) -> R.chunk(n, step, limit, f1) end,
|
||||
&do_chunk(&1, n, pad, &2)
|
||||
end
|
||||
end
|
||||
|
||||
defp do_chunk(acc(h, {buffer, count} = old, t) = acc, n, pad, f1) do
|
||||
if is_nil(pad) || count == 0 do
|
||||
{:cont, acc}
|
||||
else
|
||||
buffer = :lists.reverse(buffer) ++ Enum.take(pad, n - count)
|
||||
cont_with_acc(f1, buffer, h, old, t)
|
||||
end
|
||||
defp do_chunk(acc(_, {_, 0}, _) = acc, _, _, _) do
|
||||
{:cont, acc}
|
||||
end
|
||||
|
||||
defp do_chunk(acc(h, {buffer, count} = old, t), n, pad, f1) do
|
||||
buffer = :lists.reverse(buffer) ++ Enum.take(pad, n - count)
|
||||
cont_with_acc(f1, buffer, h, old, t)
|
||||
end
|
||||
|
||||
@doc """
|
||||
@@ -186,7 +190,7 @@ defmodule Stream do
|
||||
def chunk_by(enum, fun) do
|
||||
lazy enum, nil,
|
||||
fn(f1) -> R.chunk_by(fun, f1) end,
|
||||
fn(f1) -> &do_chunk_by(&1, f1) end
|
||||
&do_chunk_by(&1, &2)
|
||||
end
|
||||
|
||||
defp do_chunk_by(acc(_, nil, _) = acc, _f1) do
|
||||
@@ -1127,12 +1131,12 @@ defmodule Stream do
|
||||
|
||||
@compile {:inline, lazy: 2, lazy: 3, lazy: 4}
|
||||
|
||||
defp lazy(%Stream{funs: funs} = lazy, fun),
|
||||
defp lazy(%Stream{done: nil, funs: funs} = lazy, fun),
|
||||
do: %{lazy | funs: [fun|funs] }
|
||||
defp lazy(enum, fun),
|
||||
do: %Stream{enum: enum, funs: [fun]}
|
||||
|
||||
defp lazy(%Stream{funs: funs, accs: accs} = lazy, acc, fun),
|
||||
defp lazy(%Stream{done: nil, funs: funs, accs: accs} = lazy, acc, fun),
|
||||
do: %{lazy | funs: [fun|funs], accs: [acc|accs] }
|
||||
defp lazy(enum, acc, fun),
|
||||
do: %Stream{enum: enum, funs: [fun], accs: [acc]}
|
||||
@@ -1171,19 +1175,20 @@ defimpl Enumerable, for: Stream do
|
||||
case reduce.({command, [acc|accs]}) do
|
||||
{:suspended, [acc|accs], continuation} ->
|
||||
{:suspended, acc, &do_each(continuation, done, accs, &1)}
|
||||
{:halted, [acc|_]} ->
|
||||
{:halted, acc}
|
||||
{:done, [acc|_] = accs} ->
|
||||
case done do
|
||||
nil ->
|
||||
{:done, acc}
|
||||
{done, fun} ->
|
||||
case done.(fun).(accs) do
|
||||
{:cont, [acc|_]} -> {:done, acc}
|
||||
{:halt, [acc|_]} -> {:halted, acc}
|
||||
{:suspend, [acc|_]} -> {:suspended, acc, &({:done, elem(&1, 1)})}
|
||||
end
|
||||
end
|
||||
{:halted, accs} ->
|
||||
do_done {:halted, accs}, done
|
||||
{:done, accs} ->
|
||||
do_done {:done, accs}, done
|
||||
end
|
||||
end
|
||||
|
||||
defp do_done({reason, [acc|_]}, nil), do: {reason, acc}
|
||||
defp do_done({reason, [acc|t]}, {done, fun}) do
|
||||
[h|_] = Enum.reverse(t)
|
||||
case done.([acc, h], fun) do
|
||||
{:cont, [acc|_]} -> {reason, acc}
|
||||
{:halt, [acc|_]} -> {:halted, acc}
|
||||
{:suspend, [acc|_]} -> {:suspended, acc, &({:done, elem(&1, 1)})}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -431,18 +431,18 @@ defmodule System do
|
||||
@spec cmd(binary, [binary], Keyword.t) ::
|
||||
{Collectable.t, exit_status :: non_neg_integer}
|
||||
def cmd(command, args, opts \\ []) when is_binary(command) and is_list(args) do
|
||||
command = String.to_char_list(command)
|
||||
cmd = String.to_char_list(command)
|
||||
|
||||
command =
|
||||
if Path.type(command) == :absolute do
|
||||
command
|
||||
cmd =
|
||||
if Path.type(cmd) == :absolute do
|
||||
cmd
|
||||
else
|
||||
:os.find_executable(command) || command
|
||||
:os.find_executable(cmd) || :erlang.error(:enoent, [command, args, opts])
|
||||
end
|
||||
|
||||
{into, opts} = cmd_opts(opts, [:use_stdio, :exit_status, :binary, :hide, args: args], "")
|
||||
{initial, fun} = Collectable.into(into)
|
||||
do_cmd Port.open({:spawn_executable, command}, opts), initial, fun
|
||||
do_cmd Port.open({:spawn_executable, cmd}, opts), initial, fun
|
||||
end
|
||||
|
||||
defp do_cmd(port, acc, fun) do
|
||||
@@ -463,9 +463,12 @@ defmodule System do
|
||||
defp cmd_opts([{:arg0, bin}|t], opts, into) when is_binary(bin),
|
||||
do: cmd_opts(t, [{:arg0, bin}|opts], into)
|
||||
|
||||
defp cmd_opts([{:stderr_to_stdout, bool}|t], opts, into) when is_boolean(bool),
|
||||
defp cmd_opts([{:stderr_to_stdout, true}|t], opts, into),
|
||||
do: cmd_opts(t, [:stderr_to_stdout|opts], into)
|
||||
|
||||
defp cmd_opts([{:stderr_to_stdout, false}|t], opts, into),
|
||||
do: cmd_opts(t, opts, into)
|
||||
|
||||
defp cmd_opts([{:parallelism, bool}|t], opts, into) when is_boolean(bool),
|
||||
do: cmd_opts(t, [{:parallelism, bool}|opts], into)
|
||||
|
||||
|
||||
@@ -9,20 +9,7 @@
|
||||
find_import/4, format_error/1]).
|
||||
-include("elixir.hrl").
|
||||
-import(ordsets, [is_element/2]).
|
||||
|
||||
-define(atom, 'Elixir.Atom').
|
||||
-define(float, 'Elixir.Float').
|
||||
-define(io, 'Elixir.IO').
|
||||
-define(integer, 'Elixir.Integer').
|
||||
-define(kernel, 'Elixir.Kernel').
|
||||
-define(list, 'Elixir.List').
|
||||
-define(map, 'Elixir.Map').
|
||||
-define(node, 'Elixir.Node').
|
||||
-define(port, 'Elixir.Port').
|
||||
-define(process, 'Elixir.Process').
|
||||
-define(string, 'Elixir.String').
|
||||
-define(system, 'Elixir.System').
|
||||
-define(tuple, 'Elixir.Tuple').
|
||||
|
||||
default_functions() ->
|
||||
[{?kernel, elixir_imported_functions()}].
|
||||
@@ -82,7 +69,7 @@ remote_function(Meta, Receiver, Name, Arity, E) ->
|
||||
check_deprecation(Meta, Receiver, Name, Arity, E),
|
||||
|
||||
elixir_lexical:record_remote(Receiver, ?m(E, lexical_tracker)),
|
||||
case inline(Receiver, Name, Arity) of
|
||||
case elixir_rewrite:inline(Receiver, Name, Arity) of
|
||||
{AR, AN} -> {remote, AR, AN, Arity};
|
||||
false -> {remote, Receiver, Name, Arity}
|
||||
end.
|
||||
@@ -103,9 +90,9 @@ dispatch_import(Meta, Name, Args, E, Callback) ->
|
||||
dispatch_require(Meta, Receiver, Name, Args, E, Callback) when is_atom(Receiver) ->
|
||||
Arity = length(Args),
|
||||
|
||||
case rewrite(Receiver, Name, Args, Arity) of
|
||||
{ok, AR, AN, AA} ->
|
||||
Callback(AR, AN, AA);
|
||||
case elixir_rewrite:inline(Receiver, Name, Arity) of
|
||||
{AR, AN} ->
|
||||
Callback(AR, AN, Args);
|
||||
false ->
|
||||
case expand_require(Meta, Receiver, {Name, Arity}, Args, E) of
|
||||
{ok, Receiver, Quoted} -> expand_quoted(Meta, Receiver, Name, Arity, Quoted, E);
|
||||
@@ -151,11 +138,7 @@ do_expand_import(Meta, {Name, Arity} = Tuple, Args, Module, E, Result) ->
|
||||
{function, Receiver} ->
|
||||
elixir_lexical:record_import(Receiver, ?m(E, lexical_tracker)),
|
||||
elixir_locals:record_import(Tuple, Receiver, Module, ?m(E, function)),
|
||||
|
||||
case rewrite(Receiver, Name, Args, Arity) of
|
||||
{ok, _, _, _} = Res -> Res;
|
||||
false -> {ok, Receiver, Name, Args}
|
||||
end;
|
||||
{ok, Receiver, Name, Args};
|
||||
{macro, Receiver} ->
|
||||
check_deprecation(Meta, Receiver, Name, Arity, E),
|
||||
elixir_lexical:record_import(Receiver, ?m(E, lexical_tracker)),
|
||||
@@ -167,8 +150,8 @@ do_expand_import(Meta, {Name, Arity} = Tuple, Args, Module, E, Result) ->
|
||||
error -> {ok, Receiver, Name, Args}
|
||||
end;
|
||||
false when Module == ?kernel ->
|
||||
case rewrite(Module, Name, Args, Arity) of
|
||||
{ok, _, _, _} = Res -> Res;
|
||||
case elixir_rewrite:inline(Module, Name, Arity) of
|
||||
{AR, AN} -> {ok, AR, AN, Args};
|
||||
false -> error
|
||||
end;
|
||||
false ->
|
||||
@@ -342,163 +325,6 @@ elixir_imported_macros() ->
|
||||
error:undef -> []
|
||||
end.
|
||||
|
||||
rewrite(?atom, to_string, [Arg], _) ->
|
||||
{ok, erlang, atom_to_binary, [Arg, utf8]};
|
||||
rewrite(?kernel, elem, [Tuple, Index], _) ->
|
||||
{ok, erlang, element, [increment(Index), Tuple]};
|
||||
rewrite(?kernel, put_elem, [Tuple, Index, Value], _) ->
|
||||
{ok, erlang, setelement, [increment(Index), Tuple, Value]};
|
||||
rewrite(?map, 'has_key?', [Map, Key], _) ->
|
||||
{ok, maps, is_key, [Key, Map]};
|
||||
rewrite(?map, fetch, [Map, Key], _) ->
|
||||
{ok, maps, find, [Key, Map]};
|
||||
rewrite(?map, put, [Map, Key, Value], _) ->
|
||||
{ok, maps, put, [Key, Value, Map]};
|
||||
rewrite(?map, delete, [Map, Key], _) ->
|
||||
{ok, maps, remove, [Key, Map]};
|
||||
rewrite(?process, monitor, [Arg], _) ->
|
||||
{ok, erlang, monitor, [process, Arg]};
|
||||
rewrite(?string, to_atom, [Arg], _) ->
|
||||
{ok, erlang, binary_to_atom, [Arg, utf8]};
|
||||
rewrite(?string, to_existing_atom, [Arg], _) ->
|
||||
{ok, erlang, binary_to_existing_atom, [Arg, utf8]};
|
||||
rewrite(?tuple, insert_at, [Tuple, Index, Term], _) ->
|
||||
{ok, erlang, insert_element, [increment(Index), Tuple, Term]};
|
||||
rewrite(?tuple, delete_at, [Tuple, Index], _) ->
|
||||
{ok, erlang, delete_element, [increment(Index), Tuple]};
|
||||
rewrite(?tuple, duplicate, [Data, Size], _) ->
|
||||
{ok, erlang, make_tuple, [Size, Data]};
|
||||
|
||||
rewrite(Receiver, Name, Args, Arity) ->
|
||||
case inline(Receiver, Name, Arity) of
|
||||
{AR, AN} -> {ok, AR, AN, Args};
|
||||
false -> false
|
||||
end.
|
||||
|
||||
increment(Number) when is_number(Number) ->
|
||||
Number + 1;
|
||||
increment(Other) ->
|
||||
{{'.', [], [erlang, '+']}, [], [Other, 1]}.
|
||||
|
||||
inline(?atom, to_char_list, 1) -> {erlang, atom_to_list};
|
||||
inline(?io, iodata_length, 1) -> {erlang, iolist_size};
|
||||
inline(?io, iodata_to_binary, 1) -> {erlang, iolist_to_binary};
|
||||
inline(?integer, to_string, 1) -> {erlang, integer_to_binary};
|
||||
inline(?integer, to_string, 2) -> {erlang, integer_to_binary};
|
||||
inline(?integer, to_char_list, 1) -> {erlang, integer_to_list};
|
||||
inline(?integer, to_char_list, 2) -> {erlang, integer_to_list};
|
||||
inline(?float, to_string, 1) -> {erlang, float_to_binary};
|
||||
inline(?float, to_char_list, 1) -> {erlang, float_to_list};
|
||||
inline(?list, to_atom, 1) -> {erlang, list_to_atom};
|
||||
inline(?list, to_existing_atom, 1) -> {erlang, list_to_existing_atom};
|
||||
inline(?list, to_float, 1) -> {erlang, list_to_float};
|
||||
inline(?list, to_integer, 1) -> {erlang, list_to_integer};
|
||||
inline(?list, to_integer, 2) -> {erlang, list_to_integer};
|
||||
inline(?list, to_tuple, 1) -> {erlang, list_to_tuple};
|
||||
|
||||
inline(?kernel, '+', 2) -> {erlang, '+'};
|
||||
inline(?kernel, '-', 2) -> {erlang, '-'};
|
||||
inline(?kernel, '+', 1) -> {erlang, '+'};
|
||||
inline(?kernel, '-', 1) -> {erlang, '-'};
|
||||
inline(?kernel, '*', 2) -> {erlang, '*'};
|
||||
inline(?kernel, '/', 2) -> {erlang, '/'};
|
||||
inline(?kernel, '++', 2) -> {erlang, '++'};
|
||||
inline(?kernel, '--', 2) -> {erlang, '--'};
|
||||
inline(?kernel, 'not', 1) -> {erlang, 'not'};
|
||||
inline(?kernel, '<', 2) -> {erlang, '<'};
|
||||
inline(?kernel, '>', 2) -> {erlang, '>'};
|
||||
inline(?kernel, '<=', 2) -> {erlang, '=<'};
|
||||
inline(?kernel, '>=', 2) -> {erlang, '>='};
|
||||
inline(?kernel, '==', 2) -> {erlang, '=='};
|
||||
inline(?kernel, '!=', 2) -> {erlang, '/='};
|
||||
inline(?kernel, '===', 2) -> {erlang, '=:='};
|
||||
inline(?kernel, '!==', 2) -> {erlang, '=/='};
|
||||
inline(?kernel, abs, 1) -> {erlang, abs};
|
||||
inline(?kernel, apply, 2) -> {erlang, apply};
|
||||
inline(?kernel, apply, 3) -> {erlang, apply};
|
||||
inline(?kernel, binary_part, 3) -> {erlang, binary_part};
|
||||
inline(?kernel, bit_size, 1) -> {erlang, bit_size};
|
||||
inline(?kernel, byte_size, 1) -> {erlang, byte_size};
|
||||
inline(?kernel, 'div', 2) -> {erlang, 'div'};
|
||||
inline(?kernel, exit, 1) -> {erlang, exit};
|
||||
inline(?kernel, hd, 1) -> {erlang, hd};
|
||||
inline(?kernel, is_atom, 1) -> {erlang, is_atom};
|
||||
inline(?kernel, is_binary, 1) -> {erlang, is_binary};
|
||||
inline(?kernel, is_bitstring, 1) -> {erlang, is_bitstring};
|
||||
inline(?kernel, is_boolean, 1) -> {erlang, is_boolean};
|
||||
inline(?kernel, is_float, 1) -> {erlang, is_float};
|
||||
inline(?kernel, is_function, 1) -> {erlang, is_function};
|
||||
inline(?kernel, is_function, 2) -> {erlang, is_function};
|
||||
inline(?kernel, is_integer, 1) -> {erlang, is_integer};
|
||||
inline(?kernel, is_list, 1) -> {erlang, is_list};
|
||||
inline(?kernel, is_map, 1) -> {erlang, is_map};
|
||||
inline(?kernel, is_number, 1) -> {erlang, is_number};
|
||||
inline(?kernel, is_pid, 1) -> {erlang, is_pid};
|
||||
inline(?kernel, is_port, 1) -> {erlang, is_port};
|
||||
inline(?kernel, is_reference, 1) -> {erlang, is_reference};
|
||||
inline(?kernel, is_tuple, 1) -> {erlang, is_tuple};
|
||||
inline(?kernel, length, 1) -> {erlang, length};
|
||||
inline(?kernel, make_ref, 0) -> {erlang, make_ref};
|
||||
inline(?kernel, map_size, 1) -> {erlang, map_size};
|
||||
inline(?kernel, max, 2) -> {erlang, max};
|
||||
inline(?kernel, min, 2) -> {erlang, min};
|
||||
inline(?kernel, node, 0) -> {erlang, node};
|
||||
inline(?kernel, node, 1) -> {erlang, node};
|
||||
inline(?kernel, 'rem', 2) -> {erlang, 'rem'};
|
||||
inline(?kernel, round, 1) -> {erlang, round};
|
||||
inline(?kernel, self, 0) -> {erlang, self};
|
||||
inline(?kernel, send, 2) -> {erlang, send};
|
||||
inline(?kernel, spawn, 1) -> {erlang, spawn};
|
||||
inline(?kernel, spawn, 3) -> {erlang, spawn};
|
||||
inline(?kernel, spawn_link, 1) -> {erlang, spawn_link};
|
||||
inline(?kernel, spawn_link, 3) -> {erlang, spawn_link};
|
||||
inline(?kernel, spawn_monitor, 1) -> {erlang, spawn_monitor};
|
||||
inline(?kernel, spawn_monitor, 3) -> {erlang, spawn_monitor};
|
||||
inline(?kernel, throw, 1) -> {erlang, throw};
|
||||
inline(?kernel, tl, 1) -> {erlang, tl};
|
||||
inline(?kernel, trunc, 1) -> {erlang, trunc};
|
||||
inline(?kernel, tuple_size, 1) -> {erlang, tuple_size};
|
||||
|
||||
inline(?map, keys, 1) -> {maps, keys};
|
||||
inline(?map, merge, 2) -> {maps, merge};
|
||||
inline(?map, size, 1) -> {maps, size};
|
||||
inline(?map, values, 1) -> {maps, values};
|
||||
inline(?map, to_list, 1) -> {maps, to_list};
|
||||
|
||||
inline(?node, spawn, 2) -> {erlang, spawn};
|
||||
inline(?node, spawn, 3) -> {erlang, spawn_opt};
|
||||
inline(?node, spawn, 4) -> {erlang, spawn};
|
||||
inline(?node, spawn, 5) -> {erlang, spawn_opt};
|
||||
inline(?node, spawn_link, 2) -> {erlang, spawn_link};
|
||||
inline(?node, spawn_link, 4) -> {erlang, spawn_link};
|
||||
|
||||
inline(?process, exit, 2) -> {erlang, exit};
|
||||
inline(?process, spawn, 2) -> {erlang, spawn_opt};
|
||||
inline(?process, spawn, 4) -> {erlang, spawn_opt};
|
||||
inline(?process, demonitor, 1) -> {erlang, demonitor};
|
||||
inline(?process, demonitor, 2) -> {erlang, demonitor};
|
||||
inline(?process, link, 1) -> {erlang, link};
|
||||
inline(?process, unlink, 1) -> {erlang, unlink};
|
||||
|
||||
inline(?port, open, 2) -> {erlang, open_port};
|
||||
inline(?port, call, 3) -> {erlang, port_call};
|
||||
inline(?port, close, 1) -> {erlang, port_close};
|
||||
inline(?port, command, 2) -> {erlang, port_command};
|
||||
inline(?port, command, 3) -> {erlang, port_command};
|
||||
inline(?port, connect, 2) -> {erlang, port_connect};
|
||||
inline(?port, control, 3) -> {erlang, port_control};
|
||||
inline(?port, info, 1) -> {erlang, port_info};
|
||||
inline(?port, info, 2) -> {erlang, port_info};
|
||||
inline(?port, list, 0) -> {erlang, ports};
|
||||
|
||||
inline(?string, to_float, 1) -> {erlang, binary_to_float};
|
||||
inline(?string, to_integer, 1) -> {erlang, binary_to_integer};
|
||||
inline(?string, to_integer, 2) -> {erlang, binary_to_integer};
|
||||
inline(?system, stacktrace, 0) -> {erlang, get_stacktrace};
|
||||
inline(?tuple, to_list, 1) -> {erlang, tuple_to_list};
|
||||
|
||||
inline(_, _, _) -> false.
|
||||
|
||||
check_deprecation(Meta, Receiver, Name, Arity, #{file := File}) ->
|
||||
case deprecation(Receiver, Name, Arity) of
|
||||
false -> ok;
|
||||
|
||||
@@ -474,7 +474,8 @@ expand_remote(Receiver, DotMeta, Right, Meta, Args, E, EL) ->
|
||||
true -> ok
|
||||
end,
|
||||
{EArgs, EA} = expand_args(Args, E),
|
||||
{{{'.', DotMeta, [Receiver, Right]}, Meta, EArgs}, elixir_env:mergev(EL, EA)}.
|
||||
{elixir_rewrite:rewrite(Receiver, DotMeta, Right, Meta, EArgs),
|
||||
elixir_env:mergev(EL, EA)}.
|
||||
|
||||
%% Lexical helpers
|
||||
|
||||
|
||||
@@ -0,0 +1,224 @@
|
||||
-module(elixir_rewrite).
|
||||
-export([rewrite/5, inline/3]).
|
||||
|
||||
%% Convenience variables
|
||||
|
||||
-define(hidden, [{line,-1}]).
|
||||
|
||||
-define(atom, 'Elixir.Atom').
|
||||
-define(enum, 'Elixir.Enum').
|
||||
-define(float, 'Elixir.Float').
|
||||
-define(io, 'Elixir.IO').
|
||||
-define(integer, 'Elixir.Integer').
|
||||
-define(kernel, 'Elixir.Kernel').
|
||||
-define(list, 'Elixir.List').
|
||||
-define(map, 'Elixir.Map').
|
||||
-define(node, 'Elixir.Node').
|
||||
-define(port, 'Elixir.Port').
|
||||
-define(process, 'Elixir.Process').
|
||||
-define(string, 'Elixir.String').
|
||||
-define(string_chars, 'Elixir.String.Chars').
|
||||
-define(system, 'Elixir.System').
|
||||
-define(tuple, 'Elixir.Tuple').
|
||||
|
||||
%% Inline
|
||||
|
||||
%% Inline rules are straight-forward, they keep the same
|
||||
%% number and order of arguments and show up on captures.
|
||||
|
||||
inline(?atom, to_char_list, 1) -> {erlang, atom_to_list};
|
||||
inline(?io, iodata_length, 1) -> {erlang, iolist_size};
|
||||
inline(?io, iodata_to_binary, 1) -> {erlang, iolist_to_binary};
|
||||
inline(?integer, to_string, 1) -> {erlang, integer_to_binary};
|
||||
inline(?integer, to_string, 2) -> {erlang, integer_to_binary};
|
||||
inline(?integer, to_char_list, 1) -> {erlang, integer_to_list};
|
||||
inline(?integer, to_char_list, 2) -> {erlang, integer_to_list};
|
||||
inline(?float, to_string, 1) -> {erlang, float_to_binary};
|
||||
inline(?float, to_char_list, 1) -> {erlang, float_to_list};
|
||||
inline(?list, to_atom, 1) -> {erlang, list_to_atom};
|
||||
inline(?list, to_existing_atom, 1) -> {erlang, list_to_existing_atom};
|
||||
inline(?list, to_float, 1) -> {erlang, list_to_float};
|
||||
inline(?list, to_integer, 1) -> {erlang, list_to_integer};
|
||||
inline(?list, to_integer, 2) -> {erlang, list_to_integer};
|
||||
inline(?list, to_tuple, 1) -> {erlang, list_to_tuple};
|
||||
|
||||
inline(?kernel, '+', 2) -> {erlang, '+'};
|
||||
inline(?kernel, '-', 2) -> {erlang, '-'};
|
||||
inline(?kernel, '+', 1) -> {erlang, '+'};
|
||||
inline(?kernel, '-', 1) -> {erlang, '-'};
|
||||
inline(?kernel, '*', 2) -> {erlang, '*'};
|
||||
inline(?kernel, '/', 2) -> {erlang, '/'};
|
||||
inline(?kernel, '++', 2) -> {erlang, '++'};
|
||||
inline(?kernel, '--', 2) -> {erlang, '--'};
|
||||
inline(?kernel, 'not', 1) -> {erlang, 'not'};
|
||||
inline(?kernel, '<', 2) -> {erlang, '<'};
|
||||
inline(?kernel, '>', 2) -> {erlang, '>'};
|
||||
inline(?kernel, '<=', 2) -> {erlang, '=<'};
|
||||
inline(?kernel, '>=', 2) -> {erlang, '>='};
|
||||
inline(?kernel, '==', 2) -> {erlang, '=='};
|
||||
inline(?kernel, '!=', 2) -> {erlang, '/='};
|
||||
inline(?kernel, '===', 2) -> {erlang, '=:='};
|
||||
inline(?kernel, '!==', 2) -> {erlang, '=/='};
|
||||
inline(?kernel, abs, 1) -> {erlang, abs};
|
||||
inline(?kernel, apply, 2) -> {erlang, apply};
|
||||
inline(?kernel, apply, 3) -> {erlang, apply};
|
||||
inline(?kernel, binary_part, 3) -> {erlang, binary_part};
|
||||
inline(?kernel, bit_size, 1) -> {erlang, bit_size};
|
||||
inline(?kernel, byte_size, 1) -> {erlang, byte_size};
|
||||
inline(?kernel, 'div', 2) -> {erlang, 'div'};
|
||||
inline(?kernel, exit, 1) -> {erlang, exit};
|
||||
inline(?kernel, hd, 1) -> {erlang, hd};
|
||||
inline(?kernel, is_atom, 1) -> {erlang, is_atom};
|
||||
inline(?kernel, is_binary, 1) -> {erlang, is_binary};
|
||||
inline(?kernel, is_bitstring, 1) -> {erlang, is_bitstring};
|
||||
inline(?kernel, is_boolean, 1) -> {erlang, is_boolean};
|
||||
inline(?kernel, is_float, 1) -> {erlang, is_float};
|
||||
inline(?kernel, is_function, 1) -> {erlang, is_function};
|
||||
inline(?kernel, is_function, 2) -> {erlang, is_function};
|
||||
inline(?kernel, is_integer, 1) -> {erlang, is_integer};
|
||||
inline(?kernel, is_list, 1) -> {erlang, is_list};
|
||||
inline(?kernel, is_map, 1) -> {erlang, is_map};
|
||||
inline(?kernel, is_number, 1) -> {erlang, is_number};
|
||||
inline(?kernel, is_pid, 1) -> {erlang, is_pid};
|
||||
inline(?kernel, is_port, 1) -> {erlang, is_port};
|
||||
inline(?kernel, is_reference, 1) -> {erlang, is_reference};
|
||||
inline(?kernel, is_tuple, 1) -> {erlang, is_tuple};
|
||||
inline(?kernel, length, 1) -> {erlang, length};
|
||||
inline(?kernel, make_ref, 0) -> {erlang, make_ref};
|
||||
inline(?kernel, map_size, 1) -> {erlang, map_size};
|
||||
inline(?kernel, max, 2) -> {erlang, max};
|
||||
inline(?kernel, min, 2) -> {erlang, min};
|
||||
inline(?kernel, node, 0) -> {erlang, node};
|
||||
inline(?kernel, node, 1) -> {erlang, node};
|
||||
inline(?kernel, 'rem', 2) -> {erlang, 'rem'};
|
||||
inline(?kernel, round, 1) -> {erlang, round};
|
||||
inline(?kernel, self, 0) -> {erlang, self};
|
||||
inline(?kernel, send, 2) -> {erlang, send};
|
||||
inline(?kernel, spawn, 1) -> {erlang, spawn};
|
||||
inline(?kernel, spawn, 3) -> {erlang, spawn};
|
||||
inline(?kernel, spawn_link, 1) -> {erlang, spawn_link};
|
||||
inline(?kernel, spawn_link, 3) -> {erlang, spawn_link};
|
||||
inline(?kernel, spawn_monitor, 1) -> {erlang, spawn_monitor};
|
||||
inline(?kernel, spawn_monitor, 3) -> {erlang, spawn_monitor};
|
||||
inline(?kernel, throw, 1) -> {erlang, throw};
|
||||
inline(?kernel, tl, 1) -> {erlang, tl};
|
||||
inline(?kernel, trunc, 1) -> {erlang, trunc};
|
||||
inline(?kernel, tuple_size, 1) -> {erlang, tuple_size};
|
||||
|
||||
inline(?map, keys, 1) -> {maps, keys};
|
||||
inline(?map, merge, 2) -> {maps, merge};
|
||||
inline(?map, size, 1) -> {maps, size};
|
||||
inline(?map, values, 1) -> {maps, values};
|
||||
inline(?map, to_list, 1) -> {maps, to_list};
|
||||
|
||||
inline(?node, spawn, 2) -> {erlang, spawn};
|
||||
inline(?node, spawn, 3) -> {erlang, spawn_opt};
|
||||
inline(?node, spawn, 4) -> {erlang, spawn};
|
||||
inline(?node, spawn, 5) -> {erlang, spawn_opt};
|
||||
inline(?node, spawn_link, 2) -> {erlang, spawn_link};
|
||||
inline(?node, spawn_link, 4) -> {erlang, spawn_link};
|
||||
|
||||
inline(?process, exit, 2) -> {erlang, exit};
|
||||
inline(?process, spawn, 2) -> {erlang, spawn_opt};
|
||||
inline(?process, spawn, 4) -> {erlang, spawn_opt};
|
||||
inline(?process, demonitor, 1) -> {erlang, demonitor};
|
||||
inline(?process, demonitor, 2) -> {erlang, demonitor};
|
||||
inline(?process, link, 1) -> {erlang, link};
|
||||
inline(?process, unlink, 1) -> {erlang, unlink};
|
||||
|
||||
inline(?port, open, 2) -> {erlang, open_port};
|
||||
inline(?port, call, 3) -> {erlang, port_call};
|
||||
inline(?port, close, 1) -> {erlang, port_close};
|
||||
inline(?port, command, 2) -> {erlang, port_command};
|
||||
inline(?port, command, 3) -> {erlang, port_command};
|
||||
inline(?port, connect, 2) -> {erlang, port_connect};
|
||||
inline(?port, control, 3) -> {erlang, port_control};
|
||||
inline(?port, info, 1) -> {erlang, port_info};
|
||||
inline(?port, info, 2) -> {erlang, port_info};
|
||||
inline(?port, list, 0) -> {erlang, ports};
|
||||
|
||||
inline(?string, to_float, 1) -> {erlang, binary_to_float};
|
||||
inline(?string, to_integer, 1) -> {erlang, binary_to_integer};
|
||||
inline(?string, to_integer, 2) -> {erlang, binary_to_integer};
|
||||
inline(?system, stacktrace, 0) -> {erlang, get_stacktrace};
|
||||
inline(?tuple, to_list, 1) -> {erlang, tuple_to_list};
|
||||
|
||||
inline(_, _, _) -> false.
|
||||
|
||||
%% Rewrite rules
|
||||
%% Rewrite rules are more complex than regular inlining code.
|
||||
%% It receives all remote call arguments and return quoted
|
||||
%% expressions with the new environment.
|
||||
|
||||
%% Complex rewrite rules
|
||||
|
||||
rewrite(?string_chars, _DotMeta, 'to_string', _Meta, [String]) when is_binary(String) ->
|
||||
String;
|
||||
rewrite(?string_chars, DotMeta, 'to_string', Meta, [String]) ->
|
||||
Var = {'_rewrite', ?hidden, nil},
|
||||
Guard = {{'.', ?hidden, [erlang, is_binary]}, ?hidden, [Var]},
|
||||
Slow = remote(?string_chars, DotMeta, 'to_string', Meta, [Var]),
|
||||
Fast = Var,
|
||||
|
||||
{'case', ?hidden, [String, [{do,
|
||||
[{'->', ?hidden, [[{'when', ?hidden, [Var, Guard]}], Fast]},
|
||||
{'->', ?hidden, [[Var], Slow]}]
|
||||
}]]};
|
||||
|
||||
rewrite(?enum, DotMeta, 'reverse', Meta, [List]) when is_list(List) ->
|
||||
remote(lists, DotMeta, 'reverse', Meta, [List]);
|
||||
rewrite(?enum, DotMeta, 'reverse', Meta, [List]) ->
|
||||
Var = {'_rewrite', ?hidden, nil},
|
||||
Guard = {{'.', ?hidden, [erlang, is_list]}, ?hidden, [Var]},
|
||||
Slow = remote(?enum, DotMeta, 'reverse', Meta, [Var]),
|
||||
Fast = remote(lists, DotMeta, 'reverse', Meta, [Var]),
|
||||
|
||||
{'case', ?hidden, [List, [{do,
|
||||
[{'->', ?hidden, [[{'when', ?hidden, [Var, Guard]}], Fast]},
|
||||
{'->', ?hidden, [[Var], Slow]}]
|
||||
}]]};
|
||||
|
||||
rewrite(Receiver, DotMeta, Right, Meta, Args) ->
|
||||
{EReceiver, ERight, EArgs} = rewrite(Receiver, Right, Args),
|
||||
remote(EReceiver, DotMeta, ERight, Meta, EArgs).
|
||||
|
||||
%% Simple rewrite rules
|
||||
|
||||
rewrite(?atom, to_string, [Arg]) ->
|
||||
{erlang, atom_to_binary, [Arg, utf8]};
|
||||
rewrite(?kernel, elem, [Tuple, Index]) ->
|
||||
{erlang, element, [increment(Index), Tuple]};
|
||||
rewrite(?kernel, put_elem, [Tuple, Index, Value]) ->
|
||||
{erlang, setelement, [increment(Index), Tuple, Value]};
|
||||
rewrite(?map, 'has_key?', [Map, Key]) ->
|
||||
{maps, is_key, [Key, Map]};
|
||||
rewrite(?map, fetch, [Map, Key]) ->
|
||||
{maps, find, [Key, Map]};
|
||||
rewrite(?map, put, [Map, Key, Value]) ->
|
||||
{maps, put, [Key, Value, Map]};
|
||||
rewrite(?map, delete, [Map, Key]) ->
|
||||
{maps, remove, [Key, Map]};
|
||||
rewrite(?process, monitor, [Arg]) ->
|
||||
{erlang, monitor, [process, Arg]};
|
||||
rewrite(?string, to_atom, [Arg]) ->
|
||||
{erlang, binary_to_atom, [Arg, utf8]};
|
||||
rewrite(?string, to_existing_atom, [Arg]) ->
|
||||
{erlang, binary_to_existing_atom, [Arg, utf8]};
|
||||
rewrite(?tuple, insert_at, [Tuple, Index, Term]) ->
|
||||
{erlang, insert_element, [increment(Index), Tuple, Term]};
|
||||
rewrite(?tuple, delete_at, [Tuple, Index]) ->
|
||||
{erlang, delete_element, [increment(Index), Tuple]};
|
||||
rewrite(?tuple, duplicate, [Data, Size]) ->
|
||||
{erlang, make_tuple, [Size, Data]};
|
||||
rewrite(Receiver, Fun, Args) ->
|
||||
{Receiver, Fun, Args}.
|
||||
|
||||
%% Rewrite helpers
|
||||
|
||||
remote(Receiver, DotMeta, Right, Meta, Args) ->
|
||||
{{'.', DotMeta, [Receiver, Right]}, Meta, Args}.
|
||||
|
||||
increment(Number) when is_number(Number) ->
|
||||
Number + 1;
|
||||
increment(Other) ->
|
||||
{{'.', [], [erlang, '+']}, [], [Other, 1]}.
|
||||
@@ -432,6 +432,23 @@ defmodule GenEventTest do
|
||||
Logger.add_backend(:console, flush: true)
|
||||
end
|
||||
|
||||
test "add_process_handler/2 with GenEvent" do
|
||||
{:ok, snd} = GenEvent.start_link()
|
||||
GenEvent.add_handler(snd, ReplyHandler, {self(), false})
|
||||
|
||||
{:ok, fst} = GenEvent.start_link()
|
||||
:gen.call(fst, self(), {:add_process_handler, snd, snd})
|
||||
|
||||
assert GenEvent.notify(fst, :hello) == :ok
|
||||
assert_receive {:event, :hello}
|
||||
|
||||
assert GenEvent.ack_notify(fst, :hello) == :ok
|
||||
assert_receive {:event, :hello}
|
||||
|
||||
assert GenEvent.sync_notify(fst, :hello) == :ok
|
||||
assert_received {:event, :hello}
|
||||
end
|
||||
|
||||
test ":sys.get_status/2" do
|
||||
{:ok, pid} = GenEvent.start(name: :my_gen_event_name)
|
||||
:ok = GenEvent.add_handler(pid, {ReplyHandler, :ok}, {self(), true})
|
||||
|
||||
@@ -67,6 +67,19 @@ defmodule StreamTest do
|
||||
assert Enum.zip(list, list) == Enum.zip(stream, stream)
|
||||
end
|
||||
|
||||
test "chunk/4 is haltable" do
|
||||
assert 1..10 |> Stream.take(6) |> Stream.chunk(4, 4, [7, 8]) |> Enum.to_list ==
|
||||
[[1, 2, 3, 4], [5, 6, 7, 8]]
|
||||
assert 1..10 |> Stream.take(6) |> Stream.chunk(4, 4, [7, 8]) |> Stream.take(3) |> Enum.to_list ==
|
||||
[[1, 2, 3, 4], [5, 6, 7, 8]]
|
||||
assert 1..10 |> Stream.take(6) |> Stream.chunk(4, 4, [7, 8]) |> Stream.take(2) |> Enum.to_list ==
|
||||
[[1, 2, 3, 4], [5, 6, 7, 8]]
|
||||
assert 1..10 |> Stream.take(6) |> Stream.chunk(4, 4, [7, 8]) |> Stream.take(1) |> Enum.to_list ==
|
||||
[[1, 2, 3, 4]]
|
||||
assert 1..6 |> Stream.take(6) |> Stream.chunk(4, 4, [7, 8]) |> Enum.to_list ==
|
||||
[[1, 2, 3, 4], [5, 6, 7, 8]]
|
||||
end
|
||||
|
||||
test "chunk_by/2" do
|
||||
stream = Stream.chunk_by([1, 2, 2, 3, 4, 4, 6, 7, 7], &(rem(&1, 2) == 1))
|
||||
|
||||
|
||||
@@ -65,6 +65,16 @@ defmodule SystemTest do
|
||||
opts = [into: [], cd: System.cwd!, env: %{"foo" => "bar"},
|
||||
arg0: "hecho", stderr_to_stdout: true, parallelism: true]
|
||||
assert {["hello\n"], 0} = System.cmd "echo", ["hello"], opts
|
||||
|
||||
with_tmp_dir(fn dir ->
|
||||
new_path = Path.join([dir, "echo2"])
|
||||
File.cp!(System.find_executable("echo"), new_path)
|
||||
assert :enoent = catch_error(System.cmd(new_path, ["hello"]))
|
||||
|
||||
File.cd!(dir)
|
||||
assert :enoent = catch_error(System.cmd("echo2", ["hello"]))
|
||||
assert {"hello\n", 0} = System.cmd(Path.join([System.cwd, "echo2"]), ["hello"])
|
||||
end)
|
||||
end
|
||||
|
||||
test "find_executable/1" do
|
||||
@@ -72,4 +82,17 @@ defmodule SystemTest do
|
||||
assert is_binary System.find_executable("erl")
|
||||
assert !System.find_executable("does-not-really-exist-from-elixir")
|
||||
end
|
||||
|
||||
defp with_tmp_dir(function) do
|
||||
dir = tmp_dir_name()
|
||||
tmp_dir = System.tmp_dir!
|
||||
tmp_path = Path.join [tmp_dir, dir]
|
||||
File.rm_rf! tmp_path
|
||||
File.mkdir_p! tmp_path
|
||||
File.cd! tmp_dir, fn -> function.(dir) end
|
||||
end
|
||||
|
||||
defp tmp_dir_name do
|
||||
:crypto.rand_bytes(4) |> Base.encode16
|
||||
end
|
||||
end
|
||||
|
||||
@@ -220,9 +220,17 @@ defmodule ExUnit.Runner do
|
||||
%{test | state: {:failed, {{:EXIT, test_pid}, error, []}}}
|
||||
after
|
||||
timeout ->
|
||||
stacktrace =
|
||||
try do
|
||||
Process.info(test_pid, :current_stacktrace)
|
||||
catch
|
||||
_, _ -> []
|
||||
else
|
||||
{:current_stacktrace, stacktrace} -> stacktrace
|
||||
end
|
||||
Process.exit(test_pid, :kill)
|
||||
Process.demonitor(test_ref, [:flush])
|
||||
%{test | state: {:failed, {:error, %ExUnit.TimeoutError{timeout: timeout}, []}}}
|
||||
%{test | state: {:failed, {:error, %ExUnit.TimeoutError{timeout: timeout}, stacktrace}}}
|
||||
end
|
||||
|
||||
exec_on_exit(test, test_pid)
|
||||
|
||||
@@ -50,8 +50,9 @@ defmodule ExUnitTest do
|
||||
end
|
||||
end
|
||||
|
||||
assert capture_io(fn -> ExUnit.run end) =~
|
||||
"** (ExUnit.TimeoutError) test timed out after 10ms"
|
||||
output = capture_io(fn -> ExUnit.run end)
|
||||
assert output =~ "** (ExUnit.TimeoutError) test timed out after 10ms"
|
||||
assert output =~ ~r"\(stdlib\) timer\.erl:\d+: :timer\.sleep/1"
|
||||
end
|
||||
|
||||
test "filtering cases with tags" do
|
||||
|
||||
@@ -42,7 +42,7 @@ defmodule IEx.Helpers do
|
||||
h(Enum.reverse/1)
|
||||
|
||||
To discover all available functions for a module, type the module name
|
||||
follow by a dot, then press tab to trigger autocomplete. For example:
|
||||
followed by a dot, then press tab to trigger autocomplete. For example:
|
||||
|
||||
Enum.
|
||||
|
||||
|
||||
@@ -13,12 +13,12 @@ defmodule Mix.Compilers.Elixir do
|
||||
in between modules, which helps it recompile only the modules that
|
||||
have changed at runtime.
|
||||
"""
|
||||
def compile(manifest, srcs, exts, dest, force, on_start) do
|
||||
all = Mix.Utils.extract_files(srcs, exts)
|
||||
all_entries = read_manifest(manifest)
|
||||
def compile(manifest, srcs, skip, exts, dest, force, on_start) do
|
||||
all = Mix.Utils.extract_files(srcs -- skip, exts)
|
||||
{all_entries, skip_entries} = read_manifest(manifest, skip)
|
||||
|
||||
removed =
|
||||
for {_b, _m, source, _d, _f} <- all_entries, not(source in all), do: source
|
||||
for {_b, _m, source, _d, _f, _bin} <- all_entries, not(source in all), do: source
|
||||
|
||||
changed =
|
||||
if force do
|
||||
@@ -32,23 +32,24 @@ defmodule Mix.Compilers.Elixir do
|
||||
# Otherwise let's start with the new ones
|
||||
# plus the ones that have changed
|
||||
for(source <- all,
|
||||
not Enum.any?(all_entries, fn {_b, _m, s, _d, _f} -> s == source end),
|
||||
not Enum.any?(all_entries, fn {_b, _m, s, _d, _f, _bin} -> s == source end),
|
||||
do: source)
|
||||
++
|
||||
for({_b, _m, source, _d, files} <- all_entries,
|
||||
for({_b, _m, source, _d, files, _bin} <- all_entries,
|
||||
times = Enum.map([source|files], &HashDict.fetch!(all_mtimes, &1)),
|
||||
Mix.Utils.stale?(times, [modified]),
|
||||
do: source)
|
||||
end
|
||||
|
||||
{entries, changed} = remove_stale_entries(all_entries, removed ++ changed, [], [])
|
||||
{entries, changed} = remove_stale_entries(all_entries, removed ++ changed)
|
||||
stale = changed -- removed
|
||||
|
||||
cond do
|
||||
stale != [] ->
|
||||
do_compile(manifest, entries, stale, dest, on_start)
|
||||
compile_manifest(manifest, entries ++ skip_entries, stale, dest, on_start)
|
||||
:ok
|
||||
removed != [] ->
|
||||
write_manifest(manifest, entries ++ skip_entries)
|
||||
:ok
|
||||
true ->
|
||||
:noop
|
||||
@@ -56,7 +57,7 @@ defmodule Mix.Compilers.Elixir do
|
||||
end
|
||||
|
||||
defp mtimes(entries) do
|
||||
Enum.reduce(entries, HashDict.new, fn {_b, _m, source, _d, files}, dict ->
|
||||
Enum.reduce(entries, HashDict.new, fn {_b, _m, source, _d, files, _bin}, dict ->
|
||||
Enum.reduce([source|files], dict, fn file, dict ->
|
||||
if HashDict.has_key?(dict, file) do
|
||||
dict
|
||||
@@ -82,16 +83,14 @@ defmodule Mix.Compilers.Elixir do
|
||||
end
|
||||
end
|
||||
|
||||
defp do_compile(manifest, entries, stale, dest, on_start) do
|
||||
defp compile_manifest(manifest, entries, stale, dest, on_start) do
|
||||
Mix.Project.build_structure
|
||||
on_start.()
|
||||
cwd = File.cwd!
|
||||
|
||||
# Starts a server responsible for keeping track which files
|
||||
# were compiled and the dependencies in between them.
|
||||
{:ok, pid} = Agent.start_link(fn ->
|
||||
Enum.map(entries, &Tuple.insert_at(&1, 5, nil))
|
||||
end)
|
||||
{:ok, pid} = Agent.start_link(fn -> entries end)
|
||||
|
||||
try do
|
||||
_ = Kernel.ParallelCompiler.files :lists.usort(stale),
|
||||
@@ -103,7 +102,7 @@ defmodule Mix.Compilers.Elixir do
|
||||
entries
|
||||
end
|
||||
after
|
||||
Agent.stop pid
|
||||
Agent.stop(pid, :infinity)
|
||||
end
|
||||
|
||||
:ok
|
||||
@@ -145,7 +144,7 @@ defmodule Mix.Compilers.Elixir do
|
||||
|
||||
# This function receives the manifest entries and some source
|
||||
# files that have changed. It then, recursively, figures out
|
||||
# all the files that changed (thanks to the dependencies) and
|
||||
# all the files that changed (via the module dependencies) and
|
||||
# return their sources as the remaining entries.
|
||||
defp remove_stale_entries(all, []) do
|
||||
{all, []}
|
||||
@@ -155,9 +154,12 @@ defmodule Mix.Compilers.Elixir do
|
||||
remove_stale_entries(all, :lists.usort(changed), [], [])
|
||||
end
|
||||
|
||||
defp remove_stale_entries([{beam, module, source, _d, _f} = entry|t], changed, removed, acc) do
|
||||
defp remove_stale_entries([{beam, module, source, _d, _f, _bin} = entry|t], changed, removed, acc) do
|
||||
if source in changed do
|
||||
atom = String.to_atom(module)
|
||||
_ = File.rm(beam)
|
||||
_ = :code.purge(atom)
|
||||
_ = :code.delete(atom)
|
||||
remove_stale_entries(t, changed, [module|removed], acc)
|
||||
else
|
||||
remove_stale_entries(t, changed, removed, [entry|acc])
|
||||
@@ -167,7 +169,7 @@ defmodule Mix.Compilers.Elixir do
|
||||
defp remove_stale_entries([], changed, removed, acc) do
|
||||
# If any of the dependencies for the remaining entries
|
||||
# were removed, get its source so we can remove them.
|
||||
next_changed = for {_b, _m, source, deps, _f} <- acc,
|
||||
next_changed = for {_b, _m, source, deps, _f, _bin} <- acc,
|
||||
Enum.any?(deps, &(&1 in removed)),
|
||||
do: source
|
||||
|
||||
@@ -179,27 +181,40 @@ defmodule Mix.Compilers.Elixir do
|
||||
|
||||
# Reads the manifest returning the results as tuples.
|
||||
# The beam files are read, removed and stored in memory.
|
||||
defp read_manifest(manifest) do
|
||||
defp read_manifest(manifest, skip_paths) do
|
||||
initial = {[], []}
|
||||
|
||||
case File.read(manifest) do
|
||||
{:ok, contents} ->
|
||||
Enum.reduce String.split(contents, "\n"), [], fn x, acc ->
|
||||
case String.split(x, "\t") do
|
||||
[beam, module, source|deps] ->
|
||||
{deps, files} =
|
||||
case Enum.split_while(deps, &(&1 != "Elixir")) do
|
||||
{deps, ["Elixir"|files]} -> {deps, files}
|
||||
{deps, _} -> {deps, []}
|
||||
end
|
||||
[{beam, module, source, deps, files}|acc]
|
||||
_ ->
|
||||
acc
|
||||
end
|
||||
skip_paths = Enum.map(skip_paths, &(&1 <> "/"))
|
||||
Enum.reduce String.split(contents, "\n"), initial, fn x, acc ->
|
||||
read_manifest_entry(String.split(x, "\t"), acc, skip_paths)
|
||||
end
|
||||
{:error, _} ->
|
||||
[]
|
||||
initial
|
||||
end
|
||||
end
|
||||
|
||||
defp read_manifest_entry([beam, module, source|deps], {keep, skip}, skip_paths) do
|
||||
{deps, files} =
|
||||
case Enum.split_while(deps, &(&1 != "Elixir")) do
|
||||
{deps, ["Elixir"|files]} -> {deps, files}
|
||||
{deps, _} -> {deps, []}
|
||||
end
|
||||
|
||||
entry = {beam, module, source, deps, files, nil}
|
||||
|
||||
if String.starts_with?(source, skip_paths) do
|
||||
{keep, [entry|skip]}
|
||||
else
|
||||
{[entry|keep], skip}
|
||||
end
|
||||
end
|
||||
|
||||
defp read_manifest_entry(_, acc, _skip_paths) do
|
||||
acc
|
||||
end
|
||||
|
||||
# Writes the manifest separating entries by tabs.
|
||||
defp write_manifest(_manifest, []) do
|
||||
:ok
|
||||
@@ -213,6 +228,11 @@ defmodule Mix.Compilers.Elixir do
|
||||
[beam, module, source | tail] |> Enum.join("\t")
|
||||
end)
|
||||
|
||||
# The Mix.Dep.Lock keeps all the project dependencies. Since Elixir
|
||||
# is a dependency itself, we need to touch the lock so the current
|
||||
# Elixir version, used to compile the files above, is properly stored.
|
||||
Mix.Dep.Lock.touch
|
||||
|
||||
File.mkdir_p!(Path.dirname(manifest))
|
||||
File.write!(manifest, Enum.join(lines, "\n"))
|
||||
end
|
||||
|
||||
+14
-30
@@ -40,6 +40,8 @@ defmodule Mix.Config do
|
||||
@doc """
|
||||
Configures the given application.
|
||||
|
||||
Keyword lists are always deep merged.
|
||||
|
||||
## Examples
|
||||
|
||||
The given `opts` are merged into the existing configuration
|
||||
@@ -69,6 +71,8 @@ defmodule Mix.Config do
|
||||
@doc """
|
||||
Configures the given key for the given application.
|
||||
|
||||
Keyword lists are always deep merged.
|
||||
|
||||
## Examples
|
||||
|
||||
The given `opts` are merged into the existing values for `key`
|
||||
@@ -91,8 +95,7 @@ defmodule Mix.Config do
|
||||
quote do
|
||||
var!(config, Mix.Config) =
|
||||
Mix.Config.merge(var!(config, Mix.Config),
|
||||
[{unquote(app), [{unquote(key), unquote(opts)}]}],
|
||||
fn _app, _key, v1, v2 -> Keyword.merge(v1, v2) end)
|
||||
[{unquote(app), [{unquote(key), unquote(opts)}]}])
|
||||
end
|
||||
end
|
||||
|
||||
@@ -116,7 +119,7 @@ defmodule Mix.Config do
|
||||
defmacro import_config(file) do
|
||||
quote do
|
||||
var!(config, Mix.Config) =
|
||||
Mix.Config.read_wildcard!(Path.expand(unquote(file), __DIR__), var!(config, Mix.Config))
|
||||
Mix.Config.read_wildcard!(var!(config, Mix.Config), Path.expand(unquote(file), __DIR__))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -142,7 +145,7 @@ defmodule Mix.Config do
|
||||
@doc """
|
||||
Reads many configuration files given by wildcard into a single config.
|
||||
"""
|
||||
def read_wildcard!(path, config) do
|
||||
def read_wildcard!(config, path) do
|
||||
paths = case Path.wildcard(path) do
|
||||
[] -> [path]
|
||||
o -> o
|
||||
@@ -201,34 +204,15 @@ defmodule Mix.Config do
|
||||
"""
|
||||
def merge(config1, config2) do
|
||||
Keyword.merge(config1, config2, fn _, app1, app2 ->
|
||||
Keyword.merge(app1, app2)
|
||||
Keyword.merge(app1, app2, &deep_merge/3)
|
||||
end)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Merges two configurations.
|
||||
|
||||
The configuration of each application is merged together
|
||||
and a callback is invoked in case of conflicts receiving
|
||||
the app, the conflicting key and both values. It must return
|
||||
a value that will be used as part of the conflict resolution.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> Mix.Config.merge([app: [k: :v1]], [app: [k: :v2]],
|
||||
...> fn app, k, v1, v2 -> {app, k, v1, v2} end)
|
||||
[app: [k: {:app, :k, :v1, :v2}]]
|
||||
|
||||
"""
|
||||
def merge(config1, config2, callback) do
|
||||
Keyword.merge(config1, config2, fn app, app1, app2 ->
|
||||
Keyword.merge(app1, app2, fn k, v1, v2 ->
|
||||
if v1 == v2 do
|
||||
v1
|
||||
else
|
||||
callback.(app, k, v1, v2)
|
||||
end
|
||||
end)
|
||||
end)
|
||||
defp deep_merge(_key, value1, value2) do
|
||||
if Keyword.keyword?(value1) and Keyword.keyword?(value2) do
|
||||
Keyword.merge(value1, value2, &deep_merge/3)
|
||||
else
|
||||
value2
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -17,7 +17,7 @@ defmodule Mix.Generator do
|
||||
def create_file(path, contents, opts \\ []) when is_binary(path) do
|
||||
Mix.shell.info [:green, "* creating ", :reset, Path.relative_to_cwd(path)]
|
||||
|
||||
if opts[:force] || overwriting?(path) do
|
||||
if opts[:force] || Mix.Utils.overwriting?(path) do
|
||||
File.mkdir_p!(Path.dirname(path))
|
||||
File.write!(path, contents)
|
||||
end
|
||||
@@ -31,15 +31,6 @@ defmodule Mix.Generator do
|
||||
File.mkdir_p! path
|
||||
end
|
||||
|
||||
defp overwriting?(path) do
|
||||
if File.exists?(path) do
|
||||
full = Path.expand(path)
|
||||
Mix.shell.yes?(Path.relative_to_cwd(full) <> " already exists, overwrite?")
|
||||
else
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
@doc false
|
||||
defmacro from_file(path) do
|
||||
quote do
|
||||
|
||||
@@ -72,5 +72,8 @@ defmodule Mix.Tasks.Archive.Build do
|
||||
end
|
||||
|
||||
Mix.Archive.create(source, target)
|
||||
|
||||
Mix.shell.info "Generated archive #{target} with MIX_ENV=#{Mix.env}"
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
@@ -54,13 +54,12 @@ defmodule Mix.Tasks.Archive.Install do
|
||||
if opts[:force] || should_install?(src, previous) do
|
||||
remove_previous_versions(previous)
|
||||
|
||||
dest = Mix.Local.archives_path()
|
||||
archive = Path.join(dest, basename(src))
|
||||
archive = Path.join(Mix.Local.archives_path(), basename(src))
|
||||
check_file_exists(archive)
|
||||
|
||||
File.mkdir_p!(dest)
|
||||
File.write!(archive, Mix.Utils.read_path!(src, opts))
|
||||
Mix.shell.info [:green, "* creating ", :reset, Path.relative_to_cwd(archive)]
|
||||
if Mix.Utils.copy_path!(src, archive, opts) do
|
||||
Mix.shell.info [:green, "* creating ", :reset, Path.relative_to_cwd(archive)]
|
||||
end
|
||||
|
||||
true = Code.append_path(Mix.Archive.ebin(archive))
|
||||
else
|
||||
@@ -80,7 +79,7 @@ defmodule Mix.Tasks.Archive.Install do
|
||||
defp should_install?(_src, previous_files) do
|
||||
files = Enum.map_join(previous_files, ", ", &Path.basename/1)
|
||||
|
||||
Mix.shell.yes?("Found existing archives: #{files}.\n" <>
|
||||
Mix.shell.yes?("Found existing archive(s): #{files}.\n" <>
|
||||
"Are you sure you want to replace them?")
|
||||
end
|
||||
|
||||
|
||||
@@ -22,8 +22,8 @@ defmodule Mix.Tasks.Compile.Elixir do
|
||||
* `--debug-info` (`--no-debug-info`) - attach (or not) debug info to compiled modules
|
||||
* `--ignore-module-conflict` - do not emit warnings if a module was previously defined
|
||||
* `--warnings-as-errors` - treat warnings as errors and return a non-zero exit code
|
||||
* `--elixirc-paths` - paths to lookup for Elixir source.
|
||||
Can be given multiple times and, once given, overrides the project elixirc_paths config.
|
||||
* `--elixirc-paths` - restrict the original elixirc paths to
|
||||
a subset of the ones specified. Can be given multiple times.
|
||||
|
||||
## Configuration
|
||||
|
||||
@@ -51,10 +51,12 @@ defmodule Mix.Tasks.Compile.Elixir do
|
||||
|
||||
project = Mix.Project.config
|
||||
dest = Mix.Project.compile_path(project)
|
||||
srcs = case Keyword.get_values(opts, :elixirc_paths) do
|
||||
[] -> project[:elixirc_paths]
|
||||
ep -> ep
|
||||
end
|
||||
srcs = project[:elixirc_paths]
|
||||
skip =
|
||||
case Keyword.get_values(opts, :elixirc_paths) do
|
||||
[] -> []
|
||||
ep -> srcs -- ep
|
||||
end
|
||||
|
||||
manifest = manifest()
|
||||
configs = Mix.Project.config_files ++ Mix.Tasks.Compile.Erlang.manifests
|
||||
@@ -62,16 +64,10 @@ defmodule Mix.Tasks.Compile.Elixir do
|
||||
force = opts[:force] || local_deps_changed?(manifest)
|
||||
|| Mix.Utils.stale?(configs, [manifest])
|
||||
|
||||
result = Mix.Compilers.Elixir.compile(manifest, srcs, [:ex], dest, force, fn ->
|
||||
Mix.Compilers.Elixir.compile(manifest, srcs, skip, [:ex], dest, force, fn ->
|
||||
true = Code.prepend_path(dest)
|
||||
set_compiler_opts(project, opts, [])
|
||||
end)
|
||||
|
||||
# The Mix.Dep.Lock keeps all the project dependencies. Since Elixir
|
||||
# is a dependency itself, we need to touch the lock so the current
|
||||
# Elixir version, used to compile the files above, is properly stored.
|
||||
unless result == :noop, do: Mix.Dep.Lock.touch
|
||||
result
|
||||
end
|
||||
|
||||
@doc """
|
||||
|
||||
@@ -26,11 +26,10 @@ defmodule Mix.Tasks.Deps.Compile do
|
||||
|
||||
import Mix.Dep, only: [loaded: 1, available?: 1, loaded_by_name: 2,
|
||||
format_dep: 1, make?: 1, mix?: 1, rebar?: 1]
|
||||
|
||||
|
||||
@spec run(OptionParser.argv) :: :ok
|
||||
def run(args) do
|
||||
Mix.Project.get!
|
||||
Mix.Task.run "deps.loadpaths"
|
||||
|
||||
case OptionParser.parse(args) do
|
||||
{_, [], _} ->
|
||||
@@ -45,6 +44,8 @@ defmodule Mix.Tasks.Deps.Compile do
|
||||
shell = Mix.shell
|
||||
config = Mix.Project.deps_config
|
||||
|
||||
Mix.Task.run "deps.loadpaths"
|
||||
|
||||
compiled =
|
||||
Enum.map(deps, fn %Mix.Dep{app: app, status: status, opts: opts, scm: scm} = dep ->
|
||||
check_unavailable!(app, status)
|
||||
|
||||
@@ -2,7 +2,7 @@ defmodule Mix.Tasks.Local.Hex do
|
||||
use Mix.Task
|
||||
|
||||
@hex_url "https://hex.pm/installs/hex.ez"
|
||||
@hex_requirement ">= 0.4.3"
|
||||
@hex_requirement ">= 0.5.0"
|
||||
|
||||
@shortdoc "Install hex locally"
|
||||
|
||||
@@ -18,7 +18,8 @@ defmodule Mix.Tasks.Local.Hex do
|
||||
"""
|
||||
@spec run(OptionParser.argv) :: boolean
|
||||
def run(args) do
|
||||
Mix.Tasks.Archive.Install.run [@hex_url, "--shell" | args]
|
||||
url = @hex_url <> "?elixir=" <> System.version
|
||||
Mix.Tasks.Archive.Install.run [url, "--shell" | args]
|
||||
end
|
||||
|
||||
@doc false
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
defmodule Mix.Tasks.Local.Rebar do
|
||||
use Mix.Task
|
||||
|
||||
import Mix.Generator, only: [create_file: 3]
|
||||
|
||||
@rebar_url "http://s3.hex.pm/rebar"
|
||||
@shortdoc "Install rebar locally"
|
||||
|
||||
@@ -31,11 +29,13 @@ defmodule Mix.Tasks.Local.Rebar do
|
||||
end
|
||||
|
||||
defp do_install(path, opts) do
|
||||
rebar = Mix.Utils.read_path!(path)
|
||||
local_rebar_path = Mix.Rebar.local_rebar_path
|
||||
File.mkdir_p! Path.dirname(local_rebar_path)
|
||||
create_file local_rebar_path, rebar, opts
|
||||
:ok = :file.change_mode local_rebar_path, 0o755
|
||||
|
||||
if Mix.Utils.copy_path!(path, local_rebar_path, opts) do
|
||||
:ok = :file.change_mode local_rebar_path, 0o755
|
||||
Mix.shell.info [:green, "* creating ", :reset, Path.relative_to_cwd(local_rebar_path)]
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
@@ -68,7 +68,8 @@ defmodule Mix.Tasks.New do
|
||||
end
|
||||
|
||||
defp do_generate(app, mod, path, opts) do
|
||||
assigns = [app: app, mod: mod, otp_app: otp_app(mod, !!opts[:sup])]
|
||||
assigns = [app: app, mod: mod, otp_app: otp_app(mod, !!opts[:sup]),
|
||||
version: get_version(System.version)]
|
||||
|
||||
create_file "README.md", readme_template(assigns)
|
||||
create_file ".gitignore", gitignore_text
|
||||
@@ -162,6 +163,15 @@ defmodule Mix.Tasks.New do
|
||||
end
|
||||
end
|
||||
|
||||
defp get_version(version) do
|
||||
{:ok, version} = Version.parse(version)
|
||||
"#{version.major}.#{version.minor}" <>
|
||||
case version.pre do
|
||||
[h|_] -> "-#{h}"
|
||||
[] -> ""
|
||||
end
|
||||
end
|
||||
|
||||
defp in_umbrella? do
|
||||
apps = Path.dirname(File.cwd!)
|
||||
|
||||
@@ -196,7 +206,7 @@ defmodule Mix.Tasks.New do
|
||||
def project do
|
||||
[app: :<%= @app %>,
|
||||
version: "0.0.1",
|
||||
elixir: "~> <%= System.version %>",
|
||||
elixir: "~> <%= @version %>",
|
||||
deps: deps]
|
||||
end
|
||||
|
||||
@@ -231,7 +241,7 @@ defmodule Mix.Tasks.New do
|
||||
version: "0.0.1",
|
||||
deps_path: "../../deps",
|
||||
lockfile: "../../mix.lock",
|
||||
elixir: "~> <%= System.version %>",
|
||||
elixir: "~> <%= @version %>",
|
||||
deps: deps]
|
||||
end
|
||||
|
||||
|
||||
+97
-22
@@ -354,10 +354,8 @@ defmodule Mix.Utils do
|
||||
end
|
||||
|
||||
@doc """
|
||||
Opens and reads content from either a URL or a local filesystem path.
|
||||
|
||||
Used by tasks like `archive.install` and `local.rebar` that support
|
||||
installation either from a URL or a local file.
|
||||
Opens and reads content from either a URL or a local filesystem path
|
||||
and returns the contents as a binary.
|
||||
|
||||
Raises if the given path is not a URL, nor a file or if the
|
||||
file or URL are invalid.
|
||||
@@ -370,9 +368,9 @@ defmodule Mix.Utils do
|
||||
def read_path!(path, opts \\ []) do
|
||||
cond do
|
||||
url?(path) && opts[:shell] ->
|
||||
read_shell(path)
|
||||
read_shell(path, nil)
|
||||
url?(path) ->
|
||||
read_httpc(path)
|
||||
read_httpc(path, nil)
|
||||
file?(path) ->
|
||||
read_file(path)
|
||||
true ->
|
||||
@@ -380,11 +378,65 @@ defmodule Mix.Utils do
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Copies content from either a URL or a local filesystem path to
|
||||
target path.
|
||||
|
||||
Used by tasks like `archive.install` and `local.rebar` that support
|
||||
installation either from a URL or a local file.
|
||||
|
||||
Raises if the given path is not a URL, nor a file or if the
|
||||
file or URL are invalid.
|
||||
|
||||
## Options
|
||||
|
||||
* `:shell` - Forces the use of `wget` or `curl` to fetch the file if the
|
||||
given path is a URL.
|
||||
|
||||
* `:force` - Forces overwriting target file without a shell prompt.
|
||||
"""
|
||||
def copy_path!(source, target, opts \\ []) when is_binary(source) and is_binary(target) do
|
||||
if opts[:force] || overwriting?(target) do
|
||||
cond do
|
||||
url?(source) && opts[:shell] ->
|
||||
read_shell(source, target)
|
||||
url?(source) ->
|
||||
read_httpc(source, target)
|
||||
file?(source) ->
|
||||
copy_file(source, target)
|
||||
true ->
|
||||
Mix.raise "Expected #{source} to be a url or a local file path"
|
||||
end
|
||||
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Prompts the user to overwrite the file if it exists. Returns
|
||||
the user input.
|
||||
"""
|
||||
def overwriting?(path) do
|
||||
if File.exists?(path) do
|
||||
full = Path.expand(path)
|
||||
Mix.shell.yes?(Path.relative_to_cwd(full) <> " already exists, overwrite?")
|
||||
else
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
defp read_file(path) do
|
||||
File.read!(path)
|
||||
end
|
||||
|
||||
defp read_httpc(path) do
|
||||
defp copy_file(source, target) do
|
||||
File.mkdir_p!(Path.dirname(target))
|
||||
File.cp!(source, target)
|
||||
end
|
||||
|
||||
defp read_httpc(path, target) do
|
||||
{:ok, _} = Application.ensure_all_started(:ssl)
|
||||
{:ok, _} = Application.ensure_all_started(:inets)
|
||||
|
||||
@@ -401,11 +453,21 @@ defmodule Mix.Utils do
|
||||
if http_proxy, do: proxy(http_proxy)
|
||||
if https_proxy, do: proxy(https_proxy)
|
||||
|
||||
# We are using relaxed: true because some clients is returning a Location
|
||||
if target do
|
||||
File.mkdir_p!(Path.dirname(target))
|
||||
File.rm(target)
|
||||
req_opts = [stream: String.to_char_list(target)]
|
||||
else
|
||||
req_opts = [body_format: :binary]
|
||||
end
|
||||
|
||||
# We are using relaxed: true because some servers is returning a Location
|
||||
# header with relative paths, which does not follow the spec. This would
|
||||
# cause the request to fail with {:error, :no_scheme} unless :relaxed
|
||||
# is given.
|
||||
case :httpc.request(:get, request, [relaxed: true], [body_format: :binary], :mix) do
|
||||
case :httpc.request(:get, request, [relaxed: true], req_opts, :mix) do
|
||||
{:ok, :saved_to_file} ->
|
||||
:ok
|
||||
{:ok, {{_, status, _}, _, body}} when status in 200..299 ->
|
||||
body
|
||||
{:ok, {{_, status, _}, _, _}} ->
|
||||
@@ -418,9 +480,12 @@ defmodule Mix.Utils do
|
||||
end
|
||||
|
||||
defp proxy(proxy) do
|
||||
uri = URI.parse(proxy)
|
||||
:ok = :httpc.set_options([{proxy_scheme(uri.scheme),
|
||||
{{String.to_char_list(uri.host), uri.port}, []}}], :mix)
|
||||
uri = URI.parse(proxy)
|
||||
|
||||
if uri.host && uri.port do
|
||||
host = String.to_char_list(uri.host)
|
||||
:httpc.set_options([{proxy_scheme(uri.scheme), {{host, uri.port}, []}}], :hex)
|
||||
end
|
||||
end
|
||||
|
||||
defp proxy_scheme(scheme) do
|
||||
@@ -430,34 +495,44 @@ defmodule Mix.Utils do
|
||||
end
|
||||
end
|
||||
|
||||
defp read_shell(path) do
|
||||
defp read_shell(path, target) do
|
||||
filename = URI.parse(path).path |> Path.basename
|
||||
out_path = Path.join(System.tmp_dir!, filename)
|
||||
out_path = target || Path.join(System.tmp_dir!, filename)
|
||||
|
||||
File.mkdir_p!(Path.dirname(out_path))
|
||||
File.rm(out_path)
|
||||
|
||||
status = cond do
|
||||
System.find_executable("wget") ->
|
||||
Mix.shell.cmd(~s(wget -nv -O "#{out_path}" "#{path}"))
|
||||
System.find_executable("curl") ->
|
||||
Mix.shell.cmd(~s(curl -s -S -L -o "#{out_path}" "#{path}"))
|
||||
windows? && System.find_executable("powershell") ->
|
||||
command = ~s[$ErrorActionPreference = 'Stop'; ] <>
|
||||
~s[$client = new-object System.Net.WebClient; ] <>
|
||||
~s[$client.DownloadFile(\\"#{path}\\", \\"#{out_path}\\")]
|
||||
Mix.shell.cmd(~s[powershell -Command "& {#{command}}"])
|
||||
System.find_executable("wget") ->
|
||||
Mix.shell.cmd(~s(wget -nv -O "#{out_path}" "#{path}"))
|
||||
System.find_executable("curl") ->
|
||||
Mix.shell.cmd(~s(curl -s -S -L -o "#{out_path}" "#{path}"))
|
||||
windows? ->
|
||||
Mix.shell.error "powershell, wget or curl not installed"
|
||||
true ->
|
||||
Mix.shell.error "wget or curl not installed"
|
||||
1
|
||||
end
|
||||
|
||||
check_command!(status, path, out_path)
|
||||
check_command!(status, path, target)
|
||||
|
||||
data = File.read!(out_path)
|
||||
File.rm!(out_path)
|
||||
data
|
||||
unless target do
|
||||
data = File.read!(out_path)
|
||||
File.rm!(out_path)
|
||||
data
|
||||
end
|
||||
end
|
||||
|
||||
defp check_command!(0, _path, _out_path), do: :ok
|
||||
defp check_command!(_status, path, nil) do
|
||||
Mix.raise "Could not fetch data, please download manually from " <>
|
||||
"#{inspect path}"
|
||||
end
|
||||
defp check_command!(_status, path, out_path) do
|
||||
Mix.raise "Could not fetch data, please download manually from " <>
|
||||
"#{inspect path} and copy it to #{inspect out_path}"
|
||||
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
use Mix.Config
|
||||
config :app, Repo, key: [nested: true]
|
||||
@@ -13,7 +13,7 @@ defmodule Mix.ConfigTest do
|
||||
assert var!(config, Mix.Config) == [lager: [key: :value]]
|
||||
|
||||
config :lager, other: :value
|
||||
assert var!(config, Mix.Config) == [lager: [other: :value, key: :value]]
|
||||
assert var!(config, Mix.Config) == [lager: [key: :value, other: :value]]
|
||||
|
||||
config :lager, key: :other
|
||||
assert var!(config, Mix.Config) == [lager: [key: :other, other: :value]]
|
||||
@@ -26,10 +26,19 @@ defmodule Mix.ConfigTest do
|
||||
assert var!(config, Mix.Config) == [app: [{Repo, key: :value}]]
|
||||
|
||||
config :app, Repo, other: :value
|
||||
assert var!(config, Mix.Config) == [app: [{Repo, other: :value, key: :value}]]
|
||||
assert var!(config, Mix.Config) == [app: [{Repo, key: :value, other: :value}]]
|
||||
|
||||
config :app, Repo, key: :other
|
||||
assert var!(config, Mix.Config) == [app: [{Repo, [key: :other, other: :value]}]]
|
||||
assert var!(config, Mix.Config) == [app: [{Repo, key: :other, other: :value}]]
|
||||
|
||||
config :app, Repo, key: [nested: false]
|
||||
assert var!(config, Mix.Config) == [app: [{Repo, key: [nested: false], other: :value}]]
|
||||
|
||||
config :app, Repo, key: [nested: true]
|
||||
assert var!(config, Mix.Config) == [app: [{Repo, key: [nested: true], other: :value}]]
|
||||
|
||||
config :app, Repo, key: :other
|
||||
assert var!(config, Mix.Config) == [app: [{Repo, key: :other, other: :value}]]
|
||||
end
|
||||
|
||||
test "import_config/1" do
|
||||
@@ -44,6 +53,14 @@ defmodule Mix.ConfigTest do
|
||||
assert var!(config, Mix.Config) == [my_app: [key: :value]]
|
||||
end
|
||||
|
||||
test "import_config/1 with nested" do
|
||||
use Mix.Config
|
||||
config :app, Repo, key: [nested: false, other: true]
|
||||
|
||||
import_config fixture_path("configs/nested.exs")
|
||||
assert var!(config, Mix.Config) == [app: [{Repo, key: [nested: true, other: true]}]]
|
||||
end
|
||||
|
||||
test "import_config/1 with bad path" do
|
||||
use Mix.Config
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ defmodule Mix.Tasks.ArchiveTest do
|
||||
# Install it!
|
||||
Mix.Tasks.Archive.Build.run ["--no-elixir-version-check"]
|
||||
assert File.regular? "archive-0.1.0.ez"
|
||||
assert_received {:mix_shell, :info, ["Generated archive archive-0.1.0.ez with MIX_ENV=dev"]}
|
||||
|
||||
send self, {:mix_shell_input, :yes?, true}
|
||||
Mix.Tasks.Archive.Install.run []
|
||||
@@ -54,6 +55,7 @@ defmodule Mix.Tasks.ArchiveTest do
|
||||
Mix.Project.push(ArchiveProject2)
|
||||
Mix.Tasks.Archive.Build.run ["--no_compile"]
|
||||
assert File.regular? "archive-0.2.0.ez"
|
||||
assert_received {:mix_shell, :info, ["Generated archive archive-0.2.0.ez with MIX_ENV=dev"]}
|
||||
|
||||
send self, {:mix_shell_input, :yes?, true}
|
||||
Mix.Tasks.Archive.Install.run []
|
||||
|
||||
@@ -55,14 +55,18 @@ defmodule Mix.Tasks.Compile.ElixirTest do
|
||||
end
|
||||
end
|
||||
|
||||
test "removes old artifact files" do
|
||||
test "removes, purges and deletes old artifacts" do
|
||||
in_fixture "no_mixfile", fn ->
|
||||
assert Mix.Tasks.Compile.Elixir.run([]) == :ok
|
||||
assert File.regular?("_build/dev/lib/sample/ebin/Elixir.A.beam")
|
||||
assert Code.ensure_loaded?(A)
|
||||
|
||||
File.rm!("lib/a.ex")
|
||||
assert Mix.Tasks.Compile.Elixir.run([]) == :ok
|
||||
refute File.regular?("_build/dev/lib/sample/ebin/Elixir.A.beam")
|
||||
refute Code.ensure_loaded?(A)
|
||||
refute String.contains?(File.read!("_build/dev/lib/sample/.compile.elixir"),
|
||||
"Elixir.A.beam")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -169,7 +173,7 @@ defmodule Mix.Tasks.Compile.ElixirTest do
|
||||
|
||||
defmodule SourcePathsProject do
|
||||
def project do
|
||||
[app: :source_paths, elixirc_paths: ["unknown"]]
|
||||
[app: :source_paths, elixirc_paths: ["web", "lib"]]
|
||||
end
|
||||
end
|
||||
|
||||
@@ -178,11 +182,18 @@ defmodule Mix.Tasks.Compile.ElixirTest do
|
||||
|
||||
in_fixture "no_mixfile", fn ->
|
||||
# Nothing to compile with the custom source paths
|
||||
assert Mix.Tasks.Compile.Elixir.run([])
|
||||
assert Mix.Tasks.Compile.Elixir.run(["--elixirc-paths", "web"])
|
||||
refute_received {:mix_shell, :info, ["Compiled lib/a.ex"]}
|
||||
|
||||
assert Mix.Tasks.Compile.Elixir.run(["--elixirc-paths", "lib"])
|
||||
assert_received {:mix_shell, :info, ["Compiled lib/a.ex"]}
|
||||
|
||||
# Compiling just web does not remove lib artifacts
|
||||
assert Mix.Tasks.Compile.Elixir.run(["--elixirc-paths", "web"])
|
||||
refute_received {:mix_shell, :info, ["Compiled lib/a.ex"]}
|
||||
|
||||
assert Mix.Tasks.Compile.Elixir.run(["--elixirc-paths", "lib"])
|
||||
refute_received {:mix_shell, :info, ["Compiled lib/a.ex"]}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -58,8 +58,8 @@ defmodule MixTest.Case do
|
||||
|
||||
def purge(modules) do
|
||||
Enum.each modules, fn(m) ->
|
||||
:code.delete(m)
|
||||
:code.purge(m)
|
||||
:code.delete(m)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{application, elixir,
|
||||
[{description, "elixir"},
|
||||
{vsn, "1.0.0"},
|
||||
{vsn, "1.0.1"},
|
||||
{modules, [
|
||||
elixir
|
||||
]},
|
||||
|
||||
Reference in New Issue
Block a user