Building C/C++ Dependencies with Different Compiler/Linker options with Bazel

Viewed 847

So I'm working on modifying a project to build with clang++ and sanitizers (for fuzzing), as opposed to just g++. It builds using bazel.

The project currently downloads some of its build dependencies (m4/bison/flex) and builds those using the make_configure rule in https://github.com/bazelbuild/rules_foreign_cc. Importantly, these are just used for code generation, and aren't linked/compiled against.

Unfortunately, those very dependencies happen to have various sanitizer issues. That means if we build using --copt="-fsanitize=address", we can't then use them for code generation and the build fails! Now, if we ran into sensitization issues with linked dependencies, that would be unavoidable and something we'd need to work with the maintainers to fix, but right now we'd really just prefer to work around those, since they don't as directly affect the security and reliability of the actual target that we're compiling.

Is there an easy way to specify, more or less, "please ignore the compiler flags/linker options/etc. passed on the command line for just this target and use this other set instead" for a rule? It seems like linkopts/copts/cxxopts that are passed via the command line (or via a global config) are additive in most cases, and we'd like to avoid that. If there isn't, what's the best way to approach solving that? Saving/unsetting/resetting all of the variables as we go in a custom rule that wraps the actual build rule?

Thanks, Everett

2 Answers

So, found my answer eventually.

There's a few approaches that will probably work here. One is a custom SkyLark rule that overrides the tools provided by the command line. That does have the potential advantage of being more "future proof" against other command line options that may not play nicely with some options passed to the compiler/linker.

However, what wound up being easier was learning the fact that the gcc/clang family of compilers has a fun behavior that plays nicely with bazel--if you specify "conflicting" command line parameters, it appears that the later one is always taken. Therefore, all we had to do is ensure that "-fno-sanitize=address" was passed into the build rule for the targets that we wanted to turn off ASAN for, while we passed "-fsanitize=address" into the global build.

Hope this helps anyone else who gets stuck on a problem like this. --Everett

"The Correct Way" would be to define your own toolchain(s) and therein add a feature, e.g.:

    asan_feature = feature(
        name = "address_sanitizer",
        enabled = True,
            flag_set(
                actions = all_compile_actions + all_link_actions,
                flag_groups = [
                    flag_group(flags = ["-fsanitize=address"]),
                ],
            ),
        ],
    )

that you pass along with features to create_cc_toolchain_config_info().

You can make this feature enabled by default or not for any given toolchain and you can control features used build wide on CLI, per package and per target (e.g. cc_binary):

cc_binary(
    ...
    features = ["-address_sanitizer"],
)

The obvious downside is, there is some initial work involved.

The upside being, compiler config / behavior really is (should be) a function of toolchain configuration and not something for each target to fiddle with. You get an abstract structure that is independent from the specific compiler underlying any given toolchain.

Related