1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3#
4# Copyright 2020 The Chromium OS Authors. All rights reserved.
5# Use of this source code is governed by a BSD-style license that can be
6# found in the LICENSE file.
7
8"""Script to checkout the ChromeOS source.
9
10This script sets up the ChromeOS source in the given directory, matching a
11particular release of ChromeOS.
12"""
13
14from __future__ import print_function
15
16__author__ = ('asharif@google.com (Ahmad Sharif) '
17              'llozano@google.com (Luis Lozano) '
18              'raymes@google.com (Raymes Khoury) '
19              'shenhan@google.com (Han Shen)')
20
21import argparse
22import os
23import sys
24
25from cros_utils import command_executer
26from cros_utils import logger
27from cros_utils import misc
28
29
30def Usage(parser, message):
31  print('ERROR: %s' % message)
32  parser.print_help()
33  sys.exit(0)
34
35
36def Main(argv):
37  """Build ChromeOS."""
38  # Common initializations
39  cmd_executer = command_executer.GetCommandExecuter()
40
41  parser = argparse.ArgumentParser()
42  parser.add_argument(
43      '--chromeos_root',
44      dest='chromeos_root',
45      help='Target directory for ChromeOS installation.')
46  parser.add_argument(
47      '--clobber_chroot',
48      dest='clobber_chroot',
49      action='store_true',
50      help='Delete the chroot and start fresh',
51      default=False)
52  parser.add_argument(
53      '--clobber_board',
54      dest='clobber_board',
55      action='store_true',
56      help='Delete the board and start fresh',
57      default=False)
58  parser.add_argument(
59      '--rebuild',
60      dest='rebuild',
61      action='store_true',
62      help='Rebuild all board packages except the toolchain.',
63      default=False)
64  parser.add_argument(
65      '--cflags',
66      dest='cflags',
67      default='',
68      help='CFLAGS for the ChromeOS packages')
69  parser.add_argument(
70      '--cxxflags',
71      dest='cxxflags',
72      default='',
73      help='CXXFLAGS for the ChromeOS packages')
74  parser.add_argument(
75      '--ldflags',
76      dest='ldflags',
77      default='',
78      help='LDFLAGS for the ChromeOS packages')
79  parser.add_argument(
80      '--board', dest='board', help='ChromeOS target board, e.g. x86-generic')
81  parser.add_argument(
82      '--package', dest='package', help='The package needs to be built')
83  parser.add_argument(
84      '--label',
85      dest='label',
86      help='Optional label symlink to point to build dir.')
87  parser.add_argument(
88      '--dev',
89      dest='dev',
90      default=False,
91      action='store_true',
92      help=('Make the final image in dev mode (eg writable, '
93            'more space on image). Defaults to False.'))
94  parser.add_argument(
95      '--debug',
96      dest='debug',
97      default=False,
98      action='store_true',
99      help=('Optional. Build chrome browser with "-g -O0". '
100            "Notice, this also turns on '--dev'. "
101            'Defaults to False.'))
102  parser.add_argument(
103      '--env', dest='env', default='', help='Env to pass to build_packages.')
104  parser.add_argument(
105      '--vanilla',
106      dest='vanilla',
107      default=False,
108      action='store_true',
109      help='Use default ChromeOS toolchain.')
110  parser.add_argument(
111      '--vanilla_image',
112      dest='vanilla_image',
113      default=False,
114      action='store_true',
115      help=('Use prebuild packages for building the image. '
116            'It also implies the --vanilla option is set.'))
117
118  options = parser.parse_args(argv[1:])
119
120  if options.chromeos_root is None:
121    Usage(parser, '--chromeos_root must be set')
122  options.chromeos_root = os.path.expanduser(options.chromeos_root)
123  scripts_dir = os.path.join(options.chromeos_root, 'src', 'scripts')
124  if not os.path.isdir(scripts_dir):
125    Usage(parser, '--chromeos_root must be set up first. Use setup_chromeos.py')
126
127  if options.board is None:
128    Usage(parser, '--board must be set')
129
130  if options.debug:
131    options.dev = True
132
133  build_packages_env = options.env
134  if build_packages_env.find('EXTRA_BOARD_FLAGS=') != -1:
135    logger.GetLogger().LogFatal(
136        ('Passing "EXTRA_BOARD_FLAGS" in "--env" is not supported. '
137         'This flags is used internally by this script. '
138         'Contact the author for more detail.'))
139
140  if options.rebuild:
141    build_packages_env += ' EXTRA_BOARD_FLAGS=-e'
142    # EXTRA_BOARD_FLAGS=-e should clean up the object files for the chrome
143    # browser but it doesn't. So do it here.
144    misc.RemoveChromeBrowserObjectFiles(options.chromeos_root, options.board)
145
146  # Build with afdo_use by default.
147  # To change the default use --env="USE=-afdo_use".
148  build_packages_env = misc.MergeEnvStringWithDict(
149      build_packages_env, {'USE': 'chrome_internal afdo_use -cros-debug'})
150
151  build_packages_command = misc.GetBuildPackagesCommand(
152      board=options.board, usepkg=options.vanilla_image, debug=options.debug)
153
154  if options.package:
155    build_packages_command += ' {0}'.format(options.package)
156
157  build_image_command = misc.GetBuildImageCommand(options.board, options.dev)
158
159  if options.vanilla or options.vanilla_image:
160    command = misc.GetSetupBoardCommand(
161        options.board,
162        usepkg=options.vanilla_image,
163        force=options.clobber_board)
164    command += '; ' + build_packages_env + ' ' + build_packages_command
165    command += '&& ' + build_packages_env + ' ' + build_image_command
166    ret = cmd_executer.ChrootRunCommand(options.chromeos_root, command)
167    return ret
168
169  # Setup board
170  if not os.path.isdir(options.chromeos_root + '/chroot/build/' +
171                       options.board) or options.clobber_board:
172    # Run build_tc.py from binary package
173    ret = cmd_executer.ChrootRunCommand(
174        options.chromeos_root,
175        misc.GetSetupBoardCommand(options.board, force=options.clobber_board))
176    logger.GetLogger().LogFatalIf(ret, 'setup_board failed')
177  else:
178    logger.GetLogger().LogOutput('Did not setup_board '
179                                 'because it already exists')
180
181  if options.debug:
182    # Perform 2-step build_packages to build a debug chrome browser.
183
184    # Firstly, build everything that chromeos-chrome depends on normally.
185    if options.rebuild:
186      # Give warning about "--rebuild" and "--debug". Under this combination,
187      # only dependencies of "chromeos-chrome" get rebuilt.
188      logger.GetLogger().LogWarning(
189          '--rebuild" does not correctly re-build every package when '
190          '"--debug" is enabled. ')
191
192      # Replace EXTRA_BOARD_FLAGS=-e with "-e --onlydeps"
193      build_packages_env = build_packages_env.replace(
194          'EXTRA_BOARD_FLAGS=-e', 'EXTRA_BOARD_FLAGS="-e --onlydeps"')
195    else:
196      build_packages_env += ' EXTRA_BOARD_FLAGS=--onlydeps'
197
198    ret = cmd_executer.ChrootRunCommand(
199        options.chromeos_root, 'CFLAGS="$(portageq-%s envvar CFLAGS) %s" '
200        'CXXFLAGS="$(portageq-%s envvar CXXFLAGS) %s" '
201        'LDFLAGS="$(portageq-%s envvar LDFLAGS) %s" '
202        'CHROME_ORIGIN=SERVER_SOURCE '
203        '%s '
204        '%s --skip_chroot_upgrade'
205        'chromeos-chrome' % (options.board, options.cflags, options.board,
206                             options.cxxflags, options.board, options.ldflags,
207                             build_packages_env, build_packages_command))
208
209    logger.GetLogger().LogFatalIf(\
210      ret, 'build_packages failed while trying to build chromeos-chrome deps.')
211
212    # Secondly, build chromeos-chrome using debug mode.
213    # Replace '--onlydeps' with '--nodeps'.
214    if options.rebuild:
215      build_packages_env = build_packages_env.replace(
216          'EXTRA_BOARD_FLAGS="-e --onlydeps"', 'EXTRA_BOARD_FLAGS=--nodeps')
217    else:
218      build_packages_env = build_packages_env.replace(
219          'EXTRA_BOARD_FLAGS=--onlydeps', 'EXTRA_BOARD_FLAGS=--nodeps')
220    ret = cmd_executer.ChrootRunCommand(
221        options.chromeos_root, 'CFLAGS="$(portageq-%s envvar CFLAGS) %s" '
222        'CXXFLAGS="$(portageq-%s envvar CXXFLAGS) %s" '
223        'LDFLAGS="$(portageq-%s envvar LDFLAGS) %s" '
224        'CHROME_ORIGIN=SERVER_SOURCE BUILDTYPE=Debug '
225        '%s '
226        '%s --skip_chroot_upgrade'
227        'chromeos-chrome' % (options.board, options.cflags, options.board,
228                             options.cxxflags, options.board, options.ldflags,
229                             build_packages_env, build_packages_command))
230    logger.GetLogger().LogFatalIf(
231        ret,
232        'build_packages failed while trying to build debug chromeos-chrome.')
233
234    # Now, we have built chromeos-chrome and all dependencies.
235    # Finally, remove '-e' from EXTRA_BOARD_FLAGS,
236    # otherwise, chromeos-chrome gets rebuilt.
237    build_packages_env = build_packages_env.replace(\
238      'EXTRA_BOARD_FLAGS=--nodeps', '')
239
240    # Up to now, we have a debug built chromos-chrome browser.
241    # Fall through to build the rest of the world.
242
243  # Build packages
244  ret = cmd_executer.ChrootRunCommand(
245      options.chromeos_root, 'CFLAGS="$(portageq-%s envvar CFLAGS) %s" '
246      'CXXFLAGS="$(portageq-%s envvar CXXFLAGS) %s" '
247      'LDFLAGS="$(portageq-%s envvar LDFLAGS) %s" '
248      'CHROME_ORIGIN=SERVER_SOURCE '
249      '%s '
250      '%s --skip_chroot_upgrade' %
251      (options.board, options.cflags, options.board, options.cxxflags,
252       options.board, options.ldflags, build_packages_env,
253       build_packages_command))
254
255  logger.GetLogger().LogFatalIf(ret, 'build_packages failed')
256  if options.package:
257    return 0
258  # Build image
259  ret = cmd_executer.ChrootRunCommand(
260      options.chromeos_root, build_packages_env + ' ' + build_image_command)
261
262  logger.GetLogger().LogFatalIf(ret, 'build_image failed')
263
264  flags_file_name = 'flags.txt'
265  flags_file_path = ('%s/src/build/images/%s/latest/%s' %
266                     (options.chromeos_root, options.board, flags_file_name))
267  with open(flags_file_path, 'w', encoding='utf-8') as flags_file:
268    flags_file.write('CFLAGS=%s\n' % options.cflags)
269    flags_file.write('CXXFLAGS=%s\n' % options.cxxflags)
270    flags_file.write('LDFLAGS=%s\n' % options.ldflags)
271
272  if options.label:
273    image_dir_path = ('%s/src/build/images/%s/latest' % (options.chromeos_root,
274                                                         options.board))
275    real_image_dir_path = os.path.realpath(image_dir_path)
276    command = ('ln -sf -T %s %s/%s' % (os.path.basename(real_image_dir_path),
277                                       os.path.dirname(real_image_dir_path),
278                                       options.label))
279
280    ret = cmd_executer.RunCommand(command)
281    logger.GetLogger().LogFatalIf(
282        ret, 'Failed to apply symlink label %s' % options.label)
283
284  return ret
285
286
287if __name__ == '__main__':
288  retval = Main(sys.argv)
289  sys.exit(retval)
290