xref: /aosp_15_r20/external/toolchain-utils/build_tc.py (revision 760c253c1ed00ce9abd48f8546f08516e57485fe)
1*760c253cSXin Li#!/usr/bin/env python3
2*760c253cSXin Li# -*- coding: utf-8 -*-
3*760c253cSXin Li# Copyright 2010 The ChromiumOS Authors
4*760c253cSXin Li# Use of this source code is governed by a BSD-style license that can be
5*760c253cSXin Li# found in the LICENSE file.
6*760c253cSXin Li
7*760c253cSXin Li"""Script to build the ChromeOS toolchain.
8*760c253cSXin Li
9*760c253cSXin LiThis script sets up the toolchain if you give it the gcctools directory.
10*760c253cSXin Li"""
11*760c253cSXin Li
12*760c253cSXin Li
13*760c253cSXin Li__author__ = "[email protected] (Ahmad Sharif)"
14*760c253cSXin Li
15*760c253cSXin Liimport argparse
16*760c253cSXin Liimport getpass
17*760c253cSXin Liimport os
18*760c253cSXin Liimport sys
19*760c253cSXin Liimport tempfile
20*760c253cSXin Li
21*760c253cSXin Lifrom cros_utils import command_executer
22*760c253cSXin Lifrom cros_utils import constants
23*760c253cSXin Lifrom cros_utils import misc
24*760c253cSXin Liimport tc_enter_chroot
25*760c253cSXin Li
26*760c253cSXin Li
27*760c253cSXin Liclass ToolchainPart(object):
28*760c253cSXin Li    """Class to hold the toolchain pieces."""
29*760c253cSXin Li
30*760c253cSXin Li    def __init__(
31*760c253cSXin Li        self,
32*760c253cSXin Li        name,
33*760c253cSXin Li        source_path,
34*760c253cSXin Li        chromeos_root,
35*760c253cSXin Li        board,
36*760c253cSXin Li        incremental,
37*760c253cSXin Li        build_env,
38*760c253cSXin Li        gcc_enable_ccache=False,
39*760c253cSXin Li    ):
40*760c253cSXin Li        self._name = name
41*760c253cSXin Li        self._source_path = misc.CanonicalizePath(source_path)
42*760c253cSXin Li        self._chromeos_root = chromeos_root
43*760c253cSXin Li        self._board = board
44*760c253cSXin Li        self._ctarget = misc.GetCtargetFromBoard(
45*760c253cSXin Li            self._board, self._chromeos_root
46*760c253cSXin Li        )
47*760c253cSXin Li        self._gcc_libs_dest = misc.GetGccLibsDestForBoard(
48*760c253cSXin Li            self._board, self._chromeos_root
49*760c253cSXin Li        )
50*760c253cSXin Li        self.tag = "%s-%s" % (name, self._ctarget)
51*760c253cSXin Li        self._ce = command_executer.GetCommandExecuter()
52*760c253cSXin Li        self._mask_file = os.path.join(
53*760c253cSXin Li            self._chromeos_root,
54*760c253cSXin Li            "chroot",
55*760c253cSXin Li            "etc/portage/package.mask/cross-%s" % self._ctarget,
56*760c253cSXin Li        )
57*760c253cSXin Li        self._new_mask_file = None
58*760c253cSXin Li
59*760c253cSXin Li        self._chroot_source_path = os.path.join(
60*760c253cSXin Li            constants.MOUNTED_TOOLCHAIN_ROOT, self._name
61*760c253cSXin Li        ).lstrip("/")
62*760c253cSXin Li        self._incremental = incremental
63*760c253cSXin Li        self._build_env = build_env
64*760c253cSXin Li        self._gcc_enable_ccache = gcc_enable_ccache
65*760c253cSXin Li
66*760c253cSXin Li    def RunSetupBoardIfNecessary(self):
67*760c253cSXin Li        cross_symlink = os.path.join(
68*760c253cSXin Li            self._chromeos_root,
69*760c253cSXin Li            "chroot",
70*760c253cSXin Li            "usr/local/bin/emerge-%s" % self._board,
71*760c253cSXin Li        )
72*760c253cSXin Li        if not os.path.exists(cross_symlink):
73*760c253cSXin Li            command = "setup_board --board=%s" % self._board
74*760c253cSXin Li            self._ce.ChrootRunCommand(self._chromeos_root, command)
75*760c253cSXin Li
76*760c253cSXin Li    def Build(self):
77*760c253cSXin Li        rv = 1
78*760c253cSXin Li        try:
79*760c253cSXin Li            self.UninstallTool()
80*760c253cSXin Li            self.MoveMaskFile()
81*760c253cSXin Li            self.MountSources(False)
82*760c253cSXin Li            self.RemoveCompiledFile()
83*760c253cSXin Li            rv = self.BuildTool()
84*760c253cSXin Li        finally:
85*760c253cSXin Li            self.UnMoveMaskFile()
86*760c253cSXin Li        return rv
87*760c253cSXin Li
88*760c253cSXin Li    def RemoveCompiledFile(self):
89*760c253cSXin Li        compiled_file = os.path.join(
90*760c253cSXin Li            self._chromeos_root,
91*760c253cSXin Li            "chroot",
92*760c253cSXin Li            "var/tmp/portage/cross-%s" % self._ctarget,
93*760c253cSXin Li            "%s-9999" % self._name,
94*760c253cSXin Li            ".compiled",
95*760c253cSXin Li        )
96*760c253cSXin Li        command = "rm -f %s" % compiled_file
97*760c253cSXin Li        self._ce.RunCommand(command)
98*760c253cSXin Li
99*760c253cSXin Li    def MountSources(self, unmount_source):
100*760c253cSXin Li        mount_points = []
101*760c253cSXin Li        mounted_source_path = os.path.join(
102*760c253cSXin Li            self._chromeos_root, "chroot", self._chroot_source_path
103*760c253cSXin Li        )
104*760c253cSXin Li        src_mp = tc_enter_chroot.MountPoint(
105*760c253cSXin Li            self._source_path, mounted_source_path, getpass.getuser(), "ro"
106*760c253cSXin Li        )
107*760c253cSXin Li        mount_points.append(src_mp)
108*760c253cSXin Li
109*760c253cSXin Li        build_suffix = "build-%s" % self._ctarget
110*760c253cSXin Li        build_dir = "%s-%s" % (self._source_path, build_suffix)
111*760c253cSXin Li
112*760c253cSXin Li        if not self._incremental and os.path.exists(build_dir):
113*760c253cSXin Li            command = "rm -rf %s/*" % build_dir
114*760c253cSXin Li            self._ce.RunCommand(command)
115*760c253cSXin Li
116*760c253cSXin Li        # Create a -build directory for the objects.
117*760c253cSXin Li        command = "mkdir -p %s" % build_dir
118*760c253cSXin Li        self._ce.RunCommand(command)
119*760c253cSXin Li
120*760c253cSXin Li        mounted_build_dir = os.path.join(
121*760c253cSXin Li            self._chromeos_root,
122*760c253cSXin Li            "chroot",
123*760c253cSXin Li            "%s-%s" % (self._chroot_source_path, build_suffix),
124*760c253cSXin Li        )
125*760c253cSXin Li        build_mp = tc_enter_chroot.MountPoint(
126*760c253cSXin Li            build_dir, mounted_build_dir, getpass.getuser()
127*760c253cSXin Li        )
128*760c253cSXin Li        mount_points.append(build_mp)
129*760c253cSXin Li
130*760c253cSXin Li        if unmount_source:
131*760c253cSXin Li            unmount_statuses = [mp.UnMount() == 0 for mp in mount_points]
132*760c253cSXin Li            assert all(unmount_statuses), "Could not unmount all mount points!"
133*760c253cSXin Li        else:
134*760c253cSXin Li            mount_statuses = [mp.DoMount() == 0 for mp in mount_points]
135*760c253cSXin Li
136*760c253cSXin Li            if not all(mount_statuses):
137*760c253cSXin Li                mounted = [
138*760c253cSXin Li                    mp
139*760c253cSXin Li                    for mp, status in zip(mount_points, mount_statuses)
140*760c253cSXin Li                    if status
141*760c253cSXin Li                ]
142*760c253cSXin Li                unmount_statuses = [mp.UnMount() == 0 for mp in mounted]
143*760c253cSXin Li                assert all(
144*760c253cSXin Li                    unmount_statuses
145*760c253cSXin Li                ), "Could not unmount all mount points!"
146*760c253cSXin Li
147*760c253cSXin Li    def UninstallTool(self):
148*760c253cSXin Li        command = "sudo CLEAN_DELAY=0 emerge -C cross-%s/%s" % (
149*760c253cSXin Li            self._ctarget,
150*760c253cSXin Li            self._name,
151*760c253cSXin Li        )
152*760c253cSXin Li        self._ce.ChrootRunCommand(self._chromeos_root, command)
153*760c253cSXin Li
154*760c253cSXin Li    def BuildTool(self):
155*760c253cSXin Li        env = self._build_env
156*760c253cSXin Li        # FEATURES=buildpkg adds minutes of time so we disable it.
157*760c253cSXin Li        # TODO(shenhan): keep '-sandbox' for a while for compatibility, then remove
158*760c253cSXin Li        # it after a while.
159*760c253cSXin Li        features = (
160*760c253cSXin Li            "nostrip userpriv userfetch -usersandbox -sandbox noclean "
161*760c253cSXin Li            "-buildpkg"
162*760c253cSXin Li        )
163*760c253cSXin Li        env["FEATURES"] = features
164*760c253cSXin Li
165*760c253cSXin Li        if self._incremental:
166*760c253cSXin Li            env["FEATURES"] += " keepwork"
167*760c253cSXin Li
168*760c253cSXin Li        if "USE" in env:
169*760c253cSXin Li            env["USE"] += " multislot mounted_%s" % self._name
170*760c253cSXin Li        else:
171*760c253cSXin Li            env["USE"] = "multislot mounted_%s" % self._name
172*760c253cSXin Li
173*760c253cSXin Li        # Disable ccache in our compilers. cache may be problematic for us.
174*760c253cSXin Li        # It ignores compiler environments settings and it is not clear if
175*760c253cSXin Li        # the cache hit algorithm verifies all the compiler binaries or
176*760c253cSXin Li        # just the driver.
177*760c253cSXin Li        if self._name == "gcc" and not self._gcc_enable_ccache:
178*760c253cSXin Li            env["USE"] += " -wrapper_ccache"
179*760c253cSXin Li
180*760c253cSXin Li        env["%s_SOURCE_PATH" % self._name.upper()] = os.path.join(
181*760c253cSXin Li            "/", self._chroot_source_path
182*760c253cSXin Li        )
183*760c253cSXin Li        env["ACCEPT_KEYWORDS"] = "~*"
184*760c253cSXin Li        env_string = " ".join(['%s="%s"' % var for var in env.items()])
185*760c253cSXin Li        command = "emerge =cross-%s/%s-9999" % (self._ctarget, self._name)
186*760c253cSXin Li        full_command = "sudo %s %s" % (env_string, command)
187*760c253cSXin Li        rv = self._ce.ChrootRunCommand(self._chromeos_root, full_command)
188*760c253cSXin Li        if rv != 0:
189*760c253cSXin Li            return rv
190*760c253cSXin Li        if self._name == "gcc":
191*760c253cSXin Li            command = "sudo cp -r /usr/lib/gcc/%s %s" % (
192*760c253cSXin Li                self._ctarget,
193*760c253cSXin Li                self._gcc_libs_dest,
194*760c253cSXin Li            )
195*760c253cSXin Li            rv = self._ce.ChrootRunCommand(self._chromeos_root, command)
196*760c253cSXin Li        return rv
197*760c253cSXin Li
198*760c253cSXin Li    def MoveMaskFile(self):
199*760c253cSXin Li        self._new_mask_file = None
200*760c253cSXin Li        if os.path.isfile(self._mask_file):
201*760c253cSXin Li            self._new_mask_file = tempfile.mktemp()
202*760c253cSXin Li            command = "sudo mv %s %s" % (self._mask_file, self._new_mask_file)
203*760c253cSXin Li            self._ce.RunCommand(command)
204*760c253cSXin Li
205*760c253cSXin Li    def UnMoveMaskFile(self):
206*760c253cSXin Li        if self._new_mask_file:
207*760c253cSXin Li            command = "sudo mv %s %s" % (self._new_mask_file, self._mask_file)
208*760c253cSXin Li            self._ce.RunCommand(command)
209*760c253cSXin Li
210*760c253cSXin Li
211*760c253cSXin Lidef Main(argv):
212*760c253cSXin Li    """The main function."""
213*760c253cSXin Li    # Common initializations
214*760c253cSXin Li    parser = argparse.ArgumentParser()
215*760c253cSXin Li    parser.add_argument(
216*760c253cSXin Li        "-c",
217*760c253cSXin Li        "--chromeos_root",
218*760c253cSXin Li        dest="chromeos_root",
219*760c253cSXin Li        default="../../",
220*760c253cSXin Li        help=("ChromeOS root checkout directory" " uses ../.. if none given."),
221*760c253cSXin Li    )
222*760c253cSXin Li    parser.add_argument(
223*760c253cSXin Li        "-g",
224*760c253cSXin Li        "--gcc_dir",
225*760c253cSXin Li        dest="gcc_dir",
226*760c253cSXin Li        help="The directory where gcc resides.",
227*760c253cSXin Li    )
228*760c253cSXin Li    parser.add_argument(
229*760c253cSXin Li        "--binutils_dir",
230*760c253cSXin Li        dest="binutils_dir",
231*760c253cSXin Li        help="The directory where binutils resides.",
232*760c253cSXin Li    )
233*760c253cSXin Li    parser.add_argument(
234*760c253cSXin Li        "-x",
235*760c253cSXin Li        "--gdb_dir",
236*760c253cSXin Li        dest="gdb_dir",
237*760c253cSXin Li        help="The directory where gdb resides.",
238*760c253cSXin Li    )
239*760c253cSXin Li    parser.add_argument(
240*760c253cSXin Li        "-b",
241*760c253cSXin Li        "--board",
242*760c253cSXin Li        dest="board",
243*760c253cSXin Li        default="x86-alex",
244*760c253cSXin Li        help="The target board.",
245*760c253cSXin Li    )
246*760c253cSXin Li    parser.add_argument(
247*760c253cSXin Li        "-n",
248*760c253cSXin Li        "--noincremental",
249*760c253cSXin Li        dest="noincremental",
250*760c253cSXin Li        default=False,
251*760c253cSXin Li        action="store_true",
252*760c253cSXin Li        help="Use FEATURES=keepwork to do incremental builds.",
253*760c253cSXin Li    )
254*760c253cSXin Li    parser.add_argument(
255*760c253cSXin Li        "--cflags",
256*760c253cSXin Li        dest="cflags",
257*760c253cSXin Li        default="",
258*760c253cSXin Li        help="Build a compiler with specified CFLAGS",
259*760c253cSXin Li    )
260*760c253cSXin Li    parser.add_argument(
261*760c253cSXin Li        "--cxxflags",
262*760c253cSXin Li        dest="cxxflags",
263*760c253cSXin Li        default="",
264*760c253cSXin Li        help="Build a compiler with specified CXXFLAGS",
265*760c253cSXin Li    )
266*760c253cSXin Li    parser.add_argument(
267*760c253cSXin Li        "--cflags_for_target",
268*760c253cSXin Li        dest="cflags_for_target",
269*760c253cSXin Li        default="",
270*760c253cSXin Li        help="Build the target libraries with specified flags",
271*760c253cSXin Li    )
272*760c253cSXin Li    parser.add_argument(
273*760c253cSXin Li        "--cxxflags_for_target",
274*760c253cSXin Li        dest="cxxflags_for_target",
275*760c253cSXin Li        default="",
276*760c253cSXin Li        help="Build the target libraries with specified flags",
277*760c253cSXin Li    )
278*760c253cSXin Li    parser.add_argument(
279*760c253cSXin Li        "--ldflags",
280*760c253cSXin Li        dest="ldflags",
281*760c253cSXin Li        default="",
282*760c253cSXin Li        help="Build a compiler with specified LDFLAGS",
283*760c253cSXin Li    )
284*760c253cSXin Li    parser.add_argument(
285*760c253cSXin Li        "-d",
286*760c253cSXin Li        "--debug",
287*760c253cSXin Li        dest="debug",
288*760c253cSXin Li        default=False,
289*760c253cSXin Li        action="store_true",
290*760c253cSXin Li        help="Build a compiler with -g3 -O0 appended to both"
291*760c253cSXin Li        " CFLAGS and CXXFLAGS.",
292*760c253cSXin Li    )
293*760c253cSXin Li    parser.add_argument(
294*760c253cSXin Li        "-m",
295*760c253cSXin Li        "--mount_only",
296*760c253cSXin Li        dest="mount_only",
297*760c253cSXin Li        default=False,
298*760c253cSXin Li        action="store_true",
299*760c253cSXin Li        help="Just mount the tool directories.",
300*760c253cSXin Li    )
301*760c253cSXin Li    parser.add_argument(
302*760c253cSXin Li        "-u",
303*760c253cSXin Li        "--unmount_only",
304*760c253cSXin Li        dest="unmount_only",
305*760c253cSXin Li        default=False,
306*760c253cSXin Li        action="store_true",
307*760c253cSXin Li        help="Just unmount the tool directories.",
308*760c253cSXin Li    )
309*760c253cSXin Li    parser.add_argument(
310*760c253cSXin Li        "--extra_use_flags",
311*760c253cSXin Li        dest="extra_use_flags",
312*760c253cSXin Li        default="",
313*760c253cSXin Li        help="Extra flag for USE, to be passed to the ebuild. "
314*760c253cSXin Li        "('multislot' and 'mounted_<tool>' are always passed.)",
315*760c253cSXin Li    )
316*760c253cSXin Li    parser.add_argument(
317*760c253cSXin Li        "--gcc_enable_ccache",
318*760c253cSXin Li        dest="gcc_enable_ccache",
319*760c253cSXin Li        default=False,
320*760c253cSXin Li        action="store_true",
321*760c253cSXin Li        help="Enable ccache for the gcc invocations",
322*760c253cSXin Li    )
323*760c253cSXin Li
324*760c253cSXin Li    options = parser.parse_args(argv)
325*760c253cSXin Li
326*760c253cSXin Li    chromeos_root = misc.CanonicalizePath(options.chromeos_root)
327*760c253cSXin Li    if options.gcc_dir:
328*760c253cSXin Li        gcc_dir = misc.CanonicalizePath(options.gcc_dir)
329*760c253cSXin Li        assert gcc_dir and os.path.isdir(gcc_dir), "gcc_dir does not exist!"
330*760c253cSXin Li    if options.binutils_dir:
331*760c253cSXin Li        binutils_dir = misc.CanonicalizePath(options.binutils_dir)
332*760c253cSXin Li        assert os.path.isdir(binutils_dir), "binutils_dir does not exist!"
333*760c253cSXin Li    if options.gdb_dir:
334*760c253cSXin Li        gdb_dir = misc.CanonicalizePath(options.gdb_dir)
335*760c253cSXin Li        assert os.path.isdir(gdb_dir), "gdb_dir does not exist!"
336*760c253cSXin Li    if options.unmount_only:
337*760c253cSXin Li        options.mount_only = False
338*760c253cSXin Li    elif options.mount_only:
339*760c253cSXin Li        options.unmount_only = False
340*760c253cSXin Li    build_env = {}
341*760c253cSXin Li    if options.cflags:
342*760c253cSXin Li        build_env["CFLAGS"] = "`portageq envvar CFLAGS` " + options.cflags
343*760c253cSXin Li    if options.cxxflags:
344*760c253cSXin Li        build_env["CXXFLAGS"] = "`portageq envvar CXXFLAGS` " + options.cxxflags
345*760c253cSXin Li    if options.cflags_for_target:
346*760c253cSXin Li        build_env["CFLAGS_FOR_TARGET"] = options.cflags_for_target
347*760c253cSXin Li    if options.cxxflags_for_target:
348*760c253cSXin Li        build_env["CXXFLAGS_FOR_TARGET"] = options.cxxflags_for_target
349*760c253cSXin Li    if options.ldflags:
350*760c253cSXin Li        build_env["LDFLAGS"] = options.ldflags
351*760c253cSXin Li    if options.debug:
352*760c253cSXin Li        debug_flags = "-g3 -O0"
353*760c253cSXin Li        if "CFLAGS" in build_env:
354*760c253cSXin Li            build_env["CFLAGS"] += " %s" % (debug_flags)
355*760c253cSXin Li        else:
356*760c253cSXin Li            build_env["CFLAGS"] = debug_flags
357*760c253cSXin Li        if "CXXFLAGS" in build_env:
358*760c253cSXin Li            build_env["CXXFLAGS"] += " %s" % (debug_flags)
359*760c253cSXin Li        else:
360*760c253cSXin Li            build_env["CXXFLAGS"] = debug_flags
361*760c253cSXin Li    if options.extra_use_flags:
362*760c253cSXin Li        build_env["USE"] = options.extra_use_flags
363*760c253cSXin Li
364*760c253cSXin Li    # Create toolchain parts
365*760c253cSXin Li    toolchain_parts = {}
366*760c253cSXin Li    for board in options.board.split(","):
367*760c253cSXin Li        if options.gcc_dir:
368*760c253cSXin Li            tp = ToolchainPart(
369*760c253cSXin Li                "gcc",
370*760c253cSXin Li                gcc_dir,
371*760c253cSXin Li                chromeos_root,
372*760c253cSXin Li                board,
373*760c253cSXin Li                not options.noincremental,
374*760c253cSXin Li                build_env,
375*760c253cSXin Li                options.gcc_enable_ccache,
376*760c253cSXin Li            )
377*760c253cSXin Li            toolchain_parts[tp.tag] = tp
378*760c253cSXin Li            tp.RunSetupBoardIfNecessary()
379*760c253cSXin Li        if options.binutils_dir:
380*760c253cSXin Li            tp = ToolchainPart(
381*760c253cSXin Li                "binutils",
382*760c253cSXin Li                binutils_dir,
383*760c253cSXin Li                chromeos_root,
384*760c253cSXin Li                board,
385*760c253cSXin Li                not options.noincremental,
386*760c253cSXin Li                build_env,
387*760c253cSXin Li            )
388*760c253cSXin Li            toolchain_parts[tp.tag] = tp
389*760c253cSXin Li            tp.RunSetupBoardIfNecessary()
390*760c253cSXin Li        if options.gdb_dir:
391*760c253cSXin Li            tp = ToolchainPart(
392*760c253cSXin Li                "gdb",
393*760c253cSXin Li                gdb_dir,
394*760c253cSXin Li                chromeos_root,
395*760c253cSXin Li                board,
396*760c253cSXin Li                not options.noincremental,
397*760c253cSXin Li                build_env,
398*760c253cSXin Li            )
399*760c253cSXin Li            toolchain_parts[tp.tag] = tp
400*760c253cSXin Li            tp.RunSetupBoardIfNecessary()
401*760c253cSXin Li
402*760c253cSXin Li    rv = 0
403*760c253cSXin Li    try:
404*760c253cSXin Li        for tag in toolchain_parts:
405*760c253cSXin Li            tp = toolchain_parts[tag]
406*760c253cSXin Li            if options.mount_only or options.unmount_only:
407*760c253cSXin Li                tp.MountSources(options.unmount_only)
408*760c253cSXin Li            else:
409*760c253cSXin Li                rv = rv + tp.Build()
410*760c253cSXin Li    finally:
411*760c253cSXin Li        print("Exiting...")
412*760c253cSXin Li    return rv
413*760c253cSXin Li
414*760c253cSXin Li
415*760c253cSXin Liif __name__ == "__main__":
416*760c253cSXin Li    retval = Main(sys.argv[1:])
417*760c253cSXin Li    sys.exit(retval)
418