diff options
Diffstat (limited to 'src/contrib/SDL-3.2.20/build-scripts/setup-gdk-desktop.py')
-rwxr-xr-x | src/contrib/SDL-3.2.20/build-scripts/setup-gdk-desktop.py | 303 |
1 files changed, 303 insertions, 0 deletions
diff --git a/src/contrib/SDL-3.2.20/build-scripts/setup-gdk-desktop.py b/src/contrib/SDL-3.2.20/build-scripts/setup-gdk-desktop.py new file mode 100755 index 0000000..d2309a0 --- /dev/null +++ b/src/contrib/SDL-3.2.20/build-scripts/setup-gdk-desktop.py | |||
@@ -0,0 +1,303 @@ | |||
1 | #!/usr/bin/env python | ||
2 | |||
3 | import argparse | ||
4 | import functools | ||
5 | import logging | ||
6 | import os | ||
7 | from pathlib import Path | ||
8 | import re | ||
9 | import shutil | ||
10 | import subprocess | ||
11 | import tempfile | ||
12 | import textwrap | ||
13 | import urllib.request | ||
14 | import zipfile | ||
15 | |||
16 | # Update both variables when updating the GDK | ||
17 | GIT_REF = "June_2024_Update_1" | ||
18 | GDK_EDITION = "240601" # YYMMUU | ||
19 | |||
20 | logger = logging.getLogger(__name__) | ||
21 | |||
22 | class GdDesktopConfigurator: | ||
23 | def __init__(self, gdk_path, arch, vs_folder, vs_version=None, vs_toolset=None, temp_folder=None, git_ref=None, gdk_edition=None): | ||
24 | self.git_ref = git_ref or GIT_REF | ||
25 | self.gdk_edition = gdk_edition or GDK_EDITION | ||
26 | self.gdk_path = gdk_path | ||
27 | self.temp_folder = temp_folder or Path(tempfile.gettempdir()) | ||
28 | self.dl_archive_path = Path(self.temp_folder) / f"{ self.git_ref }.zip" | ||
29 | self.gdk_extract_path = Path(self.temp_folder) / f"GDK-{ self.git_ref }" | ||
30 | self.arch = arch | ||
31 | self.vs_folder = vs_folder | ||
32 | self._vs_version = vs_version | ||
33 | self._vs_toolset = vs_toolset | ||
34 | |||
35 | def download_archive(self) -> None: | ||
36 | gdk_url = f"https://github.com/microsoft/GDK/archive/refs/tags/{ GIT_REF }.zip" | ||
37 | logger.info("Downloading %s to %s", gdk_url, self.dl_archive_path) | ||
38 | urllib.request.urlretrieve(gdk_url, self.dl_archive_path) | ||
39 | assert self.dl_archive_path.is_file() | ||
40 | |||
41 | def extract_zip_archive(self) -> None: | ||
42 | extract_path = self.gdk_extract_path.parent | ||
43 | assert self.dl_archive_path.is_file() | ||
44 | logger.info("Extracting %s to %s", self.dl_archive_path, extract_path) | ||
45 | with zipfile.ZipFile(self.dl_archive_path) as zf: | ||
46 | zf.extractall(extract_path) | ||
47 | assert self.gdk_extract_path.is_dir(), f"{self.gdk_extract_path} must exist" | ||
48 | |||
49 | def extract_development_kit(self) -> None: | ||
50 | extract_dks_cmd = self.gdk_extract_path / "SetupScripts/ExtractXboxOneDKs.cmd" | ||
51 | assert extract_dks_cmd.is_file() | ||
52 | logger.info("Extracting GDK Development Kit: running %s", extract_dks_cmd) | ||
53 | cmd = ["cmd.exe", "/C", str(extract_dks_cmd), str(self.gdk_extract_path), str(self.gdk_path)] | ||
54 | logger.debug("Running %r", cmd) | ||
55 | subprocess.check_call(cmd) | ||
56 | |||
57 | def detect_vs_version(self) -> str: | ||
58 | vs_regex = re.compile("VS([0-9]{4})") | ||
59 | supported_vs_versions = [] | ||
60 | for p in self.gaming_grdk_build_path.iterdir(): | ||
61 | if not p.is_dir(): | ||
62 | continue | ||
63 | if m := vs_regex.match(p.name): | ||
64 | supported_vs_versions.append(m.group(1)) | ||
65 | logger.info(f"Supported Visual Studio versions: {supported_vs_versions}") | ||
66 | vs_versions = set(self.vs_folder.parts).intersection(set(supported_vs_versions)) | ||
67 | if not vs_versions: | ||
68 | raise RuntimeError("Visual Studio version is incompatible") | ||
69 | if len(vs_versions) > 1: | ||
70 | raise RuntimeError(f"Too many compatible VS versions found ({vs_versions})") | ||
71 | vs_version = vs_versions.pop() | ||
72 | logger.info(f"Used Visual Studio version: {vs_version}") | ||
73 | return vs_version | ||
74 | |||
75 | def detect_vs_toolset(self) -> str: | ||
76 | toolset_paths = [] | ||
77 | for ts_path in self.gdk_toolset_parent_path.iterdir(): | ||
78 | if not ts_path.is_dir(): | ||
79 | continue | ||
80 | ms_props = ts_path / "Microsoft.Cpp.props" | ||
81 | if not ms_props.is_file(): | ||
82 | continue | ||
83 | toolset_paths.append(ts_path.name) | ||
84 | logger.info("Detected Visual Studio toolsets: %s", toolset_paths) | ||
85 | assert toolset_paths, "Have we detected at least one toolset?" | ||
86 | |||
87 | def toolset_number(toolset: str) -> int: | ||
88 | if m:= re.match("[^0-9]*([0-9]+).*", toolset): | ||
89 | return int(m.group(1)) | ||
90 | return -9 | ||
91 | |||
92 | return max(toolset_paths, key=toolset_number) | ||
93 | |||
94 | @property | ||
95 | def vs_version(self) -> str: | ||
96 | if self._vs_version is None: | ||
97 | self._vs_version = self.detect_vs_version() | ||
98 | return self._vs_version | ||
99 | |||
100 | @property | ||
101 | def vs_toolset(self) -> str: | ||
102 | if self._vs_toolset is None: | ||
103 | self._vs_toolset = self.detect_vs_toolset() | ||
104 | return self._vs_toolset | ||
105 | |||
106 | @staticmethod | ||
107 | def copy_files_and_merge_into(srcdir: Path, dstdir: Path) -> None: | ||
108 | logger.info(f"Copy {srcdir} to {dstdir}") | ||
109 | for root, _, files in os.walk(srcdir): | ||
110 | dest_root = dstdir / Path(root).relative_to(srcdir) | ||
111 | if not dest_root.is_dir(): | ||
112 | dest_root.mkdir() | ||
113 | for file in files: | ||
114 | srcfile = Path(root) / file | ||
115 | dstfile = dest_root / file | ||
116 | shutil.copy(srcfile, dstfile) | ||
117 | |||
118 | def copy_msbuild(self) -> None: | ||
119 | vc_toolset_parent_path = self.vs_folder / "MSBuild/Microsoft/VC" | ||
120 | if 1: | ||
121 | logger.info(f"Detected compatible Visual Studio version: {self.vs_version}") | ||
122 | srcdir = vc_toolset_parent_path | ||
123 | dstdir = self.gdk_toolset_parent_path | ||
124 | assert srcdir.is_dir(), "Source directory must exist" | ||
125 | assert dstdir.is_dir(), "Destination directory must exist" | ||
126 | |||
127 | self.copy_files_and_merge_into(srcdir=srcdir, dstdir=dstdir) | ||
128 | |||
129 | @property | ||
130 | def game_dk_path(self) -> Path: | ||
131 | return self.gdk_path / "Microsoft GDK" | ||
132 | |||
133 | @property | ||
134 | def game_dk_latest_path(self) -> Path: | ||
135 | return self.game_dk_path / self.gdk_edition | ||
136 | |||
137 | @property | ||
138 | def windows_sdk_path(self) -> Path: | ||
139 | return self.gdk_path / "Windows Kits/10" | ||
140 | |||
141 | @property | ||
142 | def gaming_grdk_build_path(self) -> Path: | ||
143 | return self.game_dk_latest_path / "GRDK" | ||
144 | |||
145 | @property | ||
146 | def gdk_toolset_parent_path(self) -> Path: | ||
147 | return self.gaming_grdk_build_path / f"VS{self.vs_version}/flatDeployment/MSBuild/Microsoft/VC" | ||
148 | |||
149 | @property | ||
150 | def env(self) -> dict[str, str]: | ||
151 | game_dk = self.game_dk_path | ||
152 | game_dk_latest = self.game_dk_latest_path | ||
153 | windows_sdk_dir = self.windows_sdk_path | ||
154 | gaming_grdk_build = self.gaming_grdk_build_path | ||
155 | |||
156 | return { | ||
157 | "GRDKEDITION": f"{self.gdk_edition}", | ||
158 | "GameDK": f"{game_dk}\\", | ||
159 | "GameDKLatest": f"{ game_dk_latest }\\", | ||
160 | "WindowsSdkDir": f"{ windows_sdk_dir }\\", | ||
161 | "GamingGRDKBuild": f"{ gaming_grdk_build }\\", | ||
162 | "VSInstallDir": f"{ self.vs_folder }\\", | ||
163 | } | ||
164 | |||
165 | def create_user_props(self, path: Path) -> None: | ||
166 | vc_targets_path = self.gaming_grdk_build_path / f"VS{ self.vs_version }/flatDeployment/MSBuild/Microsoft/VC/{ self.vs_toolset }" | ||
167 | vc_targets_path16 = self.gaming_grdk_build_path / f"VS2019/flatDeployment/MSBuild/Microsoft/VC/{ self.vs_toolset }" | ||
168 | vc_targets_path17 = self.gaming_grdk_build_path / f"VS2022/flatDeployment/MSBuild/Microsoft/VC/{ self.vs_toolset }" | ||
169 | additional_include_directories = ";".join(str(p) for p in self.gdk_include_paths) | ||
170 | additional_library_directories = ";".join(str(p) for p in self.gdk_library_paths) | ||
171 | durango_xdk_install_path = self.gdk_path / "Microsoft GDK" | ||
172 | with path.open("w") as f: | ||
173 | f.write(textwrap.dedent(f"""\ | ||
174 | <?xml version="1.0" encoding="utf-8"?> | ||
175 | <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | ||
176 | <PropertyGroup> | ||
177 | <VCTargetsPath>{ vc_targets_path }\\</VCTargetsPath> | ||
178 | <VCTargetsPath16>{ vc_targets_path16 }\\</VCTargetsPath16> | ||
179 | <VCTargetsPath17>{ vc_targets_path17 }\\</VCTargetsPath17> | ||
180 | <BWOI_GDK_Path>{ self.gaming_grdk_build_path }\\</BWOI_GDK_Path> | ||
181 | <Platform Condition="'$(Platform)' == ''">Gaming.Desktop.x64</Platform> | ||
182 | <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> | ||
183 | <XdkEditionTarget>{ self.gdk_edition }</XdkEditionTarget> | ||
184 | <DurangoXdkInstallPath>{ durango_xdk_install_path }</DurangoXdkInstallPath> | ||
185 | |||
186 | <DefaultXdkEditionRootVS2019>$(DurangoXdkInstallPath)\\{self.gdk_edition}\\GRDK\\VS2019\\flatDeployment\\MSBuild\\Microsoft\\VC\\{self.vs_toolset}\\Platforms\\$(Platform)\\</DefaultXdkEditionRootVS2019> | ||
187 | <XdkEditionRootVS2019>$(DurangoXdkInstallPath)\\{self.gdk_edition}\\GRDK\\VS2019\\flatDeployment\\MSBuild\\Microsoft\\VC\\{self.vs_toolset}\\Platforms\\$(Platform)\\</XdkEditionRootVS2019> | ||
188 | <DefaultXdkEditionRootVS2022>$(DurangoXdkInstallPath)\\{self.gdk_edition}\\GRDK\\VS2022\\flatDeployment\\MSBuild\\Microsoft\\VC\\{self.vs_toolset}\\Platforms\\$(Platform)\\</DefaultXdkEditionRootVS2022> | ||
189 | <XdkEditionRootVS2022>$(DurangoXdkInstallPath)\\{self.gdk_edition}\\GRDK\\VS2022\\flatDeployment\\MSBuild\\Microsoft\\VC\\{self.vs_toolset}\\Platforms\\$(Platform)\\</XdkEditionRootVS2022> | ||
190 | |||
191 | <Deterministic>true</Deterministic> | ||
192 | <DisableInstalledVCTargetsUse>true</DisableInstalledVCTargetsUse> | ||
193 | <ClearDevCommandPromptEnvVars>true</ClearDevCommandPromptEnvVars> | ||
194 | </PropertyGroup> | ||
195 | <ItemDefinitionGroup Condition="'$(Platform)' == 'Gaming.Desktop.x64'"> | ||
196 | <ClCompile> | ||
197 | <AdditionalIncludeDirectories>{ additional_include_directories };%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> | ||
198 | </ClCompile> | ||
199 | <Link> | ||
200 | <AdditionalLibraryDirectories>{ additional_library_directories };%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> | ||
201 | </Link> | ||
202 | </ItemDefinitionGroup> | ||
203 | </Project> | ||
204 | """)) | ||
205 | |||
206 | @property | ||
207 | def gdk_include_paths(self) -> list[Path]: | ||
208 | return [ | ||
209 | self.gaming_grdk_build_path / "gamekit/include", | ||
210 | ] | ||
211 | |||
212 | @property | ||
213 | def gdk_library_paths(self) -> list[Path]: | ||
214 | return [ | ||
215 | self.gaming_grdk_build_path / f"gamekit/lib/{self.arch}", | ||
216 | ] | ||
217 | |||
218 | @property | ||
219 | def gdk_binary_path(self) -> list[Path]: | ||
220 | return [ | ||
221 | self.gaming_grdk_build_path / "bin", | ||
222 | self.game_dk_path / "bin", | ||
223 | ] | ||
224 | |||
225 | @property | ||
226 | def build_env(self) -> dict[str, str]: | ||
227 | gdk_include = ";".join(str(p) for p in self.gdk_include_paths) | ||
228 | gdk_lib = ";".join(str(p) for p in self.gdk_library_paths) | ||
229 | gdk_path = ";".join(str(p) for p in self.gdk_binary_path) | ||
230 | return { | ||
231 | "GDK_INCLUDE": gdk_include, | ||
232 | "GDK_LIB": gdk_lib, | ||
233 | "GDK_PATH": gdk_path, | ||
234 | } | ||
235 | |||
236 | def print_env(self) -> None: | ||
237 | for k, v in self.env.items(): | ||
238 | print(f"set \"{k}={v}\"") | ||
239 | print() | ||
240 | for k, v in self.build_env.items(): | ||
241 | print(f"set \"{k}={v}\"") | ||
242 | print() | ||
243 | print(f"set \"PATH=%GDK_PATH%;%PATH%\"") | ||
244 | print(f"set \"LIB=%GDK_LIB%;%LIB%\"") | ||
245 | print(f"set \"INCLUDE=%GDK_INCLUDE%;%INCLUDE%\"") | ||
246 | |||
247 | |||
248 | def main(): | ||
249 | logging.basicConfig(level=logging.INFO) | ||
250 | parser = argparse.ArgumentParser(allow_abbrev=False) | ||
251 | parser.add_argument("--arch", choices=["amd64"], default="amd64", help="Architecture") | ||
252 | parser.add_argument("--download", action="store_true", help="Download GDK") | ||
253 | parser.add_argument("--extract", action="store_true", help="Extract downloaded GDK") | ||
254 | parser.add_argument("--copy-msbuild", action="store_true", help="Copy MSBuild files") | ||
255 | parser.add_argument("--temp-folder", help="Temporary folder where to download and extract GDK") | ||
256 | parser.add_argument("--gdk-path", required=True, type=Path, help="Folder where to store the GDK") | ||
257 | parser.add_argument("--ref-edition", type=str, help="Git ref and GDK edition separated by comma") | ||
258 | parser.add_argument("--vs-folder", required=True, type=Path, help="Installation folder of Visual Studio") | ||
259 | parser.add_argument("--vs-version", required=False, type=int, help="Visual Studio version") | ||
260 | parser.add_argument("--vs-toolset", required=False, help="Visual Studio toolset (e.g. v150)") | ||
261 | parser.add_argument("--props-folder", required=False, type=Path, default=Path(), help="Visual Studio toolset (e.g. v150)") | ||
262 | parser.add_argument("--no-user-props", required=False, dest="user_props", action="store_false", help="Don't ") | ||
263 | args = parser.parse_args() | ||
264 | |||
265 | logging.basicConfig(level=logging.INFO) | ||
266 | |||
267 | git_ref = None | ||
268 | gdk_edition = None | ||
269 | if args.ref_edition is not None: | ||
270 | git_ref, gdk_edition = args.ref_edition.split(",", 1) | ||
271 | try: | ||
272 | int(gdk_edition) | ||
273 | except ValueError: | ||
274 | parser.error("Edition should be an integer (YYMMUU) (Y=year M=month U=update)") | ||
275 | |||
276 | configurator = GdDesktopConfigurator( | ||
277 | arch=args.arch, | ||
278 | git_ref=git_ref, | ||
279 | gdk_edition=gdk_edition, | ||
280 | vs_folder=args.vs_folder, | ||
281 | vs_version=args.vs_version, | ||
282 | vs_toolset=args.vs_toolset, | ||
283 | gdk_path=args.gdk_path, | ||
284 | temp_folder=args.temp_folder, | ||
285 | ) | ||
286 | |||
287 | if args.download: | ||
288 | configurator.download_archive() | ||
289 | |||
290 | if args.extract: | ||
291 | configurator.extract_zip_archive() | ||
292 | |||
293 | configurator.extract_development_kit() | ||
294 | |||
295 | if args.copy_msbuild: | ||
296 | configurator.copy_msbuild() | ||
297 | |||
298 | if args.user_props: | ||
299 | configurator.print_env() | ||
300 | configurator.create_user_props(args.props_folder / "Directory.Build.props") | ||
301 | |||
302 | if __name__ == "__main__": | ||
303 | raise SystemExit(main()) | ||