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