#!make -- for syntax highlighting
# "make prep"
# Generate that build-dir, the compat.h and compat.patch
# and compat-patched sources, but without recursing into it yet.

THIS_MAKEFILE := $(lastword $(MAKEFILE_LIST))

# Apparently GNU Make 4.0 strips leading ./ from MAKEFILE_LIST. Yay.
KBUILD_STAGE ?= $(if $(filter      scripts/Makefile.modpost \
			$(srctree)/scripts/Makefile.modpost,\
			$(MAKEFILE_LIST)),modpost)

build-dir := build-$(KERNELRELEASE)

ifeq (modpost,$(KBUILD_STAGE))
$(error MODPOST not expected)
else

SHELL := /bin/bash

obj.build := $(obj)/$(build-dir)
PHONY += $(obj.build)

# we don't have an official Kbuild entry point.  This is only used to generate
# the compat.h and compat.patch, and prepare the patched sources in the
# specific build directory, where the actual "make modules" will happen.

# symlink pointing to the compat patch in the cocci cache,
# if necessary, the compat.patch will be generated there first.
compat.h         := $(obj.build)/compat.h
compat.patch     := $(obj.build)/compat.patch
current_build_symlink  := build-current

CURRENT_BUILD_SYMLINKS := compat.h compat.patch .compat_test
CURRENT_BUILD_SYMLINKS += compat.$(KERNELRELEASE).h
CURRENT_BUILD_SYMLINKS += compat.$(KERNELRELEASE).patch
CURRENT_BUILD_SYMLINKS += .compat_test.$(KERNELRELEASE)

# symlinks, specific for current build KERNELRELEASE
drop_KERNELRELEASE_from_filename = \
	$(patsubst %.$(KERNELRELEASE),%,\
	$(patsubst %.$(KERNELRELEASE)$(suffix $(@F)),%$(suffix $(@F)),$(@F)))
symlink_target = $(filter-out $(current_build_symlink),$(drop_KERNELRELEASE_from_filename))
cmd_symlink_from_current_build = \
	set -- $(obj.build)/$(symlink_target) $@; \
	target=$$1 link_name=$$2; \
	test $$target -ef $$link_name || \
	{ $(if $(V),,$(kecho) '  LN      $(@F) -> $(build-dir)/$(symlink_target)';) \
	ln $(if $(V),-v) -s -r -f -T $(obj.build)/$(symlink_target) $@ ; }

# how to create these symlink
cur.symlinks := $(addprefix $(obj)/,$(CURRENT_BUILD_SYMLINKS))
$(cur.symlinks) $(obj)/$(current_build_symlink): FORCE
	$(call cmd,symlink_from_current_build)

# ======================================================================

#
# run compat tests from inside Kbuild, and generate compat.h from that,
# To avoid a spurious modpost run, pretend to be a multi-object module,
# but ask Kbuild to only build part of it.
#

HOST_EXTRACFLAGS += -I$(src) -std=c11 $(EXTRA_CFLAGS)
hostprogs := drbd-kernel-compat/gen_patch_names

# for the "compat.h" step
# make ... obj=dummy-for-compat.o dummy-for-compat-h.o
dummy-for-compat-y += dummy-for-compat-h.o
# also ask for "kernelrelease" and symlinks
$(obj)/dummy-for-compat-h.o: | $(obj.build)/.drbd_kernelrelease
$(obj)/dummy-for-compat-h.o: | $(obj)/$(current_build_symlink)
$(obj)/dummy-for-compat-h.o: | $(filter-out %.patch,$(cur.symlinks))
$(obj)/dummy-for-compat-h.o: $(compat.h)
	@true

# same trick for the prep / patch step
# make ... obj=dummy-for-prep.o dummy-for-patch.o
# more dependencies, and how to build them, further below.
dummy-for-prep-y += dummy-for-patch.o
patch-target := $(obj)/dummy-for-patch.o
$(patch-target): $(obj)/dummy-for-compat-h.o
	@true

ifneq ($(shell date -r $(objtree)/.config),$(shell date -r $(obj.build)/.config.timestamp 2> /dev/null))
COMPAT_FORCE := FORCE
endif

# Red hat's kernel header files needs additional includes they
# miss to add to the files.
# E.g. linux/blk_types.h on RHEL 7.6 needs rh_kabi.h and errno.h
# otherwise the compat test already fails in processing linux/blk_types.h
RH_KABI_H := $(wildcard $(KDIR)/include/linux/rh_kabi.h)
ifdef RH_KABI_H
      COMPAT_CFLAGS := -include "linux/rh_kabi.h" -include "linux/errno.h"
endif

CC_looks_sane := $(shell </dev/null $(CC) -P -x c -c -o /dev/null - >&2 && echo yep)
ifndef CC_looks_sane
$(error compiler not present or misbehaving)
endif

TEST_C := $(sort $(wildcard $(src)/drbd-kernel-compat/tests/*.c))
$(if $(TEST_C),,$(error === Confused: No compat test cases found.))

test_result_prefix := $(obj.build)/.compat_test/
TEST_R := $(TEST_C:$(src)/drbd-kernel-compat/tests/%.c=$(test_result_prefix)%.result)
$(test_result_prefix)%.result: $(src)/drbd-kernel-compat/tests/%.c $(COMPAT_FORCE)
	$(call cmd,compat_test_result)

# check these first:
$(filter-out $(test_result_prefix)have_sane_test_environment%,$(TEST_R)): $(filter $(test_result_prefix)have_sane_test_environment%,$(TEST_R))

quiet_cmd_compat_test_result = COMPAT  $*
      cmd_compat_test_result =							\
	set -e ; mkdir -p $(@D)/ ;						\
	var=`echo COMPAT_$* | tr -- -a-z _A-Z | tr -dc A-Z0-9_` ;		\
	if $(CC) $(c_flags) -Wno-error -Werror-implicit-function-declaration $(COMPAT_CFLAGS) \
		-c -o /dev/null $< > $(@D)/$*.stdout 2> $(@D)/$*.stderr	\
		-D"KBUILD_MODNAME=\"compat_dummy\"" ;				\
	then									\
		[[ $$var = COMPAT_HAVE_SANE_TEST_ENVIRONMENT_ALWAYS_FAIL ]] && exit 1;	\
		echo "\#define $$var" ;						\
	else									\
		if [[ $$var = COMPAT_HAVE_SANE_TEST_ENVIRONMENT ]] ; then	\
			cat $(@D)/$*.stderr >&2;				\
			exit 1;							\
		fi ;								\
		echo "/* \#undef $$var */" ;					\
	fi > $@.tmp ; mv $@.tmp $@

# funky trick with patsubst printf xarg to shorten the command line a bit
# older kernel still redirects stdin from the first prerequisite,
# and does not yet have an implicit { ; } command block,
# which would override the pipe and have xargs feed from the first result file :-(
filechk_compat.h = { printf '$(obj.build)/%s ' $(patsubst $(obj.build)/%,%,$(TEST_R)) | xargs cat; }

$(compat.h): $(TEST_R) FORCE
	$(call filechk,compat.h)
	$(Q)touch -r $(objtree)/.config $(obj.build)/.config.timestamp

# Generate / find from cache the compat.patch.
# If we used an old compat.patch from the cache,
# "backdate" the compat.h to the used compat.patch
# to avoid spurious checks in future runs.
$(compat.patch): $(compat.h) $(src)/drbd-kernel-compat/gen_patch_names.c
	$(MAKE) -C $(src) -f Makefile.spatch $@
	-set -- $(addprefix $(obj.build)/compat,.patch .h);	\
	test $$1 -nt $$2 || touch -r $$1 $$2

# ======================================================================

# list of known source files
include $(src)/Kbuild.drbd-module-sources

patches-dir := $(obj.build)/.patches
build-patches :=
build-sources :=

# Modern patchutils could "splitdiff -a -D $(patches-dir)".
# still won't work exactly the same, as cocci is inconsistent with its annotation
# and sometimes has a './' prefix, but most of the time it has not.
quiet_cmd_splitdiff = SPLIT   $<
      cmd_splitdiff = \
		mkdir -p $(patches-dir) \
		&& cd $(patches-dir) \
		&& $(PERL) $(src)/drbd-kernel-compat/splitdiff.pl < $<

annotate-orig-pos := \
	$(src)/drbd-kernel-compat/annotate-diff-with-original-file-position-pragmas.pl

PATCH := patch --batch --forward --reject-file=- $(if $(V),--verbose,--silent)
# - apply compat patch to tmp output, leave original unchanged,
# - re-diff original and tmp output, annotate with line number pragma,
# - apply annotated patch to final output
#
# patch --output will ignore both umask and original file permissions :-(
# Even with --output=-, it tries to create (but not use) a temp file
# named ./-.XXXXXXX in whatever current directory it is in,
# which probably is the linux headers / source location, and may not be
# writable for this user. And it would duplicated the original to stdout,
# should there be more than one "header" in the patch file.
# Use chmod, so a CI user != build user is able to collect resulting patched sources.
quiet_cmd_apply_compat_patch = PATCH   $@
      cmd_apply_compat_patch =						\
	set -e; set -- $^; out=$@ patch=$$1 orig=$$2;			\
	test -d $(@D) || mkdir -p $(@D);				\
	if test -s $$patch ; then					\
		$(PATCH) --output $$out.tmp $$orig $$patch ;		\
		diff -u $$orig $$out.tmp |				\
		DRBDSRC=$(src)						\
		OUTDIR=$(obj.build)					\
		PATCHES_DIR=$(patches-dir)				\
		$(PERL) $(annotate-orig-pos)				\
		| $(PATCH) --output $$out $$orig ;			\
		chmod --reference $$orig $$out ;			\
		: rm -f $$out.tmp; 					\
	else { echo "\# 1 \"$$orig\""; cat $$orig; } > $$out; fi

orig-source = $(src)/$(s)
build-source = $(obj.build)/$(s)
patch-name = $(patches-dir)/$(subst /,_,$(s)).patch

define apply-compat-patch-template
build-patches += $(patch-name)
build-sources += $(build-source)
$(build-source): $(patch-name) $(orig-source)
	$$(call cmd,apply_compat_patch)
endef
$(foreach s,$(filter %.c %.h,$(drbd-module-sources)),$(eval $(apply-compat-patch-template)))

.ts.compat.patch := $(obj.build)/.ts.compat.patch
$(build-sources): $(.ts.compat.patch)

# We could use &: (a "grouped" target) to let make know that one execution of
# the recipe will generate all of the target patches.
# :-( but older make cannot do that yet :-(
# So we have the "time stamp compat patch" target.
$(build-patches): $(.ts.compat.patch)

$(.ts.compat.patch): $(compat.patch)
	$(call cmd,splitdiff)
	$(Q)touch -r $< $(build-patches) $(.ts.compat.patch)

# === build-$(KERNELRELEASE)/ (drbd with compat patches applied) ======

ifndef cmd_copy
quiet_cmd_copy = COPY    $@
      cmd_copy = cat $< > $@
endif

$(obj.build)/Kbuild: $(src)/Kbuild.drbd
	$(Q)mkdir -p $(@D)
	$(call cmd,copy)

$(obj.build)/Module.supported: $(src)/Module.supported
	$(Q)mkdir -p $(@D)
	$(call cmd,copy)

# Ask for the "apply_compat_patch" rules above to run,
# for the Kbuild template to be copied over,
# for the buildtag, kernelrelease, kernel.config.gz to be generated,
generate-these :=
generate-these += $(build-sources)
generate-these += $(obj.build)/Kbuild
generate-these += $(obj.build)/drbd_buildtag.c
generate-these += $(obj.build)/.drbd_kernelrelease
generate-these += $(obj.build)/.kernel.config.gz
generate-these += $(if $(wildcard $(src)/Module.supported),$(obj.build)/Module.supported)

$(patch-target): $(generate-these)

# Older kernels may need this when building against an OFED stack.
# But no need to rebuild anything just because the build itself updated that
# file, as it is expected to do. -=> order only prerequisite.
$(patch-target): | $(obj.build)/Module.symvers
$(obj.build)/Module.symvers:
	$(Q)cat /dev/null $(KBUILD_EXTRA_SYMBOLS) $(OFED_MODULE_SYMVERS) > $@

# ======================================================================

define filechk_.drbd_kernelrelease
	printf "%s\n"				\
	"KDIR=$(KDIR)"				\
	$(if $(O),"O=$(O)")			\
	"VERSION=$(VERSION)"			\
	"PATCHLEVEL=$(PATCHLEVEL)"		\
	"SUBLEVEL=$(SUBLEVEL)"			\
	"EXTRAVERSION=$(EXTRAVERSION)"		\
	"LOCALVERSION=$(LOCALVERSION)"		\
	"KERNELRELEASE=$(KERNELRELEASE)"	\
	"KERNELVERSION=$(KERNELVERSION)"
endef

# older kernels unconditionally redirect stdin from first prerequisite ($<)
$(obj.build)/.drbd_kernelrelease: /dev/null FORCE
	$(call filechk,.drbd_kernelrelease)

kconfig := $(realpath $(word 1,$(wildcard $(objtree)/.config $(srctree)/.config)))
$(obj.build)/.kernel.config.gz: $(obj.build)/.drbd_kernelrelease $(kconfig)
	@$(kecho) '  GEN     $@ $(echo-why)'
	$(Q)config=$(kconfig)                                   ; \
	{ echo -e "#\n# drbd.o was compiled with"           ; \
	  echo "#  `$(CC) -v 2>&1 | tail -1`"               ; \
	  echo "# against this kernelrelease:"              ; \
	  sed 's/^/#  /' $<                                 ; \
	  echo    "# kernel .config from"                   ; \
	  echo    "#  $$config"                             ; \
	  echo -e "# follows\n#\n"                          ; \
	  cat $$config ; } | gzip > $@.new && mv $@.new $@

# for some reason some of the commands below only work correctly in bash,
# and not in e.g. dash. I'm too lazy to fix it to be compatible.
$(obj.build)/drbd_buildtag.c: $(addprefix $(src)/,$(drbd-module-sources)) $(THIS_MAKEFILE)
	@$(kecho) '  GEN     $@ $(echo-why)'
	@set -e;								\
	if [ -z "${WANT_DRBD_REPRODUCIBLE_BUILD}" ] || [ -z "${SOURCE_DATE_EPOCH}" ] ; then	\
		buildinfo="build by $$USER@$$HOSTNAME, `date "+%F %T"`" ;			\
	else 											\
		buildinfo="reproducible build, `date -u -d@${SOURCE_DATE_EPOCH} "+%F %T"`" ; 	\
	fi ; 											\
	if test -e $(src)/../.git &&						\
	   GITHEAD=$$(cd $(src) && git rev-parse HEAD); then			\
		GITDIFF=$$(cd $(src)/.. && git diff --name-only HEAD |		\
			tr -s '\t\n' '  ' |					\
			sed -e 's/^/ /;s/ *$$//');				\
		build_tag="GIT-hash: $$GITHEAD$$GITDIFF";			\
	elif test -e $(src)/.drbd_git_revision ; then				\
		build_tag=$$(cat $(src)/.drbd_git_revision);			\
	else									\
		echo >&2 "Your DRBD source tree is broken. Unpack again.";      \
		exit 1;								\
	fi ;									\
	exec > $@.new;								\
	echo -e "/* automatically generated. DO NOT EDIT. */";			\
	echo -e "#include <linux/drbd.h>";					\
	echo -e "#include <linux/drbd_config.h>";				\
	echo -e "const char *drbd_buildtag(void)\n{";				\
	echo -e "\treturn \"$$build_tag\"\n";					\
	echo -e "\t\t\" $$buildinfo\";\n}";					\
	mv -f $@.new $@
endif
