build_tc.py revision e4b6f220f3cc7d3277daca7bca25f89d648dae8e
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 rv = self._ce.ChrootRunCommand(self._chromeos_root, full_command) 155 if rv != 0: 156 return rv 157 if self._name == "gcc": 158 command = ("sudo cp -r /usr/lib/gcc/%s /build/%s/usr/lib/gcc/." % 159 (self._ctarget, self._board)) 160 rv = self._ce.ChrootRunCommand(self._chromeos_root, command) 161 return rv 162 163 def MoveMaskFile(self): 164 self._new_mask_file = None 165 if os.path.isfile(self._mask_file): 166 self._new_mask_file = tempfile.mktemp() 167 command = "sudo mv %s %s" % (self._mask_file, self._new_mask_file) 168 self._ce.RunCommand(command) 169 170 def UnMoveMaskFile(self): 171 if self._new_mask_file: 172 command = "sudo mv %s %s" % (self._new_mask_file, self._mask_file) 173 self._ce.RunCommand(command) 174 175 176def Main(argv): 177 """The main function.""" 178 # Common initializations 179 parser = optparse.OptionParser() 180 parser.add_option("-c", 181 "--chromeos_root", 182 dest="chromeos_root", 183 default="../../", 184 help=("ChromeOS root checkout directory" 185 " uses ../.. if none given.")) 186 parser.add_option("-g", 187 "--gcc_dir", 188 dest="gcc_dir", 189 help="The directory where gcc resides.") 190 parser.add_option("--binutils_dir", 191 dest="binutils_dir", 192 help="The directory where binutils resides.") 193 parser.add_option("-x", 194 "--gdb_dir", 195 dest="gdb_dir", 196 help="The directory where gdb resides.") 197 parser.add_option("-b", 198 "--board", 199 dest="board", 200 default="x86-agz", 201 help="The target board.") 202 parser.add_option("-n", 203 "--noincremental", 204 dest="noincremental", 205 default=False, 206 action="store_true", 207 help="Use FEATURES=keepwork to do incremental builds.") 208 parser.add_option("--cflags", 209 dest="cflags", 210 default="", 211 help="Build a compiler with specified CFLAGS") 212 parser.add_option("--cxxflags", 213 dest="cxxflags", 214 default="", 215 help="Build a compiler with specified CXXFLAGS") 216 parser.add_option("--cflags_for_target", 217 dest="cflags_for_target", 218 default="", 219 help="Build the target libraries with specified flags") 220 parser.add_option("--cxxflags_for_target", 221 dest="cxxflags_for_target", 222 default="", 223 help="Build the target libraries with specified flags") 224 parser.add_option("--ldflags", 225 dest="ldflags", 226 default="", 227 help="Build a compiler with specified LDFLAGS") 228 parser.add_option("-d", 229 "--debug", 230 dest="debug", 231 default=False, 232 action="store_true", 233 help="Build a compiler with -g3 -O0 appended to both" 234 " CFLAGS and CXXFLAGS.") 235 parser.add_option("-m", 236 "--mount_only", 237 dest="mount_only", 238 default=False, 239 action="store_true", 240 help="Just mount the tool directories.") 241 parser.add_option("-u", 242 "--unmount_only", 243 dest="unmount_only", 244 default=False, 245 action="store_true", 246 help="Just unmount the tool directories.") 247 parser.add_option("--extra_use_flags", 248 dest="extra_use_flags", 249 default="", 250 help="Extra flag for USE, to be passed to the ebuild. " 251 "('multislot' and 'mounted_<tool>' are always passed.)") 252 parser.add_option("--gcc_enable_ccache", 253 dest="gcc_enable_ccache", 254 default=False, 255 action="store_true", 256 help="Enable ccache for the gcc invocations") 257 258 options, _ = parser.parse_args(argv) 259 260 chromeos_root = misc.CanonicalizePath(options.chromeos_root) 261 if options.gcc_dir: 262 gcc_dir = misc.CanonicalizePath(options.gcc_dir) 263 assert gcc_dir and os.path.isdir(gcc_dir), "gcc_dir does not exist!" 264 if options.binutils_dir: 265 binutils_dir = misc.CanonicalizePath(options.binutils_dir) 266 assert os.path.isdir(binutils_dir), "binutils_dir does not exist!" 267 if options.gdb_dir: 268 gdb_dir = misc.CanonicalizePath(options.gdb_dir) 269 assert os.path.isdir(gdb_dir), "gdb_dir does not exist!" 270 if options.unmount_only: 271 options.mount_only = False 272 elif options.mount_only: 273 options.unmount_only = False 274 build_env = {} 275 if options.cflags: 276 build_env["CFLAGS"] = "`portageq envvar CFLAGS` " + options.cflags 277 if options.cxxflags: 278 build_env["CXXFLAGS"] = "`portageq envvar CXXFLAGS` " + options.cxxflags 279 if options.cflags_for_target: 280 build_env["CFLAGS_FOR_TARGET"] = options.cflags_for_target 281 if options.cxxflags_for_target: 282 build_env["CXXFLAGS_FOR_TARGET"] = options.cxxflags_for_target 283 if options.ldflags: 284 build_env["LDFLAGS"] = options.ldflags 285 if options.debug: 286 debug_flags = "-g3 -O0" 287 if "CFLAGS" in build_env: 288 build_env["CFLAGS"] += " %s" % (debug_flags) 289 else: 290 build_env["CFLAGS"] = debug_flags 291 if "CXXFLAGS" in build_env: 292 build_env["CXXFLAGS"] += " %s" % (debug_flags) 293 else: 294 build_env["CXXFLAGS"] = debug_flags 295 if options.extra_use_flags: 296 build_env["USE"] = options.extra_use_flags 297 298 # Create toolchain parts 299 toolchain_parts = {} 300 for board in options.board.split(","): 301 if options.gcc_dir: 302 tp = ToolchainPart("gcc", gcc_dir, chromeos_root, board, 303 not options.noincremental, build_env, 304 options.gcc_enable_ccache) 305 toolchain_parts[tp.tag] = tp 306 tp.RunSetupBoardIfNecessary() 307 if options.binutils_dir: 308 tp = ToolchainPart("binutils", binutils_dir, chromeos_root, board, 309 not options.noincremental, build_env) 310 toolchain_parts[tp.tag] = tp 311 tp.RunSetupBoardIfNecessary() 312 if options.gdb_dir: 313 tp = ToolchainPart("gdb", gdb_dir, chromeos_root, board, 314 not options.noincremental, build_env) 315 toolchain_parts[tp.tag] = tp 316 tp.RunSetupBoardIfNecessary() 317 318 rv = 0 319 try: 320 for tag in toolchain_parts: 321 tp = toolchain_parts[tag] 322 if options.mount_only or options.unmount_only: 323 tp.MountSources(options.unmount_only) 324 else: 325 rv = rv + tp.Build() 326 finally: 327 print "Exiting..." 328 return rv 329 330if __name__ == "__main__": 331 retval = Main(sys.argv) 332 sys.exit(retval) 333