{"thread":{"id":"64539","subject":"[PATCH 00/13] RFC: Convert to Cargo workspace","startedAt":"2025-11-27T01:10:37Z","lastAt":"2025-11-27T01:10:56Z","messageCount":14,"participants":["Ezekiel Newren via GitGitGadget"],"isPatch":true,"patchVersion":1,"patchTotal":13},"messages":[{"id":"531349","messageId":"pull.2110.git.git.1764205835.gitgitgadget@gmail.com","threadId":"64539","inReplyTo":null,"subject":"[PATCH 00/13] RFC: Convert to Cargo workspace","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-11-27T01:10:22Z","receivedAt":"2025-11-27T01:10:37Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"The goal of this patch series is to get feedback on converting from a single\ncargo crate to a cargo workspace. This series is incomplete, and as such, is\nmarked as RFC.\n\nWhy using a cargo workspace is better than a single crate:\n\n * Better modularity.\n * Since a crate is the smallest unit of compilation in Rust (not individual\n   files), multiple crates avoid recompiling everything\n * A C header is created for each crate by cbindgen (if requested), avoiding\n   a single monolithic C header from all Rust sources.\n * This separates the dependencies of each crate, which is important if\n   we’re forced to take an old or new dependency for a non-library crate,\n   for example, and don’t want those altered dependencies affecting other\n   crates.\n\nI am particularly interested in feedback on the known issues below:\n\n * github workflow rust-analysis: broken because I removed the rust-version\n   statement in Cargo.toml. The minimum Rust version supported by Git should\n   be documented, but we shouldn’t require building with that minimum\n   version.\n * win+Meson build: failing because it can't find the static library\n * Rust unit testing: not implemented yet, Make and Meson need to call cargo\n   test\n * Conflicts with Brian's \"SHA-1/SHA-256 interoperability\" patch series\n\nMuch of Patrick's earlier work needed to be removed because it assumed a\nsingle-crate layout.\n\nThe Rust crates are located under rust/, but cargo build must be invoked\nfrom the top-level Git directory. cbindgen is included as part of this\nseries, but nothing uses it yet. The generated/ directory is where cbindgen\nplaces the generated C header files.\n\nEzekiel Newren (13):\n  make: undo Patrick's changes concerning Rust\n  meson: undo Patrick's changes concerning Rust\n  cargo: convert from a crate to a workspace\n  build: build Rust with Makefile and Meson\n  .gitignore: ignore /generated/\n  cargo: create crate generate-headers\n  cargo: create crate link-with-c\n  rust/gitcore: link with c\n  varint.h: unsigned char -> uint8_t\n  make: delete files in generated/\n  github-workflows: unify with rust parameters in make and meson\n  github workflows: install Rust\n  rust/build-rust.sh: update dir_git_root variable instantiation\n\n .github/workflows/main.yml          |  62 ++++++++++++++-\n .gitignore                          |   1 +\n Cargo.toml                          |  17 ++--\n Makefile                            | 118 ++++++++++++++++------------\n ci/install-dependencies.sh          |  14 ++--\n ci/install-rust-toolchain.sh        |  30 +++++++\n ci/install-rustup.sh                |  25 ++++++\n ci/make-test-artifacts.sh           |   9 +++\n ci/run-build-and-tests.sh           |  17 +++-\n meson.build                         |  83 ++++++++++++++-----\n meson_options.txt                   |   4 +-\n rust/build-crate.sh                 |  63 +++++++++++++++\n rust/cbindgen-template.toml         |  13 +++\n rust/generate-headers/Cargo.toml    |  12 +++\n rust/generate-headers/src/main.rs   |  44 +++++++++++\n rust/gitcore/Cargo.toml             |  12 +++\n rust/gitcore/build.rs               |   7 ++\n {src => rust/gitcore/src}/lib.rs    |   0\n {src => rust/gitcore/src}/varint.rs |   0\n rust/link-with-c/Cargo.toml         |   9 +++\n rust/link-with-c/src/lib.rs         |  77 ++++++++++++++++++\n shared.mak                          |   1 -\n src/cargo-meson.sh                  |  39 ---------\n src/meson.build                     |  41 ----------\n varint.h                            |   4 +-\n 25 files changed, 528 insertions(+), 174 deletions(-)\n create mode 100755 ci/install-rust-toolchain.sh\n create mode 100755 ci/install-rustup.sh\n create mode 100755 rust/build-crate.sh\n create mode 100644 rust/cbindgen-template.toml\n create mode 100644 rust/generate-headers/Cargo.toml\n create mode 100644 rust/generate-headers/src/main.rs\n create mode 100644 rust/gitcore/Cargo.toml\n create mode 100644 rust/gitcore/build.rs\n rename {src => rust/gitcore/src}/lib.rs (100%)\n rename {src => rust/gitcore/src}/varint.rs (100%)\n create mode 100644 rust/link-with-c/Cargo.toml\n create mode 100644 rust/link-with-c/src/lib.rs\n delete mode 100755 src/cargo-meson.sh\n delete mode 100644 src/meson.build\n\n\nbase-commit: a99f379adf116d53eb11957af5bab5214915f91d\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-2110%2Fezekielnewren%2Fcargo-workspace-v1\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-2110/ezekielnewren/cargo-workspace-v1\nPull-Request: https://github.com/git/git/pull/2110\n-- \ngitgitgadget\n"},{"id":"531350","messageId":"12ab631072e6076f9dbcd905c62a42795ea9dbfa.1764205835.git.gitgitgadget@gmail.com","threadId":"64539","inReplyTo":"pull.2110.git.git.1764205835.gitgitgadget@gmail.com","subject":"[PATCH 01/13] make: undo Patrick's changes concerning Rust","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-11-27T01:10:23Z","receivedAt":"2025-11-27T01:10:40Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n Makefile   | 49 -------------------------------------------------\n shared.mak |  1 -\n 2 files changed, 50 deletions(-)\n\ndiff --git a/Makefile b/Makefile\nindex 7e0f77e298..28bc00e648 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -483,14 +483,6 @@ include shared.mak\n # Define LIBPCREDIR=/foo/bar if your PCRE header and library files are\n # in /foo/bar/include and /foo/bar/lib directories.\n #\n-# == Optional Rust support ==\n-#\n-# Define WITH_RUST if you want to include features and subsystems written in\n-# Rust into Git. For now, Rust is still an optional feature of the build\n-# process. With Git 3.0 though, Rust will always be enabled.\n-#\n-# Building Rust code requires Cargo.\n-#\n # == SHA-1 and SHA-256 defines ==\n #\n # === SHA-1 backend ===\n@@ -691,7 +683,6 @@ OBJECTS =\n OTHER_PROGRAMS =\n PROGRAM_OBJS =\n PROGRAMS =\n-RUST_SOURCES =\n EXCLUDED_PROGRAMS =\n SCRIPT_PERL =\n SCRIPT_PYTHON =\n@@ -928,18 +919,6 @@ TEST_SHELL_PATH = $(SHELL_PATH)\n \n LIB_FILE = libgit.a\n \n-ifdef DEBUG\n-RUST_TARGET_DIR = target/debug\n-else\n-RUST_TARGET_DIR = target/release\n-endif\n-\n-ifeq ($(uname_S),Windows)\n-RUST_LIB = $(RUST_TARGET_DIR)/gitcore.lib\n-else\n-RUST_LIB = $(RUST_TARGET_DIR)/libgitcore.a\n-endif\n-\n GITLIBS = common-main.o $(LIB_FILE)\n EXTLIBS =\n \n@@ -963,15 +942,6 @@ BASIC_LDFLAGS =\n ARFLAGS = rcs\n PTHREAD_CFLAGS =\n \n-# Rust flags\n-CARGO_ARGS =\n-ifndef V\n-CARGO_ARGS += --quiet\n-endif\n-ifndef DEBUG\n-CARGO_ARGS += --release\n-endif\n-\n # For the 'sparse' target\n SPARSE_FLAGS ?= -std=gnu99 -D__STDC_NO_VLA__\n SP_EXTRA_FLAGS =\n@@ -1333,9 +1303,7 @@ LIB_OBJS += urlmatch.o\n LIB_OBJS += usage.o\n LIB_OBJS += userdiff.o\n LIB_OBJS += utf8.o\n-ifndef WITH_RUST\n LIB_OBJS += varint.o\n-endif\n LIB_OBJS += version.o\n LIB_OBJS += versioncmp.o\n LIB_OBJS += walker.o\n@@ -1534,9 +1502,6 @@ CLAR_TEST_OBJS += $(UNIT_TEST_DIR)/unit-test.o\n \n UNIT_TEST_OBJS += $(UNIT_TEST_DIR)/test-lib.o\n \n-RUST_SOURCES += src/lib.rs\n-RUST_SOURCES += src/varint.rs\n-\n GIT-VERSION-FILE: FORCE\n \t@OLD=$$(cat $@ 2>/dev/null || :) && \\\n \t$(call version_gen,\"$(shell pwd)\",GIT-VERSION-FILE.in,$@) && \\\n@@ -1566,14 +1531,6 @@ endif\n ALL_CFLAGS = $(DEVELOPER_CFLAGS) $(CPPFLAGS) $(CFLAGS) $(CFLAGS_APPEND)\n ALL_LDFLAGS = $(LDFLAGS) $(LDFLAGS_APPEND)\n \n-ifdef WITH_RUST\n-BASIC_CFLAGS += -DWITH_RUST\n-GITLIBS += $(RUST_LIB)\n-ifeq ($(uname_S),Windows)\n-EXTLIBS += -luserenv\n-endif\n-endif\n-\n ifdef SANITIZE\n SANITIZERS := $(foreach flag,$(subst $(comma),$(space),$(SANITIZE)),$(flag))\n BASIC_CFLAGS += -fsanitize=$(SANITIZE) -fno-sanitize-recover=$(SANITIZE)\n@@ -2963,12 +2920,6 @@ scalar$X: scalar.o GIT-LDFLAGS $(GITLIBS)\n $(LIB_FILE): $(LIB_OBJS)\n \t$(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^\n \n-$(RUST_LIB): Cargo.toml $(RUST_SOURCES)\n-\t$(QUIET_CARGO)cargo build $(CARGO_ARGS)\n-\n-.PHONY: rust\n-rust: $(RUST_LIB)\n-\n export DEFAULT_EDITOR DEFAULT_PAGER\n \n Documentation/GIT-EXCLUDED-PROGRAMS: FORCE\ndiff --git a/shared.mak b/shared.mak\nindex 0e7492076e..5c7bc94785 100644\n--- a/shared.mak\n+++ b/shared.mak\n@@ -56,7 +56,6 @@ ifndef V\n \tQUIET_MKDIR_P_PARENT  = @echo '   ' MKDIR -p $(@D);\n \n ## Used in \"Makefile\"\n-\tQUIET_CARGO    = @echo '   ' CARGO $@;\n \tQUIET_CC       = @echo '   ' CC $@;\n \tQUIET_AR       = @echo '   ' AR $@;\n \tQUIET_LINK     = @echo '   ' LINK $@;\n-- \ngitgitgadget\n\n"},{"id":"531351","messageId":"dd8787e3e07a567acf56c23c69599aaefd87b3b0.1764205835.git.gitgitgadget@gmail.com","threadId":"64539","inReplyTo":"pull.2110.git.git.1764205835.gitgitgadget@gmail.com","subject":"[PATCH 02/13] meson: undo Patrick's changes concerning Rust","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-11-27T01:10:24Z","receivedAt":"2025-11-27T01:10:41Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n meson.build        | 19 ++-----------------\n meson_options.txt  |  4 ++--\n src/cargo-meson.sh | 39 ---------------------------------------\n src/meson.build    | 41 -----------------------------------------\n 4 files changed, 4 insertions(+), 99 deletions(-)\n delete mode 100755 src/cargo-meson.sh\n delete mode 100644 src/meson.build\n\ndiff --git a/meson.build b/meson.build\nindex 2b763f7c53..8966596ee8 100644\n--- a/meson.build\n+++ b/meson.build\n@@ -220,7 +220,7 @@ project('git', 'c',\n   # learned to define __STDC_VERSION__ with C11 and later. We thus require\n   # GNU C99 and fall back to C11. Meson only learned to handle the fallback\n   # with version 1.3.0, so on older versions we use GNU C99 unconditionally.\n-  default_options: meson.version().version_compare('>=1.3.0') ? ['rust_std=2018', 'c_std=gnu99,c11'] : ['rust_std=2018', 'c_std=gnu99'],\n+  default_options: meson.version().version_compare('>=1.3.0') ? ['c_std=gnu99,c11'] : ['c_std=gnu99'],\n )\n \n fs = import('fs')\n@@ -1709,21 +1709,6 @@ version_def_h = custom_target(\n )\n libgit_sources += version_def_h\n \n-cargo = find_program('cargo', dirs: program_path, native: true, required: get_option('rust'))\n-rust_option = get_option('rust').disable_auto_if(not cargo.found())\n-if rust_option.allowed()\n-  subdir('src')\n-  libgit_c_args += '-DWITH_RUST'\n-\n-  if host_machine.system() == 'windows'\n-    libgit_dependencies += compiler.find_library('userenv')\n-  endif\n-else\n-  libgit_sources += [\n-    'varint.c',\n-  ]\n-endif\n-\n libgit = declare_dependency(\n   link_with: static_library('git',\n     sources: libgit_sources,\n@@ -2270,7 +2255,7 @@ summary({\n   'pcre2': pcre2,\n   'perl': perl_features_enabled,\n   'python': target_python.found(),\n-  'rust': rust_option.allowed(),\n+  'rust': get_option('with_rust'),\n }, section: 'Auto-detected features', bool_yn: true)\n \n summary({\ndiff --git a/meson_options.txt b/meson_options.txt\nindex 143dee9237..0059b7c7df 100644\n--- a/meson_options.txt\n+++ b/meson_options.txt\n@@ -71,8 +71,8 @@ option('zlib_backend', type: 'combo', choices: ['auto', 'zlib', 'zlib-ng'], valu\n # Build tweaks.\n option('breaking_changes', type: 'boolean', value: false,\n   description: 'Enable upcoming breaking changes.')\n-option('rust', type: 'feature', value: 'auto',\n-  description: 'Enable building with Rust.')\n+option('with_rust', type: 'boolean', value: true,\n+  description: 'Enable building with Rust, true by default.')\n option('macos_use_homebrew_gettext', type: 'boolean', value: true,\n   description: 'Use gettext from Homebrew instead of the slightly-broken system-provided one.')\n \ndiff --git a/src/cargo-meson.sh b/src/cargo-meson.sh\ndeleted file mode 100755\nindex 3998db0435..0000000000\n--- a/src/cargo-meson.sh\n+++ /dev/null\n@@ -1,39 +0,0 @@\n-#!/bin/sh\n-\n-if test \"$#\" -lt 2\n-then\n-\texit 1\n-fi\n-\n-SOURCE_DIR=\"$1\"\n-BUILD_DIR=\"$2\"\n-BUILD_TYPE=debug\n-\n-shift 2\n-\n-for arg\n-do\n-\tcase \"$arg\" in\n-\t--release)\n-\t\tBUILD_TYPE=release;;\n-\tesac\n-done\n-\n-cargo build --lib --quiet --manifest-path=\"$SOURCE_DIR/Cargo.toml\" --target-dir=\"$BUILD_DIR\" \"$@\"\n-RET=$?\n-if test $RET -ne 0\n-then\n-\texit $RET\n-fi\n-\n-case \"$(cargo -vV | sed -s 's/^host: \\(.*\\)$/\\1/')\" in\n-\t*-windows-*)\n-\t\tLIBNAME=gitcore.lib;;\n-\t*)\n-\t\tLIBNAME=libgitcore.a;;\n-esac\n-\n-if ! cmp \"$BUILD_DIR/$BUILD_TYPE/$LIBNAME\" \"$BUILD_DIR/libgitcore.a\" >/dev/null 2>&1\n-then\n-\tcp \"$BUILD_DIR/$BUILD_TYPE/$LIBNAME\" \"$BUILD_DIR/libgitcore.a\"\n-fi\ndiff --git a/src/meson.build b/src/meson.build\ndeleted file mode 100644\nindex 25b9ad5a14..0000000000\n--- a/src/meson.build\n+++ /dev/null\n@@ -1,41 +0,0 @@\n-libgit_rs_sources = [\n-  'lib.rs',\n-  'varint.rs',\n-]\n-\n-# Unfortunately we must use a wrapper command to move the output file into the\n-# current build directory. This can fixed once `cargo build --artifact-dir`\n-# stabilizes. See https://github.com/rust-lang/cargo/issues/6790 for that\n-# effort.\n-cargo_command = [\n-  shell,\n-  meson.current_source_dir() / 'cargo-meson.sh',\n-  meson.project_source_root(),\n-  meson.current_build_dir(),\n-]\n-if get_option('buildtype') == 'release'\n-  cargo_command += '--release'\n-endif\n-\n-libgit_rs = custom_target('git_rs',\n-  input: libgit_rs_sources + [\n-    meson.project_source_root() / 'Cargo.toml',\n-  ],\n-  output: 'libgitcore.a',\n-  command: cargo_command,\n-)\n-libgit_dependencies += declare_dependency(link_with: libgit_rs)\n-\n-if get_option('tests')\n-  test('rust', cargo,\n-    args: [\n-      'test',\n-      '--manifest-path',\n-      meson.project_source_root() / 'Cargo.toml',\n-      '--target-dir',\n-      meson.current_build_dir() / 'target',\n-    ],\n-    timeout: 0,\n-    protocol: 'rust',\n-  )\n-endif\n-- \ngitgitgadget\n\n"},{"id":"531352","messageId":"e49ffba5c99ce40b67500bac52a36ae83d10647b.1764205835.git.gitgitgadget@gmail.com","threadId":"64539","inReplyTo":"pull.2110.git.git.1764205835.gitgitgadget@gmail.com","subject":"[PATCH 03/13] cargo: convert from a crate to a workspace","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-11-27T01:10:25Z","receivedAt":"2025-11-27T01:10:43Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n Cargo.toml                          | 15 +++++----------\n rust/gitcore/Cargo.toml             | 10 ++++++++++\n {src => rust/gitcore/src}/lib.rs    |  0\n {src => rust/gitcore/src}/varint.rs |  0\n 4 files changed, 15 insertions(+), 10 deletions(-)\n create mode 100644 rust/gitcore/Cargo.toml\n rename {src => rust/gitcore/src}/lib.rs (100%)\n rename {src => rust/gitcore/src}/varint.rs (100%)\n\ndiff --git a/Cargo.toml b/Cargo.toml\nindex 2f51bf5d5f..02273f5142 100644\n--- a/Cargo.toml\n+++ b/Cargo.toml\n@@ -1,10 +1,5 @@\n-[package]\n-name = \"gitcore\"\n-version = \"0.1.0\"\n-edition = \"2018\"\n-rust-version = \"1.49.0\"\n-\n-[lib]\n-crate-type = [\"staticlib\"]\n-\n-[dependencies]\n+[workspace]\n+members = [\n+    \"rust/gitcore\",\n+]\n+resolver = \"2\"\ndiff --git a/rust/gitcore/Cargo.toml b/rust/gitcore/Cargo.toml\nnew file mode 100644\nindex 0000000000..2f51bf5d5f\n--- /dev/null\n+++ b/rust/gitcore/Cargo.toml\n@@ -0,0 +1,10 @@\n+[package]\n+name = \"gitcore\"\n+version = \"0.1.0\"\n+edition = \"2018\"\n+rust-version = \"1.49.0\"\n+\n+[lib]\n+crate-type = [\"staticlib\"]\n+\n+[dependencies]\ndiff --git a/src/lib.rs b/rust/gitcore/src/lib.rs\nsimilarity index 100%\nrename from src/lib.rs\nrename to rust/gitcore/src/lib.rs\ndiff --git a/src/varint.rs b/rust/gitcore/src/varint.rs\nsimilarity index 100%\nrename from src/varint.rs\nrename to rust/gitcore/src/varint.rs\n-- \ngitgitgadget\n\n"},{"id":"531353","messageId":"a4b79e70c152a8774b59822763045456a8c6b828.1764205835.git.gitgitgadget@gmail.com","threadId":"64539","inReplyTo":"pull.2110.git.git.1764205835.gitgitgadget@gmail.com","subject":"[PATCH 04/13] build: build Rust with Makefile and Meson","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-11-27T01:10:26Z","receivedAt":"2025-11-27T01:10:44Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n Makefile            | 91 ++++++++++++++++++++++++++++++++++++++-------\n meson.build         | 63 ++++++++++++++++++++++++++++++-\n rust/build-crate.sh | 58 +++++++++++++++++++++++++++++\n 3 files changed, 197 insertions(+), 15 deletions(-)\n create mode 100755 rust/build-crate.sh\n\ndiff --git a/Makefile b/Makefile\nindex 28bc00e648..0aa6b3ae73 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -483,6 +483,14 @@ include shared.mak\n # Define LIBPCREDIR=/foo/bar if your PCRE header and library files are\n # in /foo/bar/include and /foo/bar/lib directories.\n #\n+# == Optional Rust support ==\n+#\n+# Define WITH_RUST=false if you don't want to include features and subsystems\n+# written in Rust into Git. For now, Rust is still an optional feature of the\n+# build process. With Git 3.0 though, Rust will be mandatory.\n+#\n+# Building Rust code requires Cargo.\n+#\n # == SHA-1 and SHA-256 defines ==\n #\n # === SHA-1 backend ===\n@@ -919,9 +927,55 @@ TEST_SHELL_PATH = $(SHELL_PATH)\n \n LIB_FILE = libgit.a\n \n-GITLIBS = common-main.o $(LIB_FILE)\n EXTLIBS =\n \n+GIT_BUILD_DIR := $(CURDIR)\n+export GIT_BUILD_DIR\n+\n+RUST_CRATES := gitcore\n+.PHONY: rust-compile rust-clean\n+\n+WITH_RUST ?= true\n+ifeq ($(WITH_RUST),true)\n+\n+ifeq ($(DEBUG), 1)\n+\tRUST_BUILD_MODE := debug\n+else\n+\tRUST_BUILD_MODE := release\n+endif\n+\n+RUST_LIBS := $(foreach c,$(RUST_CRATES),$(GIT_BUILD_DIR)/lib$(c).a)\n+\n+rust-compile:\n+\t@for c in $(RUST_CRATES); do \\\n+\t\techo \"Building $$c...\"; \\\n+\t\t./rust/build-crate.sh $(GIT_BUILD_DIR) $(RUST_BUILD_MODE) $$c || exit $$?; \\\n+\tdone\n+\n+rust-clean:\n+\t$(RM) $(RUST_LIBS) Cargo.lock\n+\tcargo clean\n+\n+$(GIT_BUILD_DIR)/lib%.a:\n+\techo $(RUST_LIBS)\n+\t./rust/build-crate.sh $(GIT_BUILD_DIR) $(RUST_BUILD_MODE) $*\n+\n+UNAME_S := $(shell uname -s)\n+ifeq ($(UNAME_S),Linux)\n+\tEXTLIBS += -ldl\n+endif\n+\n+else ifeq ($(WITH_RUST),false)\n+rust-compile:\n+\t:\n+rust-clean:\n+\t:\n+else\n+$(error 'WITH_RUST' must be true or false)\n+endif\n+\n+GITLIBS = common-main.o $(LIB_FILE)\n+\n GIT_USER_AGENT = git/$(GIT_VERSION)\n \n ifeq ($(wildcard sha1collisiondetection/lib/sha1.h),sha1collisiondetection/lib/sha1.h)\n@@ -938,6 +992,11 @@ CC_LD_DYNPATH = -Wl,-rpath,\n BASIC_CFLAGS = -I.\n BASIC_LDFLAGS =\n \n+ifeq ($(WITH_RUST),true)\n+\tBASIC_CFLAGS += -DWITH_RUST\n+\tBASIC_CFLAGS += -fPIE\n+endif\n+\n # library flags\n ARFLAGS = rcs\n PTHREAD_CFLAGS =\n@@ -1303,7 +1362,9 @@ LIB_OBJS += urlmatch.o\n LIB_OBJS += usage.o\n LIB_OBJS += userdiff.o\n LIB_OBJS += utf8.o\n+ifeq ($(WITH_RUST),false)\n LIB_OBJS += varint.o\n+endif\n LIB_OBJS += version.o\n LIB_OBJS += versioncmp.o\n LIB_OBJS += walker.o\n@@ -2409,6 +2470,10 @@ endif\n # from the dependency list, that would make each entry appear twice.\n LIBS = $(filter-out %.o, $(GITLIBS)) $(EXTLIBS)\n \n+ifeq ($(WITH_RUST),true)\n+LIBS += $(RUST_LIBS)\n+endif\n+\n BASIC_CFLAGS += $(COMPAT_CFLAGS)\n LIB_OBJS += $(COMPAT_OBJS)\n \n@@ -2564,7 +2629,7 @@ git.sp git.s git.o: EXTRA_CPPFLAGS = \\\n \t'-DGIT_MAN_PATH=\"$(mandir_relative_SQ)\"' \\\n \t'-DGIT_INFO_PATH=\"$(infodir_relative_SQ)\"'\n \n-git$X: git.o GIT-LDFLAGS $(BUILTIN_OBJS) $(GITLIBS)\n+git$X: git.o GIT-LDFLAGS $(BUILTIN_OBJS) $(GITLIBS) rust-compile\n \t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) \\\n \t\t$(filter %.o,$^) $(LIBS)\n \n@@ -2889,17 +2954,17 @@ headless-git.o: compat/win32/headless.c GIT-CFLAGS\n headless-git$X: headless-git.o git.res GIT-LDFLAGS\n \t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) $(ALL_LDFLAGS) -mwindows -o $@ $< git.res\n \n-git-%$X: %.o GIT-LDFLAGS $(GITLIBS)\n+git-%$X: %.o GIT-LDFLAGS $(GITLIBS) rust-compile\n \t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)\n \n-git-imap-send$X: imap-send.o $(IMAP_SEND_BUILDDEPS) GIT-LDFLAGS $(GITLIBS)\n+git-imap-send$X: imap-send.o $(IMAP_SEND_BUILDDEPS) GIT-LDFLAGS $(GITLIBS) rust-compile\n \t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \\\n \t\t$(IMAP_SEND_LDFLAGS) $(LIBS)\n \n-git-http-fetch$X: http.o http-walker.o http-fetch.o GIT-LDFLAGS $(GITLIBS)\n+git-http-fetch$X: http.o http-walker.o http-fetch.o GIT-LDFLAGS $(GITLIBS) rust-compile\n \t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \\\n \t\t$(CURL_LIBCURL) $(LIBS)\n-git-http-push$X: http.o http-push.o GIT-LDFLAGS $(GITLIBS)\n+git-http-push$X: http.o http-push.o GIT-LDFLAGS $(GITLIBS) rust-compile\n \t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \\\n \t\t$(CURL_LIBCURL) $(EXPAT_LIBEXPAT) $(LIBS)\n \n@@ -2909,11 +2974,11 @@ $(REMOTE_CURL_ALIASES): $(REMOTE_CURL_PRIMARY)\n \tln -s $< $@ 2>/dev/null || \\\n \tcp $< $@\n \n-$(REMOTE_CURL_PRIMARY): remote-curl.o http.o http-walker.o GIT-LDFLAGS $(GITLIBS)\n+$(REMOTE_CURL_PRIMARY): remote-curl.o http.o http-walker.o GIT-LDFLAGS $(GITLIBS) rust-compile\n \t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \\\n \t\t$(CURL_LIBCURL) $(EXPAT_LIBEXPAT) $(LIBS)\n \n-scalar$X: scalar.o GIT-LDFLAGS $(GITLIBS)\n+scalar$X: scalar.o GIT-LDFLAGS $(GITLIBS) rust-compile\n \t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) \\\n \t\t$(filter %.o,$^) $(LIBS)\n \n@@ -3286,7 +3351,7 @@ perf: all\n \n t/helper/test-tool$X: $(patsubst %,t/helper/%,$(TEST_BUILTINS_OBJS)) $(UNIT_TEST_DIR)/test-lib.o\n \n-t/helper/test-%$X: t/helper/test-%.o GIT-LDFLAGS $(GITLIBS)\n+t/helper/test-%$X: t/helper/test-%.o GIT-LDFLAGS $(GITLIBS) rust-compile\n \t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(filter %.a,$^) $(LIBS)\n \n check-sha1:: t/helper/test-tool$X\n@@ -3751,7 +3816,7 @@ cocciclean:\n \t$(RM) -r .build/contrib/coccinelle\n \t$(RM) contrib/coccinelle/*.cocci.patch\n \n-clean: profile-clean coverage-clean cocciclean\n+clean: profile-clean coverage-clean cocciclean rust-clean\n \t$(RM) -r .build $(UNIT_TEST_BIN)\n \t$(RM) GIT-TEST-SUITES\n \t$(RM) po/git.pot po/git-core.pot\n@@ -3907,13 +3972,13 @@ FUZZ_CXXFLAGS ?= $(ALL_CFLAGS)\n .PHONY: fuzz-all\n fuzz-all: $(FUZZ_PROGRAMS)\n \n-$(FUZZ_PROGRAMS): %: %.o oss-fuzz/dummy-cmd-main.o $(GITLIBS) GIT-LDFLAGS\n+$(FUZZ_PROGRAMS): %: %.o oss-fuzz/dummy-cmd-main.o $(GITLIBS) GIT-LDFLAGS rust-compile\n \t$(QUIET_LINK)$(FUZZ_CXX) $(FUZZ_CXXFLAGS) -o $@ $(ALL_LDFLAGS) \\\n \t\t-Wl,--allow-multiple-definition \\\n \t\t$(filter %.o,$^) $(filter %.a,$^) $(LIBS) $(LIB_FUZZING_ENGINE)\n \n $(UNIT_TEST_PROGS): $(UNIT_TEST_BIN)/%$X: $(UNIT_TEST_DIR)/%.o $(UNIT_TEST_OBJS) \\\n-\t$(GITLIBS) GIT-LDFLAGS\n+\t$(GITLIBS) GIT-LDFLAGS rust-compile\n \t$(call mkdir_p_parent_template)\n \t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) \\\n \t\t$(filter %.o,$^) $(filter %.a,$^) $(LIBS)\n@@ -3932,7 +3997,7 @@ $(UNIT_TEST_DIR)/clar.suite: $(UNIT_TEST_DIR)/clar-decls.h $(UNIT_TEST_DIR)/gene\n $(UNIT_TEST_DIR)/clar/clar.o: $(UNIT_TEST_DIR)/clar.suite\n $(CLAR_TEST_OBJS): $(UNIT_TEST_DIR)/clar-decls.h\n $(CLAR_TEST_OBJS): EXTRA_CPPFLAGS = -I$(UNIT_TEST_DIR)\n-$(CLAR_TEST_PROG): $(UNIT_TEST_DIR)/clar.suite $(CLAR_TEST_OBJS) $(GITLIBS) GIT-LDFLAGS\n+$(CLAR_TEST_PROG): $(UNIT_TEST_DIR)/clar.suite $(CLAR_TEST_OBJS) $(GITLIBS) GIT-LDFLAGS rust-compile\n \t$(call mkdir_p_parent_template)\n \t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)\n \ndiff --git a/meson.build b/meson.build\nindex 8966596ee8..94ec5e4ac0 100644\n--- a/meson.build\n+++ b/meson.build\n@@ -267,6 +267,55 @@ version_gen_environment.set('GIT_DATE', get_option('build_date'))\n version_gen_environment.set('GIT_USER_AGENT', get_option('user_agent'))\n version_gen_environment.set('GIT_VERSION', get_option('version'))\n \n+rust_crates = ['gitcore']\n+\n+rust_builds = []\n+with_rust = get_option('with_rust')\n+if with_rust\n+  rustc = find_program('rustc', required : false)\n+  cargo = find_program('cargo', required : false)\n+\n+  if not rustc.found() or not cargo.found()\n+    error('Rust toolchain not found. Reconfigure with -Dwith_rust=false')\n+  endif\n+\n+  if get_option('optimization') in ['2', '3', 's', 'z']\n+    rust_build_profile = 'release'\n+  else\n+    rust_build_profile = 'debug'\n+  endif\n+\n+  # Run `rustup show active-toolchain` and capture output\n+  rustup_out = run_command('rustup', 'show', 'active-toolchain',\n+                           check: true).stdout().strip()\n+\n+  rust_environment = script_environment\n+\n+  foreach crate : rust_crates\n+    if rustup_out.contains('windows-msvc')\n+      libfile = crate + '.lib'\n+    else\n+      libfile = 'lib' + crate + '.a'\n+    endif\n+\n+    rust_builds += custom_target(\n+      'rust_build_'+crate,\n+      output: libfile,\n+      build_by_default: true,\n+      build_always_stale: true,\n+      command: [\n+        meson.project_source_root() / 'rust' / 'build-crate.sh',\n+        meson.current_build_dir(), rust_build_profile, crate,\n+      ],\n+      install: false,\n+      env: rust_environment,\n+    )\n+  endforeach\n+else\n+  message('Rust components disabled (-Dwith_rust=false)')\n+endif\n+\n+\n compiler = meson.get_compiler('c')\n \n libgit_sources = [\n@@ -548,6 +597,10 @@ libgit_sources = [\n   'xdiff/xutils.c',\n ]\n \n+if not with_rust\n+  libgit_sources += 'varint.c'\n+endif\n+\n libgit_sources += custom_target(\n   input: 'command-list.txt',\n   output: 'command-list.h',\n@@ -782,6 +835,10 @@ libgit_c_args = [\n   '-DSHELL_PATH=\"' + fs.as_posix(target_shell.full_path()) + '\"',\n ]\n \n+if with_rust\n+  libgit_c_args += '-DWITH_RUST'\n+endif\n+\n system_attributes = get_option('gitattributes')\n if system_attributes != ''\n   libgit_c_args += '-DETC_GITATTRIBUTES=\"' + system_attributes + '\"'\n@@ -1274,6 +1331,7 @@ elif host_machine.system() == 'windows'\n   ]\n \n   libgit_dependencies += compiler.find_library('ntdll')\n+  libgit_dependencies += compiler.find_library('userenv')\n   libgit_include_directories += 'compat/win32'\n   if compiler.get_id() == 'msvc'\n     libgit_include_directories += 'compat/vcbuild/include'\n@@ -1710,14 +1768,15 @@ version_def_h = custom_target(\n libgit_sources += version_def_h\n \n libgit = declare_dependency(\n-  link_with: static_library('git',\n+  link_with: [\n+    static_library('git',\n     sources: libgit_sources,\n     c_args: libgit_c_args + [\n       '-DGIT_VERSION_H=\"' + version_def_h.full_path() + '\"',\n     ],\n     dependencies: libgit_dependencies,\n     include_directories: libgit_include_directories,\n-  ),\n+  )] + rust_builds,\n   compile_args: libgit_c_args,\n   dependencies: libgit_dependencies,\n   include_directories: libgit_include_directories,\ndiff --git a/rust/build-crate.sh b/rust/build-crate.sh\nnew file mode 100755\nindex 0000000000..b3debf70e5\n--- /dev/null\n+++ b/rust/build-crate.sh\n@@ -0,0 +1,58 @@\n+#!/bin/sh\n+\n+rustc -vV || exit $?\n+cargo --version || exit $?\n+\n+dir_git_root=${0%/*}\n+dir_build=$1\n+rust_build_profile=$2\n+crate=$3\n+\n+if [ \"$dir_git_root\" = \"\" ]; then\n+  echo \"did not specify the directory for the root of git\"\n+  exit 1\n+fi\n+\n+if [ \"$dir_build\" = \"\" ]; then\n+  echo \"did not specify the build directory\"\n+  exit 1\n+fi\n+\n+if [ \"$rust_build_profile\" = \"\" ]; then\n+  echo \"did not specify the rust_build_profile\"\n+  exit 1\n+fi\n+\n+if [ \"$rust_build_profile\" = \"release\" ]; then\n+  rust_args=\"--release\"\n+  export RUSTFLAGS=''\n+elif [ \"$rust_build_profile\" = \"debug\" ]; then\n+  rust_args=\"\"\n+  export RUSTFLAGS='-C debuginfo=2 -C opt-level=1 -C force-frame-pointers=yes'\n+else\n+  echo \"illegal rust_build_profile value $rust_build_profile\"\n+  exit 1\n+fi\n+\n+libfile=\"lib${crate}.a\"\n+if rustc -vV | grep windows-msvc; then\n+  libfile=\"${crate}.lib\"\n+  PATH=\"$(echo $PATH | tr ':' '\\n' | grep -Ev \"^(/mingw64/bin|/usr/bin)$\" | paste -sd: -):/mingw64/bin:/usr/bin\"\n+fi\n+\n+CARGO_TARGET_DIR=$dir_git_root/.build/rust/$crate\n+export CARGO_TARGET_DIR\n+\n+cargo clean && pwd && USE_LINKING=\"false\" cargo build -p $crate $rust_args\n+\n+src=$CARGO_TARGET_DIR/$rust_build_profile/$libfile\n+dst=$dir_build/$libfile\n+\n+if [ ! -f $src ]; then\n+  echo >&2 \"::error:: cannot find path of static library $src is not a file or does not exist\"\n+  exit 5\n+fi\n+\n+rm $dst 2>/dev/null\n+echo mv $src $dst\n+mv $src $dst\n-- \ngitgitgadget\n\n"},{"id":"531354","messageId":"66648e01e486f2c74202cb15618b95e87e6aea18.1764205835.git.gitgitgadget@gmail.com","threadId":"64539","inReplyTo":"pull.2110.git.git.1764205835.gitgitgadget@gmail.com","subject":"[PATCH 05/13] .gitignore: ignore /generated/","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-11-27T01:10:27Z","receivedAt":"2025-11-27T01:10:45Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n .gitignore | 1 +\n Makefile   | 2 +-\n 2 files changed, 2 insertions(+), 1 deletion(-)\n\ndiff --git a/.gitignore b/.gitignore\nindex 78a45cb5be..5df42eee5a 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -258,3 +258,4 @@ Release/\n /contrib/buildsystems/out\n /contrib/libgit-rs/target\n /contrib/libgit-sys/target\n+/generated/\ndiff --git a/Makefile b/Makefile\nindex 0aa6b3ae73..8a73afca16 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -953,7 +953,7 @@ rust-compile:\n \tdone\n \n rust-clean:\n-\t$(RM) $(RUST_LIBS) Cargo.lock\n+\t$(RM) $(RUST_LIBS) Cargo.lock generated/\n \tcargo clean\n \n $(GIT_BUILD_DIR)/lib%.a:\n-- \ngitgitgadget\n\n"},{"id":"531355","messageId":"4a17edade9aa18eaa7a173bf2bd5eecda74508c3.1764205835.git.gitgitgadget@gmail.com","threadId":"64539","inReplyTo":"pull.2110.git.git.1764205835.gitgitgadget@gmail.com","subject":"[PATCH 06/13] cargo: create crate generate-headers","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-11-27T01:10:28Z","receivedAt":"2025-11-27T01:10:47Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n Cargo.toml                        |  1 +\n rust/cbindgen-template.toml       | 13 +++++++++\n rust/generate-headers/Cargo.toml  | 12 +++++++++\n rust/generate-headers/src/main.rs | 44 +++++++++++++++++++++++++++++++\n 4 files changed, 70 insertions(+)\n create mode 100644 rust/cbindgen-template.toml\n create mode 100644 rust/generate-headers/Cargo.toml\n create mode 100644 rust/generate-headers/src/main.rs\n\ndiff --git a/Cargo.toml b/Cargo.toml\nindex 02273f5142..b7b5890934 100644\n--- a/Cargo.toml\n+++ b/Cargo.toml\n@@ -1,5 +1,6 @@\n [workspace]\n members = [\n+    \"rust/generate-headers\",\n     \"rust/gitcore\",\n ]\n resolver = \"2\"\ndiff --git a/rust/cbindgen-template.toml b/rust/cbindgen-template.toml\nnew file mode 100644\nindex 0000000000..13c192d60e\n--- /dev/null\n+++ b/rust/cbindgen-template.toml\n@@ -0,0 +1,13 @@\n+## don't include any system files, not even git-compat-util.h\n+# sys_includes = []\n+\n+autogen_warning = \"/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */\"\n+\n+language = \"C\"\n+no_includes = true\n+usize_is_size_t = true\n+style = \"tag\"\n+tab_width = 8\n+\n+[parse]\n+parse_deps = false\ndiff --git a/rust/generate-headers/Cargo.toml b/rust/generate-headers/Cargo.toml\nnew file mode 100644\nindex 0000000000..9216d83d75\n--- /dev/null\n+++ b/rust/generate-headers/Cargo.toml\n@@ -0,0 +1,12 @@\n+[package]\n+name = \"generate-headers\"\n+version = \"0.1.0\"\n+edition = \"2018\"\n+\n+[dependencies]\n+cbindgen = \"0.24.0\"\n+textwrap = \"=0.16.1\"\n+once_cell = \"=1.20.3\"\n+unicode-width = \"=0.1.13\"\n+quote = \"=1.0.41\"\n+syn = \"=2.0.106\"\ndiff --git a/rust/generate-headers/src/main.rs b/rust/generate-headers/src/main.rs\nnew file mode 100644\nindex 0000000000..da68c8ae3d\n--- /dev/null\n+++ b/rust/generate-headers/src/main.rs\n@@ -0,0 +1,44 @@\n+use cbindgen;\n+\n+use std::io::Write;\n+use std::path::PathBuf;\n+use cbindgen::Config;\n+\n+fn generate_header<F>(crate_name: String, editor: F)\n+where\n+    F: Fn(&mut Config)\n+{\n+    let dir_workspace = PathBuf::default();\n+    let dir_rust = dir_workspace.join(\"rust\");\n+    let dir_crate = dir_rust.join(crate_name.clone());\n+    let dir_generated = dir_workspace.join(\"generated\");\n+    if !dir_generated.exists() {\n+        std::fs::create_dir(dir_generated.clone()).unwrap();\n+    }\n+\n+    let file_cbindgen = dir_rust.join(\"cbindgen-template.toml\");\n+    let file_out = dir_generated.join(format!(\"{}.h\", crate_name.clone()));\n+\n+    let mut config = Config::from_file(file_cbindgen.display().to_string().as_str()).unwrap();\n+    config.include_guard = Some(format!(\"{}_H\", crate_name.to_uppercase()));\n+\n+    editor(&mut config);\n+\n+    let mut buffer = Vec::<u8>::new();\n+    cbindgen::Builder::new()\n+        .with_crate(dir_crate.clone())\n+        .with_config(config)\n+        .with_std_types(true)\n+        .generate()\n+        .expect(\"Unable to generate bindings\")\n+        .write(&mut buffer);\n+\n+    let mut fd = std::fs::File::create(file_out).unwrap();\n+    fd.write(buffer.as_slice()).unwrap();\n+}\n+\n+fn main() {\n+    // cargo run -p generate-headers\n+\n+    generate_header(String::from(\"gitcore\"), |_|{});\n+}\n-- \ngitgitgadget\n\n"},{"id":"531356","messageId":"e51a78cfb5dd6d34948dc26d21513450469c6790.1764205835.git.gitgitgadget@gmail.com","threadId":"64539","inReplyTo":"pull.2110.git.git.1764205835.gitgitgadget@gmail.com","subject":"[PATCH 07/13] cargo: create crate link-with-c","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-11-27T01:10:29Z","receivedAt":"2025-11-27T01:10:48Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n rust/link-with-c/Cargo.toml |  9 +++++\n rust/link-with-c/src/lib.rs | 77 +++++++++++++++++++++++++++++++++++++\n 2 files changed, 86 insertions(+)\n create mode 100644 rust/link-with-c/Cargo.toml\n create mode 100644 rust/link-with-c/src/lib.rs\n\ndiff --git a/rust/link-with-c/Cargo.toml b/rust/link-with-c/Cargo.toml\nnew file mode 100644\nindex 0000000000..adb38fdc9c\n--- /dev/null\n+++ b/rust/link-with-c/Cargo.toml\n@@ -0,0 +1,9 @@\n+[package]\n+name = \"link-with-c\"\n+version = \"0.1.0\"\n+edition = \"2018\"\n+\n+[lib]\n+crate-type = [\"staticlib\", \"rlib\"]\n+\n+[dependencies]\ndiff --git a/rust/link-with-c/src/lib.rs b/rust/link-with-c/src/lib.rs\nnew file mode 100644\nindex 0000000000..f6faaf774d\n--- /dev/null\n+++ b/rust/link-with-c/src/lib.rs\n@@ -0,0 +1,77 @@\n+use std::collections::HashMap;\n+use std::path::PathBuf;\n+\n+\n+fn parse_bool_from_str(value: &str) -> bool {\n+    match value {\n+        \"1\" | \"true\"  | \"yes\" | \"on\"  => true,\n+        \"0\" | \"false\" | \"no\"  | \"off\" => false,\n+        _ => false\n+    }\n+}\n+\n+fn parse_bool_from_option(value: Option<&String>, default: bool) -> bool {\n+    match value {\n+        Some(v) => {\n+            parse_bool_from_str(v.as_str())\n+        },\n+        None => default,\n+    }\n+}\n+\n+/// To build without linking against C libraries run `USE_LINKING=false cargo build`\n+/// To run tests set GIT_BUILD_DIR and run `USE_LINKING=true cargo test`\n+pub struct BuildHelper {\n+    crate_env: HashMap<String, String>,\n+}\n+\n+\n+impl BuildHelper {\n+    pub fn new(crate_env: HashMap<String, String>) -> Self {\n+        Self {\n+            crate_env,\n+        }\n+    }\n+\n+    pub fn crate_name(&self) -> String {\n+        self.crate_env[\"CARGO_PKG_NAME\"].clone()\n+    }\n+\n+    pub fn dir_crate(&self) -> PathBuf {\n+        PathBuf::from(self.crate_env[\"CARGO_MANIFEST_DIR\"].clone())\n+    }\n+\n+    pub fn build(self) {\n+        let use_linking = parse_bool_from_option(self.crate_env.get(\"USE_LINKING\"), self.crate_env.get(\"CARGO_TARGET_DIR\").is_none());\n+        let dir_crate = self.dir_crate();\n+        let dir_git = dir_crate.parent().unwrap().parent().unwrap();\n+\n+        println!(\"cargo:rerun-if-changed={}\", dir_git.display());\n+\n+        if use_linking {\n+            if let Some(git_build_dir) = self.crate_env.get(\"GIT_BUILD_DIR\") {\n+                let path_git_build_dir = PathBuf::from(git_build_dir);\n+                let is_abs = path_git_build_dir.is_absolute();\n+                if !is_abs {\n+                    panic!(\"GIT_BUILD_DIR must be an absolute path: {}'\", path_git_build_dir.display());\n+                }\n+                if !path_git_build_dir.is_dir() {\n+                    panic!(\"'GIT_BUILD_DIR' is not a directory: {}\", path_git_build_dir.display());\n+                }\n+                println!(\"cargo:rustc-link-search=native={}\", git_build_dir);\n+            } else {\n+                panic!(\"environment variable 'GIT_BUILD_DIR' is not set\");\n+            }\n+\n+            println!(\"cargo:rustc-link-lib=static=git\");\n+            println!(\"cargo:rustc-link-lib=pcre2-8\");\n+            if self.crate_env.get(\"ZLIB_NG\").is_some() {\n+                println!(\"cargo:rustc-link-lib=z-ng\");\n+            } else {\n+                println!(\"cargo:rustc-link-lib=z\");\n+            }\n+        } else {\n+            println!(\"cargo:warning={} is not linking against C objects, `USE_LINKING=true cargo test`\", self.crate_env[\"CARGO_PKG_NAME\"]);\n+        }\n+    }\n+}\n-- \ngitgitgadget\n\n"},{"id":"531357","messageId":"288bdb39d23a0130876cad8d52ab54cbc4463a1d.1764205835.git.gitgitgadget@gmail.com","threadId":"64539","inReplyTo":"pull.2110.git.git.1764205835.gitgitgadget@gmail.com","subject":"[PATCH 08/13] rust/gitcore: link with c","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-11-27T01:10:30Z","receivedAt":"2025-11-27T01:10:50Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n Cargo.toml              | 1 +\n rust/gitcore/Cargo.toml | 6 ++++--\n rust/gitcore/build.rs   | 7 +++++++\n 3 files changed, 12 insertions(+), 2 deletions(-)\n create mode 100644 rust/gitcore/build.rs\n\ndiff --git a/Cargo.toml b/Cargo.toml\nindex b7b5890934..4585fda0fc 100644\n--- a/Cargo.toml\n+++ b/Cargo.toml\n@@ -2,5 +2,6 @@\n members = [\n     \"rust/generate-headers\",\n     \"rust/gitcore\",\n+    \"rust/link-with-c\",\n ]\n resolver = \"2\"\ndiff --git a/rust/gitcore/Cargo.toml b/rust/gitcore/Cargo.toml\nindex 2f51bf5d5f..fadac77d42 100644\n--- a/rust/gitcore/Cargo.toml\n+++ b/rust/gitcore/Cargo.toml\n@@ -2,9 +2,11 @@\n name = \"gitcore\"\n version = \"0.1.0\"\n edition = \"2018\"\n-rust-version = \"1.49.0\"\n \n [lib]\n-crate-type = [\"staticlib\"]\n+crate-type = [\"staticlib\", \"rlib\"]\n \n [dependencies]\n+\n+[build-dependencies]\n+link-with-c = { path = \"../link-with-c\" }\ndiff --git a/rust/gitcore/build.rs b/rust/gitcore/build.rs\nnew file mode 100644\nindex 0000000000..7427a5e45d\n--- /dev/null\n+++ b/rust/gitcore/build.rs\n@@ -0,0 +1,7 @@\n+use link_with_c::BuildHelper;\n+\n+fn main() {\n+    BuildHelper::new(std::env::vars().collect())\n+        // .generate_header(|_|{})\n+        .build();\n+}\n-- \ngitgitgadget\n\n"},{"id":"531358","messageId":"8649a6c0f6633c13fa1c0ae2ff05889043d1d8b7.1764205835.git.gitgitgadget@gmail.com","threadId":"64539","inReplyTo":"pull.2110.git.git.1764205835.gitgitgadget@gmail.com","subject":"[PATCH 09/13] varint.h: unsigned char -> uint8_t","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-11-27T01:10:31Z","receivedAt":"2025-11-27T01:10:52Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n varint.h | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/varint.h b/varint.h\nindex eb401935bd..a618a4b1ce 100644\n--- a/varint.h\n+++ b/varint.h\n@@ -1,7 +1,7 @@\n #ifndef VARINT_H\n #define VARINT_H\n \n-uint8_t encode_varint(uint64_t, unsigned char *);\n-uint64_t decode_varint(const unsigned char **);\n+uint8_t encode_varint(uint64_t, uint8_t *);\n+uint64_t decode_varint(const uint8_t **);\n \n #endif /* VARINT_H */\n-- \ngitgitgadget\n\n"},{"id":"531359","messageId":"72429e04843a90e3cf9a1d697c1ca7f57de8950f.1764205835.git.gitgitgadget@gmail.com","threadId":"64539","inReplyTo":"pull.2110.git.git.1764205835.gitgitgadget@gmail.com","subject":"[PATCH 10/13] make: delete files in generated/","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-11-27T01:10:32Z","receivedAt":"2025-11-27T01:10:53Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n Makefile | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/Makefile b/Makefile\nindex 8a73afca16..6da9db7d83 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -953,7 +953,7 @@ rust-compile:\n \tdone\n \n rust-clean:\n-\t$(RM) $(RUST_LIBS) Cargo.lock generated/\n+\t$(RM) $(RUST_LIBS) Cargo.lock generated/*.h\n \tcargo clean\n \n $(GIT_BUILD_DIR)/lib%.a:\n-- \ngitgitgadget\n\n"},{"id":"531360","messageId":"218f995c384ad7e97589d56a8ae09b6782666836.1764205835.git.gitgitgadget@gmail.com","threadId":"64539","inReplyTo":"pull.2110.git.git.1764205835.gitgitgadget@gmail.com","subject":"[PATCH 11/13] github-workflows: unify with rust parameters in make and meson","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-11-27T01:10:33Z","receivedAt":"2025-11-27T01:10:54Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\n-Drust=enabled -> -Dwith_rust=enabled\nWITH_RUST=YesPlease -> WITH_RUST=true\n\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n ci/run-build-and-tests.sh | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/ci/run-build-and-tests.sh b/ci/run-build-and-tests.sh\nindex 8bda62b921..d4f7cbd15f 100755\n--- a/ci/run-build-and-tests.sh\n+++ b/ci/run-build-and-tests.sh\n@@ -8,9 +8,9 @@\n case \"$jobname\" in\n fedora-breaking-changes-musl|linux-breaking-changes)\n \texport WITH_BREAKING_CHANGES=YesPlease\n-\texport WITH_RUST=YesPlease\n+\texport WITH_RUST=true\n \tMESONFLAGS=\"$MESONFLAGS -Dbreaking_changes=true\"\n-\tMESONFLAGS=\"$MESONFLAGS -Drust=enabled\"\n+\tMESONFLAGS=\"$MESONFLAGS -Dwith_rust=enabled\"\n \t;;\n linux-TEST-vars)\n \texport OPENSSL_SHA1_UNSAFE=YesPlease\n-- \ngitgitgadget\n\n"},{"id":"531361","messageId":"20558a8e1e037227b194e9a04c0a9c461e214fb7.1764205835.git.gitgitgadget@gmail.com","threadId":"64539","inReplyTo":"pull.2110.git.git.1764205835.gitgitgadget@gmail.com","subject":"[PATCH 12/13] github workflows: install Rust","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-11-27T01:10:34Z","receivedAt":"2025-11-27T01:10:55Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n .github/workflows/main.yml   | 62 +++++++++++++++++++++++++++++++++++-\n ci/install-dependencies.sh   | 14 ++++----\n ci/install-rust-toolchain.sh | 30 +++++++++++++++++\n ci/install-rustup.sh         | 25 +++++++++++++++\n ci/make-test-artifacts.sh    |  9 ++++++\n ci/run-build-and-tests.sh    | 13 ++++++++\n 6 files changed, 145 insertions(+), 8 deletions(-)\n create mode 100755 ci/install-rust-toolchain.sh\n create mode 100755 ci/install-rustup.sh\n\ndiff --git a/.github/workflows/main.yml b/.github/workflows/main.yml\nindex cc54824c38..396b0108a0 100644\n--- a/.github/workflows/main.yml\n+++ b/.github/workflows/main.yml\n@@ -26,6 +26,13 @@ jobs:\n     outputs:\n       enabled: ${{ steps.check-ref.outputs.enabled }}${{ steps.skip-if-redundant.outputs.enabled }}\n       skip_concurrent: ${{ steps.check-ref.outputs.skip_concurrent }}\n+      rust_version_minimum: 1.63.0\n+      rust_version_windows: 1.78.0\n+      rust_version_musl: 1.72.0\n+      ## the rust target is inferred by rustup unless specified\n+      rust_target_windows_make: x86_64-pc-windows-gnu\n+      rust_target_windows_meson: x86_64-pc-windows-msvc\n+      rust_target_32bit_linux: i686-unknown-linux-gnu\n     steps:\n       - name: try to clone ci-config branch\n         run: |\n@@ -108,12 +115,28 @@ jobs:\n     needs: ci-config\n     if: needs.ci-config.outputs.enabled == 'yes'\n     runs-on: windows-latest\n+    env:\n+      CARGO_HOME: \"/c/Users/runneradmin/.cargo\"\n     concurrency:\n       group: windows-build-${{ github.ref }}\n       cancel-in-progress: ${{ needs.ci-config.outputs.skip_concurrent == 'yes' }}\n     steps:\n     - uses: actions/checkout@v5\n     - uses: git-for-windows/setup-git-for-windows-sdk@v1\n+      with:\n+        flavor: full\n+    - name: Install rustup via github actions\n+      uses: actions-rs/toolchain@v1\n+      with:\n+        toolchain: stable\n+        profile: minimal\n+        override: false\n+    - name: Install Rust toolchain\n+      shell: bash\n+      env:\n+        RUST_VERSION: ${{ needs.ci-config.outputs.rust_version_windows }}\n+        RUST_TARGET: ${{ needs.ci-config.outputs.rust_target_windows_make }}\n+      run: ci/install-rust-toolchain.sh\n     - name: build\n       shell: bash\n       env:\n@@ -254,12 +277,26 @@ jobs:\n     needs: ci-config\n     if: needs.ci-config.outputs.enabled == 'yes'\n     runs-on: windows-latest\n+    env:\n+      CARGO_HOME: \"/c/Users/runneradmin/.cargo\"\n     concurrency:\n       group: windows-meson-build-${{ github.ref }}\n       cancel-in-progress: ${{ needs.ci-config.outputs.skip_concurrent == 'yes' }}\n     steps:\n     - uses: actions/checkout@v5\n     - uses: actions/setup-python@v6\n+    - name: Install rustup via github actions\n+      uses: actions-rs/toolchain@v1\n+      with:\n+        toolchain: stable\n+        profile: minimal\n+        override: false\n+    - name: Install Rust toolchain\n+      shell: bash\n+      env:\n+        RUST_VERSION: ${{ needs.ci-config.outputs.rust_version_windows }}\n+        RUST_TARGET: ${{ needs.ci-config.outputs.rust_target_windows_meson }}\n+      run: ci/install-rust-toolchain.sh\n     - name: Set up dependencies\n       shell: pwsh\n       run: pip install meson ninja\n@@ -329,11 +366,24 @@ jobs:\n       jobname: ${{matrix.vector.jobname}}\n       CI_JOB_IMAGE: ${{matrix.vector.pool}}\n       TEST_OUTPUT_DIRECTORY: ${{github.workspace}}/t\n+      CARGO_HOME: \"/Users/runner/.cargo\"\n     runs-on: ${{matrix.vector.pool}}\n     steps:\n     - uses: actions/checkout@v5\n     - run: ci/install-dependencies.sh\n-    - run: ci/run-build-and-tests.sh\n+    - name: Install rustup via github actions\n+      uses: actions-rs/toolchain@v1\n+      with:\n+        toolchain: stable\n+        profile: minimal\n+        override: false\n+    - name: Install Rust toolchain\n+      shell: bash\n+      env:\n+        RUST_VERSION: ${{ needs.ci-config.outputs.rust_version_minimum }}\n+      run: ci/install-rust-toolchain.sh\n+    - name: Run build and tests\n+      run: ci/run-build-and-tests.sh\n     - name: print test failures\n       if: failure() && env.FAILED_TEST_ARTIFACTS != ''\n       run: ci/print-test-failures.sh\n@@ -395,9 +445,11 @@ jobs:\n           cc: gcc\n         - jobname: linux-musl-meson\n           image: alpine:latest\n+          rust_version_override: ${{ needs.ci-config.outputs.rust_version_musl }}\n         # Supported until 2025-04-02.\n         - jobname: linux32\n           image: i386/ubuntu:focal\n+          rust_target_override: ${{ needs.ci-config.outputs.rust_target_32bit_linux }}\n         # A RHEL 8 compatible distro.  Supported until 2029-05-31.\n         - jobname: almalinux-8\n           image: almalinux:8\n@@ -409,6 +461,7 @@ jobs:\n       CC: ${{matrix.vector.cc}}\n       CI_JOB_IMAGE: ${{matrix.vector.image}}\n       CUSTOM_PATH: /custom\n+      CARGO_HOME: /home/builder/.cargo\n     runs-on: ubuntu-latest\n     container: ${{matrix.vector.image}}\n     steps:\n@@ -433,6 +486,13 @@ jobs:\n     - run: ci/install-dependencies.sh\n     - run: useradd builder --create-home\n     - run: chown -R builder .\n+    - name: Install rustup via script\n+      run: sudo --preserve-env --set-home --user=builder ci/install-rustup.sh\n+    - name: Install Rust toolchain\n+      env:\n+        RUST_VERSION: ${{ matrix.vector.rust_version_override || needs.ci-config.outputs.rust_version_minimum }}\n+        RUST_TARGET: ${{ matrix.vector.rust_target_override || '' }}\n+      run: sudo --preserve-env --set-home --user=builder ci/install-rust-toolchain.sh\n     - run: sudo --preserve-env --set-home --user=builder ci/run-build-and-tests.sh\n     - name: print test failures\n       if: failure() && env.FAILED_TEST_ARTIFACTS != ''\ndiff --git a/ci/install-dependencies.sh b/ci/install-dependencies.sh\nindex 50628ee2dd..f1b3ee2dd9 100755\n--- a/ci/install-dependencies.sh\n+++ b/ci/install-dependencies.sh\n@@ -26,7 +26,7 @@ fi\n \n case \"$distro\" in\n alpine-*)\n-\tapk add --update shadow sudo meson ninja-build gcc libc-dev curl-dev openssl-dev expat-dev gettext \\\n+\tapk add --update shadow sudo meson ninja-build gcc libc-dev curl curl-dev openssl-dev expat-dev gettext \\\n \t\tzlib-ng-dev pcre2-dev python3 musl-libintl perl-utils ncurses \\\n \t\tapache2 apache2-http2 apache2-proxy apache2-ssl apache2-webdav apr-util-dbd_sqlite3 \\\n \t\tbash cvs gnupg perl-cgi perl-dbd-sqlite perl-io-tty >/dev/null\n@@ -37,7 +37,7 @@ fedora-*|almalinux-*)\n \t\tMESON_DEPS=\"meson ninja\";;\n \tesac\n \tdnf -yq update >/dev/null &&\n-\tdnf -yq install shadow-utils sudo make pkg-config gcc findutils diffutils perl python3 gawk gettext zlib-devel expat-devel openssl-devel curl-devel pcre2-devel $MESON_DEPS cargo >/dev/null\n+\tdnf -yq install shadow-utils sudo make pkg-config gcc findutils diffutils perl python3 gawk gettext zlib-devel expat-devel openssl-devel curl curl-devel pcre2-devel $MESON_DEPS cargo >/dev/null\n \t;;\n ubuntu-*|i386/ubuntu-*|debian-*)\n \t# Required so that apt doesn't wait for user input on certain packages.\n@@ -61,8 +61,8 @@ ubuntu-*|i386/ubuntu-*|debian-*)\n \tsudo apt-get -q update\n \tsudo apt-get -q -y install \\\n \t\t$LANGUAGES apache2 cvs cvsps git gnupg $SVN \\\n-\t\tmake libssl-dev libcurl4-openssl-dev libexpat-dev wget sudo default-jre \\\n-\t\ttcl tk gettext zlib1g-dev perl-modules liberror-perl libauthen-sasl-perl \\\n+\t\tmake libssl-dev curl libcurl4-openssl-dev libexpat-dev wget sudo default-jre \\\n+\t\ttcl tk gettext zlib1g zlib1g-dev perl-modules liberror-perl libauthen-sasl-perl \\\n \t\tlibemail-valid-perl libio-pty-perl libio-socket-ssl-perl libnet-smtp-ssl-perl libdbd-sqlite3-perl libcgi-pm-perl \\\n \t\tlibsecret-1-dev libpcre2-dev meson ninja-build pkg-config cargo \\\n \t\t${CC_PACKAGE:-${CC:-gcc}} $PYTHON_PACKAGE\n@@ -134,7 +134,7 @@ ClangFormat)\n \tsudo apt-get -q -y install clang-format\n \t;;\n StaticAnalysis)\n-\tsudo apt-get -q -y install coccinelle libcurl4-openssl-dev libssl-dev \\\n+\tsudo apt-get -q -y install coccinelle curl libcurl4-openssl-dev libssl-dev \\\n \t\tlibexpat-dev gettext make\n \t;;\n RustAnalysis)\n@@ -149,8 +149,8 @@ RustAnalysis)\n \tsudo chmod a+x \"$CUSTOM_PATH/cargo-msrv\"\n \t;;\n sparse)\n-\tsudo apt-get -q -y install libssl-dev libcurl4-openssl-dev \\\n-\t\tlibexpat-dev gettext zlib1g-dev sparse\n+\tsudo apt-get -q -y install libssl-dev curl libcurl4-openssl-dev \\\n+\t\tlibexpat-dev gettext zlib1g zlib1g-dev sparse\n \t;;\n Documentation)\n \tsudo apt-get -q -y install asciidoc xmlto docbook-xsl-ns make\ndiff --git a/ci/install-rust-toolchain.sh b/ci/install-rust-toolchain.sh\nnew file mode 100755\nindex 0000000000..06a29c4cfa\n--- /dev/null\n+++ b/ci/install-rust-toolchain.sh\n@@ -0,0 +1,30 @@\n+#!/bin/sh\n+\n+if [ \"$CARGO_HOME\" = \"\" ]; then\n+  echo >&2 \"::error:: CARGO_HOME is not set\"\n+  exit 2\n+fi\n+export PATH=\"$CARGO_HOME/bin:$PATH\"\n+rustup -vV || exit $?\n+\n+## Enforce the correct Rust toolchain\n+rustup override unset || true\n+\n+## install a specific version of rust\n+if [ \"$RUST_TARGET\" != \"\" ]; then\n+  rustup default --force-non-host \"$RUST_VERSION-$RUST_TARGET\" || exit $?\n+else\n+  rustup default \"$RUST_VERSION\" || exit $?\n+fi\n+\n+rustc -vV || exit $?\n+\n+RE_RUST_TARGET=\"$RUST_TARGET\"\n+if [ \"$RUST_TARGET\" = \"\" ]; then\n+  RE_RUST_TARGET=\"[^ ]+\"\n+fi\n+\n+if ! rustup show active-toolchain | grep -E \"^$RUST_VERSION-$RE_RUST_TARGET \\(default\\)$\"; then\n+  echo >&2 \"::error:: wrong Rust toolchain, active-toolchain: $(rustup show active-toolchain)\"\n+  exit 3\n+fi\ndiff --git a/ci/install-rustup.sh b/ci/install-rustup.sh\nnew file mode 100755\nindex 0000000000..0036231aee\n--- /dev/null\n+++ b/ci/install-rustup.sh\n@@ -0,0 +1,25 @@\n+#!/bin/sh\n+\n+## github workflows actions-rs/toolchain@v1 doesn't work for docker\n+## targets. This script should only be used if the ci pipeline\n+## doesn't support installing rust on a particular target.\n+\n+if [ \"$(id -u)\" -eq 0 ]; then\n+  echo >&2 \"::warning:: installing rust as root\"\n+fi\n+\n+if [ \"$CARGO_HOME\" = \"\" ]; then\n+  echo >&2 \"::error:: CARGO_HOME is not set\"\n+  exit 2\n+fi\n+\n+export RUSTUP_HOME=$CARGO_HOME\n+\n+## install rustup\n+curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain none -y\n+if [ ! -f $CARGO_HOME/env ]; then\n+  echo \"PATH=$CARGO_HOME/bin:\\$PATH\" > $CARGO_HOME/env\n+fi\n+. $CARGO_HOME/env\n+\n+rustup -vV\ndiff --git a/ci/make-test-artifacts.sh b/ci/make-test-artifacts.sh\nindex 74141af0cc..e37ed7030c 100755\n--- a/ci/make-test-artifacts.sh\n+++ b/ci/make-test-artifacts.sh\n@@ -7,6 +7,15 @@ mkdir -p \"$1\" # in case ci/lib.sh decides to quit early\n \n . ${0%/*}/lib.sh\n \n+## ensure rustup is in the PATH variable\n+if [ \"$CARGO_HOME\" = \"\" ]; then\n+  echo >&2 \"::error:: CARGO_HOME is not set\"\n+  exit 2\n+fi\n+export PATH=\"$CARGO_HOME/bin:$PATH\"\n+\n+rustc -vV\n+\n group Build make artifacts-tar ARTIFACTS_DIRECTORY=\"$1\"\n \n check_unignored_build_artifacts\ndiff --git a/ci/run-build-and-tests.sh b/ci/run-build-and-tests.sh\nindex d4f7cbd15f..e87c5174c4 100755\n--- a/ci/run-build-and-tests.sh\n+++ b/ci/run-build-and-tests.sh\n@@ -5,6 +5,15 @@\n \n . ${0%/*}/lib.sh\n \n+## ensure rustup is in the PATH variable\n+if [ \"$CARGO_HOME\" = \"\" ]; then\n+  echo >&2 \"::error:: CARGO_HOME is not set\"\n+  exit 2\n+fi\n+. $CARGO_HOME/env\n+\n+rustc -vV || exit $?\n+\n case \"$jobname\" in\n fedora-breaking-changes-musl|linux-breaking-changes)\n \texport WITH_BREAKING_CHANGES=YesPlease\n@@ -60,5 +69,9 @@ case \"$jobname\" in\n \t;;\n esac\n \n+if [ -d \"$CARGO_HOME\" ]; then\n+  rm -rf $CARGO_HOME\n+fi\n+\n check_unignored_build_artifacts\n save_good_tree\n-- \ngitgitgadget\n\n"},{"id":"531362","messageId":"0f797a238c3c102814a3402caca2539a518ecb74.1764205835.git.gitgitgadget@gmail.com","threadId":"64539","inReplyTo":"pull.2110.git.git.1764205835.gitgitgadget@gmail.com","subject":"[PATCH 13/13] rust/build-rust.sh: update dir_git_root variable instantiation","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-11-27T01:10:35Z","receivedAt":"2025-11-27T01:10:56Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n Makefile            |  6 ++++--\n meson.build         |  1 +\n rust/build-crate.sh | 15 ++++++++++-----\n 3 files changed, 15 insertions(+), 7 deletions(-)\n\ndiff --git a/Makefile b/Makefile\nindex 6da9db7d83..848db03227 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -931,6 +931,8 @@ EXTLIBS =\n \n GIT_BUILD_DIR := $(CURDIR)\n export GIT_BUILD_DIR\n+MAKEFILE_DIR := $(dir $(lastword $(MAKEFILE_LIST)))\n+export MAKEFILE_DIR\n \n RUST_CRATES := gitcore\n .PHONY: rust-compile rust-clean\n@@ -949,7 +951,7 @@ RUST_LIBS := $(foreach c,$(RUST_CRATES),$(GIT_BUILD_DIR)/lib$(c).a)\n rust-compile:\n \t@for c in $(RUST_CRATES); do \\\n \t\techo \"Building $$c...\"; \\\n-\t\t./rust/build-crate.sh $(GIT_BUILD_DIR) $(RUST_BUILD_MODE) $$c || exit $$?; \\\n+\t\t./rust/build-crate.sh $(MAKEFILE_DIR) $(GIT_BUILD_DIR) $(RUST_BUILD_MODE) $$c || exit $$?; \\\n \tdone\n \n rust-clean:\n@@ -958,7 +960,7 @@ rust-clean:\n \n $(GIT_BUILD_DIR)/lib%.a:\n \techo $(RUST_LIBS)\n-\t./rust/build-crate.sh $(GIT_BUILD_DIR) $(RUST_BUILD_MODE) $*\n+\t./rust/build-crate.sh $(MAKEFILE_DIR) $(GIT_BUILD_DIR) $(RUST_BUILD_MODE) $*\n \n UNAME_S := $(shell uname -s)\n ifeq ($(UNAME_S),Linux)\ndiff --git a/meson.build b/meson.build\nindex 94ec5e4ac0..3154cd684a 100644\n--- a/meson.build\n+++ b/meson.build\n@@ -305,6 +305,7 @@ if with_rust\n       build_always_stale: true,\n       command: [\n         meson.project_source_root() / 'rust' / 'build-crate.sh',\n+        meson.project_source_root(),\n         meson.current_build_dir(), rust_build_profile, crate,\n       ],\n       install: false,\ndiff --git a/rust/build-crate.sh b/rust/build-crate.sh\nindex b3debf70e5..50f5b0058c 100755\n--- a/rust/build-crate.sh\n+++ b/rust/build-crate.sh\n@@ -3,10 +3,12 @@\n rustc -vV || exit $?\n cargo --version || exit $?\n \n-dir_git_root=${0%/*}\n-dir_build=$1\n-rust_build_profile=$2\n-crate=$3\n+dir_git_root=$1\n+cd $dir_git_root || exit $?\n+dir_git_root=$PWD\n+dir_build=$2\n+rust_build_profile=$3\n+crate=$4\n \n if [ \"$dir_git_root\" = \"\" ]; then\n   echo \"did not specify the directory for the root of git\"\n@@ -38,9 +40,12 @@ libfile=\"lib${crate}.a\"\n if rustc -vV | grep windows-msvc; then\n   libfile=\"${crate}.lib\"\n   PATH=\"$(echo $PATH | tr ':' '\\n' | grep -Ev \"^(/mingw64/bin|/usr/bin)$\" | paste -sd: -):/mingw64/bin:/usr/bin\"\n+  echo \"PATH=$PATH\"\n fi\n \n-CARGO_TARGET_DIR=$dir_git_root/.build/rust/$crate\n+echo \"libfile=$libfile\"\n+\n+CARGO_TARGET_DIR=$dir_build/.build/rust/$crate\n export CARGO_TARGET_DIR\n \n cargo clean && pwd && USE_LINKING=\"false\" cargo build -p $crate $rust_args\n-- \ngitgitgadget\n"}]}