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