build(rust): link migrated code into native libraries

Build feature-matched Rust archives for the native Make library path. Flatten
their members into libzstd.a so static consumers resolve C-to-Rust and
Rust-to-C references in one archive, and whole-archive link shared outputs so
every migrated public ABI export remains available.

Rust compression and decompression features now mirror the C partial-library
switches. This prevents compression-only artifacts from retaining DDict
references to decompression-only C helpers.

Add a C archive smoke test that round-trips data and exercises the DDict
lifecycle through lib/libzstd.a, rather than direct test-object links.

Test Plan:
- cargo fmt, strict Clippy, cargo test --all-targets, release build
- Rust checks with compression-only, decompression-only, and no features
- full, partial, forced-HUF, and 32-bit static/shared Make library probes
- test-rust-lib-smoke, fuzzer, zstreamtest, and invalidDictionaries

Refs: rust/README.md
This commit is contained in:
2026-07-10 21:41:22 +02:00
parent 24d01b92fb
commit d784406374
7 changed files with 193 additions and 13 deletions
+1
View File
@@ -18,6 +18,7 @@ datagen
paramgrill
paramgrill32
roundTripCrash
rustLibSmoke
longmatch
symbols
legacy
+12 -1
View File
@@ -308,6 +308,13 @@ largeDictionary: $(ZSTDMT_OBJECTS) $(PRGDIR)/datagen.c largeDictionary.c
CLEAN += invalidDictionaries
invalidDictionaries : $(ZSTD_OBJECTS) invalidDictionaries.c
# Exercise the public static archive as an external C consumer would. This
# catches a nested Rust archive or unresolved C/Rust cycle that direct object
# links cannot expose.
CLEAN += rustLibSmoke
rustLibSmoke : $(LIB_BINDIR)/libzstd.a rustLibSmoke.c
$(LINK.c) $(filter %.c,$^) $(filter %.a,$^) -o $@$(EXT)
CLEAN += legacy
legacy : CPPFLAGS += -UZSTD_LEGACY_SUPPORT -DZSTD_LEGACY_SUPPORT=4
legacy : $(ZSTD_FILES) legacy.c
@@ -413,7 +420,7 @@ check: test-zstd
fuzztest: test-fuzzer test-zstream test-decodecorpus
.PHONY: test
test: test-zstd test-cli-tests test-fullbench test-fuzzer test-zstream test-invalidDictionaries test-legacy test-decodecorpus
test: test-zstd test-cli-tests test-fullbench test-fuzzer test-zstream test-invalidDictionaries test-rust-lib-smoke test-legacy test-decodecorpus
ifeq ($(QEMU_SYS),)
test: test-pool
endif
@@ -491,6 +498,10 @@ test-largeDictionary: largeDictionary
test-invalidDictionaries: invalidDictionaries
$(QEMU_SYS) ./invalidDictionaries
.PHONY: test-rust-lib-smoke
test-rust-lib-smoke: rustLibSmoke
$(QEMU_SYS) ./rustLibSmoke
test-legacy: legacy
$(QEMU_SYS) ./legacy
+66
View File
@@ -0,0 +1,66 @@
/*
* Link and run through the installed-style static archive, rather than the
* direct C-object-plus-Rust-archive test path used by the other test tools.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "zstd.h"
static int checkError(size_t result, const char* operation)
{
if (!ZSTD_isError(result)) return 0;
fprintf(stderr, "%s: %s\n", operation, ZSTD_getErrorName(result));
return 1;
}
int main(void)
{
enum { sourceSize = 64 * 1024 };
unsigned char source[sourceSize];
size_t const compressedCapacity = ZSTD_compressBound(sourceSize);
void* compressed = malloc(compressedCapacity);
void* restored = malloc(sourceSize);
size_t compressedSize;
size_t restoredSize;
ZSTD_DDict* ddict;
size_t index;
if (compressed == NULL || restored == NULL) {
fputs("allocation failed\n", stderr);
free(restored);
free(compressed);
return 1;
}
for (index = 0; index < sourceSize; ++index) {
source[index] = (unsigned char)((index * 13 + index / 97) & 0x1F);
}
compressedSize = ZSTD_compress(compressed, compressedCapacity,
source, sourceSize, 3);
if (checkError(compressedSize, "ZSTD_compress")) goto cleanup;
restoredSize = ZSTD_decompress(restored, sourceSize, compressed, compressedSize);
if (checkError(restoredSize, "ZSTD_decompress")) goto cleanup;
if (restoredSize != sourceSize || memcmp(source, restored, sourceSize) != 0) {
fputs("round trip mismatch\n", stderr);
goto cleanup;
}
/* This must pull the Rust DDict object and resolve its C helper cycle. */
ddict = ZSTD_createDDict(NULL, 0);
if (ddict == NULL || ZSTD_freeDDict(ddict) != 0) {
fputs("DDict lifecycle failed\n", stderr);
goto cleanup;
}
free(restored);
free(compressed);
return 0;
cleanup:
free(restored);
free(compressed);
return 1;
}