SDL: ci: enable ccache

From c153f83df083a171d596adf16bc74a3f0a5be43c Mon Sep 17 00:00:00 2001
From: Anonymous Maarten <[EMAIL REDACTED]>
Date: Thu, 20 Feb 2025 23:24:38 +0100
Subject: [PATCH] ci: enable ccache

---
 .github/workflows/create-test-plan.py | 44 ++++++++++++++++++++++-----
 .github/workflows/generic.yml         | 42 +++++++++++++++++++++++++
 2 files changed, 79 insertions(+), 7 deletions(-)

diff --git a/.github/workflows/create-test-plan.py b/.github/workflows/create-test-plan.py
index b1ef4e5d6f621..407efe9de8625 100755
--- a/.github/workflows/create-test-plan.py
+++ b/.github/workflows/create-test-plan.py
@@ -166,6 +166,7 @@ class JobDetails:
     platform: str
     artifact: str
     no_cmake: bool
+    ccache: bool = False
     build_tests: bool = True
     container: str = ""
     cmake_build_type: str = "RelWithDebInfo"
@@ -231,6 +232,7 @@ def to_workflow(self, enable_artifacts: bool) -> dict[str, str|bool]:
             "name": self.name,
             "key": self.key,
             "os": self.os,
+            "ccache": self.ccache,
             "container": self.container if self.container else "",
             "platform": self.platform,
             "artifact": self.artifact,
@@ -421,6 +423,7 @@ def spec_to_job(spec: JobSpec, key: str, trackmem_symbol_names: bool) -> JobDeta
             if spec.name.startswith("Ubuntu"):
                 assert spec.os.value.startswith("ubuntu-")
                 job.apt_packages.extend((
+                    "ccache",
                     "gnome-desktop-testing",
                     "libasound2-dev",
                     "libpulse-dev",
@@ -454,6 +457,7 @@ def spec_to_job(spec: JobSpec, key: str, trackmem_symbol_names: bool) -> JobDeta
                 job.apt_packages.extend((
                     "libunwind-dev",  # For SDL_test memory tracking
                 ))
+            job.ccache = True
             if trackmem_symbol_names:
                 # older libunwind is slow
                 job.cmake_arguments.append("-DSDLTEST_TIMEOUT_MULTIPLIER=2")
@@ -462,8 +466,10 @@ def spec_to_job(spec: JobSpec, key: str, trackmem_symbol_names: bool) -> JobDeta
             fpic = True
         case SdlPlatform.Ios | SdlPlatform.Tvos:
             job.brew_packages.extend([
+                "ccache",
                 "ninja",
             ])
+            job.ccache = True
             job.clang_tidy = False
             job.run_tests = False
             job.test_pkg_config = False
@@ -506,8 +512,12 @@ def spec_to_job(spec: JobSpec, key: str, trackmem_symbol_names: bool) -> JobDeta
                 ))
                 job.shared_lib = SharedLibType.DYLIB
                 job.static_lib = StaticLibType.A
+            job.ccache = True
             job.apt_packages = []
-            job.brew_packages.append("ninja")
+            job.brew_packages.extend((
+                "ccache",
+                "ninja",
+            ))
             if job.clang_tidy:
                 job.brew_packages.append("llvm")
             if spec.xcode:
@@ -515,6 +525,7 @@ def spec_to_job(spec: JobSpec, key: str, trackmem_symbol_names: bool) -> JobDeta
         case SdlPlatform.Android:
             job.android_gradle = spec.android_gradle
             job.android_mk = spec.android_mk
+            job.apt_packages.append("ccache")
             job.run_tests = False
             job.shared_lib = SharedLibType.SO
             job.static_lib = StaticLibType.A
@@ -525,6 +536,7 @@ def spec_to_job(spec: JobSpec, key: str, trackmem_symbol_names: bool) -> JobDeta
             if spec.android_mk or spec.android_gradle:
                 job.apt_packages = []
             if not spec.no_cmake:
+                job.ccache = True
                 job.cmake_arguments.extend((
                     f"-DANDROID_PLATFORM={spec.android_platform}",
                     f"-DANDROID_ABI={spec.android_abi}",
@@ -542,6 +554,8 @@ def spec_to_job(spec: JobSpec, key: str, trackmem_symbol_names: bool) -> JobDeta
         case SdlPlatform.Emscripten:
             job.clang_tidy = False  # clang-tidy does not understand -gsource-map
             job.shared = False
+            job.ccache = True
+            job.apt_packages.append("ccache")
             job.cmake_config_emulator = "emcmake"
             job.cmake_build_type = "Debug"
             job.test_pkg_config = False
@@ -567,11 +581,12 @@ def spec_to_job(spec: JobSpec, key: str, trackmem_symbol_names: bool) -> JobDeta
             job.setup_python = True
             job.pypi_packages.append("selenium")
         case SdlPlatform.Ps2:
+            job.ccache = False  #  actions/ccache does not work in psp container (incompatible tar of busybox)
             build_parallel = False
             job.shared = False
             job.sudo = ""
             job.apt_packages = []
-            job.apk_packages = ["cmake", "gmp", "mpc1", "mpfr4", "ninja", "pkgconf", "git", ]
+            job.apk_packages = ["ccache", "cmake", "gmp", "mpc1", "mpfr4", "ninja", "pkgconf", "git", ]
             job.cmake_toolchain_file = "${PS2DEV}/ps2sdk/ps2dev.cmake"
             job.clang_tidy = False
             job.run_tests = False
@@ -580,10 +595,11 @@ def spec_to_job(spec: JobSpec, key: str, trackmem_symbol_names: bool) -> JobDeta
             job.ldflags = ["-L${PS2DEV}/ps2sdk/ee/lib", "-L${PS2DEV}/gsKit/lib", "-L${PS2DEV}/ps2sdk/ports/lib", ]
             job.static_lib = StaticLibType.A
         case SdlPlatform.Psp:
+            job.ccache = False  #  actions/ccache does not work in psp container (incompatible tar of busybox)
             build_parallel = False
             job.sudo = ""
             job.apt_packages = []
-            job.apk_packages = ["cmake", "gmp", "mpc1", "mpfr4", "ninja", "pkgconf", ]
+            job.apk_packages = ["ccache", "cmake", "gmp", "mpc1", "mpfr4", "ninja", "pkgconf", ]
             job.cmake_toolchain_file = "${PSPDEV}/psp/share/pspdev.cmake"
             job.clang_tidy = False
             job.run_tests = False
@@ -593,9 +609,10 @@ def spec_to_job(spec: JobSpec, key: str, trackmem_symbol_names: bool) -> JobDeta
             job.pollute_directories = ["${PSPDEV}/include", "${PSPDEV}/psp/include", "${PSPDEV}/psp/sdk/include", ]
             job.static_lib = StaticLibType.A
         case SdlPlatform.Vita:
+            job.ccache = True
             job.sudo = ""
             job.apt_packages = []
-            job.apk_packages = ["cmake", "ninja", "pkgconf", "bash", "tar"]
+            job.apk_packages = ["ccache", "cmake", "ninja", "pkgconf", "bash", "tar"]
             job.cmake_toolchain_file = "${VITASDK}/share/vita.toolchain.cmake"
             assert spec.vita_gles is not None
             job.setup_vita_gles_type = {
@@ -616,8 +633,10 @@ def spec_to_job(spec: JobSpec, key: str, trackmem_symbol_names: bool) -> JobDeta
             job.cc = "arm-vita-eabi-gcc"
             job.static_lib = StaticLibType.A
         case SdlPlatform.Haiku:
+            job.ccache = True
             fpic = False
             job.run_tests = False
+            job.apt_packages.append("ccache")
             job.cc = "x86_64-unknown-haiku-gcc"
             job.cxx = "x86_64-unknown-haiku-g++"
             job.sudo = ""
@@ -629,20 +648,23 @@ def spec_to_job(spec: JobSpec, key: str, trackmem_symbol_names: bool) -> JobDeta
             job.shared_lib = SharedLibType.SO_0
             job.static_lib = StaticLibType.A
         case SdlPlatform.PowerPC64 | SdlPlatform.PowerPC:
+            job.ccache = True
             # FIXME: Enable SDL_WERROR
             job.werror = False
             job.clang_tidy = False
             job.run_tests = False
             job.sudo = ""
-            job.apt_packages = []
+            job.apt_packages = ["ccache"]
             job.shared_lib = SharedLibType.SO_0
             job.static_lib = StaticLibType.A
             job.cmake_arguments.extend((
                 "-DSDL_UNIX_CONSOLE_BUILD=ON",
             ))
         case SdlPlatform.LoongArch64:
+            job.ccache = True
             fpic = True
             job.run_tests = False
+            job.apt_packages.append("ccache")
             job.cc = "${LOONGARCH64_CC}"
             job.cxx = "${LOONGARCH64_CXX}"
             job.cmake_arguments.extend((
@@ -654,14 +676,16 @@ def spec_to_job(spec: JobSpec, key: str, trackmem_symbol_names: bool) -> JobDeta
             job.shared_lib = SharedLibType.SO_0
             job.static_lib = StaticLibType.A
         case SdlPlatform.N3ds:
+            job.ccache = True
             job.shared = False
-            job.apt_packages = ["ninja-build", "binutils"]
+            job.apt_packages = ["ccache", "ninja-build", "binutils"]
             job.clang_tidy = False
             job.run_tests = False
             job.cc_from_cmake = True
             job.cmake_toolchain_file = "${DEVKITPRO}/cmake/3DS.cmake"
             job.static_lib = StaticLibType.A
         case SdlPlatform.Msys2:
+            job.ccache = True
             job.shell = "msys2 {0}"
             assert spec.msys2_platform
             job.msys2_msystem = spec.msys2_platform.value
@@ -676,9 +700,10 @@ def spec_to_job(spec: JobSpec, key: str, trackmem_symbol_names: bool) -> JobDeta
             job.shared_lib = SharedLibType.WIN32
             job.static_lib = StaticLibType.A
         case SdlPlatform.Riscos:
+            job.ccache = False  # FIXME: enable when container gets upgrade
             # FIXME: Enable SDL_WERROR
             job.werror = False
-            job.apt_packages = ["cmake", "ninja-build"]
+            job.apt_packages = ["ccache", "cmake", "ninja-build"]
             job.test_pkg_config = False
             job.shared = False
             job.run_tests = False
@@ -721,6 +746,11 @@ def spec_to_job(spec: JobSpec, key: str, trackmem_symbol_names: bool) -> JobDeta
         job.check_sources = True
         job.setup_python = True
 
+    if job.ccache:
+        job.cmake_arguments.extend((
+            "-DCMAKE_C_COMPILER_LAUNCHER=ccache",
+            "-DCMAKE_CXX_COMPILER_LAUNCHER=ccache",
+        ))
     if not build_parallel:
         job.cmake_build_arguments.append("-j1")
     if job.cflags:
diff --git a/.github/workflows/generic.yml b/.github/workflows/generic.yml
index 6842b9054d147..9776431e3da83 100644
--- a/.github/workflows/generic.yml
+++ b/.github/workflows/generic.yml
@@ -34,6 +34,7 @@ jobs:
             ${{ (!matrix.platform.msys2-no-perl && format('{0}-perl', matrix.platform.msys2-env)) || '' }}
             ${{ matrix.platform.msys2-env }}-pkg-config
             ${{ matrix.platform.msys2-env }}-clang-tools-extra
+            ${{ (matrix.platform.ccache && format('{0}-ccache', matrix.platform.msys2-env)) || '' }}
       - name: 'About this job'
         run: |
           echo "key=${{ matrix.platform.key }}"
@@ -165,6 +166,36 @@ jobs:
             done
           done
 
+      - name: 'Calculate ccache key'
+        if: ${{ matrix.platform.ccache }}
+        id: prepare-restore-ccache
+        run: |
+          echo "timestamp=$(date -u "+%Y%m%d%H%M_%S")" >> "$GITHUB_OUTPUT"
+      - name: 'Restore ccache'
+        if: ${{ matrix.platform.ccache }}
+        uses: actions/cache/restore@v4
+        id: restore-ccache
+        with:
+          path: ${{ runner.temp }}/ccache
+          key: ccache-${{ matrix.platform.key }}-${{ steps.prepare-restore-ccache.outputs.timestamp }}
+          restore-keys: |
+            ccache-${{matrix.platform.key}}
+      - name: 'Configure ccache'
+        if: ${{ matrix.platform.ccache }}
+        run: |
+          echo 'CCACHE_DIR=${{ runner.temp }}/ccache' >>${GITHUB_ENV}
+      - name: 'Prepare ccache'
+        if: ${{ matrix.platform.ccache && steps.restore-ccache.outputs.cache-hit }}
+        run: |
+          if [ "x${{ runner.os }}" = "xmacOS" ]; then
+            touch_date="2025-02-01T12:00:00Z"
+          else
+            touch_date="2025-02-01"
+          fi
+          find "${CCACHE_DIR}" -type f -exec touch -a -m -d "$touch_date" {} +
+          ccache -s
+          ccache -z
+
       - name: 'Configure (CMake)'
         if: ${{ !matrix.platform.no-cmake }}
         #shell: ${{ matrix.platform.shell }}
@@ -357,6 +388,17 @@ jobs:
         if: ${{ matrix.platform.xcode-sdk != '' }}
         run: |
           xcodebuild -project Xcode/SDL/SDL.xcodeproj -target SDL3 -configuration Release -sdk ${{ matrix.platform.xcode-sdk }} clean build
+      - name: 'Prune old ccache files'
+        if: ${{ matrix.platform.ccache }}
+        run: |
+          ccache --evict-older-than=1d
+          ccache -s
+      - name: 'Save ccache'
+        if: ${{ matrix.platform.ccache }}
+        uses: actions/cache/save@v4
+        with:
+          path: ${{ runner.temp }}/ccache
+          key: ${{ steps.restore-ccache.outputs.cache-primary-key }}
       - name: 'Check Sources'
         if: ${{ matrix.platform.check-sources }}
         run: |