create-test-plan.py 48 KB

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