Vertexwahn
Vertexwahn

Reputation: 8142

Include a generated header file using a relative path

Currently, I try to build a library with Bazel (5.1.0) that originally uses CMake as a build system.

I am running into a problem when trying to include a generated a header file using a relative path (in the CMake build it uses configure_file):

(The following example can also be found here)

WORKSPACE.bazel:

workspace(name = "TemplateRule")

main.cpp:

#include "kernels/bvh/some_header.h"
#include <iostream>

int main() {
    std::cout << VERSION_STR << std::endl;
}

kernels/bvh/some_header.h:

#pragma once

// include config.h using a relative path
// if changed to kernels/config.h everything works as expected
// unfortunately, this is legacy code that I cannot change
#include "../config.h"    

config.h.in:

#pragma once

#define VERSION_STR "@VERSION_STR@"

BUILD.bazel

load("//bazel:expand_template.bzl", "expand_template")

expand_template(
    name = "config_h",
    template = "config.h.in",
    out = "kernels/config.h",
    substitutions = {
        "@VERSION_STR@": "1.0.3",
    },
)

cc_binary(
    name = "HelloWorld",
    srcs = [
        "main.cpp",
        "kernels/bvh/some_header.h",
        ":config_h",
    ],
)

bazel/BUILD.bazel: < empty >

bazel/expand_template.bzl:

# Copied from https://github.com/tensorflow/tensorflow/blob/master/third_party/common.bzl with minor modifications
# SPDX-License-Identifier: Apache-2.0

def expand_template_impl(ctx):
    ctx.actions.expand_template(
        template = ctx.file.template,
        output = ctx.outputs.out,
        substitutions = ctx.attr.substitutions,
    )

_expand_template = rule(
    implementation = expand_template_impl,
    attrs = {
        "template": attr.label(mandatory = True, allow_single_file = True),
        "substitutions": attr.string_dict(mandatory = True),
        "out": attr.output(mandatory = True),
    },
    output_to_genfiles = True,
)

def expand_template(name, template, substitutions, out):
    _expand_template(
        name = name,
        template = template,
        substitutions = substitutions,
        out = out,
    )

When I run bazel build //...

I get the error:

In file included from main.cpp:1:
kernel/some_header.h:3:10: fatal error: ../config.h: No such file or directory
    3 | #include "../config.h"
      |          ^~~~~~~~~~~~~

When I include config.h in main.cpp and remove it from kernel/bvh/some_header.h everything works as expected.

Any ideas how to the relative path .../config.h working?

Creating the file config.h and compiling the code manually works as expected:

g++  main.cpp kernel/bvh/some_header.h config.h

According to the Best Practices relative paths using .. should be avoided, but you can find such things in legacy code that uses CMake to build. Is this a restriction of Bazel? or is there a workaround?

Upvotes: 2

Views: 804

Answers (1)

Swift - Friday Pie
Swift - Friday Pie

Reputation: 14589

While behaviour and order of search for file while using quotes is technically platform defined, most common principle in case of use path relative to file from which inclusion happens. For kernel/bvh/some_header.h the path ../config.h is resulting in searching ONLY in kernel folder, working folder or kernel/bvh/ will not be checked. Some compilers check alternative locations. The order of search is part of compiler documentation.

I once saw use of ../../config.h inside of another header kernel.h in situation like this and it worked. It was a maintenance liability. The kernel.h was moved to different location and it picked up a wrong folder and wrong file, which resulted in ODR breach much later in project's timeline. One who had committed the move had left ../../config.h in place and made a copy. Some units were using old header while other started to use modified one at new location.

For that reason the use reverse relative paths outside of library infrastructure is not recommended. Instead partial relative paths, e.g. kernel/config.h are preferred, assuming that core folder would be in list of include paths. In practice real projects can use preprocessor macros, e.g.#include KERNEL_CONFIG_FILE. An illustration can be seen in freetype, boost, etc.

Upvotes: 1

Related Questions