Skip to content

Commit

Permalink
Re-enabled multiple version support (#269)
Browse files Browse the repository at this point in the history
* Re-enabled multiple version support

Prior to this change, running a project in a different version of
elixir or erlang than lexical was compiled under would cause an
error.

There were two reasons for this:

 1. The release is extremely sensitive to the version of elixir /
 erlang, and would not start if they didn't match. This would happen
 if include_erts is false.
 2. Even if include_erts is true, finding the elixir executable to run
 would use the inherited environment variables from the server
 process. ASDF munges the path, and would find the version of elixir
 that was used to start lexical, not the one the project requests.

The solution is to enable `use_erts` and to undo the path munging that
asdf performs. We need to do something similar with rtx.

* Updated to work with rtx

* Update apps/remote_control/lib/lexical/remote_control.ex

Co-authored-by: Étienne Lévesque <[email protected]>

---------

Co-authored-by: Étienne Lévesque <[email protected]>
  • Loading branch information
scohen and Blond11516 authored Jul 21, 2023
1 parent 0af6418 commit 2dc3d49
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 29 deletions.
42 changes: 39 additions & 3 deletions apps/remote_control/lib/lexical/remote_control.ex
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,13 @@ defmodule Lexical.RemoteControl do

def elixir_executable(%Project{} = project) do
root_path = Project.root_path(project)
version_manager = version_manager()
env = reset_env(version_manager, root_path)

path_result =
case version_manager() do
:asdf ->
case System.cmd("asdf", ~w(which elixir), cd: root_path) do
case System.cmd("asdf", ~w(which elixir), cd: root_path, env: env) do
{path, 0} ->
String.trim(path)

Expand All @@ -105,7 +107,7 @@ defmodule Lexical.RemoteControl do
end

:rtx ->
case System.cmd("rtx", ~w(which elixir), cd: root_path) do
case System.cmd("rtx", ~w(which elixir), cd: root_path, env: env) do
{path, 0} ->
String.trim(path)

Expand All @@ -122,7 +124,7 @@ defmodule Lexical.RemoteControl do
{:error, :no_elixir}

executable when is_binary(executable) ->
{:ok, executable}
{:ok, executable, env}
end
end

Expand Down Expand Up @@ -152,4 +154,38 @@ defmodule Lexical.RemoteControl do
defp asdf?, do: is_binary(System.find_executable("asdf"))

defp rtx?, do: is_binary(System.find_executable("rtx"))

# We launch lexical by asking the version managers to provide an environment,
# which contains path munging. This initial environment is present in the running
# VM, and needs to be undone so we can find the correct elixir executable in the project.
defp reset_env(:asdf, _root_path) do
orig_path = System.get_env("PATH_SAVE", System.get_env("PATH"))

Enum.map(System.get_env(), fn
{"ASDF_ELIXIR_VERSION", _} -> {"ASDF_ELIXIR_VERSION", nil}
{"ASDF_ERLANG_VERSION", _} -> {"ASDF_ERLANG_VERSION", nil}
{"PATH", _} -> {"PATH", orig_path}
other -> other
end)
end

defp reset_env(:rtx, root_path) do
{env, _} = System.cmd("rtx", ~w(), cd: root_path)

env
|> String.trim()
|> String.split("\n")
|> Enum.map(fn "export " <> key_and_value ->
[key, value] =
key_and_value
|> String.split("=", parts: 2)
|> Enum.map(&String.trim/1)

{key, value}
end)
end

defp reset_env(_, _) do
System.get_env()
end
end
13 changes: 11 additions & 2 deletions apps/remote_control/lib/lexical/remote_control/project_node.ex
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,15 @@ defmodule Lexical.RemoteControl.ProjectNode do

def start(%__MODULE__{} = state, paths, from) do
port_wrapper = port_wrapper_executable()
{:ok, elixir_executable} = RemoteControl.elixir_executable(state.project)

{:ok, elixir_executable, environment_variables} =
RemoteControl.elixir_executable(state.project)

erlang_env =
Enum.map(environment_variables, fn {key, value} ->
{String.to_charlist(key), String.to_charlist(value)}
end)

node_name = node_name(state.project)

args = [
Expand All @@ -46,7 +54,8 @@ defmodule Lexical.RemoteControl.ProjectNode do
port =
Port.open({:spawn_executable, port_wrapper},
args: args,
cd: Project.root_path(state.project)
cd: Project.root_path(state.project),
env: erlang_env
)

%{state | port: port, started_by: from}
Expand Down
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ defmodule Lexical.LanguageServer.MixProject do
mix: :load
],
include_executables_for: [:unix],
include_erts: false,
include_erts: true,
cookie: "lexical",
rel_templates_path: "rel/deploy",
strip_beams: false,
Expand Down
24 changes: 1 addition & 23 deletions rel/deploy/overlays/start_lexical.sh
Original file line number Diff line number Diff line change
@@ -1,15 +1,5 @@
#!/usr/bin/env bash

set_up_version_manager() {
if [ -e $HOME/.asdf ]; then
VERSION_MANAGER="asdf"
elif [ -e $HOME/.rtx ]; then
VERSION_MANAGER="rtx"
else
VERSION_MANAGER="none"
fi
}

readlink_f () {
cd "$(dirname "$1")" > /dev/null || exit 1
filename="$(basename "$1")"
Expand All @@ -26,16 +16,4 @@ else
dir=${ELS_INSTALL_PREFIX}
fi

set_up_version_manager

case "$VERSION_MANAGER" in
asdf)
asdf env elixir "${dir}/bin/lexical" start
;;
rtx)
rtx env -s bash elixir "${dir}/bin/lexical" start
;;
*)
"${dir}/bin/lexical" start
;;
esac
"${dir}/bin/lexical" start

0 comments on commit 2dc3d49

Please sign in to comment.