zlib-streams
Version:
WASM-based Compression Streams API implementation using zlib, with support for deflate64 decompression.
441 lines (384 loc) • 20.5 kB
Plain Text
CC=gcc
CFLAGS=-I./src/zlib -I./src -I./test -I./src/zlib/contrib/infback9 -O2 -w -MMD -MP
DEBUG_CFLAGS=-I./src/zlib -I./src -I./test -I./src/zlib/contrib/infback9 -O0 -g3 -ggdb3 -fno-inline -fno-omit-frame-pointer -w -MMD -MP
## By default we don't enable the INF9 tracing macros so debug binaries
## don't embed large diagnostic strings. To enable tracing for local
## debugging, uncomment and set DEBUG_DEFINES accordingly.
# Enable zlib's Tracev/Tracevv debug output and inf9 trace hooks for debug builds
# For traced wasm runs we enable INF9_TRACE via DEBUG_DEFINES_TRACED (used by the traced-wasm target)
## Debug defines are disabled by default for a clean commit-ready Makefile.
DEBUG_DEFINES=
DEBUG_DEFINES_TRACED=
SRC=src/inflate9.c src/zlib/contrib/infback9/inftree9.c src/zlib/zutil.c src/zlib/inflate.c src/zlib/inftrees.c
TESTSRC=zlib/crc32.c
LIBS=-lz
## Build the payload decompressor test binary used for verification (deduplicated sources)
PD_SRCS = test/payload_decompress.c \
src/inflate9.c src/zlib/inffast.c src/zlib/inftrees.c src/zlib/zutil.c src/zlib/inflate.c src/zlib/infback.c \
src/zlib/contrib/infback9/infback9.c src/zlib/contrib/infback9/inftree9.c src/zlib/crc32.c src/zlib/adler32.c src/zlib/trees.c
PD_NOWINDOW_SRCS = test/payload_decompress_nowindow.c \
src/inflate9.c src/zlib/inffast.c src/zlib/inftrees.c src/zlib/zutil.c src/zlib/inflate.c src/zlib/infback.c \
src/zlib/contrib/infback9/infback9.c src/zlib/contrib/infback9/inftree9.c src/zlib/crc32.c src/zlib/adler32.c src/zlib/trees.c
PD_NOWINDOW_DEBUG_OBJS = $(PD_NOWINDOW_SRCS:%.c=build/debug/%.o)
# Dedicated reference test that uses inflateBack9 (infback9.c) directly
PD_REF_SRCS = test/payload_decompress_ref.c \
src/zlib/contrib/infback9/infback9.c src/zlib/contrib/infback9/inftree9.c src/zlib/crc32.c src/zlib/adler32.c src/zlib/trees.c src/zlib/zutil.c
PD_REF_OBJS = $(PD_REF_SRCS:%.c=build/debug/%.o)
PD_REF_OBJS_RELEASE = $(PD_REF_SRCS:%.c=build/%.o)
# object paths for release and debug builds
PD_OBJS = $(PD_SRCS:%.c=build/%.o)
PD_DEBUG_OBJS = $(PD_SRCS:%.c=build/debug/%.o)
all: test_all
clean:
@echo "Cleaning build artifacts, dist, tmp, and generated files"
rm -rf ./test/payload_decompress_test_debug ./test/payload_decompress_ref_debug ./test/payload_decompress_test_debug.* ./test/payload_decompress_ref_debug.* build tmp *.d dist/*.wasm tmp/all_runs tmp/run_all_verify.log
# remove node generated artifacts if present
rm -f src/wasm/tests/*.out || true
test/payload_decompress_test: $(PD_OBJS)
mkdir -p test
$(CC) $(CFLAGS) $(PD_OBJS) -o $@
# Debug variant of the payload decompressor (with debug symbols)
test_all: test/payload_decompress_test test/payload_decompress_test_debug test/payload_decompress_ref test/payload_decompress_ref_debug
@echo "Built test binaries: test/payload_decompress_test test/payload_decompress_test_debug test/payload_decompress_ref test/payload_decompress_ref_debug"
# Build-only debug target used by IDEs (compile only, do not run)
test/payload_decompress_test_debug: $(PD_DEBUG_OBJS)
mkdir -p test
$(CC) $(DEBUG_DEFINES) $(DEBUG_CFLAGS) $(PD_DEBUG_OBJS) -o $@
@echo "Debug binary built: $@"
# Build a true reference binary that uses inflateBack9 (infback9.c)
test/payload_decompress_ref_debug: $(PD_REF_OBJS)
mkdir -p test
$(CC) $(DEBUG_DEFINES) $(DEBUG_CFLAGS) $(PD_REF_OBJS) -o $@
# Build nowindow debug variant
test/payload_decompress_nowindow_debug: $(PD_NOWINDOW_DEBUG_OBJS)
mkdir -p test
$(CC) $(DEBUG_DEFINES) $(DEBUG_CFLAGS) $(PD_NOWINDOW_DEBUG_OBJS) -o $@
test/payload_decompress_ref: $(PD_REF_OBJS_RELEASE)
mkdir -p test
$(CC) $(CFLAGS) $(PD_REF_OBJS_RELEASE) -o $@
# pattern rules to compile sources into build object dirs
build/%.o: %.c
@echo "CC $< -> $@"
@mkdir -p $(dir $@)
$(CC) $(CFLAGS) -c $< -o $@
build/debug/%.o: %.c
@echo "CC (debug) $< -> $@"
@mkdir -p $(dir $@)
$(CC) $(DEBUG_DEFINES) $(DEBUG_CFLAGS) -c $< -o $@
run_ref_test:
@mkdir -p tmp
@sh -c '\
./test/payload_decompress_ref_debug ./test/ref-data/10k_lines.deflate64 tmp/out_ref.bin 2> tmp/trace_ref.log; \
rc=$$?; \
if [ $$rc -eq 0 ] && [ -s tmp/out_ref.bin ]; then \
echo OK >> tmp/trace_ref.log; \
else \
echo KO >> tmp/trace_ref.log; \
fi'
run_all_payloads_verify_ci:
@echo "Building quiet debug harnesses (no INF9_TRACE) and running full verification..."
@$(MAKE) DEBUG_DEFINES= test/payload_decompress_test_debug test/payload_decompress_ref_debug test/payload_decompress_nowindow_debug >/dev/null 2>&1 || true
@mkdir -p tmp/all_runs
@./test/run_all_payloads_and_verify.sh | tee tmp/all_runs/run_all_verify.log
@echo "wrote tmp/all_runs/run_all_verify.log"
ci:
@mkdir -p tmp
@echo "CI START: $$(date)" > tmp/ci_run.log
@echo "Running make test (build)" >> tmp/ci_run.log
@$(MAKE) test_all >> tmp/ci_run.log 2>&1 || true
@for target run_ref_test; do \
echo "=== $$target ===" >> tmp/ci_run.log; \
$(MAKE) $$target >> tmp/ci_run.log 2>&1 || true; \
done
@echo "CI DONE: $$(date)" >> tmp/ci_run.log
@echo "Summary generated at $$(date)" > tmp/ci_summary.txt
@for out in tmp/out_*.bin; do [ -f "$$out" ] && stat -f"%N %z" "$$out" >> tmp/ci_summary.txt || true; done
@echo "" >> tmp/ci_summary.txt
@echo "-- last 10 lines of traces --" >> tmp/ci_summary.txt
@for trace in tmp/trace_*.log; do echo "--- $$trace ---" >> tmp/ci_summary.txt; tail -n 10 "$$trace" >> tmp/ci_summary.txt 2>/dev/null || true; echo "" >> tmp/ci_summary.txt; done
# Include generated dependency files (if present)
-include $(PD_OBJS:.o=.d) $(PD_DEBUG_OBJS:.o=.d)
# -----------------------------------------------------------------------------
# WASM build target (convenience target to produce dist/zlib-streams-dev.wasm)
# -----------------------------------------------------------------------------
EMCC ?= emsdk/upstream/emscripten/emcc
WASM_SRCS = src/wasm/inflate9_stream_wasm.c src/wasm/inflate_stream_wasm.c src/wasm/deflate_stream_wasm.c src/wasm/allocator.c src/inflate9.c \
src/zlib/contrib/infback9/inftree9.c \
src/zlib/inffast.c src/zlib/inflate.c src/zlib/inftrees.c src/zlib/zutil.c \
src/zlib/crc32.c src/zlib/adler32.c src/zlib/trees.c src/zlib/deflate.c
WASM_CFLAGS = -Isrc -Isrc/zlib -Isrc/zlib/contrib/infback9 -O2 -flto -DDYNAMIC_CRC_TABLE -DBUILDFIXED -DZ_SOLO
wasm: dist/zlib-streams-dev.wasm
wasm_traced: dist/zlib-streams_traced.wasm
dist/zlib-streams_traced.wasm: $(WASM_SRCS)
@echo "Building traced $@ using $(EMCC)"
@mkdir -p dist
$(EMCC) $(WASM_SRCS) $(WASM_CFLAGS) $(DEBUG_DEFINES_TRACED) -s WASM=1 -s STANDALONE_WASM=1 --no-entry \
-s EXPORTED_FUNCTIONS='["_inflate9_new","_inflate9_init","_inflate9_init_raw","_inflate9_process","_inflate9_end","_inflate9_last_consumed","_inflate_new","_inflate_init","_inflate_init_raw","_inflate_init_gzip","_inflate_process","_inflate_end","_inflate_last_consumed","_deflate_new","_deflate_init","_deflate_init_raw","_deflate_init_gzip","_deflate_process","_deflate_end","_deflate_last_consumed","_malloc","_free"]' \
-o $@
# Run reference C and WASM test suites over payloads in test/ref-data
run_ref_c_tests: test/payload_decompress_test_debug test/payload_decompress_ref_debug test/payload_decompress_nowindow_debug
@echo "Running C reference harnesses over deflate64 payloads"
@mkdir -p tmp/all_runs
@./test/run_deflate64_suite.sh
run_ref_wasm_tests: dist/zlib-streams-dev.wasm
@echo "Running WASM runner over deflate64 payloads (sequential by default)"
@mkdir -p tmp/all_runs
@$(MAKE) run_ref_wasm_seq
@$(MAKE) run_wasm_roundtrip_mem
run_ref_wasm_seq: dist/zlib-streams-dev.wasm
@echo "Running WASM runner sequentially per payload (safe, non-blocking)"
@mkdir -p tmp/all_runs
@sh -c '\
for p in test/ref-data/*deflate64*; do \
[ -f "$$p" ] || continue; \
f=$$(basename "$$p"); \
printf "\nPayload: %s\n" "$$f"; \
rm -f tmp/all_runs/wasm__$$f.out; \
node src/wasm/tests/test_inflate9_stream.js dist/zlib-streams-dev.wasm "$$p" tmp/all_runs/wasm__$$f.out || printf " NODE_RC:%s for %s\n" "$$?" "$$f"; \
if [ -f tmp/all_runs/wasm__$$f.out ]; then \
printf " wrote tmp/all_runs/wasm__%s.out (size=%s)\n" "$$f" "$(stat -f%z tmp/all_runs/wasm__$$f.out 2>/dev/null || echo unknown)"; \
ref=tmp/all_runs/payload_decompress_ref_debug__$$f.out; \
if [ -f "$$ref" ]; then \
h1=`shasum -a 256 tmp/all_runs/wasm__$$f.out | cut -d\ -f1`; \
h2=`shasum -a 256 "$$ref" | cut -d\ -f1`; \
if [ "$$h1" = "$$h2" ]; then \
printf " SHA256 MATCH: %s\n" "$$h1"; \
else \
printf " SHA256 MISMATCH: wasm=%s ref=%s\n" "$$h1" "$$h2"; \
fi; \
else \
printf " ref missing: %s\n" "$$ref"; \
fi; \
fi; \
done'
run_ref_tests: run_ref_c_tests run_ref_wasm_tests
@echo "Completed C+WASM reference test suites"
run_wasm_roundtrip: dist/zlib-streams-dev.wasm
@echo "Running wasm roundtrip tests over tmp/all_runs/roundtrip_input*"
@mkdir -p tmp/all_runs
# populate tmp/all_runs with canonical roundtrip inputs
@cp -f test/ref-data/roundtrip_inputs/roundtrip_input.txt tmp/all_runs/roundtrip_input.txt
@cp -f test/ref-data/roundtrip_inputs/roundtrip_input1.txt tmp/all_runs/roundtrip_input1.txt
@cp -f test/ref-data/roundtrip_inputs/roundtrip_input3.bin tmp/all_runs/roundtrip_input3.bin
@sh -c '\
for p in tmp/all_runs/roundtrip_input*; do \
[ -f "$$p" ] || continue; \
f=$$(basename "$$p"); \
printf "\nInput: %s\n" "$$f"; \
rm -f tmp/all_runs/roundtrip_out__$$f; \
node src/wasm/tests/test_round_trip_stream.js dist/zlib-streams-dev.wasm "$$p" tmp/all_runs/roundtrip_out__$$f 2>&1 | sed -n '1,200p'; \
rc=$$?; \
if [ $$rc -eq 0 ]; then \
printf " OK: %s\n" "$$f"; \
else \
printf " FAIL (rc=%s): %s\n" "$$rc" "$$f"; \
fi; \
done'
run_wasm_roundtrip_mem: dist/zlib-streams-dev.wasm
@echo "Running wasm roundtrip (in-memory) tests over tmp/all_runs/roundtrip_input*"
@mkdir -p tmp/all_runs
@sh -c '\
for p in tmp/all_runs/roundtrip_input*; do \
[ -f "$$p" ] || continue; \
f=$$(basename "$$p"); \
printf "\nInput: %s\n" "$$f"; \
node src/wasm/tests/test_round_trip_stream_mem.js dist/zlib-streams-dev.wasm "$$p" 2>&1 | sed -n "1,200p"; \
rc=$$?; \
if [ $$rc -eq 0 ]; then \
printf " OK: %s\n" "$$f"; \
else \
printf " FAIL (rc=%s): %s\n" "$$rc" "$$f"; \
fi; \
done'
run_transform_roundtrip: dist/zlib-streams-dev.wasm
@echo "Running TransformStream roundtrip test"
@node src/wasm/tests/test_transform_roundtrip.js dist/zlib-streams-dev.wasm
deno_run_tests:
@echo "Running Deno test suite (deno/run_all_tests.sh)"
@chmod +x deno/run_all_tests.sh || true
@./deno/run_all_tests.sh dist/zlib-streams.wasm
run_inflate9_roundtrip_all: dist/zlib-streams-dev.wasm
@echo "Running inflate9 roundtrip over deflate64 payloads"
@node src/wasm/tests/test_inflate9_roundtrip_all.js dist/zlib-streams-dev.wasm
test_decompressionstream_inflate9: dist/zlib-streams-dev.wasm
@echo "Testing DecompressionStreamZlib against native inflate9 for deflate64 payloads"
@node src/wasm/tests/test_decompressionstream_inflate9.js dist/zlib-streams-dev.wasm
# Single target to run the main wasm/TransformStream tests used during development
run_all_tests: dist/zlib-streams-dev.wasm
@echo "Running all wasm TransformStream tests"
@$(MAKE) run_ref_wasm_tests
@$(MAKE) run_wasm_roundtrip
# Additional roundtrip tests not covered by the generic runners
@node src/wasm/tests/test_round_trip_stream_deflate.js dist/zlib-streams-dev.wasm
@node src/wasm/tests/test_round_trip_stream_gzip.js dist/zlib-streams-dev.wasm
@mkdir -p tmp/all_runs
# Ensure a raw-deflate test input exists (create from a repeated pattern)
@node -e "const fs=require('fs'), z=require('zlib'); fs.writeFileSync('tmp/all_runs/roundtrip_input3.bin', z.deflateRawSync(Buffer.alloc(24000, 'x')));"
@node src/wasm/tests/test_inflate_stream.js dist/zlib-streams-dev.wasm tmp/all_runs/roundtrip_input3.bin tmp/all_runs/inflate_stream_out.bin
# CLI helper run (small sample) for regressions/throughput checks
@node src/wasm/tests/run_roundtrip_cli.js deflate 24000 dist/zlib-streams-dev.wasm
@$(MAKE) run_transform_roundtrip
@$(MAKE) test_decompressionstream_inflate9
@echo "Completed run_all_tests"
dist/zlib-streams-dev.wasm: $(WASM_SRCS)
@echo "Building $@ using $(EMCC)"
@mkdir -p dist
$(EMCC) $(WASM_SRCS) $(WASM_CFLAGS) -s WASM=1 -s STANDALONE_WASM=1 --no-entry \
-s EXPORTED_FUNCTIONS='["_inflate9_new","_inflate9_init","_inflate9_init_raw","_inflate9_process","_inflate9_end","_inflate9_last_consumed","_inflate_new","_inflate_init","_inflate_init_raw","_inflate_init_gzip","_inflate_process","_inflate_end","_inflate_last_consumed","_deflate_new","_deflate_init","_deflate_init_raw","_deflate_init_gzip","_deflate_process","_deflate_end","_deflate_last_consumed","_malloc","_free"]' \
-o $@
cp src/wasm/api/zlib-streams.js dist/zlib-streams.js
# Production-optimized wasm: smaller build with -Oz and no extra runtime methods.
wasm_prod: dist/zlib-streams.wasm
dist/zlib-streams.wasm: $(WASM_SRCS)
@echo "Building production wasm $@ using $(EMCC)"
@mkdir -p dist
$(EMCC) $(WASM_SRCS) $(WASM_CFLAGS) -Oz -flto -s WASM=1 -s STANDALONE_WASM=1 --no-entry \
-s FILESYSTEM=0 -s DISABLE_EXCEPTION_CATCHING=1 \
-s EXPORTED_FUNCTIONS='["_inflate9_new","_inflate9_init","_inflate9_init_raw","_inflate9_process","_inflate9_end","_inflate9_last_consumed","_inflate_new","_inflate_init","_inflate_init_raw","_inflate_init_gzip","_inflate_process","_inflate_end","_inflate_last_consumed","_deflate_new","_deflate_init","_deflate_init_raw","_deflate_init_gzip","_deflate_process","_deflate_end","_deflate_last_consumed","_malloc","_free"]' \
-o $@
cp src/wasm/api/zlib-streams.js dist/zlib-streams.js
@which wasm-opt >/dev/null 2>&1 && { echo "Running wasm-opt -Oz --enable-bulk-memory-opt"; wasm-opt -Oz --enable-bulk-memory-opt -o $@ $@ || true; } || true
# -----------------------------------------------------------------------------
# Optional generator: build a C++ tool that creates Deflate64 ZIPs using the
# 7-Zip SDK. This target is guarded: it only compiles when the SDK is present
# at src/7zip. The generator is not built by default.
# -----------------------------------------------------------------------------
CXX ?= g++
CXXFLAGS = -std=c++17 -I./src -I./src/7zip -O2
SDK_ROOT = src/7zip/CPP/7zip
# Guard building the SDK-backed generator. Set ENABLE_SDK_GENERATOR=1 to
# attempt compilation. By default, we skip to avoid platform-specific SDK
# build errors on non-Windows hosts.
ENABLE_SDK_GENERATOR ?= 0
# Only require the heavy SDK archive when we actually plan to build the
# SDK-backed generator. This avoids building the SDK object archive when
# the generator is being built in CLI-only mode.
SDK_PREREQS := $(if $(filter 1,$(ENABLE_SDK_GENERATOR)),build/lib7zip.a,)
# Minimal curated list of SDK sources required to build a Zip creator that
# uses compress/deflate/deflate64. This avoids compiling UI/Explorer/Windows
# specific code which fails on non-Windows hosts. Add more files here if
# the linker reports missing symbols.
SDK_FAST_SRCS = \
$(SDK_ROOT)/Archive/Zip/ZipAddCommon.cpp \
$(SDK_ROOT)/Archive/Zip/ZipHandler.cpp \
$(SDK_ROOT)/Archive/Zip/ZipHandlerOut.cpp \
$(SDK_ROOT)/Archive/Zip/ZipIn.cpp \
$(SDK_ROOT)/Archive/Zip/ZipItem.cpp \
$(SDK_ROOT)/Archive/Zip/ZipOut.cpp \
$(SDK_ROOT)/Archive/Zip/ZipRegister.cpp \
$(SDK_ROOT)/Archive/Zip/ZipUpdate.cpp \
$(SDK_ROOT)/Common/CreateCoder.cpp \
$(SDK_ROOT)/../Common/MyString.cpp \
$(SDK_ROOT)/../Common/StringConvert.cpp \
$(SDK_ROOT)/../Common/UTFConvert.cpp \
$(SDK_ROOT)/../Common/IntToString.cpp \
$(SDK_ROOT)/../Common/MyWindows.cpp \
$(SDK_ROOT)/Common/StreamObjects.cpp \
$(SDK_ROOT)/Common/OutMemStream.cpp \
$(SDK_ROOT)/Common/OutBuffer.cpp \
$(SDK_ROOT)/Common/InBuffer.cpp \
$(SDK_ROOT)/Common/InOutTempBuffer.cpp \
$(SDK_ROOT)/Common/StreamUtils.cpp \
$(SDK_ROOT)/Common/OffsetStream.cpp \
$(SDK_ROOT)/Common/FileStreams.cpp \
$(SDK_ROOT)/Common/CWrappers.cpp \
$(SDK_ROOT)/Common/FilterCoder.cpp \
$(SDK_ROOT)/Compress/CopyCoder.cpp \
$(SDK_ROOT)/Compress/DeflateRegister.cpp \
$(SDK_ROOT)/Compress/DeflateEncoder.cpp \
$(SDK_ROOT)/Compress/DeflateDecoder.cpp \
$(SDK_ROOT)/Compress/LzOutWindow.cpp \
$(SDK_ROOT)/Compress/Deflate64Register.cpp \
$(SDK_ROOT)/../Windows/FileIO.cpp \
$(SDK_ROOT)/../Windows/FileFind.cpp \
$(SDK_ROOT)/../Windows/FileName.cpp \
$(SDK_ROOT)/../Windows/FileMapping.cpp \
$(SDK_ROOT)/../Windows/System.cpp \
$(SDK_ROOT)/../Windows/TimeUtils.cpp \
$(SDK_ROOT)/../Windows/PropVariant.cpp \
$(SDK_ROOT)/../Windows/PropVariantConv.cpp \
$(SDK_ROOT)/../Windows/MemoryGlobal.cpp \
$(SDK_ROOT)/../Windows/MemoryLock.cpp \
$(SDK_ROOT)/../Windows/SystemInfo.cpp
SDK_C_SRCS := $(wildcard src/7zip/C/*.c)
SDK_C_OBJS := $(patsubst src/7zip/C/%.c,build/7zip/C/%.o,$(SDK_C_SRCS))
SDK_CPP := $(shell [ -d src/7zip ] && [ "$(ENABLE_SDK_GENERATOR)" -eq "1" ] && echo $(SDK_FAST_SRCS) || echo)
SDK_OBJS := $(patsubst $(SDK_ROOT)/%.cpp,build/7zip/%.o,$(SDK_CPP))
# Any extra non-SDK .cpp sources that should be compiled into the SDK archive
SDK_EXTRA_SRCS := src/generator/sdk_deflate64.cpp \
src/generator/win_compat.cpp \
src/generator/com_shims.cpp \
src/generator/sdk_shims.cpp
SDK_EXTRA_OBJS := $(patsubst src/%.cpp,build/7zip/%.o,$(SDK_EXTRA_SRCS))
build/lib7zip.a: $(SDK_OBJS) $(SDK_C_OBJS) $(SDK_EXTRA_OBJS)
@echo "Archiving SDK objects -> $@"
@mkdir -p $(dir $@)
@ar rcs $@ $(SDK_OBJS) $(SDK_C_OBJS) $(SDK_EXTRA_OBJS)
# Rule to build SDK sources (in-tree)
build/7zip/%.o: $(SDK_ROOT)/%.cpp
@echo "CXX sdk $< -> $@"
@mkdir -p $(dir $@)
$(CXX) $(CXXFLAGS) -I./src/7zip/CPP -I./src/7zip/CPP/7zip -I./src/generator -include src/generator/compat_prefix.h -c $< -o $@
# Rule to build extra local cpp sources into the SDK object tree
build/7zip/%.o: %.cpp
@echo "CXX local $< -> $@"
@mkdir -p $(dir $@)
$(CXX) $(CXXFLAGS) -I./src/7zip/CPP -I./src/7zip/CPP/7zip -c $< -o $@
# Rule to build C sources from src/7zip/C into the SDK object tree
build/7zip/C/%.o: $(SDK_ROOT)/../../C/%.c
@echo "CC sdk C $< -> $@"
@mkdir -p $(dir $@)
$(CC) $(CFLAGS) -I./src/7zip/C -I./src/7zip/CPP -I./src/7zip/CPP/7zip -c $< -o $@
# specific rule for sources under src/generator
build/7zip/generator/%.o: src/generator/%.cpp
@echo "CXX gen $< -> $@"
@mkdir -p $(dir $@)
$(CXX) $(CXXFLAGS) -I./src/7zip/CPP -I./src/7zip/CPP/7zip -c $< -o $@
generator: $(SDK_PREREQS)
@if [ "$(ENABLE_SDK_GENERATOR)" -ne "1" ]; then \
mkdir -p bin; \
echo "Building CLI-only generator (requires '7z' in PATH)"; \
$(CXX) $(CXXFLAGS) -o bin/create_deflate64 src/generator/create_deflate64.cpp || (echo "generator build failed"; exit 1); \
echo "built bin/create_deflate64"; \
else \
if [ -d src/7zip ]; then \
mkdir -p bin; \
echo "Building SDK-backed generator and linking with SDK library"; \
$(CXX) $(CXXFLAGS) -DUSE_7ZIP_SDK -I./src/7zip/CPP -I./src/7zip/CPP/7zip -o bin/create_deflate64 src/generator/create_deflate64.cpp build/7zip/Compress/Deflate64Register.o build/lib7zip.a || (echo "generator build failed"; exit 1); \
echo "built bin/create_deflate64"; \
else \
echo "skipping generator build: src/7zip not present (run 'make src/7zip' to fetch)"; \
fi; \
fi
# Helper: download and extract 7-Zip SDK into src/7zip
src/7zip:
@echo "Downloading 7-Zip LZMA SDK and extracting into src/7zip";
@mkdir -p tmp
@test -f tmp/lzma2501.7z || curl -sSL -o tmp/lzma2501.7z https://7-zip.org/a/lzma2501.7z
@mkdir -p src
@rm -rf src/7zip
@echo "Extracting tmp/lzma2501.7z -> src/7zip";
@mkdir -p src/7zip
@command -v 7z >/dev/null 2>&1 || (echo "7z not found: install p7zip to extract the SDK or extract tmp/lzma2501.7z manually"; exit 1)
@7z x -y -otmp tmp/lzma2501.7z >/dev/null 2>&1 || (echo "failed to extract SDK with 7z"; exit 1)
@# The archive contains a 7zip folder; move it to src/7zip/CPP
@if [ -d tmp/CPP ]; then mv tmp/CPP src/7zip/CPP; elif [ -d tmp/7z ]; then mv tmp/7z src/7zip/CPP; else mv tmp/* src/7zip/CPP 2>/dev/null || true; fi
@echo "SDK extracted to src/7zip/CPP"