diff --git a/.github/workflows/create-test-plan.py b/.github/workflows/create-test-plan.py index c0e847c05f..8d5bdf621d 100755 --- a/.github/workflows/create-test-plan.py +++ b/.github/workflows/create-test-plan.py @@ -12,6 +12,7 @@ from typing import Optional logger = logging.getLogger(__name__) +WINDOWS_GAMEINPUT_VERSION = "v3.3.195.0 " class AppleArch(Enum): Aarch64 = "aarch64" @@ -217,6 +218,7 @@ class JobDetails: msys2_msystem: str = "" msys2_packages: list[str] = dataclasses.field(default_factory=list) werror: bool = True + microsoft_gameinput_version: str = "" msvc_vcvars_arch: str = "" msvc_vcvars_sdk: str = "" msvc_project: str = "" @@ -286,6 +288,7 @@ class JobDetails: "android-mk": self.android_mk, "werror": self.werror, "sudo": self.sudo, + "microsoft-gameinput-version": self.microsoft_gameinput_version, "msvc-vcvars-arch": self.msvc_vcvars_arch, "msvc-vcvars-sdk": self.msvc_vcvars_sdk, "msvc-project": self.msvc_project, @@ -437,6 +440,9 @@ def spec_to_job(spec: JobSpec, key: str, trackmem_symbol_names: bool, ctest_args job.setup_libusb_arch = "x86" case MsvcArch.X64: job.setup_libusb_arch = "x64" + job.microsoft_gameinput_version = WINDOWS_GAMEINPUT_VERSION + job.cflags.append("-I$GAMEINPUT_INCLUDE") + job.cxxflags.append("-I$GAMEINPUT_INCLUDE") case SdlPlatform.Linux: if spec.name.startswith("Ubuntu"): assert spec.os.value.startswith("ubuntu-") @@ -764,6 +770,9 @@ def spec_to_job(spec: JobSpec, key: str, trackmem_symbol_names: bool, ctest_args job.msys2_packages.append(f"{msys2_env}-clang-tools-extra") if job.ccache: job.msys2_packages.append(f"{msys2_env}-ccache") + job.microsoft_gameinput_version = WINDOWS_GAMEINPUT_VERSION + job.cflags.append("-I$GAMEINPUT_INCLUDE") + job.cxxflags.append("-I$GAMEINPUT_INCLUDE") case SdlPlatform.Riscos: job.ccache = False # FIXME: enable when container gets upgrade # FIXME: Enable SDL_WERROR diff --git a/.github/workflows/generic.yml b/.github/workflows/generic.yml index 2ac0e30743..8250e50e72 100644 --- a/.github/workflows/generic.yml +++ b/.github/workflows/generic.yml @@ -167,6 +167,14 @@ jobs: echo '#error "System SDL headers must not be used by build system"' >"$dest" done done + - name: 'Set up Microsoft.GameInput headers' + if: ${{ matrix.platform.microsoft-gameinput-version != '' }} + run: | + python build-scripts/download-gameinput-headers.py \ + --version ${{ matrix.platform.microsoft-gameinput-version }} \ + -o $HOME/gameinput + echo "GAMEINPUT_INCLUDE=$(cygpath -w "$HOME/gameinput")" >>$GITHUB_ENV + echo "INCLUDE=$INCLUDE;$(cygpath -w "$HOME/gameinput")" >>$GITHUB_ENV - name: 'Calculate ccache key' if: ${{ matrix.platform.ccache }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6929f152d3..b619a514b9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -345,7 +345,7 @@ jobs: id: releaser run: | python build-scripts/build-release.py ` - --actions msvc ` + --actions download msvc ` --commit ${{ inputs.commit }} ` --root "${{ steps.zip.outputs.path }}" ` --github ` @@ -507,7 +507,7 @@ jobs: id: releaser run: | python build-scripts/build-release.py \ - --actions mingw \ + --actions download mingw \ --commit ${{ inputs.commit }} \ --root "${{ steps.tar.outputs.path }}" \ --github \ @@ -619,7 +619,7 @@ jobs: id: releaser run: | python build-scripts/build-release.py \ - --actions android \ + --actions download android \ --android-api 21 \ --android-ndk-home "${{ steps.setup-ndk.outputs.ndk-path }}" \ --commit ${{ inputs.commit }} \ diff --git a/build-scripts/build-release.py b/build-scripts/build-release.py index d3725382ce..4e153a1bf9 100755 --- a/build-scripts/build-release.py +++ b/build-scripts/build-release.py @@ -175,12 +175,15 @@ class VisualStudio: assert msbuild_path.is_file(), "MSBuild.exe does not exist" return msbuild_path - def build(self, arch_platform: VsArchPlatformConfig, projects: list[Path]): + def build(self, arch_platform: VsArchPlatformConfig, projects: list[Path], include_paths: list[str]): assert projects, "Need at least one project to build" vsdev_cmd_str = f"\"{self.vsdevcmd}\" -arch={arch_platform.arch}" msbuild_cmd_str = " && ".join([f"\"{self.msbuild}\" \"{project}\" /m /p:BuildInParallel=true /p:Platform={arch_platform.platform} /p:Configuration={arch_platform.configuration}" for project in projects]) - bat_contents = f"{vsdev_cmd_str} && {msbuild_cmd_str}\n" + include_contents = "%INCLUDE%" + if include_paths: + include_contents = f"{';'.join(str(p) for p in include_paths)};" + include_contents + bat_contents = textwrap.dedent(f"{vsdev_cmd_str} && set INCLUDE={include_contents} && {msbuild_cmd_str}") bat_path = Path(tempfile.gettempdir()) / "cmd.bat" with bat_path.open("w") as f: f.write(bat_contents) @@ -740,6 +743,12 @@ class Releaser: member.name = "/".join(Path(member.name).parts[1:]) return member for dep in self.release_info.get("dependencies", {}): + if "command" in self.release_info["dependencies"][dep]: + for arch, triplet in ARCH_TO_TRIPLET.items(): + shutil.copytree(self.deps_path / dep, str(mingw_deps_path / triplet), dirs_exist_ok=True) + (mingw_deps_path / triplet / "bin").mkdir(exist_ok=True) + (mingw_deps_path / triplet / "lib/pkgconfig").mkdir(exist_ok=True, parents=True) + continue extract_path = mingw_deps_path / f"extract-{dep}" extract_path.mkdir() with chdir(extract_path): @@ -856,8 +865,8 @@ class Releaser: f"cmake", f"-S", str(self.root), "-B", str(build_path), f"-DCMAKE_BUILD_TYPE={build_type}", - f'''-DCMAKE_C_FLAGS="-ffile-prefix-map={self.root}=/src/{self.project}"''', - f'''-DCMAKE_CXX_FLAGS="-ffile-prefix-map={self.root}=/src/{self.project}"''', + f'''-DCMAKE_C_FLAGS="-ffile-prefix-map={self.root}=/src/{self.project}" "-I{str(mingw_deps_path / triplet)}/include"''', + f'''-DCMAKE_CXX_FLAGS="-ffile-prefix-map={self.root}=/src/{self.project}" "-I{str(mingw_deps_path / triplet)}/include"''', f"-DCMAKE_PREFIX_PATH={mingw_deps_path / triplet}", f"-DCMAKE_INSTALL_PREFIX={install_path}", f"-DCMAKE_INSTALL_INCLUDEDIR=include", @@ -1129,6 +1138,15 @@ class Releaser: f.write(f"dep-path={self.deps_path.absolute()}\n") for dep, depinfo in self.release_info.get("dependencies", {}).items(): + if "command" in depinfo: + (self.deps_path / dep).mkdir(exist_ok=True, parents=True) + command_args = configure_text_list(shlex.split(depinfo["command"]), context=self.get_context(extra_context={ + "DEPS_PATH": str(self.deps_path / dep), + })) + if command_args[0].endswith(".py"): + command_args.insert(0, sys.executable) + self.executer.run(command_args) + continue startswith = depinfo["startswith"] dep_repo = depinfo["repo"] dep_string_data = self.executer.check_output(["gh", "-R", dep_repo, "release", "list", "--exclude-drafts", "--exclude-pre-releases", "--json", "name,createdAt,tagName", "--jq", f'[.[]|select(.name|startswith("{startswith}"))]|max_by(.createdAt)']).strip() @@ -1143,6 +1161,10 @@ class Releaser: def verify_dependencies(self): for dep, depinfo in self.release_info.get("dependencies", {}).items(): + if "command" in depinfo: + command_matches = glob.glob(depinfo["artifact"], root_dir=self.deps_path) + assert len(command_matches) == 1, f"Exactly one archive matches command {dep} dependency: {command_matches}" + continue if "mingw" in self.release_info: mingw_matches = glob.glob(self.release_info["mingw"]["dependencies"][dep]["artifact"], root_dir=self.deps_path) assert len(mingw_matches) == 1, f"Exactly one archive matches mingw {dep} dependency: {mingw_matches}" @@ -1174,7 +1196,12 @@ class Releaser: deps_path = self.root / "msvc-deps" shutil.rmtree(deps_path, ignore_errors=True) dep_roots = [] - for dep, depinfo in self.release_info["msvc"].get("dependencies", {}).items(): + dep_includes = [] + for dep in self.release_info.get("dependencies"): + if "command" in self.release_info["dependencies"][dep]: + dep_includes.append(self.deps_path / dep / "include") + continue + depinfo = self.release_info["msvc"]["dependencies"][dep] dep_extract_path = deps_path / f"extract-{dep}" msvc_zip = self.deps_path / glob.glob(depinfo["artifact"], root_dir=self.deps_path)[0] with zipfile.ZipFile(msvc_zip, "r") as zf: @@ -1184,13 +1211,18 @@ class Releaser: dep_roots.append(contents_msvc_zip[0]) for arch in self.release_info["msvc"].get("cmake", {}).get("archs", []): - self._build_msvc_cmake(arch_platform=self._arch_to_vs_platform(arch=arch), dep_roots=dep_roots) + self._build_msvc_cmake(arch_platform=self._arch_to_vs_platform(arch=arch), dep_roots=dep_roots, dep_includes=dep_includes) with self.section_printer.group("Create SDL VC development zip"): self._build_msvc_devel() def _build_msvc_msbuild(self, arch_platform: VsArchPlatformConfig, vs: VisualStudio): platform_context = self.get_context(arch_platform.extra_context()) - for dep, depinfo in self.release_info["msvc"].get("dependencies", {}).items(): + include_paths = [] + for dep in self.release_info.get("dependencies", {}):#release_info["msvc"].get("dependencies", {}).items(): + if "command" in self.release_info["dependencies"][dep]: + include_paths.append(self.deps_path / dep / "include") + continue + depinfo = self.release_info["msvc"]["dependencies"][dep] msvc_zip = self.deps_path / glob.glob(depinfo["artifact"], root_dir=self.deps_path)[0] src_globs = [configure_text(instr["src"], context=platform_context) for instr in depinfo["copy"]] @@ -1238,7 +1270,7 @@ class Releaser: shutil.copy(src=src, dst=dir_b_props) with self.section_printer.group(f"Build {arch_platform.arch} VS binary"): - vs.build(arch_platform=arch_platform, projects=projects) + vs.build(arch_platform=arch_platform, projects=projects, include_paths=include_paths) if self.dry: for b in built_paths: @@ -1274,7 +1306,7 @@ class Releaser: def _arch_platform_to_install_path(self, arch_platform: VsArchPlatformConfig) -> Path: return self._arch_platform_to_build_path(arch_platform) / "prefix" - def _build_msvc_cmake(self, arch_platform: VsArchPlatformConfig, dep_roots: list[Path]): + def _build_msvc_cmake(self, arch_platform: VsArchPlatformConfig, dep_roots: list[Path], dep_includes: list[Path]): build_path = self._arch_platform_to_build_path(arch_platform) install_path = self._arch_platform_to_install_path(arch_platform) platform_context = self.get_context(extra_context=arch_platform.extra_context()) @@ -1290,7 +1322,7 @@ class Releaser: if not self.fast: for b in built_paths: b.unlink(missing_ok=True) - + cppflags = list(f"-I{inc}" for inc in dep_includes) shutil.rmtree(install_path, ignore_errors=True) build_path.mkdir(parents=True, exist_ok=True) with self.section_printer.group(f"Configure VC CMake project for {arch_platform.arch}"): @@ -1303,6 +1335,8 @@ class Releaser: "-DCMAKE_INSTALL_LIBDIR=lib", f"-DCMAKE_BUILD_TYPE={build_type}", f"-DCMAKE_INSTALL_PREFIX={install_path}", + f"-DCMAKE_C_FLAGS={shlex.join(cppflags)}", + f"-DCMAKE_CXX_FLAGS={shlex.join(cppflags)}", # MSVC debug information format flags are selected by an abstraction "-DCMAKE_POLICY_DEFAULT_CMP0141=NEW", # MSVC debug information format diff --git a/build-scripts/download-gameinput-headers.py b/build-scripts/download-gameinput-headers.py new file mode 100755 index 0000000000..13d50028c0 --- /dev/null +++ b/build-scripts/download-gameinput-headers.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 + +import argparse +import logging +from pathlib import Path +import urllib.request + +logger = logging.getLogger(__name__) + +def download_headers(tag: str, lowercase: bool, output: Path): + base_url = f"https://raw.githubusercontent.com/microsoftconnect/GameInput/refs/tags/{tag}/" + url_relpaths = ( + "include/GameInput.h", + "include/v0/GameInput.h", + "include/v1/GameInput.h", + "include/v2/GameInput.h", + ) + remove_prefix = "include/" + for url_relpath in url_relpaths: + url = base_url + url_relpath + local_relpath = url_relpath.removeprefix(remove_prefix) + if lowercase: + local_relpath = local_relpath.lower() + local_path = output / local_relpath + + local_dirpath = local_path.parent + local_dirpath.mkdir(parents=False, exist_ok=True) + + logger.info("Downloading %s to %s...", url, local_path) + urllib.request.urlretrieve(url, local_path) + logger.info("... done") + +def main(): + logging.basicConfig(level=logging.INFO) + + parser = argparse.ArgumentParser(description="Download Microsoft.GameInput headers", allow_abbrev=False) + parser.add_argument("--version", required=True, help="GameInput release tag (see https://github.com/microsoftconnect/GameInput/tags)") + parser.add_argument("--no-lowercase", action="store_false", dest="lowercase", help="Don't lowercase downloaded headers") + parser.add_argument("-o", "--output", type=Path, default=Path.cwd(), help="Headers will be stored here (subdirectories created as ") + args = parser.parse_args() + download_headers(tag=args.version, lowercase=args.lowercase, output=args.output) + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/build-scripts/release-info.json b/build-scripts/release-info.json index 4847751414..e70683bc3a 100644 --- a/build-scripts/release-info.json +++ b/build-scripts/release-info.json @@ -1,6 +1,12 @@ { "name": "SDL3", "remote": "libsdl-org/SDL", + "dependencies": { + "gameinput": { + "command": "build-scripts/download-gameinput-headers.py -o @<@DEPS_PATH@>@/include --version v3.3.195.0", + "artifact": "gameinput/include/gameinput.h" + } + }, "version": { "file": "include/SDL3/SDL_version.h", "re_major": "^#define SDL_MAJOR_VERSION\\s+([0-9]+)$", @@ -54,6 +60,9 @@ "build-scripts/pkg-support/mingw/cmake/SDL3Config.cmake", "build-scripts/pkg-support/mingw/cmake/SDL3ConfigVersion.cmake" ] + }, + "dependencies": { + "gameinput": {} } }, "msvc": { @@ -129,6 +138,9 @@ "include/SDL3": [ "include/SDL3/*.h" ] + }, + "dependencies": { + "gameinput": {} } }, "android": { diff --git a/build-scripts/setup-gdk-desktop.py b/build-scripts/setup-gdk-desktop.py index d2309a0e31..8dc259d11b 100755 --- a/build-scripts/setup-gdk-desktop.py +++ b/build-scripts/setup-gdk-desktop.py @@ -262,8 +262,6 @@ def main(): parser.add_argument("--no-user-props", required=False, dest="user_props", action="store_false", help="Don't ") args = parser.parse_args() - logging.basicConfig(level=logging.INFO) - git_ref = None gdk_edition = None if args.ref_edition is not None: