From 5d3f0832f604c51533e1130e0d7e030781b7c460 Mon Sep 17 00:00:00 2001 From: Matthew Suozzo Date: Sun, 11 Oct 2026 13:56:21 -0400 Subject: [PATCH 1/3] feat: support building for WASI Build the vendored libgit2 for `target_os = "wasi"` (tested on wasm32-wasip1; wasip2 is untested). There is no system libgit2 to link against, so WASI always builds the vendored copy. In git2, path conversions accept WASI's `OsStr`, and `Repository::open_ext` and `discover_path` join ceiling directories themselves, since std's `env::join_paths` is unsupported on WASI. WASI preview 1 lacks several things libgit2's unix build assumes: - mmap: build with `NO_MMAP`, so pack windows are read into memory. - threads: leave `GIT_THREADS` undefined, and reject the -threads targets, where git2's `Send` types could then race inside libgit2. - network connections: replace `streams/socket.c` with a stream that always fails, and reject the `https` and `ssh` features. - user and process identity: wasi/compat.c defines getuid and friends (uid 0, pid 1). wasi-libc has no pwd.h, so wasi/include/pwd.h provides one; libgit2 only consults it when setuid, so the home directory comes from $HOME. On wasm32, `size_t` is `unsigned long` but `SIZE_MAX == UINT_MAX`, so libgit2's `integer.h` passes a `size_t *` to `__builtin_uadd_overflow`. The types have the same width, so clang's error is downgraded for WASI as a stopgap until libgit2 uses the type-generic builtins (libgit2/libgit2#7406). Add a CI job that builds for wasm32-wasip1 with wasi-sdk. --- .github/workflows/main.yml | 31 ++++++++++++++++ README.md | 14 ++++++++ libgit2-sys/build.rs | 61 +++++++++++++++++++++++++++++--- libgit2-sys/wasi/compat.c | 64 ++++++++++++++++++++++++++++++++++ libgit2-sys/wasi/compat.h | 19 ++++++++++ libgit2-sys/wasi/include/pwd.h | 31 ++++++++++++++++ src/repo.rs | 5 ++- src/util.rs | 40 ++++++++++++++++++--- 8 files changed, 253 insertions(+), 12 deletions(-) create mode 100644 libgit2-sys/wasi/compat.c create mode 100644 libgit2-sys/wasi/compat.h create mode 100644 libgit2-sys/wasi/include/pwd.h diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 50125f30ed..8f70eb1eaa 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -75,6 +75,36 @@ jobs: run: rustup component add clippy - run: cargo clippy --all-targets --all-features -- -Dwarnings + wasi: + name: WASI + runs-on: ubuntu-latest + strategy: + matrix: + build: [stable, beta, nightly, MSRV] + include: + - build: stable + rust: stable + - build: beta + rust: beta + - build: nightly + rust: nightly + - build: MSRV + rust: "1.87" + env: + WASI_SDK_VERSION: 34 + steps: + - uses: actions/checkout@master + with: + submodules: true + - name: Install Rust (rustup) + run: rustup update ${{ matrix.rust }} --no-self-update && rustup default ${{ matrix.rust }} && rustup target add wasm32-wasip1 + - name: Install wasi-sdk + run: | + curl -fsSL https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-${WASI_SDK_VERSION}/wasi-sdk-${WASI_SDK_VERSION}.0-x86_64-linux.tar.gz | tar xz + echo "CC_wasm32_wasip1=$PWD/wasi-sdk-${WASI_SDK_VERSION}.0-x86_64-linux/bin/clang" >> $GITHUB_ENV + echo "AR_wasm32_wasip1=$PWD/wasi-sdk-${WASI_SDK_VERSION}.0-x86_64-linux/bin/llvm-ar" >> $GITHUB_ENV + - run: cargo build --locked --target wasm32-wasip1 + rustfmt: name: Rustfmt runs-on: ubuntu-latest @@ -94,6 +124,7 @@ jobs: if: always() needs: - test + - wasi - rustfmt runs-on: ubuntu-latest steps: diff --git a/README.md b/README.md index d25e9dbb2d..d1d26705c2 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,20 @@ If the `ssh` feature is enabled then this library depends on libssh2 which depends on OpenSSL. To get OpenSSL working follow the [`openssl` crate's instructions](https://github.com/sfackler/rust-openssl/blob/master/openssl/src/lib.rs#L31). +## Building for WASI + +libgit2 can be built for `wasm32-wasip1`, without the `https` and `ssh` +features since WASI preview 1 cannot open network connections. libgit2 is +always built from source for this target, which needs a C compiler for WASI +such as [wasi-sdk](https://github.com/WebAssembly/wasi-sdk); point the +[`cc` crate](https://docs.rs/cc) at it: + +```sh +$ export CC_wasm32_wasip1=/path/to/wasi-sdk/bin/clang +$ export AR_wasm32_wasip1=/path/to/wasi-sdk/bin/llvm-ar +$ cargo build --target wasm32-wasip1 +``` + # License This project is licensed under either of diff --git a/libgit2-sys/build.rs b/libgit2-sys/build.rs index 60fb26f430..29ef8f43cd 100644 --- a/libgit2-sys/build.rs +++ b/libgit2-sys/build.rs @@ -109,8 +109,18 @@ The build is now aborting. To disable, unset the variable or use `LIBGIT2_NO_VEN return; } + let target = env::var("TARGET").unwrap(); + let wasi = env::var("CARGO_CFG_TARGET_OS").unwrap() == "wasi"; + if wasi && target.ends_with("-threads") { + panic!("libgit2 is built without thread support on WASI, so the `{target}` target is not supported"); + } + if wasi && (https || ssh) { + panic!("the `https` and `ssh` features are not supported on WASI, which cannot open network connections"); + } + // To use zlib-ng in zlib-compat mode, we have to build libgit2 ourselves. - let try_to_use_system_libgit2 = !vendored && !zlib_ng_compat; + // There is no system libgit2 to link against when targeting WASI. + let try_to_use_system_libgit2 = !vendored && !zlib_ng_compat && !wasi; if try_to_use_system_libgit2 && try_system_libgit2(unstable_sha256).is_ok() { // using system libgit2 has worked return; @@ -124,7 +134,6 @@ The build is now aborting. To disable, unset the variable or use `LIBGIT2_NO_VEN .status(); } - let target = env::var("TARGET").unwrap(); let windows = target.contains("windows"); let dst = PathBuf::from(env::var_os("OUT_DIR").unwrap()); let include = dst.join("include"); @@ -153,7 +162,13 @@ The build is now aborting. To disable, unset the variable or use `LIBGIT2_NO_VEN // compiled apparently and have internal #define's to make sure they're // compiled correctly. add_c_files(&mut cfg, "libgit2/src/libgit2/transports"); - add_c_files(&mut cfg, "libgit2/src/libgit2/streams"); + if wasi { + // WASI preview 1 has no sockets or name resolution; `wasi/compat.c` + // provides a socket stream that always fails instead. + add_c_files_except(&mut cfg, "libgit2/src/libgit2/streams", &["socket.c"]); + } else { + add_c_files(&mut cfg, "libgit2/src/libgit2/streams"); + } // Always use bundled HTTP parser (llhttp) for now cfg.include("libgit2/deps/llhttp"); @@ -204,6 +219,27 @@ The build is now aborting. To disable, unset the variable or use `LIBGIT2_NO_VEN if target.contains("gnu") { cfg.define("__USE_MINGW_ANSI_STDIO", "1"); } + } else if wasi { + // Of the unix sources only realpath.c applies: WASI has no processes, + // and no mmap (NO_MMAP makes libgit2 read files into memory, see + // util/posix.c). + cfg.file("libgit2/src/util/unix/realpath.c"); + cfg.define("NO_MMAP", None); + // Declarations and stubs for the POSIX bits wasi-libc lacks + // (user/process identity, pwd.h, sockets). + cfg.include("wasi/include"); + cfg.flag("-include").flag("wasi/compat.h"); + cfg.file("wasi/compat.c"); + // Declare getpid without wasi-libc's deprecation warning. Rust's + // bundled wasi-libc has no libwasi-emulated-getpid, so wasi/compat.c + // defines it. + cfg.define("_WASI_EMULATED_GETPID", None); + // On wasm32 size_t is unsigned long while SIZE_MAX == UINT_MAX, so + // libgit2's integer.h passes size_t * to __builtin_uadd_overflow. Same + // width, so downgrade clang's error until libgit2 uses the type-generic + // builtin: https://github.com/libgit2/libgit2/pull/7406 + cfg.flag("-Wno-incompatible-pointer-types"); + cfg.flag("-fvisibility=hidden"); } else { add_c_files(&mut cfg, "libgit2/src/util/unix"); cfg.flag("-fvisibility=hidden"); @@ -217,7 +253,10 @@ The build is now aborting. To disable, unset the variable or use `LIBGIT2_NO_VEN features.push_str("#ifndef INCLUDE_features_h\n"); features.push_str("#define INCLUDE_features_h\n"); - features.push_str("#define GIT_THREADS 1\n"); + // WASI is built without threads; the -threads targets are rejected above. + if !wasi { + features.push_str("#define GIT_THREADS 1\n"); + } features.push_str("#define GIT_TRACE 1\n"); features.push_str("#define GIT_HTTPPARSER_BUILTIN 1\n"); @@ -227,6 +266,11 @@ The build is now aborting. To disable, unset the variable or use `LIBGIT2_NO_VEN if windows { features.push_str("#define GIT_IO_WSAPOLL 1\n"); + } else if wasi { + // posix.c requires a poll backend even though only socket.c polls. + features.push_str("#define GIT_IO_POLL 1\n"); + // wasi-libc has getentropy (WASI random_get) and no /dev/urandom. + features.push_str("#define GIT_RAND_GETENTROPY 1\n"); } else { // Should we fallback to `select` as more systems have that? features.push_str("#define GIT_IO_POLL 1\n"); @@ -327,6 +371,7 @@ The build is now aborting. To disable, unset the variable or use `LIBGIT2_NO_VEN println!("cargo:rerun-if-changed=libgit2/include"); println!("cargo:rerun-if-changed=libgit2/src"); println!("cargo:rerun-if-changed=libgit2/deps"); + println!("cargo:rerun-if-changed=wasi"); } fn cp_r(from: impl AsRef, to: impl AsRef) { @@ -345,6 +390,10 @@ fn cp_r(from: impl AsRef, to: impl AsRef) { } fn add_c_files(build: &mut cc::Build, path: impl AsRef) { + add_c_files_except(build, path, &[]); +} + +fn add_c_files_except(build: &mut cc::Build, path: impl AsRef, skip: &[&str]) { let path = path.as_ref(); if !path.exists() { panic!("Path {} does not exist", path.display()); @@ -358,7 +407,9 @@ fn add_c_files(build: &mut cc::Build, path: impl AsRef) { let path = e.path(); if e.file_type().unwrap().is_dir() { // skip dirs for now - } else if path.extension().and_then(|s| s.to_str()) == Some("c") { + } else if path.extension().and_then(|s| s.to_str()) == Some("c") + && !skip.iter().any(|&s| e.file_name() == s) + { build.file(&path); } } diff --git a/libgit2-sys/wasi/compat.c b/libgit2-sys/wasi/compat.c new file mode 100644 index 0000000000..9de7b34fd4 --- /dev/null +++ b/libgit2-sys/wasi/compat.c @@ -0,0 +1,64 @@ +/* + * Definitions for symbols libgit2 references that have no WASI preview 1 + * equivalent. There is a single user (uid 0) and a single process, and no + * sockets. + */ +#include + +#include "git2_util.h" +#include "streams/socket.h" + +uid_t getuid(void) +{ + return 0; +} + +uid_t geteuid(void) +{ + return 0; +} + +gid_t getgid(void) +{ + return 0; +} + +pid_t getpid(void) +{ + return 1; +} + +pid_t getppid(void) +{ + return 0; +} + +pid_t getpgid(pid_t pid) +{ + GIT_UNUSED(pid); + return 1; +} + +pid_t getsid(pid_t pid) +{ + GIT_UNUSED(pid); + return 1; +} + +/* Replaces streams/socket.c: settable through git_libgit2_opts, unused. */ +int git_socket_stream__connect_timeout = 0; +int git_socket_stream__timeout = 0; + +int git_socket_stream_global_init(void) +{ + return 0; +} + +int git_socket_stream_new(git_stream **out, const char *host, const char *port) +{ + GIT_UNUSED(host); + GIT_UNUSED(port); + *out = NULL; + git_error_set(GIT_ERROR_NET, "sockets are not supported on WASI"); + return -1; +} diff --git a/libgit2-sys/wasi/compat.h b/libgit2-sys/wasi/compat.h new file mode 100644 index 0000000000..9fb895419b --- /dev/null +++ b/libgit2-sys/wasi/compat.h @@ -0,0 +1,19 @@ +/* + * Force-included into every libgit2 translation unit when targeting WASI. + * wasi-libc omits these POSIX functions (libgit2 calls them to seed its PRNG, + * check file ownership and find the home directory); compat.c defines them. + * getpid is declared by wasi-libc itself under _WASI_EMULATED_GETPID. + */ +#ifndef LIBGIT2_SYS_WASI_COMPAT_H +#define LIBGIT2_SYS_WASI_COMPAT_H + +#include + +uid_t getuid(void); +uid_t geteuid(void); +gid_t getgid(void); +pid_t getppid(void); +pid_t getpgid(pid_t pid); +pid_t getsid(pid_t pid); + +#endif diff --git a/libgit2-sys/wasi/include/pwd.h b/libgit2-sys/wasi/include/pwd.h new file mode 100644 index 0000000000..c318063d9c --- /dev/null +++ b/libgit2-sys/wasi/include/pwd.h @@ -0,0 +1,31 @@ +/* + * Minimal pwd.h for WASI, whose libc has none. libgit2 includes it from + * sysdir.c and only calls getpwuid_r when running setuid or in a macOS + * sandbox, neither of which happens with compat.c's identity stubs, so this + * just needs to compile; it reports no entry if ever reached. + */ +#ifndef LIBGIT2_SYS_WASI_PWD_H +#define LIBGIT2_SYS_WASI_PWD_H + +#include +#include + +struct passwd { + char *pw_name; + char *pw_passwd; + uid_t pw_uid; + gid_t pw_gid; + char *pw_gecos; + char *pw_dir; + char *pw_shell; +}; + +static inline int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf, + size_t buflen, struct passwd **result) +{ + (void)uid; (void)pwd; (void)buf; (void)buflen; + *result = NULL; + return 0; +} + +#endif diff --git a/src/repo.rs b/src/repo.rs index 00d2aadb6f..8eb441201f 100644 --- a/src/repo.rs +++ b/src/repo.rs @@ -1,5 +1,4 @@ use libc::{c_char, c_int, c_uint, c_void, size_t}; -use std::env; use std::ffi::{CStr, CString, OsStr}; use std::mem; use std::path::{Path, PathBuf}; @@ -217,7 +216,7 @@ impl Repository { crate::init(); // Normal file path OK (does not need Windows conversion). let path = path.as_ref().into_c_string()?; - let ceiling_dirs_os = env::join_paths(ceiling_dirs)?; + let ceiling_dirs_os = util::join_paths(ceiling_dirs)?; let ceiling_dirs = ceiling_dirs_os.into_c_string()?; let mut ret = ptr::null_mut(); unsafe { @@ -277,7 +276,7 @@ impl Repository { let buf = Buf::new(); // Normal file path OK (does not need Windows conversion). let path = path.as_ref().into_c_string()?; - let ceiling_dirs_os = env::join_paths(ceiling_dirs)?; + let ceiling_dirs_os = util::join_paths(ceiling_dirs)?; let ceiling_dirs = ceiling_dirs_os.into_c_string()?; unsafe { try_call!(raw::git_repository_discover( diff --git a/src/util.rs b/src/util.rs index 8cd1d5b167..88bf818c91 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,6 +1,10 @@ use libc::{c_char, c_int, size_t}; use std::cmp::Ordering; use std::ffi::{CString, OsStr, OsString}; +#[cfg(unix)] +use std::os::unix::ffi::OsStrExt; +#[cfg(target_os = "wasi")] +use std::os::wasi::ffi::OsStrExt; use std::path::{Component, Path, PathBuf}; use crate::{raw, Error}; @@ -82,6 +86,36 @@ where iter2cstrs(cstrs) } +/// Joins paths into a list such as libgit2's ceiling directories. +/// +/// This is `env::join_paths`, except on WASI, where std does not support it +/// and the paths are joined with `:` as on unix. +pub fn join_paths(paths: I) -> Result +where + T: AsRef, + I: IntoIterator, +{ + #[cfg(not(target_os = "wasi"))] + { + Ok(std::env::join_paths(paths)?) + } + #[cfg(target_os = "wasi")] + { + let mut joined = OsString::new(); + for (i, path) in paths.into_iter().enumerate() { + let path = path.as_ref(); + if path.as_bytes().contains(&b':') { + return Err(Error::from_str("path segment contains separator `:`")); + } + if i > 0 { + joined.push(":"); + } + joined.push(path); + } + Ok(joined) + } +} + /// Converts an iterator of things into a git array of c-strings. /// /// Returns a tuple `(cstrings, pointers, git_strarray)`. The first two values @@ -105,9 +139,8 @@ where Ok((cstrs, ptrs, raw)) } -#[cfg(unix)] +#[cfg(any(unix, target_os = "wasi"))] pub fn bytes2path(b: &[u8]) -> &Path { - use std::os::unix::prelude::*; Path::new(OsStr::from_bytes(b)) } #[cfg(windows)] @@ -170,9 +203,8 @@ impl IntoCString for &OsStr { } impl IntoCString for OsString { - #[cfg(unix)] + #[cfg(any(unix, target_os = "wasi"))] fn into_c_string(self) -> Result { - use std::os::unix::prelude::*; let s: &OsStr = self.as_ref(); Ok(CString::new(s.as_bytes())?) } From 0af3f126abba12ee61a08cb5ddd304f218f0b63a Mon Sep 17 00:00:00 2001 From: Matthew Suozzo Date: Sun, 11 Oct 2026 15:27:29 -0400 Subject: [PATCH 2/3] DO NOT MERGE: use libz-sys from git until 1.1.30 is released --- Cargo.lock | 5 ++--- Cargo.toml | 5 +++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a3b2204bf2..fa521379d4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -544,9 +544,8 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b70e7a7df205e92a1a4cd9aaae7898dac0aa555503cc0a649494d0d60e7651d" +version = "1.1.29" +source = "git+https://github.com/rust-lang/libz-sys?rev=86e55f804bc117885acdde4b4d663d5e20bc293f#86e55f804bc117885acdde4b4d663d5e20bc293f" dependencies = [ "cc", "cmake", diff --git a/Cargo.toml b/Cargo.toml index 4909964f19..8a97781a48 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -64,6 +64,11 @@ zlib-ng-compat = ["libgit2-sys/zlib-ng-compat"] [workspace] members = ["systest", "git2-curl"] +# DO NOT MERGE: wasm32-wasip1 needs rust-lang/libz-sys#273, which is not +# released yet. Drop this once rust-lang/libz-sys#276 ships 1.1.30. +[patch.crates-io] +libz-sys = { git = "https://github.com/rust-lang/libz-sys", rev = "86e55f804bc117885acdde4b4d663d5e20bc293f" } + [package.metadata.docs.rs] features = ["https", "ssh"] From d9772dab223bde97ec585f3df146012415c8a9aa Mon Sep 17 00:00:00 2001 From: Matthew Suozzo Date: Sun, 11 Oct 2026 15:42:26 -0400 Subject: [PATCH 3/3] test: run the test suite on WASI Run the tests for wasm32-wasip1 under wasmtime in CI instead of only building. MSRV still only builds: before Rust 1.94, std on WASI can't canonicalize paths and its remove_dir_all fails on what the tests create. - std has no temporary directory on WASI, so tests create theirs with `test::tempdir()`, which points tempfile at /tmp there, as tempfile's own tests do. The runner preopens /tmp. - Test helpers with only unix and windows variants (`realpath`, `ostr_to_cstr`, file URLs, the symlink in `repo_status`) now cover WASI, and `search_path` joins paths itself, since `env::join_paths` is unsupported there. - Skipped on WASI: `compile_fail` (trybuild runs cargo), `non_utf8_branch` (WASI paths are UTF-8), and two tests that remove directories through paths ending in a slash, which wasmtime rejects (bytecodealliance/wasmtime#11524). forge-parent: okkxkyxpvspw --- .github/workflows/main.yml | 13 ++++++++++++ src/blame.rs | 4 ++-- src/blob.rs | 7 +++---- src/build.rs | 9 ++++----- src/config.rs | 5 ++--- src/cred.rs | 9 ++++----- src/index.rs | 3 +-- src/odb.rs | 13 ++++++------ src/oid.rs | 7 +++---- src/rebase.rs | 3 +++ src/remote.rs | 28 ++++++++++++++------------ src/repo.rs | 28 +++++++++++++------------- src/submodule.rs | 5 ++--- src/test.rs | 21 ++++++++++++++----- src/worktree.rs | 6 ++---- tests/compile_fail.rs | 2 ++ tests/end_to_end.rs | 41 ++++++++++++++++++++++++++++---------- tests/global_state.rs | 9 +++++++++ 18 files changed, 132 insertions(+), 81 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8f70eb1eaa..55655f9f3a 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -92,6 +92,7 @@ jobs: rust: "1.87" env: WASI_SDK_VERSION: 34 + WASMTIME_VERSION: 49.0.2 steps: - uses: actions/checkout@master with: @@ -103,7 +104,19 @@ jobs: curl -fsSL https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-${WASI_SDK_VERSION}/wasi-sdk-${WASI_SDK_VERSION}.0-x86_64-linux.tar.gz | tar xz echo "CC_wasm32_wasip1=$PWD/wasi-sdk-${WASI_SDK_VERSION}.0-x86_64-linux/bin/clang" >> $GITHUB_ENV echo "AR_wasm32_wasip1=$PWD/wasi-sdk-${WASI_SDK_VERSION}.0-x86_64-linux/bin/llvm-ar" >> $GITHUB_ENV + - name: Install wasmtime + run: | + curl -fsSL https://github.com/bytecodealliance/wasmtime/releases/download/v${WASMTIME_VERSION}/wasmtime-v${WASMTIME_VERSION}-x86_64-linux.tar.xz | tar xJ + echo "$PWD/wasmtime-v${WASMTIME_VERSION}-x86_64-linux" >> $GITHUB_PATH + # Before Rust 1.94, std on WASI can't canonicalize paths and its + # remove_dir_all fails on the directories the tests create, so MSRV only + # builds. - run: cargo build --locked --target wasm32-wasip1 + if: matrix.build == 'MSRV' + - run: cargo test --locked --target wasm32-wasip1 + if: matrix.build != 'MSRV' + env: + CARGO_TARGET_WASM32_WASIP1_RUNNER: wasmtime run --dir /tmp rustfmt: name: Rustfmt diff --git a/src/blame.rs b/src/blame.rs index 81c69d9b85..6101d10079 100644 --- a/src/blame.rs +++ b/src/blame.rs @@ -475,7 +475,7 @@ mod tests { #[test] fn buffer_signatures() { // Regression tests for #1253 - let td = tempfile::TempDir::new().unwrap(); + let td = crate::test::tempdir().unwrap(); let path = td.path(); let repo = crate::Repository::init(path).unwrap(); @@ -560,7 +560,7 @@ mod tests { #[test] fn buffer_empty() { // Regression tests for #1288 - let td = tempfile::TempDir::new().unwrap(); + let td = crate::test::tempdir().unwrap(); let path = td.path(); let repo = crate::Repository::init(path).unwrap(); diff --git a/src/blob.rs b/src/blob.rs index 3d605739f0..c514fda896 100644 --- a/src/blob.rs +++ b/src/blob.rs @@ -156,11 +156,10 @@ mod tests { use std::fs::File; use std::io::prelude::*; use std::path::Path; - use tempfile::TempDir; #[test] fn buffer() { - let td = TempDir::new().unwrap(); + let td = crate::test::tempdir().unwrap(); let repo = Repository::init(td.path()).unwrap(); let id = repo.blob(&[5, 4, 6]).unwrap(); let blob = repo.find_blob(id).unwrap(); @@ -180,7 +179,7 @@ mod tests { #[test] fn path() { - let td = TempDir::new().unwrap(); + let td = crate::test::tempdir().unwrap(); let path = td.path().join("foo"); File::create(&path).unwrap().write_all(&[7, 8, 9]).unwrap(); let repo = Repository::init(td.path()).unwrap(); @@ -192,7 +191,7 @@ mod tests { #[test] fn stream() { - let td = TempDir::new().unwrap(); + let td = crate::test::tempdir().unwrap(); let repo = Repository::init(td.path()).unwrap(); let mut ws = repo.blob_writer(Some(Path::new("foo"))).unwrap(); let wl = ws.write(&[10, 11, 12]).unwrap(); diff --git a/src/build.rs b/src/build.rs index 51d6713b48..62e4229a4d 100644 --- a/src/build.rs +++ b/src/build.rs @@ -776,7 +776,6 @@ mod tests { use crate::{CheckoutNotificationType, FileMode, Repository}; use std::fs; use std::path::Path; - use tempfile::TempDir; #[test] fn smoke() { @@ -786,9 +785,9 @@ mod tests { #[test] fn smoke2() { - let td = TempDir::new().unwrap(); + let td = crate::test::tempdir().unwrap(); Repository::init_bare(td.path().join("bare")).unwrap(); - let url = if cfg!(unix) { + let url = if !cfg!(windows) { format!("file://{}/bare", td.path().display()) } else { format!( @@ -823,8 +822,8 @@ mod tests { /// Issue regression test #365 #[test] fn notify_callback() { - let td = TempDir::new().unwrap(); - let cd = TempDir::new().unwrap(); + let td = crate::test::tempdir().unwrap(); + let cd = crate::test::tempdir().unwrap(); { let mut opts = crate::RepositoryInitOptions::new(); diff --git a/src/config.rs b/src/config.rs index 1cb95e278a..ef882dc49d 100644 --- a/src/config.rs +++ b/src/config.rs @@ -642,7 +642,6 @@ impl<'cfg> Drop for ConfigEntry<'cfg> { #[cfg(test)] mod tests { use std::fs::File; - use tempfile::TempDir; use crate::Config; @@ -656,7 +655,7 @@ mod tests { #[test] fn persisted() { - let td = TempDir::new().unwrap(); + let td = crate::test::tempdir().unwrap(); let path = td.path().join("foo"); File::create(&path).unwrap(); @@ -686,7 +685,7 @@ mod tests { #[test] fn multivar() { - let td = TempDir::new().unwrap(); + let td = crate::test::tempdir().unwrap(); let path = td.path().join("foo"); File::create(&path).unwrap(); diff --git a/src/cred.rs b/src/cred.rs index e8d4cfdcfe..7c6e79dc31 100644 --- a/src/cred.rs +++ b/src/cred.rs @@ -506,12 +506,11 @@ mod test { use std::fs::File; use std::io::prelude::*; use std::path::Path; - use tempfile::TempDir; use crate::{Config, ConfigLevel, Cred, CredentialHelper}; macro_rules! test_cfg( ($($k:expr => $v:expr),*) => ({ - let td = TempDir::new().unwrap(); + let td = crate::test::tempdir().unwrap(); let mut cfg = Config::new().unwrap(); cfg.add_file(&td.path().join("cfg"), ConfigLevel::App, false).unwrap(); $(cfg.set_str($k, $v).unwrap();)* @@ -566,7 +565,7 @@ mod test { return; } // shell scripts don't work on Windows - let td = TempDir::new().unwrap(); + let td = crate::test::tempdir().unwrap(); let path = td.path().join("script"); let written_bytes = File::create(&path) .unwrap() @@ -597,7 +596,7 @@ echo username=c if cfg!(windows) { return; } // shell scripts don't work on Windows - let td = TempDir::new().unwrap(); + let td = crate::test::tempdir().unwrap(); let path = td.path().join("git-credential-some-script"); let written_bytes = File::create(&path) .unwrap() @@ -643,7 +642,7 @@ echo username=$1 if cfg!(windows) { return; } // shell scripts don't work on Windows - let td = TempDir::new().unwrap(); + let td = crate::test::tempdir().unwrap(); let path = td.path().join("script"); let written_bytes = File::create(&path) .unwrap() diff --git a/src/index.rs b/src/index.rs index 5ff91bffe7..9633641c1d 100644 --- a/src/index.rs +++ b/src/index.rs @@ -885,7 +885,6 @@ impl Binding for IndexEntry { mod tests { use std::fs::{self, File}; use std::path::Path; - use tempfile::TempDir; use crate::{ ErrorCode, Index, IndexEntry, IndexTime, ObjectFormat, Oid, Repository, ResetType, @@ -983,7 +982,7 @@ mod tests { let obj = repo.find_object(commit, None).unwrap(); repo.reset(&obj, ResetType::Hard, None).unwrap(); - let td2 = TempDir::new().unwrap(); + let td2 = crate::test::tempdir().unwrap(); let url = crate::test::path2url(root); let repo = Repository::clone(&url, td2.path()).unwrap(); let obj = repo.find_object(commit, None).unwrap(); diff --git a/src/odb.rs b/src/odb.rs index 8244e99bc5..e7d4f97d5a 100644 --- a/src/odb.rs +++ b/src/odb.rs @@ -586,11 +586,10 @@ pub(crate) extern "C" fn write_pack_progress_cb( mod tests { use crate::{Buf, ObjectType, Oid, Repository}; use std::io::prelude::*; - use tempfile::TempDir; #[test] fn read() { - let td = TempDir::new().unwrap(); + let td = crate::test::tempdir().unwrap(); let repo = Repository::init(td.path()).unwrap(); let dat = [4, 3, 5, 6, 9]; let id = repo.blob(&dat).unwrap(); @@ -605,7 +604,7 @@ mod tests { #[test] fn read_header() { - let td = TempDir::new().unwrap(); + let td = crate::test::tempdir().unwrap(); let repo = Repository::init(td.path()).unwrap(); let dat = [4, 3, 5, 6, 9]; let id = repo.blob(&dat).unwrap(); @@ -618,7 +617,7 @@ mod tests { #[test] fn write() { - let td = TempDir::new().unwrap(); + let td = crate::test::tempdir().unwrap(); let repo = Repository::init(td.path()).unwrap(); let dat = [4, 3, 5, 6, 9]; let db = repo.odb().unwrap(); @@ -629,7 +628,7 @@ mod tests { #[test] fn writer() { - let td = TempDir::new().unwrap(); + let td = crate::test::tempdir().unwrap(); let repo = Repository::init(td.path()).unwrap(); let dat = [4, 3, 5, 6, 9]; let db = repo.odb().unwrap(); @@ -645,7 +644,7 @@ mod tests { #[test] fn exists() { - let td = TempDir::new().unwrap(); + let td = crate::test::tempdir().unwrap(); let repo = Repository::init(td.path()).unwrap(); let dat = [4, 3, 5, 6, 9]; let db = repo.odb().unwrap(); @@ -655,7 +654,7 @@ mod tests { #[test] fn exists_prefix() { - let td = TempDir::new().unwrap(); + let td = crate::test::tempdir().unwrap(); let repo = Repository::init(td.path()).unwrap(); let dat = [4, 3, 5, 6, 9]; let db = repo.odb().unwrap(); diff --git a/src/oid.rs b/src/oid.rs index 01f2885756..950f3016e4 100644 --- a/src/oid.rs +++ b/src/oid.rs @@ -406,7 +406,6 @@ mod tests { use super::Oid; use crate::ObjectFormat; use crate::ObjectType; - use tempfile::TempDir; #[test] fn conversions() { @@ -588,7 +587,7 @@ mod tests { #[test] fn hash_file() { - let td = TempDir::new().unwrap(); + let td = crate::test::tempdir().unwrap(); let path = td.path().join("hello.txt"); let mut file = File::create(&path).unwrap(); file.write_all("Hello".as_bytes()).unwrap(); @@ -599,7 +598,7 @@ mod tests { #[test] fn hash_file_ext_sha1() -> Result<(), Error> { - let td = TempDir::new().unwrap(); + let td = crate::test::tempdir().unwrap(); let path = td.path().join("test.txt"); let mut file = File::create(&path).unwrap(); file.write_all(b"test content").unwrap(); @@ -613,7 +612,7 @@ mod tests { #[test] #[cfg(feature = "unstable-sha256")] fn hash_file_ext_sha256() -> Result<(), Error> { - let td = TempDir::new().unwrap(); + let td = crate::test::tempdir().unwrap(); let path = td.path().join("test.txt"); let mut file = File::create(&path).unwrap(); file.write_all(b"test content").unwrap(); diff --git a/src/rebase.rs b/src/rebase.rs index 986ee8c530..c304628971 100644 --- a/src/rebase.rs +++ b/src/rebase.rs @@ -395,7 +395,10 @@ mod tests { } } + // wasmtime can't remove directories whose paths end in a slash, as + // libgit2 passes them: https://github.com/bytecodealliance/wasmtime/issues/11524 #[test] + #[cfg_attr(target_os = "wasi", ignore)] fn keeping_original_author_msg() { let (td, repo) = crate::test::repo_init(); let head_target = repo.head().unwrap().target().unwrap(); diff --git a/src/remote.rs b/src/remote.rs index 6424604708..17279243e6 100644 --- a/src/remote.rs +++ b/src/remote.rs @@ -827,7 +827,6 @@ mod tests { use crate::{AutotagOption, PushOptions, RemoteUpdateFlags}; use crate::{Direction, FetchOptions, ObjectFormat, Remote, RemoteCallbacks, Repository}; use std::cell::Cell; - use tempfile::TempDir; #[test] fn smoke() { @@ -852,12 +851,12 @@ mod tests { #[test] fn create_remote() { - let td = TempDir::new().unwrap(); + let td = crate::test::tempdir().unwrap(); let remote = td.path().join("remote"); Repository::init_bare(&remote).unwrap(); let (_td, repo) = crate::test::repo_init(); - let url = if cfg!(unix) { + let url = if !cfg!(windows) { format!("file://{}", remote.display()) } else { format!( @@ -944,7 +943,7 @@ mod tests { #[test] fn create_remote_anonymous() { - let td = TempDir::new().unwrap(); + let td = crate::test::tempdir().unwrap(); let repo = Repository::init(td.path()).unwrap(); let origin = repo.remote_anonymous("/path/to/nowhere").unwrap(); @@ -955,12 +954,12 @@ mod tests { #[test] fn empty_remote_list() { // Regression tests for issue #1217 - let td = TempDir::new().unwrap(); + let td = crate::test::tempdir().unwrap(); let repo = Repository::init(td.path()).unwrap(); - let remote_dir = TempDir::new().unwrap(); + let remote_dir = crate::test::tempdir().unwrap(); let _remote_repo = Repository::init_bare(remote_dir.path()).unwrap(); - let remote_url = if cfg!(unix) { + let remote_url = if !cfg!(windows) { format!("file://{}", remote_dir.path().display()) } else { format!( @@ -985,7 +984,7 @@ mod tests { #[test] fn transfer_cb() { let (td, _repo) = crate::test::repo_init(); - let td2 = TempDir::new().unwrap(); + let td2 = crate::test::tempdir().unwrap(); let url = crate::test::path2url(td.path()); let repo = Repository::init(td2.path()).unwrap(); @@ -1021,7 +1020,7 @@ mod tests { #[test] fn connect_list() { let (td, _repo) = crate::test::repo_init(); - let td2 = TempDir::new().unwrap(); + let td2 = crate::test::tempdir().unwrap(); let url = crate::test::path2url(td.path()); let repo = Repository::init(td2.path()).unwrap(); @@ -1052,8 +1051,8 @@ mod tests { #[test] fn push() { let (_td, repo) = crate::test::repo_init(); - let td2 = TempDir::new().unwrap(); - let td3 = TempDir::new().unwrap(); + let td2 = crate::test::tempdir().unwrap(); + let td3 = crate::test::tempdir().unwrap(); let url = crate::test::path2url(td2.path()); let mut opts = crate::RepositoryInitOptions::new(); @@ -1085,14 +1084,17 @@ mod tests { assert_eq!(commit.message(), Ok("initial\n\nbody")); } + // wasmtime can't remove directories whose paths end in a slash, as + // libgit2 passes them: https://github.com/bytecodealliance/wasmtime/issues/11524 #[test] + #[cfg_attr(target_os = "wasi", ignore)] fn prune() { let (td, remote_repo) = crate::test::repo_init(); let oid = remote_repo.head().unwrap().target().unwrap(); let commit = remote_repo.find_commit(oid).unwrap(); remote_repo.branch("stale", &commit, true).unwrap(); - let td2 = TempDir::new().unwrap(); + let td2 = crate::test::tempdir().unwrap(); let url = crate::test::path2url(td.path()); let repo = Repository::clone(&url, &td2).unwrap(); @@ -1132,7 +1134,7 @@ mod tests { let (_td, repo) = crate::test::repo_init(); let oid = repo.head().unwrap().target().unwrap(); - let td2 = TempDir::new().unwrap(); + let td2 = crate::test::tempdir().unwrap(); let url = crate::test::path2url(td2.path()); let mut opts = crate::RepositoryInitOptions::new(); opts.bare(true); diff --git a/src/repo.rs b/src/repo.rs index 8eb441201f..fdefad38de 100644 --- a/src/repo.rs +++ b/src/repo.rs @@ -3691,7 +3691,7 @@ mod tests { #[test] fn smoke_init() { - let td = TempDir::new().unwrap(); + let td = crate::test::tempdir().unwrap(); let path = td.path(); let repo = Repository::init(path).unwrap(); @@ -3706,7 +3706,7 @@ mod tests { #[test] #[cfg(feature = "unstable-sha256")] fn smoke_init_sha256() { - let td = TempDir::new().unwrap(); + let td = crate::test::tempdir().unwrap(); let path = td.path(); let mut opts = RepositoryInitOptions::new(); @@ -3727,7 +3727,7 @@ mod tests { #[test] fn smoke_init_bare() { - let td = TempDir::new().unwrap(); + let td = crate::test::tempdir().unwrap(); let path = td.path(); let repo = Repository::init_bare(path).unwrap(); @@ -3739,7 +3739,7 @@ mod tests { #[test] #[cfg(feature = "unstable-sha256")] fn smoke_init_bare_sha256() { - let td = TempDir::new().unwrap(); + let td = crate::test::tempdir().unwrap(); let path = td.path(); let mut opts = RepositoryInitOptions::new(); @@ -3754,7 +3754,7 @@ mod tests { #[test] fn smoke_open() { - let td = TempDir::new().unwrap(); + let td = crate::test::tempdir().unwrap(); let path = td.path(); Repository::init(td.path()).unwrap(); let repo = Repository::open(path).unwrap(); @@ -3775,7 +3775,7 @@ mod tests { #[test] #[cfg(feature = "unstable-sha256")] fn smoke_open_sha256() { - let td = TempDir::new().unwrap(); + let td = crate::test::tempdir().unwrap(); let path = td.path(); let mut opts = RepositoryInitOptions::new(); @@ -3803,7 +3803,7 @@ mod tests { #[test] fn smoke_open_bare() { - let td = TempDir::new().unwrap(); + let td = crate::test::tempdir().unwrap(); let path = td.path(); Repository::init_bare(td.path()).unwrap(); @@ -3818,7 +3818,7 @@ mod tests { #[test] #[cfg(feature = "unstable-sha256")] fn smoke_open_bare_sha256() { - let td = TempDir::new().unwrap(); + let td = crate::test::tempdir().unwrap(); let path = td.path(); let mut opts = RepositoryInitOptions::new(); @@ -3893,13 +3893,13 @@ mod tests { #[test] fn makes_dirs() { - let td = TempDir::new().unwrap(); + let td = crate::test::tempdir().unwrap(); Repository::init(td.path().join("a/b/c/d")).unwrap(); } #[test] fn smoke_discover() { - let td = TempDir::new().unwrap(); + let td = crate::test::tempdir().unwrap(); let subdir = td.path().join("subdi"); fs::create_dir(&subdir).unwrap(); Repository::init_bare(td.path()).unwrap(); @@ -3912,7 +3912,7 @@ mod tests { #[test] fn smoke_discover_path() { - let td = TempDir::new().unwrap(); + let td = crate::test::tempdir().unwrap(); let subdir = td.path().join("subdi"); fs::create_dir(&subdir).unwrap(); Repository::init_bare(td.path()).unwrap(); @@ -3925,7 +3925,7 @@ mod tests { #[test] fn smoke_discover_path_ceiling_dir() { - let td = TempDir::new().unwrap(); + let td = crate::test::tempdir().unwrap(); let subdir = td.path().join("subdi"); fs::create_dir(&subdir).unwrap(); let ceilingdir = subdir.join("ceiling"); @@ -3940,7 +3940,7 @@ mod tests { #[test] fn smoke_open_ext() { - let td = TempDir::new().unwrap(); + let td = crate::test::tempdir().unwrap(); let subdir = td.path().join("subdir"); fs::create_dir(&subdir).unwrap(); Repository::init(td.path()).unwrap(); @@ -4863,7 +4863,7 @@ Committer Name "#, #[test] fn refdb_compress_bare_repo() { - let td = TempDir::new().unwrap(); + let td = crate::test::tempdir().unwrap(); let repo = Repository::init_bare(td.path()).unwrap(); // Compressing a bare repo with no refs should succeed. repo.refdb_compress().unwrap(); diff --git a/src/submodule.rs b/src/submodule.rs index 4f88877029..e88f65e61b 100644 --- a/src/submodule.rs +++ b/src/submodule.rs @@ -331,7 +331,6 @@ impl<'cb> Default for SubmoduleUpdateOptions<'cb> { mod tests { use std::fs; use std::path::Path; - use tempfile::TempDir; use url::Url; use crate::Repository; @@ -339,7 +338,7 @@ mod tests { #[test] fn smoke() { - let td = TempDir::new().unwrap(); + let td = crate::test::tempdir().unwrap(); let repo = Repository::init(td.path()).unwrap(); let mut s1 = repo .submodule("/path/to/nowhere", Path::new("foo"), true) @@ -455,7 +454,7 @@ mod tests { crate::test::commit(&parent); // Clone the parent to init its submodules - let td = TempDir::new().unwrap(); + let td = crate::test::tempdir().unwrap(); let new_parent = Repository::clone(url_parent.as_ref(), &td).unwrap(); let mut submodules = new_parent.submodules().unwrap(); diff --git a/src/test.rs b/src/test.rs index b391aa562f..7f2a20e087 100644 --- a/src/test.rs +++ b/src/test.rs @@ -1,7 +1,7 @@ use std::fs::File; use std::io; use std::path::{Path, PathBuf}; -#[cfg(unix)] +#[cfg(any(unix, target_os = "wasi"))] use std::ptr; use tempfile::TempDir; use url::Url; @@ -17,8 +17,16 @@ macro_rules! t { }; } +/// Creates a temporary directory. std has no temporary directory on WASI, so +/// tests there use `/tmp`, which the test runner must preopen. +pub fn tempdir() -> io::Result { + #[cfg(target_os = "wasi")] + let _ = tempfile::env::override_temp_dir(Path::new("/tmp")); + TempDir::new() +} + fn repo_init_ext(format: ObjectFormat) -> (TempDir, Repository) { - let td = TempDir::new().unwrap(); + let td = tempdir().unwrap(); let mut opts = RepositoryInitOptions::new(); opts.initial_head("main"); opts.object_format(format); @@ -70,7 +78,7 @@ pub fn worktrees_env_init(repo: &Repository) -> (TempDir, Branch<'_>) { let oid = repo.head().unwrap().target().unwrap(); let commit = repo.find_commit(oid).unwrap(); let branch = repo.branch("wt-branch", &commit, true).unwrap(); - let wtdir = TempDir::new().unwrap(); + let wtdir = tempdir().unwrap(); (wtdir, branch) } @@ -78,11 +86,14 @@ pub fn worktrees_env_init(repo: &Repository) -> (TempDir, Branch<'_>) { pub fn realpath(original: &Path) -> io::Result { Ok(original.canonicalize()?.to_path_buf()) } -#[cfg(unix)] +#[cfg(any(unix, target_os = "wasi"))] pub fn realpath(original: &Path) -> io::Result { use libc::c_char; use std::ffi::{CStr, CString, OsString}; - use std::os::unix::prelude::*; + #[cfg(unix)] + use std::os::unix::ffi::{OsStrExt, OsStringExt}; + #[cfg(target_os = "wasi")] + use std::os::wasi::ffi::{OsStrExt, OsStringExt}; extern "C" { fn realpath(name: *const c_char, resolved: *mut c_char) -> *mut c_char; } diff --git a/src/worktree.rs b/src/worktree.rs index e568e6d182..bab53e4ded 100644 --- a/src/worktree.rs +++ b/src/worktree.rs @@ -285,13 +285,11 @@ mod tests { use crate::WorktreeAddOptions; use crate::WorktreeLockStatus; - use tempfile::TempDir; - #[test] fn smoke_add_no_ref() { let (_td, repo) = crate::test::repo_init(); - let wtdir = TempDir::new().unwrap(); + let wtdir = crate::test::tempdir().unwrap(); let wt_path = wtdir.path().join("tree-no-ref-dir"); let opts = WorktreeAddOptions::new(); @@ -309,7 +307,7 @@ mod tests { fn smoke_add_locked() { let (_td, repo) = crate::test::repo_init(); - let wtdir = TempDir::new().unwrap(); + let wtdir = crate::test::tempdir().unwrap(); let wt_path = wtdir.path().join("locked-tree"); let mut opts = WorktreeAddOptions::new(); opts.lock(true); diff --git a/tests/compile_fail.rs b/tests/compile_fail.rs index fc7018106d..57ea1f5f4d 100644 --- a/tests/compile_fail.rs +++ b/tests/compile_fail.rs @@ -1,6 +1,8 @@ //! Regression tests for things that should not compile but used to +// trybuild runs cargo, and WASI has no processes. #[test] +#[cfg_attr(target_os = "wasi", ignore)] fn test_compile_fail() { let t = trybuild::TestCases::new(); t.compile_fail("tests/compile_fail/remote_refspec.rs"); diff --git a/tests/end_to_end.rs b/tests/end_to_end.rs index e08209ced6..67afd50e74 100644 --- a/tests/end_to_end.rs +++ b/tests/end_to_end.rs @@ -14,13 +14,21 @@ use std::ptr; use tempfile::TempDir; +/// Creates a temporary directory. std has no temporary directory on WASI, so +/// tests there use `/tmp`, which the test runner must preopen. +fn tempdir() -> std::io::Result { + #[cfg(target_os = "wasi")] + let _ = tempfile::env::override_temp_dir(Path::new("/tmp")); + TempDir::new() +} + // Skip on MacOS, where git cannot even create a branch with a non-UTF8 name -// Same on Windows +// Same on Windows, and on WASI, whose paths must be UTF-8 #[test] -#[cfg_attr(any(windows, target_os = "macos"), ignore)] +#[cfg_attr(any(windows, target_os = "macos", target_os = "wasi"), ignore)] fn non_utf8_branch() { - let td = TempDir::new().unwrap(); + let td = tempdir().unwrap(); let path = td.path(); { let mut opts = RepositoryInitOptions::new(); @@ -56,10 +64,13 @@ fn non_utf8_branch() { // based on util.rs IntoCString for OsString // Need cfg() guards since the file is compiled on Windows even when // the test is skipped - #[cfg(unix)] + #[cfg(any(unix, target_os = "wasi"))] fn ostr_to_cstr(s: OsString) -> CString { use std::ffi::OsStr; - use std::os::unix::prelude::*; + #[cfg(unix)] + use std::os::unix::ffi::OsStrExt; + #[cfg(target_os = "wasi")] + use std::os::wasi::ffi::OsStrExt; let s: &OsStr = s.as_ref(); CString::new(s.as_bytes()).unwrap() } @@ -163,7 +174,7 @@ fn non_utf8_branch() { #[test] fn stash_length() { // Test that reflog() and len() allow tracking the number of stashed entries - let td = TempDir::new().unwrap(); + let td = tempdir().unwrap(); let path = td.path(); let mut opts = RepositoryInitOptions::new(); @@ -231,7 +242,7 @@ fn branch_name_on_init() { // no commits are made yet and the branch doesn't exist // Test with "main" { - let td = TempDir::new().unwrap(); + let td = tempdir().unwrap(); let path = td.path(); let mut opts = RepositoryInitOptions::new(); @@ -247,7 +258,7 @@ fn branch_name_on_init() { } // Test with "somerandombranchnamehere" { - let td = TempDir::new().unwrap(); + let td = tempdir().unwrap(); let path = td.path(); let mut opts = RepositoryInitOptions::new(); @@ -265,7 +276,7 @@ fn branch_name_on_init() { #[test] fn repo_status() { - let td = TempDir::new().unwrap(); + let td = tempdir().unwrap(); let path = td.path(); let mut opts = RepositoryInitOptions::new(); @@ -322,6 +333,16 @@ fn repo_status() { #[cfg(windows)] std::os::windows::fs::symlink_file(to, from).unwrap(); + + // WASI only allows relative symlinks that stay within a preopened + // directory, and std's symlink there is unstable. + #[cfg(target_os = "wasi")] + { + use std::os::wasi::ffi::OsStrExt; + let to = CString::new(to.file_name().unwrap().as_bytes()).unwrap(); + let from = CString::new(from.as_os_str().as_bytes()).unwrap(); + assert_eq!(unsafe { libc::symlink(to.as_ptr(), from.as_ptr()) }, 0); + } } create_symlink(&path.join("Unchanged"), &path.join("IndexTypechange")); @@ -408,7 +429,7 @@ fn repo_status() { #[test] fn repo_status_clean() { - let td = TempDir::new().unwrap(); + let td = tempdir().unwrap(); let path = td.path(); let mut opts = RepositoryInitOptions::new(); diff --git a/tests/global_state.rs b/tests/global_state.rs index 357364039b..72e95728ba 100644 --- a/tests/global_state.rs +++ b/tests/global_state.rs @@ -8,7 +8,16 @@ use git2::{ConfigLevel, IntoCString}; // initialization in libgit2's `git_sysdir_global_init` function. #[test] fn search_path() -> Result<(), Box> { + #[cfg(not(target_os = "wasi"))] use std::env::join_paths; + // std::env::join_paths is unsupported on WASI, where libgit2 separates + // paths with ':' as on unix. + #[cfg(target_os = "wasi")] + fn join_paths<'a>( + paths: impl Iterator, + ) -> std::io::Result { + Ok(paths.copied().collect::>().join(":").into()) + } let path = "fake_path"; let original = unsafe { opts::get_search_path(ConfigLevel::Global) };