Andrew Top | c2b4089 | 2017-01-19 14:03:49 -0800 | [diff] [blame] | 1 | # |
| 2 | # Copyright 2016 Google Inc. All Rights Reserved. |
| 3 | # |
| 4 | # Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | # you may not use this file except in compliance with the License. |
| 6 | # You may obtain a copy of the License at |
| 7 | # |
| 8 | # http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | # |
| 10 | # Unless required by applicable law or agreed to in writing, software |
| 11 | # distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | # See the License for the specific language governing permissions and |
| 14 | # limitations under the License. |
| 15 | # |
| 16 | """Functionality to enumerate and represent starboard ports.""" |
| 17 | |
| 18 | import importlib |
| 19 | import logging |
| 20 | import os |
| 21 | import re |
| 22 | |
| 23 | |
| 24 | # The list of files that must be present in a directory to allow it to be |
| 25 | # considered a valid port. |
| 26 | _PORT_FILES = [ |
| 27 | 'gyp_configuration.gypi', |
| 28 | 'gyp_configuration.py', |
| 29 | 'starboard_platform.gyp', |
| 30 | 'configuration_public.h', |
| 31 | 'atomic_public.h', |
| 32 | 'thread_types_public.h', |
| 33 | ] |
| 34 | |
| 35 | |
| 36 | # Whether to emit warnings if it finds a directory that almost has a port. |
| 37 | # TODO: Enable when tree is clean. |
| 38 | _WARN_ON_ALMOST_PORTS = False |
| 39 | |
| 40 | |
| 41 | def _IsValidPortFilenameList(directory, filenames): |
| 42 | """Determines if |filenames| contains the required files for a valid port.""" |
| 43 | missing = set(_PORT_FILES) - set(filenames) |
| 44 | if missing: |
| 45 | if len(missing) < (len(_PORT_FILES) / 2) and _WARN_ON_ALMOST_PORTS: |
| 46 | logging.warning('Directory %s contains several files needed for a port, ' |
| 47 | 'but not all of them. In particular, it is missing: %s', |
| 48 | directory, ', '.join(missing)) |
| 49 | return not missing |
| 50 | |
| 51 | |
| 52 | def _GetPortName(root, directory): |
| 53 | """Gets the name of a port found at |directory| off of |root|.""" |
| 54 | |
| 55 | assert directory.startswith(root) |
| 56 | start = len(root) + 1 # Remove the trailing slash from the root. |
| 57 | |
| 58 | assert start < len(directory) |
| 59 | |
| 60 | # Calculate the name based on relative path from search root to port. |
| 61 | return re.sub(r'[^a-zA-Z0-9_]', r'-', directory[start:]) |
| 62 | |
| 63 | |
| 64 | class PlatformInfo(object): |
| 65 | """Information about a specific starboard port.""" |
| 66 | |
| 67 | @classmethod |
| 68 | def EnumeratePorts(cls, root_path): |
| 69 | """Generator that iterates over starboard ports found under |path|.""" |
| 70 | for current_path, _, filenames in os.walk(root_path): |
| 71 | if _IsValidPortFilenameList(current_path, filenames): |
| 72 | if current_path == root_path: |
| 73 | logging.warning('Found port at search path root: %s', current_path) |
| 74 | port_name = _GetPortName(root_path, current_path) |
| 75 | yield PlatformInfo(port_name, current_path) |
| 76 | |
| 77 | def __init__(self, name, path): |
| 78 | self.port_name = name |
| 79 | self.path = path |
| 80 | |
| 81 | def ImportModule(self, root_module, module_name=None): |
| 82 | """Load a platform specific python module using importlib. |
| 83 | |
| 84 | Load the python module named |module_name| relative to |root_module|. |
| 85 | Args: |
| 86 | module: Name of a python module to load. If None, load the platform |
| 87 | directory as a python module. |
| 88 | root_module: An already-loaded module |
| 89 | Returns: |
| 90 | A module loaded with importlib.import_module |
| 91 | Throws: |
| 92 | ImportError if the module fails to be loaded. |
| 93 | """ |
| 94 | # From the relative path to the |root_module|'s directory, construct a full |
| 95 | # python package name and attempt to load it. |
| 96 | relative_path = os.path.relpath( |
| 97 | self.path, os.path.dirname(root_module.__file__)) |
| 98 | components = os.path.normpath(relative_path).split(os.sep) |
| 99 | components = [root_module.__name__] + components |
| 100 | if module_name: |
| 101 | components.append(module_name) |
| 102 | full_package_name = '.'.join(components) |
| 103 | return importlib.import_module(full_package_name) |