Skip to content

Commit

Permalink
Add wellknownimports package, for providing actual source code for th…
Browse files Browse the repository at this point in the history
…e standard imports (#310)

This adds wellknownimports.WithStandardImports as an alternative
to protocompile.WithStandardImports. The alternative provides
source code for the imports instead of just providing the embedded
descriptors from generated Go code. This allows users to make use
of the extension declarations added to `descriptor.proto` in v27.0
(they are absent from embedded descriptors since they are marked
as source-only retention).
  • Loading branch information
jhump authored Jun 4, 2024
1 parent 4190af1 commit 16a0337
Show file tree
Hide file tree
Showing 18 changed files with 3,023 additions and 0 deletions.
7 changes: 7 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,13 @@ $(PROTOC): $(CACHE)/protoc-$(PROTOC_VERSION).zip
unzip -o -q $< -d $(PROTOC_DIR) && \
touch $@

.PHONY: wellknownimports
wellknownimports: $(PROTOC) $(sort $(wildcard $(PROTOC_DIR)/include/google/protobuf/*.proto)) $(sort $(wildcard $(PROTOC_DIR)/include/google/protobuf/*/*.proto))
@rm -rf wellknownimports/google 2>/dev/null && true
@mkdir -p wellknownimports/google/protobuf/compiler
cp -R $(PROTOC_DIR)/include/google/protobuf/*.proto wellknownimports/google/protobuf
cp -R $(PROTOC_DIR)/include/google/protobuf/compiler/*.proto wellknownimports/google/protobuf/compiler

internal/testdata/all.protoset: $(PROTOC) $(sort $(wildcard internal/testdata/*.proto))
cd $(@D) && $(PROTOC) --descriptor_set_out=$(@F) --include_imports -I. $(filter-out protoc,$(^F))

Expand Down
21 changes: 21 additions & 0 deletions resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,27 @@ func SourceAccessorFromMap(srcs map[string]string) func(string) (io.ReadCloser,

// WithStandardImports returns a new resolver that knows about the same standard
// imports that are included with protoc.
//
// Note that this uses the descriptors embedded in generated code in the packages
// of the Protobuf Go module, except for "google/protobuf/cpp_features.proto" and
// "google/protobuf/java_features.proto". For those two files, compiled descriptors
// are embedded in this module because there is no package in the Protobuf Go module
// that contains generated code for those files. This resolver also provides results
// for the "google/protobuf/go_features.proto", which is technically not a standard
// file (it is not included with protoc) but is included in generated code in the
// Protobuf Go module.
//
// As of v0.14.0 of this module (and v1.34.2 of the Protobuf Go module and v27.0 of
// Protobuf), the contents of the standard import "google/protobuf/descriptor.proto"
// contain extension declarations which are *absent* from the descriptors that this
// resolver returns. That is because extension declarations are only retained in
// source, not at runtime, which means they are not available in the embedded
// descriptors in generated code.
//
// To use versions of the standard imports that *do* include these extension
// declarations, see wellknownimports.WithStandardImports instead. As of this
// writing, the declarations are only needed to prevent source files from
// illegally re-defining the custom features for C++, Java, and Go.
func WithStandardImports(r Resolver) Resolver {
return ResolverFunc(func(name string) (SearchResult, error) {
res, err := r.FindFileByPath(name)
Expand Down
146 changes: 146 additions & 0 deletions wellknownimports/google/protobuf/any.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
// Copyright 2020-2024 Buf Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

syntax = "proto3";

package google.protobuf;

option go_package = "google.golang.org/protobuf/types/known/anypb";
option java_package = "com.google.protobuf";
option java_outer_classname = "AnyProto";
option java_multiple_files = true;
option objc_class_prefix = "GPB";
option csharp_namespace = "Google.Protobuf.WellKnownTypes";

// `Any` contains an arbitrary serialized protocol buffer message along with a
// URL that describes the type of the serialized message.
//
// Protobuf library provides support to pack/unpack Any values in the form
// of utility functions or additional generated methods of the Any type.
//
// Example 1: Pack and unpack a message in C++.
//
// Foo foo = ...;
// Any any;
// any.PackFrom(foo);
// ...
// if (any.UnpackTo(&foo)) {
// ...
// }
//
// Example 2: Pack and unpack a message in Java.
//
// Foo foo = ...;
// Any any = Any.pack(foo);
// ...
// if (any.is(Foo.class)) {
// foo = any.unpack(Foo.class);
// }
// // or ...
// if (any.isSameTypeAs(Foo.getDefaultInstance())) {
// foo = any.unpack(Foo.getDefaultInstance());
// }
//
// Example 3: Pack and unpack a message in Python.
//
// foo = Foo(...)
// any = Any()
// any.Pack(foo)
// ...
// if any.Is(Foo.DESCRIPTOR):
// any.Unpack(foo)
// ...
//
// Example 4: Pack and unpack a message in Go
//
// foo := &pb.Foo{...}
// any, err := anypb.New(foo)
// if err != nil {
// ...
// }
// ...
// foo := &pb.Foo{}
// if err := any.UnmarshalTo(foo); err != nil {
// ...
// }
//
// The pack methods provided by protobuf library will by default use
// 'type.googleapis.com/full.type.name' as the type URL and the unpack
// methods only use the fully qualified type name after the last '/'
// in the type URL, for example "foo.bar.com/x/y.z" will yield type
// name "y.z".
//
// JSON
// ====
// The JSON representation of an `Any` value uses the regular
// representation of the deserialized, embedded message, with an
// additional field `@type` which contains the type URL. Example:
//
// package google.profile;
// message Person {
// string first_name = 1;
// string last_name = 2;
// }
//
// {
// "@type": "type.googleapis.com/google.profile.Person",
// "firstName": <string>,
// "lastName": <string>
// }
//
// If the embedded message type is well-known and has a custom JSON
// representation, that representation will be embedded adding a field
// `value` which holds the custom JSON in addition to the `@type`
// field. Example (for message [google.protobuf.Duration][]):
//
// {
// "@type": "type.googleapis.com/google.protobuf.Duration",
// "value": "1.212s"
// }
//
message Any {
// A URL/resource name that uniquely identifies the type of the serialized
// protocol buffer message. This string must contain at least
// one "/" character. The last segment of the URL's path must represent
// the fully qualified name of the type (as in
// `path/google.protobuf.Duration`). The name should be in a canonical form
// (e.g., leading "." is not accepted).
//
// In practice, teams usually precompile into the binary all types that they
// expect it to use in the context of Any. However, for URLs which use the
// scheme `http`, `https`, or no scheme, one can optionally set up a type
// server that maps type URLs to message definitions as follows:
//
// * If no scheme is provided, `https` is assumed.
// * An HTTP GET on the URL must yield a [google.protobuf.Type][]
// value in binary format, or produce an error.
// * Applications are allowed to cache lookup results based on the
// URL, or have them precompiled into a binary to avoid any
// lookup. Therefore, binary compatibility needs to be preserved
// on changes to types. (Use versioned type names to manage
// breaking changes.)
//
// Note: this functionality is not currently available in the official
// protobuf release, and it is not used for type URLs beginning with
// type.googleapis.com. As of May 2023, there are no widely used type server
// implementations and no plans to implement one.
//
// Schemes other than `http`, `https` (or the empty scheme) might be
// used with implementation specific semantics.
//
string type_url = 1;

// Must be a valid serialized protocol buffer of the above specified type.
bytes value = 2;
}
191 changes: 191 additions & 0 deletions wellknownimports/google/protobuf/api.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
// Copyright 2020-2024 Buf Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

syntax = "proto3";

package google.protobuf;

import "google/protobuf/source_context.proto";
import "google/protobuf/type.proto";

option java_package = "com.google.protobuf";
option java_outer_classname = "ApiProto";
option java_multiple_files = true;
option objc_class_prefix = "GPB";
option csharp_namespace = "Google.Protobuf.WellKnownTypes";
option go_package = "google.golang.org/protobuf/types/known/apipb";

// Api is a light-weight descriptor for an API Interface.
//
// Interfaces are also described as "protocol buffer services" in some contexts,
// such as by the "service" keyword in a .proto file, but they are different
// from API Services, which represent a concrete implementation of an interface
// as opposed to simply a description of methods and bindings. They are also
// sometimes simply referred to as "APIs" in other contexts, such as the name of
// this message itself. See https://cloud.google.com/apis/design/glossary for
// detailed terminology.
message Api {
// The fully qualified name of this interface, including package name
// followed by the interface's simple name.
string name = 1;

// The methods of this interface, in unspecified order.
repeated Method methods = 2;

// Any metadata attached to the interface.
repeated Option options = 3;

// A version string for this interface. If specified, must have the form
// `major-version.minor-version`, as in `1.10`. If the minor version is
// omitted, it defaults to zero. If the entire version field is empty, the
// major version is derived from the package name, as outlined below. If the
// field is not empty, the version in the package name will be verified to be
// consistent with what is provided here.
//
// The versioning schema uses [semantic
// versioning](http://semver.org) where the major version number
// indicates a breaking change and the minor version an additive,
// non-breaking change. Both version numbers are signals to users
// what to expect from different versions, and should be carefully
// chosen based on the product plan.
//
// The major version is also reflected in the package name of the
// interface, which must end in `v<major-version>`, as in
// `google.feature.v1`. For major versions 0 and 1, the suffix can
// be omitted. Zero major versions must only be used for
// experimental, non-GA interfaces.
//
string version = 4;

// Source context for the protocol buffer service represented by this
// message.
SourceContext source_context = 5;

// Included interfaces. See [Mixin][].
repeated Mixin mixins = 6;

// The source syntax of the service.
Syntax syntax = 7;
}

// Method represents a method of an API interface.
message Method {
// The simple name of this method.
string name = 1;

// A URL of the input message type.
string request_type_url = 2;

// If true, the request is streamed.
bool request_streaming = 3;

// The URL of the output message type.
string response_type_url = 4;

// If true, the response is streamed.
bool response_streaming = 5;

// Any metadata attached to the method.
repeated Option options = 6;

// The source syntax of this method.
Syntax syntax = 7;
}

// Declares an API Interface to be included in this interface. The including
// interface must redeclare all the methods from the included interface, but
// documentation and options are inherited as follows:
//
// - If after comment and whitespace stripping, the documentation
// string of the redeclared method is empty, it will be inherited
// from the original method.
//
// - Each annotation belonging to the service config (http,
// visibility) which is not set in the redeclared method will be
// inherited.
//
// - If an http annotation is inherited, the path pattern will be
// modified as follows. Any version prefix will be replaced by the
// version of the including interface plus the [root][] path if
// specified.
//
// Example of a simple mixin:
//
// package google.acl.v1;
// service AccessControl {
// // Get the underlying ACL object.
// rpc GetAcl(GetAclRequest) returns (Acl) {
// option (google.api.http).get = "/v1/{resource=**}:getAcl";
// }
// }
//
// package google.storage.v2;
// service Storage {
// rpc GetAcl(GetAclRequest) returns (Acl);
//
// // Get a data record.
// rpc GetData(GetDataRequest) returns (Data) {
// option (google.api.http).get = "/v2/{resource=**}";
// }
// }
//
// Example of a mixin configuration:
//
// apis:
// - name: google.storage.v2.Storage
// mixins:
// - name: google.acl.v1.AccessControl
//
// The mixin construct implies that all methods in `AccessControl` are
// also declared with same name and request/response types in
// `Storage`. A documentation generator or annotation processor will
// see the effective `Storage.GetAcl` method after inherting
// documentation and annotations as follows:
//
// service Storage {
// // Get the underlying ACL object.
// rpc GetAcl(GetAclRequest) returns (Acl) {
// option (google.api.http).get = "/v2/{resource=**}:getAcl";
// }
// ...
// }
//
// Note how the version in the path pattern changed from `v1` to `v2`.
//
// If the `root` field in the mixin is specified, it should be a
// relative path under which inherited HTTP paths are placed. Example:
//
// apis:
// - name: google.storage.v2.Storage
// mixins:
// - name: google.acl.v1.AccessControl
// root: acls
//
// This implies the following inherited HTTP annotation:
//
// service Storage {
// // Get the underlying ACL object.
// rpc GetAcl(GetAclRequest) returns (Acl) {
// option (google.api.http).get = "/v2/acls/{resource=**}:getAcl";
// }
// ...
// }
message Mixin {
// The fully qualified name of the interface which is included.
string name = 1;

// If non-empty specifies a path under which inherited HTTP paths
// are rooted.
string root = 2;
}
Loading

0 comments on commit 16a0337

Please sign in to comment.