Selkie.git | lib/Selkie/App/Internal/ | ErrorLogPlatform.rakumod
=begin pod
=head1 NAME
Selkie::App::Internal::ErrorLogPlatform - platform fd adapter for error logging
=head1 DESCRIPTION
Internal implementation detail used by C<Selkie::App::Internal::ErrorLog>.
=end pod
unit module Selkie::App::Internal::ErrorLogPlatform;
use NativeCall;
use Selkie::App::Internal::PosixFD;
# Official Windows MoarVM builds use /MT, so their integer file descriptors
# belong to a private, statically linked CRT. No NativeCall DLL owns that
# descriptor table, and MoarVM descriptors must never be passed to another
# CRT. Windows therefore redirects three independent stderr views: Raku's
# PROCESS::<$ERR> object, the Win32 standard handle, and — with descriptors
# created wholly inside ucrtbase (see the ucrt-* subs below) — the shared
# UCRT's fd 2, which C-level fprintf(stderr) writers go through. POSIX has
# a single libc, so mutating fd 2 there covers everything at once.
our sub error-log-api-spec(Bool:D $is-win --> Map:D) is export(:test) {
$is-win
?? Map.new(
api => "win32",
library => "kernel32",
create-file => "CreateFileW",
close-handle => "CloseHandle",
)
!! Map.new(
api => "posix",
library => Str,
dup => "dup",
dup2 => "dup2",
close => "close",
)
}
our constant IS-WINDOWS is export = $*DISTRO.is-win;
our constant WINDOWS-STD-ERROR-HANDLE is export = 0xFFFF_FFF4;
our constant WINDOWS-FILE-APPEND-DATA is export = 0x0000_0004;
our constant WINDOWS-SYNCHRONIZE is export = 0x0010_0000;
our constant WINDOWS-FILE-SHARE-ALL is export = 0x0000_0007;
our constant WINDOWS-OPEN-ALWAYS is export = 4;
our constant WINDOWS-FILE-ATTRIBUTE-NORMAL is export = 0x0000_0080;
our sub fd-dup(int32 $fd --> int32) is export {
posix-fd-dup($fd)
}
our sub fd-dup2(int32 $source, int32 $target --> int32) is export {
posix-fd-dup2($source, $target)
}
our sub fd-close(int32 $fd --> int32) is export {
posix-fd-close($fd)
}
# GetStdHandle and SetStdHandle take a DWORD, not a signed integer. The
# standard-handle constants are C casts such as ((DWORD)-12), whose value at
# the ABI boundary is the unsigned 32-bit integer 0xFFFFFFF4. Passing -12 as
# an int32 is not portable through foreign-function interfaces.
our sub windows-get-std-handle(uint32 --> Pointer) is export
is native("kernel32") is symbol("GetStdHandle") { * }
our sub windows-set-std-handle(uint32, Pointer --> int32) is export
is native("kernel32") is symbol("SetStdHandle") { * }
our sub windows-create-file(
Str is encoded("utf16"), uint32, uint32, Pointer,
uint32, uint32, Pointer --> Pointer
) is export is native("kernel32") is symbol("CreateFileW") { * }
our sub windows-close-handle(Pointer --> int32) is export
is native("kernel32") is symbol("CloseHandle") { * }
# The shared Universal CRT (ucrtbase.dll) keeps its own descriptor table and
# FILE* set, bound to the console at process start and never re-read from the
# Win32 standard-handle table. Every MSYS2-ucrt64 or modern-MSVC DLL in the
# process — GLib/libvips warnings, sqlcipher diagnostics, ffmpeg chatter —
# fprintf(stderr)s through it, so SetStdHandle alone leaves all of that on the
# terminal, where a single newline scrolls the TUI behind notcurses' back.
# Redirect it the same way the POSIX path redirects fd 2, but with both fds
# created *inside ucrtbase*: _wopen the log, _dup2 it over ucrt fd 2, keep a
# _dup of the original for restore. MoarVM's private /MT CRT is never
# involved, so the caveat at the top of this file is respected.
our constant UCRT-O-WRONLY is export = 0x0001;
our constant UCRT-O-APPEND is export = 0x0008;
our constant UCRT-O-CREAT is export = 0x0100;
our constant UCRT-O-BINARY is export = 0x8000;
our constant UCRT-S-IREAD-IWRITE is export = 0x0180;
our constant WINDOWS-HANDLE-FLAG-PROTECT-FROM-CLOSE is export = 0x0002;
our sub ucrt-dup(int32 --> int32) is export
is native("ucrtbase") is symbol("_dup") { * }
our sub ucrt-dup2(int32, int32 --> int32) is export
is native("ucrtbase") is symbol("_dup2") { * }
our sub ucrt-close(int32 --> int32) is export
is native("ucrtbase") is symbol("_close") { * }
our sub ucrt-wopen(Str is encoded("utf16"), int32, int32 --> int32) is export
is native("ucrtbase") is symbol("_wopen") { * }
our sub ucrt-get-osfhandle(int32 --> Pointer) is export
is native("ucrtbase") is symbol("_get_osfhandle") { * }
our sub windows-set-handle-information(Pointer, uint32, uint32 --> int32) is export
is native("kernel32") is symbol("SetHandleInformation") { * }