diff options
author | Ari Hausman-Cohen <arihc@google.com> | 2016-05-03 14:04:37 -0700 |
---|---|---|
committer | Ari Hausman-Cohen <arihc@google.com> | 2016-05-04 20:13:48 +0000 |
commit | 0ebce37d35afe62b227d58fa1637106ff62f3c60 (patch) | |
tree | 77fb20d6ba8b1def3bd788f7d5c60e6d4c732842 | |
parent | 2778e0bc4fa849cad88694374e460e6ee8a7f255 (diff) | |
download | bdk-0ebce37d35afe62b227d58fa1637106ff62f3c60.tar.gz |
Replaces Target with Platform as appropriate.
os, bsp, and versions of both have been replaced
in target with a Platform wrapping them all. The
Platform object also provides methods/properties
to get os root, build cache, and other cache dirs, as
well as wrap device/os linking.
BUG: 28401438
Change-Id: Id1d37e89b3b2295557a5071854772d6f4718b9da
TEST: Unit tests pass, ran all affected commands
38 files changed, 451 insertions, 685 deletions
diff --git a/cli/lib/bsp/device.py b/cli/lib/bsp/device.py index d6ad014..6378051 100644 --- a/cli/lib/bsp/device.py +++ b/cli/lib/bsp/device.py @@ -27,7 +27,7 @@ from core import util import error -DEVICE_KEY_NAME = 'device_name' +DEVICE_KEY_FULL_NAME = 'device_name' DEVICE_KEY_PACKAGES = 'packages' DEVICE_KEY_VENDOR = 'vendor' DEVICE_KEY_ARCH = 'arch' @@ -92,7 +92,8 @@ class Device(object): """Class to represent devices with BSPs available. Attributes: - name: the name of the package. + name: the short name of the package. + full_name: the full name of the package. vendor: the vendor of the device. arch: the architecture of the device. version: the version of the device. @@ -102,7 +103,7 @@ class Device(object): (path is relative to OS root). """ - def __init__(self, name, vendor, arch, package_map): + def __init__(self, name, full_name, vendor, arch, package_map): """Initializes a Device. Args: @@ -114,6 +115,7 @@ class Device(object): where relative_path is relative to the BDK root. """ self.name = name + self.full_name = full_name self.vendor = vendor self.arch = arch self.version = '0.0.0' @@ -148,7 +150,7 @@ class Device(object): packages_string += '\n * {0}'.format(subpackage_string) device_status = max(device_status, subpackage_status) - device_string = '{} {} - {}'.format(self.name, self.version, + device_string = '{} {} - {}'.format(self.full_name, self.version, status.status_string(device_status)) if verbose: device_string += packages_string @@ -416,7 +418,7 @@ class Device(object): pkg.uninstall() @classmethod - def from_dict(cls, dic, packages): + def from_dict(cls, dic, name, packages): """Create a Device from a dict. Merges together the name-based package identification with @@ -424,6 +426,7 @@ class Device(object): Args: dic: the dictionary to build the device from. + name: the name of the device. packages: a dictionary mapping { package_name : Package } Returns: @@ -433,21 +436,21 @@ class Device(object): KeyError: An expected key is missing. ValueError: A non-existant package or subpackage name is used. """ - name = dic[DEVICE_KEY_NAME] + full_name = dic[DEVICE_KEY_FULL_NAME] package_map = {} for (package_name, subpackage_map) in ( dic[DEVICE_KEY_PACKAGES].iteritems()): pkg = packages.get(package_name) if not pkg: raise ValueError( - 'Package {0} for {1} does not exist.'.format(package_name, - name)) + 'Package {} for {} does not exist.'.format(package_name, + full_name)) for subpackage in subpackage_map: if subpackage not in pkg.subpackages: raise ValueError( - 'Subpackage {0}.{1} for {2} does not exist.'.format( - package_name, subpackage, name)) + 'Subpackage {}.{} for {} does not exist.'.format( + package_name, subpackage, full_name)) package_map[pkg] = subpackage_map - return cls(name, dic[DEVICE_KEY_VENDOR], dic[DEVICE_KEY_ARCH], - package_map) + return cls(name, full_name, dic[DEVICE_KEY_VENDOR], + dic[DEVICE_KEY_ARCH], package_map) diff --git a/cli/lib/bsp/device_stub.py b/cli/lib/bsp/device_stub.py index 6372f6d..2a47ae9 100644 --- a/cli/lib/bsp/device_stub.py +++ b/cli/lib/bsp/device_stub.py @@ -28,9 +28,11 @@ class Error(device.Error): class StubDevice(object): - def __init__(self, name='', vendor='', arch='', package_map=None, - version='', downloaded=False, should_link_version=None): + def __init__(self, name='', full_name='', vendor='', arch='', + package_map=None, version='', downloaded=False, + should_link_version=None): self.name = name + self.full_name = full_name self.vendor = vendor self.arch = arch self.version = version diff --git a/cli/lib/bsp/device_unittest.py b/cli/lib/bsp/device_unittest.py index 247602f..53ca216 100644 --- a/cli/lib/bsp/device_unittest.py +++ b/cli/lib/bsp/device_unittest.py @@ -64,44 +64,57 @@ class DeviceTest(unittest.TestCase): device.package = package_stub.StubPackageModule(self.stub_tar_package) self.dev = device.Device( - 'test_device', 'test_vendor', 'test_arch', + 'test_device', 'Test Device', 'test_vendor', 'test_arch', {self.package1: {'subpackage_1': 'path1', 'subpackage_2': 'path2'}, self.package2: {'subpackage_2': 'path3'}}) + def test_init(self): + # Check all public attributes. + self.assertEqual(self.dev.name, 'test_device') + self.assertEqual(self.dev.full_name, 'Test Device') + self.assertEqual(self.dev.vendor, 'test_vendor') + self.assertEqual(self.dev.arch, 'test_arch') + self.assertEqual(self.dev.version, '0.0.0') + def test_from_dict(self): - dev = device.Device.from_dict(self.dev_json, self.packages) + dev = device.Device.from_dict(self.dev_json, 'a_name', self.packages) self.assertIsInstance(dev, device.Device) # pylint: disable=protected-access self.assertTrue(self.package1 in dev._package_map) self.assertEqual(dev._package_map[self.package1]['subpackage_1'], 'path/to/link') + self.assertEqual(dev.name, 'a_name') + self.assertEqual(dev.full_name, 'Test Device 1') + self.assertEqual(dev.vendor, 'test_vendor') + self.assertEqual(dev.arch, 'test_arch') + self.assertEqual(dev.version, '0.0.0') def test_from_dict_bad_package(self): self.dev_json['packages']['nonexistent'] = {} with self.assertRaisesRegexp(ValueError, 'Package.*nonexistent.*does not exist'): - device.Device.from_dict(self.dev_json, self.packages) + device.Device.from_dict(self.dev_json, 'a_name', self.packages) def test_from_dict_bad_subpackage(self): self.dev_json['packages']['package_1']['nonexistent'] = {} with self.assertRaisesRegexp(ValueError, 'Sub.*nonexistent.*does not exist'): - device.Device.from_dict(self.dev_json, self.packages) + device.Device.from_dict(self.dev_json, 'a_name', self.packages) def test_from_dict_missing_keys(self): del self.dev_json['arch'] with self.assertRaisesRegexp(KeyError, 'arch'): - device.Device.from_dict(self.dev_json, self.packages) + device.Device.from_dict(self.dev_json, 'a_name', self.packages) self.dev_json['arch'] = 'test_arch' del self.dev_json['vendor'] with self.assertRaisesRegexp(KeyError, 'vendor'): - device.Device.from_dict(self.dev_json, self.packages) + device.Device.from_dict(self.dev_json, 'a_name', self.packages) self.dev_json['vendor'] = 'test_vendor' del self.dev_json['packages'] with self.assertRaisesRegexp(KeyError, 'packages'): - device.Device.from_dict(self.dev_json, self.packages) + device.Device.from_dict(self.dev_json, 'a_name', self.packages) def test_status_installed(self): self.assertEqual(self.dev.status(self._OS_VERSION)[0], status.INSTALLED) @@ -329,7 +342,7 @@ class DeviceTest(unittest.TestCase): non_matching_git = package_stub.StubPackage( 'git', None, '<branch>:' + self.stub_tar_package.tarball_version, None) - dev = device.Device('name', 'vend', 'arch', { + dev = device.Device('name', 'full_name', 'vend', 'arch', { non_matching_tar: {}, matching: {}, non_matching_git: {}}) self.assertEqual(dev.match_tarball('file1'), 'match') @@ -344,7 +357,7 @@ class DeviceTest(unittest.TestCase): self.stub_open.files = {'file1': stubs.StubFile('file1')} self.stub_os.path.should_exist = ['file1'] self.stub_hashlib.should_return = '<correct_version>' - dev = device.Device('name', 'vend', 'arch', + dev = device.Device('name', 'full_name', 'vend', 'arch', {non_matching_tar: {}, non_matching_git: {}}) self.assertEqual(dev.match_tarball('file1'), None) diff --git a/cli/lib/bsp/manifest.py b/cli/lib/bsp/manifest.py index f7de742..a1ed34e 100644 --- a/cli/lib/bsp/manifest.py +++ b/cli/lib/bsp/manifest.py @@ -77,6 +77,7 @@ class Manifest(object): for (short_name, device_spec) in dic[MANIFEST_KEY_DEVICES].iteritems(): try: devices[short_name] = device.Device.from_dict(device_spec, + short_name, packages) except KeyError as e: raise KeyError('device {}: {}'.format(short_name, e)) diff --git a/cli/lib/cli/clicommand.py b/cli/lib/cli/clicommand.py index 67b5154..6655a52 100644 --- a/cli/lib/cli/clicommand.py +++ b/cli/lib/cli/clicommand.py @@ -25,7 +25,6 @@ from metrics import metrics_util from metrics.data_types import timing from project import common from project import project_spec -from project import target class Error(error.Error): @@ -40,19 +39,6 @@ class TargetError(Error): """"Raised when an invalid target is given.""" -class ValidationError(Error): - """Raised when specified values are confirmed to be invalid.""" - description = 'Error validating project spec' - - -class NotDownloadedError(ValidationError): - """Raised when a specified target OS or BSP isn't downloaded.""" - - -class CompatibilityError(ValidationError): - """Raised when a specified target OS and BSP aren't compatible.""" - - # pylint: disable=abstract-class-not-used class Command(object): """A general cli command.""" @@ -99,70 +85,12 @@ class Command(object): def __init__(self): self._timer = timer.Timer(type(self).__name__) - def ValidateTarget(self, target_, os=True, os_downloaded=True, - board=True, board_downloaded=True): - """Confirm that a target_ specifies valid board and os. - - If validating both os and board, also confirms that the two are - compatible. - - Args: - target_: The target_ to validate. - os: If True, validate that the desired os exists. - os_downloaded: If True, validate that the desired os is downloaded. - Ignored if os = False. - board: If True, validate that the desired board exists. - board_downloaded: If True, validate that the desired board is - downloaded. Ignored if board = False. - - Raises: - ValidationError: if a required OS or Board version is not installed. - """ - validation_message_start = '{}: Invalid target "{}": '.format( - target_.origin, target_.name) - if os: - if target_.os != 'brillo': - raise ValidationError(validation_message_start + - 'The BDK only supports "brillo" ' - 'as an operating system ' - '(requested "{}").'.format(target_.os)) - if os_downloaded and target_.os_version != util.GetOSVersion(): - # TODO(b/27677026): Validate download status once OSes are - # downloaded like BSPs. Throw NotDownloadedError. - raise ValidationError( - validation_message_start + - 'This version of the BDK only supports ' - 'Brillo version {} (requested {}).'.format( - util.GetOSVersion(), target_.os_version)) - if board: - try: - device = target_.get_device() - except target.Error as e: - raise ValidationError(validation_message_start + - '{}'.format(e)) - if board_downloaded and not device.is_available(): - raise NotDownloadedError( - validation_message_start + - 'Support package for "{0}" is not downloaded. ' - 'Run `bdk bsp download {0}`.'.format(target_.board)) - - if os and board: - # TODO(b/27654613): Check for compatibility. - compatible = True - if not compatible: - raise CompatibilityError( - 'Requested OS version {} and board "{}.{}", ' - 'which are not compatible.'.format(target_.os_version, - target_.board, - target_.board_version)) - - def GetSpecAndTarget(self, args, **kwargs): + def GetSpecAndTarget(self, args): """Creates and verifies the spec and target_ to use. Args: args: parsed arg namespace; spec and target_ are based on args.specification and args.target. - **kwargs: args to pass to ValidateTarget(). Returns: A tuple of (ProjectSpec, Target) objects. @@ -189,8 +117,6 @@ class Command(object): except KeyError as e: raise TargetError(e) - self.ValidateTarget(target_, **kwargs) - return (spec, target_) def SetMetricsClass(self, class_name): diff --git a/cli/lib/cli/clicommand_unittest.py b/cli/lib/cli/clicommand_unittest.py index 9b5bc9b..c529e56 100644 --- a/cli/lib/cli/clicommand_unittest.py +++ b/cli/lib/cli/clicommand_unittest.py @@ -20,96 +20,11 @@ import unittest -from bsp import device_stub from cli import clicommand -from core import util_stub from core import config_stub from metrics import hit_store_stub from metrics import metrics_util from metrics.data_types import hit_stub -from project import target_stub - - -class ValidateTargetTest(unittest.TestCase): - - _BSP = 'devicename' - - def setUp(self): - self.stub_util = util_stub.StubUtil() - - clicommand.util = self.stub_util - - self.dev = device_stub.StubDevice(self._BSP, downloaded=True) - self.command = clicommand.Command() - self.target = target_stub.StubTarget( - name='test_target', os='brillo', - os_version=self.stub_util.GetOSVersion(), board=self._BSP, - board_version=self.dev.version, device=self.dev) - - def test_validate_valid(self): - self.command.ValidateTarget(self.target) - self.command.ValidateTarget(self.target, os=False) - self.command.ValidateTarget(self.target, board=False) - self.command.ValidateTarget(self.target, os=False, board=False) - - def test_validate_invalid_os(self): - self.target.os = 'not_brillo' - with self.assertRaises(clicommand.ValidationError): - self.command.ValidateTarget(self.target) - self.command.ValidateTarget(self.target, os=False) - with self.assertRaises(clicommand.ValidationError): - # Even if we ignore download state, the os still is invalid. - self.command.ValidateTarget(self.target, os_downloaded=False) - with self.assertRaises(clicommand.ValidationError): - self.command.ValidateTarget(self.target, board=False) - self.command.ValidateTarget(self.target, os=False, board=False) - - def test_validate_invalid_bsp(self): - self.target.device_raises = True - with self.assertRaises(clicommand.ValidationError): - self.command.ValidateTarget(self.target) - with self.assertRaises(clicommand.ValidationError): - self.command.ValidateTarget(self.target, os=False) - self.command.ValidateTarget(self.target, board=False) - with self.assertRaises(clicommand.ValidationError): - # Even if we ignore download state, the bsp still doesn't exist. - self.command.ValidateTarget(self.target, board_downloaded=False) - self.command.ValidateTarget(self.target, os=False, board=False) - - def test_validate_os_not_downloaded(self): - self.target.os_version = 'not_a_version' - with self.assertRaises(clicommand.ValidationError): - self.command.ValidateTarget(self.target) - self.command.ValidateTarget(self.target, os_downloaded=False) - # Doesn't matter if os_downloaded is True as long as os is False - self.command.ValidateTarget(self.target, os=False, os_downloaded=True) - with self.assertRaises(clicommand.ValidationError): - self.command.ValidateTarget(self.target, board=False) - self.command.ValidateTarget(self.target, os=False, board=False) - - def test_validate_bsp_not_downloaded(self): - self.dev.downloaded = False - with self.assertRaises(clicommand.ValidationError): - self.command.ValidateTarget(self.target) - with self.assertRaises(clicommand.ValidationError): - self.command.ValidateTarget(self.target, os=False) - self.command.ValidateTarget(self.target, board_downloaded=False) - # Doesn't matter if board_downloaded is True as long as board is False. - self.command.ValidateTarget(self.target, board=False, - board_downloaded=True) - self.command.ValidateTarget(self.target, os=False, board=False) - - def test_validate_bsp_invalid_version(self): - self.target.device_raises = True - with self.assertRaises(clicommand.ValidationError): - self.command.ValidateTarget(self.target) - with self.assertRaises(clicommand.ValidationError): - self.command.ValidateTarget(self.target, os=False) - self.command.ValidateTarget(self.target, board=False) - with self.assertRaises(clicommand.ValidationError): - # Even if we ignore download state, the bsp still doesn't exist. - self.command.ValidateTarget(self.target, board_downloaded=False) - self.command.ValidateTarget(self.target, os=False, board=False) def _keyboard_interrupt_func(_args): diff --git a/cli/lib/commands/bsp/download.py b/cli/lib/commands/bsp/download.py index 75be397..4eeb71e 100644 --- a/cli/lib/commands/bsp/download.py +++ b/cli/lib/commands/bsp/download.py @@ -80,7 +80,7 @@ class Update(clicommand.Command): if device_status <= status.LINKED: # LINKED or INSTALLED print 'Successfully installed all packages for {}.'.format( - device_.name) + device_.full_name) return 0 elif device_status == status.UNRECOGNIZED: print 'The following paths exist but do not link to BSP packages:' @@ -90,7 +90,8 @@ class Update(clicommand.Command): 'removing them and installing again.') return 0 else: - print 'Failed to install some packages for {}.'.format(device_.name) + print 'Failed to install some packages for {}.'.format( + device_.full_name) return 1 def IdentifyTarballs(self, tarballs, device_): diff --git a/cli/lib/commands/bsp/refresh.py b/cli/lib/commands/bsp/refresh.py index 9c39546..e8c0330 100644 --- a/cli/lib/commands/bsp/refresh.py +++ b/cli/lib/commands/bsp/refresh.py @@ -73,7 +73,7 @@ class Refresh(clicommand.Command): print 'Re-installing...' device.install(extract_only, args.accept_licenses, link_os, verbose=True) - print 'Successfully refreshed {}.'.format(device.name) + print 'Successfully refreshed {}.'.format(device.full_name) return 0 def IdentifyTarballs(self, tarballs, device): diff --git a/cli/lib/commands/v2/build/image.py b/cli/lib/commands/v2/build/image.py index 53e21fc..c6e1401 100644 --- a/cli/lib/commands/v2/build/image.py +++ b/cli/lib/commands/v2/build/image.py @@ -38,20 +38,14 @@ class Image(clicommand.Command): image_build.IMAGE_TYPE_ODM)) def Run(self, args): - try: - # Technically we don't need to validate the board; the user could - # have built with it then uninstalled it, and the build is checked - # elsewhere. We do still need the os downloaded, as it contains - # necessary tools. - spec, target = self.GetSpecAndTarget(args, board=False) - except clicommand.Error as error: - print str(error) - return 1 + spec, target = self.GetSpecAndTarget(args) + platform = target.platform + # Needs to be downloaded since we use some of the prebuilt tools. + platform.verify_downloaded() - image_build.AddTargetOsPacks(spec, target) + image_build.AddPlatformOsPacks(spec, platform) - target_dir = os.path.join(spec.config.cache_dir, - '{}.cache'.format(target.name)) + target_dir = spec.config.cache_dir_for_target(target) try: mountpoint = os.path.join(os.path.sep, args.type) image_build.CreateTargetCache(spec, target, target_dir, mountpoint, @@ -61,7 +55,7 @@ class Image(clicommand.Command): return 1 try: - ret = image_build.BuildImage(args.type, target, target_dir, + ret = image_build.BuildImage(args.type, platform, target_dir, spec.config.output_dir) except image_build.Error as e: print 'Error building image for target {} at {}: {}'.format( diff --git a/cli/lib/commands/v2/build/platform.py b/cli/lib/commands/v2/build/platform.py index 9f79352..946a4bd 100644 --- a/cli/lib/commands/v2/build/platform.py +++ b/cli/lib/commands/v2/build/platform.py @@ -38,32 +38,26 @@ class Platform(clicommand.Command): Returns: 0 if successful, an exit code otherwise. """ - try: - _, target = self.GetSpecAndTarget(args) - except clicommand.Error as error: - print str(error) - return 1 + _, target = self.GetSpecAndTarget(args) + platform = target.platform + platform.verify_downloaded() - platform_dir = target.platform_build_cache() try: # Build the platform cache. - ret = build.BuildPlatform(target, platform_dir, args.args) + ret = build.BuildPlatform(platform, args.args) # Obtain and cache SELinux information. # GetBoardComboSepolicyDirs() will either raise an exception or # return a valid value, so no need to update |ret|. - sepolicy_dirs = policy.GetBoardComboSepolicyDirs(target) - policy.SaveSepolicyDirsCache(platform_dir, sepolicy_dirs) - - except build.BuildUtilityError as e: + sepolicy_dirs = policy.GetBoardComboSepolicyDirs(platform) + policy.SaveSepolicyDirsCache(platform.build_cache, sepolicy_dirs) + except (build.Error, policy.Error) as e: print 'Failed to build platform: {}'.format(e) return 1 - except policy.SELinuxCommandError as e: - print 'Failed to build platform: SELinux policy error: {}'.format(e) - return 1 if ret: print 'Failed to build platform: unknown error code {}'.format(ret) else: - print 'Successfully built platform in {}'.format(platform_dir) + print 'Successfully built platform in {}'.format( + platform.build_cache) return ret diff --git a/cli/lib/commands/v2/environment/setup.py b/cli/lib/commands/v2/environment/setup.py index afe693b..0dece94 100644 --- a/cli/lib/commands/v2/environment/setup.py +++ b/cli/lib/commands/v2/environment/setup.py @@ -29,11 +29,11 @@ from environment import sysroot_util from environment import toolchain_util -# Note: this should be kept in sync with project.target.PLATFORM_CACHE_FORMAT +# Note: this should be kept in sync with project.platform.PLATFORM_CACHE_FORMAT SAMPLE_MAKEFILE_TEMPLATE = """\ BDK={bdk_command} -DEVICE={board}.{board_version} -OS={os}.{os_version} +DEVICE={board_namespace} +OS={os_namespace} PLATFORM_CACHE:=$(shell $(BDK) config check platform_cache)/$(OS)/$(DEVICE) SYSROOT=$(PLATFORM_CACHE)/sysroot @@ -75,12 +75,9 @@ class Setup(clicommand.Command): def Run(self, args): """Sets up a sysroot and toolchain.""" - try: - _, target = self.GetSpecAndTarget(args) - except clicommand.Error as error: - print(error) - return 1 - device_ = target.get_device() + _, target = self.GetSpecAndTarget(args) + platform = target.platform + platform.verify_downloaded() # TODO(b/27385334): maybe toolchain/sysroot setup should really be # different commands, with a helper command that invokes both. @@ -88,18 +85,16 @@ class Setup(clicommand.Command): # the necessary flags to use the sysroot into the tools. # Check args. - platform_build_cache = target.platform_build_cache() - product_out = util.GetAndroidProductOut(platform_build_cache, - target.board) - if not os.path.isdir(product_out): + if not os.path.isdir(platform.product_out_cache): print('Could not find platform build output "{}". ' 'You must run\n' ' bdk build platform\n' 'before you set up your build environment.'.format( - product_out)) + platform.product_out_cache)) return 1 - if device_.arch not in sysroot_util.SysrootUtil.DEFAULT_ARCH_INCLUDES: - print('Unknown architecture: {}'.format(device_.arch)) + if (platform.device.arch not in + sysroot_util.SysrootUtil.DEFAULT_ARCH_INCLUDES): + print('Unknown architecture: {}'.format(platform.device.arch)) return 1 if not os.path.isfile(args.pkgconfig_csv): print('No such file: {}'.format(args.pkgconfig_csv)) @@ -111,37 +106,37 @@ class Setup(clicommand.Command): return 1 # Basic values. - shared_libs = os.path.join(product_out, 'obj', 'lib') - static_libs = os.path.join(product_out, 'obj', 'STATIC_LIBRARIES') + shared_libs = os.path.join(platform.product_out_cache, 'obj', 'lib') + static_libs = os.path.join(platform.product_out_cache, 'obj', + 'STATIC_LIBRARIES') # TODO(b/27458309): determine an actual location for this. - board_packages = util.GetOSPath(target.os_version, - 'device', device_.vendor, target.board, - 'developer', 'packages.csv') - sysroot_path = target.platform_cache('sysroot') - toolchain_path = target.platform_cache('toolchain') + board_packages = platform.os_path('device', + platform.device.vendor, + platform.device.name, + 'developer', + 'packages.csv') ret = 0 # Generate the sysroot. - if os.path.exists(sysroot_path): + if os.path.exists(platform.sysroot): print ('Not generating sysroot: ' - 'something already exists at {}.'.format(sysroot_path)) + 'something already exists at {}.'.format(platform.sysroot)) else: sysroot_success = False - brillo_sysroot = sysroot.Sysroot(sysroot_path) - setup_util = sysroot_util.SysrootUtil(brillo_sysroot, - target.os_version) + brillo_sysroot = sysroot.Sysroot(platform.sysroot) + setup_util = sysroot_util.SysrootUtil(brillo_sysroot, platform) # Setup from a base, add libs. try: - setup_util.SetupBaseSysroot(device_.arch, shared_libs, - static_libs) + setup_util.SetupBaseSysroot(platform.device.arch, + shared_libs, static_libs) setup_util.AddSharedLibsFromCSV(shared_libs, args.pkgconfig_csv) # Add board libs, if any - with device_.linked(target.os_version): + with platform.linked(): if os.path.isfile(board_packages): - setup_util.AddSharedLibsFromCSV(shared_libs, - board_packages, - suffix=target.board) + setup_util.AddSharedLibsFromCSV( + shared_libs, board_packages, + suffix=platform.device.name) sysroot_success = True except (IOError, ValueError, device.Error) as e: print('Failed to generate sysroot: {}'.format(e)) @@ -151,28 +146,28 @@ class Setup(clicommand.Command): brillo_sysroot.Destroy() # Generate toolchain. - if os.path.exists(toolchain_path): + if os.path.exists(platform.toolchain): print('Not generating toolchain: ' - 'something already exists at {}.'.format(toolchain_path)) + 'something already exists at {}.'.format(platform.toolchain)) else: toolchain_success = False try: - os.makedirs(toolchain_path) - toolchain_util.GenerateToolchain(target.os_version, host_arch, - device_.arch, toolchain_path) + os.makedirs(platform.toolchain) + toolchain_util.GenerateToolchain(platform, host_arch, + platform.toolchain) toolchain_success = True except toolchain_util.Error as e: print('Failed to generate toolchain: {}'.format(e)) finally: if not toolchain_success: ret = 1 - shutil.rmtree(toolchain_path) + shutil.rmtree(platform.toolchain) if not ret: print('Finished building environment.') print('Sample Makefile:\n\n' + SAMPLE_MAKEFILE_TEMPLATE.format( - bdk_command=util.GetBDKPath('cli/bdk'), os=target.os, - os_version=target.os_version, board=target.board, - board_version=target.board_version)) + bdk_command=util.GetBDKPath('cli', 'bdk'), + os_namespace=platform.os_namespace, + board_namespace=platform.board_namespace)) return ret diff --git a/cli/lib/commands/v2/root/adb.py b/cli/lib/commands/v2/root/adb.py index 857ac5c..c0c7396 100644 --- a/cli/lib/commands/v2/root/adb.py +++ b/cli/lib/commands/v2/root/adb.py @@ -41,15 +41,10 @@ class Adb(clicommand.Command): Returns: 0 if successful, an exit code otherwise. """ - try: - # All we need is the adb binary which is checked below. - _, target = self.GetSpecAndTarget(args, os=False, board=False) - except clicommand.Error as error: - print(str(error)) - return 1 + _, target = self.GetSpecAndTarget(args) + platform = target.platform - adb_tool = tool.HostToolWrapper('adb', target.platform_build_cache(), - target.board) + adb_tool = tool.HostToolWrapper('adb', platform) if not adb_tool.exists(): print('Could not find adb executable at {}. Use `bdk build ' 'platform` to build it.'.format(adb_tool.path())) diff --git a/cli/lib/commands/v2/root/fastboot.py b/cli/lib/commands/v2/root/fastboot.py index 056a4f6..c333b04 100644 --- a/cli/lib/commands/v2/root/fastboot.py +++ b/cli/lib/commands/v2/root/fastboot.py @@ -41,16 +41,10 @@ class Fastboot(clicommand.Command): Returns: 0 if successful, an exit code otherwise. """ - try: - # All we need is the fastboot binary which is checked below. - _, target = self.GetSpecAndTarget(args, os=False, board=False) - except clicommand.Error as error: - print(str(error)) - return 1 + _, target = self.GetSpecAndTarget(args) + platform = target.platform - fastboot_tool = tool.HostToolWrapper('fastboot', - target.platform_build_cache(), - target.board) + fastboot_tool = tool.HostToolWrapper('fastboot', platform) if not fastboot_tool.exists(): print('Could not find fastboot executable at {}. Use `bdk build ' 'platform` to build it.'.format(fastboot_tool.path())) diff --git a/cli/lib/commands/v2/root/flash.py b/cli/lib/commands/v2/root/flash.py index 22e4cfe..e3eed5f 100644 --- a/cli/lib/commands/v2/root/flash.py +++ b/cli/lib/commands/v2/root/flash.py @@ -47,11 +47,9 @@ class Flash(clicommand.Command): Returns: 0 if successful, an exit code otherwise. """ - try: - spec, target = self.GetSpecAndTarget(args) - except clicommand.Error as error: - print str(error) - return 1 + spec, target = self.GetSpecAndTarget(args) + platform = target.platform + platform.verify_downloaded() system_image_dir = spec.config.output_dir @@ -59,11 +57,11 @@ class Flash(clicommand.Command): # put it in fastboot mode if it's not. provision_args = ['-s', args.s] if args.s else [] - provision.provision_device(target, system_image_dir, provision_args) + provision.provision_device(platform, system_image_dir, provision_args) if args.reboot: fastboot_tool = tool.HostToolWrapper( - 'fastboot', target.platform_build_cache()) + 'fastboot', platform) fastboot_tool.run(provision_args + ['reboot']) return 0 diff --git a/cli/lib/commands/v2/root/init.py b/cli/lib/commands/v2/root/init.py index 90ec2c0..d85a1e7 100644 --- a/cli/lib/commands/v2/root/init.py +++ b/cli/lib/commands/v2/root/init.py @@ -23,7 +23,7 @@ import os from cli import clicommand from core import util -from project import target +from project import platform IMAGE_SPEC_TEMPLATE = """\ @@ -89,19 +89,16 @@ class Init(clicommand.Command): def Run(self, args): filename = os.path.join(os.getcwd(), util.PROJECT_SPEC_FILENAME) - # Spoof up a target to take advantage of Command.ValidateTarget. - spoof_target = target.Target(args.project, 'brillo', args.os_version, - args.board, args.board_version) - spoof_target.origin.source_file = 'attempted project spec' - + # Create a platform object to check if requested OS and BSP + # are compatible (will raise if not), and then warn if the + # components need to be downloaded. + platform_ = platform.Platform('brillo', args.os_version, + args.board, args.board_version, + platform.BUILD_TYPE_USERDEBUG) try: - self.ValidateTarget(spoof_target) - except clicommand.NotDownloadedError as e: + platform_.verify_downloaded() + except platform.NotDownloadedError as e: print('Warning: {}'.format(e)) - except clicommand.ValidationError as e: - print(e) - print('Project spec not written. Exiting.') - return 1 sample = IMAGE_SPEC_TEMPLATE.format( board=args.board, board_version=args.board_version, diff --git a/cli/lib/commands/v2/root/sync.py b/cli/lib/commands/v2/root/sync.py index 25ed191..38bff47 100644 --- a/cli/lib/commands/v2/root/sync.py +++ b/cli/lib/commands/v2/root/sync.py @@ -67,30 +67,25 @@ class Sync(clicommand.Command): Returns: 0 if successful, an exit code otherwise. """ - try: - # Don't need board as long as we have the build output, which is - # checked in image_build.BuildImage(). - spec, target = self.GetSpecAndTarget(args, board=False) - except clicommand.Error as error: - print(str(error)) - return 1 + spec, target = self.GetSpecAndTarget(args) + platform = target.platform + platform.verify_downloaded() - image_build.AddTargetOsPacks(spec, target) + image_build.AddPlatformOsPacks(spec, platform) - target_dir = os.path.join(spec.config.cache_dir, - '{}.cache'.format(target.name)) + target_dir = spec.config.cache_dir_for_target(target) mountpoint = os.path.join(os.path.sep, _SYNC_DIR) try: image_build.CreateTargetCache(spec, target, target_dir, - mountpoint=mountpoint, update=True) + mountpoint=mountpoint, + update=True) except dependency.Error as e: print('Dependencies unmet for target {}: {}'.format(target.name, e)) return 1 # TODO(dpursell): we might want to start encapsulating adb behind # a Python class instead of calling it directly (http://b/27854198). - platform_dir = target.platform_build_cache() - adb_tool = tool.HostToolWrapper('adb', platform_dir, target.board) + adb_tool = tool.HostToolWrapper('adb', platform) if not adb_tool.exists(): print('Could not find adb executable at {}. Use ' '`bdk build platform` to build it.'.format(adb_tool.path())) diff --git a/cli/lib/core/build.py b/cli/lib/core/build.py index c8f7b42..1db3cdb 100644 --- a/cli/lib/core/build.py +++ b/cli/lib/core/build.py @@ -22,7 +22,6 @@ import os import subprocess from core import tool -from core import util import error @@ -30,19 +29,11 @@ class Error(error.Error): """General build failure.""" -class BuildTypeError(Error): - """Raised when an invalid build type is given.""" - - class BuildUtilityError(Error): """Raised when a build utility fails or is missing.""" -BUILD_TYPES = ('user', 'userdebug', 'eng') - - -def _GetBuildScript(os_version, bsp, build_type, out_dir=None, - extra_make_args=None): +def _GetBuildScript(platform, extra_make_args=None): """Creates a shell script to run a platform build. The script is small but uses several variables, which is why it's @@ -51,10 +42,7 @@ def _GetBuildScript(os_version, bsp, build_type, out_dir=None, own file. Args: - os_version: version of the Brillo OS to build. - bsp: name of the BSP to build. - build_type: one of BUILD_TYPES. - out_dir: directory for build output or None to build in-tree. + platform: Platform to build. extra_make_args: additional make args as a list of strings or None. Returns: @@ -62,48 +50,36 @@ def _GetBuildScript(os_version, bsp, build_type, out_dir=None, """ make_args = [] - if out_dir is not None: - # Android build treats relative paths as relative to the source root, - # use abspath() to put it relative to the caller's current directory - # instead. - make_args.append('OUT_DIR="{}"'.format(os.path.abspath(out_dir))) + make_args.append('OUT_DIR="{}"'.format(platform.build_cache)) if extra_make_args: make_args.extend(extra_make_args) - cmds = ['cd "{}"'.format(util.GetOSPath(os_version)), + cmds = ['cd "{}"'.format(platform.os_path()), '. build/envsetup.sh', - 'lunch "{}-{}"'.format(bsp, build_type), + 'lunch "{}-{}"'.format(platform.device.name, platform.build_type), 'm {}'.format(' '.join(make_args))] # Link all commands with && to exit immediately if one fails. return ' && '.join(cmds) -def BuildPlatform(target, out_dir=None, extra_make_args=None): +def BuildPlatform(platform, extra_make_args=None): """Builds the Brillo platform. Caller should validate target OS and Board before calling. Args: - target: core.parsing.target.Target to build. - out_dir: directory for build output or None to build in-tree. + platform: Platform to build. extra_make_args: additional make args as a list of strings or None. Returns: The build exit code. Raises: - BuildTypeError: target build_type is invalid. BuildUtilityError: subprocess invocation fails. """ - if target.build_type not in BUILD_TYPES: - raise BuildTypeError( - 'Invalid build type: {} (must be one of {})'.format( - target.build_type, BUILD_TYPES)) - - build_script = _GetBuildScript(target.os_version, target.board, - target.build_type, out_dir, extra_make_args) + build_script = _GetBuildScript(platform, extra_make_args) # Set up the build environment. We strip out most environment variables, # but some values (e.g. USER, TERM) are useful or necessary for the build. @@ -111,11 +87,11 @@ def BuildPlatform(target, out_dir=None, extra_make_args=None): if var in os.environ} # Make sure the output directory exists so we can log to it. - if not os.path.isdir(out_dir): - os.makedirs(out_dir) - log_file_path = os.path.join(out_dir, 'bdk_last_build.log') + if not os.path.isdir(platform.build_cache): + os.makedirs(platform.build_cache) + log_file_path = os.path.join(platform.build_cache, 'bdk_last_build.log') - with target.get_device().linked(target.os_version): + with platform.linked(): # The build script uses bash features like && to link multiple commands # and to source scripts, so we invoke via bash. try: diff --git a/cli/lib/core/build_unittest.py b/cli/lib/core/build_unittest.py index 5fe4963..b18bab0 100644 --- a/cli/lib/core/build_unittest.py +++ b/cli/lib/core/build_unittest.py @@ -23,7 +23,7 @@ import unittest from bsp import device_stub from core import build from core import util_stub -from project import target_stub +from project import platform_stub from test import stubs @@ -49,21 +49,20 @@ class BuildPlatformTest(unittest.TestCase): self.device = device_stub.StubDevice( should_link_version=self._OS_VERSION) - self.target = target_stub.StubTarget( + self.platform = platform_stub.StubPlatform( board=self._BSP, build_type=self._BUILD_TYPE, - os_version=self._OS_VERSION, + os_version=self._OS_VERSION, build_cache=self._OUT_DIR, device=self.device) def test_success(self): make_command = self.stub_subprocess.AddCommand() tee_command = self.stub_subprocess.AddCommand() - self.assertEqual(0, build.BuildPlatform(self.target, self._OUT_DIR)) + self.assertEqual(0, build.BuildPlatform(self.platform)) # pylint: disable=protected-access make_command.AssertCallWas( - build._GetBuildScript(self.target.os_version, - self._BSP, self._BUILD_TYPE, self._OUT_DIR), + build._GetBuildScript(self.platform), stdout=self.stub_subprocess.PIPE, stderr=self.stub_subprocess.STDOUT, executable='bash', shell=True, env={}) @@ -77,14 +76,12 @@ class BuildPlatformTest(unittest.TestCase): self.stub_subprocess.AddCommand(['tee', self._LOG_FILE]) self.assertEqual(0, build.BuildPlatform( - self.target, self._OUT_DIR, - extra_make_args=extra_make_args)) + self.platform, extra_make_args=extra_make_args)) # pylint: disable=protected-access make_command.AssertCallWas( - build._GetBuildScript(self.target.os_version, - self._BSP, self._BUILD_TYPE, self._OUT_DIR, - extra_make_args=extra_make_args), + build._GetBuildScript( + self.platform, extra_make_args=extra_make_args), stdout=self.stub_subprocess.PIPE, stderr=self.stub_subprocess.STDOUT, executable='bash', shell=True, env={}) @@ -97,7 +94,7 @@ class BuildPlatformTest(unittest.TestCase): tee_command = self.stub_subprocess.AddCommand(['tee', self._LOG_FILE]) self.assertEqual(0, build.BuildPlatform( - self.target, self._OUT_DIR)) + self.platform, self._OUT_DIR)) # Make sure USER passes through but PATH does not. make_command.AssertCallContained(env={'USER': 'test_user'}) @@ -109,12 +106,7 @@ class BuildPlatformTest(unittest.TestCase): self.stub_subprocess.AddCommand(['tee', self._LOG_FILE]) self.assertEqual(1, build.BuildPlatform( - self.target, self._OUT_DIR)) - - def test_invalid_build_type(self): - self.target.build_type = 'bad_build_type' - with self.assertRaises(build.BuildTypeError): - build.BuildPlatform(self.target, self._OUT_DIR) + self.platform, self._OUT_DIR)) @staticmethod def _raise_oserror(): @@ -125,7 +117,7 @@ class BuildPlatformTest(unittest.TestCase): # Expect a make command. self.stub_subprocess.AddCommand(init_side_effect=self._raise_oserror) with self.assertRaises(build.BuildUtilityError): - build.BuildPlatform(self.target, self._OUT_DIR) + build.BuildPlatform(self.platform, self._OUT_DIR) def test_missing_tee(self): """Tests passing additional make args from the user.""" @@ -134,4 +126,4 @@ class BuildPlatformTest(unittest.TestCase): self.stub_subprocess.AddCommand(['tee', self._LOG_FILE], init_side_effect=self._raise_oserror) with self.assertRaises(build.BuildUtilityError): - build.BuildPlatform(self.target, self._OUT_DIR) + build.BuildPlatform(self.platform, self._OUT_DIR) diff --git a/cli/lib/core/image_build.py b/cli/lib/core/image_build.py index 462376f..4e721b6 100644 --- a/cli/lib/core/image_build.py +++ b/cli/lib/core/image_build.py @@ -93,17 +93,16 @@ def _CheckBuildImagePaths(cache_dir, output_dir, product_out, host_tools, os.makedirs(output_dir) -def AddTargetOsPacks(spec, target): - """Adds target OS packs to the spec. +def AddPlatformOsPacks(spec, platform): + """Adds platform OS packs to the spec. Args: spec: project.project_spec.ProjectSpec to add the OS packs to. - target: project.target.Target object to get OS info from. + platform: project.platform.Platform object to get OS info from. """ - platform_dir = target.platform_build_cache() - product_out = util.GetAndroidProductOut(platform_dir, target.board) + product_out = platform.product_out_cache os_packs = packs.Packs() - os_packs.namespace = target.os_namespace + os_packs.namespace = platform.os_namespace os_core = pack.Pack(os_packs.namespace, 'generated_system') os_core.add_provides('os.core') base_copy = pack.Copy(os_core) @@ -324,14 +323,14 @@ def _CreateBuildProps(build_root, product_out, image_type, info_file): build_root.WriteFile(info_file, 'mount_point={}'.format(image_type)) -def BuildImage(image_type, target, cache_dir, output_dir): +def BuildImage(image_type, platform, cache_dir, output_dir): """Builds the image specified by image_type. Caller should validate target OS before calling. Args: image_type: The type of the image. One of IMAGE_TYPES. - target: project.target.Target to build an image of. + platform: project.platform.Platform to build an image of. cache_dir: Directory to build the image in. This path may be prepared by prior callers. E.g., CreateTargetCache(). output_dir: Directory to place built .img file in. @@ -349,12 +348,10 @@ def BuildImage(image_type, target, cache_dir, output_dir): raise ImageTypeError('image_type must be one of {}: {}'.format( IMAGE_TYPES, image_type)) # Set some useful variables. - build_tools = util.GetOSPath(target.os_version, - 'build', 'tools', 'releasetools') - platform_out = target.platform_build_cache() - product_out = util.GetAndroidProductOut(platform_out, target.board) + build_tools = platform.os_path('build', 'tools', 'releasetools') + product_out = platform.product_out_cache host_arch = util.GetHostArch() - host_tools = os.path.join(platform_out, 'host', host_arch, 'bin') + host_tools = os.path.join(platform.build_cache, 'host', host_arch, 'bin') output_file = os.path.join(output_dir, '{}.img'.format(image_type)) ret = 1 @@ -369,9 +366,9 @@ def BuildImage(image_type, target, cache_dir, output_dir): build_root = sysroot.Sysroot(cache_dir, copy_newer_only=True) # Build 'filecontexts.bin' and 'sepolicy' SELinux files. - with target.get_device().linked(target.os_version): - policy.BuildSepolicy(target, platform_out, cache_dir) - policy.BuildFileContexts(target, platform_out, cache_dir) + with platform.linked(): + policy.BuildSepolicy(platform, cache_dir) + policy.BuildFileContexts(platform, cache_dir) _CreateBuildProps(build_root, product_out, image_type, 'image_info.txt') diff --git a/cli/lib/core/image_build_unittest.py b/cli/lib/core/image_build_unittest.py index 7d4b312..09bdb8d 100644 --- a/cli/lib/core/image_build_unittest.py +++ b/cli/lib/core/image_build_unittest.py @@ -28,6 +28,7 @@ from environment import sysroot_stub from project import dependency from project import pack from project import packmap_stub +from project import platform_stub from project import project_spec_stub from project import target_stub from test import stubs @@ -40,7 +41,9 @@ class TestData(object): _BUILD_TYPE = 'user' _BRILLO_VERSION = '9.9' _CACHE_DIR = '/product/artifacts' + _OS_DIR = '/bing/bong' _PLATFORM_DIR = '/foo/bar' + _PRODUCT_DIR = '/bip/boop' _IMAGE_OUT_DIR = '/baz/pop' @@ -73,11 +76,12 @@ class BuildImageBase(TestData): self.device = device_stub.StubDevice( should_link_version=self._BRILLO_VERSION) - self.target = target_stub.StubTarget( - board=self._BSP, os_version=self._BRILLO_VERSION, - device=self.device) - self.target.platform_dir = self._PLATFORM_DIR - + self.platform = platform_stub.StubPlatform( + os_version=self._BRILLO_VERSION, + device=self.device, build_cache=self._PLATFORM_DIR, + product_out_cache=self._PRODUCT_DIR, + os_dir=self._OS_DIR) + self.target = target_stub.StubTarget(platform=self.platform) self.product_out_dir = None self.image_type = None @@ -95,17 +99,15 @@ class BuildImageBase(TestData): # Must have copy dir, product out dir, image out dir, # host tools, and image_build tools. - platform_dir = self.target.platform_build_cache() - product_out_dir = self.stub_util.GetAndroidProductOut( - platform_dir, self.target.board) + platform_dir = self.platform.build_cache + product_out_dir = self.platform.product_out_cache self.product_out_dir = product_out_dir self.stub_os.path.should_be_dir = [ product_out_dir, self._IMAGE_OUT_DIR, self._CACHE_DIR, - self.target.platform_build_cache('host', - self.stub_util.GetHostArch(), - 'bin'), - self.stub_util.GetOSPath(self._BRILLO_VERSION, - 'build', 'tools', 'releasetools')] + self.stub_os.path.join(platform_dir, 'host', + self.stub_util.GetHostArch(), + 'bin'), + self.platform.os_path('build', 'tools', 'releasetools')] self.stub_os.path.should_exist += copy.deepcopy( self.stub_os.path.should_be_dir) @@ -141,7 +143,7 @@ class BaseTests(object): command = self.stub_subprocess.AddCommand() self.assertEqual( 0, - image_build.BuildImage(self.image_type, self.target, + image_build.BuildImage(self.image_type, self.platform, self._CACHE_DIR, self._IMAGE_OUT_DIR)) command.AssertCallWas(expected_call) @@ -152,7 +154,7 @@ class BaseTests(object): self.SetupForImageImage_Build() self.assertEqual( 1, - image_build.BuildImage(self.image_type, self.target, + image_build.BuildImage(self.image_type, self.platform, self._CACHE_DIR, self._IMAGE_OUT_DIR)) def test_image_missing_paths(self): @@ -167,13 +169,13 @@ class BaseTests(object): if directory != self._IMAGE_OUT_DIR: with self.assertRaises(image_build.PathError): image_build.BuildImage( - self.image_type, self.target, + self.image_type, self.platform, self._CACHE_DIR, self._IMAGE_OUT_DIR) else: self.stub_os.should_makedirs.append(self._IMAGE_OUT_DIR) self.assertEqual( 0, - image_build.BuildImage(self.image_type, self.target, + image_build.BuildImage(self.image_type, self.platform, self._CACHE_DIR, self._IMAGE_OUT_DIR)) # Put it back in. @@ -186,7 +188,7 @@ class BaseTests(object): # Shouldn't reach the linking point. self.device.should_link_version = None with self.assertRaises(self.stub_util.HostUnsupportedArchError): - image_build.BuildImage(self.image_type, self.target, + image_build.BuildImage(self.image_type, self.platform, self._CACHE_DIR, self._IMAGE_OUT_DIR) @@ -280,7 +282,7 @@ class CreateTargetCacheTest(TestData, unittest.TestCase): def test_dependency_error(self): target = target_stub.StubTarget( - board=self._BSP, submap_raises=dependency.Error('dep error')) + submap_raises=dependency.Error('dep error')) with self.assertRaises(dependency.Error): image_build.CreateTargetCache(self.spec, target, self.cache_dir) @@ -288,9 +290,7 @@ class CreateTargetCacheTest(TestData, unittest.TestCase): p = pack.Pack('brillo.{}'.format(self._BRILLO_VERSION), 'some_os_stuff') simple_map = packmap_stub.StubPackMap( destinations={'/system/bin/servicemanager': [pack.Copy(p)]}) - target = target_stub.StubTarget( - os='brillo', os_version=self._BRILLO_VERSION, board=self._BSP, - submaps=[simple_map]) + target = target_stub.StubTarget(submaps=[simple_map]) # sysroot stub will not be touched because the OS will be under /system. image_build.CreateTargetCache(self.spec, target, self.cache_dir, mountpoint='/odm') @@ -303,9 +303,7 @@ class CreateTargetCacheTest(TestData, unittest.TestCase): cpy.override_build = False simple_map = packmap_stub.StubPackMap( destinations={cpy.dst: [cpy]}) - target = target_stub.StubTarget( - os='brillo', os_version=self._BRILLO_VERSION, board=self._BSP, - submaps=[simple_map]) + target = target_stub.StubTarget(submaps=[simple_map]) self.stub_sysroot_generator.should_makedirs = [ self.stub_os.path.dirname(cpy.dst)[1:]] self.stub_sysroot_generator.should_add_file = [(cpy.src, @@ -390,9 +388,7 @@ class CreateTargetCacheTest(TestData, unittest.TestCase): copies = p.copies + [self.copy] simple_map = packmap_stub.StubPackMap( destinations={copy.dst: [copy] for copy in copies}) - target = target_stub.StubTarget( - os='brillo', os_version=self._BRILLO_VERSION, board=self._BSP, - submaps=[simple_map]) + target = target_stub.StubTarget(submaps=[simple_map]) self.stub_sysroot_generator.should_makedirs = [ self.stub_os.path.dirname(copy_servicemanager.dst)[1:], self.stub_os.path.dirname(copy_fs_config_files.dst)[1:]] diff --git a/cli/lib/core/provision.py b/cli/lib/core/provision.py index 0261abc..b4ce7bd 100644 --- a/cli/lib/core/provision.py +++ b/cli/lib/core/provision.py @@ -98,11 +98,11 @@ class _ProvisionDir(object): shutil.rmtree(self.temp_dir, ignore_errors=True) -def _get_provision_tool(target): +def _get_provision_tool(platform): """Creates a ProvisionDeviceTool with proper settings. Args: - target: The Target to get the provision tool for. + platform: The Platform to get the provision tool for. Returns: The created ProvisionDeviceTool. @@ -112,10 +112,9 @@ def _get_provision_tool(target): """ # The provision-device script needs fastboot to be on PATH, so we create # a fastboot tool to add it's parent directory to the provision tool PATH. - fastboot_tool = tool.HostToolWrapper('fastboot', - target.platform_build_cache()) + fastboot_tool = tool.HostToolWrapper('fastboot', platform) provision_tool = tool.ProvisionDeviceTool( - target, env={'PATH': os.path.dirname(fastboot_tool.path())}) + platform, env={'PATH': os.path.dirname(fastboot_tool.path())}) for t in (fastboot_tool, provision_tool): if not t.exists(): @@ -126,14 +125,14 @@ def _get_provision_tool(target): return provision_tool -def provision_device(target, system_image_dir=None, provision_args=None): +def provision_device(platform, system_image_dir=None, provision_args=None): """Provisions the attached device using the `provision-device` script. Requires that the platform has been built so that the image files, fastboot, and provision-device all exist in platform_build_out. Args: - target: the project Target object. + platform: the Platform to provision. system_image_dir: directory containing the custom system.img to flash. If None or the system.img file does not exist, uses the default system.img from platform_build_out instead. @@ -144,7 +143,7 @@ def provision_device(target, system_image_dir=None, provision_args=None): core.util.OSVersionError: the target requests an invalid os version. core.tool.Error: (or a subclass) a problem occurs running the tool. """ - provision_tool = _get_provision_tool(target) + provision_tool = _get_provision_tool(platform) # Combine the platform ANDROID_PRODUCT_OUT directory with our system.img. with _ProvisionDir(provision_tool.environment['ANDROID_PRODUCT_OUT'], diff --git a/cli/lib/core/provision_unittest.py b/cli/lib/core/provision_unittest.py index eb4bba4..4ed65aa 100644 --- a/cli/lib/core/provision_unittest.py +++ b/cli/lib/core/provision_unittest.py @@ -26,7 +26,7 @@ from core import tool from core import util from core import util_stub import error -from project import target_stub +from project import platform_stub from test import stubs @@ -38,12 +38,12 @@ class ProvisionDeviceTest(unittest.TestCase): _CUSTOM_SYSTEM_IMAGE_PATH = os.path.join(_CUSTOM_SYSTEM_IMAGE_DIR, 'system.img') _HOST_ARCH = 'foo_arch' + _OS_DIR = '/os/tree' _OS_VERSION = '55.55' - _PLATFORM_OUT = '/platform' _BUILD_TYPE = 'userdebug' # Necessary paths derived from our constants. - _PLATFORM_BUILD_OUT = os.path.join(_PLATFORM_OUT, _BUILD_TYPE) + _PLATFORM_BUILD_OUT = os.path.join('/platform', _BUILD_TYPE) _PRODUCT_BUILD_OUT = util.GetAndroidProductOut(_PLATFORM_BUILD_OUT, _BSP) _PROVISION_DEVICE_PATH = os.path.join(_PRODUCT_BUILD_OUT, 'provision-device') @@ -71,11 +71,10 @@ class ProvisionDeviceTest(unittest.TestCase): provision.tool.subprocess = self.stub_subprocess provision.tool.util = self.stub_util - self.device = device_stub.StubDevice() - self.target = target_stub.StubTarget( + self.platform = platform_stub.StubPlatform( board=self._BSP, os_version=self._OS_VERSION, - device=self.device, - platform_dir=self._PLATFORM_OUT, build_type=self._BUILD_TYPE) + build_cache=self._PLATFORM_BUILD_OUT, + build_type=self._BUILD_TYPE, os_dir=self._OS_DIR) # Give more obvious names to some commonly used variables. self.provision_temp_dir = self.stub_tempfile.temp_dir @@ -98,14 +97,14 @@ class ProvisionDeviceTest(unittest.TestCase): self._PROVISION_DEVICE_PATH, os.path.join(self.provision_temp_dir, 'provision-device'))) self.stub_os.should_makedirs.append(self.provision_temp_dir) - self.device.should_link_version = self._OS_VERSION + self.platform.device.should_link_version = self._OS_VERSION def test_call(self): """Tests a successful provision-device call.""" self.prepare_os_files() command = self.stub_subprocess.AddCommand() - provision.provision_device(self.target) + provision.provision_device(self.platform) # provision-device environment must have: # * PATH contain the fastboot executable. # * ANDROID_BUILD_TOP point to the OS source root. @@ -114,8 +113,7 @@ class ProvisionDeviceTest(unittest.TestCase): [self._PROVISION_DEVICE_PATH], shell=False, cwd=None, stdout=None, stderr=None, env={'PATH': self.stub_os.path.dirname(self._FASTBOOT_PATH), - 'ANDROID_BUILD_TOP': self.stub_util.GetOSPath( - self.target.os_version), + 'ANDROID_BUILD_TOP': self._OS_DIR, 'ANDROID_HOST_OUT': self._HOST_BUILD_OUT, 'ANDROID_PRODUCT_OUT': self.provision_temp_dir}) @@ -125,7 +123,7 @@ class ProvisionDeviceTest(unittest.TestCase): self.stub_os.environ['PATH'] = '/foo/bar:/baz' command = self.stub_subprocess.AddCommand() - provision.provision_device(self.target) + provision.provision_device(self.platform) self.assertEqual( self.stub_os.path.dirname(self._FASTBOOT_PATH) + ':/foo/bar:/baz', command.GetCallArgs()[1]['env']['PATH']) @@ -156,7 +154,7 @@ class ProvisionDeviceTest(unittest.TestCase): # up. self.stub_subprocess.AddCommand(side_effect=check_symlinks) - provision.provision_device(self.target) + provision.provision_device(self.platform) def test_custom_system_image(self): """Tests that a custom system.img is used if provided.""" @@ -174,7 +172,7 @@ class ProvisionDeviceTest(unittest.TestCase): self.stub_subprocess.AddCommand(side_effect=check_custom_image_link) provision.provision_device( - self.target, system_image_dir=self._CUSTOM_SYSTEM_IMAGE_DIR) + self.platform, system_image_dir=self._CUSTOM_SYSTEM_IMAGE_DIR) def test_custom_system_image_missing(self): """Tests specifying a nonexistent custom system.img.""" @@ -194,7 +192,7 @@ class ProvisionDeviceTest(unittest.TestCase): self.stub_subprocess.AddCommand(side_effect=check_default_image_link) provision.provision_device( - self.target, system_image_dir=self._CUSTOM_SYSTEM_IMAGE_DIR) + self.platform, system_image_dir=self._CUSTOM_SYSTEM_IMAGE_DIR) def test_provision_exit_code(self): """Tests non-zero exit codes are propagated.""" @@ -204,7 +202,7 @@ class ProvisionDeviceTest(unittest.TestCase): self.stub_subprocess.AddCommand(ret_code=errno) with self.assertRaises(tool.Error) as e: - provision.provision_device(self.target) + provision.provision_device(self.platform) self.assertEqual(errno, e.exception.errno) def test_fastboot_missing(self): @@ -214,7 +212,7 @@ class ProvisionDeviceTest(unittest.TestCase): self.stub_subprocess.AddCommand() with self.assertRaises(provision.MissingBuildError): - provision.provision_device(self.target) + provision.provision_device(self.platform) def test_provision_device_missing(self): """Tests failure when provision-device is missing.""" @@ -223,4 +221,4 @@ class ProvisionDeviceTest(unittest.TestCase): self.stub_subprocess.AddCommand() with self.assertRaises(provision.MissingBuildError): - provision.provision_device(self.target) + provision.provision_device(self.platform) diff --git a/cli/lib/core/tool.py b/cli/lib/core/tool.py index 575236c..10233a4 100644 --- a/cli/lib/core/tool.py +++ b/cli/lib/core/tool.py @@ -184,19 +184,19 @@ class ToolWrapper(object): class HostToolWrapper(ToolWrapper): """Wraps a tool from out/host/<arch>/bin/.""" - def __init__(self, path, build_out, bsp=None, env=None): + def __init__(self, path, platform, env=None): """Initializes a HostToolWrapper. Args: - path: tool path relative to <build_top>/out/host/<arch>/bin/. - build_out: root of the build output folder where the tool lives. - bsp: the BSP name. Optional, but should be set for any tool that - uses ANDROID_PRODUCT_OUT (e.g. fastboot and adb). + path: tool path relative to <build_out>/host/<arch>/bin/. + platform: the platform the tool is associated with. env: a dictionary of additional environmental variables to set. """ # Initialize path to '' at first so we can use ANDROID_HOST_OUT. super(HostToolWrapper, self).__init__('', env=env) - self.set_android_environment(build_out=build_out, bsp=bsp) + self.set_android_environment(source_top=platform.os_path(), + build_out=platform.build_cache, + bsp=platform.device.name) self._tool_path = os.path.join(self.environment['ANDROID_HOST_OUT'], 'bin', path) @@ -204,11 +204,11 @@ class HostToolWrapper(ToolWrapper): class HostToolRunner(object): """Serves as a HostToolWrapper factory.""" - def __init__(self, build_out): - self._build_out = build_out + def __init__(self, platform): + self._platform = platform def run(self, path, args): - host_tool = HostToolWrapper(path, self._build_out) + host_tool = HostToolWrapper(path, self._platform) return host_tool.run(args) @@ -244,29 +244,29 @@ class ProvisionDeviceTool(ToolWrapper): they will have to call provision-device manually. """ - def __init__(self, target, env=None): + def __init__(self, platform, env=None): """Initializes a ProvisionDeviceTool. Args: - target: The Target to provision. + platform: The Platform to provision. env: a dictionary of additional environmental variables to set. """ # Initialize path to '' at first so we can use ANDROID_PRODUCT_OUT. super(ProvisionDeviceTool, self).__init__('', env=env) self.set_android_environment( - source_top=util.GetOSPath(target.os_version), - build_out=target.platform_build_cache(), - bsp=target.board) + source_top=platform.os_path(), + build_out=platform.build_cache, + bsp=platform.device.name) self._tool_path = os.path.join(self.environment['ANDROID_PRODUCT_OUT'], 'provision-device') - self._target = target + self._platform = platform # provision-device is a shell script, so it needs to know PATH in order # to find utilities. self.add_caller_env_path() def run(self, arg_array=None, piped=False): - with self._target.get_device().linked(self._target.os_version): + with self._platform.linked(): super(ProvisionDeviceTool, self).run(arg_array, piped=piped) diff --git a/cli/lib/core/tool_unittest.py b/cli/lib/core/tool_unittest.py index db67412..3cf2f37 100644 --- a/cli/lib/core/tool_unittest.py +++ b/cli/lib/core/tool_unittest.py @@ -23,7 +23,7 @@ from bsp import device_stub from core import config from core import tool from core import util_stub -from project import target_stub +from project import platform_stub from test import stubs @@ -39,6 +39,11 @@ class ToolWrapperTest(unittest.TestCase): tool.util = self.stub_util tool.subprocess = self.stub_subprocess + self.platform = platform_stub.StubPlatform( + os_version='12.34', os_dir='/source', board='board_name', + device=device_stub.StubDevice(should_link_version='12.34'), + build_cache='/build/out') + def test_run(self): """Tests a basic tool run.""" t = tool.ToolWrapper('/my/tool') @@ -150,7 +155,7 @@ class ToolWrapperTest(unittest.TestCase): def test_host_tool_wrapper(self): """Tests HostToolWrapper construction.""" self.stub_util.arch = 'test_arch' - t = tool.HostToolWrapper('tool', '/build/out') + t = tool.HostToolWrapper('tool', self.platform) self.stub_subprocess.AddCommand(['/build/out/host/test_arch/bin/tool']) t.run() @@ -166,12 +171,7 @@ class ToolWrapperTest(unittest.TestCase): """Tests ProvisionDeviceTool construction.""" self.stub_util.arch = 'test_arch' self.stub_util.os_version = '12.34' - self.stub_util.os_path = '/source' - target = target_stub.StubTarget( - os_version='12.34', board='board_name', build_type='out', - device=device_stub.StubDevice(should_link_version='12.34'), - platform_dir='/build') - t = tool.ProvisionDeviceTool(target) + t = tool.ProvisionDeviceTool(self.platform) command = self.stub_subprocess.AddCommand() t.run() diff --git a/cli/lib/core/util.py b/cli/lib/core/util.py index 4b01fe7..3e8349b 100644 --- a/cli/lib/core/util.py +++ b/cli/lib/core/util.py @@ -30,6 +30,7 @@ class Error(error.Error): class HostUnsupportedArchError(Error): """Raised when run on an unsupported host architecture.""" + description = 'The BDK only supports Linux machines.' class PathError(Error): @@ -71,7 +72,7 @@ def GetBDKVersion(): vars()['bdk_version'] = version return version - +# TODO(b/28527859): replace via Platform. def GetOSVersion(): """Find the OS version""" # TODO(b/27653682): Separate versions for OS & BDK. @@ -80,7 +81,7 @@ def GetOSVersion(): # versions. return GetBDKVersion() - +# TODO(b/28527859): move into Platform. def GetOSPath(os_version, *relpath_args): """Find a path relative to the base of an OS. @@ -96,7 +97,7 @@ def GetOSPath(os_version, *relpath_args): # so we can figure out where the OS is based on that. return GetBDKPath('..', '..', *relpath_args) - +# TODO(b/28527859): move into Platform. def DEPRECATED_GetDefaultOSPath(*relpath): """DEPRECATED - use GetOSPath. @@ -176,5 +177,5 @@ def GetHostArch(): HostError: host architecture is not supported. """ if os.uname()[0].lower() != 'linux': - raise HostUnsupportedArchError('The BDK only supports Linux machines.') + raise HostUnsupportedArchError() return 'linux-x86' diff --git a/cli/lib/environment/sysroot_util.py b/cli/lib/environment/sysroot_util.py index c599ffb..9178a8a 100644 --- a/cli/lib/environment/sysroot_util.py +++ b/cli/lib/environment/sysroot_util.py @@ -20,8 +20,6 @@ import os import shutil -from core import util - def _FilterHeaders(filename): return filename.endswith('.h') @@ -102,9 +100,9 @@ class SysrootUtil(object): 'Libs: -L${{libdir}} -l{3}\n' 'Cflags: -I${{includedir}}\n') - def __init__(self, sysroot, os_version): + def __init__(self, sysroot, platform): self.sysroot = sysroot - self.os_version = os_version + self.platform = platform def _FormatForCopy(self, src, dest): """Helper to account for special formatting signals. @@ -123,7 +121,7 @@ class SysrootUtil(object): (result_src, result_dest), src and dest transformed and ready to be passed into sysroot.AddDir. """ - result_src = util.GetOSPath(self.os_version, src) + result_src = self.platform.os_path(src) result_dest = dest if src.endswith('/..'): basename = os.path.basename(src[:-3]) @@ -202,7 +200,8 @@ class SysrootUtil(object): # Write the .pc file. try: - self._AddPkgConfigFile(suffixed_name, deps, util.GetOSVersion()) + self._AddPkgConfigFile(suffixed_name, deps, + self.platform.os_version) except IOError as e: errors.append('.pc file: {}'.format(e)) diff --git a/cli/lib/environment/sysroot_util_unittest.py b/cli/lib/environment/sysroot_util_unittest.py index 3139c27..46ebd15 100644 --- a/cli/lib/environment/sysroot_util_unittest.py +++ b/cli/lib/environment/sysroot_util_unittest.py @@ -19,27 +19,29 @@ import unittest -from core import util_stub from environment import sysroot_stub from environment import sysroot_util +from project import platform_stub from test import stubs class SysrootUtilTest(unittest.TestCase): + + OS_VERSION = '65.43' + def setUp(self): self.stub_os = stubs.StubOs() self.stub_open = stubs.StubOpen(self.stub_os) self.stub_shutil = stubs.StubShutil(self.stub_os) - self.stub_util = util_stub.StubUtil() sysroot_util.os = self.stub_os sysroot_util.open = self.stub_open.open sysroot_util.shutil = self.stub_shutil - sysroot_util.util = self.stub_util self.sysroot = sysroot_stub.StubSysroot('sysroot/path') + self.platform = platform_stub.StubPlatform(os_version=self.OS_VERSION) self.sysrt_util = sysroot_util.SysrootUtil( - self.sysroot, self.stub_util.GetOSVersion()) + self.sysroot, self.platform) def test_add_pc_file(self): self.sysroot.should_write = [ @@ -64,11 +66,8 @@ class SysrootUtilTest(unittest.TestCase): (self.stub_os.path.join('libsrc', 'libtest.so'), self.stub_os.path.join(lib_dir, 'libtest-suffix.so'))] self.sysroot.should_add_dir = [ - (self.stub_util.GetOSPath(self.stub_util.GetOSVersion(), - 'headers/1'), - include_dir, True), - (self.stub_util.GetOSPath(self.stub_util.GetOSVersion(), - 'headers/deeper/2/../2'), + (self.platform.os_path('headers/1'), include_dir, True), + (self.platform.os_path('headers/deeper/2/../2'), self.stub_os.path.join(include_dir, '2'), True)] self.sysroot.should_pass_filter = ['file.h', 'h.h', '.h.h', 'longer/path/to/thing.h'] @@ -102,12 +101,8 @@ class SysrootUtilTest(unittest.TestCase): (self.stub_os.path.join('libsrc', 'libtest2.so'), self.stub_os.path.join(lib_dir, 'libtest2-suffix.so'))] self.sysroot.should_add_dir = [ - (self.stub_util.GetOSPath(self.stub_util.GetOSVersion(), - 'headers/1'), - include_dir, True), - (self.stub_util.GetOSPath(self.stub_util.GetOSVersion(), - 'headers/2'), - include_dir, True)] + (self.platform.os_path('headers/1'), include_dir, True), + (self.platform.os_path('headers/2'), include_dir, True)] self.sysroot.should_pass_filter = ['file.h', 'h.h', '.h.h', 'longer/path/to/thing.h'] self.sysroot.should_fail_filter = ['h.cpp', 'h', 'file.h.tar'] diff --git a/cli/lib/environment/toolchain_util.py b/cli/lib/environment/toolchain_util.py index 9f1c81a..0d22154 100644 --- a/cli/lib/environment/toolchain_util.py +++ b/cli/lib/environment/toolchain_util.py @@ -21,7 +21,6 @@ import glob import os import stat -from core import util from environment import sysroot_util import error @@ -100,13 +99,12 @@ def _GenerateWrapper(src, dest, flags=None): os.chmod(dest, st.st_mode | stat.S_IEXEC) -def GenerateToolchain(os_version, host, target, output_dir): +def GenerateToolchain(platform, host, output_dir): """Generate a toolchain. Args: - os_version: OS version to generate toolchain for. + platform: Platform to generate toolchain for. host: Host architecture to generate toolchain for. - target: Target architecture to generate toolchain for. output_dir: Where to put generated tools. Raises: @@ -117,9 +115,10 @@ def GenerateToolchain(os_version, host, target, output_dir): os.makedirs(output_dir) # Put together some variables based on host and target. - existing_tools = util.GetOSPath( - os_version, EXISTING_TOOLS_FORMAT.format(host_arch=host)) - tool_prefix = os.path.join(existing_tools, ARCH_TOOL_PREFIX[target]) + existing_tools = platform.os_path( + EXISTING_TOOLS_FORMAT.format(host_arch=host)) + tool_prefix = os.path.join( + existing_tools, ARCH_TOOL_PREFIX[platform.device.arch]) prefix_len = len(tool_prefix) # Walk the existing tools, wrapping them all. @@ -133,7 +132,7 @@ def GenerateToolchain(os_version, host, target, output_dir): tool = path[prefix_len:] try: output_tool = os.path.join(output_dir, tool) - tool_flags = _ToolFlags(tool, target) + tool_flags = _ToolFlags(tool, platform.device.arch) # Write a simple wrapper. _GenerateWrapper(path, output_tool, tool_flags) except (IOError, OSError) as e: diff --git a/cli/lib/environment/toolchain_util_unittest.py b/cli/lib/environment/toolchain_util_unittest.py index cd1d703..db6609d 100644 --- a/cli/lib/environment/toolchain_util_unittest.py +++ b/cli/lib/environment/toolchain_util_unittest.py @@ -20,41 +20,51 @@ import stat import unittest +from bsp import device_stub from core import util_stub from environment import toolchain_util +from project import platform_stub from test import stubs class ToolchainUtilTest(unittest.TestCase): + + OS_VERSION = '43.21' + TARGET_ARCH = 'x86' + HOST_ARCH = 'host_arch' + def setUp(self): self.stub_os = stubs.StubOs() self.stub_open = stubs.StubOpen(self.stub_os) self.stub_glob = stubs.StubGlob(self.stub_os) - self.stub_util = util_stub.StubUtil() + self.stub_util = util_stub.StubUtil(os_version=self.OS_VERSION) toolchain_util.os = self.stub_os toolchain_util.open = self.stub_open.open toolchain_util.glob = self.stub_glob toolchain_util.util = self.stub_util + self.platform = platform_stub.StubPlatform( + os_version=self.OS_VERSION, + device=device_stub.StubDevice(arch=self.TARGET_ARCH)) + + def test_generate_toolchain(self): stub_tools = ['no_flags', 'g++'] tool_paths = {} tool_dests = {} for tool in stub_tools: - tool_paths[tool] = self.stub_util.GetOSPath( - self.stub_util.GetOSVersion(), + tool_paths[tool] = self.platform.os_path( toolchain_util.EXISTING_TOOLS_FORMAT.format( - host_arch='host_arch'), - toolchain_util.ARCH_TOOL_PREFIX['x86'] + tool) + host_arch=self.HOST_ARCH), + toolchain_util.ARCH_TOOL_PREFIX[self.TARGET_ARCH] + tool) self.stub_os.path.should_exist.append(tool_paths[tool]) tool_dests[tool] = self.stub_os.path.join('dest', tool) self.stub_os.should_chmod.append((tool_dests[tool], stat.S_IEXEC)) self.stub_os.should_makedirs = ['dest'] - toolchain_util.GenerateToolchain(self.stub_util.GetOSVersion(), - 'host_arch', 'x86', 'dest') + toolchain_util.GenerateToolchain(self.platform, self.HOST_ARCH, 'dest') # Should have created dest dir. self.assertTrue(self.stub_os.path.isdir('dest')) for tool in stub_tools: diff --git a/cli/lib/project/config.py b/cli/lib/project/config.py index 480d9e6..a4316c5 100644 --- a/cli/lib/project/config.py +++ b/cli/lib/project/config.py @@ -89,6 +89,9 @@ class Config(object): '<default target="{}"/></config>').format( self._output_dir, self._cache_dir, self._default_target) + def cache_dir_for_target(self, target): + return os.path.join(self.cache_dir, '{}.cache'.format(target.name)) + @classmethod def from_element(cls, node): """Creates a new Config from the @node. diff --git a/cli/lib/project/platform.py b/cli/lib/project/platform.py index b1884fb..05df895 100644 --- a/cli/lib/project/platform.py +++ b/cli/lib/project/platform.py @@ -25,7 +25,10 @@ from core import util from project import common -BUILD_TYPES = ('user', 'userdebug', 'eng') +BUILD_TYPE_USER = 'user' +BUILD_TYPE_USERDEBUG = 'userdebug' +BUILD_TYPE_ENG = 'eng' +BUILD_TYPES = (BUILD_TYPE_USER, BUILD_TYPE_USERDEBUG, BUILD_TYPE_ENG) # Note: this should be kept in sync with @@ -98,12 +101,10 @@ class Platform(object): self._os = os_name self._os_version = os_version - # Board. - self._board = board - self._board_version = board_version + # Device. self._device = None # Init device raises a BoardError if it can't get the device. - self._init_device() + self._init_device(board, board_version) # TODO(b/27654613): Check OS and board compatibility. @@ -120,22 +121,22 @@ class Platform(object): os_namespace=self.os_namespace, board_namespace=self.board_namespace) - def _init_device(self): + def _init_device(self, name, version): """Initialize the bsp.Device associated with this Target. Raises: BoardError: If the target board and version is not in the manifest. """ bsp_manifest = manifest.Manifest.from_json() - device = bsp_manifest.devices.get(self.board) + device = bsp_manifest.devices.get(name) if not device: raise BoardError('unrecognized name "{}". Run `bdk bsp list` ' 'to see available boards.'.format( - self.board)) - if device.version != self.board_version: + name)) + if device.version != version: raise BoardError('version {} ({} is the only available ' 'version for {}).'.format( - self.board_version, device.version, self.board)) + version, device.version, name)) self._device = device @property @@ -151,16 +152,8 @@ class Platform(object): return '{}.{}'.format(self.os, self.os_version) @property - def board(self): - return self._board - - @property - def board_version(self): - return self._board_version - - @property def board_namespace(self): - return '{}.{}'.format(self.board, self.board_version) + return '{}.{}'.format(self.device.name, self.device.version) @property def build_type(self): @@ -176,7 +169,7 @@ class Platform(object): @property def product_out_cache(self): - return os.path.join(self.build_cache, 'target', 'product', self.board) + return util.GetAndroidProductOut(self.build_cache, self.device.name) @property def toolchain(self): @@ -209,4 +202,4 @@ class Platform(object): if not self.device.is_available(): raise NotDownloadedError( 'Board Support Package for "{0}". ' - 'Run `bdk bsp download {0}`.'.format(self.board)) + 'Run `bdk bsp download {0}`.'.format(self.device.name)) diff --git a/cli/lib/project/platform_stub.py b/cli/lib/project/platform_stub.py new file mode 100644 index 0000000..aeaa272 --- /dev/null +++ b/cli/lib/project/platform_stub.py @@ -0,0 +1,83 @@ +# +# Copyright (C) 2016 The Android Open Source Project +# +# 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. + + +"""Stubs for classes in platform.py.""" + + +import os + +from bsp import device_stub + + +class StubPlatform(object): + """A Stub for the Platform class.""" + + def __init__(self, os_name='', os_version='', board='', board_version='', + build_type='', device=None, build_cache='', + product_out_cache='', sysroot='', toolchain='', + cache_dir='', os_dir='', verify_raises=Exception): + # Properties. + self.os = os_name + self.os_version = os_version + self.device = device or device_stub.StubDevice() + self.device.name = board + self.device.version = board_version + self.build_type = build_type + self.build_cache = build_cache + self.product_out_cache = product_out_cache + self.sysroot = sysroot + self.toolchain = toolchain + + # Helpers. + self.cache_dir = cache_dir + self.os_dir = os_dir + self.verify_raises = verify_raises + + @property + def os_namespace(self): + return '{}.{}'.format(self.os, self.os_version) + + @property + def board_namespace(self): + return '{}.{}'.format(self.device.name, self.device.version) + + def cache_path(self, *relpath): + return os.path.join(self.cache_dir, *relpath) + + def os_path(self, *relpath): + return os.path.join(self.os_dir, *relpath) + + def linked(self): + return self.device.linked(self.os_version) + + def verify_downloaded(self): + if self.verify_raises: + # pylint: disable=raising-bad-type + raise self.verify_raises + + +class StubPlatformModule(object): + + class Error(Exception): + pass + + def __init__(self, valid=True): + self.valid = valid + + def Platform(self, *args, **kwargs): + if not self.valid: + raise self.Error + return StubPlatform(*args, **kwargs) diff --git a/cli/lib/project/platform_unittest.py b/cli/lib/project/platform_unittest.py index 601f54c..0bf37ee 100644 --- a/cli/lib/project/platform_unittest.py +++ b/cli/lib/project/platform_unittest.py @@ -62,8 +62,6 @@ class PlatformTest(unittest.TestCase): # Boring ones. self.assertEqual(self.platform.os, self._OS) self.assertEqual(self.platform.os_version, self._OS_VERSION) - self.assertEqual(self.platform.board, self._BSP) - self.assertEqual(self.platform.board_version, self._BSP_VERSION) self.assertEqual(self.platform.build_type, self._BUILD_TYPE) # Generated ones. @@ -73,6 +71,8 @@ class PlatformTest(unittest.TestCase): self.assertIn(self._BSP, self.platform.board_namespace) self.assertIn(self._BSP_VERSION, self.platform.board_namespace) self.assertEqual(self.platform.device, self.dev) + self.assertEqual(self.platform.device.name, self._BSP) + self.assertEqual(self.platform.device.version, self._BSP_VERSION) cache_root = self.stub_os.path.join( self.stub_user_config.USER_CONFIG.platform_cache, self.platform.os_namespace, @@ -88,7 +88,7 @@ class PlatformTest(unittest.TestCase): self.platform.product_out_cache, self.stub_os.path.join( self.platform.build_cache, 'target', 'product', - self.platform.board)) + self.platform.device.name)) def test_init_bad_build_type(self): with self.assertRaises(platform.BuildTypeError): diff --git a/cli/lib/project/project_spec_unittest.py b/cli/lib/project/project_spec_unittest.py index f105536..24ec224 100644 --- a/cli/lib/project/project_spec_unittest.py +++ b/cli/lib/project/project_spec_unittest.py @@ -25,6 +25,7 @@ from project import config_stub from project import project_spec from project import dependency from project import packs +from project import platform_stub from project import target_stub @@ -88,6 +89,11 @@ class ProjectSpecTest(unittest.TestCase): self.minimal_board = packs.PacksFactory().new(file=board) self.minimal_os = packs.PacksFactory().new(file=os) + # Stub out the platform module during tests + # to avoid needing the bsp manifest. + self.stub_platform = platform_stub.StubPlatformModule() + project_spec.targets.target.platform = self.stub_platform + def tearDown(self): pass @@ -123,6 +129,12 @@ class ParseTest(ProjectSpecTest): with self.assertRaises(dependency.UndefinedPackError): b.packmap.report_missing() + def test_minimal_invalid(self): + # If the platform doesn't validate, we should get a load error. + self.stub_platform.valid = False + with self.assertRaises(common.LoadError): + self.project_spec_from_str(MINIMAL_BDK_XML) + def test_minimal_wrong_os_version(self): b = self.project_spec_from_str(MINIMAL_BDK_XML) # Create a mismatched os pack version. @@ -201,7 +213,8 @@ class ParseTest(ProjectSpecTest): with self.assertRaises(dependency.UnsatisfiedVirtualPackError) as uvpe: for tgt in b.targets.values(): # Test all targets we didnt load an os for. - if tgt.os != 'brillo' or tgt.os_version != '12': + if (tgt.platform.os != 'brillo' or + tgt.platform.os_version != '12'): tgt.create_submap(b.packmap) for line in str(uvpe.exception).split('\n'): diff --git a/cli/lib/project/target.py b/cli/lib/project/target.py index 719b6d8..dc60195 100644 --- a/cli/lib/project/target.py +++ b/cli/lib/project/target.py @@ -17,27 +17,17 @@ """This file provides all Target element related parsing.""" -import os - -from bsp import manifest -from core import user_config from project import common +from project import platform -# Note: this should be kept in sync with -# commands.environment.setup.SAMPLE_MAKEFILE_TEMPLATE -PLATFORM_CACHE_FORMAT = os.path.join('{user_configured_platform_cache}', - '{os}.{os_version}', - '{board}.{board_version}') - - -class Error(common.Error): +class Error(common.LoadErrorWithOrigin): """General Error for targets.""" -class BoardError(Error): - """Raised when there is a problem with the specified board/version.""" - description = 'Invalid board' +class TargetLoadError(Error): + """Raised when there is a problem loading a target.""" + description = 'Failed to load target' class Target(object): @@ -47,32 +37,29 @@ class Target(object): it is intended for. """ - # TODO(b/28296932): Rename the 'os' input variable to not conflict with the - # import. - # pylint: disable=redefined-outer-name - def __init__(self, name, os='', os_version='', board='', - board_version=''): + def __init__(self, name): self._name = name self._pack = None self._pack_name = '' - self._os = os - self._os_version = os_version - self._board = board - self._board_version = board_version - self._build = 'userdebug' self._origin = common.Origin() + self._platform = None def __repr__(self): return ('<target name="{}" os="{}" os-version="{}" ' 'board="{}" board-version="{}" build="{}" pack="{}"/>').format( - self._name, self._os, self._os_version, self._board, - self._board_version, self._build, self._pack_name) + self._name, self.platform.os, self.platform.os_version, + self.platform.device.name, self.platform.device.version, + self.platform.build_type, self._pack_name) @property def name(self): return self._name @property + def platform(self): + return self._platform + + @property def pack_name(self): return self._pack_name @@ -84,96 +71,26 @@ class Target(object): def origin(self): return self._origin - @property - def os(self): - return self._os - - @os.setter - def os(self, b): - self._os = b - - @property - def os_namespace(self): - return '{}.{}'.format(self.os, self.os_version) - - @property - def os_version(self): - return self._os_version - - @os_version.setter - def os_version(self, b): - self._os_version = b - - @property - def board(self): - return self._board - - @board.setter - def board(self, b): - self._board = b - - @property - def board_version(self): - return self._board_version - - @board_version.setter - def board_version(self, b): - self._board_version = b - - @property - def board_namespace(self): - return '{}.{}'.format(self.board, self.board_version) - - @property - def build_type(self): - return self._build - - def get_device(self): - """Get the bsp.Device associated with this Target. - - Returns: - The bsp.Device associated with this Target. - - Raises: - BoardError: If the target board and version is not in the manifest. - """ - bsp_manifest = manifest.Manifest.from_json() - board = bsp_manifest.devices.get(self.board) - if not board: - raise BoardError('unrecognized name "{}". ' - 'Run `bdk bsp list` to see available ' - 'boards.'.format(self.board)) - if board.version != self.board_version: - raise BoardError('"{}" only has version {} (requested {}).'.format( - self.board, board.version, self.board_version)) - return board - - def platform_cache(self, *relpath): - return os.path.join( - PLATFORM_CACHE_FORMAT.format( - user_configured_platform_cache=( - user_config.USER_CONFIG.platform_cache), - os=self.os, - os_version=self.os_version, - board=self.board, - board_version=self.board_version), - *relpath) - - def platform_build_cache(self, *relpath): - return self.platform_cache(self.build_type, *relpath) - def load(self, ele): """Populates this instance from Element @ele.""" self._origin = ele.origin.copy() ele.limit_attribs(['name', 'pack', 'os', 'board', 'os-version', 'board-version', 'build']) self._pack_name = ele.get_attrib('pack') - self._os = ele.get_attrib('os') - self._os_version = ele.get_attrib('os-version') - self._board = ele.get_attrib('board') - self._board_version = ele.get_attrib('board-version') + + # Platform. + build_type = 'userdebug' if 'build' in ele.attrib: - self._build = ele.get_attrib('build') + build_type = ele.get_attrib('build') + try: + self._platform = platform.Platform( + os_name=ele.get_attrib('os'), + os_version=ele.get_attrib('os-version'), + board=ele.get_attrib('board'), + board_version=ele.get_attrib('board-version'), + build_type=build_type) + except platform.Error as e: + raise TargetLoadError(self._origin, e) def create_submap(self, global_packmap): """Create a packmap from the global packmap. @@ -187,7 +104,8 @@ class Target(object): Returns: A submap of global_packmap for this target, with paths validated. """ - aliases = {'os': self.os_namespace, 'board': self.board_namespace} + aliases = {'os': self.platform.os_namespace, + 'board': self.platform.board_namespace} packmap = global_packmap.submap(self._pack_name, aliases) packmap.check_paths() return packmap diff --git a/cli/lib/project/target_stub.py b/cli/lib/project/target_stub.py index f236533..fb31120 100644 --- a/cli/lib/project/target_stub.py +++ b/cli/lib/project/target_stub.py @@ -18,49 +18,22 @@ """A stub for the Target class.""" -import os - -from project import target - - class StubTarget(object): # TODO(b/28296932): Rename the 'os' input variable to not conflict with the # import. # pylint: disable=redefined-outer-name - def __init__(self, name='', os='', os_version='', - board='', board_version='', build_type='', + def __init__(self, name='', origin='test_target_origin', - platform_dir=None, - device_raises=False, device=None, + platform=None, submap_raises=None, submaps=None): self.name = name - self.os = os - self.os_version = os_version - self.os_namespace = '{}.{}'.format(os, os_version) - self.board = board - self.board_version = board_version - self.board_namespace = '{}.{}'.format(board, board_version) - self.build_type = build_type self.origin = origin - self.device = device - self.platform_dir = platform_dir + self.platform = platform - self.device_raises = device_raises self._submap_raises = submap_raises self._submaps = submaps or [] - def get_device(self): - if self.device_raises: - raise target.BoardError - return self.device - - def platform_cache(self, *relpath): - return os.path.join(self.platform_dir, *relpath) - - def platform_build_cache(self, *relpath): - return self.platform_cache(self.build_type, *relpath) - def create_submap(self, _packmap): if self._submap_raises: # Pylint issues an error about raising NoneType, since diff --git a/cli/lib/selinux/policy.py b/cli/lib/selinux/policy.py index 04bd9e3..7e6f4f6 100644 --- a/cli/lib/selinux/policy.py +++ b/cli/lib/selinux/policy.py @@ -26,7 +26,6 @@ import tempfile import error from core import tool -from core import util class Error(error.Error): @@ -35,6 +34,7 @@ class Error(error.Error): class SELinuxCommandError(Error): """Raised when an SELinux-related command fails.""" + description = 'SELinux command error' POLICYVERS = 29 @@ -95,11 +95,11 @@ def _RunSepolicyAnalyze(build_out, _input_file, _output_file): sepolicy_analize = tool.HostToolWrapper('sepolicy-analyze', build_out) -def GetBoardComboSepolicyDirs(target): +def GetBoardComboSepolicyDirs(platform): """Gets the BOARD_SEPOLICY_DIRS variable from the build.""" - cmds = ['cd "{}"'.format(util.GetOSPath(target.os_version)), + cmds = ['cd "{}"'.format(platform.os_path()), '. build/envsetup.sh', - 'lunch "{}-{}"'.format(target.board, target.build_type), + 'lunch "{}-{}"'.format(platform.device.name, platform.build_type), 'get_build_var BOARD_SEPOLICY_DIRS'] # Link all commands with && to exit immediately if one fails. @@ -127,15 +127,14 @@ def SaveSepolicyDirsCache(platform_out, sepolicy_dirs): dirs_file = os.path.join(platform_out, SEPOLICY_DIRS_FILE) with open(dirs_file, 'w') as fw: fw.write('\n'.join(sepolicy_dirs)) - - except Exception as e: + except IOError as e: raise SELinuxCommandError( 'Failed to save BOARD_SEPOLICY_DIRS cache: {}'.format(e)) -def LoadCachedSepolicyDirs(platform_out, target): +def LoadCachedSepolicyDirs(platform): """Loads and parses the BOARD_SEPOLICY_DIRS cache file.""" - cache_path = os.path.join(platform_out, SEPOLICY_DIRS_FILE) + cache_path = os.path.join(platform.build_cache, SEPOLICY_DIRS_FILE) board_sepolicy_dirs = None if os.access(cache_path, os.R_OK): @@ -148,34 +147,34 @@ def LoadCachedSepolicyDirs(platform_out, target): if not board_sepolicy_dirs: # Cache did not exist, or was empty. - board_sepolicy_dirs = GetBoardComboSepolicyDirs(target) - SaveSepolicyDirsCache(platform_out, board_sepolicy_dirs) + board_sepolicy_dirs = GetBoardComboSepolicyDirs(platform) + SaveSepolicyDirsCache(platform.build_cache, board_sepolicy_dirs) return board_sepolicy_dirs -def BuildSepolicy(target, platform_out, cache_dir): +def BuildSepolicy(platform, cache_dir): """Builds the main 'sepolicy' SELinux policy file. This needs to be built before attempting to build the 'file_contexts.bin' file. """ interm_dir = tempfile.mkdtemp() - runner = tool.HostToolRunner(platform_out) - + runner = tool.HostToolRunner(platform) + # TODO: platformify try: - os_path = util.GetOSPath(target.os_version) + os_path = platform.os_path() main_sepolicy_dir = os.path.join(os_path, 'system', 'sepolicy') all_paths = [main_sepolicy_dir] - all_paths.extend(LoadCachedSepolicyDirs(platform_out, target)) + all_paths.extend(LoadCachedSepolicyDirs(platform)) policy_inputs = _ExpandSepolicyPaths(os_path, all_paths, SEPOLICY_BUILD_FILES) policy_conf = os.path.join(interm_dir, 'policy.conf') m4_opts = ['-D mls_num_sens=%s' % MLS_SENS, '-D mls_num_cats=%s' % MLS_CATS, - '-D target_build_variant=%s' % target.build_type] + '-D target_build_variant=%s' % platform.build_type] _RunM4(policy_inputs, policy_conf, m4_opts) sepolicy_path = os.path.join(cache_dir, 'root', 'sepolicy') @@ -186,16 +185,15 @@ def BuildSepolicy(target, platform_out, cache_dir): shutil.rmtree(interm_dir, ignore_errors=True) -def BuildFileContexts(target, platform_out, cache_dir): +def BuildFileContexts(platform, cache_dir): """Builds the 'file_contexts.bin' SELinux policy file. This requires a valid 'sepolicy' file in |cache_dir|. """ interm_dir = tempfile.mkdtemp() - runner = tool.HostToolRunner(platform_out) - + runner = tool.HostToolRunner(platform) try: - os_path = util.GetOSPath(target.os_version) + os_path = platform.os_path() fc_local = os.path.join(interm_dir, 'file_contexts.local.tmp') fc_device = os.path.join(interm_dir, 'file_contexts.device.tmp') @@ -210,7 +208,7 @@ def BuildFileContexts(target, platform_out, cache_dir): main_fc = os.path.join(main_sepolicy_dir, FC) _RunM4([main_fc], fc_local) - board_sepolicy_dirs = LoadCachedSepolicyDirs(platform_out, target) + board_sepolicy_dirs = LoadCachedSepolicyDirs(platform) if os.access(os.path.join(cache_dir, FC), os.R_OK): # There's a 'file_contexts' file in |cache_dir| that we need to diff --git a/cli/lib/selinux/policy_stub.py b/cli/lib/selinux/policy_stub.py index b3b231a..c94afd7 100644 --- a/cli/lib/selinux/policy_stub.py +++ b/cli/lib/selinux/policy_stub.py @@ -21,8 +21,8 @@ class StubPolicy(object): """Stubs the selinux.policy module.""" - def BuildSepolicy(self, _target, _platform_out, _product_out): + def BuildSepolicy(self, _platform, _cache_dir): pass - def BuildFileContexts(self, _target, _platform_out, _product_out): + def BuildFileContexts(self, _platform, _cache_dir): pass |