# # Copyright (C) 2017 Codethink Limited # Copyright (C) 2018 Bloomberg Finance LP # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library. If not, see . # # Authors: # Tristan Maat import os import platform import psutil from .._exceptions import PlatformError, ImplError, SandboxError from ..sandbox import SandboxDummy from .. import utils class Platform: # Platform() # # A class to manage platform-specific details. Currently holds the # sandbox factory as well as platform helpers. # def __init__(self): self._local_sandbox = None self.dummy_reasons = [] self._setup_sandbox() def _setup_sandbox(self): # Try to setup buildbox-run sandbox, otherwise fallback to the dummy sandbox. try: self._setup_buildboxrun_sandbox() except (SandboxError, utils.ProgramNotFoundError): self._setup_dummy_sandbox() def _check_sandbox(self, Sandbox): Sandbox._dummy_reasons = [] try: Sandbox.check_available() except SandboxError as Error: self.dummy_reasons += Sandbox._dummy_reasons raise Error @classmethod def create_instance(cls): return Platform() def get_cpu_count(self, cap=None): # `psutil.Process.cpu_affinity()` is not available on all platforms. # So, fallback to getting the total cpu count in cases where it is not # available. dummy_process = psutil.Process() if hasattr(dummy_process, "cpu_affinity"): cpu_count = len(dummy_process.cpu_affinity()) else: cpu_count = os.cpu_count() if cap is None: return cpu_count else: return min(cpu_count, cap) @staticmethod def get_host_os(): system = platform.uname().system.lower() if system == "darwin" and platform.mac_ver()[0]: # mac_ver() returns a non-empty release string on macOS. return "macos" else: return system # canonicalize_arch(): # # This returns the canonical, OS-independent architecture name # or raises a PlatformError if the architecture is unknown. # @staticmethod def canonicalize_arch(arch): # Note that these are all expected to be lowercase, as we want a # case-insensitive lookup. Windows can report its arch in ALLCAPS. aliases = { "aarch32": "aarch32", "aarch64": "aarch64", "aarch64-be": "aarch64-be", "amd64": "x86-64", "arm": "aarch32", "armv8l": "aarch64", "armv8b": "aarch64-be", "i386": "x86-32", "i486": "x86-32", "i586": "x86-32", "i686": "x86-32", "power-isa-be": "power-isa-be", "power-isa-le": "power-isa-le", "powerpc": "power-isa-be", "powerpc64": "power-isa-be", # Used in GCC/LLVM "powerpc64le": "power-isa-le", # Used in GCC/LLVM "ppc64": "power-isa-be", "ppc64le": "power-isa-le", "sparc": "sparc-v9", "sparc64": "sparc-v9", "sparc-v9": "sparc-v9", "sun4v": "sparc-v9", "x86-32": "x86-32", "x86-64": "x86-64", } try: return aliases[arch.replace("_", "-").lower()] except KeyError: raise PlatformError("Unknown architecture: {}".format(arch)) # get_host_arch(): # # This returns the architecture of the host machine. The possible values # map from uname -m in order to be a OS independent list. # # Returns: # (string): String representing the architecture @staticmethod def get_host_arch(): uname = platform.uname() if uname.system.lower() == "aix": # IBM AIX systems reports their serial number as the machine # hardware identifier. So, we need to look at the reported processor # in this case. return Platform.canonicalize_arch(uname.processor) else: # Otherwise, use the hardware identifier from uname return Platform.canonicalize_arch(uname.machine) ################################################################## # Sandbox functions # ################################################################## # create_sandbox(): # # Create a build sandbox suitable for the environment # # Args: # args (dict): The arguments to pass to the sandbox constructor # kwargs (file): The keyword arguments to pass to the sandbox constructor # # Returns: # (Sandbox) A sandbox # def create_sandbox(self, *args, **kwargs): # pylint: disable=method-hidden raise ImplError("Platform {platform} does not implement create_sandbox()".format(platform=type(self).__name__)) def check_sandbox_config(self, config): # pylint: disable=method-hidden raise ImplError( "Platform {platform} does not implement check_sandbox_config()".format(platform=type(self).__name__) ) # Buildbox run sandbox methods def _check_sandbox_config_buildboxrun(self, config): from ..sandbox._sandboxbuildboxrun import SandboxBuildBoxRun # pylint: disable=cyclic-import SandboxBuildBoxRun.check_sandbox_config(self, config) @staticmethod def _create_buildboxrun_sandbox(*args, **kwargs): from ..sandbox._sandboxbuildboxrun import SandboxBuildBoxRun # pylint: disable=cyclic-import return SandboxBuildBoxRun(*args, **kwargs) def _setup_buildboxrun_sandbox(self): from ..sandbox._sandboxbuildboxrun import SandboxBuildBoxRun # pylint: disable=cyclic-import self._check_sandbox(SandboxBuildBoxRun) self.check_sandbox_config = self._check_sandbox_config_buildboxrun self.create_sandbox = self._create_buildboxrun_sandbox return True # Dummy sandbox methods @staticmethod def _check_dummy_sandbox_config(config): pass def _create_dummy_sandbox(self, *args, **kwargs): dummy_reasons = " and ".join(self.dummy_reasons) kwargs["dummy_reason"] = dummy_reasons return SandboxDummy(*args, **kwargs) def _setup_dummy_sandbox(self): self.check_sandbox_config = Platform._check_dummy_sandbox_config self.create_sandbox = self._create_dummy_sandbox return True