create-test-plan.py 50 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045
  1. #!/usr/bin/env python
  2. import argparse
  3. import dataclasses
  4. import fnmatch
  5. from enum import Enum
  6. import json
  7. import logging
  8. import os
  9. import pathlib
  10. import re
  11. import shlex
  12. from typing import Optional
  13. logger = logging.getLogger(__name__)
  14. class AppleArch(Enum):
  15. Aarch64 = "aarch64"
  16. X86_64 = "x86_64"
  17. class MsvcArch(Enum):
  18. X86 = "x86"
  19. X64 = "x64"
  20. Arm64 = "arm64"
  21. class JobOs(Enum):
  22. WindowsLatest = "windows-latest"
  23. Windows2022 = "windows-2022"
  24. Windows11ARM = "windows-11-arm"
  25. UbuntuLatest = "ubuntu-latest"
  26. MacosLatest = "macos-latest"
  27. Ubuntu22_04 = "ubuntu-22.04"
  28. Ubuntu24_04 = "ubuntu-24.04"
  29. Ubuntu24_04_arm = "ubuntu-24.04-arm"
  30. Macos14 = "macos-14" # macOS Sonoma (2023)
  31. Macos15 = "macos-15" # macOS Sequoia (2024)
  32. Macos26 = "macos-26" # macOS Tahoe (2025)
  33. class JobArch(Enum):
  34. X64 = "x64"
  35. Arm64 = "arm64"
  36. def get_job_arch(job_os: JobOs) -> JobArch:
  37. if "macos" in job_os.value:
  38. return JobArch.Arm64
  39. elif "windows" in job_os.value:
  40. if "arm" in job_os.value:
  41. return JobArch.Arm64
  42. else:
  43. return JobArch.X64
  44. elif "ubuntu" in job_os.value:
  45. if "arm" in job_os.value:
  46. return JobArch.Arm64
  47. else:
  48. return JobArch.X64
  49. else:
  50. raise ValueError(job_os)
  51. class SdlPlatform(Enum):
  52. Android = "android"
  53. Emscripten = "emscripten"
  54. Haiku = "haiku"
  55. LoongArch64 = "loongarch64"
  56. Msys2 = "msys2"
  57. Cygwin = "cygwin"
  58. Linux = "linux"
  59. MacOS = "macos"
  60. Ios = "ios"
  61. Tvos = "tvos"
  62. Msvc = "msvc"
  63. N3ds = "n3ds"
  64. PowerPC = "powerpc"
  65. PowerPC64 = "powerpc64"
  66. Ps2 = "ps2"
  67. Psp = "psp"
  68. Vita = "vita"
  69. Riscos = "riscos"
  70. FreeBSD = "freebsd"
  71. NetBSD = "netbsd"
  72. OpenBSD = "openbsd"
  73. NGage = "ngage"
  74. DJGPP = "djgpp"
  75. class Msys2Platform(Enum):
  76. Mingw32 = "mingw32"
  77. Mingw64 = "mingw64"
  78. Clang64 = "clang64"
  79. Ucrt64 = "ucrt64"
  80. class IntelCompiler(Enum):
  81. Icc = "icc"
  82. Icx = "icx"
  83. class VitaGLES(Enum):
  84. Pib = "pib"
  85. Pvr = "pvr"
  86. @dataclasses.dataclass(slots=True)
  87. class JobSpec:
  88. name: str
  89. priority: bool
  90. os: JobOs
  91. platform: SdlPlatform
  92. artifact: Optional[str]
  93. container: Optional[str] = None
  94. no_cmake: bool = False
  95. xcode: bool = False
  96. android_mk: bool = False
  97. android_gradle: bool = False
  98. lean: bool = False
  99. android_arch: Optional[str] = None
  100. android_abi: Optional[str] = None
  101. android_platform: Optional[int] = None
  102. msys2_platform: Optional[Msys2Platform] = None
  103. intel: Optional[IntelCompiler] = None
  104. apple_framework: Optional[bool] = None
  105. apple_archs: Optional[set[AppleArch]] = None
  106. msvc_project: Optional[str] = None
  107. msvc_arch: Optional[MsvcArch] = None
  108. clang_cl: bool = False
  109. gdk: bool = False
  110. vita_gles: Optional[VitaGLES] = None
  111. more_hard_deps: bool = False
  112. JOB_SPECS = {
  113. "msys2-mingw32": JobSpec(name="Windows (msys2, mingw32)", priority=False, os=JobOs.WindowsLatest, platform=SdlPlatform.Msys2, artifact="SDL-mingw32", msys2_platform=Msys2Platform.Mingw32, ),
  114. "msys2-mingw64": JobSpec(name="Windows (msys2, mingw64)", priority=False, os=JobOs.WindowsLatest, platform=SdlPlatform.Msys2, artifact="SDL-mingw64", msys2_platform=Msys2Platform.Mingw64, ),
  115. "msys2-clang64": JobSpec(name="Windows (msys2, clang64)", priority=False, os=JobOs.WindowsLatest, platform=SdlPlatform.Msys2, artifact="SDL-mingw64-clang", msys2_platform=Msys2Platform.Clang64, ),
  116. "msys2-ucrt64": JobSpec(name="Windows (msys2, ucrt64)", priority=True, os=JobOs.WindowsLatest, platform=SdlPlatform.Msys2, artifact="SDL-mingw64-ucrt", msys2_platform=Msys2Platform.Ucrt64, ),
  117. "cygwin": JobSpec(name="Cygwin", priority=False, os=JobOs.WindowsLatest, platform=SdlPlatform.Cygwin, artifact="SDL-cygwin", ),
  118. "msvc-x64": JobSpec(name="Windows (MSVC, x64)", priority=True, os=JobOs.WindowsLatest, platform=SdlPlatform.Msvc, artifact="SDL-VC-x64", msvc_arch=MsvcArch.X64, msvc_project="VisualC/SDL.sln", ),
  119. "msvc-x86": JobSpec(name="Windows (MSVC, x86)", priority=True, os=JobOs.WindowsLatest, platform=SdlPlatform.Msvc, artifact="SDL-VC-x86", msvc_arch=MsvcArch.X86, msvc_project="VisualC/SDL.sln", ),
  120. "msvc-arm64": JobSpec(name="Windows (MSVC, ARM64)", priority=False, os=JobOs.Windows11ARM, platform=SdlPlatform.Msvc, artifact="SDL-VC-arm64", msvc_arch=MsvcArch.Arm64, msvc_project="VisualC/SDL.sln", ),
  121. "msvc-clang-x64": JobSpec(name="Windows (MSVC, clang-cl x64)", priority=False, os=JobOs.WindowsLatest, platform=SdlPlatform.Msvc, artifact="SDL-clang-cl-x64", msvc_arch=MsvcArch.X64, clang_cl=True, ),
  122. "msvc-clang-x86": JobSpec(name="Windows (MSVC, clang-cl x86)", priority=False, os=JobOs.WindowsLatest, platform=SdlPlatform.Msvc, artifact="SDL-clang-cl-x86", msvc_arch=MsvcArch.X86, clang_cl=True, ),
  123. "msvc-gdk-x64": JobSpec(name="GDK (MSVC, x64)", priority=False, os=JobOs.Windows2022, platform=SdlPlatform.Msvc, artifact="SDL-VC-GDK", msvc_arch=MsvcArch.X64, msvc_project="VisualC-GDK/SDL.sln", gdk=True, no_cmake=True, ),
  124. "ubuntu-22.04": JobSpec(name="Ubuntu 22.04", priority=False, os=JobOs.Ubuntu22_04, platform=SdlPlatform.Linux, artifact="SDL-ubuntu22.04", ),
  125. "ubuntu-latest": JobSpec(name="Ubuntu (latest)", priority=False, os=JobOs.UbuntuLatest, platform=SdlPlatform.Linux, artifact="SDL-ubuntu-latest", ),
  126. "ubuntu-24.04-arm64": JobSpec(name="Ubuntu 24.04 (ARM64)", priority=False, os=JobOs.Ubuntu24_04_arm, platform=SdlPlatform.Linux, artifact="SDL-ubuntu24.04-arm64", ),
  127. "steamrt3": JobSpec(name="Steam Linux Runtime 3.0 (x86_64)", priority=False, os=JobOs.UbuntuLatest, platform=SdlPlatform.Linux, artifact="SDL-steamrt3", container="registry.gitlab.steamos.cloud/steamrt/sniper/sdk:latest" ),
  128. "steamrt4": JobSpec(name="Steam Linux Runtime 4.0 (x86_64)", priority=True, os=JobOs.UbuntuLatest, platform=SdlPlatform.Linux, artifact="SDL-steamrt4", container="registry.gitlab.steamos.cloud/steamrt/steamrt4/sdk:latest", more_hard_deps = True, ),
  129. "steamrt4-arm64": JobSpec(name="Steam Linux Runtime 4.0 (arm64)", priority=False, os=JobOs.Ubuntu24_04_arm, platform=SdlPlatform.Linux, artifact="SDL-steamrt4-arm64", container="registry.gitlab.steamos.cloud/steamrt/steamrt4/sdk/arm64:latest", more_hard_deps = True, ),
  130. "ubuntu-intel-icx": JobSpec(name="Ubuntu 24.04 (Intel oneAPI)", priority=False, os=JobOs.Ubuntu24_04, platform=SdlPlatform.Linux, artifact="SDL-ubuntu22.04-oneapi", intel=IntelCompiler.Icx, ),
  131. "ubuntu-intel-icc": JobSpec(name="Ubuntu 22.04 (Intel Compiler)", priority=False, os=JobOs.Ubuntu22_04, platform=SdlPlatform.Linux, artifact="SDL-ubuntu22.04-icc", intel=IntelCompiler.Icc, ),
  132. "macos-framework-x64": JobSpec(name="MacOS (Framework) (x64)", priority=False, os=JobOs.Macos14, platform=SdlPlatform.MacOS, artifact="SDL-macos-framework", apple_framework=True, apple_archs={AppleArch.Aarch64, AppleArch.X86_64, }, ),
  133. "macos-framework-arm64": JobSpec(name="MacOS (Framework) (arm64)", priority=True, os=JobOs.MacosLatest, platform=SdlPlatform.MacOS, artifact=None, apple_framework=True, apple_archs={AppleArch.Aarch64, AppleArch.X86_64, }, xcode=True, ),
  134. "macos-26-framework-arm64": JobSpec(name="MacOS 26 (Framework) (arm64)",priority=False, os=JobOs.Macos26, platform=SdlPlatform.MacOS, artifact=None, apple_framework=True, apple_archs={AppleArch.Aarch64, AppleArch.X86_64, }, ),
  135. "macos-gnu-arm64": JobSpec(name="MacOS (GNU prefix)", priority=False, os=JobOs.MacosLatest, platform=SdlPlatform.MacOS, artifact="SDL-macos-arm64-gnu", apple_framework=False, apple_archs={AppleArch.Aarch64, }, ),
  136. "ios": JobSpec(name="iOS (CMake & xcode)", priority=True, os=JobOs.MacosLatest, platform=SdlPlatform.Ios, artifact="SDL-ios-arm64", xcode=True, ),
  137. "tvos": JobSpec(name="tvOS (CMake & xcode)", priority=True, os=JobOs.MacosLatest, platform=SdlPlatform.Tvos, artifact="SDL-tvos-arm64", xcode=True, ),
  138. "android-cmake": JobSpec(name="Android (CMake)", priority=False, os=JobOs.UbuntuLatest, platform=SdlPlatform.Android, artifact="SDL-android-arm64", android_abi="arm64-v8a", android_arch="aarch64", android_platform=23, ),
  139. "android-cmake-lean": JobSpec(name="Android (CMake, lean)", priority=False, os=JobOs.UbuntuLatest, platform=SdlPlatform.Android, artifact="SDL-lean-android-arm64", android_abi="arm64-v8a", android_arch="aarch64", android_platform=23, lean=True, ),
  140. "android-mk": JobSpec(name="Android (Android.mk)", priority=True, os=JobOs.UbuntuLatest, platform=SdlPlatform.Android, artifact=None, no_cmake=True, android_mk=True, ),
  141. "android-gradle": JobSpec(name="Android (Gradle)", priority=False, os=JobOs.UbuntuLatest, platform=SdlPlatform.Android, artifact=None, no_cmake=True, android_gradle=True, ),
  142. "emscripten": JobSpec(name="Emscripten", priority=True, os=JobOs.UbuntuLatest, platform=SdlPlatform.Emscripten, artifact="SDL-emscripten", ),
  143. "haiku": JobSpec(name="Haiku", priority=False, os=JobOs.UbuntuLatest, platform=SdlPlatform.Haiku, artifact="SDL-haiku-x64", container="ghcr.io/haiku/cross-compiler:x86_64-r1beta5", ),
  144. "loongarch64": JobSpec(name="LoongArch64", priority=False, os=JobOs.UbuntuLatest, platform=SdlPlatform.LoongArch64, artifact="SDL-loongarch64", ),
  145. "n3ds": JobSpec(name="Nintendo 3DS", priority=False, os=JobOs.UbuntuLatest, platform=SdlPlatform.N3ds, artifact="SDL-n3ds", container="devkitpro/devkitarm:latest", ),
  146. "ppc": JobSpec(name="PowerPC", priority=False, os=JobOs.UbuntuLatest, platform=SdlPlatform.PowerPC, artifact="SDL-ppc", container="dockcross/linux-ppc:latest", ),
  147. "ppc64": JobSpec(name="PowerPC64", priority=False, os=JobOs.UbuntuLatest, platform=SdlPlatform.PowerPC64, artifact="SDL-ppc64le", container="dockcross/linux-ppc64le:latest", ),
  148. "ps2": JobSpec(name="Sony PlayStation 2", priority=False, os=JobOs.UbuntuLatest, platform=SdlPlatform.Ps2, artifact="SDL-ps2", container="ps2dev/ps2dev:latest", ),
  149. "psp": JobSpec(name="Sony PlayStation Portable", priority=False, os=JobOs.UbuntuLatest, platform=SdlPlatform.Psp, artifact="SDL-psp", container="pspdev/pspdev:latest", ),
  150. "vita-pib": JobSpec(name="Sony PlayStation Vita (GLES w/ pib)", priority=False, os=JobOs.UbuntuLatest, platform=SdlPlatform.Vita, artifact="SDL-vita-pib", container="vitasdk/vitasdk:latest", vita_gles=VitaGLES.Pib, ),
  151. "vita-pvr": JobSpec(name="Sony PlayStation Vita (GLES w/ PVR_PSP2)", priority=False, os=JobOs.UbuntuLatest, platform=SdlPlatform.Vita, artifact="SDL-vita-pvr", container="vitasdk/vitasdk:latest", vita_gles=VitaGLES.Pvr, ),
  152. "riscos": JobSpec(name="RISC OS", priority=False, os=JobOs.UbuntuLatest, platform=SdlPlatform.Riscos, artifact="SDL-riscos", container="riscosdotinfo/riscos-gccsdk-4.7:latest", ),
  153. "netbsd": JobSpec(name="NetBSD", priority=False, os=JobOs.UbuntuLatest, platform=SdlPlatform.NetBSD, artifact="SDL-netbsd-x64", ),
  154. "openbsd": JobSpec(name="OpenBSD", priority=False, os=JobOs.UbuntuLatest, platform=SdlPlatform.OpenBSD, artifact="SDL-openbsd-x64", ),
  155. "freebsd": JobSpec(name="FreeBSD", priority=False, os=JobOs.UbuntuLatest, platform=SdlPlatform.FreeBSD, artifact="SDL-freebsd-x64", ),
  156. "ngage": JobSpec(name="N-Gage", priority=False, os=JobOs.WindowsLatest, platform=SdlPlatform.NGage, artifact="SDL-ngage", ),
  157. "djgpp": JobSpec(name="DOS (DJGPP)", priority=False, os=JobOs.UbuntuLatest, platform=SdlPlatform.DJGPP, artifact="SDL-djgpp", ),
  158. }
  159. class StaticLibType(Enum):
  160. STATIC_LIB = "SDL3-static.lib"
  161. A = "libSDL3.a"
  162. class SharedLibType(Enum):
  163. WIN32 = "SDL3.dll"
  164. CYGDLL = "cygSDL3.dll"
  165. SO_0 = "libSDL3.so.0"
  166. SO = "libSDL3.so"
  167. DYLIB = "libSDL3.0.dylib"
  168. FRAMEWORK = "SDL3.framework/Versions/A/SDL3"
  169. @dataclasses.dataclass(slots=True)
  170. class JobDetails:
  171. name: str
  172. key: str
  173. os: str
  174. platform: str
  175. artifact: str
  176. no_cmake: bool
  177. ccache: bool = False
  178. build_tests: bool = True
  179. container: str = ""
  180. cmake_build_type: str = "RelWithDebInfo"
  181. shell: str = "sh"
  182. sudo: str = "sudo"
  183. cmake_config_emulator: str = ""
  184. apk_packages: list[str] = dataclasses.field(default_factory=list)
  185. apt_packages: list[str] = dataclasses.field(default_factory=list)
  186. brew_packages: list[str] = dataclasses.field(default_factory=list)
  187. cmake_toolchain_file: str = ""
  188. cmake_arguments: list[str] = dataclasses.field(default_factory=list)
  189. cmake_generator: str = "Ninja"
  190. cmake_build_arguments: list[str] = dataclasses.field(default_factory=list)
  191. clang_tidy: bool = True
  192. cppflags: list[str] = dataclasses.field(default_factory=list)
  193. cc: str = ""
  194. cxx: str = ""
  195. cflags: list[str] = dataclasses.field(default_factory=list)
  196. cxxflags: list[str] = dataclasses.field(default_factory=list)
  197. ldflags: list[str] = dataclasses.field(default_factory=list)
  198. pollute_directories: list[str] = dataclasses.field(default_factory=list)
  199. use_cmake: bool = True
  200. shared: bool = True
  201. static: bool = True
  202. shared_lib: Optional[SharedLibType] = None
  203. static_lib: Optional[StaticLibType] = None
  204. run_tests: bool = True
  205. test_pkg_config: bool = True
  206. cc_from_cmake: bool = False
  207. source_cmd: str = ""
  208. pretest_cmd: str = ""
  209. java: bool = False
  210. android_apks: list[str] = dataclasses.field(default_factory=list)
  211. android_ndk: bool = False
  212. android_mk: bool = False
  213. android_gradle: bool = False
  214. minidump: bool = False
  215. intel: bool = False
  216. msys2_msystem: str = ""
  217. msys2_packages: list[str] = dataclasses.field(default_factory=list)
  218. cygwin_packages: list[str] = dataclasses.field(default_factory=list)
  219. werror: bool = True
  220. msvc_vcvars_arch: str = ""
  221. msvc_vcvars_sdk: str = ""
  222. msvc_project: str = ""
  223. msvc_project_flags: list[str] = dataclasses.field(default_factory=list)
  224. setup_ninja: bool = False
  225. setup_libusb_arch: str = ""
  226. xcode_sdk: str = ""
  227. cpactions: bool = False
  228. setup_gdk_folder: str = ""
  229. cpactions_os: str = ""
  230. cpactions_version: str = ""
  231. cpactions_arch: str = ""
  232. cpactions_setup_cmd: str = ""
  233. cpactions_install_cmd: str = ""
  234. setup_vita_gles_type: str = ""
  235. check_sources: bool = False
  236. setup_python: bool = False
  237. pypi_packages: list[str] = dataclasses.field(default_factory=list)
  238. setup_gage_sdk_path: str = ""
  239. binutils_strings: str = "strings"
  240. ctest_args: str = ""
  241. def to_workflow(self, enable_artifacts: bool) -> dict[str, str|bool]:
  242. data = {
  243. "name": self.name,
  244. "key": self.key,
  245. "os": self.os,
  246. "ccache": self.ccache,
  247. "container": self.container if self.container else "",
  248. "platform": self.platform,
  249. "artifact": self.artifact,
  250. "enable-artifacts": enable_artifacts,
  251. "shell": self.shell,
  252. "msys2-msystem": self.msys2_msystem,
  253. "msys2-packages": my_shlex_join(self.msys2_packages),
  254. "cygwin-packages": my_shlex_join(self.cygwin_packages),
  255. "android-ndk": self.android_ndk,
  256. "java": self.java,
  257. "intel": self.intel,
  258. "apk-packages": my_shlex_join(self.apk_packages),
  259. "apt-packages": my_shlex_join(self.apt_packages),
  260. "test-pkg-config": self.test_pkg_config,
  261. "brew-packages": my_shlex_join(self.brew_packages),
  262. "pollute-directories": my_shlex_join(self.pollute_directories),
  263. "no-cmake": self.no_cmake,
  264. "build-tests": self.build_tests,
  265. "source-cmd": self.source_cmd,
  266. "pretest-cmd": self.pretest_cmd,
  267. "cmake-config-emulator": self.cmake_config_emulator,
  268. "cc": self.cc,
  269. "cxx": self.cxx,
  270. "cflags": my_shlex_join(self.cppflags + self.cflags),
  271. "cxxflags": my_shlex_join(self.cppflags + self.cxxflags),
  272. "ldflags": my_shlex_join(self.ldflags),
  273. "cmake-generator": self.cmake_generator,
  274. "cmake-toolchain-file": self.cmake_toolchain_file,
  275. "clang-tidy": self.clang_tidy,
  276. "cmake-arguments": my_shlex_join(self.cmake_arguments),
  277. "cmake-build-arguments": my_shlex_join(self.cmake_build_arguments),
  278. "shared": self.shared,
  279. "static": self.static,
  280. "shared-lib": self.shared_lib.value if self.shared_lib else None,
  281. "static-lib": self.static_lib.value if self.static_lib else None,
  282. "cmake-build-type": self.cmake_build_type,
  283. "run-tests": self.run_tests,
  284. "android-apks": my_shlex_join(self.android_apks),
  285. "android-gradle": self.android_gradle,
  286. "android-mk": self.android_mk,
  287. "werror": self.werror,
  288. "sudo": self.sudo,
  289. "msvc-vcvars-arch": self.msvc_vcvars_arch,
  290. "msvc-vcvars-sdk": self.msvc_vcvars_sdk,
  291. "msvc-project": self.msvc_project,
  292. "msvc-project-flags": my_shlex_join(self.msvc_project_flags),
  293. "setup-ninja": self.setup_ninja,
  294. "setup-libusb-arch": self.setup_libusb_arch,
  295. "cc-from-cmake": self.cc_from_cmake,
  296. "xcode-sdk": self.xcode_sdk,
  297. "cpactions": self.cpactions,
  298. "cpactions-os": self.cpactions_os,
  299. "cpactions-version": self.cpactions_version,
  300. "cpactions-arch": self.cpactions_arch,
  301. "cpactions-setup-cmd": self.cpactions_setup_cmd,
  302. "cpactions-install-cmd": self.cpactions_install_cmd,
  303. "setup-vita-gles-type": self.setup_vita_gles_type,
  304. "setup-gdk-folder": self.setup_gdk_folder,
  305. "check-sources": self.check_sources,
  306. "setup-python": self.setup_python,
  307. "pypi-packages": my_shlex_join(self.pypi_packages),
  308. "setup-ngage-sdk-path": self.setup_gage_sdk_path,
  309. "binutils-strings": self.binutils_strings,
  310. "ctest-args": self.ctest_args,
  311. }
  312. return {k: v for k, v in data.items() if v != ""}
  313. def my_shlex_join(s):
  314. def escape(s):
  315. if s[:1] == "'" and s[-1:] == "'":
  316. return s
  317. if set(s).intersection(set("; \t")):
  318. return f'"{s}"'
  319. return s
  320. return " ".join(escape(s))
  321. def spec_to_job(spec: JobSpec, key: str, trackmem_symbol_names: bool, ctest_args: list[str]) -> JobDetails:
  322. job = JobDetails(
  323. name=spec.name,
  324. key=key,
  325. os=spec.os.value,
  326. artifact=spec.artifact or "",
  327. container=spec.container or "",
  328. platform=spec.platform.value,
  329. sudo="sudo",
  330. no_cmake=spec.no_cmake,
  331. )
  332. spec_arch = get_job_arch(spec.os)
  333. if job.os.startswith("ubuntu"):
  334. job.apt_packages.extend([
  335. "ninja-build",
  336. "pkg-config",
  337. ])
  338. pretest_cmd = []
  339. if trackmem_symbol_names:
  340. pretest_cmd.append("export SDL_TRACKMEM_SYMBOL_NAMES=1")
  341. else:
  342. pretest_cmd.append("export SDL_TRACKMEM_SYMBOL_NAMES=0")
  343. win32 = spec.platform in (SdlPlatform.Msys2, SdlPlatform.Msvc)
  344. fpic = None
  345. build_parallel = True
  346. if spec.lean:
  347. job.cmake_arguments.append("-DSDL_LEAN_AND_MEAN=TRUE")
  348. if win32:
  349. job.cmake_arguments.append("-DSDLTEST_PROCDUMP=ON")
  350. job.minidump = True
  351. if spec.intel is not None:
  352. match spec.intel:
  353. case IntelCompiler.Icx:
  354. job.cc = "icx"
  355. job.cxx = "icpx"
  356. case IntelCompiler.Icc:
  357. job.cc = "icc"
  358. job.cxx = "icpc"
  359. # Disable deprecation warning
  360. job.cppflags.append("-diag-disable=10441")
  361. # Avoid 'Catastrophic error: cannot open precompiled header file'
  362. job.cmake_arguments.append("-DCMAKE_DISABLE_PRECOMPILE_HEADERS:BOOL=ON")
  363. job.clang_tidy = False
  364. case _:
  365. raise ValueError(f"Invalid intel={spec.intel}")
  366. job.source_cmd = f"source /opt/intel/oneapi/setvars.sh;"
  367. job.intel = True
  368. job.shell = "bash"
  369. job.cmake_arguments.extend((
  370. f"-DCMAKE_C_COMPILER={job.cc}",
  371. f"-DCMAKE_CXX_COMPILER={job.cxx}",
  372. "-DCMAKE_SYSTEM_NAME=Linux",
  373. ))
  374. match spec.platform:
  375. case SdlPlatform.Msvc:
  376. job.setup_ninja = not spec.gdk
  377. job.clang_tidy = False # complains about \threadsafety: "unknown command tag name [clang-diagnostic-documentation-unknown-command]"
  378. job.msvc_project = spec.msvc_project if spec.msvc_project else ""
  379. job.msvc_project_flags.append("-p:TreatWarningsAsError=true")
  380. job.test_pkg_config = False
  381. job.shared_lib = SharedLibType.WIN32
  382. job.static_lib = StaticLibType.STATIC_LIB
  383. job.cmake_arguments.extend((
  384. "-DCMAKE_MSVC_DEBUG_INFORMATION_FORMAT=ProgramDatabase",
  385. "-DCMAKE_EXE_LINKER_FLAGS=-DEBUG",
  386. "-DCMAKE_SHARED_LINKER_FLAGS=-DEBUG",
  387. ))
  388. job.cmake_arguments.append("'-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded$<$<CONFIG:Debug>:Debug>'")
  389. if spec.clang_cl:
  390. job.cmake_arguments.extend((
  391. "-DCMAKE_C_COMPILER=clang-cl",
  392. "-DCMAKE_CXX_COMPILER=clang-cl",
  393. ))
  394. match spec.msvc_arch:
  395. case MsvcArch.X86:
  396. job.cflags.append("/clang:-m32")
  397. job.cxxflags.append("/clang:-m32")
  398. job.ldflags.append("/MACHINE:X86")
  399. case MsvcArch.X64:
  400. job.cflags.append("/clang:-m64")
  401. job.cxxflags.append("/clang:-m64")
  402. job.ldflags.append("/MACHINE:X64")
  403. case _:
  404. raise ValueError(f"Unsupported clang-cl architecture (arch={spec.msvc_arch})")
  405. if spec.msvc_project:
  406. match spec.msvc_arch:
  407. case MsvcArch.X86:
  408. msvc_platform = "Win32"
  409. case MsvcArch.X64:
  410. msvc_platform = "x64"
  411. case MsvcArch.Arm64:
  412. msvc_platform = "ARM64"
  413. case _:
  414. raise ValueError(f"Unsupported vcxproj architecture (arch={spec.msvc_arch})")
  415. if spec.gdk:
  416. msvc_platform = f"Gaming.Desktop.{msvc_platform}"
  417. job.msvc_project_flags.append(f"-p:Platform={msvc_platform}")
  418. match spec.msvc_arch:
  419. case MsvcArch.X86:
  420. assert spec_arch == JobArch.X64
  421. job.msvc_vcvars_arch = "x64_x86"
  422. target_job_arch = JobArch.X64
  423. case MsvcArch.X64:
  424. assert spec_arch == JobArch.X64
  425. job.msvc_vcvars_arch = "x64"
  426. target_job_arch = JobArch.X64
  427. case MsvcArch.Arm64:
  428. match spec_arch:
  429. case JobArch.Arm64:
  430. job.msvc_vcvars_arch = "arm64"
  431. case JobArch.X64:
  432. job.msvc_vcvars_arch = "x64_arm64"
  433. case _:
  434. raise ValueError(get_job_arch(spec.os))
  435. target_job_arch = JobArch.Arm64
  436. case _:
  437. raise ValueError(spec.msvc_arch)
  438. job.run_tests = target_job_arch == spec_arch
  439. if spec.gdk:
  440. job.setup_gdk_folder = "VisualC-GDK"
  441. else:
  442. match spec.msvc_arch:
  443. case MsvcArch.X86:
  444. job.setup_libusb_arch = "x86"
  445. case MsvcArch.X64:
  446. job.setup_libusb_arch = "x64"
  447. case SdlPlatform.Linux:
  448. if spec.name.startswith("Ubuntu"):
  449. assert spec.os.value.startswith("ubuntu-")
  450. job.apt_packages.extend((
  451. "ccache",
  452. "gnome-desktop-testing",
  453. "libasound2-dev",
  454. "libpulse-dev",
  455. "libaudio-dev",
  456. "libjack-dev",
  457. "libsndio-dev",
  458. "libusb-1.0-0-dev",
  459. "libx11-dev",
  460. "libxext-dev",
  461. "libxrandr-dev",
  462. "libxcursor-dev",
  463. "libxfixes-dev",
  464. "libxi-dev",
  465. "libxss-dev",
  466. "libxtst-dev",
  467. "libwayland-dev",
  468. "libxkbcommon-dev",
  469. "libdrm-dev",
  470. "libgbm-dev",
  471. "libgl1-mesa-dev",
  472. "libgles2-mesa-dev",
  473. "libegl1-mesa-dev",
  474. "libdbus-1-dev",
  475. "libibus-1.0-dev",
  476. "libudev-dev",
  477. "fcitx-libs-dev",
  478. "libfribidi-dev",
  479. # testffmpeg
  480. "libavcodec-dev",
  481. "libavfilter-dev",
  482. "libavutil-dev",
  483. "libswresample-dev",
  484. "libswscale-dev",
  485. ))
  486. match = re.match(r"ubuntu-(?P<year>[0-9]+)\.(?P<month>[0-9]+|latest).*", spec.os.value)
  487. ubuntu_ge_24 = True
  488. if match and match["month"] != "latest":
  489. ubuntu_year, ubuntu_month = [int(match["year"]), int(match["month"])]
  490. ubuntu_ge_24 = ubuntu_year >= 24
  491. job.apt_packages.extend(("libpipewire-0.3-dev", "libdecor-0-dev"))
  492. if not ubuntu_ge_24:
  493. job.clang_tidy = False
  494. job.apt_packages.extend((
  495. "libunwind-dev", # For SDL_test memory tracking
  496. ))
  497. job.ccache = True
  498. if trackmem_symbol_names:
  499. # older libunwind is slow
  500. job.cmake_arguments.append("-DSDLTEST_TIMEOUT_MULTIPLIER=2")
  501. job.shared_lib = SharedLibType.SO_0
  502. job.static_lib = StaticLibType.A
  503. fpic = True
  504. job.cmake_arguments.append("-DSDLTEST_GDB=ON")
  505. job.apt_packages.append("gdb")
  506. if spec.more_hard_deps:
  507. # Some distros prefer to make important dependencies
  508. # mandatory, so that SDL won't start up but lack expected
  509. # functionality if they're missing
  510. job.cmake_arguments.extend([
  511. "-DSDL_ALSA_SHARED=OFF",
  512. "-DSDL_FRIBIDI_SHARED=OFF",
  513. "-DSDL_HIDAPI_LIBUSB_SHARED=OFF",
  514. "-DSDL_PULSEAUDIO_SHARED=OFF",
  515. "-DSDL_X11_SHARED=OFF",
  516. "-DSDL_WAYLAND_LIBDECOR_SHARED=OFF",
  517. "-DSDL_WAYLAND_SHARED=OFF",
  518. ])
  519. case SdlPlatform.Ios | SdlPlatform.Tvos:
  520. job.brew_packages.extend([
  521. "ccache",
  522. "ninja",
  523. ])
  524. job.ccache = True
  525. job.clang_tidy = False
  526. job.run_tests = False
  527. job.test_pkg_config = False
  528. job.shared_lib = SharedLibType.DYLIB
  529. job.static_lib = StaticLibType.A
  530. match spec.platform:
  531. case SdlPlatform.Ios:
  532. if spec.xcode:
  533. job.xcode_sdk = 'iphoneos'
  534. job.cmake_arguments.extend([
  535. "-DCMAKE_SYSTEM_NAME=iOS",
  536. "-DCMAKE_OSX_ARCHITECTURES=\"arm64\"",
  537. "-DCMAKE_OSX_DEPLOYMENT_TARGET=11.0",
  538. ])
  539. case SdlPlatform.Tvos:
  540. if spec.xcode:
  541. job.xcode_sdk = 'appletvos'
  542. job.cmake_arguments.extend([
  543. "-DCMAKE_SYSTEM_NAME=tvOS",
  544. "-DCMAKE_OSX_ARCHITECTURES=\"arm64\"",
  545. "-DCMAKE_OSX_DEPLOYMENT_TARGET=11.0",
  546. ])
  547. case SdlPlatform.MacOS:
  548. if spec.apple_framework:
  549. job.static = False
  550. job.clang_tidy = False
  551. job.test_pkg_config = False
  552. job.cmake_arguments.extend((
  553. "'-DCMAKE_OSX_ARCHITECTURES=x86_64;arm64'",
  554. "-DCMAKE_OSX_DEPLOYMENT_TARGET=10.13",
  555. "-DSDL_FRAMEWORK=ON",
  556. ))
  557. job.shared_lib = SharedLibType.FRAMEWORK
  558. else:
  559. job.clang_tidy = True
  560. job.cmake_arguments.extend((
  561. "-DCMAKE_OSX_ARCHITECTURES=arm64",
  562. "-DCMAKE_OSX_DEPLOYMENT_TARGET=10.13",
  563. "-DCLANG_TIDY_BINARY=$(brew --prefix llvm)/bin/clang-tidy",
  564. ))
  565. job.brew_packages.extend((
  566. # Brew provides a single architecture (aarch64), so it's not usable for fat libraries
  567. "ffmpeg", # testffmpeg
  568. ))
  569. job.shared_lib = SharedLibType.DYLIB
  570. job.static_lib = StaticLibType.A
  571. job.ccache = True
  572. job.apt_packages = []
  573. job.brew_packages.extend((
  574. "ninja",
  575. ))
  576. if job.ccache:
  577. job.brew_packages.append("ccache")
  578. if job.clang_tidy:
  579. job.brew_packages.append("llvm")
  580. if spec.xcode:
  581. job.xcode_sdk = "macosx"
  582. case SdlPlatform.Android:
  583. job.android_gradle = spec.android_gradle
  584. job.android_mk = spec.android_mk
  585. job.apt_packages.append("ccache")
  586. job.run_tests = False
  587. job.shared_lib = SharedLibType.SO
  588. job.static_lib = StaticLibType.A
  589. if spec.android_mk or not spec.no_cmake:
  590. job.android_ndk = True
  591. if spec.android_gradle or not spec.no_cmake:
  592. job.java = True
  593. if spec.android_mk or spec.android_gradle:
  594. job.apt_packages = []
  595. if not spec.no_cmake:
  596. job.ccache = True
  597. job.cmake_arguments.extend((
  598. f"-DANDROID_PLATFORM={spec.android_platform}",
  599. f"-DANDROID_ABI={spec.android_abi}",
  600. ))
  601. job.cmake_toolchain_file = "${ANDROID_NDK_HOME}/build/cmake/android.toolchain.cmake"
  602. job.cc = f"${{ANDROID_NDK_HOME}}/toolchains/llvm/prebuilt/linux-x86_64/bin/clang --target={spec.android_arch}-none-linux-androideabi{spec.android_platform}"
  603. job.android_apks = [
  604. "testaudiorecording-apk",
  605. "testautomation-apk",
  606. "testcontroller-apk",
  607. "testmultiaudio-apk",
  608. "testsprite-apk",
  609. ]
  610. # -fPIC is required after updating NDK from 21 to 28
  611. job.cflags.append("-fPIC")
  612. job.cxxflags.append("-fPIC")
  613. case SdlPlatform.Emscripten:
  614. emscripten_tests = not spec.priority
  615. job.clang_tidy = False # clang-tidy does not understand -gsource-map
  616. job.shared = False
  617. job.ccache = True
  618. job.apt_packages.append("ccache")
  619. job.cmake_config_emulator = "emcmake"
  620. job.cmake_build_type = "Debug"
  621. job.test_pkg_config = False
  622. job.cmake_arguments.extend((
  623. "-DSDLTEST_BROWSER=chrome",
  624. "-DSDLTEST_TIMEOUT_MULTIPLIER=4",
  625. "-DSDLTEST_CHROME_BINARY=${CHROME_BINARY}",
  626. ))
  627. job.cflags.extend((
  628. "-gsource-map",
  629. "-ffile-prefix-map=${PWD}=/SDL",
  630. ))
  631. job.ldflags.extend((
  632. "--source-map-base", "/",
  633. ))
  634. pretest_cmd.extend((
  635. "# Start local HTTP server",
  636. "cmake --build build --target serve-sdl-tests --verbose &",
  637. "chrome --version",
  638. "chromedriver --version",
  639. ))
  640. job.static_lib = StaticLibType.A
  641. if emscripten_tests:
  642. job.setup_python = True
  643. job.pypi_packages.append("selenium")
  644. else:
  645. job.cmake_arguments.extend((
  646. "-DSDL_TESTS=OFF",
  647. ))
  648. job.run_tests = False
  649. case SdlPlatform.Ps2:
  650. job.ccache = False # actions/ccache does not work in psp container (incompatible tar of busybox)
  651. build_parallel = False
  652. job.shared = False
  653. job.sudo = ""
  654. job.apt_packages = []
  655. job.apk_packages = ["ccache", "cmake", "gmp", "mpc1", "mpfr4", "ninja", "pkgconf", "git", ]
  656. job.cmake_toolchain_file = "${PS2DEV}/ps2sdk/ps2dev.cmake"
  657. job.clang_tidy = False
  658. job.run_tests = False
  659. job.shared = False
  660. job.cc = "mips64r5900el-ps2-elf-gcc"
  661. job.ldflags = ["-L${PS2DEV}/ps2sdk/ee/lib", "-L${PS2DEV}/gsKit/lib", "-L${PS2DEV}/ps2sdk/ports/lib", ]
  662. job.static_lib = StaticLibType.A
  663. case SdlPlatform.Psp:
  664. job.ccache = False # actions/ccache does not work in psp container (incompatible tar of busybox)
  665. build_parallel = False
  666. job.sudo = ""
  667. job.apt_packages = []
  668. job.apk_packages = ["ccache", "cmake", "gmp", "mpc1", "mpfr4", "ninja", "pkgconf", ]
  669. job.cmake_toolchain_file = "${PSPDEV}/psp/share/pspdev.cmake"
  670. job.clang_tidy = False
  671. job.run_tests = False
  672. job.shared = False
  673. job.cc = "psp-gcc"
  674. job.ldflags = ["-L${PSPDEV}/lib", "-L${PSPDEV}/psp/lib", "-L${PSPDEV}/psp/sdk/lib", ]
  675. job.pollute_directories = ["${PSPDEV}/include", "${PSPDEV}/psp/include", "${PSPDEV}/psp/sdk/include", ]
  676. job.static_lib = StaticLibType.A
  677. case SdlPlatform.Vita:
  678. job.ccache = True
  679. job.sudo = ""
  680. job.apt_packages.extend(["ccache", "cmake", "unzip"])
  681. job.cmake_toolchain_file = "${VITASDK}/share/vita.toolchain.cmake"
  682. assert spec.vita_gles is not None
  683. job.setup_vita_gles_type = {
  684. VitaGLES.Pib: "pib",
  685. VitaGLES.Pvr: "pvr",
  686. }[spec.vita_gles]
  687. job.cmake_arguments.extend((
  688. f"-DVIDEO_VITA_PIB={ 'true' if spec.vita_gles == VitaGLES.Pib else 'false' }",
  689. f"-DVIDEO_VITA_PVR={ 'true' if spec.vita_gles == VitaGLES.Pvr else 'false' }",
  690. "-DSDL_ARMNEON=ON",
  691. "-DSDL_ARMSIMD=ON",
  692. ))
  693. # Fix vita.toolchain.cmake (https://github.com/vitasdk/vita-toolchain/pull/253)
  694. job.source_cmd = r"""sed -i -E "s#set\\( PKG_CONFIG_EXECUTABLE \"\\$\\{VITASDK}/bin/arm-vita-eabi-pkg-config\" \\)#set\\( PKG_CONFIG_EXECUTABLE \"${VITASDK}/bin/arm-vita-eabi-pkg-config\" CACHE PATH \"Path of pkg-config executable\" \\)#" ${VITASDK}/share/vita.toolchain.cmake"""
  695. job.clang_tidy = False
  696. job.run_tests = False
  697. job.shared = False
  698. job.cc = "arm-vita-eabi-gcc"
  699. job.static_lib = StaticLibType.A
  700. case SdlPlatform.Haiku:
  701. job.ccache = True
  702. fpic = False
  703. job.run_tests = False
  704. job.apt_packages.append("ccache")
  705. job.cc = "x86_64-unknown-haiku-gcc"
  706. job.cxx = "x86_64-unknown-haiku-g++"
  707. job.sudo = ""
  708. job.cmake_arguments.extend((
  709. f"-DCMAKE_C_COMPILER={job.cc}",
  710. f"-DCMAKE_CXX_COMPILER={job.cxx}",
  711. "-DCMAKE_SYSTEM_NAME=Haiku",
  712. ))
  713. job.shared_lib = SharedLibType.SO_0
  714. job.static_lib = StaticLibType.A
  715. case SdlPlatform.PowerPC64 | SdlPlatform.PowerPC:
  716. job.ccache = True
  717. # FIXME: Enable SDL_WERROR
  718. job.werror = False
  719. job.clang_tidy = False
  720. job.run_tests = False
  721. job.sudo = ""
  722. job.apt_packages = ["ccache"]
  723. job.shared_lib = SharedLibType.SO_0
  724. job.static_lib = StaticLibType.A
  725. job.cmake_arguments.extend((
  726. "-DSDL_UNIX_CONSOLE_BUILD=ON",
  727. ))
  728. case SdlPlatform.LoongArch64:
  729. job.ccache = True
  730. fpic = True
  731. job.run_tests = False
  732. job.apt_packages.append("ccache")
  733. job.cc = "${LOONGARCH64_CC}"
  734. job.cxx = "${LOONGARCH64_CXX}"
  735. job.cmake_arguments.extend((
  736. f"-DCMAKE_C_COMPILER={job.cc}",
  737. f"-DCMAKE_CXX_COMPILER={job.cxx}",
  738. "-DSDL_UNIX_CONSOLE_BUILD=ON",
  739. "-DCMAKE_SYSTEM_NAME=Linux",
  740. ))
  741. job.shared_lib = SharedLibType.SO_0
  742. job.static_lib = StaticLibType.A
  743. case SdlPlatform.N3ds:
  744. job.cmake_generator = "Unix Makefiles"
  745. job.cmake_build_arguments.append("-j$(nproc)")
  746. job.ccache = False
  747. job.shared = False
  748. job.apt_packages = []
  749. job.clang_tidy = False
  750. job.run_tests = False
  751. job.cc_from_cmake = True
  752. job.cmake_toolchain_file = "${DEVKITPRO}/cmake/3DS.cmake"
  753. job.binutils_strings = "/opt/devkitpro/devkitARM/bin/arm-none-eabi-strings"
  754. job.static_lib = StaticLibType.A
  755. case SdlPlatform.Msys2:
  756. job.ccache = True
  757. job.shell = "msys2 {0}"
  758. assert spec.msys2_platform
  759. job.msys2_msystem = spec.msys2_platform.value
  760. job.shared_lib = SharedLibType.WIN32
  761. job.static_lib = StaticLibType.A
  762. msys2_env = {
  763. "mingw32": "mingw-w64-i686",
  764. "mingw64": "mingw-w64-x86_64",
  765. "clang64": "mingw-w64-clang-x86_64",
  766. "ucrt64": "mingw-w64-ucrt-x86_64",
  767. }[spec.msys2_platform.value]
  768. job.msys2_packages.extend([
  769. f"{msys2_env}-cc",
  770. f"{msys2_env}-cmake",
  771. f"{msys2_env}-ffmpeg",
  772. f"{msys2_env}-ninja",
  773. f"{msys2_env}-pkg-config",
  774. ])
  775. if spec.msys2_platform not in (Msys2Platform.Mingw32, ):
  776. job.msys2_packages.append(f"{msys2_env}-perl")
  777. job.msys2_packages.append(f"{msys2_env}-clang-tools-extra")
  778. if job.ccache:
  779. job.msys2_packages.append(f"{msys2_env}-ccache")
  780. case SdlPlatform.Cygwin:
  781. job.ccache = False # Missing evict-older-than option
  782. job.clang_tidy = False # error finding files [clang-diagnostic-error] cause might be space in command path
  783. job.test_pkg_config = False # Linefeed issue in test_pkgconfig.sh
  784. job.shell = "bash --noprofile --norc -eo pipefail -o igncr {0}"
  785. job.shared_lib = SharedLibType.CYGDLL
  786. job.static_lib = StaticLibType.A
  787. job.cmake_arguments.append("-DSDLTEST_GDB=ON")
  788. job.cygwin_packages.extend([
  789. "cmake",
  790. "gcc-core",
  791. "gcc-g++",
  792. "gdb",
  793. "ninja",
  794. "pkg-config",
  795. "perl",
  796. "python",
  797. ])
  798. case SdlPlatform.Riscos:
  799. job.ccache = False # FIXME: enable when container gets upgrade
  800. # FIXME: Enable SDL_WERROR
  801. job.werror = False
  802. job.apt_packages = ["ccache", "cmake", "ninja-build"]
  803. job.test_pkg_config = False
  804. job.shared = False
  805. job.run_tests = False
  806. job.sudo = ""
  807. job.cmake_arguments.extend((
  808. "-DRISCOS:BOOL=ON",
  809. "-DCMAKE_DISABLE_PRECOMPILE_HEADERS:BOOL=ON",
  810. "-DSDL_GCC_ATOMICS:BOOL=OFF",
  811. ))
  812. job.cmake_toolchain_file = "/home/riscos/env/toolchain-riscos.cmake"
  813. job.static_lib = StaticLibType.A
  814. case SdlPlatform.FreeBSD | SdlPlatform.NetBSD | SdlPlatform.OpenBSD:
  815. job.cpactions = True
  816. job.no_cmake = True
  817. job.run_tests = False
  818. job.apt_packages = []
  819. job.shared_lib = SharedLibType.SO_0
  820. job.static_lib = StaticLibType.A
  821. match spec.platform:
  822. case SdlPlatform.FreeBSD:
  823. job.cpactions_os = "freebsd"
  824. job.cpactions_version = "14.4"
  825. job.cpactions_arch = "x86-64"
  826. job.cpactions_setup_cmd = "sudo pkg update"
  827. job.cpactions_install_cmd = "sudo pkg install -y cmake ninja pkgconf libXcursor libXext libXinerama libXi libXfixes libXrandr libXScrnSaver libXxf86vm wayland wayland-protocols libxkbcommon mesa-libs libglvnd evdev-proto libinotify alsa-lib jackit pipewire pulseaudio sndio dbus zh-fcitx ibus libudev-devd"
  828. job.cmake_arguments.extend((
  829. "-DSDL_CHECK_REQUIRED_INCLUDES=/usr/local/include",
  830. "-DSDL_CHECK_REQUIRED_LINK_OPTIONS=-L/usr/local/lib",
  831. ))
  832. case SdlPlatform.NetBSD:
  833. job.cpactions_os = "netbsd"
  834. job.cpactions_version = "10.1"
  835. job.cpactions_arch = "x86-64"
  836. job.cpactions_setup_cmd = "export PATH=\"/usr/pkg/sbin:/usr/pkg/bin:/sbin:$PATH\"; export PKG_CONFIG_PATH=\"/usr/pkg/lib/pkgconfig\";export PKG_PATH=\"https://cdn.netBSD.org/pub/pkgsrc/packages/NetBSD/$(uname -p)/$(uname -r|cut -f \"1 2\" -d.)/All/\";echo \"PKG_PATH=$PKG_PATH\";echo \"uname -a -> \"$(uname -a)\"\";sudo -E sysctl -w security.pax.aslr.enabled=0;sudo -E sysctl -w security.pax.aslr.global=0;sudo -E pkgin clean;sudo -E pkgin update"
  837. job.cpactions_install_cmd = "sudo -E pkgin -y install cmake dbus pkgconf ninja-build pulseaudio libxkbcommon wayland wayland-protocols libinotify libusb1"
  838. case SdlPlatform.OpenBSD:
  839. job.cpactions_os = "openbsd"
  840. job.cpactions_version = "7.7"
  841. job.cpactions_arch = "x86-64"
  842. job.cpactions_setup_cmd = "sudo pkg_add -u"
  843. job.cpactions_install_cmd = "sudo pkg_add cmake ninja pkgconf wayland wayland-protocols libxkbcommon libinotify pulseaudio dbus ibus"
  844. case SdlPlatform.NGage:
  845. build_parallel = False
  846. job.cmake_build_type = "Release"
  847. job.setup_ninja = True
  848. job.static_lib = StaticLibType.STATIC_LIB
  849. job.shared_lib = None
  850. job.clang_tidy = False
  851. job.werror = False # FIXME: enable SDL_WERROR
  852. job.shared = False
  853. job.run_tests = False
  854. job.setup_gage_sdk_path = "C:/ngagesdk"
  855. job.cmake_toolchain_file = "C:/ngagesdk/cmake/ngage-toolchain.cmake"
  856. job.test_pkg_config = False
  857. case SdlPlatform.DJGPP:
  858. build_parallel = False
  859. job.ccache = True
  860. job.apt_packages = ["ccache", "libfl-dev"] # djgpp needs libfl.so.2
  861. job.cmake_build_type = "Release"
  862. job.setup_ninja = True
  863. job.static_lib = StaticLibType.A
  864. job.shared_lib = None
  865. job.clang_tidy = False
  866. job.werror = False # FIXME: enable SDL_WERROR
  867. job.shared = False
  868. job.run_tests = False
  869. job.test_pkg_config = False
  870. job.cmake_toolchain_file = "$GITHUB_WORKSPACE/build-scripts/i586-pc-msdosdjgpp.cmake"
  871. case _:
  872. raise ValueError(f"Unsupported platform={spec.platform}")
  873. if "ubuntu" in spec.name.lower():
  874. job.check_sources = True
  875. job.setup_python = True
  876. if job.ccache:
  877. job.cmake_arguments.extend((
  878. "-DCMAKE_C_COMPILER_LAUNCHER=ccache",
  879. "-DCMAKE_CXX_COMPILER_LAUNCHER=ccache",
  880. ))
  881. job.ctest_args = shlex.join(ctest_args)
  882. if not build_parallel:
  883. job.cmake_build_arguments.append("-j1")
  884. if job.cflags or job.cppflags:
  885. job.cmake_arguments.append(f"-DCMAKE_C_FLAGS=\"{my_shlex_join(job.cflags + job.cppflags)}\"")
  886. if job.cxxflags or job.cppflags:
  887. job.cmake_arguments.append(f"-DCMAKE_CXX_FLAGS=\"{my_shlex_join(job.cxxflags + job.cppflags)}\"")
  888. if job.ldflags:
  889. job.cmake_arguments.append(f"-DCMAKE_SHARED_LINKER_FLAGS=\"{my_shlex_join(job.ldflags)}\"")
  890. job.cmake_arguments.append(f"-DCMAKE_EXE_LINKER_FLAGS=\"{my_shlex_join(job.ldflags)}\"")
  891. job.pretest_cmd = "\n".join(pretest_cmd)
  892. def tf(b):
  893. return "ON" if b else "OFF"
  894. if fpic is not None:
  895. job.cmake_arguments.append(f"-DCMAKE_POSITION_INDEPENDENT_CODE={tf(fpic)}")
  896. if job.no_cmake:
  897. job.cmake_arguments = []
  898. return job
  899. def spec_to_platform(spec: JobSpec, key: str, enable_artifacts: bool, trackmem_symbol_names: bool, ctest_args:list[str]) -> dict[str, str|bool]:
  900. logger.info("spec=%r", spec)
  901. job = spec_to_job(
  902. spec,
  903. key=key,
  904. trackmem_symbol_names=trackmem_symbol_names,
  905. ctest_args=ctest_args,
  906. )
  907. logger.info("job=%r", job)
  908. platform = job.to_workflow(enable_artifacts=enable_artifacts)
  909. logger.info("platform=%r", platform)
  910. return platform
  911. def main():
  912. parser = argparse.ArgumentParser(allow_abbrev=False)
  913. parser.add_argument("--github-variable-prefix", default="platforms")
  914. parser.add_argument("--github-ci", action="store_true")
  915. parser.add_argument("--verbose", action="store_true")
  916. parser.add_argument("--commit-message-file")
  917. parser.add_argument("--no-artifact", dest="enable_artifacts", action="store_false")
  918. parser.add_argument("--trackmem-symbol-names", dest="trackmem_symbol_names", action="store_true")
  919. parser.add_argument("--priority-only", dest="priority_only", action="store_true")
  920. args = parser.parse_args()
  921. logging.basicConfig(level=logging.INFO if args.verbose else logging.WARNING)
  922. remaining_keys = set(JOB_SPECS.keys())
  923. all_level_keys = (
  924. # Level 1
  925. (
  926. "android-mk",
  927. ),
  928. )
  929. filters = []
  930. ctest_args = []
  931. if args.commit_message_file:
  932. with open(args.commit_message_file, "r") as f:
  933. commit_message = f.read()
  934. for m in re.finditer(r"\[sdl-ci-filter (.*)]", commit_message, flags=re.M):
  935. filters.append(m.group(1).strip(" \t\n\r\t'\""))
  936. if re.search(r"\[sdl-ci-artifacts?]", commit_message, flags=re.M):
  937. args.enable_artifacts = True
  938. if re.search(r"\[sdl-ci-(full-)?trackmem(-symbol-names)?]", commit_message, flags=re.M):
  939. args.trackmem_symbol_names = True
  940. for m in re.finditer(r"\[sdl-ci-ctest-args? (.*)]", commit_message, flags=re.M):
  941. ctest_args.extend(shlex.split(m.group(1)))
  942. if not filters:
  943. if args.priority_only:
  944. filters.extend(spec_id for spec_id, spec in JOB_SPECS.items() if spec.priority)
  945. else:
  946. filters.append("*")
  947. logger.info("filters: %r", filters)
  948. all_level_platforms = {}
  949. all_platforms = {key: spec_to_platform(spec, key=key, enable_artifacts=args.enable_artifacts, trackmem_symbol_names=args.trackmem_symbol_names, ctest_args=ctest_args) for key, spec in JOB_SPECS.items()}
  950. for level_i, level_keys in enumerate(all_level_keys, 1):
  951. level_key = f"level{level_i}"
  952. logger.info("Level %d: keys=%r", level_i, level_keys)
  953. assert all(k in remaining_keys for k in level_keys)
  954. level_platforms = tuple(all_platforms[key] for key in level_keys)
  955. remaining_keys.difference_update(level_keys)
  956. all_level_platforms[level_key] = level_platforms
  957. logger.info("=" * 80)
  958. logger.info("Keys before filter: %r", remaining_keys)
  959. filtered_remaining_keys = set()
  960. for filter in filters:
  961. filtered_remaining_keys.update(fnmatch.filter(remaining_keys, filter))
  962. logger.info("Keys after filter: %r", filtered_remaining_keys)
  963. remaining_keys = filtered_remaining_keys
  964. logger.info("Remaining: %r", remaining_keys)
  965. all_level_platforms["others"] = tuple(all_platforms[key] for key in remaining_keys)
  966. if args.github_ci:
  967. for level, platforms in all_level_platforms.items():
  968. platforms_json = json.dumps(platforms)
  969. txt = f"{args.github_variable_prefix}-{level}={platforms_json}"
  970. logger.info("%s", txt)
  971. if "GITHUB_OUTPUT" in os.environ:
  972. with open(os.environ["GITHUB_OUTPUT"], "a") as f:
  973. f.write(txt)
  974. f.write("\n")
  975. else:
  976. logger.warning("GITHUB_OUTPUT not defined")
  977. return 0
  978. if __name__ == "__main__":
  979. raise SystemExit(main())