Compare commits

...
37 Commits
Author SHA1 Message Date
José Valim 89460154bb Release v1.0.1 2014-10-07 13:33:07 +02:00
José Valim b026e8877a --elixirc-paths should be a subset of project paths
Since the manifest is shared, --elixirc-paths must
be a subset as we need to know what to change and what
to not change in the manifest file.

Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2014-10-07 13:25:42 +02:00
José Valim 597381b2e0 Write to manifest when file is removed
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2014-10-06 22:10:01 +02:00
José Valim df70729b14 Test that we do purge and delete artifacts
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2014-10-06 17:26:26 +02:00
José Valim 61b116c1a5 Also purge and delete during tests
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2014-10-06 17:21:31 +02:00
José Valim d37a10bbd0 Purge and delete modules before compiling them
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2014-10-06 17:21:25 +02:00
José Valim 5d75c5c861 Refactor and add new rewrite rules
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2014-10-06 16:46:01 +02:00
Eric Meadows-Jönsson db4089cc71 Don't crash on malformed proxy env var
Signed-off-by: Eric Meadows-Jönsson <eric.meadows.jonsson@gmail.com>
2014-10-06 11:35:24 +02:00
José Valim f9f4f14edf Update CHANGELOG 2014-10-02 21:10:39 +02:00
Eric Meadows-Jönsson 58946ce879 Give higher prio to powershell on windows
Signed-off-by: Eric Meadows-Jönsson <eric.meadows.jonsson@gmail.com>
2014-10-01 18:32:23 +02:00
Eric Meadows-Jönsson de1d5ff793 Send elixir version as parameter when installing hex
Signed-off-by: Eric Meadows-Jönsson <eric.meadows.jonsson@gmail.com>
2014-09-29 21:22:07 +02:00
Eric Meadows-Jönsson a4be8fb260 Fix :stderr_to_stdout option
Signed-off-by: Eric Meadows-Jönsson <eric.meadows.jonsson@gmail.com>
2014-09-29 21:21:58 +02:00
Eric Meadows-Jönsson 031a27f5fa Fix spec for surround_many
Signed-off-by: Eric Meadows-Jönsson <eric.meadows.jonsson@gmail.com>
2014-09-29 21:21:50 +02:00
José Valim f3c1931cac Update CHANGELOG 2014-09-27 11:34:48 +02:00
José Valim 2e48d81c20 Move printing out of copy_path, always create dir
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2014-09-27 11:33:03 +02:00
José Valim e7ef3fc95d Merge pull request #2787 from elixir-lang/emj-hex-file-error
Report correct location if local.hex failed

Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2014-09-27 11:32:52 +02:00
José Valim 2d01aa1c6a Do not pass false into port command
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2014-09-27 11:08:04 +02:00
José Valim ae07124550 Merge pull request #2789 from alco/system-cmd-path-resolution
System.cmd path resolution

Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2014-09-27 11:07:28 +02:00
José Valim 1f744f4dac Also remove elixir_exp from stacktraces
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2014-09-25 10:37:10 +02:00
José Valim ad88ac642f Remove :elixir module from stacktraces
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2014-09-25 10:30:19 +02:00
Eric Meadows-Jönsson f7956e9d80 Bump Hex requirement
Signed-off-by: Eric Meadows-Jönsson <eric.meadows.jonsson@gmail.com>
2014-09-23 21:44:24 +02:00
Eric Meadows-Jönsson da6892aa96 Add MIX_ENV output to archive.build
Signed-off-by: Eric Meadows-Jönsson <eric.meadows.jonsson@gmail.com>
2014-09-22 23:32:07 +02:00
José Valim 7a3bd20804 Update CHANGELOG 2014-09-20 13:43:52 +02:00
José Valim f7015166dc Filter out :elixir_lexical from stacktraces
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2014-09-20 13:41:47 +02:00
José Valim c6f346a355 Change version requirement to be MAJOR.MINOR
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2014-09-20 13:41:41 +02:00
José Valim 911a3ba97f Update release instructions
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2014-09-19 18:26:40 +02:00
José Valim a1668a6b28 Ensure Mix.Config is deep merged
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2014-09-16 19:53:47 +02:00
José Valim a4cb74a83f Include stracktrace on ExUnit timeouts
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2014-09-16 19:53:43 +02:00
José Valim bbf9552186 Add tests for add_process_handler/2
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2014-09-15 19:33:17 +02:00
Eric Meadows-Jönsson e621b5828e Load dependencies before deps.check compiles
Signed-off-by: Eric Meadows-Jönsson <eric.meadows.jonsson@gmail.com>
2014-09-15 10:40:35 +02:00
José Valim 68e282c50a Do not require {ref, :done}, instead allow sync removal
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2014-09-14 23:15:57 +02:00
José Valim 403a50fbcd Ensure chunk/4 is haltable
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2014-09-13 16:14:23 +02:00
José Valim 8e98cd4f41 Start v1.0.1-dev 2014-09-12 12:34:52 +02:00
budstein 499c385000 Fix a typo in Code.eval_file's doc
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2014-09-12 12:33:37 +02:00
Alexei Sholik aecafc099d Fix a typo in IO.inspect’s doc
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2014-09-12 12:33:31 +02:00
Haoming Zhu 785c7d700a fix previous commit typo bug
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2014-09-12 12:33:15 +02:00
Bill Gathen cecc8a0f95 Fix my own typo in helpers.ex
Brand-new language, and I put a ding in it. Sorry about that. :-(

Congratulations on 1.0!

Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2014-09-12 12:32:53 +02:00
41 changed files with 680 additions and 419 deletions
+20
View File
@@ -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
+2 -2
View File
@@ -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
View File
@@ -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
View File
@@ -1 +1 @@
1.0.0
1.0.1
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
View File
@@ -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
+20 -25
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+6 -4
View File
@@ -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
+2 -1
View File
@@ -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)
+2 -1
View File
@@ -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
View File
@@ -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
+10 -7
View File
@@ -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)
+7 -181
View File
@@ -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;
+2 -1
View File
@@ -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
+224
View File
@@ -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]}.
+17
View File
@@ -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})
+13
View File
@@ -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))
+23
View File
@@ -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
+9 -1
View File
@@ -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)
+3 -2
View File
@@ -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
+1 -1
View File
@@ -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.
+51 -31
View File
@@ -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
View File
@@ -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
+1 -10
View File
@@ -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
+3
View File
@@ -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
+5 -6
View File
@@ -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
+9 -13
View File
@@ -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 """
+3 -2
View File
@@ -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)
+3 -2
View File
@@ -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
+6 -6
View File
@@ -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
+13 -3
View File
@@ -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
View File
@@ -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
View File
@@ -0,0 +1,2 @@
use Mix.Config
config :app, Repo, key: [nested: true]
+20 -3
View File
@@ -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
+2
View File
@@ -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 []
+14 -3
View File
@@ -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
+1 -1
View File
@@ -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
View File
@@ -1,6 +1,6 @@
{application, elixir,
[{description, "elixir"},
{vsn, "1.0.0"},
{vsn, "1.0.1"},
{modules, [
elixir
]},