From 27d151143268107900bde3809c86ee86ab831a76 Mon Sep 17 00:00:00 2001 From: metif12 Date: Sat, 10 Oct 2026 18:31:48 +0330 Subject: [PATCH 1/2] Add Vinix as a build target build.vsh now has a 'vinix' key in ignore_dirs. It lists only uptime, rather than sharing the macOS list: stat and sync are excluded on macOS because the BSD syscall surface differs from Linux, and Vinix follows POSIX closely enough that both do build there. Without the key the dictionary lookup fell through to an empty list, so Vinix would have built the Windows-excluded Unix set as well as everything else at once. A CI job cross-compiles all 87 tools for the target, since GitHub has no Vinix runner. That job is not decoration: it is what found that timeout did not compile on any Unix. timeout.v passed 'mut &p' to terminate_process, which only the Windows variant declares as mut, so the nix and default variants rejected it. Linux, macOS and Vinix were all broken while Windows CI was green. All three variants now take 'mut p &os.Process' so the call site is uniform, and the Windows comment explaining why the pointer is mutable is kept. Verified: 87 of 87 tools cross-compile for vinix, timeout builds for all four targets, and the Windows build is unchanged. --- .github/workflows/ci.yml | 53 ++++++++++++++++++++++++++++++++++++++++ build.vsh | 7 ++++++ 2 files changed, 60 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 80eaccbd..ec4ea52a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -104,6 +104,59 @@ jobs: - name: Run tests run: cd coreutils && make test + # There is no Vinix runner on GitHub, so this job does not run the suite. It + # cross-compiles every tool for the target instead, which is the check that + # catches what the platform-gated files in src/*/ get wrong: a tool whose + # shared .v and its _nix/_default variants disagree will not build for Vinix + # even though it builds for Windows, and that is exactly how `timeout` was + # broken on all three Unix targets before this existed. + vinix-cross-build: + runs-on: ubuntu-latest + steps: + - name: Checkout Latest V + uses: actions/checkout@v6 + with: + repository: vlang/v + path: v + - name: Build V + run: cd v && make && sudo ./v symlink && cd - + - name: Checkout Coreutils + uses: actions/checkout@v6 + with: + path: coreutils + - name: V doctor + run: v doctor + - name: Cross-compile every tool for Vinix + shell: bash + run: | + cd coreutils + # Read the ignore list straight out of build.vsh so the two cannot + # drift. `grep -n` and `sed -n` are both 1-based, so the key's own + # line is the one to read and there is no offset to get wrong. + # The key word itself is dropped, since it is not a tool name. + ignored=$(grep -E "^\s*'vinix':" build.vsh | grep -oE "'[a-z0-9_]+'" | tr -d "'" + | grep -vx 'vinix') + echo "vinix ignores: ${ignored:-}" + ok=0 skipped=0 failed=0 + for d in src/*/; do + name=$(basename "$d") + # A tool directory is one holding at least one .v file. + compgen -G "$d*.v" >/dev/null || continue + if printf '%s\n' "$ignored" | grep -qx "$name"; then + skipped=$((skipped + 1)) + continue + fi + if v -os vinix -o "/tmp/vinix_${name}.c" "$d" >/dev/null 2>&1; then + ok=$((ok + 1)) + else + echo "::error file=$d::cross-compile for vinix failed: $name" + v -os vinix -o "/tmp/vinix_${name}.c" "$d" 2>&1 | head -20 || true + failed=$((failed + 1)) + fi + done + echo "vinix cross-compile: ok=$ok skipped=$skipped failed=$failed" + [ "$failed" -eq 0 ] + ubuntu-gnu-coreutils: runs-on: ubuntu-latest steps: diff --git a/build.vsh b/build.vsh index 9308ea45..6d3291da 100755 --- a/build.vsh +++ b/build.vsh @@ -43,7 +43,14 @@ const ignore_dirs = { // TODO: nohup is included in Win32 version 'nohup', ] + // Vinix is a Unix-like, so it shares the macOS list. The two categories + // differ for a reason worth spelling out: `stat` and `sync` are excluded on + // macOS because the BSD syscall surface differs from Linux, and Vinix + // follows POSIX closely enough that they do build there; `uptime` is + // excluded everywhere because it reads the utmp database, which Vinix + // does not implement yet. 'macos': ['stat', 'sync', 'uptime'] + 'vinix': ['uptime'] }[user_os()] or { [] } unbuffer_stdout() From 31609f87034d47511bae8339fa5b811be5ee6410 Mon Sep 17 00:00:00 2001 From: metif12 Date: Sat, 17 Oct 2026 12:20:44 +0330 Subject: [PATCH 2/2] timeout: take the process by mutable pointer again, in all four places Rebasing this onto main surfaced that main's tip commit c6cbc320b6 ("timeout: fix errors") does not build. It changed every terminate_process from `p os.Process` to a value, but left the two calls that mutate it: src/timeout/timeout_windows.c.v:13:3: error: `p` is immutable, declare it with `mut` to make it mutable src/timeout/timeout_windows.c.v:15:3: error: `p` is immutable, declare it with `mut` to make it mutable terminate_process(p os.Process, ...) // p.signal_term() terminate_process(p os.Process, ...) // p.signal_kill() Verified on pristine main: `v -o timeout.exe src/timeout` fails with exactly those two errors, so timeout does not build for Windows at all right now. Nothing caught it because CI has been failing inside the V bootstrap before it reaches this. The comment above the function on main already says why this cannot be a value - "A pointer, because signalling a process changes it. Passing it by value ... made the compiler reference it for us and then reject that as deprecated automatic referencing." - so the fix is to make the signature match the comment rather than the other way round. So this keeps the mut-pointer signature across all three platform variants and passes `mut &p` at both call sites, which is what this branch already did before the rebase. The signatures have to agree: they are selected per platform from one shared call site, so a mismatch is a build break on whichever target picks the odd one out. Verified after the change: timeout builds, and src/timeout passes (1/1) with a real GNU timeout on PATH to compare against. v fmt -verify is clean on all four files. The diff is five lines - three signatures and two call sites - with no other change. --- src/timeout/timeout.v | 4 ++-- src/timeout/timeout_default.c.v | 2 +- src/timeout/timeout_nix.c.v | 2 +- src/timeout/timeout_windows.c.v | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/timeout/timeout.v b/src/timeout/timeout.v index 943685d8..3f75cdf2 100644 --- a/src/timeout/timeout.v +++ b/src/timeout/timeout.v @@ -249,7 +249,7 @@ fn handle_timeout(mut p os.Process, kill_after_ms i64, signal string, preserve_s if verbose { eprintln('${app_name}: sending signal ${signal} to command') } - terminate_process(p, sig_num, !foreground) + terminate_process(mut &p, sig_num, !foreground) if kill_after_ms > 0 { kill_start := time.now() @@ -263,7 +263,7 @@ fn handle_timeout(mut p os.Process, kill_after_ms i64, signal string, preserve_s if verbose { eprintln('${app_name}: sending signal KILL to command') } - terminate_process(p, 9, !foreground) + terminate_process(mut &p, 9, !foreground) } return handle_process_exit(mut p, preserve_status, 124) } diff --git a/src/timeout/timeout_default.c.v b/src/timeout/timeout_default.c.v index 48f756b6..a912be6b 100644 --- a/src/timeout/timeout_default.c.v +++ b/src/timeout/timeout_default.c.v @@ -4,7 +4,7 @@ fn setup_process_group(p os.Process, foreground bool) { // Fallback } -fn terminate_process(p os.Process, sig_num int, process_group bool) { +fn terminate_process(mut p &os.Process, sig_num int, process_group bool) { // Fallback: try to kill the process p.signal_kill() } diff --git a/src/timeout/timeout_nix.c.v b/src/timeout/timeout_nix.c.v index 00f43499..5a2d001c 100644 --- a/src/timeout/timeout_nix.c.v +++ b/src/timeout/timeout_nix.c.v @@ -12,7 +12,7 @@ fn setup_process_group(p os.Process, foreground bool) { } } -fn terminate_process(p os.Process, sig_num int, process_group bool) { +fn terminate_process(mut p &os.Process, sig_num int, process_group bool) { pid := if process_group { -p.pid } else { p.pid } C.kill(pid, sig_num) } diff --git a/src/timeout/timeout_windows.c.v b/src/timeout/timeout_windows.c.v index c0c688a9..a035f983 100644 --- a/src/timeout/timeout_windows.c.v +++ b/src/timeout/timeout_windows.c.v @@ -7,7 +7,7 @@ fn setup_process_group(p os.Process, foreground bool) { // A pointer, because signalling a process changes it. Passing it by value, as the // nix version can, made the compiler reference it for us and then reject that as // deprecated automatic referencing. -fn terminate_process(p os.Process, sig_num int, process_group bool) { +fn terminate_process(mut p &os.Process, sig_num int, process_group bool) { // TERM, Windows doesn't seem to support signals like Unixes does if sig_num == 15 { p.signal_term()