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
27 changes: 19 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ 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.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
# The library is built with the released mcpp, 2026.11.9.1: its tests are
# mtest's, and mtest uses mcpplibs.os, so the package being built has to stand
# for itself in its tests' graph, which mcpp does from 2026.11.9.1; it also
# 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 @@ -37,7 +39,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.7.2' }}
MCPP_INSTALL: ${{ (github.event.inputs.mcpp_ref || vars.MCPP_SOURCE_REF) && 'mcpp' || 'mcpp@2026.11.9.1' }}
XLINGS_VERSION: v2026.10.26.1
XLINGS_NON_INTERACTIVE: '1'

Expand Down Expand Up @@ -79,7 +81,10 @@ jobs:

- name: The mcpp under cross-validation
run: |
# The configured mirror (GLOBAL), then CN (GitCode first) from the fourth attempt: a cached index
# can predate the release, and xlings keeps it when an anonymous refresh fails.
for attempt in 1 2 3 4 5 6; do
[ "$attempt" = 4 ] && { xlings config --mirror CN > /dev/null 2>&1 || true; }
xlings update > /dev/null 2>&1 || true
if xlings install "$MCPP_INSTALL" -y -g; then break; fi
[ "$attempt" = 6 ] && { echo "::error::mcpp could not be installed"; exit 1; }
Expand Down Expand Up @@ -113,7 +118,7 @@ jobs:
run: |
profile='${{ matrix.profile }}'
mcpp test --profile "$profile" 2>&1 | tee tests.log
for name in platform_test fs_test process_test ipc_test unit_test dirs_terminal_test linux_test; do
for name in platform_test fs_test process_test ipc_test unit_test dirs_terminal_test linux_test watch_test; do
grep -q "^$name \.\.\. ok" tests.log || { echo "::error::$name did not run or did not pass"; exit 1; }
done

Expand Down Expand Up @@ -177,7 +182,10 @@ jobs:

- name: The mcpp under cross-validation
run: |
# The configured mirror (GLOBAL), then CN (GitCode first) from the fourth attempt: a cached index
# can predate the release, and xlings keeps it when an anonymous refresh fails.
for attempt in 1 2 3 4 5 6; do
[ "$attempt" = 4 ] && { xlings config --mirror CN > /dev/null 2>&1 || true; }
xlings update > /dev/null 2>&1 || true
if xlings install "$MCPP_INSTALL" -y -g; then break; fi
[ "$attempt" = 6 ] && { echo "::error::mcpp could not be installed"; exit 1; }
Expand Down Expand Up @@ -211,7 +219,7 @@ jobs:
run: |
profile='${{ matrix.profile }}'
mcpp test --profile "$profile" ${{ matrix.target }} 2>&1 | tee tests.log
for name in platform_test fs_test process_test ipc_test unit_test dirs_terminal_test linux_test library_test; do
for name in platform_test fs_test process_test ipc_test unit_test dirs_terminal_test linux_test library_test watch_test; do
grep -q "^$name \.\.\. ok" tests.log || { echo "::error::$name did not run or did not pass"; exit 1; }
done

Expand Down Expand Up @@ -263,7 +271,7 @@ jobs:
run: |
set -euo pipefail
mcpp test --target x86_64-openkal 2>&1 | tee tests.log
for name in platform_test fs_test process_test ipc_test unit_test dirs_terminal_test linux_test; do
for name in platform_test fs_test process_test ipc_test unit_test dirs_terminal_test linux_test watch_test; do
grep -q "^$name \.\.\. ok" tests.log || { echo "::error::$name did not pass as an image"; exit 1; }
done
mcpp test --target aarch64-openkal --no-run
Expand Down Expand Up @@ -382,7 +390,10 @@ jobs:
| bash -s "$XLINGS_VERSION"
echo "$HOME/.xlings/subos/current/bin" >> "$GITHUB_PATH"
export PATH="$HOME/.xlings/subos/current/bin:$PATH"
# The configured mirror (GLOBAL), then CN (GitCode first) from the fourth attempt: a cached index
# can predate the release, and xlings keeps it when an anonymous refresh fails.
for attempt in 1 2 3 4 5 6; do
[ "$attempt" = 4 ] && { xlings config --mirror CN > /dev/null 2>&1 || true; }
xlings update > /dev/null 2>&1 || true
if xlings install "$MCPP_INSTALL" -y -g; then break; fi
[ "$attempt" = 6 ] && { echo "::error::mcpp could not be installed"; exit 1; }
Expand Down Expand Up @@ -413,7 +424,7 @@ jobs:
run: |
set -euo pipefail
mcpp test --target wasm32-emscripten --profile '${{ matrix.profile }}' 2>&1 | tee tests.log
for name in platform_test fs_test dirs_terminal_test linux_test library_test; do
for name in platform_test fs_test dirs_terminal_test linux_test library_test watch_test; do
grep -q "^$name \.\.\. ok" tests.log || { echo "::error::$name did not run or did not pass"; exit 1; }
done
- name: The compile-time constants of Emscripten (R3)
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.7.2 -y -g
xlings install mcpp@2026.11.9.1 -y -g
spec='${{ matrix.toolchain }}'
case "$spec" in
msvc@system) mcpp toolchain default msvc ;;
Expand Down
24 changes: 24 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,29 @@
# Changelog

## 0.4.2

- **Which names changed (`fs::watcher`).** A watcher holds names --- files and
directories, which need not exist yet --- and `changed(wait)` answers which
of them changed since it last answered, each once and as it was added: a
file's content, kind or node (a file replaced by a rename over it, as editors
save), a name made or removed, a directory's entries. The system's notice says
when to look --- inotify on Linux and kqueue on macOS by the kernel's calls,
`ReadDirectoryChangesW` on Windows --- and each name's stamp, kind and
identity say what changed, so every system answers alike: a notice of another
name, or one write a system tells as several notices, tells nothing more. A
write that leaves the size, and the time within the volume's resolution, as
they were is not told. An ok image and Emscripten have no notice, and the
watcher looks when asked (`notified()` false). `native_handle()` is the descriptor an event
loop waits upon on Linux and macOS (mcpplibs/os#21).
- **The tests are mtest's.** Each test file declares an object of
`mcpplibs.mtest` (0.5.2, a `[dev-dependencies]` entry) and no `main`; a test
that starts copies of itself declares what a copy does (`child`), and its
cases are reported, listed and selected as mtest's. The suites of
`checks/hosted` and `checks/emscripten` include the same files. Built with
mcpp 2026.11.9.1, under which the package being built stands for itself in its
tests' graph: mtest, which uses mcpplibs.os, is built against the os under
test.

## 0.4.1

- **A standard stream kept (`stdio::capture`).** `capture::begin(stream::error)`
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ dropped (0.2.8).
| `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` |
| `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`; which names changed (`watcher`: inotify, kqueue, `ReadDirectoryChangesW`; a look when asked in an image and on Emscripten) |
| `mcpplibs.os.process` | `spawn`, `run` (and with bounds: a hard bound, a grace, a drain, output limits), `exec`, a command line joined as it is for the command interpreter (`one_string`), pipes, both outputs on one pipe, output onto this program's error output, units and `end_unit`, declared units, observation and retirement, bound lifetime, bounded waits and reads; `self`, `describe`, `alive`, `threads` |
| `mcpplibs.os.interrupt` | `requested`, `wait`, `end_as_requested`, `end_as_output_closed` |
| `mcpplibs.os.stdio` | the three streams as bytes; a bounded read of input; the output sent to the error stream for a while (`output_to_error`) |
Expand Down
4 changes: 4 additions & 0 deletions checks/emscripten/mcpp.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,7 @@ version = "0.1.0"

[dependencies]
os = { path = "../.." }

# The tests are mtest's (tests/*.cpp of the package, each reached by an include).
[dev-dependencies]
mcpplibs.mtest = "0.5.2"
2 changes: 1 addition & 1 deletion checks/emscripten/tests/dirs_terminal_test.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// dirs_terminal_test.cpp of mcpplibs.os, compiled for Emscripten. 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;
import mcpplibs.mtest;
#include "../../../tests/dirs_terminal_test.cpp"
2 changes: 1 addition & 1 deletion checks/emscripten/tests/facts_test.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +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;
import mcpplibs.mtest;
#include "../../../tests/facts_test.cpp"
2 changes: 1 addition & 1 deletion checks/emscripten/tests/fs_test.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// fs_test.cpp of mcpplibs.os, compiled for Emscripten. 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;
import mcpplibs.mtest;
#include "../../../tests/fs_test.cpp"
2 changes: 1 addition & 1 deletion checks/emscripten/tests/library_test.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// library_test.cpp of mcpplibs.os, compiled for Emscripten. 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;
import mcpplibs.mtest;
#include "../../../tests/library_test.cpp"
2 changes: 1 addition & 1 deletion checks/emscripten/tests/linux_test.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// linux_test.cpp of mcpplibs.os, compiled for Emscripten. 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;
import mcpplibs.mtest;
#include "../../../tests/linux_test.cpp"
2 changes: 1 addition & 1 deletion checks/emscripten/tests/platform_test.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// platform_test.cpp of mcpplibs.os, compiled for Emscripten. 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;
import mcpplibs.mtest;
#include "../../../tests/platform_test.cpp"
6 changes: 6 additions & 0 deletions checks/emscripten/tests/watch_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// watch_test.cpp of mcpplibs.os, compiled for Emscripten. The imports are restated
// because the dependency scan reads this file and not the one it includes.
import std;
import mcpplibs.os;
import mcpplibs.mtest;
#include "../../../tests/watch_test.cpp"
4 changes: 4 additions & 0 deletions checks/hosted/mcpp.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,7 @@ 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).
# CI checks that every test of the package has its counterpart.

# The tests are mtest's (tests/*.cpp of the package, each reached by an include).
[dev-dependencies]
mcpplibs.mtest = "0.5.2"
2 changes: 1 addition & 1 deletion checks/hosted/tests/dirs_terminal_test.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// dirs_terminal_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;
import mcpplibs.mtest;
#include "../../../tests/dirs_terminal_test.cpp"
2 changes: 1 addition & 1 deletion checks/hosted/tests/facts_test.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +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;
import mcpplibs.mtest;
#include "../../../tests/facts_test.cpp"
2 changes: 1 addition & 1 deletion checks/hosted/tests/fs_test.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// fs_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;
import mcpplibs.mtest;
#include "../../../tests/fs_test.cpp"
2 changes: 1 addition & 1 deletion checks/hosted/tests/ipc_test.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// ipc_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;
import mcpplibs.mtest;
#include "../../../tests/ipc_test.cpp"
2 changes: 1 addition & 1 deletion checks/hosted/tests/library_test.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// library_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;
import mcpplibs.mtest;
#include "../../../tests/library_test.cpp"
2 changes: 1 addition & 1 deletion checks/hosted/tests/linux_test.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// linux_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;
import mcpplibs.mtest;
#include "../../../tests/linux_test.cpp"
2 changes: 1 addition & 1 deletion checks/hosted/tests/platform_test.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// platform_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;
import mcpplibs.mtest;
#include "../../../tests/platform_test.cpp"
2 changes: 1 addition & 1 deletion checks/hosted/tests/process_test.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// process_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;
import mcpplibs.mtest;
#include "../../../tests/process_test.cpp"
2 changes: 1 addition & 1 deletion checks/hosted/tests/unit_test.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Restate imports for the dependency scanner, as the other hosted wrappers do.
#include "../../../tests/check.hpp"
import std;
import mcpplibs.os.process;
import mcpplibs.os.ipc;
import mcpplibs.os.env;
import mcpplibs.mtest;
#include "../../../tests/unit_test.cpp"
6 changes: 6 additions & 0 deletions checks/hosted/tests/watch_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// watch_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.
import std;
import mcpplibs.os;
import mcpplibs.mtest;
#include "../../../tests/watch_test.cpp"
13 changes: 10 additions & 3 deletions mcpp.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
namespace = "mcpplibs"
name = "os"
version = "0.4.1"
version = "0.4.2"
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 @@ -105,7 +105,7 @@ sources = ["src/windows/posix/stdio.cpp"]
# ownership are the C library's (src/posix/fs.cpp), and the rest is
# src/emscripten/.
[target.'cfg(os = "emscripten")'.build]
sources = ["src/targets/emscripten.cppm", "src/emscripten/*.cpp", "src/posix/fs.cpp"]
sources = ["src/targets/emscripten.cppm", "src/emscripten/*.cpp", "src/posix/fs.cpp", "src/watch/none.cpp"]

# AN OK IMAGE (`<arch>-openkal', openkal SPEC clause 12): no implementation
# beneath --- the loader binds the image's `kal_*' --- and the platform modules
Expand All @@ -114,8 +114,15 @@ sources = ["src/targets/emscripten.cppm", "src/emscripten/*.cpp", "src/posix/fs.
# 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", "src/linux/linux.cpp"]
sources = ["src/targets/openkal.cppm", "src/openkal/*.cpp", "src/linux/linux.cpp", "src/watch/none.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
# `<arch>-openkal` (mcpp SPEC-013), so this package declares none of them.

# The tests are mtest's: each file of tests/ declares an object and no `main'.
# mtest uses this package, and its tests build it against the os under test
# (mcpp 2026.11.9.1: the package being built stands for itself in its tests'
# graph).
[dev-dependencies]
mcpplibs.mtest = "0.5.2"
Loading
Loading