Skip to content

Commit 57dd570

Browse files
committed
Add console:print_err/1 and route io standard_error to stderr
Signed-off-by: Paul Guyot <pguyot@kallisys.net>
1 parent 8cb9b4a commit 57dd570

6 files changed

Lines changed: 49 additions & 10 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99
### Added
1010
- Added `filename:dirname/1`, `filename:basename/1,2`, `filename:extension/1`, `filename:rootname/1,2` and `filename:join/2`
1111
- Added `init:get_arguments/0`
12+
- Added `console:print_err/1` to write to standard error
1213
- Added Erlang distribution over serial (uart)
1314
- Added WASM32 JIT backend for Emscripten platform
1415
- Added `network:wifi_scan/0,1` to ESP32 network driver to scan available APs when in sta or sta+ap mode.
@@ -54,6 +55,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
5455
`header_continuation` / `trailer_header_continuation` response events are no longer emitted
5556

5657
### Fixed
58+
- Route `io:put_chars(standard_error, ...)` and `io:format(standard_error, ...)` to stderr instead
59+
of aliasing them to standard_io (diagnostics no longer pollute an escript's stdout)
5760
- Stop using deprecated `term_from_int32` on STM32 platform
5861
- Stop using deprecated `term_from_int32` on RP2 platform
5962
- Stop using deprecated `term_from_int32` on ESP32 platform

UPDATING.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ were using the `0x210000` offset.
4242
* Instantiate with `const module = await AtomVM({ arguments: [...] })`
4343
* The old implicit global-script pattern is no longer supported
4444
* `AtomVM.worker.js` is no longer emitted; only `AtomVM.mjs` and `AtomVM.wasm` are needed
45+
- `io:put_chars(standard_error, ...)` (and `io:format/3`, `io:fwrite/3` targeting
46+
`standard_error`) now write directly to the OS standard error stream via `console:print_err/1`,
47+
instead of being routed through the process group leader as an alias for `standard_io`. Code or
48+
tests that captured `standard_error` output by swapping the group leader must instead redirect
49+
or intercept the underlying stderr stream, or pass an explicit pid/device.
4550

4651
## v0.6.4 -> v0.6.5
4752

libs/eavmlib/src/console.erl

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
%%-----------------------------------------------------------------------------
2525
-module(console).
2626

27-
-export([start/0, puts/1, puts/2, flush/0, flush/1, print/1]).
27+
-export([start/0, puts/1, puts/2, flush/0, flush/1, print/1, print_err/1]).
2828

2929
%%-----------------------------------------------------------------------------
3030
%% @param Text the string data to write to the console
@@ -74,6 +74,17 @@ flush(Console) ->
7474
print(_Text) ->
7575
erlang:nif_error(undefined).
7676

77+
%%-----------------------------------------------------------------------------
78+
%% @param Text the data to write to the standard error
79+
%% @returns ok if the data was written, or {error, Reason}, if there was
80+
%% an error.
81+
%% @doc Write a string to the console's standard error.
82+
%% @end
83+
%%-----------------------------------------------------------------------------
84+
-spec print_err(Text :: iodata()) -> ok | {error, term()}.
85+
print_err(_Text) ->
86+
erlang:nif_error(undefined).
87+
7788
%% Internal operations
7889

7990
%% @private

libs/estdlib/src/io.erl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ put_chars(standard_io, Chars) ->
257257
execute_request(Leader, {put_chars, unicode, Chars})
258258
end;
259259
put_chars(standard_error, Chars) ->
260-
put_chars(standard_io, Chars).
260+
console:print_err(Chars).
261261

262262
%% @private
263263
convert_request({requests, Requests}) ->

src/libAtomVM/nifs.c

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,7 @@ static term nif_atomvm_get_start_beam(Context *ctx, int argc, term argv[]);
257257
static term nif_atomvm_read_priv(Context *ctx, int argc, term argv[]);
258258
static term nif_atomvm_get_creation(Context *ctx, int argc, term argv[]);
259259
static term nif_console_print(Context *ctx, int argc, term argv[]);
260+
static term nif_console_print_err(Context *ctx, int argc, term argv[]);
260261
static term nif_base64_encode(Context *ctx, int argc, term argv[]);
261262
static term nif_base64_decode(Context *ctx, int argc, term argv[]);
262263
static term nif_base64_encode_to_string(Context *ctx, int argc, term argv[]);
@@ -827,6 +828,10 @@ static const struct Nif console_print_nif = {
827828
.base.type = NIFFunctionType,
828829
.nif_ptr = nif_console_print
829830
};
831+
static const struct Nif console_print_err_nif = {
832+
.base.type = NIFFunctionType,
833+
.nif_ptr = nif_console_print_err
834+
};
830835
static const struct Nif base64_encode_nif = {
831836
.base.type = NIFFunctionType,
832837
.nif_ptr = nif_base64_encode
@@ -5635,16 +5640,14 @@ static term nif_atomvm_get_creation(Context *ctx, int argc, term argv[])
56355640
return term_make_maybe_boxed_int64(ctx->global->creation, &ctx->heap);
56365641
}
56375642

5638-
static term nif_console_print(Context *ctx, int argc, term argv[])
5643+
static term console_print_to(FILE *stream, Context *ctx, term argv[])
56395644
{
5640-
UNUSED(argc);
5641-
56425645
term t = argv[0];
56435646
if (term_is_binary(t)) {
56445647
const char *data = term_binary_data(t);
5645-
unsigned long n = term_binary_size(t);
5646-
fprintf(stdout, "%.*s", (int) n, data);
5647-
fflush(stdout);
5648+
size_t n = term_binary_size(t);
5649+
fwrite(data, 1, n, stream);
5650+
fflush(stream);
56485651
} else {
56495652
size_t size;
56505653
switch (interop_iolist_size(t, &size)) {
@@ -5655,6 +5658,10 @@ static term nif_console_print(Context *ctx, int argc, term argv[])
56555658
case InteropBadArg:
56565659
RAISE_ERROR(BADARG_ATOM);
56575660
}
5661+
if (size == 0) {
5662+
fflush(stream);
5663+
return OK_ATOM;
5664+
}
56585665
char *buf = malloc(size);
56595666
if (IS_NULL_PTR(buf)) {
56605667
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
@@ -5669,13 +5676,25 @@ static term nif_console_print(Context *ctx, int argc, term argv[])
56695676
free(buf);
56705677
RAISE_ERROR(BADARG_ATOM);
56715678
}
5672-
fprintf(stdout, "%.*s", (int) size, buf);
5673-
fflush(stdout);
5679+
fwrite(buf, 1, size, stream);
5680+
fflush(stream);
56745681
free(buf);
56755682
}
56765683
return OK_ATOM;
56775684
}
56785685

5686+
static term nif_console_print(Context *ctx, int argc, term argv[])
5687+
{
5688+
UNUSED(argc);
5689+
return console_print_to(stdout, ctx, argv);
5690+
}
5691+
5692+
static term nif_console_print_err(Context *ctx, int argc, term argv[])
5693+
{
5694+
UNUSED(argc);
5695+
return console_print_to(stderr, ctx, argv);
5696+
}
5697+
56795698
// clang-format off
56805699
static const char b64_table[64] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
56815700
'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',

src/libAtomVM/nifs.gperf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@ code_server:type_resolver/2, IF_HAVE_JIT(&code_server_type_resolver_nif)
225225
code_server:import_resolver/2, IF_HAVE_JIT(&code_server_import_resolver_nif)
226226
code_server:set_native_code/3, IF_HAVE_JIT(&code_server_set_native_code_nif)
227227
console:print/1, &console_print_nif
228+
console:print_err/1, &console_print_err_nif
228229
base64:encode/1, &base64_encode_nif
229230
base64:encode/2, &base64_encode_nif
230231
base64:decode/1, &base64_decode_nif

0 commit comments

Comments
 (0)