Build separate native and WASI pipelines with shared multicall names

Checkpoint before integration and package validation.

Assisted-by: SurmAgent:unknown [read] [edit] [bash]
This commit is contained in:
2026-11-12 09:36:27 +00:00
parent 0b79bb4227
commit d44f1d9677
325 changed files with 134189 additions and 506 deletions
+7 -1
View File
@@ -56,7 +56,13 @@ nix develop -c nu userland/bundle.nu
nix develop -c cargo build -p git --target wasm32-wasip2 --release
```
`nix flake check` checks the packaged host, its bundle, and sandbox-compatible tests.
`nix build` uses separate Crane pipelines for the native host and WASI userland.
Go tools and QuickJS build separately. Nix assembles the bundle without `bundle.nu`.
Both Nix and the incremental development bundle read the command names from `userland/tools.json`.
Coreutils, diffutils, procps, and brush use relative symlinks to one binary each.
`nix flake check` checks the packaged host, its bundle, and the whole host/VFS test suite.
Sandbox-incompatible tests print an explicit skip reason instead of a flake-level filter.
## Tests
+15 -4
View File
@@ -9,7 +9,14 @@ Keep the complete world folder between starts.
## Build and start
Run host commands from a checkout of the `v3` branch on Citadel.
Use Nix with flakes. The package supplies the native Rust builder and its tools.
Use Nix with flakes and the `pipe-operators` experimental feature.
The package supplies the native Rust builder and its tools.
`nix build` builds the host and userland through separate Crane pipelines.
The userland pipeline targets `wasm32-wasip2`.
Separate Go derivations build awk and esbuild for WASI preview1.
QuickJS builds from its vendored C sources with WASI clang.
Nix assembles the final bundle without the development bundle script.
The package entry point is:
@@ -30,11 +37,15 @@ The source-tree bundle builder is:
nix develop -c nu userland/bundle.nu
```
It builds the world commands and creates `target/userland/bundle` by default.
It snapshots committed source, not uncommitted edits.
It builds the world commands incrementally with the configured Cargo cache.
Pass a destination to place the bundle under your shared executor target directory.
The script snapshots the current source files, including uncommitted edits.
The source archive includes the complete `config/`, `skills/`, and `extensions/` directories.
The bundle contains plain source files. `sausage init` creates their repository and its first commit on `main`.
The world copier preserves symlinks, including dangling links in the source tree.
Both bundle builders read `userland/tools.json` for the package and command names.
Coreutils, diffutils, procps, and brush each occupy one real binary in `bin/`.
Their command names are relative symlinks to those binaries.
The world copier preserves these symlinks and dangling links in the source tree.
A fresh development world needs no manual source copy.
The bundle includes `/bin/web.wasm` and an enabled `/etc/systemd/system/web.service`.
World init starts that service on `0.0.0.0:8080`.
+96 -64
View File
@@ -15,18 +15,22 @@
flake-utils.lib.eachSystem flake-utils.lib.allSystems (system:
let
pkgs = import nixpkgs { inherit system; overlays = [ rust-overlay.overlays.default ]; };
inherit (pkgs) lib;
rust = pkgs.rust-bin.stable.latest.default.override {
targets = [ "wasm32-wasip2" ];
extensions = [ "rust-src" ];
};
craneLib = (crane.mkLib pkgs).overrideToolchain rust;
src = pkgs.nix-gitignore.gitignoreSource [] ./.;
mold = pkgs.lib.optionals pkgs.stdenv.hostPlatform.isLinux [ pkgs.mold ];
hostTarget = pkgs.lib.toUpper (builtins.replaceStrings [ "-" ] [ "_" ] pkgs.stdenv.hostPlatform.rust.rustcTarget);
src = ./. |> pkgs.nix-gitignore.gitignoreSource [];
tools = ./userland/tools.json |> builtins.readFile |> builtins.fromJSON;
commandArgs = tools |> builtins.filter (t: t.package != null)
|> map (t: t.package) |> lib.unique |> map (p: "-p ${p}") |> lib.concatStringsSep " ";
mold = lib.optionals pkgs.stdenv.hostPlatform.isLinux [ pkgs.mold ];
hostTarget = pkgs.stdenv.hostPlatform.rust.rustcTarget |> builtins.replaceStrings [ "-" ] [ "_" ] |> lib.toUpper;
moldVar = "CARGO_TARGET_${hostTarget}_RUSTFLAGS";
moldEnv = pkgs.lib.optionalAttrs pkgs.stdenv.hostPlatform.isLinux { ${moldVar} = "-C link-arg=-fuse-ld=mold"; };
moldEnv = lib.optionalAttrs pkgs.stdenv.hostPlatform.isLinux { ${moldVar} = "-C link-arg=-fuse-ld=mold"; };
builderTools = [ kache.packages.${system}.default pkgs.bubblewrap pkgs.nushell pkgs.wasm-tools pkgs.bash pkgs.coreutils ];
runtimePath = pkgs.lib.makeBinPath ([ rust pkgs.stdenv.cc ] ++ builderTools ++ mold);
runtimePath = [ rust pkgs.stdenv.cc ] ++ builderTools ++ mold |> lib.makeBinPath;
wasiCc = pkgs.pkgsCross.wasi32.stdenv.cc;
wasiCcEnv = {
CC_wasm32_wasip2 = "${wasiCc}/bin/${wasiCc.targetPrefix}cc";
@@ -41,7 +45,7 @@
aarch64-darwin = "sha256-Wu/9jVoMG3msHXCvg9WxkJllX9nGRaeU3EPxAfd5g4w=";
}.${system};
};
# rusty_v8's documented archive hook prevents downloads inside the Nix sandbox.
# rusty_v8's archive hook prevents downloads inside the Nix sandbox.
v8Env = { RUSTY_V8_ARCHIVE = v8Archive; };
cwdInit = pkgs.runCommand "sausage-cwd-init" { nativeBuildInputs = [ rust pkgs.nushell pkgs.wasm-tools ]; } ''
cp -r ${./userland/cwd-init} cwd-init
@@ -50,60 +54,93 @@
mkdir $out
cp cwd-init/init.o $out/
'';
linker = pkgs.writeScript "sausage-wasi-link.nu" ("#!${pkgs.nushell}/bin/nu\n" + builtins.readFile ./userland/cwd-init/link.nu);
members = (builtins.fromTOML (builtins.readFile ./Cargo.toml)).workspace.members;
commands = builtins.filter (p: pkgs.lib.hasPrefix "userland/" p && !(pkgs.lib.hasPrefix "userland/brush/" p)) members;
commandArgs = pkgs.lib.concatMapStringsSep " " (p: "-p " + (builtins.fromTOML (builtins.readFile (./. + "/${p}/Cargo.toml"))).package.name) commands;
linker = pkgs.runCommand "sausage-wasi-link" { nativeBuildInputs = [ pkgs.nushell ]; } ''
cp ${./userland/cwd-init/link.nu} $out
chmod +x $out
patchShebangs $out
'';
common = {
pname = "sausage";
version = "0.3.0";
inherit src;
strictDeps = true;
buildInputs = [ pkgs.openssl ];
nativeBuildInputs = [ pkgs.pkg-config pkgs.cmake pkgs.python3 pkgs.perl pkgs.nushell pkgs.wasm-tools pkgs.go ] ++ mold;
nativeBuildInputs = [ pkgs.pkg-config pkgs.cmake pkgs.python3 pkgs.perl pkgs.nushell pkgs.wasm-tools ];
# The WASI linker initializes each guest's cwd before main.
SAUSAGE_CWD_INIT = "${cwdInit}/init.o";
CARGO_TARGET_WASM32_WASIP2_LINKER = linker;
CARGO_TARGET_WASM32_WASIP2_RUSTFLAGS = "-C link-arg=sausage-cwd-init";
GOTOOLCHAIN = "local";
} // v8Env // wasiCcEnv // moldEnv;
cargoArtifacts = craneLib.buildDepsOnly (common // {
# Registry gix crates need the real APIs of the local WASI patches.
extraDummyScript = ''
chmod -R u+w $out/userland/git/vendor
cp -r ${./userland/git/vendor}/. $out/userland/git/vendor/
'';
buildPhaseCargoCommand = ''
cargoWithProfile build --locked -p sausage -p sausage-vfs
cargoWithProfile build --locked --target wasm32-wasip2 ${commandArgs}
cargoWithProfile build --locked --target wasm32-wasip2 -p brush-shell --no-default-features --features minimal,tokio/time
'';
doCheck = false;
});
userland = craneLib.mkCargoDerivation (common // {
inherit cargoArtifacts;
pname = "sausage-userland";
preBuild = ''
export CARGO_TARGET_DIR="$PWD/target"
export GOCACHE="$TMPDIR/go-cache"
'';
buildPhaseCargoCommand = "nu userland/bundle.nu";
doCheck = false;
installPhaseCommand = ''
mkdir -p $out
cp -a target/userland/bundle/. $out/
test -f $out/bin/web.wasm
test -f $out/srv/www/index.html
test -f $out/etc/systemd/system/web.service
test -f $out/etc/systemd/system/multi-user.target.wants/web.service
'';
doInstallCargoArtifacts = false;
dontCheckForBrokenSymlinks = true;
});
sausageUnwrapped = craneLib.buildPackage (common // {
inherit cargoArtifacts;
doNotRemoveReferencesToVendorDir = true;
} // wasiCcEnv;
hostArgs = common // v8Env // moldEnv // {
cargoExtraArgs = "--locked -p sausage";
buildInputs = [ pkgs.openssl ];
nativeBuildInputs = common.nativeBuildInputs ++ mold;
doCheck = false;
};
hostArtifacts = craneLib.buildDepsOnly hostArgs;
userlandArgs = common // {
pname = "sausage-userland";
CARGO_BUILD_TARGET = "wasm32-wasip2";
cargoExtraArgs = "--locked ${commandArgs}";
doCheck = false;
};
userlandArtifacts = craneLib.buildDepsOnly userlandArgs;
userland = craneLib.buildPackage (userlandArgs // { cargoArtifacts = userlandArtifacts; });
goTool = name: source: subPackages:
(pkgs.buildGoModule {
pname = "sausage-${name}";
version = "vendored";
src = source;
inherit subPackages;
vendorHash = null;
env.CGO_ENABLED = "0";
ldflags = [ "-s" "-w" ];
doCheck = false;
dontStrip = true;
postInstall = ''
mv $out/bin/wasip1_wasm/* $out/bin/${name}.wasm
rmdir $out/bin/wasip1_wasm
'';
}).overrideAttrs (old: { env = old.env // { GOOS = "wasip1"; GOARCH = "wasm"; }; });
awk = goTool "awk" ./userland/goawk/source [ "." ];
esbuild = goTool "esbuild" ./userland/esbuild/source [ "cmd/esbuild" ];
qjs = pkgs.stdenv.mkDerivation {
pname = "sausage-qjs";
version = "0.18.0";
src = ./userland/qjs/source;
nativeBuildInputs = [ pkgs.cmake pkgs.llvmPackages_21.lld ];
cmakeFlags = [ "-DCMAKE_SYSTEM_NAME=WASI" "-DCMAKE_C_COMPILER=${wasiCcEnv.CC_wasm32_wasip2}" "-DCMAKE_AR=${wasiCcEnv.AR_wasm32_wasip2}" "-DCMAKE_RANLIB=${wasiCc}/bin/${wasiCc.targetPrefix}ranlib" ];
buildFlags = [ "qjs_exe" ];
installPhase = ''
runHook preInstall
mkdir -p $out/bin
cp qjs $out/bin/qjs.wasm
runHook postInstall
'';
dontStrip = true;
};
bundle = pkgs.runCommand "sausage-bundle" {} ''
mkdir -p $out/bin $out/src/sausage $out/etc/sausage $out/etc/systemd/system/multi-user.target.wants $out/srv/www
${tools |> map (t: ''
cp ${(if t.package != null then userland else { inherit awk esbuild qjs; }.${t.binary})}/bin/${t.binary}.wasm $out/bin/
${t.names |> map (name: ''
${lib.optionalString (name != t.binary) "ln -s ${t.binary}.wasm $out/bin/${name}.wasm"}
ln -s ${t.binary}.wasm $out/bin/${name}
'') |> lib.concatStrings}
'') |> lib.concatStrings}
cp -R --no-preserve=mode ${src}/. $out/src/sausage/
cp ${./config/config.yaml} $out/etc/sausage/config.yaml
cp ${./userland/web/web.service} $out/etc/systemd/system/web.service
touch $out/etc/systemd/system/multi-user.target.wants/web.service
cp ${./userland/web/index.html} ${./userland/web/credentials.js} ${./userland/web/credentials-view.js} $out/srv/www/
'';
sausageUnwrapped = craneLib.buildPackage (hostArgs // {
cargoArtifacts = hostArtifacts;
doNotRemoveReferencesToVendorDir = true;
});
# Keep the wrapper separate from crane's removal of toolchain references.
sausage = pkgs.symlinkJoin {
@@ -111,40 +148,35 @@
paths = [ sausageUnwrapped ];
nativeBuildInputs = [ pkgs.makeWrapper ];
postBuild = ''
wrapProgram $out/bin/sausage --set-default SAUSAGE_USERLAND ${userland} \
wrapProgram $out/bin/sausage --set-default SAUSAGE_USERLAND ${bundle} \
--prefix PATH : ${runtimePath} \
--set-default CC_wasm32_wasip2 ${wasiCcEnv.CC_wasm32_wasip2} \
--set-default AR_wasm32_wasip2 ${wasiCcEnv.AR_wasm32_wasip2} \
${pkgs.lib.optionalString pkgs.stdenv.hostPlatform.isLinux "--set-default ${moldVar} '-C link-arg=-fuse-ld=mold'"}
${lib.optionalString pkgs.stdenv.hostPlatform.isLinux "--set-default ${moldVar} '-C link-arg=-fuse-ld=mold'"}
'';
meta.mainProgram = "sausage";
};
in {
packages.default = sausage;
packages = { default = sausage; inherit bundle userland awk esbuild qjs; };
checks.package = sausage;
checks.tests = craneLib.mkCargoDerivation (common // {
inherit cargoArtifacts;
pnameSuffix = "-test";
nativeBuildInputs = common.nativeBuildInputs ++ [ pkgs.git ];
checks.tests = craneLib.cargoTest (hostArgs // {
cargoArtifacts = hostArtifacts;
cargoExtraArgs = "--locked -p sausage -p sausage-vfs";
cargoTestExtraArgs = "-- --nocapture";
doCheck = true;
buildPhaseCargoCommand = "";
checkPhaseCargoCommand = ''
cargoWithProfile test --locked -p sausage -p sausage-vfs --lib
cargoWithProfile test --locked -p sausage --test it -- harness:: p1:: durable:: cred:: cred_ --skip undo_harness::
cargoWithProfile test --locked -p sausage-vfs --test layers --test regressions --test streams --test wasi
'';
nativeBuildInputs = hostArgs.nativeBuildInputs ++ [ pkgs.git ];
SAUSAGE_TEST_NO_BUILDER_SANDBOX = "1";
SAUSAGE_USERLAND = bundle;
preCheck = ''
export SAUSAGE_USERLAND="${userland}"
export SAUSAGE_CACHE="$TMPDIR/sausage-cache"
export CARGO_TARGET_DIR="$PWD/target"
export RUST_BACKTRACE=1
mkdir -p target/userland
ln -s ${userland} target/bundle
ln -s ${userland} target/userland/bundle
ln -s ${bundle} target/bundle
ln -s ${bundle} target/userland/bundle
'';
doInstallCargoArtifacts = false;
});
checks.shell = pkgs.runCommand "sausage-shell" { } ''
checks.shell = pkgs.runCommand "sausage-shell" {} ''
export SAUSAGE_CACHE="$TMPDIR/sausage-cache"
${sausage}/bin/sausage sh "$TMPDIR/world" -c \
'set -e; echo hi | tr a-z A-Z; git --version; rg --version; awk "BEGIN {print 42}"; echo payload > /payload; tar -cf /test.tar /payload; tar -tf /test.tar; qjs -e "console.log(42)"; box --help; cargo --help; undo list' > output
@@ -159,7 +191,7 @@
'';
apps.default = { type = "app"; program = "${sausage}/bin/sausage"; };
devShells.default = pkgs.mkShell (v8Env // wasiCcEnv // moldEnv // {
packages = [ rust pkgs.wasmtime pkgs.openssl pkgs.pkg-config ] ++ builderTools ++ mold;
packages = [ rust pkgs.wasmtime pkgs.openssl pkgs.pkg-config pkgs.go pkgs.cmake pkgs.llvmPackages_21.lld ] ++ builderTools ++ mold;
});
});
}
+19 -6
View File
@@ -78,15 +78,24 @@ async fn bundle_copy_and_real_shell() -> anyhow::Result<()> {
for entry in std::fs::read_dir(dir.path().join("world/bin"))? {
let entry = entry?;
if entry.file_type()?.is_symlink() {
assert_eq!(
std::fs::read_link(entry.path())?,
PathBuf::from(format!("{}.wasm", entry.file_name().to_string_lossy()))
);
let target = std::fs::read_link(entry.path())?;
assert_eq!(target.components().count(), 1, "{target:?}");
assert_eq!(target.extension().unwrap(), "wasm");
assert!(std::fs::symlink_metadata(entry.path().parent().unwrap().join(target))?.is_file());
} else {
assert_eq!(entry.path().extension().unwrap(), "wasm");
assert_eq!(entry.metadata()?.nlink(), 1);
}
}
let tools: Vec<serde_json::Value> = serde_json::from_str(include_str!("../../../userland/tools.json"))?;
for tool in tools {
let binary = format!("{}.wasm", tool["binary"].as_str().unwrap());
let bin = dir.path().join("world/bin");
assert!(std::fs::symlink_metadata(bin.join(&binary))?.is_file());
for name in tool["names"].as_array().unwrap() {
assert_eq!(std::fs::read_link(bin.join(name.as_str().unwrap()))?, PathBuf::from(&binary));
}
}
assert!(world.root.stat("/usr/local/bin", true).await?.directory);
let writable = world
.root
@@ -244,12 +253,16 @@ async fn bundle_copy_and_real_shell() -> anyhow::Result<()> {
let expected: String = (1..=10000).map(|n| format!("{n}\n")).collect();
assert_eq!(actual, expected);
let source_cat = std::fs::read(bundle.join("bin/cat.wasm"))?;
std::fs::write(
dir.path().join("world/bin/replacement.wasm"),
wat::parse_str(r#"(module (memory (export "memory") 1) (func (export "_start")))"#)?,
)?;
let head = std::fs::read(dir.path().join("world/bin/head.wasm"))?;
let mut out = Vec::new();
assert_eq!(
exec.exec(
Command::Shell(
"cp /bin/true.wasm /bin/cat.wasm; cat /test.txt; head -n 1 /test.txt".into()
"rm /bin/cat /bin/cat.wasm; cp /bin/replacement.wasm /bin/cat.wasm; ln -s cat.wasm /bin/cat; cat /test.txt; head -n 1 /test.txt".into()
),
Options::default(),
&mut |stream, bytes| {
@@ -266,7 +279,7 @@ async fn bundle_copy_and_real_shell() -> anyhow::Result<()> {
assert_eq!(std::fs::read(dir.path().join("world/bin/head.wasm"))?, head);
assert_eq!(
exec.exec(
Command::Shell("cp /bin/true.wasm /usr/local/bin/head.wasm; head /test.txt".into()),
Command::Shell("cp /bin/replacement.wasm /usr/local/bin/head.wasm; head /test.txt".into()),
Options::default(),
&mut |_, bytes| assert!(bytes.is_empty())
)
+13 -4
View File
@@ -10,20 +10,29 @@ def main [host: path = 'result/bin/sausage'] {
print --stderr --no-newline $initialized.stderr
if $initialized.exit_code != 0 { error make {msg: 'packaged host init failed'} }
let script = r#'set -e
/bin/printf 'coreutils OK\n'
printf 'same\n' > /same
cmp /same /same
diff /same /same
ps | grep -q PID
sh -c 'echo sh OK'
bash -c 'echo bash OK'
qjs -e 'console.log("qjs source OK")'
mkdir -p /src/projects
cd /src/projects
cargo new packaged-probe --vcs none
printf 'fn main() { println!("packaged builder OK"); }\n' > packaged-probe/src/main.rs
cd packaged-probe
cargo build --release
./target/wasm32-wasip2/release/packaged-probe.wasm
cargo run --release
'#
let result = (^$host sh $world -c $script | complete)
print --no-newline $result.stdout
print --stderr --no-newline $result.stderr
if $result.exit_code != 0 { error make {msg: 'packaged builder failed'} }
if not ($result.stdout | lines | any {|line| $line == 'packaged builder OK' }) {
error make {msg: 'compiled program did not produce its expected output'}
for expected in ['coreutils OK' 'sh OK' 'bash OK' 'qjs source OK' 'packaged builder OK'] {
if not ($result.stdout | lines | any {|line| $line == $expected }) {
error make {msg: $'packaged check did not print: ($expected)'}
}
}
rm --recursive --force $temporary
print 'Packaged builder check passed.'
+1 -1
View File
@@ -67,7 +67,7 @@ harness = false
[features]
# Input backends. Each can be enabled for any target, and is offered where it
# works (see `build.rs`), so the defaults suit every target.
default = ["basic", "reedline", "minimal", "vt"]
default = ["minimal", "tokio/time"]
basic = ["brush-interactive/basic"]
minimal = ["brush-interactive/minimal"]
reedline = ["brush-interactive/reedline"]
+18 -21
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env nu
# Build independent commands and plain source files. World creation initializes their repository.
# Build the shared tool list incrementally and copy plain sources for world initialization.
def main [destination?: path] {
let here = ($env.FILE_PWD | path expand)
let destination = ($destination | default ($here | path join .. target userland bundle) | path expand)
@@ -12,32 +12,29 @@ def main [destination?: path] {
}
let manifest = ($here | path join .. Cargo.toml)
let metadata = (^cargo metadata --manifest-path $manifest --no-deps --format-version 1 | from json)
let packages = ($metadata.packages | where {|p|
($p.id in $metadata.workspace_members) and ($p.manifest_path | str starts-with $here) and not ($p.manifest_path | str starts-with ($here | path join brush source))
})
let arguments = ($packages | where {|p| $p.targets | any {|t| 'bin' in $t.kind } } | each {|p| ['-p' $p.name] } | flatten)
let tools = (open ($here | path join tools.json))
let packages = ($tools | where package != null | get package | uniq)
let arguments = ($packages | each {|p| ['-p' $p] } | flatten)
do { cd $here; ^cargo build --manifest-path $manifest --release --locked --target wasm32-wasip2 ...$arguments }
if $env.LAST_EXIT_CODE != 0 { error make {msg: 'userland build failed'} }
^nu ($here | path join brush build.nu)
if $env.LAST_EXIT_CODE != 0 { error make {msg: 'brush build failed'} }
if ($bin | path exists) { rm --recursive --force $bin }
mkdir $bin
^nu ($here | path join preview1.nu) $bin
if $env.LAST_EXIT_CODE != 0 { error make {msg: 'preview1 build failed'} }
for package in $packages {
let tools = ($package.manifest_path | path dirname | path join tools.txt)
for binary in ($package.targets | where {|t| 'bin' in $t.kind }) {
let source = ($metadata.target_directory | path join wasm32-wasip2 release $'($binary.name).wasm')
let names = if ($tools | path exists) { open --raw $tools | lines | where {|s| ($s | str trim) != '' } } else { [$binary.name] }
for name in $names {
^cp --remove-destination $source ($bin | path join $'($name).wasm')
if $env.LAST_EXIT_CODE != 0 { error make {msg: $'bundle copy failed: ($name)'} }
}
}
for tool in ($tools | where package != null) {
let source = ($metadata.target_directory | path join wasm32-wasip2 release $'($tool.binary).wasm')
^cp --remove-destination $source ($bin | path join $'($tool.binary).wasm')
if $env.LAST_EXIT_CODE != 0 { error make {msg: $'bundle copy failed: ($tool.binary)'} }
}
let brush = ($metadata.target_directory | path join wasm32-wasip2 release brush.wasm)
for name in [sh bash] {
^cp --remove-destination $brush ($bin | path join $'($name).wasm')
if $env.LAST_EXIT_CODE != 0 { error make {msg: 'brush copy failed'} }
for tool in $tools {
for name in $tool.names {
if $name != $tool.binary {
^ln -s $'($tool.binary).wasm' ($bin | path join $'($name).wasm')
if $env.LAST_EXIT_CODE != 0 { error make {msg: $'tool alias failed: ($name).wasm'} }
}
^ln -s $'($tool.binary).wasm' ($bin | path join $name)
if $env.LAST_EXIT_CODE != 0 { error make {msg: $'tool alias failed: ($name)'} }
}
}
let units = ($destination | path join etc systemd system)
mkdir ($units | path join multi-user.target.wants)
+5
View File
@@ -2,6 +2,11 @@
name = "sausage-coreutils"
version = "0.1.0"
edition = "2024"
autobins = false
[[bin]]
name = "coreutils"
path = "src/main.rs"
[dependencies]
rustix = { version = "1.1.6", features = ["std"] }
-5
View File
@@ -1,5 +0,0 @@
//! Implements the arch command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("arch").expect("utility supplies localization");
std::process::exit(uu_arch::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the b2sum command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("b2sum").expect("utility supplies localization");
std::process::exit(uu_b2sum::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the base32 command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("base32").expect("utility supplies localization");
std::process::exit(uu_base32::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the base64 command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("base64").expect("utility supplies localization");
std::process::exit(uu_base64::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the basename command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("basename").expect("utility supplies localization");
std::process::exit(uu_basename::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the basenc command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("basenc").expect("utility supplies localization");
std::process::exit(uu_basenc::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the cat command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("cat").expect("utility supplies localization");
std::process::exit(uu_cat::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the cksum command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("cksum").expect("utility supplies localization");
std::process::exit(uu_cksum::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the comm command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("comm").expect("utility supplies localization");
std::process::exit(uu_comm::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the cp command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("cp").expect("utility supplies localization");
std::process::exit(uu_cp::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the csplit command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("csplit").expect("utility supplies localization");
std::process::exit(uu_csplit::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the cut command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("cut").expect("utility supplies localization");
std::process::exit(uu_cut::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the date command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("date").expect("utility supplies localization");
std::process::exit(uu_date::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the dd command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("dd").expect("utility supplies localization");
std::process::exit(uu_dd::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the dir command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("ls").expect("utility supplies localization");
std::process::exit(uu_dir::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the dircolors command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("dircolors").expect("utility supplies localization");
std::process::exit(uu_dircolors::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the dirname command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("dirname").expect("utility supplies localization");
std::process::exit(uu_dirname::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the echo command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("echo").expect("utility supplies localization");
std::process::exit(uu_echo::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the expand command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("expand").expect("utility supplies localization");
std::process::exit(uu_expand::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the expr command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("expr").expect("utility supplies localization");
std::process::exit(uu_expr::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the factor command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("factor").expect("utility supplies localization");
std::process::exit(uu_factor::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the false command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("false").expect("utility supplies localization");
std::process::exit(uu_false::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the fmt command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("fmt").expect("utility supplies localization");
std::process::exit(uu_fmt::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the fold command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("fold").expect("utility supplies localization");
std::process::exit(uu_fold::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the head command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("head").expect("utility supplies localization");
std::process::exit(uu_head::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the hostid command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("hostid").expect("utility supplies localization");
std::process::exit(uu_hostid::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the join command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("join").expect("utility supplies localization");
std::process::exit(uu_join::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the link command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("link").expect("utility supplies localization");
std::process::exit(uu_link::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the ln command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("ln").expect("utility supplies localization");
std::process::exit(uu_ln::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the ls command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("ls").expect("utility supplies localization");
std::process::exit(uu_ls::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the md5sum command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("md5sum").expect("utility supplies localization");
std::process::exit(uu_md5sum::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the mkdir command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("mkdir").expect("utility supplies localization");
std::process::exit(uu_mkdir::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the mktemp command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("mktemp").expect("utility supplies localization");
std::process::exit(uu_mktemp::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the mv command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("mv").expect("utility supplies localization");
std::process::exit(uu_mv::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the nice command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("nice").expect("utility supplies localization");
std::process::exit(uu_nice::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the nl command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("nl").expect("utility supplies localization");
std::process::exit(uu_nl::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the nproc command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("nproc").expect("utility supplies localization");
std::process::exit(uu_nproc::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the numfmt command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("numfmt").expect("utility supplies localization");
std::process::exit(uu_numfmt::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the od command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("od").expect("utility supplies localization");
std::process::exit(uu_od::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the paste command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("paste").expect("utility supplies localization");
std::process::exit(uu_paste::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the pathchk command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("pathchk").expect("utility supplies localization");
std::process::exit(uu_pathchk::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the pr command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("pr").expect("utility supplies localization");
std::process::exit(uu_pr::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the printenv command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("printenv").expect("utility supplies localization");
std::process::exit(uu_printenv::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the printf command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("printf").expect("utility supplies localization");
std::process::exit(uu_printf::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the ptx command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("ptx").expect("utility supplies localization");
std::process::exit(uu_ptx::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the pwd command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("pwd").expect("utility supplies localization");
std::process::exit(uu_pwd::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the readlink command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("readlink").expect("utility supplies localization");
std::process::exit(uu_readlink::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the realpath command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("realpath").expect("utility supplies localization");
std::process::exit(uu_realpath::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the rm command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("rm").expect("utility supplies localization");
std::process::exit(uu_rm::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the rmdir command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("rmdir").expect("utility supplies localization");
std::process::exit(uu_rmdir::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the seq command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("seq").expect("utility supplies localization");
std::process::exit(uu_seq::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the sha1sum command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("sha1sum").expect("utility supplies localization");
std::process::exit(uu_sha1sum::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the sha224sum command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("sha224sum").expect("utility supplies localization");
std::process::exit(uu_sha224sum::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the sha256sum command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("sha256sum").expect("utility supplies localization");
std::process::exit(uu_sha256sum::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the sha384sum command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("sha384sum").expect("utility supplies localization");
std::process::exit(uu_sha384sum::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the sha512sum command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("sha512sum").expect("utility supplies localization");
std::process::exit(uu_sha512sum::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the shred command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("shred").expect("utility supplies localization");
std::process::exit(uu_shred::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the shuf command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("shuf").expect("utility supplies localization");
std::process::exit(uu_shuf::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the sleep command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("sleep").expect("utility supplies localization");
std::process::exit(uu_sleep::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the sort command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("sort").expect("utility supplies localization");
std::process::exit(uu_sort::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the split command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("split").expect("utility supplies localization");
std::process::exit(uu_split::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the sum command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("sum").expect("utility supplies localization");
std::process::exit(uu_sum::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the tail command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("tail").expect("utility supplies localization");
std::process::exit(uu_tail::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the tee command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("tee").expect("utility supplies localization");
std::process::exit(uu_tee::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the test command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("test").expect("utility supplies localization");
std::process::exit(uu_test::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the touch command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("touch").expect("utility supplies localization");
std::process::exit(uu_touch::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the tr command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("tr").expect("utility supplies localization");
std::process::exit(uu_tr::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the true command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("true").expect("utility supplies localization");
std::process::exit(uu_true::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the truncate command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("truncate").expect("utility supplies localization");
std::process::exit(uu_truncate::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the tsort command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("tsort").expect("utility supplies localization");
std::process::exit(uu_tsort::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the tty command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("tty").expect("utility supplies localization");
std::process::exit(uu_tty::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the uname command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("uname").expect("utility supplies localization");
std::process::exit(uu_uname::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the unexpand command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("unexpand").expect("utility supplies localization");
std::process::exit(uu_unexpand::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the uniq command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("uniq").expect("utility supplies localization");
std::process::exit(uu_uniq::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the unlink command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("unlink").expect("utility supplies localization");
std::process::exit(uu_unlink::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the vdir command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("ls").expect("utility supplies localization");
std::process::exit(uu_vdir::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the wc command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("wc").expect("utility supplies localization");
std::process::exit(uu_wc::uumain(uucore::args_os()));
}
-5
View File
@@ -1,5 +0,0 @@
//! Implements the yes command as an independent WASI binary.
fn main() {
uucore::locale::setup_localization("yes").expect("utility supplies localization");
std::process::exit(uu_yes::uumain(uucore::args_os()));
}
+100
View File
@@ -0,0 +1,100 @@
//! Dispatch the bundled uutils commands by argv[0], or as coreutils <command>.
fn main() {
let argv: Vec<_> = std::env::args_os().collect();
let invoked = std::path::Path::new(&argv[0]).file_stem().unwrap();
let second = invoked == "coreutils";
if second {
if argv.len() < 2 {
eprintln!("Usage: coreutils <utility> [arguments...]");
std::process::exit(2);
}
uucore::set_utility_is_second_arg();
}
let name = uucore::util_name();
let name = name.strip_suffix(".wasm").unwrap_or(name);
let locale = match name { "dir" | "vdir" => "ls", _ => name };
uucore::locale::setup_localization(locale).expect("utility supplies localization");
let args = uucore::args_os().skip(usize::from(second));
let code = match name {
"arch" => uu_arch::uumain(args),
"b2sum" => uu_b2sum::uumain(args),
"base32" => uu_base32::uumain(args),
"base64" => uu_base64::uumain(args),
"basename" => uu_basename::uumain(args),
"basenc" => uu_basenc::uumain(args),
"cat" => uu_cat::uumain(args),
"cksum" => uu_cksum::uumain(args),
"comm" => uu_comm::uumain(args),
"cp" => uu_cp::uumain(args),
"csplit" => uu_csplit::uumain(args),
"cut" => uu_cut::uumain(args),
"date" => uu_date::uumain(args),
"dd" => uu_dd::uumain(args),
"dir" => uu_dir::uumain(args),
"dircolors" => uu_dircolors::uumain(args),
"dirname" => uu_dirname::uumain(args),
"echo" => uu_echo::uumain(args),
"expand" => uu_expand::uumain(args),
"expr" => uu_expr::uumain(args),
"factor" => uu_factor::uumain(args),
"false" => uu_false::uumain(args),
"fmt" => uu_fmt::uumain(args),
"fold" => uu_fold::uumain(args),
"head" => uu_head::uumain(args),
"hostid" => uu_hostid::uumain(args),
"join" => uu_join::uumain(args),
"link" => uu_link::uumain(args),
"ln" => uu_ln::uumain(args),
"ls" => uu_ls::uumain(args),
"md5sum" => uu_md5sum::uumain(args),
"mkdir" => uu_mkdir::uumain(args),
"mktemp" => uu_mktemp::uumain(args),
"mv" => uu_mv::uumain(args),
"nice" => uu_nice::uumain(args),
"nl" => uu_nl::uumain(args),
"nproc" => uu_nproc::uumain(args),
"numfmt" => uu_numfmt::uumain(args),
"od" => uu_od::uumain(args),
"paste" => uu_paste::uumain(args),
"pathchk" => uu_pathchk::uumain(args),
"pr" => uu_pr::uumain(args),
"printenv" => uu_printenv::uumain(args),
"printf" => uu_printf::uumain(args),
"ptx" => uu_ptx::uumain(args),
"pwd" => uu_pwd::uumain(args),
"readlink" => uu_readlink::uumain(args),
"realpath" => uu_realpath::uumain(args),
"rm" => uu_rm::uumain(args),
"rmdir" => uu_rmdir::uumain(args),
"seq" => uu_seq::uumain(args),
"sha1sum" => uu_sha1sum::uumain(args),
"sha224sum" => uu_sha224sum::uumain(args),
"sha256sum" => uu_sha256sum::uumain(args),
"sha384sum" => uu_sha384sum::uumain(args),
"sha512sum" => uu_sha512sum::uumain(args),
"shred" => uu_shred::uumain(args),
"shuf" => uu_shuf::uumain(args),
"sleep" => uu_sleep::uumain(args),
"sort" => uu_sort::uumain(args),
"split" => uu_split::uumain(args),
"sum" => uu_sum::uumain(args),
"tail" => uu_tail::uumain(args),
"tee" => uu_tee::uumain(args),
"test" => uu_test::uumain(args),
"touch" => uu_touch::uumain(args),
"tr" => uu_tr::uumain(args),
"true" => uu_true::uumain(args),
"truncate" => uu_truncate::uumain(args),
"tsort" => uu_tsort::uumain(args),
"tty" => uu_tty::uumain(args),
"uname" => uu_uname::uumain(args),
"unexpand" => uu_unexpand::uumain(args),
"uniq" => uu_uniq::uumain(args),
"unlink" => uu_unlink::uumain(args),
"vdir" => uu_vdir::uumain(args),
"wc" => uu_wc::uumain(args),
"yes" => uu_yes::uumain(args),
_ => { eprintln!("coreutils: unknown utility: {name}"); 127 }
};
std::process::exit(code);
}
+8 -8
View File
@@ -1,8 +1,8 @@
#!/usr/bin/env nu
# Build pinned Go commands offline and copy the pinned QuickJS release.
# Build the pinned Go tools and QuickJS C sources for WASI preview1 during development.
def main [bin: path] {
let here = $env.FILE_PWD
let target = $env.CARGO_TARGET_DIR
let target = ($env.CARGO_TARGET_DIR | path expand)
mkdir $target $bin
for tool in [
{name: awk, source: goawk, package: '.'}
@@ -19,11 +19,11 @@ def main [bin: path] {
^cp --remove-destination $output ($bin | path join $'($tool.name).wasm')
if $env.LAST_EXIT_CODE != 0 { error make {msg: 'preview1 bundle copy failed'} }
}
let qjs = ($here | path join qjs qjs.wasm)
let hash = (open --raw $qjs | hash sha256)
if $hash != '2c218412cb8b17e46ee91bea7556ec38f5b9b6bd6312fddd3ceeed14aa71eaaa' {
error make {msg: 'QuickJS release hash mismatch'}
}
^cp --remove-destination $qjs ($bin | path join qjs.wasm)
let build = ($target | path join qjs)
^cmake -S ($here | path join qjs source) -B $build -DCMAKE_SYSTEM_NAME=WASI $'-DCMAKE_C_COMPILER=($env.CC_wasm32_wasip2)' $'-DCMAKE_AR=($env.AR_wasm32_wasip2)' $'-DCMAKE_RANLIB=($env.AR_wasm32_wasip2 | str replace "-ar" "-ranlib")' -DCMAKE_BUILD_TYPE=Release
if $env.LAST_EXIT_CODE != 0 { error make {msg: 'QuickJS configure failed'} }
^cmake --build $build --target qjs_exe --parallel
if $env.LAST_EXIT_CODE != 0 { error make {msg: 'QuickJS build failed'} }
^cp --remove-destination ($build | path join qjs) ($bin | path join qjs.wasm)
if $env.LAST_EXIT_CODE != 0 { error make {msg: 'QuickJS bundle copy failed'} }
}
+7 -7
View File
@@ -1,13 +1,13 @@
# QuickJS-ng
The bundle includes the unmodified WASI preview1 command from QuickJS-ng v0.18.0.
Sausage builds QuickJS-ng v0.18.0 from the unmodified C sources in `source/`.
The Nix package uses the existing WASI clang toolchain and upstream's CMake `qjs_exe` target.
The development bundle uses the same sources and target through `userland/preview1.nu`.
Neither path downloads a prebuilt executable.
- URL: https://github.com/quickjs-ng/quickjs/releases/download/v0.18.0/qjs-wasi.wasm
- SHA-256: `2c218412cb8b17e46ee91bea7556ec38f5b9b6bd6312fddd3ceeed14aa71eaaa`
- License: MIT. https://github.com/quickjs-ng/quickjs/blob/v0.18.0/LICENSE
`userland/preview1.nu` checks the hash before it copies the binary.
The binary is committed so bundle builds do not download it.
- Source: https://github.com/quickjs-ng/quickjs/tree/v0.18.0
- License: MIT. See `source/LICENSE`.
- Upstream WASI build: https://github.com/quickjs-ng/quickjs/blob/v0.18.0/.github/workflows/ci.yml
```sh
qjs -e 'console.log(6 * 7)'
Binary file not shown.
+4
View File
@@ -0,0 +1,4 @@
# Don't show changes in generated files when doing git diff
gen/** -diff
builtin-*.h -diff
+7
View File
@@ -0,0 +1,7 @@
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/" # Location of package manifests
schedule:
interval: "weekly"
+920
View File
@@ -0,0 +1,920 @@
name: ci
on:
pull_request:
paths:
- '**'
- '!.gitignore'
- '!LICENSE'
- '!README.md'
- '!docs/**'
- '!examples/**'
- '.github/workflows/ci.yml'
push:
branches:
- master
paths:
- '**'
- '!.gitignore'
- '!LICENSE'
- '!README.md'
- '!docs/**'
- '!examples/**'
- '.github/workflows/ci.yml'
jobs:
codegen:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: build
run: |
make codegen
- name: Check if the git repository is clean
run: (exit "$(git status --porcelain --untracked-files=no | head -255 | wc -l)") || (echo "Dirty git tree"; git diff; exit 1)
ci:
runs-on: ${{ matrix.config.os }}
name: ${{ matrix.config.os }} (${{ matrix.config.configType }}${{ matrix.config.arch }})
env:
ASAN_OPTIONS: halt_on_error=1
MSAN_OPTIONS: halt_on_error=1
UBSAN_OPTIONS: halt_on_error=1
defaults:
run:
shell: ${{ matrix.config.arch == '' && 'sh' || 'alpine.sh' }} {0}
strategy:
fail-fast: false
matrix:
config:
# see https://github.com/quickjs-ng/quickjs/issues/879 - tl;dr flaky
#- { os: ubuntu-24.04-arm, configType: Debug }
#- { os: ubuntu-24.04-arm, configType: Release, runTest262: true }
- { os: ubuntu-latest, configType: Debug }
- { os: ubuntu-latest, configType: Release, runTest262: true }
- { os: ubuntu-latest, configType: examples }
- { os: ubuntu-latest, configType: shared }
- { os: ubuntu-latest, configType: asan+ubsan, runTest262: true }
- { os: ubuntu-latest, configType: msan }
- { os: ubuntu-latest, configType: tcc }
- { os: ubuntu-latest, arch: x86, runTest262: true }
- { os: ubuntu-latest, arch: riscv64 }
- { os: macos-15, configType: Debug }
- { os: macos-15, configType: Release }
- { os: macos-15, configType: examples }
- { os: macos-15, configType: shared }
- { os: macos-15, configType: asan+ubsan, runTest262: true }
steps:
- uses: actions/checkout@v7
with:
submodules: true
- name: install TCC
if: ${{ matrix.config.configType == 'tcc' }}
run: |
pushd "$RUNNER_TEMP"
git clone https://repo.or.cz/tinycc.git
cd tinycc
git checkout c21576f8a32715ab439690d18184b0e02022bbbd
./configure
make
sudo make install
tcc -v
popd
echo "CC=tcc" >> $GITHUB_ENV;
- name: Install extra dependencies
if: ${{ matrix.config.arch != '' }}
shell: 'sh'
run: |
sudo apt update && sudo apt install -y binfmt-support
- name: Setup Alpine
if: ${{ matrix.config.arch != '' }}
uses: quickjs-ng/setup-alpine@master
with:
arch: ${{ matrix.config.arch }}
packages: "build-base make cmake git"
apk-tools-url: https://raw.githubusercontent.com/quickjs-ng/apk-for-ci/refs/heads/master/apk.static#!sha256!34bb1a96f0258982377a289392d4ea9f3f4b767a4bb5806b1b87179b79ad8a1c
- name: uname
run: uname -a
- name: Set build ENV vars
run: |
if [ "${{ matrix.config.configType }}" = "Debug" ]; then
echo "BUILD_TYPE=Debug" >> $GITHUB_ENV;
elif [ "${{ matrix.config.configType }}" = "examples" ]; then
echo "QJS_BUILD_EXAMPLES=ON" >> $GITHUB_ENV;
elif [ "${{ matrix.config.configType }}" = "shared" ]; then
echo "BUILD_SHARED_LIBS=ON" >> $GITHUB_ENV;
elif [ "${{ matrix.config.configType }}" = "asan+ubsan" ]; then
echo "BUILD_TYPE=RelWithDebInfo" >> $GITHUB_ENV;
echo "QJS_ENABLE_ASAN=ON" >> $GITHUB_ENV;
echo "QJS_ENABLE_UBSAN=ON" >> $GITHUB_ENV;
elif [ "${{ matrix.config.configType }}" = "msan" ]; then
echo "BUILD_TYPE=RelWithDebInfo" >> $GITHUB_ENV;
echo "QJS_ENABLE_MSAN=ON" >> $GITHUB_ENV;
echo "CC=clang" >> $GITHUB_ENV;
fi
- name: build
run: |
make \
BUILD_TYPE=$BUILD_TYPE \
QJS_BUILD_EXAMPLES=$QJS_BUILD_EXAMPLES \
BUILD_SHARED_LIBS=$BUILD_SHARED_LIBS \
QJS_ENABLE_ASAN=$QJS_ENABLE_ASAN \
QJS_ENABLE_UBSAN=$QJS_ENABLE_UBSAN \
QJS_ENABLE_MSAN=$QJS_ENABLE_MSAN
- name: stats
if: ${{ matrix.config.configType != 'examples' }}
run: |
make stats
- name: cxxtest
run: |
make cxxtest
- name: test
if: ${{ matrix.config.configType != 'examples' }}
run: |
make test
- name: test examples
if: ${{ matrix.config.configType == 'examples' }}
run: |
cp build/fib.so examples/
cp build/point.so examples/
./build/qjs examples/test_fib.js
./build/qjs examples/test_point.js
./build/qjs tests/test_bjson.js
./build/function_source
- name: test 262
if: ${{ matrix.config.runTest262 }}
run: |
git submodule update --init --checkout --depth 1
make test262
- name: test standalone
run: |
./build/qjs -c examples/hello.js -o hello
./hello
- name: test api
run: |
./build/api-test
- name: test lre
run: |
./build/lre-test
windows-msvc:
runs-on: ${{ matrix.config.os }}
strategy:
fail-fast: false
matrix:
config:
- { arch: x64, vsArch: x64, buildType: Debug, os: windows-latest, msvc: 18 }
- { arch: x64, vsArch: x64, buildType: Release, os: windows-latest, msvc: 18 }
- { arch: Win32, vsArch: x86, buildType: Debug, os: windows-latest, msvc: 18 }
- { arch: Win32, vsArch: x86, buildType: Release, os: windows-latest, msvc: 18 }
- { arch: ARM64, vsArch: arm64, buildType: Debug, os: windows-11-arm, msvc: 18 }
- { arch: ARM64, vsArch: arm64, buildType: Release, os: windows-11-arm, msvc: 18 }
steps:
- uses: actions/checkout@v7
- name: build
run: |
cmake -B build -DQJS_BUILD_WERROR=ON -DQJS_BUILD_EXAMPLES=ON -G "Visual Studio ${{matrix.config.msvc}}" -A ${{matrix.config.arch}}
cmake --build build --config ${{matrix.config.buildType}}
- name: stats
run: |
build\${{matrix.config.buildType}}\qjs.exe -qd
- name: test
run: |
cp build\${{matrix.config.buildType}}\fib.dll examples\
cp build\${{matrix.config.buildType}}\point.dll examples\
build\${{matrix.config.buildType}}\qjs.exe examples\test_fib.js
build\${{matrix.config.buildType}}\qjs.exe examples\test_point.js
build\${{matrix.config.buildType}}\run-test262.exe -c tests.conf
build\${{matrix.config.buildType}}\function_source.exe
- name: test standalone
run: |
build\${{matrix.config.buildType}}\qjs.exe -c examples\hello.js -o hello.exe
.\hello.exe
- name: test api
run: |
build\${{matrix.config.buildType}}\api-test.exe
- name: test lre
run: |
build\${{matrix.config.buildType}}\lre-test.exe
- name: Set up Visual Studio shell
uses: egor-tensin/vs-shell@v2
with:
arch: ${{ matrix.config.vsArch }}
- name: cxxtest
run: |
cl.exe /DJS_NAN_BOXING=0 /Zs cxxtest.cc
cl.exe /DJS_NAN_BOXING=1 /Zs cxxtest.cc
windows-clang:
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
buildType: [Debug, Release]
steps:
- uses: actions/checkout@v7
- name: install ninja
run: |
choco install ninja
ninja.exe --version
- name: build
run: |
git submodule update --init --checkout --depth 1
cmake -B build -DQJS_BUILD_EXAMPLES=ON -DCMAKE_BUILD_TYPE=${{matrix.buildType}} -DCMAKE_C_COMPILER=clang.exe -G "Ninja"
cmake --build build --config ${{matrix.buildType}}
- name: stats
run: |
build\qjs.exe -qd
- name: cxxtest
run: |
clang.exe -D JS_NAN_BOXING=0 -fsyntax-only cxxtest.cc
clang.exe -D JS_NAN_BOXING=1 -fsyntax-only cxxtest.cc
- name: test
run: |
cp build\fib.dll examples\
cp build\point.dll examples\
build\qjs.exe examples\test_fib.js
build\qjs.exe examples\test_point.js
build\run-test262.exe -c tests.conf
build\function_source.exe
windows-clang-cl:
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
buildType: [Debug, Release]
steps:
- uses: actions/checkout@v7
- name: build
run: |
git submodule update --init --checkout --depth 1
cmake -B build -DQJS_BUILD_WERROR=ON -DQJS_BUILD_EXAMPLES=ON -G "Visual Studio 18" -T ClangCL
cmake --build build --config ${{matrix.buildType}}
- name: stats
run: |
build\${{matrix.buildType}}\qjs.exe -qd
- name: cxxtest
run: |
clang-cl.exe /DJS_NAN_BOXING=0 /Zs cxxtest.cc
clang-cl.exe /DJS_NAN_BOXING=1 /Zs cxxtest.cc
- name: test
run: |
cp build\${{matrix.buildType}}\fib.dll examples\
cp build\${{matrix.buildType}}\point.dll examples\
build\${{matrix.buildType}}\qjs.exe examples\test_fib.js
build\${{matrix.buildType}}\qjs.exe examples\test_point.js
build\${{matrix.buildType}}\run-test262.exe -c tests.conf
build\${{matrix.buildType}}\function_source.exe
- name: test api
run: |
build\${{matrix.buildType}}\api-test.exe
- name: test lre
run: |
build\${{matrix.buildType}}\lre-test.exe
windows-ninja:
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
buildType: [Debug, Release]
steps:
- uses: actions/checkout@v7
- name: install ninja
run: |
choco install ninja
ninja.exe --version
- name: build
run: |
git submodule update --init --checkout --depth 1
cmake -B build -DQJS_BUILD_WERROR=ON -DQJS_BUILD_EXAMPLES=ON -DCMAKE_BUILD_TYPE=${{matrix.buildType}} -G "Ninja"
cmake --build build
- name: stats
run: |
build\qjs.exe -qd
- name: test
run: |
cp build\fib.dll examples\
cp build\point.dll examples\
build\qjs.exe examples\test_fib.js
build\qjs.exe examples\test_point.js
build\run-test262.exe -c tests.conf
build\function_source.exe
- name: test api
run: |
build\api-test.exe
- name: test lre
run: |
build\lre-test.exe
windows-sdk:
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
arch: [x64, Win32]
buildType: [Debug, Release]
steps:
- uses: actions/checkout@v7
- name: Install windows sdk
uses: ChristopheLav/windows-sdk-install@v1
with:
version-sdk: 26100
features: 'OptionId.DesktopCPPx86,OptionId.DesktopCPPx64'
- name: build
run: |
cmake -B build -DQJS_BUILD_WERROR=ON -DQJS_BUILD_EXAMPLES=ON -DCMAKE_SYSTEM_VERSION="10.0.26100.0" -A ${{matrix.arch}}
cmake --build build --config ${{matrix.buildType}}
- name: stats
run: |
build\${{matrix.buildType}}\qjs.exe -qd
- name: test
run: |
cp build\${{matrix.buildType}}\fib.dll examples\
cp build\${{matrix.buildType}}\point.dll examples\
build\${{matrix.buildType}}\qjs.exe examples\test_fib.js
build\${{matrix.buildType}}\qjs.exe examples\test_point.js
build\${{matrix.buildType}}\run-test262.exe -c tests.conf
build\${{matrix.buildType}}\function_source.exe
- name: test api
run: |
build\${{matrix.buildType}}\api-test.exe
- name: test lre
run: |
build\${{matrix.buildType}}\lre-test.exe
windows-mingw:
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
buildType: [Debug, Release]
sys:
- mingw32
- mingw64
- clang64
- ucrt64
defaults:
run:
shell: msys2 {0}
steps:
- uses: actions/checkout@v7
- name: Setup MSYS2
uses: msys2/setup-msys2@v2
with:
update: true
msystem: ${{matrix.sys}}
install: >-
git
make
pacboy: >-
cmake:p
ninja:p
toolchain:p
- name: build
run: |
make BUILD_TYPE=${{matrix.buildType}}
- name: stats
run: |
make stats
ldd build/qjs
- name: cxxtest
if: ${{ matrix.sys != 'clang64' }}
run: |
make cxxtest
- name: cxxtest
if: ${{ matrix.sys == 'clang64' }}
run: |
make cxxtest CXX=clang++
- name: test
run: |
make test
- name: test standalone
run: |
./build/qjs -c examples/hello.js -o hello.exe
./hello
- name: test api
run: |
./build/api-test
- name: test lre
run: |
./build/lre-test
windows-mingw-shared:
runs-on: windows-latest
defaults:
run:
shell: msys2 {0}
steps:
- uses: actions/checkout@v7
- name: Setup MSYS2
uses: msys2/setup-msys2@v2
with:
install: >-
git
make
pacboy: >-
cmake:p
ninja:p
toolchain:p
- name: build
run: |
make BUILD_SHARED_LIBS=ON
ldd build/qjs
- name: stats
run: |
make stats
emscripten:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: mymindstorm/setup-emsdk@v16
- name: check emsdk
run: emcc -v
- name: build
run: |
emcmake cmake -B build -DQJS_BUILD_WERROR=ON -DQJS_BUILD_LIBC=ON
emmake make -C build qjs_wasm "-j$(getconf _NPROCESSORS_ONLN)"
- name: result
run: ls -lh build
wasi:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v7
- uses: jcbhmr/setup-wasmtime@v2
- name: setup wasi-sdk
run: |
wget -nv https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-29/wasi-sdk-29.0-x86_64-linux.deb -P "$RUNNER_TEMP"
sudo apt install "$RUNNER_TEMP/wasi-sdk-29.0-x86_64-linux.deb"
- name: test
run: |
cmake -B build -DQJS_BUILD_WERROR=ON -DCMAKE_TOOLCHAIN_FILE=/opt/wasi-sdk/share/cmake/wasi-sdk.cmake
make -C build qjs_exe
wasmtime run build/qjs -qd
echo "console.log('hello wasi!');" > t.js
wasmtime run --dir . build/qjs t.js
wasmtime run --dir . build/qjs --stack-size 64K tests/wasi-stack-limit.js
wasmtime run --dir . build/qjs tests/wasi-stack-limit.js
- name: build wasi reactor
run: |
cmake -B build -DQJS_BUILD_WERROR=ON -DCMAKE_TOOLCHAIN_FILE=/opt/wasi-sdk/share/cmake/wasi-sdk.cmake -DQJS_WASI_REACTOR=ON
make -C build qjs_wasi
ls -lh build/qjs.wasm
- name: check wasi reactor exports
run: |
exports=$(/opt/wasi-sdk/bin/llvm-nm --extern-only --defined-only --just-symbol-name build/qjs.wasm)
expected=(
# Reactor entry points
qjs_init qjs_init_argv qjs_get_context qjs_destroy
# libc allocator
malloc free realloc calloc
# Some of core JS_EXTERN API
JS_NewRuntime JS_FreeRuntime JS_NewContext JS_FreeContext
JS_Eval JS_FreeValue JS_GetException JS_ToCStringLen2
# Some of quickjs-libc JS_LIBC_EXTERN API
js_std_add_helpers js_std_init_handlers
)
missing=0
for sym in "${expected[@]}"; do
echo "$exports" | grep -qx "$sym" || { echo "missing export: $sym"; missing=1; }
done
exit $missing
- name: check wasi reactor table is exported and growable
shell: node {0}
run: |
const m = new WebAssembly.Module(require('fs').readFileSync('build/qjs.wasm'));
const imports = {};
for (const i of WebAssembly.Module.imports(m)) (imports[i.module] ??= {})[i.name] = () => {};
new WebAssembly.Instance(m, imports).exports.__indirect_function_table.grow(1);
cosmopolitan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: setup cosmocc
run: |
wget -nv https://github.com/jart/cosmopolitan/releases/download/4.0.2/cosmocc-4.0.2.zip -P "$RUNNER_TEMP"
mkdir -p "$RUNNER_TEMP/cosmocc"
unzip -q "$RUNNER_TEMP/cosmocc-4.0.2.zip" -d "$RUNNER_TEMP/cosmocc"
echo "$RUNNER_TEMP/cosmocc/bin" >> "$GITHUB_PATH"
- name: setup ape loader
run: |
sudo cp "$RUNNER_TEMP/cosmocc/bin/ape-x86_64.elf" /usr/bin/ape
sudo sh -c "echo ':APE:M::MZqFpD::/usr/bin/ape:' > /proc/sys/fs/binfmt_misc/register"
sudo sh -c "echo ':APE-jart:M::jartsr::/usr/bin/ape:' > /proc/sys/fs/binfmt_misc/register"
- name: build
run: |
cmake -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=cosmocc -DCMAKE_AR="$(command -v cosmoar)"
cmake --build build -j$(getconf _NPROCESSORS_ONLN)
- name: stats
run: |
build/qjs -qd
- name: test
run: |
build/run-test262 -c tests.conf
cygwin:
runs-on: windows-latest
defaults:
run:
shell: C:\cygwin\bin\bash.exe --login --norc -eo pipefail -o igncr '{0}'
env:
CYGWIN_NOWINPATH: 1
CHERE_INVOKING: 1
steps:
- name: Set up Cygwin
uses: cygwin/cygwin-install-action@master
with:
packages: make cmake gcc-g++ bash
- uses: actions/checkout@v7
- name: build
run: make
- name: stats
run: make stats
- name: cxxtest
run: |
make cxxtest
- name: test
run: make test
- name: test api
run: |
./build/api-test
- name: test lre
run: |
./build/lre-test
openbsd:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: build + test
uses: vmactions/openbsd-vm@v1
with:
usesh: true
prepare: |
pkg_add cmake gmake
run: |
gmake
gmake stats
freebsd:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: build + test
uses: vmactions/freebsd-vm@v1
with:
usesh: true
prepare: |
pkg install -y cmake gmake
run: |
gmake
gmake stats
android:
runs-on: ubuntu-latest
container: reactnativecommunity/react-native-android:v13.0
steps:
- uses: actions/checkout@v7
- name: Configure android arm64
# see build options you can use in https://developer.android.com/ndk/guides/cmake
run: |
mkdir build
cd build
"$ANDROID_HOME/cmake/3.22.1/bin/cmake" "-DCMAKE_TOOLCHAIN_FILE=$ANDROID_HOME/ndk/26.0.10792818/build/cmake/android.toolchain.cmake" -DCMAKE_BUILD_TYPE=Release -DANDROID_ABI="arm64-v8a" -DANDROID_PLATFORM=android-24 -DQJS_BUILD_LIBC=ON ..
- name: Build android arm64
run: |
"$ANDROID_HOME/cmake/3.22.1/bin/cmake" --build build --target qjs
ls -lh build
ios:
runs-on: macos-latest
steps:
- uses: actions/checkout@v7
- name: configure
run: |
cmake -B build -GXcode -DQJS_BUILD_WERROR=ON -DCMAKE_SYSTEM_NAME:STRING=iOS -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED:BOOL=NO -DQJS_BUILD_LIBC=ON
- name: build
run: |
cmake --build build --config Release --target qjs
ls -lh build
mimalloc-linux:
runs-on: ubuntu-24.04
env:
QJS_BUILD_CLI_WITH_MIMALLOC: ON
MIMALLOC_SHOW_STATS: 1
steps:
- uses: actions/checkout@v7
- name: install dependencies
run: |
sudo apt update && sudo apt -y install libmimalloc-dev
- name: build
run: |
make
- name: cxxtest
run: |
make cxxtest
- name: test
run: |
make test
mimalloc-macos:
runs-on: macos-latest
env:
QJS_BUILD_CLI_WITH_STATIC_MIMALLOC: ON
MIMALLOC_SHOW_STATS: 1
steps:
- uses: actions/checkout@v7
- name: install dependencies
run: |
brew install mimalloc
- name: build
run: |
make
- name: cxxtest
run: |
make cxxtest
- name: test
run: |
make test
amalgam:
strategy:
matrix:
# TODO(bnoordhuis) test on windows
config:
- { os: ubuntu-latest }
- { os: macos-latest }
runs-on: ${{ matrix.config.os }}
steps:
- uses: actions/checkout@v7
- name: build
run: |
make
- name: create amalgamation
run: |
make amalgam
- name: build amalgamation
run: |
unzip -d "$RUNNER_TEMP" build/quickjs-amalgam.zip
cc -Wall -I. -o "$RUNNER_TEMP/run-test262.o" -c run-test262.c
cc -Wall -I/ -DQJS_BUILD_LIBC -o "$RUNNER_TEMP/quickjs-amalgam.o" -c "$RUNNER_TEMP/quickjs-amalgam.c"
cc -o "$RUNNER_TEMP/run-test262" "$RUNNER_TEMP/run-test262.o" "$RUNNER_TEMP/quickjs-amalgam.o" -lm
- name: test
run: |
make test RUN262="$RUNNER_TEMP/run-test262"
jscheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: jscheck
run: |
make jscheck
parserless:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: build
run: |
make QJS_DISABLE_PARSER=ON QJS_BUILD_EXAMPLES=ON
- name: test
run: |
./build/hello
./build/hello_module
meson:
runs-on: ${{ matrix.platform }}
name: meson on ${{ matrix.platform }} (${{ matrix.mode.name }} ${{ matrix.flavor }}, ${{ matrix.features.name }})
strategy:
fail-fast: false
matrix:
flavor:
- debug
- release
features:
- name: default
args: ""
- name: libc
args: -Dlibc=true
- name: mimalloc
args: -Dcli_mimalloc=auto
mode:
- name: default
args: -Dtests=enabled
extra_envs: {}
# Alternative compiler setups
- name: gcc
args: -Dtests=enabled
extra_envs:
CC: gcc
CXX: g++
- name: clang
args: -Dtests=enabled
extra_envs:
CC: clang
CXX: clang++
- name: sanitize
args: >-
"-Db_sanitize=address,undefined"
extra_envs: {}
# This is for MSVC, which only supports AddressSanitizer.
# https://learn.microsoft.com/en-us/cpp/sanitizers/
- name: sanitize+asanonly
args: -Db_sanitize=address
extra_envs:
ASAN_OPTIONS: report_globals=0:halt_on_error=1:abort_on_error=1:print_summary=1
- name: clang+sanitize
args: >-
"-Db_sanitize=address,undefined"
"-Db_lundef=false"
extra_envs:
CC: clang
CXX: clang++
- name: clang+msan
args: >-
"-Db_sanitize=memory"
"-Db_lundef=false"
extra_envs:
CC: clang
CXX: clang++
# default clang on GitHub hosted runners is from MSYS2.
# Use Visual Studio supplied clang-cl instead.
# clang-cl's AddressSanitizer does not support the debug CRT (/MDd),
# so force the release CRT (/MD) for both debug and release builds.
- name: clang-cl+sanitize
args: >-
"-Db_sanitize=address,undefined"
"-Db_lundef=false"
"-Db_vscrt=md"
extra_envs:
CC: clang-cl
CXX: clang-cl
# clang-cl's AddressSanitizer reports false positive
# global-buffer-overflows on pooled string literals on Windows,
# same as MSVC; disable global checks (covered on Linux).
ASAN_OPTIONS: report_globals=0:halt_on_error=1:abort_on_error=1:print_summary=1
platform:
- ubuntu-latest
- windows-latest
- macos-latest
exclude:
# clang-cl only makes sense on windows.
- platform: ubuntu-latest
mode:
name: clang-cl+sanitize
- platform: macos-latest
mode:
name: clang-cl+sanitize
# Use clang-cl instead of MSYS2 clang.
#
# we already tested clang+sanitize on linux,
# if this doesn't work, it should be an issue for MSYS2 team to consider.
- platform: windows-latest
mode:
name: clang
- platform: windows-latest
mode:
name: clang+sanitize
- platform: windows-latest
mode:
name: clang+msan
# MSVC-only sanitizers
- platform: ubuntu-latest
mode:
name: sanitize+asanonly
- platform: macos-latest
mode:
name: sanitize+asanonly
- platform: windows-latest
mode:
name: sanitize
# clang is the default on macos
# also gcc is an alias to clang
- platform: macos-latest
mode:
name: clang
- platform: macos-latest
mode:
name: gcc
# gcc is the default on linux
- platform: ubuntu-latest
mode:
name: gcc
# only run sanitizer tests on linux
#
# gcc/clang's codegen shouldn't massively change across platforms,
# and linux supports most of the sanitizers.
- platform: macos-latest
mode:
name: clang+sanitize
- platform: macos-latest
mode:
# macos does not support msan
name: clang+msan
- platform: macos-latest
mode:
name: sanitize
steps:
- name: Setup meson
run: |
pipx install meson ninja
- name: Install mimalloc
if: ${{ matrix.platform == 'ubuntu-latest' && matrix.features.name == 'mimalloc' }}
run: sudo apt update && sudo apt -y install libmimalloc-dev
- name: Install mimalloc
if: ${{ matrix.platform == 'macos-latest' && matrix.features.name == 'mimalloc' }}
run: brew install mimalloc
# TODO: Install mimalloc on Windows
# You need to:
# - checkout mimalloc
# - `msbuild mimalloc\ide\vs2022\mimalloc.sln`
# - not cmake, because https://github.com/microsoft/mimalloc/issues/575#issuecomment-1112723975
# - it is possible to integrate vcpkg with meson, but:
# - 1. the above issue
# - 2. the vcpkg port of mimalloc is outdated, and still broken on server 2019
# - or maintain a meson port for mimalloc (and ensure it behaves correctly)
# - Make it findable with cmake
# - not the simpler pkg-config. although there was pkg-config in PATH,
# but that was from strawberry perl and thus broken
# ... good job, microsoft :))))
- name: Checkout
uses: actions/checkout@v7
- name: Activate MSVC and Configure
if: ${{ matrix.platform == 'windows-latest' }}
env: ${{ matrix.mode.extra_envs }}
run: |
$extraArgs = @()
if ($env:CC -eq 'clang-cl') {
# Newer clang-cl no longer emits the asan runtime /defaultlib directives
# when -fsanitize is passed through /clang:, and meson links with lld-link
# directly (not the clang driver), so the asan/ubsan runtime is never pulled
# in. Link it explicitly and make its directory discoverable at link and run
# time (the dynamic runtime ships as a DLL).
$runtimeDir = (clang -print-runtime-dir).Trim()
$env:LIB = "$runtimeDir;$env:LIB"
"LIB=$env:LIB" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
$runtimeDir | Out-File -FilePath $env:GITHUB_PATH -Append -Encoding utf8
$libs = "clang_rt.asan_dynamic-x86_64.lib clang_rt.asan_dynamic_runtime_thunk-x86_64.lib"
$extraArgs = @("-Dc_link_args=$libs", "-Dcpp_link_args=$libs")
}
meson setup build-${{ matrix.flavor }} --buildtype=${{ matrix.flavor }} ${{ matrix.mode.args }} ${{ matrix.features.args }} --vsenv @extraArgs
- name: Configuring
if: ${{ matrix.platform != 'windows-latest' }}
env: ${{ matrix.mode.extra_envs }}
run: |
meson setup build-${{ matrix.flavor }} --buildtype=${{ matrix.flavor }} ${{ matrix.mode.args }} ${{ matrix.features.args }}
- name: Building
run: |
meson compile -C build-${{ matrix.flavor }}
- name: Running tests
env: ${{ matrix.mode.extra_envs }}
run: |
meson test -C build-${{ matrix.flavor }} --timeout-multiplier 5 --print-errorlogs
meson test --benchmark -C build-${{ matrix.flavor }} --timeout-multiplier 5 --print-errorlogs
- uses: actions/upload-artifact@v7
if: ${{ failure() }}
with:
name: ${{ matrix.platform }}-${{ matrix.mode.name }}-${{ matrix.features.name }}-${{ matrix.flavor }}-logs
path: build-${{ matrix.flavor }}/meson-logs
+49
View File
@@ -0,0 +1,49 @@
name: Docs
on:
push:
branches:
- master
paths:
- 'docs/**'
- '.github/workflows/*docs.yml'
jobs:
build:
name: Build Docusaurus
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version-file: 'docs/.nvmrc'
- name: Install dependencies
working-directory: ./docs
run: npm install
- name: Build
working-directory: ./docs
run: npm run build
- name: Upload Build Artifact
uses: actions/upload-pages-artifact@v5
with:
path: docs/build
deploy:
name: Deploy to GitHub Pages
needs: build
# Grant GITHUB_TOKEN the permissions required to make a Pages deployment
permissions:
pages: write # to deploy to Pages
id-token: write # to verify the deployment originates from an appropriate source
# Deploy to the github-pages environment
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v5
+61
View File
@@ -0,0 +1,61 @@
name: gc-stress
on:
pull_request:
paths:
- '**'
- '!.gitignore'
- '!LICENSE'
- '!README.md'
- '!docs/**'
- '!examples/**'
- '.github/workflows/gc-stress.yml'
push:
branches:
- master
paths:
- '**'
- '!.gitignore'
- '!LICENSE'
- '!README.md'
- '!docs/**'
- '!examples/**'
- '.github/workflows/gc-stress.yml'
jobs:
gc-stress:
runs-on: ${{ matrix.os }}
name: ${{ matrix.os }}
timeout-minutes: 30
defaults:
run:
shell: ${{ matrix.os == 'windows-latest' && 'msys2 {0}' || 'bash' }}
env:
ASAN_OPTIONS: halt_on_error=1:detect_leaks=0
strategy:
fail-fast: false
matrix:
include:
- { os: ubuntu-latest, asan: 'ON' }
- { os: macos-latest, asan: 'ON' }
- { os: windows-latest, asan: 'OFF' }
steps:
- uses: actions/checkout@v7
- name: Setup MSYS2
if: matrix.os == 'windows-latest'
uses: msys2/setup-msys2@v2
with:
install: >-
git
make
pacboy: >-
cmake:p
ninja:p
toolchain:p
- name: build & test
run: >-
make test
BUILD_TYPE=RelWithDebInfo
QJS_ENABLE_GC_STRESS=ON
QJS_ENABLE_ASAN=${{ matrix.asan }}
RUN262='build/run-test262 -x gc-stress-skip.txt'
+215
View File
@@ -0,0 +1,215 @@
name: release
on:
pull_request:
push:
tags:
- "v*.*.*"
jobs:
check_meson_version:
runs-on: ubuntu-22.04
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
steps:
- uses: actions/checkout@v7
- name: install meson and ninja
run: |
pip install --upgrade meson ninja
- name: check meson version
run: |
MESON_VERSION=$(meson introspect meson.build --projectinfo | jq -r '.version')
test "${{ github.ref_name }}" = "v${MESON_VERSION}"
linux:
runs-on: ubuntu-22.04
strategy:
fail-fast: false
matrix:
arch: [aarch64, armv7, riscv64, x86, x86_64]
steps:
- uses: actions/checkout@v7
- uses: quickjs-ng/setup-alpine@master
with:
# mimalloc v3 (mimalloc3-dev) lives in the edge testing repo; enable it.
# riscv64 has no v3 build there, so it falls back to mimalloc2-dev.
branch: edge
arch: ${{matrix.arch}}
extra-repositories: |
http://dl-cdn.alpinelinux.org/alpine/edge/testing
packages: "build-base make cmake ${{ matrix.arch == 'riscv64' && 'mimalloc2-dev' || 'mimalloc3-dev' }}"
apk-tools-url: https://raw.githubusercontent.com/quickjs-ng/apk-for-ci/refs/heads/master/apk.static#!sha256!34bb1a96f0258982377a289392d4ea9f3f4b767a4bb5806b1b87179b79ad8a1c
- name: build
shell: alpine.sh {0}
run: |
mkdir build
cd build
cmake -DQJS_BUILD_WERROR=ON -DQJS_BUILD_CLI_STATIC=ON -DQJS_BUILD_CLI_WITH_STATIC_MIMALLOC=ON ..
cd ..
cmake --build build --target qjs_exe -j$(getconf _NPROCESSORS_ONLN)
cmake --build build --target qjsc -j$(getconf _NPROCESSORS_ONLN)
mv build/qjs build/qjs-linux-${{matrix.arch}}
mv build/qjsc build/qjsc-linux-${{matrix.arch}}
- name: check
shell: alpine.sh {0}
run: |
file build/*-linux-${{matrix.arch}}
- name: upload
uses: actions/upload-artifact@v7
with:
name: qjs-linux-${{matrix.arch}}
path: build/*-linux-${{matrix.arch}}
macos:
runs-on: macos-latest
strategy:
fail-fast: false
matrix:
arch: [arm64, x86_64]
steps:
- uses: actions/checkout@v7
- name: install mimalloc
if: ${{ matrix.arch == 'arm64' }}
run: brew install mimalloc
- name: build
run: |
mkdir build
cd build
cmake -DCMAKE_OSX_ARCHITECTURES=${{matrix.arch}} -DQJS_BUILD_WERROR=ON ${{ matrix.arch == 'arm64' && '-DQJS_BUILD_CLI_WITH_STATIC_MIMALLOC=ON -DCMAKE_PREFIX_PATH=/opt/homebrew' || '' }} ..
make -j$(getconf _NPROCESSORS_ONLN)
- name: build amalgamation
if: ${{ matrix.arch == 'arm64' }}
run: make amalgam # writes build/quickjs-amalgam.zip; runs qjs natively on arm64
- name: rename
run: |
mv build/qjs build/qjs-darwin-${{matrix.arch}}
mv build/qjsc build/qjsc-darwin-${{matrix.arch}}
- name: check
run: |
lipo -info build/qjs-darwin-${{matrix.arch}} build/qjsc-darwin-${{matrix.arch}}
- name: upload amalgamation
if: ${{ matrix.arch == 'arm64' }}
uses: actions/upload-artifact@v7
with:
name: quickjs-amalgam.zip
path: build/quickjs-amalgam.zip
compression-level: 0 # already compressed
- name: upload
uses: actions/upload-artifact@v7
with:
name: qjs-darwin-${{matrix.arch}}
path: build/*-darwin-${{matrix.arch}}
windows:
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
arch: [x86, x86_64]
defaults:
run:
shell: msys2 {0}
steps:
- uses: actions/checkout@v7
- name: Setup MSYS2
uses: msys2/setup-msys2@v2
with:
msystem: ${{ matrix.arch == 'x86' && 'mingw32' || 'ucrt64' }}
install: >-
git
make
pacboy: >-
cmake:p
ninja:p
toolchain:p
${{ matrix.arch == 'x86_64' && 'mimalloc:p' || '' }}
- name: build
env:
QJS_BUILD_CLI_WITH_STATIC_MIMALLOC: ${{ matrix.arch == 'x86_64' && 'ON' || 'OFF' }}
run: |
make
mv build/qjs.exe build/qjs-windows-${{matrix.arch}}.exe
mv build/qjsc.exe build/qjsc-windows-${{matrix.arch}}.exe
- name: check
run: |
file build/qjs-windows-${{matrix.arch}}.exe
ldd build/qjs-windows-${{matrix.arch}}.exe build/qjsc-windows-${{matrix.arch}}.exe
- name: upload
uses: actions/upload-artifact@v7
with:
name: qjs-windows-${{matrix.arch}}
path: build/*-windows-${{matrix.arch}}.exe
wasi:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v7
- name: setup wasi-sdk
run: |
wget -nv https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-29/wasi-sdk-29.0-x86_64-linux.deb -P "$RUNNER_TEMP"
sudo apt install "$RUNNER_TEMP/wasi-sdk-29.0-x86_64-linux.deb"
- name: build
run: |
cmake -B build -DCMAKE_TOOLCHAIN_FILE=/opt/wasi-sdk/share/cmake/wasi-sdk.cmake -DQJS_BUILD_WERROR=ON
make -C build qjs_exe
mv build/qjs build/qjs-wasi.wasm
- name: build wasi reactor
run: |
cmake -B build -DCMAKE_TOOLCHAIN_FILE=/opt/wasi-sdk/share/cmake/wasi-sdk.cmake -DQJS_BUILD_WERROR=ON -DQJS_WASI_REACTOR=ON
make -C build qjs_wasi
mv build/qjs.wasm build/qjs-wasi-reactor.wasm
- name: upload
uses: actions/upload-artifact@v7
with:
name: qjs-wasi
path: |
build/qjs-wasi.wasm
build/qjs-wasi-reactor.wasm
cosmopolitan:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v7
- name: setup cosmocc
run: |
wget -nv https://github.com/jart/cosmopolitan/releases/download/4.0.2/cosmocc-4.0.2.zip -P "$RUNNER_TEMP"
mkdir -p "$RUNNER_TEMP/cosmocc"
unzip -q "$RUNNER_TEMP/cosmocc-4.0.2.zip" -d "$RUNNER_TEMP/cosmocc"
echo "$RUNNER_TEMP/cosmocc/bin" >> "$GITHUB_PATH"
- name: setup ape loader
run: |
sudo cp "$RUNNER_TEMP/cosmocc/bin/ape-x86_64.elf" /usr/bin/ape
sudo sh -c "echo ':APE:M::MZqFpD::/usr/bin/ape:' > /proc/sys/fs/binfmt_misc/register"
sudo sh -c "echo ':APE-jart:M::jartsr::/usr/bin/ape:' > /proc/sys/fs/binfmt_misc/register"
- name: build
run: |
cmake -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=cosmocc -DCMAKE_AR="$(command -v cosmoar)" -DQJS_BUILD_CLI_STATIC=ON
cmake --build build --target qjs_exe -j$(getconf _NPROCESSORS_ONLN)
cmake --build build --target qjsc -j$(getconf _NPROCESSORS_ONLN)
mv build/qjs build/qjs-cosmo.com
mv build/qjsc build/qjsc-cosmo.com
- name: check
run: |
file build/*-cosmo.com
build/qjs-cosmo.com -qd
- name: upload
uses: actions/upload-artifact@v7
with:
name: qjs-cosmo
path: build/*-cosmo.com
upload-to-release:
needs: [linux, macos, windows, wasi, cosmopolitan, check_meson_version]
runs-on: ubuntu-22.04
steps:
- name: get assets
uses: actions/download-artifact@v8
with:
path: build
merge-multiple: true
- run: ls -R build
- name: release
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
uses: softprops/action-gh-release@v3
with:
files: |
build/*
+23
View File
@@ -0,0 +1,23 @@
name: Test Docs
on:
pull_request:
paths:
- 'docs/**'
- '.github/workflows/*docs.yml'
jobs:
test-docs:
name: Test Docusaurus build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version-file: 'docs/.nvmrc'
- name: Install dependencies
working-directory: ./docs
run: npm install
- name: Build
working-directory: ./docs
run: npm run build
+32
View File
@@ -0,0 +1,32 @@
name: tsan
on:
push:
branches:
- master
paths:
- '**'
- '!.gitignore'
- '!LICENSE'
- '!README.md'
- '!docs/**'
- '!examples/**'
- '.github/workflows/tsan.yml'
- '.github/workflows/valgrind.yml'
workflow_dispatch:
jobs:
linux:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
submodules: true
- name: build
run: |
cmake -B build -DCMAKE_BUILD_TYPE=RelWithDebInfo -DQJS_BUILD_WERROR=ON -DQJS_ENABLE_TSAN=ON
cmake --build build -j`nproc`
- name: test
run: |
git submodule update --init --checkout --depth 1
./build/run-test262 -m -c test262.conf -c test262-fast.conf -a
+33
View File
@@ -0,0 +1,33 @@
name: valgrind
on:
push:
branches:
- master
paths:
- '**'
- '!.gitignore'
- '!LICENSE'
- '!README.md'
- '!docs/**'
- '!examples/**'
- '.github/workflows/tsan.yml'
- '.github/workflows/valgrind.yml'
workflow_dispatch:
jobs:
linux:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
submodules: true
- name: install valgrind
run: sudo apt-get update && sudo apt-get install valgrind
- name: build
run: |
make BUILD_TYPE=RelWithDebInfo
- name: test
run: |
git submodule update --init --checkout --depth 1
valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes --error-exitcode=1 ./build/run-test262 -m -c test262.conf -c test262-fast.conf -a
+15
View File
@@ -0,0 +1,15 @@
*.a
*.orig
*.so
.obj/
build/
build-*/
unicode/
.idea
cmake-*
.vs
out/
CMakeUserPresets.json
fuzz
.vscode/
microbench*.txt

Some files were not shown because too many files have changed in this diff Show More