| # |
| # Copyright 2016 Google Inc. All Rights Reserved. |
| # |
| # Licensed under the Apache License, Version 2.0 (the "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 "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. |
| # |
| """Functionality to enumerate and represent starboard ports.""" |
| |
| import importlib |
| import os |
| import sys |
| |
| if "environment" in sys.modules: |
| environment = sys.modules["environment"] |
| else: |
| env_path = os.path.abspath(os.path.dirname(__file__)) |
| if env_path not in sys.path: |
| sys.path.append(env_path) |
| environment = importlib.import_module("environment") |
| |
| |
| import logging |
| import re |
| |
| |
| # The list of files that must be present in a directory to allow it to be |
| # considered a valid port. |
| _PORT_FILES = [ |
| 'gyp_configuration.gypi', |
| 'gyp_configuration.py', |
| 'starboard_platform.gyp', |
| 'configuration_public.h', |
| 'atomic_public.h', |
| 'thread_types_public.h', |
| ] |
| |
| |
| # Whether to emit warnings if it finds a directory that almost has a port. |
| # TODO: Enable when tree is clean. |
| _WARN_ON_ALMOST_PORTS = False |
| |
| |
| def _IsValidPortFilenameList(directory, filenames): |
| """Determines if |filenames| contains the required files for a valid port.""" |
| missing = set(_PORT_FILES) - set(filenames) |
| if missing: |
| if len(missing) < (len(_PORT_FILES) / 2) and _WARN_ON_ALMOST_PORTS: |
| logging.warning('Directory %s contains several files needed for a port, ' |
| 'but not all of them. In particular, it is missing: %s', |
| directory, ', '.join(missing)) |
| return not missing |
| |
| |
| def _GetPortName(root, directory): |
| """Gets the name of a port found at |directory| off of |root|.""" |
| |
| assert directory.startswith(root) |
| start = len(root) + 1 # Remove the trailing slash from the root. |
| |
| assert start < len(directory) |
| |
| # Calculate the name based on relative path from search root to port. |
| return re.sub(r'[^a-zA-Z0-9_]', r'-', directory[start:]) |
| |
| |
| def _GetAllPlatforms(port_root_paths): |
| """Retrieves information about all available Cobalt ports. |
| |
| Args: |
| port_root_paths: List of paths that will be crawled to find ports. |
| |
| Returns: |
| Dict mapping each available port to its location in the filesystem. |
| """ |
| platform_dict = {} |
| for path in port_root_paths: |
| for port in PlatformInfo.EnumeratePorts(path): |
| platform_dict[port.port_name] = port.path |
| return platform_dict |
| |
| |
| def GetAllPorts(): |
| """Gets all available starboard ports from the host app. |
| |
| Returns: |
| Dictionary mapping port names to their path in the filesystem. |
| """ |
| port_root_paths = environment.GetStarboardPortRoots() |
| return _GetAllPlatforms(port_root_paths) |
| |
| |
| class PlatformInfo(object): |
| """Information about a specific starboard port.""" |
| |
| @classmethod |
| def EnumeratePorts(cls, root_path, exclusion_set=None): |
| """Generator that iterates over starboard ports found under |path|.""" |
| if not exclusion_set: |
| exclusion_set = set() |
| for current_path, directories, filenames in os.walk(root_path): |
| # Don't walk into any directories in the exclusion set. |
| directories[:] = (x for x in directories |
| if os.path.join(current_path, x) not in exclusion_set) |
| # Determine if the current directory is a valid port directory. |
| if _IsValidPortFilenameList(current_path, filenames): |
| if current_path == root_path: |
| logging.warning('Found port at search path root: %s', current_path) |
| port_name = _GetPortName(root_path, current_path) |
| yield PlatformInfo(port_name, current_path) |
| |
| def __init__(self, name, path): |
| self.port_name = name |
| self.path = path |
| |
| def ImportModule(self, root_module, module_name=None): |
| """Load a platform specific python module using importlib. |
| |
| Load the python module named |module_name| relative to |root_module|. |
| Args: |
| module: Name of a python module to load. If None, load the platform |
| directory as a python module. |
| root_module: An already-loaded module |
| Returns: |
| A module loaded with importlib.import_module |
| Throws: |
| ImportError if the module fails to be loaded. |
| """ |
| # From the relative path to the |root_module|'s directory, construct a full |
| # python package name and attempt to load it. |
| relative_path = os.path.relpath( |
| self.path, os.path.dirname(root_module.__file__)) |
| components = os.path.normpath(relative_path).split(os.sep) |
| components = [root_module.__name__] + components |
| if module_name: |
| components.append(module_name) |
| full_package_name = '.'.join(components) |
| return importlib.import_module(full_package_name) |