diff --git a/GNUmakefile b/GNUmakefile
index 8cb5d3e486..73714b4bce 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -1,61 +1,63 @@
 #
 # Copyright 2008-2023 the Pacemaker project contributors
 #
 # The version control history for this file may have further details.
 #
 # This source code is licensed under the GNU General Public License version 2
 # or later (GPLv2+) WITHOUT ANY WARRANTY.
 #
 
 default: build
 .PHONY: default
 
 -include Makefile
 
 # The main purpose of this GNUmakefile is that its targets can be invoked
 # without having to call autogen.sh and configure first. That means automake
 # variables may or may not be defined. Here, we use the current working
 # directory if a relevant variable hasn't been defined.
 abs_srcdir	?= $(shell pwd)
 
 GLIB_CFLAGS	?= $(pkg-config --cflags glib-2.0)
 
 PACKAGE		?= pacemaker
 
 .PHONY: init
 init:
 	test -e configure && test -e libltdl || ./autogen.sh
 	test -e Makefile || ./configure
 
 .PHONY: build
 build: init
 	$(MAKE) $(AM_MAKEFLAGS) core
 
 ## RPM-related targets (deprecated; use targets in rpm subdirectory instead)
 
 # Pass option depending on whether automake has been run or not
 USE_FILE = $(shell test -e rpm/Makefile || echo "-f Makefile.am")
 
 .PHONY: $(PACKAGE).spec chroot dirty export mock rc release rpm rpmlint srpm
 $(PACKAGE).spec chroot dirty export mock rc release rpm rpmlint srpm:
 	$(MAKE) $(AM_MAKEFLAGS) -C rpm $(USE_FILE) "$@"
 
-.PHONY: mock-% rpm-% spec-% srpm-%
-mock-% rpm-% spec-% srpm-%:
+mock-% rpm-% spec-% srpm-%: FORCE
 	$(MAKE) $(AM_MAKEFLAGS) -C rpm $(USE_FILE) "$@"
 
 ## Development-related targets
 ## (deprecated; use targets in devel subdirectory instead)
 
 COVLEVEL        	?= low
 COVERAGE_TARGETS	= coverage coverage-cts coverage-clean
 COVERITY_TARGETS	= coverity coverity-analyze coverity-clean coverity-corp
 
 .PHONY: clang $(COVERAGE_TARGETS) $(COVERITY_TARGETS) cppcheck indent
 clang $(COVERAGE_TARGETS) $(COVERITY_TARGETS) cppcheck indent:
 	@echo 'Deprecated: Use "make -C devel $@" instead'
 	$(MAKE) $(AM_MAKEFLAGS)				\
 		CLANG_checkers=$(CLANG_checkers)	\
 		COVLEVEL=$(COVLEVEL)			\
 		CPPCHECK_ARGS=$(CPPCHECK_ARGS)		\
 		-C devel "$@"
+
+.PHONY: FORCE
+FORCE:
diff --git a/Makefile.am b/Makefile.am
index a3952ac19b..7425e29bd5 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,113 +1,120 @@
 #
 # Copyright 2003-2023 the Pacemaker project contributors
 #
 # The version control history for this file may have further details.
 #
 # This source code is licensed under the GNU General Public License version 2
 # or later (GPLv2+) WITHOUT ANY WARRANTY.
 #
 
 # This directory must be same as in configure.ac's AC_CONFIG_MACRO_DIR
 ACLOCAL_AMFLAGS		= -I m4
 
 EXTRA_DIST		= CONTRIBUTING.md	\
 			  GNUmakefile		\
 			  INSTALL.md		\
 			  README.markdown	\
 			  autogen.sh		\
 			  m4/CC_CHECK_LDFLAGS.m4	\
 			  m4/CHECK_ENUM_VALUE.m4	\
 			  m4/gnulib-cache.m4	\
 			  m4/gnulib-tool.m4	\
 			  m4/PKG_CHECK_VAR.m4	\
 			  m4/REQUIRE_HEADER.m4	\
 			  m4/version.m4
 
 DISTCLEANFILES		= config.status
 
 MAINTAINERCLEANFILES	= Makefile.in		\
 			  aclocal.m4		\
 			  config.guess		\
 			  config.sub		\
 			  configure		\
 			  depcomp		\
 			  install-sh		\
 			  ltmain.sh		\
 			  missing		\
 			  py-compile		\
 			  test-driver
 
 # Don't try to install files outside build directory for "make distcheck".
 AM_DISTCHECK_CONFIGURE_FLAGS	= --prefix="$$dc_install_base/usr"			\
 				  --sysconfdir="$$dc_install_base/etc"			\
 				  --with-initdir="$$dc_install_base/etc/init.d"		\
 				  --with-ocfdir="$$dc_install_base/usr/lib/ocf"		\
 				  --with-systemdsystemunitdir="$$dc_install_base$(systemdsystemunitdir)"
 
 # Only these will get built with a plain "make"
 CORE	= include lib daemons tools xml po python cts rpm
 
 SUBDIRS	= $(CORE) agents devel doc etc maint tests
 
 AM_CPPFLAGS		= -I$(top_srcdir)/include
 
 doc_DATA = README.markdown COPYING
 
 licensedir              = $(docdir)/licenses/
 dist_license_DATA	= $(wildcard licenses/*)
 
 # Directories that should be created on install and removed on uninstall
 ## owned by root:haclient, mode 0750
 ROOT_DIRS	= $(PACEMAKER_CONFIG_DIR)
 ## owned by hacluster:haclient, mode 0750
 DAEMON_R_DIRS	= $(CRM_CONFIG_DIR)	\
 		  $(CRM_CORE_DIR)	\
 		  $(CRM_BLACKBOX_DIR)
 ## owned by hacluster:haclient, mode 0770
 DAEMON_RW_DIRS	= $(CRM_BUNDLE_DIR)	\
 		  $(CRM_LOG_DIR)
 
+.PHONY: core
 core:
 	@echo "Building only core components and tests: $(CORE)"
 	@for subdir in $(CORE); do \
 		echo "Building $$subdir"; \
 		$(MAKE) $(AM_MAKEFLAGS) -C $$subdir all || exit 1; \
 	done
 
+.PHONY: core-clean
 core-clean:
 	@echo "Cleaning only core components and tests: $(CORE)"
 	@for subdir in $(CORE); do \
 		echo "Cleaning $$subdir"; \
 		$(MAKE) $(AM_MAKEFLAGS) -C $$subdir clean || exit 1; \
 	done
 
+.PHONY: install-exec-local
 install-exec-local:
 	for DIR in $(ROOT_DIRS) $(DAEMON_R_DIRS); do				\
 		$(INSTALL) -d -m 750 "$(DESTDIR)/$$DIR";			\
 	done
 	for DIR in $(DAEMON_RW_DIRS); do					\
 		$(INSTALL) -d -m 770 "$(DESTDIR)/$$DIR";			\
 	done
 	-for DIR in $(ROOT_DIRS); do						\
 		chgrp $(CRM_DAEMON_GROUP) "$(DESTDIR)/$$DIR";			\
 	done
 	-for DIR in $(DAEMON_R_DIRS) $(DAEMON_RW_DIRS); do			 \
 		chown $(CRM_DAEMON_USER):$(CRM_DAEMON_GROUP) "$(DESTDIR)/$$DIR"; \
 	done
 
 # Remove created directories only if they're empty
+.PHONY: uninstall-hook
 uninstall-hook:
 	-for DIR in $(ROOT_DIRS) $(DAEMON_R_DIRS) $(DAEMON_RW_DIRS); do	\
 		rmdir "$(DESTDIR)/$$DIR";				\
 	done
 
+.PHONY: clean-generic
 clean-generic:
 	-rm -f *.tar.bz2 *.sed
 
 PACKAGE         ?= pacemaker
 
+.PHONY: clean-local
 clean-local:
 	-rm -f $(builddir)/$(PACKAGE)-*.tar.gz
 
+.PHONY: distclean-local
 distclean-local:
 	-rm -rf libltdl autom4te.cache
diff --git a/cts/Makefile.am b/cts/Makefile.am
index ac2e8abf82..139c5f5223 100644
--- a/cts/Makefile.am
+++ b/cts/Makefile.am
@@ -1,90 +1,95 @@
 #
 # Copyright 2001-2023 the Pacemaker project contributors
 #
 # The version control history for this file may have further details.
 #
 # This source code is licensed under the GNU General Public License version 2
 # or later (GPLv2+) WITHOUT ANY WARRANTY.
 #
 
 MAINTAINERCLEANFILES    = Makefile.in
 
 # Test commands and globally applicable test files should be in $(testdir),
 # and command-specific test data should be in a command-specific subdirectory.
 testdir			= $(datadir)/$(PACKAGE)/tests
 test_SCRIPTS		= cts-attrd \
 			  cts-cli		\
 			  cts-exec		\
 			  cts-fencing		\
 			  cts-lab 		\
 			  cts-regression	\
 			  cts-scheduler
 dist_test_DATA		= README.md			\
 			  valgrind-pcmk.suppressions
 
 clidir		= $(testdir)/cli
 dist_cli_DATA	= cli/constraints.xml 				\
 		  cli/crmadmin-cluster-remote-guest-nodes.xml	\
 		  cli/crm_diff_new.xml				\
 		  cli/crm_diff_old.xml				\
 		  cli/crm_mon.xml				\
 		  cli/crm_mon-feature_set.xml			\
 		  cli/crm_mon-partial.xml			\
 		  cli/crm_mon-rsc-maint.xml			\
 		  cli/crm_mon-T180.xml				\
 		  cli/crm_mon-unmanaged.xml			\
 		  cli/crm_resource_digests.xml			\
 		  cli/regression.acls.exp			\
 		  cli/regression.crm_mon.exp			\
 		  cli/regression.daemons.exp			\
 		  cli/regression.dates.exp			\
 		  cli/regression.error_codes.exp		\
 		  cli/regression.feature_set.exp		\
 		  cli/regression.rules.exp			\
 		  cli/regression.tools.exp			\
 		  cli/regression.upgrade.exp			\
 		  cli/regression.validity.exp			\
 		  cli/regression.access_render.exp
 
 ctsdir			= $(datadir)/$(PACKAGE)/tests/cts
 cts_SCRIPTS		= cts
 
 # Commands intended to be run only via other commands
 halibdir		= $(CRM_DAEMON_DIR)
 dist_halib_SCRIPTS	= cts-log-watcher
 
 noinst_SCRIPTS		= cluster_test
 
+.PHONY: scheduler-list
 scheduler-list:
 	@for T in "$(srcdir)"/scheduler/xml/*.xml; do       \
 		echo $$(basename $$T .xml);             \
 	done
 
 CLEANFILES	= $(builddir)/.regression.failed.diff
 
+.PHONY: clean-local
 clean-local:
 	rm -f scheduler/*/*.pe
 
 SUBDIRS	= benchmark scheduler support
 
+.PHONY: cts-support-install
 cts-support-install:
 	$(MAKE) $(AM_MAKEFLAGS) -C support cts-support
 	$(builddir)/support/cts-support install
 
+.PHONY: cts-support-uninstall
 cts-support-uninstall:
 	$(MAKE) $(AM_MAKEFLAGS) -C support cts-support
 	$(builddir)/support/cts-support uninstall
 
 # Everything listed here is a python script, typically generated from a .in file
 # (though that is not a requirement).  We want to run pylint on all of these
 # things after they've been built.
 python_files = cts-attrd \
 			   cts-exec \
 			   cts-fencing \
 			   cts-lab \
 			   cts-log-watcher \
 			   cts-regression \
 			   cts-scheduler
 
+.PHONY: pylint
 pylint: $(python_files)
 	PYTHONPATH=$(top_builddir)/python pylint --rcfile $(top_srcdir)/python/pylintrc $(python_files)
diff --git a/cts/scheduler/Makefile.am b/cts/scheduler/Makefile.am
index 9074390a2c..c51795dc5a 100644
--- a/cts/scheduler/Makefile.am
+++ b/cts/scheduler/Makefile.am
@@ -1,18 +1,19 @@
 #
 # Copyright 2001-2021 the Pacemaker project contributors
 #
 # The version control history for this file may have further details.
 #
 # This source code is licensed under the GNU General Public License version 2
 # or later (GPLv2+) WITHOUT ANY WARRANTY.
 #
 MAINTAINERCLEANFILES    = Makefile.in
 
 pedir		= $(datadir)/$(PACKAGE)/tests/scheduler
 
+.PHONY: list
 list:
 	@for T in "$(srcdir)"/xml/*.xml; do       \
 		echo $$(basename $$T .xml);             \
 	done
 
 SUBDIRS	= dot exp scores stderr summary xml
diff --git a/daemons/attrd/Makefile.am b/daemons/attrd/Makefile.am
index 6bb81c441e..0af7faf8cd 100644
--- a/daemons/attrd/Makefile.am
+++ b/daemons/attrd/Makefile.am
@@ -1,48 +1,53 @@
 #
-# Copyright 2004-2022 the Pacemaker project contributors
+# Copyright 2004-2023 the Pacemaker project contributors
 #
 # The version control history for this file may have further details.
 #
 # This source code is licensed under the GNU General Public License version 2
 # or later (GPLv2+) WITHOUT ANY WARRANTY.
 #
 
 include $(top_srcdir)/mk/common.mk
 
 halibdir	= $(CRM_DAEMON_DIR)
 
 halib_PROGRAMS	= pacemaker-attrd
 
 noinst_HEADERS  = pacemaker-attrd.h
 
 pacemaker_attrd_CFLAGS	= $(CFLAGS_HARDENED_EXE)
 pacemaker_attrd_LDFLAGS	= $(LDFLAGS_HARDENED_EXE)
 
 pacemaker_attrd_LDADD	= $(top_builddir)/lib/cluster/libcrmcluster.la	\
 			  $(top_builddir)/lib/pengine/libpe_rules.la	\
 			  $(top_builddir)/lib/common/libcrmcommon.la	\
 			  $(top_builddir)/lib/cib/libcib.la		\
 			  $(top_builddir)/lib/lrmd/liblrmd.la		\
 			  $(CLUSTERLIBS)
 
 pacemaker_attrd_SOURCES	= attrd_alerts.c 	\
 						  attrd_attributes.c \
 						  attrd_cib.c 		\
 						  attrd_corosync.c 	\
 						  attrd_elections.c \
 						  attrd_ipc.c 		\
 						  attrd_messages.c 		\
 						  attrd_sync.c 		\
 						  attrd_utils.c 	\
 						  pacemaker-attrd.c
 
+.PHONY: clean-generic
 clean-generic:
 	rm -f *.log *.debug *.xml *~
 
-if BUILD_LEGACY_LINKS
+.PHONY: install-exec-hook
 install-exec-hook:
+if BUILD_LEGACY_LINKS
 	cd $(DESTDIR)$(CRM_DAEMON_DIR) && rm -f attrd && $(LN_S) pacemaker-attrd attrd
+endif
 
+.PHONY: uninstall-hook
 uninstall-hook:
+if BUILD_LEGACY_LINKS
 	cd $(DESTDIR)$(CRM_DAEMON_DIR) && rm -f attrd
 endif
diff --git a/daemons/based/Makefile.am b/daemons/based/Makefile.am
index 9fafd732d9..1799232e9a 100644
--- a/daemons/based/Makefile.am
+++ b/daemons/based/Makefile.am
@@ -1,49 +1,54 @@
 #
-# Copyright 2004-2021 the Pacemaker project contributors
+# Copyright 2004-2023 the Pacemaker project contributors
 #
 # The version control history for this file may have further details.
 #
 # This source code is licensed under the GNU General Public License version 2
 # or later (GPLv2+) WITHOUT ANY WARRANTY.
 #
 
 include $(top_srcdir)/mk/common.mk
 
 EXTRA_DIST	= cib.pam
 
 halibdir	= $(CRM_DAEMON_DIR)
 
 COMMONLIBS	= $(top_builddir)/lib/common/libcrmcommon.la \
 		$(top_builddir)/lib/cib/libcib.la
 
 halib_PROGRAMS	= pacemaker-based
 
 noinst_HEADERS	= based_transaction.h \
 		  pacemaker-based.h
 
 pacemaker_based_CFLAGS	= $(CFLAGS_HARDENED_EXE)
 pacemaker_based_LDFLAGS	= $(LDFLAGS_HARDENED_EXE)
 
 pacemaker_based_LDADD	= $(top_builddir)/lib/cluster/libcrmcluster.la \
 			  $(COMMONLIBS) $(CLUSTERLIBS)
 
 pacemaker_based_SOURCES	= pacemaker-based.c \
 			  based_callbacks.c \
 			  based_io.c \
 			  based_messages.c \
 			  based_notify.c \
 			  based_operation.c \
 			  based_remote.c \
 			  based_transaction.c
 
+.PHONY: clean-generic
 clean-generic:
 	rm -f *.log *.debug *.xml *~
 
-if BUILD_LEGACY_LINKS
+.PHONY: install-exec-hook
 install-exec-hook:
+if BUILD_LEGACY_LINKS
 	$(MKDIR_P) -- $(DESTDIR)$(CRM_DAEMON_DIR)
 	cd $(DESTDIR)$(CRM_DAEMON_DIR) && rm -f cib && $(LN_S) pacemaker-based cib
+endif
 
+.PHONY: uninstall-hook
 uninstall-hook:
+if BUILD_LEGACY_LINKS
 	cd $(DESTDIR)$(CRM_DAEMON_DIR) && rm -f cib
 endif
diff --git a/daemons/controld/Makefile.am b/daemons/controld/Makefile.am
index 08be1ffbaa..a74a9d7e0c 100644
--- a/daemons/controld/Makefile.am
+++ b/daemons/controld/Makefile.am
@@ -1,87 +1,89 @@
 #
 # Copyright 2018-2023 the Pacemaker project contributors
 #
 # The version control history for this file may have further details.
 #
 # This source code is licensed under the GNU General Public License version 2
 # or later (GPLv2+) WITHOUT ANY WARRANTY.
 #
 
 include $(top_srcdir)/mk/common.mk
 include $(top_srcdir)/mk/man.mk
 
 halibdir	= $(CRM_DAEMON_DIR)
 
 halib_PROGRAMS	= pacemaker-controld
 
 noinst_HEADERS	= controld_alerts.h		\
 		  controld_callbacks.h		\
 		  controld_cib.h		\
 		  controld_fencing.h		\
 		  controld_fsa.h		\
 		  controld_globals.h		\
 		  controld_lrm.h		\
 		  controld_membership.h		\
 		  controld_messages.h		\
 		  controld_metadata.h		\
 		  controld_throttle.h		\
 		  controld_timers.h		\
 		  controld_transition.h		\
 		  controld_utils.h		\
 		  pacemaker-controld.h
 
 pacemaker_controld_CFLAGS	= $(CFLAGS_HARDENED_EXE)
 pacemaker_controld_LDFLAGS	= $(LDFLAGS_HARDENED_EXE)
 
 pacemaker_controld_LDADD = $(top_builddir)/lib/fencing/libstonithd.la		\
 			   $(top_builddir)/lib/pacemaker/libpacemaker.la \
 			   $(top_builddir)/lib/pengine/libpe_rules.la		\
 			   $(top_builddir)/lib/cib/libcib.la			\
 			   $(top_builddir)/lib/cluster/libcrmcluster.la		\
 			   $(top_builddir)/lib/common/libcrmcommon.la		\
 			   $(top_builddir)/lib/services/libcrmservice.la	\
 			   $(top_builddir)/lib/lrmd/liblrmd.la			\
 			   $(CLUSTERLIBS)
 
 pacemaker_controld_SOURCES = pacemaker-controld.c	\
 			     controld_alerts.c		\
 			     controld_attrd.c		\
 			     controld_callbacks.c	\
 			     controld_cib.c		\
 			     controld_control.c		\
 			     controld_corosync.c	\
 			     controld_election.c	\
 			     controld_execd.c		\
 			     controld_execd_state.c	\
 			     controld_fencing.c		\
 			     controld_fsa.c		\
 			     controld_join_client.c	\
 			     controld_join_dc.c		\
 			     controld_matrix.c		\
 			     controld_membership.c	\
 			     controld_messages.c	\
 			     controld_metadata.c	\
 			     controld_remote_ra.c	\
 			     controld_schedulerd.c	\
 			     controld_te_actions.c	\
 			     controld_te_callbacks.c	\
 			     controld_te_events.c	\
 			     controld_te_utils.c	\
 			     controld_throttle.c	\
 			     controld_timers.c		\
 			     controld_transition.c	\
 			     controld_utils.c
 
 if BUILD_XML_HELP
 man7_MANS 	= pacemaker-controld.7
 endif
 
 CLEANFILES = $(man7_MANS)
 
 if BUILD_LEGACY_LINKS
+.PHONY: install-exec-hook
 install-exec-hook:
 	cd $(DESTDIR)$(CRM_DAEMON_DIR) && rm -f crmd && $(LN_S) pacemaker-controld crmd
 
+.PHONY: uninstall-hook
 uninstall-hook:
 	cd $(DESTDIR)$(CRM_DAEMON_DIR) && rm -f crmd
 endif
diff --git a/daemons/execd/Makefile.am b/daemons/execd/Makefile.am
index 466f0df27d..97b5de35ea 100644
--- a/daemons/execd/Makefile.am
+++ b/daemons/execd/Makefile.am
@@ -1,76 +1,78 @@
 #
-# Copyright 2012-2021 the Pacemaker project contributors
+# Copyright 2012-2023 the Pacemaker project contributors
 #
 # The version control history for this file may have further details.
 #
 # This source code is licensed under the GNU Lesser General Public License
 # version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
 #
 
 include $(top_srcdir)/mk/common.mk
 include $(top_srcdir)/mk/man.mk
 
 halibdir		= $(CRM_DAEMON_DIR)
 
 halib_PROGRAMS		= pacemaker-execd cts-exec-helper
 
 EXTRA_DIST	= pacemaker-remoted.8.inc
 
 pacemaker_execd_CFLAGS		= $(CFLAGS_HARDENED_EXE)
 pacemaker_execd_LDFLAGS		= $(LDFLAGS_HARDENED_EXE)
 
 pacemaker_execd_LDADD		= $(top_builddir)/lib/common/libcrmcommon.la \
 				  $(top_builddir)/lib/services/libcrmservice.la	\
 				  $(top_builddir)/lib/fencing/libstonithd.la
 pacemaker_execd_SOURCES		= pacemaker-execd.c execd_commands.c \
 				  execd_alerts.c
 
 if BUILD_REMOTE
 sbin_PROGRAMS		= pacemaker-remoted
 if BUILD_SYSTEMD
 systemdsystemunit_DATA	= pacemaker_remote.service
 else
 initdir			= $(INITDIR)
 init_SCRIPTS		= pacemaker_remote
 endif
 
 pacemaker_remoted_CPPFLAGS	= -DPCMK__COMPILE_REMOTE $(AM_CPPFLAGS)
 
 pacemaker_remoted_CFLAGS	= $(CFLAGS_HARDENED_EXE)
 pacemaker_remoted_LDFLAGS	= $(LDFLAGS_HARDENED_EXE)
 
 pacemaker_remoted_LDADD		= $(pacemaker_execd_LDADD) \
 				  $(top_builddir)/lib/lrmd/liblrmd.la
 pacemaker_remoted_SOURCES	= $(pacemaker_execd_SOURCES) \
 				  remoted_tls.c remoted_pidone.c remoted_proxy.c
 endif
 
 cts_exec_helper_LDADD	= $(top_builddir)/lib/common/libcrmcommon.la    \
 			  $(top_builddir)/lib/lrmd/liblrmd.la		\
 			  $(top_builddir)/lib/cib/libcib.la		\
 			  $(top_builddir)/lib/services/libcrmservice.la \
 			  $(top_builddir)/lib/pengine/libpe_status.la
 cts_exec_helper_SOURCES	= cts-exec-helper.c
 
 noinst_HEADERS  = pacemaker-execd.h
 
 CLEANFILES = $(man8_MANS)
 
 # Always create a symlink for the old pacemaker_remoted name, so that bundle
 # container images using a current Pacemaker will run on cluster nodes running
 # Pacemaker 1 (>=1.1.17).
+.PHONY: install-exec-hook
 install-exec-hook:
 if BUILD_LEGACY_LINKS
 	cd $(DESTDIR)$(CRM_DAEMON_DIR) && rm -f lrmd && $(LN_S) pacemaker-execd lrmd
 endif
 if BUILD_REMOTE
 	cd $(DESTDIR)$(sbindir) && rm -f pacemaker_remoted && $(LN_S) pacemaker-remoted pacemaker_remoted
 endif
 
+.PHONY: uninstall-hook
 uninstall-hook:
 if BUILD_LEGACY_LINKS
 	cd $(DESTDIR)$(CRM_DAEMON_DIR) && rm -f lrmd
 endif
 if BUILD_REMOTE
 	cd $(DESTDIR)$(sbindir) && rm -f pacemaker_remoted
 endif
diff --git a/daemons/fenced/Makefile.am b/daemons/fenced/Makefile.am
index 2ca008861e..988374e5ea 100644
--- a/daemons/fenced/Makefile.am
+++ b/daemons/fenced/Makefile.am
@@ -1,52 +1,54 @@
 #
 # Original Author: Sun Jiang Dong <sunjd@cn.ibm.com>
 # Copyright 2004 International Business Machines
 #
 # with later changes copyright 2004-2023 the Pacemaker project contributors.
 # The version control history for this file may have further details.
 #
 # This source code is licensed under the GNU General Public License version 2
 # or later (GPLv2+) WITHOUT ANY WARRANTY.
 #
 
 include $(top_srcdir)/mk/common.mk
 include $(top_srcdir)/mk/man.mk
 
 halibdir	= $(CRM_DAEMON_DIR)
 
 halib_PROGRAMS	= pacemaker-fenced cts-fence-helper
 
 noinst_HEADERS	= pacemaker-fenced.h
 
 if BUILD_XML_HELP
 man7_MANS	= pacemaker-fenced.7
 endif
 
 cts_fence_helper_SOURCES	= cts-fence-helper.c
 cts_fence_helper_LDADD		= $(top_builddir)/lib/common/libcrmcommon.la	\
 				  $(top_builddir)/lib/fencing/libstonithd.la
 
 pacemaker_fenced_YFLAGS		= -d
 pacemaker_fenced_CFLAGS		= $(CFLAGS_HARDENED_EXE)
 pacemaker_fenced_LDFLAGS	= $(LDFLAGS_HARDENED_EXE)
 pacemaker_fenced_LDADD		= $(top_builddir)/lib/common/libcrmcommon.la		\
 				  $(top_builddir)/lib/cib/libcib.la		\
 				  $(top_builddir)/lib/cluster/libcrmcluster.la		\
 				  $(top_builddir)/lib/fencing/libstonithd.la		\
 				  $(top_builddir)/lib/pengine/libpe_status.la		\
 				  $(top_builddir)/lib/pacemaker/libpacemaker.la	\
 				  $(CLUSTERLIBS)
 pacemaker_fenced_SOURCES	= pacemaker-fenced.c \
 				  fenced_commands.c \
 				  fenced_remote.c \
 				  fenced_history.c
 
 CLEANFILES = $(man7_MANS) $(man8_MANS)
 
 if BUILD_LEGACY_LINKS
+.PHONY: install-exec-hook
 install-exec-hook:
 	cd $(DESTDIR)$(CRM_DAEMON_DIR) && rm -f stonithd && $(LN_S) pacemaker-fenced stonithd
 
+.PHONY: uninstall-hook
 uninstall-hook:
 	cd $(DESTDIR)$(CRM_DAEMON_DIR) && rm -f stonithd
 endif
diff --git a/daemons/schedulerd/Makefile.am b/daemons/schedulerd/Makefile.am
index 57e819bd98..b0aca7eba4 100644
--- a/daemons/schedulerd/Makefile.am
+++ b/daemons/schedulerd/Makefile.am
@@ -1,53 +1,59 @@
 #
-# Copyright 2004-2021 the Pacemaker project contributors
+# Copyright 2004-2023 the Pacemaker project contributors
 #
 # The version control history for this file may have further details.
 #
 # This source code is licensed under the GNU General Public License version 2
 # or later (GPLv2+) WITHOUT ANY WARRANTY.
 #
 
 include $(top_srcdir)/mk/common.mk
 include $(top_srcdir)/mk/man.mk
 
 AM_CPPFLAGS	+= -I$(top_builddir) -I$(top_srcdir)
 
 halibdir	= $(CRM_DAEMON_DIR)
 
 ## binary progs
 
 halib_PROGRAMS	= pacemaker-schedulerd
 
 if BUILD_XML_HELP
 man7_MANS =	pacemaker-schedulerd.7
 endif
 
 ## SOURCES
 
 noinst_HEADERS 	= pacemaker-schedulerd.h
 
 pacemaker_schedulerd_CFLAGS	= $(CFLAGS_HARDENED_EXE)
 pacemaker_schedulerd_LDFLAGS	= $(LDFLAGS_HARDENED_EXE)
 pacemaker_schedulerd_LDADD	= $(top_builddir)/lib/common/libcrmcommon.la \
 							  $(top_builddir)/lib/pengine/libpe_status.la \
 							  $(top_builddir)/lib/pacemaker/libpacemaker.la
 # libcib for get_object_root()
 pacemaker_schedulerd_SOURCES	= pacemaker-schedulerd.c
 pacemaker_schedulerd_SOURCES	+= schedulerd_messages.c
 
+.PHONY: install-exec-local
 install-exec-local:
 	$(INSTALL) -d -m 750 $(DESTDIR)/$(PE_STATE_DIR)
 	-chown $(CRM_DAEMON_USER):$(CRM_DAEMON_GROUP) $(DESTDIR)/$(PE_STATE_DIR)
 
-if BUILD_LEGACY_LINKS
+.PHONY: install-exec-hook
 install-exec-hook:
+if BUILD_LEGACY_LINKS
 	cd $(DESTDIR)$(CRM_DAEMON_DIR) && rm -f pengine && $(LN_S) pacemaker-schedulerd pengine
+endif
 
+.PHONY: uninstall-hook
 uninstall-hook:
+if BUILD_LEGACY_LINKS
 	cd $(DESTDIR)$(CRM_DAEMON_DIR) && rm -f pengine
 endif
 
+.PHONY: uninstall-local
 uninstall-local:
 	-rmdir $(DESTDIR)/$(PE_STATE_DIR)
 
 CLEANFILES = $(man7_MANS)
diff --git a/devel/Makefile.am b/devel/Makefile.am
index b4b211d6a1..f0d01e6cac 100644
--- a/devel/Makefile.am
+++ b/devel/Makefile.am
@@ -1,300 +1,307 @@
 #
 # Copyright 2020-2023 the Pacemaker project contributors
 #
 # The version control history for this file may have further details.
 #
 # This source code is licensed under the GNU General Public License version 2
 # or later (GPLv2+) WITHOUT ANY WARRANTY.
 #
 
 include $(top_srcdir)/mk/common.mk
 include $(top_srcdir)/mk/release.mk
 
 # Coccinelle is a tool that takes special patch-like files (called semantic patches) and
 # applies them throughout a source tree.  This is useful when refactoring, changing APIs,
 # catching dangerous or incorrect code, and other similar tasks.  It's not especially
 # easy to write a semantic patch but most users should only be concerned about running
 # the target and inspecting the results.
 #
 # Documentation (including examples, which are the most useful):
 #     https://coccinelle.gitlabpages.inria.fr/website/docs/
 #
 # Run the "make cocci" target to just output what would be done, or "make cocci-inplace"
 # to apply the changes to the source tree.
 #
 # COCCI_FILES may be set on the command line, if you want to test just a single file
 # while it's under development.  Otherwise, it is a list of all the files that are ready
 # to be run.
 #
 # ref-passed-variables-inited.cocci seems to be returning some false positives around
 # GHashTableIters, so it is disabled for the moment.
 COCCI_FILES ?=	coccinelle/string-any-of.cocci			\
 		coccinelle/string-empty.cocci			\
 		coccinelle/string-null-matches.cocci		\
 		coccinelle/use-func.cocci
 
 
 dist_noinst_SCRIPTS	= coccinelle/test/testrunner.sh
 EXTRA_DIST		= README gdbhelpers $(COCCI_FILES)		\
 			  coccinelle/ref-passed-variables-inited.cocci	\
 			  coccinelle/rename-fn.cocci			\
 			  coccinelle/test/ref-passed-variables-inited.input.c \
 			  coccinelle/test/ref-passed-variables-inited.output
 
 # Any file in this list is allowed to use any of the pcmk__ internal functions.
 # Coccinelle can use any transformation that depends on "internal" to rewrite
 # code to use the internal functions.
 MAY_USE_INTERNAL_FILES = $(shell find .. -path "../lib/*.c" -o -path "../lib/*private.h" -o -path "../tools/*.c" -o -path "../daemons/*.c" -o -path '../include/pcmki/*h' -o -name '*internal.h')
 
 # And then any file in this list is public API, which may not use internal
 # functions.  Thus, only those transformations that do not depend on "internal"
 # may be applied.
 OTHER_FILES = $(shell find ../include -name '*h' -a \! -name '*internal.h' -a \! -path '../include/pcmki/*')
 
+.PHONY: cocci
 cocci:
 	-for cf in $(COCCI_FILES); do \
 		for f in $(MAY_USE_INTERNAL_FILES); do \
 			spatch $(_SPATCH_FLAGS) -D internal --very-quiet --local-includes --preprocess --sp-file $$cf $$f; \
 		done ; \
 		for f in $(OTHER_FILES); do \
 			spatch $(_SPATCH_FLAGS) --very-quiet --local-includes --preprocess --sp-file $$cf $$f; \
 		done ; \
 	done
 
+.PHONY: cocci-inplace
 cocci-inplace:
 	$(MAKE) $(AM_MAKEFLAGS) _SPATCH_FLAGS=--in-place cocci
 
+.PHONY: cocci-test
 cocci-test:
 	for f in coccinelle/test/*.c; do \
 		coccinelle/test/testrunner.sh $$f; \
 	done
 
 #
 # Static analysis
 #
 
 ## clang
 
 # See scan-build(1) for possible checkers (leave empty to use default set)
 CLANG_checkers ?=
 
+.PHONY: clang
 clang:
 	OUT=$$(cd $(top_builddir)					\
 		&& scan-build $(CLANG_checkers:%=-enable-checker %)	\
 		$(MAKE) $(AM_MAKEFLAGS) CFLAGS="-std=c99 $(CFLAGS)"	\
 		clean all 2>&1);					\
 	REPORT=$$(echo "$$OUT"						\
 		| sed -n -e "s/.*'scan-view \(.*\)'.*/\1/p");		\
 	[ -z "$$REPORT" ] && echo "$$OUT" || scan-view "$$REPORT"
 
 ## coverity
 
 # Aggressiveness (low, medium, or high)
 COVLEVEL	?= low
 
 # Generated outputs
 COVERITY_DIR	= $(abs_top_builddir)/coverity-$(TAG)
 COVTAR		= $(abs_top_builddir)/$(PACKAGE)-coverity-$(TAG).tgz
 COVEMACS	= $(abs_top_builddir)/$(TAG).coverity
 COVHTML		= $(COVERITY_DIR)/output/errors
 
 # Coverity outputs are phony so they get rebuilt every invocation
 
 .PHONY: $(COVERITY_DIR)
 $(COVERITY_DIR): coverity-clean
 	$(MAKE) $(AM_MAKEFLAGS) -C $(top_builddir) init core-clean
 	$(AM_V_GEN)cd $(top_builddir)	\
 		&& cov-build --dir "$@" $(MAKE) $(AM_MAKEFLAGS) core
 
 # Public coverity instance
 
 .PHONY: $(COVTAR)
 $(COVTAR): $(COVERITY_DIR)
 	$(AM_V_GEN)tar czf "$@" --transform="s@.*$(TAG)@cov-int@" "$<"
 
 .PHONY: coverity
 coverity: $(COVTAR)
 	@echo "Now go to https://scan.coverity.com/users/sign_in and upload:"
 	@echo "  $(COVTAR)"
 	@echo "then make clean at the top level"
 
 # Licensed coverity instance
 #
 # The prerequisites are a little hacky; rather than actually required, some
 # of them are designed so that things execute in the proper order (which is
 # not the same as GNU make's order-only prerequisites).
 
 .PHONY: coverity-analyze
 coverity-analyze: $(COVERITY_DIR)
 	@echo ""
 	@echo "Analyzing (waiting for coverity license if necessary) ..."
 	cd $(top_builddir) && cov-analyze --dir "$<" --wait-for-license	\
 		--security --aggressiveness-level "$(COVLEVEL)"
 
 .PHONY: $(COVEMACS)
 $(COVEMACS): coverity-analyze
 	$(AM_V_GEN)cd $(top_builddir)	\
 		&& cov-format-errors --dir "$(COVERITY_DIR)" --emacs-style > "$@"
 
 .PHONY: $(COVHTML)
 $(COVHTML): $(COVEMACS)
 	$(AM_V_GEN)cd $(top_builddir)	\
 		&& cov-format-errors --dir "$(COVERITY_DIR)" --html-output "$@"
 
 .PHONY: coverity-corp
 coverity-corp: $(COVHTML)
 	$(MAKE) $(AM_MAKEFLAGS) -C $(top_builddir) core-clean
 	@echo "Done. See:"
 	@echo "  file://$(COVHTML)/index.html"
 	@echo "When no longer needed, make coverity-clean"
 
 # Remove all outputs regardless of tag
 .PHONY: coverity-clean
 coverity-clean:
 	-rm -rf "$(abs_builddir)"/coverity-*			\
 		"$(abs_builddir)"/$(PACKAGE)-coverity-*.tgz	\
 		"$(abs_builddir)"/*.coverity
 
 
 ## cppcheck
 
 # Use CPPCHECK_ARGS to pass extra cppcheck options, e.g.:
 # --enable={warning,style,performance,portability,information,all}
 # --inconclusive --std=posix
 # -DBUILD_PUBLIC_LIBPACEMAKER -DDEFAULT_CONCURRENT_FENCING_TRUE
 CPPCHECK_ARGS ?=
 
 CPPCHECK_DIRS = replace lib daemons tools
 CPPCHECK_OUT = $(abs_top_builddir)/cppcheck.out
 
+.PHONY: cppcheck
 cppcheck:
 	cppcheck $(CPPCHECK_ARGS) -I $(top_srcdir)/include	\
 		--output-file=$(CPPCHECK_OUT)			\
 		--max-configs=30 --inline-suppr -q		\
 		--library=posix --library=gnu --library=gtk	\
 		$(GLIB_CFLAGS) -D__GNUC__ 			\
 		$(foreach dir,$(CPPCHECK_DIRS),$(top_srcdir)/$(dir))
 	@echo "Done: See $(CPPCHECK_OUT)"
 	@echo "When no longer needed, make cppcheck-clean"
 
 .PHONY: cppcheck-clean
 cppcheck-clean:
 	-rm -f "$(CPPCHECK_OUT)"
 
 #
 # Coverage/profiling
 #
 
 COVERAGE_DIR = $(top_builddir)/coverage
 
 # Check coverage of unit tests
 .PHONY: coverage
 coverage: coverage-partial-clean
 	cd $(top_builddir)						\
 	&& $(MAKE) $(AM_MAKEFLAGS)					\
 	&& lcov --no-external --exclude='*_test.c' -c -i -d .		\
 		-o pacemaker_base.info					\
 	&& $(MAKE) $(AM_MAKEFLAGS) check				\
 	&& lcov --no-external --exclude='*_test.c' -c -d .		\
 		-o pacemaker_test.info					\
 	&& lcov -a pacemaker_base.info -a pacemaker_test.info		\
 		-o pacemaker_total.info					\
 	&& lcov --remove pacemaker_total.info -o pacemaker_filtered.info\
 		"$(abs_top_builddir)/tools/*"				\
 		"$(abs_top_builddir)/daemons/*/*"			\
 		"$(abs_top_builddir)/replace/*"				\
 		"$(abs_top_builddir)/lib/gnu/*"
 	genhtml $(top_builddir)/pacemaker_filtered.info -o $(COVERAGE_DIR) -s -t "Pacemaker code coverage"
 
 # Check coverage of CLI regression tests
 .PHONY: coverage-cts
 coverage-cts: coverage-partial-clean
 	cd $(top_builddir)						\
 	&& $(MAKE) $(AM_MAKEFLAGS)					\
 	&& lcov --no-external -c -i -d tools -o pacemaker_base.info	\
 	&& cts/cts-cli							\
 	&& lcov --no-external -c -d tools -o pacemaker_test.info	\
 	&& lcov -a pacemaker_base.info -a pacemaker_test.info		\
 		-o pacemaker_total.info
 	genhtml $(top_builddir)/pacemaker_total.info -o $(COVERAGE_DIR) -s
 
 # Remove coverage-related files that aren't needed across runs
 .PHONY: coverage-partial-clean
 coverage-partial-clean:
 	-rm -f $(top_builddir)/pacemaker_*.info
 	-rm -rf $(COVERAGE_DIR)
 	-find $(top_builddir) -name "*.gcda" -exec rm -f \{\} \;
 
 # This target removes all coverage-related files.  It is only to be run when
 # done with coverage analysis and you are ready to go back to normal development,
 # starting with re-running ./configure.  It is not to be run in between
 # "make coverage" runs.
 #
 # In particular, the *.gcno files are generated when the source is built.
 # Removing those files will break "make coverage" until the whole source tree
 # has been built and the *.gcno files generated again.
 .PHONY: coverage-clean
 coverage-clean: coverage-partial-clean
 	-find $(top_builddir) -name "*.gcno" -exec rm -f \{\} \;
 
 #
 # indent cannot cope with all our exceptions and needs heavy manual editing
 #
 
 # indent target: Limit indent to these directories
 INDENT_DIRS	?= .
 
 # indent target: Extra options to pass to indent
 INDENT_OPTS	?=
 
 INDENT_IGNORE_PATHS	= daemons/controld/controld_fsa.h	\
 			  lib/gnu/*
 INDENT_PACEMAKER_STYLE	= --blank-lines-after-declarations		\
 			  --blank-lines-after-procedures		\
 			  --braces-after-func-def-line			\
 			  --braces-on-if-line				\
 			  --braces-on-struct-decl-line			\
 			  --break-before-boolean-operator		\
 			  --case-brace-indentation4			\
 			  --case-indentation4				\
 			  --comment-indentation0			\
 			  --continuation-indentation4			\
 			  --continue-at-parentheses			\
 			  --cuddle-do-while				\
 			  --cuddle-else					\
 			  --declaration-comment-column0			\
 			  --declaration-indentation1			\
 			  --else-endif-column0				\
 			  --honour-newlines				\
 			  --indent-label0				\
 			  --indent-level4				\
 			  --line-comments-indentation0			\
 			  --line-length80				\
 			  --no-blank-lines-after-commas			\
 			  --no-comment-delimiters-on-blank-lines	\
 			  --no-space-after-function-call-names		\
 			  --no-space-after-parentheses			\
 			  --no-tabs					\
 			  --preprocessor-indentation2			\
 			  --procnames-start-lines			\
 			  --space-after-cast				\
 			  --start-left-side-of-comments			\
 			  --swallow-optional-blank-lines		\
 			  --tab-size8
 
+.PHONY: indent
 indent:
 	VERSION_CONTROL=none					\
 		find $(INDENT_DIRS) -type f -name "*.[ch]"	\
 		$(INDENT_IGNORE_PATHS:%= ! -path '%')		\
 		-exec indent $(INDENT_PACEMAKER_STYLE) $(INDENT_OPTS) \{\} \;
 
 #
 # Scratch file for ad-hoc testing
 #
 
 EXTRA_PROGRAMS		= scratch
 nodist_scratch_SOURCES	= scratch.c
 scratch_LDADD		= $(top_builddir)/lib/common/libcrmcommon.la
 
+.PHONY: clean-local
 clean-local: coverage-clean coverity-clean cppcheck-clean
 	-rm -f $(EXTRA_PROGRAMS)
diff --git a/doc/Makefile.am b/doc/Makefile.am
index 14001457ad..324aa892ae 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -1,152 +1,167 @@
 #
-# Copyright 2003-2021 the Pacemaker project contributors
+# Copyright 2003-2023 the Pacemaker project contributors
 #
 # The version control history for this file may have further details.
 #
 # This source code is licensed under the GNU General Public License version 2
 # or later (GPLv2+) WITHOUT ANY WARRANTY.
 #
 include $(top_srcdir)/mk/common.mk
 
 # Define release-related variables
 include $(top_srcdir)/mk/release.mk
 
 # What formats to use for book uploads (i.e. "make www";
 # use BOOK_FORMATS in sphinx subdirectory to change local builds)
 BOOK_FORMATS		?= html singlehtml pdf epub
 
 # SNMP MIB
 mibdir		= $(datadir)/snmp/mibs
 dist_mib_DATA	= PCMK-MIB.txt
 
 # Deprecated plaintext documents (dynamically converted to HTML)
 DEPRECATED_ORIGINAL	= crm_fencing.txt
 DEPRECATED_GENERATED	=
 if BUILD_ASCIIDOC
 DEPRECATED_GENERATED	+= $(DEPRECATED_ORIGINAL:%.txt=%.html)
 endif
 DEPRECATED_ALL		= $(DEPRECATED_ORIGINAL) $(DEPRECATED_GENERATED)
 
 doc_DATA		= $(DEPRECATED_ALL)
 noinst_SCRIPTS		= abi-check
 
 SUBDIRS		= sphinx
 
 EXTRA_DIST	= $(DEPRECATED_ORIGINAL)
 
 # toplevel rsync destination for www targets (without trailing slash)
 RSYNC_DEST      ?= root@www.clusterlabs.org:/var/www/html
 
 # recursive, preserve symlinks/permissions/times, verbose, compress,
 # don't cross filesystems, sparse, show progress
 RSYNC_OPTS      = -rlptvzxS --progress
 
 if IS_ASCIIDOC
 ASCIIDOC_HTML_ARGS	= --unsafe --backend=xhtml11
 ASCIIDOC_DBOOK_ARGS	= -b docbook -d book
 else
 ASCIIDOC_HTML_ARGS	= --backend=html5
 ASCIIDOC_DBOOK_ARGS	= -b docbook45 -d book
 endif
 
 %.html: %.txt
 	$(AM_V_GEN)$(ASCIIDOC_CONV) $(ASCIIDOC_HTML_ARGS) --out-file=$@ $< $(PCMK_quiet)
 
 # For Makefile debugging
 .PHONY: vars
 vars:
 	@echo DEPRECATED_ORIGINAL=\'$(DEPRECATED_ORIGINAL)\'
 	@echo DEPRECATED_GENERATED=\'$(DEPRECATED_GENERATED)\'
 	@echo LAST_RELEASE=\'$(LAST_RELEASE)\'
 	@echo TAG=\'$(TAG)\'
 
 
 .PHONY: deprecated-upload
 deprecated-upload: $(DEPRECATED_ALL)
 	rsync $(RSYNC_OPTS) $(DEPRECATED_ALL) "$(RSYNC_DEST)/$(PACKAGE)/doc/"
 
 .PHONY: deprecated-clean
 deprecated-clean:
 	-rm -f $(DEPRECATED_GENERATED)
 
 
 # Annotated source code as HTML
 
 # Cleaning first ensures we don't index unrelated stuff like RPM sources
+.PHONY: global
 global:
 	$(MAKE) $(AM_MAKEFLAGS) -C .. clean-generic
 	$(MAKE) $(AM_MAKEFLAGS) -C ../rpm rpm-clean
 	cd .. && gtags -q && htags -sanhIT doc
 
+.PHONY: global-upload
 global-upload: global
 	rsync $(RSYNC_OPTS) HTML/ "$(RSYNC_DEST)/$(PACKAGE)/global/$(TAG)/"
 
+.PHONY: global-clean
 global-clean:
 	-rm -rf HTML
 
 
 # Man pages as HTML
 
 %.8.html: %.8
 	groff -mandoc `man -w ./$<` -T html > $@
 
 %.7.html: %.7
 	groff -mandoc `man -w ./$<` -T html > $@
 
+.PHONY: manhtml
 manhtml:
 	$(MAKE) $(AM_MAKEFLAGS) -C .. all
 	find .. -name "[a-z]*.[78]" -exec $(MAKE) $(AM_MAKEFLAGS) \{\}.html \;
 
+.PHONY: manhtml-upload
 manhtml-upload: manhtml
 	find .. -name "[a-z]*.[78].html" -exec \
 		rsync $(RSYNC_OPTS) \{\} "$(RSYNC_DEST)/$(PACKAGE)/man/" \;
 
+.PHONY: manhtml-clean
 manhtml-clean:
 	-find .. -name "[a-z]*.[78].html" -exec rm \{\} \;
 
 
 # API documentation as HTML
 
+.PHONY: doxygen
 doxygen: Doxyfile
 	doxygen Doxyfile
 
+.PHONY: doxygen-upload
 doxygen-upload: doxygen
 	rsync $(RSYNC_OPTS) api/html/ "$(RSYNC_DEST)/$(PACKAGE)/doxygen/$(TAG)/"
 
+.PHONY: doxygen-clean
 doxygen-clean:
 	-rm -rf api
 
 
 # ABI compatibility report as HTML
 
+.PHONY: abi
 abi: abi-check
 	./abi-check $(PACKAGE) $(LAST_RELEASE) $(TAG)
 
+.PHONY: abi-www
 abi-www:
 	export RSYNC_DEST=$(RSYNC_DEST); ./abi-check -u $(PACKAGE) $(LAST_RELEASE) $(TAG)
 
+.PHONY: abi-clean
 abi-clean:
 	-rm -rf abi_dumps compat_reports
 
 
 # The main documentation books (which are actually in the sphinx subdirectory)
+.PHONY: books-upload
 books-upload:
 	$(MAKE) $(AM_MAKEFLAGS)	-C sphinx clean
 	$(MAKE) $(AM_MAKEFLAGS)	-C sphinx	\
 		RSYNC_DEST="$(RSYNC_DEST)"	\
 		BOOK_FORMATS="$(BOOK_FORMATS)"	\
 		books-upload
 
 
 # All online documentation (except ABI compatibility, which is run separately)
 .PHONY: www
 www: clean-local deprecated-upload manhtml-upload global-upload doxygen-upload books-upload
 
+.PHONY: clean-local
 clean-local: global-clean manhtml-clean doxygen-clean abi-clean deprecated-clean
 
 # "make check" will cause "make all" to be run, which means docs will get built
 # as a part of running tests if they haven't already.  That seems unnecessary, so
 # override the default check-recursive rule with this one that just returns.  If
 # we ever need to add tests to this directory, this rule will have to come out.
+.PHONY: check-recursive
 check-recursive:
 	@true
diff --git a/doc/sphinx/Makefile.am b/doc/sphinx/Makefile.am
index e144172a09..3879683a17 100644
--- a/doc/sphinx/Makefile.am
+++ b/doc/sphinx/Makefile.am
@@ -1,200 +1,210 @@
 #
 # Copyright 2003-2023 the Pacemaker project contributors
 #
 # The version control history for this file may have further details.
 #
 # This source code is licensed under the GNU General Public License version 2
 # or later (GPLv2+) WITHOUT ANY WARRANTY.
 #
 include $(top_srcdir)/mk/common.mk
 
 # Define release-related variables
 include $(top_srcdir)/mk/release.mk
 
 # Things you might want to override on the command line
 
 # Books to generate
 BOOKS		?= Clusters_from_Scratch	\
 		   Pacemaker_Administration	\
 		   Pacemaker_Development	\
 		   Pacemaker_Explained		\
 		   Pacemaker_Python_API 	\
 		   Pacemaker_Remote
 
 # Output formats to generate. Possible values:
 #  html       (multiple HTML files)
 #  dirhtml    (HTML files named index.html in multiple directories)
 #  singlehtml (a single large HTML file)
 #  text
 #  pdf
 #  epub
 #  latex
 #  linkcheck  (not actually a format; check validity of external links)
 #
 # The results will end up in <book>/_build/<format>
 BOOK_FORMATS	?= singlehtml
 
 # Set to "a4paper" or "letterpaper" if building latex format
 PAPER          ?= letterpaper
 
 # Additional options for sphinx-build
 SPHINXFLAGS	?=
 
 # toplevel rsync destination for www targets (without trailing slash)
 RSYNC_DEST	?= root@www.clusterlabs.org:/var/www/html
 
 # End of useful overrides
 
 
 # Example scheduler transition graphs
 # @TODO The original CIB XML for these is long lost. Ideally, we would recreate
 # something similar and keep those here instead of the DOTs (or use a couple of
 # scheduler regression test inputs instead), then regenerate the SVG
 # equivalents using crm_simulate and dot when making a release.
 DOTS =	$(wildcard shared/images/*.dot)
 
 # Vector sources for generated PNGs (including SVG equivalents of DOTS, created
 # manually using dot)
 SVGS =	$(wildcard shared/images/pcmk-*.svg) $(DOTS:%.dot=%.svg)
 
 # PNG images generated from SVGS
 #
 # These will not be accessible in a VPATH build, which will generate warnings
 # when building the documentation, but the make will still succeed. It is
 # nontrivial to get them working for VPATH builds and not worth the effort.
 PNGS_GENERATED	= $(SVGS:%.svg=%.png)
 
 # Original PNG image sources
 PNGS_Clusters_from_Scratch = $(wildcard Clusters_from_Scratch/images/*.png)
 PNGS_Pacemaker_Explained   = $(wildcard Pacemaker_Explained/images/*.png)
 PNGS_Pacemaker_Remote      = $(wildcard Pacemaker_Remote/images/*.png)
 
 STATIC_FILES	= $(wildcard _static/*.css)
 
 EXTRA_DIST	= $(wildcard */*.rst) $(DOTS) $(SVGS)		\
 		  $(PNGS_Clusters_from_Scratch)			\
 		  $(PNGS_Pacemaker_Explained)			\
 		  $(PNGS_Pacemaker_Remote)			\
 		  $(wildcard Pacemaker_Python_API/_templates/*rst) \
 		  $(STATIC_FILES)				\
 		  conf.py.in
 
 # recursive, preserve symlinks/permissions/times, verbose, compress,
 # don't cross filesystems, sparse, show progress
 RSYNC_OPTS      = -rlptvzxS --progress
 
 PACKAGE_SERIES=$(shell echo "$VERSION" | awk -F. '{ print $1"."$2 }'`)
 
 BOOK_RSYNC_DEST	= $(RSYNC_DEST)/$(PACKAGE)/doc/$(PACKAGE_SERIES)
 
 BOOK		= none
 
 DEPS_intro			= shared/pacemaker-intro.rst $(PNGS_GENERATED)
 
 DEPS_Clusters_from_Scratch	= $(DEPS_intro) $(PNGS_Clusters_from_Scratch)
 DEPS_Pacemaker_Administration	= $(DEPS_intro)
 DEPS_Pacemaker_Development	=
 DEPS_Pacemaker_Explained	= $(DEPS_intro) $(PNGS_Pacemaker_Explained)
 DEPS_Pacemaker_Python_API 	= ../../python
 DEPS_Pacemaker_Remote		= $(PNGS_Pacemaker_Remote)
 
 if BUILD_SPHINX_DOCS
 
 INKSCAPE_CMD	= $(INKSCAPE) --export-dpi=90 -C
 
 # Pattern rule to generate PNGs from SVGs
 # (--export-png works with Inkscape <1.0, --export-filename with >=1.0;
 # create the destination directory in case this is a VPATH build)
 %.png: %.svg
 	$(AM_V_at)-$(MKDIR_P) "$(shell dirname "$@")"
 	$(AM_V_GEN) {							\
 		$(INKSCAPE_CMD) --export-png="$@" "$<" 2>/dev/null	\
 		|| $(INKSCAPE_CMD) --export-filename="$@" "$<";		\
 	} $(PCMK_quiet)
 
 # Create a book's Sphinx configuration.
 # Create the book directory in case this is a VPATH build.
 $(BOOKS:%=%/conf.py): conf.py.in
 	$(AM_V_at)-$(MKDIR_P) "$(@:%/conf.py=%)"
 	$(AM_V_GEN)sed							\
 		-e 's/%VERSION%/$(VERSION)/g'				\
 		-e 's/%BOOK_ID%/$(@:%/conf.py=%)/g'			\
 		-e 's/%BOOK_TITLE%/$(subst _, ,$(@:%/conf.py=%))/g'	\
 		-e 's#%SRC_DIR%#$(abs_srcdir)#g'			\
 		-e 's#%ABS_TOP_SRCDIR%#$(abs_top_srcdir)#g'			\
 		$(<) > "$@"
 
 $(BOOK)/_build: $(STATIC_FILES) $(BOOK)/conf.py $(DEPS_$(BOOK)) $(wildcard $(srcdir)/$(BOOK)/*.rst)
 	@echo 'Building "$(subst _, ,$(BOOK))" because of $?' $(PCMK_quiet)
 	$(AM_V_at)rm -rf "$@"
 	$(AM_V_BOOK)for format in $(BOOK_FORMATS); do			\
 		echo -e "\n * Building $$format" $(PCMK_quiet);		\
 		doctrees="doctrees";					\
 		real_format="$$format";					\
 		case "$$format" in					\
 			pdf) real_format="latex" ;;			\
 			gettext) doctrees="gettext-doctrees" ;;		\
 		esac;							\
 		$(SPHINX) -b "$$real_format" -d "$@/$$doctrees"		\
 			-c "$(builddir)/$(BOOK)"			\
 			-D latex_elements.papersize=$(PAPER)		\
 			$(SPHINXFLAGS)					\
 			"$(srcdir)/$(BOOK)" "$@/$$format"		\
 			$(PCMK_quiet);					\
 		if [ "$$format" = "pdf" ]; then				\
 			$(MAKE) $(AM_MAKEFLAGS)	-C "$@/$$format"	\
 				all-pdf;				\
 		fi;							\
 	done
 endif
 
 build-$(PACKAGE_SERIES).txt: all
 	$(AM_V_GEN)echo "Generated on `date --utc` from version $(TAG)" > "$@"
 
 .PHONY: books-upload
 books-upload: all build-$(PACKAGE_SERIES).txt
 if BUILD_SPHINX_DOCS
 	@echo "Uploading $(PACKAGE_SERIES) documentation set"
 	@for book in $(BOOKS); do 					\
 		echo " * $$book";					\
 		rsync $(RSYNC_OPTS) $(BOOK_FORMATS:%=$$book/_build/%)	\
 			"$(BOOK_RSYNC_DEST)/$$book/";			\
 	done
 	@rsync $(RSYNC_OPTS) "$(builddir)/build-$(PACKAGE_SERIES).txt"	\
 		"$(RSYNC_DEST)/$(PACKAGE)/doc"
+endif
 
+.PHONY: all-local
 all-local:
+if BUILD_SPHINX_DOCS
 	@for book in $(BOOKS); do					\
 		$(MAKE) $(AM_MAKEFLAGS) BOOK=$$book			\
 			PAPER="$(PAPER)" SPHINXFLAGS="$(SPHINXFLAGS)"	\
 			BOOK_FORMATS="$(BOOK_FORMATS)" $$book/_build;	\
 	done
+endif
 
+.PHONY: install-data-local
 install-data-local: all-local
+if BUILD_SPHINX_DOCS
 	$(AM_V_at)for book in $(BOOKS); do				\
 		for format in $(BOOK_FORMATS); do			\
 			formatdir="$$book/_build/$$format";		\
 			for f in `find "$$formatdir" -print`; do	\
 				dname="`echo $$f | sed s:_build/::`";	\
 				dloc="$(DESTDIR)/$(docdir)/$$dname";	\
 				if [ -d "$$f" ]; then			\
 					$(INSTALL) -d -m 755 "$$dloc";	\
 				else					\
 					$(INSTALL_DATA) "$$f" "$$dloc";	\
 				fi					\
 			done;						\
 		done;							\
 	done
+endif
 
+.PHONY: uninstall-local
 uninstall-local:
+if BUILD_SPHINX_DOCS
 	$(AM_V_at)for book in $(BOOKS); do		\
 		rm -rf "$(DESTDIR)/$(docdir)/$$book";	\
 	done
 endif
 
+.PHONY: clean-local
 clean-local:
 	$(AM_V_at)-rm -rf				\
 		$(BOOKS:%="$(builddir)/%/_build")	\
 		$(BOOKS:%="$(builddir)/%/conf.py")	\
 		$(BOOKS:%="$(builddir)/%/generated")	\
 		$(PNGS_GENERATED)
diff --git a/etc/Makefile.am b/etc/Makefile.am
index b810f82db9..8773f14c92 100644
--- a/etc/Makefile.am
+++ b/etc/Makefile.am
@@ -1,38 +1,40 @@
 #
-# Copyright 2021-2022 the Pacemaker project contributors
+# Copyright 2021-2023 the Pacemaker project contributors
 #
 # The version control history for this file may have further details.
 #
 # This source code is licensed under the GNU General Public License version 2
 # or later (GPLv2+) WITHOUT ANY WARRANTY.
 #
 
 MAINTAINERCLEANFILES    = Makefile.in
 
 configdir		= @CONFIGDIR@
 CONFIGS			= crm_mon pacemaker
 
 if !BUILD_SYSTEMD
 initdir			= $(INITDIR)
 init_SCRIPTS		= init.d/pacemaker
 endif
 
 logrotatedir		= $(sysconfdir)/logrotate.d
 logrotate_DATA		= logrotate.d/pacemaker
 
 EXTRA_DIST		= $(foreach f,$(CONFIGS),sysconfig/$(f))
 
 # Don't overwrite user's existing config files
+.PHONY: install-data-local
 install-data-local:
 	$(AM_V_at)$(MKDIR_P) $(DESTDIR)$(configdir)
 	$(AM_V_at)for f in $(CONFIGS); do				\
 		dest="$(DESTDIR)$(configdir)/$$f";			\
 		[ -e "$$dest" ] && dest="$$dest.new";			\
 		$(INSTALL_DATA) "$(srcdir)/sysconfig/$$f" "$$dest";	\
 	done
 
+.PHONY: uninstall-local
 uninstall-local:
 	$(AM_V_at)for f in $(CONFIGS); do		\
 		dest="$(DESTDIR)$(configdir)/$$f";	\
 		rm -f "$$dest" "$$dest.new";		\
 	done
diff --git a/include/Makefile.am b/include/Makefile.am
index ffc25068ae..76eee90b95 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -1,40 +1,41 @@
 #
 # Copyright 2003-2023 the Pacemaker project contributors
 #
 # The version control history for this file may have further details.
 #
 # This source code is licensed under the GNU General Public License version 2
 # or later (GPLv2+) WITHOUT ANY WARRANTY.
 #
 
 MAINTAINERCLEANFILES    = Makefile.in config.h.in
 
 noinst_HEADERS	        = config.h 			\
 			  crm_internal.h		\
 			  doxygen.h			\
 			  pacemaker.h \
 			  pacemaker-internal.h		\
 			  portability.h                 \
 			  gettext.h
 pkginclude_HEADERS	= crm_config.h
 
 SUBDIRS                 =  crm pcmki
 
 # gettext.h is supplied by the gettext project
 
 GETTEXT_H		?= $(datadir)/gettext/gettext.h
 
+.PHONY: update-gettext
 update-gettext:
 	@if [ ! -e "$(GETTEXT_H)" ]; then				\
 		echo "$(GETTEXT_H) not found";				\
 	else								\
 		cp "$(GETTEXT_H)" gettext.h;				\
 		"$(GIT)" diff --quiet gettext.h 2>/dev/null;		\
 		if [ $$? -eq 0 ]; then					\
 			echo "No update needed";			\
 		else							\
 			"$(GIT)" add gettext.h;				\
 			echo 'Review changes then run:';		\
 			echo 'git commit -m "Low: NLS: update gettext.h from upstream"'; \
 		fi							\
 	fi
diff --git a/lib/cib/Makefile.am b/lib/cib/Makefile.am
index 721fca1ffa..98c1729822 100644
--- a/lib/cib/Makefile.am
+++ b/lib/cib/Makefile.am
@@ -1,28 +1,29 @@
 #
-# Copyright 2004-2018 the Pacemaker project contributors
+# Copyright 2004-2023 the Pacemaker project contributors
 #
 # The version control history for this file may have further details.
 #
 # This source code is licensed under the GNU General Public License version 2
 # or later (GPLv2+) WITHOUT ANY WARRANTY.
 #
 include $(top_srcdir)/mk/common.mk
 
 ## libraries
 lib_LTLIBRARIES		= libcib.la
 
 ## SOURCES
 libcib_la_SOURCES	= cib_ops.c cib_utils.c cib_client.c cib_native.c cib_attrs.c
 libcib_la_SOURCES	+= cib_file.c cib_remote.c
 
 libcib_la_LDFLAGS	= -version-info 31:0:4
 libcib_la_CPPFLAGS	= -I$(top_srcdir) $(AM_CPPFLAGS)
 
 libcib_la_CFLAGS	= $(CFLAGS_HARDENED_LIB)
 libcib_la_LDFLAGS	+= $(LDFLAGS_HARDENED_LIB)
 
 libcib_la_LIBADD	= $(top_builddir)/lib/pengine/libpe_rules.la \
 			  $(top_builddir)/lib/common/libcrmcommon.la
 
+.PHONY: clean-generic
 clean-generic:
 	rm -f *.log *.debug *.xml *~
diff --git a/lib/cluster/Makefile.am b/lib/cluster/Makefile.am
index 9225f29152..25fe6604ea 100644
--- a/lib/cluster/Makefile.am
+++ b/lib/cluster/Makefile.am
@@ -1,29 +1,30 @@
 #
-# Copyright 2004-2018 the Pacemaker project contributors
+# Copyright 2004-2023 the Pacemaker project contributors
 #
 # The version control history for this file may have further details.
 #
 # This source code is licensed under the GNU General Public License version 2
 # or later (GPLv2+) WITHOUT ANY WARRANTY.
 #
 include $(top_srcdir)/mk/common.mk
 
 noinst_HEADERS	= crmcluster_private.h
 
 ## libraries
 lib_LTLIBRARIES	= libcrmcluster.la 
 
 libcrmcluster_la_LDFLAGS = -version-info 30:0:1
 
 libcrmcluster_la_CFLAGS  = $(CFLAGS_HARDENED_LIB)
 libcrmcluster_la_LDFLAGS += $(LDFLAGS_HARDENED_LIB)
 
 libcrmcluster_la_LIBADD  = $(top_builddir)/lib/common/libcrmcommon.la $(top_builddir)/lib/fencing/libstonithd.la $(CLUSTERLIBS)
 
 libcrmcluster_la_SOURCES = election.c cluster.c membership.c
 if BUILD_CS_SUPPORT
 libcrmcluster_la_SOURCES += cpg.c corosync.c
 endif
 
+.PHONY: clean-generic
 clean-generic:
 	rm -f *.log *.debug *.xml *~
diff --git a/lib/common/Makefile.am b/lib/common/Makefile.am
index 03a2f66a5c..d07b05f545 100644
--- a/lib/common/Makefile.am
+++ b/lib/common/Makefile.am
@@ -1,125 +1,126 @@
 #
 # Copyright 2004-2023 the Pacemaker project contributors
 #
 # The version control history for this file may have further details.
 #
 # This source code is licensed under the GNU General Public License version 2
 # or later (GPLv2+) WITHOUT ANY WARRANTY.
 #
 include $(top_srcdir)/mk/common.mk
 
 AM_CPPFLAGS		+= -I$(top_builddir)/lib/gnu -I$(top_srcdir)/lib/gnu
 
 ## libraries
 lib_LTLIBRARIES	= libcrmcommon.la
 check_LTLIBRARIES = libcrmcommon_test.la
 
 # Disable -Wcast-qual if used, because we do some hacky casting,
 # and because libxml2 has some signatures that should be const but aren't
 # for backward compatibility reasons.
 
 # s390 needs -fPIC 
 # s390-suse-linux/bin/ld: .libs/ipc.o: relocation R_390_PC32DBL against `__stack_chk_fail@@GLIBC_2.4' can not be used when making a shared object; recompile with -fPIC
 
 CFLAGS		= $(CFLAGS_COPY:-Wcast-qual=) -fPIC
 
 # Without "." here, check-recursive will run through the subdirectories first
 # and then run "make check" here.  This will fail, because there's things in
 # the subdirectories that need check_LTLIBRARIES built first.  Adding "." here
 # changes the order so the subdirectories are processed afterwards.
 SUBDIRS = . tests
 
 noinst_HEADERS		= crmcommon_private.h mock_private.h
 
 libcrmcommon_la_LDFLAGS	= -version-info 45:0:11
 
 libcrmcommon_la_CFLAGS	= $(CFLAGS_HARDENED_LIB)
 libcrmcommon_la_LDFLAGS	+= $(LDFLAGS_HARDENED_LIB)
 
 libcrmcommon_la_LIBADD	= @LIBADD_DL@ $(top_builddir)/lib/gnu/libgnu.la
 
 # If configured with --with-profiling or --with-coverage, BUILD_PROFILING will
 # be set and -fno-builtin will be added to the CFLAGS.  However, libcrmcommon
 # uses the fabs() function which is normally supplied by gcc as one of its
 # builtins.  Therefore we need to explicitly link against libm here or the
 # tests won't link.
 if BUILD_PROFILING
 libcrmcommon_la_LIBADD	+= -lm
 endif
 
 # Use += rather than backlashed continuation lines for parsing by bumplibs
 libcrmcommon_la_SOURCES	=
 libcrmcommon_la_SOURCES	+= acl.c
 libcrmcommon_la_SOURCES	+= actions.c
 libcrmcommon_la_SOURCES	+= agents.c
 libcrmcommon_la_SOURCES	+= alerts.c
 libcrmcommon_la_SOURCES	+= attrs.c
 libcrmcommon_la_SOURCES	+= cib.c
 if BUILD_CIBSECRETS
 libcrmcommon_la_SOURCES	+= cib_secrets.c
 endif
 libcrmcommon_la_SOURCES	+= cmdline.c
 libcrmcommon_la_SOURCES	+= digest.c
 libcrmcommon_la_SOURCES	+= health.c
 libcrmcommon_la_SOURCES	+= io.c
 libcrmcommon_la_SOURCES	+= ipc_attrd.c
 libcrmcommon_la_SOURCES	+= ipc_client.c
 libcrmcommon_la_SOURCES	+= ipc_common.c
 libcrmcommon_la_SOURCES	+= ipc_controld.c
 libcrmcommon_la_SOURCES	+= ipc_pacemakerd.c
 libcrmcommon_la_SOURCES 	+= ipc_schedulerd.c
 libcrmcommon_la_SOURCES	+= ipc_server.c
 libcrmcommon_la_SOURCES	+= iso8601.c
 libcrmcommon_la_SOURCES	+= lists.c
 libcrmcommon_la_SOURCES	+= logging.c
 libcrmcommon_la_SOURCES	+= mainloop.c
 libcrmcommon_la_SOURCES	+= messages.c
 libcrmcommon_la_SOURCES	+= nodes.c
 libcrmcommon_la_SOURCES	+= nvpair.c
 libcrmcommon_la_SOURCES	+= options.c
 libcrmcommon_la_SOURCES	+= output.c
 libcrmcommon_la_SOURCES	+= output_html.c
 libcrmcommon_la_SOURCES	+= output_log.c
 libcrmcommon_la_SOURCES	+= output_none.c
 libcrmcommon_la_SOURCES	+= output_text.c
 libcrmcommon_la_SOURCES	+= output_xml.c
 libcrmcommon_la_SOURCES	+= patchset.c
 libcrmcommon_la_SOURCES	+= patchset_display.c
 libcrmcommon_la_SOURCES	+= pid.c
 libcrmcommon_la_SOURCES	+= procfs.c
 libcrmcommon_la_SOURCES	+= remote.c
 libcrmcommon_la_SOURCES	+= results.c
 libcrmcommon_la_SOURCES	+= schemas.c
 libcrmcommon_la_SOURCES	+= scores.c
 libcrmcommon_la_SOURCES	+= strings.c
 libcrmcommon_la_SOURCES	+= utils.c
 libcrmcommon_la_SOURCES	+= watchdog.c
 libcrmcommon_la_SOURCES	+= xml.c
 libcrmcommon_la_SOURCES	+= xml_attr.c
 libcrmcommon_la_SOURCES	+= xml_display.c
 libcrmcommon_la_SOURCES	+= xpath.c
 
 #
 # libcrmcommon_test is used only with unit tests, so we can mock system calls.
 # See mock.c for details.
 #
 
 include $(top_srcdir)/mk/tap.mk
 
 libcrmcommon_test_la_SOURCES	= $(libcrmcommon_la_SOURCES)
 libcrmcommon_test_la_SOURCES	+= mock.c
 libcrmcommon_test_la_LDFLAGS	= $(libcrmcommon_la_LDFLAGS) -rpath $(libdir) $(LDFLAGS_WRAP)
 # If GCC emits a builtin function in place of something we've mocked up, that will
 # get used instead of the mocked version which leads to unexpected test results.  So
 # disable all builtins.  Older versions of GCC (at least, on RHEL7) will still emit
 # replacement code for strdup (and possibly other functions) unless -fno-inline is
 # also added.
 libcrmcommon_test_la_CFLAGS	= $(libcrmcommon_la_CFLAGS) -DPCMK__UNIT_TESTING -fno-builtin -fno-inline
 # If -fno-builtin is used, -lm also needs to be added.  See the comment at
 # BUILD_PROFILING above.
 libcrmcommon_test_la_LIBADD	= $(libcrmcommon_la_LIBADD) -lcmocka -lm
 
 nodist_libcrmcommon_test_la_SOURCES = $(nodist_libcrmcommon_la_SOURCES)
 
+.PHONY: clean-generic
 clean-generic:
 	rm -f *.log *.debug *.xml *~
diff --git a/lib/pengine/Makefile.am b/lib/pengine/Makefile.am
index 805772b87a..3a450a5923 100644
--- a/lib/pengine/Makefile.am
+++ b/lib/pengine/Makefile.am
@@ -1,81 +1,82 @@
 #
 # Copyright 2004-2023 the Pacemaker project contributors
 #
 # The version control history for this file may have further details.
 #
 # This source code is licensed under the GNU General Public License version 2
 # or later (GPLv2+) WITHOUT ANY WARRANTY.
 #
 include $(top_srcdir)/mk/common.mk
 
 # Without "." here, check-recursive will run through the subdirectories first
 # and then run "make check" here.  This will fail, because there's things in
 # the subdirectories that need check_LTLIBRARIES built first.  Adding "." here
 # changes the order so the subdirectories are processed afterwards.
 SUBDIRS = . tests
 
 ## libraries
 lib_LTLIBRARIES		= libpe_rules.la libpe_status.la
 check_LTLIBRARIES 	= libpe_rules_test.la libpe_status_test.la
 
 ## SOURCES
 noinst_HEADERS		= pe_status_private.h
 
 libpe_rules_la_LDFLAGS	= -version-info 30:0:4
 
 libpe_rules_la_CFLAGS	= $(CFLAGS_HARDENED_LIB)
 libpe_rules_la_LDFLAGS	+= $(LDFLAGS_HARDENED_LIB)
 
 libpe_rules_la_LIBADD	= $(top_builddir)/lib/common/libcrmcommon.la
 libpe_rules_la_SOURCES	= rules.c rules_alerts.c common.c
 
 libpe_status_la_LDFLAGS	= -version-info 34:0:6
 
 libpe_status_la_CFLAGS	= $(CFLAGS_HARDENED_LIB)
 libpe_status_la_LDFLAGS	+= $(LDFLAGS_HARDENED_LIB)
 
 libpe_status_la_LIBADD	= $(top_builddir)/lib/common/libcrmcommon.la
 # Use += rather than backlashed continuation lines for parsing by bumplibs
 libpe_status_la_SOURCES	=
 libpe_status_la_SOURCES	+= bundle.c
 libpe_status_la_SOURCES	+= clone.c
 libpe_status_la_SOURCES	+= common.c
 libpe_status_la_SOURCES	+= complex.c
 libpe_status_la_SOURCES	+= failcounts.c
 libpe_status_la_SOURCES	+= group.c
 libpe_status_la_SOURCES	+= native.c
 libpe_status_la_SOURCES	+= pe_actions.c
 libpe_status_la_SOURCES	+= pe_health.c
 libpe_status_la_SOURCES	+= pe_digest.c
 libpe_status_la_SOURCES	+= pe_notif.c
 libpe_status_la_SOURCES	+= pe_output.c
 libpe_status_la_SOURCES	+= remote.c
 libpe_status_la_SOURCES	+= rules.c
 libpe_status_la_SOURCES	+= status.c
 libpe_status_la_SOURCES	+= tags.c
 libpe_status_la_SOURCES	+= unpack.c
 libpe_status_la_SOURCES	+= utils.c
 
 #
 # libpe_rules_test and libpe_status_test are only used with unit tests, so we can
 # mock system calls.  See lib/common/mock.c for details.
 #
 
 include $(top_srcdir)/mk/tap.mk
 
 libpe_rules_test_la_SOURCES = $(libpe_rules_la_SOURCES)
 libpe_rules_test_la_LDFLAGS = $(libpe_rules_la_LDFLAGS) -rpath $(libdir) $(LDFLAGS_WRAP)
 # See comments on libcrmcommon_test_la in lib/common/Makefile.am regarding these flags.
 libpe_rules_test_la_CFLAGS = $(libpe_rules_la_CFLAGS) -DPCMK__UNIT_TESTING \
 			     -fno-builtin -fno-inline
 libpe_rules_test_la_LIBADD = $(top_builddir)/lib/common/libcrmcommon_test.la -lcmocka -lm
 
 libpe_status_test_la_SOURCES = $(libpe_status_la_SOURCES)
 libpe_status_test_la_LDFLAGS = $(libpe_status_la_LDFLAGS) -rpath $(libdir) $(LDFLAGS_WRAP)
 # See comments on libcrmcommon_test_la in lib/common/Makefile.am regarding these flags.
 libpe_status_test_la_CFLAGS = $(libpe_status_la_CFLAGS) -DPCMK__UNIT_TESTING \
 			      -fno-builtin -fno-inline
 libpe_status_test_la_LIBADD = $(top_builddir)/lib/common/libcrmcommon_test.la -lcmocka -lm
 
+.PHONY: clean-generic
 clean-generic:
 	rm -f *.log *.debug *~
diff --git a/python/Makefile.am b/python/Makefile.am
index ab15edc8a2..42bd197d5a 100644
--- a/python/Makefile.am
+++ b/python/Makefile.am
@@ -1,20 +1,22 @@
 #
 # Copyright 2023 the Pacemaker project contributors
 #
 # The version control history for this file may have further details.
 #
 # This source code is licensed under the GNU General Public License version 2
 # or later (GPLv2+) WITHOUT ANY WARRANTY.
 #
 
 MAINTAINERCLEANFILES    = Makefile.in
 
 EXTRA_DIST = pylintrc
 
 SUBDIRS	= pacemaker tests
 
+.PHONY: check-local
 check-local:
 	PYTHONPATH=$(top_srcdir)/python:$(top_builddir)/python $(PYTHON) -m unittest discover -v -s $(top_srcdir)/python/tests $(top_builddir)/python/tests
 
+.PHONY: pylint
 pylint:
 	pylint $(SUBDIRS)
diff --git a/rpm/Makefile.am b/rpm/Makefile.am
index 91e381aaf0..28abb56586 100644
--- a/rpm/Makefile.am
+++ b/rpm/Makefile.am
@@ -1,291 +1,294 @@
 #
 # Copyright 2003-2023 the Pacemaker project contributors
 #
 # The version control history for this file may have further details.
 #
 # This source code is licensed under the GNU General Public License version 2
 # or later (GPLv2+) WITHOUT ANY WARRANTY.
 #
 
 # We want to support the use case where this file is fed straight to make
 # without running automake first, so define defaults for any automake variables
 # used in this file.
 top_srcdir              ?= ..
 abs_srcdir              ?= $(shell pwd)
 abs_builddir            ?= $(abs_srcdir)
 MAKE                    ?= make
 PACKAGE                 ?= pacemaker
 AM_V_at                 ?= @
 MKDIR_P                 ?= mkdir -p
 
 include $(top_srcdir)/mk/common.mk
 include $(top_srcdir)/mk/release.mk
 
 EXTRA_DIST		= pacemaker.spec.in		\
 			  rpmlintrc
 
 # Extra options to pass to rpmbuild (this can be used to override the location
 # options this file normally passes, or to override macros used by the spec)
 RPM_EXTRA		?=
 
 # Where to put RPM artifacts; possible values:
 #
 # - subtree (default): RPM sources (i.e. TARFILE) in top-level build directory,
 #   everything else in dedicated "rpm" subdirectory of build tree
 #
 # - toplevel (deprecated): RPM sources, spec, and source rpm in top-level build
 #   directory, everything else uses the usual rpmbuild defaults
 #
 # - anything else: The value will be treated as a directory path to be used for
 #   all RPM artifacts. WARNING: The entire directory will get removed with
 #   "make clean" or "make rpm-clean".
 #
 RPMDEST         	?= subtree
 
 RPM_SPEC_DIR_subtree	= $(abs_builddir)/SPECS
 RPM_SRCRPM_DIR_subtree	= $(abs_builddir)/SRPMS
 RPM_OPTS_subtree	= --define "_sourcedir $(abs_builddir)/.." 	\
 			  --define "_topdir $(abs_builddir)"
 RPM_CLEAN_subtree	= "$(abs_builddir)/BUILD"			\
 			  "$(abs_builddir)/BUILDROOT"			\
 			  "$(abs_builddir)/RPMS"			\
 			  "$(abs_builddir)/SPECS"			\
 			  "$(abs_builddir)/SRPMS"
 
 RPM_SPEC_DIR_toplevel	= $(abs_builddir)/..
 RPM_SRCRPM_DIR_toplevel	= $(abs_builddir)/..
 RPM_OPTS_toplevel	= --define "_sourcedir $(abs_builddir)/.." 		\
 			  --define "_specdir   $(RPM_SPEC_DIR_toplevel)"	\
 			  --define "_srcrpmdir $(RPM_SRCRPM_DIR_toplevel)"
 RPM_CLEAN_toplevel	=
 
 RPM_SPEC_DIR_other	= $(RPMDEST)/SPECS
 RPM_SRCRPM_DIR_other	= $(RPMDEST)/SRPMS
 RPM_OPTS_other		= --define "_sourcedir $(abs_builddir)/.." 	\
 			  --define "_topdir $(RPMDEST)"
 RPM_CLEAN_other		= "$(RPMDEST)"
 
 RPMTYPE		= $(shell case "$(RPMDEST)" in 			\
 			toplevel$(rparen) echo toplevel ;;	\
 			subtree$(rparen) echo subtree ;;	\
 			*$(rparen) echo other ;;		\
 			esac)
 RPM_SPEC_DIR	= $(RPM_SPEC_DIR_$(RPMTYPE))
 RPM_SRCRPM_DIR	= $(RPM_SRCRPM_DIR_$(RPMTYPE))
 RPM_OPTS	= $(RPM_OPTS_$(RPMTYPE)) $(RPM_EXTRA)
 RPM_CLEAN	= $(RPM_CLEAN_$(RPMTYPE))
 
 WITH		?= --without doc
 
 # If $(BUILD_COUNTER) is an existing file, its contents will be used as the
 # spec version in built RPMs, unless $(SPECVERSION) is set to override it,
 # and the next increment will be written back to the file after building.
 BUILD_COUNTER	?= $(shell test -e build.counter && echo build.counter || echo ../build.counter)
 
 LAST_COUNT      = $(shell test -e "$(BUILD_COUNTER)" && cat "$(BUILD_COUNTER)" || echo 0)
 COUNT           = $(shell expr 1 + $(LAST_COUNT))
 SPECVERSION	?= $(COUNT)
 
 # SPEC_COMMIT is identical to TAG for DIST and tagged releases, otherwise it is
 # the short commit ID (which must be used in order for "make export" to use the
 # same archive name as "make dist")
 SPEC_COMMIT	?= $(shell						\
 		case $(TAG) in						\
 		    Pacemaker-*|DIST$(rparen)				\
 		        echo '$(TAG)' ;;				\
 		    *$(rparen)						\
 		        "$(GIT)" log --pretty=format:%h -n 1 '$(TAG)';;	\
 		esac)$(DIRTY_EXT)
 SPEC_ABBREV	= $(shell printf %s '$(SPEC_COMMIT)' | wc -c)
 SPEC_RELEASE	= $(shell case "$(WITH)" in 				\
 		  *pre_release*$(rparen)				\
 			[ "$(LAST_RELEASE)" = "$(TAG)" ]		\
 				&& echo "$(LAST_RELEASE)"		\
 				|| echo "$(NEXT_RELEASE)" ;;		\
 		  *$(rparen)						\
 			echo "$(LAST_RELEASE)" ;;			\
 		  esac)
 SPEC_RELEASE_NO	= $(shell echo $(SPEC_RELEASE) | sed -e s:Pacemaker-:: -e s:-.*::)
 
 MOCK_DIR	= $(abs_builddir)/mock
 MOCK_OPTIONS	?= --resultdir="$(MOCK_DIR)" --no-cleanup-after
 
 F	?= $(shell test ! -e /etc/fedora-release && echo 0; test -e /etc/fedora-release && rpm --eval %{fedora})
 ARCH	?= $(shell test ! -e /etc/fedora-release && uname -m; test -e /etc/fedora-release && rpm --eval %{_arch})
 MOCK_CFG	?= $(shell test -e /etc/fedora-release && echo fedora-$(F)-$(ARCH))
 
 distdir		= $(top_distdir)/rpm
 TARFILE		= $(abs_builddir)/../$(top_distdir).tar.gz
 
 # Create a source distribution based on a git archive. (If we aren't in a git
 # checkout, do a make dist instead.)
+.PHONY: export
 export:
 	cd $(abs_srcdir)/..;							\
 	if [ -z "$(CHECKOUT)" ] && [ -f "$(TARFILE)" ]; then			\
 	    echo "`date`: Using existing tarball: $(TARFILE)";			\
 	elif [ -z "$(CHECKOUT)" ]; then						\
 	    $(MAKE) $(AM_MAKEFLAGS) dist;					\
 	    echo "`date`: Rebuilt tarball: $(TARFILE)";				\
 	elif [ -n "$(DIRTY_EXT)" ]; then					\
 	    "$(GIT)" commit -m "DO-NOT-PUSH" -a;				\
 	    "$(GIT)" archive --prefix=$(top_distdir)/ -o "$(TARFILE)"		\
 		HEAD^{tree};							\
 	    "$(GIT)" reset --mixed HEAD^;					\
 	    echo "`date`: Rebuilt $(TARFILE)";					\
 	elif [ -f "$(TARFILE)" ]; then						\
 	    echo "`date`: Using existing tarball: $(TARFILE)";			\
 	else									\
 	    "$(GIT)" archive --prefix=$(top_distdir)/ -o "$(TARFILE)"		\
 		$(TAG)^{tree};							\
 	    echo "`date`: Rebuilt $(TARFILE)";					\
 	fi
 
 # Depend on spec-clean so the spec gets rebuilt every time
 $(RPM_SPEC_DIR)/$(PACKAGE).spec: spec-clean pacemaker.spec.in
 	$(AM_V_at)$(MKDIR_P) "$(RPM_SPEC_DIR)"
 	$(AM_V_GEN)if [ x"`"$(GIT)" ls-files				\
 		-m pacemaker.spec.in 2>/dev/null`" != x ]; then		\
 	    cat "$(abs_srcdir)/pacemaker.spec.in";			\
 	elif "$(GIT)" cat-file -e $(TAG):rpm/pacemaker.spec.in		\
 		2>/dev/null; then					\
 	    "$(GIT)" show $(TAG):rpm/pacemaker.spec.in;			\
 	elif "$(GIT)" cat-file -e $(TAG):pacemaker.spec.in 2>/dev/null;	\
 		then							\
 	    "$(GIT)" show $(TAG):pacemaker.spec.in;			\
 	else 								\
 	    cat "$(abs_srcdir)/pacemaker.spec.in";			\
 	fi | sed							\
 	    -e 's/^\(%global pcmkversion \).*/\1$(SPEC_RELEASE_NO)/'	\
 	    -e 's/^\(%global specversion \).*/\1$(SPECVERSION)/' 	\
 	    -e 's/^\(%global commit \).*/\1$(SPEC_COMMIT)/'		\
 	    -e 's/^\(%global commit_abbrev \).*/\1$(SPEC_ABBREV)/'	\
 	    -e "s/PACKAGE_DATE/$$(date +'%a %b %d %Y')/"		\
 	    -e 's/PACKAGE_VERSION/$(SPEC_RELEASE_NO)-$(SPECVERSION)/'	\
 	    > "$@"
 
 .PHONY: spec $(PACKAGE).spec
 spec $(PACKAGE).spec: $(RPM_SPEC_DIR)/$(PACKAGE).spec
 
 spec-clean:
 	-rm -f "$(RPM_SPEC_DIR)/$(PACKAGE).spec"
 
 .PHONY: srpm
 srpm:	export srpm-clean $(RPM_SPEC_DIR)/$(PACKAGE).spec
 	if [ -e "$(BUILD_COUNTER)" ]; then		\
 		echo $(COUNT) > "$(BUILD_COUNTER)";	\
 	fi
 	rpmbuild -bs $(RPM_OPTS) $(WITH) "$(RPM_SPEC_DIR)/$(PACKAGE).spec"
 
 .PHONY: srpm-clean
 srpm-clean:
 	-rm -f "$(RPM_SRCRPM_DIR)"/*.src.rpm
 
 # e.g. make WITH="--with pre_release" rpm
 .PHONY: rpm
 rpm:	srpm
 	@echo To create custom builds, edit the flags and options in $(PACKAGE).spec first
 	rpmbuild $(RPM_OPTS) $(WITH) --rebuild "$(RPM_SRCRPM_DIR)"/*.src.rpm
 
 .PHONY: rpm-clean
 rpm-clean: spec-clean srpm-clean
 	-if [ -n "$(RPM_CLEAN)" ]; then rm -rf $(RPM_CLEAN); fi
 
 .PHONY: rpmlint
 rpmlint: $(RPM_SPEC_DIR)/$(PACKAGE).spec
 	rpmlint -f rpmlintrc "$<"
 
 .PHONY: rpm-dep
 rpm-dep: $(RPM_SPEC_DIR)/$(PACKAGE).spec
 	sudo yum-builddep "$(RPM_SPEC_DIR)/$(PACKAGE).spec"
 
 .PHONY: release
 release:
 	$(MAKE) $(AM_MAKEFLAGS) TAG=$(LAST_RELEASE) rpm
 
 # Build the highest-versioned rc tag
 .PHONY: rc
 rc:
 	@if [ -z "$(CHECKOUT)" ]; then					\
 		echo 'This target must be run from a git checkout';	\
 		exit 1;							\
 	fi
 	$(MAKE) $(AM_MAKEFLAGS) TAG="$$("$(GIT)" tag -l 2>/dev/null	\
 		| sed -n -e 's/^\(Pacemaker-[0-9.]*-rc[0-9]*\)$$/\1/p'	\
 		| sort -Vr | head -n 1)" rpm
 
 .PHONY: chroot
 chroot: mock-$(MOCK_CFG) mock-install-$(MOCK_CFG) mock-sh-$(MOCK_CFG)
 	@echo Done
 
 .PHONY: mock-next
 mock-next:
 	$(MAKE) $(AM_MAKEFLAGS) F=$(shell expr 1 + $(F)) mock
 
 .PHONY: mock-rawhide
 mock-rawhide:
 	$(MAKE) $(AM_MAKEFLAGS) F=rawhide mock
 
 mock-install-%:
 	@echo "Installing packages"
 	mock --root=$* $(MOCK_OPTIONS) --install "$(MOCK_DIR)"/*.rpm \
 		vi sudo valgrind lcov gdb fence-agents psmisc
 
 .PHONY: mock-install
 mock-install: mock-install-$(MOCK_CFG)
 	@echo Done
 
 .PHONY: mock-sh
 mock-sh: mock-sh-$(MOCK_CFG)
 	@echo Done
 
 mock-sh-%:
 	@echo Connecting
 	mock --root=$* $(MOCK_OPTIONS) --shell
 	@echo Done
 
 mock-%: srpm mock-clean
 	mock $(MOCK_OPTIONS) --root=$* --no-cleanup-after --rebuild	\
 		$(WITH) "$(RPM_SRCRPM_DIR)"/*.src.rpm
 
 .PHONY: mock
 mock:   mock-$(MOCK_CFG)
 	@echo Done
 
 .PHONY: dirty
 dirty:
 	$(MAKE) $(AM_MAKEFLAGS) DIRTY=yes mock
 
 .PHONY: mock-clean
 mock-clean:
 	-rm -rf "$(MOCK_DIR)"
 
 # Make debugging makefile issues easier
+.PHONY: vars
 vars:
 	@echo "CHECKOUT=$(CHECKOUT)"
 	@echo "VERSION=$(VERSION)"
 	@echo "COMMIT=$(COMMIT)"
 	@echo "TAG=$(TAG)"
 	@echo "DIRTY=$(DIRTY)"
 	@echo "DIRTY_EXT=$(DIRTY_EXT)"
 	@echo "LAST_RELEASE=$(LAST_RELEASE)"
 	@echo "NEXT_RELEASE=$(NEXT_RELEASE)"
 	@echo "top_distdir=$(top_distdir)"
 	@echo "RPMDEST=$(RPMDEST)"
 	@echo "RPMTYPE=$(RPMTYPE)"
 	@echo "RPM_SPEC_DIR=$(RPM_SPEC_DIR)"
 	@echo "RPM_SRCRPM_DIR=$(RPM_SRCRPM_DIR)"
 	@echo "RPM_OPTS=$(RPM_OPTS)"
 	@echo "RPM_CLEAN=$(RPM_CLEAN)"
 	@echo "WITH=$(WITH)"
 	@echo "BUILD_COUNTER=$(BUILD_COUNTER)"
 	@echo "LAST_COUNT=$(LAST_COUNT)"
 	@echo "COUNT=$(COUNT)"
 	@echo "SPECVERSION=$(SPECVERSION)"
 	@echo "SPEC_COMMIT=$(SPEC_COMMIT)"
 	@echo "SPEC_ABBREV=$(SPEC_ABBREV)"
 	@echo "SPEC_RELEASE=$(SPEC_RELEASE)"
 	@echo "SPEC_RELEASE_NO=$(SPEC_RELEASE_NO)"
 	@echo "TARFILE=$(TARFILE)"
 
+.PHONY: clean-local
 clean-local: mock-clean rpm-clean
 	-rm -f "$(TARFILE)"
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 96019defcb..8d490a26ce 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1,21 +1,22 @@
 #
-# Copyright 2020-2022 the Pacemaker project contributors
+# Copyright 2020-2023 the Pacemaker project contributors
 #
 # The version control history for this file may have further details.
 #
 # This source code is licensed under the GNU General Public License version 2
 # or later (GPLv2+) WITHOUT ANY WARRANTY.
 #
 
 # tap-test is copied from /usr/share/automake-*/tap-driver.sh.
 EXTRA_DIST = tap-driver.sh \
 	     tap-test \
 	     test-headers.sh
 
+.PHONY: check
 check: check-headers
 
 .PHONY: check-headers
 check-headers:
 	CFLAGS="$(CFLAGS)" CXXFLAGS="$(CXXFLAGS)" CC="$(CC)"		\
 		CXX="$(CXX)" CPPFLAGS="$(CPPFLAGS)" LIBS="$(LIBS)"	\
 		SRCDIR="$(top_srcdir)" sh "$(srcdir)/test-headers.sh"
diff --git a/xml/Makefile.am b/xml/Makefile.am
index d3f2229c17..55e89508e4 100644
--- a/xml/Makefile.am
+++ b/xml/Makefile.am
@@ -1,321 +1,326 @@
 #
 # Copyright 2004-2023 the Pacemaker project contributors
 #
 # The version control history for this file may have further details.
 #
 # This source code is licensed under the GNU General Public License version 2
 # or later (GPLv2+) WITHOUT ANY WARRANTY.
 #
 
 include $(top_srcdir)/mk/common.mk
 
 noarch_pkgconfig_DATA	= $(builddir)/pacemaker-schemas.pc
 
 # Pacemaker has 3 schemas: the CIB schema, the API schema (for command-line
 # tool XML output), and a legacy schema for crm_mon --as-xml.
 #
 # See README.md for details on updating CIB schema files (API is similar)
 
 # The CIB and crm_mon schemas are installed directly in CRM_SCHEMA_DIRECTORY
 # for historical reasons, while the API schema is installed in a subdirectory.
 APIdir	= $(CRM_SCHEMA_DIRECTORY)/api
 CIBdir	= $(CRM_SCHEMA_DIRECTORY)
 MONdir	= $(CRM_SCHEMA_DIRECTORY)
 
 basexsltdir		= $(CRM_SCHEMA_DIRECTORY)/base
 dist_basexslt_DATA	= $(srcdir)/base/access-render-2.xsl
 
 # Extract a sorted list of available numeric schema versions
 # from filenames like NAME-MAJOR[.MINOR][.MINOR-MINOR].rng
 numeric_versions = $(shell ls -1 $(1) \
 			  | sed -n -e 's/^.*-\([0-9][0-9.]*\).rng$$/\1/p' \
 			  | sort -u -t. -k 1,1n -k 2,2n -k 3,3n)
 
 # @COMPAT: pacemaker-next is deprecated since 2.1.5
 version_pairs = $(join \
 			    $(1),$(addprefix \
 			      -,$(wordlist \
 			        2,$(words $(1)),$(1) \
 			      ) next \
 			    ) \
 			  )
 
 version_pairs_last = $(wordlist \
 			    $(words \
 			      $(wordlist \
 			        2,$(1),$(2) \
 			      ) \
 			    ),$(1),$(2) \
 			  )
 
 # NOTE: All files in API_request_base, CIB_cfg_base, API_base, and CIB_base
 # need to start with a unique prefix.  These variables all get iterated over
 # and globbed, and two files starting with the same prefix will cause
 # problems.
 
 # Names of API schemas that form the choices for pacemaker-result content
 API_request_base	= command-output	\
 			  crm_attribute 	\
 			  crm_error 	\
 			  crm_mon		\
 			  crm_node 		\
 			  crm_resource		\
 			  crm_rule \
 			  crm_shadow		\
 			  crm_simulate		\
 			  crmadmin		\
 			  digests		\
 			  pacemakerd 		\
 			  stonith_admin		\
 			  version
 
 # Names of CIB schemas that form the choices for cib/configuration content
 CIB_cfg_base		= options nodes resources constraints fencing acls tags alerts
 
 # Names of all schemas (including top level and those included by others)
 API_base		= $(API_request_base)	\
 			  any-element		\
 			  failure		\
 			  fence-event 		\
 			  generic-list		\
 			  instruction		\
 			  item			\
 			  node-attrs		\
 			  node-history		\
 			  nodes			\
 			  patchset		\
 			  resources		\
 			  status		\
 			  subprocess-output
 CIB_base		= cib $(CIB_cfg_base) status score rule nvset
 
 # Static schema files and transforms (only CIB has transforms)
 # 
 # This is more complicated than it should be due to the need to support
 # VPATH builds and "make distcheck". We need the absolute paths for reliable
 # substitution back and forth, and relative paths for distributed files.
 API_abs_files		= $(foreach base,$(API_base),$(wildcard $(abs_srcdir)/api/$(base)-*.rng))
 CIB_abs_files		= $(foreach base,$(CIB_base),$(wildcard $(abs_srcdir)/$(base).rng $(abs_srcdir)/$(base)-*.rng))
 CIB_abs_xsl		= $(abs_srcdir)/upgrade-1.3.xsl			\
 			  $(abs_srcdir)/upgrade-2.10.xsl		\
 			  $(wildcard $(abs_srcdir)/upgrade-*enter.xsl)	\
 			  $(wildcard $(abs_srcdir)/upgrade-*leave.xsl)
 MON_abs_files 		= $(abs_srcdir)/crm_mon.rng
 API_files		= $(foreach base,$(API_base),$(wildcard $(srcdir)/api/$(base)-*.rng))
 CIB_files		= $(foreach base,$(CIB_base),$(wildcard $(srcdir)/$(base).rng $(srcdir)/$(base)-*.rng))
 CIB_xsl			= $(srcdir)/upgrade-1.3.xsl			\
 			  $(srcdir)/upgrade-2.10.xsl		\
 			  $(wildcard $(srcdir)/upgrade-*enter.xsl)	\
 			  $(wildcard $(srcdir)/upgrade-*leave.xsl)
 MON_files 		= $(srcdir)/crm_mon.rng
 
 # Sorted lists of all numeric schema versions
 API_numeric_versions	= $(call numeric_versions,${API_files})
 CIB_numeric_versions	= $(call numeric_versions,${CIB_files})
 MON_numeric_versions 	= $(call numeric_versions,$(wildcard $(srcdir)/api/crm_mon*.rng))
 
 # The highest numeric schema version
 API_max			?= $(lastword $(API_numeric_versions))
 CIB_max			?= $(lastword $(CIB_numeric_versions))
 MON_max 			?= $(lastword $(MON_numeric_versions))
 
 # Sorted lists of all schema versions (including "next")
 # @COMPAT: pacemaker-next is deprecated since 2.1.5
 API_versions		= next $(API_numeric_versions)
 CIB_versions		= next $(CIB_numeric_versions)
 
 # Build tree locations of static schema files and transforms (for VPATH builds)
 API_build_copies	= $(foreach f,$(API_abs_files),$(subst $(abs_srcdir),$(abs_builddir),$(f)))
 CIB_build_copies	= $(foreach f,$(CIB_abs_files) $(CIB_abs_xsl),$(subst $(abs_srcdir),$(abs_builddir),$(f)))
 MON_build_copies 	= $(foreach f,$(MON_abs_files),$(subst $(abs_srcdir),$(abs_builddir),$(f)))
 
 # Dynamically generated schema files
 API_generated		= api/api-result.rng $(foreach base,$(API_versions),api/api-result-$(base).rng)
 CIB_generated		= pacemaker.rng $(foreach base,$(CIB_versions),pacemaker-$(base).rng) versions.rng
 MON_generated 		= crm_mon.rng
 
 CIB_version_pairs	= $(call version_pairs,${CIB_numeric_versions})
 CIB_version_pairs_cnt	= $(words ${CIB_version_pairs})
 CIB_version_pairs_last  = $(call version_pairs_last,${CIB_version_pairs_cnt},${CIB_version_pairs})
 
 dist_API_DATA		= $(API_files)
 dist_CIB_DATA		= $(CIB_files) $(CIB_xsl)
 
 nodist_API_DATA		= $(API_generated)
 nodist_CIB_DATA		= $(CIB_generated)
 nodist_MON_DATA		= $(MON_generated)
 
 EXTRA_DIST		= README.md			\
 			  best-match.sh			\
 			  cibtr-2.rng			\
 			  context-of.xsl		\
 			  ocf-meta2man.xsl		\
 			  regression.sh			\
 			  upgrade-2.10-roundtrip.xsl	\
 			  upgrade-detail.xsl		\
 			  xslt_cibtr-2.rng		\
 			  assets			\
 			  test-2			\
 			  test-2-enter			\
 			  test-2-leave			\
 			  test-2-roundtrip
 
+.PHONY: cib-versions
 cib-versions:
 	@echo "Max: $(CIB_max)"
 	@echo "Available: $(CIB_versions)"
 
+.PHONY: api-versions
 api-versions:
 	@echo "Max: $(API_max)"
 	@echo "Available: $(API_versions)"
 
 # Dynamically generated top-level API schema
 api/api-result.rng: api/api-result-$(API_max).rng
 	$(AM_V_at)$(MKDIR_P) api # might not exist in VPATH build
 	$(AM_V_SCHEMA)cp $(top_builddir)/xml/$< $@
 
 api/api-result-%.rng: $(API_build_copies) best-match.sh Makefile.am
 	$(AM_V_at)echo '<?xml version="1.0" encoding="UTF-8"?>' > $@
 	$(AM_V_at)echo '<grammar xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">' >> $@
 	$(AM_V_at)echo '  <start>' >> $@
 	$(AM_V_at)echo '    <element name="pacemaker-result">' >> $@
 	$(AM_V_at)echo '      <attribute name="api-version"> <text /> </attribute>' >> $@
 	$(AM_V_at)echo '      <attribute name="request"> <text /> </attribute>' >> $@
 	$(AM_V_at)echo '      <optional>' >> $@
 	$(AM_V_at)echo '        <choice>' >> $@
 	$(AM_V_at)for rng in $(API_request_base); do $(srcdir)/best-match.sh api/$$rng $(*) $(@) "          " || :; done
 	$(AM_V_at)echo '        </choice>' >> $@
 	$(AM_V_at)echo '      </optional>' >> $@
 	$(AM_V_at)$(srcdir)/best-match.sh api/status $(*) $(@) "      " || :
 	$(AM_V_at)echo '    </element>' >> $@
 	$(AM_V_at)echo '  </start>' >> $@
 	$(AM_V_SCHEMA)echo '</grammar>' >> $@
 
 crm_mon.rng: api/crm_mon-$(MON_max).rng
 	$(AM_V_at)echo '<?xml version="1.0" encoding="UTF-8"?>' > $@
 	$(AM_V_at)echo '<grammar xmlns="http://relaxng.org/ns/structure/1.0"' >> $@
 	$(AM_V_at)echo '         datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">' >> $@
 	$(AM_V_at)echo '    <start>' >> $@
 	$(AM_V_at)echo '        <ref name="element-crm_mon-old"/>' >> $@
 	$(AM_V_at)echo '    </start>' >> $@
 	$(AM_V_at)echo '    <define name="element-crm_mon-old">' >> $@
 	$(AM_V_at)echo '        <element name="crm_mon">' >> $@
 	$(AM_V_at)echo '            <attribute name="version"> <text/> </attribute>' >> $@
 	$(AM_V_at)echo '            <externalRef href="$(<)" />' >> $@
 	$(AM_V_at)echo '        </element>' >> $@
 	$(AM_V_at)echo '    </define>' >> $@
 	$(AM_V_SCHEMA)echo '</grammar>' >> $@
 
 # Dynamically generated top-level CIB schema
 pacemaker.rng: pacemaker-$(CIB_max).rng
 	$(AM_V_SCHEMA)cp $(top_builddir)/xml/$< $@
 
 pacemaker-%.rng: $(CIB_build_copies) best-match.sh Makefile.am
 	$(AM_V_at)echo '<?xml version="1.0" encoding="UTF-8"?>' > $@
 	$(AM_V_at)echo '<grammar xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">' >> $@
 	$(AM_V_at)echo '  <start>' >> $@
 	$(AM_V_at)echo '    <element name="cib">' >> $@
 	$(AM_V_at)$(srcdir)/best-match.sh cib $(*) $(@) "      "
 	$(AM_V_at)echo '      <element name="configuration">' >> $@
 	$(AM_V_at)echo '        <interleave>' >> $@
 	$(AM_V_at)for rng in $(CIB_cfg_base); do $(srcdir)/best-match.sh $$rng $(*) $(@) "          " || :; done
 	$(AM_V_at)echo '        </interleave>' >> $@
 	$(AM_V_at)echo '      </element>' >> $@
 	$(AM_V_at)echo '      <optional>' >> $@
 	$(AM_V_at)echo '        <element name="status">' >> $@
 	$(AM_V_at)$(srcdir)/best-match.sh status $(*) $(@) "          "
 	$(AM_V_at)echo '        </element>' >> $@
 	$(AM_V_at)echo '      </optional>' >> $@
 	$(AM_V_at)echo '    </element>' >> $@
 	$(AM_V_at)echo '  </start>' >> $@
 	$(AM_V_SCHEMA)echo '</grammar>' >> $@
 
 # Dynamically generated CIB schema listing all pacemaker versions
 versions.rng: pacemaker-$(CIB_max).rng Makefile.am
 	$(AM_V_at)echo '<?xml version="1.0" encoding="UTF-8"?>' > $@
 	$(AM_V_at)echo '<grammar xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">' >> $@
 	$(AM_V_at)echo '  <start>' >> $@
 	$(AM_V_at)echo '   <interleave>' >> $@
 	$(AM_V_at)echo '    <optional>' >> $@
 	$(AM_V_at)echo '      <attribute name="validate-with">' >> $@
 	$(AM_V_at)echo '        <choice>' >> $@
 	$(AM_V_at)echo '          <value>none</value>' >> $@
 	$(AM_V_at)echo '          <value>pacemaker-0.6</value>' >> $@
 	$(AM_V_at)echo '          <value>transitional-0.6</value>' >> $@
 	$(AM_V_at)echo '          <value>pacemaker-0.7</value>' >> $@
 	$(AM_V_at)echo '          <value>pacemaker-1.1</value>' >> $@
 	$(AM_V_at)for rng in $(CIB_versions); do echo "          <value>pacemaker-$$rng</value>" >> $@; done
 	$(AM_V_at)echo '        </choice>' >> $@
 	$(AM_V_at)echo '      </attribute>' >> $@
 	$(AM_V_at)echo '    </optional>' >> $@
 	$(AM_V_at)echo '    <attribute name="admin_epoch"><data type="nonNegativeInteger"/></attribute>' >> $@
 	$(AM_V_at)echo '    <attribute name="epoch"><data type="nonNegativeInteger"/></attribute>' >> $@
 	$(AM_V_at)echo '    <attribute name="num_updates"><data type="nonNegativeInteger"/></attribute>' >> $@
 	$(AM_V_at)echo '   </interleave>' >> $@
 	$(AM_V_at)echo '  </start>' >> $@
 	$(AM_V_SCHEMA)echo '</grammar>' >> $@
 
 # diff fails with ec=2 if no predecessor is found;
 # this uses '=' GNU extension to sed, if that's not available,
 # one can use: hline=`echo "$${p}" | grep -Fn "$${hunk}" | cut -d: -f1`;
 # XXX: use line information from hunk to avoid "not detected" for ambiguity
 version_diff = \
 	@for p in $(1); do \
 	  set `echo "$${p}" | tr '-' ' '`; \
 	  echo "\#\#\# *-$$2.rng vs. predecessor"; \
 	  for v in *-$$2.rng; do \
 	    echo "\#\#\#\# $${v} vs. predecessor"; b=`echo "$${v}" | cut -d- -f1`; \
 	    old=`./best-match.sh $${b} $$1`; \
 	    p=`diff -u "$${old}" "$${v}" 2>/dev/null`; \
 	    case $$? in \
 	    1) echo "$${p}" | sed -n -e '/^@@ /!d;=;p' \
 	       -e ':l;n;/^\([- ]\|+.*<[^ />]\+\([^/>]\+="ID\|>$$\)\)/bl;s/^[+ ]\(.*\)/\1/p' \
 	       | while read hline; do \
 	           read h && read i || break; \
 	           iline=`grep -Fn "$${i}" "$${v}" | cut -d: -f1`; \
 	           ctxt="(not detected)"; \
 	           if test `echo "$${iline}" | wc -l` -eq 1; then \
 	             ctxt=`{ sed -n -e "1,$$(($${iline}-1))p" "$${v}"; \
 	                     echo "<inject id=\"GOAL\"/>$${i}"; \
 	                     sed -n -e "$$(($${iline}+1)),$$ p" "$${v}"; \
 	                   } | $(XSLTPROC) --param skip 1 context-of.xsl -`; \
 	           fi; \
 	           echo "$${p}" | sed -n -e "$$(($${hline}-2)),$${hline}!d" \
 	             -e '/^\(+++\|---\)/p'; \
 	           echo "$${h} context: $${ctxt}"; \
 	           echo "$${p}" | sed -n -e "1,$${hline}d" \
 	             -e '/^\(---\|@@ \)/be;p;d;:e;n;be'; \
 	           done; \
 	       ;; \
 	    2) echo "\#\#\#\#\# $${v} has no predecessor";; \
 	    esac; \
 	  done; \
 	done
 
+.PHONY: diff
 diff: best-match.sh
 	@echo "#  Comparing changes in + since $(CIB_max)"
 	$(call version_diff,${CIB_version_pairs_last})
 
+.PHONY: fulldiff
 fulldiff: best-match.sh
 	@echo "#  Comparing all changes across all the subsequent increments"
 	$(call version_diff,${CIB_version_pairs})
 
 CLEANFILES = $(API_generated) $(CIB_generated) $(MON_generated)
 
 # Remove pacemaker schema files generated by *any* source version. This allows
 # "make -C xml clean" to have the desired effect when checking out an earlier
 # revision in a source tree.
+.PHONY: clean-local
 clean-local:
 	if [ "x$(srcdir)" != "x$(builddir)" ]; then					\
 		rm -f $(API_build_copies) $(CIB_build_copies) $(MON_build_copies);	\
 	fi
 	rm -f $(builddir)/pacemaker-[0-9]*.[0-9]*.rng
 
 # Enable ability to use $@ in prerequisite
 .SECONDEXPANSION:
 
 # For VPATH builds, copy the static schema files into the build tree
 $(API_build_copies) $(CIB_build_copies) $(MON_build_copies): $$(subst $(abs_builddir),$(srcdir),$$(@))
 	$(AM_V_GEN)if [ "x$(srcdir)" != "x$(builddir)" ]; then	\
 		$(MKDIR_P) "$(dir $(@))";			\
 		cp "$(<)" "$(@)";				\
 	fi