build_tc.py revision 215c12ff93f89edcf42fa744e9767ff606d4df0f
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._gcc_libs_dest = misc.GetGccLibsDestForBoard(self._board, 36 self._chromeos_root) 37 self.tag = "%s-%s" % (name, self._ctarget) 38 self._ce = command_executer.GetCommandExecuter() 39 self._mask_file = os.path.join( 40 self._chromeos_root, 41 "chroot", 42 "etc/portage/package.mask/cross-%s" % self._ctarget) 43 self._new_mask_file = None 44 45 self._chroot_source_path = os.path.join(constants.MOUNTED_TOOLCHAIN_ROOT, 46 self._name).lstrip("/") 47 self._incremental = incremental 48 self._build_env = build_env 49 self._gcc_enable_ccache = gcc_enable_ccache 50 51 def RunSetupBoardIfNecessary(self): 52 cross_symlink = os.path.join( 53 self._chromeos_root, 54 "chroot", 55 "usr/local/bin/emerge-%s" % self._board) 56 if not os.path.exists(cross_symlink): 57 command = ("%s/setup_board --board=%s" % 58 (misc.CHROMEOS_SCRIPTS_DIR, self._board)) 59 self._ce.ChrootRunCommand(self._chromeos_root, command) 60 61 def Build(self): 62 rv = 1 63 try: 64 self.UninstallTool() 65 self.MoveMaskFile() 66 self.MountSources(False) 67 self.RemoveCompiledFile() 68 rv = self.BuildTool() 69 finally: 70 self.UnMoveMaskFile() 71 return rv 72 73 def RemoveCompiledFile(self): 74 compiled_file = os.path.join(self._chromeos_root, 75 "chroot", 76 "var/tmp/portage/cross-%s" % self._ctarget, 77 "%s-9999" % self._name, 78 ".compiled") 79 command = "rm -f %s" % compiled_file 80 self._ce.RunCommand(command) 81 82 def MountSources(self, unmount_source): 83 mount_points = [] 84 mounted_source_path = os.path.join(self._chromeos_root, 85 "chroot", 86 self._chroot_source_path) 87 src_mp = tc_enter_chroot.MountPoint( 88 self._source_path, 89 mounted_source_path, 90 getpass.getuser(), 91 "ro") 92 mount_points.append(src_mp) 93 94 build_suffix = "build-%s" % self._ctarget 95 build_dir = "%s-%s" % (self._source_path, build_suffix) 96 97 if not self._incremental and os.path.exists(build_dir): 98 command = "rm -rf %s/*" % build_dir 99 self._ce.RunCommand(command) 100 101 # Create a -build directory for the objects. 102 command = "mkdir -p %s" % build_dir 103 self._ce.RunCommand(command) 104 105 mounted_build_dir = os.path.join( 106 self._chromeos_root, "chroot", "%s-%s" % 107 (self._chroot_source_path, build_suffix)) 108 build_mp = tc_enter_chroot.MountPoint( 109 build_dir, 110 mounted_build_dir, 111 getpass.getuser()) 112 mount_points.append(build_mp) 113 114 if unmount_source: 115 unmount_statuses = [mp.UnMount() == 0 for mp in mount_points] 116 assert all(unmount_statuses), "Could not unmount all mount points!" 117 else: 118 mount_statuses = [mp.DoMount() == 0 for mp in mount_points] 119 120 if not all(mount_statuses): 121 mounted = [mp for mp, status in zip(mount_points, mount_statuses) if status] 122 unmount_statuses = [mp.UnMount() == 0 for mp in mounted] 123 assert all(unmount_statuses), "Could not unmount all mount points!" 124 125 126 def UninstallTool(self): 127 command = "sudo CLEAN_DELAY=0 emerge -C cross-%s/%s" % (self._ctarget, self._name) 128 self._ce.ChrootRunCommand(self._chromeos_root, command) 129 130 def BuildTool(self): 131 env = self._build_env 132 # FEATURES=buildpkg adds minutes of time so we disable it. 133 # TODO(shenhan): keep '-sandbox' for a while for compatibility, then remove 134 # it after a while. 135 features = "nostrip userpriv userfetch -usersandbox -sandbox noclean -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" % 164 (self._ctarget, 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 = optparse.OptionParser() 185 parser.add_option("-c", 186 "--chromeos_root", 187 dest="chromeos_root", 188 default="../../", 189 help=("ChromeOS root checkout directory" 190 " uses ../.. if none given.")) 191 parser.add_option("-g", 192 "--gcc_dir", 193 dest="gcc_dir", 194 help="The directory where gcc resides.") 195 parser.add_option("--binutils_dir", 196 dest="binutils_dir", 197 help="The directory where binutils resides.") 198 parser.add_option("-x", 199 "--gdb_dir", 200 dest="gdb_dir", 201 help="The directory where gdb resides.") 202 parser.add_option("-b", 203 "--board", 204 dest="board", 205 default="x86-agz", 206 help="The target board.") 207 parser.add_option("-n", 208 "--noincremental", 209 dest="noincremental", 210 default=False, 211 action="store_true", 212 help="Use FEATURES=keepwork to do incremental builds.") 213 parser.add_option("--cflags", 214 dest="cflags", 215 default="", 216 help="Build a compiler with specified CFLAGS") 217 parser.add_option("--cxxflags", 218 dest="cxxflags", 219 default="", 220 help="Build a compiler with specified CXXFLAGS") 221 parser.add_option("--cflags_for_target", 222 dest="cflags_for_target", 223 default="", 224 help="Build the target libraries with specified flags") 225 parser.add_option("--cxxflags_for_target", 226 dest="cxxflags_for_target", 227 default="", 228 help="Build the target libraries with specified flags") 229 parser.add_option("--ldflags", 230 dest="ldflags", 231 default="", 232 help="Build a compiler with specified LDFLAGS") 233 parser.add_option("-d", 234 "--debug", 235 dest="debug", 236 default=False, 237 action="store_true", 238 help="Build a compiler with -g3 -O0 appended to both" 239 " CFLAGS and CXXFLAGS.") 240 parser.add_option("-m", 241 "--mount_only", 242 dest="mount_only", 243 default=False, 244 action="store_true", 245 help="Just mount the tool directories.") 246 parser.add_option("-u", 247 "--unmount_only", 248 dest="unmount_only", 249 default=False, 250 action="store_true", 251 help="Just unmount the tool directories.") 252 parser.add_option("--extra_use_flags", 253 dest="extra_use_flags", 254 default="", 255 help="Extra flag for USE, to be passed to the ebuild. " 256 "('multislot' and 'mounted_<tool>' are always passed.)") 257 parser.add_option("--gcc_enable_ccache", 258 dest="gcc_enable_ccache", 259 default=False, 260 action="store_true", 261 help="Enable ccache for the gcc invocations") 262 263 options, _ = parser.parse_args(argv) 264 265 chromeos_root = misc.CanonicalizePath(options.chromeos_root) 266 if options.gcc_dir: 267 gcc_dir = misc.CanonicalizePath(options.gcc_dir) 268 assert gcc_dir and os.path.isdir(gcc_dir), "gcc_dir does not exist!" 269 if options.binutils_dir: 270 binutils_dir = misc.CanonicalizePath(options.binutils_dir) 271 assert os.path.isdir(binutils_dir), "binutils_dir does not exist!" 272 if options.gdb_dir: 273 gdb_dir = misc.CanonicalizePath(options.gdb_dir) 274 assert os.path.isdir(gdb_dir), "gdb_dir does not exist!" 275 if options.unmount_only: 276 options.mount_only = False 277 elif options.mount_only: 278 options.unmount_only = False 279 build_env = {} 280 if options.cflags: 281 build_env["CFLAGS"] = "`portageq envvar CFLAGS` " + options.cflags 282 if options.cxxflags: 283 build_env["CXXFLAGS"] = "`portageq envvar CXXFLAGS` " + options.cxxflags 284 if options.cflags_for_target: 285 build_env["CFLAGS_FOR_TARGET"] = options.cflags_for_target 286 if options.cxxflags_for_target: 287 build_env["CXXFLAGS_FOR_TARGET"] = options.cxxflags_for_target 288 if options.ldflags: 289 build_env["LDFLAGS"] = options.ldflags 290 if options.debug: 291 debug_flags = "-g3 -O0" 292 if "CFLAGS" in build_env: 293 build_env["CFLAGS"] += " %s" % (debug_flags) 294 else: 295 build_env["CFLAGS"] = debug_flags 296 if "CXXFLAGS" in build_env: 297 build_env["CXXFLAGS"] += " %s" % (debug_flags) 298 else: 299 build_env["CXXFLAGS"] = debug_flags 300 if options.extra_use_flags: 301 build_env["USE"] = options.extra_use_flags 302 303 # Create toolchain parts 304 toolchain_parts = {} 305 for board in options.board.split(","): 306 if options.gcc_dir: 307 tp = ToolchainPart("gcc", gcc_dir, chromeos_root, board, 308 not options.noincremental, build_env, 309 options.gcc_enable_ccache) 310 toolchain_parts[tp.tag] = tp 311 tp.RunSetupBoardIfNecessary() 312 if options.binutils_dir: 313 tp = ToolchainPart("binutils", binutils_dir, chromeos_root, board, 314 not options.noincremental, build_env) 315 toolchain_parts[tp.tag] = tp 316 tp.RunSetupBoardIfNecessary() 317 if options.gdb_dir: 318 tp = ToolchainPart("gdb", gdb_dir, chromeos_root, board, 319 not options.noincremental, build_env) 320 toolchain_parts[tp.tag] = tp 321 tp.RunSetupBoardIfNecessary() 322 323 rv = 0 324 try: 325 for tag in toolchain_parts: 326 tp = toolchain_parts[tag] 327 if options.mount_only or options.unmount_only: 328 tp.MountSources(options.unmount_only) 329 else: 330 rv = rv + tp.Build() 331 finally: 332 print "Exiting..." 333 return rv 334 335if __name__ == "__main__": 336 retval = Main(sys.argv) 337 sys.exit(retval) 338