Protocol Buffers - Google's data interchange format (grpc依赖)
https://developers.google.com/protocol-buffers/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
304 lines
12 KiB
304 lines
12 KiB
"""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("//bazel/common:proto_info.bzl", "ProtoInfo") |
|
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, |
|
]
|
|
|