| # Copyright 2020 Google LLC |
| # |
| # 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 |
| # |
| # https://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. |
| """Builds an Android target in a secure sandbox.""" |
| |
| import argparse |
| import os |
| from . import config |
| from . import nsjail |
| from . import rbe |
| |
| _DEFAULT_COMMAND_WRAPPER = \ |
| '/src/tools/treble/build/sandbox/build_android_target.sh' |
| |
| |
| def build(build_target, variant, nsjail_bin, chroot, dist_dir, build_id, |
| max_cpus, build_goals, config_file=None, |
| command_wrapper=_DEFAULT_COMMAND_WRAPPER, use_rbe=False, |
| readonly_bind_mount=None, env=[]): |
| """Builds an Android target in a secure sandbox. |
| |
| Args: |
| build_target: A string with the name of the build target. |
| variant: A string with the build variant. |
| nsjail_bin: A string with the path to the nsjail binary. |
| chroot: A string with the path to the chroot of the NsJail sandbox. |
| dist_dir: A string with the path to the Android dist directory. |
| build_id: A string with the Android build identifier. |
| max_cpus: An integer with maximum number of CPUs. |
| build_goals: A list of strings with the goals and options to provide to the |
| build command. |
| config_file: A string path to an overlay configuration file. |
| command_wrapper: A string path to the command wrapper. |
| use_rbe: If true, will attempt to use RBE for the build. |
| readonly_bind_mount: A string path to a path to be mounted as read-only. |
| env: An array of environment variables to define in the NsJail sandbox in the |
| `var=val` syntax. |
| |
| Returns: |
| A list of commands that were executed. Each command is a list of strings. |
| """ |
| if config_file: |
| cfg = config.Config(config_file) |
| android_target = cfg.get_build_config_android_target(build_target) |
| if cfg.has_tag(build_target, 'skip'): |
| print('Warning: skipping build_target "{}" due to tag being set'.format(build_target)) |
| return [] |
| else: |
| android_target = build_target |
| |
| # All builds are required to run with the root of the |
| # Android source tree as the current directory. |
| source_dir = os.getcwd() |
| command = [ |
| command_wrapper, |
| '%s-%s' % (android_target, variant), |
| '/src', |
| 'make', |
| '-j', |
| ] + build_goals |
| |
| readonly_bind_mounts = [] |
| if readonly_bind_mount: |
| readonly_bind_mounts = [readonly_bind_mount] |
| |
| extra_nsjail_args = [] |
| cleanup = lambda: None |
| nsjail_wrapper = [] |
| if use_rbe: |
| cleanup = rbe.setup(env) |
| env = rbe.prepare_env(env) |
| extra_nsjail_args.extend(rbe.get_extra_nsjail_args()) |
| readonly_bind_mounts.extend(rbe.get_readonlybind_mounts()) |
| nsjail_wrapper = rbe.get_nsjail_bin_wrapper() |
| |
| ret = nsjail.run( |
| nsjail_bin=nsjail_bin, |
| chroot=chroot, |
| overlay_config=config_file, |
| source_dir=source_dir, |
| command=command, |
| build_target=build_target, |
| dist_dir=dist_dir, |
| build_id=build_id, |
| max_cpus=max_cpus, |
| extra_nsjail_args=extra_nsjail_args, |
| readonly_bind_mounts=readonly_bind_mounts, |
| env=env, |
| nsjail_wrapper=nsjail_wrapper) |
| |
| cleanup() |
| |
| return ret |
| |
| |
| def arg_parser(): |
| """Returns an ArgumentParser for sanboxed android builds.""" |
| # Use the top level module docstring for the help description |
| parser = argparse.ArgumentParser( |
| description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) |
| parser.add_argument( |
| '--build_target', |
| help='The build target.') |
| parser.add_argument( |
| '--variant', default='userdebug', help='The Android build variant.') |
| parser.add_argument( |
| '--nsjail_bin', |
| required=True, |
| help='Path to NsJail binary.') |
| parser.add_argument( |
| '--chroot', |
| required=True, |
| help='Path to the chroot to be used for building the Android ' |
| 'platform. This will be mounted as the root filesystem in the ' |
| 'NsJail sandbox.') |
| parser.add_argument( |
| '--config_file', |
| required=True, |
| help='Path to the overlay configuration file.') |
| parser.add_argument( |
| '--command_wrapper', |
| default=_DEFAULT_COMMAND_WRAPPER, |
| help='Path to the command wrapper. ' |
| 'Defaults to \'%s\'.' % _DEFAULT_COMMAND_WRAPPER) |
| parser.add_argument( |
| '--readonly_bind_mount', |
| help='Path to the a path to be mounted as readonly inside the secure ' |
| 'build sandbox.') |
| parser.add_argument( |
| '--env', '-e', |
| type=str, |
| default=[], |
| action='append', |
| help='Specify an environment variable to the NSJail sandbox. Can be specified ' |
| 'muliple times. Syntax: var_name=value') |
| parser.add_argument( |
| '--dist_dir', |
| help='Path to the Android dist directory. This is where ' |
| 'Android platform release artifacts will be written.') |
| parser.add_argument( |
| '--build_id', |
| help='Build identifier what will label the Android platform ' |
| 'release artifacts.') |
| parser.add_argument( |
| '--max_cpus', |
| type=int, |
| help='Limit of concurrent CPU cores that the NsJail sanbox ' |
| 'can use.') |
| parser.add_argument( |
| '--context', |
| action='append', |
| default=[], |
| help='One or more contexts used to select build goals from the ' |
| 'configuration.') |
| parser.add_argument( |
| '--use_rbe', |
| action='store_true', |
| help='Executes the build on RBE') |
| return parser |
| |
| |
| def parse_args(parser): |
| """Parses command line arguments. |
| |
| Returns: |
| A dict of all the arguments parsed. |
| """ |
| # Convert the Namespace object to a dict |
| return vars(parser.parse_args()) |
| |
| |
| def main(): |
| args = parse_args(arg_parser()) |
| |
| # The --build_target argument could not be required |
| # using the standard 'required' argparse option because |
| # the argparser is reused by merge_android_sandboxed.py which |
| # does not require --build_target. |
| if args['build_target'] is None: |
| raise ValueError('--build_target is required.') |
| |
| cfg = config.Config(args['config_file']) |
| build_goals = cfg.get_build_goals(args['build_target'], set(args['context'])) |
| |
| build( |
| build_target=args['build_target'], |
| variant=args['variant'], |
| nsjail_bin=args['nsjail_bin'], |
| chroot=args['chroot'], |
| config_file=args['config_file'], |
| command_wrapper=args['command_wrapper'], |
| readonly_bind_mount=args['readonly_bind_mount'], |
| env=args['env'], |
| dist_dir=args['dist_dir'], |
| build_id=args['build_id'], |
| max_cpus=args['max_cpus'], |
| use_rbe=args['use_rbe'], |
| build_goals=build_goals) |
| |
| |
| if __name__ == '__main__': |
| main() |