# No optimisation or warning flags here: CRAN sets those, and overriding them
# is a policy violation. The defines below are the trim set validated against
# the pinned miniz release and recorded in tools/vendor/manifest.tsv; keep the
# two in step or tools/vendor/verify fails.
#
#   ARCHIVE/ARCHIVE_WRITING  drop the ZIP reader and writer entirely
#   STDIO                    no file I/O; zukomp only ever sees memory
#   TIME                     no clock access (also keeps gzip output deterministic)
#   ZLIB_COMPATIBLE_NAMES    do not declare compress/inflate/crc32/adler32 etc.,
#                            which would collide with the zlib R itself links
#   PNG_APIS                 local patch; drops the PNG writer, which upstream
#                            guards only by MINIZ_NO_DEFLATE_APIS
#   ASSERT                   local patch; MZ_ASSERT expands to assert() and is
#                            reachable from malformed input. R supplies -DNDEBUG
#                            so it is already compiled out of normal and CRAN
#                            builds -- this makes that explicit, so a -UNDEBUG
#                            build (devtools' debug install) cannot abort the R
#                            session where the shipping build raises a condition
PKG_CPPFLAGS = -I. -I../inst/include -Ivendor/miniz \
  -DMINIZ_NO_ARCHIVE_APIS \
  -DMINIZ_NO_ARCHIVE_WRITING_APIS \
  -DMINIZ_NO_STDIO \
  -DMINIZ_NO_TIME \
  -DMINIZ_NO_ZLIB_COMPATIBLE_NAMES \
  -DMINIZ_NO_PNG_APIS \
  -DMINIZ_NO_ASSERT

# $(C_VISIBILITY) is R's own substitution for -fvisibility=hidden, empty on a
# toolchain without it (and on Windows, where R exports from a .def file
# instead). With it, zukomp.so exports R_init_zukomp and nothing else:
# every .Call entry point is reached through R_registerRoutines and every C
# ABI function through R_RegisterCCallable, so no consumer ever needed a
# symbol. An exported miniz is a liability rather than a surface -- it can
# bind to another package's independently vendored miniz in the same process,
# which is the failure the family's hidden-visibility rule exists to prevent.
# test-abi.R asserts the exact export set on the installed object.
#
# It reaches libzukomp.a too, deliberately: the archive rule below compiles
# with $(ALL_CFLAGS), which carries PKG_CFLAGS. A hidden symbol in a static
# archive still resolves inside the link that pulls it in, so a consumer's
# own objects call mz_zip_* exactly as before -- what changes is that the
# consumer's shared object no longer re-exports miniz. That is the same rule
# applied one package over: zuxlsx links this archive, and a miniz exported
# from zuxlsx.so could bind to someone else's copy just as one exported from
# zukomp.so could. tools/check-linking.sh is what proves the archive still
# links.
PKG_CFLAGS = $(C_VISIBILITY)

# Listed explicitly rather than via $(wildcard ...): R only auto-compiles
# src/*.c, miniz lives in a subdirectory, and a GNU make wildcard would
# force SystemRequirements: GNU make. Add new sources here.
OBJECTS = \
  init.o \
  zu_status.o \
  zu_registry.o \
  zu_buf.o \
  zu_stream.o \
  zu_whole.o \
  codec_identity.o \
  codec_deflate.o \
  zu_gzip.o \
  zukomp_r.o \
  zukomp_api.o \
  zukomp_test.o \
  vendor/miniz/miniz.o

# ---------------------------------------------------------------------------
# The LinkingTo surface: libzukomp.a, installed to <pkg>/lib${R_ARCH} by
# src/install.libs.R
#
# zukomp's own codecs work on memory buffers, which is all the registry needs
# and all zukomp.so should contain. A consumer that has to read a ZIP
# *container* -- xlsxio in zuxlsx, reading the parts of an .xlsx -- needs the
# archive layer this build trims away, and is a C library that cannot be
# rewritten around the registry.
#
# It gets a SECOND compilation of the same miniz.c rather than a widened
# first one, and that is the point: zukomp.so keeps exactly the trim it has
# today, so test-abi.R's "no ZIP archive symbol survives the trim" and the
# zlib-name audit stay true of the shared object R loads. The ZIP code exists
# only inside the static archive, which is linked into someone else's binary.
#
# -U after $(ALL_CPPFLAGS) rather than a second flag list: the compiler
# applies -D and -U in command-line order, so this is the trim above minus
# three entries, and the two cannot drift apart.
#
#   ARCHIVE_APIS  the point of the exercise: mz_zip_reader_*
#   STDIO         mz_zip_reader_init_file() opens a path, so it needs FILE*
#   TIME          keeps mz_zip_archive_file_stat.m_time real rather than
#                 miniz's m_padding, so a consumer compiling miniz.h at its
#                 defaults reads the field it thinks it is reading. No clock
#                 is read: the writer is what calls time(), and it stays off.
#
# Still trimmed here, and a consumer must match the first of them or its
# declarations will not describe this archive:
#
#   ZLIB_COMPATIBLE_NAMES   or miniz #defines compress/crc32/adler32 over
#                           every translation unit, colliding with the zlib
#                           R itself links
#   ARCHIVE_WRITING_APIS    reader only, per the design; writing an .xlsx is
#                           not in scope for anything that consumes this
#   PNG_APIS, ASSERT        local patches; neither changes a declaration a
#                           consumer can reach
# The archive inherits $(ALL_CFLAGS), which is the point rather than an
# accident: it carries $(CPICFLAGS), so these objects are position independent
# and can be linked into a consumer's shared object. It also carries whatever
# CFLAGS the installing R was configured with, so under CRAN's LTO flavour the
# archive holds bitcode rather than objects -- checked, and $(AR) and the
# linker handle it, but it does mean the archive is only as portable as the
# toolchain that built it. README.md says so where consumers will read it.
#
# "all" is the documented hook for post-SHLIB work (Writing R Extensions
# 1.2.1), and it has to be the FIRST target in this file: make takes the
# first target it reads as the default goal, so a rule placed above this one
# becomes what an unqualified `make` builds -- which shows up as the shared
# object quietly not being relinked.
.PHONY: all

all: $(SHLIB) libzukomp.a

libzukomp.a: vendor/miniz/miniz_zip.o
	rm -f libzukomp.a
	$(AR) rcs libzukomp.a vendor/miniz/miniz_zip.o

MINIZ_ZIP_CPPFLAGS = \
  -UMINIZ_NO_ARCHIVE_APIS \
  -UMINIZ_NO_STDIO \
  -UMINIZ_NO_TIME

vendor/miniz/miniz_zip.o: vendor/miniz/miniz.c
	$(CC) $(ALL_CPPFLAGS) $(MINIZ_ZIP_CPPFLAGS) $(ALL_CFLAGS) \
	  -c vendor/miniz/miniz.c -o vendor/miniz/miniz_zip.o
