Skip to content

Commit f7594af

Browse files
committed
Merge pull request #2346 from pguyot/w25/console-print-err
Add console:print_err/1 and route io standard_error to stderr These changes are made under both the "Apache 2.0" and the "GNU Lesser General Public License 2.1 or later" license terms (dual license). SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
2 parents d880182 + 57dd570 commit f7594af

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
@@ -31,6 +31,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3131
- Added support for map comprehensions
3232
- Added USB CDC port drivers for ESP32, RP2, and STM32 platforms
3333
- Added a Linux `gpio` driver for the generic_unix port (in `avm_unix`) using sysfs
34+
- Added `console:print_err/1` to write to standard error
3435

3536
### Changed
3637
- Updated network type db() to dbm() to reflect the actual representation of the type
@@ -63,6 +64,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6364
`header_continuation` / `trailer_header_continuation` response events are no longer emitted
6465

6566
### Fixed
67+
- Route `io:put_chars(standard_error, ...)` and `io:format(standard_error, ...)` to stderr instead
68+
of aliasing them to standard_io (diagnostics no longer pollute an escript's stdout)
6669
- Stop using deprecated `term_from_int32` on STM32 platform
6770
- Stop using deprecated `term_from_int32` on RP2 platform
6871
- 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[]);
@@ -829,6 +830,10 @@ static const struct Nif console_print_nif = {
829830
.base.type = NIFFunctionType,
830831
.nif_ptr = nif_console_print
831832
};
833+
static const struct Nif console_print_err_nif = {
834+
.base.type = NIFFunctionType,
835+
.nif_ptr = nif_console_print_err
836+
};
832837
static const struct Nif base64_encode_nif = {
833838
.base.type = NIFFunctionType,
834839
.nif_ptr = nif_base64_encode
@@ -5796,16 +5801,14 @@ static term nif_atomvm_get_creation(Context *ctx, int argc, term argv[])
57965801
return term_make_maybe_boxed_int64(ctx->global->creation, &ctx->heap);
57975802
}
57985803

5799-
static term nif_console_print(Context *ctx, int argc, term argv[])
5804+
static term console_print_to(FILE *stream, Context *ctx, term argv[])
58005805
{
5801-
UNUSED(argc);
5802-
58035806
term t = argv[0];
58045807
if (term_is_binary(t)) {
58055808
const char *data = term_binary_data(t);
5806-
unsigned long n = term_binary_size(t);
5807-
fprintf(stdout, "%.*s", (int) n, data);
5808-
fflush(stdout);
5809+
size_t n = term_binary_size(t);
5810+
fwrite(data, 1, n, stream);
5811+
fflush(stream);
58095812
} else {
58105813
size_t size;
58115814
switch (interop_iolist_size(t, &size)) {
@@ -5816,6 +5819,10 @@ static term nif_console_print(Context *ctx, int argc, term argv[])
58165819
case InteropBadArg:
58175820
RAISE_ERROR(BADARG_ATOM);
58185821
}
5822+
if (size == 0) {
5823+
fflush(stream);
5824+
return OK_ATOM;
5825+
}
58195826
char *buf = malloc(size);
58205827
if (IS_NULL_PTR(buf)) {
58215828
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
@@ -5830,13 +5837,25 @@ static term nif_console_print(Context *ctx, int argc, term argv[])
58305837
free(buf);
58315838
RAISE_ERROR(BADARG_ATOM);
58325839
}
5833-
fprintf(stdout, "%.*s", (int) size, buf);
5834-
fflush(stdout);
5840+
fwrite(buf, 1, size, stream);
5841+
fflush(stream);
58355842
free(buf);
58365843
}
58375844
return OK_ATOM;
58385845
}
58395846

5847+
static term nif_console_print(Context *ctx, int argc, term argv[])
5848+
{
5849+
UNUSED(argc);
5850+
return console_print_to(stdout, ctx, argv);
5851+
}
5852+
5853+
static term nif_console_print_err(Context *ctx, int argc, term argv[])
5854+
{
5855+
UNUSED(argc);
5856+
return console_print_to(stderr, ctx, argv);
5857+
}
5858+
58405859
// clang-format off
58415860
static const char b64_table[64] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
58425861
'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
@@ -230,6 +230,7 @@ code_server:type_resolver/2, IF_HAVE_JIT(&code_server_type_resolver_nif)
230230
code_server:import_resolver/2, IF_HAVE_JIT(&code_server_import_resolver_nif)
231231
code_server:set_native_code/3, IF_HAVE_JIT(&code_server_set_native_code_nif)
232232
console:print/1, &console_print_nif
233+
console:print_err/1, &console_print_err_nif
233234
base64:encode/1, &base64_encode_nif
234235
base64:encode/2, &base64_encode_nif
235236
base64:decode/1, &base64_decode_nif

0 commit comments

Comments
 (0)