SDL_mixer: build: Use minor version number for feature releases

From d7542cc2832dc129127c7e290b1d275329259435 Mon Sep 17 00:00:00 2001
From: Simon McVittie <[EMAIL REDACTED]>
Date: Thu, 5 May 2022 17:20:29 +0100
Subject: [PATCH] build: Use minor version number for feature releases

Similar to what has been done in SDL itself, this allows micro versions
with an odd minor version to be used as prereleases for a stable
release. It also allows micro versions with an even minor version to
be used to fix regressions in their corresponding .0 release
if that becomes necessary.

Signed-off-by: Simon McVittie <smcv@collabora.com>
---
 .github/workflows/linux.yml               |   3 +
 CMakeLists.txt                            |  47 +++++++
 Makefile.os2                              |  10 +-
 README-versions.md                        |  59 +++++++++
 VisualC/Version.rc                        |   8 +-
 Xcode/Info-Framework.plist                |   4 +-
 Xcode/SDL_mixer.xcodeproj/project.pbxproj |   4 +-
 configure.ac                              |  43 ++++---
 include/SDL_mixer.h                       |  16 ++-
 release_checklist.md                      |  95 ++++++++++++++
 src/mixer.c                               |  23 ++++
 test-versioning.sh                        | 145 ++++++++++++++++++++++
 version.rc                                |   8 +-
 13 files changed, 435 insertions(+), 30 deletions(-)
 create mode 100644 README-versions.md
 create mode 100644 release_checklist.md
 create mode 100755 test-versioning.sh

diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml
index ec0bf3a4..61518e02 100644
--- a/.github/workflows/linux.yml
+++ b/.github/workflows/linux.yml
@@ -33,6 +33,9 @@ jobs:
           sudo apt -y update
           sudo apt -y install ninja-build libsdl2-dev
 
+      - name: Check that versioning is consistent
+        run: ./test-versioning.sh
+
       - name: Configure CMake
         env:
           CC: ${{matrix.compiler}}
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 31082e25..87367b54 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -10,6 +10,12 @@ project(SDL2_mixer C)
 # FIXME: missing CMakeLists.txt for MPG123
 set(SUPPORT_MP3_MPG123 OFF CACHE BOOL "" FORCE)
 
+# See docs/release_checklist.md
+set(MAJOR_VERSION 2)
+set(MINOR_VERSION 5)
+set(MICRO_VERSION 0)
+set(FULL_VERSION "${MAJOR_VERSION}.${MINOR_VERSION}.${MICRO_VERSION}")
+
 option(SUPPORT_WAV "Support loading WAVE music" ON)
 option(SUPPORT_FLAC "Support loading FLAC music with libFLAC" OFF)
 option(SUPPORT_OGG "Support loading OGG Vorbis music via Tremor" OFF)
@@ -32,6 +38,47 @@ if (NOT ANDROID AND NOT (TARGET SDL2 OR TARGET SDL2-static))
 	endif()
 endif()
 
+# Calculate a libtool-like version number
+math(EXPR BINARY_AGE "${MINOR_VERSION} * 100 + ${MICRO_VERSION}")
+if(MINOR_VERSION MATCHES "[02468]$")
+    # Stable branch, 2.6.1 -> libSDL2_mixer-2.0.so.0.600.1
+    set(INTERFACE_AGE ${MICRO_VERSION})
+else()
+    # Development branch, 2.5.1 -> libSDL2_mixer-2.0.so.0.501.0
+    set(INTERFACE_AGE 0)
+endif()
+
+# Increment this if there is an incompatible change - but if that happens,
+# we should rename the library from SDL2 to SDL3, at which point this would
+# reset to 0 anyway.
+set(LT_MAJOR "0")
+
+math(EXPR LT_AGE "${BINARY_AGE} - ${INTERFACE_AGE}")
+math(EXPR LT_CURRENT "${LT_MAJOR} + ${LT_AGE}")
+set(LT_REVISION "${INTERFACE_AGE}")
+# For historical reasons, the library name redundantly includes the major
+# version twice: libSDL2_mixer-2.0.so.0.
+# TODO: in SDL 3, set the OUTPUT_NAME to plain SDL3_mixer, which will simplify
+# it to libSDL3_mixer.so.0
+set(LT_RELEASE "2.0")
+set(LT_VERSION "${LT_MAJOR}.${LT_AGE}.${LT_REVISION}")
+
+# The following should match the versions in the Xcode project file.
+# Each version is 1 higher than you might expect, for compatibility
+# with libtool: macOS ABI versioning is 1-based, unlike other platforms
+# which are normally 0-based.
+math(EXPR DYLIB_CURRENT_VERSION_MAJOR "${LT_MAJOR} + ${LT_AGE} + 1")
+math(EXPR DYLIB_CURRENT_VERSION_MINOR "${LT_REVISION}")
+math(EXPR DYLIB_COMPAT_VERSION_MAJOR "${LT_MAJOR} + 1")
+set(DYLIB_CURRENT_VERSION "${DYLIB_CURRENT_VERSION_MAJOR}.${DYLIB_CURRENT_VERSION_MINOR}.0")
+# For historical reasons this is 3.0.0 rather than the expected 1.0.0
+set(DYLIB_COMPATIBILITY_VERSION "3.0.0")
+
+# For the static assertions in mixer.c
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DSDL_BUILD_MAJOR_VERSION=${MAJOR_VERSION}")
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DSDL_BUILD_MINOR_VERSION=${MINOR_VERSION}")
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DSDL_BUILD_MICRO_VERSION=${MICRO_VERSION}")
+
 include_directories(include src src/codecs)
 
 add_library(SDL2_mixer)
diff --git a/Makefile.os2 b/Makefile.os2
index a72d2e4a..de9ddc91 100644
--- a/Makefile.os2
+++ b/Makefile.os2
@@ -5,7 +5,10 @@
 # your own environment!.
 
 LIBNAME = SDL2mix
-VERSION = 2.0.4
+MAJOR_VERSION = 2
+MINOR_VERSION = 5
+MICRO_VERSION = 0
+VERSION = $(MAJOR_VERSION).$(MINOR_VERSION).$(MICRO_VERSION)
 
 TITLENAME = $(LIBNAME) $(VERSION)
 
@@ -135,6 +138,11 @@ CFLAGS+= -DMUSIC_MOD_MODPLUG
 LIBS+= modplug.lib
 !endif
 
+# For the static assertions in mixer.c
+CFLAGS+= -DSDL_BUILD_MAJOR_VERSION=$(MAJOR_VERSION)
+CFLAGS+= -DSDL_BUILD_MINOR_VERSION=$(MINOR_VERSION)
+CFLAGS+= -DSDL_BUILD_MICRO_VERSION=$(MICRO_VERSION)
+
 all: $(DLLFILE) playwave.exe playmus.exe
 
 $(LIBFILE): $(DLLFILE)
diff --git a/README-versions.md b/README-versions.md
new file mode 100644
index 00000000..42bc7003
--- /dev/null
+++ b/README-versions.md
@@ -0,0 +1,59 @@
+# Versioning
+
+## Since 2.5.0
+
+`SDL_mixer` follows an "odd/even" versioning policy, similar to GLib, GTK, Flatpak
+and older versions of the Linux kernel:
+
+* The major version (first part) increases when backwards compatibility
+    is broken, which will happen infrequently.
+
+* If the minor version (second part) is divisible by 2
+    (for example 2.6.x, 2.8.x), this indicates a version that
+    is believed to be stable and suitable for production use.
+
+    * In stable releases, the patchlevel or micro version (third part)
+        indicates bugfix releases. Bugfix releases should not add or
+        remove ABI, so the ".0" release (for example 2.6.0) should be
+        forwards-compatible with all the bugfix releases from the
+        same cycle (for example 2.6.1).
+
+    * The minor version increases when new API or ABI is added, or when
+        other significant changes are made. Newer minor versions are
+        backwards-compatible, but not fully forwards-compatible.
+        For example, programs built against `SDL_mixer` 2.6.x should work fine
+        with 2.8.x, but programs built against 2.8.x will not necessarily
+        work with 2.6.x.
+
+* If the minor version (second part) is not divisible by 2
+    (for example 2.5.x, 2.7.x), this indicates a development prerelease
+    that is not suitable for stable software distributions.
+    Use with caution.
+
+    * The patchlevel or micro version (third part) increases with
+        each prerelease.
+
+    * Each prerelease might add new API and/or ABI.
+
+    * Prereleases are backwards-compatible with older stable branches.
+        For example, 2.7.x will be backwards-compatible with 2.6.x.
+
+    * Prereleases are not guaranteed to be backwards-compatible with
+        each other. For example, new API or ABI added in 2.5.1
+        might be removed or changed in 2.5.2.
+        If this would be a problem for you, please do not use prereleases.
+
+    * Only upgrade to a prerelease if you can guarantee that you will
+        promptly upgrade to the stable release that follows it.
+        For example, do not upgrade to 2.5.x unless you will be able to
+        upgrade to 2.6.0 when it becomes available.
+
+    * Software distributions that have a freeze policy (in particular Linux
+        distributions with a release cycle, such as Debian and Fedora)
+        should usually only package stable releases, and not prereleases.
+
+## Before 2.5.0
+
+Older versions of `SDL_mixer` used the patchlevel (micro version,
+third part) for feature releases, and did not distinguish between feature
+and bugfix releases.
diff --git a/VisualC/Version.rc b/VisualC/Version.rc
index 5f6a4739..43567db7 100644
--- a/VisualC/Version.rc
+++ b/VisualC/Version.rc
@@ -28,8 +28,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
 //
 
 VS_VERSION_INFO VERSIONINFO
- FILEVERSION 2,0,4,0
- PRODUCTVERSION 2,0,4,0
+ FILEVERSION 2,5,0,0
+ PRODUCTVERSION 2,5,0,0
  FILEFLAGSMASK 0x3fL
 #ifdef _DEBUG
  FILEFLAGS 0x1L
@@ -46,12 +46,12 @@ BEGIN
         BEGIN
             VALUE "CompanyName", "\0"
             VALUE "FileDescription", "SDL_mixer\0"
-            VALUE "FileVersion", "2, 0, 4, 0\0"
+            VALUE "FileVersion", "2, 5, 0, 0\0"
             VALUE "InternalName", "SDL_mixer\0"
             VALUE "LegalCopyright", "Copyright  2022 Sam Lantinga\0"
             VALUE "OriginalFilename", "SDL_mixer.dll\0"
             VALUE "ProductName", "Simple DirectMedia Layer\0"
-            VALUE "ProductVersion", "2, 0, 4, 0\0"
+            VALUE "ProductVersion", "2, 5, 0, 0\0"
         END
     END
     BLOCK "VarFileInfo"
diff --git a/Xcode/Info-Framework.plist b/Xcode/Info-Framework.plist
index 98507745..f31048f6 100644
--- a/Xcode/Info-Framework.plist
+++ b/Xcode/Info-Framework.plist
@@ -15,8 +15,8 @@
 	<key>CFBundlePackageType</key>
 	<string>FMWK</string>
 	<key>CFBundleShortVersionString</key>
-	<string>2.0.4</string>
+	<string>2.5.0</string>
 	<key>CFBundleVersion</key>
-	<string>2.0.4</string>
+	<string>2.5.0</string>
 </dict>
 </plist>
diff --git a/Xcode/SDL_mixer.xcodeproj/project.pbxproj b/Xcode/SDL_mixer.xcodeproj/project.pbxproj
index 488a9e09..eff2aa7f 100644
--- a/Xcode/SDL_mixer.xcodeproj/project.pbxproj
+++ b/Xcode/SDL_mixer.xcodeproj/project.pbxproj
@@ -1108,7 +1108,7 @@
 				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
 				DEPLOYMENT_POSTPROCESSING = YES;
 				DYLIB_COMPATIBILITY_VERSION = 3.0.0;
-				DYLIB_CURRENT_VERSION = 3.2.0;
+				DYLIB_CURRENT_VERSION = 501.0.0;
 				DYLIB_INSTALL_NAME_BASE = "@rpath";
 				FRAMEWORK_SEARCH_PATHS = "\"$(SRCROOT)/$(PLATFORM)\"";
 				GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
@@ -1161,7 +1161,7 @@
 				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
 				COPY_PHASE_STRIP = NO;
 				DYLIB_COMPATIBILITY_VERSION = 3.0.0;
-				DYLIB_CURRENT_VERSION = 3.2.0;
+				DYLIB_CURRENT_VERSION = 501.0.0;
 				DYLIB_INSTALL_NAME_BASE = "@rpath";
 				ENABLE_TESTABILITY = YES;
 				FRAMEWORK_SEARCH_PATHS = "\"$(SRCROOT)/$(PLATFORM)\"";
diff --git a/configure.ac b/configure.ac
index 25c7d611..a8cc70fa 100644
--- a/configure.ac
+++ b/configure.ac
@@ -5,21 +5,21 @@ AC_CONFIG_AUX_DIR(build-scripts)
 
 dnl Set various version strings - taken gratefully from the GTk sources
 
-# Making releases:
-#   MICRO_VERSION += 1;
-#   INTERFACE_AGE += 1;
-#   BINARY_AGE += 1;
-# if any functions have been added, set INTERFACE_AGE to 0.
-# if backwards compatibility has been broken,
-# set BINARY_AGE and INTERFACE_AGE to 0.
-#
+# See release_checklist.md
 MAJOR_VERSION=2
-MINOR_VERSION=0
-MICRO_VERSION=4
-INTERFACE_AGE=2
-BINARY_AGE=4
+MINOR_VERSION=5
+MICRO_VERSION=0
 VERSION=$MAJOR_VERSION.$MINOR_VERSION.$MICRO_VERSION
 
+BINARY_AGE=`expr $MINOR_VERSION \* 100 + $MICRO_VERSION`
+AS_CASE(["$MINOR_VERSION"],
+  [*@<:@02468@:>@],
+    dnl Stable branch, 2.6.1 -> libSDL2_mixer-2.0.so.0.600.1
+    [INTERFACE_AGE="$MICRO_VERSION"],
+  [*],
+    dnl Development branch, 2.5.1 -> libSDL2_mixer-2.0.so.0.501.0
+    [INTERFACE_AGE=0])
+
 AC_SUBST(MAJOR_VERSION)
 AC_SUBST(MINOR_VERSION)
 AC_SUBST(MICRO_VERSION)
@@ -30,10 +30,20 @@ AC_SUBST(VERSION)
 dnl libtool versioning
 LT_INIT([win32-dll])
 
-LT_RELEASE=$MAJOR_VERSION.$MINOR_VERSION
-LT_CURRENT=`expr $MICRO_VERSION - $INTERFACE_AGE`
+# For historical reasons, the library name redundantly includes the major
+# version twice: libSDL2_mixer-2.0.so.0.
+# TODO: in SDL 3, stop using -release, which will simplify it to libSDL3_mixer.so.0
+LT_RELEASE=2.0
+
+# Increment this if there is an incompatible change - but if that happens,
+# we should rename the library from SDL2 to SDL3, at which point this would
+# reset to 0 anyway.
+LT_MAJOR=0
+
 LT_REVISION=$INTERFACE_AGE
 LT_AGE=`expr $BINARY_AGE - $INTERFACE_AGE`
+LT_CURRENT=`expr $LT_MAJOR + $LT_AGE`
+
 LT_EXTRA="" dnl for OS2 dll name
 m4_pattern_allow([^LT_])
 
@@ -43,6 +53,11 @@ AC_SUBST(LT_REVISION)
 AC_SUBST(LT_AGE)
 AC_SUBST(LT_EXTRA)
 
+dnl For use in static assertions
+AC_DEFINE_UNQUOTED([SDL_BUILD_MAJOR_VERSION], $MAJOR_VERSION, [ ])
+AC_DEFINE_UNQUOTED([SDL_BUILD_MINOR_VERSION], $MINOR_VERSION, [ ])
+AC_DEFINE_UNQUOTED([SDL_BUILD_MICRO_VERSION], $MICRO_VERSION, [ ])
+
 dnl Detect the canonical build and host environments
 dnl AC_CANONICAL_HOST
 
diff --git a/include/SDL_mixer.h b/include/SDL_mixer.h
index 173278e0..c508956a 100644
--- a/include/SDL_mixer.h
+++ b/include/SDL_mixer.h
@@ -37,8 +37,8 @@ extern "C" {
 /* Printable format: "%d.%d.%d", MAJOR, MINOR, PATCHLEVEL
 */
 #define SDL_MIXER_MAJOR_VERSION 2
-#define SDL_MIXER_MINOR_VERSION 0
-#define SDL_MIXER_PATCHLEVEL    4
+#define SDL_MIXER_MINOR_VERSION 5
+#define SDL_MIXER_PATCHLEVEL    0
 
 /* This macro can be used to fill a version structure with the compile-time
  * version of the SDL_mixer library.
@@ -56,17 +56,27 @@ extern "C" {
 #define MIX_PATCHLEVEL      SDL_MIXER_PATCHLEVEL
 #define MIX_VERSION(X)      SDL_MIXER_VERSION(X)
 
+#if SDL_MIXER_MAJOR_VERSION < 3 && SDL_MAJOR_VERSION < 3
 /**
  *  This is the version number macro for the current SDL_mixer version.
+ *
+ *  In versions higher than 2.9.0, the minor version overflows into
+ *  the thousands digit: for example, 2.23.0 is encoded as 4300.
+ *  This macro will not be available in SDL 3.x or SDL_mixer 3.x.
+ *
+ *  Deprecated, use SDL_MIXER_VERSION_ATLEAST or SDL_MIXER_VERSION instead.
  */
 #define SDL_MIXER_COMPILEDVERSION \
     SDL_VERSIONNUM(SDL_MIXER_MAJOR_VERSION, SDL_MIXER_MINOR_VERSION, SDL_MIXER_PATCHLEVEL)
+#endif /* SDL_MIXER_MAJOR_VERSION < 3 && SDL_MAJOR_VERSION < 3 */
 
 /**
  *  This macro will evaluate to true if compiled with SDL_mixer at least X.Y.Z.
  */
 #define SDL_MIXER_VERSION_ATLEAST(X, Y, Z) \
-    (SDL_MIXER_COMPILEDVERSION >= SDL_VERSIONNUM(X, Y, Z))
+    ((SDL_MIXER_MAJOR_VERSION >= X) && \
+     (SDL_MIXER_MAJOR_VERSION > X || SDL_MIXER_MINOR_VERSION >= Y) && \
+     (SDL_MIXER_MAJOR_VERSION > X || SDL_MIXER_MINOR_VERSION > Y || SDL_MIXER_PATCHLEVEL >= Z))
 
 /* This function gets the version of the dynamically linked SDL_mixer library.
    it should NOT be used to fill a version structure, instead you should
diff --git a/release_checklist.md b/release_checklist.md
new file mode 100644
index 00000000..7f4a10ed
--- /dev/null
+++ b/release_checklist.md
@@ -0,0 +1,95 @@
+# Release checklist
+
+## New feature release
+
+* Update `CHANGES.txt`
+
+* Bump version number to 2.EVEN.0 in all these locations:
+
+    * `include/SDL_mixer.h`:
+        `SDL_MIXER_MAJOR_VERSION`, `SDL_MIXER_MINOR_VERSION`, `SDL_MIXER_PATCHLEVEL`
+    * `configure.ac`:
+        `MAJOR_VERSION`, `MINOR_VERSION`, `MICRO_VERSION`
+    * `CMakeLists.txt`:
+        `MAJOR_VERSION`, `MINOR_VERSION`, `MICRO_VERSION`
+    * `Makefile.os2`:
+        `MAJOR_VERSION`, `MINOR_VERSION`, `MICRO_VERSION`
+    * `version.rc`:
+        `FILEVERSION`, `PRODUCTVERSION`, `FileVersion`, `ProductVersion`
+    * `VisualC/Version.rc`:
+        `FILEVERSION`, `PRODUCTVERSION`, `FileVersion`, `ProductVersion`
+
+* Bump ABI version information
+
+    * `Xcode/SDL_mixer.xcodeproj/project.pbxproj`:
+        `DYLIB_CURRENT_VERSION`, `DYLIB_COMPATIBILITY_VERSION`
+        * set first number in `DYLIB_CURRENT_VERSION` to
+            (100 * *minor*) + 1
+        * set second number in `DYLIB_CURRENT_VERSION` to 0
+        * if backwards compatibility has been broken,
+            increase `DYLIB_COMPATIBILITY_VERSION` (?)
+
+* Run `./test-versioning.sh` to verify that everything is consistent
+
+* Regenerate `configure`
+
+* Do the release
+
+## New bugfix release
+
+* Check that no new API/ABI was added
+
+    * If it was, do a new feature release (see above) instead
+
+* Bump version number from 2.Y.Z to 2.Y.(Z+1) (Y is even)
+
+    * Same places as listed above
+
+* Bump ABI version information
+
+    * `Xcode/SDL_mixer.xcodeproj/project.pbxproj`:
+        `DYLIB_CURRENT_VERSION`, `DYLIB_COMPATIBILITY_VERSION`
+        * set second number in `DYLIB_CURRENT_VERSION` to *patchlevel*
+
+* Run test/versioning.sh to verify that everything is consistent
+
+* Regenerate `configure`
+
+* Do the release
+
+## After a feature release
+
+* Create a branch like `release-2.6.x`
+
+* Bump version number to 2.ODD.0 for next development branch
+
+    * Same places as listed above
+
+* Bump ABI version information
+
+    * Same places as listed above
+    * Assume that the next feature release will contain new API/ABI
+
+* Run test/versioning.sh to verify that everything is consistent
+
+## New development prerelease
+
+* Bump version number from 2.Y.Z to 2.Y.(Z+1) (Y is odd)
+
+    * Same places as listed above
+
+* Bump ABI version information
+
+    * `Xcode/SDL_mixer.xcodeproj/project.pbxproj`:
+        `DYLIB_CURRENT_VERSION`, `DYLIB_COMPATIBILITY_VERSION`
+        * set first number in `DYLIB_CURRENT_VERSION` to
+            (100 * *minor*) + *patchlevel* + 1
+        * set second number in `DYLIB_CURRENT_VERSION` to 0
+        * if backwards compatibility has been broken,
+            increase `DYLIB_COMPATIBILITY_VERSION` (?)
+
+* Run test/versioning.sh to verify that everything is consistent
+
+* Regenerate `configure`
+
+* Do the release
diff --git a/src/mixer.c b/src/mixer.c
index 61a3ca56..cee3b31f 100644
--- a/src/mixer.c
+++ b/src/mixer.c
@@ -36,6 +36,29 @@
 #define FORM        0x4d524f46      /* "FORM" */
 #define CREA        0x61657243      /* "Crea" */
 
+#if defined(SDL_BUILD_MAJOR_VERSION) && defined(SDL_COMPILE_TIME_ASSERT)
+SDL_COMPILE_TIME_ASSERT(SDL_BUILD_MAJOR_VERSION,
+                        SDL_MIXER_MAJOR_VERSION == SDL_BUILD_MAJOR_VERSION);
+SDL_COMPILE_TIME_ASSERT(SDL_BUILD_MINOR_VERSION,
+                        SDL_MIXER_MINOR_VERSION == SDL_BUILD_MINOR_VERSION);
+SDL_COMPILE_TIME_ASSERT(SDL_BUILD_MICRO_VERSION,
+                        SDL_MIXER_PATCHLEVEL == SDL_BUILD_MICRO_VERSION);
+#endif
+
+#if defined(SDL_COMPILE_TIME_ASSERT)
+SDL_COMPILE_TIME_ASSERT(SDL_MIXER_MAJOR_VERSION_min, SDL_MIXER_MAJOR_VERSION >= 0);
+/* Limited only by the need to fit in SDL_version */
+SDL_COMPILE_TIME_ASSERT(SDL_MIXER_MAJOR_VERSION_max, SDL_MIXER_MAJOR_VERSION <= 255);
+
+SDL_COMPILE_TIME_ASSERT(SDL_MIXER_MINOR_VERSION_min, SDL_MIXER_MINOR_VERSION >= 0);
+/* Limited only by the need to fit in SDL_version */
+SDL_COMPILE_TIME_ASSERT(SDL_MIXER_MINOR_VERSION_max, SDL_MIXER_MINOR_VERSION <= 255);
+
+SDL_COMPILE_TIME_ASSERT(SDL_MIXER_PATCHLEVEL_min, SDL_MIXER_PATCHLEVEL >= 0);
+/* Limited by its encoding in SDL_VERSIONNUM and in the ABI versions */
+SDL_COMPILE_TIME_ASSERT(SDL_MIXER_PATCHLEVEL_max, SDL_MIXER_PATCHLEVEL <= 99);
+#endif
+
 static int audio_opened = 0;
 static SDL_AudioSpec mixer;
 static SDL_AudioDeviceID audio_device;
diff --git a/test-versioning.sh b/test-versioning.sh
new file mode 100755
index 00000000..57b6a09a
--- /dev/null
+++ b/test-versioning.sh
@@ -0,0 +1,145 @@
+#!/bin/sh
+# Copyright 2022 Collabora Ltd.
+# SPDX-License-Identifier: Zlib
+
+set -eu
+
+ref_major=$(sed -ne 's/^#define SDL_MIXER_MAJOR_VERSION  *//p' include/SDL_mixer.h)
+ref_minor=$(sed -ne 's/^#define SDL_MIXER_MINOR_VERSION  *//p' include/SDL_mixer.h)
+ref_micro=$(sed -ne 's/^#define SDL_MIXER_PATCHLEVEL  *//p' include/SDL_mixer.h)
+ref_version="${ref_major}.${ref_minor}.${ref_micro}"
+
+tests=0
+failed=0
+
+ok () {
+    tests=$(( tests + 1 ))
+    echo "ok - $*"
+}
+
+not_ok () {
+    tests=$(( tests + 1 ))
+    echo "not ok - $*"
+    failed=1
+}
+
+major=$(sed -ne 's/^MAJOR_VERSION=//p' configure.ac)
+minor=$(sed -ne 's/^MINOR_VERSION=//p' configure.ac)
+micro=$(sed -ne 's/^MICRO_VERSION=//p' configure.ac)
+version="${major}.${minor}.${micro}"
+
+if [ "$ref_version" = "$version" ]; then
+    ok "configure.ac $version"
+else
+    not_ok "configure.ac $version disagrees with SDL_mixer.h $ref_version"
+fi
+
+major=$(sed -ne 's/^set(MAJOR_VERSION \([0-9]\+\))$/\1/p' CMakeLists.txt)
+minor=$(sed -ne 's/^set(MINOR_VERSION \([0-9]\+\))$/\1/p' CMakeLists.txt)
+micro=$(sed -ne 's/^set(MICRO_VERSION \([0-9]\+\))$/\1/p' CMakeLists.txt)
+version="${major}.${minor}.${micro}"
+
+if [ "$ref_version" = "$version" ]; then
+    ok "CMakeLists.txt $version"
+else
+    not_ok "CMakeLists.txt $version disagrees with SDL_mixer.h $ref_version"
+fi
+
+major=$(sed -ne 's/^MAJOR_VERSION *= *//p' Makefile.os2)
+minor=$(sed -ne 's/^MINOR_VERSION *= *//p' Makefile.os2)
+micro=$(sed -ne 's/^MICRO_VERSION *= *//p' Makefile.os2)
+version="${major}.${minor}.${micro}"
+
+if [ "$ref_version" = "$version" ]; then
+    ok "Makefile.os2 $version"
+else
+    not_ok "Makefile.os2 $version disagrees with SDL_mixer.h $ref_version"
+fi
+
+for rcfile in version.rc VisualC/Version.rc; do
+    tuple=$(sed -ne 's/^ *FILEVERSION *//p' "$rcfile" | tr -d '\r')
+    ref_tuple="${ref_major},${ref_minor},${ref_micro},0"
+
+    if [ "$ref_tuple" = "$tuple" ]; then
+        ok "$rcfile FILEVERSION $tuple"
+    else
+        not_ok "$rcfile FILEVERSION $tuple disagrees with SDL_mixer.h $ref_tuple"
+    fi
+
+    tuple=$(sed -ne 's/^ *PRODUCTVERSION *//p' "$rcfile" | tr -d '\r')
+
+    if [ "$ref_tuple" = "$tuple" ]; then
+        ok "$rcfile PRODUCTVERSION $tuple"
+    else
+        not_ok "$rcfile PRODUCTVERSION $tuple disagrees with SDL_mixer.h $ref_tuple"
+    fi
+
+    tuple=$(sed -Ene 's/^ *VALUE "FileVersion", "([0-9, ]+)\\0"\r?$/\1/p' "$rcfile" | tr -d '\r')
+    ref_tuple="${ref_major}, ${ref_minor}, ${ref_micro}, 0"
+
+    if [ "$ref_tuple" = "$tuple" ]; then
+        ok "$rcfile FileVersion $tuple"
+    else
+        not_ok "$rcfile FileVersion $tuple disagrees with SDL_mixer.h $ref_tuple"
+    fi
+
+    tuple=$(sed -Ene 's/^ *VALUE "ProductVersion", "([0-9, ]+)\\0"\r?$/\1/p' "$rcfile" | tr -d '\r')
+
+    if [ "$ref_tuple" = "$tuple" ]; then
+        ok "$rcfile ProductVersion $tuple"
+    else
+        not_ok "$rcfile ProductVersion $tuple disagrees with SDL_mixer.h $ref_tuple"
+    fi
+done
+
+version=$(sed -Ene '/CFBundleShortVersionString/,+1 s/.*<string>(.*)<\/string>.*/\1/p' Xcode/Info-Framework.plist)
+
+if [ "$ref_version" = "$version" ]; then
+    ok "Info-Framework.plist CFBundleShortVersionString $version"
+else
+    not_ok "Info-Framework.plist CFBundleShortVersionString $version disagrees with SDL_mixer.h $ref_version"
+fi
+
+version=$(sed -Ene '/CFBundleVersion/,+1 s/.*<string>(.*)<\/string>.*/\1/p' Xcode/Info-Framework.plist)
+
+if [ "$ref_version" = "$version" ]; then
+    ok "Info-Framework.plist CFBundleVersion $version"
+else
+    not_ok "Info-Framework.plist CFBundleVersion $version disagrees with SDL_mixer.h $ref_version"
+fi
+
+# For simplicity this assumes we'll never break ABI before SDL 3.
+dylib_compat=$(sed -Ene 's/.*DYLIB_COMPATIBILITY_VERSION = (.*);$/\1/p' Xcode/SDL_mixer.xcodeproj/project.pbxproj)
+ref='3.0.0
+3.0.0'
+
+if [ "$ref" = "$dylib_compat" ]; then
+    ok "project.pbxproj DYLIB_COMPATIBILITY_VERSION is consistent"
+else
+    not_ok "project.pbxproj DYLIB_COMPATIBILITY_VERSION is inconsistent"
+fi
+
+dylib_cur=$(sed -Ene 's/.*DYLIB_CURRENT_VERSION = (.*);$/\1/p' Xcode/SDL_mixer.xcodeproj/project.pbxproj)
+
+case "$ref_minor" in
+    (*[02468])
+        major="$(( ref_minor * 100 + 1 ))"
+        minor="$ref_micro"
+        ;;
+    (*)
+        major="$(( ref_minor * 100 + ref_micro + 1 ))"
+        minor="0"
+        ;;
+esac
+
+ref="${major}.${minor}.0
+${major}.${minor}.0"
+
+if [ "$ref" = "$dylib_cur" ]; then
+    ok "project.pbxproj DYLIB_CURRENT_VERSION is consistent"
+else
+    not_ok "project.pbxproj DYLIB_CURRENT_VERSION is inconsistent"
+fi
+
+echo "1..$tests"
+exit "$failed"
diff --git a/version.rc b/version.rc
index cec8d109..554878b9 100644
--- a/version.rc
+++ b/version.rc
@@ -9,8 +9,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
 //
 
 VS_VERSION_INFO VERSIONINFO
- FILEVERSION 2,0,4,0
- PRODUCTVERSION 2,0,4,0
+ FILEVERSION 2,5,0,0
+ PRODUCTVERSION 2,5,0,0
  FILEFLAGSMASK 0x3fL
  FILEFLAGS 0x0L
  FILEOS 0x40004L
@@ -23,12 +23,12 @@ BEGIN
         BEGIN
             VALUE "CompanyName", "\0"
             VALUE "FileDescription", "SDL_mixer\0"
-            VALUE "FileVersion", "2, 0, 4, 0\0"
+            VALUE "FileVersion", "2, 5, 0, 0\0"
             VALUE "InternalName", "SDL_mixer\0"
             VALUE "LegalCopyright", "Copyright  2022 Sam Lantinga\0"
             VALUE "OriginalFilename", "SDL_mixer.dll\0"
             VALUE "ProductName", "Simple DirectMedia Layer\0"
-            VALUE "ProductVersion", "2, 0, 4, 0\0"
+            VALUE "ProductVersion", "2, 5, 0, 0\0"
         END
     END
     BLOCK "VarFileInfo"