...

Text file src/github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger/defs.bzl

Documentation: github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger

     1"""Generated an open-api spec for a grpc api spec.
     2
     3Reads the the api spec in protobuf format and generate an open-api spec.
     4Optionally applies settings from the grpc-service configuration.
     5"""
     6
     7load("@rules_proto//proto:defs.bzl", "ProtoInfo")
     8
     9# TODO(yannic): Replace with |proto_common.direct_source_infos| when
    10# https://github.com/bazelbuild/rules_proto/pull/22 lands.
    11def _direct_source_infos(proto_info, provided_sources = []):
    12    """Returns sequence of `ProtoFileInfo` for `proto_info`'s direct sources.
    13
    14    Files that are both in `proto_info`'s direct sources and in
    15    `provided_sources` are skipped. This is useful, e.g., for well-known
    16    protos that are already provided by the Protobuf runtime.
    17
    18    Args:
    19      proto_info: An instance of `ProtoInfo`.
    20      provided_sources: Optional. A sequence of files to ignore.
    21          Usually, these files are already provided by the
    22          Protocol Buffer runtime (e.g. Well-Known protos).
    23
    24    Returns: A sequence of `ProtoFileInfo` containing information about
    25        `proto_info`'s direct sources.
    26    """
    27
    28    source_root = proto_info.proto_source_root
    29    if "." == source_root:
    30        return [struct(file = src, import_path = src.path) for src in proto_info.direct_sources]
    31
    32    offset = len(source_root) + 1  # + '/'.
    33
    34    infos = []
    35    for src in proto_info.direct_sources:
    36        # TODO(yannic): Remove this hack when we drop support for Bazel < 1.0.
    37        local_offset = offset
    38        if src.root.path and not source_root.startswith(src.root.path):
    39            # Before Bazel 1.0, `proto_source_root` wasn't guaranteed to be a
    40            # prefix of `src.path`. This could happend, e.g., if `file` was
    41            # generated (https://github.com/bazelbuild/bazel/issues/9215).
    42            local_offset += len(src.root.path) + 1  # + '/'.
    43        infos.append(struct(file = src, import_path = src.path[local_offset:]))
    44
    45    return infos
    46
    47def _run_proto_gen_swagger(
    48        actions,
    49        proto_info,
    50        target_name,
    51        transitive_proto_srcs,
    52        protoc,
    53        protoc_gen_swagger,
    54        grpc_api_configuration,
    55        single_output,
    56        json_names_for_fields,
    57        fqn_for_swagger_name):
    58    args = actions.args()
    59
    60    args.add("--plugin", "protoc-gen-swagger=%s" % protoc_gen_swagger.path)
    61
    62    args.add("--swagger_opt", "logtostderr=true")
    63    args.add("--swagger_opt", "allow_repeated_fields_in_body=true")
    64
    65    extra_inputs = []
    66    if grpc_api_configuration:
    67        extra_inputs.append(grpc_api_configuration)
    68        args.add("--swagger_opt", "grpc_api_configuration=%s" % grpc_api_configuration.path)
    69
    70    if json_names_for_fields:
    71        args.add("--swagger_opt", "json_names_for_fields=true")
    72
    73    if fqn_for_swagger_name:
    74        args.add("--swagger_opt", "fqn_for_swagger_name=true")
    75
    76    proto_file_infos = _direct_source_infos(proto_info)
    77
    78    # TODO(yannic): Use |proto_info.transitive_descriptor_sets| when
    79    # https://github.com/bazelbuild/bazel/issues/9337 is fixed.
    80    args.add_all(proto_info.transitive_proto_path, format_each = "--proto_path=%s")
    81
    82    if single_output:
    83        args.add("--swagger_opt", "allow_merge=true")
    84        args.add("--swagger_opt", "merge_file_name=%s" % target_name)
    85
    86        swagger_file = actions.declare_file("%s.swagger.json" % target_name)
    87        args.add("--swagger_out", swagger_file.dirname)
    88
    89        args.add_all([f.import_path for f in proto_file_infos])
    90
    91        actions.run(
    92            executable = protoc,
    93            tools = [protoc_gen_swagger],
    94            inputs = depset(
    95                direct = extra_inputs,
    96                transitive = [transitive_proto_srcs],
    97            ),
    98            outputs = [swagger_file],
    99            arguments = [args],
   100        )
   101
   102        return [swagger_file]
   103
   104    # TODO(yannic): We may be able to generate all files in a single action,
   105    # but that will change at least the semantics of `use_go_template.proto`.
   106    swagger_files = []
   107    for proto_file_info in proto_file_infos:
   108        # TODO(yannic): This probably doesn't work as expected: we only add this
   109        # option after we have seen it, so `.proto` sources that happen to be
   110        # in the list of `.proto` files before `use_go_template.proto` will be
   111        # compiled without this option, and all sources that get compiled after
   112        # `use_go_template.proto` will have this option on.
   113        if proto_file_info.file.basename == "use_go_template.proto":
   114            args.add("--swagger_opt", "use_go_templates=true")
   115
   116        file_name = "%s.swagger.json" % proto_file_info.import_path[:-len(".proto")]
   117        swagger_file = actions.declare_file(
   118            "_virtual_imports/%s/%s" % (target_name, file_name),
   119        )
   120
   121        file_args = actions.args()
   122
   123        offset = len(file_name) + 1  # + '/'.
   124        file_args.add("--swagger_out", swagger_file.path[:-offset])
   125
   126        file_args.add(proto_file_info.import_path)
   127
   128        actions.run(
   129            executable = protoc,
   130            tools = [protoc_gen_swagger],
   131            inputs = depset(
   132                direct = extra_inputs,
   133                transitive = [transitive_proto_srcs],
   134            ),
   135            outputs = [swagger_file],
   136            arguments = [args, file_args],
   137        )
   138        swagger_files.append(swagger_file)
   139
   140    return swagger_files
   141
   142def _proto_gen_swagger_impl(ctx):
   143    proto = ctx.attr.proto[ProtoInfo]
   144    return [
   145        DefaultInfo(
   146            files = depset(
   147                _run_proto_gen_swagger(
   148                    actions = ctx.actions,
   149                    proto_info = proto,
   150                    target_name = ctx.attr.name,
   151                    transitive_proto_srcs = depset(
   152                        direct = ctx.files._well_known_protos,
   153                        transitive = [proto.transitive_sources],
   154                    ),
   155                    protoc = ctx.executable._protoc,
   156                    protoc_gen_swagger = ctx.executable._protoc_gen_swagger,
   157                    grpc_api_configuration = ctx.file.grpc_api_configuration,
   158                    single_output = ctx.attr.single_output,
   159                    json_names_for_fields = ctx.attr.json_names_for_fields,
   160                    fqn_for_swagger_name = ctx.attr.fqn_for_swagger_name,
   161                ),
   162            ),
   163        ),
   164    ]
   165
   166protoc_gen_swagger = rule(
   167    attrs = {
   168        "proto": attr.label(
   169            mandatory = True,
   170            providers = [ProtoInfo],
   171        ),
   172        "grpc_api_configuration": attr.label(
   173            allow_single_file = True,
   174            mandatory = False,
   175        ),
   176        "single_output": attr.bool(
   177            default = False,
   178            mandatory = False,
   179        ),
   180        "json_names_for_fields": attr.bool(
   181            default = False,
   182            mandatory = False,
   183        ),
   184        "fqn_for_swagger_name": attr.bool(
   185            default = False,
   186            mandatory = False,
   187        ),
   188        "_protoc": attr.label(
   189            default = "@com_google_protobuf//:protoc",
   190            executable = True,
   191            cfg = "host",
   192        ),
   193        "_well_known_protos": attr.label(
   194            default = "@com_google_protobuf//:well_known_protos",
   195            allow_files = True,
   196        ),
   197        "_protoc_gen_swagger": attr.label(
   198            default = Label("//protoc-gen-swagger:protoc-gen-swagger"),
   199            executable = True,
   200            cfg = "host",
   201        ),
   202    },
   203    implementation = _proto_gen_swagger_impl,
   204)

View as plain text