|
| 1 | +# Copyright (c) 2025 Qualcomm Innovation Center, Inc. |
| 2 | +# SPDX-License-Identifier: Apache-2.0 |
| 3 | + |
| 4 | +"""gtags.py |
| 5 | +
|
| 6 | +A west extension for creating tags files (GTAGS) for GNU Global. |
| 7 | +For more information on Global, see: https://www.gnu.org/software/global |
| 8 | +""" |
| 9 | + |
| 10 | +import argparse |
| 11 | +import os.path |
| 12 | +import subprocess |
| 13 | +import tempfile |
| 14 | + |
| 15 | +from west.commands import WestCommand |
| 16 | + |
| 17 | + |
| 18 | +class Gtags(WestCommand): |
| 19 | + def __init__(self): |
| 20 | + super().__init__( |
| 21 | + "gtags", |
| 22 | + "create a GNU Global tags file for the current workspace", |
| 23 | + """\ |
| 24 | +Indexes source code files in the west workspace using GNU Global's |
| 25 | +"gtags" tool. For more information on Global and gtags, see: |
| 26 | +
|
| 27 | + https://www.gnu.org/software/global/ |
| 28 | +
|
| 29 | +The index can be useful to find definitions of functions, etc., |
| 30 | +especially across repository boundaries. One example is |
| 31 | +finding the definition of a vendor HAL function that is |
| 32 | +provided by a Zephyr module for the HAL. |
| 33 | +
|
| 34 | +By default, this west command only indexes files that are |
| 35 | +tracked by git projects defined in the west. Inactive west |
| 36 | +projects are ignored by default. For more information on |
| 37 | +projects etc., see the west documentation.""", |
| 38 | + ) |
| 39 | + |
| 40 | + def do_add_parser(self, parser_adder): |
| 41 | + parser = parser_adder.add_parser( |
| 42 | + self.name, |
| 43 | + help=self.help, |
| 44 | + description=self.description, |
| 45 | + formatter_class=argparse.RawDescriptionHelpFormatter, |
| 46 | + ) |
| 47 | + |
| 48 | + parser.add_argument( |
| 49 | + "projects", |
| 50 | + nargs="*", |
| 51 | + metavar="PROJECT", |
| 52 | + help="""Name of west project to index, or |
| 53 | + its path. May be given more than once. Use |
| 54 | + "manifest" to refer to the manifest |
| 55 | + repository""", |
| 56 | + ) |
| 57 | + |
| 58 | + return parser |
| 59 | + |
| 60 | + def do_run(self, args, unknown_args): |
| 61 | + all_files = [] |
| 62 | + for project in self.manifest.get_projects(args.projects): |
| 63 | + all_files.extend(self.files_in_project(project)) |
| 64 | + |
| 65 | + with tempfile.TemporaryDirectory(suffix="gtags") as d: |
| 66 | + gtags_files = os.path.join(d, "gtags.files") |
| 67 | + with open(gtags_files, "w") as f: |
| 68 | + # Due to what looks like a limitation in GNU Global, |
| 69 | + # this won't work if there are newlines in file names. |
| 70 | + # Unlike xargs and other commands, though, gtags |
| 71 | + # doesn't seem to have a way to accept a NUL-delimited |
| 72 | + # list of input file; its manpage says file names must |
| 73 | + # be delimited by newlines. |
| 74 | + f.write("\n".join(all_files)) |
| 75 | + subprocess.run( |
| 76 | + # Note that "gtags -f -" and passing files via stdin |
| 77 | + # could run into issues on windows, and there seem to |
| 78 | + # be win32 builds of global out there |
| 79 | + ["gtags", "-f", gtags_files], |
| 80 | + cwd=self.manifest.topdir, |
| 81 | + check=True, |
| 82 | + ) |
| 83 | + |
| 84 | + def files_in_project(self, project): |
| 85 | + if not project.is_cloned() or not self.manifest.is_active(project): |
| 86 | + return [] |
| 87 | + ls_files = ( |
| 88 | + project.git(["ls-files", "**"], capture_stdout=True).stdout.decode("utf-8").splitlines() |
| 89 | + ) |
| 90 | + ret = [] |
| 91 | + for filename in ls_files: |
| 92 | + absolute = os.path.join(project.abspath, filename) |
| 93 | + # Filter out directories from the git ls-files output. |
| 94 | + # There didn't seem to be a way to tell it to do that by |
| 95 | + # itself. |
| 96 | + if os.path.isfile(absolute): |
| 97 | + ret.append(absolute) |
| 98 | + return ret |
0 commit comments