# Package details
PACKAGE_NAME := pgmon

SCRIPT := src/$(PACKAGE_NAME).py

# Figure out the version components
# Note: The release is for RPM packages, where prerelease releases are written as 0.<release>
FULL_VERSION := $(shell grep -m 1 '^VERSION = ' "$(SCRIPT)" | sed -ne 's/.*"\(.*\)".*/\1/p')
VERSION := $(shell echo $(FULL_VERSION) | sed -n 's/\(.*\)\(-rc.*\|$$\)/\1/p')
RELEASE := $(shell echo $(FULL_VERSION) | sed -n 's/.*-rc\([0-9]\+\)$$/\1/p')

# Package version formatting
ifeq ($(RELEASE),)
RPM_RELEASE := 1
RPM_VERSION := $(VERSION)-$(RPM_RELEASE)
DEB_VERSION := $(VERSION)
else
RPM_RELEASE := 0.$(RELEASE)
RPM_VERSION := $(VERSION)-$(RPM_RELEASE)
DEB_VERSION := $(VERSION)~rc$(RELEASE)
endif

# Python stuff
PYTHON ?= python3
PYLINT ?= pylint
BLACK ?= black

HAVE_COVERAGE := $(shell $(PYTHON) -c 'import coverage' 2>/dev/null && echo yes)

# Where packages are built
BUILD_DIR := build

# List of distros we support
SUPPORTED := ubuntu-20.04 \
             ubuntu-22.04 \
             ubuntu-24.04 \
	     debian-11 \
	     debian-12 \
	     debian-13 \
	     rockylinux-8 \
	     rockylinux-9 \
	     rockylinux-10 \
	     oraclelinux-7 \
	     amazonlinux-2 \
	     amazonlinux-2023 \
	     gentoo

##
# These targets are the main ones to use for most things.
##

.PHONY: all clean tgz lint format test query-tests template-coverage

all: package-all

# Dump information related to the current version
version:
	@echo "full version=$(FULL_VERSION) version=$(VERSION) rel=$(RELEASE) rpm=$(RPM_VERSION) deb=$(DEB_VERSION)"

# Clean up the build directory
clean:
	rm -rf $(BUILD_DIR)

# Check for lint
lint:
	$(PYLINT) src/pgmon.py
	$(PYLINT) src/test_pgmon.py
	$(BLACK) --check --diff src/pgmon.py
	$(BLACK) --check --diff src/test_pgmon.py

# Format the code using black
format:
	$(BLACK) src/pgmon.py
	$(BLACK) src/test_pgmon.py

# Run unit tests for the script (with coverage if it's available)
test:
ifeq ($(HAVE_COVERAGE),yes)
	cd src ; $(PYTHON) -m coverage run -m unittest && python3 -m coverage report -m
else
	cd src ; $(PYTHON) -m unittest
endif

# Run query tests
query-tests:
	cd tests ; ./run-tests.sh

# Compare the sample metrics with the Zabbix template
template-coverage:
	$(PYTHON) zabbix_templates/coverage.py sample-config/pgmon-metrics.yml zabbix_templates/pgmon_templates.yaml

# Create a tarball (openrc version)
tgz:
	rm -rf $(BUILD_DIR)/tgz/root
	mkdir -p $(BUILD_DIR)/tgz/root
	$(MAKE) install-openrc DESTDIR=$(BUILD_DIR)/tgz/root
	tar -cz -f $(BUILD_DIR)/tgz/$(PACKAGE_NAME)-$(FULL_VERSION).tgz -C $(BUILD_DIR)/tgz/root .


##
# Install targets
#
# These are mostly used by the various package building images to actually install the script
##

# Install the script at the specified base directory (common components)
.PHONY: install-common
install-common:
	# Set up directories
	mkdir -p $(DESTDIR)/etc/$(PACKAGE_NAME)
	mkdir -p ${DESTDIR}/etc/logrotate.d
	mkdir -p $(DESTDIR)/usr/bin
	mkdir -p $(DESTDIR)/usr/share/man/man1
	
	# Install script
	cp $(SCRIPT) $(DESTDIR)/usr/bin/$(PACKAGE_NAME)
	chmod 755 $(DESTDIR)/usr/bin/$(PACKAGE_NAME)
	
	# Install manpage
	cp manpages/* $(DESTDIR)/usr/share/man/man1/
	gzip -f -9 $(DESTDIR)/usr/share/man/man1/$(PACKAGE_NAME).1
	
	# Install sample config
	cp sample-config/* $(DESTDIR)/etc/$(PACKAGE_NAME)/
	
	# Install logrotate config
	cp logrotate/${PACKAGE_NAME}.logrotate ${DESTDIR}/etc/logrotate.d/${PACKAGE_NAME}

# Install for systemd
.PHONY: install-systemd
install-systemd:
	# Install the common stuff
	$(MAKE) install-common
	
	# Set up directories
	mkdir -p $(DESTDIR)/lib/systemd/system
	
	# Install systemd unit files
	cp systemd/* $(DESTDIR)/lib/systemd/system/

# Install for open-rc
.PHONY: install-openrc
install-openrc:
	# Install the common stuff
	$(MAKE) install-common
	
	# Set up directories
	mkdir -p $(DESTDIR)/etc/init.d
	mkdir -p $(DESTDIR)/etc/conf.d
	
	# Install init script
	cp openrc/pgmon.initd $(DESTDIR)/etc/init.d/pgmon
	chmod 755 $(DESTDIR)/etc/init.d/pgmon
	
	# Install init script config file
	cp openrc/pgmon.confd $(DESTDIR)/etc/conf.d/pgmon


##
# Packaging targets
#
# These targets create packages for the supported distro versions
##

# Build all packages
.PHONY: package-all
package-all: all-package-images $(foreach distro_release, $(SUPPORTED), package-$(distro_release))

# Gentoo package (tar.bz2) creation
.PHONY: package-gentoo
package-gentoo:
	mkdir -p $(BUILD_DIR)/gentoo
	tar --transform "s,^,$(PACKAGE_NAME)-$(FULL_VERSION)/," -acjf $(BUILD_DIR)/gentoo/$(PACKAGE_NAME)-$(FULL_VERSION).tar.bz2 --exclude .gitignore $(shell git ls-tree --full-tree --name-only -r HEAD)
	cp $(BUILD_DIR)/gentoo/$(PACKAGE_NAME)-$(FULL_VERSION).tar.bz2 $(BUILD_DIR)/

# Create a deb/rpm package
.PHONY: package-%
package-%: PARTS=$(subst -, ,$*)
package-%: DISTRO=$(word 1, $(PARTS))
package-%: RELEASE=$(word 2, $(PARTS))
package-%:
	mkdir -p $(BUILD_DIR)
	docker run --rm \
	    -v .:/src:ro \
	    -v ./$(BUILD_DIR):/output \
	    --user $(shell id -u):$(shell id -g) \
	    "$(DISTRO)-packager:$(RELEASE)"


##
# Install test targets
#
# These targets test installing the package on the supported distro versions
##

# Run all of the install tests
.PHONY: install-tests debian-%-install-test rockylinux-%-install-test ubuntu-%-install-test amazonlinux-%-install-test gentoo-install-test
install-tests: $(foreach distro_release, $(SUPPORTED), $(distro_release)-install-test)

# Run a Debian install test
debian-%-install-test:
	docker run --rm \
	     -v ./$(BUILD_DIR):/output \
	     debian:$* \
	     bash -c 'apt-get update && apt-get install -y /output/$(PACKAGE_NAME)-$(DEB_VERSION)-debian-$*.deb'

# Run a RedHat install test
rockylinux-%-install-test:
	docker run --rm \
	     -v ./$(BUILD_DIR):/output \
	     rockylinux/rockylinux:$* \
	     bash -c 'dnf makecache && dnf install -y /output/$(PACKAGE_NAME)-$(RPM_VERSION).el$*.noarch.rpm'

# Run an Ubuntu install test
ubuntu-%-install-test:
	docker run --rm \
	     -v ./$(BUILD_DIR):/output \
	     ubuntu:$* \
	     bash -c 'apt-get update && apt-get install -y /output/$(PACKAGE_NAME)-$(DEB_VERSION)-ubuntu-$*.deb'

# Run an OracleLinux install test (this is for EL7 since CentOS7 images no longer exist)
oraclelinux-%-install-test: MGR=$(intcmp $*, 7, yum, yum, dnf)
oraclelinux-%-install-test:
	docker run --rm \
	     -v ./$(BUILD_DIR):/output \
	     oraclelinux:$* \
	     bash -c '$(MGR) makecache && $(MGR) install -y /output/$(PACKAGE_NAME)-$(RPM_VERSION).el7.noarch.rpm'

# Run a Amazon Linux install test
# Note: AL2 is too old for dnf
amazonlinux-%-install-test: MGR=$(intcmp $*, 2, yum, yum, dnf)
amazonlinux-%-install-test:
	docker run --rm \
	     -v ./$(BUILD_DIR):/output \
	     amazonlinux:$* \
	     bash -c '$(MGR) makecache && $(MGR) install -y /output/$(PACKAGE_NAME)-$(RPM_VERSION).amzn$*.noarch.rpm'

# Run a Gentoo install test
gentoo-install-test:
	# May impliment this in the future, but would require additional headaches to set up a repo
	echo "This would take a while ... skipping for now"


##
# Packaging image targets
#
# These targets build the docker images used to create packages
##

# Target to build packaging images for all supported releases
.PHONY: all-package-images
all-package-images: $(foreach distro_release, $(SUPPORTED), package-image-$(distro_release))

# Nothing needs to be created to build te tarballs for Gentoo
.PHONY: package-image-gentoo
package-image-gentoo:
	echo "Noting to do"

# Generic target for creating images that actually build the packages
# The % is expected to be: distro-release
# The build/.package-image-% target actually does the work and touches a state file to avoid excessive building
.PHONY: package-image-%
package-image-%: PARTS=$(subst -, ,$*)
package-image-%: DISTRO=$(word 1, $(PARTS))
package-image-%: RELEASE=$(word 2, $(PARTS))
package-image-%:
	docker build -t "$(DISTRO)-packager:$(RELEASE)" --build-arg "RELEASE=$(RELEASE)" --build-arg "DISTRO=$(DISTRO)" -f docker/Dockerfile-$(DISTRO) .


##
# Inside-container targets
#
# These targets are used inside containers.  They expect the repo to be mounted
# at /src and the build directory to be mounted at /output.
##

.PHONY: actually-package-debian-% actually-package-rockylinux-% actually-package-ubuntu-% actually-package-oraclelinux-% actually-package-amazonlinux-%

# Debian package creation
actually-package-debian-%:
	$(MAKE) install-systemd DESTDIR=/output/debian-$*
	cp -r --preserve=mode DEBIAN /output/debian-$*/
	dpkg-deb -Zgzip --build /output/debian-$* "/output/$(PACKAGE_NAME)-$(DEB_VERSION)-debian-$*.deb"

# RedHat package creation
actually-package-rockylinux-%:
	mkdir -p /output/rockylinux-$*/{BUILD,RPMS,SOURCES,SPECS,SRPMS}
	sed -e "s/@@VERSION@@/$(VERSION)/g" -e "s/@@RELEASE@@/$(RPM_RELEASE)/g" RPM/$(PACKAGE_NAME).spec > /output/rockylinux-$*/SPECS/$(PACKAGE_NAME).spec
	rpmbuild --define '_topdir /output/rockylinux-$*' \
	         --define 'version $(RPM_VERSION)' \
	         -bb /output/rockylinux-$*/SPECS/$(PACKAGE_NAME).spec
	cp /output/rockylinux-$*/RPMS/noarch/$(PACKAGE_NAME)-$(RPM_VERSION).el$*.noarch.rpm /output/

# Ubuntu package creation
actually-package-ubuntu-%:
	$(MAKE) install-systemd DESTDIR=/output/ubuntu-$*
	cp -r --preserve=mode DEBIAN /output/ubuntu-$*/
	dpkg-deb -Zgzip --build /output/ubuntu-$* "/output/$(PACKAGE_NAME)-$(DEB_VERSION)-ubuntu-$*.deb"

# OracleLinux package creation
# Note: This needs to work inside OEL7, so we can't use intcmp
actually-package-oraclelinux-7:
	mkdir -p /output/oraclelinux-7/{BUILD,RPMS,SOURCES,SPECS,SRPMS}
	sed -e "s/@@VERSION@@/$(VERSION)/g" -e "s/@@RELEASE@@/$(RPM_RELEASE)/g" RPM/$(PACKAGE_NAME)-el7.spec > /output/oraclelinux-7/SPECS/$(PACKAGE_NAME).spec
	rpmbuild --define '_topdir /output/oraclelinux-7' \
	         --define 'version $(RPM_VERSION)' \
	         -bb /output/oraclelinux-7/SPECS/$(PACKAGE_NAME).spec
	cp /output/oraclelinux-7/RPMS/noarch/$(PACKAGE_NAME)-$(RPM_VERSION).el7.noarch.rpm /output/
actually-package-oraclelinux-%:
	mkdir -p /output/oraclelinux-$*/{BUILD,RPMS,SOURCES,SPECS,SRPMS}
	sed -e "s/@@VERSION@@/$(VERSION)/g" -e "s/@@RELEASE@@/$(RPM_RELEASE)/g" RPM/$(PACKAGE_NAME).spec > /output/oraclelinux-$*/SPECS/$(PACKAGE_NAME).spec
	rpmbuild --define '_topdir /output/oraclelinux-$*' \
	         --define 'version $(RPM_VERSION)' \
	         -bb /output/oraclelinux-$*/SPECS/$(PACKAGE_NAME).spec
	cp /output/oraclelinux-$*/RPMS/noarch/$(PACKAGE_NAME)-$(RPM_VERSION).el$*.noarch.rpm /output/

# Amazon Linux package creation
# Note: This needs to work inside AL2, so we can't use intcmp
actually-package-amazonlinux-2:
	mkdir -p /output/amazonlinux-2/{BUILD,RPMS,SOURCES,SPECS,SRPMS}
	sed -e "s/@@VERSION@@/$(VERSION)/g" -e "s/@@RELEASE@@/$(RPM_RELEASE)/g" RPM/$(PACKAGE_NAME)-el7.spec > /output/amazonlinux-2/SPECS/$(PACKAGE_NAME).spec
	rpmbuild --define '_topdir /output/amazonlinux-2' \
	         --define 'version $(RPM_VERSION)' \
	         -bb /output/amazonlinux-2/SPECS/$(PACKAGE_NAME).spec
	cp /output/amazonlinux-2/RPMS/noarch/$(PACKAGE_NAME)-$(RPM_VERSION).amzn2.noarch.rpm /output/
actually-package-amazonlinux-%:
	mkdir -p /output/amazonlinux-$*/{BUILD,RPMS,SOURCES,SPECS,SRPMS}
	sed -e "s/@@VERSION@@/$(VERSION)/g" -e "s/@@RELEASE@@/$(RPM_RELEASE)/g" RPM/$(PACKAGE_NAME).spec > /output/amazonlinux-$*/SPECS/$(PACKAGE_NAME).spec
	rpmbuild --define '_topdir /output/amazonlinux-$*' \
	         --define 'version $(RPM_VERSION)' \
	         -bb /output/amazonlinux-$*/SPECS/$(PACKAGE_NAME).spec
	cp /output/amazonlinux-$*/RPMS/noarch/$(PACKAGE_NAME)-$(RPM_VERSION).amzn$*.noarch.rpm /output/

