xref: /aosp_15_r20/external/toolchain-utils/tc_enter_chroot.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 enter the ChromeOS chroot with mounted sources.
8*760c253cSXin Li
9*760c253cSXin LiThis script enters the chroot with mounted sources.
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 pwd
19*760c253cSXin Liimport sys
20*760c253cSXin Li
21*760c253cSXin Lifrom cros_utils import command_executer
22*760c253cSXin Lifrom cros_utils import logger
23*760c253cSXin Lifrom cros_utils import misc
24*760c253cSXin Li
25*760c253cSXin Li
26*760c253cSXin Liclass MountPoint(object):
27*760c253cSXin Li    """Mount point class"""
28*760c253cSXin Li
29*760c253cSXin Li    def __init__(self, external_dir, mount_dir, owner, options=None):
30*760c253cSXin Li        self.external_dir = os.path.realpath(external_dir)
31*760c253cSXin Li        self.mount_dir = os.path.realpath(mount_dir)
32*760c253cSXin Li        self.owner = owner
33*760c253cSXin Li        self.options = options
34*760c253cSXin Li
35*760c253cSXin Li    def CreateAndOwnDir(self, dir_name):
36*760c253cSXin Li        retv = 0
37*760c253cSXin Li        if not os.path.exists(dir_name):
38*760c253cSXin Li            command = "mkdir -p " + dir_name
39*760c253cSXin Li            command += " || sudo mkdir -p " + dir_name
40*760c253cSXin Li            retv = command_executer.GetCommandExecuter().RunCommand(command)
41*760c253cSXin Li        if retv != 0:
42*760c253cSXin Li            return retv
43*760c253cSXin Li        pw = pwd.getpwnam(self.owner)
44*760c253cSXin Li        if os.stat(dir_name).st_uid != pw.pw_uid:
45*760c253cSXin Li            command = "sudo chown -f " + self.owner + " " + dir_name
46*760c253cSXin Li            retv = command_executer.GetCommandExecuter().RunCommand(command)
47*760c253cSXin Li        return retv
48*760c253cSXin Li
49*760c253cSXin Li    def DoMount(self):
50*760c253cSXin Li        ce = command_executer.GetCommandExecuter()
51*760c253cSXin Li        mount_signature = "%s on %s" % (self.external_dir, self.mount_dir)
52*760c253cSXin Li        command = "mount"
53*760c253cSXin Li        retv, out, _ = ce.RunCommandWOutput(command)
54*760c253cSXin Li        if mount_signature not in out:
55*760c253cSXin Li            retv = self.CreateAndOwnDir(self.mount_dir)
56*760c253cSXin Li            logger.GetLogger().LogFatalIf(retv, "Cannot create mount_dir!")
57*760c253cSXin Li            retv = self.CreateAndOwnDir(self.external_dir)
58*760c253cSXin Li            logger.GetLogger().LogFatalIf(retv, "Cannot create external_dir!")
59*760c253cSXin Li            retv = self.MountDir()
60*760c253cSXin Li            logger.GetLogger().LogFatalIf(retv, "Cannot mount!")
61*760c253cSXin Li            return retv
62*760c253cSXin Li        else:
63*760c253cSXin Li            return 0
64*760c253cSXin Li
65*760c253cSXin Li    def UnMount(self):
66*760c253cSXin Li        ce = command_executer.GetCommandExecuter()
67*760c253cSXin Li        return ce.RunCommand("sudo umount %s" % self.mount_dir)
68*760c253cSXin Li
69*760c253cSXin Li    def MountDir(self):
70*760c253cSXin Li        command = (
71*760c253cSXin Li            "sudo mount --bind " + self.external_dir + " " + self.mount_dir
72*760c253cSXin Li        )
73*760c253cSXin Li        if self.options == "ro":
74*760c253cSXin Li            command += " && sudo mount --bind -oremount,ro " + self.mount_dir
75*760c253cSXin Li        retv = command_executer.GetCommandExecuter().RunCommand(command)
76*760c253cSXin Li        return retv
77*760c253cSXin Li
78*760c253cSXin Li    def __str__(self):
79*760c253cSXin Li        ret = ""
80*760c253cSXin Li        ret += self.external_dir + "\n"
81*760c253cSXin Li        ret += self.mount_dir + "\n"
82*760c253cSXin Li        if self.owner:
83*760c253cSXin Li            ret += self.owner + "\n"
84*760c253cSXin Li        if self.options:
85*760c253cSXin Li            ret += self.options + "\n"
86*760c253cSXin Li        return ret
87*760c253cSXin Li
88*760c253cSXin Li
89*760c253cSXin Lidef Main(argv, return_output=False):
90*760c253cSXin Li    """The main function."""
91*760c253cSXin Li
92*760c253cSXin Li    parser = argparse.ArgumentParser()
93*760c253cSXin Li    parser.add_argument(
94*760c253cSXin Li        "-c",
95*760c253cSXin Li        "--chromeos_root",
96*760c253cSXin Li        dest="chromeos_root",
97*760c253cSXin Li        default="../..",
98*760c253cSXin Li        help="ChromeOS root checkout directory.",
99*760c253cSXin Li    )
100*760c253cSXin Li    parser.add_argument(
101*760c253cSXin Li        "-t",
102*760c253cSXin Li        "--toolchain_root",
103*760c253cSXin Li        dest="toolchain_root",
104*760c253cSXin Li        help="Toolchain root directory.",
105*760c253cSXin Li    )
106*760c253cSXin Li    parser.add_argument(
107*760c253cSXin Li        "-o", "--output", dest="output", help="Toolchain output directory"
108*760c253cSXin Li    )
109*760c253cSXin Li    parser.add_argument(
110*760c253cSXin Li        "--sudo",
111*760c253cSXin Li        dest="sudo",
112*760c253cSXin Li        action="store_true",
113*760c253cSXin Li        default=False,
114*760c253cSXin Li        help="Run the command with sudo.",
115*760c253cSXin Li    )
116*760c253cSXin Li    parser.add_argument(
117*760c253cSXin Li        "-r",
118*760c253cSXin Li        "--third_party",
119*760c253cSXin Li        dest="third_party",
120*760c253cSXin Li        help="The third_party directory to mount.",
121*760c253cSXin Li    )
122*760c253cSXin Li    parser.add_argument(
123*760c253cSXin Li        "-m",
124*760c253cSXin Li        "--other_mounts",
125*760c253cSXin Li        dest="other_mounts",
126*760c253cSXin Li        help="Other mount points in the form: " "dir:mounted_dir:options",
127*760c253cSXin Li    )
128*760c253cSXin Li    parser.add_argument(
129*760c253cSXin Li        "-s",
130*760c253cSXin Li        "--mount-scripts-only",
131*760c253cSXin Li        dest="mount_scripts_only",
132*760c253cSXin Li        action="store_true",
133*760c253cSXin Li        default=False,
134*760c253cSXin Li        help="Mount only the scripts dir, and not the sources.",
135*760c253cSXin Li    )
136*760c253cSXin Li    parser.add_argument(
137*760c253cSXin Li        "passthrough_argv",
138*760c253cSXin Li        nargs="*",
139*760c253cSXin Li        help="Command to be executed inside the chroot.",
140*760c253cSXin Li    )
141*760c253cSXin Li
142*760c253cSXin Li    options = parser.parse_args(argv)
143*760c253cSXin Li
144*760c253cSXin Li    chromeos_root = options.chromeos_root
145*760c253cSXin Li
146*760c253cSXin Li    chromeos_root = os.path.expanduser(chromeos_root)
147*760c253cSXin Li    if options.toolchain_root:
148*760c253cSXin Li        options.toolchain_root = os.path.expanduser(options.toolchain_root)
149*760c253cSXin Li
150*760c253cSXin Li    chromeos_root = os.path.abspath(chromeos_root)
151*760c253cSXin Li
152*760c253cSXin Li    tc_dirs = []
153*760c253cSXin Li    if options.toolchain_root is None or options.mount_scripts_only:
154*760c253cSXin Li        m = "toolchain_root not specified. Will not mount toolchain dirs."
155*760c253cSXin Li        logger.GetLogger().LogWarning(m)
156*760c253cSXin Li    else:
157*760c253cSXin Li        tc_dirs = [
158*760c253cSXin Li            options.toolchain_root + "/google_vendor_src_branch/gcc",
159*760c253cSXin Li            options.toolchain_root + "/google_vendor_src_branch/binutils",
160*760c253cSXin Li        ]
161*760c253cSXin Li
162*760c253cSXin Li    for tc_dir in tc_dirs:
163*760c253cSXin Li        if not os.path.exists(tc_dir):
164*760c253cSXin Li            logger.GetLogger().LogError(
165*760c253cSXin Li                "toolchain path " + tc_dir + " does not exist!"
166*760c253cSXin Li            )
167*760c253cSXin Li            parser.print_help()
168*760c253cSXin Li            sys.exit(1)
169*760c253cSXin Li
170*760c253cSXin Li    if not os.path.exists(chromeos_root):
171*760c253cSXin Li        logger.GetLogger().LogError(
172*760c253cSXin Li            "chromeos_root " + options.chromeos_root + " does not exist!"
173*760c253cSXin Li        )
174*760c253cSXin Li        parser.print_help()
175*760c253cSXin Li        sys.exit(1)
176*760c253cSXin Li
177*760c253cSXin Li    if not os.path.exists(chromeos_root + "/src/scripts/build_packages"):
178*760c253cSXin Li        logger.GetLogger().LogError(
179*760c253cSXin Li            options.chromeos_root + "/src/scripts/build_packages" " not found!"
180*760c253cSXin Li        )
181*760c253cSXin Li        parser.print_help()
182*760c253cSXin Li        sys.exit(1)
183*760c253cSXin Li
184*760c253cSXin Li    version_dir = os.path.realpath(
185*760c253cSXin Li        os.path.expanduser(os.path.dirname(__file__))
186*760c253cSXin Li    )
187*760c253cSXin Li
188*760c253cSXin Li    mounted_tc_root = "/usr/local/toolchain_root"
189*760c253cSXin Li    full_mounted_tc_root = chromeos_root + "/chroot/" + mounted_tc_root
190*760c253cSXin Li    full_mounted_tc_root = os.path.abspath(full_mounted_tc_root)
191*760c253cSXin Li
192*760c253cSXin Li    mount_points = []
193*760c253cSXin Li    for tc_dir in tc_dirs:
194*760c253cSXin Li        last_dir = misc.GetRoot(tc_dir)[1]
195*760c253cSXin Li        mount_point = MountPoint(
196*760c253cSXin Li            tc_dir,
197*760c253cSXin Li            full_mounted_tc_root + "/" + last_dir,
198*760c253cSXin Li            getpass.getuser(),
199*760c253cSXin Li            "ro",
200*760c253cSXin Li        )
201*760c253cSXin Li        mount_points.append(mount_point)
202*760c253cSXin Li
203*760c253cSXin Li    # Add the third_party mount point if it exists
204*760c253cSXin Li    if options.third_party:
205*760c253cSXin Li        third_party_dir = options.third_party
206*760c253cSXin Li        logger.GetLogger().LogFatalIf(
207*760c253cSXin Li            not os.path.isdir(third_party_dir),
208*760c253cSXin Li            "--third_party option is not a valid dir.",
209*760c253cSXin Li        )
210*760c253cSXin Li    else:
211*760c253cSXin Li        third_party_dir = os.path.abspath(
212*760c253cSXin Li            "%s/../../../third_party" % os.path.dirname(__file__)
213*760c253cSXin Li        )
214*760c253cSXin Li
215*760c253cSXin Li    if os.path.isdir(third_party_dir):
216*760c253cSXin Li        mount_point = MountPoint(
217*760c253cSXin Li            third_party_dir,
218*760c253cSXin Li            (
219*760c253cSXin Li                "%s/%s"
220*760c253cSXin Li                % (full_mounted_tc_root, os.path.basename(third_party_dir))
221*760c253cSXin Li            ),
222*760c253cSXin Li            getpass.getuser(),
223*760c253cSXin Li        )
224*760c253cSXin Li        mount_points.append(mount_point)
225*760c253cSXin Li
226*760c253cSXin Li    output = options.output
227*760c253cSXin Li    if output is None and options.toolchain_root:
228*760c253cSXin Li        # Mount the output directory at /usr/local/toolchain_root/output
229*760c253cSXin Li        output = options.toolchain_root + "/output"
230*760c253cSXin Li
231*760c253cSXin Li    if output:
232*760c253cSXin Li        mount_points.append(
233*760c253cSXin Li            MountPoint(
234*760c253cSXin Li                output, full_mounted_tc_root + "/output", getpass.getuser()
235*760c253cSXin Li            )
236*760c253cSXin Li        )
237*760c253cSXin Li
238*760c253cSXin Li    # Mount the other mount points
239*760c253cSXin Li    mount_points += CreateMountPointsFromString(
240*760c253cSXin Li        options.other_mounts, chromeos_root + "/chroot/"
241*760c253cSXin Li    )
242*760c253cSXin Li
243*760c253cSXin Li    last_dir = misc.GetRoot(version_dir)[1]
244*760c253cSXin Li
245*760c253cSXin Li    # Mount the version dir (v14) at /usr/local/toolchain_root/v14
246*760c253cSXin Li    mount_point = MountPoint(
247*760c253cSXin Li        version_dir, full_mounted_tc_root + "/" + last_dir, getpass.getuser()
248*760c253cSXin Li    )
249*760c253cSXin Li    mount_points.append(mount_point)
250*760c253cSXin Li
251*760c253cSXin Li    for mount_point in mount_points:
252*760c253cSXin Li        retv = mount_point.DoMount()
253*760c253cSXin Li        if retv != 0:
254*760c253cSXin Li            return retv
255*760c253cSXin Li
256*760c253cSXin Li    # Finally, create the symlink to build-gcc.
257*760c253cSXin Li    command = "sudo chown " + getpass.getuser() + " " + full_mounted_tc_root
258*760c253cSXin Li    retv = command_executer.GetCommandExecuter().RunCommand(command)
259*760c253cSXin Li
260*760c253cSXin Li    try:
261*760c253cSXin Li        CreateSymlink(
262*760c253cSXin Li            last_dir + "/build-gcc", full_mounted_tc_root + "/build-gcc"
263*760c253cSXin Li        )
264*760c253cSXin Li        CreateSymlink(
265*760c253cSXin Li            last_dir + "/build-binutils",
266*760c253cSXin Li            full_mounted_tc_root + "/build-binutils",
267*760c253cSXin Li        )
268*760c253cSXin Li    except Exception as e:
269*760c253cSXin Li        logger.GetLogger().LogError(str(e))
270*760c253cSXin Li
271*760c253cSXin Li    # Now call cros_sdk --enter with the rest of the arguments.
272*760c253cSXin Li    command = "cd %s/src/scripts && cros_sdk --enter" % chromeos_root
273*760c253cSXin Li
274*760c253cSXin Li    if len(options.passthrough_argv) > 1:
275*760c253cSXin Li        inner_command = " ".join(options.passthrough_argv[1:])
276*760c253cSXin Li        inner_command = inner_command.strip()
277*760c253cSXin Li        if inner_command.startswith("-- "):
278*760c253cSXin Li            inner_command = inner_command[3:]
279*760c253cSXin Li        command_file = "tc_enter_chroot.cmd"
280*760c253cSXin Li        command_file_path = chromeos_root + "/src/scripts/" + command_file
281*760c253cSXin Li        retv = command_executer.GetCommandExecuter().RunCommand(
282*760c253cSXin Li            "sudo rm -f " + command_file_path
283*760c253cSXin Li        )
284*760c253cSXin Li        if retv != 0:
285*760c253cSXin Li            return retv
286*760c253cSXin Li        with open(command_file_path, "w", encoding="utf-8") as f:
287*760c253cSXin Li            f.write(inner_command)
288*760c253cSXin Li        logger.GetLogger().LogCmd(inner_command)
289*760c253cSXin Li        retv = command_executer.GetCommandExecuter().RunCommand(
290*760c253cSXin Li            "chmod +x " + command_file_path
291*760c253cSXin Li        )
292*760c253cSXin Li        if retv != 0:
293*760c253cSXin Li            return retv
294*760c253cSXin Li
295*760c253cSXin Li        if options.sudo:
296*760c253cSXin Li            command += " sudo ./" + command_file
297*760c253cSXin Li        else:
298*760c253cSXin Li            command += " ./" + command_file
299*760c253cSXin Li        retv = command_executer.GetCommandExecuter().RunCommandGeneric(
300*760c253cSXin Li            command, return_output
301*760c253cSXin Li        )
302*760c253cSXin Li        return retv
303*760c253cSXin Li    else:
304*760c253cSXin Li        os.chdir("%s/src/scripts" % chromeos_root)
305*760c253cSXin Li        ce = command_executer.GetCommandExecuter()
306*760c253cSXin Li        _, out, _ = ce.RunCommandWOutput("which cros_sdk")
307*760c253cSXin Li        cros_sdk_binary = out.split()[0]
308*760c253cSXin Li        return os.execv(cros_sdk_binary, ["", "--enter"])
309*760c253cSXin Li
310*760c253cSXin Li
311*760c253cSXin Lidef CreateMountPointsFromString(mount_strings, chroot_dir):
312*760c253cSXin Li    # String has options in the form dir:mount:options
313*760c253cSXin Li    mount_points = []
314*760c253cSXin Li    if not mount_strings:
315*760c253cSXin Li        return mount_points
316*760c253cSXin Li    mount_list = mount_strings.split()
317*760c253cSXin Li    for mount_string in mount_list:
318*760c253cSXin Li        mount_values = mount_string.split(":")
319*760c253cSXin Li        external_dir = mount_values[0]
320*760c253cSXin Li        mount_dir = mount_values[1]
321*760c253cSXin Li        if len(mount_values) > 2:
322*760c253cSXin Li            options = mount_values[2]
323*760c253cSXin Li        else:
324*760c253cSXin Li            options = None
325*760c253cSXin Li        mount_point = MountPoint(
326*760c253cSXin Li            external_dir,
327*760c253cSXin Li            chroot_dir + "/" + mount_dir,
328*760c253cSXin Li            getpass.getuser(),
329*760c253cSXin Li            options,
330*760c253cSXin Li        )
331*760c253cSXin Li        mount_points.append(mount_point)
332*760c253cSXin Li    return mount_points
333*760c253cSXin Li
334*760c253cSXin Li
335*760c253cSXin Lidef CreateSymlink(target, link_name):
336*760c253cSXin Li    logger.GetLogger().LogFatalIf(
337*760c253cSXin Li        target.startswith("/"), "Can't create symlink to absolute path!"
338*760c253cSXin Li    )
339*760c253cSXin Li    real_from_file = misc.GetRoot(link_name)[0] + "/" + target
340*760c253cSXin Li    if os.path.realpath(real_from_file) != os.path.realpath(link_name):
341*760c253cSXin Li        if os.path.exists(link_name):
342*760c253cSXin Li            command = "rm -rf " + link_name
343*760c253cSXin Li            command_executer.GetCommandExecuter().RunCommand(command)
344*760c253cSXin Li        os.symlink(target, link_name)
345*760c253cSXin Li
346*760c253cSXin Li
347*760c253cSXin Liif __name__ == "__main__":
348*760c253cSXin Li    retval = Main(sys.argv)
349*760c253cSXin Li    sys.exit(retval)
350