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