blob: 08f80e69cc4f9457103b95572160af859d4f9fb3 [file] [log] [blame]
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright 2010 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Script to build the ChromeOS toolchain.
This script sets up the toolchain if you give it the gcctools directory.
"""
__author__ = "asharif@google.com (Ahmad Sharif)"
import argparse
import getpass
import os
import sys
import tempfile
from cros_utils import command_executer
from cros_utils import constants
from cros_utils import misc
import tc_enter_chroot
class ToolchainPart(object):
"""Class to hold the toolchain pieces."""
def __init__(
self,
name,
source_path,
chromeos_root,
board,
incremental,
build_env,
gcc_enable_ccache=False,
):
self._name = name
self._source_path = misc.CanonicalizePath(source_path)
self._chromeos_root = chromeos_root
self._board = board
self._ctarget = misc.GetCtargetFromBoard(
self._board, self._chromeos_root
)
self._gcc_libs_dest = misc.GetGccLibsDestForBoard(
self._board, self._chromeos_root
)
self.tag = "%s-%s" % (name, self._ctarget)
self._ce = command_executer.GetCommandExecuter()
self._mask_file = os.path.join(
self._chromeos_root,
"chroot",
"etc/portage/package.mask/cross-%s" % self._ctarget,
)
self._new_mask_file = None
self._chroot_source_path = os.path.join(
constants.MOUNTED_TOOLCHAIN_ROOT, self._name
).lstrip("/")
self._incremental = incremental
self._build_env = build_env
self._gcc_enable_ccache = gcc_enable_ccache
def RunSetupBoardIfNecessary(self):
cross_symlink = os.path.join(
self._chromeos_root,
"chroot",
"usr/local/bin/emerge-%s" % self._board,
)
if not os.path.exists(cross_symlink):
command = "setup_board --board=%s" % self._board
self._ce.ChrootRunCommand(self._chromeos_root, command)
def Build(self):
rv = 1
try:
self.UninstallTool()
self.MoveMaskFile()
self.MountSources(False)
self.RemoveCompiledFile()
rv = self.BuildTool()
finally:
self.UnMoveMaskFile()
return rv
def RemoveCompiledFile(self):
compiled_file = os.path.join(
self._chromeos_root,
"chroot",
"var/tmp/portage/cross-%s" % self._ctarget,
"%s-9999" % self._name,
".compiled",
)
command = "rm -f %s" % compiled_file
self._ce.RunCommand(command)
def MountSources(self, unmount_source):
mount_points = []
mounted_source_path = os.path.join(
self._chromeos_root, "chroot", self._chroot_source_path
)
src_mp = tc_enter_chroot.MountPoint(
self._source_path, mounted_source_path, getpass.getuser(), "ro"
)
mount_points.append(src_mp)
build_suffix = "build-%s" % self._ctarget
build_dir = "%s-%s" % (self._source_path, build_suffix)
if not self._incremental and os.path.exists(build_dir):
command = "rm -rf %s/*" % build_dir
self._ce.RunCommand(command)
# Create a -build directory for the objects.
command = "mkdir -p %s" % build_dir
self._ce.RunCommand(command)
mounted_build_dir = os.path.join(
self._chromeos_root,
"chroot",
"%s-%s" % (self._chroot_source_path, build_suffix),
)
build_mp = tc_enter_chroot.MountPoint(
build_dir, mounted_build_dir, getpass.getuser()
)
mount_points.append(build_mp)
if unmount_source:
unmount_statuses = [mp.UnMount() == 0 for mp in mount_points]
assert all(unmount_statuses), "Could not unmount all mount points!"
else:
mount_statuses = [mp.DoMount() == 0 for mp in mount_points]
if not all(mount_statuses):
mounted = [
mp
for mp, status in zip(mount_points, mount_statuses)
if status
]
unmount_statuses = [mp.UnMount() == 0 for mp in mounted]
assert all(
unmount_statuses
), "Could not unmount all mount points!"
def UninstallTool(self):
command = "sudo CLEAN_DELAY=0 emerge -C cross-%s/%s" % (
self._ctarget,
self._name,
)
self._ce.ChrootRunCommand(self._chromeos_root, command)
def BuildTool(self):
env = self._build_env
# FEATURES=buildpkg adds minutes of time so we disable it.
# TODO(shenhan): keep '-sandbox' for a while for compatibility, then remove
# it after a while.
features = (
"nostrip userpriv userfetch -usersandbox -sandbox noclean "
"-buildpkg"
)
env["FEATURES"] = features
if self._incremental:
env["FEATURES"] += " keepwork"
if "USE" in env:
env["USE"] += " multislot mounted_%s" % self._name
else:
env["USE"] = "multislot mounted_%s" % self._name
# Disable ccache in our compilers. cache may be problematic for us.
# It ignores compiler environments settings and it is not clear if
# the cache hit algorithm verifies all the compiler binaries or
# just the driver.
if self._name == "gcc" and not self._gcc_enable_ccache:
env["USE"] += " -wrapper_ccache"
env["%s_SOURCE_PATH" % self._name.upper()] = os.path.join(
"/", self._chroot_source_path
)
env["ACCEPT_KEYWORDS"] = "~*"
env_string = " ".join(['%s="%s"' % var for var in env.items()])
command = "emerge =cross-%s/%s-9999" % (self._ctarget, self._name)
full_command = "sudo %s %s" % (env_string, command)
rv = self._ce.ChrootRunCommand(self._chromeos_root, full_command)
if rv != 0:
return rv
if self._name == "gcc":
command = "sudo cp -r /usr/lib/gcc/%s %s" % (
self._ctarget,
self._gcc_libs_dest,
)
rv = self._ce.ChrootRunCommand(self._chromeos_root, command)
return rv
def MoveMaskFile(self):
self._new_mask_file = None
if os.path.isfile(self._mask_file):
self._new_mask_file = tempfile.mktemp()
command = "sudo mv %s %s" % (self._mask_file, self._new_mask_file)
self._ce.RunCommand(command)
def UnMoveMaskFile(self):
if self._new_mask_file:
command = "sudo mv %s %s" % (self._new_mask_file, self._mask_file)
self._ce.RunCommand(command)
def Main(argv):
"""The main function."""
# Common initializations
parser = argparse.ArgumentParser()
parser.add_argument(
"-c",
"--chromeos_root",
dest="chromeos_root",
default="../../",
help=("ChromeOS root checkout directory" " uses ../.. if none given."),
)
parser.add_argument(
"-g",
"--gcc_dir",
dest="gcc_dir",
help="The directory where gcc resides.",
)
parser.add_argument(
"--binutils_dir",
dest="binutils_dir",
help="The directory where binutils resides.",
)
parser.add_argument(
"-x",
"--gdb_dir",
dest="gdb_dir",
help="The directory where gdb resides.",
)
parser.add_argument(
"-b",
"--board",
dest="board",
default="x86-alex",
help="The target board.",
)
parser.add_argument(
"-n",
"--noincremental",
dest="noincremental",
default=False,
action="store_true",
help="Use FEATURES=keepwork to do incremental builds.",
)
parser.add_argument(
"--cflags",
dest="cflags",
default="",
help="Build a compiler with specified CFLAGS",
)
parser.add_argument(
"--cxxflags",
dest="cxxflags",
default="",
help="Build a compiler with specified CXXFLAGS",
)
parser.add_argument(
"--cflags_for_target",
dest="cflags_for_target",
default="",
help="Build the target libraries with specified flags",
)
parser.add_argument(
"--cxxflags_for_target",
dest="cxxflags_for_target",
default="",
help="Build the target libraries with specified flags",
)
parser.add_argument(
"--ldflags",
dest="ldflags",
default="",
help="Build a compiler with specified LDFLAGS",
)
parser.add_argument(
"-d",
"--debug",
dest="debug",
default=False,
action="store_true",
help="Build a compiler with -g3 -O0 appended to both"
" CFLAGS and CXXFLAGS.",
)
parser.add_argument(
"-m",
"--mount_only",
dest="mount_only",
default=False,
action="store_true",
help="Just mount the tool directories.",
)
parser.add_argument(
"-u",
"--unmount_only",
dest="unmount_only",
default=False,
action="store_true",
help="Just unmount the tool directories.",
)
parser.add_argument(
"--extra_use_flags",
dest="extra_use_flags",
default="",
help="Extra flag for USE, to be passed to the ebuild. "
"('multislot' and 'mounted_<tool>' are always passed.)",
)
parser.add_argument(
"--gcc_enable_ccache",
dest="gcc_enable_ccache",
default=False,
action="store_true",
help="Enable ccache for the gcc invocations",
)
options = parser.parse_args(argv)
chromeos_root = misc.CanonicalizePath(options.chromeos_root)
if options.gcc_dir:
gcc_dir = misc.CanonicalizePath(options.gcc_dir)
assert gcc_dir and os.path.isdir(gcc_dir), "gcc_dir does not exist!"
if options.binutils_dir:
binutils_dir = misc.CanonicalizePath(options.binutils_dir)
assert os.path.isdir(binutils_dir), "binutils_dir does not exist!"
if options.gdb_dir:
gdb_dir = misc.CanonicalizePath(options.gdb_dir)
assert os.path.isdir(gdb_dir), "gdb_dir does not exist!"
if options.unmount_only:
options.mount_only = False
elif options.mount_only:
options.unmount_only = False
build_env = {}
if options.cflags:
build_env["CFLAGS"] = "`portageq envvar CFLAGS` " + options.cflags
if options.cxxflags:
build_env["CXXFLAGS"] = "`portageq envvar CXXFLAGS` " + options.cxxflags
if options.cflags_for_target:
build_env["CFLAGS_FOR_TARGET"] = options.cflags_for_target
if options.cxxflags_for_target:
build_env["CXXFLAGS_FOR_TARGET"] = options.cxxflags_for_target
if options.ldflags:
build_env["LDFLAGS"] = options.ldflags
if options.debug:
debug_flags = "-g3 -O0"
if "CFLAGS" in build_env:
build_env["CFLAGS"] += " %s" % (debug_flags)
else:
build_env["CFLAGS"] = debug_flags
if "CXXFLAGS" in build_env:
build_env["CXXFLAGS"] += " %s" % (debug_flags)
else:
build_env["CXXFLAGS"] = debug_flags
if options.extra_use_flags:
build_env["USE"] = options.extra_use_flags
# Create toolchain parts
toolchain_parts = {}
for board in options.board.split(","):
if options.gcc_dir:
tp = ToolchainPart(
"gcc",
gcc_dir,
chromeos_root,
board,
not options.noincremental,
build_env,
options.gcc_enable_ccache,
)
toolchain_parts[tp.tag] = tp
tp.RunSetupBoardIfNecessary()
if options.binutils_dir:
tp = ToolchainPart(
"binutils",
binutils_dir,
chromeos_root,
board,
not options.noincremental,
build_env,
)
toolchain_parts[tp.tag] = tp
tp.RunSetupBoardIfNecessary()
if options.gdb_dir:
tp = ToolchainPart(
"gdb",
gdb_dir,
chromeos_root,
board,
not options.noincremental,
build_env,
)
toolchain_parts[tp.tag] = tp
tp.RunSetupBoardIfNecessary()
rv = 0
try:
for tag in toolchain_parts:
tp = toolchain_parts[tag]
if options.mount_only or options.unmount_only:
tp.MountSources(options.unmount_only)
else:
rv = rv + tp.Build()
finally:
print("Exiting...")
return rv
if __name__ == "__main__":
retval = Main(sys.argv[1:])
sys.exit(retval)