Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

protoc-gen-go: add paths=source_relative option #544

Merged
merged 4 commits into from
Mar 6, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 49 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,49 @@ parameter set to the directory you want to output the Go code to.
The generated files will be suffixed .pb.go. See the Test code below
for an example using such a file.

## Packages and input paths ##

The protocol buffer language has a concept of "packages" which does not
correspond well to the Go notion of packages. In generated Go code,
each source `.proto` file is associated with a single Go package. The
name and import path for this package is specified with the `go_package`
proto option:

option go_package = "github.com/golang/protobuf/ptypes/any";

The protocol buffer compiler will attempt to derive a package name and
import path if a `go_package` option is not present, but it is
best to always specify one explicitly.

There is a one-to-one relationship between source `.proto` files and
generated `.pb.go` files, but any number of `.pb.go` files may be
contained in the same Go package.

The output name of a generated file is produced by replacing the
`.proto` suffix with `.pb.go` (e.g., `foo.proto` produces `foo.pb.go`).
However, the output directory is selected in one of two ways. Let
us say we have `inputs/x.proto` with a `go_package` option of
`github.com/golang/protobuf/p`. The corresponding output file may
be:

- Relative to the import path:

protoc --go_out=. inputs/x.proto
# writes ./github.com/golang/protobuf/p/x.pb.go

(This can work well with `--go_out=$GOPATH`.)

- Relative to the input file:

protoc --go_out=paths=source_relative:. inputs/x.proto
# generate ./inputs/x.pb.go

## Generated code ##

The package comment for the proto library contains text describing
the interface provided in Go for protocol buffers. Here is an edited
version.

==========

The proto package converts data structures to and from the
wire format of protocol buffers. It works in concert with the
Go source code generated for .proto files by the protocol compiler.
Expand Down Expand Up @@ -170,22 +206,25 @@ To create and play with a Test object from the example package,
To pass extra parameters to the plugin, use a comma-separated
parameter list separated from the output directory by a colon:


protoc --go_out=plugins=grpc,import_path=mypackage:. *.proto


- `import_prefix=xxx` - a prefix that is added onto the beginning of
all imports. Useful for things like generating protos in a
subdirectory, or regenerating vendored protobufs in-place.
- `import_path=foo/bar` - used as the package if no input files
declare `go_package`. If it contains slashes, everything up to the
rightmost slash is ignored.
- `paths=(import | source_relative)` - specifies how the paths of
generated files are structured. See the "Packages and imports paths"
section above. The default is `import`.
- `plugins=plugin1+plugin2` - specifies the list of sub-plugins to
load. The only plugin in this repo is `grpc`.
- `Mfoo/bar.proto=quux/shme` - declares that foo/bar.proto is
associated with Go package quux/shme. This is subject to the
import_prefix parameter.

The following parameters are deprecated and should not be used:

- `import_prefix=xxx` - a prefix that is added onto the beginning of
all imports.
- `import_path=foo/bar` - used as the package if no input files
declare `go_package`. If it contains slashes, everything up to the
rightmost slash is ignored.

## gRPC Support ##

If a proto file specifies RPC services, protoc-gen-go can be instructed to
Expand Down
27 changes: 24 additions & 3 deletions protoc-gen-go/generator/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -324,13 +324,17 @@ func (d *FileDescriptor) goPackageName() (name GoPackageName, explicit bool) {
}

// goFileName returns the output name for the generated Go file.
func (d *FileDescriptor) goFileName() string {
func (d *FileDescriptor) goFileName(pathType pathType) string {
name := *d.Name
if ext := path.Ext(name); ext == ".proto" || ext == ".protodevel" {
name = name[:len(name)-len(ext)]
}
name += ".pb.go"

if pathType == pathTypeSourceRelative {
return name
}

// Does the file have a "go_package" option?
// If it does, it may override the filename.
if impPath, _, ok := d.goPackageOption(); ok && impPath != "" {
Expand Down Expand Up @@ -574,11 +578,19 @@ type Generator struct {
typeNameToObject map[string]Object // Key is a fully-qualified name in input syntax.
init []string // Lines to emit in the init function.
indent string
pathType pathType // How to generate output filenames.
writeOutput bool
annotateCode bool // whether to store annotations
annotations []*descriptor.GeneratedCodeInfo_Annotation // annotations to store
}

type pathType int

const (
pathTypeImport pathType = iota
pathTypeSourceRelative
)

// New creates a new generator and allocates the request and response protobufs.
func New() *Generator {
g := new(Generator)
Expand Down Expand Up @@ -623,6 +635,15 @@ func (g *Generator) CommandLineParameters(parameter string) {
g.ImportPrefix = GoImportPath(v)
case "import_path":
g.PackageImportPath = v
case "paths":
switch v {
case "import":
g.pathType = pathTypeImport
case "source_relative":
g.pathType = pathTypeSourceRelative
default:
g.Fail(fmt.Sprintf(`Unknown path type %q: want "import" or "source_relative".`, v))
}
case "plugins":
pluginList = v
case "annotate_code":
Expand Down Expand Up @@ -1206,7 +1227,7 @@ func (g *Generator) GenerateAllFiles() {
if !g.writeOutput {
continue
}
fname := file.goFileName()
fname := file.goFileName(g.pathType)
g.Response.File = append(g.Response.File, &plugin.CodeGeneratorResponse_File{
Name: proto.String(fname),
Content: proto.String(g.String()),
Expand All @@ -1215,7 +1236,7 @@ func (g *Generator) GenerateAllFiles() {
// Store the generated code annotations in text, as the protoc plugin protocol requires that
// strings contain valid UTF-8.
g.Response.File = append(g.Response.File, &plugin.CodeGeneratorResponse_File{
Name: proto.String(file.goFileName() + ".meta"),
Name: proto.String(file.goFileName(g.pathType) + ".meta"),
Content: proto.String(proto.CompactTextString(&descriptor.GeneratedCodeInfo{Annotation: g.annotations})),
})
}
Expand Down
22 changes: 17 additions & 5 deletions protoc-gen-go/golden_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,27 +51,26 @@ func TestGolden(t *testing.T) {

// Compile each package, using this binary as protoc-gen-go.
for _, sources := range packages {
args := []string{"-Itestdata", "--go_out=plugins=grpc:" + workdir}
args := []string{"-Itestdata", "--go_out=plugins=grpc,paths=source_relative:" + workdir}
args = append(args, sources...)
protoc(t, args)
}

// Compare each generated file to the golden version.
relRoot := filepath.Join(workdir, "github.com/golang/protobuf/protoc-gen-go/testdata")
filepath.Walk(workdir, func(genPath string, info os.FileInfo, _ error) error {
if info.IsDir() {
return nil
}

// For each generated file, figure out the path to the corresponding
// golden file in the testdata directory.
relPath, err := filepath.Rel(relRoot, genPath)
relPath, err := filepath.Rel(workdir, genPath)
if err != nil {
t.Errorf("filepath.Rel(%q, %q): %v", relRoot, genPath, err)
t.Errorf("filepath.Rel(%q, %q): %v", workdir, genPath, err)
return nil
}
if filepath.SplitList(relPath)[0] == ".." {
t.Errorf("generated file %q is not relative to %q", genPath, relRoot)
t.Errorf("generated file %q is not relative to %q", genPath, workdir)
}
goldenPath := filepath.Join("testdata", relPath)

Expand Down Expand Up @@ -180,6 +179,19 @@ func TestParameters(t *testing.T) {
// import_prefix applies after M.
"prefixpackage/gamma": true,
},
}, {
parameters: "paths=source_relative",
wantFiles: map[string]bool{
"alpha/a.pb.go": true,
"beta/b.pb.go": true,
},
}, {
parameters: "paths=source_relative,import_prefix=prefix",
wantFiles: map[string]bool{
// import_prefix doesn't affect filenames.
"alpha/a.pb.go": true,
"beta/b.pb.go": true,
},
}} {
name := test.parameters
if name == "" {
Expand Down