Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a16517edfb | ||
|
|
26136e53c6 | ||
|
|
6654f1b922 | ||
|
|
e30f8df8fe | ||
|
|
2fe5b77498 | ||
|
|
f1a5e6a0b1 | ||
|
|
fadf5c6519 | ||
|
|
e975613c9d | ||
|
|
b1a1dd04a5 | ||
|
|
e4e2f1a79e | ||
|
|
138b442e07 | ||
|
|
119580f544 | ||
|
|
9fd97c4663 | ||
|
|
78cee0e384 | ||
|
|
fb0379287c | ||
|
|
def65ab120 | ||
|
|
dc8cfcd4c9 | ||
|
|
84c8d23710 | ||
|
|
36c3f7594d | ||
|
|
bdaffc380b | ||
|
|
5d79b34cb5 | ||
|
|
14cddf357b | ||
|
|
92eee10bcf | ||
|
|
9b7b7d655d | ||
|
|
f40aa10436 |
+55
-3
@@ -117,13 +117,65 @@ in the long term.
|
||||
See the new `Logger` documentation for more information on the
|
||||
new features and on compatibility.
|
||||
|
||||
## v1.15.6 (2023-09-20)
|
||||
|
||||
This release also includes fixes to the Windows installer.
|
||||
|
||||
### 1. Bug fixes
|
||||
|
||||
#### EEx
|
||||
|
||||
* [EEx] Do not crash when printing tokenizer warnings
|
||||
|
||||
#### Elixir
|
||||
|
||||
* [Code] Fix formatter for nested `*` in bitstrings
|
||||
* [Code] Improve feedback when an invalid block is given `Code.quoted_to_algebra/2`
|
||||
* [Kernel] Trace functions before they are inlined
|
||||
|
||||
#### Mix
|
||||
|
||||
* [mix compile] Ensure `:extra_applications` declare in umbrella projects are loaded
|
||||
* [mix deps.get] Do not check for invalid applications before deps.get
|
||||
* [mix deps.update] Do not check for invalid applications before deps.update
|
||||
* [mix format] Load plugins when invoking the formatter from an IDE
|
||||
|
||||
## v1.15.5 (2023-08-28)
|
||||
|
||||
### 1. Enhancements
|
||||
|
||||
#### IEx
|
||||
|
||||
* [IEx.Autocomplete] Speed up loading of struct suggestions
|
||||
|
||||
### 2. Bug fixes
|
||||
|
||||
#### Elixir
|
||||
|
||||
* [Code.Fragment] Fix `Code.Fragment.surround_context/2` for aliases and submodules of non-aliases
|
||||
* [Kernel] Ensure stacktrace is included when necessary when rescuing multiple exceptions in the same branch
|
||||
* [Kernel] Fix index in error message for unused optional arguments
|
||||
|
||||
#### ExUnit
|
||||
|
||||
* [ExUnit.Diff] Fix scenario where diff would not show up due to a timed-out loop
|
||||
|
||||
#### IEx
|
||||
|
||||
* [IEx] Force group leader to run as a binary and unicode in IEx
|
||||
|
||||
#### Mix
|
||||
|
||||
* [mix compile] Do not assume `blake` is always available
|
||||
* [mix format] Load and compile plugins if specified in subdirectories
|
||||
|
||||
## v1.15.4 (2023-07-18)
|
||||
|
||||
### 1. Bug fixes
|
||||
|
||||
#### Mix
|
||||
|
||||
* [mix archive.build] Disable protocol consolidation when building archiveson archive.install
|
||||
* [mix archive.build] Disable protocol consolidation when building archives on archive.install
|
||||
* [mix compile] Track removed files per local dependency (this addresses a bug where files depending on modules from path dependencies always recompiled)
|
||||
* [mix release] Do not strip relevant chunks from Erlang/OTP 26
|
||||
|
||||
@@ -133,11 +185,11 @@ new features and on compatibility.
|
||||
|
||||
#### Elixir
|
||||
|
||||
* [Kernel] Improve better stacktraces when executing unnested Elixir code in a file
|
||||
* [Kernel] Improve stacktraces when executing unnested Elixir code in a file
|
||||
|
||||
#### Mix
|
||||
|
||||
* [Mix] Allow to opt out of starting apps in `Mix.install/2`
|
||||
* [Mix] Allow to opt-out of starting apps in `Mix.install/2`
|
||||
|
||||
### 2. Bug fixes
|
||||
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
ELIXIR_VERSION=1.15.4
|
||||
ELIXIR_VERSION=1.15.6
|
||||
|
||||
if [ $# -eq 0 ] || { [ $# -eq 1 ] && { [ "$1" = "--help" ] || [ "$1" = "-h" ]; }; }; then
|
||||
cat <<USAGE >&2
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
@if defined ELIXIR_CLI_ECHO (@echo on) else (@echo off)
|
||||
|
||||
set ELIXIR_VERSION=1.15.4
|
||||
set ELIXIR_VERSION=1.15.6
|
||||
|
||||
setlocal enabledelayedexpansion
|
||||
if ""%1""=="""" if ""%2""=="""" goto documentation
|
||||
|
||||
@@ -72,8 +72,8 @@ defmodule EEx.Compiler do
|
||||
{key, expr} =
|
||||
case :elixir_tokenizer.tokenize(expr, 1, file: "eex", check_terminators: false) do
|
||||
{:ok, _line, _column, warnings, tokens} ->
|
||||
Enum.each(Enum.reverse(warnings), fn {location, file, msg} ->
|
||||
:elixir_errors.erl_warn(location, file, msg)
|
||||
Enum.each(Enum.reverse(warnings), fn {location, msg} ->
|
||||
:elixir_errors.erl_warn(location, state.file, msg)
|
||||
end)
|
||||
|
||||
token_key(tokens, expr)
|
||||
|
||||
@@ -5,7 +5,13 @@ defmodule EEx.TokenizerTest do
|
||||
|
||||
@opts [indentation: 0, trim: false]
|
||||
|
||||
test "simple chars lists" do
|
||||
test "tokenizer warning" do
|
||||
assert ExUnit.CaptureIO.capture_io(:stderr, fn ->
|
||||
EEx.tokenize(~c"foo <% :'bar' %>", @opts)
|
||||
end) =~ "found quoted atom \"bar\" but the quotes are not required"
|
||||
end
|
||||
|
||||
test "simple charlists" do
|
||||
assert EEx.tokenize(~c"foo", @opts) ==
|
||||
{:ok, [{:text, ~c"foo", %{column: 1, line: 1}}, {:eof, %{column: 4, line: 1}}]}
|
||||
end
|
||||
|
||||
@@ -191,9 +191,13 @@ defmodule Access do
|
||||
case __STACKTRACE__ do
|
||||
[unquote(top) | _] ->
|
||||
reason =
|
||||
"#{inspect(unquote(module))} does not implement the Access behaviour. " <>
|
||||
"If you are using get_in/put_in/update_in, you can specify the field " <>
|
||||
"to be accessed using Access.key!/1"
|
||||
"""
|
||||
#{inspect(unquote(module))} does not implement the Access behaviour
|
||||
|
||||
You can use the "struct.field" syntax to access struct fields. \
|
||||
You can also use Access.key!/1 to access struct fields dynamically \
|
||||
inside get_in/put_in/update_in\
|
||||
"""
|
||||
|
||||
%{unquote(exception) | reason: reason}
|
||||
|
||||
@@ -326,9 +330,23 @@ defmodule Access do
|
||||
end
|
||||
end
|
||||
|
||||
def get(list, key, _default) when is_list(list) and is_integer(key) do
|
||||
raise ArgumentError, """
|
||||
the Access module does not support accessing lists by index, got: #{inspect(key)}
|
||||
|
||||
Accessing a list by index is typically discouraged in Elixir, \
|
||||
instead we prefer to use the Enum module to manipulate lists \
|
||||
as a whole. If you really must access a list element by index, \
|
||||
you can Enum.at/1 or the functions in the List module\
|
||||
"""
|
||||
end
|
||||
|
||||
def get(list, key, _default) when is_list(list) do
|
||||
raise ArgumentError,
|
||||
"the Access calls for keywords expect the key to be an atom, got: " <> inspect(key)
|
||||
raise ArgumentError, """
|
||||
the Access module supports only keyword lists (with atom keys), got: #{inspect(key)}
|
||||
|
||||
If you want to search lists of tuples, use List.keyfind/3\
|
||||
"""
|
||||
end
|
||||
|
||||
def get(nil, _key, default) do
|
||||
@@ -377,10 +395,26 @@ defmodule Access do
|
||||
Map.get_and_update(map, key, fun)
|
||||
end
|
||||
|
||||
def get_and_update(list, key, fun) when is_list(list) do
|
||||
def get_and_update(list, key, fun) when is_list(list) and is_atom(key) do
|
||||
Keyword.get_and_update(list, key, fun)
|
||||
end
|
||||
|
||||
def get_and_update(list, key, _fun) when is_list(list) and is_integer(key) do
|
||||
raise ArgumentError, """
|
||||
the Access module does not support accessing lists by index, got: #{inspect(key)}
|
||||
|
||||
Accessing a list by index is typically discouraged in Elixir, \
|
||||
instead we prefer to use the Enum module to manipulate lists \
|
||||
as a whole. If you really must mostify a list element by index, \
|
||||
you can Access.at/1 or the functions in the List module\
|
||||
"""
|
||||
end
|
||||
|
||||
def get_and_update(list, key, _fun) when is_list(list) do
|
||||
raise ArgumentError,
|
||||
"the Access module supports only keyword lists (with atom keys), got: " <> inspect(key)
|
||||
end
|
||||
|
||||
def get_and_update(nil, key, _fun) do
|
||||
raise ArgumentError, "could not put/update key #{inspect(key)} on a nil value"
|
||||
end
|
||||
@@ -507,6 +541,21 @@ defmodule Access do
|
||||
iex> get_in(map, [Access.key!(:user), Access.key!(:unknown)])
|
||||
** (KeyError) key :unknown not found in: %{name: \"john\"}
|
||||
|
||||
The examples above could be partially written as:
|
||||
|
||||
iex> map = %{user: %{name: "john"}}
|
||||
iex> map.user.name
|
||||
"john"
|
||||
iex> get_and_update_in(map.user.name, fn prev ->
|
||||
...> {prev, String.upcase(prev)}
|
||||
...> end)
|
||||
{"john", %{user: %{name: "JOHN"}}}
|
||||
|
||||
However, it is not possible to remove fields using the dot notation,
|
||||
as it is implified those fields must also be present. In any case,
|
||||
`Access.key!/1` is useful when the key is not known in advance
|
||||
and must be accessed dynamically.
|
||||
|
||||
An error is raised if the accessed structure is not a map/struct:
|
||||
|
||||
iex> get_in([], [Access.key!(:foo)])
|
||||
|
||||
@@ -431,7 +431,7 @@ defmodule Code.Formatter do
|
||||
{color("nil", nil, state.inspect_opts), state}
|
||||
end
|
||||
|
||||
defp quoted_to_algebra({:__block__, meta, _} = block, _context, state) do
|
||||
defp quoted_to_algebra({:__block__, meta, args} = block, _context, state) when is_list(args) do
|
||||
{block, state} = block_to_algebra(block, line(meta), closing_line(meta), state)
|
||||
{surround("(", block, ")"), state}
|
||||
end
|
||||
@@ -1419,7 +1419,9 @@ defmodule Code.Formatter do
|
||||
|
||||
defp bitstring_segment_to_algebra({{:"::", _, [segment, spec]}, i}, state, last) do
|
||||
{doc, state} = quoted_to_algebra(segment, :parens_arg, state)
|
||||
{spec, state} = bitstring_spec_to_algebra(spec, state, state.normalize_bitstring_modifiers)
|
||||
|
||||
{spec, state} =
|
||||
bitstring_spec_to_algebra(spec, state, state.normalize_bitstring_modifiers, :"::")
|
||||
|
||||
spec = wrap_in_parens_if_inspected_atom(spec)
|
||||
spec = if i == last, do: bitstring_wrap_parens(spec, i, last), else: spec
|
||||
@@ -1438,15 +1440,17 @@ defmodule Code.Formatter do
|
||||
{bitstring_wrap_parens(doc, i, last), state}
|
||||
end
|
||||
|
||||
defp bitstring_spec_to_algebra({op, _, [left, right]}, state, normalize_modifiers)
|
||||
defp bitstring_spec_to_algebra({op, _, [left, right]}, state, normalize_modifiers, paren_op)
|
||||
when op in [:-, :*] do
|
||||
normalize_modifiers = normalize_modifiers && op != :*
|
||||
{left, state} = bitstring_spec_to_algebra(left, state, normalize_modifiers)
|
||||
{left, state} = bitstring_spec_to_algebra(left, state, normalize_modifiers, op)
|
||||
{right, state} = bitstring_spec_element_to_algebra(right, state, normalize_modifiers)
|
||||
{concat(concat(left, Atom.to_string(op)), right), state}
|
||||
doc = concat(concat(left, Atom.to_string(op)), right)
|
||||
doc = if paren_op == :*, do: wrap_in_parens(doc), else: doc
|
||||
{doc, state}
|
||||
end
|
||||
|
||||
defp bitstring_spec_to_algebra(spec, state, normalize_modifiers) do
|
||||
defp bitstring_spec_to_algebra(spec, state, normalize_modifiers, _paren_op) do
|
||||
bitstring_spec_element_to_algebra(spec, state, normalize_modifiers)
|
||||
end
|
||||
|
||||
|
||||
@@ -677,6 +677,9 @@ defmodule Code.Fragment do
|
||||
{{:alias, acc}, offset} ->
|
||||
build_surround({:alias, acc}, reversed, line, offset)
|
||||
|
||||
{{:alias, parent, acc}, offset} ->
|
||||
build_surround({:alias, parent, acc}, reversed, line, offset)
|
||||
|
||||
{{:struct, acc}, offset} ->
|
||||
build_surround({:struct, acc}, reversed, line, offset)
|
||||
|
||||
@@ -753,7 +756,7 @@ defmodule Code.Fragment do
|
||||
end
|
||||
end
|
||||
|
||||
defp take_alias([h | t], acc) when h in ?A..?Z or h in ?a..?z or h in ?0..9 or h == ?_,
|
||||
defp take_alias([h | t], acc) when h in ?A..?Z or h in ?a..?z or h in ?0..?9 or h == ?_,
|
||||
do: take_alias(t, [h | acc])
|
||||
|
||||
defp take_alias(rest, acc) do
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
!include "MUI2.nsh"
|
||||
!include "StrFunc.nsh"
|
||||
${Using:StrFunc} StrStr
|
||||
${Using:StrFunc} UnStrStr
|
||||
${Using:StrFunc} UnStrRep
|
||||
|
||||
Name "Elixir"
|
||||
ManifestDPIAware true
|
||||
@@ -14,11 +12,18 @@ InstallDir "$PROGRAMFILES64\Elixir"
|
||||
|
||||
Page custom CheckOTPPageShow CheckOTPPageLeave
|
||||
|
||||
var Dialog
|
||||
var DownloadOTPLink
|
||||
var InstalledERTSVersion
|
||||
var InstalledOTPRelease
|
||||
var OTPPath
|
||||
|
||||
var Dialog
|
||||
var NoOTPLabel
|
||||
var NoOTPLabelCreated
|
||||
var OTPMismatchLabel
|
||||
var OTPMismatchLabelCreated
|
||||
var DownloadOTPLink
|
||||
var DownloadOTPLinkCreated
|
||||
var VerifyOTPButton
|
||||
var VerifyOTPButtonCreated
|
||||
Function CheckOTPPageShow
|
||||
!insertmacro MUI_HEADER_TEXT "Checking Erlang/OTP" ""
|
||||
|
||||
@@ -29,16 +34,61 @@ Function CheckOTPPageShow
|
||||
Abort
|
||||
${EndIf}
|
||||
|
||||
EnumRegKey $0 HKLM "SOFTWARE\WOW6432NODE\Ericsson\Erlang" 0
|
||||
ReadRegStr $1 HKLM "SOFTWARE\WOW6432NODE\Ericsson\Erlang\$0" ""
|
||||
StrCpy $InstalledERTSVersion $0
|
||||
StrCpy $OTPPath $1
|
||||
Call VerifyOTP
|
||||
|
||||
${If} $1 == ""
|
||||
${NSD_CreateLabel} 0 0 100% 20u "Couldn't find existing Erlang/OTP installation. Click the link below to download and install it before proceeding."
|
||||
${NSD_CreateLink} 0 25u 100% 20u "Download Erlang/OTP ${OTP_RELEASE}"
|
||||
nsDialogs::Show
|
||||
FunctionEnd
|
||||
|
||||
Function VerifyOTP
|
||||
${If} $NoOTPLabelCreated == "true"
|
||||
ShowWindow $NoOTPLabel ${SW_HIDE}
|
||||
${EndIf}
|
||||
|
||||
${If} $OTPMismatchLabelCreated == "true"
|
||||
ShowWindow $OTPMismatchLabel ${SW_HIDE}
|
||||
${EndIf}
|
||||
|
||||
${If} $DownloadOTPLinkCreated == "true"
|
||||
ShowWindow $DownloadOTPLink ${SW_HIDE}
|
||||
${Else}
|
||||
StrCpy $DownloadOTPLinkCreated "true"
|
||||
${NSD_CreateLink} 0 60u 100% 20u "Download Erlang/OTP ${OTP_RELEASE}"
|
||||
Pop $DownloadOTPLink
|
||||
${NSD_OnClick} $DownloadOTPLink OpenOTPDownloads
|
||||
ShowWindow $DownloadOTPLink ${SW_HIDE}
|
||||
${EndIf}
|
||||
|
||||
${If} $VerifyOTPButtonCreated == "true"
|
||||
ShowWindow $VerifyOTPButton ${SW_HIDE}
|
||||
${Else}
|
||||
StrCpy $VerifyOTPButtonCreated "true"
|
||||
${NSD_CreateButton} 0 80u 25% 12u "Verify Erlang/OTP"
|
||||
Pop $VerifyOTPButton
|
||||
${NSD_OnClick} $VerifyOTPButton VerifyOTP
|
||||
ShowWindow $VerifyOTPButton ${SW_HIDE}
|
||||
${EndIf}
|
||||
|
||||
StrCpy $0 0
|
||||
loop:
|
||||
EnumRegKey $1 HKLM "SOFTWARE\WOW6432NODE\Ericsson\Erlang" $0
|
||||
StrCmp $1 "" done
|
||||
ReadRegStr $1 HKLM "SOFTWARE\WOW6432NODE\Ericsson\Erlang\$1" ""
|
||||
StrCpy $OTPPath $1
|
||||
IntOp $0 $0 + 1
|
||||
goto loop
|
||||
done:
|
||||
|
||||
${If} $OTPPath == ""
|
||||
${If} $NoOTPLabelCreated != "true"
|
||||
StrCpy $NoOTPLabelCreated "true"
|
||||
${NSD_CreateLabel} 0 0 100% 20u "Couldn't find existing Erlang/OTP installation. Click the link below to download and install it before proceeding."
|
||||
Pop $NoOTPLabel
|
||||
${EndIf}
|
||||
|
||||
ShowWindow $NoOTPLabel ${SW_SHOW}
|
||||
ShowWindow $DownloadOTPLink ${SW_SHOW}
|
||||
ShowWindow $VerifyOTPButton ${SW_SHOW}
|
||||
|
||||
${Else}
|
||||
nsExec::ExecToStack `$OTPPath\bin\erl.exe -noinput -eval "\
|
||||
io:put_chars(erlang:system_info(otp_release)),\
|
||||
@@ -49,24 +99,25 @@ Function CheckOTPPageShow
|
||||
${If} $0 == 0
|
||||
StrCpy $InstalledOTPRelease $1
|
||||
${If} $InstalledOTPRelease == ${OTP_RELEASE}
|
||||
${NSD_CreateLabel} 0 0 100% 20u "Found existing Erlang/OTP $InstalledOTPRelease installation at $OTPPath. Please proceed."
|
||||
${ElseIf} $2 < ${OTP_RELEASE}
|
||||
${NSD_CreateLabel} 0 0 100% 30u "Found existing Erlang/OTP $InstalledOTPRelease installation at $OTPPath but this Elixir installer was precompiled for Erlang/OTP ${OTP_RELEASE}. \
|
||||
We recommend checking if there is an Elixir version precompiled for Erlang/OTP $InstalledOTPRelease. Otherwise, proceed."
|
||||
${NSD_CreateLabel} 0 0 100% 60u "Found existing Erlang/OTP $InstalledOTPRelease installation at $OTPPath. Please proceed."
|
||||
|
||||
${Else}
|
||||
SetErrorlevel 5
|
||||
MessageBox MB_ICONSTOP "Found existing Erlang/OTP $InstalledOTPRelease installation at $OTPPath but this Elixir version was precompiled for Erlang/OTP ${OTP_RELEASE}. \
|
||||
Please upgrade your Erlang/OTP version or choose an Elixir installer matching your Erlang/OTP version"
|
||||
Quit
|
||||
${If} $OTPMismatchLabelCreated != "true"
|
||||
StrCpy $OTPMismatchLabelCreated "true"
|
||||
${NSD_CreateLabel} 0 0 100% 60u "Found existing Erlang/OTP $InstalledOTPRelease installation at $OTPPath but this Elixir installer was precompiled for Erlang/OTP ${OTP_RELEASE}. \
|
||||
$\r$\n$\r$\nYou can either search for another Elixir installer precompiled for Erlang/OTP $InstalledOTPRelease or download Erlang/OTP ${OTP_RELEASE} and install before proceeding."
|
||||
Pop $OTPMismatchLabel
|
||||
${EndIf}
|
||||
|
||||
ShowWindow $OTPMismatchLabel ${SW_SHOW}
|
||||
ShowWindow $DownloadOTPLink ${SW_SHOW}
|
||||
ShowWindow $VerifyOTPButton ${SW_SHOW}
|
||||
${EndIf}
|
||||
${Else}
|
||||
SetErrorlevel 5
|
||||
MessageBox MB_ICONSTOP "Found existing Erlang/OTP installation at $OTPPath but checking it exited with $0: $1"
|
||||
Quit
|
||||
${EndIf}
|
||||
${EndIf}
|
||||
|
||||
nsDialogs::Show
|
||||
FunctionEnd
|
||||
|
||||
Function OpenOTPDownloads
|
||||
@@ -97,42 +148,42 @@ Function FinishPageShow
|
||||
Abort
|
||||
${EndIf}
|
||||
|
||||
${NSD_CreateCheckbox} 0 0 195u 10u "&Add $INSTDIR\bin to %PATH%"
|
||||
Pop $AddElixirToPathCheckbox
|
||||
SendMessage $AddElixirToPathCheckbox ${BM_SETCHECK} ${BST_CHECKED} 0
|
||||
; we add to PATH using erlang, so there must be an OTP installed to do so.
|
||||
${If} "$OTPPath" != ""
|
||||
${NSD_CreateCheckbox} 0 0 195u 10u "&Add $INSTDIR\bin to %PATH%"
|
||||
Pop $AddElixirToPathCheckbox
|
||||
SendMessage $AddElixirToPathCheckbox ${BM_SETCHECK} ${BST_CHECKED} 0
|
||||
|
||||
EnumRegKey $0 HKLM "SOFTWARE\WOW6432NODE\Ericsson\Erlang" 0
|
||||
ReadRegStr $0 HKLM "SOFTWARE\WOW6432NODE\Ericsson\Erlang\$0" ""
|
||||
StrCpy $OTPPath $0
|
||||
${If} $0 != ""
|
||||
${NSD_CreateCheckbox} 0 20u 195u 10u "&Add $0\bin to %PATH%"
|
||||
${NSD_CreateCheckbox} 0 20u 195u 10u "&Add $OTPPath\bin to %PATH%"
|
||||
Pop $AddOTPToPathCheckbox
|
||||
SendMessage $AddOTPToPathCheckbox ${BM_SETCHECK} ${BST_CHECKED} 0
|
||||
|
||||
${NSD_CreateLabel} 0 40u 100% 20u "Note: you need to restart your shell for the environment variable changes to take effect."
|
||||
${EndIf}
|
||||
|
||||
nsDialogs::Show
|
||||
FunctionEnd
|
||||
|
||||
var PathsToAdd
|
||||
Function FinishPageLeave
|
||||
${NSD_GetState} $AddOTPToPathCheckbox $0
|
||||
${If} $0 <> ${BST_UNCHECKED}
|
||||
ReadRegStr $0 HKCU "Environment" "Path"
|
||||
${StrStr} $1 "$0" "$OTPPath\bin"
|
||||
${If} $1 == ""
|
||||
WriteRegExpandStr HKCU "Environment" "Path" "$OTPPath\bin;$0"
|
||||
${Else}
|
||||
MessageBox MB_OK "$OTPPath\bin already in %PATH%"
|
||||
${EndIf}
|
||||
StrCpy $PathsToAdd ";$OTPPath\bin"
|
||||
${EndIf}
|
||||
|
||||
${NSD_GetState} $AddElixirToPathCheckbox $0
|
||||
${If} $0 <> ${BST_UNCHECKED}
|
||||
ReadRegStr $0 HKCU "Environment" "Path"
|
||||
${StrStr} $1 "$0" "$INSTDIR\bin"
|
||||
${If} $1 == ""
|
||||
WriteRegExpandStr HKCU "Environment" "Path" "$INSTDIR\bin;$0"
|
||||
${Else}
|
||||
MessageBox MB_OK "$INSTDIR\bin already in %PATH%"
|
||||
StrCpy $PathsToAdd "$PathsToAdd;$INSTDIR\bin"
|
||||
${EndIf}
|
||||
|
||||
${If} "$PathsToAdd" != ""
|
||||
nsExec::ExecToStack `"$OTPPath\bin\escript.exe" "$INSTDIR\update_system_path.erl" add "$PathsToAdd"`
|
||||
Pop $0
|
||||
Pop $1
|
||||
${If} $0 != 0
|
||||
SetErrorlevel 5
|
||||
MessageBox MB_ICONSTOP "adding paths failed with $0: $1"
|
||||
Quit
|
||||
${EndIf}
|
||||
${EndIf}
|
||||
FunctionEnd
|
||||
@@ -140,25 +191,27 @@ FunctionEnd
|
||||
Section "Install Elixir" SectionElixir
|
||||
SetOutPath "$INSTDIR"
|
||||
File /r "${ELIXIR_DIR}\"
|
||||
File "update_system_path.erl"
|
||||
|
||||
WriteUninstaller "Uninstall.exe"
|
||||
SectionEnd
|
||||
|
||||
; Uninstall Page: Files
|
||||
|
||||
!insertmacro MUI_UNPAGE_DIRECTORY
|
||||
!insertmacro MUI_UNPAGE_INSTFILES
|
||||
|
||||
Section "Uninstall"
|
||||
RMDir /r "$INSTDIR"
|
||||
SectionEnd
|
||||
|
||||
; Uninstall Page: Finish
|
||||
; Uninstall Page: Remove from %PATH%
|
||||
|
||||
var RemoveOTPFromPathCheckbox
|
||||
var RemoveElixirFromPathCheckbox
|
||||
Function un.FinishPageShow
|
||||
!insertmacro MUI_HEADER_TEXT "Finish Setup" ""
|
||||
!insertmacro MUI_HEADER_TEXT "Remove from %PATH%" ""
|
||||
|
||||
StrCpy $0 0
|
||||
loop:
|
||||
EnumRegKey $1 HKLM "SOFTWARE\WOW6432NODE\Ericsson\Erlang" $0
|
||||
StrCmp $1 "" done
|
||||
ReadRegStr $1 HKLM "SOFTWARE\WOW6432NODE\Ericsson\Erlang\$1" ""
|
||||
StrCpy $OTPPath $1
|
||||
IntOp $0 $0 + 1
|
||||
goto loop
|
||||
done:
|
||||
|
||||
nsDialogs::Create 1018
|
||||
Pop $Dialog
|
||||
@@ -168,6 +221,7 @@ Function un.FinishPageShow
|
||||
${EndIf}
|
||||
|
||||
ReadRegStr $0 HKCU "Environment" "Path"
|
||||
|
||||
${UnStrStr} $1 "$0" "$INSTDIR\bin"
|
||||
${If} $1 != ""
|
||||
${NSD_CreateCheckbox} 0 0 195u 10u "&Remove $INSTDIR\bin from %PATH%"
|
||||
@@ -175,9 +229,6 @@ Function un.FinishPageShow
|
||||
SendMessage $RemoveElixirFromPathCheckbox ${BM_SETCHECK} ${BST_CHECKED} 0
|
||||
${EndIf}
|
||||
|
||||
EnumRegKey $1 HKLM "SOFTWARE\WOW6432NODE\Ericsson\Erlang" 0
|
||||
ReadRegStr $1 HKLM "SOFTWARE\WOW6432NODE\Ericsson\Erlang\$1" ""
|
||||
StrCpy $OTPPath $1
|
||||
${UnStrStr} $1 "$0" "$OTPPath\bin"
|
||||
${If} $1 != ""
|
||||
${NSD_CreateCheckbox} 0 20u 195u 10u "&Remove $OTPPath\bin from %PATH%"
|
||||
@@ -188,22 +239,37 @@ Function un.FinishPageShow
|
||||
nsDialogs::Show
|
||||
FunctionEnd
|
||||
|
||||
var PathsToRemove
|
||||
Function un.FinishPageLeave
|
||||
ReadRegStr $0 HKCU "Environment" "Path"
|
||||
|
||||
${NSD_GetState} $RemoveOTPFromPathCheckbox $1
|
||||
${If} $1 <> ${BST_UNCHECKED}
|
||||
${UnStrRep} $0 "$0" "$OTPPath\bin;" ""
|
||||
StrCpy $PathsToRemove ";$OTPPath\bin"
|
||||
${EndIf}
|
||||
|
||||
${NSD_GetState} $RemoveElixirFromPathCheckbox $1
|
||||
${If} $1 <> ${BST_UNCHECKED}
|
||||
${UnStrRep} $0 "$0" "$INSTDIR\bin;" ""
|
||||
StrCpy $PathsToRemove "$PathsToRemove;$INSTDIR\bin"
|
||||
${EndIf}
|
||||
|
||||
WriteRegExpandStr HKCU "Environment" "Path" "$0"
|
||||
${If} "$PathsToRemove" != ""
|
||||
nsExec::ExecToStack `"$OTPPath\bin\escript.exe" "$INSTDIR\update_system_path.erl" remove "$PathsToRemove"`
|
||||
Pop $0
|
||||
Pop $1
|
||||
${If} $0 != 0
|
||||
SetErrorlevel 5
|
||||
MessageBox MB_ICONSTOP "removing paths failed with $0: $1"
|
||||
Quit
|
||||
${EndIf}
|
||||
${EndIf}
|
||||
FunctionEnd
|
||||
|
||||
UninstPage custom un.FinishPageShow un.FinishPageLeave
|
||||
|
||||
!insertmacro MUI_UNPAGE_DIRECTORY
|
||||
!insertmacro MUI_UNPAGE_INSTFILES
|
||||
|
||||
Section "Uninstall"
|
||||
RMDir /r "$INSTDIR"
|
||||
SectionEnd
|
||||
|
||||
!insertmacro MUI_LANGUAGE "English"
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
#!/usr/bin/env escript
|
||||
%%! -noinput
|
||||
|
||||
%% This file is used by the Elixir installer and uninstaller.
|
||||
main(["add", ";" ++ PathsToAdd]) ->
|
||||
{ok, Reg} = win32reg:open([read, write]),
|
||||
ok = win32reg:change_key(Reg, "\\hkey_current_user\\environment"),
|
||||
{ok, SystemPath} = win32reg:value(Reg, "path"),
|
||||
|
||||
NewSystemPath =
|
||||
lists:foldl(
|
||||
fun(Elem, Acc) ->
|
||||
Elem ++ ";" ++
|
||||
binary_to_list(
|
||||
iolist_to_binary(
|
||||
string:replace(Acc, Elem ++ ";", "", all)))
|
||||
end,
|
||||
SystemPath,
|
||||
string:split(PathsToAdd, ";", all)
|
||||
),
|
||||
|
||||
ok = win32reg:set_value(Reg, "Path", NewSystemPath),
|
||||
ok;
|
||||
|
||||
main(["remove", ";" ++ PathsToRemove]) ->
|
||||
{ok, Reg} = win32reg:open([read, write]),
|
||||
ok = win32reg:change_key(Reg, "\\hkey_current_user\\environment"),
|
||||
{ok, SystemPath} = win32reg:value(Reg, "path"),
|
||||
|
||||
NewSystemPath =
|
||||
lists:foldl(
|
||||
fun(Elem, Acc) ->
|
||||
binary_to_list(
|
||||
iolist_to_binary(
|
||||
string:replace(Acc, Elem ++ ";", "", all)))
|
||||
end,
|
||||
SystemPath,
|
||||
string:split(PathsToRemove, ";", all)
|
||||
),
|
||||
|
||||
ok = win32reg:set_value(Reg, "Path", NewSystemPath),
|
||||
ok.
|
||||
@@ -130,11 +130,15 @@ dispatch_require(Meta, Receiver, Name, Args, S, E, Callback) when is_atom(Receiv
|
||||
|
||||
case elixir_rewrite:inline(Receiver, Name, Arity) of
|
||||
{AR, AN} ->
|
||||
elixir_env:trace({remote_function, Meta, Receiver, Name, Arity}, E),
|
||||
Callback(AR, AN, Args);
|
||||
false ->
|
||||
case expand_require(Meta, Receiver, {Name, Arity}, Args, S, E) of
|
||||
{ok, Receiver, Quoted} -> expand_quoted(Meta, Receiver, Name, Arity, Quoted, S, E);
|
||||
error -> Callback(Receiver, Name, Args)
|
||||
{ok, Receiver, Quoted} ->
|
||||
expand_quoted(Meta, Receiver, Name, Arity, Quoted, S, E);
|
||||
error ->
|
||||
elixir_env:trace({remote_function, Meta, Receiver, Name, Arity}, E),
|
||||
Callback(Receiver, Name, Args)
|
||||
end
|
||||
end;
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ normalize_rescue(Meta, Var, Pattern, Expr, ErlangAliases) ->
|
||||
dynamic_normalize(Meta, Var, ?REQUIRES_STACKTRACE);
|
||||
|
||||
false ->
|
||||
case lists:splitwith(fun is_normalized_with_stacktrace/1, ErlangAliases) of
|
||||
case lists:partition(fun is_normalized_with_stacktrace/1, ErlangAliases) of
|
||||
{[], _} -> [];
|
||||
{_, []} -> {'__STACKTRACE__', Meta, nil};
|
||||
{Some, _} -> dynamic_normalize(Meta, Var, Some)
|
||||
|
||||
@@ -856,10 +856,6 @@ expand_remote(Receiver, DotMeta, Right, Meta, Args, S, SL, #{context := Context}
|
||||
|
||||
_ ->
|
||||
AttachedDotMeta = attach_context_module(Receiver, DotMeta, E),
|
||||
|
||||
is_atom(Receiver) andalso
|
||||
elixir_env:trace({remote_function, Meta, Receiver, Right, length(Args)}, E),
|
||||
|
||||
{EArgs, {SA, _}, EA} = mapfold(fun expand_arg/3, {SL, S}, E, Args),
|
||||
|
||||
case rewrite(Context, Receiver, AttachedDotMeta, Right, Meta, EArgs, S) of
|
||||
|
||||
@@ -118,11 +118,11 @@ format_error({function_conflict, {Receiver, {Name, Arity}}}) ->
|
||||
format_error({unused_args, {Name, Arity}}) ->
|
||||
io_lib:format("default values for the optional arguments in ~ts/~B are never used", [Name, Arity]);
|
||||
|
||||
format_error({unused_args, {Name, Arity}, 1}) ->
|
||||
io_lib:format("the default value for the first optional argument in ~ts/~B is never used", [Name, Arity]);
|
||||
format_error({unused_args, {Name, Arity}, Count}) when Arity - Count == 1 ->
|
||||
io_lib:format("the default value for the last optional argument in ~ts/~B is never used", [Name, Arity]);
|
||||
|
||||
format_error({unused_args, {Name, Arity}, Count}) ->
|
||||
io_lib:format("the default values for the first ~B optional arguments in ~ts/~B are never used", [Count, Name, Arity]);
|
||||
io_lib:format("the default values for the last ~B optional arguments in ~ts/~B are never used", [Arity - Count, Name, Arity]);
|
||||
|
||||
format_error({unused_def, {Name, Arity}, defp}) ->
|
||||
io_lib:format("function ~ts/~B is unused", [Name, Arity]);
|
||||
|
||||
@@ -275,6 +275,8 @@ defmodule Code.Formatter.ContainersTest do
|
||||
assert_format "<< 1 :: 2 - unit(3) >>", "<<1::2-unit(3)>>"
|
||||
assert_format "<< 1 :: 2 * 3 - unit(4) >>", "<<1::2*3-unit(4)>>"
|
||||
assert_format "<< 1 :: 2 - unit(3) - 4 / 5 >>", "<<1::2-unit(3)-(4 / 5)>>"
|
||||
assert_format "<<0 :: ( x - 1 ) * 5>>", "<<0::(x-1)*5>>"
|
||||
assert_format "<<0 :: 2 * 3 * 4>>", "<<0::(2*3)*4>>"
|
||||
end
|
||||
|
||||
test "in comprehensions" do
|
||||
|
||||
@@ -738,6 +738,14 @@ defmodule CodeFragmentTest do
|
||||
end: {3, 5}
|
||||
}
|
||||
end
|
||||
|
||||
for i <- 1..11 do
|
||||
assert CF.surround_context("Foo.Bar.Baz.foo(bar)", {1, i}) == %{
|
||||
context: {:alias, ~c"Foo.Bar.Baz"},
|
||||
begin: {1, 1},
|
||||
end: {1, 12}
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
test "underscored special forms" do
|
||||
@@ -747,17 +755,21 @@ defmodule CodeFragmentTest do
|
||||
end: {1, 11}
|
||||
}
|
||||
|
||||
assert CF.surround_context("__MODULE__.Foo", {1, 12}) == %{
|
||||
context: {:alias, {:local_or_var, ~c"__MODULE__"}, ~c"Foo"},
|
||||
begin: {1, 1},
|
||||
end: {1, 15}
|
||||
}
|
||||
for i <- 1..15 do
|
||||
assert CF.surround_context("__MODULE__.Foo", {1, i}) == %{
|
||||
context: {:alias, {:local_or_var, ~c"__MODULE__"}, ~c"Foo"},
|
||||
begin: {1, 1},
|
||||
end: {1, 15}
|
||||
}
|
||||
end
|
||||
|
||||
assert CF.surround_context("__MODULE__.Foo.Sub", {1, 16}) == %{
|
||||
context: {:alias, {:local_or_var, ~c"__MODULE__"}, ~c"Foo.Sub"},
|
||||
begin: {1, 1},
|
||||
end: {1, 19}
|
||||
}
|
||||
for i <- 1..19 do
|
||||
assert CF.surround_context("__MODULE__.Foo.Sub", {1, i}) == %{
|
||||
context: {:alias, {:local_or_var, ~c"__MODULE__"}, ~c"Foo.Sub"},
|
||||
begin: {1, 1},
|
||||
end: {1, 19}
|
||||
}
|
||||
end
|
||||
|
||||
assert CF.surround_context("%__MODULE__{}", {1, 5}) == %{
|
||||
context: {:struct, {:local_or_var, ~c"__MODULE__"}},
|
||||
@@ -803,17 +815,21 @@ defmodule CodeFragmentTest do
|
||||
end
|
||||
|
||||
test "attribute submodules" do
|
||||
assert CF.surround_context("@some.Foo", {1, 8}) == %{
|
||||
context: {:alias, {:module_attribute, ~c"some"}, ~c"Foo"},
|
||||
begin: {1, 1},
|
||||
end: {1, 10}
|
||||
}
|
||||
for i <- 1..10 do
|
||||
assert CF.surround_context("@some.Foo", {1, i}) == %{
|
||||
context: {:alias, {:module_attribute, ~c"some"}, ~c"Foo"},
|
||||
begin: {1, 1},
|
||||
end: {1, 10}
|
||||
}
|
||||
end
|
||||
|
||||
assert CF.surround_context("@some.Foo.Sub", {1, 12}) == %{
|
||||
context: {:alias, {:module_attribute, ~c"some"}, ~c"Foo.Sub"},
|
||||
begin: {1, 1},
|
||||
end: {1, 14}
|
||||
}
|
||||
for i <- 1..14 do
|
||||
assert CF.surround_context("@some.Foo.Sub", {1, i}) == %{
|
||||
context: {:alias, {:module_attribute, ~c"some"}, ~c"Foo.Sub"},
|
||||
begin: {1, 1},
|
||||
end: {1, 14}
|
||||
}
|
||||
end
|
||||
|
||||
assert CF.surround_context("%@some{}", {1, 5}) == %{
|
||||
context: {:struct, {:module_attribute, ~c"some"}},
|
||||
|
||||
@@ -226,6 +226,11 @@ defmodule Code.Normalizer.QuotedASTTest do
|
||||
assert quoted_to_string(quoted) <> "\n" == expected
|
||||
end
|
||||
|
||||
test "invalid block" do
|
||||
assert quoted_to_string({:__block__, [], {:bar, [], []}}) ==
|
||||
"{:__block__, [], {:bar, [], []}}"
|
||||
end
|
||||
|
||||
test "not in" do
|
||||
assert quoted_to_string(quote(do: false not in [])) == "false not in []"
|
||||
end
|
||||
|
||||
@@ -224,6 +224,17 @@ defmodule Kernel.RaiseTest do
|
||||
assert result == "an exception"
|
||||
end
|
||||
|
||||
test "named function clause (stacktrace) or runtime (no stacktrace) error" do
|
||||
result =
|
||||
try do
|
||||
Access.get("foo", 0)
|
||||
rescue
|
||||
x in [FunctionClauseError, CaseClauseError] -> Exception.message(x)
|
||||
end
|
||||
|
||||
assert result == "no function clause matching in Access.get/3"
|
||||
end
|
||||
|
||||
test "with higher precedence than catch" do
|
||||
result =
|
||||
try do
|
||||
|
||||
@@ -118,6 +118,7 @@ defmodule Kernel.TracersTest do
|
||||
require Integer
|
||||
true = Integer.is_odd(1)
|
||||
{1, ""} = Integer.parse("1")
|
||||
"foo" = Atom.to_string(:foo)
|
||||
""")
|
||||
|
||||
assert_receive {{:remote_macro, meta, Integer, :is_odd, 1}, _}
|
||||
@@ -127,6 +128,10 @@ defmodule Kernel.TracersTest do
|
||||
assert_receive {{:remote_function, meta, Integer, :parse, 1}, _}
|
||||
assert meta[:line] == 3
|
||||
assert meta[:column] == 19
|
||||
|
||||
assert_receive {{:remote_function, meta, Atom, :to_string, 1}, _}
|
||||
assert meta[:line] == 4
|
||||
assert meta[:column] == 14
|
||||
end
|
||||
|
||||
test "traces remote via captures" do
|
||||
|
||||
@@ -563,17 +563,17 @@ defmodule Kernel.WarningTest do
|
||||
end
|
||||
""")
|
||||
end) =~
|
||||
"the default values for the first 2 optional arguments in b/3 are never used\n nofile:3"
|
||||
"the default value for the last optional argument in b/3 is never used\n nofile:3"
|
||||
|
||||
assert capture_err(fn ->
|
||||
Code.eval_string(~S"""
|
||||
defmodule Sample3 do
|
||||
def a, do: b(1)
|
||||
defp b(arg1 \\ 1, arg2 \\ 2, arg3 \\ 3), do: [arg1, arg2, arg3]
|
||||
def a, do: b(1, 2)
|
||||
defp b(arg1, arg2 \\ 2, arg3 \\ 3, arg4 \\ 4), do: [arg1, arg2, arg3, arg4]
|
||||
end
|
||||
""")
|
||||
end) =~
|
||||
"the default value for the first optional argument in b/3 is never used\n nofile:3"
|
||||
"the default values for the last 2 optional arguments in b/4 are never used"
|
||||
|
||||
assert capture_err(fn ->
|
||||
Code.eval_string(~S"""
|
||||
@@ -587,17 +587,6 @@ defmodule Kernel.WarningTest do
|
||||
assert capture_err(fn ->
|
||||
Code.eval_string(~S"""
|
||||
defmodule Sample5 do
|
||||
def a, do: b(1, 2, 3)
|
||||
defp b(arg1 \\ 1, arg2 \\ 2, arg3 \\ 3)
|
||||
|
||||
defp b(arg1, arg2, arg3), do: [arg1, arg2, arg3]
|
||||
end
|
||||
""")
|
||||
end) =~ "default values for the optional arguments in b/3 are never used\n nofile:3"
|
||||
|
||||
assert capture_err(fn ->
|
||||
Code.eval_string(~S"""
|
||||
defmodule Sample6 do
|
||||
def a, do: b(1, 2)
|
||||
defp b(arg1 \\ 1, arg2 \\ 2, arg3 \\ 3)
|
||||
|
||||
@@ -605,9 +594,9 @@ defmodule Kernel.WarningTest do
|
||||
end
|
||||
""")
|
||||
end) =~
|
||||
"the default values for the first 2 optional arguments in b/3 are never used\n nofile:3"
|
||||
"the default value for the last optional argument in b/3 is never used\n nofile:3"
|
||||
after
|
||||
purge([Sample1, Sample2, Sample3, Sample4, Sample5, Sample6])
|
||||
purge([Sample1, Sample2, Sample3, Sample4, Sample5])
|
||||
end
|
||||
|
||||
test "unused import" do
|
||||
|
||||
@@ -200,11 +200,15 @@ defmodule ExUnit.Diff do
|
||||
|
||||
{diff, env}
|
||||
else
|
||||
diff_value(left, right, env)
|
||||
non_recursive_diff_value(left, right, env)
|
||||
end
|
||||
end
|
||||
|
||||
defp diff_value(left, right, env) do
|
||||
non_recursive_diff_value(left, right, env)
|
||||
end
|
||||
|
||||
defp non_recursive_diff_value(left, right, env) do
|
||||
diff_left = escape(left) |> update_diff_meta(true)
|
||||
diff_right = escape(right) |> update_diff_meta(true)
|
||||
diff = %__MODULE__{equivalent?: false, left: diff_left, right: diff_right}
|
||||
|
||||
@@ -1121,6 +1121,8 @@ defmodule ExUnit.DiffTest do
|
||||
)
|
||||
end
|
||||
|
||||
@compile {:no_warn_undefined, String}
|
||||
|
||||
test "functions" do
|
||||
identity = & &1
|
||||
inspect = inspect(identity)
|
||||
@@ -1130,11 +1132,15 @@ defmodule ExUnit.DiffTest do
|
||||
|
||||
refute_diff(identity == :a, "-#{inspect}-", "+:a+")
|
||||
refute_diff({identity, identity} == :a, "-{#{inspect}, #{inspect}}", "+:a+")
|
||||
|
||||
refute_diff({identity, :a} == {:a, identity}, "{-#{inspect}-, -:a-}", "{+:a+, +#{inspect}+}")
|
||||
|
||||
refute_diff(%{identity => identity} == :a, "-%{#{inspect} => #{inspect}}", "+:a+")
|
||||
|
||||
refute_diff(
|
||||
(&String.to_charlist/1) == (&String.unknown/1),
|
||||
"-&String.to_charlist/1-",
|
||||
"+&String.unknown/1"
|
||||
)
|
||||
|
||||
refute_diff(
|
||||
%Opaque{data: identity} == :a,
|
||||
"-#Opaque<???>-",
|
||||
|
||||
@@ -871,6 +871,7 @@ defmodule IEx do
|
||||
if Code.ensure_loaded?(:prim_tty) do
|
||||
spawn(fn ->
|
||||
{:ok, _} = Application.ensure_all_started(:iex)
|
||||
:ok = :io.setopts(binary: true, encoding: :unicode)
|
||||
_ = for fun <- Enum.reverse(after_spawn()), do: fun.()
|
||||
IEx.Server.run([register: false] ++ opts)
|
||||
end)
|
||||
|
||||
@@ -146,7 +146,7 @@ defmodule IEx.Autocomplete do
|
||||
|
||||
@doc false
|
||||
def exports(mod) do
|
||||
if Code.ensure_loaded?(mod) and function_exported?(mod, :__info__, 1) do
|
||||
if ensure_loaded?(mod) and function_exported?(mod, :__info__, 1) do
|
||||
mod.__info__(:macros) ++ (mod.__info__(:functions) -- [__info__: 1])
|
||||
else
|
||||
mod.module_info(:exports) -- [module_info: 0, module_info: 1]
|
||||
@@ -313,21 +313,23 @@ defmodule IEx.Autocomplete do
|
||||
for {alias, mod} <- aliases_from_env(shell),
|
||||
[name] = Module.split(alias),
|
||||
String.starts_with?(name, hint),
|
||||
struct?(mod) and not function_exported?(mod, :exception, 1),
|
||||
do: %{kind: :struct, name: name}
|
||||
do: {mod, name}
|
||||
|
||||
modules =
|
||||
for "Elixir." <> name = full_name <- match_modules("Elixir." <> hint, true),
|
||||
String.starts_with?(name, hint),
|
||||
mod = String.to_atom(full_name),
|
||||
struct?(mod) and not function_exported?(mod, :exception, 1),
|
||||
do: {mod, name}
|
||||
|
||||
all = aliases ++ modules
|
||||
Code.ensure_all_loaded(Enum.map(all, &elem(&1, 0)))
|
||||
|
||||
refs =
|
||||
for {mod, name} <- all,
|
||||
function_exported?(mod, :__struct__, 1) and not function_exported?(mod, :exception, 1),
|
||||
do: %{kind: :struct, name: name}
|
||||
|
||||
format_expansion(aliases ++ modules, hint)
|
||||
end
|
||||
|
||||
defp struct?(mod) do
|
||||
Code.ensure_loaded?(mod) and function_exported?(mod, :__struct__, 1)
|
||||
format_expansion(refs, hint)
|
||||
end
|
||||
|
||||
defp expand_container_context(code, context, hint, shell) do
|
||||
@@ -420,7 +422,9 @@ defmodule IEx.Autocomplete do
|
||||
defp container_context_struct(cursor, pairs, aliases, shell) do
|
||||
with {pairs, [^cursor]} <- Enum.split(pairs, -1),
|
||||
alias = value_from_alias(aliases, shell),
|
||||
true <- Keyword.keyword?(pairs) and struct?(alias) do
|
||||
true <-
|
||||
Keyword.keyword?(pairs) and ensure_loaded?(alias) and
|
||||
function_exported?(alias, :__struct__, 1) do
|
||||
{:struct, alias, pairs}
|
||||
else
|
||||
_ -> nil
|
||||
|
||||
@@ -85,9 +85,13 @@ defmodule IEx.Broker do
|
||||
yes?(IO.gets(:stdio, interrupt))
|
||||
end
|
||||
|
||||
defp yes?(string) do
|
||||
is_binary(string) and String.trim(string) in ["", "y", "Y", "yes", "YES", "Yes"]
|
||||
end
|
||||
defp yes?(string) when is_binary(string),
|
||||
do: String.trim(string) in ["", "y", "Y", "yes", "YES", "Yes"]
|
||||
|
||||
defp yes?(charlist) when is_list(charlist),
|
||||
do: yes?(List.to_string(charlist))
|
||||
|
||||
defp yes?(_), do: false
|
||||
|
||||
@doc """
|
||||
Client requests a takeover.
|
||||
|
||||
@@ -454,6 +454,9 @@ defmodule Mix.Compilers.Elixir do
|
||||
8 -> :crypto.hash(:blake2b, contents)
|
||||
_ -> :crypto.hash(:blake2s, contents)
|
||||
end
|
||||
rescue
|
||||
# Blake may not be available on all OpenSSL distribution
|
||||
_ -> :erlang.md5(contents)
|
||||
end
|
||||
|
||||
defp set_compiler_opts(opts) do
|
||||
|
||||
+23
-21
@@ -114,12 +114,12 @@ defmodule Mix.Dep do
|
||||
write_cached_deps(top, {env, target}, load_and_cache(config, top, bottom, env, target))
|
||||
|
||||
_ ->
|
||||
converge(env: env, target: target)
|
||||
converge_and_load(env: env, target: target)
|
||||
end
|
||||
end
|
||||
|
||||
defp load_and_cache(_config, top, top, env, target) do
|
||||
converge(env: env, target: target)
|
||||
converge_and_load(env: env, target: target)
|
||||
end
|
||||
|
||||
defp load_and_cache(config, _top, bottom, _env, _target) do
|
||||
@@ -152,6 +152,20 @@ defmodule Mix.Dep do
|
||||
end)
|
||||
end
|
||||
|
||||
defp converge_and_load(opts) do
|
||||
for %{app: app, opts: opts} = dep <- Mix.Dep.Converger.converge(opts) do
|
||||
case Keyword.pop(opts, :app_properties) do
|
||||
{nil, _opts} ->
|
||||
dep
|
||||
|
||||
{app_properties, opts} ->
|
||||
# We don't raise because child dependencies may be missing if manually cleaned
|
||||
:application.load({:application, app, app_properties})
|
||||
%{dep | opts: opts}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp read_cached_deps(project, env_target) do
|
||||
case Mix.State.read_cache({:cached_deps, project}) do
|
||||
{^env_target, deps} -> deps
|
||||
@@ -164,6 +178,13 @@ defmodule Mix.Dep do
|
||||
deps
|
||||
end
|
||||
|
||||
@doc """
|
||||
Although private API, this is kept to avoid breaking code on patch releases.
|
||||
"""
|
||||
def load_on_environment(opts) do
|
||||
Mix.Dep.Converger.converge(opts)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Clears loaded dependencies from the cache for the current environment.
|
||||
"""
|
||||
@@ -174,25 +195,6 @@ defmodule Mix.Dep do
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns loaded dependencies recursively on the given environment.
|
||||
|
||||
If no environment is passed, dependencies are loaded across all
|
||||
environments. The result is not cached.
|
||||
|
||||
## Exceptions
|
||||
|
||||
This function raises an exception if any of the dependencies
|
||||
provided in the project are in the wrong format.
|
||||
"""
|
||||
def load_on_environment(opts) do
|
||||
converge(opts)
|
||||
end
|
||||
|
||||
defp converge(opts) do
|
||||
Mix.Dep.Converger.converge(nil, nil, opts, &{&1, &2, &3}) |> elem(0)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Filters the given dependencies by name.
|
||||
|
||||
|
||||
@@ -60,6 +60,18 @@ defmodule Mix.Dep.Converger do
|
||||
)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Converge without lock and accumulator updates.
|
||||
|
||||
Note the dependencies returned from converge are not yet loaded.
|
||||
The relevant app keys are found under `dep.opts[:app_properties]`.
|
||||
|
||||
See `Mix.Dep.Loader.children/1` for options.
|
||||
"""
|
||||
def converge(opts \\ []) do
|
||||
converge(nil, nil, opts, &{&1, &2, &3}) |> elem(0)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Converges all dependencies from the current project,
|
||||
including nested dependencies.
|
||||
@@ -68,35 +80,15 @@ defmodule Mix.Dep.Converger do
|
||||
must return an updated dependency in case some processing
|
||||
is done.
|
||||
|
||||
Note the dependencies returned from converge are not yet loaded.
|
||||
The relevant app keys are found under `dep.opts[:app_properties]`.
|
||||
|
||||
See `Mix.Dep.Loader.children/1` for options.
|
||||
"""
|
||||
def converge(acc, lock, opts, callback) do
|
||||
{deps, acc, lock} = all(acc, lock, opts, callback)
|
||||
if remote = Mix.RemoteConverger.get(), do: remote.post_converge()
|
||||
|
||||
deps =
|
||||
for %{app: app, opts: opts} = dep <- topological_sort(deps) do
|
||||
case Keyword.pop(opts, :app_properties) do
|
||||
{nil, _opts} ->
|
||||
dep
|
||||
|
||||
{app_properties, opts} ->
|
||||
case :application.load({:application, app, app_properties}) do
|
||||
:ok ->
|
||||
:ok
|
||||
|
||||
{:error, {:already_loaded, _}} ->
|
||||
:ok
|
||||
|
||||
{:error, error} ->
|
||||
Mix.raise("Could not load application #{inspect(app)}: #{inspect(error)}")
|
||||
end
|
||||
|
||||
%{dep | opts: opts}
|
||||
end
|
||||
end
|
||||
|
||||
{deps, acc, lock}
|
||||
{topological_sort(deps), acc, lock}
|
||||
end
|
||||
|
||||
defp all(acc, lock, opts, callback) do
|
||||
|
||||
@@ -125,7 +125,7 @@ defmodule Mix.Tasks.Compile.All do
|
||||
Enum.reduce(Mix.ProjectStack.pop_after_compiler(compiler), result, & &1.(&2))
|
||||
end
|
||||
|
||||
defp project_apps(config) do
|
||||
def project_apps(config) do
|
||||
project = Mix.Project.get!()
|
||||
|
||||
properties =
|
||||
|
||||
@@ -148,8 +148,7 @@ defmodule Mix.Tasks.Compile do
|
||||
# If we are in an umbrella project, now load paths from all children.
|
||||
if apps_paths = Mix.Project.apps_paths(config) do
|
||||
loaded_paths =
|
||||
apps_paths
|
||||
|> Map.keys()
|
||||
(Mix.Tasks.Compile.All.project_apps(config) ++ Map.keys(apps_paths))
|
||||
|> Mix.AppLoader.load_apps(Mix.Dep.cached(), config, [], fn
|
||||
{_app, path}, acc -> if path, do: [path | acc], else: acc
|
||||
end)
|
||||
|
||||
@@ -41,7 +41,7 @@ defmodule Mix.Tasks.Deps.Clean do
|
||||
value = opts[switch],
|
||||
do: {key, :"#{value}"}
|
||||
|
||||
loaded_deps = Mix.Dep.load_on_environment(loaded_opts)
|
||||
loaded_deps = Mix.Dep.Converger.converge(loaded_opts)
|
||||
|
||||
apps_to_clean =
|
||||
cond do
|
||||
|
||||
@@ -57,9 +57,6 @@ defmodule Mix.Tasks.Deps.Compile do
|
||||
|
||||
case OptionParser.parse(args, switches: @switches) do
|
||||
{opts, [], _} ->
|
||||
# Because this command may be invoked explicitly with
|
||||
# deps.compile, we simply try to compile any available
|
||||
# or local dependency.
|
||||
compile(filter_available_and_local_deps(deps), opts)
|
||||
|
||||
{opts, tail, _} ->
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
defmodule Mix.Tasks.Deps do
|
||||
use Mix.Task
|
||||
|
||||
import Mix.Dep, only: [load_on_environment: 1, format_dep: 1, format_status: 1, check_lock: 1]
|
||||
import Mix.Dep, only: [format_dep: 1, format_status: 1, check_lock: 1]
|
||||
|
||||
@shortdoc "Lists dependencies and their status"
|
||||
|
||||
@@ -167,7 +167,7 @@ defmodule Mix.Tasks.Deps do
|
||||
|
||||
shell = Mix.shell()
|
||||
|
||||
load_on_environment(loaded_opts)
|
||||
Mix.Dep.Converger.converge(loaded_opts)
|
||||
|> Enum.sort_by(& &1.app)
|
||||
|> Enum.each(fn dep ->
|
||||
%Mix.Dep{scm: scm, manager: manager} = dep
|
||||
|
||||
@@ -44,7 +44,7 @@ defmodule Mix.Tasks.Deps.Tree do
|
||||
value = opts[switch],
|
||||
do: {key, :"#{value}"}
|
||||
|
||||
deps = Mix.Dep.load_on_environment(deps_opts)
|
||||
deps = Mix.Dep.Converger.converge(deps_opts)
|
||||
|
||||
root =
|
||||
case args do
|
||||
|
||||
@@ -83,7 +83,7 @@ defmodule Mix.Tasks.Deps.Unlock do
|
||||
end
|
||||
|
||||
defp unused_apps(lock) do
|
||||
apps = Mix.Dep.load_on_environment([]) |> Enum.map(& &1.app)
|
||||
apps = Mix.Dep.Converger.converge([]) |> Enum.map(& &1.app)
|
||||
|
||||
lock
|
||||
|> Map.drop(apps)
|
||||
|
||||
@@ -233,6 +233,8 @@ defmodule Mix.Tasks.Format do
|
||||
{formatter_opts_and_subs, _sources} =
|
||||
eval_deps_and_subdirectories(cwd, dot_formatter, formatter_opts, [dot_formatter])
|
||||
|
||||
formatter_opts_and_subs = load_plugins(formatter_opts_and_subs)
|
||||
|
||||
args
|
||||
|> expand_args(cwd, dot_formatter, formatter_opts_and_subs, opts)
|
||||
|> Task.async_stream(&format_file(&1, opts), ordered: false, timeout: :infinity)
|
||||
@@ -240,67 +242,11 @@ defmodule Mix.Tasks.Format do
|
||||
|> check!(opts)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns a formatter function and the formatter options to
|
||||
be used for the given file.
|
||||
|
||||
The function must be called with the contents of the file
|
||||
to be formatted. The options are returned for reflection
|
||||
purposes.
|
||||
"""
|
||||
@doc since: "1.13.0"
|
||||
def formatter_for_file(file, opts \\ []) do
|
||||
cwd = Keyword.get_lazy(opts, :root, &File.cwd!/0)
|
||||
{dot_formatter, formatter_opts} = eval_dot_formatter(cwd, opts)
|
||||
|
||||
{formatter_opts_and_subs, _sources} =
|
||||
eval_deps_and_subdirectories(cwd, dot_formatter, formatter_opts, [dot_formatter])
|
||||
|
||||
find_formatter_and_opts_for_file(Path.expand(file, cwd), formatter_opts_and_subs)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns formatter options to be used for the given file.
|
||||
"""
|
||||
# TODO: Deprecate on Elixir v1.17
|
||||
@doc deprecated: "Use formatter_for_file/2 instead"
|
||||
def formatter_opts_for_file(file, opts \\ []) do
|
||||
{_, formatter_opts} = formatter_for_file(file, opts)
|
||||
formatter_opts
|
||||
end
|
||||
|
||||
defp eval_dot_formatter(cwd, opts) do
|
||||
cond do
|
||||
dot_formatter = opts[:dot_formatter] ->
|
||||
{dot_formatter, eval_file_with_keyword_list(dot_formatter)}
|
||||
|
||||
File.regular?(Path.join(cwd, ".formatter.exs")) ->
|
||||
dot_formatter = Path.join(cwd, ".formatter.exs")
|
||||
{".formatter.exs", eval_file_with_keyword_list(dot_formatter)}
|
||||
|
||||
true ->
|
||||
{".formatter.exs", []}
|
||||
end
|
||||
end
|
||||
|
||||
# This function reads exported configuration from the imported
|
||||
# dependencies and subdirectories and deals with caching the result
|
||||
# of reading such configuration in a manifest file.
|
||||
defp eval_deps_and_subdirectories(cwd, dot_formatter, formatter_opts, sources) do
|
||||
deps = Keyword.get(formatter_opts, :import_deps, [])
|
||||
subs = Keyword.get(formatter_opts, :subdirectories, [])
|
||||
defp load_plugins({formatter_opts, subs}) do
|
||||
plugins = Keyword.get(formatter_opts, :plugins, [])
|
||||
|
||||
if not is_list(deps) do
|
||||
Mix.raise("Expected :import_deps to return a list of dependencies, got: #{inspect(deps)}")
|
||||
end
|
||||
|
||||
if not is_list(subs) do
|
||||
Mix.raise("Expected :subdirectories to return a list of directories, got: #{inspect(subs)}")
|
||||
end
|
||||
|
||||
if not is_list(plugins) do
|
||||
Mix.raise("Expected :plugins to return a list of modules, got: #{inspect(plugins)}")
|
||||
Mix.raise("Expected :plugins to return a list of directories, got: #{inspect(plugins)}")
|
||||
end
|
||||
|
||||
if plugins != [] do
|
||||
@@ -341,10 +287,69 @@ defmodule Mix.Tasks.Format do
|
||||
end}
|
||||
end)
|
||||
|
||||
formatter_opts =
|
||||
formatter_opts
|
||||
|> Keyword.put(:plugins, plugins)
|
||||
|> Keyword.put(:sigils, sigils)
|
||||
{Keyword.put(formatter_opts, :sigils, sigils),
|
||||
Enum.map(subs, fn {path, opts} -> {path, load_plugins(opts)} end)}
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns a formatter function and the formatter options to
|
||||
be used for the given file.
|
||||
|
||||
The function must be called with the contents of the file
|
||||
to be formatted. The options are returned for reflection
|
||||
purposes.
|
||||
"""
|
||||
@doc since: "1.13.0"
|
||||
def formatter_for_file(file, opts \\ []) do
|
||||
cwd = Keyword.get_lazy(opts, :root, &File.cwd!/0)
|
||||
{dot_formatter, formatter_opts} = eval_dot_formatter(cwd, opts)
|
||||
|
||||
{formatter_opts_and_subs, _sources} =
|
||||
eval_deps_and_subdirectories(cwd, dot_formatter, formatter_opts, [dot_formatter])
|
||||
|
||||
formatter_opts_and_subs = load_plugins(formatter_opts_and_subs)
|
||||
|
||||
find_formatter_and_opts_for_file(Path.expand(file, cwd), formatter_opts_and_subs)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns formatter options to be used for the given file.
|
||||
"""
|
||||
# TODO: Deprecate on Elixir v1.17
|
||||
@doc deprecated: "Use formatter_for_file/2 instead"
|
||||
def formatter_opts_for_file(file, opts \\ []) do
|
||||
{_, formatter_opts} = formatter_for_file(file, opts)
|
||||
formatter_opts
|
||||
end
|
||||
|
||||
defp eval_dot_formatter(cwd, opts) do
|
||||
cond do
|
||||
dot_formatter = opts[:dot_formatter] ->
|
||||
{dot_formatter, eval_file_with_keyword_list(dot_formatter)}
|
||||
|
||||
File.regular?(Path.join(cwd, ".formatter.exs")) ->
|
||||
dot_formatter = Path.join(cwd, ".formatter.exs")
|
||||
{".formatter.exs", eval_file_with_keyword_list(dot_formatter)}
|
||||
|
||||
true ->
|
||||
{".formatter.exs", []}
|
||||
end
|
||||
end
|
||||
|
||||
# This function reads exported configuration from the imported
|
||||
# dependencies and subdirectories and deals with caching the result
|
||||
# of reading such configuration in a manifest file.
|
||||
defp eval_deps_and_subdirectories(cwd, dot_formatter, formatter_opts, sources) do
|
||||
deps = Keyword.get(formatter_opts, :import_deps, [])
|
||||
subs = Keyword.get(formatter_opts, :subdirectories, [])
|
||||
|
||||
if not is_list(deps) do
|
||||
Mix.raise("Expected :import_deps to return a list of dependencies, got: #{inspect(deps)}")
|
||||
end
|
||||
|
||||
if not is_list(subs) do
|
||||
Mix.raise("Expected :subdirectories to return a list of directories, got: #{inspect(subs)}")
|
||||
end
|
||||
|
||||
if deps == [] and subs == [] do
|
||||
{{formatter_opts, []}, sources}
|
||||
@@ -448,7 +453,8 @@ defmodule Mix.Tasks.Format do
|
||||
_ ->
|
||||
Mix.raise(
|
||||
"Unknown dependency #{inspect(dep)} given to :import_deps in the formatter configuration. " <>
|
||||
"Make sure the dependency is listed in your mix.exs and you have run \"mix deps.get\""
|
||||
"Make sure the dependency is listed in your mix.exs for environment #{inspect(Mix.env())} " <>
|
||||
"and you have run \"mix deps.get\""
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -4,4 +4,8 @@ defmodule Umbrella.MixProject do
|
||||
def project do
|
||||
[apps_path: "apps"]
|
||||
end
|
||||
|
||||
def application do
|
||||
[extra_applications: [:runtime_tools]]
|
||||
end
|
||||
end
|
||||
|
||||
@@ -36,7 +36,7 @@ defmodule Mix.DepTest do
|
||||
defp assert_wrong_dependency(deps) do
|
||||
with_deps(deps, fn ->
|
||||
assert_raise Mix.Error, ~r"Dependency specified in the wrong format", fn ->
|
||||
Mix.Dep.load_on_environment([])
|
||||
Mix.Dep.Converger.converge([])
|
||||
end
|
||||
end)
|
||||
end
|
||||
@@ -59,7 +59,7 @@ defmodule Mix.DepTest do
|
||||
in_fixture("deps_status", fn ->
|
||||
Mix.Project.push(DepsApp)
|
||||
|
||||
deps = Mix.Dep.load_on_environment([])
|
||||
deps = Mix.Dep.Converger.converge([])
|
||||
assert length(deps) == 6
|
||||
assert Enum.find(deps, &match?(%Mix.Dep{app: :ok, status: {:ok, _}}, &1))
|
||||
assert Enum.find(deps, &match?(%Mix.Dep{app: :invalidvsn, status: {:invalidvsn, :ok}}, &1))
|
||||
@@ -102,7 +102,7 @@ defmodule Mix.DepTest do
|
||||
|
||||
with_deps(deps, fn ->
|
||||
in_fixture("deps_status", fn ->
|
||||
deps = Mix.Dep.load_on_environment([])
|
||||
deps = Mix.Dep.Converger.converge([])
|
||||
assert Enum.find(deps, &match?(%Mix.Dep{app: :ok, status: {:ok, _}}, &1))
|
||||
end)
|
||||
end)
|
||||
@@ -115,7 +115,7 @@ defmodule Mix.DepTest do
|
||||
in_fixture("deps_status", fn ->
|
||||
send(self(), {:mix_shell_input, :yes?, false})
|
||||
msg = "Could not find an SCM for dependency :ok from Mix.DepTest.ProcessDepsApp"
|
||||
assert_raise Mix.Error, msg, fn -> Mix.Dep.load_on_environment([]) end
|
||||
assert_raise Mix.Error, msg, fn -> Mix.Dep.Converger.converge([]) end
|
||||
end)
|
||||
end)
|
||||
end
|
||||
@@ -139,7 +139,7 @@ defmodule Mix.DepTest do
|
||||
with_deps(deps, fn ->
|
||||
in_fixture("deps_status", fn ->
|
||||
assert_raise Mix.Error, ~r"Invalid requirement", fn ->
|
||||
Mix.Dep.load_on_environment([])
|
||||
Mix.Dep.Converger.converge([])
|
||||
end
|
||||
end)
|
||||
end)
|
||||
@@ -150,7 +150,7 @@ defmodule Mix.DepTest do
|
||||
|
||||
with_deps(deps, fn ->
|
||||
in_fixture("deps_status", fn ->
|
||||
assert Enum.map(Mix.Dep.load_on_environment([]), & &1.app) == [:git_repo, :deps_repo]
|
||||
assert Enum.map(Mix.Dep.Converger.converge([]), & &1.app) == [:git_repo, :deps_repo]
|
||||
end)
|
||||
end)
|
||||
end
|
||||
@@ -172,7 +172,7 @@ defmodule Mix.DepTest do
|
||||
end
|
||||
""")
|
||||
|
||||
assert Enum.map(Mix.Dep.load_on_environment([]), & &1.app) == [:deps_repo]
|
||||
assert Enum.map(Mix.Dep.Converger.converge([]), & &1.app) == [:deps_repo]
|
||||
end)
|
||||
end)
|
||||
end
|
||||
@@ -185,7 +185,7 @@ defmodule Mix.DepTest do
|
||||
|
||||
with_deps(deps, fn ->
|
||||
in_fixture("deps_status", fn ->
|
||||
assert Enum.map(Mix.Dep.load_on_environment([]), & &1.app) == [:git_repo, :deps_repo]
|
||||
assert Enum.map(Mix.Dep.Converger.converge([]), & &1.app) == [:git_repo, :deps_repo]
|
||||
end)
|
||||
end)
|
||||
end
|
||||
@@ -200,7 +200,7 @@ defmodule Mix.DepTest do
|
||||
|
||||
with_deps(deps, fn ->
|
||||
in_fixture("deps_status", fn ->
|
||||
[dep1, dep2] = Mix.Dep.load_on_environment([])
|
||||
[dep1, dep2] = Mix.Dep.Converger.converge([])
|
||||
assert dep1.manager == nil
|
||||
assert dep2.manager == :rebar3
|
||||
end)
|
||||
@@ -252,7 +252,7 @@ defmodule Mix.DepTest do
|
||||
end
|
||||
""")
|
||||
|
||||
assert Enum.map(Mix.Dep.load_on_environment([]), & &1.app) == [:git_repo, :deps_repo]
|
||||
assert Enum.map(Mix.Dep.Converger.converge([]), & &1.app) == [:git_repo, :deps_repo]
|
||||
end)
|
||||
end)
|
||||
end
|
||||
@@ -362,7 +362,7 @@ defmodule Mix.DepTest do
|
||||
|
||||
with_deps(deps, fn ->
|
||||
in_tmp("load dependency with env vars", fn ->
|
||||
Mix.Dep.load_on_environment([])
|
||||
Mix.Dep.Converger.converge([])
|
||||
assert {:ok, "contents dep test"} = File.read(file_path)
|
||||
end)
|
||||
end)
|
||||
@@ -378,7 +378,7 @@ defmodule Mix.DepTest do
|
||||
|
||||
with_deps(deps, fn ->
|
||||
in_fixture("deps_status", fn ->
|
||||
[git_repo, _] = Mix.Dep.load_on_environment([])
|
||||
[git_repo, _] = Mix.Dep.Converger.converge([])
|
||||
%{app: :git_repo, status: {:overridden, _}} = git_repo
|
||||
end)
|
||||
end)
|
||||
@@ -549,7 +549,7 @@ defmodule Mix.DepTest do
|
||||
with_deps(deps, fn ->
|
||||
in_fixture("deps_status", fn ->
|
||||
# Both orders below are valid after topological sort
|
||||
assert Enum.map(Mix.Dep.load_on_environment([]), & &1.app) in [
|
||||
assert Enum.map(Mix.Dep.Converger.converge([]), & &1.app) in [
|
||||
[:git_repo, :abc_repo, :deps_repo],
|
||||
[:abc_repo, :git_repo, :deps_repo]
|
||||
]
|
||||
@@ -604,7 +604,7 @@ defmodule Mix.DepTest do
|
||||
with_deps(deps, fn ->
|
||||
in_fixture("deps_status", fn ->
|
||||
# Both orders below are valid after topological sort
|
||||
assert Enum.map(Mix.Dep.load_on_environment([]), & &1.app) in [
|
||||
assert Enum.map(Mix.Dep.Converger.converge([]), & &1.app) in [
|
||||
[:git_repo, :abc_repo, :deps_repo],
|
||||
[:abc_repo, :git_repo, :deps_repo]
|
||||
]
|
||||
@@ -639,13 +639,13 @@ defmodule Mix.DepTest do
|
||||
|
||||
with_deps(deps, fn ->
|
||||
in_fixture("deps_status", fn ->
|
||||
deps = Mix.Dep.load_on_environment(env: :other_env)
|
||||
deps = Mix.Dep.Converger.converge(env: :other_env)
|
||||
assert length(deps) == 2
|
||||
|
||||
deps = Mix.Dep.load_on_environment([])
|
||||
deps = Mix.Dep.Converger.converge([])
|
||||
assert length(deps) == 2
|
||||
|
||||
assert [dep] = Mix.Dep.load_on_environment(env: :prod)
|
||||
assert [dep] = Mix.Dep.Converger.converge(env: :prod)
|
||||
assert dep.app == :foo
|
||||
end)
|
||||
end)
|
||||
@@ -676,10 +676,10 @@ defmodule Mix.DepTest do
|
||||
|
||||
with_deps(deps, fn ->
|
||||
in_fixture("deps_status", fn ->
|
||||
loaded = Mix.Dep.load_on_environment([])
|
||||
loaded = Mix.Dep.Converger.converge([])
|
||||
assert [:deps_repo] = Enum.map(loaded, & &1.app)
|
||||
|
||||
loaded = Mix.Dep.load_on_environment(env: :test)
|
||||
loaded = Mix.Dep.Converger.converge(env: :test)
|
||||
assert [:deps_repo] = Enum.map(loaded, & &1.app)
|
||||
end)
|
||||
end)
|
||||
@@ -694,15 +694,15 @@ defmodule Mix.DepTest do
|
||||
|
||||
with_deps(deps, fn ->
|
||||
in_fixture("deps_status", fn ->
|
||||
loaded = Mix.Dep.load_on_environment([])
|
||||
loaded = Mix.Dep.Converger.converge([])
|
||||
assert [:git_repo, :deps_repo] = Enum.map(loaded, & &1.app)
|
||||
assert [divergedonly: _, noappfile: {_, _}] = Enum.map(loaded, & &1.status)
|
||||
|
||||
loaded = Mix.Dep.load_on_environment(env: :dev)
|
||||
loaded = Mix.Dep.Converger.converge(env: :dev)
|
||||
assert [:git_repo, :deps_repo] = Enum.map(loaded, & &1.app)
|
||||
assert [divergedonly: _, noappfile: {_, _}] = Enum.map(loaded, & &1.status)
|
||||
|
||||
loaded = Mix.Dep.load_on_environment(env: :test)
|
||||
loaded = Mix.Dep.Converger.converge(env: :test)
|
||||
assert [:git_repo, :deps_repo] = Enum.map(loaded, & &1.app)
|
||||
assert [divergedonly: _, noappfile: {_, _}] = Enum.map(loaded, & &1.status)
|
||||
|
||||
@@ -727,15 +727,15 @@ defmodule Mix.DepTest do
|
||||
|
||||
with_deps(deps, fn ->
|
||||
in_fixture("deps_status", fn ->
|
||||
loaded = Mix.Dep.load_on_environment([])
|
||||
loaded = Mix.Dep.Converger.converge([])
|
||||
assert [:git_repo, :deps_repo] = Enum.map(loaded, & &1.app)
|
||||
assert [unavailable: _, noappfile: {_, _}] = Enum.map(loaded, & &1.status)
|
||||
|
||||
loaded = Mix.Dep.load_on_environment(env: :dev)
|
||||
loaded = Mix.Dep.Converger.converge(env: :dev)
|
||||
assert [:deps_repo] = Enum.map(loaded, & &1.app)
|
||||
assert [noappfile: {_, _}] = Enum.map(loaded, & &1.status)
|
||||
|
||||
loaded = Mix.Dep.load_on_environment(env: :test)
|
||||
loaded = Mix.Dep.Converger.converge(env: :test)
|
||||
assert [:git_repo, :deps_repo] = Enum.map(loaded, & &1.app)
|
||||
assert [unavailable: _, noappfile: {_, _}] = Enum.map(loaded, & &1.status)
|
||||
end)
|
||||
@@ -751,18 +751,18 @@ defmodule Mix.DepTest do
|
||||
|
||||
with_deps(deps, fn ->
|
||||
in_fixture("deps_status", fn ->
|
||||
loaded = Mix.Dep.load_on_environment([])
|
||||
loaded = Mix.Dep.Converger.converge([])
|
||||
assert [:git_repo, :deps_repo] = Enum.map(loaded, & &1.app)
|
||||
assert [unavailable: _, noappfile: {_, _}] = Enum.map(loaded, & &1.status)
|
||||
|
||||
loaded = Mix.Dep.load_on_environment(env: :dev)
|
||||
loaded = Mix.Dep.Converger.converge(env: :dev)
|
||||
assert [] = Enum.map(loaded, & &1.app)
|
||||
|
||||
loaded = Mix.Dep.load_on_environment(env: :test)
|
||||
loaded = Mix.Dep.Converger.converge(env: :test)
|
||||
assert [:git_repo] = Enum.map(loaded, & &1.app)
|
||||
assert [unavailable: _] = Enum.map(loaded, & &1.status)
|
||||
|
||||
loaded = Mix.Dep.load_on_environment(env: :prod)
|
||||
loaded = Mix.Dep.Converger.converge(env: :prod)
|
||||
assert [:git_repo, :deps_repo] = Enum.map(loaded, & &1.app)
|
||||
assert [unavailable: _, noappfile: {_, _}] = Enum.map(loaded, & &1.status)
|
||||
end)
|
||||
@@ -778,15 +778,15 @@ defmodule Mix.DepTest do
|
||||
|
||||
with_deps(deps, fn ->
|
||||
in_fixture("deps_status", fn ->
|
||||
loaded = Mix.Dep.load_on_environment([])
|
||||
loaded = Mix.Dep.Converger.converge([])
|
||||
assert [:git_repo, :deps_repo] = Enum.map(loaded, & &1.app)
|
||||
assert [divergedonly: _, noappfile: {_, _}] = Enum.map(loaded, & &1.status)
|
||||
|
||||
loaded = Mix.Dep.load_on_environment(env: :dev)
|
||||
loaded = Mix.Dep.Converger.converge(env: :dev)
|
||||
assert [:git_repo, :deps_repo] = Enum.map(loaded, & &1.app)
|
||||
assert [divergedonly: _, noappfile: {_, _}] = Enum.map(loaded, & &1.status)
|
||||
|
||||
loaded = Mix.Dep.load_on_environment(env: :test)
|
||||
loaded = Mix.Dep.Converger.converge(env: :test)
|
||||
assert [:git_repo] = Enum.map(loaded, & &1.app)
|
||||
assert [unavailable: _] = Enum.map(loaded, & &1.status)
|
||||
|
||||
@@ -811,19 +811,19 @@ defmodule Mix.DepTest do
|
||||
|
||||
with_deps(deps, fn ->
|
||||
in_fixture("deps_status", fn ->
|
||||
loaded = Mix.Dep.load_on_environment([])
|
||||
loaded = Mix.Dep.Converger.converge([])
|
||||
assert [:git_repo, :deps_repo] = Enum.map(loaded, & &1.app)
|
||||
assert [unavailable: _, noappfile: {_, _}] = Enum.map(loaded, & &1.status)
|
||||
|
||||
loaded = Mix.Dep.load_on_environment(env: :dev)
|
||||
loaded = Mix.Dep.Converger.converge(env: :dev)
|
||||
assert [:deps_repo] = Enum.map(loaded, & &1.app)
|
||||
assert [noappfile: {_, _}] = Enum.map(loaded, & &1.status)
|
||||
|
||||
loaded = Mix.Dep.load_on_environment(env: :test)
|
||||
loaded = Mix.Dep.Converger.converge(env: :test)
|
||||
assert [:git_repo, :deps_repo] = Enum.map(loaded, & &1.app)
|
||||
assert [unavailable: _, noappfile: {_, _}] = Enum.map(loaded, & &1.status)
|
||||
|
||||
loaded = Mix.Dep.load_on_environment(env: :prod)
|
||||
loaded = Mix.Dep.Converger.converge(env: :prod)
|
||||
assert [] = Enum.map(loaded, & &1.app)
|
||||
end)
|
||||
end)
|
||||
@@ -848,7 +848,7 @@ defmodule Mix.DepTest do
|
||||
""")
|
||||
|
||||
Mix.State.clear_cache()
|
||||
loaded = Mix.Dep.load_on_environment([])
|
||||
loaded = Mix.Dep.Converger.converge([])
|
||||
Enum.map(loaded, &{&1.app, &1.opts[:only]})
|
||||
end)
|
||||
end)
|
||||
@@ -933,13 +933,13 @@ defmodule Mix.DepTest do
|
||||
|
||||
with_deps(deps, fn ->
|
||||
in_fixture("deps_status", fn ->
|
||||
deps = Mix.Dep.load_on_environment(target: :rpi3)
|
||||
deps = Mix.Dep.Converger.converge(target: :rpi3)
|
||||
assert length(deps) == 2
|
||||
|
||||
deps = Mix.Dep.load_on_environment([])
|
||||
deps = Mix.Dep.Converger.converge([])
|
||||
assert length(deps) == 2
|
||||
|
||||
assert [dep] = Mix.Dep.load_on_environment(target: :host)
|
||||
assert [dep] = Mix.Dep.Converger.converge(target: :host)
|
||||
assert dep.app == :foo
|
||||
end)
|
||||
end)
|
||||
@@ -973,15 +973,15 @@ defmodule Mix.DepTest do
|
||||
|
||||
with_deps(deps, fn ->
|
||||
in_fixture("deps_status", fn ->
|
||||
loaded = Mix.Dep.load_on_environment([])
|
||||
loaded = Mix.Dep.Converger.converge([])
|
||||
assert [:git_repo, :deps_repo] = Enum.map(loaded, & &1.app)
|
||||
assert [divergedtargets: _, noappfile: {_, _}] = Enum.map(loaded, & &1.status)
|
||||
|
||||
loaded = Mix.Dep.load_on_environment(target: :host)
|
||||
loaded = Mix.Dep.Converger.converge(target: :host)
|
||||
assert [:git_repo, :deps_repo] = Enum.map(loaded, & &1.app)
|
||||
assert [divergedtargets: _, noappfile: {_, _}] = Enum.map(loaded, & &1.status)
|
||||
|
||||
loaded = Mix.Dep.load_on_environment(target: :rpi3)
|
||||
loaded = Mix.Dep.Converger.converge(target: :rpi3)
|
||||
assert [:git_repo, :deps_repo] = Enum.map(loaded, & &1.app)
|
||||
assert [divergedtargets: _, noappfile: {_, _}] = Enum.map(loaded, & &1.status)
|
||||
|
||||
@@ -1006,15 +1006,15 @@ defmodule Mix.DepTest do
|
||||
|
||||
with_deps(deps, fn ->
|
||||
in_fixture("deps_status", fn ->
|
||||
loaded = Mix.Dep.load_on_environment([])
|
||||
loaded = Mix.Dep.Converger.converge([])
|
||||
assert [:git_repo, :deps_repo] = Enum.map(loaded, & &1.app)
|
||||
assert [unavailable: _, noappfile: {_, _}] = Enum.map(loaded, & &1.status)
|
||||
|
||||
loaded = Mix.Dep.load_on_environment(target: :host)
|
||||
loaded = Mix.Dep.Converger.converge(target: :host)
|
||||
assert [:deps_repo] = Enum.map(loaded, & &1.app)
|
||||
assert [noappfile: {_, _}] = Enum.map(loaded, & &1.status)
|
||||
|
||||
loaded = Mix.Dep.load_on_environment(target: :rpi3)
|
||||
loaded = Mix.Dep.Converger.converge(target: :rpi3)
|
||||
assert [:git_repo, :deps_repo] = Enum.map(loaded, & &1.app)
|
||||
assert [unavailable: _, noappfile: {_, _}] = Enum.map(loaded, & &1.status)
|
||||
end)
|
||||
@@ -1030,18 +1030,18 @@ defmodule Mix.DepTest do
|
||||
|
||||
with_deps(deps, fn ->
|
||||
in_fixture("deps_status", fn ->
|
||||
loaded = Mix.Dep.load_on_environment([])
|
||||
loaded = Mix.Dep.Converger.converge([])
|
||||
assert [:git_repo, :deps_repo] = Enum.map(loaded, & &1.app)
|
||||
assert [unavailable: _, noappfile: {_, _}] = Enum.map(loaded, & &1.status)
|
||||
|
||||
loaded = Mix.Dep.load_on_environment(target: :host)
|
||||
loaded = Mix.Dep.Converger.converge(target: :host)
|
||||
assert [] = Enum.map(loaded, & &1.app)
|
||||
|
||||
loaded = Mix.Dep.load_on_environment(target: :bbb)
|
||||
loaded = Mix.Dep.Converger.converge(target: :bbb)
|
||||
assert [:git_repo] = Enum.map(loaded, & &1.app)
|
||||
assert [unavailable: _] = Enum.map(loaded, & &1.status)
|
||||
|
||||
loaded = Mix.Dep.load_on_environment(target: :rpi3)
|
||||
loaded = Mix.Dep.Converger.converge(target: :rpi3)
|
||||
assert [:git_repo, :deps_repo] = Enum.map(loaded, & &1.app)
|
||||
assert [unavailable: _, noappfile: {_, _}] = Enum.map(loaded, & &1.status)
|
||||
end)
|
||||
@@ -1057,15 +1057,15 @@ defmodule Mix.DepTest do
|
||||
|
||||
with_deps(deps, fn ->
|
||||
in_fixture("deps_status", fn ->
|
||||
loaded = Mix.Dep.load_on_environment([])
|
||||
loaded = Mix.Dep.Converger.converge([])
|
||||
assert [:git_repo, :deps_repo] = Enum.map(loaded, & &1.app)
|
||||
assert [divergedtargets: _, noappfile: {_, _}] = Enum.map(loaded, & &1.status)
|
||||
|
||||
loaded = Mix.Dep.load_on_environment(target: :host)
|
||||
loaded = Mix.Dep.Converger.converge(target: :host)
|
||||
assert [:git_repo, :deps_repo] = Enum.map(loaded, & &1.app)
|
||||
assert [divergedtargets: _, noappfile: {_, _}] = Enum.map(loaded, & &1.status)
|
||||
|
||||
loaded = Mix.Dep.load_on_environment(target: :rpi3)
|
||||
loaded = Mix.Dep.Converger.converge(target: :rpi3)
|
||||
assert [:git_repo] = Enum.map(loaded, & &1.app)
|
||||
assert [unavailable: _] = Enum.map(loaded, & &1.status)
|
||||
|
||||
@@ -1090,19 +1090,19 @@ defmodule Mix.DepTest do
|
||||
|
||||
with_deps(deps, fn ->
|
||||
in_fixture("deps_status", fn ->
|
||||
loaded = Mix.Dep.load_on_environment([])
|
||||
loaded = Mix.Dep.Converger.converge([])
|
||||
assert [:git_repo, :deps_repo] = Enum.map(loaded, & &1.app)
|
||||
assert [unavailable: _, noappfile: {_, _}] = Enum.map(loaded, & &1.status)
|
||||
|
||||
loaded = Mix.Dep.load_on_environment(target: :host)
|
||||
loaded = Mix.Dep.Converger.converge(target: :host)
|
||||
assert [:deps_repo] = Enum.map(loaded, & &1.app)
|
||||
assert [noappfile: {_, _}] = Enum.map(loaded, & &1.status)
|
||||
|
||||
loaded = Mix.Dep.load_on_environment(target: :bbb)
|
||||
loaded = Mix.Dep.Converger.converge(target: :bbb)
|
||||
assert [:git_repo, :deps_repo] = Enum.map(loaded, & &1.app)
|
||||
assert [unavailable: _, noappfile: {_, _}] = Enum.map(loaded, & &1.status)
|
||||
|
||||
loaded = Mix.Dep.load_on_environment(target: :rpi3)
|
||||
loaded = Mix.Dep.Converger.converge(target: :rpi3)
|
||||
assert [] = Enum.map(loaded, & &1.app)
|
||||
end)
|
||||
end)
|
||||
@@ -1127,7 +1127,7 @@ defmodule Mix.DepTest do
|
||||
""")
|
||||
|
||||
Mix.State.clear_cache()
|
||||
loaded = Mix.Dep.load_on_environment([])
|
||||
loaded = Mix.Dep.Converger.converge([])
|
||||
Enum.map(loaded, &{&1.app, &1.opts[:targets]})
|
||||
end)
|
||||
end)
|
||||
|
||||
@@ -149,14 +149,14 @@ defmodule Mix.RebarTest do
|
||||
describe "integration with Mix" do
|
||||
test "inherits Rebar manager" do
|
||||
Mix.Project.push(RebarAsDep)
|
||||
deps = Mix.Dep.load_on_environment([])
|
||||
deps = Mix.Dep.Converger.converge([])
|
||||
assert Enum.all?(deps, &(&1.manager == :rebar3))
|
||||
end
|
||||
|
||||
test "parses Rebar dependencies from rebar.config" do
|
||||
Mix.Project.push(RebarAsDep)
|
||||
|
||||
deps = Mix.Dep.load_on_environment([])
|
||||
deps = Mix.Dep.Converger.converge([])
|
||||
assert Enum.all?(deps, &(&1.manager == :rebar3))
|
||||
assert Enum.find(deps, &(&1.app == :rebar_dep))
|
||||
|
||||
@@ -175,7 +175,7 @@ defmodule Mix.RebarTest do
|
||||
|
||||
Mix.Tasks.Deps.Get.run([])
|
||||
|
||||
assert Mix.Dep.load_on_environment([]) |> Enum.map(& &1.app) ==
|
||||
assert Mix.Dep.Converger.converge([]) |> Enum.map(& &1.app) ==
|
||||
[:git_repo, :git_rebar, :rebar_override]
|
||||
end)
|
||||
after
|
||||
@@ -199,7 +199,7 @@ defmodule Mix.RebarTest do
|
||||
assert :rebar_dep.any_function() == :ok
|
||||
|
||||
load_paths =
|
||||
Mix.Dep.load_on_environment([])
|
||||
Mix.Dep.Converger.converge([])
|
||||
|> Enum.map(&Mix.Dep.load_paths(&1))
|
||||
|> Enum.concat()
|
||||
|
||||
|
||||
@@ -308,6 +308,24 @@ defmodule Mix.Tasks.FormatTest do
|
||||
'''abc
|
||||
end
|
||||
"""
|
||||
|
||||
{formatter_function, _options} = Mix.Tasks.Format.formatter_for_file("a.ex")
|
||||
|
||||
assert formatter_function.("""
|
||||
if true do
|
||||
~W'''
|
||||
foo bar baz
|
||||
'''abc
|
||||
end
|
||||
""") == """
|
||||
if true do
|
||||
~W'''
|
||||
foo
|
||||
bar
|
||||
baz
|
||||
'''abc
|
||||
end
|
||||
"""
|
||||
end)
|
||||
end
|
||||
|
||||
|
||||
@@ -64,6 +64,10 @@ defmodule Mix.UmbrellaTest do
|
||||
Mix.Task.run("deps.loadpaths")
|
||||
Mix.Task.run("compile", ["--verbose"])
|
||||
|
||||
# Extra applications are picked even for umbrellas
|
||||
assert :code.where_is_file(~c"runtime_tools.app") != :non_existing
|
||||
assert :code.where_is_file(~c"observer.app") == :non_existing
|
||||
|
||||
assert_received {:mix_shell, :info, ["==> bar"]}
|
||||
assert_received {:mix_shell, :info, ["Generated bar app"]}
|
||||
assert File.regular?("_build/dev/lib/bar/ebin/Elixir.Bar.beam")
|
||||
@@ -290,7 +294,7 @@ defmodule Mix.UmbrellaTest do
|
||||
in_fixture("umbrella_dep", fn ->
|
||||
Mix.Project.push(CycleDeps)
|
||||
|
||||
assert Enum.map(Mix.Dep.load_on_environment([]), & &1.app) == [:foo, :bar, :umbrella]
|
||||
assert Enum.map(Mix.Dep.Converger.converge([]), & &1.app) == [:foo, :bar, :umbrella]
|
||||
end)
|
||||
end
|
||||
|
||||
@@ -328,7 +332,7 @@ defmodule Mix.UmbrellaTest do
|
||||
end
|
||||
""")
|
||||
|
||||
assert Enum.map(Mix.Dep.load_on_environment([]), & &1.app) == [:a, :b, :bar, :foo]
|
||||
assert Enum.map(Mix.Dep.Converger.converge([]), & &1.app) == [:a, :b, :bar, :foo]
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user