diff --git a/docs-internal/registry-parity-worklist.md b/docs-internal/registry-parity-worklist.md index f7b844c866..856aafb5d4 100644 --- a/docs-internal/registry-parity-worklist.md +++ b/docs-internal/registry-parity-worklist.md @@ -1,6 +1,6 @@ # Registry Linux-Parity Worklist -Status: worklist · Owner: registry · Last updated: 2026-07-07 +Status: worklist · Owner: registry · Last updated: 2026-07-08 ## Goal (hand this to the driver agent) @@ -85,7 +85,6 @@ actual backing: | Command(s) | Backing | |---|---| | coreutils (`sh`+80) | **uutils** (`uucore`) — established Rust project | -| ripgrep (`rg`) | real ripgrep | | duckdb, vim | real upstream C source, patched for WASI | | sqlite3 **engine** | real SQLite amalgamation (⚠️ but the *CLI* is ours — see below) | | jq | **jaq** (`jaq-core/std/json`) — established Rust jq | @@ -107,7 +106,8 @@ actual backing: | **fd** | DONE | our `secureexec-fd` on raw `regex` (not sharkdp/fd) | real **fd** (sharkdp) | | **findutils** (`find`,`xargs`) | TODO | our hand-rolled on `regex`/shims | real GNU findutils, or `uutils/findutils` | | **tree** | DONE | our hand-rolled, zero deps | real `tree`, or an established one | -| **grep** | TODO | our `secureexec-grep` on raw `regex` (**not** an established grep pkg) | **real GNU grep**, or a popular established grep (ripgrep's `grep` crates) | +| **grep** | DONE | our `secureexec-grep` on raw `regex` (**not** an established grep pkg) | real **GNU grep** | +| **ripgrep** (`rg`) | TODO | our `secureexec-grep` recursive search shim, not real ripgrep | real upstream **ripgrep** | | **zip** | DONE | our 203-line `zip.c` over zlib/minizip (not Info-ZIP) | real Info-ZIP, or an established lib's CLI | | **unzip** | DONE | our 669-line `unzip.c` over zlib/minizip | real Info-ZIP unzip | | **sqlite3 CLI** | DONE | our 558-line `sqlite3_cli.c` (engine is real SQLite; the shell is ours) | real SQLite `shell.c` (its official CLI) | @@ -245,9 +245,30 @@ works (`wasi-spawn` broker), so `xargs` is not a blocker. including `fd --version` reporting `fd 10.4.2`, in `2026-07-08T06-25-00-0700-fd-vitest-upstream-after-dir-format.log`. Rev: `mrskpomv` — `fix(fd): build upstream fd-find`. -- **grep — moderate.** Decision is real GNU grep (gnulib cascade → sysroot stubs) - or ripgrep's `grep-*` crates (threads → serial patch). NB the current - `secureexec-grep` also backs `rg`, so the shipped "ripgrep" is custom too. +- **grep — DONE.** Replaced the `@agentos-software/grep` package's custom + `secureexec-grep` command wrapper with upstream GNU grep 3.12 from the official + GNU release tarball. The real GNU `grep` binary builds through the C toolchain; + `egrep` and `fgrep` are separate tiny WASM launchers that preserve GNU's + upstream obsolescent scripts by spawning `grep -E` / `grep -F` through the + AgentOS process broker. Sysroot fix stayed one layer down: wasi-libc no longer + advertises/exports its nonstandard two-argument `opendirat`, which conflicted + with gnulib's helper. Proof: official GNU release listing captured in + `2026-07-08T06-29-11-0700-grep-gnu-ftp-latest.log`; configure options captured + in `2026-07-08T06-29-39-0700-grep-upstream-configure-help-probe.log`; + sysroot `opendirat` patch check and rebuild pass in + `2026-07-08T06-34-27-0700-grep-wasi-libc-opendirat-patch-check-definition.log` + and `2026-07-08T06-34-33-0700-grep-sysroot-rebuild-opendirat-definition.log`; + GNU grep build passes in + `2026-07-08T06-35-01-0700-grep-gnu-wasm-build-after-opendirat-symbol.log`; + `egrep`/`fgrep` launcher builds pass in + `2026-07-08T06-40-07-0700-grep-egrep-wrapper-build.log` and + `2026-07-08T06-40-42-0700-grep-fgrep-wrapper-build.log`; package build and + check-types pass in `2026-07-08T06-42-24-0700-grep-package-build-final.log` + and `2026-07-08T06-42-18-0700-grep-check-types-final.log`; e2e grep tests + pass 8/8 in `2026-07-08T06-43-59-0700-grep-vitest-after-wrapper-cache-repair.log`. + Rev: `uyukolvr` — `fix(grep): build upstream GNU grep`. + NB: `secureexec-grep` remains only because `@agentos-software/ripgrep` still + uses it for `rg`; replace `rg` with real ripgrep in a separate rev. - **zip / unzip — moderate.** Real **Info-ZIP** source (fetch+pin like zlib/sqlite); zlib is already vendored. Filesystem + `isatty`/`utime`/`chmod`/perms stubs; no sockets/threads/spawn. Friction is Info-ZIP's crufty build, not syscalls. @@ -255,8 +276,8 @@ works (`wasi-spawn` broker), so `xargs` is not a blocker. works. **uutils/findutils** (Rust) avoids gnulib; **GNU findutils** (C) hits the same gnulib cascade as wget. Prefer uutils unless GNU parity is required. -Ranked easiest→hardest: **sqlite3-CLI · http-get (drop) · tree · fd · grep · zip · -unzip · findutils.** +Ranked easiest→hardest: **sqlite3-CLI · http-get (drop) · tree · fd · grep · +ripgrep · zip · unzip · findutils.** ## Status tracking (how the driver reports progress in this doc) diff --git a/packages/runtime-core/commands/egrep b/packages/runtime-core/commands/egrep index c78e121f14..6ab0536c2a 100644 Binary files a/packages/runtime-core/commands/egrep and b/packages/runtime-core/commands/egrep differ diff --git a/packages/runtime-core/commands/fgrep b/packages/runtime-core/commands/fgrep index c78e121f14..01aa3dea12 100644 Binary files a/packages/runtime-core/commands/fgrep and b/packages/runtime-core/commands/fgrep differ diff --git a/packages/runtime-core/commands/grep b/packages/runtime-core/commands/grep index c78e121f14..53f5049c16 100644 Binary files a/packages/runtime-core/commands/grep and b/packages/runtime-core/commands/grep differ diff --git a/software/grep/agentos-package.json b/software/grep/agentos-package.json index e75bc0d243..2b14ba7c37 100644 --- a/software/grep/agentos-package.json +++ b/software/grep/agentos-package.json @@ -1,11 +1,9 @@ { "commands": [ - "grep" + "grep", + "egrep", + "fgrep" ], - "aliases": { - "egrep": "grep", - "fgrep": "grep" - }, "registry": { "title": "grep", "description": "GNU grep pattern matching (grep, egrep, fgrep).", diff --git a/software/grep/bin/egrep b/software/grep/bin/egrep index c78e121f14..6ab0536c2a 100644 Binary files a/software/grep/bin/egrep and b/software/grep/bin/egrep differ diff --git a/software/grep/bin/fgrep b/software/grep/bin/fgrep index c78e121f14..01aa3dea12 100644 Binary files a/software/grep/bin/fgrep and b/software/grep/bin/fgrep differ diff --git a/software/grep/bin/grep b/software/grep/bin/grep index c78e121f14..53f5049c16 100644 Binary files a/software/grep/bin/grep and b/software/grep/bin/grep differ diff --git a/software/grep/native/crates/cmd-egrep/Cargo.toml b/software/grep/native/crates/cmd-egrep/Cargo.toml new file mode 100644 index 0000000000..b3eb4c9222 --- /dev/null +++ b/software/grep/native/crates/cmd-egrep/Cargo.toml @@ -0,0 +1,14 @@ +[package] +workspace = "../../../../../toolchain" +name = "cmd-egrep" +version.workspace = true +edition.workspace = true +license.workspace = true +description = "egrep launcher for GNU grep" + +[[bin]] +name = "egrep" +path = "src/main.rs" + +[dependencies] +shims = { package = "secureexec-shims", path = "../../../../../toolchain/crates/libs/shims" } diff --git a/software/grep/native/crates/cmd-egrep/src/main.rs b/software/grep/native/crates/cmd-egrep/src/main.rs new file mode 100644 index 0000000000..b6671b6c38 --- /dev/null +++ b/software/grep/native/crates/cmd-egrep/src/main.rs @@ -0,0 +1,4 @@ +fn main() { + let args: Vec = std::env::args_os().collect(); + std::process::exit(shims::grep_alias::egrep(args)); +} diff --git a/software/grep/native/crates/cmd-fgrep/Cargo.toml b/software/grep/native/crates/cmd-fgrep/Cargo.toml new file mode 100644 index 0000000000..573b6b100e --- /dev/null +++ b/software/grep/native/crates/cmd-fgrep/Cargo.toml @@ -0,0 +1,14 @@ +[package] +workspace = "../../../../../toolchain" +name = "cmd-fgrep" +version.workspace = true +edition.workspace = true +license.workspace = true +description = "fgrep launcher for GNU grep" + +[[bin]] +name = "fgrep" +path = "src/main.rs" + +[dependencies] +shims = { package = "secureexec-shims", path = "../../../../../toolchain/crates/libs/shims" } diff --git a/software/grep/native/crates/cmd-fgrep/src/main.rs b/software/grep/native/crates/cmd-fgrep/src/main.rs new file mode 100644 index 0000000000..3f81c7a4d5 --- /dev/null +++ b/software/grep/native/crates/cmd-fgrep/src/main.rs @@ -0,0 +1,4 @@ +fn main() { + let args: Vec = std::env::args_os().collect(); + std::process::exit(shims::grep_alias::fgrep(args)); +} diff --git a/software/grep/native/crates/cmd-grep/Cargo.toml b/software/grep/native/crates/cmd-grep/Cargo.toml deleted file mode 100644 index 3214711f24..0000000000 --- a/software/grep/native/crates/cmd-grep/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -workspace = "../../../../../toolchain" -name = "cmd-grep" -version.workspace = true -edition.workspace = true -license.workspace = true -description = "grep standalone binary for secure-exec VM" - -[[bin]] -name = "grep" -path = "src/main.rs" - -[dependencies] -secureexec-grep = { path = "../../../../../toolchain/crates/libs/grep" } diff --git a/software/grep/native/crates/cmd-grep/src/main.rs b/software/grep/native/crates/cmd-grep/src/main.rs deleted file mode 100644 index ef8a789bdf..0000000000 --- a/software/grep/native/crates/cmd-grep/src/main.rs +++ /dev/null @@ -1,13 +0,0 @@ -fn main() { - use std::io::Write; - - let args: Vec = std::env::args_os().collect(); - let mut code = secureexec_grep::main(args); - if let Err(error) = std::io::stdout().flush() { - eprintln!("Error flushing stdout: {error}"); - if code == 0 { - code = 2; - } - } - std::process::exit(code); -} diff --git a/software/grep/test/grep.test.ts b/software/grep/test/grep.test.ts new file mode 100644 index 0000000000..245bc42216 --- /dev/null +++ b/software/grep/test/grep.test.ts @@ -0,0 +1,113 @@ +import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { dirname, join } from "node:path"; +import { createWasmVmRuntime } from "@agentos/test-harness"; +import { + COMMANDS_DIR, + NodeFileSystem, + createKernel, + describeIf, + hasWasmBinaries, +} from "@agentos/test-harness"; +import type { Kernel } from "@agentos/test-harness"; +import { afterEach, describe, expect, it } from "vitest"; + +let tempRoot: string | undefined; + +async function createTestVFS(): Promise { + tempRoot = await mkdtemp(join(tmpdir(), "agentos-grep-")); + await writeFixture( + "/project/notes.txt", + ["Alpha", "beta", "alphabet", "delta"].join("\n") + "\n", + ); + await writeFixture( + "/project/other.txt", + ["gamma", "Beta blocker", "literal a+b"].join("\n") + "\n", + ); + return new NodeFileSystem({ root: tempRoot }); +} + +async function writeFixture(path: string, contents: string): Promise { + if (!tempRoot) throw new Error("fixture root not initialized"); + const hostPath = join(tempRoot, path.replace(/^\/+/, "")); + await mkdir(dirname(hostPath), { recursive: true }); + await writeFile(hostPath, contents); +} + +describeIf(hasWasmBinaries, "GNU grep command", { timeout: 10_000 }, () => { + let kernel: Kernel; + + afterEach(async () => { + await kernel?.dispose(); + if (tempRoot) { + await rm(tempRoot, { recursive: true, force: true }); + tempRoot = undefined; + } + }); + + async function mountFixture(): Promise { + const vfs = await createTestVFS(); + kernel = createKernel({ filesystem: vfs }); + await kernel.mount(createWasmVmRuntime({ commandDirs: [COMMANDS_DIR] })); + return vfs; + } + + it("reports the upstream GNU grep version", async () => { + await mountFixture(); + + const result = await kernel.exec("grep --version", {}); + expect(result.stdout).toContain("GNU grep 3.12"); + }); + + it("prints matching lines from a file", async () => { + await mountFixture(); + + const result = await kernel.exec("grep alpha /project/notes.txt", {}); + expect(result.stdout).toBe("alphabet\n"); + }); + + it("supports case-insensitive search", async () => { + await mountFixture(); + + const result = await kernel.exec("grep -i beta /project/notes.txt", {}); + expect(result.stdout).toBe("beta\n"); + }); + + it("supports inverted matches", async () => { + await mountFixture(); + + const result = await kernel.exec("grep -v Alpha /project/notes.txt", {}); + expect(result.stdout).toBe("beta\nalphabet\ndelta\n"); + }); + + it("counts matches", async () => { + await mountFixture(); + + const result = await kernel.exec("grep -c a /project/notes.txt", {}); + expect(result.stdout).toBe("4\n"); + }); + + it("prints file names with matches", async () => { + await mountFixture(); + + const result = await kernel.exec( + "grep -l gamma /project/notes.txt /project/other.txt", + {}, + ); + expect(result.stdout).toBe("/project/other.txt\n"); + }); + + it("supports egrep extended regex alias", async () => { + await mountFixture(); + + const result = await kernel.exec("egrep 'Alpha|delta' /project/notes.txt", {}); + expect(result.stdout).toBe("Alpha\ndelta\n"); + }); + + it("supports fgrep fixed-string alias", async () => { + await mountFixture(); + + const result = await kernel.exec("fgrep 'a+b' /project/other.txt", {}); + expect(result.stdout).toBe("literal a+b\n"); + }); +}); diff --git a/toolchain/Cargo.lock b/toolchain/Cargo.lock index 7fe76204f8..a6ec2c0a2b 100644 --- a/toolchain/Cargo.lock +++ b/toolchain/Cargo.lock @@ -840,6 +840,13 @@ dependencies = [ "uu_echo", ] +[[package]] +name = "cmd-egrep" +version = "0.1.0" +dependencies = [ + "secureexec-shims", +] + [[package]] name = "cmd-env" version = "0.1.0" @@ -882,6 +889,13 @@ dependencies = [ "fd-find", ] +[[package]] +name = "cmd-fgrep" +version = "0.1.0" +dependencies = [ + "secureexec-shims", +] + [[package]] name = "cmd-file" version = "0.1.0" @@ -917,13 +931,6 @@ dependencies = [ "secureexec-git", ] -[[package]] -name = "cmd-grep" -version = "0.1.0" -dependencies = [ - "secureexec-grep", -] - [[package]] name = "cmd-gzip" version = "0.1.0" diff --git a/toolchain/Makefile b/toolchain/Makefile index a287e55630..309fc0b273 100644 --- a/toolchain/Makefile +++ b/toolchain/Makefile @@ -24,7 +24,6 @@ COMMAND_PACKAGES := $(addprefix -p cmd-,$(COMMAND_NAMES)) # Alias symlinks: link_name:target_binary ALIAS_SYMLINKS := \ - egrep:grep fgrep:grep \ gunzip:gzip zcat:gzip \ bash:sh \ dir:ls vdir:ls \ @@ -213,7 +212,7 @@ wasm: vendor patch-vendor patch-std wasm-opt-check # codex the codex fork build (codex, codex-exec) # `just toolchain-cmd ` calls this. `make wasm` builds the fast # Rust set; `make -C c programs install` builds/installs the fast C set. -C_COMMANDS := zip unzip envsubst sqlite3 curl wget duckdb vim +C_COMMANDS := zip unzip envsubst sqlite3 curl wget grep duckdb vim .PHONY: cmd/% cmd/%: diff --git a/toolchain/c/Makefile b/toolchain/c/Makefile index d11cc666b8..991118950b 100644 --- a/toolchain/c/Makefile +++ b/toolchain/c/Makefile @@ -68,10 +68,10 @@ COMMANDS_DIR ?= ../target/wasm32-wasip1/release/commands # Fast real commands installed by the default software gate. Slow/heavy commands # (duckdb, vim) remain available through explicit parent `make cmd/`. -COMMANDS := zip unzip envsubst sqlite3 curl wget tree +COMMANDS := zip unzip envsubst sqlite3 curl wget grep tree # Programs requiring patched sysroot (Tier 2+ custom host imports) -PATCHED_PROGRAMS := http_get_test isatty_test getpid_test getppid_test getppid_verify userinfo pipe_test dup_test spawn_child spawn_exit_code pipeline kill_child waitpid_return waitpid_edge syscall_coverage getpwuid_test signal_tests sigaction_self sigaction_behavior delayed_tcp_echo delayed_kill pipe_edge tcp_accept_spawn tcp_echo tcp_server http_server udp_echo unix_socket signal_handler dns_lookup sqlite3 curl wget tree fs_probe +PATCHED_PROGRAMS := http_get_test isatty_test getpid_test getppid_test getppid_verify userinfo pipe_test dup_test spawn_child spawn_exit_code pipeline kill_child waitpid_return waitpid_edge syscall_coverage getpwuid_test signal_tests sigaction_self sigaction_behavior delayed_tcp_echo delayed_kill pipe_edge tcp_accept_spawn tcp_echo tcp_server http_server udp_echo unix_socket signal_handler dns_lookup sqlite3 curl wget grep tree fs_probe # Discover all package command and test-program C source files. ALL_SOURCES := $(foreach dir,$(C_SOURCE_DIRS),$(wildcard $(dir)/*.c)) @@ -89,7 +89,7 @@ endif ifeq ($(wildcard $(PATCHED_SYSROOT)/lib/wasm32-wasi/libc.a),) CUSTOM_WASM_PROG_NAMES := else - CUSTOM_WASM_PROG_NAMES := curl wget sqlite3 tree + CUSTOM_WASM_PROG_NAMES := curl wget grep sqlite3 tree endif WASM_PROG_NAMES := $(sort $(basename $(notdir $(SOURCES))) $(CUSTOM_WASM_PROG_NAMES)) @@ -156,6 +156,9 @@ WGET_URL := https://ftp.gnu.org/gnu/wget/wget-$(WGET_VERSION).tar.gz WGET_UPSTREAM_BUILD_DIR := $(BUILD_DIR)/wget-upstream WGET_UPSTREAM_OVERLAY_INCLUDE_DIR := overlays/wget/include WGET_UPSTREAM_OVERLAY_FILES := $(wildcard $(WGET_UPSTREAM_OVERLAY_INCLUDE_DIR)/*.h) +GREP_VERSION := 3.12 +GREP_URL := https://ftp.gnu.org/gnu/grep/grep-$(GREP_VERSION).tar.xz +GREP_UPSTREAM_BUILD_DIR := $(BUILD_DIR)/grep-upstream TREE_VERSION := 2.3.2 TREE_URL := https://gitlab.com/OldManProgrammer/unix-tree/-/archive/$(TREE_VERSION)/unix-tree-$(TREE_VERSION).tar.gz MINIZIP_URL := https://github.com/madler/zlib/archive/refs/tags/v1.3.1.zip @@ -621,6 +624,18 @@ $(BUILD_DIR)/wget: scripts/build-wget-upstream.sh $(WGET_UPSTREAM_OVERLAY_FILES) --ranlib "$(abspath $(WASI_SDK_DIR)/bin/llvm-ranlib)" \ --output "$(abspath $@)" +$(BUILD_DIR)/grep: scripts/build-grep-upstream.sh $(PATCHED_SYSROOT)/lib/wasm32-wasi/libc.a $(WASI_SDK_DIR)/bin/clang + @mkdir -p $(BUILD_DIR) + bash scripts/build-grep-upstream.sh \ + --version "$(GREP_VERSION)" \ + --url "$(GREP_URL)" \ + --cache-dir "$(abspath $(LIBS_CACHE))" \ + --build-dir "$(abspath $(GREP_UPSTREAM_BUILD_DIR))" \ + --cc "$(abspath $(CC)) --target=wasm32-wasip1 --sysroot=$(abspath $(SYSROOT))" \ + --ar "$(abspath $(WASI_SDK_DIR)/bin/llvm-ar)" \ + --ranlib "$(abspath $(WASI_SDK_DIR)/bin/llvm-ranlib)" \ + --output "$(abspath $@)" + # duckdb: upstream DuckDB CLI built from source with our patched WASI/POSIX sysroot $(BUILD_DIR)/duckdb: libs/duckdb/CMakeLists.txt $(PATCHED_SYSROOT)/lib/wasm32-wasi/libc.a $(WASI_SDK_DIR)/bin/clang $(WASI_SDK_DIR)/bin/clang++ cmake/FindThreads.cmake scripts/build-duckdb.sh include/fcntl.h include/ifaddrs.h include/net/if.h include/sys/ioctl.h @mkdir -p $(BUILD_DIR) diff --git a/toolchain/c/scripts/build-grep-upstream.sh b/toolchain/c/scripts/build-grep-upstream.sh new file mode 100755 index 0000000000..ea65005863 --- /dev/null +++ b/toolchain/c/scripts/build-grep-upstream.sh @@ -0,0 +1,158 @@ +#!/usr/bin/env bash +set -euo pipefail + +usage() { + cat <<'EOF' +Usage: build-grep-upstream.sh \ + --version \ + --url \ + --cache-dir \ + --build-dir \ + --cc \ + --ar \ + --ranlib \ + --output +EOF +} + +VERSION="" +URL="" +CACHE_DIR="" +BUILD_DIR="" +CC_CMD="" +AR_CMD="" +RANLIB_CMD="" +OUTPUT="" + +while [[ $# -gt 0 ]]; do + case "$1" in + --version) + VERSION="$2" + shift 2 + ;; + --url) + URL="$2" + shift 2 + ;; + --cache-dir) + CACHE_DIR="$2" + shift 2 + ;; + --build-dir) + BUILD_DIR="$2" + shift 2 + ;; + --cc) + CC_CMD="$2" + shift 2 + ;; + --ar) + AR_CMD="$2" + shift 2 + ;; + --ranlib) + RANLIB_CMD="$2" + shift 2 + ;; + --output) + OUTPUT="$2" + shift 2 + ;; + *) + echo "Unknown argument: $1" >&2 + usage >&2 + exit 1 + ;; + esac +done + +if [[ -z "$VERSION" || -z "$URL" || -z "$CACHE_DIR" || -z "$BUILD_DIR" || -z "$CC_CMD" || -z "$AR_CMD" || -z "$RANLIB_CMD" || -z "$OUTPUT" ]]; then + usage >&2 + exit 1 +fi + +fetch() { + local url="$1" + local out="$2" + if command -v curl >/dev/null 2>&1; then + curl --retry 3 --retry-all-errors -fSL "$url" -o "$out" + elif command -v wget >/dev/null 2>&1; then + wget -q "$url" -O "$out" + else + echo "Neither curl nor wget is available to fetch $url" >&2 + exit 1 + fi +} + +mkdir -p "$CACHE_DIR" +rm -rf "$BUILD_DIR" +mkdir -p "$BUILD_DIR" + +TARBALL="$CACHE_DIR/grep-${VERSION}.tar.xz" +if [[ ! -f "$TARBALL" ]]; then + echo "Fetching upstream GNU grep ${VERSION} release tarball..." + fetch "$URL" "$TARBALL" +fi + +echo "Extracting upstream GNU grep ${VERSION}..." +tar -xJf "$TARBALL" -C "$BUILD_DIR" + +SRC_DIR="$BUILD_DIR/grep-${VERSION}" +if [[ ! -d "$SRC_DIR" ]]; then + echo "Expected extracted source at $SRC_DIR" >&2 + exit 1 +fi + +pushd "$SRC_DIR" >/dev/null + +echo "Configuring upstream GNU grep for wasm32-wasip1..." +CC="$CC_CMD" \ +AR="$AR_CMD" \ +RANLIB="$RANLIB_CMD" \ +PKG_CONFIG=false \ +PCRE_CFLAGS="" \ +PCRE_LIBS="" \ +gl_cv_func_select_supports0=yes \ +gl_cv_func_select_detects_ebadf=yes \ +gl_cv_func_pselect_detects_ebadf=yes \ +ac_cv_func_clock_getres=no \ +ac_cv_func_clock_gettime=no \ +CFLAGS="-O2 -flto -D_WASI_EMULATED_SIGNAL -D_WASI_EMULATED_MMAN -D_WASI_EMULATED_PROCESS_CLOCKS -DFD_SETSIZE=8192" \ +LIBS="-lwasi-emulated-signal -lwasi-emulated-mman -lwasi-emulated-process-clocks" \ +./configure \ + --host=wasm32-unknown-wasi \ + --disable-shared \ + --disable-nls \ + --disable-perl-regexp \ + --disable-threads + +echo "Building upstream GNU grep support library..." +make -C lib + +echo "Building upstream GNU grep..." +make -C src grep + +BIN="" +for candidate in "src/grep" "src/.libs/grep" "src/grep.wasm"; do + if [[ -f "$candidate" ]]; then + BIN="$candidate" + break + fi +done + +if [[ -z "$BIN" ]]; then + echo "Unable to locate built grep binary in src/" >&2 + exit 1 +fi + +mkdir -p "$(dirname "$OUTPUT")" +if command -v wasm-opt >/dev/null 2>&1; then + echo "Optimizing GNU grep WASM binary..." + wasm-opt -O3 --strip-debug --all-features "$BIN" -o "$OUTPUT" +else + cp "$BIN" "$OUTPUT" +fi + +popd >/dev/null + +echo "Built upstream GNU grep at $OUTPUT" diff --git a/toolchain/crates/libs/shims/src/grep_alias.rs b/toolchain/crates/libs/shims/src/grep_alias.rs new file mode 100644 index 0000000000..bf2c83cc4b --- /dev/null +++ b/toolchain/crates/libs/shims/src/grep_alias.rs @@ -0,0 +1,36 @@ +//! GNU grep obsolescent alias launchers. +//! +//! Upstream GNU grep installs `egrep` and `fgrep` as scripts that warn and exec +//! `grep -E` / `grep -F`. Registry commands are WASM modules, so these compiled +//! launchers preserve the upstream script behavior through the process broker. + +use std::ffi::OsString; +use std::process::Stdio; + +pub fn egrep(args: Vec) -> i32 { + run_alias(args, "egrep", "-E") +} + +pub fn fgrep(args: Vec) -> i32 { + run_alias(args, "fgrep", "-F") +} + +fn run_alias(args: Vec, name: &str, option: &str) -> i32 { + eprintln!("{name}: warning: {name} is obsolescent; using grep {option}"); + + let mut command = std::process::Command::new("grep"); + command + .arg(option) + .args(args.into_iter().skip(1)) + .stdin(Stdio::inherit()) + .stdout(Stdio::inherit()) + .stderr(Stdio::inherit()); + + match command.status() { + Ok(status) => status.code().unwrap_or(1), + Err(error) => { + eprintln!("{name}: failed to run grep: {error}"); + 127 + } + } +} diff --git a/toolchain/crates/libs/shims/src/lib.rs b/toolchain/crates/libs/shims/src/lib.rs index 9e2358b3e8..7bbc25a050 100644 --- a/toolchain/crates/libs/shims/src/lib.rs +++ b/toolchain/crates/libs/shims/src/lib.rs @@ -5,6 +5,7 @@ //! rather than using uutils versions. pub mod env; +pub mod grep_alias; pub mod nice; pub mod nohup; pub mod stdbuf; diff --git a/toolchain/std-patches/wasi-libc/0026-hide-nonstandard-opendirat.patch b/toolchain/std-patches/wasi-libc/0026-hide-nonstandard-opendirat.patch new file mode 100644 index 0000000000..06c29abde4 --- /dev/null +++ b/toolchain/std-patches/wasi-libc/0026-hide-nonstandard-opendirat.patch @@ -0,0 +1,27 @@ +--- a/libc-bottom-half/headers/public/__header_dirent.h ++++ b/libc-bottom-half/headers/public/__header_dirent.h +@@ -39,7 +39,6 @@ struct dirent *readdir(DIR *); + void rewinddir(DIR *); + void seekdir(DIR *, long); + long telldir(DIR *); +-DIR *opendirat(int, const char *); + void rewinddir(DIR *); + int scandirat(int, const char *, struct dirent ***, + int (*)(const struct dirent *), +--- a/libc-bottom-half/sources/at_fdcwd.c ++++ b/libc-bottom-half/sources/at_fdcwd.c +@@ -52,14 +52,6 @@ int mkdirat(int dirfd, const char *pathname, mode_t mode) { + return __wasilibc_nocwd_mkdirat_nomode(dirfd, pathname); + } + +-DIR *opendirat(int dirfd, const char *path) { +- if (dirfd == AT_FDCWD || path[0] == '/') { +- return opendir(path); +- } +- +- return __wasilibc_nocwd_opendirat(dirfd, path); +-} +- + int scandirat(int dirfd, const char *dirp, struct dirent ***namelist, + int (*filter)(const struct dirent *), + int (*compar)(const struct dirent **, const struct dirent **)) {