From ebc43676b710f833c455570b152f7e30241c17a1 Mon Sep 17 00:00:00 2001 From: ddidderr Date: Fri, 10 Jul 2026 20:11:42 +0200 Subject: [PATCH] build(rust): rebuild the archive for C test and CLI links Make Rust sources normal prerequisites of native C test executables and CLI builds. A changed Rust module now rebuilds libzstd_rs.a and relinks the target, instead of relying on a manually prepared archive that can silently be stale. The archive is linked after C objects, allowing migrated C shims to resolve their Rust ABI symbols while remaining compatible with the existing makefile flows. The migration guide now documents this behavior. Test Plan: - make -B -C tests fuzzer - ./tests/fuzzer -i1 --no-big-tests - make -B -C programs zstd - ./programs/zstd --version Refs: rust/README.md --- programs/Makefile | 25 +++++++++++++++++++------ rust/README.md | 7 ++++--- tests/Makefile | 22 +++++++++++++++++++++- 3 files changed, 44 insertions(+), 10 deletions(-) diff --git a/programs/Makefile b/programs/Makefile index f4af5e981..929791cb3 100644 --- a/programs/Makefile +++ b/programs/Makefile @@ -21,6 +21,19 @@ zstd-release: LIBZSTD_MK_DIR = ../lib include $(LIBZSTD_MK_DIR)/libzstd.mk +# Link the in-progress Rust implementation after C objects whenever a program +# uses a migrated C shim. Source inputs are prerequisites so a changed Rust +# module rebuilds the archive and relinks the executable automatically. +CARGO ?= cargo +RUST_DIR := ../rust +RUST_MANIFEST := $(RUST_DIR)/Cargo.toml +RUST_STATICLIB := $(RUST_DIR)/target/release/libzstd_rs.a +RUST_SOURCES := $(RUST_MANIFEST) $(RUST_DIR)/Cargo.lock \ + $(shell find $(RUST_DIR)/src -type f -name '*.rs' -print) + +$(RUST_STATICLIB): $(RUST_SOURCES) + $(CARGO) build --manifest-path $(RUST_MANIFEST) --release + ifeq ($(shell $(CC) -v 2>&1 | $(GREP) -c "gcc version "), 1) ALIGN_LOOP = -falign-loops=32 else @@ -152,7 +165,7 @@ else # BUILD_DIR is defined ZSTD_OBJ := $(addprefix $(BUILD_DIR)/, $(ZSTD_ALL_OBJ)) -$(BUILD_DIR)/zstd : $(ZSTD_OBJ) +$(BUILD_DIR)/zstd : $(ZSTD_OBJ) $(RUST_STATICLIB) @echo "$(THREAD_MSG)" @echo "$(ZLIB_MSG)" @echo "$(LZMA_MSG)" @@ -199,7 +212,7 @@ zstd32 : $(ZSTDLIB_FULL_SRC) $(ZSTD_CLI_SRC) CLEAN += zstd-nolegacy zstd-nolegacy : LDFLAGS += $(THREAD_LD) $(ZLIBLD) $(LZMALD) $(LZ4LD) $(DEBUGFLAGS_LD) zstd-nolegacy : CPPFLAGS += -UZSTD_LEGACY_SUPPORT -DZSTD_LEGACY_SUPPORT=0 -zstd-nolegacy : $(ZSTDLIB_CORE_SRC) $(ZDICT_SRC) $(ZSTD_CLI_OBJ) +zstd-nolegacy : $(ZSTDLIB_CORE_SRC) $(ZDICT_SRC) $(ZSTD_CLI_OBJ) $(RUST_STATICLIB) $(CC) $(FLAGS) $^ -o $@$(EXT) $(LDFLAGS) .PHONY: zstd-nomt @@ -253,20 +266,20 @@ endif ## zstd-small: minimal target, supporting only zstd compression and decompression. no bench. no legacy. no other format. CLEAN += zstd-small zstd-frugal zstd-small: CFLAGS = -Os -Wl,-s -zstd-frugal zstd-small: $(ZSTDLIB_CORE_SRC) zstdcli.c util.c timefn.c fileio.c fileio_asyncio.c +zstd-frugal zstd-small: $(ZSTDLIB_CORE_SRC) zstdcli.c util.c timefn.c fileio.c fileio_asyncio.c $(RUST_STATICLIB) $(CC) $(FLAGS) -DZSTD_NOBENCH -DZSTD_NODICT -DZSTD_NOTRACE -UZSTD_LEGACY_SUPPORT -DZSTD_LEGACY_SUPPORT=0 $^ -o $@$(EXT) CLEAN += zstd-decompress -zstd-decompress: $(ZSTDLIB_COMMON_SRC) $(ZSTDLIB_DECOMPRESS_SRC) zstdcli.c util.c timefn.c fileio.c fileio_asyncio.c +zstd-decompress: $(ZSTDLIB_COMMON_SRC) $(ZSTDLIB_DECOMPRESS_SRC) zstdcli.c util.c timefn.c fileio.c fileio_asyncio.c $(RUST_STATICLIB) $(CC) $(FLAGS) -DZSTD_NOBENCH -DZSTD_NODICT -DZSTD_NOCOMPRESS -DZSTD_NOTRACE -UZSTD_LEGACY_SUPPORT -DZSTD_LEGACY_SUPPORT=0 $^ -o $@$(EXT) CLEAN += zstd-compress -zstd-compress: $(ZSTDLIB_COMMON_SRC) $(ZSTDLIB_COMPRESS_SRC) zstdcli.c util.c timefn.c fileio.c fileio_asyncio.c +zstd-compress: $(ZSTDLIB_COMMON_SRC) $(ZSTDLIB_COMPRESS_SRC) zstdcli.c util.c timefn.c fileio.c fileio_asyncio.c $(RUST_STATICLIB) $(CC) $(FLAGS) -DZSTD_NOBENCH -DZSTD_NODICT -DZSTD_NODECOMPRESS -DZSTD_NOTRACE -UZSTD_LEGACY_SUPPORT -DZSTD_LEGACY_SUPPORT=0 $^ -o $@$(EXT) ## zstd-dictBuilder: executable supporting dictionary creation and compression (only) CLEAN += zstd-dictBuilder -zstd-dictBuilder: $(ZSTDLIB_COMMON_SRC) $(ZSTDLIB_COMPRESS_SRC) $(ZDICT_SRC) zstdcli.c util.c timefn.c fileio.c fileio_asyncio.c dibio.c +zstd-dictBuilder: $(ZSTDLIB_COMMON_SRC) $(ZSTDLIB_COMPRESS_SRC) $(ZDICT_SRC) zstdcli.c util.c timefn.c fileio.c fileio_asyncio.c dibio.c $(RUST_STATICLIB) $(CC) $(FLAGS) -DZSTD_NOBENCH -DZSTD_NODECOMPRESS -DZSTD_NOTRACE $^ -o $@$(EXT) CLEAN += zstdmt diff --git a/rust/README.md b/rust/README.md index be6bb8fe8..c587662cb 100644 --- a/rust/README.md +++ b/rust/README.md @@ -39,9 +39,10 @@ source file whose implementation has moved to Rust remains in the original makefile source list as a small shim so header configuration and platform preprocessor behavior stay available during the transition. -The original test makefile links `target/release/libzstd_rs.a` with whole-archive -semantics. Rebuild the Rust archive before running C tests so the executable -does not use a stale implementation. +The test and program makefiles make `target/release/libzstd_rs.a` a normal +link prerequisite. They rebuild the Rust archive and relink the executable +when a Rust source changes, so original C tests do not accidentally use a stale +implementation. ## Validation diff --git a/tests/Makefile b/tests/Makefile index b604e6eba..1aaeae6a7 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -36,6 +36,18 @@ PRGDIR = ../programs PYTHON ?= python3 TESTARTEFACT := versionsTest +# The Rust implementation is linked into C compatibility tests. Keep the +# archive a real prerequisite so a changed Rust source always relinks a test. +CARGO ?= cargo +RUST_DIR := ../rust +RUST_MANIFEST := $(RUST_DIR)/Cargo.toml +RUST_STATICLIB := $(RUST_DIR)/target/release/libzstd_rs.a +RUST_SOURCES := $(RUST_MANIFEST) $(RUST_DIR)/Cargo.lock \ + $(shell find $(RUST_DIR)/src -type f -name '*.rs' -print) + +$(RUST_STATICLIB): $(RUST_SOURCES) + $(CARGO) build --manifest-path $(RUST_MANIFEST) --release + DEBUGFLAGS += -g -Wno-c++-compat CPPFLAGS += -I$(LIB_SRCDIR) -I$(LIB_SRCDIR)/common -I$(LIB_SRCDIR)/compress -I$(LIB_SRCDIR)/legacy \ -I$(LIB_SRCDIR)/dictBuilder -I$(LIB_SRCDIR)/deprecated -I$(PRGDIR) \ @@ -74,7 +86,6 @@ MULTITHREAD_CPP = -DZSTD_MULTITHREAD MULTITHREAD_LD = -pthread endif MULTITHREAD = $(MULTITHREAD_CPP) $(MULTITHREAD_LD) -LDFLAGS += -Wl,--whole-archive ../rust/target/release/libzstd_rs.a -Wl,--no-whole-archive VOID = /dev/null ZSTREAM_TESTTIME ?= -T90s @@ -245,6 +256,15 @@ CLEAN += poolTests poolTests : $(PRGDIR)/util.c $(PRGDIR)/timefn.c poolTests.c $(LIB_SRCDIR)/common/pool.c $(LIB_SRCDIR)/common/threading.c $(LIB_SRCDIR)/common/zstd_common.c $(LIB_SRCDIR)/common/error_private.c $(LINK.c) $(MULTITHREAD) $^ -o $@$(EXT) +# These static C test executables exercise Rust replacements. The normal +# prerequisite is also an archive input, placed after C sources by `$^`, and +# rebuilding Rust therefore relinks rather than leaving a stale implementation. +RUST_LINK_TARGETS := $(FULLBENCHS) fuzzer fuzzer32 zstreamtest zstreamtest32 \ + zstreamtest_asan zstreamtest_tsan zstreamtest_ubsan \ + paramgrill roundTripCrash longmatch largeDictionary \ + invalidDictionaries legacy decodecorpus poolTests +$(RUST_LINK_TARGETS): $(RUST_STATICLIB) + .PHONY: versionsTest versionsTest: clean $(PYTHON) test-zstd-versions.py