go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/common/proto/protoc/protoc.go (about) 1 // Copyright 2021 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package protoc contains helpers for running `protoc` using protos files 16 // stored in the Go source tree. 17 package protoc 18 19 import ( 20 "context" 21 "fmt" 22 "os" 23 "os/exec" 24 "path" 25 "sort" 26 "strings" 27 28 "go.chromium.org/luci/common/logging" 29 ) 30 31 // CompileParams are passed to Compile. 32 type CompileParams struct { 33 Inputs *StagedInputs // a staging directory with proto files 34 OutputDescriptorSet string // a path to write the descriptor set to 35 GoEnabled bool // true to use protoc-gen-go plugin 36 GoPackageMap map[string]string // maps a proto path to a go package name 37 GoDeprecatedGRPCPlugin bool // true to use deprecated grpc protoc-gen-go plugin 38 GoGRPCEnabled bool // true to use protoc-gen-go-grpc 39 GoPGVEnabled bool // enable protoc-gen-validate support 40 } 41 42 // Compile runs protoc over staged inputs. 43 func Compile(ctx context.Context, p *CompileParams) error { 44 // Common protoc arguments. 45 args := []string{ 46 "--descriptor_set_out=" + p.OutputDescriptorSet, 47 "--include_imports", 48 "--include_source_info", 49 } 50 for _, path := range p.Inputs.Paths { 51 args = append(args, "--proto_path="+path) 52 } 53 54 if p.GoEnabled { 55 // protoc-gen-go plugin arguments. 56 var params []string 57 for k, v := range p.GoPackageMap { 58 params = append(params, fmt.Sprintf("M%s=%s", k, v)) 59 } 60 sort.Strings(params) 61 if p.GoDeprecatedGRPCPlugin { 62 params = append(params, "plugins=grpc") 63 } 64 args = append(args, fmt.Sprintf("--go_out=%s:%s", strings.Join(params, ","), p.Inputs.OutputDir)) 65 66 // protoc-gen-go-grpc plugin arguments. 67 if p.GoGRPCEnabled { 68 args = append(args, fmt.Sprintf("--go-grpc_out=%s", p.Inputs.OutputDir)) 69 } 70 71 if p.GoPGVEnabled { 72 args = append(args, fmt.Sprintf("--validate_out=lang=go:%s", p.Inputs.OutputDir)) 73 } 74 } 75 76 // protoc searches import paths purely lexicographically. Since we pass 77 // import paths as absolute, we must use absolute paths for all input protos 78 // as well, otherwise protoc gets confused. 79 for _, f := range p.Inputs.ProtoFiles { 80 args = append(args, path.Join(p.Inputs.InputDir, f)) 81 } 82 83 logging.Debugf(ctx, "protoc %s", strings.Join(args, " ")) 84 protoc := exec.Command("protoc", args...) 85 protoc.Stdout = os.Stdout 86 protoc.Stderr = os.Stderr 87 return protoc.Run() 88 }