diff --git a/programs/Makefile b/programs/Makefile index 929791cb3..3a2622164 100644 --- a/programs/Makefile +++ b/programs/Makefile @@ -27,12 +27,55 @@ include $(LIBZSTD_MK_DIR)/libzstd.mk 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) +# Keep Rust's HUF implementation in lockstep with libzstd.mk's C selection. +# Forced modes may arrive as libzstd.mk variables or as direct -D flags in +# MOREFLAGS/CFLAGS/CPPFLAGS, as used by the C-only build matrix. +RUST_HUF_FORCE_X1 := $(HUF_FORCE_DECOMPRESS_X1) +RUST_HUF_FORCE_X2 := $(HUF_FORCE_DECOMPRESS_X2) +ifneq ($(filter -DHUF_FORCE_DECOMPRESS_X1 -DHUF_FORCE_DECOMPRESS_X1=%,$(CPPFLAGS) $(CFLAGS) $(MOREFLAGS)),) +RUST_HUF_FORCE_X1 := 1 +endif +ifneq ($(filter -DHUF_FORCE_DECOMPRESS_X2 -DHUF_FORCE_DECOMPRESS_X2=%,$(CPPFLAGS) $(CFLAGS) $(MOREFLAGS)),) +RUST_HUF_FORCE_X2 := 1 +endif + +# A separate target directory per mode prevents a forced build from reusing the +# default archive, and also gives link targets a configuration-specific input. +ifneq ($(RUST_HUF_FORCE_X1),0) +ifneq ($(RUST_HUF_FORCE_X2),0) +$(error HUF_FORCE_DECOMPRESS_X1 and HUF_FORCE_DECOMPRESS_X2 are mutually exclusive) +endif +endif + +RUST_HUF_FEATURE := +RUST_BUILD_CONFIG := default +ifneq ($(RUST_HUF_FORCE_X1),0) +RUST_HUF_FEATURE := huf-force-decompress-x1 +RUST_BUILD_CONFIG := huf-force-decompress-x1 +endif +ifneq ($(RUST_HUF_FORCE_X2),0) +RUST_HUF_FEATURE := huf-force-decompress-x2 +RUST_BUILD_CONFIG := huf-force-decompress-x2 +endif + +RUST_TARGET_DIR := $(RUST_DIR)/target/$(RUST_BUILD_CONFIG) +RUST_STATICLIB := $(RUST_TARGET_DIR)/release/libzstd_rs.a +RUST_TARGET_32 ?= i686-unknown-linux-gnu +RUST_STATICLIB_32 := $(RUST_TARGET_DIR)/$(RUST_TARGET_32)/release/libzstd_rs.a +RUST_CARGO_FLAGS := --manifest-path $(RUST_MANIFEST) --release \ + --target-dir $(RUST_TARGET_DIR) +ifneq ($(RUST_HUF_FEATURE),) +RUST_CARGO_FLAGS += --features $(RUST_HUF_FEATURE) +endif + $(RUST_STATICLIB): $(RUST_SOURCES) - $(CARGO) build --manifest-path $(RUST_MANIFEST) --release + $(CARGO) build $(RUST_CARGO_FLAGS) + +$(RUST_STATICLIB_32): $(RUST_SOURCES) + $(CARGO) build $(RUST_CARGO_FLAGS) --target $(RUST_TARGET_32) ifeq ($(shell $(CC) -v 2>&1 | $(GREP) -c "gcc version "), 1) ALIGN_LOOP = -falign-loops=32 @@ -205,7 +248,7 @@ zstd32 : CPPFLAGS += -DZSTD_LEGACY_SUPPORT=$(ZSTD_LEGACY_SUPPORT) ifneq (,$(filter Windows%,$(OS))) zstd32 : $(RES32_FILE) endif -zstd32 : $(ZSTDLIB_FULL_SRC) $(ZSTD_CLI_SRC) +zstd32 : $(ZSTDLIB_FULL_SRC) $(ZSTD_CLI_SRC) $(RUST_STATICLIB_32) $(CC) -m32 $(FLAGS) $^ -o $@$(EXT) ## zstd-nolegacy: same scope as zstd, with removed support of legacy formats @@ -265,7 +308,10 @@ 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 +# The target-specific CFLAGS assignment would otherwise hide an explicitly +# requested HUF decoder mode from the C sources while the Rust archive sees it. +ZSTD_SMALL_HUF_CFLAGS := $(filter -DHUF_FORCE_DECOMPRESS_X1% -DHUF_FORCE_DECOMPRESS_X2%,$(CFLAGS)) +zstd-small: CFLAGS = -Os -Wl,-s $(ZSTD_SMALL_HUF_CFLAGS) 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) diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 591f157a2..443b949c2 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -6,5 +6,10 @@ edition = "2021" [lib] crate-type = ["staticlib"] +[features] +default = [] +huf-force-decompress-x1 = [] +huf-force-decompress-x2 = [] + [dependencies] libc = "0.2" diff --git a/rust/README.md b/rust/README.md index 1038e2ac7..0dd75ec38 100644 --- a/rust/README.md +++ b/rust/README.md @@ -41,10 +41,11 @@ 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 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. +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. ## Validation diff --git a/tests/Makefile b/tests/Makefile index 1aaeae6a7..3a70a2ed3 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -41,12 +41,55 @@ TESTARTEFACT := versionsTest 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) +# Keep Rust's HUF implementation in lockstep with libzstd.mk's C selection. +# Forced modes may arrive as libzstd.mk variables or as direct -D flags in +# MOREFLAGS/CFLAGS/CPPFLAGS, as used by the C-only build matrix. +RUST_HUF_FORCE_X1 := $(HUF_FORCE_DECOMPRESS_X1) +RUST_HUF_FORCE_X2 := $(HUF_FORCE_DECOMPRESS_X2) +ifneq ($(filter -DHUF_FORCE_DECOMPRESS_X1 -DHUF_FORCE_DECOMPRESS_X1=%,$(CPPFLAGS) $(CFLAGS) $(MOREFLAGS)),) +RUST_HUF_FORCE_X1 := 1 +endif +ifneq ($(filter -DHUF_FORCE_DECOMPRESS_X2 -DHUF_FORCE_DECOMPRESS_X2=%,$(CPPFLAGS) $(CFLAGS) $(MOREFLAGS)),) +RUST_HUF_FORCE_X2 := 1 +endif + +# A separate target directory per mode prevents a forced build from reusing the +# default archive, and also gives link targets a configuration-specific input. +ifneq ($(RUST_HUF_FORCE_X1),0) +ifneq ($(RUST_HUF_FORCE_X2),0) +$(error HUF_FORCE_DECOMPRESS_X1 and HUF_FORCE_DECOMPRESS_X2 are mutually exclusive) +endif +endif + +RUST_HUF_FEATURE := +RUST_BUILD_CONFIG := default +ifneq ($(RUST_HUF_FORCE_X1),0) +RUST_HUF_FEATURE := huf-force-decompress-x1 +RUST_BUILD_CONFIG := huf-force-decompress-x1 +endif +ifneq ($(RUST_HUF_FORCE_X2),0) +RUST_HUF_FEATURE := huf-force-decompress-x2 +RUST_BUILD_CONFIG := huf-force-decompress-x2 +endif + +RUST_TARGET_DIR := $(RUST_DIR)/target/$(RUST_BUILD_CONFIG) +RUST_STATICLIB := $(RUST_TARGET_DIR)/release/libzstd_rs.a +RUST_TARGET_32 ?= i686-unknown-linux-gnu +RUST_STATICLIB_32 := $(RUST_TARGET_DIR)/$(RUST_TARGET_32)/release/libzstd_rs.a +RUST_CARGO_FLAGS := --manifest-path $(RUST_MANIFEST) --release \ + --target-dir $(RUST_TARGET_DIR) +ifneq ($(RUST_HUF_FEATURE),) +RUST_CARGO_FLAGS += --features $(RUST_HUF_FEATURE) +endif + $(RUST_STATICLIB): $(RUST_SOURCES) - $(CARGO) build --manifest-path $(RUST_MANIFEST) --release + $(CARGO) build $(RUST_CARGO_FLAGS) + +$(RUST_STATICLIB_32): $(RUST_SOURCES) + $(CARGO) build $(RUST_CARGO_FLAGS) --target $(RUST_TARGET_32) DEBUGFLAGS += -g -Wno-c++-compat CPPFLAGS += -I$(LIB_SRCDIR) -I$(LIB_SRCDIR)/common -I$(LIB_SRCDIR)/compress -I$(LIB_SRCDIR)/legacy \ @@ -259,12 +302,15 @@ poolTests : $(PRGDIR)/util.c $(PRGDIR)/timefn.c poolTests.c $(LIB_SRCDIR)/common # 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 \ +RUST_LINK_TARGETS := fullbench fuzzer zstreamtest \ zstreamtest_asan zstreamtest_tsan zstreamtest_ubsan \ paramgrill roundTripCrash longmatch largeDictionary \ invalidDictionaries legacy decodecorpus poolTests $(RUST_LINK_TARGETS): $(RUST_STATICLIB) +RUST_LINK_TARGETS_32 := fullbench32 fuzzer32 zstreamtest32 +$(RUST_LINK_TARGETS_32): $(RUST_STATICLIB_32) + .PHONY: versionsTest versionsTest: clean $(PYTHON) test-zstd-versions.py