From: Ezekiel Newren via GitGitGadget Date: Thu, 27 Nov 2025 01:10:26 GMT Subject: [PATCH 04/13] build: build Rust with Makefile and Meson Message-ID: In-Reply-To: From: Ezekiel Newren Signed-off-by: Ezekiel Newren --- Makefile | 91 ++++++++++++++++++++++++++++++++++++++------- meson.build | 63 ++++++++++++++++++++++++++++++- rust/build-crate.sh | 58 +++++++++++++++++++++++++++++ 3 files changed, 197 insertions(+), 15 deletions(-) create mode 100755 rust/build-crate.sh diff --git a/Makefile b/Makefile index 28bc00e648..0aa6b3ae73 100644 --- a/Makefile +++ b/Makefile @@ -483,6 +483,14 @@ include shared.mak # Define LIBPCREDIR=/foo/bar if your PCRE header and library files are # in /foo/bar/include and /foo/bar/lib directories. # +# == Optional Rust support == +# +# Define WITH_RUST=false if you don't want to include features and subsystems +# written in Rust into Git. For now, Rust is still an optional feature of the +# build process. With Git 3.0 though, Rust will be mandatory. +# +# Building Rust code requires Cargo. +# # == SHA-1 and SHA-256 defines == # # === SHA-1 backend === @@ -919,9 +927,55 @@ TEST_SHELL_PATH = $(SHELL_PATH) LIB_FILE = libgit.a -GITLIBS = common-main.o $(LIB_FILE) EXTLIBS = +GIT_BUILD_DIR := $(CURDIR) +export GIT_BUILD_DIR + +RUST_CRATES := gitcore +.PHONY: rust-compile rust-clean + +WITH_RUST ?= true +ifeq ($(WITH_RUST),true) + +ifeq ($(DEBUG), 1) + RUST_BUILD_MODE := debug +else + RUST_BUILD_MODE := release +endif + +RUST_LIBS := $(foreach c,$(RUST_CRATES),$(GIT_BUILD_DIR)/lib$(c).a) + +rust-compile: + @for c in $(RUST_CRATES); do \ + echo "Building $$c..."; \ + ./rust/build-crate.sh $(GIT_BUILD_DIR) $(RUST_BUILD_MODE) $$c || exit $$?; \ + done + +rust-clean: + $(RM) $(RUST_LIBS) Cargo.lock + cargo clean + +$(GIT_BUILD_DIR)/lib%.a: + echo $(RUST_LIBS) + ./rust/build-crate.sh $(GIT_BUILD_DIR) $(RUST_BUILD_MODE) $* + +UNAME_S := $(shell uname -s) +ifeq ($(UNAME_S),Linux) + EXTLIBS += -ldl +endif + +else ifeq ($(WITH_RUST),false) +rust-compile: + : +rust-clean: + : +else +$(error 'WITH_RUST' must be true or false) +endif + +GITLIBS = common-main.o $(LIB_FILE) + GIT_USER_AGENT = git/$(GIT_VERSION) ifeq ($(wildcard sha1collisiondetection/lib/sha1.h),sha1collisiondetection/lib/sha1.h) @@ -938,6 +992,11 @@ CC_LD_DYNPATH = -Wl,-rpath, BASIC_CFLAGS = -I. BASIC_LDFLAGS = +ifeq ($(WITH_RUST),true) + BASIC_CFLAGS += -DWITH_RUST + BASIC_CFLAGS += -fPIE +endif + # library flags ARFLAGS = rcs PTHREAD_CFLAGS = @@ -1303,7 +1362,9 @@ LIB_OBJS += urlmatch.o LIB_OBJS += usage.o LIB_OBJS += userdiff.o LIB_OBJS += utf8.o +ifeq ($(WITH_RUST),false) LIB_OBJS += varint.o +endif LIB_OBJS += version.o LIB_OBJS += versioncmp.o LIB_OBJS += walker.o @@ -2409,6 +2470,10 @@ endif # from the dependency list, that would make each entry appear twice. LIBS = $(filter-out %.o, $(GITLIBS)) $(EXTLIBS) +ifeq ($(WITH_RUST),true) +LIBS += $(RUST_LIBS) +endif + BASIC_CFLAGS += $(COMPAT_CFLAGS) LIB_OBJS += $(COMPAT_OBJS) @@ -2564,7 +2629,7 @@ git.sp git.s git.o: EXTRA_CPPFLAGS = \ '-DGIT_MAN_PATH="$(mandir_relative_SQ)"' \ '-DGIT_INFO_PATH="$(infodir_relative_SQ)"' -git$X: git.o GIT-LDFLAGS $(BUILTIN_OBJS) $(GITLIBS) +git$X: git.o GIT-LDFLAGS $(BUILTIN_OBJS) $(GITLIBS) rust-compile $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) \ $(filter %.o,$^) $(LIBS) @@ -2889,17 +2954,17 @@ headless-git.o: compat/win32/headless.c GIT-CFLAGS headless-git$X: headless-git.o git.res GIT-LDFLAGS $(QUIET_LINK)$(CC) $(ALL_CFLAGS) $(ALL_LDFLAGS) -mwindows -o $@ $< git.res -git-%$X: %.o GIT-LDFLAGS $(GITLIBS) +git-%$X: %.o GIT-LDFLAGS $(GITLIBS) rust-compile $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS) -git-imap-send$X: imap-send.o $(IMAP_SEND_BUILDDEPS) GIT-LDFLAGS $(GITLIBS) +git-imap-send$X: imap-send.o $(IMAP_SEND_BUILDDEPS) GIT-LDFLAGS $(GITLIBS) rust-compile $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \ $(IMAP_SEND_LDFLAGS) $(LIBS) -git-http-fetch$X: http.o http-walker.o http-fetch.o GIT-LDFLAGS $(GITLIBS) +git-http-fetch$X: http.o http-walker.o http-fetch.o GIT-LDFLAGS $(GITLIBS) rust-compile $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \ $(CURL_LIBCURL) $(LIBS) -git-http-push$X: http.o http-push.o GIT-LDFLAGS $(GITLIBS) +git-http-push$X: http.o http-push.o GIT-LDFLAGS $(GITLIBS) rust-compile $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \ $(CURL_LIBCURL) $(EXPAT_LIBEXPAT) $(LIBS) @@ -2909,11 +2974,11 @@ $(REMOTE_CURL_ALIASES): $(REMOTE_CURL_PRIMARY) ln -s $< $@ 2>/dev/null || \ cp $< $@ -$(REMOTE_CURL_PRIMARY): remote-curl.o http.o http-walker.o GIT-LDFLAGS $(GITLIBS) +$(REMOTE_CURL_PRIMARY): remote-curl.o http.o http-walker.o GIT-LDFLAGS $(GITLIBS) rust-compile $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \ $(CURL_LIBCURL) $(EXPAT_LIBEXPAT) $(LIBS) -scalar$X: scalar.o GIT-LDFLAGS $(GITLIBS) +scalar$X: scalar.o GIT-LDFLAGS $(GITLIBS) rust-compile $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) \ $(filter %.o,$^) $(LIBS) @@ -3286,7 +3351,7 @@ perf: all t/helper/test-tool$X: $(patsubst %,t/helper/%,$(TEST_BUILTINS_OBJS)) $(UNIT_TEST_DIR)/test-lib.o -t/helper/test-%$X: t/helper/test-%.o GIT-LDFLAGS $(GITLIBS) +t/helper/test-%$X: t/helper/test-%.o GIT-LDFLAGS $(GITLIBS) rust-compile $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(filter %.a,$^) $(LIBS) check-sha1:: t/helper/test-tool$X @@ -3751,7 +3816,7 @@ cocciclean: $(RM) -r .build/contrib/coccinelle $(RM) contrib/coccinelle/*.cocci.patch -clean: profile-clean coverage-clean cocciclean +clean: profile-clean coverage-clean cocciclean rust-clean $(RM) -r .build $(UNIT_TEST_BIN) $(RM) GIT-TEST-SUITES $(RM) po/git.pot po/git-core.pot @@ -3907,13 +3972,13 @@ FUZZ_CXXFLAGS ?= $(ALL_CFLAGS) .PHONY: fuzz-all fuzz-all: $(FUZZ_PROGRAMS) -$(FUZZ_PROGRAMS): %: %.o oss-fuzz/dummy-cmd-main.o $(GITLIBS) GIT-LDFLAGS +$(FUZZ_PROGRAMS): %: %.o oss-fuzz/dummy-cmd-main.o $(GITLIBS) GIT-LDFLAGS rust-compile $(QUIET_LINK)$(FUZZ_CXX) $(FUZZ_CXXFLAGS) -o $@ $(ALL_LDFLAGS) \ -Wl,--allow-multiple-definition \ $(filter %.o,$^) $(filter %.a,$^) $(LIBS) $(LIB_FUZZING_ENGINE) $(UNIT_TEST_PROGS): $(UNIT_TEST_BIN)/%$X: $(UNIT_TEST_DIR)/%.o $(UNIT_TEST_OBJS) \ - $(GITLIBS) GIT-LDFLAGS + $(GITLIBS) GIT-LDFLAGS rust-compile $(call mkdir_p_parent_template) $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) \ $(filter %.o,$^) $(filter %.a,$^) $(LIBS) @@ -3932,7 +3997,7 @@ $(UNIT_TEST_DIR)/clar.suite: $(UNIT_TEST_DIR)/clar-decls.h $(UNIT_TEST_DIR)/gene $(UNIT_TEST_DIR)/clar/clar.o: $(UNIT_TEST_DIR)/clar.suite $(CLAR_TEST_OBJS): $(UNIT_TEST_DIR)/clar-decls.h $(CLAR_TEST_OBJS): EXTRA_CPPFLAGS = -I$(UNIT_TEST_DIR) -$(CLAR_TEST_PROG): $(UNIT_TEST_DIR)/clar.suite $(CLAR_TEST_OBJS) $(GITLIBS) GIT-LDFLAGS +$(CLAR_TEST_PROG): $(UNIT_TEST_DIR)/clar.suite $(CLAR_TEST_OBJS) $(GITLIBS) GIT-LDFLAGS rust-compile $(call mkdir_p_parent_template) $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS) diff --git a/meson.build b/meson.build index 8966596ee8..94ec5e4ac0 100644 --- a/meson.build +++ b/meson.build @@ -267,6 +267,55 @@ version_gen_environment.set('GIT_DATE', get_option('build_date')) version_gen_environment.set('GIT_USER_AGENT', get_option('user_agent')) version_gen_environment.set('GIT_VERSION', get_option('version')) +rust_crates = ['gitcore'] + +rust_builds = [] +with_rust = get_option('with_rust') +if with_rust + rustc = find_program('rustc', required : false) + cargo = find_program('cargo', required : false) + + if not rustc.found() or not cargo.found() + error('Rust toolchain not found. Reconfigure with -Dwith_rust=false') + endif + + if get_option('optimization') in ['2', '3', 's', 'z'] + rust_build_profile = 'release' + else + rust_build_profile = 'debug' + endif + + # Run `rustup show active-toolchain` and capture output + rustup_out = run_command('rustup', 'show', 'active-toolchain', + check: true).stdout().strip() + + rust_environment = script_environment + + foreach crate : rust_crates + if rustup_out.contains('windows-msvc') + libfile = crate + '.lib' + else + libfile = 'lib' + crate + '.a' + endif + + rust_builds += custom_target( + 'rust_build_'+crate, + output: libfile, + build_by_default: true, + build_always_stale: true, + command: [ + meson.project_source_root() / 'rust' / 'build-crate.sh', + meson.current_build_dir(), rust_build_profile, crate, + ], + install: false, + env: rust_environment, + ) + endforeach +else + message('Rust components disabled (-Dwith_rust=false)') +endif + + compiler = meson.get_compiler('c') libgit_sources = [ @@ -548,6 +597,10 @@ libgit_sources = [ 'xdiff/xutils.c', ] +if not with_rust + libgit_sources += 'varint.c' +endif + libgit_sources += custom_target( input: 'command-list.txt', output: 'command-list.h', @@ -782,6 +835,10 @@ libgit_c_args = [ '-DSHELL_PATH="' + fs.as_posix(target_shell.full_path()) + '"', ] +if with_rust + libgit_c_args += '-DWITH_RUST' +endif + system_attributes = get_option('gitattributes') if system_attributes != '' libgit_c_args += '-DETC_GITATTRIBUTES="' + system_attributes + '"' @@ -1274,6 +1331,7 @@ elif host_machine.system() == 'windows' ] libgit_dependencies += compiler.find_library('ntdll') + libgit_dependencies += compiler.find_library('userenv') libgit_include_directories += 'compat/win32' if compiler.get_id() == 'msvc' libgit_include_directories += 'compat/vcbuild/include' @@ -1710,14 +1768,15 @@ version_def_h = custom_target( libgit_sources += version_def_h libgit = declare_dependency( - link_with: static_library('git', + link_with: [ + static_library('git', sources: libgit_sources, c_args: libgit_c_args + [ '-DGIT_VERSION_H="' + version_def_h.full_path() + '"', ], dependencies: libgit_dependencies, include_directories: libgit_include_directories, - ), + )] + rust_builds, compile_args: libgit_c_args, dependencies: libgit_dependencies, include_directories: libgit_include_directories, diff --git a/rust/build-crate.sh b/rust/build-crate.sh new file mode 100755 index 0000000000..b3debf70e5 --- /dev/null +++ b/rust/build-crate.sh @@ -0,0 +1,58 @@ +#!/bin/sh + +rustc -vV || exit $? +cargo --version || exit $? + +dir_git_root=${0%/*} +dir_build=$1 +rust_build_profile=$2 +crate=$3 + +if [ "$dir_git_root" = "" ]; then + echo "did not specify the directory for the root of git" + exit 1 +fi + +if [ "$dir_build" = "" ]; then + echo "did not specify the build directory" + exit 1 +fi + +if [ "$rust_build_profile" = "" ]; then + echo "did not specify the rust_build_profile" + exit 1 +fi + +if [ "$rust_build_profile" = "release" ]; then + rust_args="--release" + export RUSTFLAGS='' +elif [ "$rust_build_profile" = "debug" ]; then + rust_args="" + export RUSTFLAGS='-C debuginfo=2 -C opt-level=1 -C force-frame-pointers=yes' +else + echo "illegal rust_build_profile value $rust_build_profile" + exit 1 +fi + +libfile="lib${crate}.a" +if rustc -vV | grep windows-msvc; then + libfile="${crate}.lib" + PATH="$(echo $PATH | tr ':' '\n' | grep -Ev "^(/mingw64/bin|/usr/bin)$" | paste -sd: -):/mingw64/bin:/usr/bin" +fi + +CARGO_TARGET_DIR=$dir_git_root/.build/rust/$crate +export CARGO_TARGET_DIR + +cargo clean && pwd && USE_LINKING="false" cargo build -p $crate $rust_args + +src=$CARGO_TARGET_DIR/$rust_build_profile/$libfile +dst=$dir_build/$libfile + +if [ ! -f $src ]; then + echo >&2 "::error:: cannot find path of static library $src is not a file or does not exist" + exit 5 +fi + +rm $dst 2>/dev/null +echo mv $src $dst +mv $src $dst -- gitgitgadget