Skip to content

Commit

Permalink
Require Erlang/OTP 26+ (#14045)
Browse files Browse the repository at this point in the history
  • Loading branch information
josevalim authored Dec 10, 2024
1 parent 80c51cc commit ef7c118
Show file tree
Hide file tree
Showing 16 changed files with 27 additions and 241 deletions.
4 changes: 1 addition & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@ jobs:
otp_latest: true
- otp_version: "27.0"
- otp_version: "26.0"
- otp_version: "25.3"
- otp_version: "25.0"
- otp_version: master
development: true
- otp_version: maint
Expand Down Expand Up @@ -81,7 +79,7 @@ jobs:
name: Windows Server 2019, Erlang/OTP ${{ matrix.otp_version }}
strategy:
matrix:
otp_version: ["25.3", "26.2", "27.1"]
otp_version: ["26.2", "27.1"]
runs-on: windows-2022
steps:
- name: Configure Git
Expand Down
2 changes: 0 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,6 @@ jobs:
fail-fast: true
matrix:
include:
- otp: 25
otp_version: "25.3"
- otp: 26
otp_version: "26.0"
- otp: 27
Expand Down
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ SOURCE_DATE_EPOCH_FILE = $(SOURCE_DATE_EPOCH_PATH)/SOURCE_DATE_EPOCH
#==> Functions

define CHECK_ERLANG_RELEASE
erl -noshell -eval '{V,_} = string:to_integer(erlang:system_info(otp_release)), io:fwrite("~s", [is_integer(V) and (V >= 25)])' -s erlang halt | grep -q '^true'; \
erl -noshell -eval '{V,_} = string:to_integer(erlang:system_info(otp_release)), io:fwrite("~s", [is_integer(V) and (V >= 26)])' -s erlang halt | grep -q '^true'; \
if [ $$? != 0 ]; then \
echo "At least Erlang/OTP 25.0 is required to build Elixir"; \
echo "At least Erlang/OTP 26.0 is required to build Elixir"; \
exit 1; \
fi
endef
Expand Down
2 changes: 1 addition & 1 deletion RELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@

### Back in main

1. Bump /VERSION file, bin/elixir and bin/elixir.bat
1. Bump /VERSION file, bin/elixir, bin/elixir.bat, and bin/elixir.ps1

2. Start new /CHANGELOG.md

Expand Down
4 changes: 0 additions & 4 deletions bin/elixir
Original file line number Diff line number Diff line change
Expand Up @@ -215,10 +215,6 @@ SCRIPT_PATH=$(dirname "$SELF")
if [ "$OSTYPE" = "cygwin" ]; then SCRIPT_PATH=$(cygpath -m "$SCRIPT_PATH"); fi
if [ "$MODE" != "iex" ]; then ERL="-s elixir start_cli $ERL"; fi

if [ "$OS" != "Windows_NT" ] && [ -z "$NO_COLOR" ]; then
if test -t 1 -a -t 2; then ERL="-elixir ansi_enabled true $ERL"; fi
fi

# One MAY change ERTS_BIN= but you MUST NOT change
# ERTS_BIN=$ERTS_BIN as it is handled by Elixir releases.
ERTS_BIN=
Expand Down
2 changes: 1 addition & 1 deletion bin/elixir.ps1
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/env pwsh

$ELIXIR_VERSION = "1.18.0-dev"
$ELIXIR_VERSION = "1.19.0-dev"

$scriptPath = Split-Path -Parent $PSCommandPath
$erlExec = "erl"
Expand Down
16 changes: 2 additions & 14 deletions lib/elixir/lib/application.ex
Original file line number Diff line number Diff line change
Expand Up @@ -908,8 +908,7 @@ defmodule Application do
`:permanent`, or `:transient`. See `t:restart_type/1` for more information.
* `:mode` - (since v1.15.0) if the applications should be started serially
(`:serial`, default) or concurrently (`:concurrent`). This option requires
Erlang/OTP 26+.
(`:serial`, default) or concurrently (`:concurrent`).
"""
@spec ensure_all_started(app | [app], type: restart_type(), mode: :serial | :concurrent) ::
Expand All @@ -930,18 +929,7 @@ defmodule Application do

def ensure_all_started(apps, opts) when is_list(apps) and is_list(opts) do
opts = Keyword.validate!(opts, type: :temporary, mode: :serial)

if function_exported?(:application, :ensure_all_started, 3) do
:application.ensure_all_started(apps, opts[:type], opts[:mode])
else
# TODO: Remove this clause when we require Erlang/OTP 26+
Enum.reduce_while(apps, {:ok, []}, fn app, {:ok, acc} ->
case :application.ensure_all_started(app, opts[:type]) do
{:ok, apps} -> {:cont, {:ok, apps ++ acc}}
{:error, e} -> {:halt, {:error, e}}
end
end)
end
:application.ensure_all_started(apps, opts[:type], opts[:mode])
end

@doc """
Expand Down
8 changes: 4 additions & 4 deletions lib/elixir/lib/code.ex
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,7 @@ defmodule Code do
* `:cache` - (since v1.15.0) when true, the code path is cached
the first time it is traversed in order to reduce file system
operations. It requires Erlang/OTP 26, otherwise it is a no-op.
operations.
"""
@spec append_path(Path.t(), cache: boolean()) :: true | false
Expand Down Expand Up @@ -374,7 +374,7 @@ defmodule Code do
* `:cache` - (since v1.15.0) when true, the code path is cached
the first time it is traversed in order to reduce file system
operations. It requires Erlang/OTP 26, otherwise it is a no-op.
operations.
"""
@spec prepend_path(Path.t(), cache: boolean()) :: boolean()
Expand Down Expand Up @@ -403,7 +403,7 @@ defmodule Code do
* `:cache` - when true, the code path is cached the first time
it is traversed in order to reduce file system operations.
It requires Erlang/OTP 26, otherwise it is a no-op.
"""
@doc since: "1.15.0"
@spec prepend_paths([Path.t()], cache: boolean()) :: :ok
Expand Down Expand Up @@ -432,7 +432,7 @@ defmodule Code do
* `:cache` - when true, the code path is cached the first time
it is traversed in order to reduce file system operations.
It requires Erlang/OTP 26, otherwise it is a no-op.
"""
@doc since: "1.15.0"
@spec append_paths([Path.t()], cache: boolean()) :: :ok
Expand Down
1 change: 0 additions & 1 deletion lib/elixir/lib/string.ex
Original file line number Diff line number Diff line change
Expand Up @@ -1829,7 +1829,6 @@ defmodule String do
the `:fast_ascii` algorithm to see if it yields performance benefits in your
specific scenario:
* You are running Erlang/OTP 26 or newer on a 64 bit platform
* You expect most of your strings to be longer than ~64 bytes
* You expect most of your strings to contain mostly ASCII codepoints
Expand Down
2 changes: 2 additions & 0 deletions lib/elixir/pages/references/compatibility-and-deprecations.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ Erlang/OTP versioning is independent from the versioning of Elixir. Erlang relea

Elixir version | Supported Erlang/OTP versions
:------------- | :-------------------------------
1.19 | 26 - 27
1.18 | 25 - 27
1.17 | 25 - 27
1.16 | 24 - 26
1.15 | 24 - 26
Expand Down
48 changes: 10 additions & 38 deletions lib/elixir/src/elixir.erl
Original file line number Diff line number Diff line change
Expand Up @@ -26,27 +26,22 @@
-type struct() :: #{'__struct__' := atom(), atom() => any()}.

%% OTP Application API
%% TODO: Remove Erlang/OTP 26+ checks

load_paths(OTP, Paths) when OTP >= 26 -> code:add_pathsa(Paths, cache);
load_paths(_OTP, Paths) -> code:add_pathsa(Paths).

start(_Type, _Args) ->
OTP = parse_otp_release(),
preload_common_modules(),
set_stdio_and_stderr_to_binary_and_maybe_utf8(OTP),
ok = io:setopts(standard_io, [binary]),
check_file_encoding(file:native_name_encoding()),

case init:get_argument(elixir_root) of
{ok, [[Root]]} ->
load_paths(OTP, [
code:add_pathsa([
Root ++ "/eex/ebin",
Root ++ "/ex_unit/ebin",
Root ++ "/iex/ebin",
Root ++ "/logger/ebin",
Root ++ "/mix/ebin",
Root ++ "/elixir/ebin"
]);
], cache);
_ ->
ok
end,
Expand All @@ -57,11 +52,11 @@ start(_Type, _Args) ->
end,

case application:get_env(elixir, ansi_enabled) of
{ok, _} -> ok;
{ok, _} ->
ok;

undefined ->
%% Remove prim_tty module check as well as checks from scripts on Erlang/OTP 26
ANSIEnabled = erlang:module_loaded(prim_tty) andalso (prim_tty:isatty(stdout) == true),
application:set_env(elixir, ansi_enabled, ANSIEnabled)
application:set_env(elixir, ansi_enabled, prim_tty:isatty(stdout) == true)
end,

Tokenizer = case code:ensure_loaded('Elixir.String.Tokenizer') of
Expand Down Expand Up @@ -117,19 +112,6 @@ stop(Tab) ->
config_change(_Changed, _New, _Remove) ->
ok.

set_stdio_and_stderr_to_binary_and_maybe_utf8(OTP) when OTP >= 26 ->
ok = io:setopts(standard_io, [binary]),
ok;
set_stdio_and_stderr_to_binary_and_maybe_utf8(_OTP) ->
Opts =
case init:get_argument(noshell) of
{ok, _} -> [binary, {encoding, utf8}];
error -> [binary]
end,
ok = io:setopts(standard_io, Opts),
ok = io:setopts(standard_error, [{encoding, utf8}]),
ok.

preload_common_modules() ->
%% We attempt to load those modules here so throughout
%% the codebase we can avoid code:ensure_loaded/1 checks.
Expand All @@ -140,10 +122,10 @@ preload_common_modules() ->
parse_otp_release() ->
%% Whenever we change this check, we should also change Makefile.
case string:to_integer(erlang:system_info(otp_release)) of
{Num, _} when Num >= 25 ->
{Num, _} when Num >= 26 ->
Num;
_ ->
io:format(standard_error, "ERROR! Unsupported Erlang/OTP version, expected Erlang/OTP 25+~n", []),
io:format(standard_error, "ERROR! Unsupported Erlang/OTP version, expected Erlang/OTP 26+~n", []),
erlang:halt(1)
end.

Expand Down Expand Up @@ -177,19 +159,9 @@ check_file_encoding(Encoding) ->
end.

%% Boot and process given options. Invoked by Elixir's script.
%% TODO: Delete prim_tty branches on Erlang/OTP 26.

start() ->
case code:ensure_loaded(prim_tty) of
{module, _} ->
user_drv:start(#{initial_shell => iex:shell()});
{error, _} ->
case init:get_argument(elixir_root) of
{ok, [[Root]]} -> code:add_patha(Root ++ "/iex/ebin");
_ -> ok
end,
'Elixir.IEx.CLI':deprecated()
end.
user_drv:start(#{initial_shell => iex:shell()}).

start_cli() ->
{ok, _} = application:ensure_all_started(elixir),
Expand Down
6 changes: 0 additions & 6 deletions lib/elixir/src/elixir_json.erl
Original file line number Diff line number Diff line change
Expand Up @@ -314,14 +314,8 @@ list_loop([Elem | Rest], Encode) -> [$,, Encode(Elem, Encode) | list_loop(Rest,
encode_map(Map, Encode) when is_map(Map) ->
do_encode_map(Map, Encode).

%% TODO: Remove conditional once Erlang/OTP 26+ is exclusively supported.
-if(?OTP_RELEASE >= 26).
do_encode_map(Map, Encode) when is_function(Encode, 2) ->
encode_object([[$,, key(Key, Encode), $: | Encode(Value, Encode)] || Key := Value <- Map]).
-else.
do_encode_map(Map, Encode) when is_function(Encode, 2) ->
encode_object([[$,, key(Key, Encode), $: | Encode(Value, Encode)] || {Key, Value} <- maps:to_list(Map)]).
-endif.

-spec encode_map_checked(map(), encoder()) -> iodata().
encode_map_checked(Map, Encode) ->
Expand Down
4 changes: 2 additions & 2 deletions lib/iex/lib/iex/autocomplete.ex
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ defmodule IEx.Autocomplete do
def remsh(node) do
fn e ->
try do
:erpc.call(node, IEx.Autocomplete, :expand, [e, IEx.Broker.shell()])
:erpc.call(node, IEx.Autocomplete, :expand, [e, :shell.whereis()])
catch
_, _ -> {:no, ~c"", []}
end
Expand All @@ -41,7 +41,7 @@ defmodule IEx.Autocomplete do
Some of the expansion has to use the current shell
environment, which is found via the broker.
"""
def expand(code, shell \\ IEx.Broker.shell()) do
def expand(code, shell \\ :shell.whereis()) do
case path_fragment(code) do
[] -> expand_code(code, shell)
path -> expand_path(path)
Expand Down
26 changes: 1 addition & 25 deletions lib/iex/lib/iex/broker.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,35 +9,11 @@ defmodule IEx.Broker do

## Shell API

@doc """
Finds the IEx server.
"""
@spec shell :: shell()
# TODO: Use shell:whereis() from Erlang/OTP 26+.
def shell() do
if user = Process.whereis(:user) do
if user_drv = get_from_dict(user, :user_drv) do
if group = get_from_dict(user_drv, :current_group) do
get_from_dict(group, :shell)
end
end
end
end

defp get_from_dict(pid, key) do
with {:dictionary, dictionary} <- Process.info(pid, :dictionary),
{^key, value} <- List.keyfind(dictionary, key, 0) do
value
else
_ -> nil
end
end

@doc """
Finds the evaluator and server running inside `:user_drv`, on this node exclusively.
"""
@spec evaluator(shell()) :: {evaluator :: pid, server :: pid} | nil
def evaluator(pid \\ shell()) do
def evaluator(pid \\ :shell.whereis()) do
if pid do
{:dictionary, dictionary} = Process.info(pid, :dictionary)
{dictionary[:evaluator], pid}
Expand Down
Loading

0 comments on commit ef7c118

Please sign in to comment.