diff --git a/programs/Makefile b/programs/Makefile index 3a2622164..48d229223 100644 --- a/programs/Makefile +++ b/programs/Makefile @@ -77,6 +77,25 @@ $(RUST_STATICLIB): $(RUST_SOURCES) $(RUST_STATICLIB_32): $(RUST_SOURCES) $(CARGO) build $(RUST_CARGO_FLAGS) --target $(RUST_TARGET_32) +# Most program objects use a configuration-hashed directory, but the compact +# direct-source variants below do not. Give their C outputs an independent +# mode stamp so they cannot outlive a different Rust HUF archive selection. +# The stamp is an empty archive, so normal linker `$^` recipes can consume it. +RUST_HUF_C_MODE_FILE := $(RUST_DIR)/target/.huf-c-mode-programs +RUST_HUF_C_MODE_STAMP := $(RUST_DIR)/target/.huf-c-mode-programs.a +.PHONY: FORCE_RUST_HUF_C_MODE +FORCE_RUST_HUF_C_MODE: +$(RUST_HUF_C_MODE_STAMP): FORCE_RUST_HUF_C_MODE + @mkdir -p $(@D) + @if test ! -r $(RUST_HUF_C_MODE_FILE) \ + || test "$$(cat $(RUST_HUF_C_MODE_FILE))" != "$(RUST_BUILD_CONFIG)" \ + || test ! -f $@; then \ + printf '%s\n' "$(RUST_BUILD_CONFIG)" > $(RUST_HUF_C_MODE_FILE); \ + $(AR) rcs $@; \ + touch $@; \ + fi +CLEAN += $(RUST_HUF_C_MODE_FILE) $(RUST_HUF_C_MODE_STAMP) + ifeq ($(shell $(CC) -v 2>&1 | $(GREP) -c "gcc version "), 1) ALIGN_LOOP = -falign-loops=32 else @@ -328,6 +347,10 @@ 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 $(RUST_STATICLIB) $(CC) $(FLAGS) -DZSTD_NOBENCH -DZSTD_NODECOMPRESS -DZSTD_NOTRACE $^ -o $@$(EXT) +RUST_DIRECT_LINK_TARGETS := zstd32 zstd-nolegacy zstd-small zstd-frugal \ + zstd-decompress zstd-compress zstd-dictBuilder +$(RUST_DIRECT_LINK_TARGETS): $(RUST_HUF_C_MODE_STAMP) + CLEAN += zstdmt zstdmt: zstd ln -sf zstd zstdmt diff --git a/rust/README.md b/rust/README.md index 369006de6..3e9be4d08 100644 --- a/rust/README.md +++ b/rust/README.md @@ -45,9 +45,9 @@ preprocessor behavior stay available during the transition. The test and program makefiles select an archive directory for the active C configuration: default, forced HUF X1/X2, and the matching Rust target for -32-bit C binaries. They rebuild the selected archive and relink the executable -when a Rust source changes, so original C tests do not accidentally use a -stale or configuration-incompatible implementation. +32-bit C binaries. When the HUF mode changes, they also rebuild cached C +outputs before linking. This prevents original C tests from using a stale or +configuration-incompatible implementation. ## Validation diff --git a/tests/Makefile b/tests/Makefile index 3a70a2ed3..3a4fd8735 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -91,6 +91,25 @@ $(RUST_STATICLIB): $(RUST_SOURCES) $(RUST_STATICLIB_32): $(RUST_SOURCES) $(CARGO) build $(RUST_CARGO_FLAGS) --target $(RUST_TARGET_32) +# These test objects have flat filenames, unlike the configuration-hashed +# program objects. Track the HUF mode separately so a C object set compiled +# for one decoder is never relinked with a Rust archive for another decoder. +# The stamp is an empty archive, so normal linker `$^` recipes can consume it. +RUST_HUF_C_MODE_FILE := $(RUST_DIR)/target/.huf-c-mode-tests +RUST_HUF_C_MODE_STAMP := $(RUST_DIR)/target/.huf-c-mode-tests.a +.PHONY: FORCE_RUST_HUF_C_MODE +FORCE_RUST_HUF_C_MODE: +$(RUST_HUF_C_MODE_STAMP): FORCE_RUST_HUF_C_MODE + @mkdir -p $(@D) + @if test ! -r $(RUST_HUF_C_MODE_FILE) \ + || test "$$(cat $(RUST_HUF_C_MODE_FILE))" != "$(RUST_BUILD_CONFIG)" \ + || test ! -f $@; then \ + printf '%s\n' "$(RUST_BUILD_CONFIG)" > $(RUST_HUF_C_MODE_FILE); \ + $(AR) rcs $@; \ + touch $@; \ + fi +CLEAN += $(RUST_HUF_C_MODE_FILE) $(RUST_HUF_C_MODE_STAMP) + 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) \ @@ -118,6 +137,8 @@ ZSTDMT_OBJ4 := $(subst $(LIB_SRCDIR)/legacy/,zstdmt_l_,$(ZSTDMT_OBJ3)) ZSTDMT_OBJ5 := $(ZSTDMT_OBJ4:.c=.o) ZSTDMT_OBJECTS := $(ZSTDMT_OBJ5:.S=.o) +$(ZSTD_OBJECTS) $(ZSTDMT_OBJECTS): $(RUST_HUF_C_MODE_STAMP) + # Define *.exe as extension for Windows systems ifneq (,$(filter Windows%,$(OS))) EXT =.exe @@ -311,6 +332,8 @@ $(RUST_LINK_TARGETS): $(RUST_STATICLIB) RUST_LINK_TARGETS_32 := fullbench32 fuzzer32 zstreamtest32 $(RUST_LINK_TARGETS_32): $(RUST_STATICLIB_32) +$(RUST_LINK_TARGETS) $(RUST_LINK_TARGETS_32): $(RUST_HUF_C_MODE_STAMP) + .PHONY: versionsTest versionsTest: clean $(PYTHON) test-zstd-versions.py