#################################
# General Makefile 	   	#
# for RPM packaging	   	#
# https://yum.postgresql.org	#
#			   	#
# Devrim Gunduz		   	#
# devrim@gunduz.org	   	#
#################################
#
# 'build' target is for RPM buildfarm
#
# The build targets are all for use via the Makefile in each subdirectory.
# There are base targets like 'rpm' that detect the target PostgreSQL based on
# the path being built on, and there are version-qualified targets that specify
# a Pg version explicitly like rpm12. These are listed as "rpmNN" etc below.
#
# Targets:
#
#     clean
#       Remove all build products
#
#     prep
#     prepNN
#       Download sources etc
#
#     rpm
#     rpmNN (where "NN" is the Pg major)
#       build rpm and output in CWD
#
#     srpm
#     srpmNN
#       build srpm and output in CWD
#
#     build
#     buildNN
#       build both rpm and srpm and output in $HOME/rpmNN
#
# There are variants of the rpm, srpm and build targets that are prefixed
# with 'nosign' and/or 'noprep', and/or suffixed 'testing', e.g.
#
#     nosignrpm12
#     nosignnopreprpm12testing
#
# The 'nosign' variants skip rpm signing. The 'noprep' variants don't run the
# 'prep' step implicitly. The 'testing' targets just use a different build tree
# with 'testing' suffix. In most cases you should ignore all of them and prefer
# to set the variables TESTING=1, SIGN_FLAGS="" and PREP=0 as appropriate.
#
# User-settable variables at make command line:
#
#    PREP=0
#       suppress dependency downloads (otherwise done by default)
#       Same as using 'noprep' target prefix.
#
#    TESTING=1
#       suffix build with 'testing'. Same as using 'testing'
#       target suffix.
#
#    USE_GIT_PULL=0
#       suppress automatic "git pull" during package
#       build prep.
#
#    GIT
#       Command to use for git. You can add extra args here too.
#
#    INSTALL_DEPENDS=1
#       do an automatic "yum builddep" or "dnf build-dep"
#       for a package's spec file before building it.
#
#    SIGN_FLAGS=--sign
#       Override default "--sign" flags for signing or supply your own.
#       You can pass SIGN_FLAGS= to skip signing.
#
#    EXTRA_RPM_MACROS=--define "foo bar"
#       pass extra macros to rpm commands. Note this doesn't
#       work for the builddep step on EL-6; there you will have
#       to add to $HOME/.rpmmacros manually.
#
#
#

# Pg versions to generate targets for
PG_MAJOR_VERSIONS ?= 9.2 9.3 9.4 9.5 9.6 10 11 12

# Detect what we're building
ARCH ?= $(shell rpm --eval "%{_arch}")
SPECFILE=$(wildcard *.spec)

# Determine product etc from path if possible. Paths are e.g.:
#
# rpm/redhat/12/geos36/EL-6
#
# distcode and/or pgver could be 'master' if we're being invoked
# directly from one of the shared directories. For DIST the user
# should then suppply it. For PGVER we'll generate make targets
# for all versions for now anyway.
#
# The remaining path is assigned to 'rest' at each step then the trailing
# slash stripped with substitution references.
#
PATH_DISTCODE=$(subst -,_,$(notdir $(PWD)))
rest:=$(dir $(PWD))
rest:=$(rest:/=)
PATH_PKGNAME=$(notdir $(rest))
rest:=$(dir $(rest))
rest:=$(rest:/=)
PATH_PGVER=$(notdir $(rest))

# If the path has a Pg major version on it, make convenient targets 'rpm', 'srpm'
# and 'build':
#
ifneq ($(findstring $(PATH_PGVER),$(PG_MAJOR_VERSIONS)),)
$(info 'make rpm', 'make srpm' and 'make build' will target postgres $(PATH_PGVER))
tprefix:=$(if $(subst 0,,$(SIGN)),nosign)$(if $(subst 0,,$(PREP)),noprep)
tsuffix:=$(subst .,,$(PATH_PGVER))$(if $(subst 0,,$(TESTING)),testing,)
default: $(tprefix)build$(tsuffix)
rpm: $(tprefix)rpm$(tsuffix)
srpm: $(tprefix)rpm$(tsuffix)
build: $(tprefix)build$(tsuffix)
tprefix:=
tsuffix:=
else
default: build
rpm srpm build:
	$(error you need to use targets with a version suffix like rpm12 or build12)
endif

# Variables to map dir names like EL-8 to DIST suffixes like .rhel8
#
# Computed varnames are used to allow for the weird and wonderful variety
# of DIST names seen in the wild, and so users can override them with command
# line assignment.
#
# define vars like DIST_EL-5=.rhel5
$(foreach rhelver,5 6,\
  $(eval DIST_EL_$(rhelver) := .rhel$(rhelver)) \
)
# define vars like DIST_F-30=.f30
$(foreach fedoraver,30 31,\
  $(eval DIST_F_$(fedoraver):=.f$(fedoraver)) \
)
# Define vars like DIST_SLES-12=.sles12
$(foreach slesver,12,\
  $(eval DIST_SLES_$(slesver):=.f$(slesver)) \
)

ifndef DIST
  DIST := $(DIST_$(PATH_DISTCODE))
endif

ifeq ($(DIST),)
  $(error No DIST set in including Makefile and no var named DIST_$(PATH_DISTCODE) defined. Supply DIST on command line.)
else
  $(info Building for DIST=$(DIST))
endif

USE_GIT_PULL ?= 1
GIT ?= git
ifeq ($(USE_GIT_PULL),1)
  git_pull ?= $(GIT) pull
else
  git_pull ?=
endif

INSTALL_DEPENDS ?= 0
ifneq ($(INSTALL_DEPENDS),0)
  ifneq ($(shell test -e /usr/bin/dnf),)
    # Requires dnf-plugins-core package
    install_builddep_spec = dnf build-dep -y $$(SPECFILE)
    nodeps =
  else
    # requires yum-utils package
    install_builddep_spec = yum-builddep -y $$(SPECFILE)
    nodeps =
  endif
else
  install_builddep_spec =
  nodeps = --nodeps
endif

SIGN_FLAGS ?= --sign

dead_package_test = if [ -f dead.package ]; then echo "This package is marked as dead. Build won't continue"; exit 1; fi

# macros to pass to rpm commands. Note that this is a lazy(recursive) variable
# on purpose; it's expanded in the context of each templated set of targets
# with a different set of versions.
EXTRA_RPM_MACROS ?=

allclean:
	$(GIT) clean -dfx

clean:
	rm -rf i386/ i586/ i686/ x86_64/ noarch/
	rm -f *.src.rpm
	rm -f *.tar *.tar.gz *.tar.bz2 *.tgz *.zip .xz

# Template for make targets for Pg versions.
#
# $(call) supplies numbered arguments which are rather inconvenient. So instead
# we set simple variables with := in the innermost loop of the foreach that expands
# this template, giving us named arguments, and we $(eval) the result.
#
# The doubling of $(var) expansion is needed wherever the first pass that reads
# the defineed variable would otherwise eagerly expand variables. We don't want
# derived vars to be expanded until after $(1) etc are set by $(call) during
# $(foreach) expansion below then the generated body evaluated with $(eval).
#
# Use 'make -d' and 'make -p' for debugging. You may also find it useful to
# replace the $(eval $(call)) with $(info $(call)) to log the generated body
# rather than execute it. To see generated targets run
#
#     make -p 2>&1 | egrep '^[a-z][^=]*:[^=]'| cut -d : -f 1  | sort -u
#
# since for some bizarre reason make doesn't support dumping target names.
#
define version_targets

# For debugging, uncomment this:
#$$(info generating target for major=$$(major) ($$(nodotmajor)) with options: $$(filter-out ,$$(nosign) $$(noprep) $$(testing)))

# variant suffix to use for variables to avoid clashes.
v := $$(nodotmajor)$$(nosign)$$(noprep)$$(testing)

# macros to define for all the build variants
rpmmacros$$(v) := \
	    --define "_sourcedir $(PWD)" \
	    --define "_specdir $(PWD)" \
	    --define "dist $(DIST)" \
	    --define "pginstdir /usr/pgsql-$$(major)" \
	    --define "pgmajorversion $$(nodotmajor)" \
	    --define "pgpackageversion $$(major)" \
	    $(EXTRA_RPM_MACROS)

# Output everything to the rpmbasedir, for 'build' targets
rpmbasedir := $(HOME)/rpm$$(nodotmajor)$$(testing)
rpmdirs_buildroot$$(v) := \
	    --define "_builddir $$(rpmbasedir)/BUILD" \
	    --define "_buildrootdir $$(rpmbasedir)/BUILDROOT" \
	    --define "_rpmdir $$(rpmbasedir)/RPMS/" \
	    --define "_srcrpmdir $$(rpmbasedir)/SRPMS"

# Build in cwd subdirs, output to cwd, for 'rpm' and 'srpm' targets
rpmdirs_cwd$$(v) := \
	    --define "_builddir $(PWD)rpm$$(nodotmajor)$$(testing)/BUILD" \
	    --define "_buildrootdir $(PWD)rpm$$(nodotmajor)$$(testing)/BUILDROOT" \
	    --define "_rpmdir $(PWD)" \
	    --define "_srcrpmdir $(PWD)"

# target name fragments for generating the noprep and nosign variants
prep_target := $$(if $$(filter noprep,$$(noprep)),,prep$$(nodotmajor))

# We only need one definition of the prep test but it's simpler to define
# them for all cases since ifeq is expanded eagerly on the first pass
# and inline if is inconvenient for targets with recipes.
ifeq ($$(nosign)$$(noprep)$$(testing),)
$$(prep_target):
	@$(dead_package_test)
	# Update spec file, patches, etc, before running spectool:
	$(git_pull)
	# Use spectool to download source files, especially tarballs.
	spectool -g -S $(rpmmacros) $(SPECFILE)
	# If requested, install build-depends from the specfile.
	$(install_builddep_spec)

alltargets .PHONY: $$(prep_target)
endif

# Combined source and build step that forces source step to run and
# output to the rpmbasedir.
$$(nosign)$$(noprep)build$$(nodotmajor)$$(testing): $$(prep_target)
	rpmbuild $$(rpmmacros$(v)) $$(rpmdirs_buildroot$(v)) $(sign_flag) $(nodeps) -bs $(SPECFILE)
	rpmbuild $$(rpmmacros$(v)) $$(rpmdirs_buildroot$(v)) $(sign_flag) $(nodeps) -bb $(SPECFILE)

# Build just the srpm and output to PWD
$$(nosign)$$(noprep)srpm$$(nodotmajor)$$(testing): $$(prep_target)
	rpmbuild $$(rpmmacros$(v)) $$(rpmdirs_cwd$(v)) $(sign_flag) $(nodeps) -bs $(SPECFILE)

# build just the rpm and output to PWD
$$(nosign)$$(noprep)rpm$$(nodotmajor)$$(testing): $$(prep_target)
	 rpmbuild $$(rpmmacros$(v)) $$(rpmdirs_cwd$(v)) $(sign_flag) $(nodeps) -bb $(SPECFILE)

alltargets .PHONY: \
    $$(nosign)$$(noprep)build$$(nodotmajor)$$(testing) \
    $$(nosign)$$(noprep)srpm$$(nodotmajor)$$(testing) \
    $$(nosign)$$(noprep)rpm$$(nodotmajor)$$(testing)

# Clear vars to help prevent mistakes
major:=
nodotmajor:=
nosign:=
noprep:=
testing:=

endef

# Generate targets for each major version with nosign and noprep variants and testing
# variants of all of them.
#
# Use of 'sign', 'prep' etc works around the inability to specify the empty
# string as one of the entries in a foreach by passing placeholders "prep",
# "sign" or "notesting" when we want empty, then filtering them out.
#
$(foreach pgmajorversion,$(PG_MAJOR_VERSIONS),\
  $(foreach signvariant,sign nosign,\
    $(foreach prepvariant,prep noprep,\
      $(foreach testingvariant,notesting testing,\
	    $(eval major := $(pgmajorversion)) \
		$(eval nodotmajor := $$(subst .,,$$(major))) \
		$(eval nosign := $$(filter-out sign,$(signvariant))) \
		$(eval noprep := $$(filter-out prep,$(prepvariant))) \
		$(eval testing := $$(filter-out notesting,$(testingvariant))) \
		$(eval sign_flag := $$(if $$(nosign),,$(SIGN_FLAGS))) \
        $(eval $(version_targets)) \
      ) \
    ) \
  ) \
)
