From 87097b26ce33d653483487ac6246a88847c2d740 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 2 Mar 2026 13:20:42 +0100 Subject: [PATCH 1/5] WASM function for building C++ projects --- Cargo.lock | 7 ++ Cargo.toml | 1 + flake.nix | 2 +- nix-wasm-plugin-cc/Cargo.toml | 10 +++ nix-wasm-plugin-cc/src/lib.rs | 152 ++++++++++++++++++++++++++++++++++ nix-wasm-rust/src/lib.rs | 2 +- 6 files changed, 172 insertions(+), 2 deletions(-) create mode 100644 nix-wasm-plugin-cc/Cargo.toml create mode 100644 nix-wasm-plugin-cc/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 6fbc873..5e2ca4a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -291,6 +291,13 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +[[package]] +name = "nix-wasm-plugin-cc" +version = "0.1.0" +dependencies = [ + "nix-wasm-rust", +] + [[package]] name = "nix-wasm-plugin-fib" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index bfb6449..7bcaa3b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ members = [ "nix-wasm-plugin-test", "nix-wasm-plugin-quickjs", "nix-wasm-plugin-grep", + "nix-wasm-plugin-cc", ] resolver = "2" diff --git a/flake.nix b/flake.nix index 29586fc..940951d 100644 --- a/flake.nix +++ b/flake.nix @@ -60,7 +60,7 @@ ''; workspaceVendor = rustPlatform.fetchCargoVendor { src = self; - hash = "sha256-vkTdv3StxslmBOKy8mFfz5afOiMjBujFd4IU6pkgqGc="; + hash = "sha256-1UaXTG9l1oRuvikyhArTi3iye+kjCCTDXOSaRYoldnM="; }; stdlibVendor = rustPlatform.fetchCargoVendor { src = rustPlatform.rustcSrc; diff --git a/nix-wasm-plugin-cc/Cargo.toml b/nix-wasm-plugin-cc/Cargo.toml new file mode 100644 index 0000000..17ea236 --- /dev/null +++ b/nix-wasm-plugin-cc/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "nix-wasm-plugin-cc" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +nix-wasm-rust = { path = "../nix-wasm-rust" } diff --git a/nix-wasm-plugin-cc/src/lib.rs b/nix-wasm-plugin-cc/src/lib.rs new file mode 100644 index 0000000..c4c58db --- /dev/null +++ b/nix-wasm-plugin-cc/src/lib.rs @@ -0,0 +1,152 @@ +use nix_wasm_rust::{warn, Value}; +use std::collections::{HashMap, HashSet}; + +struct File { + path: String, + includes: Vec, +} + +#[no_mangle] +pub extern "C" fn cc(args: Value) -> Value { + let builtins = args + .get_attr("builtins") + .expect("missing 'builtins' argument"); + let path = args.get_attr("path").expect("missing 'path' argument"); + + let read_dir = builtins.get_attr("readDir").unwrap(); + + // First pass: scan all .cc and .hh files, recording their direct includes. + let mut cc_files: Vec = vec![]; + let mut all_files: HashMap = HashMap::new(); + scan_files(&read_dir, &path, "", &mut cc_files, &mut all_files); + + // Build a suffix map for efficient include resolution. + // For a file "foo/bar/xyzzy/util.hh", this inserts: + // "util.hh" -> "foo/bar/xyzzy/util.hh" + // "xyzzy/util.hh" -> "foo/bar/xyzzy/util.hh" + // "bar/xyzzy/util.hh" -> "foo/bar/xyzzy/util.hh" + // "foo/bar/xyzzy/util.hh" -> "foo/bar/xyzzy/util.hh" + let mut suffix_map: HashMap = HashMap::new(); + for (value, file) in &all_files { + let rel_path = &file.path; + let parts: Vec<&str> = rel_path.split('/').collect(); + for i in 0..parts.len() { + let suffix = parts[i..].join("/"); + suffix_map.entry(suffix).or_insert_with(|| *value); + } + } + + // Second pass: for each .cc file, compute the transitive closure of includes. + let mut results = vec![]; + for cc_file_val in &cc_files { + //warn!("processing {path}...", path = all_files[cc_file_val].path); + let mut all_includes: HashMap = HashMap::new(); + let mut visited = HashSet::new(); + collect_transitive_includes( + *cc_file_val, + &all_files, + &suffix_map, + &mut all_includes, + &mut visited, + ); + + let mut sorted_includes: Vec<_> = all_includes.iter().collect(); + sorted_includes.sort_by_key(|(include, _)| *include); + let include_values: Vec = sorted_includes + .iter() + .map(|(include, path_val)| { + Value::make_attrset(&[ + ("include", Value::make_string(include)), + ("path", **path_val), + ]) + }) + .collect(); + let entry = Value::make_attrset(&[ + ("file", *cc_file_val), + ("includes", Value::make_list(&include_values)), + ]); + results.push(entry); + } + + Value::make_list(&results) +} + +fn scan_files( + read_dir: &Value, + path_val: &Value, + prefix: &str, + cc_files: &mut Vec, + all_files: &mut HashMap, +) { + for (name, file_type) in read_dir.call(&[*path_val]).get_attrset() { + let child = path_val.make_path(&name); + let path = if prefix.is_empty() { + name.clone() + } else { + format!("{prefix}/{name}") + }; + let file_type = file_type.get_string(); + match file_type.as_str() { + "regular" => { + if name.ends_with(".cc") || name.ends_with(".hh") { + let contents = child.read_file(); + let includes = extract_includes(&contents); + if name.ends_with(".cc") { + cc_files.push(child); + } + all_files.insert(child, File { path, includes }); + } + } + "directory" => { + scan_files(read_dir, &child, &path, cc_files, all_files); + } + _ => {} + } + } +} + +fn collect_transitive_includes( + file: Value, + all_files: &HashMap, + suffix_map: &HashMap, + all_includes: &mut HashMap, + visited: &mut HashSet, +) { + if !visited.insert(file) { + return; + } + + let file = &all_files[&file]; + + for inc in &file.includes { + if let Some(resolved) = suffix_map.get(inc) { + all_includes.entry(inc.clone()).or_insert(*resolved); + collect_transitive_includes(*resolved, all_files, suffix_map, all_includes, visited); + } else { + warn!("{file}: included file not found: {inc}", file = file.path); + } + } +} + +fn extract_includes(contents: &[u8]) -> Vec { + let Ok(text) = std::str::from_utf8(contents) else { + return vec![]; + }; + let mut includes = vec![]; + for line in text.lines() { + let trimmed = line.trim(); + if let Some(rest) = trimmed.strip_prefix('#') { + let rest = rest.trim_start(); + let Some(rest) = rest.strip_prefix("include") else { + continue; + }; + let rest = rest.trim(); + if let Some(path) = rest.strip_prefix('"') { + if let Some(path) = path.strip_suffix('"') { + includes.push(path.to_string()); + } + } + } + } + includes +} diff --git a/nix-wasm-rust/src/lib.rs b/nix-wasm-rust/src/lib.rs index 9d39be6..c0e0823 100644 --- a/nix-wasm-rust/src/lib.rs +++ b/nix-wasm-rust/src/lib.rs @@ -31,7 +31,7 @@ macro_rules! warn { // FIXME: use externref for Values? #[repr(transparent)] -#[derive(Clone, Debug, Copy)] +#[derive(Clone, Debug, Copy, Eq, PartialEq, Hash)] pub struct Value(ValueId); type ValueId = u32; From f056ca8acd00eb1a167fa80d9ab4cc4f7924f7ec Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 3 Mar 2026 17:55:06 +0100 Subject: [PATCH 2/5] C++ improvements --- nix-wasm-plugin-cc/demo.nix | 123 ++++++++++++++++++++++++++++++++++ nix-wasm-plugin-cc/src/lib.rs | 57 ++++++++++++---- 2 files changed, 166 insertions(+), 14 deletions(-) create mode 100644 nix-wasm-plugin-cc/demo.nix diff --git a/nix-wasm-plugin-cc/demo.nix b/nix-wasm-plugin-cc/demo.nix new file mode 100644 index 0000000..30a6394 --- /dev/null +++ b/nix-wasm-plugin-cc/demo.nix @@ -0,0 +1,123 @@ +with import {}; + +rec { + cc = builtins.wasm { + path = ../target/wasm32-unknown-unknown/release/nix_wasm_plugin_cc.wasm; + function = "cc"; + }; + +/* + nixSrc = builtins.fetchTree { + type = "git"; + url = "/home/eelco/Dev/nix"; + }; +*/ + + sources = cc { + inherit builtins; + dirs = [ + { root = /home/eelco/Dev/nix/src/libutil; + prefix = ""; + } + #{ root = /home/eelco/Dev/nix/src/libstore; + # prefix = "libstore"; + #} + ]; + files = { + "nix/store/config.hh" = builtins.toFile "config.hh" + '' + #pragma once + #define NIX_LOCAL_SYSTEM "x86_64-linux" + #define NIX_SUPPORT_ACL 1 + #define NIX_WITH_AWS_AUTH 1 + ''; + "util-config-private.hh" = builtins.toFile "util-config-private.hh" + '' + #pragma once + #define HAVE_LIBCPUID 1 + #define HAVE_POSIX_FALLOCATE 1 + ''; + "store-config-private.hh" = builtins.toFile "store-config-private.hh" + '' + #pragma once + #define CAN_LINK_SYMLINK 1 + #define DETERMINATE_NIX_VERSION "3.16.3" + #define HAVE_EMBEDDED_SANDBOX_SHELL 0 + #define HAVE_LCHOWN 1 + #define HAVE_POSIX_FALLOCATE 1 + #define HAVE_SECCOMP 1 + #define HAVE_STATVFS 1 + #undef IS_STATIC + #define LSOF "lsof" + #define NIX_CONF_DIR "/etc/nix" + #define NIX_DATA_DIR "/home/eelco/Dev/nix/outputs/out/share" + #define NIX_LOG_DIR "/nix/var/log/nix" + #define NIX_MAN_DIR "/home/eelco/Dev/nix/outputs/out/share/man" + #define NIX_PREFIX "/home/eelco/Dev/nix/outputs/out" + #define NIX_STATE_DIR "/nix/var/nix" + #define NIX_STORE_DIR "/nix/store" + #define NIX_USE_WASMTIME 1 + #define PACKAGE_VERSION "2.33.3" + #define SANDBOX_SHELL "/nix/store/cbwbz05v2iqhn2d1w118y1rw97cqimjf-busybox-1.36.1/bin/busybox" + ''; + "util-unix-config-private.hh" = builtins.toFile "util-unix-config-private.hh" + '' + #pragma once + #define HAVE_CLOSE_RANGE 1 + #define HAVE_DECL_AT_SYMLINK_NOFOLLOW 1 + #define HAVE_LUTIMES 1 + #define HAVE_PIPE2 1 + #define HAVE_STRSIGNAL 1 + #define HAVE_SYSCONF 1 + #define HAVE_UTIMENSAT 1 + ''; + }; + }; + + source = builtins.elemAt sources 0; + + compileCpp = source: runCommandCC + "${builtins.baseNameOf source.path}.o" + { + __structuredAttrs = true; + includes = source.includes; + srcPath = source.path; + src = source.src; + buildInputs = [ boost nlohmann_json libsodium libarchive brotli libcpuid libblake3 openssl ]; + } + '' + for name in "''${!includes[@]}"; do + mkdir -p "$(dirname "$name")" + ln -s "''${includes[$name]}" "$name" + done + + srcDir="$(dirname "$srcPath")" + mkdir -p "$srcDir" + ln -s "$src" "$srcPath" + + mkdir -p "$out/$srcDir" + # FIXME: figure out the -I flags automatically. + gcc -std=c++23 -O1 -c "$srcPath" -o "$out/$srcDir/$(basename "$srcPath").o" -I . -I include -I unix/include -I linux/include -I windows/include -I widecharwidth + ''; + + all = map compileCpp sources; + + compile1 = compileCpp source; + + compile_ = map compileCpp (builtins.filter (x: x.path == "unix/processes.cc") sources); + + link = name: objects: runCommandCC + name + { + inherit objects; + buildInputs = [ boost libarchive openssl libsodium libblake3 brotli libcpuid ]; + } + '' + mkdir -p $out/lib + g++ -o $out/lib/$name.so \ + $(find $objects -name '*.o' -type f) \ + -lboost_context -lboost_iostreams -lboost_url -larchive -lcrypto -lsodium -lblake3 -lbrotlicommon -lbrotlienc -lbrotlidec -lcpuid -shared + ''; + + libutil = link "libnixutil.so" (map compileCpp sources); +} diff --git a/nix-wasm-plugin-cc/src/lib.rs b/nix-wasm-plugin-cc/src/lib.rs index c4c58db..30a6840 100644 --- a/nix-wasm-plugin-cc/src/lib.rs +++ b/nix-wasm-plugin-cc/src/lib.rs @@ -11,14 +11,40 @@ pub extern "C" fn cc(args: Value) -> Value { let builtins = args .get_attr("builtins") .expect("missing 'builtins' argument"); - let path = args.get_attr("path").expect("missing 'path' argument"); + let dirs = args.get_attr("dirs").expect("missing 'dirs' argument"); let read_dir = builtins.get_attr("readDir").unwrap(); // First pass: scan all .cc and .hh files, recording their direct includes. let mut cc_files: Vec = vec![]; let mut all_files: HashMap = HashMap::new(); - scan_files(&read_dir, &path, "", &mut cc_files, &mut all_files); + for entry in dirs.get_list() { + let root = entry.get_attr("root").expect("missing 'root' attribute"); + let prefix = entry + .get_attr("prefix") + .expect("missing 'prefix' attribute") + .get_string(); + scan_files(&read_dir, &root, &prefix, &mut cc_files, &mut all_files); + } + + // Process explicit files: each key is a path (possibly with slashes), + // the value is the file's path value. + if let Some(files) = args.get_attr("files") { + for (name, file_val) in files.get_attrset() { + let contents = file_val.read_file(); + let includes = extract_includes(&contents); + if name.ends_with(".cc") { + cc_files.push(file_val); + } + all_files.insert( + file_val, + File { + path: name, + includes, + }, + ); + } + } // Build a suffix map for efficient include resolution. // For a file "foo/bar/xyzzy/util.hh", this inserts: @@ -40,6 +66,7 @@ pub extern "C" fn cc(args: Value) -> Value { let mut results = vec![]; for cc_file_val in &cc_files { //warn!("processing {path}...", path = all_files[cc_file_val].path); + let cc_file = &all_files[cc_file_val]; let mut all_includes: HashMap = HashMap::new(); let mut visited = HashSet::new(); collect_transitive_includes( @@ -50,20 +77,17 @@ pub extern "C" fn cc(args: Value) -> Value { &mut visited, ); - let mut sorted_includes: Vec<_> = all_includes.iter().collect(); - sorted_includes.sort_by_key(|(include, _)| *include); - let include_values: Vec = sorted_includes - .iter() - .map(|(include, path_val)| { - Value::make_attrset(&[ - ("include", Value::make_string(include)), - ("path", **path_val), - ]) + let include_attrs: Vec<(&str, Value)> = all_includes + .values() + .map(|path_val| { + let inc_file = &all_files[path_val]; + (inc_file.path.as_str(), *path_val) }) .collect(); let entry = Value::make_attrset(&[ - ("file", *cc_file_val), - ("includes", Value::make_list(&include_values)), + ("src", *cc_file_val), + ("path", Value::make_string(&cc_file.path)), + ("includes", Value::make_attrset(&include_attrs)), ]); results.push(entry); } @@ -88,7 +112,12 @@ fn scan_files( let file_type = file_type.get_string(); match file_type.as_str() { "regular" => { - if name.ends_with(".cc") || name.ends_with(".hh") { + if name.ends_with(".cc") + || name.ends_with(".hh") + || name.ends_with(".h") + || name.ends_with(".sb") + || name.ends_with(".md") + { let contents = child.read_file(); let includes = extract_includes(&contents); if name.ends_with(".cc") { From 5cf31113c5e2723daecc1643a6e515ff2668b131 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 3 Mar 2026 18:40:58 +0100 Subject: [PATCH 3/5] Checkpoint --- nix-wasm-plugin-cc/demo.nix | 6 +-- nix-wasm-plugin-cc/src/lib.rs | 73 +++++++++++++++++++---------------- 2 files changed, 42 insertions(+), 37 deletions(-) diff --git a/nix-wasm-plugin-cc/demo.nix b/nix-wasm-plugin-cc/demo.nix index 30a6394..90296e5 100644 --- a/nix-wasm-plugin-cc/demo.nix +++ b/nix-wasm-plugin-cc/demo.nix @@ -20,7 +20,7 @@ rec { prefix = ""; } #{ root = /home/eelco/Dev/nix/src/libstore; - # prefix = "libstore"; + # prefix = ""; #} ]; files = { @@ -37,7 +37,7 @@ rec { #define HAVE_LIBCPUID 1 #define HAVE_POSIX_FALLOCATE 1 ''; - "store-config-private.hh" = builtins.toFile "store-config-private.hh" + "store-config-private.hh" = pkgs.writeText "store-config-private.hh" '' #pragma once #define CAN_LINK_SYMLINK 1 @@ -58,7 +58,7 @@ rec { #define NIX_STORE_DIR "/nix/store" #define NIX_USE_WASMTIME 1 #define PACKAGE_VERSION "2.33.3" - #define SANDBOX_SHELL "/nix/store/cbwbz05v2iqhn2d1w118y1rw97cqimjf-busybox-1.36.1/bin/busybox" + #define SANDBOX_SHELL "${pkgs.busybox}/bin/busybox" ''; "util-unix-config-private.hh" = builtins.toFile "util-unix-config-private.hh" '' diff --git a/nix-wasm-plugin-cc/src/lib.rs b/nix-wasm-plugin-cc/src/lib.rs index 30a6840..9862bbe 100644 --- a/nix-wasm-plugin-cc/src/lib.rs +++ b/nix-wasm-plugin-cc/src/lib.rs @@ -1,9 +1,10 @@ use nix_wasm_rust::{warn, Value}; use std::collections::{HashMap, HashSet}; -struct File { +struct FileInfo { path: String, - includes: Vec, + sys_includes: Vec, + user_includes: Vec, } #[no_mangle] @@ -16,15 +17,14 @@ pub extern "C" fn cc(args: Value) -> Value { let read_dir = builtins.get_attr("readDir").unwrap(); // First pass: scan all .cc and .hh files, recording their direct includes. - let mut cc_files: Vec = vec![]; - let mut all_files: HashMap = HashMap::new(); + let mut all_files: HashMap = HashMap::new(); for entry in dirs.get_list() { let root = entry.get_attr("root").expect("missing 'root' attribute"); let prefix = entry .get_attr("prefix") .expect("missing 'prefix' attribute") .get_string(); - scan_files(&read_dir, &root, &prefix, &mut cc_files, &mut all_files); + scan_files(&read_dir, &root, &prefix, &mut all_files); } // Process explicit files: each key is a path (possibly with slashes), @@ -32,17 +32,8 @@ pub extern "C" fn cc(args: Value) -> Value { if let Some(files) = args.get_attr("files") { for (name, file_val) in files.get_attrset() { let contents = file_val.read_file(); - let includes = extract_includes(&contents); - if name.ends_with(".cc") { - cc_files.push(file_val); - } - all_files.insert( - file_val, - File { - path: name, - includes, - }, - ); + let file_info = extract_includes(name, &contents); + all_files.insert(file_val, file_info); } } @@ -64,9 +55,11 @@ pub extern "C" fn cc(args: Value) -> Value { // Second pass: for each .cc file, compute the transitive closure of includes. let mut results = vec![]; - for cc_file_val in &cc_files { + for (cc_file_val, cc_file) in all_files.iter() { + if !cc_file.path.ends_with(".cc") { + continue; + } //warn!("processing {path}...", path = all_files[cc_file_val].path); - let cc_file = &all_files[cc_file_val]; let mut all_includes: HashMap = HashMap::new(); let mut visited = HashSet::new(); collect_transitive_includes( @@ -99,10 +92,12 @@ fn scan_files( read_dir: &Value, path_val: &Value, prefix: &str, - cc_files: &mut Vec, - all_files: &mut HashMap, + all_files: &mut HashMap, ) { for (name, file_type) in read_dir.call(&[*path_val]).get_attrset() { + if name == "windows" { + continue; + } // FIXME: hack let child = path_val.make_path(&name); let path = if prefix.is_empty() { name.clone() @@ -119,15 +114,12 @@ fn scan_files( || name.ends_with(".md") { let contents = child.read_file(); - let includes = extract_includes(&contents); - if name.ends_with(".cc") { - cc_files.push(child); - } - all_files.insert(child, File { path, includes }); + let file_info = extract_includes(path, &contents); + all_files.insert(child, file_info); } } "directory" => { - scan_files(read_dir, &child, &path, cc_files, all_files); + scan_files(read_dir, &child, &path, all_files); } _ => {} } @@ -136,7 +128,7 @@ fn scan_files( fn collect_transitive_includes( file: Value, - all_files: &HashMap, + all_files: &HashMap, suffix_map: &HashMap, all_includes: &mut HashMap, visited: &mut HashSet, @@ -147,21 +139,29 @@ fn collect_transitive_includes( let file = &all_files[&file]; - for inc in &file.includes { + for inc in &file.user_includes { if let Some(resolved) = suffix_map.get(inc) { all_includes.entry(inc.clone()).or_insert(*resolved); collect_transitive_includes(*resolved, all_files, suffix_map, all_includes, visited); } else { - warn!("{file}: included file not found: {inc}", file = file.path); + // FIXME: hack + if !inc.contains("windows") { + warn!("{file}: included file not found: {inc}", file = file.path); + } } } } -fn extract_includes(contents: &[u8]) -> Vec { +fn extract_includes(path: String, contents: &[u8]) -> FileInfo { + let mut file_info = FileInfo { + path, + sys_includes: vec![], + user_includes: vec![], + }; let Ok(text) = std::str::from_utf8(contents) else { - return vec![]; + return file_info; }; - let mut includes = vec![]; + // FIXME: process #ifdefs so we can skip #includes that don't apply for line in text.lines() { let trimmed = line.trim(); if let Some(rest) = trimmed.strip_prefix('#') { @@ -172,10 +172,15 @@ fn extract_includes(contents: &[u8]) -> Vec { let rest = rest.trim(); if let Some(path) = rest.strip_prefix('"') { if let Some(path) = path.strip_suffix('"') { - includes.push(path.to_string()); + file_info.user_includes.push(path.to_string()); + } + } + if let Some(path) = rest.strip_prefix('<') { + if let Some(path) = path.strip_suffix('>') { + file_info.sys_includes.push(path.to_string()); } } } } - includes + file_info } From e15f74326769b58d8b5cf0fbd939e82c475bb5a1 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 3 Mar 2026 20:08:51 +0100 Subject: [PATCH 4/5] Auto-detect buildInputs --- nix-wasm-plugin-cc/demo.nix | 81 +++++++++++++++++++---------------- nix-wasm-plugin-cc/src/lib.rs | 41 +++++++++++++++++- 2 files changed, 85 insertions(+), 37 deletions(-) diff --git a/nix-wasm-plugin-cc/demo.nix b/nix-wasm-plugin-cc/demo.nix index 90296e5..873c358 100644 --- a/nix-wasm-plugin-cc/demo.nix +++ b/nix-wasm-plugin-cc/demo.nix @@ -6,6 +6,44 @@ rec { function = "cc"; }; + compileCpp = source: runCommandCC + "${builtins.baseNameOf source.path}.o" + { + __structuredAttrs = true; + includes = source.includes; + srcPath = source.path; + src = source.src; + buildInputs = map (dep: depsMap.${dep}) source.deps; + inherit (source) deps; + } + '' + for name in "''${!includes[@]}"; do + mkdir -p "$(dirname "$name")" + ln -s "''${includes[$name]}" "$name" + done + + srcDir="$(dirname "$srcPath")" + mkdir -p "$srcDir" + ln -s "$src" "$srcPath" + + mkdir -p "$out/$srcDir" + # FIXME: figure out the -I flags automatically. + gcc -std=c++23 -O1 -c "$srcPath" -o "$out/$srcDir/$(basename "$srcPath").o" -I . -I include -I unix/include -I linux/include -I windows/include -I widecharwidth + ''; + + link = name: objects: runCommandCC + name + { + inherit objects; + buildInputs = map (dep: depsMap.${dep}) (builtins.concatLists (map (obj: obj.deps) objects)); + } + '' + mkdir -p $out/lib + g++ -o $out/lib/$name.so \ + $(find $objects -name '*.o' -type f) \ + -lboost_context -lboost_iostreams -lboost_url -larchive -lcrypto -lsodium -lblake3 -lbrotlicommon -lbrotlienc -lbrotlidec -lcpuid -shared + ''; + /* nixSrc = builtins.fetchTree { type = "git"; @@ -76,29 +114,13 @@ rec { source = builtins.elemAt sources 0; - compileCpp = source: runCommandCC - "${builtins.baseNameOf source.path}.o" - { - __structuredAttrs = true; - includes = source.includes; - srcPath = source.path; - src = source.src; - buildInputs = [ boost nlohmann_json libsodium libarchive brotli libcpuid libblake3 openssl ]; - } - '' - for name in "''${!includes[@]}"; do - mkdir -p "$(dirname "$name")" - ln -s "''${includes[$name]}" "$name" - done - - srcDir="$(dirname "$srcPath")" - mkdir -p "$srcDir" - ln -s "$src" "$srcPath" - - mkdir -p "$out/$srcDir" - # FIXME: figure out the -I flags automatically. - gcc -std=c++23 -O1 -c "$srcPath" -o "$out/$srcDir/$(basename "$srcPath").o" -I . -I include -I unix/include -I linux/include -I windows/include -I widecharwidth - ''; + depsMap = pkgs // { + libcpuid = pkgs.runCommand "libcpuid" { inherit (pkgs) libcpuid; } + '' + # 12345 + ln -s $libcpuid $out + ''; + }; all = map compileCpp sources; @@ -106,18 +128,5 @@ rec { compile_ = map compileCpp (builtins.filter (x: x.path == "unix/processes.cc") sources); - link = name: objects: runCommandCC - name - { - inherit objects; - buildInputs = [ boost libarchive openssl libsodium libblake3 brotli libcpuid ]; - } - '' - mkdir -p $out/lib - g++ -o $out/lib/$name.so \ - $(find $objects -name '*.o' -type f) \ - -lboost_context -lboost_iostreams -lboost_url -larchive -lcrypto -lsodium -lblake3 -lbrotlicommon -lbrotlienc -lbrotlidec -lcpuid -shared - ''; - libutil = link "libnixutil.so" (map compileCpp sources); } diff --git a/nix-wasm-plugin-cc/src/lib.rs b/nix-wasm-plugin-cc/src/lib.rs index 9862bbe..d8dce76 100644 --- a/nix-wasm-plugin-cc/src/lib.rs +++ b/nix-wasm-plugin-cc/src/lib.rs @@ -61,12 +61,14 @@ pub extern "C" fn cc(args: Value) -> Value { } //warn!("processing {path}...", path = all_files[cc_file_val].path); let mut all_includes: HashMap = HashMap::new(); + let mut all_deps: HashSet = HashSet::new(); let mut visited = HashSet::new(); collect_transitive_includes( *cc_file_val, &all_files, &suffix_map, &mut all_includes, + &mut all_deps, &mut visited, ); @@ -81,6 +83,15 @@ pub extern "C" fn cc(args: Value) -> Value { ("src", *cc_file_val), ("path", Value::make_string(&cc_file.path)), ("includes", Value::make_attrset(&include_attrs)), + ( + "deps", + Value::make_list( + &all_deps + .iter() + .map(|s| Value::make_string(s)) + .collect::>(), + ), + ), ]); results.push(entry); } @@ -131,6 +142,7 @@ fn collect_transitive_includes( all_files: &HashMap, suffix_map: &HashMap, all_includes: &mut HashMap, + all_deps: &mut HashSet, visited: &mut HashSet, ) { if !visited.insert(file) { @@ -142,7 +154,14 @@ fn collect_transitive_includes( for inc in &file.user_includes { if let Some(resolved) = suffix_map.get(inc) { all_includes.entry(inc.clone()).or_insert(*resolved); - collect_transitive_includes(*resolved, all_files, suffix_map, all_includes, visited); + collect_transitive_includes( + *resolved, + all_files, + suffix_map, + all_includes, + all_deps, + visited, + ); } else { // FIXME: hack if !inc.contains("windows") { @@ -150,6 +169,26 @@ fn collect_transitive_includes( } } } + + for include in &file.sys_includes { + if include.starts_with("boost/") { + all_deps.insert("boost".to_string()); + } else if include.starts_with("brotli/") { + all_deps.insert("brotli".to_string()); + } else if include.starts_with("openssl/") { + all_deps.insert("openssl".to_string()); + } else if include == "archive.h" { + all_deps.insert("libarchive".to_string()); + } else if include == "sodium.h" { + all_deps.insert("libsodium".to_string()); + } else if include == "blake3.h" { + all_deps.insert("libblake3".to_string()); + } else if include.starts_with("nlohmann/json") { + all_deps.insert("nlohmann_json".to_string()); + } else if include.starts_with("libcpuid/") { + all_deps.insert("libcpuid".to_string()); + } + } } fn extract_includes(path: String, contents: &[u8]) -> FileInfo { From 9e5572d3d07ca5da1b7de24451ca513b7a1fe3b5 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 6 Mar 2026 22:17:33 +0100 Subject: [PATCH 5/5] Rename --- Cargo.lock | 14 +-- Cargo.toml | 2 +- flake.nix | 2 +- .../Cargo.toml | 2 +- .../demo.nix | 88 ++++++++++++++----- .../src/lib.rs | 2 +- 6 files changed, 79 insertions(+), 31 deletions(-) rename {nix-wasm-plugin-cc => nix-wasm-plugin-nix-make}/Cargo.toml (80%) rename {nix-wasm-plugin-cc => nix-wasm-plugin-nix-make}/demo.nix (59%) rename {nix-wasm-plugin-cc => nix-wasm-plugin-nix-make}/src/lib.rs (99%) diff --git a/Cargo.lock b/Cargo.lock index 5e2ca4a..14a69f0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -291,13 +291,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" -[[package]] -name = "nix-wasm-plugin-cc" -version = "0.1.0" -dependencies = [ - "nix-wasm-rust", -] - [[package]] name = "nix-wasm-plugin-fib" version = "0.1.0" @@ -336,6 +329,13 @@ dependencies = [ "num", ] +[[package]] +name = "nix-wasm-plugin-nix-make" +version = "0.1.0" +dependencies = [ + "nix-wasm-rust", +] + [[package]] name = "nix-wasm-plugin-quickjs" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 7bcaa3b..34e30ed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ members = [ "nix-wasm-plugin-test", "nix-wasm-plugin-quickjs", "nix-wasm-plugin-grep", - "nix-wasm-plugin-cc", + "nix-wasm-plugin-nix-make", ] resolver = "2" diff --git a/flake.nix b/flake.nix index 940951d..403a348 100644 --- a/flake.nix +++ b/flake.nix @@ -60,7 +60,7 @@ ''; workspaceVendor = rustPlatform.fetchCargoVendor { src = self; - hash = "sha256-1UaXTG9l1oRuvikyhArTi3iye+kjCCTDXOSaRYoldnM="; + hash = "sha256-7+qf/W+ZAPWWghAzF33RDBLwZUrA51USjkGujXBRF4U="; }; stdlibVendor = rustPlatform.fetchCargoVendor { src = rustPlatform.rustcSrc; diff --git a/nix-wasm-plugin-cc/Cargo.toml b/nix-wasm-plugin-nix-make/Cargo.toml similarity index 80% rename from nix-wasm-plugin-cc/Cargo.toml rename to nix-wasm-plugin-nix-make/Cargo.toml index 17ea236..394a1d3 100644 --- a/nix-wasm-plugin-cc/Cargo.toml +++ b/nix-wasm-plugin-nix-make/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "nix-wasm-plugin-cc" +name = "nix-wasm-plugin-nix-make" version = "0.1.0" edition = "2021" diff --git a/nix-wasm-plugin-cc/demo.nix b/nix-wasm-plugin-nix-make/demo.nix similarity index 59% rename from nix-wasm-plugin-cc/demo.nix rename to nix-wasm-plugin-nix-make/demo.nix index 873c358..5576f1c 100644 --- a/nix-wasm-plugin-cc/demo.nix +++ b/nix-wasm-plugin-nix-make/demo.nix @@ -1,9 +1,9 @@ with import {}; rec { - cc = builtins.wasm { - path = ../target/wasm32-unknown-unknown/release/nix_wasm_plugin_cc.wasm; - function = "cc"; + getDeps = builtins.wasm { + path = ../target/wasm32-unknown-unknown/release/nix_wasm_plugin_nix_make.wasm; + function = "getDeps"; }; compileCpp = source: runCommandCC @@ -13,7 +13,7 @@ rec { includes = source.includes; srcPath = source.path; src = source.src; - buildInputs = map (dep: depsMap.${dep}) source.deps; + buildInputs = map (dep: pkgs'.${dep}) source.deps; inherit (source) deps; } '' @@ -35,7 +35,7 @@ rec { name { inherit objects; - buildInputs = map (dep: depsMap.${dep}) (builtins.concatLists (map (obj: obj.deps) objects)); + buildInputs = map (dep: pkgs'.${dep}) (builtins.concatLists (map (obj: obj.deps) objects)); } '' mkdir -p $out/lib @@ -44,14 +44,7 @@ rec { -lboost_context -lboost_iostreams -lboost_url -larchive -lcrypto -lsodium -lblake3 -lbrotlicommon -lbrotlienc -lbrotlidec -lcpuid -shared ''; -/* - nixSrc = builtins.fetchTree { - type = "git"; - url = "/home/eelco/Dev/nix"; - }; -*/ - - sources = cc { + sources = getDeps { inherit builtins; dirs = [ { root = /home/eelco/Dev/nix/src/libutil; @@ -112,21 +105,76 @@ rec { }; }; - source = builtins.elemAt sources 0; + allSources = getDeps { + inherit builtins; + dirs = [ + { root = /home/eelco/Dev/nix/src; + prefix = ""; + } + #{ root = /home/eelco/Dev/nix/src/libstore; + # prefix = ""; + #} + ]; + files = { + "nix/store/config.hh" = builtins.toFile "config.hh" + '' + #pragma once + #define NIX_LOCAL_SYSTEM "x86_64-linux" + #define NIX_SUPPORT_ACL 1 + #define NIX_WITH_AWS_AUTH 1 + ''; + "util-config-private.hh" = builtins.toFile "util-config-private.hh" + '' + #pragma once + #define HAVE_LIBCPUID 1 + #define HAVE_POSIX_FALLOCATE 1 + ''; + "store-config-private.hh" = pkgs.writeText "store-config-private.hh" + '' + #pragma once + #define CAN_LINK_SYMLINK 1 + #define DETERMINATE_NIX_VERSION "3.16.3" + #define HAVE_EMBEDDED_SANDBOX_SHELL 0 + #define HAVE_LCHOWN 1 + #define HAVE_POSIX_FALLOCATE 1 + #define HAVE_SECCOMP 1 + #define HAVE_STATVFS 1 + #undef IS_STATIC + #define LSOF "lsof" + #define NIX_CONF_DIR "/etc/nix" + #define NIX_DATA_DIR "/home/eelco/Dev/nix/outputs/out/share" + #define NIX_LOG_DIR "/nix/var/log/nix" + #define NIX_MAN_DIR "/home/eelco/Dev/nix/outputs/out/share/man" + #define NIX_PREFIX "/home/eelco/Dev/nix/outputs/out" + #define NIX_STATE_DIR "/nix/var/nix" + #define NIX_STORE_DIR "/nix/store" + #define NIX_USE_WASMTIME 1 + #define PACKAGE_VERSION "2.33.3" + #define SANDBOX_SHELL "${pkgs.busybox}/bin/busybox" + ''; + "util-unix-config-private.hh" = builtins.toFile "util-unix-config-private.hh" + '' + #pragma once + #define HAVE_CLOSE_RANGE 1 + #define HAVE_DECL_AT_SYMLINK_NOFOLLOW 1 + #define HAVE_LUTIMES 1 + #define HAVE_PIPE2 1 + #define HAVE_STRSIGNAL 1 + #define HAVE_SYSCONF 1 + #define HAVE_UTIMENSAT 1 + ''; + }; + }; + - depsMap = pkgs // { + pkgs' = pkgs // { libcpuid = pkgs.runCommand "libcpuid" { inherit (pkgs) libcpuid; } '' - # 12345 ln -s $libcpuid $out ''; }; all = map compileCpp sources; - compile1 = compileCpp source; - - compile_ = map compileCpp (builtins.filter (x: x.path == "unix/processes.cc") sources); - libutil = link "libnixutil.so" (map compileCpp sources); } diff --git a/nix-wasm-plugin-cc/src/lib.rs b/nix-wasm-plugin-nix-make/src/lib.rs similarity index 99% rename from nix-wasm-plugin-cc/src/lib.rs rename to nix-wasm-plugin-nix-make/src/lib.rs index d8dce76..71613e4 100644 --- a/nix-wasm-plugin-cc/src/lib.rs +++ b/nix-wasm-plugin-nix-make/src/lib.rs @@ -8,7 +8,7 @@ struct FileInfo { } #[no_mangle] -pub extern "C" fn cc(args: Value) -> Value { +pub extern "C" fn getDeps(args: Value) -> Value { let builtins = args .get_attr("builtins") .expect("missing 'builtins' argument");