#
# Makefile for PDFio.
#
# Copyright © 2021-2023 by Michael R Sweet.
#
# Licensed under Apache License v2.0.  See the file "LICENSE" for more
# information.
#

# POSIX makefile
.POSIX:

# Build silently
.SILENT:

# Variables
AR		=	ar
ARFLAGS		=	cr
CC		=	cc
CFLAGS		=
CODESIGN_IDENTITY =	Developer ID
COMMONFLAGS	=	-Os -g
#COMMONFLAGS	=	-O0 -g -fsanitize=address
CPPFLAGS	=
DESTDIR		=	$(DSTROOT)
DSO		=	cc
DSOFLAGS	=
DSONAME		=
LDFLAGS		=
LIBS		=	-lm -lz
RANLIB		=	ranlib
VERSION		=	1.1.3
prefix		=	/usr/local


# Base rules
.SUFFIXES:	.c .h .o
.c.o:
	echo Compiling $<...
	$(CC) $(CFLAGS) $(CPPFLAGS) $(COMMONFLAGS) -c $<


# Files
PUBHEADERS	=	\
			pdfio.h \
			pdfio-content.h
PUBOBJS		=	\
			pdfio-aes.o \
			pdfio-array.o \
			pdfio-common.o \
			pdfio-content.o \
			pdfio-crypto.o \
			pdfio-dict.o \
			pdfio-file.o \
			pdfio-md5.o \
			pdfio-object.o \
			pdfio-page.o \
			pdfio-rc4.o \
			pdfio-sha256.o \
			pdfio-stream.o \
			pdfio-string.o \
			pdfio-token.o \
			pdfio-value.o
LIBOBJS		=	\
			$(PUBOBJS) \
			ttf.o
OBJS		=	\
			$(LIBOBJS) \
			pdfiototext.o \
			testpdfio.o \
			testttf.o
TARGETS		=	\
			$(DSONAME) \
			libpdfio.a \
			pdfiototext \
			testpdfio \
			testttf


# Make everything
all:		$(TARGETS)

all-shared:
	if test `uname` = Darwin; then \
		$(MAKE) DSONAME="libpdfio.1.dylib" -$(MAKEFLAGS) all; \
	else \
		$(MAKE) COMMONFLAGS="-g -Os -fPIC" DSONAME="libpdfio.so.1" -$(MAKEFLAGS) all; \
	fi

debug:
	$(MAKE) -$(MAKEFLAGS) COMMONFLAGS="-g -fsanitize=address -DDEBUG=1" clean all

macos:
	$(MAKE) -$(MAKEFLAGS) COMMONFLAGS="-Os -mmacosx-version-min=11 -arch x86_64 -arch arm64" clean all


# Clean everything
clean:
	rm -f $(TARGETS) $(OBJS)


# Install everything
install:	$(TARGETS)
	echo Installing header files to $(DESTDIR)$(prefix)/include...
	-mkdir -p $(DESTDIR)$(prefix)/include
	cp $(PUBHEADERS) $(DESTDIR)$(prefix)/include
	echo Installing library files to $(DESTDIR)$(prefix)/lib...
	-mkdir -p $(DESTDIR)$(prefix)/lib
	cp libpdfio.a $(DESTDIR)$(prefix)/lib
	$(RANLIB) $(DESTDIR)$(prefix)/lib/libpdfio.a
	if test "x$(DSONAME)" = xlibpdfio.so.1; then \
		cp $(DSONAME) $(DESTDIR)$(prefix)/lib; \
		ln -sf libpdfio.so.1 $(DESTDIR)$(prefix)/lib/libpdfio.so; \
	elif test "x$(DSONAME)" = xlibpdfio.1.dylib; then \
		cp $(DSONAME) $(DESTDIR)$(prefix)/lib; \
		codesign -s "$(CODESIGN_IDENTITY)" -o runtime --timestamp $(DESTDIR)$(prefix)/lib/libpdfio.1.dylib; \
		ln -sf libpdfio.1.dylib $(DESTDIR)$(prefix)/lib/libpdfio.dylib; \
	fi
	echo Installing pkg-config files to $(DESTDIR)$(prefix)/lib/pkgconfig...
	-mkdir -p $(DESTDIR)$(prefix)/lib/pkgconfig
	echo 'prefix="$(prefix)"' >$(DESTDIR)$(prefix)/lib/pkgconfig/pdfio.pc
	echo 'Version: $(VERSION)' >>$(DESTDIR)$(prefix)/lib/pkgconfig/pdfio.pc
	cat pdfio.pc.in >>$(DESTDIR)$(prefix)/lib/pkgconfig/pdfio.pc
	echo Installing documentation to $(DESTDIR)$(prefix)/share/doc/pdfio...
	-mkdir -p $(DESTDIR)$(prefix)/share/doc/pdfio
	cp doc/pdfio.html doc/pdfio-512.png LICENSE NOTICE $(DESTDIR)$(prefix)/share/doc/pdfio
	echo Installing man page to $(DESTDIR)$(prefix)/share/man/man3...
	-mkdir -p $(DESTDIR)$(prefix)/share/man/man3
	cp doc/pdfio.3 $(DESTDIR)$(prefix)/share/man/man3

install-shared:
	if test `uname` = Darwin; then \
		$(MAKE) DSONAME="libpdfio.1.dylib" -$(MAKEFLAGS) install; \
	else \
		$(MAKE) DSONAME="libpdfio.so.1" -$(MAKEFLAGS) install; \
	fi


# Test everything
test:	testpdfio
	./testttf 2>test.log
	./testpdfio 2>test.log

valgrind:	testpdfio
	valgrind --leak-check=full ./testpdfio


# pdfio library
libpdfio.a:		$(LIBOBJS)
	echo Archiving $@...
	$(AR) $(ARFLAGS) $@ $(LIBOBJS)
	$(RANLIB) $@

libpdfio.so.1:		$(LIBOBJS)
	echo Linking $@...
	$(CC) $(DSOFLAGS) $(COMMONFLAGS) -shared -o $@ -Wl,-soname,$@ $(LIBOBJS) $(LIBS)

libpdfio.1.dylib:	$(LIBOBJS)
	echo Linking $@...
	$(CC) $(DSOFLAGS) $(COMMONFLAGS) -dynamiclib -o $@ -install_name $(prefix)/lib/$@ -current_version $(VERSION) -compatibility_version 1.0 $(LIBOBJS) $(LIBS)


# pdfio1.def (Windows DLL exports file...)
#
# I'd love to use __declspec(dllexport) but MS puts it before the function
# declaration instead of after like everyone else, and it breaks Codedoc and
# other tools I rely on...
pdfio1.def: $(LIBOBJS) Makefile
	echo Generating $@...
	echo "LIBRARY pdfio1" >$@
	echo "VERSION 1.0" >>$@
	echo "EXPORTS" >>$@
	nm $(LIBOBJS) 2>/dev/null | grep "T _" | awk '{print $$3}' | \
		grep -v '^_ttf' | sed -e '1,$$s/^_//' | sort >>$@


# pdfio text extraction (demo, doesn't handle a lot of things yet)
pdfiototext:		pdfiototext.o libpdfio.a
	echo Linking $@...
	$(CC) $(LDFLAGS) $(COMMONFLAGS) -o $@ pdfiototext.o libpdfio.a $(LIBS)


# pdfio test program
testpdfio:		testpdfio.o libpdfio.a
	echo Linking $@...
	$(CC) $(LDFLAGS) $(COMMONFLAGS) -o $@ testpdfio.o libpdfio.a $(LIBS)

# TTF test program
testttf:	ttf.o testttf.o
	echo Linking $@...
	$(CC) $(LDFLAGS) $(COMMONFLAGS) -o testttf ttf.o testttf.o $(LIBS)


# Dependencies
$(OBJS):		pdfio.h pdfio-private.h Makefile
pdfio-content.o:	pdfio-content.h ttf.h
testttf.o:		ttf.h
ttf.o:			ttf.h

# Make documentation using Codedoc <https://www.msweet.org/codedoc>
DOCFLAGS	=	\
			--author "Michael R Sweet" \
			--copyright "Copyright (c) 2021-2023 by Michael R Sweet" \
			--docversion $(VERSION)

.PHONY: doc
doc:
	echo Generating documentation...
	codedoc $(DOCFLAGS) --title "PDFio Programming Manual v$(VERSION)" $(PUBHEADERS) $(PUBOBJS:.o=.c) --body doc/pdfio.md --coverimage doc/pdfio-512.png pdfio.xml >doc/pdfio.html
	codedoc $(DOCFLAGS) --title "PDFio Programming Manual v$(VERSION)" --body doc/pdfio.md --coverimage doc/pdfio-epub.png pdfio.xml --epub doc/pdfio.epub
	codedoc $(DOCFLAGS) --title "pdf read/write library" --man pdfio --section 3 --body doc/pdfio.md pdfio.xml >doc/pdfio.3
	rm -f pdfio.xml


# Fuzz-test the library <>
.PHONY: afl
afl:
	$(MAKE) -$(MAKEFLAGS) CC="afl-clang-fast" COMMONFLAGS="-g" clean all
	test afl-output || rm -rf afl-output
	afl-fuzz -x afl-pdf.dict -i afl-input -o afl-output -V 600 -e pdf -t 5000 ./testpdfio @@


# Analyze code with the Clang static analyzer <https://clang-analyzer.llvm.org>
clang:
	clang $(CPPFLAGS) --analyze $(OBJS:.o=.c) 2>clang.log
	rm -rf $(OBJS:.o=.plist)
	test -s clang.log && (echo "$(GHA_ERROR)Clang detected issues."; echo ""; cat clang.log; exit 1) || exit 0


# Analyze code using Cppcheck <http://cppcheck.sourceforge.net>
cppcheck:
	cppcheck $(CPPFLAGS) --template=gcc --addon=cert.py --suppressions-list=.cppcheck $(OBJS:.o=.c) 2>cppcheck.log
	test -s cppcheck.log && (echo "$(GHA_ERROR)Cppcheck detected issues."; echo ""; cat cppcheck.log; exit 1) || exit 0
