Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2b338092b6 | ||
|
|
557b779bd4 | ||
|
|
ece40480f6 | ||
|
|
a77445c46b | ||
|
|
4d55fa6814 | ||
|
|
b9d6f4f9c1 | ||
|
|
b0cf76c74c | ||
|
|
7841fbb8e0 | ||
|
|
f5773fed42 | ||
|
|
e2b8ab9a0d | ||
|
|
1dc6d10f30 | ||
|
|
e7138fb2c6 | ||
|
|
04ecc05adc | ||
|
|
8aab53b941 | ||
|
|
07c276ab5c | ||
|
|
89e3a81adc | ||
|
|
9752f1ce05 |
@@ -116,6 +116,30 @@ Percentage | Module
|
||||
77.19% | Total
|
||||
```
|
||||
|
||||
## v1.7.2 (2018-08-05)
|
||||
|
||||
### 1. Bug fixes
|
||||
|
||||
#### Elixir
|
||||
|
||||
* [DateTime] Take negative years into account in `DateTime.from_iso8601/1`
|
||||
* [Kernel] Do not emit warnings for repeated docs over different clauses due to false positives
|
||||
|
||||
#### Mix
|
||||
|
||||
* [mix compile] Properly mark top-level dependencies as optional and as runtime. This fixes a bug where Mix attempted to start optional dependencies of a package when those optional dependencies were not available
|
||||
* [mix compile] Avoid deadlock when a config has a timestamp later than current time
|
||||
* [mix help] Show task and alias help when both are available
|
||||
* [mix test] Do not fail suite if there are no test files
|
||||
|
||||
## v1.7.1 (2018-07-26)
|
||||
|
||||
### 1. Bug fixes
|
||||
|
||||
#### Elixir
|
||||
|
||||
* [Calendar] Work-around a Dialyzer bug that causes it to loop for a long time, potentially indefinitely
|
||||
|
||||
## v1.7.0 (2018-07-25)
|
||||
|
||||
### 1. Enhancements
|
||||
|
||||
@@ -263,24 +263,29 @@ defmodule Date do
|
||||
@spec from_iso8601(String.t(), Calendar.calendar()) :: {:ok, t} | {:error, atom}
|
||||
def from_iso8601(string, calendar \\ Calendar.ISO)
|
||||
|
||||
[match_date, guard_date, read_date] = Calendar.ISO.__match_date__()
|
||||
|
||||
def from_iso8601(unquote(match_date), calendar) when unquote(guard_date) do
|
||||
{year, month, day} = unquote(read_date)
|
||||
|
||||
with {:ok, date} <- new(year, month, day, Calendar.ISO) do
|
||||
convert(date, calendar)
|
||||
end
|
||||
end
|
||||
|
||||
def from_iso8601(<<?-, next, rest::binary>>, calendar) when next != ?- do
|
||||
with {:ok, %{year: year} = date} <- from_iso8601(<<next>> <> rest, calendar) do
|
||||
def from_iso8601(<<?-, rest::binary>>, calendar) do
|
||||
with {:ok, %{year: year} = date} <- raw_from_iso8601(rest, calendar) do
|
||||
{:ok, %{date | year: -year}}
|
||||
end
|
||||
end
|
||||
|
||||
def from_iso8601(<<_::binary>>, _calendar) do
|
||||
{:error, :invalid_format}
|
||||
def from_iso8601(<<rest::binary>>, calendar) do
|
||||
raw_from_iso8601(rest, calendar)
|
||||
end
|
||||
|
||||
[match_date, guard_date, read_date] = Calendar.ISO.__match_date__()
|
||||
|
||||
defp raw_from_iso8601(string, calendar) do
|
||||
with unquote(match_date) <- string,
|
||||
true <- unquote(guard_date) do
|
||||
{year, month, day} = unquote(read_date)
|
||||
|
||||
with {:ok, date} <- new(year, month, day, Calendar.ISO) do
|
||||
convert(date, calendar)
|
||||
end
|
||||
else
|
||||
_ -> {:error, :invalid_format}
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
|
||||
@@ -485,6 +485,14 @@ defmodule DateTime do
|
||||
iex> datetime
|
||||
#DateTime<2015-01-23 21:20:07.123Z>
|
||||
|
||||
iex> {:ok, datetime, 0} = DateTime.from_iso8601("-2015-01-23T23:50:07Z")
|
||||
iex> datetime
|
||||
#DateTime<-2015-01-23 23:50:07Z>
|
||||
|
||||
iex> {:ok, datetime, 9000} = DateTime.from_iso8601("-2015-01-23T23:50:07,123+02:30")
|
||||
iex> datetime
|
||||
#DateTime<-2015-01-23 21:20:07.123Z>
|
||||
|
||||
iex> DateTime.from_iso8601("2015-01-23P23:50:07")
|
||||
{:error, :invalid_format}
|
||||
iex> DateTime.from_iso8601("2015-01-23 23:50:07A")
|
||||
@@ -505,18 +513,28 @@ defmodule DateTime do
|
||||
@doc since: "1.4.0"
|
||||
@spec from_iso8601(String.t(), Calendar.calendar()) ::
|
||||
{:ok, t, Calendar.utc_offset()} | {:error, atom}
|
||||
def from_iso8601(string, calendar \\ Calendar.ISO)
|
||||
|
||||
def from_iso8601(<<?-, rest::binary>>, calendar) do
|
||||
raw_from_iso8601(rest, calendar, true)
|
||||
end
|
||||
|
||||
def from_iso8601(<<rest::binary>>, calendar) do
|
||||
raw_from_iso8601(rest, calendar, false)
|
||||
end
|
||||
|
||||
@sep [?\s, ?T]
|
||||
[match_date, guard_date, read_date] = Calendar.ISO.__match_date__()
|
||||
[match_time, guard_time, read_time] = Calendar.ISO.__match_time__()
|
||||
|
||||
def from_iso8601(string, calendar \\ Calendar.ISO) when is_binary(string) do
|
||||
with <<unquote(match_date), sep, unquote(match_time), rest::binary>>
|
||||
when unquote(guard_date) and sep in @sep and unquote(guard_time) <- string,
|
||||
defp raw_from_iso8601(string, calendar, is_negative_datetime) do
|
||||
with <<unquote(match_date), sep, unquote(match_time), rest::binary>> <- string,
|
||||
true <- unquote(guard_date) and sep in @sep and unquote(guard_time),
|
||||
{microsecond, rest} <- Calendar.ISO.parse_microsecond(rest),
|
||||
{offset, ""} <- Calendar.ISO.parse_offset(rest) do
|
||||
{year, month, day} = unquote(read_date)
|
||||
{hour, minute, second} = unquote(read_time)
|
||||
year = if is_negative_datetime, do: -year, else: year
|
||||
|
||||
cond do
|
||||
not calendar.valid_date?(year, month, day) ->
|
||||
@@ -525,6 +543,24 @@ defmodule DateTime do
|
||||
not calendar.valid_time?(hour, minute, second, microsecond) ->
|
||||
{:error, :invalid_time}
|
||||
|
||||
offset == 0 ->
|
||||
datetime = %DateTime{
|
||||
calendar: calendar,
|
||||
year: year,
|
||||
month: month,
|
||||
day: day,
|
||||
hour: hour,
|
||||
minute: minute,
|
||||
second: second,
|
||||
microsecond: microsecond,
|
||||
std_offset: 0,
|
||||
utc_offset: 0,
|
||||
zone_abbr: "UTC",
|
||||
time_zone: "Etc/UTC"
|
||||
}
|
||||
|
||||
{:ok, datetime, 0}
|
||||
|
||||
is_nil(offset) ->
|
||||
{:error, :missing_offset}
|
||||
|
||||
|
||||
@@ -171,10 +171,35 @@ defmodule NaiveDateTime do
|
||||
Calendar.microsecond(),
|
||||
Calendar.calendar()
|
||||
) :: {:ok, t} | {:error, atom}
|
||||
def new(year, month, day, hour, minute, second, microsecond \\ {0, 0}, calendar \\ Calendar.ISO) do
|
||||
with {:ok, date} <- Date.new(year, month, day, calendar),
|
||||
{:ok, time} <- Time.new(hour, minute, second, microsecond, calendar),
|
||||
do: new(date, time)
|
||||
def new(year, month, day, hour, minute, second, microsecond \\ {0, 0}, calendar \\ Calendar.ISO)
|
||||
|
||||
def new(year, month, day, hour, minute, second, microsecond, calendar)
|
||||
when is_integer(microsecond) do
|
||||
new(year, month, day, hour, minute, second, {microsecond, 6}, calendar)
|
||||
end
|
||||
|
||||
def new(year, month, day, hour, minute, second, microsecond, calendar) do
|
||||
cond do
|
||||
not calendar.valid_date?(year, month, day) ->
|
||||
{:error, :invalid_date}
|
||||
|
||||
not calendar.valid_time?(hour, minute, second, microsecond) ->
|
||||
{:error, :invalid_time}
|
||||
|
||||
true ->
|
||||
naive_datetime = %NaiveDateTime{
|
||||
calendar: calendar,
|
||||
year: year,
|
||||
month: month,
|
||||
day: day,
|
||||
hour: hour,
|
||||
minute: minute,
|
||||
second: second,
|
||||
microsecond: microsecond
|
||||
}
|
||||
|
||||
{:ok, naive_datetime}
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
@@ -470,19 +495,23 @@ defmodule NaiveDateTime do
|
||||
@spec from_iso8601(String.t(), Calendar.calendar()) :: {:ok, t} | {:error, atom}
|
||||
def from_iso8601(string, calendar \\ Calendar.ISO)
|
||||
|
||||
def from_iso8601(<<?-, next, rest::binary>>, calendar) when next != ?- do
|
||||
with {:ok, %{year: year} = naive_datetime} <- from_iso8601(<<next>> <> rest, calendar) do
|
||||
def from_iso8601(<<?-, rest::binary>>, calendar) do
|
||||
with {:ok, %{year: year} = naive_datetime} <- raw_from_iso8601(rest, calendar) do
|
||||
{:ok, %{naive_datetime | year: -year}}
|
||||
end
|
||||
end
|
||||
|
||||
def from_iso8601(<<rest::binary>>, calendar) do
|
||||
raw_from_iso8601(rest, calendar)
|
||||
end
|
||||
|
||||
@sep [?\s, ?T]
|
||||
[match_date, guard_date, read_date] = Calendar.ISO.__match_date__()
|
||||
[match_time, guard_time, read_time] = Calendar.ISO.__match_time__()
|
||||
|
||||
def from_iso8601(string, calendar) when is_binary(string) do
|
||||
with <<unquote(match_date), sep, unquote(match_time), rest::binary>>
|
||||
when unquote(guard_date) and sep in @sep and unquote(guard_time) <- string,
|
||||
defp raw_from_iso8601(string, calendar) do
|
||||
with <<unquote(match_date), sep, unquote(match_time), rest::binary>> <- string,
|
||||
true <- unquote(guard_date) and sep in @sep and unquote(guard_time),
|
||||
{microsec, rest} <- Calendar.ISO.parse_microsecond(rest),
|
||||
{_offset, ""} <- Calendar.ISO.parse_offset(rest) do
|
||||
{year, month, day} = unquote(read_date)
|
||||
|
||||
@@ -217,14 +217,19 @@ defmodule Time do
|
||||
@spec from_iso8601(String.t(), Calendar.calendar()) :: {:ok, t} | {:error, atom}
|
||||
def from_iso8601(string, calendar \\ Calendar.ISO)
|
||||
|
||||
def from_iso8601(<<?T, h, rest::binary>>, calendar) when h in ?0..?9 do
|
||||
from_iso8601(<<h, rest::binary>>, calendar)
|
||||
def from_iso8601(<<?T, rest::binary>>, calendar) do
|
||||
raw_from_iso8601(rest, calendar)
|
||||
end
|
||||
|
||||
def from_iso8601(<<rest::binary>>, calendar) do
|
||||
raw_from_iso8601(rest, calendar)
|
||||
end
|
||||
|
||||
[match_time, guard_time, read_time] = Calendar.ISO.__match_time__()
|
||||
|
||||
def from_iso8601(string, calendar) do
|
||||
with <<unquote(match_time), rest::binary>> when unquote(guard_time) <- string,
|
||||
defp raw_from_iso8601(string, calendar) do
|
||||
with <<unquote(match_time), rest::binary>> <- string,
|
||||
true <- unquote(guard_time),
|
||||
{microsec, rest} <- Calendar.ISO.parse_microsecond(rest),
|
||||
{_offset, ""} <- Calendar.ISO.parse_offset(rest) do
|
||||
{hour, min, sec} = unquote(read_time)
|
||||
|
||||
@@ -1331,16 +1331,6 @@ defmodule Module do
|
||||
|
||||
[{_, current_line, current_sign, current_doc, current_doc_meta}] ->
|
||||
signature = merge_signatures(current_sign, signature, 1)
|
||||
|
||||
if is_binary(doc) and is_binary(current_doc) and not is_nil(body) do
|
||||
message =
|
||||
"redefining @doc attribute previously set at line #{current_line}. " <>
|
||||
"If you want to redefine a previously specified doc, " <>
|
||||
"use a definition without a body after the original documentation"
|
||||
|
||||
:elixir_errors.warn(line, env.file, message)
|
||||
end
|
||||
|
||||
doc = if is_nil(doc), do: current_doc, else: doc
|
||||
doc = if is_nil(doc) && impl, do: false, else: doc
|
||||
doc_meta = Map.merge(current_doc_meta, doc_meta)
|
||||
|
||||
@@ -564,10 +564,10 @@ defmodule System do
|
||||
iex> System.cmd("echo", ["hello"])
|
||||
{"hello\n", 0}
|
||||
|
||||
iex> System.cmd("echo", ["hello"]), env: [{"MIX_ENV", "test"}]
|
||||
iex> System.cmd("echo", ["hello"], env: [{"MIX_ENV", "test"}])
|
||||
{"hello\n", 0}
|
||||
|
||||
iex> System.cmd("echo", ["hello"]), into: IO.stream(:stdio, :line)
|
||||
iex> System.cmd("echo", ["hello"], into: IO.stream(:stdio, :line))
|
||||
hello
|
||||
{%IO.Stream{}, 0}
|
||||
|
||||
|
||||
@@ -293,8 +293,7 @@ defmodule DateTimeTest do
|
||||
end
|
||||
|
||||
test "from_iso8601/1 handles positive and negative offsets" do
|
||||
assert DateTime.from_iso8601("2015-01-24T09:50:07-10:00")
|
||||
|> elem(1) ==
|
||||
assert DateTime.from_iso8601("2015-01-24T09:50:07-10:00") |> elem(1) ==
|
||||
%DateTime{
|
||||
microsecond: {0, 0},
|
||||
month: 1,
|
||||
@@ -309,8 +308,7 @@ defmodule DateTimeTest do
|
||||
second: 7
|
||||
}
|
||||
|
||||
assert DateTime.from_iso8601("2015-01-24T09:50:07+10:00")
|
||||
|> elem(1) ==
|
||||
assert DateTime.from_iso8601("2015-01-24T09:50:07+10:00") |> elem(1) ==
|
||||
%DateTime{
|
||||
microsecond: {0, 0},
|
||||
month: 1,
|
||||
@@ -324,6 +322,98 @@ defmodule DateTimeTest do
|
||||
minute: 50,
|
||||
second: 7
|
||||
}
|
||||
|
||||
assert DateTime.from_iso8601("0000-01-01T01:22:07+10:30") |> elem(1) ==
|
||||
%DateTime{
|
||||
microsecond: {0, 0},
|
||||
month: 12,
|
||||
std_offset: 0,
|
||||
time_zone: "Etc/UTC",
|
||||
utc_offset: 0,
|
||||
year: -1,
|
||||
zone_abbr: "UTC",
|
||||
day: 31,
|
||||
hour: 14,
|
||||
minute: 52,
|
||||
second: 7
|
||||
}
|
||||
end
|
||||
|
||||
test "from_iso8601/1 handles negative dates" do
|
||||
assert DateTime.from_iso8601("-2015-01-24T09:50:07-10:00") |> elem(1) ==
|
||||
%DateTime{
|
||||
microsecond: {0, 0},
|
||||
month: 1,
|
||||
std_offset: 0,
|
||||
time_zone: "Etc/UTC",
|
||||
utc_offset: 0,
|
||||
year: -2015,
|
||||
zone_abbr: "UTC",
|
||||
day: 24,
|
||||
hour: 19,
|
||||
minute: 50,
|
||||
second: 7
|
||||
}
|
||||
|
||||
assert DateTime.from_iso8601("-2015-01-24T09:50:07+10:00") |> elem(1) ==
|
||||
%DateTime{
|
||||
microsecond: {0, 0},
|
||||
month: 1,
|
||||
std_offset: 0,
|
||||
time_zone: "Etc/UTC",
|
||||
utc_offset: 0,
|
||||
year: -2015,
|
||||
zone_abbr: "UTC",
|
||||
day: 23,
|
||||
hour: 23,
|
||||
minute: 50,
|
||||
second: 7
|
||||
}
|
||||
|
||||
assert DateTime.from_iso8601("-0001-01-01T01:22:07+10:30") |> elem(1) ==
|
||||
%DateTime{
|
||||
microsecond: {0, 0},
|
||||
month: 12,
|
||||
std_offset: 0,
|
||||
time_zone: "Etc/UTC",
|
||||
utc_offset: 0,
|
||||
year: -2,
|
||||
zone_abbr: "UTC",
|
||||
day: 31,
|
||||
hour: 14,
|
||||
minute: 52,
|
||||
second: 7
|
||||
}
|
||||
|
||||
assert DateTime.from_iso8601("-0001-01-01T01:22:07-10:30") |> elem(1) ==
|
||||
%DateTime{
|
||||
microsecond: {0, 0},
|
||||
month: 1,
|
||||
std_offset: 0,
|
||||
time_zone: "Etc/UTC",
|
||||
utc_offset: 0,
|
||||
year: -1,
|
||||
zone_abbr: "UTC",
|
||||
day: 1,
|
||||
hour: 11,
|
||||
minute: 52,
|
||||
second: 7
|
||||
}
|
||||
|
||||
assert DateTime.from_iso8601("-0001-12-31T23:22:07-10:30") |> elem(1) ==
|
||||
%DateTime{
|
||||
microsecond: {0, 0},
|
||||
month: 1,
|
||||
std_offset: 0,
|
||||
time_zone: "Etc/UTC",
|
||||
utc_offset: 0,
|
||||
year: 0,
|
||||
zone_abbr: "UTC",
|
||||
day: 1,
|
||||
hour: 9,
|
||||
minute: 52,
|
||||
second: 7
|
||||
}
|
||||
end
|
||||
|
||||
test "from_unix/2" do
|
||||
|
||||
@@ -1109,34 +1109,6 @@ defmodule Kernel.WarningTest do
|
||||
purge(Sample)
|
||||
end
|
||||
|
||||
test "duplicated docs across multiple clauses" do
|
||||
output =
|
||||
capture_err(fn ->
|
||||
Code.eval_string("""
|
||||
defmodule Sample do
|
||||
@doc "Something"
|
||||
def foo(a)
|
||||
|
||||
@doc "Something"
|
||||
def foo(1), do: :ok
|
||||
|
||||
@doc "Something"
|
||||
def foo(2), do: :ok
|
||||
|
||||
@doc "Something"
|
||||
def foo(a)
|
||||
end
|
||||
""")
|
||||
end)
|
||||
|
||||
assert output =~ "redefining @doc attribute previously set at line 2"
|
||||
assert output =~ "nofile:5"
|
||||
assert output =~ "nofile:8"
|
||||
refute output =~ "nofile:11"
|
||||
after
|
||||
purge(Sample)
|
||||
end
|
||||
|
||||
test "reserved doc metadata keys" do
|
||||
output =
|
||||
capture_err(fn ->
|
||||
|
||||
+27
-3
@@ -117,6 +117,11 @@ defmodule Mix.Dep do
|
||||
end
|
||||
end
|
||||
|
||||
# optional and runtime only matter at the top level.
|
||||
# Any non-top level dependency that is optional and
|
||||
# is still available means it has been fulfilled.
|
||||
@child_keep_opts [:optional, :runtime]
|
||||
|
||||
defp load_and_cache(_config, top, top, env) do
|
||||
converge(env: env)
|
||||
end
|
||||
@@ -124,11 +129,30 @@ defmodule Mix.Dep do
|
||||
defp load_and_cache(config, _top, bottom, _env) do
|
||||
{_, deps} = Mix.ProjectStack.read_cache({:cached_deps, bottom})
|
||||
app = Keyword.fetch!(config, :app)
|
||||
top_level = for dep <- deps, dep.app == app, child <- dep.deps, do: child.app
|
||||
|
||||
seen = populate_seen(MapSet.new(), [app])
|
||||
children = get_deps(deps, tl(Enum.uniq(get_children(deps, seen, [app]))))
|
||||
Enum.map(children, &%{&1 | top_level: &1.app in top_level})
|
||||
|
||||
top_level =
|
||||
for dep <- deps,
|
||||
dep.app == app,
|
||||
child <- dep.deps,
|
||||
do: {child.app, Keyword.take(child.opts, @child_keep_opts)},
|
||||
into: %{}
|
||||
|
||||
Enum.map(children, fn %{app: app} = dep ->
|
||||
case top_level do
|
||||
%{^app => child_opts} ->
|
||||
opts =
|
||||
dep.opts
|
||||
|> Keyword.drop(@child_keep_opts)
|
||||
|> Keyword.merge(child_opts)
|
||||
|
||||
%{dep | top_level: true, opts: opts}
|
||||
|
||||
%{} ->
|
||||
%{dep | top_level: false}
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
defp read_cached_deps(project, env) do
|
||||
|
||||
@@ -99,19 +99,25 @@ defmodule Mix.ProjectStack do
|
||||
|
||||
@spec config_mtime() :: [integer]
|
||||
def config_mtime() do
|
||||
get_and_update(fn state ->
|
||||
get_and_update_in(state.stack, fn
|
||||
[%{config_mtime: nil, config_files: files} = h | t] ->
|
||||
mtime = files |> Enum.map(&Mix.Utils.last_modified/1) |> Enum.max()
|
||||
{mtime, [%{h | config_mtime: mtime} | t]}
|
||||
|
||||
[%{config_mtime: mtime} | _] = stack ->
|
||||
{mtime, stack}
|
||||
|
||||
[] ->
|
||||
{0, []}
|
||||
mtime_or_files =
|
||||
get(fn
|
||||
%{stack: [%{config_mtime: nil, config_files: files} | _]} -> files
|
||||
%{stack: [%{config_mtime: mtime} | _]} -> mtime
|
||||
%{stack: []} -> 0
|
||||
end)
|
||||
end)
|
||||
|
||||
if is_list(mtime_or_files) do
|
||||
mtime = mtime_or_files |> Enum.map(&Mix.Utils.last_modified/1) |> Enum.max()
|
||||
|
||||
cast(fn state ->
|
||||
update_in(state.stack, fn [h | t] -> [%{h | config_mtime: mtime} | t] end)
|
||||
end)
|
||||
|
||||
mtime
|
||||
else
|
||||
mtime = mtime_or_files
|
||||
mtime
|
||||
end
|
||||
end
|
||||
|
||||
@spec config_apps() :: [atom]
|
||||
|
||||
@@ -75,7 +75,6 @@ defmodule Mix.Tasks.Help do
|
||||
aliases = Enum.filter(load_aliases(), fn {name, _} -> String.contains?(name, pattern) end)
|
||||
|
||||
{docs, max} = build_doc_list(modules, aliases)
|
||||
|
||||
display_doc_list(docs, max)
|
||||
end
|
||||
|
||||
@@ -86,25 +85,39 @@ defmodule Mix.Tasks.Help do
|
||||
def run([task]) do
|
||||
loadpaths!()
|
||||
|
||||
{doc, location} = verbose_doc(task)
|
||||
opts = Application.get_env(:mix, :colors)
|
||||
|
||||
if ansi_docs?(opts) do
|
||||
opts = [width: width()] ++ opts
|
||||
IO.ANSI.Docs.print_heading("mix #{task}", opts)
|
||||
IO.ANSI.Docs.print(doc, opts)
|
||||
else
|
||||
IO.puts("# mix #{task}\n")
|
||||
IO.puts(doc)
|
||||
end
|
||||
opts =
|
||||
if ansi_docs?(opts) do
|
||||
[width: width()] ++ opts
|
||||
else
|
||||
opts
|
||||
end
|
||||
|
||||
IO.puts("Location: #{location}")
|
||||
for doc <- verbose_doc(task) do
|
||||
print_doc(task, doc, opts)
|
||||
end
|
||||
end
|
||||
|
||||
def run(_) do
|
||||
Mix.raise("Unexpected arguments, expected \"mix help\" or \"mix help TASK\"")
|
||||
end
|
||||
|
||||
defp print_doc(task, {doc, location, note}, opts) do
|
||||
if ansi_docs?(opts) do
|
||||
opts = [width: width()] ++ opts
|
||||
IO.ANSI.Docs.print_heading("mix #{task}", opts)
|
||||
IO.ANSI.Docs.print(doc, opts)
|
||||
IO.puts("Location: #{location}")
|
||||
note && IO.puts("") && IO.ANSI.Docs.print(note, opts)
|
||||
else
|
||||
IO.puts("# mix #{task}\n")
|
||||
IO.puts(doc)
|
||||
IO.puts("\nLocation: #{location}")
|
||||
note && IO.puts([?\n, note, ?\n, ?\n])
|
||||
end
|
||||
end
|
||||
|
||||
# Loadpaths without checks because tasks may be defined in deps.
|
||||
defp loadpaths! do
|
||||
args = ["--no-elixir-version-check", "--no-deps-check", "--no-archives-check"]
|
||||
@@ -194,14 +207,29 @@ defmodule Mix.Tasks.Help do
|
||||
defp verbose_doc(task) do
|
||||
aliases = load_aliases()
|
||||
|
||||
if Map.has_key?(aliases, task) do
|
||||
task_name = aliases[task]
|
||||
doc = "Alias for " <> inspect(task_name)
|
||||
{doc, "mix.exs"}
|
||||
else
|
||||
module = Mix.Task.get!(task)
|
||||
doc = Mix.Task.moduledoc(module) || "There is no documentation for this task"
|
||||
{doc, where_is_file(module)}
|
||||
has_alias? = Map.has_key?(aliases, task)
|
||||
has_task? = Mix.Task.get(task)
|
||||
|
||||
cond do
|
||||
has_alias? and has_task? ->
|
||||
note = "There is also a task named \"#{task}\". The documentation is shown next."
|
||||
[alias_doc(aliases[task], note), task_doc(task)]
|
||||
|
||||
has_alias? ->
|
||||
[alias_doc(aliases[task], nil)]
|
||||
|
||||
true ->
|
||||
[task_doc(task)]
|
||||
end
|
||||
end
|
||||
|
||||
defp alias_doc(task_name, note) do
|
||||
{"Alias for " <> inspect(task_name), "mix.exs", note}
|
||||
end
|
||||
|
||||
defp task_doc(task) do
|
||||
module = Mix.Task.get!(task)
|
||||
doc = Mix.Task.moduledoc(module) || "There is no documentation for this task"
|
||||
{doc, where_is_file(module), nil}
|
||||
end
|
||||
end
|
||||
|
||||
@@ -407,7 +407,7 @@ defmodule Mix.Tasks.Test do
|
||||
Mix.shell().info("No stale tests")
|
||||
|
||||
files == [] ->
|
||||
raise_or_error_at_exit("There are no tests to run", opts)
|
||||
Mix.shell().info("There are no tests to run")
|
||||
|
||||
true ->
|
||||
message = "Paths given to `mix test` did not match any directory/file: "
|
||||
|
||||
@@ -509,6 +509,77 @@ defmodule Mix.DepTest do
|
||||
end)
|
||||
end
|
||||
|
||||
test "nested deps with optional matching" do
|
||||
Process.put(:custom_deps_git_repo_opts, optional: true)
|
||||
|
||||
# deps_repo brings git_repo but it is optional
|
||||
deps = [
|
||||
{:deps_repo, "0.1.0", path: "custom/deps_repo"},
|
||||
{:git_repo, "0.1.0", git: MixTest.Case.fixture_path("git_repo")}
|
||||
]
|
||||
|
||||
with_deps(deps, fn ->
|
||||
in_fixture("deps_status", fn ->
|
||||
File.mkdir_p!("custom/deps_repo/lib")
|
||||
|
||||
File.write!("custom/deps_repo/lib/a.ex", """
|
||||
# Check that the child dependency is top_level and optional
|
||||
[%Mix.Dep{app: :git_repo, top_level: true, opts: opts}] = Mix.Dep.cached()
|
||||
true = Keyword.fetch!(opts, :optional)
|
||||
""")
|
||||
|
||||
Mix.Tasks.Deps.Get.run([])
|
||||
Mix.Tasks.Deps.Compile.run([])
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
test "nested deps with runtime override on parent" do
|
||||
Process.put(:custom_deps_git_repo_opts, runtime: false)
|
||||
|
||||
deps = [
|
||||
{:deps_repo, "0.1.0", path: "custom/deps_repo"},
|
||||
{:git_repo, "0.1.0", git: MixTest.Case.fixture_path("git_repo")}
|
||||
]
|
||||
|
||||
with_deps(deps, fn ->
|
||||
in_fixture("deps_status", fn ->
|
||||
File.mkdir_p!("custom/deps_repo/lib")
|
||||
|
||||
File.write!("custom/deps_repo/lib/a.ex", """
|
||||
# Check that the child dependency is top_level and optional
|
||||
[%Mix.Dep{app: :git_repo, top_level: true, opts: opts}] = Mix.Dep.cached()
|
||||
false = Keyword.fetch!(opts, :runtime)
|
||||
""")
|
||||
|
||||
Mix.Tasks.Deps.Get.run([])
|
||||
Mix.Tasks.Deps.Compile.run([])
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
test "nested deps with runtime override on child" do
|
||||
deps = [
|
||||
{:deps_repo, "0.1.0", path: "custom/deps_repo"},
|
||||
{:git_repo, "0.1.0", git: MixTest.Case.fixture_path("git_repo"), runtime: false}
|
||||
]
|
||||
|
||||
with_deps(deps, fn ->
|
||||
in_fixture("deps_status", fn ->
|
||||
File.mkdir_p!("custom/deps_repo/lib")
|
||||
|
||||
File.write!("custom/deps_repo/lib/a.ex", """
|
||||
# Check that the child dependency is top_level and optional
|
||||
[%Mix.Dep{app: :git_repo, top_level: true, opts: opts}] = Mix.Dep.cached()
|
||||
false = Keyword.has_key?(opts, :runtime)
|
||||
""")
|
||||
|
||||
Mix.Tasks.Deps.Get.run([])
|
||||
Mix.Tasks.Deps.Compile.run([])
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
test "nested deps with overrides" do
|
||||
# deps_repo brings git_repo but it is overriden
|
||||
deps = [
|
||||
|
||||
@@ -136,6 +136,33 @@ defmodule Mix.Tasks.HelpTest do
|
||||
end)
|
||||
end
|
||||
|
||||
defmodule ShadowedAliases do
|
||||
def project do
|
||||
[aliases: [compile: "compile"]]
|
||||
end
|
||||
end
|
||||
|
||||
test "help TASK && ALIAS", context do
|
||||
Mix.Project.push(ShadowedAliases)
|
||||
|
||||
in_tmp(context.test, fn ->
|
||||
output =
|
||||
capture_io(fn ->
|
||||
Mix.Tasks.Help.run(["compile"])
|
||||
end)
|
||||
|
||||
assert output =~ "# mix compile\n\n"
|
||||
assert output =~ "Alias for \"compile\"\n"
|
||||
assert output =~ ~r/^Location: mix.exs/m
|
||||
|
||||
assert output =~
|
||||
"\nThere is also a task named \"compile\". The documentation is shown next.\n"
|
||||
|
||||
assert output =~ "## Command line options"
|
||||
assert output =~ ~r/^Location:/m
|
||||
end)
|
||||
end
|
||||
|
||||
test "help --search PATTERN", context do
|
||||
in_tmp(context.test, fn ->
|
||||
Mix.Tasks.Help.run(["--search", "deps"])
|
||||
|
||||
@@ -61,6 +61,12 @@ defmodule Mix.Tasks.LoadconfigTest do
|
||||
Mix.Task.run("loadconfig", [config])
|
||||
assert config in Mix.Project.config_files()
|
||||
assert Mix.Project.config_mtime() > mtime
|
||||
|
||||
# Touching it should not have any deadlocks
|
||||
File.touch!(config, {{2030, 1, 1}, {0, 0, 0}})
|
||||
Mix.Task.run("loadconfig", [config])
|
||||
assert config in Mix.Project.config_files()
|
||||
assert Mix.Project.config_mtime() > mtime
|
||||
end)
|
||||
end
|
||||
|
||||
|
||||
Reference in New Issue
Block a user