1# Copyright 2020 Google LLC 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# https://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14"""Builds an Android target in a secure sandbox.""" 15 16import argparse 17import os 18from . import config 19from . import nsjail 20from . import rbe 21 22_DEFAULT_COMMAND_WRAPPER = \ 23 '/src/tools/treble/build/sandbox/build_android_target.sh' 24 25 26def build(build_target, 27 release_target, 28 variant, 29 nsjail_bin, 30 chroot, 31 dist_dir, 32 build_id, 33 max_cpus, 34 build_goals, 35 config_file=None, 36 command_wrapper=_DEFAULT_COMMAND_WRAPPER, 37 use_rbe=False, 38 readonly_bind_mounts=[], 39 env=[]): 40 """Builds an Android target in a secure sandbox. 41 42 Args: 43 build_target: A string with the name of the build target. 44 release_target: The release target config, e.g., next, trunk_food, ... 45 variant: A string with the build variant. 46 nsjail_bin: A string with the path to the nsjail binary. 47 chroot: A string with the path to the chroot of the NsJail sandbox. 48 dist_dir: A string with the path to the Android dist directory. 49 build_id: A string with the Android build identifier. 50 max_cpus: An integer with maximum number of CPUs. 51 build_goals: A list of strings with the goals and options to provide to the 52 build command. 53 config_file: A string path to an overlay configuration file. 54 command_wrapper: A string path to the command wrapper. 55 use_rbe: If true, will attempt to use RBE for the build. 56 readonly_bind_mounts: A list of string paths to be mounted as read-only. 57 env: An array of environment variables to define in the NsJail sandbox in 58 the `var=val` syntax. 59 60 Returns: 61 A list of commands that were executed. Each command is a list of strings. 62 """ 63 if config_file: 64 cfg = config.Config(config_file) 65 android_target = cfg.get_build_config_android_target(build_target) 66 if cfg.has_tag(build_target, 'skip'): 67 print('Warning: skipping build_target "{}" due to tag being set'.format( 68 build_target)) 69 return [] 70 else: 71 android_target = build_target 72 73 # All builds are required to run with the root of the 74 # Android source tree as the current directory. 75 source_dir = os.getcwd() 76 command = [ 77 command_wrapper, 78 '%s-%s-%s' % (android_target, release_target, variant), 79 '/src', 80 'make', 81 '-j', 82 ] + build_goals 83 84 extra_nsjail_args = [] 85 cleanup = lambda: None 86 nsjail_wrapper = [] 87 if use_rbe: 88 cleanup = rbe.setup(env) 89 env = rbe.prepare_env(env) 90 extra_nsjail_args.extend(rbe.get_extra_nsjail_args()) 91 readonly_bind_mounts.extend(rbe.get_readonlybind_mounts()) 92 nsjail_wrapper = rbe.get_nsjail_bin_wrapper() 93 94 ret = nsjail.run( 95 nsjail_bin=nsjail_bin, 96 chroot=chroot, 97 overlay_config=config_file, 98 source_dir=source_dir, 99 command=command, 100 build_target=build_target, 101 dist_dir=dist_dir, 102 build_id=build_id, 103 max_cpus=max_cpus, 104 extra_nsjail_args=extra_nsjail_args, 105 readonly_bind_mounts=readonly_bind_mounts, 106 env=env, 107 nsjail_wrapper=nsjail_wrapper) 108 109 cleanup() 110 111 return ret 112 113 114def arg_parser(): 115 """Returns an ArgumentParser for sanboxed android builds.""" 116 # Use the top level module docstring for the help description 117 parser = argparse.ArgumentParser( 118 description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) 119 parser.add_argument('--build_target', help='The build target.') 120 parser.add_argument( 121 '--release_target', 122 required=True, 123 help='Release target config, e.g., next, trunk_food, trunk_staging, ...') 124 parser.add_argument( 125 '--variant', default='userdebug', help='The Android build variant.') 126 parser.add_argument( 127 '--nsjail_bin', required=True, help='Path to NsJail binary.') 128 parser.add_argument( 129 '--chroot', 130 required=True, 131 help='Path to the chroot to be used for building the Android ' 132 'platform. This will be mounted as the root filesystem in the ' 133 'NsJail sandbox.') 134 parser.add_argument( 135 '--config_file', 136 required=True, 137 help='Path to the overlay configuration file.') 138 parser.add_argument( 139 '--command_wrapper', 140 default=_DEFAULT_COMMAND_WRAPPER, 141 help='Path to the command wrapper. ' 142 'Defaults to \'%s\'.' % _DEFAULT_COMMAND_WRAPPER) 143 parser.add_argument( 144 '--readonly_bind_mount', 145 type=str, 146 default=[], 147 action='append', 148 help='Path to the a path to be mounted as readonly inside the secure ' 149 'build sandbox. Can be specified multiple times') 150 parser.add_argument( 151 '--env', 152 '-e', 153 type=str, 154 default=[], 155 action='append', 156 help='Specify an environment variable to the NSJail sandbox. Can be specified ' 157 'muliple times. Syntax: var_name=value') 158 parser.add_argument( 159 '--dist_dir', 160 help='Path to the Android dist directory. This is where ' 161 'Android platform release artifacts will be written.') 162 parser.add_argument( 163 '--build_id', 164 help='Build identifier what will label the Android platform ' 165 'release artifacts.') 166 parser.add_argument( 167 '--max_cpus', 168 type=int, 169 help='Limit of concurrent CPU cores that the NsJail sanbox ' 170 'can use.') 171 parser.add_argument( 172 '--context', 173 action='append', 174 default=[], 175 help='One or more contexts used to select build goals from the ' 176 'configuration.') 177 parser.add_argument( 178 '--use_rbe', action='store_true', help='Executes the build on RBE') 179 return parser 180 181 182def parse_args(parser): 183 """Parses command line arguments. 184 185 Returns: 186 A dict of all the arguments parsed. 187 """ 188 # Convert the Namespace object to a dict 189 return vars(parser.parse_args()) 190 191 192def main(): 193 args = parse_args(arg_parser()) 194 195 # The --build_target argument could not be required 196 # using the standard 'required' argparse option because 197 # the argparser is reused by merge_android_sandboxed.py which 198 # does not require --build_target. 199 if args['build_target'] is None: 200 raise ValueError('--build_target is required.') 201 202 cfg = config.Config(args['config_file']) 203 build_goals = cfg.get_build_goals(args['build_target'], set(args['context'])) 204 build_flags = cfg.get_build_flags(args['build_target'], set(args['context'])) 205 206 build( 207 build_target=args['build_target'], 208 release_target=args['release_target'], 209 variant=args['variant'], 210 nsjail_bin=args['nsjail_bin'], 211 chroot=args['chroot'], 212 config_file=args['config_file'], 213 command_wrapper=args['command_wrapper'], 214 readonly_bind_mounts=args['readonly_bind_mount'], 215 env=args['env'], 216 dist_dir=args['dist_dir'], 217 build_id=args['build_id'], 218 max_cpus=args['max_cpus'], 219 use_rbe=args['use_rbe'], 220 build_goals=build_goals + build_flags) 221 222 223if __name__ == '__main__': 224 main() 225