Open silbermm opened 2 years ago
The question is: Why does it work with Erlang module?
Digging into the source code of IEx.Helpers.open/1
,
We find that the problematic function is:
@elixir_apps ~w(eex elixir ex_unit iex logger mix)a
@otp_apps ~w(kernel stdlib)a
@apps @elixir_apps ++ @otp_apps
defp rewrite_source(module, source) do
case :application.get_application(module) do
{:ok, app} when app in @apps ->
Application.app_dir(app, rewrite_source(source))
_ ->
beam_path = :code.which(module)
if is_list(beam_path) and List.starts_with?(beam_path, :code.root_dir()) do
app_vsn = beam_path |> Path.dirname() |> Path.dirname() |> Path.basename()
Path.join([:code.root_dir(), "lib", app_vsn, rewrite_source(source)])
else
List.to_string(source)
end
end
end
Application.app_dir
is failing,
When the alternate path with :code.which
works.
Well if :code.which
works, what does it return for both of our cases?
I modified the source code of help_command.ex:
def process(%{topic: <<":" <> erlang_module>>, open: open}) do
IO.puts(:code.which(:"#{erlang_module}"))
And:
def process(%{topic: topic, open: open}) do
IO.puts(:code.which(:"Elixir.#{topic}"))
Recompiling and reinstalling the escript, I ran:
$ exdoc Enum --open
/usr/home/rowland/.asdf/install/elixir/1.13.3-otp-24/.mix/escripts/exdoc/Elixir.Enum.beam
<error message>
$ exdoc :lists --open
/home/rowland/.asdf/installs/erlang/24.3.2/lib/stdlib-3.17.1/ebin/lists.beam
Note that in the first case, Elixir is bundled into the escript itself,
But in the second case, it's using my local installation of Erlang.
Since rewrite_source/2
uses the directory it finds,
Even if we switched Elixir to use the :code.which
result,
It wouldn't find the .ex
files we need.
This makes sense when we remember that escripts produce an executable that can be run on any system with Erlang installed. Escript pre-supposes that a system has Erlang on it, and doesn't bundle the code inside the escript. Opting instead to use the local installation, wherever it is.
A potential solution: Since we're using mix to install the escript, We can presuppose that the machine has Elixir installed, After all it wouldn't be able to compile the script otherwise.
Looking again at rewrite_source
:
@elixir_apps ~w(eex elixir ex_unit iex logger mix)a
@otp_apps ~w(kernel stdlib)a
@apps @elixir_apps ++ @otp_apps
defp rewrite_source(module, source) do
case :application.get_application(module) do
{:ok, app} when app in @apps ->
Application.app_dir(app, rewrite_source(source))
There are only 8 different apps that we need directories for,
We could compute those 8 paths at compilation,
Or have the user set an environment variable, such as ELIXIR_SOURCE_PATH
And write a custom open
function to use those paths.
I think this has something to do with the
escript
build, because it works fine inside ofiex
.I've tried a few different solutions:
embed_elixir: true
to the escript configelixir
to the list of applicationsstrip_beams: false
In the end, none of those worked.
The error I get is: