2

I just started using Bazel a couple days ago in hopes of something better than CMake. I have a small library that contains only protobuf definitions in its own repository. I've gotten bazel building the proto types and see them in the bazel-bin/proto directory, but am unsure of how to proceed to make this directory an include in dependent workspaces/packages so that I can utilize the output header files?

proto-repo: BUILD

load("@rules_cc//cc:defs.bzl", "cc_proto_library")
load("@rules_proto//proto:defs.bzl", "proto_library")

cc_library(
    name = "my-protobuf-common",
    hdrs = [
        ":my-proto-lib",
    ],
    copts = ["--std=c++17"],
    includes = [
        ":my-proto-lib",
    ],
    linkstatic = True,
    visibility = ["//visibility:public"],
    deps = [":my-proto-lib"],
)

cc_proto_library(
    name = "my-proto-lib",
    visibility = ["//visibility:public"],
    deps = [":my-proto"],
)

proto_library(
    name = "my-proto",
    srcs = [
        "proto/point.proto",
        "proto/point-geodetic.proto",
        "proto/point-ned.proto",
    ],
    visibility = ["//visibility:public"],
)

dependent repo (workspace correctly pulls as external and i see proto build output): BUILD

load("@rules_cc//cc:defs.bzl", "cc_library")

cc_library(
    name = "my-service",
    srcs = [
        "app/bazel-test.cpp",
    ],
    hdrs = [
        "@mpc//:my-protobuf-common",
    ],
    copts = ["--std=c++17"],
    deps = [        
        "@mpc//:my-protobuf-common",
    ],
)

bazel-test.cpp

#include <iostream>
#include <proto/point.pb.h>

int main() {
    MyProtobufCommon::Point p;
}

build error:

app/bazel-test.cpp:2:10: fatal error: proto/point.pb.h: No such file or directory
    2 | #include <proto/point.pb.h>
      |          ^~~~~~~~~~~~~~~~~~
weagle08
  • 1,763
  • 1
  • 18
  • 27
  • In the same workspace I can do the following: ``` cc_library( name = "my-lib", hdrs = [ ":my-proto-lib" ], deps = [":my-proto-lib"] ) ``` then in a c++ file that requires it I can just: ```#include "proto/point.pb.h"``` but in a dependent workspace that pulls this repository as an external it doesn't seem to work. I think it may be a misunderstanding of mine of how to reference other workspaces. – weagle08 May 09 '22 at 17:27

2 Answers2

3

In general, you should be able to depend on the cc_proto_library directly, so the intermediate cc_library my-protobuf-common isn't needed generally. The cc toolchain uses -iquote to add the proto deps, so I believe #include "proto/point.pb.h" has to be used.

proto-repo/WORKSPACE:

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

http_archive(
    name = "rules_proto",
    sha256 = "66bfdf8782796239d3875d37e7de19b1d94301e8972b3cbd2446b332429b4df1",
    strip_prefix = "rules_proto-4.0.0",
    urls = [
        "https://mirror.bazel.build/github.com/bazelbuild/rules_proto/archive/refs/tags/4.0.0.tar.gz",
        "https://github.com/bazelbuild/rules_proto/archive/refs/tags/4.0.0.tar.gz",
    ],
)
load("@rules_proto//proto:repositories.bzl", "rules_proto_dependencies", "rules_proto_toolchains")
rules_proto_dependencies()
rules_proto_toolchains()

proto-repo/BUILD:

cc_proto_library(
  name = "point_cc_proto",
  deps = [":point"],
  visibility = ["//visibility:public"],
)

proto_library(
  name = "point",
  srcs = ["proto/point.proto"],
)

proto-repo/proto/point.proto:

syntax = "proto3";

package my_protos.point;

message Point {
  optional int32 x = 1;
  optional int32 y = 2;
}

main-repo/WORKSPACE:

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

local_repository(
  name = "my_protos",
  path = "../proto-repo",
)

http_archive(
    name = "rules_proto",
    sha256 = "66bfdf8782796239d3875d37e7de19b1d94301e8972b3cbd2446b332429b4df1",
    strip_prefix = "rules_proto-4.0.0",
    urls = [
        "https://mirror.bazel.build/github.com/bazelbuild/rules_proto/archive/refs/tags/4.0.0.tar.gz",
        "https://github.com/bazelbuild/rules_proto/archive/refs/tags/4.0.0.tar.gz",
    ],
)
load("@rules_proto//proto:repositories.bzl", "rules_proto_dependencies", "rules_proto_toolchains")
rules_proto_dependencies()
rules_proto_toolchains()

main-repo/BUILD:

cc_binary(
  name = "main",
  srcs = ["main.cc"],
  deps = ["@my_protos//:point_cc_proto"],
)

main-repo/main.cc:

#include <iostream>
#include "proto/point.pb.h"

int main() {

  my_protos::point::Point p;
  p.set_x(123);
  p.set_y(456);
  std::cout << p.DebugString();

  return 0;
}

Usage:

main-repo$ bazel run main
INFO: Analyzed target //:main (43 packages loaded, 570 targets configured).
INFO: Found 1 target...
Target //:main up-to-date:
  bazel-bin/main
INFO: Elapsed time: 6.166s, Critical Path: 5.33s
INFO: 106 processes: 4 internal, 102 linux-sandbox.
INFO: Build completed successfully, 106 total actions
INFO: Build completed successfully, 106 total actions
x: 123
y: 456
ahumesky
  • 4,203
  • 8
  • 12
  • OMG, it was the <> vs "", thank you so much! I would have never tried that because I always use the <> when including from remote libraries. I have the intermediate library in case I ever add some helper transform classes to the protobuf library. – weagle08 May 10 '22 at 00:04
  • since you seem to be pretty knowledgeable on bazel, care to answer this one: [workspace files](https://stackoverflow.com/questions/72179478/why-dependent-workspace-doesnt-automatically-include-execute-the-workspace-file). Also, would you say Bazel is usable for a pretty large production environment or should I stick with CMake? – weagle08 May 10 '22 at 00:26
  • 1
    I gave an answer there, and the short answer is "yes that's basically how it works, and there's a new system designed to address the inconveniences, https://bazel.build/docs/bzlmod". For the 2nd question, Google uses Bazel (or a version of it) to build most everything, so to the degree that Google has a large production environment, I would say yes. See https://bazel.build/community/users#google and others there. – ahumesky May 10 '22 at 00:54
  • 1
    One thing to keep in mind is that Google and other companies and projects use remote caching and remote execution extensively with Bazel to achieve scalability and build performance for large projects (hundreds of thousands of targets or more), which takes time and effort to setup and maintain. – ahumesky May 10 '22 at 01:04
2

cc_library.includes takes strings representing paths, not labels. You want to set includes = ["."] in my-protobuf-common.

Similarly, cc_library.hdrs is for source files of headers which targets that depend on this one will #include. Listing something in both deps and hdrs doesn't make sense, for this use case you don't need hdrs at all.

Also, use cc_binary to build the file with a main. cc_library doesn't do a full link.

Additionally, copts = ["--std=c++17"] is rarely a good idea. That only sets the flag for the files in that cc_library, which can change its ABI so that linking to other parts of the build doesn't work. In this case, the cc_library-equivalent parts of cc_proto_library won't get the flag passed. Use the bazel command-line flag --copt=--std=c++17 instead to apply it to the entire build.

Brian Silverman
  • 3,085
  • 11
  • 13
  • I found I didn't need the include at all, and the reason for the copts being in the build file itself is I'm hoping it works like the min version in CMake which emits an error if you try to build in a project with a lesser version of gcc/g++. Thanks for the feedback. – weagle08 May 10 '22 at 12:37