Giter Site home page Giter Site logo

hedronvision / bazel-compile-commands-extractor Goto Github PK

View Code? Open in Web Editor NEW
594.0 12.0 90.0 368 KB

Goal: Enable awesome tooling for Bazel users of the C language family.

License: Other

Starlark 18.98% Python 80.64% C++ 0.38%
bazel bazel-build tools clangd clang-tooling clang cross-platform hacktoberfest c ccls

bazel-compile-commands-extractor's Introduction

Hedron's Compile Commands Extractor for Bazel — User Interface

What is this project trying to do for me?

First, provide Bazel users cross-platform autocomplete for the C language family (C++, C, Objective-C, Objective-C++, and CUDA), and thereby make development more efficient and fun!

More generally, export Bazel build actions into the compile_commands.json format that enables great tooling decoupled from Bazel.

Usage Visuals

Usage Animation

▲ Extracts compile_commands.json, enabling clangd autocomplete in your editor ▼

clangd help example

Status

Pretty great with only very minor rough edges. We use this every day and love it.

If there haven't been commits in a while, it's because of stability, not neglect. This is in daily use inside Hedron.

For everyday use, we'd recommend using this rather than the platform-specific IDE adapters (like Tulsi or the ASwB/CLion plugin to the extent it works), except the times when you need some platform-editor-specific feature (e.g. Apple's NextStep Interface Builder) that's not ever going to be supported in a cross-platform editor.

Outside Testimonials

There are lots of people using this tool. That includes large companies and projects with tricky stacks, like in robotics.

We're including a couple of things they've said. We hope they'll give you enough confidence to give this tool a try, too!

"Thanks for an awesome tool! Super easy to set up and use." — a robotics engineer at Boston Dynamics

"Thank you for showing so much rigor in what would otherwise be just some uninteresting tooling project. This definitely feels like a passing the baton/torch moment. My best wishes for everything you do in life." — author of the previous best tool of this type

Usage

Basic Setup Time: 10m

Howdy, Bazel user 🤠. Let's get you set up fast with some awesome tooling for the C language family.

There's a bunch of text here but only because we're trying to spell things out and make them easy. If you have issues, let us know; we'd love your help making things even better and more complete—and we'd love to help you!

First, add this tool to your Bazel setup.

If you have a MODULE.bazel file and are using the new bzlmod system

Copy this into your MODULE.bazel, making sure to update to the latest commit per the instructions below.

# Hedron's Compile Commands Extractor for Bazel
# https://github.com/hedronvision/bazel-compile-commands-extractor
bazel_dep(name = "hedron_compile_commands", dev_dependency = True)
git_override(
    module_name = "hedron_compile_commands",
    remote = "https://github.com/hedronvision/bazel-compile-commands-extractor.git",
    commit = "0e990032f3c5a866e72615cf67e5ce22186dcb97",
    # Replace the commit hash (above) with the latest (https://github.com/hedronvision/bazel-compile-commands-extractor/commits/main).
    # Even better, set up Renovate and let it do the work for you (see "Suggestion: Updates" in the README).
)

If you're using the traditional WORKSPACE system

Copy this into the top of your Bazel WORKSPACE file, making sure to update to the latest commit per the instructions below. Putting it at the top will prevent other tools from clobbering any of its dependencies with old versions; we promise to keep ours dependency versions up-to-date.

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")


# Hedron's Compile Commands Extractor for Bazel
# https://github.com/hedronvision/bazel-compile-commands-extractor
http_archive(
    name = "hedron_compile_commands",

    # Replace the commit hash (0e990032f3c5a866e72615cf67e5ce22186dcb97) in both places (below) with the latest (https://github.com/hedronvision/bazel-compile-commands-extractor/commits/main), rather than using the stale one here.
    # Even better, set up Renovate and let it do the work for you (see "Suggestion: Updates" in the README).
    url = "https://github.com/hedronvision/bazel-compile-commands-extractor/archive/0e990032f3c5a866e72615cf67e5ce22186dcb97.tar.gz",
    strip_prefix = "bazel-compile-commands-extractor-0e990032f3c5a866e72615cf67e5ce22186dcb97",
    # When you first run this tool, it'll recommend a sha256 hash to put here with a message like: "DEBUG: Rule 'hedron_compile_commands' indicated that a canonical reproducible form can be obtained by modifying arguments sha256 = ..."
)
load("@hedron_compile_commands//:workspace_setup.bzl", "hedron_compile_commands_setup")
hedron_compile_commands_setup()
load("@hedron_compile_commands//:workspace_setup_transitive.bzl", "hedron_compile_commands_setup_transitive")
hedron_compile_commands_setup_transitive()
load("@hedron_compile_commands//:workspace_setup_transitive_transitive.bzl", "hedron_compile_commands_setup_transitive_transitive")
hedron_compile_commands_setup_transitive_transitive()
load("@hedron_compile_commands//:workspace_setup_transitive_transitive_transitive.bzl", "hedron_compile_commands_setup_transitive_transitive_transitive")
hedron_compile_commands_setup_transitive_transitive_transitive()

Either way: Get Updates via Renovate

Improvements come frequently, so we'd recommend keeping up-to-date.

We'd strongly recommend you set up Renovate (or similar) at some point to keep this dependency (and others) up-to-date by default. [We aren't affiliated with Renovate or anything, but we think it's awesome. It watches for new versions and sends you PRs for review or automated testing. It's free and easy to set up. It's been astoundingly useful in our codebase, and we've worked with the wonderful maintainer to make things great for Bazel use. And it's used in official Bazel repositories.] Here's a Renovate configuration example from one of our projects, in the hope that it might save you time.

If not now, maybe come back to this step later, or watch this repo for updates. [Or hey, maybe give us a quick star, while you're thinking about watching.] Like Abseil, we live at head; the latest commit to the main branch is the commit you want. So don't rely on release notifications; use Renovate or poll manually for new commits.

Second, get the extractor running.

We'll generate a compile_commands.json file in the root of the Bazel workspace.

That file describes how Bazel is compiling all the (Objective-)C(++) or CUDA files. With the compile commands in a common format, build-system-independent tooling (e.g. clangd autocomplete, clang-tidy linting etc.), can get to work.

We'll get it running and then move onto the next section while it whirrs away. But in the future, every time you want tooling (like autocomplete) to see new BUILD-file changes, rerun the command you chose below! Clangd will automatically pick up the changes.

There are four common paths:

1. Have a relatively simple codebase, where every target builds without needing any additional configuration or flags beyond what's in .bazelrc?

In that case, just bazel run @hedron_compile_commands//:refresh_all

Note: you have to bazel run this tool, not just bazel build it.

2. Are there Bazel flags, e.g., --config=my_important_flags_or_toolchains --compilation_mode=dbg, that you apply manually apply to all your builds while developing?

It's fairly important that you supply those same Bazel flags when running this tool, too, so we can accurately understand the build, where files are being generated, etc.

Append, e.g. -- --config=my_important_flags_or_toolchains --compilation_mode=dbg to the above, or whatever flags you normally build with while developing.

Note: the extra -- is not a typo, and functions to pass the flags to this tool when it runs rather than when it builds. Your command should look like:

bazel run @hedron_compile_commands//:refresh_all -- --config=my_important_flags_or_toolchains --compilation_mode=dbg

3. Often, though, you'll want to specify the top-level, output targets you care about and/or what flags they individually need. This avoids issues where some targets can't be built on their own; they need configuration on the command line or by a parent rule. An example of the latter is an android_library, which probably cannot be built independently of the android_binary that configures it.

In that case, you can easily specify the top-level, output targets you're working on and the flags needed to build them.

Open a BUILD file—we'd recommend using (or creating) //BUILD—and add something like:

load("@hedron_compile_commands//:refresh_compile_commands.bzl", "refresh_compile_commands")

refresh_compile_commands(
    name = "refresh_compile_commands",

    # Specify the targets of interest.
    # For example, specify a dict of targets and any flags required to build.
    targets = {
      "//:my_output_1": "--important_flag1 --important_flag2=true",
      "//:my_output_2": "",
    },
    # No need to add flags already in .bazelrc. They're automatically picked up.
    # If you don't need flags, a list of targets is also okay, as is a single target string.
    # Wildcard patterns, like //... for everything, *are* allowed here, just like a build.
      # As are additional targets (+) and subtractions (-), like in bazel query https://docs.bazel.build/versions/main/query.html#expressions
    # And if you're working on a header-only library, specify a test or binary target that compiles it.
)

(For more details on refresh_compile_commands, look at the docs at the top of refresh_compile_commands.bzl).

Finally, you'll need to bazel run :refresh_compile_commands

4. Using ccls or another tool that, unlike clangd, doesn't want or need headers in compile_commands.json?

Similar to the above, we'll use refresh_compile_commands for configuration, but instead of setting targets, set exclude_headers = "all".

If you've got a very large project and compile_commands.json is taking a while to generate:

Adding exclude_external_sources = True and exclude_headers = "external" can help, with some tradeoffs.

For now, we'd suggest continuing on to set up clangd (below). Thereafter, if you your project proves to be large enough that it stretches the capacity of clangd and/or this tool to index quickly, take a look at the docs at the top of refresh_compile_commands.bzl for instructions on how to tune those flags and others.

Editor Setup — for autocomplete based on compile_commands.json

VSCode

Let's get clangd's extension installed and configured.

code --install-extension llvm-vs-code-extensions.vscode-clangd
# We also need make sure that Microsoft's C++ extension is not involved and interfering.
code --uninstall-extension ms-vscode.cpptools

Then, open VSCode user settings, so things will be automatically set up for all projects you open.

Search for "clangd".

Add the following three separate entries to "clangd.arguments":

--header-insertion=never
--compile-commands-dir=${workspaceFolder}/
--query-driver=**

(Just copy each as written; VSCode will correctly expand ${workspaceFolder} for each workspace.)

  • They get rid of (overzealous) header insertion; locate the compile commands correctly, even when browsing system headers outside the source tree; and cause clangd to interrogate Bazel's compiler wrappers to figure out which system headers are included by default.
  • If your Bazel WORKSPACE is a subdirectory of your project, change --compile-commands-dir to point into that subdirectory by overriding the flags in your workspace settings. You'll need to re-specify all the flags when you override, because the workspace settings replace all the flags in the user settings.

Turn on: Clangd: Check Updates

  • You always want the latest! New great features and fixes are always getting added to clangd.
  • We'll assume you always have the latest and aren't using an old version nor Apple's clangd intended for Xcode. While we can and do make great efforts to workaround issues in the current version of clangd, we remove those workarounds when clangd fixes them upstream. This keeps the code simple and development velocity fast!

If turning on automatic updates doesn't prompt you to download the actual clangd server binary, hit (CMD/CTRL+SHIFT+P)->Download language Server.

You may need to subsequently reload VSCode [(CMD/CTRL+SHIFT+P)->reload] for the plugin to load. The clangd download should prompt you to do so when it completes.

If you work on your repository with others...

... and would like these settings to be automatically applied for your teammates, also add the settings to the VSCode workspace settings and then check .vscode/settings.json into source control.

Other Editors

If you're using another editor, you'll need to follow the same rough steps as above: get the latest version of clangd set up to extend the editor and then supply the same flags as VSCode. We know people have had an easy time setting up this tool with other editors, like Emacs and Vim+YouCompleteMe(YCM), for example.

Once you've succeeded in setting up another editor—or set up clang-tidy, or otherwise seen anything that might improve this readme—we'd love it if you'd give back and contribute what you know! Just edit this README.md on GitHub and file a PR :)

"Smooth Edges" — what we've enjoyed using this for

You should now be all set to go! Way to make it through setup.

There should be a compile_commands.json file in the root of your workspace, enabling your editor to provide great, clang-based autocomplete. And you should know what target to bazel run to refresh that autocomplete, when you make BUILD-file changes big enough to require a refresh.

Behind the scenes, that compile_commands.json file contains entries describing all the commands used to build every source file in your project. And, for now, there's also one entry per header, describing one way it is compiled. (This gets you great autocomplete in header files, too, so you don't have to think about clangd's biggest rough edge). Crucially, all these commands have been sufficiently de-Bazeled for clang tooling (or you!) to understand them.

Here's what you should be expecting, based on our experience:

We use this tool every day to develop a cross-platform library for iOS and Android on macOS. Expect Android completion in Android source, macOS in macOS, iOS in iOS, etc. People use it on Linux/Ubuntu and Windows, too.

All the usual clangd features should work. CMD/CTRL+click navigation (or option if you've changed keybindings), smart rename, autocomplete, highlighting etc. Everything you expect in an IDE should be there (because most good IDEs are backed by clangd). As a general principle: If you're choosing tooling that needs to understand a programming language, you want it to be based on a compiler frontend for that language, which clangd does as part of the LLVM/clang project.

Everything should also work for generated files, though you may have to run a build for the generated file to exist. If you're using this with remote execution or cache, you'll likely have to use --remote_download_regex to pull down the header and source files and to avoid errors in-editor, now that build without the bytes (--remote_download_toplevel) is the Bazel default. If you work through this, we'd love it if you'd give back and file a PR adding good instructions for everyone else --or at least share what you've learned in an issue. You'll also want to pull down *.d dependency files on non-Windows; they let us find headers much faster when they're available as a cache. We'd appreciate if you'd also check to make sure that they're pulled down even without (--noexperimental_inmemory_dotd_files). Thanks for helping!

Rough Edges

Otherwise, we've self-filed issues for the rough edges we know about and are tracking. We'd love to hear from you there about what you're seeing, good and bad. Please add things if you find more rough edges, and let us know if you need help or more features.

On the other hand, if you've set things up and they're working well, we'd still love to hear from you. Please file a "non-issue" in the issues tab describing your success! We'd love to hear what you're working on, what platforms you're using, and what you're finding most useful. And maybe also toss a star our way so we know it was helpful to you.

We'd also love to work with you on contributions and improvements, of course! Development setup is easy, not onerous; we've got a great doc to guide you quickly into being able to make the changes you need. The codebase is super clean and friendly. Stepping into the code is a fun and efficient way to get the improvements you want.


Other Projects Likely Of Interest

If you're using Bazel for the C language family, you'll likely also want some of our other tooling, like...

  1. A good way of making secure network requests: hedronvision/bazel-make-cc-https-easy
  2. A way to use std::filesystem across platforms: hedronvision/bazel-cc-filesystem-backport

Looking for implementation details instead? Want to dive into the codebase? See ImplementationReadme.md.

Bazel/Blaze maintainer reading this? If you'd be interested in integrating this into official Bazel tools, let us know in an issue or email, and let's talk! We love getting to use Bazel and would love to help.

bazel-compile-commands-extractor's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

bazel-compile-commands-extractor's Issues

Add a parameter to disable adding to the .gitignore

Thanks for building this.
I am trying to use this in my workspace.
I see the function _ensure_gitignore_entries will automatically add stuff in the .gitignore file.

It will be appreciated if we could have a parameter to control this behavior.
It might make sense in your environment. But it might not in others.
For example, our .gitignore already ignored compile_commands.json and I don't use clangd directly.
So for me, I just need to ignore the external directory.

Bazel commands run from the script fail due to client lock

I was unable to run this command (and unable to see that it was failing due to suppression of the output). When I turned off capture_output in the script I saw that bazel aquery was failing with:

Another command holds the client lock:
pid=(some pid)
owner=client

It appears this is because a bazel script cannot call bazel externally as the parent process has the client lock. I believe I can get around this by adding an --output_base argument to the bazel aquery call so that we don't use the same lock as regular bazel (this is fine as its a read only call). See https://bazel.build/docs/scripts

Is it possible to add this to the script? I am confused as to why I am running into it and it works fine for you, perhaps a difference in the codebase setups.

codegen dependency

Hi -- some targets in our build generate .pb.cc files from Protobuf type definitions. I noticed that without actually running those builds, we don't have those generated files yet, and the hedron scan fails. I was able to get around it by building those targets, but I'd like to be able to specify them as deps of the refresh_compile_commands rule in our BUILD so that those builds actually get run before running the scan. Is there a way to do that?

On MacOS Monterey 12.4 with XCode 13.4, clang complains of no sysroot

I'm using a vanilla MacOS with XCode environment - on a freshly installed Apple Studio (M1 Ultra) and Bazel 5.2.0-homebrew. I'm using option 3 (BUILD with refresh_compile_commands).

I tried a single cc_library target and it complains that:

>>> While locating the headers you use, we encountered a compiler warning or error.
    No need to worry; your code doesn't have to compile for this tool to work.
    However, we'll still print the errors and warnings in case they're helpful for you in fixing them.
    If the errors are about missing files that Bazel should generate:
        You might want to run a build of your code with --keep_going.
        That way, everything possible is generated, browsable and indexed for autocomplete.
    But, if you have *already* built your code successfully:
        Please make sure you're supplying this tool with the same flags you use to build.
        You can either use a refresh_compile_commands rule or the special -- syntax. Please see the README.
        [Supplying flags normally won't work. That just causes this tool to be built with those flags.]
    Continuing gracefully...
clang: warning: no such sysroot directory: '/Library/Developer/CommandLineTools/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk' [-Wmissing-sysroot]
clang: warning: no such sysroot directory: '/Library/Developer/CommandLineTools/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk' [-Wmissing-sysroot]
clang: warning: no such sysroot directory: '/Library/Developer/CommandLineTools/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk' [-Wmissing-sysroot]

(several more instances)

>>> Finished extracting commands for <redacted>

And indeed, the path /Library/Developer/CommandLineTools/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk does not exist. It looks like it moved and is now at /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk. This is a symlink to its siblings reflecting the latest version on the system.

I assume this isn't a bazel-compile-commands-extractor bug. But I don't know Bazel's toolchain resolution logic well enough to figure out where that path is baked in.

Any ideas?

invalid syntax while running this tool

bazel-out/k8-fastbuild/bin/external/hedron_compile_commands/refresh_all.runfiles/hedron_compile_commands/refresh_all.py", line 317
header_search_process = _search_headers([header_cmd[0], f'@{path}'])
^
SyntaxError: invalid syntax

The command failed to output compile_commands.json

I am trying to use it in my workspace.
I modified the top-level BUILD.bazel file to add refresh_compile_commands as suggested in case 3.
After running bazel run :refresh_compile_commands, it shows the following errors.
I have masked the name related to my workspace by using xxx, yyy and etc.

Traceback (most recent call last):
  File "/home/xxx/.cache/bazel/_bazel_xxx/eebe212d3d0401f0e0780e090816ed4f/execroot/main_heads_yyy/bazel-out/k8-fastbuild/bin/refresh_compile_commands.runfiles/main_heads_yyy/refresh_compile_commands.py", line 813, in <module>
    compile_command_entries.extend(_get_commands(target, flags))
  File "/home/xxx/.cache/bazel/_bazel_xxx/eebe212d3d0401f0e0780e090816ed4f/execroot/main_heads_yyy/bazel-out/k8-fastbuild/bin/refresh_compile_commands.runfiles/main_heads_yyy/refresh_compile_commands.py", line 709, in _get_commands
    yield from _convert_compile_commands(parsed_aquery_output)
  File "/home/xxx/.cache/bazel/_bazel_xxx/eebe212d3d0401f0e0780e090816ed4f/execroot/main_heads_yyy/bazel-out/k8-fastbuild/bin/refresh_compile_commands.runfiles/main_heads_yyy/refresh_compile_commands.py", line 627, in _convert_compile_commands
    for source_files, header_files, compile_command_args in outputs:
  File "/usr/lib/python3.8/concurrent/futures/_base.py", line 619, in result_iterator
    yield fs.pop().result()
  File "/usr/lib/python3.8/concurrent/futures/_base.py", line 437, in result
    return self.__get_result()
  File "/usr/lib/python3.8/concurrent/futures/_base.py", line 389, in __get_result
    raise self._exception
  File "/usr/lib/python3.8/concurrent/futures/thread.py", line 57, in run
    result = self.fn(*self.args, **self.kwargs)
  File "/home/xxx/.cache/bazel/_bazel_xxx/eebe212d3d0401f0e0780e090816ed4f/execroot/main_heads_yyy/bazel-out/k8-fastbuild/bin/refresh_compile_commands.runfiles/main_heads_yyy/refresh_compile_commands.py", line 585, in _get_cpp_command_for_files
    source_files, header_files = _get_files(compile_action)
  File "/home/xxx/.cache/bazel/_bazel_xxx/eebe212d3d0401f0e0780e090816ed4f/execroot/main_heads_yyy/bazel-out/k8-fastbuild/bin/refresh_compile_commands.runfiles/main_heads_yyy/refresh_compile_commands.py", line 487, in _get_files
    return {source_file}, _get_headers(compile_action, source_file) if file_exists else set()
  File "/home/xxx/.cache/bazel/_bazel_xxx/eebe212d3d0401f0e0780e090816ed4f/execroot/main_heads_yyy/bazel-out/k8-fastbuild/bin/refresh_compile_commands.runfiles/main_heads_yyy/refresh_compile_commands.py", line 420, in _get_headers
    action_key, headers = json.load(cache_file)
  File "/usr/lib/python3.8/json/__init__.py", line 293, in load
    return loads(fp.read(),
  File "/usr/lib/python3.8/json/__init__.py", line 357, in loads
    return _default_decoder.decode(s)
  File "/usr/lib/python3.8/json/decoder.py", line 337, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  File "/usr/lib/python3.8/json/decoder.py", line 355, in raw_decode
    raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

Thanks.

header-only targets

Hi! Awesome project! I really appreciate the quality of it.
I have a problem. Maybe it's a known issue but I'm not sure how to debug it.
I noticed that if I create header-only targets in bazel, for instance something like:

cc_library(
    name = "foo",
    hdrs = [
        "foo.h"
    ],
    deps = [
        "@com_google_absl//absl/types:optional"
    ]
)

Doesn't seem to include the header absl/types/optional.h in the compile_commands.json. It looks like aquery is somehow skipping this target as it's not a compilation unit. And maybe doesn't properly generate the right compiler flags. What are your thoughts, maybe I'm missing something?

🎉 ~~100~~ 200 Stars!

This isn't an issue per se, but rather a celebration of a great product.

Congratulations on reaching 100 200 stars! 🎉

This wonderful tool is only possible thanks to the tireless efforts of superstar author and maintainer @cpsauer.

If you've interacted with Chris at all (@lixiaoquan, @aapeliv, @JKurland, @HaberR, @pqn, @lummax, @dominictobias, @aminya, @jwbee, @matta, @rshanor, @LoSealL, @Alksi, @johnzeng, @klementtan, @alexander-born, @jun-sheaf, and countless more), you'll know how helpful, responsive, and friendly he is.

There aren't very many repositories where the author preemptively opens (and then resolves!) outstanding issues (#1-#6, #8), notices and incorporates the progress of external forks (#9, #33), and interacts with and empathizes with users and contributors (too many to count).

He's far too humble to write this post - so that's what I'm here for.

Go Chris, and go hedronvision/bazel-compile-commands-extractor!

<3 Sam

-fno-canonical-system-headers is a problem with clangd

I'm not sure what the best fix for this issue is, but I thought I'd report it so at least the workaround I came up with is easier to find.

I'm running bazel on Debian Testing. By default on this system bazel uses the system compiler, which is gcc/g++, and passes it the -fno-canonical-system-headers flag. The flag ends up in my compile_commands.json, which then passes it to clangd, which issues a warning about the unrecognized command line argument.

The bazel code that sets this up is https://github.com/bazelbuild/bazel/blob/e1a912c5af5b4bb3ac9d722bd3857ddbbc03f1db/tools/cpp/unix_cc_configure.bzl#L233-L240

According to clangd's documentation this can be worked around with something like this in a .clangd file:

CompileFlags:
  Remove: -fno-canonical-system-headers

I found that this fixes things for clangd, but not other clang tools. For example, clang-tidy still has an issue with -fno-canonical-system-headers -- it refuses to continue. So, I tried this hack to remove the flag from compile_commands.json and it seemed to work:

sed -i 's/ -fno-canonical-system-headers / /g' compile_commands.json

The best fix is likely to generate compile_commands.json with bazel configured to use clang++ instead, but I haven't tried that. The second best fix is for all clang tools to support (or ignore) -fno-canonical-system-headers, like they do for most every other gcc flag.

The third and fourth best fixes might be to:

  • handle this in bazel-compile-commands-extractor, maybe even going as far as handling a CompileFlags modifier as described here: https://clangd.llvm.org/config#compileflags
  • recommend that people handle it the way I did above, with sed (or similar)

Included dependency breaks intellisense

I've found that including a particular dependency tends to break the intellisense derived from this project. Here's a repo that you can clone to reproduce the issue--would you mind taking a look?

Failed to run in the project ApolloAuto

Hello,

Thanks for building this tool. Now I am trying to use it to the code ApolloAuto (https://github.com/ApolloAuto/apollo). I check out the code of Apollo v6.0 and make the below changes:

  1. add the below code in the file https://github.com/ApolloAuto/apollo/blob/master/WORKSPACE:
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")


# Hedron's Compile Commands Extractor for Bazel
# https://github.com/hedronvision/bazel-compile-commands-extractor
http_archive(
    name = "hedron_compile_commands",

    # Replace the commit hash in both places (below) with the latest, rather than using the stale one here.
    # Even better, set up Renovate and let it do the work for you (see "Suggestion: Updates" in the README).
    url = "https://github.com/hedronvision/bazel-compile-commands-extractor/archive/670e86177b6b5c001b03f4efdfba0f8019ff523f.tar.gz",
    strip_prefix = "bazel-compile-commands-extractor-670e86177b6b5c001b03f4efdfba0f8019ff523f",
    # When you first run this tool, it'll recommend a sha256 hash to put here with a message like: "DEBUG: Rule 'hedron_compile_commands' indicated that a canonical reproducible form can be obtained by modifying arguments sha256 = ..."
)
load("@hedron_compile_commands//:workspace_setup.bzl", "hedron_compile_commands_setup")
hedron_compile_commands_setup()
  1. I replace the line 251 bazel build ${CMDLINE_OPTIONS} ${job_args} -- ${formatted_targets} of the file https://github.com/ApolloAuto/apollo/blob/master/scripts/apollo_build.sh by bazel run @hedron_compile_commands//:refresh_all -- ${CMDLINE_OPTIONS} ${job_args} -- ${formatted_targets}

  2. Update the python in the container to python 3.7

  3. Add a new line exclude_headers = "all" in the file BUILD which is in the folder Apollo/.cache/bazel/540135163923dd7d5820f3ee4b306b32/external/hedron_compile_commands. If I don't manually add this, it will report error that it expect the setting of exclude_headers

But when I run it, it reports the error as below:

[WARNING] No nvidia-driver found. CPU will be used.
[WARNING] No nvidia-driver found. CPU will be used.
[INFO] Apollo Environment Settings:
[INFO]     APOLLO_ROOT_DIR: /apollo
[INFO]     APOLLO_CACHE_DIR: /apollo/.cache
[INFO]     APOLLO_IN_DOCKER: true
[INFO]     APOLLO_VERSION: HEAD-2020-09-21-e79f9d6765
[INFO]     DOCKER_IMG: dev-x86_64-18.04-20200914_0742
[INFO]     APOLLO_ENV:  STAGE=dev USE_ESD_CAN=false
[INFO]     USE_GPU: USE_GPU_HOST=0 USE_GPU_TARGET=0
[WARNING] No nvidia-driver found. CPU will be used.
[WARNING] No nvidia-driver found. CPU will be used.
[ OK ] Running CPU build on x86_64 platform.
[WARNING] ESD CAN library supplied by ESD Electronics doesn't exist.
[WARNING] If you need ESD CAN, please refer to:
[WARNING]   third_party/can_card_library/esd_can/README.md
[INFO] Build Overview:
[INFO]     USE_GPU: 0  [ 0 for CPU, 1 for GPU ]
[INFO]     Bazel Options: --config=cpu
[INFO]     Build Targets: //modules/... union //cyber/...
[INFO]     Disabled:      except //modules/drivers/canbus/can_client/esd/... except //modules/perception/... except //modules/planning/open_space/trajectory_smoother:planning_block except //modules/map/pnc_map:cuda_pnc_util except //modules/map/pnc_map:cuda_util_test
(11:38:02) INFO: Current date is 2022-06-28
(11:38:02) INFO: Analyzed target @hedron_compile_commands//:refresh_all (0 packages loaded, 0 targets configured).
(11:38:02) INFO: Found 1 target...
Target @hedron_compile_commands//:refresh_all up-to-date:
  bazel-bin/external/hedron_compile_commands/refresh_all.py
  bazel-bin/external/hedron_compile_commands/refresh_all
(11:38:02) INFO: Elapsed time: 0.184s, Critical Path: 0.00s
(11:38:02) INFO: 0 processes.
(11:38:02) INFO: Build completed successfully, 1 total action
(11:38:02) INFO: Running command line: bazel-bin/external/hedron_compile_commands/refresh_all '--config=cpu' '--jobs=6' '--local_ram_resources=HOST_RAM*0.7' -- //modules/... //cyber/... -//modules/drivers/canbu(11:38:02) INFO: Build completed successfully, 1 total action
>>> Automatically added entries to .gitignore to avoid problems.
>>> Automatically added //external workspace link:
    This link makes it easy for you--and for build tooling--to see the external dependencies you bring in. It also makes your source tree have the same directory structure as the build sandbox.
    It's a win/win: It's easier for you to browse the code you use, and it eliminates whole categories of edge cases for build tooling.
>>> Analyzing commands used in @//...
(11:38:02) ERROR: Error while parsing 'mnemonic('(Objc|Cpp)Compile',deps(@//...)) //modules/... //cyber/... -//modules/drivers/canbus/can_client/esd/... -//modules/perception/... -//modules/planning/open_space/trajectory_smoother:planning_block -//modules/map/pnc_map:cuda_pnc_util -//modules/map/pnc_map:cuda_util_test': unexpected token '//modules/...' after query expression 'mnemonic('(Objc|Cpp)Compile', deps(@//...))'

aquery failed. Command: ['bazel', 'aquery', "mnemonic('(Objc|Cpp)Compile',deps(@//...))", '--output=jsonproto', '--include_artifacts=false', '--ui_event_filters=-info', '--noshow_progress', '--features=-compiler_param_file', '--config=cpu', '--jobs=6', '--local_ram_resources=HOST_RAM*0.7', '--', '//modules/...', '//cyber/...', '-//modules/drivers/canbus/can_client/esd/...', '-//modules/perception/...', '-//modules/planning/open_space/trajectory_smoother:planning_block', '-//modules/map/pnc_map:cuda_pnc_util', '-//modules/map/pnc_map:cuda_util_test']
>>> Failed extracting commands for @//...
    Continuing gracefully...

Failed to get action keys from Bazel

The command "bazel run --cpu=k8 :refresh_compile_commands" reports an error. What does it mean?
image

Here is what I have in the BUILD
image

Here is the full log:
image

Any idea?
Thanks

work with bazel new_local_repository error

Hi, thanks for create such useful tool. I'm meet some trouble when work with bazel new_local_repository.
when I using new_local_repository to add my local openssl as a repository to my project, the external/openssl/include will soft link to /usr/include

# workspace.bzl
native.new_local_repository(
    name = "openssl",
    path = "/usr",
    build_file = clean_dep("//third_party/openssl:BUILD"),
)

clangd will throw a error, In included file: 'stdlib.h' file not found, when I open source code which include iostream, string, etc...

In file included from /usr/include/c++/10/ext/string_conversions.h:41,
                 from /usr/include/c++/10/bits/basic_string.h:6535,
                 from /usr/include/c++/10/string:55,
                 from bazel-out/k8-fastbuild/bin/app/demo/arena-demo.pb.h:8,
                 from bazel-out/k8-fastbuild/bin/app/demo/arena-demo.trpc.pb.h:8,
                 from ./app/demo/demo_service.h:6,
                 from /root/workspace/test_project/demo/app/demo/demo_service.cc:4:
/usr/include/c++/10/cstdlib:75:15: fatal error: stdlib.h: No such file or directory
   75 | #include_next <stdlib.h>
      |               ^~~~~~~~~~

I try to find some message in clangd log, and I find ..."-isystem","external/openssl/include",... in compile_command.json, it will cause gcc to think /usr/include is already included. So I try to compile with -v parameter and the gcc output proves it
ignoring duplicate directory "/usr/include"
In gcc10, cstdlib will using #include_next<stdlib.h> to find next stdlib.h in search path, but external/openssl/include is in front of /usr/include/c++/10, so it cannot find another stdlib.h file.
Now I temporarily removed the openssl dependency, Hope you can provide some useful help, Thanks~

Windows!

Update: Windows support landed! Thanks all. The latest commits should have it. You can go ahead and start using this tool on Windows.

We haven't yet tested on Windows. Windows might need some patching parallel to that for macOS (in refresh.template.py), but it should be a relatively easy adaptation compared to writing things from scratch. If you're trying to use it on Windows, let us know here. We'd love to work together to get things working smoothly.

assert when executing "bazel run //:refresh_compile_commands"

Thanks for this tool, it is very easy to setup. I am not sure this is the right place to raise a question.

I run into this error when trying to extract compile_command.json from this project

https://github.com/larq/compute-engine

  File "/home/xiaoquan.li/.cache/bazel/_bazel_xiaoquan.li/3668b3601d2bf895d72824373699ee16/execroot/larq_compute_engine/bazel-out/k8-opt/bin/external/hedron_compile_commands/extract.runfiles/hedron_compile_commands/extract.py", line 201, in <module>
    for files, command in outputs:
  File "/usr/lib/python3.6/concurrent/futures/_base.py", line 586, in result_iterator
    yield fs.pop().result()
  File "/usr/lib/python3.6/concurrent/futures/_base.py", line 425, in result
    return self.__get_result()
  File "/usr/lib/python3.6/concurrent/futures/_base.py", line 384, in __get_result
    raise self._exception
  File "/usr/lib/python3.6/concurrent/futures/thread.py", line 56, in run
    result = self.fn(*self.args, **self.kwargs)
  File "/home/xiaoquan.li/.cache/bazel/_bazel_xiaoquan.li/3668b3601d2bf895d72824373699ee16/execroot/larq_compute_engine/bazel-out/k8-opt/bin/external/hedron_compile_commands/extract.runfiles/hedron_compile_commands/extract.py", line 183, in _get_cpp_command_for_files
    files = _get_files(args)
  File "/home/xiaoquan.li/.cache/bazel/_bazel_xiaoquan.li/3668b3601d2bf895d72824373699ee16/execroot/larq_compute_engine/bazel-out/k8-opt/bin/external/hedron_compile_commands/extract.runfiles/hedron_compile_commands/extract.py", line 81, in _get_files
    files = source_files + _get_headers(compile_args, source_files[0])
  File "/home/xiaoquan.li/.cache/bazel/_bazel_xiaoquan.li/3668b3601d2bf895d72824373699ee16/execroot/larq_compute_engine/bazel-out/k8-opt/bin/external/hedron_compile_commands/extract.runfiles/hedron_compile_commands/extract.py", line 64, in _get_headers
    assert len(headers) == len(set(headers)), "Compiler should have ensured uniqueness of header files"
AssertionError: Compiler should have ensured uniqueness of header files
 >>> Finished extracting commands for //larq_compute_engine/mlir:lce-tf-opt

This is how I setup bazel-compile-commands-extractor for this target (this target can be built successfully)

diff --git a/BUILD b/BUILD
index 27aee18..fe0cf9a 100644
--- a/BUILD
+++ b/BUILD
@@ -9,3 +9,17 @@ sh_binary(
         "//larq_compute_engine:compute_engine_py",
     ],
 )
+
+load("@hedron_compile_commands//:refresh_compile_commands.bzl", "refresh_compile_commands")
+
+refresh_compile_commands(
+    name = "refresh_compile_commands",
+
+    # Specify the targets of interest.
+    # For example, specify a dict of targets and their arguments:
+    targets = {
+        "//larq_compute_engine/mlir:lce-tf-opt": ""
+    },
+    # For more details, feel free to look into refresh_compile_commands.bzl if you want.
+)
+
diff --git a/WORKSPACE b/WORKSPACE
index 5a415a2..9cdc5e2 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -39,3 +39,17 @@ tf_workspace1()
 load("@org_tensorflow//tensorflow:workspace0.bzl", "tf_workspace0")

 tf_workspace0()
+
+# Hedron's Compile Commands Extractor for Bazel
+# https://github.com/hedronvision/bazel-compile-commands-extractor
+http_archive(
+    name = "hedron_compile_commands",
+
+    # Replace the commit hash in both places (below) with the latest.
+    # Even better, set up Renovate and let it do the work for you (see "Suggestion: Updates" below).
+    url = "https://github.com/hedronvision/bazel-compile-commands-extractor/archive/9d8b3d5925728c3206010ed0062826a9faaebc2c.tar.gz",
+    strip_prefix = "bazel-compile-commands-extractor-9d8b3d5925728c3206010ed0062826a9faaebc2c",
+)
+load("@hedron_compile_commands//:workspace_setup.bzl", "hedron_compile_commands_setup")
+hedron_compile_commands_setup()
+
(END)

@aapeliv's issue -- Let's work together!

Hi @aapeliv,

Thanks for giving this tool a whirl! I saw you'd starred. But I also noticed you'd pushed a commit to a fork, presumably to work around an issue you were running into.

Any chance we could work together to resolve in the main repo whatever problem you were running into? Would love to understand and fix what went wrong both for you and for others in the future who might run into it.

I'd love to at least hear what your setup is, and what went wrong. Bonus points if you'd be willing to help reproduce the issue or get us to a fix!

Thanks!
Chris

compile_commands.json is large! Could we make it smaller?

Sure is. [It's ~260MB on the main Hedron repo.]

Why? It contains a description of all the ways how every file is compiled, including headers included in lots of compilations. We're hoping that, at least temporarily, you can spare the disk space.

Need it smaller? There are things we can do but we intentionally haven't because we anticipate that this problem will be temporary. The easiest thing would be to modify refresh.template.py to only output the first entry per file. Since clangd currently chooses just one command per file anyway, this shouldn't hurt usability. We haven't done this already because clangd will hopefully not need compile commands for headers in the future, and will hopefully take advantage multiple compile commands per file. The size without headers is only a couple MB, and without duplicate files, 16MB.

[Self-Filed] Could we also support Swift?

Status: There's an Apple project to (basically) add Swift support to clangd here. It doesn't look mature enough yet (2/2021), but perhaps it will be by the time we'd need it.

Suggested Workaround: Use Tulsi for now.
(Hedron Friendly Faces should use our internal XcodeAdapter wrapper for Tulsi)

[Windows] Support --features=compiler_param_file

Build using --features=compiler_param_file would wrap all source files and flags into a .params file, I think we should support to read from the .params file instead of reading from command line directly.

The extractor sometimes picks a non-package-local .cc file's command for header files

In most cases the commands extractor seems to generate compile commands that builds the "most closely related" .cc file with each header. E.g. a compile command for "a/thing.h" would actually build "a/thing.cc", and those are usually paired in a single cc_library rule in the //a package. This is unsurprising and intuitive; if I wanted to check if a/thing.h compiled I'd probably build //a:thing. This is also what bazel build --compile_one_dependency a/thing.h would do.

I have noticed that sometimes the bazel-compile-commands-extractor does something else -- it will pick a .cc file in some other package, when a .cc file in the same package is present and would be a better choice.

This was confusing to me in one situation, where I was changing an API and knew most of my project would be temporarily broken. However, I had fixed the local code, so (in the terms of the example above) bazel build //a/... worked and bazel test //a/... passed, but my clangd for a/thing.h was in a borked state because some other package in my project was not building. I can't remember exactly, but I may have also tried to run clang-tidy thing.h and had it fail for the same reason.

Here is one example:

I have a cc_library rule:

# In the //base package (base/BUILD.bazel)
cc_library(
    name = "test",
    srcs = ["test.cxx"],
    hdrs = ["test.hxx"],
    deps = [
        "//third_party/hash-library:sha256",
        ":base",
    ],
    visibility = ["//visibility:public"],
    linkopts = ["-lboost_program_options"],
)

But my compile_commands.json uses a .cxx file from some other package when building "base/test.hxx":

  "file": "base/test.hxx",
  "command": "/usr/lib/llvm-13/bin/clang [...] -c wavl/tree_test.cxx -o bazel-out/k8-fastbuild/bin/wavl/_objs/tree_test/tree_test.pic.o",

Here is the rule with wavl/tree_test.cxx:

# In the //wavl package (wavl/BUILD.bazel)
cc_test(
    name = "tree_test",
    srcs = ["tree_test.cxx"],
    deps = [
        "//base:test",  # wavl/tree_test.cxx includes base/test.hxx
        ":tree",
        "//base",
    ],
)

Bazel has some idea that base/test.cxx is a better choice, because bazel build --subcommands --compile_one_dependency base/test.hxx picks the //base:test target:

$ bazel build --subcommands --compile_one_dependency base/test.hxx
Loading: 
Loading: 0 packages loaded
Analyzing: target //base:test (0 packages loaded, 0 targets configured)
INFO: Analyzed target //base:test (0 packages loaded, 0 targets configured).
INFO: Found 1 target...
bazel: Entering directory `/home/matt/.cache/bazel/_bazel_matt/b06faae89ff42e5fae839cbe27f8d9e7/execroot/__main__/'
[0 / 4] [Prepa] BazelWorkspaceStatusAction stable-status.txt
SUBCOMMAND: # //base:test [action 'Compiling base/test.cxx', configuration: eacdf726293faba19ac503f2e5fbecb17c1cd58b5c40ea287666f3a8e3b68318, execution platform: @local_config_platform//:host]
(cd /home/matt/.cache/bazel/_bazel_matt/b06faae89ff42e5fae839cbe27f8d9e7/execroot/__main__ && \
  exec env - \
    PATH=blah blah blah \
    PWD=/proc/self/cwd \
  /usr/bin/gcc -U_FORTIFY_SOURCE -fstack-protector -Wall -Wunused-but-set-parameter -Wno-free-nonheap-object -fno-omit-frame-pointer '-std=c++0x' -MD -MF bazel-out/k8-fastbuild/bin/base/_objs/test/test.pic.d '-frandom-seed=bazel-out/k8-fastbuild/bin/base/_objs/test/test.pic.o' -fPIC -iquote . -iquote bazel-out/k8-fastbuild/bin -Wextra -Werror '-std=gnu++20' -fno-canonical-system-headers -Wno-builtin-macro-redefined '-D__DATE__="redacted"' '-D__TIMESTAMP__="redacted"' '-D__TIME__="redacted"' -c base/test.cxx -o bazel-out/k8-fastbuild/bin/base/_objs/test/test.pic.o)
# Configuration: eacdf726293faba19ac503f2e5fbecb17c1cd58b5c40ea287666f3a8e3b68318
# Execution platform: @local_config_platform//:host
bazel: Leaving directory `/home/matt/.cache/bazel/_bazel_matt/b06faae89ff42e5fae839cbe27f8d9e7/execroot/__main__/'
Target //base:test up-to-date:
  bazel-bin/base/libtest.a
  bazel-bin/base/libtest.so
INFO: Elapsed time: 1.899s, Critical Path: 1.85s
INFO: 2 processes: 1 internal, 1 linux-sandbox.
INFO: Build completed successfully, 2 total actions
INFO: Build completed successfully, 2 total actions

Improve compile_commands refresh time.

Hi,

First thanks for an awesome tool! Super easy to set up and use. I am new to clangd and bazel, so sorry in advance for any dumb questions.

Do you have any ideas about how to cache results and speed up regeneration time? I work across a large monorepo and even after specifying some broad targets in refresh_compile_commands, the refresh command takes about 9min.

Happy to help implement a fix if you have suggestions. Thanks for the advice!

assert prevents `refresh.template.py` from handling custom CC toolchains

Steps to reproduce:

Output:

  File "$HOME/.cache/bazel/_bazel/008ff1d9dbee3b1e6642ec19c761ba55/execroot/__main__/bazel-out/k8-fastbuild/bin/external/hedron_compile_commands/extract.runfiles/hedron_compile_commands/extract.py", line 128, in _check_in_clang_args_format
    assert re.search(r'(?:clang|clang\+\+|gcc|g\+\+)(?:-[0-9\.]+)?$', compile_args[0]), f"Compiler doesn't look like normal clang/gcc. Time to add windows support? CMD: {compile_args}"
AssertionError: Compiler doesn't look like normal clang/gcc. Time to add windows support? CMD: ['external/llvm_toolchain/bin/cc_wrapper.sh', '--target=x86_64-unknown-linux-gnu', '-U_FORTIFY_SOURCE', '-fstack-protector', '-fno-omit-frame-pointer', '-fcolor-diagnostics', '-Wall', '-Wthread-safety', '-Wself-assign', '-std=c++17', '-stdlib=libc++', '-MD', '-MF', 'bazel-out/k8-fastbuild/bin/lib/_objs/hello-time/hello-time.pic.d', '-frandom-seed=bazel-out/k8-fastbuild/bin/lib/_objs/hello-time/hello-time.pic.o', '-fPIC', '-iquote', '.', '-iquote', 'bazel-out/k8-fastbuild/bin', '-no-canonical-prefixes', '-Wno-builtin-macro-redefined', '-D__DATE__="redacted"', '-D__TIMESTAMP__="redacted"', '-D__TIME__="redacted"', '-fdebug-prefix-map=external/llvm_toolchain_llvm/=__bazel_toolchain_llvm_repo__/', '-c', 'lib/hello-time.cc', '-o', 'bazel-out/k8-fastbuild/bin/lib/_objs/hello-time/hello-time.pic.o']

extract.py does not like the external/llvm_toolchain/bin/cc_wrapper.sh script. If we fix that regex to also accept the wrapper script, we get: FileNotFoundError: [Errno 2] No such file or directory: 'external/llvm_toolchain/bin/cc_wrapper.sh': 'external/llvm_toolchain/bin/cc_wrapper.sh'. If that file happens to be vendored in the workspace and just call to a binary from an external repository, the call inside the wrapper script fails.

Not sure what a good way to solve this would be.

my repo already has external directory...

but that does not point to external bazel-out/.../../../external. It would be nice if the name of this link could be configurable in refresh_compile_commands. Alternatively the script could be smarter to look in several possible paths for external: external, bazel-external, vendor, bazel-vendor.

Use standard license?

Hi there,

Would love to use your tool. However, my company (and many other companies) have a blanket ban on using open source software that is not licensed under one of the standard licenses (eg. MIT, BSD-3, Apache, etc). In addition, I work on an open source project that is under the Cloud Native Compute Foundation (CNCF), and CNCF bans usage of any open source projects that are not using one of the standard licenses. It seems like your license is almost exactly the same as the MIT license, but because its not verbatim identical many people can't use your tool.

If you'd be willing to change your license to a standard open source license that would be great, and I think would enable a lot more people to use your tool.

Assertion fails and aborts compile commands generation

Thanks for this great tool!

For specific targets I get an assertion error here:

assert source_path_for_sanity_check is None or split[1].endswith(source_path_for_sanity_check), "Something went wrong in makefile parsing to get headers. First entry should be the source file. Output:\n" + headers_makefile_out


AssertionError: Something went wrong in makefile parsing to get headers. First entry should be the source file. Output: 
interpolator_fuzz_test.o: \
  external/host_ros_clang_toolchain/lib/clang/11.0.1/share/asan_blacklist.txt \
  test/interpolator_fuzz_test.cpp \
...

Not sure, but it seems that the source file is not the first but the second entry?

Install instructions can't be taken literally

What is ln -s bazel-out/../../../external . supposed to mean in practice? It clearly cannot be taken literally. Is ../../../ meant to stand in for something like k8-opt or k8-fastbuild and if so, how is the user meant to switch between toolchain configs?

Some third-party headers have red-underlined include issues?

Suggested Workaround: Ignore them. They're the result of people writing headers that don't include what they use and instead assume that they'll be included in a certain order. Eigen is an example.

Status: Can't think of a good way to fix--we shouldn't get into modifying 3rd party libraries to fix this. Impact is minimal. You shouldn't be editing read-only copies of 3rd party libraries anyway and it won't break autocomplete in your source files. The impact is that browsing the source of those libraries might be a bit more annoying. If you see a better way, comment here and we'll reopen!

.h files interpretted as C

I'm working on a project in which we use .h files for cpp headers. For some reason, clangd treats these like C files after I've run the hedron commands extractor, and then VSCode complains a lot because we're including C++ libraries in what it perceives to be C code. Is there some extra piece of config I can provide to either this tool, or to clangd directly that will instruct it to treat these files like cpp?

Improving confusing behavior when flags are passed to the refresher build

I have been using the extractor with GCC with no issue. But when I try to use Clang, I get a lot of errors that are like this:

clang-13: error: no such file or directory: 'bazel-out/k8-fastbuild/bin/external/sdformat/src/EmbeddedSdf.cc'
clang-13: error: no input files
clang-13: error: no such file or directory: 'bazel-out/k8-fastbuild/bin/external/yaml_cpp_internal/drake-src/convert.cpp'
clang-13: error: no input files
...

These external packages are the dependencies of another package.

@molar's issue -- Let's work together!

Hi, @molar,

Thanks for giving this tool a whirl! I happened to notice that you'd pushed a commit to a fork--it looked like undoing the Grail LLVM toolchain's compiler wrapping.

Any chance we could work together to resolve in the main repo whatever problem you were running into? I would love, at least to understand what's going wrong. Ideally we'd get it fixed--for you and for others in the future.

Thanks!
Chris

P.S. Was it related to problems finding standard library headers? That's why we'd unwrapped things for Apple platforms, but it hadn't seemed to be a problem with Grail's LLVM toolchain in the quick macOS tests I ran. Or maybe on a first run, before the wrappers had been generated? In case it's useful, there's some discussion over in #15 (comment), from when we first opened up grailbio support.

Why it is compiling my code? And generation time is very long.

I have a huge Bazel repo and I want to generate compile_commands.json.

After I run bazel run @hedron_compile_commands//:refresh_all, I found it is compiling my code, a lot of ccplus processes have been started:

image

My question is why does it need to compile my code? I believe that we can get the compile commands without compiling the source code.

Besides, compiling my code needs a long time (about 15 minutes), so it takes a long time to generate compile_commands.json.

refresh_compile_commands doesn't work with `...` targets

I recently tried using option 3 from README.md (refresh_compile_commands). While a dictionary of single targets works, it fails when I try using ...:

Works:

refresh_compile_commands(
    name = "refresh_compile_commands",
    targets = {
        "//image_io:png_io": "",
    },
)

Broken:

refresh_compile_commands(
    name = "refresh_compile_commands",
    targets = {
        "//image_io:...": "",
    },
)

It complains that:

>>> Analyzing commands used in //image_io:...
DEBUG: Rule 'hedron_compile_commands' indicated that a canonical reproducible form can be obtained by modifying arguments sha256 = "0aa3befb3984209128c5dcb151dbab2e318469385b4946cb3cfb3252cf21de63"
DEBUG: Repository hedron_compile_commands instantiated at:
  /Users/jiawen/jiawen-compdb/WORKSPACE:176:13: in <toplevel>
Repository rule http_archive defined at:
  /private/var/tmp/_bazel_jiawen/c2113bc9b1dd8fbe0e5eaa7df43be077/external/bazel_tools/tools/build_defs/repo/http.bzl:353:31: in <toplevel>
ERROR: Skipping '//image_io:...': no such target '//image_io:...': target '...' not declared in package 'image_io' defined by /Users/jiawen/jiawen-compdb/image_io/BUILD
WARNING: Target pattern parsing failed.
ERROR: no such target '//image_io:...': target '...' not declared in package 'image_io' defined by /Users/jiawen/jiawen-compdb/image_io/BUILD
Bazel aquery failed. Command: ['bazel', 'aquery', "mnemonic('(Objc|Cpp)Compile',deps(//image_io:...))", '--output=jsonproto', '--include_artifacts=false', '--ui_event_filters=-info', '--noshow_progress', '--features=-compiler_param_file']
>>> Failed extracting commands for //image_io:...
    Continuing gracefully...

This makes sense to me because bazel aquery does not support deps(//image_io:...) - it exits with the same error.

Any ideas?

Not detecting c++ version

In my .bazelrc I set the C++ version by adding the following:

build --cxxopt='-std=c++20'

however when I run bazel run @hedron_compile_commands//:refresh_all, it does not detect the c++ version and sets -std=c++11 in my compile_commands.json. This causes clagd to be unable to detect > c++11 features.

--query-driver leading to wrong default includes

System version : Macos Monterey 12.1

Problem:
With default settings mentioned in README.md, clangd counld find many system headers like stdlib.h. And in VSCode

#include <iostream>

will be marked error.

Solved with:
"--query-driver=/usr/bin/clang++"

Maybe mention this in README.md, and it might save people a couple of hours.

clangd problem is mentioned here

mypy correctness

We're using https://github.com/thundergolfer/bazel-mypy-integration, and I encountered the following issue in our CI in my //:refresh_compile_commands target:

bazel-out/k8-opt/bin/refresh_compile_commands.py:67: error: "Callable[[], Any]" has no attribute "has_logged"
bazel-out/k8-opt/bin/refresh_compile_commands.py:95: error: Incompatible types in assignment (expression has type "List[str]", variable has type "Generator[str, None, None]")
bazel-out/k8-opt/bin/refresh_compile_commands.py:97: error: No overload variant of "run" matches argument types "Generator[str, None, None]", "bool", "str", "bool"
bazel-out/k8-opt/bin/refresh_compile_commands.py:97: note: Possible overload variants:
bazel-out/k8-opt/bin/refresh_compile_commands.py:97: note:     def run(args: Union[Union[bytes, str], Sequence[Union[str, bytes, _PathLike[str], _PathLike[bytes]]]], bufsize: int = ..., executable: Union[str, bytes, _PathLike[str], _PathLike[bytes], None] = ..., stdin: Union[None, int, IO[Any]] = ..., stdout: Union[None, int, IO[Any]] = ..., stderr: Union[None, int, IO[Any]] = ..., preexec_fn: Callable[[], Any] = ..., close_fds: bool = ..., shell: bool = ..., cwd: Union[str, bytes, _PathLike[str], _PathLike[bytes], None] = ..., env: Union[Mapping[bytes, Union[bytes, str]], Mapping[str, Union[bytes, str]], None] = ..., universal_newlines: bool = ..., startupinfo: Any = ..., creationflags: int = ..., restore_signals: bool = ..., start_new_session: bool = ..., pass_fds: Any = ..., *, capture_output: bool = ..., check: bool = ..., encoding: str, errors: Optional[str] = ..., input: Optional[str] = ..., text: Optional[bool] = ..., timeout: Optional[float] = ...) -> CompletedProcess[str]
bazel-out/k8-opt/bin/refresh_compile_commands.py:97: note:     def run(args: Union[Union[bytes, str], Sequence[Union[str, bytes, _PathLike[str], _PathLike[bytes]]]], bufsize: int = ..., executable: Union[str, bytes, _PathLike[str], _PathLike[bytes], None] = ..., stdin: Union[None, int, IO[Any]] = ..., stdout: Union[None, int, IO[Any]] = ..., stderr: Union[None, int, IO[Any]] = ..., preexec_fn: Callable[[], Any] = ..., close_fds: bool = ..., shell: bool = ..., cwd: Union[str, bytes, _PathLike[str], _PathLike[bytes], None] = ..., env: Union[Mapping[bytes, Union[bytes, str]], Mapping[str, Union[bytes, str]], None] = ..., universal_newlines: Literal[False] = ..., startupinfo: Any = ..., creationflags: int = ..., restore_signals: bool = ..., start_new_session: bool = ..., pass_fds: Any = ..., *, capture_output: bool = ..., check: bool = ..., encoding: None = ..., errors: None = ..., input: Optional[bytes] = ..., text: Optional[Literal[False]] = ..., timeout: Optional[float] = ...) -> CompletedProcess[bytes]
bazel-out/k8-opt/bin/refresh_compile_commands.py:97: note:     def run(args: Union[Union[bytes, str], Sequence[Union[str, bytes, _PathLike[str], _PathLike[bytes]]]], bufsize: int = ..., executable: Union[str, bytes, _PathLike[str], _PathLike[bytes], None] = ..., stdin: Union[None, int, IO[Any]] = ..., stdout: Union[None, int, IO[Any]] = ..., stderr: Union[None, int, IO[Any]] = ..., preexec_fn: Callable[[], Any] = ..., close_fds: bool = ..., shell: bool = ..., cwd: Union[str, bytes, _PathLike[str], _PathLike[bytes], None] = ..., env: Union[Mapping[bytes, Union[bytes, str]], Mapping[str, Union[bytes, str]], None] = ..., universal_newlines: bool = ..., startupinfo: Any = ..., creationflags: int = ..., restore_signals: bool = ..., start_new_session: bool = ..., pass_fds: Any = ..., *, capture_output: bool = ..., check: bool = ..., encoding: Optional[str] = ..., errors: Optional[str] = ..., input: Union[bytes, str, None] = ..., text: Optional[bool] = ..., timeout: Optional[float] = ...) -> CompletedProcess[Any]
bazel-out/k8-opt/bin/refresh_compile_commands.py:97: note:     <3 more non-matching overloads not shown>
bazel-out/k8-opt/bin/refresh_compile_commands.py:216: error: "Callable[[List[str], str], Any]" has no attribute "has_logged_missing_file_error"
bazel-out/k8-opt/bin/refresh_compile_commands.py:217: error: "Callable[[List[str], str], Any]" has no attribute "has_logged_missing_file_error"

A good fix would be to enforce mypy in this repo, but it might suffice for now if I can somehow pass a manual tag to the py_binary defined here, just so I can pass CI:

native.py_binary(name = name, srcs = [script_name])

Maybe just add a kwargs field to refresh_compile_commands that forwards to the py_binary?

def refresh_compile_commands(name, targets = None):

Setting `--experimental_no_product_name_out_symlink` with a custom `--symlink_prefix` removes `bazel-out`, breaking paths that go through it

When setting --symlink_prefix on the commandline or .bazelrc, the bazel-out symlink might not exist or be somewhere else. But extract.py currently assumes bazel-out exists and generates a compile_commands.json file containing many references to it.

There seems to be no way of accessing the symlink_prefix via an environment variable or similar, so the refresh_compile_commands() might need an additional argument.

compile_commands.json contains entries for headers! And it takes a little while to generate! ~20s on the Hedron main repo at the time of writing. Could we speed it up?

Status: The slowness is from having clang preprocess every source file in the whole project to figure out which headers it uses (in refresh.template.py). We'll need to do this for clangd until it does this for us in its index. Clangd issue. Once this is fixed, things should be much faster and we should consider regenerating automatically on BUILD file save. Without the preprocessing, refresh.py only takes 8s or so, single threaded.

Suggested Workaround: You might be able to refresh compile_commands.json slightly less often, making the current runtime okay. If you're adding files, clangd should make pretty decent guesses at completions, using commands from nearby files. And if you're deleting files, there's not a problem. So you may not need to rerun refresh.py on every change to BUILD files. Instead, maybe refresh becomes something you run every so often when you can spare the time, making the current runtime okay.

If that doesn't work for you, consider trying to only index the part of the codebase you care about using the refresh_compile_commands rule (see README). Usually the runtime is only a real problem on huge repos--and there you're probably only editing a small subset of the codebase.

Finally, there are now options to exclude indexing for external workspaces. See docs at the top of the refesh_compile_command.

Otherwise: If you'd like to explore ways it could be made faster or help, read on!

Feature Request / Discussion - Allow multiple compile_commands.json / settings the target path to the generated compile_commands.json

We have a rather large repo, which means the generation of the compile commands takes a very long time
We would like to split the compile_commands.json into multiple files, allowing us to better "re-generate" a subset of the compile_commands.json. As far as i can tell, multiple compile_commands.json is supported by clangd.

// packageA
refresh_compile_commands(
    name = "refresh_compile_commands",
    targets = {
        "//packageA/...": "",
    },
)

will currently overwrite the top-level one, so the suggestion is to allow a different path for the resulting compile_commands.json.

Thanks for your time on this.

Request: support passing build options to the extractor

On Linux, which typically defaults to the gcc toolchain using GNU libstdc++, the following suffices to convince bazel to use clang and LLVM libc++ instead:

CC=clang bazel build --copt=-stdlib=libc++ --linkopt=-stdlib=libc++ //...

That is a small hack that Bazel supports for convenience, but the "principled" way to use non-system toolchains is to set one up explicitly and select it with --config=<blah> something like this imagined configuration (notice the lack of CC=clang):

bazel build --config=llvm-libcxx //...

However, I'm having trouble getting @hedron_compile_commands//:refresh_all to use non-default build options.

A CC=clang bazel run @hedron_compile_commands//:refresh_all invocation does honor the CC=clang portion, but if I run CC=clang bazel run --copt=-stdlib=libc++ --linkopt=-stdlib=libc++ @hedron_compile_commands//:refresh_all it has no effect for the generated compile_commands.json.

Context: I'm experimenting with C++20 coroutines. For this to work with gcc, I must pass -fcoroutines. For this to work with clang, I must pass -stdlib=libc++. For a reasonable experience with clang tooling the compile_commands.json should be set up correctly.

Currently I've worked around this problem by hacking my workspace's .bazelrc temporarily when running @hedron_compile_commands//:refresh_all, with the script at the bottom.

It would be nice if the extractor supported passing build args to the extractor.

#!/usr/bin/bash
#
# See https://github.com/hedronvision/bazel-compile-commands-extractor
#
# Setting CC=clang will cause Bazel's toolchain configuration logic to use
# clang instead of gcc.  This is unecessary on systems where clang is the
# default compiler (e.g. MacOS and FreeBSD).
#
# Setting --copt=-stdlib=libc++ causes clang to use LLVM libc++ instead of
# GNU libstdc++.  This is necessary only on systems where libc++ is not
# already the default (e.g. MacOS and FreeBSD).  See
# https://libcxx.llvm.org/UsingLibcxx.html
# opts=(--copt=-stdlib=libc++ --linkopt=-stdlib=libc++)
#
# Note: instead of the .bazelrc hacks done hbelow, we'd like to simply run
# this:
#
# CC=clang bazel run \
#     --copt='-stdlib=libc++' \
#     --linkopt='-stdlib=libc++' \
#     @hedron_compile_commands//:refresh_all
#
# ...but that doesn't work.

if ! workspace=$(bazel info workspace); then
    echo 2>&1 "error: must be run in a Bazel workspace"
    exit 1
fi
cd $workspace

# Back up the user's original .bazelrc in prep for modification.
original_bazelrc=
if [[ -f .bazelrc ]]; then
    cp .bazelrc orig.bazelrc
    original_bazelrc=orig.bazelrc
fi

# Script cleanup: restore the original .bazelrc or delete the one we're
# about to create.
function cleanup {
    if [[ -n "$original_bazelrc" ]]; then
        mv "$original_bazelrc" .bazelrc
    else
        rm -f .bazelrc
    fi
}
trap cleanup EXIT

# The @hedron_compile_commands//:refresh_all rule seems to always use the
# default build configuration.  So, put -stdlib build options into the
# default config (temporarily).
echo "build --copt='-stdlib=libc++'" >> .bazelrc
echo "build --linkopt='-stdlib=libc++'" >> .bazelrc

# Run with CC=clang for reasons described above.
CC=clang bazel run "${opts[@]}" @hedron_compile_commands//:refresh_all

How to add new language support?

I built a native starlark rules rules_cuda. Practice show that this compile_commands.json generator works pretty well (Great work :) ). So I'd like extend it for my rule, maybe in a private fork, since it is not widely adopted now. Any suggestion?

FATAL ERROR: Python 3.7 or later is required. Please update!

This is caused by system python version is too old. Boring issue. The interesting part is that https://github.com/bazelbuild/rules_python supports hermetic Python toolchain that the rule handles downloading the interpreter automatically. I think hedron_compile_commands_setup() should be expanded to hedron_compile_commands_setup(use_system_python=False) (where use_system_python default to have True value) and automatically setup the toolchain it needs.

Individual Setup Issues

I'm following the usage instructions from your repo's README.md,

  • Completed the WORKSPACE setup
  • Tried both of the common paths to get the extractor running ( both of them gave me no output compile_commands.json file)
  • I have added this to my build file, sh-edge being one of the targets that need to be built. How can I change this if I'd like to build all targets within the build directory?
load("@hedron_compile_commands//:refresh_compile_commands.bzl", "refresh_compile_commands")

refresh_compile_commands(
    name = "refresh_compile_commands",

    # Specify the targets of interest.
    # For example, specify a dict of targets and any flags required to build.
    # (No need to add flags already in .bazelrc. They're automatically picked up.)
    targets = {
      #"//:my_output_1": "--important_flag1 --important_flag2=true", 
      #"//:my_output_2": "",
      ":sh-edge": "--config linux64 --config beta --experimental_scale_timeouts=3.0",
       #sh-edge being one of the targets that need to be built. How can I change this if I'd like to build all targets within the build 
       #directory?
    },
    # If you don't need flags, a list of targets is also okay, as is a single target string.
    # For more details, feel free to look into refresh_compile_commands.bzl if you want.
)
  • got clangd's extension installed and configured according to the given instructions.

After the above configuration, running a bazel build command, gives a successful build, INFO: Build completed successfully,however I'm unable to find the compile_commands.json file.

Unrecognized option: --ui_event_filters=-info

Hi, when I use the latest version of this repo and got this error message:

>>> Analyzing commands used in @//...
ERROR: Unrecognized option: --ui_event_filters=-info
aquery failed. Command: bazel aquery 'mnemonic('"'"'(Objc|Cpp)Compile'"'"',deps(@//...))' --output=jsonproto --ui_event_filters=-info --noshow_progress
>>> Failed extracting commands for @//...
    Continuing gracefully...

I found something here:

./hedron_compile_commands/refresh.template.py:        "--ui_event_filters=-info",

so I just comment it

./hedron_compile_commands/refresh.template.py:       # "--ui_event_filters=-info",

And than erverthing is fine.

I don't know why(I'm new in bazel) but it looks like a bug?

Direct access to the native Python rules is deprecated

When I try to use this tool by running bazel run @hedron_compile_commands//:refresh_all, I get this error:

 >>> Analyzing commands used in @//...
DEBUG: Rule 'hedron_compile_commands' indicated that a canonical reproducible form can be obtained by modifying arguments sha256 = "a3ee1e3a778489bc9f831a3ce6c5293c688c2d22f743f8bec97b7037812e6184"
DEBUG: Repository hedron_compile_commands instantiated at:
  /home/aminya/drake/WORKSPACE:28:13: in <toplevel>
Repository rule http_archive defined at:
  /home/aminya/.cache/bazel/_bazel_aminya/e28718b65588b6f1482a6ab238164c48/external/bazel_tools/tools/build_defs/repo/http.bzl:336:31: in <toplevel>
DEBUG: Rule 'hedron_compile_commands' indicated that a canonical reproducible form can be obtained by modifying arguments sha256 = "a3ee1e3a778489bc9f831a3ce6c5293c688c2d22f743f8bec97b7037812e6184"
DEBUG: Repository hedron_compile_commands instantiated at:
  /home/aminya/drake/WORKSPACE:28:13: in <toplevel>
Repository rule http_archive defined at:
  /home/aminya/.cache/bazel/_bazel_aminya/e28718b65588b6f1482a6ab238164c48/external/bazel_tools/tools/build_defs/repo/http.bzl:336:31: in <toplevel>
ERROR: /home/aminya/.cache/bazel/_bazel_aminya/e28718b65588b6f1482a6ab238164c48/external/hedron_compile_commands/BUILD:19:10: in py_binary rule @hedron_compile_commands//:extract: Direct access to the native Python rules is deprecated. Please load py_binary from the rules_python repository. See http://github.com/bazelbuild/rules_python and https://github.com/bazelbuild/bazel/issues/9006. You can temporarily bypass this error by setting --incompatible_load_python_rules_from_bzl=false.
ERROR: Analysis of target '@hedron_compile_commands//:extract' failed; build aborted: Analysis of target '@hedron_compile_commands//:extract' failed
ERROR: Build failed. Not running target
 >>> Finished extracting commands for @//...

How can I fix this?

clangd has trouble resolving GCC include_next for C++ system headers for headers with a ".h" extension

In my C++ project the file naming convention is foo.cc for source files and foo.h for headers, which I think is not too unusual. I was puzzled about why clangd seemed to be unable to find any system headers (e.g. #include <cassert>) only when looking at my header files -- the source files worked just fine. One exciting journey through the clangd source code later, I discovered that it extracts a different set of system include paths for C- and C++-language files (naturally), but, in the absence of a -x gcc flag specifying it explicitly, will guess the language based on the file's extension.

I was able to work around this issue without renaming my header files by adding --cxxopt=-xc++ to the refresh_compile_commands() rule in my BUILD file, like so:

refresh_compile_commands(
    name = "refresh_compile_commands",
    targets = {
        "//myproject/...": "--cxxopt=-xc++",
    },
)

This was a big headache to track down, though. Would it make sense for bazel-compile-commands-extractor to always add this flag when running bazel aquery? If not, maybe a note in the FAQ :)

[Self-Filed] [Feature] Running clang-tidy from the command line

Hey all! Self-filing an issue to collect interest and feedback.

Some people have been using this tool and compile_commands.json to run clang-tidy from the command line. [Clang-tidy can also run as part of clangd while editing.] Sounds like other tools have some issues around headers, which we can solve.

Doing this well would probably involve creating a target that builds a script that runs a bazel-cached clang-tidy aspect over the codebase.

LMK if interested. Would love some help on this one. I do think it'd be much easier to build well within the context of this tool than standalone.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.