Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions apps/expert/lib/expert/engine_node.ex
Original file line number Diff line number Diff line change
Expand Up @@ -171,8 +171,12 @@ defmodule Expert.EngineNode do
# Expert release, and we build it on the fly for the project elixir+opt
# versions if it was not built yet.
defp glob_paths(%Project{} = project) do
lsp = Expert.get_lsp()
project_name = Project.name(project)
{:ok, elixir, env} = Expert.Port.elixir_executable(project)

GenLSP.info(lsp, "Found elixir for #{project_name} at #{elixir}")

expert_priv = :code.priv_dir(:expert)
packaged_engine_source = Path.join([expert_priv, "engine_source", "apps", "engine"])

Expand Down Expand Up @@ -200,12 +204,9 @@ defmodule Expert.EngineNode do

launcher = Expert.Port.path()

GenLSP.info(
Expert.get_lsp(),
"Finding or building engine for project #{Project.name(project)}"
)
GenLSP.info(lsp, "Finding or building engine for project #{project_name}")

with_progress(project, "Building engine for #{Project.name(project)}", fn ->
with_progress(project, "Building engine for #{project_name}", fn ->
port =
Port.open(
{:spawn_executable, launcher},
Expand Down
114 changes: 12 additions & 102 deletions apps/expert/lib/expert/port.ex
Original file line number Diff line number Diff line change
Expand Up @@ -34,113 +34,23 @@ defmodule Expert.Port do
def elixir_executable(%Project{} = project) do
root_path = Project.root_path(project)

{path_result, env} =
with nil <- version_manager_path_and_env("asdf", root_path),
nil <- version_manager_path_and_env("mise", root_path),
nil <- version_manager_path_and_env("rtx", root_path) do
{File.cd!(root_path, fn -> System.find_executable("elixir") end), System.get_env()}
end
# We run a shell in interactive mode to populate the PATH with the right value
# at the project root. Otherwise, we either can't find an elixir executable,
# we use the wrong version if the user uses a version manager like asdf/mise,
# or we get an incomplete PATH not including erl or any other version manager
# managed programs.
shell = System.get_env("SHELL")

case path_result do
nil ->
{:error, :no_elixir}

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

defp version_manager_path_and_env(manager, root_path) do
with true <- is_binary(System.find_executable(manager)),
env = reset_env(manager, root_path),
{path, 0} <- System.cmd(manager, ~w(which elixir), cd: root_path, env: env) do
{String.trim(path), env}
else
_ ->
nil
end
end

# We launch expert 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
{env, _} = System.cmd("asdf", ~w(env elixir), cd: root_path)
{path, 0} = System.cmd(shell, ["-i", "-l", "-c", "cd #{root_path} && echo $PATH"])
elixir = :os.find_executable(~c"elixir", to_charlist(path))

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

{key, value}
Enum.map(System.get_env(), fn
{"PATH", _path} -> {"PATH", path}
other -> other
end)
|> Enum.reject(&is_nil/1)

asdf_path =
case List.keyfind(env, "ASDF_INSTALL_PATH", 0) do
{_, path} -> Path.join(path, "../../../shims")
_ -> ""
end

Enum.map(System.get_env(), fn
{"PATH", path} -> {"PATH", "#{asdf_path}:#{path}"}
other -> other
end)
end

defp reset_env("rtx", root_path) do
{env, _} = System.cmd("rtx", ~w(env -s bash), 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)
|> then(fn
["PATH", path] -> ["PATH", String.trim(path, "'")]
other -> other
end)

{key, value}

_ ->
nil
end)
|> Enum.reject(&is_nil/1)
end

defp reset_env("mise", root_path) do
{env, _} = System.cmd("mise", ~w(env -s bash), 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)
|> then(fn
["PATH", path] -> ["PATH", String.trim(path, "'")]
other -> other
end)

{key, value}

_ ->
nil
end)
|> Enum.reject(&is_nil/1)
{:ok, elixir, env}
end

@doc """
Expand Down
Loading