Compare commits

...
25 Commits
Author SHA1 Message Date
José Valim a16517edfb Release v1.15.6 2023-09-20 10:32:46 +02:00
Wojtek Mach 26136e53c6 Improve Windows Installer (#12945)
1. If we couldn't verify installed OTP, don't quit but let user proceed with installing just Elixir

2. When checking installed OTPs, pick the recent most installed. Previously we were picking the first one which was basically guaranteed to be too old.

3. If we find an existing Elixir installation but it doesn't match the OTP version, offer to download the OTP version this Elixir installer was compiled against (which usually would be the newer version)

4. Add "Verify Erlang/OTP" button which re-checks for installed OTP.

Closes #12678.
2023-09-20 10:30:17 +02:00
José Valim 6654f1b922 Load plugins on formatter_for_file, closes #12930 2023-09-14 09:13:14 +02:00
José Valim e30f8df8fe Do not loop when invalid block is given to formatter, closes #12922 2023-09-13 09:50:05 +02:00
José Valim 2fe5b77498 Trace functions before they are inlined, closes #12925 2023-09-13 09:39:03 +02:00
José Valim f1a5e6a0b1 Properly print warnings from tokenizer in EEx, closes #12926 2023-09-13 09:17:45 +02:00
José Valim fadf5c6519 Do not always load applications during convergence, closes #12682 2023-09-12 11:54:57 +02:00
José Valim e975613c9d Load extra applications for umbrellas 2023-09-12 10:12:01 +02:00
Jean Klingler b1a1dd04a5 Fix formatter for :* in bitstring modifiers (#12923) 2023-09-12 08:12:09 +02:00
José Valim e4e2f1a79e Include environment in mix format missing dependency error 2023-09-11 17:36:21 +02:00
José Valim 138b442e07 Improve error message on Access module 2023-09-04 14:01:09 +02:00
Wojtek Mach 119580f544 Fix PATH handling on Windows Installer (#12900) 2023-08-30 17:09:56 +02:00
José Valim 9fd97c4663 Release v1.15.5 2023-08-28 13:45:12 +02:00
José Valim 78cee0e384 Speed up loading of struct suggestions, closes #12674 2023-08-27 10:12:24 +02:00
Zach Allaun fb0379287c Fix Code.Fragment.surround_context/2 for submodules of non-aliases (#12890) 2023-08-24 20:55:53 +02:00
José Valim def65ab120 Do not store anonymous functions in cache 2023-08-23 14:15:55 +02:00
José Valim dc8cfcd4c9 Load and compile plugins from cache, closes #12880 2023-08-23 12:24:22 +02:00
sabiwara 84c8d23710 Always pass stracktrace when necessary 2023-08-23 12:06:07 +02:00
Jean Klingler 36c3f7594d Fix bug in surround_context alias handling (#12830) 2023-08-03 23:22:18 +09:00
José Valim bdaffc380b Fix infinite loop when diffing functions, closes #12828 2023-08-02 20:45:57 +02:00
José Valim 5d79b34cb5 Force group leader to run as a binary and unicode in IEx 2023-07-31 15:46:39 +02:00
José Valim 14cddf357b Also handle charlist returns from stdio
Closes #12687.
2023-07-28 12:24:26 +02:00
José Valim 92eee10bcf Optional arguments must be counted from the end, closes #10095 2023-07-19 13:52:17 +02:00
José Valim 9b7b7d655d Do not assume blake is always available
Closes #12809.
2023-07-19 00:22:32 +02:00
José Valim f40aa10436 Fix type in changelog 2023-07-18 13:21:23 +02:00
42 changed files with 625 additions and 331 deletions
+55 -3
View File
@@ -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
View File
@@ -1 +1 @@
1.15.4
1.15.6
+1 -1
View File
@@ -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
View File
@@ -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
+2 -2
View File
@@ -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)
+7 -1
View File
@@ -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
+55 -6
View File
@@ -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)])
+10 -6
View File
@@ -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
+4 -1
View File
@@ -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.
+6 -2
View File
@@ -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;
+1 -1
View File
@@ -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)
-4
View File
@@ -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
+3 -3
View File
@@ -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
+36 -20
View File
@@ -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
+6 -17
View File
@@ -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
+5 -1
View File
@@ -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}
+8 -2
View File
@@ -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<???>-",
+1
View File
@@ -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)
+14 -10
View File
@@ -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
+7 -3
View File
@@ -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.
+3
View File
@@ -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
View File
@@ -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.
+16 -24
View File
@@ -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
+1 -1
View File
@@ -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 =
+1 -2
View File
@@ -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)
+1 -1
View File
@@ -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
-3
View File
@@ -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, _} ->
+2 -2
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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)
+69 -63
View File
@@ -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
+58 -58
View File
@@ -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)
+4 -4
View File
@@ -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()
+18
View File
@@ -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
+6 -2
View File
@@ -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