blob: 4c9f5136b43e63362bddce2c19d48cb8cc198c2d [file] [log] [blame]
#
# Copyright 2016 The Cobalt Authors. 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.
#
"""Build related constants and helper functions."""
import importlib
import importlib.machinery
import importlib.util
import logging
import os
import sys
from starboard.build.platforms import PLATFORMS
from starboard.tools import paths
_STARBOARD_TOOLCHAINS_DIR_KEY = 'STARBOARD_TOOLCHAINS_DIR'
_STARBOARD_TOOLCHAINS_DIR_NAME = 'starboard-toolchains'
class ClangSpecification(object):
"""A specific version of Clang."""
def __init__(self, revision, version):
assert revision
assert version
self.revision = revision
self.version = version
def GetToolchainsDir():
"""Gets the directory that contains all downloaded toolchains."""
home_dir = os.environ.get('HOME')
toolchains_dir = os.path.realpath(
os.getenv(_STARBOARD_TOOLCHAINS_DIR_KEY,
os.path.join(home_dir, _STARBOARD_TOOLCHAINS_DIR_NAME)))
if not os.path.isdir(toolchains_dir):
# Ensure the toolchains directory exists.
os.mkdir(toolchains_dir)
return toolchains_dir
def _GetClangBasePath(clang_spec):
return os.path.join(GetToolchainsDir(),
'x86_64-linux-gnu-clang-chromium-' + clang_spec.revision)
def GetClangBinPath(clang_spec):
return os.path.join(_GetClangBasePath(clang_spec), 'bin')
def _ModuleLoaded(module_name, module_path):
if module_name not in sys.modules:
return False
# Sometimes one of these has .pyc and the other has .py, but we don't care.
extensionless_loaded_path = os.path.splitext(
os.path.abspath(sys.modules['platform_module'].__file__))[0]
extensionless_module_path = os.path.splitext(os.path.abspath(module_path))[0]
return extensionless_loaded_path == extensionless_module_path
def _LoadPlatformModule(platform_name, file_name, function_name):
"""Loads a platform module.
The function will use the provided platform name to load
a python file with a matching name that contains the module.
Args:
platform_name: Platform name.
file_name: The file that contains the module. It can be
gyp_configuration.py or test_filters.py
function_name: The function name of the module.
For gyp_configuration.py, it is CreatePlatformConfig.
For test_filters.py, it is GetTestFilters
Returns: Instance of a class derived from module path.
"""
try:
logging.debug('Loading platform %s for "%s".', file_name, platform_name)
if platform_name in PLATFORMS:
platform_path = os.path.join(paths.REPOSITORY_ROOT,
PLATFORMS[platform_name])
module_path = os.path.join(platform_path, file_name)
if not _ModuleLoaded('platform_module', module_path):
loader = importlib.machinery.SourceFileLoader('platform_module',
module_path)
spec = importlib.util.spec_from_file_location(
'platform_module', module_path, loader=loader)
platform_module = importlib.util.module_from_spec(spec)
loader.exec_module(platform_module)
else:
platform_module = sys.modules['platform_module']
else:
module_path = os.path.join('config', f'{platform_name}.py')
platform_module = importlib.import_module(f'config.{platform_name}')
except (ImportError, IOError):
logging.exception('Unable to import "%s".', module_path)
return None
if not hasattr(platform_module, function_name):
logging.error('"%s" does not contain %s.', module_path, function_name)
return None
try:
platform_attr = getattr(platform_module, function_name)()
return platform_attr, platform_path
except RuntimeError:
logging.exception('Exception in %s.', function_name)
return None
def _LoadPlatformConfig(platform_name):
"""Loads a platform specific configuration.
The function will use the provided platform name to load
a python file with a matching name that contains the platform
specific configuration.
Args:
platform_name: Platform name.
Returns:
Instance of a class derived from PlatformConfiguration.
"""
platform_configuration, platform_path = _LoadPlatformModule(
platform_name, 'gyp_configuration.py', 'CreatePlatformConfig')
platform_configuration.SetDirectory(platform_path)
return platform_configuration
def _LoadPlatformTestFilters(platform_name):
"""Loads the platform specific test filters.
The function will use the provided platform name to load
a python file with a matching name that contains the platform
specific test filters.
Args:
platform_name: Platform name.
Returns:
Instance of a class derived from TestFilters.
"""
platform_test_filters, _ = _LoadPlatformModule(platform_name,
'test_filters.py',
'CreateTestFilters')
return platform_test_filters
# Global cache of the platform configurations, so that platform config objects
# are only created once.
_PLATFORM_CONFIG_DICT = {}
_PLATFORM_TEST_FILTERS_DICT = {}
def GetPlatformConfig(platform_name):
"""Returns a platform specific configuration.
This function will return a cached platform configuration object, loading it
if it doesn't exist via a call to _LoadPlatformConfig().
Args:
platform_name: Platform name.
Returns:
Instance of a class derived from PlatformConfiguration.
"""
if platform_name not in _PLATFORM_CONFIG_DICT:
_PLATFORM_CONFIG_DICT[platform_name] = _LoadPlatformConfig(platform_name)
return _PLATFORM_CONFIG_DICT[platform_name]
def GetPlatformTestFilters(platform_name):
"""Returns a platform specific test filters.
Args:
platform_name: Platform name.
Returns:
Instance of a class derived from TestFilters.
"""
if platform_name not in _PLATFORM_TEST_FILTERS_DICT:
_PLATFORM_TEST_FILTERS_DICT[platform_name] = _LoadPlatformTestFilters(
platform_name)
return _PLATFORM_TEST_FILTERS_DICT[platform_name]