aboutsummaryrefslogtreecommitdiffhomepage
path: root/bazel/generate_cc.bzl
blob: ae747aa42ca0ab2d97d32ae7c8356e639d55d891 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
"""Generates C++ grpc stubs from proto_library rules.

This is an internal rule used by cc_grpc_library, and shouldn't be used
directly.
"""

def generate_cc_impl(ctx):
  """Implementation of the generate_cc rule."""
  protos = [f for src in ctx.attr.srcs for f in src.proto.direct_sources]
  includes = [f for src in ctx.attr.srcs for f in src.proto.transitive_imports]
  outs = []
  # label_len is length of the path from WORKSPACE root to the location of this build file
  label_len = 0
  # proto_root is the directory relative to which generated include paths should be
  proto_root = ""
  if ctx.label.package:
    # The +1 is for the trailing slash.
    label_len += len(ctx.label.package) + 1
  if ctx.label.workspace_root:
    label_len += len(ctx.label.workspace_root) + 1
    proto_root = "/" + ctx.label.workspace_root

  if ctx.executable.plugin:
    outs += [proto.path[label_len:-len(".proto")] + ".grpc.pb.h" for proto in protos]
    outs += [proto.path[label_len:-len(".proto")] + ".grpc.pb.cc" for proto in protos]
    if ctx.attr.generate_mocks:
      outs += [proto.path[label_len:-len(".proto")] + "_mock.grpc.pb.h" for proto in protos]
  else:
    outs += [proto.path[label_len:-len(".proto")] + ".pb.h" for proto in protos]
    outs += [proto.path[label_len:-len(".proto")] + ".pb.cc" for proto in protos]
  out_files = [ctx.new_file(out) for out in outs]
  dir_out = str(ctx.genfiles_dir.path + proto_root)

  arguments = []
  if ctx.executable.plugin:
    arguments += ["--plugin=protoc-gen-PLUGIN=" + ctx.executable.plugin.path]
    flags = list(ctx.attr.flags)
    if ctx.attr.generate_mocks:
      flags.append("generate_mock_code=true")
    arguments += ["--PLUGIN_out=" + ",".join(flags) + ":" + dir_out]
    additional_input = [ctx.executable.plugin]
  else:
    arguments += ["--cpp_out=" + ",".join(ctx.attr.flags) + ":" + dir_out]
    additional_input = []

  # Import protos relative to their workspace root so that protoc prints the
  # right include paths.
  for include in includes:
    directory = include.path
    if directory.startswith("external"):
      external_sep = directory.find("/")
      repository_sep = directory.find("/", external_sep + 1)
      arguments += ["--proto_path=" + directory[:repository_sep]]
    else:
      arguments += ["--proto_path=."]
  # Include the output directory so that protoc puts the generated code in the
  # right directory.
  arguments += ["--proto_path={0}{1}".format(dir_out, proto_root)]
  arguments += [proto.path for proto in protos]

  # create a list of well known proto files if the argument is non-None
  well_known_proto_files = []
  if ctx.attr.well_known_protos:
    f = ctx.attr.well_known_protos.files.to_list()[0].dirname
    if f != "external/com_google_protobuf/src/google/protobuf":
      print("Error: Only @com_google_protobuf//:well_known_protos is supported")
    else:
      # f points to "external/com_google_protobuf/src/google/protobuf"
      # add -I argument to protoc so it knows where to look for the proto files.
      arguments += ["-I{0}".format(f + "/../..")]
      well_known_proto_files = [f for f in ctx.attr.well_known_protos.files]

  ctx.action(
      inputs = protos + includes + additional_input + well_known_proto_files,
      outputs = out_files,
      executable = ctx.executable._protoc,
      arguments = arguments,
  )

  return struct(files=depset(out_files))

_generate_cc = rule(
    attrs = {
        "srcs": attr.label_list(
            mandatory = True,
            non_empty = True,
            providers = ["proto"],
        ),
        "plugin": attr.label(
            executable = True,
            providers = ["files_to_run"],
            cfg = "host",
        ),
        "flags": attr.string_list(
            mandatory = False,
            allow_empty = True,
        ),
        "well_known_protos" : attr.label(
            mandatory = False,
        ),
        "generate_mocks" : attr.bool(
            default = False,
            mandatory = False,
        ),
        "_protoc": attr.label(
            default = Label("//external:protocol_compiler"),
            executable = True,
            cfg = "host",
        ),
    },
    # We generate .h files, so we need to output to genfiles.
    output_to_genfiles = True,
    implementation = generate_cc_impl,
)

def generate_cc(well_known_protos, **kwargs):
  if well_known_protos:
    _generate_cc(well_known_protos="@com_google_protobuf//:well_known_protos", **kwargs)
  else:
    _generate_cc(**kwargs)