Intellij: CLion cannot resolve headers included from other headers

Created on 14 Feb 2019  路  8Comments  路  Source: bazelbuild/intellij

When opening a header file in CLion that includes a header in a different package, the included header cannot be resolved. The included header is underlined in red and trying to go to declaration produces the "cannot find declaration to go to" message.

screen shot 2019-02-13 at 5 39 09 pm

The project syncs successfully and can be built from the command line with no issues.

I have recreated this in a sample project available here: https://github.com/brownry1986/ClionBazelHeaderSymlinks

Below is my version information:
Operating System: macOS 10.14.2
Bazel Version: 0.22.0-homebrew
CLion Version: 2018.3.4 Build #CL-183.5429.37
Bazel Plugin Version: v2019.01.28.0.2

CLion c++ macos bug

Most helpful comment

@jin We have implemented an internal aspect that augments the include info to provide the actual path for include directories instead of the symlinked path. We are planning to post this aspect publicly in the near future.

Is there any appetite to fix this directly in the bazel plugin?

All 8 comments

The sample project has strip_include_prefix = "src/main/include". Does changing that to includes = ["src/main/include"] resolve this? See #145 -- using strip_include_prefix did not play well with CLion (and TTBOMK is not yet fixed).

Indeed, strip_include_prefix does not seem to be supported with CLion. Changing to includes = ["src/main/include"] does not seem to have any impact. @jwnimmer-tri I saw that in response to the linked issue you put together an aspect to trick CLion into using a different path to search for includes. Is it possible to use a similar method to add multiple paths for CLion to search for headers?

I haven't tried that, but I see no reason why adding several paths would be any worse than just one. In https://github.com/RobotLocomotion/drake/blob/eacbb43a31d62c1f2b852c90bcef2f907bcf23dc/tools/clion/aspect.bzl#L45 is where I insert the one path; I suspect it could inject several by calling insert multiple times.

It's all a bit of a house of cards, though. If you can live with includes and not use strip_include_prefix nor include_prefix, I would recommend avoiding them.

@jwnimmer-tri switching to use includes = [ "src/main/include" ] did actually work (I didn't give clion enough time to finish loading symbols). However, we don't want to go this route as we don't want to impact how our code is built.

Instead we are using an aspect similar to what you referred to above to modify the transitive_include_directory based on the strip_include_prefix and include_prefix. This seems to have fixed our header resolution issues reported here and in #509 . We are still testing out the aspect code, but once it is complete I will post it back here and commit it to the sample project.

Thanks for your help!

What is the status of the issue? @brownry1986 are you still blocked?

@jin We have implemented an internal aspect that augments the include info to provide the actual path for include directories instead of the symlinked path. We are planning to post this aspect publicly in the near future.

Is there any appetite to fix this directly in the bazel plugin?

@brownry1986

How do you handle the case when the #include statement in the source code will need to change to use the real directory structure?

I have updated the above sample project (https://github.com/brownry1986/ClionBazelHeaderSymlinks) to include a bazel wrapper and an aspect that allows for proper resolution of header files in Clion. In order to use the apsect, you will need to modify the bazel settings in CLion to point the bazel binary path to the location of the bazel wrapper.

The main logic is in the code snippet below where we inspect each transitive include directory and translate the path to modify virtual include paths to the actual path to the include directory.

def _replace_virtual_includes(transitive_include_directories, ctx):
    results = []
    include_info = _calculate_include_dirs(ctx)

    for t in transitive_include_directories:
        if t.find("/_virtual_includes/") >= 0:
            parts = t.replace("/_virtual_includes/", "/", 1).split("/")
            if parts[BAZEL_OUT_PREFIX_DEPTH] == "external":
                results.append(t)
                continue

            package_name = "/".join(parts[BAZEL_OUT_PREFIX_DEPTH:])

            if package_name in include_info.keys():
                results.extend(include_info[package_name])
            else:
                fail("no include info for package " + package_name)
        else:
            fail("invalid transitive include directory" + t)

    return _uniq(results)

def _extract_include_directories(ctx):
    headers = []
    if hasattr(ctx.rule.attr, "hdrs"):
        headers += ctx.rule.attr.hdrs
    return _uniq([file.dirname for hdr in headers for file in hdr.files.to_list()])

def _strip_bazel_out_directory(package, include_dir, strip_include_prefix):
    return "/".join(include_dir.split("/")[:BAZEL_OUT_PREFIX_DEPTH]) + "/" + package + strip_include_prefix

def _calculate_include_dirs(ctx):
    package = ctx.label.package + "/" if ctx.label.package else ctx.label.package
    strip_include_prefix = getattr(ctx.rule.attr, "strip_include_prefix", None)
    include_prefix = getattr(ctx.rule.attr, "include_prefix", None)

    include_info = dict()
    for d in getattr(ctx.rule.attr, "deps", []):
        include_info.update(d.include_info)

    updated_include_dirs = []
    if strip_include_prefix != None or include_prefix != None:
        strip_include_prefix = strip_include_prefix or ""
        include_dirs = _extract_include_directories(ctx)
        for i in include_dirs:
            if i.startswith(package):
                updated_include_dirs.append(package + strip_include_prefix)
            else:
                updated_include_dirs.append(_strip_bazel_out_directory(package, i, strip_include_prefix))

    include_info.update({package + ctx.rule.attr.name: _uniq(updated_include_dirs)})

    return include_info
Was this page helpful?
0 / 5 - 0 ratings

Related issues

ghost picture ghost  路  5Comments

dwtj picture dwtj  路  6Comments

sambercovici picture sambercovici  路  9Comments

sundermann picture sundermann  路  5Comments

KaoruDev picture KaoruDev  路  3Comments