Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 16 additions & 10 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ name: CI
# run under node (checks/emscripten); a program that starts one
# fails at LINK naming the openkal symbol (checks/absent)
#
# The library is built with the released mcpp, 2026.11.5.1. The ok images
# The library is built with the released mcpp, 2026.11.7.2, which resolves
# within the platform's version group (an earlier mcpp takes the newest member
# of a range and then refuses it against the group). The ok images
# (MCPP_PLATFORM) and checks/hosted select openkal-platform 0.24 and its
# OpenKal 0.24 implementation, musl 0.28 and C++ runtime 0.19 group. The library
# itself needs 2026.10.27.1 or later, the first that selects a platform
Expand All @@ -35,7 +37,7 @@ env:
MCPP_SOURCE_REF: ${{ github.event.inputs.mcpp_ref || vars.MCPP_SOURCE_REF || '' }}
# 2026.11.3.1: the std module compiled with the platform's words (openkal-platform 0.23.0's
# `-fcf-protection=full` refused every `import std` of an earlier mcpp's).
MCPP_INSTALL: ${{ (github.event.inputs.mcpp_ref || vars.MCPP_SOURCE_REF) && 'mcpp' || 'mcpp@2026.11.5.1' }}
MCPP_INSTALL: ${{ (github.event.inputs.mcpp_ref || vars.MCPP_SOURCE_REF) && 'mcpp' || 'mcpp@2026.11.7.2' }}
XLINGS_VERSION: v2026.10.26.1
XLINGS_NON_INTERACTIVE: '1'

Expand Down Expand Up @@ -225,10 +227,12 @@ jobs:
timeout-minutes: 120
env:
# openkal 0.24.0, which this release is written against, in the group of
# openkal-platform 0.24.2, whose C++ run-time keeps the volume of a
# normalized name (openkal-llvm-runtime 0.19.2); the long-spelling tests
# of 0.3.1 need it on Windows.
MCPP_PLATFORM: openkal-platform@0.24.2
# openkal-platform 0.24.4: its C++ run-time keeps the volume of a
# normalized name (openkal-llvm-runtime 0.19.2, which the long-spelling
# tests of 0.3.1 need on Windows), and its tools are openkal-ld and
# openkal-run 0.5.3, whose okrun enters an image in its convention's
# floating-point state.
MCPP_PLATFORM: openkal-platform@0.24.4
defaults:
run:
shell: bash
Expand Down Expand Up @@ -278,10 +282,12 @@ jobs:
timeout-minutes: 45
env:
# openkal 0.24.0, which this release is written against, in the group of
# openkal-platform 0.24.2, whose C++ run-time keeps the volume of a
# normalized name (openkal-llvm-runtime 0.19.2); the long-spelling tests
# of 0.3.1 need it on Windows.
MCPP_PLATFORM: openkal-platform@0.24.2
# openkal-platform 0.24.4: its C++ run-time keeps the volume of a
# normalized name (openkal-llvm-runtime 0.19.2, which the long-spelling
# tests of 0.3.1 need on Windows), and its tools are openkal-ld and
# openkal-run 0.5.3, whose okrun enters an image in its convention's
# floating-point state.
MCPP_PLATFORM: openkal-platform@0.24.4
strategy:
fail-fast: false
matrix:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/unit-lifecycle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ jobs:
- name: Install the actual native compiler
run: |
xlings update
xlings install mcpp@2026.11.5.1 -y -g
xlings install mcpp@2026.11.7.2 -y -g
spec='${{ matrix.toolchain }}'
case "$spec" in
msvc@system) mcpp toolchain default msvc ;;
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ compile_commands.json
*.log
mcpp.lock

.mcpp/
43 changes: 43 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,48 @@
# Changelog

## 0.4.0

- **`is_openkal`, named as its platform is.** A build for the openkal target
(`<arch>-openkal`, mcpp's `cfg(os = "openkal")`), whose artifact is an ok
image, is `is_openkal`, as a build for Linux is `is_linux`: each
`is_<platform>` names the target of the build, and `platform::openkal` is
this one's. `is_image`, its name since 0.1.0, read as though it named a
container's or a disk's image, and as though only it were over openkal, which
every build of this package is. It remains, deprecated, as the same constant.
- **A platform question for a native build and an image alike.** `can_run_on(p)`
says whether this build can run on `p` (a native build on its own system, an
image on every system a loader runs upon), and `on<P>(f)` runs `f` when the
build runs on `P` now. `if constexpr (can_run_on(P))` keeps another system's
platform module out of a native build, as `if constexpr (is_<platform>)` does;
`host()` then decides in an image, where `is_<platform>` is false and a branch
under it never runs. `on<P>` takes the platform as an argument, so its body is
a template a build that cannot run on `P` never instantiates; a lambda without
the parameter is refused. `is_<platform>` keeps its meaning: a native build of
that platform (README, "A platform question").
- **The platform modules in an image (R5 as revised).** A platform module is
defined where it can be answered: in a native build of its platform, and in an
image where the image can ask the system it runs upon. `linux::translator()`
reads /proc as on Linux; `windows::elevated()` reads the integrity level
`whoami /groups` lists, by SID; `windows::on_dev_drive()` asks `fsutil devdrv
query`; `posix::effective_user()` reads /proc on Linux and `/usr/bin/id -u` on
macOS; `macos::translated()` is `false`, since macOS runs images through an
aarch64 okrun only. The system's programs are started by absolute name with a
bound of ten seconds, and each answer is asked once. The code pages, long
names, the registry, the writable drives and the creation mask remain
undefined in an image: okrun carries an image's streams and names, and the
others belong to a native installation. Before 0.4.0 an image defined no
platform module, and a program shipped as an image that guarded them with
`if constexpr (is_<platform>)` lost them without a word (mcpp-community/mcpp
design 2026-11-08, S2 and S3).
- **The user and the creation mask are the kernel's.** On Linux and macOS
`posix::effective_user()` and `posix::set_creation_mask()` make the kernel's
own calls (src/posix/kernel.h), as the descriptors and the signals already
did. In the hosted arrangement the C library is openkal-musl, which sits on
openkal: openkal names no user, so its `geteuid` answers the constant 1000,
and it has no `umask`. The answer was then 1000 for every user, and a mask
was set nowhere; it is now the process's under every C library. Measured on
the CI runners (user 1001) and under `unshare -r` (user 0).

## 0.3.1

- **A name in the long spelling is the name it spells.** `fs::extended` writes
Expand Down
57 changes: 49 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ with compile-time platform constants and a few platform modules.

```toml
[dependencies]
os = { namespace = "mcpplibs", version = "0.3.1" }
mcpplibs.os = "0.4.0"
```

```cpp
Expand Down Expand Up @@ -106,7 +106,7 @@ dropped (0.2.8).

| Module | Contents |
|---|---|
| `mcpplibs.os.platform` | `PLATFORM`, `ARCH`, `is_linux` / `is_macos` / `is_windows` / `is_posix` / `is_emscripten`, `exe_suffix`, `path_list_separator`; `host()`, a constant expression on a native target |
| `mcpplibs.os.platform` | `PLATFORM`, `ARCH`, `is_linux` / `is_macos` / `is_windows` / `is_posix` / `is_openkal` / `is_emscripten`, `exe_suffix`, `path_list_separator`; `host()`, a constant expression on a native target; `can_run_on(p)` and `on<P>(f)`, a platform question that holds in a native build and an image alike |
| `mcpplibs.os.error` | `errc` (openkal's closed set of conditions), `error`, `result<T>` |
| `mcpplibs.os.env` | variables (names without case on Windows), `NAME=value` entries as overrides (`assignments`), arguments, the program's own image, lists of directories in the host's separator (`split_list`, `join_list`), PATH search with PATHEXT |
| `mcpplibs.os.fs` | `write_atomic`, `publish` (no replace), `append`, `sync`, `caps`, `lock`, links, `set_executable`, `stamp_of`, `identity_of`, `owner_of`, `writable`, `locate`, `absolute`, `names_a_place`, `canonical`, a bounded `read`; what a volume does, measured (`measure`, `volume_type`), and the operations that follow it (`place`, `exchange`); names in the host's rules without the file system: `generic`, `native`, `extended`, `is_absolute`, `same`, `within` |
Expand All @@ -120,7 +120,7 @@ dropped (0.2.8).
| `mcpplibs.os.machine` | the machine's architecture (through Rosetta and WoW64), memory and the memory available now, processors (logical, physical, of differing speed) |
| `mcpplibs.os.random` | bytes from the system's source of unpredictable bytes; `hex` |
| `mcpplibs.os.library` | shared libraries at run time: `open` (a path or a name the system searches for), `loaded` (one the process has, without loading it), a handle's `symbol` / `function<F>`, `process_symbol` (as the loader resolves one), `suffix` |
| `mcpplibs.os.linux` / `.macos` / `.windows` / `.posix` | platform modules: declared on every target, defined only on their own |
| `mcpplibs.os.linux` / `.macos` / `.windows` / `.posix` | platform modules: declared on every target, defined on their own and, where an image can answer, in an image |

## Rules

Expand All @@ -133,9 +133,10 @@ dropped (0.2.8).
- in an image (`<arch>-openkal`) it is an ordinary function, and using it in a constant
expression is a compile error.
- **R4.** A native handle does not appear in a portable type.
- **R5.** A platform module is declared on every target and defined only on its own. A call
outside `if constexpr` for that platform is a link error. The same call in a discarded branch
is not an odr-use, so it links. CI asserts both.
- **R5.** A platform module is declared on every target and defined where it can be answered: in
a native build of its own platform, and in an image where the image can ask the system it runs
upon (0.4.0). Elsewhere a call outside `if constexpr` is a link error. The same call in a
discarded branch is not an odr-use, so it links. CI asserts both.
- **R6.** Where openkal is not in its shared form, this package's resources do not cross a
shared-object boundary.
- **R7.** A name reaches the system as UTF-8 and comes back as UTF-8 on every host, never
Expand All @@ -145,16 +146,56 @@ dropped (0.2.8).
nothing else. The tests write, read and rename names in four scripts.

Interface units import `std` and contain no system header and no macro. Implementation units
on openkal import only `openkal.*`. The Windows and macOS units declare the functions they
on openkal import only `openkal.*` and this package's own modules. The Windows and macOS units declare the functions they
call themselves (`src/windows/win32.h`, `src/macos/darwin.h`). Linux's and macOS's kernel
calls are in `src/linux/arch/` and `src/macos/arch/`, which the manifest selects by
architecture.

## A platform question, in a native build and an image

Two questions are asked of the platform, and each has its form:

| Question | Form | Native build | Image |
|---|---|---|---|
| Is this a native build for the platform? | `if constexpr (os::is_windows)` | a constant | `false` |
| Does this build run on the platform now? | `if constexpr (os::can_run_on(P))` with `if (os::host() == P)`, or `os::on<P>([&](auto) { ... })` | a constant; another system's branch is discarded | known when the image is loaded |

The first is right for code that belongs to a native build alone: a console's code page set
for the C library's narrow functions has no meaning in an image, whose streams okrun carries.
The second is right for code that is to take effect wherever the program runs, which is what a
program shipped as an image needs:

```cpp
if constexpr (os::can_run_on(os::platform::windows))
if (os::host() == os::platform::windows)
onDevDrive = os::windows::on_dev_drive(cache);

os::on<os::platform::linux>([&](auto) { sandbox = os::linux::translator().value_or(""); });
```

`on<P>` takes the platform as a parameter, so the body is a template and a build that cannot
run on `P` never instantiates it; a lambda without the parameter is refused, because its body
would name another system's module on every build. The platform modules in an image:

| Function | In an image |
|---|---|
| `linux::translator()` | `/proc/self/status`, the tracer's `comm` and the environment, as on Linux |
| `windows::elevated()` | the integrity level `whoami /groups` lists (SID `S-1-16-12288` or `S-1-16-16384`) |
| `windows::on_dev_drive()` | `fsutil devdrv query <volume>`; not known when the tool answers in another language or refuses |
| `posix::effective_user()` | `/proc/self/status` on Linux, `/usr/bin/id -u` on macOS; the largest number when neither answers |
| `macos::translated()` | `false`: macOS runs images through an aarch64 okrun only |
| `windows::code_page()`, `console_output_code_page()`, `long_paths_enabled()`, `machine_registry_path()`, `writable_fixed_drive()`, `posix::set_creation_mask()` | not defined: okrun carries an image's streams and names, and the others belong to a native installation |

The system's programs are started by absolute name (`%SystemRoot%\System32`, `/usr/bin`), with a
bound of ten seconds, and each answer is asked once per process. `host()` decides which system's
question applies, which is that system's convention; whether a program can be started is
openkal's capability and is answered by the start itself (openkal SPEC clause 30).

## As an ok image

Built for `<arch>-openkal` (openkal SPEC clause 12), a program over this library
is one image per architecture that `okrun` runs on Linux, macOS and Windows.
`os::PLATFORM` is `platform::openkal` and `os::is_image` is true; `os::host()`
`os::PLATFORM` is `platform::openkal` and `os::is_openkal` is true; `os::host()`
is an ordinary function there, answered when the image is loaded, and
`os::conventions()` follows it; `exe_suffix` and `path_list_separator` are not
provided, so code that reads them is a compile error rather than a wrong answer
Expand Down
4 changes: 3 additions & 1 deletion checks/constants/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ namespace os = mcpplibs::os;

static_assert(os::host() == os::PLATFORM);
static_assert(os::is_linux + os::is_macos + os::is_windows + os::is_emscripten == 1);
static_assert(!os::is_image);
static_assert(!os::is_openkal);
// 0.1.0-0.3.x's name, kept as a deprecated alias of the same constant (0.4.0): it compiles, warning.
static_assert(os::is_image == os::is_openkal);
static_assert(os::exe_suffix == (os::is_windows ? ".exe" : ""));
static_assert(os::path_list_separator == (os::is_windows ? ';' : ':'));
static_assert(os::conventions_of(os::host()).exe_suffix == os::exe_suffix);
Expand Down
5 changes: 5 additions & 0 deletions checks/discarded/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,10 @@ int main() {
if constexpr (os::is_linux) n += static_cast<int>(os::linux::translator().has_value());
if constexpr (os::is_macos) n += static_cast<int>(os::macos::translated());
if constexpr (os::is_windows) n += static_cast<int>(os::windows::elevated());
// The same calls in the form that also holds in an image (0.4.0).
if constexpr (os::can_run_on(os::platform::linux))
if (os::host() == os::platform::linux) n += static_cast<int>(os::linux::translator().has_value());
os::on<os::platform::macos>([&](auto) { n += static_cast<int>(os::macos::translated()); });
os::on<os::platform::windows>([&](auto) { n += static_cast<int>(os::windows::elevated()); });
return n >= 0 ? 0 : 1;
}
6 changes: 6 additions & 0 deletions checks/emscripten/tests/facts_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// facts_test.cpp of mcpplibs.os, compiled in this arrangement. The imports are restated
// because the dependency scan reads this file and not the one it includes.
#include "../../../tests/check.hpp"
import std;
import mcpplibs.os;
#include "../../../tests/facts_test.cpp"
2 changes: 1 addition & 1 deletion checks/hosted/mcpp.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ version = "0.1.0"
os = { path = "../.." }

[target.'cfg(any(linux, macos, windows))']
platform = "openkal-platform@0.24.2"
platform = "openkal-platform@0.24.4"

# The tests are the package's own, each reached by an include from
# tests/<name>.cpp here (a test program is a file of the package that runs it).
Expand Down
6 changes: 6 additions & 0 deletions checks/hosted/tests/facts_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// facts_test.cpp of mcpplibs.os, compiled in this arrangement. The imports are restated
// because the dependency scan reads this file and not the one it includes.
#include "../../../tests/check.hpp"
import std;
import mcpplibs.os;
#include "../../../tests/facts_test.cpp"
11 changes: 7 additions & 4 deletions mcpp.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
namespace = "mcpplibs"
name = "os"
version = "0.3.1"
version = "0.4.0"
description = "mcpplibs.os: a C++ operating-system library over openkal --- processes, files, environment, terminals, directories and local IPC, with compile-time platform constants and a few platform modules."
license = "Apache-2.0"
authors = ["mcpplibs"]
Expand Down Expand Up @@ -108,10 +108,13 @@ sources = ["src/windows/posix/stdio.cpp"]
sources = ["src/targets/emscripten.cppm", "src/emscripten/*.cpp", "src/posix/fs.cpp"]

# AN OK IMAGE (`<arch>-openkal', openkal SPEC clause 12): no implementation
# beneath --- the loader binds the image's `kal_*' --- and no platform module
# defined. `host()' and the conventions are answered when the image is loaded.
# beneath --- the loader binds the image's `kal_*' --- and the platform modules
# defined where an image can answer them (R5 as revised in 0.4.0): Linux's
# translator from /proc, Windows' token and developer volume and the POSIX
# effective user from the system's own programs, macOS' translation as never.
# `host()' and the conventions are answered when the image is loaded.
[target.'cfg(os = "openkal")'.build]
sources = ["src/targets/openkal.cppm", "src/openkal/*.cpp"]
sources = ["src/targets/openkal.cppm", "src/openkal/*.cpp", "src/linux/linux.cpp"]

# The C library, the C++ run-time and the loader of an image are its
# platform's: mcpp 2026.10.27.1 and later select openkal-platform for
Expand Down
7 changes: 6 additions & 1 deletion src/linux/arch/aarch64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ constexpr long nr_write = 64,
nr_uname = 160,
nr_sysinfo = 179,
nr_statfs = 43,
nr_renameat2 = 276;
nr_renameat2 = 276,
nr_geteuid = 175, nr_umask = 166;

constexpr long f_dupfd_cloexec = 1030, sig_unblock = 1;

Expand Down Expand Up @@ -61,6 +62,10 @@ void raise_signal(int signal) {
call(nr_kill, call(nr_getpid), signal);
}

unsigned effective_user() { return static_cast<unsigned>(call(nr_geteuid)); }

unsigned creation_mask(unsigned mask) { return static_cast<unsigned>(call(nr_umask, static_cast<long>(mask))); }

} // namespace mcpplibs::os::kernel

namespace mcpplibs::os::linux_kernel {
Expand Down
7 changes: 6 additions & 1 deletion src/linux/arch/riscv64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ constexpr long nr_write = 64,
nr_uname = 160,
nr_sysinfo = 179,
nr_statfs = 43,
nr_renameat2 = 276;
nr_renameat2 = 276,
nr_geteuid = 175, nr_umask = 166;

constexpr long f_dupfd_cloexec = 1030, sig_unblock = 1;

Expand Down Expand Up @@ -61,6 +62,10 @@ void raise_signal(int signal) {
call(nr_kill, call(nr_getpid), signal);
}

unsigned effective_user() { return static_cast<unsigned>(call(nr_geteuid)); }

unsigned creation_mask(unsigned mask) { return static_cast<unsigned>(call(nr_umask, static_cast<long>(mask))); }

} // namespace mcpplibs::os::kernel

namespace mcpplibs::os::linux_kernel {
Expand Down
7 changes: 6 additions & 1 deletion src/linux/arch/x86_64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ constexpr long nr_write = 1,
nr_uname = 63,
nr_sysinfo = 99,
nr_statfs = 137,
nr_renameat2 = 316;
nr_renameat2 = 316,
nr_geteuid = 107, nr_umask = 95;

constexpr long f_dupfd_cloexec = 1030, sig_unblock = 1;

Expand Down Expand Up @@ -61,6 +62,10 @@ void raise_signal(int signal) {
call(nr_kill, call(nr_getpid), signal);
}

unsigned effective_user() { return static_cast<unsigned>(call(nr_geteuid)); }

unsigned creation_mask(unsigned mask) { return static_cast<unsigned>(call(nr_umask, static_cast<long>(mask))); }

} // namespace mcpplibs::os::kernel

namespace mcpplibs::os::linux_kernel {
Expand Down
Loading
Loading