c++bazelconditional-compilationbazel-rulesbazel-cpp

Bazel conditional compile a C++ library if a flag is set (when building the library or one of targets that use it)


I'm trying to conditionally compile a C++ library with bazel depending on if a particular flag is set when building/running one of the target that use the library or the library itself.

My library has something like:

//libraryheader.hpp
#ifdef MY_MACRO
#pragma message("MY_MACRO is defined")
//do something
#else
// do something else
#endif

Here's what I've tried. The BUILD file looks something like

#BUILD
load("@bazel_skylib//rules:common_settings.bzl", "string_flag")

use_the_feature_macro = string_flag(
    name = "use_the_feature",
    build_setting_default = "",
)

cc_library(
    name = "my_library",
    testonly = True,
    srcs = [
        "somefile.cpp",
    ],
    hdrs = [
        "libraryheader.hpp",
    ],
    copts = select({
        ":use_the_feature_macro": ["-DMY_MACRO"],
        "//conditions:default": [],
    }),
    data = [],
    visibility = ["//path/to/smth:__subpackages__"],
    deps = [
        "//path/to/smth:targ1",
        ...
    ],
)

When trying to build with a command like bazel build --use_the_feature //path/to:my_library I keep getting errors about the unrecognized option. I've also tried with `bazel build --//path/to:use_feature //path/to:my_library but it doesn't work. Still unrecognised option

ERROR: --use_the_feature :: Unrecognized option: --use_the_feature

What I whish to achieve is to be able to conditionally compile the library both when compiling the library by itself or when compiling a target that uses the library. For now, looking at the bazel documentation I don't know if I should use string_flag or config_setting and the examples on their github are quite difficult to understand.


Solution

  • What you seem to be missing is a config_setting that depends on the flag value, which then you can use in your select().

    Here is a complete working example:

    load("@bazel_skylib//rules:common_settings.bzl", "string_flag")
    
    config_setting(
        name = "feature_is_used",
        flag_values = {
            ":use_the_feature": "yes",
        },
    )
    
    string_flag(
        name = "use_the_feature",
        build_setting_default = "",
    )
    
    cc_library(
        name = "my_library",
        testonly = True,
        srcs = ["somefile.cpp"],
        hdrs = ["libraryheader.hpp"],
        copts = select({
            ":feature_is_used": ["-DMY_MACRO"],
            "//conditions:default": [],
        }),
    )
    

    Then you can use bazel build --//:use_the_feature=yes :my_library to build with the macro. There's nothing special about the word "yes", you can enable macros one-by-one this way, or you can switch to using a bool_flag if you want a single toggle.


    For completeness, here are the remaining files needed for the build:

    bazel_dep(name = "bazel_skylib", version = "1.5.0")
    
    // libraryheader.hpp
    #ifdef MY_MACRO
    #pragma message("MY_MACRO is defined")
    // do something
    #else
    // do something else
    #endif