blob: 3db4631017fc1b263769b3cc38476309178f7e07 [file] [log] [blame]
# 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)