From c184795fc0eaf660b4fc06e7ee63aa9c136ff1aa Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Thu, 2 Oct 2025 09:29:26 +0200 Subject: meson: add infrastructure to build internal Rust library Add the infrastructure into Meson to build an internal Rust library. Building the Rust parts of Git are for now entirely optional, as they are mostly intended as a test balloon for both Git developers, but also for distributors of Git. So for now, they may contain: - New features that are not mission critical to Git and that users can easily live without. - Alternative implementations of small subsystems. If these test balloons are successful, we will eventually make Rust a mandatory dependency for our build process in Git 3.0. The availability of a Rust toolchain will be auto-detected by Meson at setup time. This behaviour can be tweaked via the `-Drust=` feature toggle. Next to the linkable Rust library, also wire up tests that can be executed via `meson test`. This allows us to use the native unit testing capabilities of Rust. Note that the Rust edition is currently set to 2018. This edition is supported by Rust 1.49, which is the target for the upcoming gcc-rs backend. For now we don't use any features of Rust that would require a newer version, so settling on this old version makes sense so that gcc-rs may become an alternative backend for compiling Git. If we _do_ want to introduce features that were added in more recent editions of Rust though we should reevaluate that choice. Inspired-by: Ezekiel Newren Signed-off-by: Patrick Steinhardt Signed-off-by: Junio C Hamano --- src/cargo-meson.sh | 32 ++++++++++++++++++++++++++++++++ src/lib.rs | 0 src/meson.build | 40 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+) create mode 100755 src/cargo-meson.sh create mode 100644 src/lib.rs create mode 100644 src/meson.build (limited to 'src') diff --git a/src/cargo-meson.sh b/src/cargo-meson.sh new file mode 100755 index 0000000000..99400986d9 --- /dev/null +++ b/src/cargo-meson.sh @@ -0,0 +1,32 @@ +#!/bin/sh + +if test "$#" -lt 2 +then + exit 1 +fi + +SOURCE_DIR="$1" +BUILD_DIR="$2" +BUILD_TYPE=debug + +shift 2 + +for arg +do + case "$arg" in + --release) + BUILD_TYPE=release;; + esac +done + +cargo build --lib --quiet --manifest-path="$SOURCE_DIR/Cargo.toml" --target-dir="$BUILD_DIR" "$@" +RET=$? +if test $RET -ne 0 +then + exit $RET +fi + +if ! cmp "$BUILD_DIR/$BUILD_TYPE/libgitcore.a" "$BUILD_DIR/libgitcore.a" >/dev/null 2>&1 +then + cp "$BUILD_DIR/$BUILD_TYPE/libgitcore.a" "$BUILD_DIR/libgitcore.a" +fi diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/meson.build b/src/meson.build new file mode 100644 index 0000000000..c8d874b210 --- /dev/null +++ b/src/meson.build @@ -0,0 +1,40 @@ +libgit_rs_sources = [ + 'lib.rs', +] + +# Unfortunately we must use a wrapper command to move the output file into the +# current build directory. This can fixed once `cargo build --artifact-dir` +# stabilizes. See https://github.com/rust-lang/cargo/issues/6790 for that +# effort. +cargo_command = [ + shell, + meson.current_source_dir() / 'cargo-meson.sh', + meson.project_source_root(), + meson.current_build_dir(), +] +if get_option('buildtype') == 'release' + cargo_command += '--release' +endif + +libgit_rs = custom_target('git_rs', + input: libgit_rs_sources + [ + meson.project_source_root() / 'Cargo.toml', + ], + output: 'libgitcore.a', + command: cargo_command, +) +libgit_dependencies += declare_dependency(link_with: libgit_rs) + +if get_option('tests') + test('rust', cargo, + args: [ + 'test', + '--manifest-path', + meson.project_source_root() / 'Cargo.toml', + '--target-dir', + meson.current_build_dir() / 'target', + ], + timeout: 0, + protocol: 'rust', + ) +endif -- cgit v1.3 From 8832e728d362992a38eef89613b44d24f18e6c2a Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Thu, 2 Oct 2025 09:29:31 +0200 Subject: varint: reimplement as test balloon for Rust Implement a trivial test balloon for our Rust build infrastructure by reimplementing the "varint.c" subsystem in Rust. This subsystem is chosen because it is trivial to convert and because it doesn't have any dependencies to other components of Git. If support for Rust is enabled, we stop compiling "varint.c" and instead compile and use "src/varint.rs". Signed-off-by: Patrick Steinhardt Signed-off-by: Junio C Hamano --- Makefile | 3 ++ meson.build | 5 +++- src/lib.rs | 1 + src/meson.build | 1 + src/varint.rs | 92 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 src/varint.rs (limited to 'src') diff --git a/Makefile b/Makefile index 31e79342e1..2a7fc5cb1f 100644 --- a/Makefile +++ b/Makefile @@ -1307,7 +1307,9 @@ LIB_OBJS += urlmatch.o LIB_OBJS += usage.o LIB_OBJS += userdiff.o LIB_OBJS += utf8.o +ifndef WITH_RUST LIB_OBJS += varint.o +endif LIB_OBJS += version.o LIB_OBJS += versioncmp.o LIB_OBJS += walker.o @@ -1499,6 +1501,7 @@ CLAR_TEST_OBJS += $(UNIT_TEST_DIR)/unit-test.o UNIT_TEST_OBJS += $(UNIT_TEST_DIR)/test-lib.o RUST_SOURCES += src/lib.rs +RUST_SOURCES += src/varint.rs GIT-VERSION-FILE: FORCE @OLD=$$(cat $@ 2>/dev/null || :) && \ diff --git a/meson.build b/meson.build index 234a9e9d6f..37dfa28601 100644 --- a/meson.build +++ b/meson.build @@ -522,7 +522,6 @@ libgit_sources = [ 'usage.c', 'userdiff.c', 'utf8.c', - 'varint.c', 'version.c', 'versioncmp.c', 'walker.c', @@ -1707,6 +1706,10 @@ rust_option = get_option('rust').disable_auto_if(not cargo.found()) if rust_option.allowed() subdir('src') libgit_c_args += '-DWITH_RUST' +else + libgit_sources += [ + 'varint.c', + ] endif libgit = declare_dependency( diff --git a/src/lib.rs b/src/lib.rs index e69de29bb2..9da70d8b57 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -0,0 +1 @@ +pub mod varint; diff --git a/src/meson.build b/src/meson.build index c8d874b210..25b9ad5a14 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,5 +1,6 @@ libgit_rs_sources = [ 'lib.rs', + 'varint.rs', ] # Unfortunately we must use a wrapper command to move the output file into the diff --git a/src/varint.rs b/src/varint.rs new file mode 100644 index 0000000000..6e610bdd8e --- /dev/null +++ b/src/varint.rs @@ -0,0 +1,92 @@ +#[no_mangle] +pub unsafe extern "C" fn decode_varint(bufp: *mut *const u8) -> u64 { + let mut buf = *bufp; + let mut c = *buf; + let mut val = u64::from(c & 127); + + buf = buf.add(1); + + while (c & 128) != 0 { + val = val.wrapping_add(1); + if val == 0 || val.leading_zeros() < 7 { + return 0; // overflow + } + + c = *buf; + buf = buf.add(1); + + val = (val << 7) + u64::from(c & 127); + } + + *bufp = buf; + val +} + +#[no_mangle] +pub unsafe extern "C" fn encode_varint(value: u64, buf: *mut u8) -> u8 { + let mut varint: [u8; 16] = [0; 16]; + let mut pos = varint.len() - 1; + + varint[pos] = (value & 127) as u8; + + let mut value = value >> 7; + while value != 0 { + pos -= 1; + value -= 1; + varint[pos] = 128 | (value & 127) as u8; + value >>= 7; + } + + if !buf.is_null() { + std::ptr::copy_nonoverlapping(varint.as_ptr().add(pos), buf, varint.len() - pos); + } + + (varint.len() - pos) as u8 +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_decode_varint() { + unsafe { + assert_eq!(decode_varint(&mut [0x00].as_slice().as_ptr()), 0); + assert_eq!(decode_varint(&mut [0x01].as_slice().as_ptr()), 1); + assert_eq!(decode_varint(&mut [0x7f].as_slice().as_ptr()), 127); + assert_eq!(decode_varint(&mut [0x80, 0x00].as_slice().as_ptr()), 128); + assert_eq!(decode_varint(&mut [0x80, 0x01].as_slice().as_ptr()), 129); + assert_eq!(decode_varint(&mut [0x80, 0x7f].as_slice().as_ptr()), 255); + + // Overflows are expected to return 0. + assert_eq!(decode_varint(&mut [0x88; 16].as_slice().as_ptr()), 0); + } + } + + #[test] + fn test_encode_varint() { + unsafe { + let mut varint: [u8; 16] = [0; 16]; + + assert_eq!(encode_varint(0, std::ptr::null_mut()), 1); + + assert_eq!(encode_varint(0, varint.as_mut_slice().as_mut_ptr()), 1); + assert_eq!(varint, [0; 16]); + + assert_eq!(encode_varint(10, varint.as_mut_slice().as_mut_ptr()), 1); + assert_eq!(varint, [10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); + + assert_eq!(encode_varint(127, varint.as_mut_slice().as_mut_ptr()), 1); + assert_eq!(varint, [127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); + + assert_eq!(encode_varint(128, varint.as_mut_slice().as_mut_ptr()), 2); + assert_eq!(varint, [128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); + + assert_eq!(encode_varint(129, varint.as_mut_slice().as_mut_ptr()), 2); + assert_eq!(varint, [128, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); + + assert_eq!(encode_varint(255, varint.as_mut_slice().as_mut_ptr()), 2); + assert_eq!(varint, [128, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); + } + } +} -- cgit v1.3