| # Copyright 2024 - The Android Open Source Project |
| # |
| # Licensed under the Apache License, Version 2.0 (the', help='License'); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an', help='AS IS' BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| from collections import UserDict |
| import json |
| import logging |
| import os |
| from pathlib import Path |
| import platform |
| import subprocess |
| from utils import rust_version |
| |
| |
| class BaseEnvironment(UserDict): |
| """Base class for environment management, providing a common foundation for |
| |
| both Posix and Windows environments. |
| """ |
| |
| def __init__(self, aosp: Path): |
| paths = [ |
| str( |
| aosp |
| / "external" |
| / "qemu" |
| / "android" |
| / "third_party" |
| / "chromium" |
| / "depot_tools" |
| ) |
| ] |
| |
| # Append prebuilt rust toolchain except for darwin_aarch64 |
| # TODO(360874898): aarch64-apple-darwin prebuilt rust toolchain supported from 1.77.1 |
| if not (platform.system() == "Darwin" and platform.machine() == "arm64"): |
| paths.append( |
| str( |
| aosp |
| / "prebuilts" |
| / "rust" |
| / f"{platform.system().lower()}-x86" |
| / f"{rust_version()}" |
| / "bin" |
| ) |
| ) |
| |
| paths.append(os.environ.get("PATH", "")) |
| |
| super().__init__({"PATH": os.pathsep.join(paths)}) |
| |
| |
| class PosixEnvironment(BaseEnvironment): |
| |
| def __init__(self, aosp: Path): |
| super().__init__(aosp) |
| |
| |
| class VisualStudioNotFoundException(Exception): |
| pass |
| |
| |
| class VisualStudioMissingVarException(Exception): |
| pass |
| |
| |
| class VisualStudioNativeWorkloadNotFoundException(Exception): |
| pass |
| |
| |
| class WindowsEnvironment(BaseEnvironment): |
| """Environment manager for Windows systems, specifically handling Visual Studio integration.""" |
| |
| def __init__(self, aosp: Path): |
| assert platform.system() == "Windows" |
| super().__init__(aosp) |
| for key in os.environ: |
| self[key.upper()] = os.environ[key] |
| |
| vs = self._visual_studio() |
| logging.info("Loading environment from %s", vs) |
| env_lines = subprocess.check_output( |
| [vs, "&&", "set"], encoding="utf-8" |
| ).splitlines() |
| for line in env_lines: |
| if "=" in line: |
| key, val = line.split("=", 1) |
| # Variables in windows are case insensitive, but not in python dict! |
| self[key.upper()] = val |
| |
| # Set PYTHONUTF8 to 1 |
| self["PYTHONUTF8"] = "1" |
| |
| if not "VSINSTALLDIR" in self: |
| raise VisualStudioMissingVarException( |
| "Missing VSINSTALLDIR in environment" |
| ) |
| |
| if not "VCTOOLSINSTALLDIR" in self: |
| raise VisualStudioMissingVarException( |
| "Missing VCTOOLSINSTALLDIR in environment" |
| ) |
| |
| def _visual_studio(self) -> Path: |
| """Locates the Visual Studio installation and its Native Desktop workload. |
| |
| Raises: |
| VisualStudioNotFoundException: When Visual Studio is not found. |
| VisualStudioNativeWorkloadNotFoundException: When the Native Desktop |
| workload is not found. |
| |
| Returns: |
| Path: Path to the Visual Studio vcvars64.bat file. |
| """ |
| prgrfiles = Path(os.getenv("ProgramFiles(x86)", "C:\Program Files (x86)")) |
| res = subprocess.check_output([ |
| str( |
| prgrfiles / "Microsoft Visual Studio" / "Installer" / "vswhere.exe" |
| ), |
| "-requires", |
| "Microsoft.VisualStudio.Workload.NativeDesktop", |
| "-sort", |
| "-format", |
| "json", |
| "-utf8", |
| ]) |
| vsresult = json.loads(res) |
| if len(vsresult) == 0: |
| raise VisualStudioNativeWorkloadNotFoundException( |
| "No visual studio with the native desktop load available." |
| ) |
| |
| for install in vsresult: |
| logging.debug("Considering %s", install["displayName"]) |
| candidates = list( |
| Path(install["installationPath"]).glob("**/vcvars64.bat") |
| ) |
| |
| if len(candidates) > 0: |
| return candidates[0].absolute() |
| |
| # Oh oh, no visual studio.. |
| raise VisualStudioNotFoundException( |
| "Unable to detect a visual studio installation with the native desktop" |
| " workload." |
| ) |
| |
| |
| def get_default_environment(aosp: Path): |
| """Returns the appropriate environment manager based on the current operating system. |
| |
| The environment will make sure the following things hold: |
| |
| - Ninja will be on the PATH |
| - The visual studio tools environment will be loaded |
| """ |
| if platform.system() == "Windows": |
| return WindowsEnvironment(aosp) |
| return PosixEnvironment(aosp) |