SDL_mixer: Rewrite CMake build script

From 52d3f2c2a870d6b707b95fbfad6c814a66eb485e Mon Sep 17 00:00:00 2001
From: Anonymous Maarten <[EMAIL REDACTED]>
Date: Tue, 17 May 2022 10:14:25 +0200
Subject: [PATCH] Rewrite CMake build script

---
 .github/fetch_sdl_vc.ps1                      |  40 +
 .github/fetch_yasm.ps1                        |  33 +
 .github/workflows/main.yml                    | 127 ++-
 CMakeLists.txt                                | 996 +++++++++++++++---
 Makefile.in                                   |   7 +
 SDL2_mixerConfig.cmake.in                     | 101 ++
 .../cmake/sdl2_mixer-config-version.cmake     |  54 +
 .../pkg-support/cmake/sdl2_mixer-config.cmake |  76 ++
 .../CMake/sdl2_mixer-config-version.cmake     |  48 +
 .../resources/CMake/sdl2_mixer-config.cmake   |  64 ++
 Xcode/pkg-support/resources/ReadMe.txt        |  12 +
 cmake/CommonFindSDL2.cmake                    |  22 +
 cmake/FindFLAC.cmake                          |  32 +
 cmake/FindFluidSynth.cmake                    |  32 +
 cmake/FindMPG123.cmake                        |  32 +
 cmake/FindPrivateSDL2.cmake                   |  47 +
 cmake/FindSDL2main.cmake                      |  24 +
 cmake/Findlibxmp-lite.cmake                   |  31 +
 cmake/Findlibxmp.cmake                        |  30 +
 cmake/Findmodplug.cmake                       |  33 +
 cmake/Findopusfile.cmake                      |  37 +
 cmake/Findtremor.cmake                        |  32 +
 cmake/Findvorbisfile.cmake                    |  32 +
 cmake/PrivateSdlFunctions.cmake               | 243 +++++
 cmake/test/CMakeLists.txt                     |  49 +
 cmake/test/main.c                             |  18 +
 configure                                     | 169 ++-
 configure.ac                                  |  86 +-
 external/flac                                 |   2 +-
 external/ogg                                  |   2 +-
 external/opusfile                             |   2 +-
 external/tremor                               |   2 +-
 .../cmake/sdl2_mixer-config-version.cmake     |  19 +
 .../pkg-support/cmake/sdl2_mixer-config.cmake |  22 +
 sdl2_mixer-config-version.cmake.in            |  12 +
 sdl2_mixer-config.cmake.in                    | 145 +++
 src/codecs/music_cmd.c                        |   4 +-
 test-versioning.sh                            |  20 +-
 38 files changed, 2482 insertions(+), 255 deletions(-)
 create mode 100644 .github/fetch_sdl_vc.ps1
 create mode 100644 .github/fetch_yasm.ps1
 create mode 100644 SDL2_mixerConfig.cmake.in
 create mode 100644 VisualC/pkg-support/cmake/sdl2_mixer-config-version.cmake
 create mode 100644 VisualC/pkg-support/cmake/sdl2_mixer-config.cmake
 create mode 100644 Xcode/pkg-support/resources/CMake/sdl2_mixer-config-version.cmake
 create mode 100644 Xcode/pkg-support/resources/CMake/sdl2_mixer-config.cmake
 create mode 100644 cmake/CommonFindSDL2.cmake
 create mode 100644 cmake/FindFLAC.cmake
 create mode 100644 cmake/FindFluidSynth.cmake
 create mode 100644 cmake/FindMPG123.cmake
 create mode 100644 cmake/FindPrivateSDL2.cmake
 create mode 100644 cmake/FindSDL2main.cmake
 create mode 100644 cmake/Findlibxmp-lite.cmake
 create mode 100644 cmake/Findlibxmp.cmake
 create mode 100644 cmake/Findmodplug.cmake
 create mode 100644 cmake/Findopusfile.cmake
 create mode 100644 cmake/Findtremor.cmake
 create mode 100644 cmake/Findvorbisfile.cmake
 create mode 100644 cmake/PrivateSdlFunctions.cmake
 create mode 100644 cmake/test/CMakeLists.txt
 create mode 100644 cmake/test/main.c
 create mode 100644 mingw/pkg-support/cmake/sdl2_mixer-config-version.cmake
 create mode 100644 mingw/pkg-support/cmake/sdl2_mixer-config.cmake
 create mode 100644 sdl2_mixer-config-version.cmake.in
 create mode 100644 sdl2_mixer-config.cmake.in

diff --git a/.github/fetch_sdl_vc.ps1 b/.github/fetch_sdl_vc.ps1
new file mode 100644
index 00000000..2e3cc7c6
--- /dev/null
+++ b/.github/fetch_sdl_vc.ps1
@@ -0,0 +1,40 @@
+$ErrorActionPreference = "Stop"
+
+$project_root = "$psScriptRoot\.."
+Write-Output "project_root: $project_root"
+
+$sdl2_version = "2.0.9"
+$sdl2_zip = "SDL2-devel-$($sdl2_version)-VC.zip"
+
+$sdl2_url = "https://github.com/libsdl-org/SDL/releases/download/release-$($sdl2_version)/SDL2-devel-$($sdl2_version)-VC.zip"
+$sdl2_dlpath = "$($Env:TEMP)\$sdl2_zip"
+
+$sdl2_bindir = "$($project_root)"
+$sdl2_extractdir = "$($sdl2_bindir)\SDL2-$($sdl2_version)"
+$sdl2_root_name = "SDL2-devel-VC"
+
+echo "sdl2_bindir:     $sdl2_bindir"
+echo "sdl2_extractdir: $sdl2_extractdir"
+echo "sdl2_root_name:  $sdl2_root_name"
+
+echo "Cleaning previous artifacts"
+if (Test-Path $sdl2_extractdir) {
+    Remove-Item $sdl2_extractdir -Recurse -Force
+}
+if (Test-Path "$($sdl2_bindir)/$sdl2_root_name") {
+    Remove-Item "$($sdl2_bindir)/$sdl2_root_name" -Recurse -Force
+}
+if (Test-Path $sdl2_dlpath) {
+    Remove-Item $sdl2_dlpath -Force
+}
+
+Write-Output "Downloading $sdl2_url"
+Invoke-WebRequest -Uri $sdl2_url -OutFile $sdl2_dlpath
+
+Write-Output "Extracting archive"
+Expand-Archive $sdl2_dlpath -DestinationPath $sdl2_bindir
+
+Write-Output "Setting up SDL2 folder"
+Rename-Item $sdl2_extractdir $sdl2_root_name
+
+Write-Output "Done"
diff --git a/.github/fetch_yasm.ps1 b/.github/fetch_yasm.ps1
new file mode 100644
index 00000000..5353e46f
--- /dev/null
+++ b/.github/fetch_yasm.ps1
@@ -0,0 +1,33 @@
+$ErrorActionPreference = "Stop"
+
+$project_root = "$psScriptRoot\.."
+Write-Output "project_root: $project_root"
+
+$yasm_version = "1.3.0"
+$yasm_dlexe = "yasm-$yasm_version-win64.exe"
+
+$yasm_url = "https://github.com/yasm/yasm/releases/download/v$yasm_version/$yasm_dlexe"
+$yasm_exename = "yasm.exe"
+$yasm_exepath = "$project_root/yasm.exe"
+
+$yasm_dlpath = "$project_root\$yasm_dlexe"
+
+echo "yasm_dlpath:  $yasm_dlpath"
+echo "yasm_exename: $yasm_exename"
+echo "yasm_exepath: $yasm_exepath"
+
+echo "Cleaning previous artifacts"
+if (Test-Path $yasm_dlpath) {
+    Remove-Item $yasm_dlpath -Force
+}
+if (Test-Path $yasm_exepath) {
+    Remove-Item $yasm_exepath -Force
+}
+
+Write-Output "Downloading $yasm_dlexe ($yasm_url)"
+Invoke-WebRequest -Uri $yasm_url -OutFile $yasm_dlpath
+
+Write-Output "Moving $yasm_dlexe to $yasm_exename"
+Rename-Item $yasm_dlpath $yasm_exename
+
+Write-Output "Done"
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index a1dcf5ec..f3d6a05c 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -15,10 +15,16 @@ jobs:
       fail-fast: false
       matrix:
         platform:
-        - { name: Windows (mingw32),      os: windows-latest, shell: 'msys2 {0}', msystem: mingw32, msys-env: mingw-w64-i686 }
-        - { name: Windows (mingw64),      os: windows-latest, shell: 'msys2 {0}', msystem: mingw64, msys-env: mingw-w64-x86_64 }
-        - { name: Linux (CMake),          os: ubuntu-20.04,   shell: sh,   cmake: '-GNinja' }
-        - { name: Linux (autotools),      os: ubuntu-20.04,   shell: sh }
+        - { name: Windows (MSVC+CMake),         os: windows-2019, shell: sh,   cmake: '-DSDL2MIXER_VENDORED=ON -GNinja', msvc: 1, shared: 1, static: 0 }
+        - { name: Windows (mingw32+autotools),  os: windows-latest, shell: 'msys2 {0}', msystem: mingw32, msys-env: mingw-w64-i686, shared: 1, static: 1 }
+        - { name: Windows (mingw64+CMake),      os: windows-latest, shell: 'msys2 {0}', msystem: mingw64, msys-env: mingw-w64-x86_64, shared: 1, static: 0,
+            cmake: '-DSDL2MIXER_VENDORED=OFF -G "Ninja Multi-Config"' }
+        - { name: Linux (autotools),            os: ubuntu-20.04,   shell: sh, shared: 1, static: 1}
+        - { name: Linux (CMake),                os: ubuntu-20.04,   shell: sh, cmake: '-DSDL2MIXER_VENDORED=ON -GNinja', shared: 1, static: 0 }
+        - { name: 'Linux (CMake, static)',      os: ubuntu-20.04,   shell: sh, cmake: '-DSDL2MIXER_VENDORED=ON -DBUILD_SHARED_LIBS=OFF -GNinja', shared: 0, static: 1 }
+        - { name: Macos (autotools),            os: macos-latest,   shell: sh, shared: 1, static: 1 }
+        - { name: Macos (CMake),                os: macos-latest,   shell: sh, cmake: '-DSDL2MIXER_VENDORED=ON -GNinja', shared: 1, static: 0 }
+
 
     steps:
     - name: Set up MSYS2
@@ -39,7 +45,23 @@ jobs:
           ${{ matrix.platform.msys-env }}-opusfile
           ${{ matrix.platform.msys-env }}-ninja
           ${{ matrix.platform.msys-env }}-pkg-config
-
+    - name: Setup Macos dependencies
+      if: runner.os == 'macOS'
+      run: |
+        brew install \
+          sdl2 \
+          autoconf \
+          automake \
+          libtool \
+          ninja \
+          pkg-config \
+          mpg123 \
+          flac \
+          libmodplug \
+          fluidsynth \
+          libvorbis \
+          opusfile \
+          ${NULL+}
     - name: Setup Linux dependencies
       if: runner.os == 'Linux'
       run: |
@@ -61,56 +83,101 @@ jobs:
     - uses: actions/checkout@v2
       with:
         submodules: recursive
+
+    - name: Setup MSVC dependencies
+      if: "matrix.platform.msvc"
+      shell: pwsh
+      run: |
+        echo "::group::Downloading SDL"
+        .github/fetch_sdl_vc.ps1
+        echo "SDL2_DIR=$Env:GITHUB_WORKSPACE/SDL2-devel-VC" >> $Env:GITHUB_ENV
+        echo "::endgroup::"
+        echo "::group::Downloading yasm"
+        .github/fetch_yasm.ps1
+        echo "${{ github.workspace }}" >> $Env:GITHUB_PATH
+        echo "::endgroup::"
+    - name: Setup Ninja for MSVC
+      if: "matrix.platform.msvc"
+      uses: ashutoshvarma/setup-ninja@master
+      with:
+        version: 1.10.2
+    - uses: ilammy/msvc-dev-cmd@v1
+      if: "matrix.platform.msvc"
+      with:
+        arch: x64
+
     - name: Check that versioning is consistent
       # We only need to run this once: arbitrarily use the Linux/CMake build
       if: "runner.os == 'Linux' && matrix.platform.cmake"
       run: ./test-versioning.sh
 
-    - name: Configure CMake
+    - name: Setup (CMake)
+      if: ${{ matrix.platform.cmake && !matrix.platform.msystem && !matrix.platform.msvc }}
+      uses: jwlawson/actions-setup-cmake@v1.12
+      with:
+        cmake-version: '3.16'
+    - name: Configure (CMake)
       if: "matrix.platform.cmake"
       run: |
-        cmake -B build \
+        set -- \
           -DCMAKE_POSITION_INDEPENDENT_CODE=ON \
           -DCMAKE_BUILD_TYPE=Release \
-          -DCMAKE_VERBOSE_MAKEFILE=ON \
-          -DSUPPORT_FLAC=ON \
-          -DSUPPORT_OGG=ON \
-          -DSUPPORT_OPUS=ON \
-          -DSUPPORT_MP3_MPG123=ON \
-          -DSUPPORT_MOD_MODPLUG=ON \
+          -DSDL2MIXER_FLAC=ON \
+          -DSDL2MIXER_FLAC_LIBFLAC=ON \
+          -DSDL2MIXER_VORBIS=VORBISFILE \
+          -DSDL2MIXER_OPUS=ON \
+          -DSDL2MIXER_MP3_MPG123=ON \
+          -DSDL2MIXER_MOD_MODPLUG=ON \
+          -DCMAKE_INSTALL_PREFIX=prefix_cmake \
+          ${NULL+}
+        
+        cmake -B build \
+          "$@" \
           ${{ matrix.platform.cmake }}
-    - name: Build
+    - name: Build (CMake)
+      if: "matrix.platform.cmake"
+      run: |
+        cmake --build build/ --config Release --parallel --verbose
+    - name: Install (CMake)
       if: "matrix.platform.cmake"
-      run: cmake --build build/ --config Release
-#    - name: Install
-#      if: "matrix.platform.shell == 'sh' && matrix.platform.cmake"
-#      run: |
-#        set -eu
-#        rm -fr DESTDIR-cmake
-#        DESTDIR=$(pwd)/DESTDIR-cmake cmake --install build/ --config Release
-#        ( cd DESTDIR-cmake; find ) | LC_ALL=C sort -u
+      run: |
+        set -eu
+        rm -fr prefix_cmake
+        cmake --install build/ --config Release
+        echo "SDL2_mixer_DIR=$(pwd)/prefix_cmake" >> $GITHUB_ENV
+        ( cd prefix_cmake; find . ) | LC_ALL=C sort -u
 
-    - name: Configure Autotools
+    - name: Configure (Autotools)
       if: "! matrix.platform.cmake"
       run: |
         set -eu
         rm -fr build-autotools
         mkdir build-autotools
         ./autogen.sh
-        ( cd build-autotools && ../configure )
-    - name: Build with Autotools
+        set -- -prefix=$(pwd)/prefix_autotools
+        ( cd build-autotools && ../configure "$@" )
+    - name: Build (Autotools)
       if: "! matrix.platform.cmake"
       run: |
         set -eu
         parallel="$(getconf _NPROCESSORS_ONLN)"
         make -j"${parallel}" -C build-autotools V=1
-    - name: Install with Autotools
+    - name: Install (Autotools)
       if: "! matrix.platform.cmake"
       run: |
         set -eu
         curdir="$(pwd)"
         parallel="$(getconf _NPROCESSORS_ONLN)"
-        rm -fr DESTDIR-autotools
-        mkdir DESTDIR-autotools
-        make -j"${parallel}" -C build-autotools install DESTDIR="${curdir}/DESTDIR-autotools" V=1
-        ( cd DESTDIR-autotools; find ) | LC_ALL=C sort -u
+        rm -rf prefix_autotools
+        make -j"${parallel}" -C build-autotools install V=1
+        echo "SDL2_mixer_DIR=$(pwd)/prefix_autotools" >> $GITHUB_ENV
+        ( cd prefix_autotools; find ) | LC_ALL=C sort -u
+
+    - name: Verify CMake configuration files
+      run: |
+        cmake -S cmake/test -B cmake_config_build \
+          -DCMAKE_BUILD_TYPE=Release \
+          -DCMAKE_PREFIX_PATH="${{ env.SDL2_mixer_DIR }};${{ env.SDL2_DIR }}" \
+          -DTEST_SHARED=${{ matrix.platform.shared }} \
+          -DTEST_STATIC=${{ matrix.platform.static }}
+        cmake --build cmake_config_build --verbose --config Release
diff --git a/CMakeLists.txt b/CMakeLists.txt
index cdfa2c8c..be4a2a69 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,212 +1,880 @@
-cmake_minimum_required(VERSION 3.1.0)
-project(SDL2_mixer C)
+cmake_minimum_required(VERSION 3.16)
 
-# FIXME: CMAKE SUPPORT IN SDL2_mixer IS VERY INCOMPLETE YET !!!
-#
-# FIXME: make it able build against system codec libraries, too.
-# FIXME: handle library versioning.
-# FIXME: test accross different target platforms.
+list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
 
 # See docs/release_checklist.md
 set(MAJOR_VERSION 2)
 set(MINOR_VERSION 5)
 set(MICRO_VERSION 1)
-set(FULL_VERSION "${MAJOR_VERSION}.${MINOR_VERSION}.${MICRO_VERSION}")
+set(SDL_REQUIRED_VERSION 2.0.9)
 
-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 libvorbis" OFF)
-option(SUPPORT_OPUS "Support loading OGG Opus music via libopusfile" OFF)
-option(SUPPORT_MP3_MPG123 "Support loading MP3 music via MPG123" OFF)
-option(SUPPORT_MOD_MODPLUG "Support loading MOD music via modplug" OFF)
-option(SUPPORT_MID_FLUIDSYNTH "Support loading MIDI music via FluidSynth" OFF)
-option(SUPPORT_MID_TIMIDITY "Support loading MIDI music via TiMidity" ON)
+# For historical reasons this is 3.0.0 rather than the expected 1.0.0
+set(DYLIB_COMPATIBILITY_VERSION "3.0.0")
 
-option(BUILD_SHARED_LIBS "Enable shared library" ON)
+include(PrivateSdlFunctions)
+sdl_calculate_derived_version_variables()
 
-if (NOT (TARGET SDL2::SDL2 OR TARGET SDL2::SDL2-static))
-    find_package(SDL2 REQUIRED)
-    if(NOT TARGET SDL2::SDL2)
-        # SDL < 2.0.12
-        add_library(SDL2::SDL2 INTERFACE IMPORTED)
-        set_target_properties(SDL2::SDL2 PROPERTIES
-            INTERFACE_INCLUDE_DIRECTORIES ${SDL2_INCLUDE_DIRS} ${SDL2_INCLUDE_DIR}
-            INTERFACE_LINK_LIBRARIES ${SDL2_LIBRARIES} ${SDL2_LIBRARY}
-        )
-    endif()
+if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR)
+    message(FATAL_ERROR "Prevented in-tree built. Please create a build directory outside of the SDL_mixer source code and call cmake from there")
 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")
+project(SDL2_mixer
+    LANGUAGES C
+    VERSION "${FULL_VERSION}"
+)
 
-# 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}")
+message(STATUS "Configuring ${PROJECT_NAME} ${PROJECT_VERSION}")
 
-include_directories(include src src/codecs)
+if(POLICY CMP0112)
+    # Target file component generator expressions do not add target dependencies.
+    cmake_policy(SET CMP0112 NEW)
+endif()
 
-add_library(SDL2_mixer)
-add_library(SDL2::mixer ALIAS SDL2_mixer)
+# Set defaults preventing destination file conflicts
+set(SDL2MIXER_DEBUG_POSTFIX "d"
+    CACHE STRING "Name suffix for debug builds")
+mark_as_advanced(SDL2MIXER_DEBUG_POSTFIX)
 
-if(SUPPORT_MID_TIMIDITY)
-    set(TIMIDITY_SRCS
-        src/codecs/timidity/common.c
-        src/codecs/timidity/instrum.c
-        src/codecs/timidity/mix.c
-        src/codecs/timidity/output.c
-        src/codecs/timidity/playmidi.c
-        src/codecs/timidity/readmidi.c
-        src/codecs/timidity/resample.c
-        src/codecs/timidity/tables.c
-        src/codecs/timidity/timidity.c
-    )
+# Assume MSVC projects don't have a package manager and need vendored dependencies (by default).
+# Most other platforms have some kind of package manager.
+# FIXME: consider a package manager such as conan/vcpkg instead of vendoring
+if(MSVC)
+    set(vendored_default ON)
+else()
+    set(vendored_default OFF)
 endif()
 
-target_sources(SDL2_mixer PRIVATE
-        src/effect_position.c
-        src/effects_internal.c
-        src/effect_stereoreverse.c
-        src/mixer.c
-        src/music.c
-        src/utils.c
-        src/codecs/load_aiff.c
-        src/codecs/load_voc.c
-        src/codecs/music_cmd.c
-        src/codecs/music_wav.c
-        src/codecs/music_drflac.c
-        src/codecs/music_flac.c
-        src/codecs/music_drmp3.c
-        src/codecs/music_mpg123.c
-        src/codecs/mp3utils.c
-        src/codecs/music_ogg.c
-        src/codecs/music_ogg_stb.c
-        src/codecs/music_opus.c
-        src/codecs/music_modplug.c
-        src/codecs/music_xmp.c
-        src/codecs/music_fluidsynth.c
-        src/codecs/music_timidity.c
-        src/codecs/music_nativemidi.c
-        ${TIMIDITY_SRCS}
-)
+include(CheckSymbolExists)
+include(CMakeDependentOption)
+include(CMakePackageConfigHelpers)
+include(GNUInstallDirs)
 
-if (SUPPORT_WAV)
-    target_compile_definitions(SDL2_mixer PRIVATE -DMUSIC_WAV)
-endif()
+option(CMAKE_POSITION_INDEPENDENT_CODE "Build static libraries with -fPIC" ON)
+option(BUILD_SHARED_LIBS "Build the library as a shared library" ON)
+
+# Save BUILD_SHARED_LIBS variable as soon as possible
+set(SDL2MIXER_BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS})
+
+option(SDL2MIXER_INSTALL "Enable SDL2mixer install target" ON)
+option(SDL2MIXER_DEPS_SHARED "Default value for loading dependencies dynamically" ON)
+option(SDL2MIXER_VENDORED "Use vendored third-party libraries" ${vendored_default})
 
-if (SUPPORT_OGG OR SUPPORT_FLAC OR SUPPORT_OPUS)
-    add_subdirectory(external/ogg)
+option(SDL2MIXER_SAMPLES "Build the SDL2_mixer sample program(s)" ON)
+cmake_dependent_option(SDL2MIXER_SAMPLES_INSTALL "Install the SDL2_mixer sample program(s)" OFF "SDL2MIXER_SAMPLES;SDL2MIXER_INSTALL" OFF)
+
+if(UNIX AND NOT APPLE)
+    set(sdl2mixer_cmd_default ON)
+else()
+    set(sdl2mixer_cmd_default OFF)
 endif()
+option(SDL2MIXER_CMD "Support an external music player" ${sdl2mixer_cmd_default})
+
+option(SDL2MIXER_FLAC "Enable FLAC music" ON)
+
+cmake_dependent_option(SDL2MIXER_FLAC_LIBFLAC "Enable FLAC music using libFLAC" ON SDL2MIXER_FLAC OFF)
+cmake_dependent_option(SDL2MIXER_FLAC_LIBFLAC_SHARED "Dynamically load LIBFLAC" "${SDL2MIXER_DEPS_SHARED}" SDL2MIXER_FLAC_LIBFLAC OFF)
+
+cmake_dependent_option(SDL2MIXER_FLAC_DRFLAC "Enable FLAC music using drflac" ON SDL2MIXER_FLAC OFF)
+
+option(SDL2MIXER_MOD "Support loading MOD music" ON)
 
-if (SUPPORT_FLAC)
-    target_compile_definitions(SDL2_mixer PRIVATE -DMUSIC_FLAC_LIBFLAC)
-    add_subdirectory(external/flac)
-    target_include_directories(SDL2_mixer PRIVATE external/flac/include)
-    target_link_libraries(SDL2_mixer PRIVATE FLAC)
+cmake_dependent_option(SDL2MIXER_MOD_MODPLUG "Support loading MOD music via modplug" ON SDL2MIXER_MOD OFF)
+cmake_dependent_option(SDL2MIXER_MOD_MODPLUG_SHARED "Dynamically load modplug" "${SDL2MIXER_DEPS_SHARED}" SDL2MIXER_MOD_MODPLUG OFF)
+
+cmake_dependent_option(SDL2MIXER_MOD_XMP "Support loading MOD music via libxmp" OFF SDL2MIXER_MOD OFF)
+cmake_dependent_option(SDL2MIXER_MOD_XMP_LITE "Use libxmp-lite instead of libxmp" OFF SDL2MIXER_MOD_XMP OFF)
+cmake_dependent_option(SDL2MIXER_MOD_XMP_SHARED "Dynamically load libxmp(-lite)" "${SDL2MIXER_DEPS_SHARED}" SDL2MIXER_MOD_XMP OFF)
+
+if(SDL2MIXER_MOD AND NOT (SDL2MIXER_MOD_MODPLUG OR SDL2MIXER_MOD_XMP))
+    message(FATAL_EROR "MOD support was enabled (SDL2MIXER_MOD) but neither mpdplug (SDL2MIXER_MOD_MODPLUG) or xmp (SDL2MIXER_MOD_XMP) was enabled.")
 endif()
 
-if (SUPPORT_OGG)
-    target_compile_definitions(SDL2_mixer PRIVATE -DMUSIC_OGG)
-    add_subdirectory(external/vorbis)
-    target_include_directories(SDL2_mixer PRIVATE external/vorbis/include)
-    target_link_libraries(SDL2_mixer PRIVATE vorbisfile vorbis ogg)
+option(SDL2MIXER_MP3 "Enable MP3 music" ON)
+
+cmake_dependent_option(SDL2MIXER_MP3_DRMP3 "Support loading MP3 music via dr_mp3" ON SDL2MIXER_MP3 OFF)
+
+cmake_dependent_option(SDL2MIXER_MP3_MPG123 "Support loading MP3 music via MPG123" OFF SDL2MIXER_MP3 OFF)
+cmake_dependent_option(SDL2MIXER_MP3_MPG123_SHARED "Dynamically load mpg123" "${SDL2MIXER_DEPS_SHARED}" SDL2MIXER_MP3_MPG123 OFF)
+
+if(SDL2MIXER_MP3 AND NOT (SDL2MIXER_MP3_DRMP3 OR SDL2MIXER_MP3_MPG123))
+    message(FATAL_ERROR "MP3 support was enabled (SDL2MIXER_MP3) but neither drmp3 (SDL2MIXER_MP3_DRMP3) or mpg123 (SDL2MIXER_MP3_MPG123) were enabled.")
 endif()
 
-if (SUPPORT_OPUS)
-    target_compile_definitions(SDL2_mixer PRIVATE -DMUSIC_OPUS -DOPUSFILE_HEADER=<opusfile.h>)
-    set(OP_DISABLE_HTTP ON CACHE BOOL "Disable HTTP support")
-    set(OP_DISABLE_EXAMPLES ON CACHE BOOL "Do not build example applications")
-    set(OP_DISABLE_DOCS ON CACHE BOOL "Do not build API documentation")
-    add_subdirectory(external/opus)
-    add_subdirectory(external/opusfile)
-    target_link_libraries(SDL2_mixer PRIVATE opusfile)
+option(SDL2MIXER_MIDI "Enable MIDI music" ON)
+
+cmake_dependent_option(SDL2MIXER_MIDI_FLUIDSYNTH "Support FluidSynth MIDI output" ON "SDL2MIXER_MIDI;NOT SDL2MIXER_VENDORED" OFF)
+cmake_dependent_option(SDL2MIXER_MIDI_FLUIDSYNTH_SHARED "Dynamically load libfluidsynth" "${SDL2MIXER_DEPS_SHARED}" SDL2MIXER_MIDI_FLUIDSYNTH OFF)
+
+if(WIN32 OR APPLE OR HAIKU)
+    cmake_dependent_option(SDL2MIXER_MIDI_NATIVE "Support native MIDI output" ON SDL2MIXER_MIDI OFF)
+else()
+    set(SDL2MIXER_MIDI_NATIVE OFF)
 endif()
 
-if (SUPPORT_MP3_MPG123)
-    target_compile_definitions(SDL2_mixer PRIVATE -DMUSIC_MP3_MPG123)
-    add_subdirectory(external/mpg123/ports/cmake)
-    target_include_directories(SDL2_mixer PRIVATE external/mpg123/ports/cmake/src/libmpg123)
-    target_link_libraries(SDL2_mixer PRIVATE libmpg123)
+cmake_dependent_option(SDL2MIXER_MIDI_TIMIDITY "Support timidity MIDI output" ON SDL2MIXER_MIDI OFF)
+
+if(SDL2MIXER_MIDI AND NOT (SDL2MIXER_MIDI_TIMIDITY OR SDL2MIXER_MIDI_NATIVE OR SDL2MIXER_MIDI_FLUIDSYNTH))
+    message(FATAL_ERROR "MIDI support was enabled (SDL2MIXER_MIDI) but neither FluidSynth (SDL2MIXER_MIDI_FLUIDSYNTH), native (SDL2MIXER_MIDI_NATIVE) or timidity (SDL2MIXER_MIDI_TIMIDITY) was enabled")
 endif()
 
-if (SUPPORT_MOD_MODPLUG)
-    target_compile_definitions(SDL2_mixer PRIVATE -DMUSIC_MOD_MODPLUG -DMODPLUG_HEADER=<modplug.h>)
-    add_subdirectory(external/libmodplug)
-    target_include_directories(SDL2_mixer PRIVATE external/libmodplug/src)
-    target_link_libraries(SDL2_mixer PRIVATE modplug)
+option(SDL2MIXER_OPUS "Enable Opus music" ON)
+cmake_dependent_option(SDL2MIXER_OPUS_SHARED "Dynamically load libopus" ON SDL2MIXER_OPUS OFF)
+
+set(sdl2mixer_vorbis_strings STB TREMOR VORBISFILE)
+set(SDL2MIXER_VORBIS "STB" CACHE STRING "Enable OGG Vorbis music")
+set_property(CACHE SDL2MIXER_VORBIS PROPERTY STRINGS "${sdl2mixer_vorbis_strings}")
+if(SDL2MIXER_VORBIS)
+    if(NOT SDL2MIXER_VORBIS IN_LIST sdl2mixer_vorbis_strings)
+        message(FATAL_ERROR "SDL2MIXER_VORBIS contains an invalid value (=${SDL2MIXER_VORBIS}). It must be one of ${sdl2mixer_vorbis_strings}.")
+    endif()
+endif()
+set(SDL2MIXER_VORBIS_STB OFF)
+set(SDL2MIXER_VORBIS_TREMOR OFF)
+set(SDL2MIXER_VORBIS_VORBISFILE OFF)
+if(SDL2MIXER_VORBIS STREQUAL "STB")
+    set(SDL2MIXER_VORBIS_STB ON)
 endif()
+if(SDL2MIXER_VORBIS STREQUAL "TREMOR")
+    set(SDL2MIXER_VORBIS_TREMOR ON)
+endif()
+if(SDL2MIXER_VORBIS STREQUAL "VORBISFILE")
+    set(SDL2MIXER_VORBIS_VORBISFILE ON)
+endif()
+cmake_dependent_option(SDL2MIXER_VORBIS_TREMOR_SHARED "Dynamically load tremor library" ON SDL2MIXER_VORBIS_TREMOR OFF)
+cmake_dependent_option(SDL2MIXER_VORBIS_VORBISFILE_SHARED "Dynamically load vorbisfile library" ON SDL2MIXER_VORBIS_VORBISFILE OFF)
+
+option(SDL2MIXER_WAVE "Enable streaming WAVE music" ON)
 
-if (SUPPORT_MID_FLUIDSYNTH)
-    target_compile_definitions(SDL2_mixer PRIVATE -DMUSIC_MID_FLUIDSYNTH)
-    target_link_libraries(SDL2_mixer PRIVATE fluidsynth)
+if(SDL2MIXER_VORBIS_TREMOR OR SDL2MIXER_VORBIS_VORBISFILE OR SDL2MIXER_FLAC_LIBFLAC OR SDL2MIXER_OPUS)
+    set(SDL2MIXER_OGG TRUE)
+    set(SDL2MIXER_OGG_install FALSE)
+    if(SDL2MIXER_VORBIS_VORBISFILE_SHARED OR SDL2MIXER_FLAC_SHARED OR SDL2MIXER_OPUS_SHARED)
+        set(SDL2MIXER_OGG_SHARED TRUE)
+        set(SDL2MIXER_OGG_install TRUE)
+    else()
+        set(SDL2MIXER_OGG_SHARED FALSE)
+        if(NOT SDL2MIXER_BUILD_SHARED_LIBS)
+            set(SDL2MIXER_OGG_install TRUE)
+        endif()
+    endif()
+else()
+    set(SDL2MIXER_OGG FALSE)
 endif()
 
-if (SUPPORT_MID_TIMIDITY)
-    target_compile_definitions(SDL2_mixer PRIVATE -DMUSIC_MID_TIMIDITY)
+if(SDL2MIXER_BUILD_SHARED_LIBS)
+    set(sdl2_mixer_export_name SDL2_mixer)
+    set(sdl2_mixer_install_name_infix shared)
+    set(sdl2_target_name SDL2::SDL2)
+else()
+    set(sdl2_mixer_export_name SDL2_mixer-static)
+    set(sdl2_mixer_install_name_infix static)
+    set(sdl2_target_name SDL2::SDL2-static)
 endif()
 
-if(BUILD_SHARED_LIBS)
-    if(WIN32 OR OS2)
-        set_target_properties(SDL2_mixer PROPERTIES PREFIX "")
-    endif()
+sdl_find_sdl2(${sdl2_target_name} ${SDL_REQUIRED_VERSION})
+
+set(BUILD_SHARED_LIBS ${SDL2MIXER_BUILD_SHARED_LIBS})
+add_library(SDL2_mixer
+    src/codecs/load_aiff.c
+    src/codecs/load_voc.c
+    src/codecs/mp3utils.c
+    src/codecs/music_cmd.c
+    src/codecs/music_drflac.c
+    src/codecs/music_drmp3.c
+    src/codecs/music_flac.c
+    src/codecs/music_fluidsynth.c
+    src/codecs/music_modplug.c
+    src/codecs/music_mpg123.c
+    src/codecs/music_nativemidi.c
+    src/codecs/music_ogg.c
+    src/codecs/music_ogg_stb.c
+    src/codecs/music_opus.c
+    src/codecs/music_timidity.c
+    src/codecs/music_wav.c
+    src/codecs/music_xmp.c
+    src/effect_position.c
+    src/effect_stereoreverse.c
+    src/effects_internal.c
+    src/mixer.c
+    src/music.c
+    src/utils.c
+)
+add_library(SDL2_mixer::${sdl2_mixer_export_name} ALIAS SDL2_mixer)
+target_include_directories(SDL2_mixer
+    PUBLIC
+        "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
+        "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/SDL2>"
+    PRIVATE
+        src
+        src/codecs
+)
+target_compile_definitions(SDL2_mixer PRIVATE
+    BUILD_SDL
+    SDL_BUILD_MAJOR_VERSION=${MAJOR_VERSION}
+    SDL_BUILD_MINOR_VERSION=${MINOR_VERSION}
+    SDL_BUILD_MICRO_VERSION=${MICRO_VERSION}
+)
+target_link_libraries(SDL2_mixer PRIVATE $<BUILD_INTERFACE:${sdl2_target_name}>)
+if(WIN32 AND BUILD_SHARED_LIBS)
+    target_sources(SDL2_mixer PRIVATE
+        version.rc
+    )
+endif()
+set_target_properties(SDL2_mixer PROPERTIES
+    DEFINE_SYMBOL DLL_EXPORT
+    PUBLIC_HEADER "include/SDL_mixer.h"
+    EXPORT_NAME ${sdl2_mixer_export_name}
+    C_VISIBILITY_PRESET "hidden"
+)
+if(NOT ANDROID)
+    set_target_properties(SDL2_mixer PROPERTIES
+        DEBUG_POSTFIX "${SDL2MIXER_DEBUG_POSTFIX}"
+    )
     if(APPLE)
-        # TODO: Handle DYLIB_COMPATIBILITY_VERSION, DYLIB_CURRENT_VERSION here
+        # the SOVERSION property corresponds to the compatibility version and VERSION corresponds to the current version
+        # https://cmake.org/cmake/help/latest/prop_tgt/SOVERSION.html#mach-o-versions
+        set_target_properties(SDL2_mixer PROPERTIES
+            SOVERSION "${DYLIB_COMPATIBILITY_VERSION}"
+            VERSION "${DYLIB_CURRENT_VERSION}"
+        )
+    else()
+        set_target_properties(SDL2_mixer PROPERTIES
+            SOVERSION "${LT_MAJOR}"
+            VERSION "${LT_VERSION}"
+        )
     endif()
-    if(WIN32)
-        target_compile_definitions(SDL2_mixer PRIVATE -DDLL_EXPORT)
-        target_sources(SDL2_mixer PRIVATE version.rc)
-    elseif(OS2)
+endif()
+if(SDL2MIXER_BUILD_SHARED_LIBS AND (APPLE OR (UNIX AND NOT ANDROID)))
+    add_custom_command(TARGET SDL2_mixer POST_BUILD
+        COMMAND "${CMAKE_COMMAND}" -E create_symlink "$<TARGET_SONAME_FILE_NAME:SDL2_mixer>" "libSDL2_mixer$<$<CONFIG:Debug>:${SDL2MIXER_DEBUG_POSTFIX}>$<TARGET_FILE_SUFFIX:SDL2_mixer>"
+        # BYPRODUCTS "libSDL2_mixer$<$<CONFIG:Debug>:${SDL2MIXER_DEBUG_POSTFIX}>$<TARGET_FILE_SUFFIX:SDL2_mixer>" # Needs CMake 3.20
+        WORKING_DIRECTORY "${PROJECT_BINARY_DIR}"
+    )
+endif()
+if(SDL2MIXER_BUILD_SHARED_LIBS)
+    if(WIN32 OR OS2)
+        set_target_properties(SDL2_mixer PROPERTIES
+            PREFIX ""
+        )
+    endif()
+    if(OS2)
         # OS/2 doesn't support a DLL name longer than 8 characters.
         set_target_properties(SDL2_mixer PROPERTIES
-               OUTPUT_NAME "SDL2mix"
+            OUTPUT_NAME "SDL2mix"
         )
     elseif(UNIX AND NOT ANDROID)
-        # This is compatible with the libtool build
         set_target_properties(SDL2_mixer PROPERTIES
-               VERSION ${LT_VERSION}
-               SOVERSION ${LT_MAJOR}
-               OUTPUT_NAME "SDL2_mixer-${LT_RELEASE}"
+            OUTPUT_NAME "SDL2_mixer-${LT_RELEASE}"
         )
     endif()
+else()
+    if(MSVC OR (WATCOM AND (WIN32 OR OS2)))
+        set_tar

(Patch may be truncated, please check the link at the top of this post.)