|
|
|
"""Implementation of the aspect that powers the upb_*_proto_library() rules."""
|
|
|
|
|
|
|
|
load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "find_cpp_toolchain")
|
|
|
|
load("//bazel/common:proto_common.bzl", "proto_common")
|
|
|
|
load(":upb_proto_library_internal/cc_library_func.bzl", "cc_library_func")
|
|
|
|
load(":upb_proto_library_internal/copts.bzl", "UpbProtoLibraryCoptsInfo")
|
|
|
|
|
|
|
|
# begin:github_only
|
|
|
|
_is_google3 = False
|
|
|
|
# end:github_only
|
|
|
|
|
|
|
|
# begin:google_only
|
|
|
|
# _is_google3 = True
|
|
|
|
# end:google_only
|
|
|
|
|
|
|
|
GeneratedSrcsInfo = provider(
|
|
|
|
"Provides generated headers and sources",
|
|
|
|
fields = {
|
|
|
|
"srcs": "list of srcs",
|
|
|
|
"hdrs": "list of hdrs",
|
|
|
|
"thunks": "Experimental, do not use. List of srcs defining C API. Incompatible with hdrs.",
|
|
|
|
},
|
|
|
|
)
|
|
|
|
|
|
|
|
def output_dir(ctx, proto_info):
|
|
|
|
"""Returns the output directory where generated proto files will be placed.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
ctx: Rule context.
|
|
|
|
proto_info: ProtoInfo provider.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
A string specifying the output directory
|
|
|
|
"""
|
|
|
|
proto_root = proto_info.proto_source_root
|
|
|
|
if proto_root.startswith(ctx.bin_dir.path):
|
|
|
|
path = proto_root
|
|
|
|
else:
|
|
|
|
path = ctx.bin_dir.path + "/" + proto_root
|
|
|
|
|
|
|
|
if proto_root == ".":
|
|
|
|
path = ctx.bin_dir.path
|
|
|
|
return path
|
|
|
|
|
|
|
|
def _concat_lists(lists):
|
|
|
|
ret = []
|
|
|
|
for lst in lists:
|
|
|
|
ret = ret + lst
|
|
|
|
return ret
|
|
|
|
|
|
|
|
def _merge_generated_srcs(srcs):
|
|
|
|
return GeneratedSrcsInfo(
|
|
|
|
srcs = _concat_lists([s.srcs for s in srcs]),
|
|
|
|
hdrs = _concat_lists([s.hdrs for s in srcs]),
|
|
|
|
thunks = _concat_lists([s.thunks for s in srcs]),
|
|
|
|
)
|
|
|
|
|
|
|
|
def _get_implicit_weak_field_sources(ctx, proto_info):
|
|
|
|
# Creating one .cc file for each Message in a proto allows the linker to be more aggressive
|
|
|
|
# about removing unused classes. However, since the number of outputs won't be known at Blaze
|
|
|
|
# analysis time, all of the generated source files are put in a directory and a TreeArtifact is
|
|
|
|
# used to represent them.
|
|
|
|
proto_artifacts = []
|
|
|
|
for proto_source in proto_info.direct_sources:
|
|
|
|
# We can have slashes in the target name. For example, proto_source can be:
|
|
|
|
# dir/a.proto. However proto_source.basename will return a.proto, when in reality
|
|
|
|
# the BUILD file declares it as dir/a.proto, because target name contains a slash.
|
|
|
|
# There is no good workaround for this.
|
|
|
|
# I am using ctx.label.package to check if the name of the target contains slash or not.
|
|
|
|
# This is similar to what declare_directory does.
|
|
|
|
if not proto_source.short_path.startswith(ctx.label.package):
|
|
|
|
fail("This should never happen, proto source {} path does not start with {}.".format(
|
|
|
|
proto_source.short_path,
|
|
|
|
ctx.label.package,
|
|
|
|
))
|
|
|
|
proto_source_name = proto_source.short_path[len(ctx.label.package) + 1:]
|
|
|
|
last_dot = proto_source_name.rfind(".")
|
|
|
|
if last_dot != -1:
|
|
|
|
proto_source_name = proto_source_name[:last_dot]
|
|
|
|
proto_artifacts.append(ctx.actions.declare_directory(proto_source_name + ".upb_weak_minitables"))
|
|
|
|
|
|
|
|
return proto_artifacts
|
|
|
|
|
|
|
|
def _get_feature_configuration(ctx, cc_toolchain, proto_info):
|
|
|
|
requested_features = list(ctx.features)
|
|
|
|
|
|
|
|
# Disable the whole-archive behavior for protobuf generated code when the
|
|
|
|
# proto_one_output_per_message feature is enabled.
|
|
|
|
requested_features.append("disable_whole_archive_for_static_lib_if_proto_one_output_per_message")
|
|
|
|
unsupported_features = list(ctx.disabled_features)
|
|
|
|
if len(proto_info.direct_sources) != 0:
|
|
|
|
requested_features.append("header_modules")
|
|
|
|
else:
|
|
|
|
unsupported_features.append("header_modules")
|
|
|
|
return cc_common.configure_features(
|
|
|
|
ctx = ctx,
|
|
|
|
cc_toolchain = cc_toolchain,
|
|
|
|
requested_features = requested_features,
|
|
|
|
unsupported_features = unsupported_features,
|
|
|
|
)
|
|
|
|
|
|
|
|
def _generate_srcs_list(ctx, generator, proto_info):
|
|
|
|
if len(proto_info.direct_sources) == 0:
|
|
|
|
return GeneratedSrcsInfo(srcs = [], hdrs = [], thunks = [], includes = [])
|
|
|
|
|
|
|
|
ext = "." + generator
|
|
|
|
srcs = []
|
|
|
|
thunks = []
|
|
|
|
hdrs = proto_common.declare_generated_files(
|
|
|
|
ctx.actions,
|
|
|
|
extension = ext + ".h",
|
|
|
|
proto_info = proto_info,
|
|
|
|
)
|
|
|
|
if not (generator == "upb" and _is_google3):
|
|
|
|
# TODO: The OSS build should also exclude this file for the upb generator,
|
|
|
|
# as it is empty and unnecessary. We only added it to make the OSS build happy on
|
|
|
|
# Windows and macOS.
|
|
|
|
srcs += proto_common.declare_generated_files(
|
|
|
|
ctx.actions,
|
|
|
|
extension = ext + ".c",
|
|
|
|
proto_info = proto_info,
|
|
|
|
)
|
|
|
|
if generator == "upb":
|
|
|
|
thunks = proto_common.declare_generated_files(
|
|
|
|
ctx.actions,
|
|
|
|
extension = ext + ".thunks.c",
|
|
|
|
proto_info = proto_info,
|
|
|
|
)
|
|
|
|
ctx.actions.run_shell(
|
|
|
|
inputs = hdrs,
|
|
|
|
outputs = thunks,
|
|
|
|
command = " && ".join([
|
|
|
|
"sed 's/UPB_INLINE //' {} > {}".format(hdr.path, thunk.path)
|
|
|
|
for (hdr, thunk) in zip(hdrs, thunks)
|
|
|
|
]),
|
|
|
|
progress_message = "Generating thunks for upb protos API for: " + ctx.label.name,
|
|
|
|
mnemonic = "GenUpbProtosThunks",
|
|
|
|
)
|
|
|
|
|
|
|
|
return GeneratedSrcsInfo(
|
|
|
|
srcs = srcs,
|
|
|
|
hdrs = hdrs,
|
|
|
|
thunks = thunks,
|
|
|
|
)
|
|
|
|
|
|
|
|
def _generate_upb_protos(ctx, generator, proto_info, feature_configuration):
|
|
|
|
implicit_weak = generator == "upb_minitable" and cc_common.is_enabled(
|
|
|
|
feature_configuration = feature_configuration,
|
|
|
|
feature_name = "proto_one_output_per_message",
|
|
|
|
)
|
|
|
|
|
|
|
|
srcs = _generate_srcs_list(ctx, generator, proto_info)
|
|
|
|
additional_args = ctx.actions.args()
|
|
|
|
|
|
|
|
if implicit_weak:
|
|
|
|
srcs.srcs.extend(_get_implicit_weak_field_sources(ctx, proto_info))
|
|
|
|
additional_args.add("--upb_minitable_opt=one_output_per_message")
|
|
|
|
|
|
|
|
proto_common.compile(
|
|
|
|
actions = ctx.actions,
|
|
|
|
proto_info = proto_info,
|
|
|
|
proto_lang_toolchain_info = _get_lang_toolchain(ctx, generator),
|
|
|
|
generated_files = srcs.srcs + srcs.hdrs,
|
|
|
|
experimental_exec_group = "proto_compiler",
|
|
|
|
additional_args = additional_args,
|
|
|
|
)
|
|
|
|
|
|
|
|
return srcs
|
|
|
|
|
|
|
|
def _generate_name(ctx, generator, thunks = False):
|
|
|
|
if thunks:
|
|
|
|
return ctx.rule.attr.name + "." + generator + ".thunks"
|
|
|
|
return ctx.rule.attr.name + "." + generator
|
|
|
|
|
|
|
|
def _get_dep_cc_infos(target, ctx, generator, cc_provider, dep_cc_provider):
|
|
|
|
rule_deps = ctx.rule.attr.deps
|
|
|
|
dep_ccinfos = [dep[cc_provider].cc_info for dep in rule_deps]
|
|
|
|
if dep_cc_provider:
|
|
|
|
# This gives access to our direct sibling. eg. foo.upb.h can #include "foo.upb_minitable.h"
|
|
|
|
dep_ccinfos.append(target[dep_cc_provider].cc_info)
|
|
|
|
|
|
|
|
# This gives access to imports. eg. foo.upb.h can #include "import1.upb_minitable.h"
|
|
|
|
# But not transitive imports, eg. foo.upb.h cannot #include "transitive_import1.upb_minitable.h"
|
|
|
|
dep_ccinfos += [dep[dep_cc_provider].cc_info for dep in rule_deps]
|
|
|
|
|
|
|
|
return dep_ccinfos
|
|
|
|
|
|
|
|
def _get_lang_toolchain(ctx, generator):
|
|
|
|
lang_toolchain_name = "_" + generator + "_toolchain"
|
|
|
|
return getattr(ctx.attr, lang_toolchain_name)[proto_common.ProtoLangToolchainInfo]
|
|
|
|
|
|
|
|
def _compile_upb_protos(ctx, files, generator, dep_ccinfos, cc_provider, proto_info):
|
|
|
|
cc_info = cc_library_func(
|
|
|
|
ctx = ctx,
|
|
|
|
name = _generate_name(ctx, generator),
|
|
|
|
hdrs = files.hdrs,
|
|
|
|
srcs = files.srcs,
|
|
|
|
includes = [output_dir(ctx, proto_info)],
|
|
|
|
copts = ctx.attr._copts[UpbProtoLibraryCoptsInfo].copts,
|
|
|
|
dep_ccinfos = dep_ccinfos,
|
|
|
|
)
|
|
|
|
|
|
|
|
if files.thunks:
|
|
|
|
cc_info_with_thunks = cc_library_func(
|
|
|
|
ctx = ctx,
|
|
|
|
name = _generate_name(ctx, generator, files.thunks),
|
|
|
|
hdrs = [],
|
|
|
|
srcs = files.thunks,
|
|
|
|
includes = [output_dir(ctx, proto_info)],
|
|
|
|
copts = ctx.attr._copts[UpbProtoLibraryCoptsInfo].copts,
|
|
|
|
dep_ccinfos = dep_ccinfos + [cc_info],
|
|
|
|
)
|
|
|
|
return cc_provider(
|
|
|
|
cc_info = cc_info,
|
|
|
|
cc_info_with_thunks = cc_info_with_thunks,
|
|
|
|
)
|
|
|
|
else:
|
|
|
|
return cc_provider(
|
|
|
|
cc_info = cc_info,
|
|
|
|
)
|
|
|
|
|
|
|
|
_GENERATORS = ["upb", "upbdefs", "upb_minitable"]
|
|
|
|
|
|
|
|
def _get_hint_providers(ctx, generator):
|
|
|
|
if generator not in _GENERATORS:
|
|
|
|
fail("Please add new generator '{}' to _GENERATORS list".format(generator))
|
|
|
|
|
|
|
|
possible_owners = []
|
|
|
|
for generator in _GENERATORS:
|
|
|
|
possible_owners.append(ctx.label.relative(_generate_name(ctx, generator)))
|
|
|
|
possible_owners.append(ctx.label.relative(_generate_name(ctx, generator, thunks = True)))
|
|
|
|
|
|
|
|
if hasattr(cc_common, "CcSharedLibraryHintInfo"):
|
|
|
|
return [cc_common.CcSharedLibraryHintInfo(owners = possible_owners)]
|
|
|
|
elif hasattr(cc_common, "CcSharedLibraryHintInfo_6_X_constructor_do_not_use"):
|
|
|
|
# This branch can be deleted once 6.X is not supported by upb rules
|
|
|
|
return [cc_common.CcSharedLibraryHintInfo_6_X_constructor_do_not_use(owners = possible_owners)]
|
|
|
|
|
|
|
|
return []
|
|
|
|
|
|
|
|
def upb_proto_aspect_impl(
|
|
|
|
target,
|
|
|
|
ctx,
|
|
|
|
generator,
|
|
|
|
cc_provider,
|
|
|
|
dep_cc_provider,
|
|
|
|
file_provider,
|
|
|
|
provide_cc_shared_library_hints = True):
|
|
|
|
"""A shared aspect implementation for upb_*proto_library() rules.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
target: The `target` parameter from the aspect function.
|
|
|
|
ctx: The `ctx` parameter from the aspect function.
|
|
|
|
generator: A string describing which aspect we are generating. This triggers several special
|
|
|
|
behaviors, and ideally this will be refactored to be less magical.
|
|
|
|
cc_provider: The provider that this aspect will attach to the target. Should contain a
|
|
|
|
`cc_info` field. The aspect will ensure that each compilation action can compile and link
|
|
|
|
against this provider's cc_info for all proto_library() deps.
|
|
|
|
dep_cc_provider: For aspects that depend on other aspects, this is the provider of the aspect
|
|
|
|
that we depend on. The aspect wil be able to include the header files from this provider.
|
|
|
|
file_provider: A provider that this aspect will attach to the target to expose the source
|
|
|
|
files generated by this aspect. These files are primarily useful for returning in
|
|
|
|
DefaultInfo(), so users who build the upb_*proto_library() rule directly can view the
|
|
|
|
generated sources.
|
|
|
|
provide_cc_shared_library_hints: Whether shared library hints should be provided.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
The `cc_provider` and `file_provider` providers as described above.
|
|
|
|
"""
|
|
|
|
dep_ccinfos = _get_dep_cc_infos(target, ctx, generator, cc_provider, dep_cc_provider)
|
|
|
|
if not getattr(ctx.rule.attr, "srcs", []):
|
|
|
|
# This target doesn't declare any sources, reexport all its deps instead.
|
|
|
|
# This is known as an "alias library":
|
|
|
|
# https://bazel.build/reference/be/protocol-buffer#proto_library.srcs
|
|
|
|
files = _merge_generated_srcs([dep[file_provider].srcs for dep in ctx.rule.attr.deps])
|
|
|
|
wrapped_cc_info = cc_provider(
|
|
|
|
cc_info = cc_common.merge_cc_infos(direct_cc_infos = dep_ccinfos),
|
|
|
|
)
|
|
|
|
else:
|
|
|
|
proto_info = target[ProtoInfo]
|
|
|
|
cc_toolchain = find_cpp_toolchain(ctx)
|
|
|
|
feature_configuration = _get_feature_configuration(ctx, cc_toolchain, proto_info)
|
|
|
|
files = _generate_upb_protos(
|
|
|
|
ctx,
|
|
|
|
generator,
|
|
|
|
proto_info,
|
|
|
|
feature_configuration,
|
|
|
|
)
|
|
|
|
wrapped_cc_info = _compile_upb_protos(
|
|
|
|
ctx,
|
|
|
|
files,
|
|
|
|
generator,
|
|
|
|
dep_ccinfos + [_get_lang_toolchain(ctx, generator).runtime[CcInfo]],
|
|
|
|
cc_provider,
|
|
|
|
proto_info,
|
|
|
|
)
|
|
|
|
|
|
|
|
hints = _get_hint_providers(ctx, generator) if provide_cc_shared_library_hints else []
|
|
|
|
|
|
|
|
return hints + [
|
|
|
|
file_provider(srcs = files),
|
|
|
|
wrapped_cc_info,
|
|
|
|
]
|