elixir icon indicating copy to clipboard operation
elixir copied to clipboard

`IEx.Helpers.c/1` no longer writes compilation errors to attached remote console

Open gmalkas opened this issue 9 months ago • 3 comments

Elixir and Erlang/OTP versions

Erlang/OTP 26 [erts-14.2.2] [source] [64-bit] [smp:24:24] [ds:24:24:10] [async-threads:1] [jit:ns]

Elixir 1.16.2 (compiled with Erlang/OTP 24)

Operating system

Debian 12

Current behavior

On Elixir 1.16.2, starting a brand new Mix project, then:

$ echo "IO.puts(a)" > script.exs
$ iex --sname main -S mix
iex(main@iris)1> c("script.exs")
    error: undefined variable "a"
    │
  1 │ IO.puts(a)
    │         ^
    │
    └─ script.exs:1:9


== Compilation error in file script.exs ==
** (CompileError) script.exs: cannot compile file (errors have been logged)

** (CompileError) compile error
    (iex 1.16.2) lib/iex/helpers.ex:210: IEx.Helpers.c/2

But if doing the same thing from a remote shell:

$ iex --sname baz --remsh main
Erlang/OTP 26 [erts-14.2.2] [source] [64-bit] [smp:24:24] [ds:24:24:10] [async-threads:1] [jit:ns]

Interactive Elixir (1.16.2) - press Ctrl+C to exit (type h() ENTER for help)
iex(main@iris)1> c("script.exs")
** (CompileError) compile error
    (iex 1.16.2) lib/iex/helpers.ex:210: IEx.Helpers.c/2
iex(main@iris)1>

while the actual compilation error is shown on the "main" shell:

iex(main@iris)1>     error: undefined variable "a"
    │
  1 │ IO.puts(a)
    │         ^
    │
    └─ script.exs:1:9


== Compilation error in file script.exs ==
** (CompileError) script.exs: cannot compile file (errors have been logged)

Expected behavior

On Elixir 1.13, the compilation error is shown on the remote shell:

$ iex --sname baz --remsh main
sh: 1: exec: tty_sl: not found
Erlang/OTP 26 [erts-14.2.2] [source] [64-bit] [smp:24:24] [ds:24:24:10] [async-threads:1] [jit:ns]

Interactive Elixir (1.13.0) - press Ctrl+C to exit (type h() ENTER for help)
iex(main@iris)1> c("script.exs")

== Compilation error in file script.exs ==
** (CompileError) script.exs:1: undefined function a/0 (there is no such import)
    (elixir 1.13.0) lib/kernel/parallel_compiler.ex:347: anonymous fn/5 in Kernel.ParallelCompiler.spawn_workers/7
** (CompileError)  compile error
    (iex 1.13.0) lib/iex/helpers.ex:203: IEx.Helpers.c/2
iex(main@iris)1>

This is extremely useful when running ad-hoc scripts on running systems. On Elixir 1.16, the compilation error is still logged on stderr of the main Elixir process, which can be accessed with journalctl when running an Elixir release with systemd for example, but it's not as intuitive as having the full error logged in the shell that requested the compilation.

I suspect this was broken by the new parallel compiler introduced in 1.15.

Thank you!

gmalkas avatar May 02 '24 21:05 gmalkas

This is tricky. Compilation warnings/errors print to stderr which I would believe to be the semantically correct place (while before it did not). However, Erlang does not redirect stderr across nodes, which means they stay in the original shell.

We have three options:

  1. Change Erlang to also redirect stderr across nodes
  2. Change the compiler to print to stdout
  3. Make the compiler detect if it is running a remote node and send to stdout only in such cases

I am not happy with any of them.

josevalim avatar May 03 '24 07:05 josevalim

Agreed, the solutions all seem "hacky", except maybe for the first one but that's a bigger change. I think it's good to have this issue documented though, because from a developer's perspective, it's a workflow that has been broken.

For now, I will just use journalctl to check stderr of the Elixir release process, that works even if a bit tedious.

Thanks for looking into it!

gmalkas avatar May 06 '24 15:05 gmalkas

Can you send a PR to document that any compilation error is written to stderr and therefore, in case of remote nodes, it will be available in the parent node? Thank you.

josevalim avatar May 06 '24 16:05 josevalim