# GNU make syntax reigns in this file.

all_cflags += -Werror
all_cppflags += -MD -MP -MF .deps/$(subst .._,,$(subst /,_,$(subst $(srcdir)/,,$<))).d

A2X = a2x
ASCIIDOC = asciidoc
CPPCHECK = cppcheck
CPPCHECK_SUPPRESSIONS = misc/cppcheck-suppressions.txt
SHELLCHECK = shellcheck
SHELLCHECK_EXCLUDES = misc/shellcheck-excludes.txt
COMPILEDB = compiledb
CLANG_TIDY = clang-tidy
SCAN_BUILD = scan-build
DOCKER = docker
GPERF = @GPERF@
TEST = test

version := \
    $(shell (git --git-dir=$(srcdir)/.git describe --dirty || git --git-dir=$(srcdir)/.git describe || echo vunknown) \
            2>/dev/null | sed -e 's/v//' -e 's/-/+/' -e 's/-/_/g')

dist_dir = ccache-$(version)
dist_archives = \
    ccache-$(version).tar.bz2 \
    ccache-$(version).tar.gz
ifneq ($(shell uname), Darwin)
    dist_archives += ccache-$(version).tar.xz
endif

generated_docs = \
    LICENSE.html \
    doc/AUTHORS.html \
    doc/MANUAL.html \
    doc/NEWS.html \
    doc/ccache.1
built_dist_files = $(generated_sources) $(generated_docs)

headers = \
    src/ccache.h \
    src/compopt.h \
    src/conf.h \
    src/confitems.h \
    src/counters.h \
    src/envtoconfitems.h \
    src/getopt_long.h \
    src/hash.h \
    src/hashtable.h \
    src/hashtable_itr.h \
    src/hashtable_private.h \
    src/hashutil.h \
    src/language.h \
    src/macroskip.h \
    src/manifest.h \
    src/mdfour.h \
    src/minitrace.h \
    src/murmurhashneutral2.h \
    src/system.h \
    src/unify.h \
    unittest/framework.h \
    unittest/util.h
generated_headers = \
    unittest/suites.h

files_to_clean += *.tar.bz2 *.tar.gz *.tar.xz *.xml .deps/* perfdir.*
files_to_clean += compile_commands.json
files_to_clean += src/confitems_lookup.c
files_to_clean += src/envtoconfitems_lookup.c
files_to_distclean += $(built_dist_files) src/version.c unittest/suites.h
files_to_distclean += .deps dev.mk

source_dist_files = \
    $(non_3pp_sources) \
    $(3pp_sources) \
    $(headers) \
    $(test_sources) \
    CONTRIBUTING.md \
    GPL-3.0.txt \
    LICENSE.adoc \
    Makefile.in \
    README.md \
    autogen.sh \
    config.guess \
    config.h.in \
    config.sub \
    configure \
    configure.ac \
    dev.mk.in \
    doc/AUTHORS.adoc \
    doc/MANUAL.adoc \
    doc/NEWS.adoc \
    install-sh \
    m4 \
    src/confitems.gperf \
    src/confitems_lookup.c \
    src/envtoconfitems.gperf \
    src/envtoconfitems_lookup.c \
    src/main.c \
    src/minitrace.c \
    src/zlib/*.c \
    src/zlib/*.h \
    test/run \
    test/suites/*.bash

dist_files = \
    $(addprefix $(srcdir)/, $(source_dist_files)) \
    $(built_dist_files)

ifneq ($(shell sed 's/.*"\(.*\)".*/\1/' src/version.c 2>/dev/null),$(version))
  $(shell echo 'extern const char CCACHE_VERSION[]; const char CCACHE_VERSION[] = "$(version)";' >src/version.c)
endif
src/version.o: src/version.c

# $(1): Name.
# $(2): Command for fixing up source file before the gperf call.
define generate_gperf_lookup
src/$(1)_lookup.c: src/$(1).gperf
	$$(if $$(quiet),@echo "  GEN      $$@")
	$$(Q)$(2) $$< | $$(GPERF) | sed 's/#error/#warning/' >$$@.tmp
# Fix for gperf < 3.1 (fix parameter type and remove inlining of the get function):
	$$(Q)perl -00 -pi -e 's/unsigned int len/size_t len/; s/#ifdef __GNUC__.*?gnu_inline.*?#endif\n#endif\n//sg' $$@.tmp
	$$(Q)echo "size_t $(1)_count(void) { return $$$$(perl -ne '/TOTAL_KEYWORDS = (.+?),/ && print $$$$1' $$@.tmp); }" >>$$@.tmp
	$$(Q)mv $$@.tmp $$@
endef

add_confitems_numbers = \
    perl -pae '$$$$s = 1 if /^%%/; s/ITEM/$$$$n++ . ", ITEM"/e if $$$$s == 1'

$(eval $(call generate_gperf_lookup,confitems,$(add_confitems_numbers)))
$(eval $(call generate_gperf_lookup,envtoconfitems,cat))

.PHONY: dist
dist: $(dist_archives)

$(dist_archives): $(dist_files)
	tmpdir=$$(mktemp -d /tmp/tmp-ccache-dist.XXXXXX) && \
	dir=$$tmpdir/$(dist_dir) && \
	mkdir $$dir && \
	(cd $(srcdir) && \
	 rsync -r --relative $(source_dist_files) $$dir) && \
	cp $(srcdir)/doc/INSTALL-from-release-archive.md $$dir/INSTALL.md && \
	(cd $(builddir) && \
	 rsync -r --relative $(built_dist_files) $$dir) && \
	echo "Remove this file to enable developer mode." >$$dir/dev_mode_disabled && \
	(cd $$tmpdir && \
	 tarcompression= && \
	 case $@ in \
	     *.bz2) tarcompression=-j ;; \
	     *.gz) tarcompression=-z ;; \
	     *.xz) tarcompression=-J ;; \
	 esac && \
	 tar -c $$tarcompression -f $(CURDIR)/$@ $(dist_dir)) && \
	rm -rf $$tmpdir

# $(1): extra configure options
define do_distcheck
	tmpdir=$$(mktemp -d /tmp/tmp-ccache-distcheck.XXXXXX) && \
	(cd $$tmpdir && \
	 tar xf $(CURDIR)/$< && \
	 mkdir -p $(dist_dir)/build && \
	 chmod -R a-w $(dist_dir) && \
	 chmod u+w $(dist_dir)/build && \
	 cd $(dist_dir)/build && \
	 ../configure --enable-more-warnings --prefix=$$tmpdir/root $(1) && \
	 $(MAKE) install CFLAGS=-Werror V=1 && \
	 $(MAKE) installcheck) && \
	chmod -R u+w $$tmpdir/$(dist_dir) && \
	rm -rf $$tmpdir
endef

.PHONY: distcheck
distcheck: $(firstword $(dist_archives))
	$(call do_distcheck, --without-bundled-zlib)
	$(call do_distcheck, --with-bundled-zlib)
	$(call do_distcheck, CC=clang)

.PHONY: docs
docs: $(generated_docs)

%.html: %.adoc
	@mkdir -p $(@D)
	$(if $(quiet),@echo "  ASCIIDOC $@")
	$(Q)$(ASCIIDOC) -o $@ -a revnumber=$(version) -a toc -b xhtml11 $<

%.xml: %.adoc
	@mkdir -p $(@D)
# Make literals stand out as bold in the man page:
	$(if $(quiet),@echo "  ASCIIDOC $@")
	$(Q)$(ASCIIDOC) -a revnumber=$(version) -d manpage -b docbook -o - $< | \
	  perl -pe 's!<literal>(.*?)</literal>!<emphasis role="strong">\1</emphasis>!g' >$@

$(non_3pp_objs) $(test_objs): CFLAGS += @more_warnings@

doc/ccache.1: doc/MANUAL.xml
	$(if $(quiet),@echo "  A2X      $@")
	$(Q)$(A2X) --doctype manpage --format manpage $<

.PHONY: update-authors
update-authors:
	git log --pretty=format:"* %aN" \
	  | sort -u \
	  | perl -00 -p -i -e 's/^\*.*/<STDIN> . "\n"/es' doc/AUTHORS.adoc

.PHONY: check-syntax
check-syntax:
	$(CC) $(all_cppflags) -I. $(all_cflags) -S -o /dev/null $(CHK_SOURCES)

.PHONY: cppcheck
cppcheck:
	$(CPPCHECK) --suppressions-list=$(CPPCHECK_SUPPRESSIONS) \
	  --inline-suppr -q --enable=all --force -I . \
	  --template='cppcheck: warning: {id}:{file}:{line}: {message}' \
	  $(non_3pp_sources) src/confitems_lookup.c src/main.c $(test_sources)

.PHONY: shellcheck
shellcheck: test/suites/*.bash
	$(SHELLCHECK) --shell=bash --exclude=$(shell sed -e 's/:.*//' <$(SHELLCHECK_EXCLUDES) | grep -v '#' | tr '\n' ',' | sed -e 's/,$$//') $^

.PHONY: uncrustify
uncrustify:
	uncrustify -c misc/uncrustify.cfg --no-backup --replace $(non_3pp_sources) $(test_sources)

# pip install compiledb
compile_commands.json:
	$(COMPILEDB) -n $(MAKE) all unittest

.PHONY: tidy
tidy: compile_commands.json
	$(CLANG_TIDY) $(all_sources)

.PHONY: analyze
analyze:
	$(SCAN_BUILD) --use-cc=$(CC) $(srcdir)/configure
	$(SCAN_BUILD) --use-cc=$(CC) --status-bugs $(MAKE) -B

BUILDENV = ubuntu
DOCKER_IMAGE_TAG = ccache/build:$(BUILDENV)

.PHONY: docker
docker: buildenv/$(BUILDENV)/Dockerfile
	$(DOCKER) inspect $(DOCKER_IMAGE_TAG) >/dev/null || $(DOCKER) build -t $(DOCKER_IMAGE_TAG) buildenv/$(BUILDENV)
	$(DOCKER) run --rm -v $(PWD):/build -w /build $(DOCKER_IMAGE_TAG) misc/build.sh $(TEST)

.PHONY: travis
travis: .travis/Dockerfile
	$(DOCKER) inspect travis-build >/dev/null || $(DOCKER) build -t travis-build .travis
	$(DOCKER) run --rm --volume $(PWD):/src --tmpfs /dst:rw,exec --env ASAN_OPTIONS='$(ASAN_OPTIONS)' travis-build \
	sh -c "cd /src && ./autogen.sh && cd /dst && CC=$(CC) CFLAGS='$(CFLAGS)' LDFLAGS='$(LDFLAGS)' /src/configure $(HOST) && make V=$(V) && make V=$(V) $(TEST)"

-include .deps/*.d
