• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3# Copyright 2010 The Chromium OS Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7"""Script to build the ChromeOS toolchain.
8
9This script sets up the toolchain if you give it the gcctools directory.
10"""
11
12from __future__ import print_function
13
14__author__ = 'asharif@google.com (Ahmad Sharif)'
15
16import argparse
17import getpass
18import os
19import sys
20import tempfile
21
22import tc_enter_chroot
23from cros_utils import command_executer
24from cros_utils import constants
25from cros_utils import misc
26
27
28class ToolchainPart(object):
29  """Class to hold the toolchain pieces."""
30
31  def __init__(self,
32               name,
33               source_path,
34               chromeos_root,
35               board,
36               incremental,
37               build_env,
38               gcc_enable_ccache=False):
39    self._name = name
40    self._source_path = misc.CanonicalizePath(source_path)
41    self._chromeos_root = chromeos_root
42    self._board = board
43    self._ctarget = misc.GetCtargetFromBoard(self._board, self._chromeos_root)
44    self._gcc_libs_dest = misc.GetGccLibsDestForBoard(self._board,
45                                                      self._chromeos_root)
46    self.tag = '%s-%s' % (name, self._ctarget)
47    self._ce = command_executer.GetCommandExecuter()
48    self._mask_file = os.path.join(
49        self._chromeos_root, 'chroot',
50        'etc/portage/package.mask/cross-%s' % self._ctarget)
51    self._new_mask_file = None
52
53    self._chroot_source_path = os.path.join(constants.MOUNTED_TOOLCHAIN_ROOT,
54                                            self._name).lstrip('/')
55    self._incremental = incremental
56    self._build_env = build_env
57    self._gcc_enable_ccache = gcc_enable_ccache
58
59  def RunSetupBoardIfNecessary(self):
60    cross_symlink = os.path.join(self._chromeos_root, 'chroot',
61                                 'usr/local/bin/emerge-%s' % self._board)
62    if not os.path.exists(cross_symlink):
63      command = 'setup_board --board=%s' % self._board
64      self._ce.ChrootRunCommand(self._chromeos_root, command)
65
66  def Build(self):
67    rv = 1
68    try:
69      self.UninstallTool()
70      self.MoveMaskFile()
71      self.MountSources(False)
72      self.RemoveCompiledFile()
73      rv = self.BuildTool()
74    finally:
75      self.UnMoveMaskFile()
76    return rv
77
78  def RemoveCompiledFile(self):
79    compiled_file = os.path.join(self._chromeos_root, 'chroot',
80                                 'var/tmp/portage/cross-%s' % self._ctarget,
81                                 '%s-9999' % self._name, '.compiled')
82    command = 'rm -f %s' % compiled_file
83    self._ce.RunCommand(command)
84
85  def MountSources(self, unmount_source):
86    mount_points = []
87    mounted_source_path = os.path.join(self._chromeos_root, 'chroot',
88                                       self._chroot_source_path)
89    src_mp = tc_enter_chroot.MountPoint(self._source_path, mounted_source_path,
90                                        getpass.getuser(), 'ro')
91    mount_points.append(src_mp)
92
93    build_suffix = 'build-%s' % self._ctarget
94    build_dir = '%s-%s' % (self._source_path, build_suffix)
95
96    if not self._incremental and os.path.exists(build_dir):
97      command = 'rm -rf %s/*' % build_dir
98      self._ce.RunCommand(command)
99
100    # Create a -build directory for the objects.
101    command = 'mkdir -p %s' % build_dir
102    self._ce.RunCommand(command)
103
104    mounted_build_dir = os.path.join(
105        self._chromeos_root, 'chroot',
106        '%s-%s' % (self._chroot_source_path, build_suffix))
107    build_mp = tc_enter_chroot.MountPoint(build_dir, mounted_build_dir,
108                                          getpass.getuser())
109    mount_points.append(build_mp)
110
111    if unmount_source:
112      unmount_statuses = [mp.UnMount() == 0 for mp in mount_points]
113      assert all(unmount_statuses), 'Could not unmount all mount points!'
114    else:
115      mount_statuses = [mp.DoMount() == 0 for mp in mount_points]
116
117      if not all(mount_statuses):
118        mounted = [
119            mp for mp, status in zip(mount_points, mount_statuses) if status
120        ]
121        unmount_statuses = [mp.UnMount() == 0 for mp in mounted]
122        assert all(unmount_statuses), 'Could not unmount all mount points!'
123
124  def UninstallTool(self):
125    command = 'sudo CLEAN_DELAY=0 emerge -C cross-%s/%s' % (self._ctarget,
126                                                            self._name)
127    self._ce.ChrootRunCommand(self._chromeos_root, command)
128
129  def BuildTool(self):
130    env = self._build_env
131    # FEATURES=buildpkg adds minutes of time so we disable it.
132    # TODO(shenhan): keep '-sandbox' for a while for compatibility, then remove
133    # it after a while.
134    features = ('nostrip userpriv userfetch -usersandbox -sandbox noclean '
135                '-buildpkg')
136    env['FEATURES'] = features
137
138    if self._incremental:
139      env['FEATURES'] += ' keepwork'
140
141    if 'USE' in env:
142      env['USE'] += ' multislot mounted_%s' % self._name
143    else:
144      env['USE'] = 'multislot mounted_%s' % self._name
145
146    # Disable ccache in our compilers. cache may be problematic for us.
147    # It ignores compiler environments settings and it is not clear if
148    # the cache hit algorithm verifies all the compiler binaries or
149    # just the driver.
150    if self._name == 'gcc' and not self._gcc_enable_ccache:
151      env['USE'] += ' -wrapper_ccache'
152
153    env['%s_SOURCE_PATH' % self._name.upper()] = (
154        os.path.join('/', self._chroot_source_path))
155    env['ACCEPT_KEYWORDS'] = '~*'
156    env_string = ' '.join(['%s="%s"' % var for var in env.items()])
157    command = 'emerge =cross-%s/%s-9999' % (self._ctarget, self._name)
158    full_command = 'sudo %s %s' % (env_string, command)
159    rv = self._ce.ChrootRunCommand(self._chromeos_root, full_command)
160    if rv != 0:
161      return rv
162    if self._name == 'gcc':
163      command = ('sudo cp -r /usr/lib/gcc/%s %s' % (self._ctarget,
164                                                    self._gcc_libs_dest))
165      rv = self._ce.ChrootRunCommand(self._chromeos_root, command)
166    return rv
167
168  def MoveMaskFile(self):
169    self._new_mask_file = None
170    if os.path.isfile(self._mask_file):
171      self._new_mask_file = tempfile.mktemp()
172      command = 'sudo mv %s %s' % (self._mask_file, self._new_mask_file)
173      self._ce.RunCommand(command)
174
175  def UnMoveMaskFile(self):
176    if self._new_mask_file:
177      command = 'sudo mv %s %s' % (self._new_mask_file, self._mask_file)
178      self._ce.RunCommand(command)
179
180
181def Main(argv):
182  """The main function."""
183  # Common initializations
184  parser = argparse.ArgumentParser()
185  parser.add_argument(
186      '-c',
187      '--chromeos_root',
188      dest='chromeos_root',
189      default='../../',
190      help=('ChromeOS root checkout directory'
191            ' uses ../.. if none given.'))
192  parser.add_argument(
193      '-g',
194      '--gcc_dir',
195      dest='gcc_dir',
196      help='The directory where gcc resides.')
197  parser.add_argument(
198      '--binutils_dir',
199      dest='binutils_dir',
200      help='The directory where binutils resides.')
201  parser.add_argument(
202      '-x',
203      '--gdb_dir',
204      dest='gdb_dir',
205      help='The directory where gdb resides.')
206  parser.add_argument(
207      '-b',
208      '--board',
209      dest='board',
210      default='x86-alex',
211      help='The target board.')
212  parser.add_argument(
213      '-n',
214      '--noincremental',
215      dest='noincremental',
216      default=False,
217      action='store_true',
218      help='Use FEATURES=keepwork to do incremental builds.')
219  parser.add_argument(
220      '--cflags',
221      dest='cflags',
222      default='',
223      help='Build a compiler with specified CFLAGS')
224  parser.add_argument(
225      '--cxxflags',
226      dest='cxxflags',
227      default='',
228      help='Build a compiler with specified CXXFLAGS')
229  parser.add_argument(
230      '--cflags_for_target',
231      dest='cflags_for_target',
232      default='',
233      help='Build the target libraries with specified flags')
234  parser.add_argument(
235      '--cxxflags_for_target',
236      dest='cxxflags_for_target',
237      default='',
238      help='Build the target libraries with specified flags')
239  parser.add_argument(
240      '--ldflags',
241      dest='ldflags',
242      default='',
243      help='Build a compiler with specified LDFLAGS')
244  parser.add_argument(
245      '-d',
246      '--debug',
247      dest='debug',
248      default=False,
249      action='store_true',
250      help='Build a compiler with -g3 -O0 appended to both'
251      ' CFLAGS and CXXFLAGS.')
252  parser.add_argument(
253      '-m',
254      '--mount_only',
255      dest='mount_only',
256      default=False,
257      action='store_true',
258      help='Just mount the tool directories.')
259  parser.add_argument(
260      '-u',
261      '--unmount_only',
262      dest='unmount_only',
263      default=False,
264      action='store_true',
265      help='Just unmount the tool directories.')
266  parser.add_argument(
267      '--extra_use_flags',
268      dest='extra_use_flags',
269      default='',
270      help='Extra flag for USE, to be passed to the ebuild. '
271      "('multislot' and 'mounted_<tool>' are always passed.)")
272  parser.add_argument(
273      '--gcc_enable_ccache',
274      dest='gcc_enable_ccache',
275      default=False,
276      action='store_true',
277      help='Enable ccache for the gcc invocations')
278
279  options = parser.parse_args(argv)
280
281  chromeos_root = misc.CanonicalizePath(options.chromeos_root)
282  if options.gcc_dir:
283    gcc_dir = misc.CanonicalizePath(options.gcc_dir)
284    assert gcc_dir and os.path.isdir(gcc_dir), 'gcc_dir does not exist!'
285  if options.binutils_dir:
286    binutils_dir = misc.CanonicalizePath(options.binutils_dir)
287    assert os.path.isdir(binutils_dir), 'binutils_dir does not exist!'
288  if options.gdb_dir:
289    gdb_dir = misc.CanonicalizePath(options.gdb_dir)
290    assert os.path.isdir(gdb_dir), 'gdb_dir does not exist!'
291  if options.unmount_only:
292    options.mount_only = False
293  elif options.mount_only:
294    options.unmount_only = False
295  build_env = {}
296  if options.cflags:
297    build_env['CFLAGS'] = '`portageq envvar CFLAGS` ' + options.cflags
298  if options.cxxflags:
299    build_env['CXXFLAGS'] = '`portageq envvar CXXFLAGS` ' + options.cxxflags
300  if options.cflags_for_target:
301    build_env['CFLAGS_FOR_TARGET'] = options.cflags_for_target
302  if options.cxxflags_for_target:
303    build_env['CXXFLAGS_FOR_TARGET'] = options.cxxflags_for_target
304  if options.ldflags:
305    build_env['LDFLAGS'] = options.ldflags
306  if options.debug:
307    debug_flags = '-g3 -O0'
308    if 'CFLAGS' in build_env:
309      build_env['CFLAGS'] += ' %s' % (debug_flags)
310    else:
311      build_env['CFLAGS'] = debug_flags
312    if 'CXXFLAGS' in build_env:
313      build_env['CXXFLAGS'] += ' %s' % (debug_flags)
314    else:
315      build_env['CXXFLAGS'] = debug_flags
316  if options.extra_use_flags:
317    build_env['USE'] = options.extra_use_flags
318
319  # Create toolchain parts
320  toolchain_parts = {}
321  for board in options.board.split(','):
322    if options.gcc_dir:
323      tp = ToolchainPart('gcc', gcc_dir, chromeos_root, board,
324                         not options.noincremental, build_env,
325                         options.gcc_enable_ccache)
326      toolchain_parts[tp.tag] = tp
327      tp.RunSetupBoardIfNecessary()
328    if options.binutils_dir:
329      tp = ToolchainPart('binutils', binutils_dir, chromeos_root, board,
330                         not options.noincremental, build_env)
331      toolchain_parts[tp.tag] = tp
332      tp.RunSetupBoardIfNecessary()
333    if options.gdb_dir:
334      tp = ToolchainPart('gdb', gdb_dir, chromeos_root, board,
335                         not options.noincremental, build_env)
336      toolchain_parts[tp.tag] = tp
337      tp.RunSetupBoardIfNecessary()
338
339  rv = 0
340  try:
341    for tag in toolchain_parts:
342      tp = toolchain_parts[tag]
343      if options.mount_only or options.unmount_only:
344        tp.MountSources(options.unmount_only)
345      else:
346        rv = rv + tp.Build()
347  finally:
348    print('Exiting...')
349  return rv
350
351
352if __name__ == '__main__':
353  retval = Main(sys.argv[1:])
354  sys.exit(retval)
355