diff options
Diffstat (limited to 'maint/bumplibs.in')
-rw-r--r-- | maint/bumplibs.in | 291 |
1 files changed, 291 insertions, 0 deletions
diff --git a/maint/bumplibs.in b/maint/bumplibs.in new file mode 100644 index 0000000..a142660 --- /dev/null +++ b/maint/bumplibs.in @@ -0,0 +1,291 @@ +#!@BASH_PATH@ +# +# Copyright 2012-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. +# + +# List regular expressions (not globs) that match all of a library's public API +# headers. Any files ending in "internal.h" will be excluded from matches. +declare -A HEADERS +HEADERS[cib]="include/crm/cib.h include/crm/cib/.*.h" +HEADERS[crmcommon]="include/crm/crm.h + include/crm/msg_xml.h + include/crm/common/.*.h" +HEADERS[crmcluster]="include/crm/cluster.h include/crm/cluster/.*.h" +HEADERS[crmservice]="include/crm/services.*.h" +HEADERS[lrmd]="include/crm/lrmd.*.h" +HEADERS[pacemaker]="include/pacemaker.*.h" +HEADERS[pe_rules]="include/crm/pengine/ru.*.h" +HEADERS[pe_status]="include/crm/pengine/[^r].*.h include/crm/pengine/r[^u].*.h" +HEADERS[stonithd]="include/crm/stonith-ng.h include/crm/fencing/.*.h" + +yesno() { + local RESPONSE + + read -p "$1 " RESPONSE + case $(echo "$RESPONSE" | tr A-Z a-z) in + y|yes|ano|ja|si|oui) return 0 ;; + *) return 1 ;; + esac +} + +prompt_to_continue() { + yesno "Continue?" || exit 0 +} + +find_last_release() { + if [ ! -z "$1" ]; then + echo "$1" + else + git tag -l | grep Pacemaker | grep -v rc | sort -Vr | head -n 1 + fi +} + +find_libs() { + find lib -name "*.am" -exec grep "lib.*_la_LDFLAGS.*version-info" \{\} \; \ + | sed -e 's/lib\(.*\)_la_LDFLAGS.*/\1/' +} + +find_makefile() { + find lib -name Makefile.am -exec grep -l "lib${1}_la.*version-info" \{\} \; +} + +find_sources() { + local LIB="$1" + local AMFILE="$2" + local SOURCES + + # Library makefiles should use "+=" to break up long sources lines rather + # than backslashed continuation lines, to allow this script to detect + # source files correctly. Warn if that's not the case. + if + grep "lib${LIB}_la_SOURCES.*\\\\" $AMFILE + then + echo -e "\033[1;35m -- Sources list for lib$LIB is probably truncated! --\033[0m" + echo "Edit to use '+=' rather than backslashed continuation lines" + prompt_to_continue + fi + + SOURCES=$(grep "^lib${LIB}_la_SOURCES" "$AMFILE" \ + | sed -e 's/.*=//' -e 's/\\//' -e 's:\.\./gnu/:lib/gnu/:') + + for SOURCE in $SOURCES; do + if + echo $SOURCE | grep -q "/" + then + echo "$SOURCE" + else + echo "$(dirname $AMFILE)/$SOURCE" + fi + done +} + +find_headers_as_of() { + local TAG + local LIB + local FILE + local PATTERN + + TAG="$1" + LIB="$2" + + for FILE in $(git ls-tree -r --name-only "$TAG"); do + for PATTERN in ${HEADERS[$LIB]}; do + if [[ $FILE =~ $PATTERN ]] && [[ ! $FILE =~ internal.h$ ]]; then + echo "$FILE" + break + fi + done + done +} + +extract_version() { + grep "lib${1}_la.*version-info" | sed -e 's/.*version-info\s*\(\S*\)/\1/' +} + +shared_lib_name() { + local LIB="$1" + local VERSION="$2" + + echo "lib${LIB}.so.$(echo $VERSION | cut -d: -f 1)" +} + +process_lib() { + local LIB="$1" + local LAST_RELEASE="$2" + local AMFILE + local SOURCES + local HEADERS_LAST + local HEADERS_HEAD + local HEADERS_DIFF + local HEADERS_GONE + local HEADERS_ADDED + local CHANGE + local DEFAULT_CHANGE + + if [ -z "${HEADERS[$LIB]}" ]; then + echo "Can't check lib$LIB until this script is updated with its headers" + prompt_to_continue + fi + + AMFILE="$(find_makefile "$LIB")" + + # Get current shared library version + VER_NOW=$(cat $AMFILE | extract_version $LIB) + + # Check whether library existed at last release + git cat-file -e $LAST_RELEASE:$AMFILE 2>/dev/null + if [ $? -ne 0 ]; then + echo "lib$LIB is new, not changing version ($VER_NOW)" + prompt_to_continue + echo "" + return + fi + + HEADERS_LAST="$(find_headers_as_of "$LAST_RELEASE" "$LIB")" + HEADERS_HEAD="$(find_headers_as_of "HEAD" "$LIB")" + HEADERS_DIFF="$(diff <(echo "$HEADERS_LAST") <(echo "$HEADERS_HEAD"))" + HEADERS_GONE="$(echo "$HEADERS_DIFF" | sed -n -e 's/^< //p')" + HEADERS_ADDED="$(echo "$HEADERS_DIFF" | sed -n -e 's/^> //p')" + + # Check whether there were any changes to headers or sources + SOURCES="$(find_sources "$LIB" "$AMFILE")" + if [ -n "$HEADERS_GONE" ]; then + DEFAULT_CHANGE="i" # Removed public header is incompatible change + elif [ -n "$HEADERS_ADDED" ]; then + DEFAULT_CHANGE="c" # Additions are likely compatible + elif git diff --quiet -w $LAST_RELEASE..HEAD $HEADERS_HEAD $SOURCES ; then + echo "No changes to $LIB interface" + prompt_to_continue + echo "" + return + else + DEFAULT_CHANGE="f" # Sources changed, so it's at least a fix + fi + + # Show all header changes since last release + echo "- Changes in lib$LIB public headers since $LAST_RELEASE:" + if [ -n "$HEADERS_GONE" ]; then + for HEADER in $HEADERS_GONE; do + echo "-- $HEADER was removed" + done + fi + if [ -n "$HEADERS_ADDED" ]; then + for HEADER in $HEADERS_ADDED; do + echo "++ $HEADER is new" + done + fi + git --no-pager diff --color -w $LAST_RELEASE..HEAD $HEADERS_HEAD + + echo "" + if yesno "Show commits (minus refactor/build/merge) touching lib$LIB since $LAST_RELEASE [y/N]?" + then + git log --color $LAST_RELEASE..HEAD -z $HEADERS_HEAD $SOURCES $AMFILE \ + | grep -vzE "Refactor:|Build:|Merge pull request" + echo + prompt_to_continue + fi + + # @TODO this seems broken ... + #echo "" + #if yesno "Show merged PRs touching lib$LIB since $LAST_RELEASE [y/N]?" + #then + # git log --merges $LAST_RELEASE..HEAD $HEADERS_HEAD $SOURCES $AMFILE + # echo + # prompt_to_continue + #fi + + # Show summary of source changes since last release + echo "" + echo "- Headers: $HEADERS_HEAD" + echo "- Changed sources since $LAST_RELEASE:" + git --no-pager diff --color -w $LAST_RELEASE..HEAD --stat $SOURCES + echo "" + + # Ask for human guidance + echo "Are the changes to lib$LIB:" + read -p "[c]ompatible additions, [i]ncompatible additions/removals or [f]ixes? [$DEFAULT_CHANGE]: " CHANGE + [ -z "$CHANGE" ] && CHANGE="$DEFAULT_CHANGE" + + # Get (and show) shared library version at last release + VER=$(git show $LAST_RELEASE:$AMFILE | extract_version $LIB) + VER_1=$(echo $VER | awk -F: '{print $1}') + VER_2=$(echo $VER | awk -F: '{print $2}') + VER_3=$(echo $VER | awk -F: '{print $3}') + echo "lib$LIB version at $LAST_RELEASE: $VER" + + # Show current shared library version if changed + if [ $VER_NOW != $VER ]; then + echo "lib$LIB version currently: $VER_NOW" + fi + + # Calculate new library version + case $CHANGE in + i|I) + echo "New backwards-incompatible version: x+1:0:0" + VER_1=$(expr $VER_1 + 1) + VER_2=0 + VER_3=0 + + # Some headers define constants for shared library names, + # update them if the name changed + for H in $HEADERS_HEAD; do + sed -i -e "s/$(shared_lib_name "$LIB" "$VER_NOW")/$(shared_lib_name "$LIB" "$VER_1:0:0")/" $H + done + ;; + c|C) + echo "New version with backwards-compatible extensions: x+1:0:z+1" + VER_1=$(expr $VER_1 + 1) + VER_2=0 + VER_3=$(expr $VER_3 + 1) + ;; + F|f) + echo "Code changed though interfaces didn't: x:y+1:z" + VER_2=$(expr $VER_2 + 1) + ;; + *) + echo "Not updating lib$LIB version" + prompt_to_continue + CHANGE="" + ;; + esac + VER_NEW=$VER_1:$VER_2:$VER_3 + + if [ ! -z $CHANGE ]; then + if [ "$VER_NEW" != "$VER_NOW" ]; then + echo "Updating lib$LIB version from $VER_NOW to $VER_NEW" + prompt_to_continue + sed -i "s/version-info\s*$VER_NOW/version-info $VER_NEW/" $AMFILE + else + echo "No version change needed for lib$LIB" + prompt_to_continue + fi + fi + echo "" +} + +echo "Definitions:" +echo "- Compatible additions: new public API functions, structs, etc." +echo "- Incompatible additions/removals: new arguments to public API functions," +echo " new members added to the middle of public API structs," +echo " removal of any public API, etc." +echo "- Fixes: any other code changes at all" +echo "" +echo "When possible, improve backward compatibility first:" +echo "- move new members to the end of structs" +echo "- use bitfields instead of booleans" +echo "- when adding arguments, create a new function that the old one can wrap" +echo "" +prompt_to_continue + +LAST_RELEASE=$(find_last_release "$1") +for LIB in $(find_libs); do + process_lib "$LIB" "$LAST_RELEASE" +done + +# Show all proposed changes +git --no-pager diff --color -w |