xref: /aosp_15_r20/external/angle/scripts/winappsdk_setup.py (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1*8975f5c5SAndroid Build Coastguard Worker#!/usr/bin/python3
2*8975f5c5SAndroid Build Coastguard Worker#
3*8975f5c5SAndroid Build Coastguard Worker# Copyright 2024 The ANGLE Project Authors. All rights reserved.
4*8975f5c5SAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license that can be
5*8975f5c5SAndroid Build Coastguard Worker# found in the LICENSE file.
6*8975f5c5SAndroid Build Coastguard Worker#
7*8975f5c5SAndroid Build Coastguard Worker# winappsdk_setup.py:
8*8975f5c5SAndroid Build Coastguard Worker#   Downloads and processes a specific version of Windows App SDK
9*8975f5c5SAndroid Build Coastguard Worker#   for use when building ANGLE for WinUI 3 apps.
10*8975f5c5SAndroid Build Coastguard Worker#
11*8975f5c5SAndroid Build Coastguard Worker# Usage:
12*8975f5c5SAndroid Build Coastguard Worker#   python3 winappsdk_setup.py [--version <package-version>] [--output /path/to/dest] [--force]
13*8975f5c5SAndroid Build Coastguard Worker
14*8975f5c5SAndroid Build Coastguard Workerimport argparse
15*8975f5c5SAndroid Build Coastguard Workerimport fnmatch
16*8975f5c5SAndroid Build Coastguard Workerimport io
17*8975f5c5SAndroid Build Coastguard Workerimport os
18*8975f5c5SAndroid Build Coastguard Workerimport subprocess
19*8975f5c5SAndroid Build Coastguard Workerimport urllib.request
20*8975f5c5SAndroid Build Coastguard Workerimport zipfile
21*8975f5c5SAndroid Build Coastguard Worker
22*8975f5c5SAndroid Build Coastguard Workerpackage_id = "Microsoft.WindowsAppSDK"
23*8975f5c5SAndroid Build Coastguard Workeruap_version = "10.0.18362"
24*8975f5c5SAndroid Build Coastguard Worker
25*8975f5c5SAndroid Build Coastguard Workerdefault_version = "1.3.230724000"
26*8975f5c5SAndroid Build Coastguard Workerdefault_output = "third_party\WindowsAppSDK"
27*8975f5c5SAndroid Build Coastguard Worker
28*8975f5c5SAndroid Build Coastguard Worker
29*8975f5c5SAndroid Build Coastguard Workerdef download_and_extract_nuget_package(force, version, output_path):
30*8975f5c5SAndroid Build Coastguard Worker    # first check if the last download was successful
31*8975f5c5SAndroid Build Coastguard Worker    stamp = os.path.join(output_path, f"{version}.stamp")
32*8975f5c5SAndroid Build Coastguard Worker    if os.path.exists(stamp) and not force:
33*8975f5c5SAndroid Build Coastguard Worker        return
34*8975f5c5SAndroid Build Coastguard Worker
35*8975f5c5SAndroid Build Coastguard Worker    # download the package
36*8975f5c5SAndroid Build Coastguard Worker    nuget_url = f"https://www.nuget.org/api/v2/package/{package_id}/{version}"
37*8975f5c5SAndroid Build Coastguard Worker    with urllib.request.urlopen(nuget_url) as response:
38*8975f5c5SAndroid Build Coastguard Worker        if response.status == 200:
39*8975f5c5SAndroid Build Coastguard Worker            package_bytes = io.BytesIO(response.read())
40*8975f5c5SAndroid Build Coastguard Worker            # extract
41*8975f5c5SAndroid Build Coastguard Worker            os.makedirs(output_path, exist_ok=True)
42*8975f5c5SAndroid Build Coastguard Worker            with zipfile.ZipFile(package_bytes, 'r') as zip_ref:
43*8975f5c5SAndroid Build Coastguard Worker                zip_ref.extractall(output_path)
44*8975f5c5SAndroid Build Coastguard Worker            # make a stamp to avoid re-downloading
45*8975f5c5SAndroid Build Coastguard Worker            with open(stamp, 'w') as file:
46*8975f5c5SAndroid Build Coastguard Worker                pass
47*8975f5c5SAndroid Build Coastguard Worker        else:
48*8975f5c5SAndroid Build Coastguard Worker            print(
49*8975f5c5SAndroid Build Coastguard Worker                f"Failed to download the {package_id} NuGet package. Status code: {response.status}"
50*8975f5c5SAndroid Build Coastguard Worker            )
51*8975f5c5SAndroid Build Coastguard Worker            sys.exit(1)
52*8975f5c5SAndroid Build Coastguard Worker
53*8975f5c5SAndroid Build Coastguard Worker
54*8975f5c5SAndroid Build Coastguard Workerdef get_win_sdk():
55*8975f5c5SAndroid Build Coastguard Worker    p = os.getenv("ProgramFiles(x86)", "C:\\Program Files (x86)")
56*8975f5c5SAndroid Build Coastguard Worker    win_sdk = os.getenv("WindowsSdkDir")
57*8975f5c5SAndroid Build Coastguard Worker    if not win_sdk:
58*8975f5c5SAndroid Build Coastguard Worker        win_sdk = os.path.join(p, "Windows Kits", "10")
59*8975f5c5SAndroid Build Coastguard Worker        print("%WindowsSdkDir% not set. Defaulting to", win_sdk)
60*8975f5c5SAndroid Build Coastguard Worker        print("You might want to run this from a Visual Studio cmd prompt.")
61*8975f5c5SAndroid Build Coastguard Worker    return win_sdk
62*8975f5c5SAndroid Build Coastguard Worker
63*8975f5c5SAndroid Build Coastguard Worker
64*8975f5c5SAndroid Build Coastguard Workerdef get_latest_sdk_version(win_sdk):
65*8975f5c5SAndroid Build Coastguard Worker    files = os.listdir(os.path.join(win_sdk, "bin"))
66*8975f5c5SAndroid Build Coastguard Worker    filtered_files = fnmatch.filter(files, "10.0.*")
67*8975f5c5SAndroid Build Coastguard Worker    sorted_files = sorted(filtered_files, reverse=True)
68*8975f5c5SAndroid Build Coastguard Worker    return sorted_files[0]
69*8975f5c5SAndroid Build Coastguard Worker
70*8975f5c5SAndroid Build Coastguard Worker
71*8975f5c5SAndroid Build Coastguard Workerdef run_winmdidl(force, win_sdk_bin, output_path, winmd, stamp_filename=None):
72*8975f5c5SAndroid Build Coastguard Worker    include = os.path.join(output_path, "include")
73*8975f5c5SAndroid Build Coastguard Worker    lib = os.path.join(output_path, "lib")
74*8975f5c5SAndroid Build Coastguard Worker
75*8975f5c5SAndroid Build Coastguard Worker    if stamp_filename:
76*8975f5c5SAndroid Build Coastguard Worker        stamp = os.path.join(include, stamp_filename)
77*8975f5c5SAndroid Build Coastguard Worker    else:
78*8975f5c5SAndroid Build Coastguard Worker        no_ext = os.path.splitext(os.path.basename(winmd))[0]
79*8975f5c5SAndroid Build Coastguard Worker        stamp = os.path.join(include, no_ext + ".idl")
80*8975f5c5SAndroid Build Coastguard Worker    if os.path.exists(stamp) and not force:
81*8975f5c5SAndroid Build Coastguard Worker        return
82*8975f5c5SAndroid Build Coastguard Worker
83*8975f5c5SAndroid Build Coastguard Worker    winmdidl = os.path.join(win_sdk_bin, "winmdidl.exe")
84*8975f5c5SAndroid Build Coastguard Worker    command = [
85*8975f5c5SAndroid Build Coastguard Worker        winmdidl,
86*8975f5c5SAndroid Build Coastguard Worker        os.path.join(lib, winmd), "/metadata_dir:C:\\Windows\\System32\\WinMetadata",
87*8975f5c5SAndroid Build Coastguard Worker        "/metadata_dir:" + os.path.join(lib, f"uap{uap_version}"),
88*8975f5c5SAndroid Build Coastguard Worker        "/metadata_dir:" + os.path.join(lib, "uap10.0"), "/outdir:" + include, "/nologo"
89*8975f5c5SAndroid Build Coastguard Worker    ]
90*8975f5c5SAndroid Build Coastguard Worker    subprocess.run(command, check=True, cwd=include)
91*8975f5c5SAndroid Build Coastguard Worker
92*8975f5c5SAndroid Build Coastguard Worker
93*8975f5c5SAndroid Build Coastguard Workerdef run_midlrt(force, win_sdk_bin, output_path, idl):
94*8975f5c5SAndroid Build Coastguard Worker    include = os.path.join(output_path, "include")
95*8975f5c5SAndroid Build Coastguard Worker    lib = os.path.join(output_path, "lib")
96*8975f5c5SAndroid Build Coastguard Worker
97*8975f5c5SAndroid Build Coastguard Worker    no_ext = os.path.splitext(os.path.basename(idl))[0]
98*8975f5c5SAndroid Build Coastguard Worker    stamp = os.path.join(include, no_ext + ".h")
99*8975f5c5SAndroid Build Coastguard Worker    if os.path.exists(stamp) and not force:
100*8975f5c5SAndroid Build Coastguard Worker        return
101*8975f5c5SAndroid Build Coastguard Worker
102*8975f5c5SAndroid Build Coastguard Worker    midlrt = os.path.join(win_sdk_bin, "midlrt.exe")
103*8975f5c5SAndroid Build Coastguard Worker    command = [
104*8975f5c5SAndroid Build Coastguard Worker        midlrt,
105*8975f5c5SAndroid Build Coastguard Worker        os.path.join(include, idl), "/metadata_dir", "C:\\Windows\\System32\\WinMetadata",
106*8975f5c5SAndroid Build Coastguard Worker        "/ns_prefix", "/nomidl", "/nologo"
107*8975f5c5SAndroid Build Coastguard Worker    ]
108*8975f5c5SAndroid Build Coastguard Worker    subprocess.run(command, check=True, cwd=include)
109*8975f5c5SAndroid Build Coastguard Worker
110*8975f5c5SAndroid Build Coastguard Worker
111*8975f5c5SAndroid Build Coastguard Workerif __name__ == "__main__":
112*8975f5c5SAndroid Build Coastguard Worker
113*8975f5c5SAndroid Build Coastguard Worker    parser = argparse.ArgumentParser(description="Setup the Windows App SDK.")
114*8975f5c5SAndroid Build Coastguard Worker    parser.add_argument(
115*8975f5c5SAndroid Build Coastguard Worker        "--version", default=default_version, help="the package version to download")
116*8975f5c5SAndroid Build Coastguard Worker    parser.add_argument(
117*8975f5c5SAndroid Build Coastguard Worker        "--output", default=default_output, help="the destination path for extracted contents")
118*8975f5c5SAndroid Build Coastguard Worker    parser.add_argument(
119*8975f5c5SAndroid Build Coastguard Worker        "--force", action="store_true", help="ignore existing files and re-download")
120*8975f5c5SAndroid Build Coastguard Worker    args = parser.parse_args()
121*8975f5c5SAndroid Build Coastguard Worker
122*8975f5c5SAndroid Build Coastguard Worker    if os.path.isabs(args.output):
123*8975f5c5SAndroid Build Coastguard Worker        output_path = args.output
124*8975f5c5SAndroid Build Coastguard Worker    else:
125*8975f5c5SAndroid Build Coastguard Worker        output_path = os.path.join(os.getcwd(), args.output)
126*8975f5c5SAndroid Build Coastguard Worker
127*8975f5c5SAndroid Build Coastguard Worker    win_sdk = get_win_sdk()
128*8975f5c5SAndroid Build Coastguard Worker    latest_sdk = get_latest_sdk_version(win_sdk)
129*8975f5c5SAndroid Build Coastguard Worker    arch = "x64"
130*8975f5c5SAndroid Build Coastguard Worker    win_sdk_bin = os.path.join(win_sdk, "bin", latest_sdk, arch)
131*8975f5c5SAndroid Build Coastguard Worker
132*8975f5c5SAndroid Build Coastguard Worker    winmd_files = {
133*8975f5c5SAndroid Build Coastguard Worker        f"uap{uap_version}\\Microsoft.Foundation.winmd": None,
134*8975f5c5SAndroid Build Coastguard Worker        f"uap{uap_version}\\Microsoft.Graphics.winmd": "Microsoft.Graphics.DirectX.idl",
135*8975f5c5SAndroid Build Coastguard Worker        f"uap{uap_version}\\Microsoft.UI.winmd": None,
136*8975f5c5SAndroid Build Coastguard Worker        "uap10.0\\Microsoft.UI.Text.winmd": None,
137*8975f5c5SAndroid Build Coastguard Worker        "uap10.0\\Microsoft.UI.Xaml.winmd": None,
138*8975f5c5SAndroid Build Coastguard Worker        "uap10.0\\Microsoft.Web.WebView2.Core.winmd": None,
139*8975f5c5SAndroid Build Coastguard Worker    }
140*8975f5c5SAndroid Build Coastguard Worker
141*8975f5c5SAndroid Build Coastguard Worker    idl_files = [
142*8975f5c5SAndroid Build Coastguard Worker        "Microsoft.Foundation.idl",
143*8975f5c5SAndroid Build Coastguard Worker        "Microsoft.Graphics.DirectX.idl",
144*8975f5c5SAndroid Build Coastguard Worker        "Microsoft.UI.Composition.idl",
145*8975f5c5SAndroid Build Coastguard Worker        "Microsoft.UI.Composition.SystemBackdrops.idl",
146*8975f5c5SAndroid Build Coastguard Worker        "Microsoft.UI.Dispatching.idl",
147*8975f5c5SAndroid Build Coastguard Worker        "Microsoft.UI.idl",
148*8975f5c5SAndroid Build Coastguard Worker        "Microsoft.UI.Input.idl",
149*8975f5c5SAndroid Build Coastguard Worker        "Microsoft.UI.Text.idl",
150*8975f5c5SAndroid Build Coastguard Worker        "Microsoft.UI.Windowing.idl",
151*8975f5c5SAndroid Build Coastguard Worker        "Microsoft.UI.Xaml.Automation.idl",
152*8975f5c5SAndroid Build Coastguard Worker        "Microsoft.UI.Xaml.Automation.Peers.idl",
153*8975f5c5SAndroid Build Coastguard Worker        "Microsoft.UI.Xaml.Automation.Provider.idl",
154*8975f5c5SAndroid Build Coastguard Worker        "Microsoft.UI.Xaml.Automation.Text.idl",
155*8975f5c5SAndroid Build Coastguard Worker        "Microsoft.UI.Xaml.Controls.idl",
156*8975f5c5SAndroid Build Coastguard Worker        "Microsoft.UI.Xaml.Controls.Primitives.idl",
157*8975f5c5SAndroid Build Coastguard Worker        "Microsoft.UI.Xaml.Data.idl",
158*8975f5c5SAndroid Build Coastguard Worker        "Microsoft.UI.Xaml.Documents.idl",
159*8975f5c5SAndroid Build Coastguard Worker        "Microsoft.UI.Xaml.idl",
160*8975f5c5SAndroid Build Coastguard Worker        "Microsoft.UI.Xaml.Input.idl",
161*8975f5c5SAndroid Build Coastguard Worker        "Microsoft.UI.Xaml.Interop.idl",
162*8975f5c5SAndroid Build Coastguard Worker        "Microsoft.UI.Xaml.Media.Animation.idl",
163*8975f5c5SAndroid Build Coastguard Worker        "Microsoft.UI.Xaml.Media.idl",
164*8975f5c5SAndroid Build Coastguard Worker        "Microsoft.UI.Xaml.Media.Imaging.idl",
165*8975f5c5SAndroid Build Coastguard Worker        "Microsoft.UI.Xaml.Media.Media3D.idl",
166*8975f5c5SAndroid Build Coastguard Worker        "Microsoft.UI.Xaml.Navigation.idl",
167*8975f5c5SAndroid Build Coastguard Worker        "Microsoft.Web.WebView2.Core.idl",
168*8975f5c5SAndroid Build Coastguard Worker    ]
169*8975f5c5SAndroid Build Coastguard Worker
170*8975f5c5SAndroid Build Coastguard Worker    progress = 1
171*8975f5c5SAndroid Build Coastguard Worker    total = len(winmd_files) + len(idl_files) + 1
172*8975f5c5SAndroid Build Coastguard Worker
173*8975f5c5SAndroid Build Coastguard Worker    # Download the NuGet package that contains all the files we need
174*8975f5c5SAndroid Build Coastguard Worker    print(
175*8975f5c5SAndroid Build Coastguard Worker        f"[{progress}/{total}] Downloading {package_id} NuGet package version {args.version} to {output_path}..."
176*8975f5c5SAndroid Build Coastguard Worker    )
177*8975f5c5SAndroid Build Coastguard Worker    download_and_extract_nuget_package(args.force, args.version, output_path)
178*8975f5c5SAndroid Build Coastguard Worker
179*8975f5c5SAndroid Build Coastguard Worker    # Generate .idl files from the .winmd files
180*8975f5c5SAndroid Build Coastguard Worker    for winmd, header in winmd_files.items():
181*8975f5c5SAndroid Build Coastguard Worker        progress += 1
182*8975f5c5SAndroid Build Coastguard Worker        print(f"[{progress}/{total}] Processing WINMD {winmd}...")
183*8975f5c5SAndroid Build Coastguard Worker        run_winmdidl(args.force, win_sdk_bin, output_path, winmd, header)
184*8975f5c5SAndroid Build Coastguard Worker
185*8975f5c5SAndroid Build Coastguard Worker    # Generate all the C++ headers and related files from the .idl files
186*8975f5c5SAndroid Build Coastguard Worker    for idl in idl_files:
187*8975f5c5SAndroid Build Coastguard Worker        progress += 1
188*8975f5c5SAndroid Build Coastguard Worker        print(f"[{progress}/{total}] Processing IDL {idl}...")
189*8975f5c5SAndroid Build Coastguard Worker        run_midlrt(args.force, win_sdk_bin, output_path, idl)
190*8975f5c5SAndroid Build Coastguard Worker
191*8975f5c5SAndroid Build Coastguard Worker    print("Setup is complete.")
192*8975f5c5SAndroid Build Coastguard Worker    print("")
193*8975f5c5SAndroid Build Coastguard Worker    print(f"Pass winappsdk_dir=\"{output_path}\" to gn gen or ninja.")
194