diff --git a/xml/Makefile.am b/xml/Makefile.am index 5816b8d079..2e6811e00f 100644 --- a/xml/Makefile.am +++ b/xml/Makefile.am @@ -1,311 +1,311 @@ # # Copyright 2004-2024 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 \ crm_ticket \ 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 \ ocf-ra \ options \ patchset \ resources \ status \ subprocess-output \ ticket 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 \ cibtr-2.rng \ context-of.xsl \ rng-helper \ 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) rng-helper Makefile.am $(AM_V_at)echo '' > $@ $(AM_V_at)echo '' >> $@ $(AM_V_at)echo ' ' >> $@ $(AM_V_at)echo ' ' >> $@ $(AM_V_at)echo ' ' >> $@ $(AM_V_at)echo ' ' >> $@ $(AM_V_at)echo ' ' >> $@ $(AM_V_at)echo ' ' >> $@ - $(AM_V_at)for rng in $(API_request_base); do $(builddir)/rng-helper api/$$rng $(*) $(@) " " || :; done + $(AM_V_at)for rng in $(API_request_base); do $(builddir)/rng-helper match api/$$rng $(*) $(@) " " || :; done $(AM_V_at)echo ' ' >> $@ $(AM_V_at)echo ' ' >> $@ - $(AM_V_at)$(builddir)/rng-helper api/status $(*) $(@) " " || : + $(AM_V_at)$(builddir)/rng-helper match api/status $(*) $(@) " " || : $(AM_V_at)echo ' ' >> $@ $(AM_V_at)echo ' ' >> $@ $(AM_V_SCHEMA)echo '' >> $@ crm_mon.rng: api/crm_mon-$(MON_max).rng $(AM_V_at)echo '' > $@ $(AM_V_at)echo '> $@ $(AM_V_at)echo ' datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">' >> $@ $(AM_V_at)echo ' ' >> $@ $(AM_V_at)echo ' ' >> $@ $(AM_V_at)echo ' ' >> $@ $(AM_V_at)echo ' ' >> $@ $(AM_V_at)echo ' ' >> $@ $(AM_V_at)echo ' ' >> $@ $(AM_V_at)echo ' ' >> $@ $(AM_V_at)echo ' ' >> $@ $(AM_V_at)echo ' ' >> $@ $(AM_V_SCHEMA)echo '' >> $@ # Dynamically generated top-level CIB schema pacemaker.rng: pacemaker-$(CIB_max).rng $(AM_V_SCHEMA)cp $(top_builddir)/xml/$< $@ pacemaker-%.rng: $(CIB_build_copies) rng-helper Makefile.am $(AM_V_at)echo '' > $@ $(AM_V_at)echo '' >> $@ $(AM_V_at)echo ' ' >> $@ $(AM_V_at)echo ' ' >> $@ - $(AM_V_at)$(builddir)/rng-helper cib $(*) $(@) " " + $(AM_V_at)$(builddir)/rng-helper match cib $(*) $(@) " " $(AM_V_at)echo ' ' >> $@ $(AM_V_at)echo ' ' >> $@ - $(AM_V_at)for rng in $(CIB_cfg_base); do $(builddir)/rng-helper $$rng $(*) $(@) " " || :; done + $(AM_V_at)for rng in $(CIB_cfg_base); do $(builddir)/rng-helper match $$rng $(*) $(@) " " || :; done $(AM_V_at)echo ' ' >> $@ $(AM_V_at)echo ' ' >> $@ $(AM_V_at)echo ' ' >> $@ $(AM_V_at)echo ' ' >> $@ - $(AM_V_at)$(builddir)/rng-helper status $(*) $(@) " " + $(AM_V_at)$(builddir)/rng-helper match status $(*) $(@) " " $(AM_V_at)echo ' ' >> $@ $(AM_V_at)echo ' ' >> $@ $(AM_V_at)echo ' ' >> $@ $(AM_V_at)echo ' ' >> $@ $(AM_V_SCHEMA)echo '' >> $@ # Dynamically generated CIB schema listing all pacemaker versions versions.rng: pacemaker-$(CIB_max).rng Makefile.am $(AM_V_at)echo '' > $@ $(AM_V_at)echo '' >> $@ $(AM_V_at)echo ' ' >> $@ $(AM_V_at)echo ' ' >> $@ $(AM_V_at)echo ' ' >> $@ $(AM_V_at)echo ' ' >> $@ $(AM_V_at)echo ' ' >> $@ $(AM_V_at)echo ' none' >> $@ $(AM_V_at)echo ' pacemaker-0.6' >> $@ $(AM_V_at)echo ' transitional-0.6' >> $@ $(AM_V_at)echo ' pacemaker-0.7' >> $@ $(AM_V_at)echo ' pacemaker-1.1' >> $@ $(AM_V_at)for rng in $(CIB_versions); do echo " pacemaker-$$rng" >> $@; done $(AM_V_at)echo ' ' >> $@ $(AM_V_at)echo ' ' >> $@ $(AM_V_at)echo ' ' >> $@ $(AM_V_at)echo ' ' >> $@ $(AM_V_at)echo ' ' >> $@ $(AM_V_at)echo ' ' >> $@ $(AM_V_at)echo ' ' >> $@ $(AM_V_at)echo ' ' >> $@ $(AM_V_SCHEMA)echo '' >> $@ .PHONY: diff diff: rng-helper @echo "# Comparing changes in + since $(CIB_max)" @./version-diff.sh ${CIB_version_pairs_last} .PHONY: fulldiff fulldiff: rng-helper @echo "# Comparing all changes across all the subsequent increments" @./version-diff.sh ${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 diff --git a/xml/rng-helper.in b/xml/rng-helper.in index 07294923ac..e466ad5b0a 100755 --- a/xml/rng-helper.in +++ b/xml/rng-helper.in @@ -1,108 +1,116 @@ #!@BASH_PATH@ # # Copyright 2014-2024 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. # -# Find the (sub-)schema that best matches a desired version. -# -# Version numbers are assumed to be in the format X.Y, -# where X and Y are integers, and Y is no more than 3 digits, -# or the special value "next". - -# (Sub-)schema name (e.g. "resources") -base="$1"; shift - -# Desired version (e.g. "1.0" or "next") -target="$1"; shift - -# If not empty, append the best match as an XML externalRef to this file -# (otherwise, just echo the best match). Using readlink allows building -# from a different directory. -destination="$(readlink -f "$1")"; shift - -# Arbitrary text to print before XML (generally spaces to indent) -prefix="$1"; shift - -# Allow building from a different directory -cd "$(dirname $0)" - list_candidates() { ls -1 "${1}.rng" "${1}"-[0-9]*.rng "${1}"-next.rng 2>/dev/null } version_from_filename() { vff_filename="$1" case "$vff_filename" in *-*.rng) echo "$vff_filename" | sed -e 's/.*-\(.*\).rng/\1/' ;; *) # special case for bare ${base}.rng, no -0.1's around anyway echo 0.1 ;; esac } filename_from_version() { ffv_version="$1" ffv_base="$2" if [ "$ffv_version" = "0.1" ]; then echo "${ffv_base}.rng" else echo "${ffv_base}-${ffv_version}.rng" fi } # Convert version string (e.g. 2.10) into integer (e.g. 2010) for comparisons int_version() { echo "$1" | awk -F. '{ printf("%d%03d\n", $1,$2); }'; } +# Find the (sub-)schema that best matches a desired version. +# +# Version numbers are assumed to be in the format X.Y, +# where X and Y are integers, and Y is no more than 3 digits, +# or the special value "next". best_match() { + # (Sub-)schema name (e.g. "resources") + local base="$1" + + # Desired version (e.g. "1.0" or "next") + local target="$2" + + # If not empty, append the best match as an XML externalRef to this file + # (otherwise, just echo the best match). + local destination="$3" + + # Arbitrary text to print before XML (generally spaces to indent) + local prefix="$4" + best="0.0" for rng in $(list_candidates "${base}"); do case ${rng} in ${base}-${target}.rng) # We found exactly what was requested best=${target} break ;; *-next.rng) # "Next" schemas cannot be a best match unless directly requested ;; *) v=$(version_from_filename "${rng}") if [ $(int_version "${v}") -gt $(int_version "${best}") ]; then # This version beats the previous best match if [ "${target}" = "next" ]; then best=${v} elif [ $(int_version "${v}") -lt $(int_version "${target}") ]; then # This value is best only if it's still less than the target best=${v} fi fi ;; esac done if [ "$best" != "0.0" ]; then found=$(filename_from_version "$best" "$base") if [ -z "$destination" ]; then echo "$(basename $found)" else echo "${prefix}" >> "$destination" fi return 0 fi return 1 } -best_match +# Allow building RNGs from a different directory +cd "$(dirname $0)" + +case "$1" in + match) + # Using readlink allows building from a different directory + best_match "$2" "$3" "$(readlink -f "$4")" "$5" + ;; + + *) + echo "Invalid command: $1" + exit 1 + ;; +esac diff --git a/xml/version-diff.sh.in b/xml/version-diff.sh.in index 4728230b3d..40d388d8dc 100644 --- a/xml/version-diff.sh.in +++ b/xml/version-diff.sh.in @@ -1,60 +1,60 @@ #!@BASH_PATH@ # # Copyright 2016-2024 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. # # 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 for p in $*; 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=$(./rng-helper "$b" "$1") + old=$(./rng-helper match "$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 -r hline; do if read -r h; then read -r i else break fi iline=$(grep -Fn "$i" "$v" | cut -d: -f1) if [ "$(echo "$iline" | wc -l)" = "1" ]; then ctxt=$({ sed -n -e "1,$((iline - 1))p" "$v" echo "$i" sed -n -e "$((iline + 1)),$ p" "$v" } | xsltproc --param skip 1 context-of.xsl -) else ctxt="(not detected)" 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