blob: 5ee822819cb59ace915bfbc194e85581954059d1 [file] [log] [blame]
#
# 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.
#
"""Classes related to building and installing platform-specific packages."""
import abc
import importlib
import logging
import os
import starboard
from starboard.tools import paths
from starboard.tools import platform
def _GetPackageClass(platform_info):
# From the relative path to the starboard directory, construct a full
# python package name and attempt to load it.
try:
module = platform_info.ImportModule(starboard)
except ImportError as e:
logging.debug('Failed to import module for platform %s with error: %s',
platform_info.port_name, e)
return None
if not hasattr(module, 'Package'):
return None
return module.Package
def _GetPlatformInfosDict():
"""Find Starboard ports that support building packages.
Returns:
A dict of [platform_name, Class] where Class inherits from PackageBase
"""
packager_modules = {}
for platform_info in platform.PlatformInfo.EnumeratePorts(
paths.STARBOARD_ROOT):
# From the relative path to the starboard directory, construct a full
# python package name and attempt to load it.
package_class = _GetPackageClass(platform_info)
if not package_class:
continue
# Populate a mapping from platform name to the module containing the
# Package class.
try:
for platform_name in package_class.SupportedPlatforms():
if platform_name in packager_modules:
logging.warning('Packager for %s is defined in multiple modules.',
platform_name)
else:
packager_modules[platform_name] = platform_info
except Exception as e: # pylint: disable=broad-except
# Catch all exceptions to avoid an error in one platform's Packager
# halting the script for other platforms' packagers.
logging.warning('Exception iterating supported platform for platform '
'%s: %s.', platform_info.port_name, e)
return packager_modules
class PackageBase(object):
"""Represents a build package that exists on the local filesystem."""
__metaclass__ = abc.ABCMeta
def __init__(self, source_dir, output_dir):
"""Initialize common paths for building Packages.
Args:
source_dir: The directory containing the application to be packaged.
output_dir: The directory into which the package should be placed.
"""
self.source_dir = source_dir
self.output_dir = output_dir
@abc.abstractmethod
def Install(self, targets=None):
"""Install the package to the specified list of targets.
Args:
targets: A list of targets to install the package to, or None on platforms
that support installing to a default target.
This method can be overridden to implement platform-specific steps to
install the package for that platform.
"""
pass
@classmethod
def AddArguments(cls, arg_parser):
"""Add platform-specific command-line arguments to the ArgumentParser.
Platforms that require additional arguments to configure building a package
can override this method and add them to |arg_parser|.
Args:
arg_parser: An ArgumentParser object.
"""
pass
@classmethod
def ExtractArguments(cls, options):
"""Extract arguments from an ArgumentParser's namespace object.
Platforms that add additional arguments can override this method to extract
the options and add them to a dict that will be passed to the Package
constructor.
Args:
options: A namespace object returned from ArgumentParser.parse_args
Returns:
A dict of kwargs to be passed to the Package constructor.
"""
return {}
class Packager(object):
"""Top level class for building a package."""
def __init__(self):
self.platform_infos = _GetPlatformInfosDict()
def SupportedPlatforms(self):
"""Get a list of platforms for which a package can be built."""
return self.platform_infos.keys()
def GetPlatformInfo(self, platform_name):
return self.platform_infos.get(platform_name, None)
def BuildPackage(self, platform_name, source_dir, output_dir, **kwargs):
"""Build a package for the specified platform.
Args:
platform_name: The platform for which a package should be built.
source_dir: The directory containing the application to be packaged.
output_dir: The directory into which the package files should be placed.
**kwargs: Platform-specific arguments.
Returns:
A PackageBase instance.
"""
package_class = _GetPackageClass(self.platform_infos[platform_name])
return package_class(source_dir=source_dir, output_dir=output_dir, **kwargs)
def AddPlatformArguments(self, platform_name, argparser):
package_class = _GetPackageClass(self.platform_infos[platform_name])
package_class.AddArguments(argparser)
def ExtractPlatformArguments(self, platform_name, options):
package_class = _GetPackageClass(self.platform_infos[platform_name])
return package_class.ExtractArguments(options)