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