Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add "west gtags" extension #85984

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions doc/develop/west/zephyr-cmds.rst
Original file line number Diff line number Diff line change
Expand Up @@ -219,3 +219,52 @@ You can list all known standard descriptor names using::
You can print the offset of the descriptors inside the image using::

west bindesc get_offset

Indexing the sources with GNU Global: ``west gtags``
****************************************************

.. important:: You must install the ``gtags`` and ``global`` programs provided
by `GNU Global`_ to use this command.

The ``west gtags`` command lets you create a GNU Global tags file for the entire
west workspace::

west gtags

.. _GNU Global: https://www.gnu.org/software/global/

This will create a tags file named ``GTAGS`` in the workspace :ref:`topdir
<west-workspace>` (it will also create other Global-related metadata files
named ``GPATH`` and ``GRTAGS`` in the same place).

You can then run the ``global`` command anywhere inside the
workspace to search for symbol locations using this tags file.

For example, to search for definitions of the ``arch_system_halt()`` function,
starting from the ``zephyr/drivers`` directory::

$ cd zephyr/drivers
$ global -x arch_system_halt
arch_system_halt 65 ../arch/arc/core/fatal.c FUNC_NORETURN void arch_system_halt(unsigned int reason)
arch_system_halt 455 ../arch/arm64/core/fatal.c FUNC_NORETURN void arch_system_halt(unsigned int reason)
arch_system_halt 137 ../arch/nios2/core/fatal.c FUNC_NORETURN void arch_system_halt(unsigned int reason)
arch_system_halt 18 ../arch/posix/core/fatal.c FUNC_NORETURN void arch_system_halt(unsigned int reason)
arch_system_halt 17 ../arch/x86/core/fatal.c FUNC_NORETURN void arch_system_halt(unsigned int reason)
arch_system_halt 126 ../arch/xtensa/core/fatal.c FUNC_NORETURN void arch_system_halt(unsigned int reason)
arch_system_halt 21 ../kernel/fatal.c FUNC_NORETURN __weak void arch_system_halt(unsigned int reason)

This prints the search symbol, the line it is defined on, a relative path to
the file it is defined in, and the line itself, for all places where the symbol
is defined.

Additional tips:

- This can also be useful to search for vendor HAL function definitions.

- See the ``global`` command's manual page for more information on how to use
this tool.

- You should run ``global``, **not** ``west global``. There is no need for a
separate ``west global`` command since ``global`` already searches for the
``GTAGS`` file starting from your current working directory. This is why you
need to run ``global`` from inside the workspace.
5 changes: 5 additions & 0 deletions scripts/west-commands.yml
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,8 @@ west-commands:
- name: patch
class: Patch
help: manage patches for Zephyr modules
- file: scripts/west_commands/gtags.py
commands:
- name: gtags
class: Gtags
help: create a GNU global tags file for the current workspace
98 changes: 98 additions & 0 deletions scripts/west_commands/gtags.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# Copyright (c) 2025 Qualcomm Innovation Center, Inc.
# SPDX-License-Identifier: Apache-2.0

"""gtags.py
A west extension for creating tags files (GTAGS) for GNU Global.
For more information on Global, see: https://www.gnu.org/software/global
"""

import argparse
import os.path
import subprocess
import tempfile

from west.commands import WestCommand


class Gtags(WestCommand):
def __init__(self):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense to allow unknown arguments and pass these to the gtags command being called?

super().__init__(
"gtags",
"create a GNU Global tags file for the current workspace",
"""\
Indexes source code files in the west workspace using GNU Global's
"gtags" tool. For more information on Global and gtags, see:
https://www.gnu.org/software/global/
The index can be useful to find definitions of functions, etc.,
especially across repository boundaries. One example is
finding the definition of a vendor HAL function that is
provided by a Zephyr module for the HAL.
By default, this west command only indexes files that are
tracked by git projects defined in the west. Inactive west
projects are ignored by default. For more information on
projects etc., see the west documentation.""",
)

def do_add_parser(self, parser_adder):
parser = parser_adder.add_parser(
self.name,
help=self.help,
description=self.description,
formatter_class=argparse.RawDescriptionHelpFormatter,
)

parser.add_argument(
"projects",
nargs="*",
metavar="PROJECT",
help="""Name of west project to index, or
its path. May be given more than once. Use
"manifest" to refer to the manifest
repository""",
)

return parser

def do_run(self, args, unknown_args):
all_files = []
for project in self.manifest.get_projects(args.projects):
all_files.extend(self.files_in_project(project))

with tempfile.TemporaryDirectory(suffix="gtags") as d:
gtags_files = os.path.join(d, "gtags.files")
with open(gtags_files, "w") as f:
# Due to what looks like a limitation in GNU Global,
# this won't work if there are newlines in file names.
# Unlike xargs and other commands, though, gtags
# doesn't seem to have a way to accept a NUL-delimited
# list of input file; its manpage says file names must
# be delimited by newlines.
f.write("\n".join(all_files))
subprocess.run(
# Note that "gtags -f -" and passing files via stdin
# could run into issues on windows, and there seem to
# be win32 builds of global out there
["gtags", "-f", gtags_files],
cwd=self.manifest.topdir,
check=True,
)

def files_in_project(self, project):
if not project.is_cloned() or not self.manifest.is_active(project):
return []
ls_files = (
project.git(["ls-files", "**"], capture_stdout=True).stdout.decode("utf-8").splitlines()
)
ret = []
for filename in ls_files:
absolute = os.path.join(project.abspath, filename)
# Filter out directories from the git ls-files output.
# There didn't seem to be a way to tell it to do that by
# itself.
if os.path.isfile(absolute):
ret.append(absolute)
return ret
Loading