• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1"""Macros that implement bootstrapping for the upb code generator."""
2
3load(
4    "//bazel:upb_minitable_proto_library.bzl",
5    "upb_minitable_proto_library",
6)
7load(
8    "//bazel:upb_proto_library.bzl",
9    "upb_proto_library",
10)
11load(
12    "//upb/cmake:build_defs.bzl",
13    "staleness_test",
14)
15
16_stages = ["_stage0", "_stage1", ""]
17_protoc = "//:protoc"
18
19_is_google3 = False
20_extra_proto_path = "-I$$(dirname $(location @com_google_protobuf//:descriptor_proto_srcs))/../.. "
21
22# This visibility is used automatically for anything used by the bootstrapping process.
23_bootstrap_visibility = [
24    "//upb_generator:__subpackages__",
25    "//upb/reflection:__pkg__",
26    "//upb:__pkg__",  # For the amalgamations.
27    "//python/dist:__pkg__",  # For the Python source package.
28]
29
30def _stage_visibility(stage, visibility):
31    return visibility if stage == "" else _bootstrap_visibility
32
33def _upbc(generator, stage):
34    if generator == "upb":
35        return "//upb_generator/c:protoc-gen-upb" + _stages[stage]
36    else:
37        return "//upb_generator/minitable:protoc-gen-upb_minitable" + _stages[stage]
38
39def bootstrap_cc_library(name, visibility = [], deps = [], bootstrap_deps = [], **kwargs):
40    """A version of cc_library() that is augmented to allow for bootstrapping the compiler.
41
42    In addition to the normal cc_library() target, this rule will also generate _stage0 and _stage1
43    targets that are used internally for bootstrapping, and will automatically have bootstrap
44    visibility. However the final target will use the normal visibility, and will behave like a
45    normal cc_library() target.
46
47    Args:
48        name: Name of this rule.  This name will resolve to a upb_proto_library().
49        deps: Normal cc_library() deps.
50        bootstrap_deps: Special bootstrap_upb_proto_library() or bootstrap_cc_library() deps.
51        visibility: Visibility of the final target.
52        **kwargs: Other arguments that will be passed through to cc_library().
53          upb_proto_library().
54    """
55    for stage in _stages:
56        native.cc_library(
57            name = name + stage,
58            deps = deps + [dep + stage for dep in bootstrap_deps],
59            visibility = _stage_visibility(stage, visibility),
60            **kwargs
61        )
62
63def bootstrap_cc_binary(name, visibility = [], deps = [], bootstrap_deps = [], **kwargs):
64    """A version of cc_binary() that is augmented to allow for bootstrapping the compiler.
65
66    In addition to the normal cc_binary() target, this rule will also generate _stage0 and _stage1
67    targets that are used internally for bootstrapping, and will automatically have bootstrap
68    visibility. However the final target will use the normal visibility, and will behave like a
69    normal cc_binary() target.
70
71    Args:
72        name: Name of this rule.  This name will resolve to a upb_proto_library().
73        deps: Normal cc_library() deps.
74        bootstrap_deps: Special bootstrap_upb_proto_library() or bootstrap_cc_library() deps.
75        visibility: Visibility of the final target.
76        **kwargs: Other arguments that will be passed through to cc_binary().
77          upb_proto_library().
78    """
79    for stage in _stages:
80        native.cc_binary(
81            name = name + stage,
82            deps = deps + [dep + stage for dep in bootstrap_deps],
83            visibility = _stage_visibility(stage, visibility),
84            **kwargs
85        )
86
87def _generated_file(proto, stage, generator, suffix):
88    stripped = proto[:-len(".proto")]
89    return "{}/{}.{}.{}".format(stage, stripped, generator, suffix)
90
91def _generated_files(protos, stage, generator, suffix):
92    return [_generated_file(proto, stage, generator, suffix) for proto in protos]
93
94def _generated_hdrs_and_srcs(protos, stage, generator):
95    ret = _generated_files(protos, stage, generator, "h")
96    if generator != "upb" or stage == "stage0":
97        ret += _generated_files(protos, stage, generator, "c")
98    return ret
99
100def _stage0_proto_staleness_test(name, src_files, src_rules, strip_prefix):
101    native.genrule(
102        name = name + "_generate_bootstrap",
103        srcs = src_rules,
104        outs = ["bootstrap_generated_sources/" + f for f in _generated_hdrs_and_srcs(src_files, "stage0", "upb")],
105        tools = [_protoc, _upbc("upb", 0)],
106        cmd =
107            "$(location " + _protoc + ") " +
108            "-I$(GENDIR)/" + strip_prefix + " " + _extra_proto_path +
109            "--plugin=protoc-gen-upb=$(location " + _upbc("upb", 0) + ") " +
110            "--upb_out=bootstrap_stage=0:$(@D)/bootstrap_generated_sources/stage0 " +
111            " ".join(src_files),
112    )
113
114    staleness_test(
115        name = name + "_stage0_staleness_test",
116        outs = _generated_hdrs_and_srcs(src_files, "stage0", "upb"),
117        generated_pattern = "bootstrap_generated_sources/%s",
118        target_files = native.glob(["stage0/**"]),
119        # To avoid skew problems for descriptor.proto/pluging.proto between
120        # GitHub repos.  It's not critical that the checked-in protos are up to
121        # date for every change, they just needs to be complete enough to have
122        # everything needed by the code generator itself.
123        tags = ["manual"],
124    )
125
126def _generate_stage1_proto(name, src_files, src_rules, generator, kwargs):
127    native.genrule(
128        name = "gen_{}_{}_stage1".format(name, generator),
129        srcs = src_rules,
130        outs = _generated_hdrs_and_srcs(src_files, "stage1", generator),
131        cmd = "$(location " + _protoc + ") " +
132              "--plugin=protoc-gen-" + generator +
133              "=$(location " + _upbc(generator, 0) + ") " + _extra_proto_path +
134              "--" + generator + "_out=bootstrap_stage=1:$(RULEDIR)/stage1 " +
135              " ".join(src_files),
136        visibility = _bootstrap_visibility,
137        tools = [
138            _protoc,
139            _upbc(generator, 0),
140        ],
141        **kwargs
142    )
143
144def _cmake_staleness_test(name, src_files, proto_lib_deps, **kwargs):
145    upb_minitable_proto_library(
146        name = name + "_minitable",
147        deps = proto_lib_deps,
148        **kwargs
149    )
150
151    # Copy the final gencode for staleness comparison
152    files = _generated_hdrs_and_srcs(src_files, "cmake", "upb") + \
153            _generated_hdrs_and_srcs(src_files, "cmake", "upb_minitable")
154    genrule = 0
155    for src in files:
156        genrule += 1
157        native.genrule(
158            name = name + "_copy_gencode_%d" % genrule,
159            outs = ["generated_sources/" + src],
160            srcs = [name + "_upb_proto", name + "_minitable"],
161            cmd = """
162                mkdir -p $(@D)
163                for src in $(SRCS); do
164                    if [[ $$src == *%s ]]; then
165                        cp -f $$src $(@D) || echo 'copy failed!'
166                    fi
167                done
168            """ % src[src.rfind("/"):],
169        )
170
171    # Keep bazel gencode in sync with our checked-in sources needed for cmake builds.
172    staleness_test(
173        name = name + "_staleness_test",
174        outs = files,
175        generated_pattern = "generated_sources/%s",
176        tags = ["manual"],
177    )
178
179def bootstrap_upb_proto_library(
180        name,
181        bootstrap_hdr,
182        google3_src_files,
183        google3_src_rules,
184        oss_src_files,
185        oss_src_rules,
186        oss_strip_prefix,
187        proto_lib_deps,
188        deps = [],
189        **kwargs):
190    """A version of upb_proto_library() that is augmented to allow for bootstrapping the compiler.
191
192    Note that this rule is only intended to be used by bootstrap_cc_library() targets. End users
193    should use the normal upb_proto_library() targets. As a result, we don't have a visibility
194    parameter: all targets will automatically have bootstrap visibility.
195
196    Args:
197        name: Name of this rule.  This name will resolve to a upb_proto_library().
198        bootstrap_hdr: The forwarding header that exposes the generated code, taking into account
199          the current stage.
200        google3_src_files: Google3 filenames of .proto files that should be built by this rule.
201          The names should be relative to the depot base.
202        google3_src_rules: Target names of the Blaze rules that will provide these filenames.
203        oss_src_files: OSS filenames of .proto files that should be built by this rule.
204        oss_src_rules: Target names of the Bazel rules that will provide these filenames.
205        oss_strip_prefix: Prefix that should be stripped from OSS file names.
206        proto_lib_deps: proto_library() rules that we will use to build the protos when we are
207          not bootstrapping.
208        deps: other bootstrap_upb_proto_library() rules that this one depends on.
209        **kwargs: Other arguments that will be passed through to cc_library(), genrule(), and
210          upb_proto_library().
211    """
212    _stage0_proto_staleness_test(name, oss_src_files, oss_src_rules, oss_strip_prefix)
213
214    # stage0 uses checked-in protos, and has no MiniTable.
215    native.cc_library(
216        name = name + "_stage0",
217        srcs = _generated_hdrs_and_srcs(oss_src_files, "stage0", "upb"),
218        hdrs = [bootstrap_hdr],
219        visibility = _bootstrap_visibility,
220        defines = ["UPB_BOOTSTRAP_STAGE=0"],
221        deps = [
222            "//upb:generated_code_support__only_for_generated_code_do_not_use__i_give_permission_to_break_me",
223            "//upb:mini_table",
224        ] + [dep + "_stage0" for dep in deps],
225        **kwargs
226    )
227
228    src_files = google3_src_files if _is_google3 else oss_src_files
229    src_rules = google3_src_rules if _is_google3 else oss_src_rules
230
231    # Generate stage1 protos (C API and MiniTables) using stage0 compiler.
232    _generate_stage1_proto(name, src_files, src_rules, "upb", kwargs)
233    _generate_stage1_proto(name, src_files, src_rules, "upb_minitable", kwargs)
234
235    native.cc_library(
236        name = name + "_minitable_stage1",
237        srcs = _generated_files(src_files, "stage1", "upb_minitable", "c"),
238        hdrs = _generated_files(src_files, "stage1", "upb_minitable", "h"),
239        visibility = _bootstrap_visibility,
240        defines = ["UPB_BOOTSTRAP_STAGE=1"],
241        deps = [
242            "//upb:generated_code_support__only_for_generated_code_do_not_use__i_give_permission_to_break_me",
243        ] + [dep + "_minitable_stage1" for dep in deps],
244        **kwargs
245    )
246    native.cc_library(
247        name = name + "_stage1",
248        srcs = _generated_files(src_files, "stage1", "upb", "h"),
249        hdrs = [bootstrap_hdr],
250        visibility = _bootstrap_visibility,
251        defines = ["UPB_BOOTSTRAP_STAGE=1"],
252        deps = [
253            "//upb:generated_code_support__only_for_generated_code_do_not_use__i_give_permission_to_break_me",
254            ":" + name + "_minitable_stage1",
255        ] + [dep + "_minitable_stage1" for dep in deps],
256        **kwargs
257    )
258
259    # The final protos are generated via normal upb_proto_library().
260    upb_proto_library(
261        name = name + "_upb_proto",
262        deps = proto_lib_deps,
263        **kwargs
264    )
265    native.cc_library(
266        name = name,
267        hdrs = [bootstrap_hdr],
268        deps = [name + "_upb_proto"],
269        visibility = _bootstrap_visibility,
270        **kwargs
271    )
272
273    _cmake_staleness_test(name, src_files, proto_lib_deps, **kwargs)
274