| #!/usr/bin/env python3 |
| # Copyright 2023 The Chromium Authors |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import hashlib |
| import os |
| import shutil |
| import subprocess |
| import sys |
| import tempfile |
| import urllib.request |
| import argparse |
| |
| # Windows 11, version 22H2 EWDK with Visual Studio Build Tools 17.1.5 |
| EWDK_URL = "https://go.microsoft.com/fwlink/?linkid=2195661" |
| # SHA256 hash of the ISO that we expect. |
| EWDK_HASH = "887d484454c677db191bf444380a3059a20dd87ee56b1028b12fd8cb52b997f0" |
| # Script in the wdk to run to set up the build environment. |
| BUILD_ENV = "BuildEnv\\SetupBuildEnv.cmd" |
| # MSVC++ project file to build. |
| BUILD_FILE = "third_party\\win_virtual_display\\driver\\ChromiumVirtualDisplayDriver.vcxproj" |
| |
| |
| def get_ewdk_iso_path(): |
| return os.path.join(tempfile.gettempdir(), "ewdk.iso") |
| |
| |
| def get_ewdk_iso_extract_path(): |
| return os.path.join(tempfile.gettempdir(), "wdk") |
| |
| |
| def hash_file(file_path): |
| """Returns SHA256 hash of a specified file.""" |
| sha256 = hashlib.sha256() |
| with open(file_path, "rb") as f: |
| for b in iter(lambda: f.read(2048), b""): |
| sha256.update(b) |
| return sha256.hexdigest() |
| |
| |
| def fetch_and_mount_ewdk(): |
| """ Fetches the EWK ISO and mounts it as a drive. |
| Returns the drive letter that it was mounted to (e.g. "D").""" |
| iso_path = get_ewdk_iso_path() |
| print(f"Downloading iso to: {iso_path}") |
| if os.path.isfile(iso_path) == False: |
| urllib.request.urlretrieve(EWDK_URL, iso_path) |
| else: |
| print(f"File exists. Skipping ISO download.") |
| # Check the hash of the download so that unexpected changes are flagged. |
| iso_hash = hash_file(iso_path) |
| if iso_hash != EWDK_HASH: |
| print(f"iso hash mismatch. Expected: {EWDK_HASH}. Got: {iso_hash}") |
| cmd = [ |
| "powershell.exe", |
| "-command", |
| "Mount-DiskImage", |
| "-ImagePath", |
| iso_path, |
| "|", |
| "Get-Volume", |
| "|ForEach-Object", |
| "DriveLetter", |
| ] |
| result = subprocess.run(cmd, capture_output=True, text=True) |
| if (result.returncode!=0): |
| raise Exception(f"Failed to mount ISO: {result.stdout}") |
| output_drive = result.stdout.strip() |
| if (len(output_drive)!=1): |
| raise Exception(f"Failed to mount ISO: No drive letter obtained.") |
| print(f"ISO mounted to drive letter: {output_drive}") |
| return output_drive |
| |
| |
| def unmount_ewdk(): |
| """Unmounts the ISO from its drive letter.""" |
| iso_path = get_ewdk_iso_path() |
| cmd = ["powershell.exe", "-command", "Dismount-DiskImage", iso_path] |
| subprocess.run(cmd) |
| |
| |
| def build(ewdk_path, output_path): |
| """Build the vcxproj using the specified ewdk path and output path.""" |
| build_env = os.path.join(ewdk_path, BUILD_ENV) |
| build_path = tempfile.gettempdir() + "\\build\\" |
| command = (f"{build_env} && msbuild {BUILD_FILE} /t:build " |
| f"/property:Platform=x64 /p:OutDir={build_path}") |
| cmd = ["cmd", "/c", command] |
| result = subprocess.run(cmd, capture_output=True, text=True) |
| print(result.stdout) |
| if "Build succeeded" not in result.stdout: |
| raise Exception("Build failed.") |
| # Copy compilation output and test certificate files to the output path. |
| print(f"Copying build output to {output_path}") |
| shutil.copytree( |
| os.path.join(build_path, "ChromiumVirtualDisplayDriver"), |
| output_path, |
| dirs_exist_ok=True, |
| ) |
| shutil.copy(os.path.join(build_path, "ChromiumVirtualDisplayDriver.cer"), |
| output_path) |
| # Helps with TraceView and debugging on the bots but not strictly necessary. |
| shutil.copy(os.path.join(build_path, "ChromiumVirtualDisplayDriver.pdb"), |
| output_path) |
| |
| def copy_drive(drive_letter, dest): |
| """Copy the contents of the specified drive letter to the specified path""" |
| src_path = drive_letter + ":\\" |
| print(f"Copying directory {src_path} to {dest}") |
| shutil.copytree(src_path, dest, dirs_exist_ok=True) |
| |
| def main(): |
| parser = argparse.ArgumentParser() |
| # Args passed by the 3pp recipe (See: recipes/recipe_modules/support_3pp/spec.proto). |
| parser.add_argument('output_prefix') |
| parser.add_argument('deps_prefix') |
| # Some environments fail when executing binaries directly on a mounted disk. |
| # This flag copies the contents to the local disk. |
| parser.add_argument('-c', '--copy_ewdk', action='store_true') |
| args = parser.parse_args() |
| mounted_drive_letter = fetch_and_mount_ewdk() |
| try: |
| ewdk_path = mounted_drive_letter + ":\\" |
| if (args.copy_ewdk): |
| ewdk_path = get_ewdk_iso_extract_path() |
| copy_drive(mounted_drive_letter, ewdk_path) |
| build(ewdk_path, args.output_prefix) |
| finally: |
| print("Unmounting ISO") |
| unmount_ewdk() |
| |
| |
| if __name__ == "__main__": |
| main() |