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