796 lines
37 KiB
Makefile
796 lines
37 KiB
Makefile
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
# ZUPT — backup compression with hybrid post-quantum encryption
|
|
# Build system. Pure GNU make, no autotools, no cmake required.
|
|
#
|
|
# Targets:
|
|
# make Build the zupt binary
|
|
# make V=1 Verbose: show every command line
|
|
# make install Install to /usr/local (override with PREFIX=/usr)
|
|
# make check Run the source-only distribution test suite
|
|
# make test-all Run the extended upstream test suite
|
|
# make test-asan Build with AddressSanitizer + UBSan
|
|
# make test-asan-run Execute the sanitizer smoke test
|
|
# make clean Remove build artifacts
|
|
#
|
|
# Build profiles (all controllable via standard env vars):
|
|
# CC=clang make Use Clang instead of GCC
|
|
# CFLAGS="-O3 -g" make Override the default optimization
|
|
# make PREFIX=/usr DESTDIR=/tmp/stage Staged install for packagers
|
|
#
|
|
# The compiler target, rather than the build host, controls architecture
|
|
# selection. This keeps cross builds from accidentally enabling host assembly.
|
|
CC ?= cc
|
|
CPPFLAGS ?=
|
|
CFLAGS ?= -O2 -g
|
|
LDFLAGS ?=
|
|
LDLIBS ?=
|
|
AR ?= ar
|
|
ARFLAGS ?= rcs
|
|
RANLIB ?= ranlib
|
|
STRIP ?= strip
|
|
PKG_CONFIG ?= pkg-config
|
|
ASFLAGS ?=
|
|
|
|
# GNU make has a built-in ARFLAGS=rv. Use archive creation flags by default,
|
|
# while preserving values supplied through the environment or command line.
|
|
ifeq ($(origin ARFLAGS),default)
|
|
ARFLAGS := rcs
|
|
endif
|
|
|
|
DESTDIR ?=
|
|
PREFIX ?= /usr/local
|
|
BINDIR ?= $(PREFIX)/bin
|
|
LIBDIR ?= $(PREFIX)/lib
|
|
INCLUDEDIR ?= $(PREFIX)/include
|
|
DATADIR ?= $(PREFIX)/share
|
|
MANDIR ?= $(DATADIR)/man
|
|
MAN1DIR ?= $(MANDIR)/man1
|
|
BASHCOMPDIR ?= $(DATADIR)/bash-completion/completions
|
|
ZSHCOMPDIR ?= $(DATADIR)/zsh/site-functions
|
|
FISHCOMPDIR ?= $(DATADIR)/fish/vendor_completions.d
|
|
LICENSEDIR ?= $(DATADIR)/licenses/zupt
|
|
GZIP ?= gzip
|
|
GZIPFLAGS ?= -9 -n
|
|
INSTALL_LEGACY_ALIAS ?= 0
|
|
INSTALL_LICENSES ?= 1
|
|
LICENSE_FILES = LICENSE LICENSE-AGPL-3.0 LICENSE-GPL-3.0 \
|
|
LICENSE-BSD-2-Clause LICENSE-BSD-3-Clause LICENSE-CC0-1.0 \
|
|
NOTICE THIRD-PARTY-NOTICES.md
|
|
|
|
# Standard packager variables above are never rewritten. Project-owned flags
|
|
# are passed alongside them on every command line.
|
|
PROJECT_CPPFLAGS := -D_DEFAULT_SOURCE -Iinclude -Isrc
|
|
PROJECT_CFLAGS := -Wall -Wextra -Woverlength-strings -std=c11
|
|
PROJECT_LDFLAGS :=
|
|
PROJECT_CLI_LDFLAGS :=
|
|
PROJECT_LDLIBS := -lm
|
|
EXEEXT :=
|
|
CREATE_TEST_ALIAS := 0
|
|
FEATURE_CPPFLAGS :=
|
|
FEATURE_LDLIBS :=
|
|
|
|
# Clang's -Wcast-align diagnoses the pointer casts required by the explicitly
|
|
# unaligned x86 load/store intrinsics, and cannot infer alignment through the
|
|
# byte-backed VaptVupt arenas. Those arenas start at malloc alignment and all
|
|
# typed offsets are rounded to at least 8 bytes. Keep the compatibility
|
|
# suppression local to the three audited translation units; every other file
|
|
# retains a caller-supplied -Wcast-align/-Werror policy.
|
|
CLANG_CAST_ALIGN_FLAGS :=
|
|
ifneq ($(findstring clang,$(shell $(CC) --version 2>/dev/null | head -n 1)),)
|
|
CLANG_CAST_ALIGN_FLAGS := -Wno-cast-align
|
|
endif
|
|
CLANG_CAST_ALIGN_OBJS := src/vv_ans.o src/vv_simd.o src/zupt_sha256_shani.o
|
|
|
|
TARGET_MACHINE ?= $(shell $(CC) -dumpmachine 2>/dev/null)
|
|
TARGET_CPU := $(firstword $(subst -, ,$(TARGET_MACHINE)))
|
|
ifeq ($(strip $(TARGET_CPU)),)
|
|
TARGET_CPU := unknown
|
|
endif
|
|
|
|
# The Windows extraction path uses the documented NtCreateFile RootDirectory
|
|
# facility so directory components are resolved relative to pinned handles.
|
|
# A self-contained PE is required because POSIX-thread MinGW toolchains may
|
|
# otherwise add an undeclared libwinpthread-1.dll runtime dependency.
|
|
ifneq ($(strip $(findstring mingw,$(TARGET_MACHINE))$(findstring windows,$(TARGET_MACHINE))),)
|
|
EXEEXT := .exe
|
|
CREATE_TEST_ALIAS := 0
|
|
PROJECT_LDFLAGS += -static
|
|
PROJECT_CLI_LDFLAGS += -municode
|
|
PROJECT_LDLIBS += -lntdll
|
|
endif
|
|
|
|
# pthread is part of bionic and the Windows implementation uses native APIs.
|
|
ifeq ($(findstring android,$(TARGET_MACHINE)),)
|
|
ifeq ($(findstring mingw,$(TARGET_MACHINE)),)
|
|
ifeq ($(findstring windows,$(TARGET_MACHINE)),)
|
|
PROJECT_CFLAGS += -pthread
|
|
PROJECT_LDLIBS += -pthread
|
|
endif
|
|
endif
|
|
endif
|
|
|
|
ifneq ($(filter $(INSTALL_LEGACY_ALIAS),0 1),$(INSTALL_LEGACY_ALIAS))
|
|
$(error INSTALL_LEGACY_ALIAS must be 0 or 1)
|
|
endif
|
|
ifneq ($(filter $(INSTALL_LICENSES),0 1),$(INSTALL_LICENSES))
|
|
$(error INSTALL_LICENSES must be 0 or 1)
|
|
endif
|
|
|
|
# --- Verbose build ---
|
|
V ?= 0
|
|
ifeq ($(V),1)
|
|
Q =
|
|
else
|
|
Q = @
|
|
endif
|
|
|
|
# --- ZUPT core sources ---
|
|
ZUPT_SOURCES = src/zupt_main.c src/zupt_format.c src/zupt_lz.c src/zupt_lzh.c \
|
|
src/zupt_xxh.c src/zupt_sha256.c src/zupt_sha256_shani.c src/zupt_aes256.c src/zupt_crypto.c \
|
|
src/zupt_crypto_sdk.c src/zupt_crypto_pqbox.c \
|
|
src/zupt_predict.c src/zupt_parallel.c src/zupt_keccak.c \
|
|
src/zupt_x25519.c src/zupt_mlkem.c src/zupt_cpuid.c src/zupt_mlock.c \
|
|
src/zupt_filetype.c src/zupt_disk.c src/zupt_dedup.c
|
|
|
|
# --- Optional system libraries (never vendored, never downloaded) ---
|
|
WITH_SDK ?= 0
|
|
WITH_PQBOX ?= 0
|
|
|
|
ifneq ($(filter $(WITH_SDK),0 1),$(WITH_SDK))
|
|
$(error WITH_SDK must be 0 or 1)
|
|
endif
|
|
ifneq ($(filter $(WITH_PQBOX),0 1),$(WITH_PQBOX))
|
|
$(error WITH_PQBOX must be 0 or 1)
|
|
endif
|
|
|
|
ifeq ($(WITH_SDK),1)
|
|
SDK_PKG_CONFIG ?= libvuptsdk
|
|
SDK_CPPFLAGS ?= $(shell $(PKG_CONFIG) --cflags $(SDK_PKG_CONFIG) 2>/dev/null)
|
|
SDK_LDLIBS ?= $(shell $(PKG_CONFIG) --libs $(SDK_PKG_CONFIG) 2>/dev/null)
|
|
ifeq ($(strip $(SDK_LDLIBS)),)
|
|
$(error WITH_SDK=1 requires the system libvuptsdk development package; set SDK_CPPFLAGS and SDK_LDLIBS to explicit system paths if no pkg-config file is provided)
|
|
endif
|
|
FEATURE_CPPFLAGS += -DZUPT_WITH_SDK $(SDK_CPPFLAGS)
|
|
FEATURE_LDLIBS += $(SDK_LDLIBS)
|
|
endif
|
|
|
|
ifeq ($(WITH_PQBOX),1)
|
|
PQBOX_PKG_CONFIG ?= libpqvaptvupt
|
|
PQBOX_CPPFLAGS ?= $(shell $(PKG_CONFIG) --cflags $(PQBOX_PKG_CONFIG) 2>/dev/null)
|
|
PQBOX_LDLIBS ?= $(shell $(PKG_CONFIG) --libs $(PQBOX_PKG_CONFIG) 2>/dev/null)
|
|
ifeq ($(strip $(PQBOX_LDLIBS)),)
|
|
$(error WITH_PQBOX=1 requires the system libpqvaptvupt development package; set PQBOX_CPPFLAGS and PQBOX_LDLIBS to explicit system paths if no pkg-config file is provided)
|
|
endif
|
|
FEATURE_CPPFLAGS += -DZUPT_WITH_PQBOX $(PQBOX_CPPFLAGS)
|
|
FEATURE_LDLIBS += $(PQBOX_LDLIBS)
|
|
endif
|
|
|
|
# --- VAPTVUPT: VaptVupt codec sources (GPL-3.0-or-later; tool is AGPL-3.0-or-later) ---
|
|
VV_SOURCES = src/vv_encoder.c src/vv_decoder.c src/vv_ans.c src/vv_bcj.c \
|
|
src/vv_huffman.c src/vv_simd.c src/vv_xxh64.c src/vaptvupt_api.c
|
|
|
|
SOURCES = $(ZUPT_SOURCES) $(VV_SOURCES)
|
|
|
|
HEADERS = include/zupt.h include/zupt_keccak.h include/zupt_mlkem.h \
|
|
include/zupt_x25519.h include/zupt_cpuid.h include/zupt_jasmin.h \
|
|
include/zupt_acsl.h \
|
|
include/vaptvupt.h include/vaptvupt_api.h include/vv_huffman.h include/vv_ans.h \
|
|
include/vv_platform.h \
|
|
src/zupt_thread.h src/zupt_parallel.h src/zupt_internal.h
|
|
|
|
PROGRAM = zupt
|
|
TARGET = $(PROGRAM)$(EXEEXT)
|
|
LEGACY_PROGRAM = vaptvupt
|
|
LEGACY_LINK = $(LEGACY_PROGRAM)$(EXEEXT)
|
|
MANPAGE = doc/zupt.1
|
|
MANPAGE_GZ = $(PROGRAM).1.gz
|
|
|
|
# Architecture-specific code is opt-in and isolated. The normal x86_64 build
|
|
# stays at the ABI baseline; in particular, no complete codec TU gets -mavx2.
|
|
SHANI_FLAGS :=
|
|
ifneq ($(filter x86_64 amd64 i386 i486 i586 i686,$(TARGET_CPU)),)
|
|
SHANI_FLAGS := -msha -mssse3 -msse4.1
|
|
endif
|
|
|
|
# Optional textual assembly is disabled by default so every
|
|
# compiler/architecture has the audited C fallback. Four files are jasminc
|
|
# outputs and zupt_aes_ctr4.s is separately identified as hand-written. When
|
|
# requested, the compiler driver assembles them while preserving cross-target
|
|
# and sysroot settings.
|
|
WITH_JASMIN ?= 0
|
|
ifneq ($(filter $(WITH_JASMIN),0 1),$(WITH_JASMIN))
|
|
$(error WITH_JASMIN must be 0 or 1)
|
|
endif
|
|
JAZZ_S = jasmin/zupt_mac_verify.s jasmin/zupt_mlkem_select.s \
|
|
jasmin/zupt_aes_ctr.s jasmin/zupt_x25519_fe.s jasmin/zupt_aes_ctr4.s
|
|
JAZZ_O :=
|
|
ifeq ($(WITH_JASMIN),1)
|
|
ifeq ($(filter x86_64 amd64,$(TARGET_CPU)),)
|
|
$(error WITH_JASMIN=1 is supported only for an x86_64 compiler target; detected $(TARGET_MACHINE))
|
|
endif
|
|
ifneq ($(words $(wildcard $(JAZZ_S))),$(words $(JAZZ_S)))
|
|
$(error WITH_JASMIN=1 requested, but one or more optional .s sources are missing)
|
|
endif
|
|
FEATURE_CPPFLAGS += -DZUPT_USE_JASMIN
|
|
JAZZ_O := $(JAZZ_S:.s=.o)
|
|
endif
|
|
|
|
# --- Object files ---
|
|
# These objects contain the baseline codec implementation. Optimized SHA-NI
|
|
# remains in its own translation unit below and is guarded at runtime.
|
|
VV_SIMD_OBJS = src/vv_encoder.o src/vv_decoder.o src/vv_simd.o
|
|
|
|
# The bundled codec uses the same warning policy as the application. Do not
|
|
# add broad -Wno-* flags here: localized upstream changes are recorded in
|
|
# THIRD-PARTY-NOTICES.md and must compile without masked diagnostics.
|
|
VV_PLAIN_OBJS = src/vv_ans.o src/vv_huffman.o src/vv_xxh64.o src/vv_bcj.o src/vaptvupt_api.o
|
|
ZUPT_OBJS = $(patsubst %.c,%.o,$(ZUPT_SOURCES))
|
|
ALL_OBJS = $(ZUPT_OBJS) $(VV_SIMD_OBJS) $(VV_PLAIN_OBJS)
|
|
|
|
# ═══════════════════════════════════════════════════════════════════
|
|
# BUILD RULES
|
|
# ═══════════════════════════════════════════════════════════════════
|
|
|
|
.DELETE_ON_ERROR:
|
|
.PHONY: all clean install uninstall test test-all release-check test-asan test-asan-run \
|
|
test-vectors test-f06 test-vv fuzz-build fuzz-format fuzz-format-run \
|
|
help audit-licenses source-audit dist check
|
|
|
|
all: $(TARGET)
|
|
|
|
# ═══════════════════════════════════════════════════════════════════
|
|
# audit-licenses — verify covered code, build, CI, and packaging files carry
|
|
# the correct SPDX marker. Legal-document completeness is audited separately
|
|
# through LICENSE*, NOTICE, and THIRD-PARTY-NOTICES.md; this target is not a
|
|
# claim of full REUSE conformance.
|
|
# AGPL-3.0-or-later for the application/core, GPL-3.0-or-later for the
|
|
# bundled codec files, BSD-2-Clause for the two xxHash-derived units, and
|
|
# CC0-1.0 for the pq-crystals/kyber-derived portions of native ML-KEM, and
|
|
# BSD-3-Clause for curve25519-donna-derived X25519 portions.
|
|
# See THIRD-PARTY-NOTICES.md.
|
|
# ═══════════════════════════════════════════════════════════════════
|
|
audit-licenses:
|
|
@MISSING=0; WRONG=0; \
|
|
for f in $$(find . -type f \( -name '*.c' -o -name '*.h' -o -name '*.hpp' \
|
|
-o -name '*.py' -o -name '*.sh' -o -name '*.yml' -o -name '*.yaml' \
|
|
-o -name '*.jazz' -o -name '*.s' -o -name '*.S' -o -name 'Makefile' \
|
|
-o -name '*.map' -o -name '*.bat' -o -name '*.command' \
|
|
-o -name '*.desktop' -o -name '*.nemo_action' -o -name '*.spec' \
|
|
-o -name '*.rb' -o -name '*.scm' -o -name '*.nix' \
|
|
-o -name '*.iss' -o -name '*.nsi' -o -name '*.fish' \
|
|
-o -name 'PKGBUILD' -o -name '_service' -o -name 'rules' \) \
|
|
-not -path './build/*' \
|
|
-not -path './build_obj/*' \
|
|
-not -path './sdk/build/*'); do \
|
|
case "$$f" in \
|
|
./src/zupt_mlkem.c) \
|
|
EXPECTED_ID="AGPL-3.0-or-later AND CC0-1.0" ;; \
|
|
./src/zupt_x25519.c) \
|
|
EXPECTED_ID="AGPL-3.0-or-later AND BSD-3-Clause" ;; \
|
|
./src/zupt_xxh.c) \
|
|
EXPECTED_ID="AGPL-3.0-or-later AND BSD-2-Clause" ;; \
|
|
./src/vv_xxh64.c) \
|
|
EXPECTED_ID="GPL-3.0-or-later AND BSD-2-Clause" ;; \
|
|
./src/vv_*.c|./src/vaptvupt_api.c|./include/vv_*.h|./include/vaptvupt*.h) \
|
|
EXPECTED_ID="GPL-3.0-or-later" ;; \
|
|
*) \
|
|
EXPECTED_ID="AGPL-3.0-or-later" ;; \
|
|
esac; \
|
|
HEADER=$$(sed -n '1,12p' "$$f"); \
|
|
HEADER_COUNT=$$(printf '%s\n' "$$HEADER" | \
|
|
grep -c 'SPDX-License-Identifier:' || true); \
|
|
ACTUAL_ID=$$(printf '%s\n' "$$HEADER" | \
|
|
sed -n 's/^.*SPDX-License-Identifier:[[:space:]]*//p' | \
|
|
sed 's/[[:space:]]*\*\/[[:space:]]*$$//; s/[[:space:]]*-->[[:space:]]*$$//; s/[[:space:]]*$$//' | \
|
|
head -n 1); \
|
|
if [ "$$HEADER_COUNT" -eq 0 ]; then \
|
|
echo " ✗ $$f (missing SPDX)"; \
|
|
MISSING=$$((MISSING+1)); \
|
|
elif [ "$$HEADER_COUNT" -ne 1 ] || [ "$$ACTUAL_ID" != "$$EXPECTED_ID" ]; then \
|
|
echo " ✗ $$f (wrong SPDX header, expected exactly once: $$EXPECTED_ID)"; \
|
|
WRONG=$$((WRONG+1)); \
|
|
fi; \
|
|
done; \
|
|
if [ $$MISSING -eq 0 ] && [ $$WRONG -eq 0 ]; then \
|
|
echo " ✓ Covered code/build/CI/packaging files carry correct SPDX markers"; \
|
|
echo " (AGPL core; GPL codec; BSD-2 XXH64; BSD-3 X25519; CC0 ML-KEM)"; \
|
|
else \
|
|
echo ""; \
|
|
echo " $$MISSING missing, $$WRONG with wrong SPDX. Aborting."; \
|
|
exit 1; \
|
|
fi
|
|
|
|
# Optional Jasmin textual assembly (x86_64 only). Use the compiler driver so a
|
|
# cross compiler's target, sysroot, assembler and reproducibility flags apply.
|
|
jasmin/%.o: jasmin/%.s
|
|
$(Q)$(CC) $(CPPFLAGS) $(ASFLAGS) -c -o $@ $<
|
|
|
|
# VaptVupt codec files are compiled for the target ABI baseline.
|
|
$(VV_SIMD_OBJS): src/%.o: src/%.c $(HEADERS)
|
|
$(Q)$(CC) $(CPPFLAGS) $(PROJECT_CPPFLAGS) $(FEATURE_CPPFLAGS) \
|
|
$(CFLAGS) $(PROJECT_CFLAGS) \
|
|
$(if $(filter $@,$(CLANG_CAST_ALIGN_OBJS)),$(CLANG_CAST_ALIGN_FLAGS)) \
|
|
-c -o $@ $<
|
|
|
|
# VaptVupt non-SIMD files use the same warning policy.
|
|
$(VV_PLAIN_OBJS): src/%.o: src/%.c $(HEADERS)
|
|
$(Q)$(CC) $(CPPFLAGS) $(PROJECT_CPPFLAGS) $(FEATURE_CPPFLAGS) \
|
|
$(CFLAGS) $(PROJECT_CFLAGS) \
|
|
$(if $(filter $@,$(CLANG_CAST_ALIGN_OBJS)),$(CLANG_CAST_ALIGN_FLAGS)) \
|
|
-c -o $@ $<
|
|
|
|
# ZUPT core files (the SHA-NI object has its own rule below with -msha)
|
|
ZUPT_OBJS_GENERIC = $(filter-out src/zupt_sha256_shani.o,$(ZUPT_OBJS))
|
|
$(ZUPT_OBJS_GENERIC): src/%.o: src/%.c $(HEADERS)
|
|
$(Q)$(CC) $(CPPFLAGS) $(PROJECT_CPPFLAGS) $(FEATURE_CPPFLAGS) \
|
|
$(CFLAGS) $(PROJECT_CFLAGS) -c -o $@ $<
|
|
|
|
# SHA-NI path needs -msha -mssse3 -msse4.1 on x86_64.
|
|
# On non-x86_64, SHANI_FLAGS is empty and the file is a no-op TU.
|
|
src/zupt_sha256_shani.o: src/zupt_sha256_shani.c $(HEADERS)
|
|
$(Q)$(CC) $(CPPFLAGS) $(PROJECT_CPPFLAGS) $(FEATURE_CPPFLAGS) \
|
|
$(CFLAGS) $(PROJECT_CFLAGS) $(SHANI_FLAGS) \
|
|
$(if $(filter $@,$(CLANG_CAST_ALIGN_OBJS)),$(CLANG_CAST_ALIGN_FLAGS)) \
|
|
-c -o $@ $<
|
|
|
|
# Final link step. Order matters: CFLAGS before LDFLAGS, then objects,
|
|
# then LDLIBS — keeps GCC/Clang happy when LDFLAGS contains -pie or
|
|
# similar position-sensitive flags.
|
|
$(TARGET): $(ALL_OBJS) $(JAZZ_O)
|
|
$(Q)$(CC) $(CFLAGS) $(PROJECT_CFLAGS) $(LDFLAGS) $(PROJECT_LDFLAGS) $(PROJECT_CLI_LDFLAGS) \
|
|
$(ALL_OBJS) $(JAZZ_O) -o $(TARGET) \
|
|
$(FEATURE_LDLIBS) $(PROJECT_LDLIBS) $(LDLIBS)
|
|
@# In-tree test compatibility only. Installation emits the legacy
|
|
@# alias solely when INSTALL_LEGACY_ALIAS=1 is requested explicitly.
|
|
$(Q)if [ "$(CREATE_TEST_ALIAS)" = 0 ]; then \
|
|
:; \
|
|
elif [ -L "$(LEGACY_LINK)" ]; then \
|
|
test "$$(readlink "$(LEGACY_LINK)")" = "$(TARGET)" || { \
|
|
echo "ERROR: refusing to replace non-ZUPT symlink: $(LEGACY_LINK)" >&2; exit 1; \
|
|
}; \
|
|
elif [ -e "$(LEGACY_LINK)" ]; then \
|
|
echo "ERROR: refusing to replace existing path: $(LEGACY_LINK)" >&2; exit 1; \
|
|
fi; \
|
|
if [ "$(CREATE_TEST_ALIAS)" = 1 ]; then ln -sf "$(TARGET)" "$(LEGACY_LINK)"; fi
|
|
@if [ "$(CREATE_TEST_ALIAS)" = 1 ]; then \
|
|
echo "Build complete: ./$(TARGET) [$(TARGET_MACHINE)] (test alias: ./$(LEGACY_LINK) -> $(TARGET))"; \
|
|
else \
|
|
echo "Build complete: ./$(TARGET) [$(TARGET_MACHINE)] (no in-tree compatibility alias)"; \
|
|
fi
|
|
|
|
# ═══════════════════════════════════════════════════════════════════
|
|
# INSTALL / UNINSTALL
|
|
# ═══════════════════════════════════════════════════════════════════
|
|
|
|
install: $(TARGET)
|
|
$(Q)mkdir -p "$(DESTDIR)$(BINDIR)"
|
|
$(Q)install -m 0755 "$(TARGET)" "$(DESTDIR)$(BINDIR)/$(TARGET)"
|
|
$(Q)if [ "$(INSTALL_LICENSES)" = 1 ]; then \
|
|
mkdir -p "$(DESTDIR)$(LICENSEDIR)"; \
|
|
install -m 0644 $(LICENSE_FILES) "$(DESTDIR)$(LICENSEDIR)/"; \
|
|
fi
|
|
$(Q)if [ "$(INSTALL_LEGACY_ALIAS)" = 1 ]; then \
|
|
ln -sf "$(TARGET)" "$(DESTDIR)$(BINDIR)/$(LEGACY_LINK)"; \
|
|
fi
|
|
|
|
$(Q)if [ -f "$(MANPAGE)" ]; then \
|
|
mkdir -p "$(DESTDIR)$(MAN1DIR)"; \
|
|
$(GZIP) $(GZIPFLAGS) -c "$(MANPAGE)" > "$(DESTDIR)$(MAN1DIR)/$(MANPAGE_GZ)"; \
|
|
chmod 0644 "$(DESTDIR)$(MAN1DIR)/$(MANPAGE_GZ)"; \
|
|
if [ "$(INSTALL_LEGACY_ALIAS)" = 1 ]; then \
|
|
ln -sf "$(MANPAGE_GZ)" "$(DESTDIR)$(MAN1DIR)/$(LEGACY_PROGRAM).1.gz"; \
|
|
fi; \
|
|
echo "Installed: $(DESTDIR)$(MAN1DIR)/$(MANPAGE_GZ)"; \
|
|
else \
|
|
echo "Warning: man page not found: $(MANPAGE)"; \
|
|
fi
|
|
|
|
# Shell completions (v2.4.7+). Honour distro path conventions where
|
|
# possible; downstream packagers can override DESTDIR + the specific
|
|
# dirs as needed.
|
|
$(Q)if [ -f completions/zupt.bash ]; then \
|
|
mkdir -p "$(DESTDIR)$(BASHCOMPDIR)"; \
|
|
install -m 0644 completions/zupt.bash \
|
|
"$(DESTDIR)$(BASHCOMPDIR)/$(PROGRAM)"; \
|
|
if [ "$(INSTALL_LEGACY_ALIAS)" = 1 ]; then \
|
|
ln -sf "$(PROGRAM)" "$(DESTDIR)$(BASHCOMPDIR)/$(LEGACY_PROGRAM)"; \
|
|
fi; \
|
|
echo "Installed: $(DESTDIR)$(BASHCOMPDIR)/$(PROGRAM)"; \
|
|
fi
|
|
$(Q)if [ -f completions/_zupt ]; then \
|
|
mkdir -p "$(DESTDIR)$(ZSHCOMPDIR)"; \
|
|
install -m 0644 completions/_zupt \
|
|
"$(DESTDIR)$(ZSHCOMPDIR)/_$(PROGRAM)"; \
|
|
if [ "$(INSTALL_LEGACY_ALIAS)" = 1 ]; then \
|
|
ln -sf "_$(PROGRAM)" "$(DESTDIR)$(ZSHCOMPDIR)/_$(LEGACY_PROGRAM)"; \
|
|
fi; \
|
|
echo "Installed: $(DESTDIR)$(ZSHCOMPDIR)/_$(PROGRAM)"; \
|
|
fi
|
|
$(Q)if [ -f completions/zupt.fish ]; then \
|
|
mkdir -p "$(DESTDIR)$(FISHCOMPDIR)"; \
|
|
install -m 0644 completions/zupt.fish \
|
|
"$(DESTDIR)$(FISHCOMPDIR)/$(PROGRAM).fish"; \
|
|
if [ "$(INSTALL_LEGACY_ALIAS)" = 1 ]; then \
|
|
ln -sf "$(PROGRAM).fish" "$(DESTDIR)$(FISHCOMPDIR)/$(LEGACY_PROGRAM).fish"; \
|
|
fi; \
|
|
echo "Installed: $(DESTDIR)$(FISHCOMPDIR)/$(PROGRAM).fish"; \
|
|
fi
|
|
|
|
@echo "Installed: $(DESTDIR)$(BINDIR)/$(TARGET)"
|
|
|
|
uninstall:
|
|
$(Q)rm -f "$(DESTDIR)$(BINDIR)/$(TARGET)"
|
|
$(Q)rm -f "$(DESTDIR)$(MAN1DIR)/$(MANPAGE_GZ)"
|
|
$(Q)rm -f "$(DESTDIR)$(BASHCOMPDIR)/$(PROGRAM)"
|
|
$(Q)rm -f "$(DESTDIR)$(ZSHCOMPDIR)/_$(PROGRAM)"
|
|
$(Q)rm -f "$(DESTDIR)$(FISHCOMPDIR)/$(PROGRAM).fish"
|
|
$(Q)set -eu; for license_file in $(LICENSE_FILES); do \
|
|
rm -f "$(DESTDIR)$(LICENSEDIR)/$${license_file##*/}"; \
|
|
done
|
|
$(Q)for item in \
|
|
"$(DESTDIR)$(BINDIR)/$(LEGACY_LINK):$(TARGET)" \
|
|
"$(DESTDIR)$(MAN1DIR)/$(LEGACY_PROGRAM).1.gz:$(MANPAGE_GZ)" \
|
|
"$(DESTDIR)$(BASHCOMPDIR)/$(LEGACY_PROGRAM):$(PROGRAM)" \
|
|
"$(DESTDIR)$(ZSHCOMPDIR)/_$(LEGACY_PROGRAM):_$(PROGRAM)" \
|
|
"$(DESTDIR)$(FISHCOMPDIR)/$(LEGACY_PROGRAM).fish:$(PROGRAM).fish"; do \
|
|
path=$${item%:*}; expected=$${item##*:}; \
|
|
if [ -L "$$path" ] && [ "$$(readlink "$$path")" = "$$expected" ]; then rm -f "$$path"; fi; \
|
|
done
|
|
|
|
# ═══════════════════════════════════════════════════════════════════
|
|
# DIST — reproducible source tarball for distro packaging
|
|
# ═══════════════════════════════════════════════════════════════════
|
|
#
|
|
# `make dist` produces zupt-VERSION.tar.gz that is BYTE-IDENTICAL given
|
|
# the same input source tree. Properties:
|
|
#
|
|
# - Files sorted by name (stable order regardless of filesystem layout)
|
|
# - mtime fixed to SOURCE_DATE_EPOCH (`.source-date-epoch`, then HEAD fallback)
|
|
# - uid/gid fixed to root (0/0) via --owner / --group
|
|
# - gzip wrapped with --no-name (no embedded timestamp/filename)
|
|
# - No binaries, no .o, no .so. Source only.
|
|
#
|
|
# The archive always represents the tree of committed HEAD, never ignored build
|
|
# output or uncommitted files. Archiving the tree object also avoids Git's
|
|
# commit-ID PAX header, so an export-ignored-only commit cannot change its bytes.
|
|
# Override DIST_TARBALL for a packaging work directory.
|
|
DIST_VERSION = $(shell sed -n 's/^\#define ZUPT_VERSION_STRING "\([^"]*\)".*/\1/p' include/zupt.h)
|
|
DIST_NAME = $(PROGRAM)-$(DIST_VERSION)
|
|
DIST_TARBALL ?= /tmp/$(DIST_NAME).tar.gz
|
|
SOURCE_DATE_EPOCH ?= $(shell epoch=$$(sed -n 's/^[[:space:]]*\([0-9][0-9]*\)[[:space:]]*$$/\1/p' .source-date-epoch 2>/dev/null | head -n 1); if test -n "$$epoch"; then printf '%s' "$$epoch"; else git log -1 --format=%ct HEAD 2>/dev/null; fi)
|
|
SOURCE_AUDIT ?= scripts/check-source-only.sh
|
|
|
|
source-audit:
|
|
$(Q)test -f "$(SOURCE_AUDIT)" || { \
|
|
echo "ERROR: source-only scanner not found: $(SOURCE_AUDIT)" >&2; \
|
|
exit 1; \
|
|
}
|
|
$(Q)bash "$(SOURCE_AUDIT)"
|
|
|
|
dist:
|
|
$(Q)set -eu; \
|
|
export LC_ALL=C; \
|
|
umask 022; \
|
|
unset TAR_OPTIONS; \
|
|
test -n "$(DIST_VERSION)" || { echo "ERROR: cannot determine source version" >&2; exit 1; }; \
|
|
test -n "$(SOURCE_DATE_EPOCH)" || { echo "ERROR: SOURCE_DATE_EPOCH is empty" >&2; exit 1; }; \
|
|
case "$(SOURCE_DATE_EPOCH)" in *[!0-9]*) echo "ERROR: SOURCE_DATE_EPOCH must be an integer" >&2; exit 1;; esac; \
|
|
test -f "$(SOURCE_AUDIT)" || { echo "ERROR: source-only scanner not found: $(SOURCE_AUDIT)" >&2; exit 1; }; \
|
|
git rev-parse --verify 'HEAD^{commit}' >/dev/null; \
|
|
git rev-parse --verify 'HEAD^{tree}' >/dev/null; \
|
|
tmp=$$(mktemp -d "$${TMPDIR:-/tmp}/zupt-dist.XXXXXXXX"); \
|
|
trap 'rm -rf -- "$$tmp"' EXIT HUP INT TERM; \
|
|
git archive --format=tar --mtime="@$(SOURCE_DATE_EPOCH)" \
|
|
--prefix="$(DIST_NAME)/" 'HEAD^{tree}' | \
|
|
$(GZIP) $(GZIPFLAGS) > "$$tmp/$(DIST_NAME).tar.gz"; \
|
|
bash "$(SOURCE_AUDIT)" --archive "$$tmp/$(DIST_NAME).tar.gz"; \
|
|
mkdir -p "$$(dirname "$(DIST_TARBALL)")"; \
|
|
mv -f "$$tmp/$(DIST_NAME).tar.gz" "$(DIST_TARBALL)"; \
|
|
trap - EXIT HUP INT TERM; \
|
|
rm -rf -- "$$tmp"; \
|
|
if command -v sha256sum >/dev/null 2>&1; then \
|
|
digest=$$(sha256sum "$(DIST_TARBALL)" | awk '{print $$1}'); \
|
|
elif command -v shasum >/dev/null 2>&1; then \
|
|
digest=$$(shasum -a 256 "$(DIST_TARBALL)" | awk '{print $$1}'); \
|
|
else \
|
|
echo "ERROR: sha256sum or shasum is required" >&2; exit 1; \
|
|
fi; \
|
|
bytes=$$(wc -c < "$(DIST_TARBALL)"); \
|
|
printf '\n Reproducible source tarball:\n %s\n sha256: %s\n bytes: %s\n' \
|
|
"$(DIST_TARBALL)" "$$digest" "$$bytes"
|
|
|
|
# ═══════════════════════════════════════════════════════════════════
|
|
# CLEAN
|
|
# ═══════════════════════════════════════════════════════════════════
|
|
|
|
clean:
|
|
$(Q)rm -f $(PROGRAM) $(PROGRAM).exe $(LEGACY_PROGRAM) $(LEGACY_PROGRAM).exe $(MANPAGE_GZ) \
|
|
zupt_asan \
|
|
test_vectors test_f06 test_vaptvupt \
|
|
fuzz_decompress fuzz_vv_decompress tests/fuzz_format \
|
|
*.gcda *.gcno *.profraw *.profdata \
|
|
src/*.o src/*.gcda src/*.gcno jasmin/*.o jasmin/*.gcda jasmin/*.gcno \
|
|
tests/*.gcda tests/*.gcno sdk/*.gcda sdk/*.gcno
|
|
$(Q)for link in $(LEGACY_PROGRAM) $(LEGACY_PROGRAM).exe; do \
|
|
if [ -L "$$link" ]; then \
|
|
case "$$(readlink "$$link")" in $(PROGRAM)|$(PROGRAM).exe) rm -f "$$link" ;; esac; \
|
|
fi; \
|
|
done
|
|
$(Q)rm -rf sdk/build build build_obj coverage
|
|
|
|
# ═══════════════════════════════════════════════════════════════════
|
|
# TEST TARGETS
|
|
# ═══════════════════════════════════════════════════════════════════
|
|
|
|
test: check
|
|
|
|
test-all: check
|
|
$(Q)bash tests/regression.sh ./$(TARGET)
|
|
$(Q)sh tests/test_threaded.sh ./$(TARGET)
|
|
$(Q)sh tests/test_pq.sh ./$(TARGET)
|
|
$(Q)bash tests/test_dedup_props.sh ./$(TARGET)
|
|
$(Q)bash tests/test_ct_timing.sh
|
|
$(Q)bash tests/test_codec_exact_size.sh
|
|
$(Q)bash tests/test_mlkem_fips203.sh
|
|
$(Q)bash tests/test_sdk.sh
|
|
$(Q)bash tests/test_pqbox.sh
|
|
$(Q)bash tests/test_audit.sh
|
|
$(Q)bash tests/test_kdf_transparency.sh
|
|
|
|
# Release-only gates need a committed Git checkout and packaging metadata.
|
|
# Keep them out of downstream %check, which intentionally has no dist rebuild.
|
|
release-check: test-all audit-licenses
|
|
$(Q)$(MAKE) sdk-test
|
|
$(Q)bash tests/test_static_analysis.sh
|
|
$(Q)bash tests/test_packaging_syntax.sh
|
|
$(Q)bash scripts/test-installed-zupt.sh ./$(TARGET)
|
|
$(Q)if [ "$(WITH_SDK)" = 1 ]; then \
|
|
bash tests/test_audit_flake.sh "$${AUDIT_FLAKE_RUNS:-3}"; \
|
|
else \
|
|
echo "SKIP: audit flake stress needs WITH_SDK=1 and system libvuptsdk"; \
|
|
fi
|
|
$(Q)$(MAKE) clean
|
|
$(Q)bash "$(SOURCE_AUDIT)"
|
|
$(Q)bash tests/test_dist_reproducible.sh
|
|
|
|
# ═══════════════════════════════════════════════════════════════════
|
|
# CHECK — distro-friendly safe subset
|
|
# ═══════════════════════════════════════════════════════════════════
|
|
#
|
|
# Targeted at downstream packagers (openSUSE OBS, Debian, Fedora) who
|
|
# need a `%check` / `override_dh_auto_test` target that:
|
|
#
|
|
# - Runs in a few minutes (not the full byte-sweep arc)
|
|
# - Doesn't call `make clean` mid-stream (rules out test_dist_reproducible.sh)
|
|
# - Doesn't depend on tools that may be absent in the build chroot
|
|
# (no python3 PyYAML, no ruby, no dpkg-parsechangelog)
|
|
# - Doesn't depend on multi-threading that's flaky under emulation
|
|
# (skips test_threaded.sh and test_pq.sh's MT subset)
|
|
# - Covers the source-only CLI, archive safety, HMAC/integrity regressions,
|
|
# codec checks and cryptographic primitive vectors
|
|
# - Verifies cryptographic primitives against NIST/RFC vectors
|
|
#
|
|
# This is the recommended target for OBS %check sections.
|
|
|
|
check: $(TARGET) test-vectors test-f06 test-vv
|
|
$(Q)sh tests/run_quick.sh ./$(TARGET)
|
|
$(Q)bash tests/test_path_traversal.sh ./$(TARGET)
|
|
$(Q)bash tests/test_arg_order.sh ./$(TARGET)
|
|
$(Q)bash tests/test_password_sources.sh ./$(TARGET)
|
|
$(Q)bash tests/test_password_prompt_signal.sh ./$(TARGET)
|
|
$(Q)bash tests/test_key_files.sh ./$(TARGET)
|
|
$(Q)ZUPT_BIN="$(CURDIR)/$(TARGET)" bash tests/test_f08_topmac.sh
|
|
$(Q)ZUPT_BIN="$(CURDIR)/$(TARGET)" bash tests/test_f09_preface.sh
|
|
$(Q)ZUPT_BIN="$(CURDIR)/$(TARGET)" bash tests/test_f10_kdf_default.sh
|
|
$(Q)ZUPT_BIN="$(CURDIR)/$(TARGET)" bash tests/test_f11_authfail_message.sh
|
|
$(Q)ZUPT_BIN="$(CURDIR)/$(TARGET)" bash tests/test_f12_comment.sh
|
|
$(Q)bash tests/test_atomic_archive_output.sh ./$(TARGET)
|
|
$(Q)bash tests/test_legacy_disk_5_2_1.sh ./$(TARGET)
|
|
$(Q)bash tests/test_disk_device_capacity.sh ./$(TARGET)
|
|
$(Q)bash tests/test_block_swap.sh ./$(TARGET)
|
|
$(Q)bash tests/test_block_type_confusion.sh ./$(TARGET)
|
|
$(Q)bash tests/test_authenticated_dedup_reorder.sh ./$(TARGET)
|
|
$(Q)bash tests/test_format_little_endian.sh ./$(TARGET)
|
|
$(Q)bash tests/test_dedup_nonce.sh ./$(TARGET)
|
|
$(Q)bash tests/test_gui_branding.sh ./$(TARGET)
|
|
$(Q)bash tests/test_help_consistency.sh ./$(TARGET)
|
|
$(Q)bash tests/test_benchmark_temp_safety.sh ./$(TARGET)
|
|
$(Q)bash tests/test_vv_decode_slack.sh ./$(TARGET)
|
|
$(Q)bash tests/test_sha256_shani.sh
|
|
$(Q)bash tests/test_hmac_incremental.sh
|
|
$(Q)bash tests/test_completions_manpage.sh
|
|
$(Q)bash tests/test_source_only.sh
|
|
$(Q)./test_vectors
|
|
@echo ""
|
|
@echo " ═════════════════════════════════════════"
|
|
@echo " All executed distro-safe checks passed (see SKIP lines above)."
|
|
@echo " ═════════════════════════════════════════"
|
|
|
|
TEST_CRYPTO_SOURCES = src/zupt_sha256.c src/zupt_sha256_shani.c \
|
|
src/zupt_crypto.c src/zupt_aes256.c src/zupt_xxh.c src/zupt_keccak.c \
|
|
src/zupt_x25519.c src/zupt_mlkem.c src/zupt_cpuid.c src/zupt_mlock.c
|
|
TEST_CRYPTO_OBJS = $(TEST_CRYPTO_SOURCES:.c=.o)
|
|
|
|
test-vectors: tests/test_vectors.c $(HEADERS) $(TEST_CRYPTO_OBJS) $(JAZZ_O)
|
|
$(Q)$(CC) $(CPPFLAGS) $(PROJECT_CPPFLAGS) $(FEATURE_CPPFLAGS) \
|
|
$(CFLAGS) $(PROJECT_CFLAGS) $(LDFLAGS) $(PROJECT_LDFLAGS) \
|
|
tests/test_vectors.c $(TEST_CRYPTO_OBJS) $(JAZZ_O) \
|
|
-o test_vectors $(FEATURE_LDLIBS) $(PROJECT_LDLIBS) $(LDLIBS)
|
|
|
|
# F-06 regression — HMAC accept-on-disjoint-bits (ZUPT 2.2.5).
|
|
# Inherits $(CFLAGS) so ZUPT_USE_JASMIN is defined on x86_64 (exercising
|
|
# the original buggy path). Links the same crypto modules as test-vectors
|
|
# plus the Jasmin .o files when available.
|
|
test-f06: tests/test_f06_hmac.c $(HEADERS) $(TEST_CRYPTO_OBJS) $(JAZZ_O)
|
|
$(Q)$(CC) $(CPPFLAGS) $(PROJECT_CPPFLAGS) $(FEATURE_CPPFLAGS) \
|
|
$(CFLAGS) $(PROJECT_CFLAGS) $(LDFLAGS) $(PROJECT_LDFLAGS) \
|
|
tests/test_f06_hmac.c $(TEST_CRYPTO_OBJS) $(JAZZ_O) -o test_f06 \
|
|
$(FEATURE_LDLIBS) $(PROJECT_LDLIBS) $(LDLIBS)
|
|
$(Q)./test_f06
|
|
|
|
# VAPTVUPT: VaptVupt codec unit tests
|
|
TEST_VV_OBJS = $(VV_SIMD_OBJS) $(VV_PLAIN_OBJS) src/zupt_xxh.o src/zupt_cpuid.o
|
|
test-vv: tests/test_vaptvupt.c $(HEADERS) $(TEST_VV_OBJS)
|
|
$(Q)$(CC) $(CPPFLAGS) $(PROJECT_CPPFLAGS) $(CFLAGS) $(PROJECT_CFLAGS) \
|
|
$(LDFLAGS) $(PROJECT_LDFLAGS) tests/test_vaptvupt.c $(TEST_VV_OBJS) \
|
|
-o test_vaptvupt $(PROJECT_LDLIBS) $(LDLIBS)
|
|
$(Q)./test_vaptvupt
|
|
|
|
ASAN_BUILD_DIR = build/asan
|
|
ASAN_CFLAGS ?= -O1 -g -fno-omit-frame-pointer -fsanitize=address,undefined
|
|
ASAN_LDFLAGS ?= -fsanitize=address,undefined
|
|
ASAN_OBJS = $(patsubst src/%.c,$(ASAN_BUILD_DIR)/%.o,$(SOURCES))
|
|
|
|
$(ASAN_BUILD_DIR):
|
|
$(Q)mkdir -p "$@"
|
|
|
|
$(ASAN_BUILD_DIR)/zupt_sha256_shani.o: src/zupt_sha256_shani.c $(HEADERS) | $(ASAN_BUILD_DIR)
|
|
$(Q)$(CC) $(CPPFLAGS) $(PROJECT_CPPFLAGS) $(FEATURE_CPPFLAGS) \
|
|
$(CFLAGS) $(PROJECT_CFLAGS) $(ASAN_CFLAGS) $(SHANI_FLAGS) -c -o $@ $<
|
|
|
|
$(ASAN_BUILD_DIR)/vv_%.o: src/vv_%.c $(HEADERS) | $(ASAN_BUILD_DIR)
|
|
$(Q)$(CC) $(CPPFLAGS) $(PROJECT_CPPFLAGS) $(FEATURE_CPPFLAGS) \
|
|
$(CFLAGS) $(PROJECT_CFLAGS) $(ASAN_CFLAGS) -c -o $@ $<
|
|
|
|
$(ASAN_BUILD_DIR)/vaptvupt_api.o: src/vaptvupt_api.c $(HEADERS) | $(ASAN_BUILD_DIR)
|
|
$(Q)$(CC) $(CPPFLAGS) $(PROJECT_CPPFLAGS) $(FEATURE_CPPFLAGS) \
|
|
$(CFLAGS) $(PROJECT_CFLAGS) $(ASAN_CFLAGS) -c -o $@ $<
|
|
|
|
$(ASAN_BUILD_DIR)/%.o: src/%.c $(HEADERS) | $(ASAN_BUILD_DIR)
|
|
$(Q)$(CC) $(CPPFLAGS) $(PROJECT_CPPFLAGS) $(FEATURE_CPPFLAGS) \
|
|
$(CFLAGS) $(PROJECT_CFLAGS) $(ASAN_CFLAGS) -c -o $@ $<
|
|
|
|
test-asan: $(ASAN_OBJS) $(JAZZ_O)
|
|
$(Q)$(CC) $(CFLAGS) $(PROJECT_CFLAGS) $(ASAN_CFLAGS) \
|
|
$(LDFLAGS) $(PROJECT_LDFLAGS) $(ASAN_LDFLAGS) \
|
|
$(ASAN_OBJS) $(JAZZ_O) -o zupt_asan \
|
|
$(FEATURE_LDLIBS) $(PROJECT_LDLIBS) $(LDLIBS)
|
|
@echo "ASAN build: ./zupt_asan"
|
|
|
|
# Build the format-parser fuzz harness. Runs against ./zupt_asan to catch
|
|
# memory errors AND crashes in mutation-fuzz of the listing/extract path.
|
|
fuzz-format: tests/fuzz_format
|
|
|
|
tests/fuzz_format: tests/fuzz_format.c
|
|
$(Q)$(CC) $(CPPFLAGS) $(CFLAGS) $(PROJECT_CFLAGS) \
|
|
$(LDFLAGS) $(PROJECT_LDFLAGS) tests/fuzz_format.c \
|
|
-o tests/fuzz_format $(PROJECT_LDLIBS) $(LDLIBS)
|
|
@echo "Format fuzz harness: ./tests/fuzz_format"
|
|
|
|
# Run 5000 iterations of mutation fuzz against the ASAN binary.
|
|
# Any crash or sanitizer error fails CI.
|
|
fuzz-format-run: tests/fuzz_format test-asan $(TARGET)
|
|
$(Q)set -eu; \
|
|
tmp=$$(mktemp -d "$${TMPDIR:-/tmp}/zupt-fuzz.XXXXXXXX"); \
|
|
trap 'rm -rf -- "$$tmp"' EXIT HUP INT TERM; \
|
|
printf '%s\n' 'fuzz seed file' > "$$tmp/input.txt"; \
|
|
./$(TARGET) c "$$tmp/seed.zupt" "$$tmp/input.txt" >/dev/null; \
|
|
ASAN_OPTIONS=detect_leaks=0:abort_on_error=1 \
|
|
./tests/fuzz_format 1000 ./zupt_asan "$$tmp/seed.zupt"
|
|
@echo " Format fuzz: 1000 iterations under ASAN/UBSAN — no crashes."
|
|
|
|
# Runs a round-trip smoke test against the ASAN/UBSAN/LSAN-instrumented binary.
|
|
# The exhaustive codec exact-size sanitizer loop remains part of test-all.
|
|
test-asan-run: test-asan
|
|
$(Q)set -eu; \
|
|
tmp=$$(mktemp -d "$${TMPDIR:-/tmp}/zupt-asan.XXXXXXXX"); \
|
|
trap 'rm -rf -- "$$tmp"' EXIT HUP INT TERM; \
|
|
export ASAN_OPTIONS=detect_leaks=1:abort_on_error=1; \
|
|
export UBSAN_OPTIONS=halt_on_error=1:print_stacktrace=1; \
|
|
printf '%s\n' 'sanitizer round-trip' > "$$tmp/input.txt"; \
|
|
./zupt_asan c "$$tmp/archive.zupt" "$$tmp/input.txt" >/dev/null; \
|
|
./zupt_asan t "$$tmp/archive.zupt" >/dev/null; \
|
|
mkdir "$$tmp/out"; \
|
|
(cd "$$tmp/out" && "$(CURDIR)/zupt_asan" x "$$tmp/archive.zupt" >/dev/null); \
|
|
extracted=$$(find "$$tmp/out" -type f -print -quit); \
|
|
test -n "$$extracted"; \
|
|
cmp "$$tmp/input.txt" "$$extracted"; \
|
|
dd if=/dev/urandom of="$$tmp/block" bs=65536 count=1 2>/dev/null; \
|
|
cp "$$tmp/block" "$$tmp/disk.img"; \
|
|
dd if="$$tmp/block" of="$$tmp/disk.img" bs=65536 seek=1 conv=notrunc 2>/dev/null; \
|
|
printf '%s\n' 'sanitizer-disk-password' > "$$tmp/password"; \
|
|
./zupt_asan disk backup --dedup -b 65536 --pass-file "$$tmp/password" -s \
|
|
"$$tmp/disk.zupt" "$$tmp/disk.img" >/dev/null; \
|
|
./zupt_asan t --pass-file "$$tmp/password" "$$tmp/disk.zupt" >/dev/null; \
|
|
./zupt_asan disk restore --pass-file "$$tmp/password" \
|
|
"$$tmp/disk.zupt" "$$tmp/restored.img" >/dev/null; \
|
|
cmp "$$tmp/disk.img" "$$tmp/restored.img"; \
|
|
./zupt_asan --help >/dev/null; \
|
|
./zupt_asan --version >/dev/null
|
|
@echo " ASAN/UBSAN: source-only smoke test passed."
|
|
|
|
# AFL++ fuzzing harnesses (requires afl-clang-fast). Compile every source with
|
|
# instrumentation while retaining translation-unit-local ISA flags.
|
|
AFL_CC ?= afl-clang-fast
|
|
FUZZ_BUILD_DIR = build/fuzz
|
|
FUZZ_SOURCES = $(filter-out src/zupt_main.c,$(SOURCES))
|
|
FUZZ_OBJS = $(patsubst src/%.c,$(FUZZ_BUILD_DIR)/%.o,$(FUZZ_SOURCES))
|
|
FUZZ_VV_OBJS = $(addprefix $(FUZZ_BUILD_DIR)/,vv_encoder.o vv_decoder.o \
|
|
vv_ans.o vv_huffman.o vv_simd.o zupt_xxh.o zupt_cpuid.o)
|
|
|
|
$(FUZZ_BUILD_DIR):
|
|
$(Q)mkdir -p "$@"
|
|
|
|
$(FUZZ_BUILD_DIR)/zupt_sha256_shani.o: src/zupt_sha256_shani.c $(HEADERS) | $(FUZZ_BUILD_DIR)
|
|
$(Q)$(AFL_CC) $(CPPFLAGS) $(PROJECT_CPPFLAGS) $(FEATURE_CPPFLAGS) \
|
|
$(CFLAGS) $(PROJECT_CFLAGS) $(ASAN_CFLAGS) $(SHANI_FLAGS) -c -o $@ $<
|
|
|
|
$(FUZZ_BUILD_DIR)/vv_%.o: src/vv_%.c $(HEADERS) | $(FUZZ_BUILD_DIR)
|
|
$(Q)$(AFL_CC) $(CPPFLAGS) $(PROJECT_CPPFLAGS) $(FEATURE_CPPFLAGS) \
|
|
$(CFLAGS) $(PROJECT_CFLAGS) $(ASAN_CFLAGS) $(VV_WARNING_FLAGS) \
|
|
$(if $(filter $(FUZZ_BUILD_DIR)/vv_decoder.o,$@),$(VV_DECODER_WARNING_FLAGS)) -c -o $@ $<
|
|
|
|
$(FUZZ_BUILD_DIR)/vaptvupt_api.o: src/vaptvupt_api.c $(HEADERS) | $(FUZZ_BUILD_DIR)
|
|
$(Q)$(AFL_CC) $(CPPFLAGS) $(PROJECT_CPPFLAGS) $(FEATURE_CPPFLAGS) \
|
|
$(CFLAGS) $(PROJECT_CFLAGS) $(ASAN_CFLAGS) $(VV_WARNING_FLAGS) -c -o $@ $<
|
|
|
|
$(FUZZ_BUILD_DIR)/%.o: src/%.c $(HEADERS) | $(FUZZ_BUILD_DIR)
|
|
$(Q)$(AFL_CC) $(CPPFLAGS) $(PROJECT_CPPFLAGS) $(FEATURE_CPPFLAGS) \
|
|
$(CFLAGS) $(PROJECT_CFLAGS) $(ASAN_CFLAGS) -c -o $@ $<
|
|
|
|
fuzz_decompress: tests/fuzz_decompress.c $(FUZZ_OBJS)
|
|
$(Q)$(AFL_CC) $(CPPFLAGS) $(PROJECT_CPPFLAGS) $(FEATURE_CPPFLAGS) \
|
|
$(CFLAGS) $(PROJECT_CFLAGS) $(ASAN_CFLAGS) \
|
|
$(LDFLAGS) $(PROJECT_LDFLAGS) $(ASAN_LDFLAGS) \
|
|
tests/fuzz_decompress.c $(FUZZ_OBJS) -o $@ \
|
|
$(FEATURE_LDLIBS) $(PROJECT_LDLIBS) $(LDLIBS)
|
|
|
|
fuzz_vv_decompress: tests/fuzz_vv_decompress.c $(FUZZ_VV_OBJS)
|
|
$(Q)$(AFL_CC) $(CPPFLAGS) $(PROJECT_CPPFLAGS) \
|
|
$(CFLAGS) $(PROJECT_CFLAGS) $(ASAN_CFLAGS) \
|
|
$(LDFLAGS) $(PROJECT_LDFLAGS) $(ASAN_LDFLAGS) \
|
|
tests/fuzz_vv_decompress.c $(FUZZ_VV_OBJS) -o $@ \
|
|
$(PROJECT_LDLIBS) $(LDLIBS)
|
|
|
|
fuzz-build: fuzz_decompress fuzz_vv_decompress
|
|
@echo "Fuzz harnesses built. Run:"
|
|
@echo " afl-fuzz -i corpus -o findings -- ./fuzz_decompress"
|
|
@echo " afl-fuzz -i corpus_vv -o findings_vv -- ./fuzz_vv_decompress"
|
|
|
|
help:
|
|
@echo "ZUPT v$(DIST_VERSION) build targets:"
|
|
@echo " make Build zupt binary"
|
|
@echo " make V=1 Build with verbose output"
|
|
@echo " make check Distro-safe source-only test suite"
|
|
@echo " make test-all Complete runtime suite; unavailable integrations SKIP"
|
|
@echo " make release-check Runtime, static, packaging, source and dist gates"
|
|
@echo " make test-vv VaptVupt codec unit tests"
|
|
@echo " make test-asan Build with AddressSanitizer"
|
|
@echo " make fuzz-build Build AFL++ fuzzing harnesses"
|
|
@echo " make dist Reproducible, audited source archive"
|
|
@echo " make source-audit Audit tracked, worktree and HEAD archive content"
|
|
@echo " make install Install to $(PREFIX)"
|
|
@echo " make uninstall Remove from $(PREFIX)"
|
|
@echo " make clean Remove build artifacts"
|
|
@echo ""
|
|
@echo "Compiler target: $(TARGET_MACHINE)"
|
|
@echo "Optional integrations (off by default):"
|
|
@echo " WITH_SDK=1 system libvuptsdk via pkg-config/overrides"
|
|
@echo " WITH_PQBOX=1 system libpqvaptvupt via pkg-config/overrides"
|
|
@echo " WITH_JASMIN=1 optional textual assembly on x86_64"
|
|
@echo " INSTALL_LEGACY_ALIAS=1 installs opt-in 'vaptvupt' compatibility links"
|
|
|
|
# ─────────────────────────────────────────────────────────────────────
|
|
# SDK targets — see sdk/Makefile.sdk
|
|
# ─────────────────────────────────────────────────────────────────────
|
|
include sdk/Makefile.sdk
|