• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2023 The Chromium Authors
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5from __future__ import annotations
6
7import functools
8import logging
9import os
10import shutil
11from typing import Optional
12
13from crossbench import path as pth
14from crossbench.plt.base import Platform
15
16
17class WinPlatform(Platform):
18  # TODO: support remote platforms
19  SEARCH_PATHS = (
20      pth.LocalPath("."),
21      pth.LocalPath(os.path.expandvars("%ProgramFiles%")),
22      pth.LocalPath(os.path.expandvars("%ProgramFiles(x86)%")),
23      pth.LocalPath(os.path.expandvars("%APPDATA%")),
24      pth.LocalPath(os.path.expandvars("%LOCALAPPDATA%")),
25  )
26
27  @property
28  def is_win(self) -> bool:
29    return True
30
31  @property
32  def name(self) -> str:
33    return "win"
34
35  @property
36  def device(self) -> str:
37    # TODO: implement
38    return ""
39
40  @functools.cached_property
41  def version(self) -> str:  #pylint: disable=invalid-overridden-method
42    return self.sh_stdout("cmd", "/c", "ver").strip()
43
44  @functools.cached_property
45  def cpu(self) -> str:  #pylint: disable=invalid-overridden-method
46    return self.sh_stdout("wmic", "cpu", "get",
47                          "name").strip().splitlines()[2].strip()
48
49  def search_binary(self, app_or_bin: pth.AnyPathLike) -> Optional[pth.AnyPath]:
50    self.assert_is_local()
51    app_or_bin_path: pth.AnyPath = self.path(app_or_bin)
52    if not app_or_bin_path.parts:
53      raise ValueError("Got empty path")
54    if app_or_bin_path.suffix.lower() not in (".exe", ".bat"):
55      raise ValueError("Expected executable path with '.exe' or '.bat' suffix, "
56                       f"but got: '{app_or_bin_path.name}'")
57    if result_path := self.which(app_or_bin):
58      assert self.exists(result_path), f"{result_path} does not exist."
59      return result_path
60    for path in self.SEARCH_PATHS:
61      # Recreate Path object for easier pyfakefs testing
62      result_path = self.path(path) / app_or_bin
63      if self.exists(result_path):
64        return result_path
65    return None
66
67  def app_version(self, app_or_bin: pth.AnyPathLike) -> str:
68    app_or_bin = self.path(app_or_bin)
69    if not self.exists(app_or_bin):
70      raise ValueError(f"Binary {app_or_bin} does not exist.")
71    if version := self.sh_stdout(
72        "powershell", "-command",
73        f"(Get-Item '{app_or_bin}').VersionInfo.ProductVersion").strip():
74      return version
75    try:
76      # Fall back to command-line tools.
77      if version := self.sh_stdout(app_or_bin, "--version").strip():
78        return version
79    except Exception as e:  # pylint: disable=broad-exception-caught
80      logging.debug("Failed to extract binary tool version: %s", e)
81    raise ValueError(f"Could not extract version for {app_or_bin}")
82
83
84  def symlink_or_copy(self, src: pth.AnyPathLike,
85                      dst: pth.AnyPathLike) -> pth.AnyPath:
86    """Windows does not support symlinking without admin support.
87    Copy files on windows but symlink everywhere else (see base Platform)."""
88    self.assert_is_local()
89    dst_path = self.path(dst)
90    shutil.copy(os.fspath(self.path(src)), os.fspath(dst_path))
91    return dst_path
92