mirror of https://github.com/docker/buildx.git
Enable to run build and invoke in background
Signed-off-by: Kohei Tokunaga <ktokunaga.mail@gmail.com>
This commit is contained in:
parent
b1b4e64c97
commit
a27b8395b1
|
@ -27,6 +27,7 @@ jobs:
|
||||||
- lint
|
- lint
|
||||||
- validate-vendor
|
- validate-vendor
|
||||||
- validate-docs
|
- validate-docs
|
||||||
|
- validate-generated-files
|
||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
name: Checkout
|
name: Checkout
|
||||||
|
|
10
Makefile
10
Makefile
|
@ -37,7 +37,7 @@ release:
|
||||||
./hack/release
|
./hack/release
|
||||||
|
|
||||||
.PHONY: validate-all
|
.PHONY: validate-all
|
||||||
validate-all: lint test validate-vendor validate-docs
|
validate-all: lint test validate-vendor validate-docs validate-generated-files
|
||||||
|
|
||||||
.PHONY: lint
|
.PHONY: lint
|
||||||
lint:
|
lint:
|
||||||
|
@ -59,6 +59,10 @@ validate-docs:
|
||||||
validate-authors:
|
validate-authors:
|
||||||
$(BUILDX_CMD) bake validate-authors
|
$(BUILDX_CMD) bake validate-authors
|
||||||
|
|
||||||
|
.PHONY: validate-generated-files
|
||||||
|
validate-generated-files:
|
||||||
|
$(BUILDX_CMD) bake validate-generated-files
|
||||||
|
|
||||||
.PHONY: test-driver
|
.PHONY: test-driver
|
||||||
test-driver:
|
test-driver:
|
||||||
./hack/test-driver
|
./hack/test-driver
|
||||||
|
@ -78,3 +82,7 @@ authors:
|
||||||
.PHONY: mod-outdated
|
.PHONY: mod-outdated
|
||||||
mod-outdated:
|
mod-outdated:
|
||||||
$(BUILDX_CMD) bake mod-outdated
|
$(BUILDX_CMD) bake mod-outdated
|
||||||
|
|
||||||
|
.PHONY: generated-files
|
||||||
|
generated-files:
|
||||||
|
$(BUILDX_CMD) bake update-generated-files
|
||||||
|
|
|
@ -804,10 +804,10 @@ func Invoke(ctx context.Context, cfg ContainerConfig) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func Build(ctx context.Context, nodes []builder.Node, opt map[string]Options, docker *dockerutil.Client, configDir string, w progress.Writer) (resp map[string]*client.SolveResponse, err error) {
|
func Build(ctx context.Context, nodes []builder.Node, opt map[string]Options, docker *dockerutil.Client, configDir string, w progress.Writer) (resp map[string]*client.SolveResponse, err error) {
|
||||||
return BuildWithResultHandler(ctx, nodes, opt, docker, configDir, w, nil, false)
|
return BuildWithResultHandler(ctx, nodes, opt, docker, configDir, w, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opt map[string]Options, docker *dockerutil.Client, configDir string, w progress.Writer, resultHandleFunc func(driverIndex int, rCtx *ResultContext), allowNoOutput bool) (resp map[string]*client.SolveResponse, err error) {
|
func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opt map[string]Options, docker *dockerutil.Client, configDir string, w progress.Writer, resultHandleFunc func(driverIndex int, rCtx *ResultContext)) (resp map[string]*client.SolveResponse, err error) {
|
||||||
if len(nodes) == 0 {
|
if len(nodes) == 0 {
|
||||||
return nil, errors.Errorf("driver required for build")
|
return nil, errors.Errorf("driver required for build")
|
||||||
}
|
}
|
||||||
|
@ -832,7 +832,7 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opt map[s
|
||||||
noOutputTargets = append(noOutputTargets, name)
|
noOutputTargets = append(noOutputTargets, name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(noOutputTargets) > 0 && !allowNoOutput {
|
if len(noOutputTargets) > 0 {
|
||||||
var warnNoOutputBuf bytes.Buffer
|
var warnNoOutputBuf bytes.Buffer
|
||||||
warnNoOutputBuf.WriteString("No output specified ")
|
warnNoOutputBuf.WriteString("No output specified ")
|
||||||
if len(noOutputTargets) == 1 && noOutputTargets[0] == "default" {
|
if len(noOutputTargets) == 1 && noOutputTargets[0] == "default" {
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"github.com/docker/buildx/bake"
|
"github.com/docker/buildx/bake"
|
||||||
"github.com/docker/buildx/build"
|
"github.com/docker/buildx/build"
|
||||||
"github.com/docker/buildx/builder"
|
"github.com/docker/buildx/builder"
|
||||||
|
controllerapi "github.com/docker/buildx/commands/controller/pb"
|
||||||
"github.com/docker/buildx/util/buildflags"
|
"github.com/docker/buildx/util/buildflags"
|
||||||
"github.com/docker/buildx/util/confutil"
|
"github.com/docker/buildx/util/confutil"
|
||||||
"github.com/docker/buildx/util/dockerutil"
|
"github.com/docker/buildx/util/dockerutil"
|
||||||
|
@ -25,10 +26,10 @@ type bakeOptions struct {
|
||||||
files []string
|
files []string
|
||||||
overrides []string
|
overrides []string
|
||||||
printOnly bool
|
printOnly bool
|
||||||
commonOptions
|
controllerapi.CommonOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
func runBake(dockerCli command.Cli, targets []string, in bakeOptions) (err error) {
|
func runBake(dockerCli command.Cli, targets []string, in bakeOptions, cFlags commonFlags) (err error) {
|
||||||
ctx := appcontext.Context()
|
ctx := appcontext.Context()
|
||||||
|
|
||||||
ctx, end, err := tracing.TraceCurrentCommand(ctx, "bake")
|
ctx, end, err := tracing.TraceCurrentCommand(ctx, "bake")
|
||||||
|
@ -60,31 +61,31 @@ func runBake(dockerCli command.Cli, targets []string, in bakeOptions) (err error
|
||||||
}
|
}
|
||||||
|
|
||||||
overrides := in.overrides
|
overrides := in.overrides
|
||||||
if in.exportPush {
|
if in.ExportPush {
|
||||||
if in.exportLoad {
|
if in.ExportLoad {
|
||||||
return errors.Errorf("push and load may not be set together at the moment")
|
return errors.Errorf("push and load may not be set together at the moment")
|
||||||
}
|
}
|
||||||
overrides = append(overrides, "*.push=true")
|
overrides = append(overrides, "*.push=true")
|
||||||
} else if in.exportLoad {
|
} else if in.ExportLoad {
|
||||||
overrides = append(overrides, "*.output=type=docker")
|
overrides = append(overrides, "*.output=type=docker")
|
||||||
}
|
}
|
||||||
if in.noCache != nil {
|
if cFlags.noCache != nil {
|
||||||
overrides = append(overrides, fmt.Sprintf("*.no-cache=%t", *in.noCache))
|
overrides = append(overrides, fmt.Sprintf("*.no-cache=%t", *cFlags.noCache))
|
||||||
}
|
}
|
||||||
if in.pull != nil {
|
if cFlags.pull != nil {
|
||||||
overrides = append(overrides, fmt.Sprintf("*.pull=%t", *in.pull))
|
overrides = append(overrides, fmt.Sprintf("*.pull=%t", *cFlags.pull))
|
||||||
}
|
}
|
||||||
if in.sbom != "" {
|
if in.SBOM != "" {
|
||||||
overrides = append(overrides, fmt.Sprintf("*.attest=%s", buildflags.CanonicalizeAttest("sbom", in.sbom)))
|
overrides = append(overrides, fmt.Sprintf("*.attest=%s", buildflags.CanonicalizeAttest("sbom", in.SBOM)))
|
||||||
}
|
}
|
||||||
if in.provenance != "" {
|
if in.Provenance != "" {
|
||||||
overrides = append(overrides, fmt.Sprintf("*.attest=%s", buildflags.CanonicalizeAttest("provenance", in.provenance)))
|
overrides = append(overrides, fmt.Sprintf("*.attest=%s", buildflags.CanonicalizeAttest("provenance", in.Provenance)))
|
||||||
}
|
}
|
||||||
contextPathHash, _ := os.Getwd()
|
contextPathHash, _ := os.Getwd()
|
||||||
|
|
||||||
ctx2, cancel := context.WithCancel(context.TODO())
|
ctx2, cancel := context.WithCancel(context.TODO())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
printer, err := progress.NewPrinter(ctx2, os.Stderr, os.Stderr, in.progress)
|
printer, err := progress.NewPrinter(ctx2, os.Stderr, os.Stderr, cFlags.progress)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -105,7 +106,7 @@ func runBake(dockerCli command.Cli, targets []string, in bakeOptions) (err error
|
||||||
// instance only needed for reading remote bake files or building
|
// instance only needed for reading remote bake files or building
|
||||||
if url != "" || !in.printOnly {
|
if url != "" || !in.printOnly {
|
||||||
b, err := builder.New(dockerCli,
|
b, err := builder.New(dockerCli,
|
||||||
builder.WithName(in.builder),
|
builder.WithName(in.Builder),
|
||||||
builder.WithContextPathHash(contextPathHash),
|
builder.WithContextPathHash(contextPathHash),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -170,12 +171,12 @@ func runBake(dockerCli command.Cli, targets []string, in bakeOptions) (err error
|
||||||
return wrapBuildError(err, true)
|
return wrapBuildError(err, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(in.metadataFile) > 0 {
|
if len(in.MetadataFile) > 0 {
|
||||||
dt := make(map[string]interface{})
|
dt := make(map[string]interface{})
|
||||||
for t, r := range resp {
|
for t, r := range resp {
|
||||||
dt[t] = decodeExporterResponse(r.ExporterResponse)
|
dt[t] = decodeExporterResponse(r.ExporterResponse)
|
||||||
}
|
}
|
||||||
if err := writeMetadataFile(in.metadataFile, dt); err != nil {
|
if err := writeMetadataFile(in.MetadataFile, dt); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -185,6 +186,7 @@ func runBake(dockerCli command.Cli, targets []string, in bakeOptions) (err error
|
||||||
|
|
||||||
func bakeCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
|
func bakeCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
|
||||||
var options bakeOptions
|
var options bakeOptions
|
||||||
|
var cFlags commonFlags
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "bake [OPTIONS] [TARGET...]",
|
Use: "bake [OPTIONS] [TARGET...]",
|
||||||
|
@ -193,27 +195,29 @@ func bakeCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
// reset to nil to avoid override is unset
|
// reset to nil to avoid override is unset
|
||||||
if !cmd.Flags().Lookup("no-cache").Changed {
|
if !cmd.Flags().Lookup("no-cache").Changed {
|
||||||
options.noCache = nil
|
cFlags.noCache = nil
|
||||||
}
|
}
|
||||||
if !cmd.Flags().Lookup("pull").Changed {
|
if !cmd.Flags().Lookup("pull").Changed {
|
||||||
options.pull = nil
|
cFlags.pull = nil
|
||||||
}
|
}
|
||||||
options.commonOptions.builder = rootOpts.builder
|
options.Builder = rootOpts.builder
|
||||||
return runBake(dockerCli, args, options)
|
options.MetadataFile = cFlags.metadataFile
|
||||||
|
// Other common flags (noCache, pull and progress) are processed in runBake function.
|
||||||
|
return runBake(dockerCli, args, options, cFlags)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
flags := cmd.Flags()
|
flags := cmd.Flags()
|
||||||
|
|
||||||
flags.StringArrayVarP(&options.files, "file", "f", []string{}, "Build definition file")
|
flags.StringArrayVarP(&options.files, "file", "f", []string{}, "Build definition file")
|
||||||
flags.BoolVar(&options.exportLoad, "load", false, `Shorthand for "--set=*.output=type=docker"`)
|
flags.BoolVar(&options.ExportLoad, "load", false, `Shorthand for "--set=*.output=type=docker"`)
|
||||||
flags.BoolVar(&options.printOnly, "print", false, "Print the options without building")
|
flags.BoolVar(&options.printOnly, "print", false, "Print the options without building")
|
||||||
flags.BoolVar(&options.exportPush, "push", false, `Shorthand for "--set=*.output=type=registry"`)
|
flags.BoolVar(&options.ExportPush, "push", false, `Shorthand for "--set=*.output=type=registry"`)
|
||||||
flags.StringVar(&options.sbom, "sbom", "", `Shorthand for "--set=*.attest=type=sbom"`)
|
flags.StringVar(&options.SBOM, "sbom", "", `Shorthand for "--set=*.attest=type=sbom"`)
|
||||||
flags.StringVar(&options.provenance, "provenance", "", `Shorthand for "--set=*.attest=type=provenance"`)
|
flags.StringVar(&options.Provenance, "provenance", "", `Shorthand for "--set=*.attest=type=provenance"`)
|
||||||
flags.StringArrayVar(&options.overrides, "set", nil, `Override target value (e.g., "targetpattern.key=value")`)
|
flags.StringArrayVar(&options.overrides, "set", nil, `Override target value (e.g., "targetpattern.key=value")`)
|
||||||
|
|
||||||
commonBuildFlags(&options.commonOptions, flags)
|
commonBuildFlags(&cFlags, flags)
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
@ -17,12 +18,14 @@ import (
|
||||||
"github.com/containerd/console"
|
"github.com/containerd/console"
|
||||||
"github.com/docker/buildx/build"
|
"github.com/docker/buildx/build"
|
||||||
"github.com/docker/buildx/builder"
|
"github.com/docker/buildx/builder"
|
||||||
|
controllerapi "github.com/docker/buildx/commands/controller/pb"
|
||||||
"github.com/docker/buildx/monitor"
|
"github.com/docker/buildx/monitor"
|
||||||
"github.com/docker/buildx/store"
|
"github.com/docker/buildx/store"
|
||||||
"github.com/docker/buildx/store/storeutil"
|
"github.com/docker/buildx/store/storeutil"
|
||||||
"github.com/docker/buildx/util/buildflags"
|
"github.com/docker/buildx/util/buildflags"
|
||||||
"github.com/docker/buildx/util/confutil"
|
"github.com/docker/buildx/util/confutil"
|
||||||
"github.com/docker/buildx/util/dockerutil"
|
"github.com/docker/buildx/util/dockerutil"
|
||||||
|
"github.com/docker/buildx/util/ioset"
|
||||||
"github.com/docker/buildx/util/platformutil"
|
"github.com/docker/buildx/util/platformutil"
|
||||||
"github.com/docker/buildx/util/progress"
|
"github.com/docker/buildx/util/progress"
|
||||||
"github.com/docker/buildx/util/tracing"
|
"github.com/docker/buildx/util/tracing"
|
||||||
|
@ -50,50 +53,15 @@ import (
|
||||||
const defaultTargetName = "default"
|
const defaultTargetName = "default"
|
||||||
|
|
||||||
type buildOptions struct {
|
type buildOptions struct {
|
||||||
contextPath string
|
|
||||||
dockerfileName string
|
|
||||||
printFunc string
|
|
||||||
|
|
||||||
allow []string
|
|
||||||
attests []string
|
|
||||||
buildArgs []string
|
|
||||||
cacheFrom []string
|
|
||||||
cacheTo []string
|
|
||||||
cgroupParent string
|
|
||||||
contexts []string
|
|
||||||
extraHosts []string
|
|
||||||
imageIDFile string
|
|
||||||
invoke string
|
|
||||||
labels []string
|
|
||||||
networkMode string
|
|
||||||
noCacheFilter []string
|
|
||||||
outputs []string
|
|
||||||
platforms []string
|
|
||||||
quiet bool
|
|
||||||
secrets []string
|
|
||||||
shmSize dockeropts.MemBytes
|
|
||||||
ssh []string
|
|
||||||
tags []string
|
|
||||||
target string
|
|
||||||
ulimits *dockeropts.UlimitOpt
|
|
||||||
commonOptions
|
|
||||||
}
|
|
||||||
|
|
||||||
type commonOptions struct {
|
|
||||||
builder string
|
|
||||||
metadataFile string
|
|
||||||
noCache *bool
|
|
||||||
progress string
|
progress string
|
||||||
pull *bool
|
invoke string
|
||||||
|
serverConfig string
|
||||||
exportPush bool
|
root string
|
||||||
exportLoad bool
|
detach bool
|
||||||
|
controllerapi.BuildOptions
|
||||||
sbom string
|
|
||||||
provenance string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func runBuild(dockerCli command.Cli, in buildOptions) (err error) {
|
func runBuild(dockerCli command.Cli, in buildOptions) error {
|
||||||
ctx := appcontext.Context()
|
ctx := appcontext.Context()
|
||||||
|
|
||||||
ctx, end, err := tracing.TraceCurrentCommand(ctx, "build")
|
ctx, end, err := tracing.TraceCurrentCommand(ctx, "build")
|
||||||
|
@ -104,89 +72,85 @@ func runBuild(dockerCli command.Cli, in buildOptions) (err error) {
|
||||||
end(err)
|
end(err)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
noCache := false
|
_, err = runBuildWithContext(ctx, dockerCli, in.BuildOptions, os.Stdin, in.progress, nil)
|
||||||
if in.noCache != nil {
|
return err
|
||||||
noCache = *in.noCache
|
}
|
||||||
}
|
|
||||||
pull := false
|
func runBuildWithContext(ctx context.Context, dockerCli command.Cli, in controllerapi.BuildOptions, inStream io.Reader, progressMode string, statusChan chan *client.SolveStatus) (res *build.ResultContext, err error) {
|
||||||
if in.pull != nil {
|
if in.Opts.NoCache && len(in.NoCacheFilter) > 0 {
|
||||||
pull = *in.pull
|
return nil, errors.Errorf("--no-cache and --no-cache-filter cannot currently be used together")
|
||||||
}
|
}
|
||||||
|
|
||||||
if noCache && len(in.noCacheFilter) > 0 {
|
if in.Quiet && progressMode != progress.PrinterModeAuto && progressMode != progress.PrinterModeQuiet {
|
||||||
return errors.Errorf("--no-cache and --no-cache-filter cannot currently be used together")
|
return nil, errors.Errorf("progress=%s and quiet cannot be used together", progressMode)
|
||||||
|
} else if in.Quiet {
|
||||||
|
progressMode = "quiet"
|
||||||
}
|
}
|
||||||
|
|
||||||
if in.quiet && in.progress != progress.PrinterModeAuto && in.progress != progress.PrinterModeQuiet {
|
contexts, err := parseContextNames(in.Contexts)
|
||||||
return errors.Errorf("progress=%s and quiet cannot be used together", in.progress)
|
|
||||||
} else if in.quiet {
|
|
||||||
in.progress = "quiet"
|
|
||||||
}
|
|
||||||
|
|
||||||
contexts, err := parseContextNames(in.contexts)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
printFunc, err := parsePrintFunc(in.printFunc)
|
printFunc, err := parsePrintFunc(in.PrintFunc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
opts := build.Options{
|
opts := build.Options{
|
||||||
Inputs: build.Inputs{
|
Inputs: build.Inputs{
|
||||||
ContextPath: in.contextPath,
|
ContextPath: in.ContextPath,
|
||||||
DockerfilePath: in.dockerfileName,
|
DockerfilePath: in.DockerfileName,
|
||||||
InStream: os.Stdin,
|
InStream: inStream,
|
||||||
NamedContexts: contexts,
|
NamedContexts: contexts,
|
||||||
},
|
},
|
||||||
BuildArgs: listToMap(in.buildArgs, true),
|
BuildArgs: listToMap(in.BuildArgs, true),
|
||||||
ExtraHosts: in.extraHosts,
|
ExtraHosts: in.ExtraHosts,
|
||||||
ImageIDFile: in.imageIDFile,
|
ImageIDFile: in.ImageIDFile,
|
||||||
Labels: listToMap(in.labels, false),
|
Labels: listToMap(in.Labels, false),
|
||||||
NetworkMode: in.networkMode,
|
NetworkMode: in.NetworkMode,
|
||||||
NoCache: noCache,
|
NoCache: in.Opts.NoCache,
|
||||||
NoCacheFilter: in.noCacheFilter,
|
NoCacheFilter: in.NoCacheFilter,
|
||||||
Pull: pull,
|
Pull: in.Opts.Pull,
|
||||||
ShmSize: in.shmSize,
|
ShmSize: dockeropts.MemBytes(in.ShmSize),
|
||||||
Tags: in.tags,
|
Tags: in.Tags,
|
||||||
Target: in.target,
|
Target: in.Target,
|
||||||
Ulimits: in.ulimits,
|
Ulimits: controllerUlimitOpt2DockerUlimit(in.Ulimits),
|
||||||
PrintFunc: printFunc,
|
PrintFunc: printFunc,
|
||||||
}
|
}
|
||||||
|
|
||||||
platforms, err := platformutil.Parse(in.platforms)
|
platforms, err := platformutil.Parse(in.Platforms)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
opts.Platforms = platforms
|
opts.Platforms = platforms
|
||||||
|
|
||||||
dockerConfig := config.LoadDefaultConfigFile(os.Stderr)
|
dockerConfig := config.LoadDefaultConfigFile(os.Stderr)
|
||||||
opts.Session = append(opts.Session, authprovider.NewDockerAuthProvider(dockerConfig))
|
opts.Session = append(opts.Session, authprovider.NewDockerAuthProvider(dockerConfig))
|
||||||
|
|
||||||
secrets, err := buildflags.ParseSecretSpecs(in.secrets)
|
secrets, err := buildflags.ParseSecretSpecs(in.Secrets)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
opts.Session = append(opts.Session, secrets)
|
opts.Session = append(opts.Session, secrets)
|
||||||
|
|
||||||
sshSpecs := in.ssh
|
sshSpecs := in.SSH
|
||||||
if len(sshSpecs) == 0 && buildflags.IsGitSSH(in.contextPath) {
|
if len(sshSpecs) == 0 && buildflags.IsGitSSH(in.ContextPath) {
|
||||||
sshSpecs = []string{"default"}
|
sshSpecs = []string{"default"}
|
||||||
}
|
}
|
||||||
ssh, err := buildflags.ParseSSHSpecs(sshSpecs)
|
ssh, err := buildflags.ParseSSHSpecs(sshSpecs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
opts.Session = append(opts.Session, ssh)
|
opts.Session = append(opts.Session, ssh)
|
||||||
|
|
||||||
outputs, err := buildflags.ParseOutputs(in.outputs)
|
outputs, err := buildflags.ParseOutputs(in.Outputs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
if in.exportPush {
|
if in.Opts.ExportPush {
|
||||||
if in.exportLoad {
|
if in.Opts.ExportLoad {
|
||||||
return errors.Errorf("push and load may not be set together at the moment")
|
return nil, errors.Errorf("push and load may not be set together at the moment")
|
||||||
}
|
}
|
||||||
if len(outputs) == 0 {
|
if len(outputs) == 0 {
|
||||||
outputs = []client.ExportEntry{{
|
outputs = []client.ExportEntry{{
|
||||||
|
@ -200,11 +164,11 @@ func runBuild(dockerCli command.Cli, in buildOptions) (err error) {
|
||||||
case "image":
|
case "image":
|
||||||
outputs[0].Attrs["push"] = "true"
|
outputs[0].Attrs["push"] = "true"
|
||||||
default:
|
default:
|
||||||
return errors.Errorf("push and %q output can't be used together", outputs[0].Type)
|
return nil, errors.Errorf("push and %q output can't be used together", outputs[0].Type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if in.exportLoad {
|
if in.Opts.ExportLoad {
|
||||||
if len(outputs) == 0 {
|
if len(outputs) == 0 {
|
||||||
outputs = []client.ExportEntry{{
|
outputs = []client.ExportEntry{{
|
||||||
Type: "docker",
|
Type: "docker",
|
||||||
|
@ -214,102 +178,76 @@ func runBuild(dockerCli command.Cli, in buildOptions) (err error) {
|
||||||
switch outputs[0].Type {
|
switch outputs[0].Type {
|
||||||
case "docker":
|
case "docker":
|
||||||
default:
|
default:
|
||||||
return errors.Errorf("load and %q output can't be used together", outputs[0].Type)
|
return nil, errors.Errorf("load and %q output can't be used together", outputs[0].Type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
opts.Exports = outputs
|
opts.Exports = outputs
|
||||||
|
|
||||||
inAttests := append([]string{}, in.attests...)
|
inAttests := append([]string{}, in.Attests...)
|
||||||
if in.provenance != "" {
|
if in.Opts.Provenance != "" {
|
||||||
inAttests = append(inAttests, buildflags.CanonicalizeAttest("provenance", in.provenance))
|
inAttests = append(inAttests, buildflags.CanonicalizeAttest("provenance", in.Opts.Provenance))
|
||||||
}
|
}
|
||||||
if in.sbom != "" {
|
if in.Opts.SBOM != "" {
|
||||||
inAttests = append(inAttests, buildflags.CanonicalizeAttest("sbom", in.sbom))
|
inAttests = append(inAttests, buildflags.CanonicalizeAttest("sbom", in.Opts.SBOM))
|
||||||
}
|
}
|
||||||
opts.Attests, err = buildflags.ParseAttests(inAttests)
|
opts.Attests, err = buildflags.ParseAttests(inAttests)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
cacheImports, err := buildflags.ParseCacheEntry(in.cacheFrom)
|
cacheImports, err := buildflags.ParseCacheEntry(in.CacheFrom)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
opts.CacheFrom = cacheImports
|
opts.CacheFrom = cacheImports
|
||||||
|
|
||||||
cacheExports, err := buildflags.ParseCacheEntry(in.cacheTo)
|
cacheExports, err := buildflags.ParseCacheEntry(in.CacheTo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
opts.CacheTo = cacheExports
|
opts.CacheTo = cacheExports
|
||||||
|
|
||||||
allow, err := buildflags.ParseEntitlements(in.allow)
|
allow, err := buildflags.ParseEntitlements(in.Allow)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
opts.Allow = allow
|
opts.Allow = allow
|
||||||
|
|
||||||
// key string used for kubernetes "sticky" mode
|
// key string used for kubernetes "sticky" mode
|
||||||
contextPathHash, err := filepath.Abs(in.contextPath)
|
contextPathHash, err := filepath.Abs(in.ContextPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
contextPathHash = in.contextPath
|
contextPathHash = in.ContextPath
|
||||||
}
|
}
|
||||||
|
|
||||||
b, err := builder.New(dockerCli,
|
b, err := builder.New(dockerCli,
|
||||||
builder.WithName(in.builder),
|
builder.WithName(in.Opts.Builder),
|
||||||
builder.WithContextPathHash(contextPathHash),
|
builder.WithContextPathHash(contextPathHash),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err = updateLastActivity(dockerCli, b.NodeGroup); err != nil {
|
if err = updateLastActivity(dockerCli, b.NodeGroup); err != nil {
|
||||||
return errors.Wrapf(err, "failed to update builder last activity time")
|
return nil, errors.Wrapf(err, "failed to update builder last activity time")
|
||||||
}
|
}
|
||||||
nodes, err := b.LoadNodes(ctx, false)
|
nodes, err := b.LoadNodes(ctx, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
imageID, res, err := buildTargets(ctx, dockerCli, nodes, map[string]build.Options{defaultTargetName: opts}, in.progress, in.metadataFile, in.invoke != "")
|
imageID, res, err := buildTargets(ctx, dockerCli, nodes, map[string]build.Options{defaultTargetName: opts}, progressMode, in.Opts.MetadataFile, statusChan)
|
||||||
err = wrapBuildError(err, false)
|
err = wrapBuildError(err, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if in.invoke != "" {
|
if in.Quiet {
|
||||||
cfg, err := parseInvokeConfig(in.invoke)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
cfg.ResultCtx = res
|
|
||||||
con := console.Current()
|
|
||||||
if err := con.SetRaw(); err != nil {
|
|
||||||
return errors.Errorf("failed to configure terminal: %v", err)
|
|
||||||
}
|
|
||||||
err = monitor.RunMonitor(ctx, cfg, func(ctx context.Context) (*build.ResultContext, error) {
|
|
||||||
_, rr, err := buildTargets(ctx, dockerCli, nodes, map[string]build.Options{defaultTargetName: opts}, in.progress, in.metadataFile, true)
|
|
||||||
return rr, err
|
|
||||||
}, io.NopCloser(os.Stdin), nopCloser{os.Stdout}, nopCloser{os.Stderr})
|
|
||||||
if err != nil {
|
|
||||||
logrus.Warnf("failed to run monitor: %v", err)
|
|
||||||
}
|
|
||||||
con.Reset()
|
|
||||||
}
|
|
||||||
|
|
||||||
if in.quiet {
|
|
||||||
fmt.Println(imageID)
|
fmt.Println(imageID)
|
||||||
}
|
}
|
||||||
return nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type nopCloser struct {
|
func buildTargets(ctx context.Context, dockerCli command.Cli, nodes []builder.Node, opts map[string]build.Options, progressMode string, metadataFile string, statusChan chan *client.SolveStatus) (imageID string, res *build.ResultContext, err error) {
|
||||||
io.WriteCloser
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c nopCloser) Close() error { return nil }
|
|
||||||
|
|
||||||
func buildTargets(ctx context.Context, dockerCli command.Cli, nodes []builder.Node, opts map[string]build.Options, progressMode string, metadataFile string, allowNoOutput bool) (imageID string, res *build.ResultContext, err error) {
|
|
||||||
ctx2, cancel := context.WithCancel(context.TODO())
|
ctx2, cancel := context.WithCancel(context.TODO())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
|
@ -320,13 +258,13 @@ func buildTargets(ctx context.Context, dockerCli command.Cli, nodes []builder.No
|
||||||
|
|
||||||
var mu sync.Mutex
|
var mu sync.Mutex
|
||||||
var idx int
|
var idx int
|
||||||
resp, err := build.BuildWithResultHandler(ctx, nodes, opts, dockerutil.NewClient(dockerCli), confutil.ConfigDir(dockerCli), printer, func(driverIndex int, gotRes *build.ResultContext) {
|
resp, err := build.BuildWithResultHandler(ctx, nodes, opts, dockerutil.NewClient(dockerCli), confutil.ConfigDir(dockerCli), progress.Tee(printer, statusChan), func(driverIndex int, gotRes *build.ResultContext) {
|
||||||
mu.Lock()
|
mu.Lock()
|
||||||
defer mu.Unlock()
|
defer mu.Unlock()
|
||||||
if res == nil || driverIndex < idx {
|
if res == nil || driverIndex < idx {
|
||||||
idx, res = driverIndex, gotRes
|
idx, res = driverIndex, gotRes
|
||||||
}
|
}
|
||||||
}, allowNoOutput)
|
})
|
||||||
err1 := printer.Wait()
|
err1 := printer.Wait()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = err1
|
err = err1
|
||||||
|
@ -354,51 +292,6 @@ func buildTargets(ctx context.Context, dockerCli command.Cli, nodes []builder.No
|
||||||
return resp[defaultTargetName].ExporterResponse["containerimage.digest"], res, err
|
return resp[defaultTargetName].ExporterResponse["containerimage.digest"], res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseInvokeConfig(invoke string) (cfg build.ContainerConfig, err error) {
|
|
||||||
cfg.Tty = true
|
|
||||||
if invoke == "default" {
|
|
||||||
return cfg, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
csvReader := csv.NewReader(strings.NewReader(invoke))
|
|
||||||
fields, err := csvReader.Read()
|
|
||||||
if err != nil {
|
|
||||||
return cfg, err
|
|
||||||
}
|
|
||||||
if len(fields) == 1 && !strings.Contains(fields[0], "=") {
|
|
||||||
cfg.Cmd = []string{fields[0]}
|
|
||||||
return cfg, nil
|
|
||||||
}
|
|
||||||
for _, field := range fields {
|
|
||||||
parts := strings.SplitN(field, "=", 2)
|
|
||||||
if len(parts) != 2 {
|
|
||||||
return cfg, errors.Errorf("invalid value %s", field)
|
|
||||||
}
|
|
||||||
key := strings.ToLower(parts[0])
|
|
||||||
value := parts[1]
|
|
||||||
switch key {
|
|
||||||
case "args":
|
|
||||||
cfg.Cmd = append(cfg.Cmd, value) // TODO: support JSON
|
|
||||||
case "entrypoint":
|
|
||||||
cfg.Entrypoint = append(cfg.Entrypoint, value) // TODO: support JSON
|
|
||||||
case "env":
|
|
||||||
cfg.Env = append(cfg.Env, value)
|
|
||||||
case "user":
|
|
||||||
cfg.User = &value
|
|
||||||
case "cwd":
|
|
||||||
cfg.Cwd = &value
|
|
||||||
case "tty":
|
|
||||||
cfg.Tty, err = strconv.ParseBool(value)
|
|
||||||
if err != nil {
|
|
||||||
return cfg, errors.Errorf("failed to parse tty: %v", err)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return cfg, errors.Errorf("unknown key %q", key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return cfg, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func printWarnings(w io.Writer, warnings []client.VertexWarning, mode string) {
|
func printWarnings(w io.Writer, warnings []client.VertexWarning, mode string) {
|
||||||
if len(warnings) == 0 || mode == progress.PrinterModeQuiet {
|
if len(warnings) == 0 || mode == progress.PrinterModeQuiet {
|
||||||
return
|
return
|
||||||
|
@ -439,15 +332,9 @@ func printWarnings(w io.Writer, warnings []client.VertexWarning, mode string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newBuildOptions() buildOptions {
|
|
||||||
ulimits := make(map[string]*units.Ulimit)
|
|
||||||
return buildOptions{
|
|
||||||
ulimits: dockeropts.NewUlimitOpt(&ulimits),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
|
func buildCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
|
||||||
options := newBuildOptions()
|
options := newBuildOptions()
|
||||||
|
cFlags := &commonFlags{}
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "build [OPTIONS] PATH | URL | -",
|
Use: "build [OPTIONS] PATH | URL | -",
|
||||||
|
@ -455,9 +342,22 @@ func buildCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
|
||||||
Short: "Start a build",
|
Short: "Start a build",
|
||||||
Args: cli.ExactArgs(1),
|
Args: cli.ExactArgs(1),
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
options.contextPath = args[0]
|
options.ContextPath = args[0]
|
||||||
options.builder = rootOpts.builder
|
options.Opts.Builder = rootOpts.builder
|
||||||
|
options.Opts.MetadataFile = cFlags.metadataFile
|
||||||
|
options.Opts.NoCache = false
|
||||||
|
if cFlags.noCache != nil {
|
||||||
|
options.Opts.NoCache = *cFlags.noCache
|
||||||
|
}
|
||||||
|
options.Opts.Pull = false
|
||||||
|
if cFlags.pull != nil {
|
||||||
|
options.Opts.Pull = *cFlags.pull
|
||||||
|
}
|
||||||
|
options.progress = cFlags.progress
|
||||||
cmd.Flags().VisitAll(checkWarnedFlags)
|
cmd.Flags().VisitAll(checkWarnedFlags)
|
||||||
|
if isExperimental() {
|
||||||
|
return launchControllerAndRunBuild(dockerCli, options)
|
||||||
|
}
|
||||||
return runBuild(dockerCli, options)
|
return runBuild(dockerCli, options)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -469,67 +369,70 @@ func buildCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
|
||||||
|
|
||||||
flags := cmd.Flags()
|
flags := cmd.Flags()
|
||||||
|
|
||||||
flags.StringSliceVar(&options.extraHosts, "add-host", []string{}, `Add a custom host-to-IP mapping (format: "host:ip")`)
|
flags.StringSliceVar(&options.ExtraHosts, "add-host", []string{}, `Add a custom host-to-IP mapping (format: "host:ip")`)
|
||||||
flags.SetAnnotation("add-host", annotation.ExternalURL, []string{"https://docs.docker.com/engine/reference/commandline/build/#add-host"})
|
flags.SetAnnotation("add-host", annotation.ExternalURL, []string{"https://docs.docker.com/engine/reference/commandline/build/#add-host"})
|
||||||
|
|
||||||
flags.StringSliceVar(&options.allow, "allow", []string{}, `Allow extra privileged entitlement (e.g., "network.host", "security.insecure")`)
|
flags.StringSliceVar(&options.Allow, "allow", []string{}, `Allow extra privileged entitlement (e.g., "network.host", "security.insecure")`)
|
||||||
|
|
||||||
flags.StringArrayVar(&options.buildArgs, "build-arg", []string{}, "Set build-time variables")
|
flags.StringArrayVar(&options.BuildArgs, "build-arg", []string{}, "Set build-time variables")
|
||||||
|
|
||||||
flags.StringArrayVar(&options.cacheFrom, "cache-from", []string{}, `External cache sources (e.g., "user/app:cache", "type=local,src=path/to/dir")`)
|
flags.StringArrayVar(&options.CacheFrom, "cache-from", []string{}, `External cache sources (e.g., "user/app:cache", "type=local,src=path/to/dir")`)
|
||||||
|
|
||||||
flags.StringArrayVar(&options.cacheTo, "cache-to", []string{}, `Cache export destinations (e.g., "user/app:cache", "type=local,dest=path/to/dir")`)
|
flags.StringArrayVar(&options.CacheTo, "cache-to", []string{}, `Cache export destinations (e.g., "user/app:cache", "type=local,dest=path/to/dir")`)
|
||||||
|
|
||||||
flags.StringVar(&options.cgroupParent, "cgroup-parent", "", "Optional parent cgroup for the container")
|
flags.StringVar(&options.CgroupParent, "cgroup-parent", "", "Optional parent cgroup for the container")
|
||||||
flags.SetAnnotation("cgroup-parent", annotation.ExternalURL, []string{"https://docs.docker.com/engine/reference/commandline/build/#cgroup-parent"})
|
flags.SetAnnotation("cgroup-parent", annotation.ExternalURL, []string{"https://docs.docker.com/engine/reference/commandline/build/#cgroup-parent"})
|
||||||
|
|
||||||
flags.StringArrayVar(&options.contexts, "build-context", []string{}, "Additional build contexts (e.g., name=path)")
|
flags.StringArrayVar(&options.Contexts, "build-context", []string{}, "Additional build contexts (e.g., name=path)")
|
||||||
|
|
||||||
flags.StringVarP(&options.dockerfileName, "file", "f", "", `Name of the Dockerfile (default: "PATH/Dockerfile")`)
|
flags.StringVarP(&options.DockerfileName, "file", "f", "", `Name of the Dockerfile (default: "PATH/Dockerfile")`)
|
||||||
flags.SetAnnotation("file", annotation.ExternalURL, []string{"https://docs.docker.com/engine/reference/commandline/build/#file"})
|
flags.SetAnnotation("file", annotation.ExternalURL, []string{"https://docs.docker.com/engine/reference/commandline/build/#file"})
|
||||||
|
|
||||||
flags.StringVar(&options.imageIDFile, "iidfile", "", "Write the image ID to the file")
|
flags.StringVar(&options.ImageIDFile, "iidfile", "", "Write the image ID to the file")
|
||||||
|
|
||||||
flags.StringArrayVar(&options.labels, "label", []string{}, "Set metadata for an image")
|
flags.StringArrayVar(&options.Labels, "label", []string{}, "Set metadata for an image")
|
||||||
|
|
||||||
flags.BoolVar(&options.exportLoad, "load", false, `Shorthand for "--output=type=docker"`)
|
flags.BoolVar(&options.Opts.ExportLoad, "load", false, `Shorthand for "--output=type=docker"`)
|
||||||
|
|
||||||
flags.StringVar(&options.networkMode, "network", "default", `Set the networking mode for the "RUN" instructions during build`)
|
flags.StringVar(&options.NetworkMode, "network", "default", `Set the networking mode for the "RUN" instructions during build`)
|
||||||
|
|
||||||
flags.StringArrayVar(&options.noCacheFilter, "no-cache-filter", []string{}, "Do not cache specified stages")
|
flags.StringArrayVar(&options.NoCacheFilter, "no-cache-filter", []string{}, "Do not cache specified stages")
|
||||||
|
|
||||||
flags.StringArrayVarP(&options.outputs, "output", "o", []string{}, `Output destination (format: "type=local,dest=path")`)
|
flags.StringArrayVarP(&options.Outputs, "output", "o", []string{}, `Output destination (format: "type=local,dest=path")`)
|
||||||
|
|
||||||
flags.StringArrayVar(&options.platforms, "platform", platformsDefault, "Set target platform for build")
|
flags.StringArrayVar(&options.Platforms, "platform", platformsDefault, "Set target platform for build")
|
||||||
|
|
||||||
if isExperimental() {
|
if isExperimental() {
|
||||||
flags.StringVar(&options.printFunc, "print", "", "Print result of information request (e.g., outline, targets) [experimental]")
|
flags.StringVar(&options.PrintFunc, "print", "", "Print result of information request (e.g., outline, targets) [experimental]")
|
||||||
}
|
}
|
||||||
|
|
||||||
flags.BoolVar(&options.exportPush, "push", false, `Shorthand for "--output=type=registry"`)
|
flags.BoolVar(&options.Opts.ExportPush, "push", false, `Shorthand for "--output=type=registry"`)
|
||||||
|
|
||||||
flags.BoolVarP(&options.quiet, "quiet", "q", false, "Suppress the build output and print image ID on success")
|
flags.BoolVarP(&options.Quiet, "quiet", "q", false, "Suppress the build output and print image ID on success")
|
||||||
|
|
||||||
flags.StringArrayVar(&options.secrets, "secret", []string{}, `Secret to expose to the build (format: "id=mysecret[,src=/local/secret]")`)
|
flags.StringArrayVar(&options.Secrets, "secret", []string{}, `Secret to expose to the build (format: "id=mysecret[,src=/local/secret]")`)
|
||||||
|
|
||||||
flags.Var(&options.shmSize, "shm-size", `Size of "/dev/shm"`)
|
flags.Var(newShmSize(&options), "shm-size", `Size of "/dev/shm"`)
|
||||||
|
|
||||||
flags.StringArrayVar(&options.ssh, "ssh", []string{}, `SSH agent socket or keys to expose to the build (format: "default|<id>[=<socket>|<key>[,<key>]]")`)
|
flags.StringArrayVar(&options.SSH, "ssh", []string{}, `SSH agent socket or keys to expose to the build (format: "default|<id>[=<socket>|<key>[,<key>]]")`)
|
||||||
|
|
||||||
flags.StringArrayVarP(&options.tags, "tag", "t", []string{}, `Name and optionally a tag (format: "name:tag")`)
|
flags.StringArrayVarP(&options.Tags, "tag", "t", []string{}, `Name and optionally a tag (format: "name:tag")`)
|
||||||
flags.SetAnnotation("tag", annotation.ExternalURL, []string{"https://docs.docker.com/engine/reference/commandline/build/#tag"})
|
flags.SetAnnotation("tag", annotation.ExternalURL, []string{"https://docs.docker.com/engine/reference/commandline/build/#tag"})
|
||||||
|
|
||||||
flags.StringVar(&options.target, "target", "", "Set the target build stage to build")
|
flags.StringVar(&options.Target, "target", "", "Set the target build stage to build")
|
||||||
flags.SetAnnotation("target", annotation.ExternalURL, []string{"https://docs.docker.com/engine/reference/commandline/build/#target"})
|
flags.SetAnnotation("target", annotation.ExternalURL, []string{"https://docs.docker.com/engine/reference/commandline/build/#target"})
|
||||||
|
|
||||||
flags.Var(options.ulimits, "ulimit", "Ulimit options")
|
flags.Var(newUlimits(&options), "ulimit", "Ulimit options")
|
||||||
|
|
||||||
flags.StringArrayVar(&options.attests, "attest", []string{}, `Attestation parameters (format: "type=sbom,generator=image")`)
|
flags.StringArrayVar(&options.Attests, "attest", []string{}, `Attestation parameters (format: "type=sbom,generator=image")`)
|
||||||
flags.StringVar(&options.sbom, "sbom", "", `Shorthand for "--attest=type=sbom"`)
|
flags.StringVar(&options.Opts.SBOM, "sbom", "", `Shorthand for "--attest=type=sbom"`)
|
||||||
flags.StringVar(&options.provenance, "provenance", "", `Shortand for "--attest=type=provenance"`)
|
flags.StringVar(&options.Opts.Provenance, "provenance", "", `Shortand for "--attest=type=provenance"`)
|
||||||
|
|
||||||
if isExperimental() {
|
if isExperimental() {
|
||||||
flags.StringVar(&options.invoke, "invoke", "", "Invoke a command after the build [experimental]")
|
flags.StringVar(&options.invoke, "invoke", "", "Invoke a command after the build [experimental]")
|
||||||
|
flags.StringVar(&options.root, "root", "", "Specify root directory of server to connect [experimental]")
|
||||||
|
flags.BoolVar(&options.detach, "detach", runtime.GOOS == "linux", "Detach buildx server (supported only on linux) [experimental]")
|
||||||
|
flags.StringVar(&options.serverConfig, "server-config", "", "Specify buildx server config file (used only when launching new server) [experimental]")
|
||||||
}
|
}
|
||||||
|
|
||||||
// hidden flags
|
// hidden flags
|
||||||
|
@ -580,11 +483,19 @@ func buildCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
|
||||||
flags.BoolVar(&ignoreBool, "force-rm", false, "Always remove intermediate containers")
|
flags.BoolVar(&ignoreBool, "force-rm", false, "Always remove intermediate containers")
|
||||||
flags.MarkHidden("force-rm")
|
flags.MarkHidden("force-rm")
|
||||||
|
|
||||||
commonBuildFlags(&options.commonOptions, flags)
|
commonBuildFlags(cFlags, flags)
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func commonBuildFlags(options *commonOptions, flags *pflag.FlagSet) {
|
// comomnFlags is a set of flags commonly shared among subcommands.
|
||||||
|
type commonFlags struct {
|
||||||
|
metadataFile string
|
||||||
|
progress string
|
||||||
|
noCache *bool
|
||||||
|
pull *bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func commonBuildFlags(options *commonFlags, flags *pflag.FlagSet) {
|
||||||
options.noCache = flags.Bool("no-cache", false, "Do not use cache when building the image")
|
options.noCache = flags.Bool("no-cache", false, "Do not use cache when building the image")
|
||||||
flags.StringVar(&options.progress, "progress", "auto", `Set type of progress output ("auto", "plain", "tty"). Use plain to show container output`)
|
flags.StringVar(&options.progress, "progress", "auto", `Set type of progress output ("auto", "plain", "tty"). Use plain to show container output`)
|
||||||
options.pull = flags.Bool("pull", false, "Always attempt to pull all referenced images")
|
options.pull = flags.Bool("pull", false, "Always attempt to pull all referenced images")
|
||||||
|
@ -744,3 +655,235 @@ func updateLastActivity(dockerCli command.Cli, ng *store.NodeGroup) error {
|
||||||
defer release()
|
defer release()
|
||||||
return txn.UpdateLastActivity(ng)
|
return txn.UpdateLastActivity(ng)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func launchControllerAndRunBuild(dockerCli command.Cli, options buildOptions) error {
|
||||||
|
ctx := context.TODO()
|
||||||
|
|
||||||
|
if options.Quiet && options.progress != "auto" && options.progress != "quiet" {
|
||||||
|
return errors.Errorf("progress=%s and quiet cannot be used together", options.progress)
|
||||||
|
} else if options.Quiet {
|
||||||
|
options.progress = "quiet"
|
||||||
|
}
|
||||||
|
if options.invoke != "" && (options.DockerfileName == "-" || options.ContextPath == "-") {
|
||||||
|
// stdin must be usable for monitor
|
||||||
|
return errors.Errorf("Dockerfile or context from stdin is not supported with invoke")
|
||||||
|
}
|
||||||
|
var invokeConfig controllerapi.ContainerConfig
|
||||||
|
if inv := options.invoke; inv != "" {
|
||||||
|
var err error
|
||||||
|
invokeConfig, err = parseInvokeConfig(inv) // TODO: produce *controller.ContainerConfig directly.
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
var c monitor.BuildxController
|
||||||
|
var err error
|
||||||
|
if options.detach {
|
||||||
|
logrus.Infof("connecting to buildx server")
|
||||||
|
c, err = newRemoteBuildxController(ctx, dockerCli, options)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to use buildx server; use --detach=false: %w", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logrus.Infof("launching local buildx controller")
|
||||||
|
c = newLocalBuildxController(ctx, dockerCli)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err := c.Close(); err != nil {
|
||||||
|
logrus.Warnf("failed to close server connection %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
f := ioset.NewSingleForwarder()
|
||||||
|
pr, pw := io.Pipe()
|
||||||
|
f.SetWriter(pw, func() io.WriteCloser {
|
||||||
|
pw.Close() // propagate EOF
|
||||||
|
logrus.Debug("propagating stdin close")
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
f.SetReader(os.Stdin)
|
||||||
|
|
||||||
|
// Start build
|
||||||
|
ref, err := c.Build(ctx, options.BuildOptions, pr, os.Stdout, os.Stderr, options.progress)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to build: %w", err) // TODO: allow invoke even on error
|
||||||
|
}
|
||||||
|
if err := pw.Close(); err != nil {
|
||||||
|
logrus.Debug("failed to close stdin pipe writer")
|
||||||
|
}
|
||||||
|
if err := pr.Close(); err != nil {
|
||||||
|
logrus.Debug("failed to close stdin pipe reader")
|
||||||
|
}
|
||||||
|
|
||||||
|
// post-build operations
|
||||||
|
if options.invoke != "" {
|
||||||
|
pr2, pw2 := io.Pipe()
|
||||||
|
f.SetWriter(pw2, func() io.WriteCloser {
|
||||||
|
pw2.Close() // propagate EOF
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
con := console.Current()
|
||||||
|
if err := con.SetRaw(); err != nil {
|
||||||
|
if err := c.Disconnect(ctx, ref); err != nil {
|
||||||
|
logrus.Warnf("disconnect error: %v", err)
|
||||||
|
}
|
||||||
|
return errors.Errorf("failed to configure terminal: %v", err)
|
||||||
|
}
|
||||||
|
err = monitor.RunMonitor(ctx, ref, options.BuildOptions, invokeConfig, c, options.progress, pr2, os.Stdout, os.Stderr)
|
||||||
|
con.Reset()
|
||||||
|
if err := pw2.Close(); err != nil {
|
||||||
|
logrus.Debug("failed to close monitor stdin pipe reader")
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
logrus.Warnf("failed to run monitor: %v", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err := c.Disconnect(ctx, ref); err != nil {
|
||||||
|
logrus.Warnf("disconnect error: %v", err)
|
||||||
|
}
|
||||||
|
// If "invoke" isn't specified, further inspection ins't provided. Finish the buildx server.
|
||||||
|
if err := c.Kill(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseInvokeConfig(invoke string) (cfg controllerapi.ContainerConfig, err error) {
|
||||||
|
cfg.Tty = true
|
||||||
|
if invoke == "default" {
|
||||||
|
return cfg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
csvReader := csv.NewReader(strings.NewReader(invoke))
|
||||||
|
fields, err := csvReader.Read()
|
||||||
|
if err != nil {
|
||||||
|
return cfg, err
|
||||||
|
}
|
||||||
|
if len(fields) == 1 && !strings.Contains(fields[0], "=") {
|
||||||
|
cfg.Cmd = []string{fields[0]}
|
||||||
|
return cfg, nil
|
||||||
|
}
|
||||||
|
cfg.NoUser = true
|
||||||
|
cfg.NoCwd = true
|
||||||
|
for _, field := range fields {
|
||||||
|
parts := strings.SplitN(field, "=", 2)
|
||||||
|
if len(parts) != 2 {
|
||||||
|
return cfg, errors.Errorf("invalid value %s", field)
|
||||||
|
}
|
||||||
|
key := strings.ToLower(parts[0])
|
||||||
|
value := parts[1]
|
||||||
|
switch key {
|
||||||
|
case "args":
|
||||||
|
cfg.Cmd = append(cfg.Cmd, value) // TODO: support JSON
|
||||||
|
case "entrypoint":
|
||||||
|
cfg.Entrypoint = append(cfg.Entrypoint, value) // TODO: support JSON
|
||||||
|
case "env":
|
||||||
|
cfg.Env = append(cfg.Env, value)
|
||||||
|
case "user":
|
||||||
|
cfg.User = value
|
||||||
|
cfg.NoUser = false
|
||||||
|
case "cwd":
|
||||||
|
cfg.Cwd = value
|
||||||
|
cfg.NoCwd = false
|
||||||
|
case "tty":
|
||||||
|
cfg.Tty, err = strconv.ParseBool(value)
|
||||||
|
if err != nil {
|
||||||
|
return cfg, errors.Errorf("failed to parse tty: %v", err)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return cfg, errors.Errorf("unknown key %q", key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cfg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func controllerUlimitOpt2DockerUlimit(u *controllerapi.UlimitOpt) *dockeropts.UlimitOpt {
|
||||||
|
if u == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
values := make(map[string]*units.Ulimit)
|
||||||
|
for k, v := range u.Values {
|
||||||
|
values[k] = &units.Ulimit{
|
||||||
|
Name: v.Name,
|
||||||
|
Hard: v.Hard,
|
||||||
|
Soft: v.Soft,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dockeropts.NewUlimitOpt(&values)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newBuildOptions() buildOptions {
|
||||||
|
return buildOptions{
|
||||||
|
BuildOptions: controllerapi.BuildOptions{
|
||||||
|
Opts: &controllerapi.CommonOptions{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newUlimits(opt *buildOptions) *ulimits {
|
||||||
|
ul := make(map[string]*units.Ulimit)
|
||||||
|
return &ulimits{opt: opt, org: dockeropts.NewUlimitOpt(&ul)}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ulimits struct {
|
||||||
|
opt *buildOptions
|
||||||
|
org *dockeropts.UlimitOpt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *ulimits) sync() {
|
||||||
|
du := &controllerapi.UlimitOpt{
|
||||||
|
Values: make(map[string]*controllerapi.Ulimit),
|
||||||
|
}
|
||||||
|
for _, l := range u.org.GetList() {
|
||||||
|
du.Values[l.Name] = &controllerapi.Ulimit{
|
||||||
|
Name: l.Name,
|
||||||
|
Hard: l.Hard,
|
||||||
|
Soft: l.Soft,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
u.opt.Ulimits = du
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *ulimits) String() string {
|
||||||
|
return u.org.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *ulimits) Set(v string) error {
|
||||||
|
err := u.org.Set(v)
|
||||||
|
u.sync()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *ulimits) Type() string {
|
||||||
|
return u.org.Type()
|
||||||
|
}
|
||||||
|
|
||||||
|
func newShmSize(opt *buildOptions) *shmSize {
|
||||||
|
return &shmSize{opt: opt}
|
||||||
|
}
|
||||||
|
|
||||||
|
type shmSize struct {
|
||||||
|
opt *buildOptions
|
||||||
|
org dockeropts.MemBytes
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *shmSize) sync() {
|
||||||
|
s.opt.ShmSize = s.org.Value()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *shmSize) String() string {
|
||||||
|
return s.org.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *shmSize) Set(v string) error {
|
||||||
|
err := s.org.Set(v)
|
||||||
|
s.sync()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *shmSize) Type() string {
|
||||||
|
return s.org.Type()
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,262 @@
|
||||||
|
package controller
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/containerd/console"
|
||||||
|
"github.com/containerd/containerd/defaults"
|
||||||
|
"github.com/containerd/containerd/pkg/dialer"
|
||||||
|
"github.com/docker/buildx/commands/controller/pb"
|
||||||
|
"github.com/docker/buildx/util/progress"
|
||||||
|
"github.com/moby/buildkit/client"
|
||||||
|
"github.com/moby/buildkit/identity"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"golang.org/x/sync/errgroup"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/backoff"
|
||||||
|
"google.golang.org/grpc/credentials/insecure"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewClient(addr string) (*Client, error) {
|
||||||
|
backoffConfig := backoff.DefaultConfig
|
||||||
|
backoffConfig.MaxDelay = 3 * time.Second
|
||||||
|
connParams := grpc.ConnectParams{
|
||||||
|
Backoff: backoffConfig,
|
||||||
|
}
|
||||||
|
gopts := []grpc.DialOption{
|
||||||
|
grpc.WithTransportCredentials(insecure.NewCredentials()),
|
||||||
|
grpc.WithConnectParams(connParams),
|
||||||
|
grpc.WithContextDialer(dialer.ContextDialer),
|
||||||
|
grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(defaults.DefaultMaxRecvMsgSize)),
|
||||||
|
grpc.WithDefaultCallOptions(grpc.MaxCallSendMsgSize(defaults.DefaultMaxSendMsgSize)),
|
||||||
|
}
|
||||||
|
conn, err := grpc.Dial(dialer.DialAddress(addr), gopts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &Client{conn: conn}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Client struct {
|
||||||
|
conn *grpc.ClientConn
|
||||||
|
closeOnce sync.Once
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) Close() (err error) {
|
||||||
|
c.closeOnce.Do(func() {
|
||||||
|
err = c.conn.Close()
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) Version(ctx context.Context) (string, string, string, error) {
|
||||||
|
res, err := c.client().Info(ctx, &pb.InfoRequest{})
|
||||||
|
if err != nil {
|
||||||
|
return "", "", "", err
|
||||||
|
}
|
||||||
|
v := res.BuildxVersion
|
||||||
|
return v.Package, v.Version, v.Revision, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) List(ctx context.Context) (keys []string, retErr error) {
|
||||||
|
res, err := c.client().List(ctx, &pb.ListRequest{})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return res.Keys, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) Disconnect(ctx context.Context, key string) error {
|
||||||
|
_, err := c.client().Disconnect(ctx, &pb.DisconnectRequest{Ref: key})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) Invoke(ctx context.Context, ref string, containerConfig pb.ContainerConfig, in io.ReadCloser, stdout io.WriteCloser, stderr io.WriteCloser) error {
|
||||||
|
if ref == "" {
|
||||||
|
return fmt.Errorf("build reference must be specified")
|
||||||
|
}
|
||||||
|
stream, err := c.client().Invoke(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return attachIO(ctx, stream, &pb.InitMessage{Ref: ref, ContainerConfig: &containerConfig}, ioAttachConfig{
|
||||||
|
stdin: in,
|
||||||
|
stdout: stdout,
|
||||||
|
stderr: stderr,
|
||||||
|
// TODO: Signal, Resize
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) Build(ctx context.Context, options pb.BuildOptions, in io.ReadCloser, w io.Writer, out console.File, progressMode string) (string, error) {
|
||||||
|
ref := identity.NewID()
|
||||||
|
pw, err := progress.NewPrinter(context.TODO(), w, out, progressMode)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
statusChan := make(chan *client.SolveStatus)
|
||||||
|
statusDone := make(chan struct{})
|
||||||
|
eg, egCtx := errgroup.WithContext(ctx)
|
||||||
|
eg.Go(func() error {
|
||||||
|
defer close(statusChan)
|
||||||
|
return c.build(egCtx, ref, options, in, statusChan)
|
||||||
|
})
|
||||||
|
eg.Go(func() error {
|
||||||
|
defer close(statusDone)
|
||||||
|
for s := range statusChan {
|
||||||
|
st := s
|
||||||
|
pw.Write(st)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
eg.Go(func() error {
|
||||||
|
<-statusDone
|
||||||
|
return pw.Wait()
|
||||||
|
})
|
||||||
|
return ref, eg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) build(ctx context.Context, ref string, options pb.BuildOptions, in io.ReadCloser, statusChan chan *client.SolveStatus) error {
|
||||||
|
eg, egCtx := errgroup.WithContext(ctx)
|
||||||
|
done := make(chan struct{})
|
||||||
|
eg.Go(func() error {
|
||||||
|
defer close(done)
|
||||||
|
if _, err := c.client().Build(egCtx, &pb.BuildRequest{
|
||||||
|
Ref: ref,
|
||||||
|
Options: &options,
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
eg.Go(func() error {
|
||||||
|
stream, err := c.client().Status(egCtx, &pb.StatusRequest{
|
||||||
|
Ref: ref,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
resp, err := stream.Recv()
|
||||||
|
if err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return errors.Wrap(err, "failed to receive status")
|
||||||
|
}
|
||||||
|
s := client.SolveStatus{}
|
||||||
|
for _, v := range resp.Vertexes {
|
||||||
|
s.Vertexes = append(s.Vertexes, &client.Vertex{
|
||||||
|
Digest: v.Digest,
|
||||||
|
Inputs: v.Inputs,
|
||||||
|
Name: v.Name,
|
||||||
|
Started: v.Started,
|
||||||
|
Completed: v.Completed,
|
||||||
|
Error: v.Error,
|
||||||
|
Cached: v.Cached,
|
||||||
|
ProgressGroup: v.ProgressGroup,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
for _, v := range resp.Statuses {
|
||||||
|
s.Statuses = append(s.Statuses, &client.VertexStatus{
|
||||||
|
ID: v.ID,
|
||||||
|
Vertex: v.Vertex,
|
||||||
|
Name: v.Name,
|
||||||
|
Total: v.Total,
|
||||||
|
Current: v.Current,
|
||||||
|
Timestamp: v.Timestamp,
|
||||||
|
Started: v.Started,
|
||||||
|
Completed: v.Completed,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
for _, v := range resp.Logs {
|
||||||
|
s.Logs = append(s.Logs, &client.VertexLog{
|
||||||
|
Vertex: v.Vertex,
|
||||||
|
Stream: int(v.Stream),
|
||||||
|
Data: v.Msg,
|
||||||
|
Timestamp: v.Timestamp,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
for _, v := range resp.Warnings {
|
||||||
|
s.Warnings = append(s.Warnings, &client.VertexWarning{
|
||||||
|
Vertex: v.Vertex,
|
||||||
|
Level: int(v.Level),
|
||||||
|
Short: v.Short,
|
||||||
|
Detail: v.Detail,
|
||||||
|
URL: v.Url,
|
||||||
|
SourceInfo: v.Info,
|
||||||
|
Range: v.Ranges,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
statusChan <- &s
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if in != nil {
|
||||||
|
eg.Go(func() error {
|
||||||
|
stream, err := c.client().Input(egCtx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := stream.Send(&pb.InputMessage{
|
||||||
|
Input: &pb.InputMessage_Init{
|
||||||
|
Init: &pb.InputInitMessage{
|
||||||
|
Ref: ref,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}); err != nil {
|
||||||
|
return fmt.Errorf("failed to init input: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
inReader, inWriter := io.Pipe()
|
||||||
|
eg2, _ := errgroup.WithContext(ctx)
|
||||||
|
eg2.Go(func() error {
|
||||||
|
<-done
|
||||||
|
return inWriter.Close()
|
||||||
|
})
|
||||||
|
go func() {
|
||||||
|
// do not wait for read completion but return here and let the caller send EOF
|
||||||
|
// this allows us to return on ctx.Done() without being blocked by this reader.
|
||||||
|
io.Copy(inWriter, in)
|
||||||
|
inWriter.Close()
|
||||||
|
}()
|
||||||
|
eg2.Go(func() error {
|
||||||
|
for {
|
||||||
|
buf := make([]byte, 32*1024)
|
||||||
|
n, err := inReader.Read(buf)
|
||||||
|
if err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
break // break loop and send EOF
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
} else if n > 0 {
|
||||||
|
if stream.Send(&pb.InputMessage{
|
||||||
|
Input: &pb.InputMessage_Data{
|
||||||
|
Data: &pb.DataMessage{
|
||||||
|
Data: buf[:n],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return stream.Send(&pb.InputMessage{
|
||||||
|
Input: &pb.InputMessage_Data{
|
||||||
|
Data: &pb.DataMessage{
|
||||||
|
EOF: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return eg2.Wait()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return eg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) client() pb.ControllerClient {
|
||||||
|
return pb.NewControllerClient(c.conn)
|
||||||
|
}
|
|
@ -0,0 +1,440 @@
|
||||||
|
package controller
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/docker/buildx/build"
|
||||||
|
"github.com/docker/buildx/commands/controller/pb"
|
||||||
|
"github.com/docker/buildx/util/ioset"
|
||||||
|
"github.com/docker/buildx/version"
|
||||||
|
controlapi "github.com/moby/buildkit/api/services/control"
|
||||||
|
"github.com/moby/buildkit/client"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"golang.org/x/sync/errgroup"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BuildFunc func(ctx context.Context, options *pb.BuildOptions, stdin io.Reader, statusChan chan *client.SolveStatus) (res *build.ResultContext, err error)
|
||||||
|
|
||||||
|
func New(buildFunc BuildFunc) *Controller {
|
||||||
|
return &Controller{
|
||||||
|
buildFunc: buildFunc,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Controller struct {
|
||||||
|
buildFunc BuildFunc
|
||||||
|
session map[string]session
|
||||||
|
sessionMu sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
type session struct {
|
||||||
|
statusChan chan *client.SolveStatus
|
||||||
|
result *build.ResultContext
|
||||||
|
inputPipe *io.PipeWriter
|
||||||
|
curInvokeCancel func()
|
||||||
|
curBuildCancel func()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Controller) Info(ctx context.Context, req *pb.InfoRequest) (res *pb.InfoResponse, err error) {
|
||||||
|
return &pb.InfoResponse{
|
||||||
|
BuildxVersion: &pb.BuildxVersion{
|
||||||
|
Package: version.Package,
|
||||||
|
Version: version.Version,
|
||||||
|
Revision: version.Revision,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Controller) List(ctx context.Context, req *pb.ListRequest) (res *pb.ListResponse, err error) {
|
||||||
|
keys := make(map[string]struct{})
|
||||||
|
|
||||||
|
m.sessionMu.Lock()
|
||||||
|
for k := range m.session {
|
||||||
|
keys[k] = struct{}{}
|
||||||
|
}
|
||||||
|
m.sessionMu.Unlock()
|
||||||
|
|
||||||
|
var keysL []string
|
||||||
|
for k := range keys {
|
||||||
|
keysL = append(keysL, k)
|
||||||
|
}
|
||||||
|
return &pb.ListResponse{
|
||||||
|
Keys: keysL,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Controller) Disconnect(ctx context.Context, req *pb.DisconnectRequest) (res *pb.DisconnectResponse, err error) {
|
||||||
|
key := req.Ref
|
||||||
|
if key == "" {
|
||||||
|
return nil, fmt.Errorf("disconnect: empty key")
|
||||||
|
}
|
||||||
|
|
||||||
|
m.sessionMu.Lock()
|
||||||
|
if s, ok := m.session[key]; ok {
|
||||||
|
if s.curBuildCancel != nil {
|
||||||
|
s.curBuildCancel()
|
||||||
|
}
|
||||||
|
if s.curInvokeCancel != nil {
|
||||||
|
s.curInvokeCancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delete(m.session, key)
|
||||||
|
m.sessionMu.Unlock()
|
||||||
|
|
||||||
|
return &pb.DisconnectResponse{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Controller) Close() error {
|
||||||
|
m.sessionMu.Lock()
|
||||||
|
for k := range m.session {
|
||||||
|
if s, ok := m.session[k]; ok {
|
||||||
|
if s.curBuildCancel != nil {
|
||||||
|
s.curBuildCancel()
|
||||||
|
}
|
||||||
|
if s.curInvokeCancel != nil {
|
||||||
|
s.curInvokeCancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m.sessionMu.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Controller) Build(ctx context.Context, req *pb.BuildRequest) (*pb.BuildResponse, error) {
|
||||||
|
ref := req.Ref
|
||||||
|
if ref == "" {
|
||||||
|
return nil, fmt.Errorf("build: empty key")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare status channel and session if not exists
|
||||||
|
m.sessionMu.Lock()
|
||||||
|
if m.session == nil {
|
||||||
|
m.session = make(map[string]session)
|
||||||
|
}
|
||||||
|
s, ok := m.session[ref]
|
||||||
|
if ok && m.session[ref].statusChan != nil {
|
||||||
|
m.sessionMu.Unlock()
|
||||||
|
return &pb.BuildResponse{}, fmt.Errorf("build or status ongoing or status didn't called")
|
||||||
|
}
|
||||||
|
statusChan := make(chan *client.SolveStatus)
|
||||||
|
s.statusChan = statusChan
|
||||||
|
m.session[ref] = session{statusChan: statusChan}
|
||||||
|
m.sessionMu.Unlock()
|
||||||
|
defer func() {
|
||||||
|
close(statusChan)
|
||||||
|
m.sessionMu.Lock()
|
||||||
|
s, ok := m.session[ref]
|
||||||
|
if ok {
|
||||||
|
s.statusChan = nil
|
||||||
|
}
|
||||||
|
m.sessionMu.Unlock()
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Prepare input stream pipe
|
||||||
|
inR, inW := io.Pipe()
|
||||||
|
m.sessionMu.Lock()
|
||||||
|
if s, ok := m.session[ref]; ok {
|
||||||
|
s.inputPipe = inW
|
||||||
|
m.session[ref] = s
|
||||||
|
} else {
|
||||||
|
m.sessionMu.Unlock()
|
||||||
|
return nil, fmt.Errorf("build: unknown key %v", ref)
|
||||||
|
}
|
||||||
|
m.sessionMu.Unlock()
|
||||||
|
defer inR.Close()
|
||||||
|
|
||||||
|
// Build the specified request
|
||||||
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
|
defer cancel()
|
||||||
|
res, err := m.buildFunc(ctx, req.Options, inR, statusChan)
|
||||||
|
m.sessionMu.Lock()
|
||||||
|
if s, ok := m.session[ref]; ok {
|
||||||
|
s.result = res
|
||||||
|
s.curBuildCancel = cancel
|
||||||
|
m.session[ref] = s
|
||||||
|
} else {
|
||||||
|
m.sessionMu.Unlock()
|
||||||
|
return nil, fmt.Errorf("build: unknown key %v", ref)
|
||||||
|
}
|
||||||
|
m.sessionMu.Unlock()
|
||||||
|
|
||||||
|
return &pb.BuildResponse{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Controller) Status(req *pb.StatusRequest, stream pb.Controller_StatusServer) error {
|
||||||
|
ref := req.Ref
|
||||||
|
if ref == "" {
|
||||||
|
return fmt.Errorf("status: empty key")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait and get status channel prepared by Build()
|
||||||
|
var statusChan <-chan *client.SolveStatus
|
||||||
|
for {
|
||||||
|
// TODO: timeout?
|
||||||
|
m.sessionMu.Lock()
|
||||||
|
if _, ok := m.session[ref]; !ok || m.session[ref].statusChan == nil {
|
||||||
|
m.sessionMu.Unlock()
|
||||||
|
time.Sleep(time.Millisecond) // TODO: wait Build without busy loop and make it cancellable
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
statusChan = m.session[ref].statusChan
|
||||||
|
m.sessionMu.Unlock()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// forward status
|
||||||
|
for ss := range statusChan {
|
||||||
|
if ss == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
cs := toControlStatus(ss)
|
||||||
|
if err := stream.Send(cs); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Controller) Input(stream pb.Controller_InputServer) (err error) {
|
||||||
|
// Get the target ref from init message
|
||||||
|
msg, err := stream.Recv()
|
||||||
|
if err != nil {
|
||||||
|
if !errors.Is(err, io.EOF) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
init := msg.GetInit()
|
||||||
|
if init == nil {
|
||||||
|
return fmt.Errorf("unexpected message: %T; wanted init", msg.GetInit())
|
||||||
|
}
|
||||||
|
ref := init.Ref
|
||||||
|
if ref == "" {
|
||||||
|
return fmt.Errorf("input: no ref is provided")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait and get input stream pipe prepared by Build()
|
||||||
|
var inputPipeW *io.PipeWriter
|
||||||
|
for {
|
||||||
|
// TODO: timeout?
|
||||||
|
m.sessionMu.Lock()
|
||||||
|
if _, ok := m.session[ref]; !ok || m.session[ref].inputPipe == nil {
|
||||||
|
m.sessionMu.Unlock()
|
||||||
|
time.Sleep(time.Millisecond) // TODO: wait Build without busy loop and make it cancellable
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
inputPipeW = m.session[ref].inputPipe
|
||||||
|
m.sessionMu.Unlock()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// Forward input stream
|
||||||
|
eg, ctx := errgroup.WithContext(context.TODO())
|
||||||
|
done := make(chan struct{})
|
||||||
|
msgCh := make(chan *pb.InputMessage)
|
||||||
|
eg.Go(func() error {
|
||||||
|
defer close(msgCh)
|
||||||
|
for {
|
||||||
|
msg, err := stream.Recv()
|
||||||
|
if err != nil {
|
||||||
|
if !errors.Is(err, io.EOF) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case msgCh <- msg:
|
||||||
|
case <-done:
|
||||||
|
return nil
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
eg.Go(func() (retErr error) {
|
||||||
|
defer close(done)
|
||||||
|
defer func() {
|
||||||
|
if retErr != nil {
|
||||||
|
inputPipeW.CloseWithError(retErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
inputPipeW.Close()
|
||||||
|
}()
|
||||||
|
for {
|
||||||
|
var msg *pb.InputMessage
|
||||||
|
select {
|
||||||
|
case msg = <-msgCh:
|
||||||
|
case <-ctx.Done():
|
||||||
|
return fmt.Errorf("canceled: %w", ctx.Err())
|
||||||
|
}
|
||||||
|
if msg == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if data := msg.GetData(); data != nil {
|
||||||
|
if len(data.Data) > 0 {
|
||||||
|
_, err := inputPipeW.Write(data.Data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if data.EOF {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return eg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Controller) Invoke(srv pb.Controller_InvokeServer) error {
|
||||||
|
ctx, cancel := context.WithCancel(context.TODO())
|
||||||
|
defer cancel()
|
||||||
|
containerIn, containerOut := ioset.Pipe()
|
||||||
|
waitInvokeDoneCh := make(chan struct{})
|
||||||
|
var cancelOnce sync.Once
|
||||||
|
curInvokeCancel := func() {
|
||||||
|
cancelOnce.Do(func() { containerOut.Close(); containerIn.Close(); cancel() })
|
||||||
|
<-waitInvokeDoneCh
|
||||||
|
}
|
||||||
|
defer curInvokeCancel()
|
||||||
|
|
||||||
|
var cfg *pb.ContainerConfig
|
||||||
|
var resultCtx *build.ResultContext
|
||||||
|
initDoneCh := make(chan struct{})
|
||||||
|
initErrCh := make(chan error)
|
||||||
|
eg, egCtx := errgroup.WithContext(ctx)
|
||||||
|
eg.Go(func() error {
|
||||||
|
return serveIO(egCtx, srv, func(initMessage *pb.InitMessage) (retErr error) {
|
||||||
|
defer func() {
|
||||||
|
if retErr != nil {
|
||||||
|
initErrCh <- retErr
|
||||||
|
}
|
||||||
|
close(initDoneCh)
|
||||||
|
}()
|
||||||
|
ref := initMessage.Ref
|
||||||
|
cfg = initMessage.ContainerConfig
|
||||||
|
|
||||||
|
// Register cancel callback
|
||||||
|
m.sessionMu.Lock()
|
||||||
|
if s, ok := m.session[ref]; ok {
|
||||||
|
if cancel := s.curInvokeCancel; cancel != nil {
|
||||||
|
logrus.Warnf("invoke: cancelling ongoing invoke of %q", ref)
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
|
s.curInvokeCancel = curInvokeCancel
|
||||||
|
m.session[ref] = s
|
||||||
|
} else {
|
||||||
|
m.sessionMu.Unlock()
|
||||||
|
return fmt.Errorf("invoke: unknown key %v", ref)
|
||||||
|
}
|
||||||
|
m.sessionMu.Unlock()
|
||||||
|
|
||||||
|
// Get the target result to invoke a container from
|
||||||
|
m.sessionMu.Lock()
|
||||||
|
if _, ok := m.session[ref]; !ok || m.session[ref].result == nil {
|
||||||
|
m.sessionMu.Unlock()
|
||||||
|
return fmt.Errorf("unknown reference: %q", ref)
|
||||||
|
}
|
||||||
|
resultCtx = m.session[ref].result
|
||||||
|
m.sessionMu.Unlock()
|
||||||
|
return nil
|
||||||
|
}, &ioServerConfig{
|
||||||
|
stdin: containerOut.Stdin,
|
||||||
|
stdout: containerOut.Stdout,
|
||||||
|
stderr: containerOut.Stderr,
|
||||||
|
// TODO: signal, resize
|
||||||
|
})
|
||||||
|
})
|
||||||
|
eg.Go(func() error {
|
||||||
|
defer containerIn.Close()
|
||||||
|
defer cancel()
|
||||||
|
select {
|
||||||
|
case <-initDoneCh:
|
||||||
|
case err := <-initErrCh:
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if cfg == nil {
|
||||||
|
return fmt.Errorf("no container config is provided")
|
||||||
|
}
|
||||||
|
if resultCtx == nil {
|
||||||
|
return fmt.Errorf("no result is provided")
|
||||||
|
}
|
||||||
|
ccfg := build.ContainerConfig{
|
||||||
|
ResultCtx: resultCtx,
|
||||||
|
Entrypoint: cfg.Entrypoint,
|
||||||
|
Cmd: cfg.Cmd,
|
||||||
|
Env: cfg.Env,
|
||||||
|
Tty: cfg.Tty,
|
||||||
|
Stdin: containerIn.Stdin,
|
||||||
|
Stdout: containerIn.Stdout,
|
||||||
|
Stderr: containerIn.Stderr,
|
||||||
|
}
|
||||||
|
if !cfg.NoUser {
|
||||||
|
ccfg.User = &cfg.User
|
||||||
|
}
|
||||||
|
if !cfg.NoCwd {
|
||||||
|
ccfg.Cwd = &cfg.Cwd
|
||||||
|
}
|
||||||
|
return build.Invoke(egCtx, ccfg)
|
||||||
|
})
|
||||||
|
err := eg.Wait()
|
||||||
|
close(waitInvokeDoneCh)
|
||||||
|
curInvokeCancel()
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func toControlStatus(s *client.SolveStatus) *pb.StatusResponse {
|
||||||
|
resp := pb.StatusResponse{}
|
||||||
|
for _, v := range s.Vertexes {
|
||||||
|
resp.Vertexes = append(resp.Vertexes, &controlapi.Vertex{
|
||||||
|
Digest: v.Digest,
|
||||||
|
Inputs: v.Inputs,
|
||||||
|
Name: v.Name,
|
||||||
|
Started: v.Started,
|
||||||
|
Completed: v.Completed,
|
||||||
|
Error: v.Error,
|
||||||
|
Cached: v.Cached,
|
||||||
|
ProgressGroup: v.ProgressGroup,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
for _, v := range s.Statuses {
|
||||||
|
resp.Statuses = append(resp.Statuses, &controlapi.VertexStatus{
|
||||||
|
ID: v.ID,
|
||||||
|
Vertex: v.Vertex,
|
||||||
|
Name: v.Name,
|
||||||
|
Total: v.Total,
|
||||||
|
Current: v.Current,
|
||||||
|
Timestamp: v.Timestamp,
|
||||||
|
Started: v.Started,
|
||||||
|
Completed: v.Completed,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
for _, v := range s.Logs {
|
||||||
|
resp.Logs = append(resp.Logs, &controlapi.VertexLog{
|
||||||
|
Vertex: v.Vertex,
|
||||||
|
Stream: int64(v.Stream),
|
||||||
|
Msg: v.Data,
|
||||||
|
Timestamp: v.Timestamp,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
for _, v := range s.Warnings {
|
||||||
|
resp.Warnings = append(resp.Warnings, &controlapi.VertexWarning{
|
||||||
|
Vertex: v.Vertex,
|
||||||
|
Level: int64(v.Level),
|
||||||
|
Short: v.Short,
|
||||||
|
Detail: v.Detail,
|
||||||
|
Url: v.URL,
|
||||||
|
Info: v.SourceInfo,
|
||||||
|
Ranges: v.Range,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return &resp
|
||||||
|
}
|
|
@ -0,0 +1,431 @@
|
||||||
|
package controller
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/docker/buildx/commands/controller/pb"
|
||||||
|
"github.com/moby/sys/signal"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"golang.org/x/sync/errgroup"
|
||||||
|
)
|
||||||
|
|
||||||
|
type msgStream interface {
|
||||||
|
Send(*pb.Message) error
|
||||||
|
Recv() (*pb.Message, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ioServerConfig struct {
|
||||||
|
stdin io.WriteCloser
|
||||||
|
stdout, stderr io.ReadCloser
|
||||||
|
|
||||||
|
// signalFn is a callback function called when a signal is reached to the client.
|
||||||
|
signalFn func(context.Context, syscall.Signal) error
|
||||||
|
|
||||||
|
// resizeFn is a callback function called when a resize event is reached to the client.
|
||||||
|
resizeFn func(context.Context, winSize) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func serveIO(attachCtx context.Context, srv msgStream, initFn func(*pb.InitMessage) error, ioConfig *ioServerConfig) (err error) {
|
||||||
|
stdin, stdout, stderr := ioConfig.stdin, ioConfig.stdout, ioConfig.stderr
|
||||||
|
stream := &debugStream{srv, "server=" + time.Now().String()}
|
||||||
|
eg, ctx := errgroup.WithContext(attachCtx)
|
||||||
|
done := make(chan struct{})
|
||||||
|
|
||||||
|
msg, err := receive(ctx, stream)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
init := msg.GetInit()
|
||||||
|
if init == nil {
|
||||||
|
return fmt.Errorf("unexpected message: %T; wanted init", msg.GetInput())
|
||||||
|
}
|
||||||
|
ref := init.Ref
|
||||||
|
if ref == "" {
|
||||||
|
return fmt.Errorf("no ref is provided")
|
||||||
|
}
|
||||||
|
if err := initFn(init); err != nil {
|
||||||
|
return fmt.Errorf("failed to initialize IO server: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if stdout != nil {
|
||||||
|
stdoutReader, stdoutWriter := io.Pipe()
|
||||||
|
eg.Go(func() error {
|
||||||
|
<-done
|
||||||
|
return stdoutWriter.Close()
|
||||||
|
})
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
// do not wait for read completion but return here and let the caller send EOF
|
||||||
|
// this allows us to return on ctx.Done() without being blocked by this reader.
|
||||||
|
io.Copy(stdoutWriter, stdout)
|
||||||
|
stdoutWriter.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
eg.Go(func() error {
|
||||||
|
defer stdoutReader.Close()
|
||||||
|
return copyToStream(1, stream, stdoutReader)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if stderr != nil {
|
||||||
|
stderrReader, stderrWriter := io.Pipe()
|
||||||
|
eg.Go(func() error {
|
||||||
|
<-done
|
||||||
|
return stderrWriter.Close()
|
||||||
|
})
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
// do not wait for read completion but return here and let the caller send EOF
|
||||||
|
// this allows us to return on ctx.Done() without being blocked by this reader.
|
||||||
|
io.Copy(stderrWriter, stderr)
|
||||||
|
stderrWriter.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
eg.Go(func() error {
|
||||||
|
defer stderrReader.Close()
|
||||||
|
return copyToStream(2, stream, stderrReader)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
msgCh := make(chan *pb.Message)
|
||||||
|
eg.Go(func() error {
|
||||||
|
defer close(msgCh)
|
||||||
|
for {
|
||||||
|
msg, err := receive(ctx, stream)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case msgCh <- msg:
|
||||||
|
case <-done:
|
||||||
|
return nil
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
eg.Go(func() error {
|
||||||
|
defer close(done)
|
||||||
|
for {
|
||||||
|
var msg *pb.Message
|
||||||
|
select {
|
||||||
|
case msg = <-msgCh:
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if msg == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if file := msg.GetFile(); file != nil {
|
||||||
|
if file.Fd != 0 {
|
||||||
|
return fmt.Errorf("unexpected fd: %v", file.Fd)
|
||||||
|
}
|
||||||
|
if stdin == nil {
|
||||||
|
continue // no stdin destination is specified so ignore the data
|
||||||
|
}
|
||||||
|
if len(file.Data) > 0 {
|
||||||
|
_, err := stdin.Write(file.Data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if file.EOF {
|
||||||
|
stdin.Close()
|
||||||
|
}
|
||||||
|
} else if resize := msg.GetResize(); resize != nil {
|
||||||
|
if ioConfig.resizeFn != nil {
|
||||||
|
ioConfig.resizeFn(ctx, winSize{
|
||||||
|
cols: resize.Cols,
|
||||||
|
rows: resize.Rows,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else if sig := msg.GetSignal(); sig != nil {
|
||||||
|
if ioConfig.signalFn != nil {
|
||||||
|
syscallSignal, ok := signal.SignalMap[sig.Name]
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ioConfig.signalFn(ctx, syscallSignal)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("unexpected message: %T", msg.GetInput())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return eg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
type ioAttachConfig struct {
|
||||||
|
stdin io.ReadCloser
|
||||||
|
stdout, stderr io.WriteCloser
|
||||||
|
signal <-chan syscall.Signal
|
||||||
|
resize <-chan winSize
|
||||||
|
}
|
||||||
|
|
||||||
|
type winSize struct {
|
||||||
|
rows uint32
|
||||||
|
cols uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func attachIO(ctx context.Context, stream msgStream, initMessage *pb.InitMessage, cfg ioAttachConfig) (retErr error) {
|
||||||
|
eg, ctx := errgroup.WithContext(ctx)
|
||||||
|
done := make(chan struct{})
|
||||||
|
|
||||||
|
if err := stream.Send(&pb.Message{
|
||||||
|
Input: &pb.Message_Init{
|
||||||
|
Init: initMessage,
|
||||||
|
},
|
||||||
|
}); err != nil {
|
||||||
|
return fmt.Errorf("failed to init: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.stdin != nil {
|
||||||
|
stdinReader, stdinWriter := io.Pipe()
|
||||||
|
eg.Go(func() error {
|
||||||
|
<-done
|
||||||
|
return stdinWriter.Close()
|
||||||
|
})
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
// do not wait for read completion but return here and let the caller send EOF
|
||||||
|
// this allows us to return on ctx.Done() without being blocked by this reader.
|
||||||
|
io.Copy(stdinWriter, cfg.stdin)
|
||||||
|
stdinWriter.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
eg.Go(func() error {
|
||||||
|
defer stdinReader.Close()
|
||||||
|
return copyToStream(0, stream, stdinReader)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.signal != nil {
|
||||||
|
eg.Go(func() error {
|
||||||
|
for {
|
||||||
|
var sig syscall.Signal
|
||||||
|
select {
|
||||||
|
case sig = <-cfg.signal:
|
||||||
|
case <-done:
|
||||||
|
return nil
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
name := sigToName[sig]
|
||||||
|
if name == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := stream.Send(&pb.Message{
|
||||||
|
Input: &pb.Message_Signal{
|
||||||
|
Signal: &pb.SignalMessage{
|
||||||
|
Name: name,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}); err != nil {
|
||||||
|
return fmt.Errorf("failed to send signal: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.resize != nil {
|
||||||
|
eg.Go(func() error {
|
||||||
|
for {
|
||||||
|
var win winSize
|
||||||
|
select {
|
||||||
|
case win = <-cfg.resize:
|
||||||
|
case <-done:
|
||||||
|
return nil
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err := stream.Send(&pb.Message{
|
||||||
|
Input: &pb.Message_Resize{
|
||||||
|
Resize: &pb.ResizeMessage{
|
||||||
|
Rows: win.rows,
|
||||||
|
Cols: win.cols,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}); err != nil {
|
||||||
|
return fmt.Errorf("failed to send resize: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
msgCh := make(chan *pb.Message)
|
||||||
|
eg.Go(func() error {
|
||||||
|
defer close(msgCh)
|
||||||
|
for {
|
||||||
|
msg, err := receive(ctx, stream)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case msgCh <- msg:
|
||||||
|
case <-done:
|
||||||
|
return nil
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
eg.Go(func() error {
|
||||||
|
eofs := make(map[uint32]struct{})
|
||||||
|
defer close(done)
|
||||||
|
for {
|
||||||
|
var msg *pb.Message
|
||||||
|
select {
|
||||||
|
case msg = <-msgCh:
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if msg == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if file := msg.GetFile(); file != nil {
|
||||||
|
if _, ok := eofs[file.Fd]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var out io.WriteCloser
|
||||||
|
switch file.Fd {
|
||||||
|
case 1:
|
||||||
|
out = cfg.stdout
|
||||||
|
case 2:
|
||||||
|
out = cfg.stderr
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unsupported fd %d", file.Fd)
|
||||||
|
|
||||||
|
}
|
||||||
|
if out == nil {
|
||||||
|
logrus.Warnf("attachIO: no writer for fd %d", file.Fd)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if len(file.Data) > 0 {
|
||||||
|
if _, err := out.Write(file.Data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if file.EOF {
|
||||||
|
eofs[file.Fd] = struct{}{}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("unexpected message: %T", msg.GetInput())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return eg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
func receive(ctx context.Context, stream msgStream) (*pb.Message, error) {
|
||||||
|
msgCh := make(chan *pb.Message)
|
||||||
|
errCh := make(chan error)
|
||||||
|
go func() {
|
||||||
|
msg, err := stream.Recv()
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, io.EOF) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
errCh <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
msgCh <- msg
|
||||||
|
}()
|
||||||
|
select {
|
||||||
|
case msg := <-msgCh:
|
||||||
|
return msg, nil
|
||||||
|
case err := <-errCh:
|
||||||
|
return nil, err
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, ctx.Err()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyToStream(fd uint32, snd msgStream, r io.Reader) error {
|
||||||
|
for {
|
||||||
|
buf := make([]byte, 32*1024)
|
||||||
|
n, err := r.Read(buf)
|
||||||
|
if err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
break // break loop and send EOF
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
} else if n > 0 {
|
||||||
|
if snd.Send(&pb.Message{
|
||||||
|
Input: &pb.Message_File{
|
||||||
|
File: &pb.FdMessage{
|
||||||
|
Fd: fd,
|
||||||
|
Data: buf[:n],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return snd.Send(&pb.Message{
|
||||||
|
Input: &pb.Message_File{
|
||||||
|
File: &pb.FdMessage{
|
||||||
|
Fd: fd,
|
||||||
|
EOF: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var sigToName = map[syscall.Signal]string{}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
for name, value := range signal.SignalMap {
|
||||||
|
sigToName[value] = name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type debugStream struct {
|
||||||
|
msgStream
|
||||||
|
prefix string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *debugStream) Send(msg *pb.Message) error {
|
||||||
|
switch m := msg.GetInput().(type) {
|
||||||
|
case *pb.Message_File:
|
||||||
|
if m.File.EOF {
|
||||||
|
logrus.Debugf("|---> File Message (sender:%v) fd=%d, EOF", s.prefix, m.File.Fd)
|
||||||
|
} else {
|
||||||
|
logrus.Debugf("|---> File Message (sender:%v) fd=%d, %d bytes", s.prefix, m.File.Fd, len(m.File.Data))
|
||||||
|
}
|
||||||
|
case *pb.Message_Resize:
|
||||||
|
logrus.Debugf("|---> Resize Message (sender:%v): %+v", s.prefix, m.Resize)
|
||||||
|
case *pb.Message_Signal:
|
||||||
|
logrus.Debugf("|---> Signal Message (sender:%v): %s", s.prefix, m.Signal.Name)
|
||||||
|
}
|
||||||
|
return s.msgStream.Send(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *debugStream) Recv() (*pb.Message, error) {
|
||||||
|
msg, err := s.msgStream.Recv()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
switch m := msg.GetInput().(type) {
|
||||||
|
case *pb.Message_File:
|
||||||
|
if m.File.EOF {
|
||||||
|
logrus.Debugf("|<--- File Message (receiver:%v) fd=%d, EOF", s.prefix, m.File.Fd)
|
||||||
|
} else {
|
||||||
|
logrus.Debugf("|<--- File Message (receiver:%v) fd=%d, %d bytes", s.prefix, m.File.Fd, len(m.File.Data))
|
||||||
|
}
|
||||||
|
case *pb.Message_Resize:
|
||||||
|
logrus.Debugf("|<--- Resize Message (receiver:%v): %+v", s.prefix, m.Resize)
|
||||||
|
case *pb.Message_Signal:
|
||||||
|
logrus.Debugf("|<--- Signal Message (receiver:%v): %s", s.prefix, m.Signal.Name)
|
||||||
|
}
|
||||||
|
return msg, nil
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,179 @@
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package buildx.controller.v1;
|
||||||
|
|
||||||
|
import "github.com/moby/buildkit/api/services/control/control.proto";
|
||||||
|
|
||||||
|
option go_package = "pb";
|
||||||
|
|
||||||
|
service Controller {
|
||||||
|
rpc Build(BuildRequest) returns (BuildResponse);
|
||||||
|
rpc Status(StatusRequest) returns (stream StatusResponse);
|
||||||
|
rpc Input(stream InputMessage) returns (InputResponse);
|
||||||
|
rpc Invoke(stream Message) returns (stream Message);
|
||||||
|
rpc List(ListRequest) returns (ListResponse);
|
||||||
|
rpc Disconnect(DisconnectRequest) returns (DisconnectResponse);
|
||||||
|
rpc Info(InfoRequest) returns (InfoResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
message BuildRequest {
|
||||||
|
string Ref = 1;
|
||||||
|
BuildOptions Options = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message BuildOptions {
|
||||||
|
string ContextPath = 1;
|
||||||
|
string DockerfileName = 2;
|
||||||
|
string PrintFunc = 3;
|
||||||
|
|
||||||
|
repeated string Allow = 4;
|
||||||
|
repeated string Attests = 5; // TODO
|
||||||
|
repeated string BuildArgs = 6;
|
||||||
|
repeated string CacheFrom = 7;
|
||||||
|
repeated string CacheTo = 8;
|
||||||
|
string CgroupParent = 9;
|
||||||
|
repeated string Contexts = 10;
|
||||||
|
repeated string ExtraHosts = 11;
|
||||||
|
string ImageIDFile = 12;
|
||||||
|
repeated string Labels = 13;
|
||||||
|
string NetworkMode = 14;
|
||||||
|
repeated string NoCacheFilter = 15;
|
||||||
|
repeated string Outputs = 16;
|
||||||
|
repeated string Platforms = 17;
|
||||||
|
bool Quiet = 18;
|
||||||
|
repeated string Secrets = 19;
|
||||||
|
int64 ShmSize = 20;
|
||||||
|
repeated string SSH = 21;
|
||||||
|
repeated string Tags = 22;
|
||||||
|
string Target = 23;
|
||||||
|
UlimitOpt Ulimits = 24;
|
||||||
|
// string Invoke: provided via Invoke API
|
||||||
|
CommonOptions Opts = 25;
|
||||||
|
}
|
||||||
|
|
||||||
|
message UlimitOpt {
|
||||||
|
map<string, Ulimit> values = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Ulimit {
|
||||||
|
string Name = 1;
|
||||||
|
int64 Hard = 2;
|
||||||
|
int64 Soft = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message CommonOptions {
|
||||||
|
string Builder = 1;
|
||||||
|
string MetadataFile = 2;
|
||||||
|
bool NoCache = 3;
|
||||||
|
// string Progress: no progress view on server side
|
||||||
|
bool Pull = 4;
|
||||||
|
bool ExportPush = 5;
|
||||||
|
bool ExportLoad = 6;
|
||||||
|
string SBOM = 7; // TODO
|
||||||
|
string Provenance = 8; // TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
message BuildResponse {}
|
||||||
|
|
||||||
|
message DisconnectRequest {
|
||||||
|
string Ref = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message DisconnectResponse {}
|
||||||
|
|
||||||
|
message ListRequest {
|
||||||
|
string Ref = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ListResponse {
|
||||||
|
repeated string keys = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message InputMessage {
|
||||||
|
oneof Input {
|
||||||
|
InputInitMessage Init = 1;
|
||||||
|
DataMessage Data = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message InputInitMessage {
|
||||||
|
string Ref = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message DataMessage {
|
||||||
|
bool EOF = 1; // true if eof was reached
|
||||||
|
bytes Data = 2; // should be chunked smaller than 4MB:
|
||||||
|
// https://pkg.go.dev/google.golang.org/grpc#MaxRecvMsgSize
|
||||||
|
}
|
||||||
|
|
||||||
|
message InputResponse {}
|
||||||
|
|
||||||
|
message Message {
|
||||||
|
oneof Input {
|
||||||
|
InitMessage Init = 1;
|
||||||
|
// FdMessage used from client to server for input (stdin) and
|
||||||
|
// from server to client for output (stdout, stderr)
|
||||||
|
FdMessage File = 2;
|
||||||
|
// ResizeMessage used from client to server for terminal resize events
|
||||||
|
ResizeMessage Resize = 3;
|
||||||
|
// SignalMessage is used from client to server to send signal events
|
||||||
|
SignalMessage Signal = 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message InitMessage {
|
||||||
|
string Ref = 1;
|
||||||
|
ContainerConfig ContainerConfig = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ContainerConfig {
|
||||||
|
repeated string Entrypoint = 1;
|
||||||
|
repeated string Cmd = 2;
|
||||||
|
repeated string Env = 3;
|
||||||
|
string User = 4;
|
||||||
|
bool NoUser = 5; // Do not set user but use the image's default
|
||||||
|
string Cwd = 6;
|
||||||
|
bool NoCwd = 7; // Do not set cwd but use the image's default
|
||||||
|
bool Tty = 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
message FdMessage {
|
||||||
|
uint32 Fd = 1; // what fd the data was from
|
||||||
|
bool EOF = 2; // true if eof was reached
|
||||||
|
bytes Data = 3; // should be chunked smaller than 4MB:
|
||||||
|
// https://pkg.go.dev/google.golang.org/grpc#MaxRecvMsgSize
|
||||||
|
}
|
||||||
|
|
||||||
|
message ResizeMessage {
|
||||||
|
uint32 Rows = 1;
|
||||||
|
uint32 Cols = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SignalMessage {
|
||||||
|
// we only send name (ie HUP, INT) because the int values
|
||||||
|
// are platform dependent.
|
||||||
|
string Name = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message StatusRequest {
|
||||||
|
string Ref = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message StatusResponse {
|
||||||
|
repeated moby.buildkit.v1.Vertex vertexes = 1;
|
||||||
|
repeated moby.buildkit.v1.VertexStatus statuses = 2;
|
||||||
|
repeated moby.buildkit.v1.VertexLog logs = 3;
|
||||||
|
repeated moby.buildkit.v1.VertexWarning warnings = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message InfoRequest {}
|
||||||
|
|
||||||
|
message InfoResponse {
|
||||||
|
BuildxVersion buildxVersion = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message BuildxVersion {
|
||||||
|
string package = 1;
|
||||||
|
string version = 2;
|
||||||
|
string revision = 3;
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
package pb
|
||||||
|
|
||||||
|
//go:generate protoc -I=. -I=../../../vendor/ --gogo_out=plugins=grpc:. controller.proto
|
|
@ -0,0 +1,78 @@
|
||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/containerd/console"
|
||||||
|
"github.com/docker/buildx/build"
|
||||||
|
controllerapi "github.com/docker/buildx/commands/controller/pb"
|
||||||
|
"github.com/docker/buildx/monitor"
|
||||||
|
"github.com/docker/cli/cli/command"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newLocalBuildxController(ctx context.Context, dockerCli command.Cli) monitor.BuildxController {
|
||||||
|
return &localController{
|
||||||
|
dockerCli: dockerCli,
|
||||||
|
ref: "local",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type localController struct {
|
||||||
|
dockerCli command.Cli
|
||||||
|
ref string
|
||||||
|
resultCtx *build.ResultContext
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *localController) Invoke(ctx context.Context, ref string, cfg controllerapi.ContainerConfig, ioIn io.ReadCloser, ioOut io.WriteCloser, ioErr io.WriteCloser) error {
|
||||||
|
if ref != b.ref {
|
||||||
|
return fmt.Errorf("unknown ref %q", ref)
|
||||||
|
}
|
||||||
|
if b.resultCtx == nil {
|
||||||
|
return fmt.Errorf("no build result is registered")
|
||||||
|
}
|
||||||
|
ccfg := build.ContainerConfig{
|
||||||
|
ResultCtx: b.resultCtx,
|
||||||
|
Entrypoint: cfg.Entrypoint,
|
||||||
|
Cmd: cfg.Cmd,
|
||||||
|
Env: cfg.Env,
|
||||||
|
Tty: cfg.Tty,
|
||||||
|
Stdin: ioIn,
|
||||||
|
Stdout: ioOut,
|
||||||
|
Stderr: ioErr,
|
||||||
|
}
|
||||||
|
if !cfg.NoUser {
|
||||||
|
ccfg.User = &cfg.User
|
||||||
|
}
|
||||||
|
if !cfg.NoCwd {
|
||||||
|
ccfg.Cwd = &cfg.Cwd
|
||||||
|
}
|
||||||
|
return build.Invoke(ctx, ccfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *localController) Build(ctx context.Context, options controllerapi.BuildOptions, in io.ReadCloser, w io.Writer, out console.File, progressMode string) (string, error) {
|
||||||
|
res, err := runBuildWithContext(ctx, b.dockerCli, options, in, progressMode, nil)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
b.resultCtx = res
|
||||||
|
return b.ref, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *localController) Kill(context.Context) error {
|
||||||
|
return nil // nop
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *localController) Close() error {
|
||||||
|
// TODO: cancel current build and invoke
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *localController) List(ctx context.Context) (res []string, _ error) {
|
||||||
|
return []string{b.ref}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *localController) Disconnect(ctx context.Context, key string) error {
|
||||||
|
return nil // nop
|
||||||
|
}
|
|
@ -0,0 +1,311 @@
|
||||||
|
//go:build linux
|
||||||
|
|
||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"os/signal"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/containerd/containerd/log"
|
||||||
|
"github.com/docker/buildx/build"
|
||||||
|
"github.com/docker/buildx/commands/controller"
|
||||||
|
controllerapi "github.com/docker/buildx/commands/controller/pb"
|
||||||
|
"github.com/docker/buildx/monitor"
|
||||||
|
"github.com/docker/buildx/util/confutil"
|
||||||
|
"github.com/docker/buildx/version"
|
||||||
|
"github.com/docker/cli/cli/command"
|
||||||
|
"github.com/moby/buildkit/client"
|
||||||
|
"github.com/pelletier/go-toml"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
serveCommandName = "_INTERNAL_SERVE"
|
||||||
|
)
|
||||||
|
|
||||||
|
type serverConfig struct {
|
||||||
|
// Specify buildx server root
|
||||||
|
Root string `toml:"root"`
|
||||||
|
|
||||||
|
// LogLevel sets the logging level [trace, debug, info, warn, error, fatal, panic]
|
||||||
|
LogLevel string `toml:"log_level"`
|
||||||
|
|
||||||
|
// Specify file to output buildx server log
|
||||||
|
LogFile string `toml:"log_file"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func newRemoteBuildxController(ctx context.Context, dockerCli command.Cli, opts buildOptions) (monitor.BuildxController, error) {
|
||||||
|
rootDir := opts.root
|
||||||
|
if rootDir == "" {
|
||||||
|
rootDir = rootDataDir(dockerCli)
|
||||||
|
}
|
||||||
|
serverRoot := filepath.Join(rootDir, "shared")
|
||||||
|
c, err := newBuildxClientAndCheck(filepath.Join(serverRoot, "buildx.sock"), 1, 0)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Info("no buildx server found; launching...")
|
||||||
|
// start buildx server via subcommand
|
||||||
|
launchFlags := []string{}
|
||||||
|
if opts.serverConfig != "" {
|
||||||
|
launchFlags = append(launchFlags, "--config", opts.serverConfig)
|
||||||
|
}
|
||||||
|
logFile, err := getLogFilePath(dockerCli, opts.serverConfig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
wait, err := launch(ctx, logFile, append([]string{serveCommandName}, launchFlags...)...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
go wait()
|
||||||
|
c, err = newBuildxClientAndCheck(filepath.Join(serverRoot, "buildx.sock"), 10, time.Second)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("cannot connect to the buildx server: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &buildxController{c, serverRoot}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func addControllerCommands(cmd *cobra.Command, dockerCli command.Cli, rootOpts *rootOptions) {
|
||||||
|
cmd.AddCommand(
|
||||||
|
serveCmd(dockerCli, rootOpts),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func serveCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
|
||||||
|
var serverConfigPath string
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: fmt.Sprintf("%s [OPTIONS]", serveCommandName),
|
||||||
|
Hidden: true,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
// Parse config
|
||||||
|
config, err := getConfig(dockerCli, serverConfigPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get config")
|
||||||
|
}
|
||||||
|
if config.LogLevel == "" {
|
||||||
|
logrus.SetLevel(logrus.InfoLevel)
|
||||||
|
} else {
|
||||||
|
lvl, err := logrus.ParseLevel(config.LogLevel)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to prepare logger: %w", err)
|
||||||
|
}
|
||||||
|
logrus.SetLevel(lvl)
|
||||||
|
}
|
||||||
|
logrus.SetFormatter(&logrus.JSONFormatter{
|
||||||
|
TimestampFormat: log.RFC3339NanoFixed,
|
||||||
|
})
|
||||||
|
root, err := prepareRootDir(dockerCli, config)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pidF := filepath.Join(root, "pid")
|
||||||
|
if err := os.WriteFile(pidF, []byte(fmt.Sprintf("%d", os.Getpid())), 0600); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err := os.Remove(pidF); err != nil {
|
||||||
|
logrus.Errorf("failed to clean up info file %q: %v", pidF, err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// prepare server
|
||||||
|
b := controller.New(func(ctx context.Context, options *controllerapi.BuildOptions, stdin io.Reader, statusChan chan *client.SolveStatus) (res *build.ResultContext, err error) {
|
||||||
|
return runBuildWithContext(ctx, dockerCli, *options, stdin, "quiet", statusChan)
|
||||||
|
})
|
||||||
|
defer b.Close()
|
||||||
|
|
||||||
|
// serve server
|
||||||
|
addr := filepath.Join(root, "buildx.sock")
|
||||||
|
if err := os.Remove(addr); err != nil && !os.IsNotExist(err) { // avoid EADDRINUSE
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err := os.Remove(addr); err != nil {
|
||||||
|
logrus.Errorf("failed to clean up socket %q: %v", addr, err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
logrus.Infof("starting server at %q", addr)
|
||||||
|
l, err := net.Listen("unix", addr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
rpc := grpc.NewServer()
|
||||||
|
controllerapi.RegisterControllerServer(rpc, b)
|
||||||
|
doneCh := make(chan struct{})
|
||||||
|
errCh := make(chan error, 1)
|
||||||
|
go func() {
|
||||||
|
defer close(doneCh)
|
||||||
|
if err := rpc.Serve(l); err != nil {
|
||||||
|
errCh <- fmt.Errorf("error on serving via socket %q: %w", addr, err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}()
|
||||||
|
var s os.Signal
|
||||||
|
sigCh := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(sigCh, os.Interrupt)
|
||||||
|
select {
|
||||||
|
case s = <-sigCh:
|
||||||
|
logrus.Debugf("got signal %v", s)
|
||||||
|
case err := <-errCh:
|
||||||
|
return err
|
||||||
|
case <-doneCh:
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
flags := cmd.Flags()
|
||||||
|
flags.StringVar(&serverConfigPath, "config", "", "Specify buildx server config file")
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func getLogFilePath(dockerCli command.Cli, configPath string) (string, error) {
|
||||||
|
config, err := getConfig(dockerCli, configPath)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to get config")
|
||||||
|
}
|
||||||
|
logFile := config.LogFile
|
||||||
|
if logFile == "" {
|
||||||
|
root, err := prepareRootDir(dockerCli, config)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
logFile = filepath.Join(root, "log")
|
||||||
|
}
|
||||||
|
return logFile, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getConfig(dockerCli command.Cli, configPath string) (*serverConfig, error) {
|
||||||
|
var defaultConfigPath bool
|
||||||
|
if configPath == "" {
|
||||||
|
defaultRoot := rootDataDir(dockerCli)
|
||||||
|
configPath = filepath.Join(defaultRoot, "config.toml")
|
||||||
|
defaultConfigPath = true
|
||||||
|
}
|
||||||
|
var config serverConfig
|
||||||
|
tree, err := toml.LoadFile(configPath)
|
||||||
|
if err != nil && !(os.IsNotExist(err) && defaultConfigPath) {
|
||||||
|
return nil, fmt.Errorf("failed to load config file %q", configPath)
|
||||||
|
} else if err == nil {
|
||||||
|
if err := tree.Unmarshal(&config); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to unmarshal config file %q", configPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &config, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func prepareRootDir(dockerCli command.Cli, config *serverConfig) (string, error) {
|
||||||
|
rootDir := config.Root
|
||||||
|
if rootDir == "" {
|
||||||
|
rootDir = rootDataDir(dockerCli)
|
||||||
|
}
|
||||||
|
if rootDir == "" {
|
||||||
|
return "", fmt.Errorf("buildx root dir must be determined")
|
||||||
|
}
|
||||||
|
if err := os.MkdirAll(rootDir, 0700); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
serverRoot := filepath.Join(rootDir, "shared")
|
||||||
|
if err := os.MkdirAll(serverRoot, 0700); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return serverRoot, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func rootDataDir(dockerCli command.Cli) string {
|
||||||
|
return filepath.Join(confutil.ConfigDir(dockerCli), "controller")
|
||||||
|
}
|
||||||
|
|
||||||
|
func newBuildxClientAndCheck(addr string, checkNum int, duration time.Duration) (*controller.Client, error) {
|
||||||
|
c, err := controller.NewClient(addr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var lastErr error
|
||||||
|
for i := 0; i < checkNum; i++ {
|
||||||
|
_, err := c.List(context.TODO())
|
||||||
|
if err == nil {
|
||||||
|
lastErr = nil
|
||||||
|
break
|
||||||
|
}
|
||||||
|
err = fmt.Errorf("failed to access server (tried %d times): %w", i, err)
|
||||||
|
logrus.Debugf("connection failure: %v", err)
|
||||||
|
lastErr = err
|
||||||
|
time.Sleep(duration)
|
||||||
|
}
|
||||||
|
if lastErr != nil {
|
||||||
|
return nil, lastErr
|
||||||
|
}
|
||||||
|
p, v, r, err := c.Version(context.TODO())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
logrus.Debugf("connected to server (\"%v %v %v\")", p, v, r)
|
||||||
|
if !(p == version.Package && v == version.Version && r == version.Revision) {
|
||||||
|
logrus.Warnf("version mismatch (server: \"%v %v %v\", client: \"%v %v %v\"); please kill and restart buildx server",
|
||||||
|
p, v, r, version.Package, version.Version, version.Revision)
|
||||||
|
}
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type buildxController struct {
|
||||||
|
*controller.Client
|
||||||
|
serverRoot string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *buildxController) Kill(ctx context.Context) error {
|
||||||
|
pidB, err := os.ReadFile(filepath.Join(c.serverRoot, "pid"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pid, err := strconv.ParseInt(string(pidB), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if pid <= 0 {
|
||||||
|
return fmt.Errorf("no PID is recorded for buildx server")
|
||||||
|
}
|
||||||
|
p, err := os.FindProcess(int(pid))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := p.Signal(syscall.SIGINT); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// TODO: Should we send SIGKILL if process doesn't finish?
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func launch(ctx context.Context, logFile string, args ...string) (func() error, error) {
|
||||||
|
bCmd := exec.CommandContext(ctx, os.Args[0], args...)
|
||||||
|
if logFile != "" {
|
||||||
|
f, err := os.OpenFile(logFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
bCmd.Stdout = f
|
||||||
|
bCmd.Stderr = f
|
||||||
|
}
|
||||||
|
bCmd.Stdin = nil
|
||||||
|
bCmd.Dir = "/"
|
||||||
|
bCmd.SysProcAttr = &syscall.SysProcAttr{
|
||||||
|
Setsid: true,
|
||||||
|
}
|
||||||
|
if err := bCmd.Start(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return bCmd.Wait, nil
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
//go:build !linux
|
||||||
|
|
||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/docker/buildx/monitor"
|
||||||
|
"github.com/docker/cli/cli/command"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newRemoteBuildxController(ctx context.Context, dockerCli command.Cli, opts buildOptions) (monitor.BuildxController, error) {
|
||||||
|
return nil, fmt.Errorf("remote buildx unsupported")
|
||||||
|
}
|
||||||
|
|
||||||
|
func addControllerCommands(cmd *cobra.Command, dockerCli command.Cli, rootOpts *rootOptions) {}
|
|
@ -86,6 +86,9 @@ func addCommands(cmd *cobra.Command, dockerCli command.Cli) {
|
||||||
duCmd(dockerCli, opts),
|
duCmd(dockerCli, opts),
|
||||||
imagetoolscmd.RootCmd(dockerCli, imagetoolscmd.RootOptions{Builder: &opts.builder}),
|
imagetoolscmd.RootCmd(dockerCli, imagetoolscmd.RootOptions{Builder: &opts.builder}),
|
||||||
)
|
)
|
||||||
|
if isExperimental() {
|
||||||
|
addControllerCommands(cmd, dockerCli, opts)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func rootFlags(options *rootOptions, flags *pflag.FlagSet) {
|
func rootFlags(options *rootOptions, flags *pflag.FlagSet) {
|
||||||
|
|
|
@ -59,6 +59,13 @@ target "validate-authors" {
|
||||||
output = ["type=cacheonly"]
|
output = ["type=cacheonly"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
target "validate-generated-files" {
|
||||||
|
inherits = ["_common"]
|
||||||
|
dockerfile = "./hack/dockerfiles/generated-files.Dockerfile"
|
||||||
|
target = "validate"
|
||||||
|
output = ["type=cacheonly"]
|
||||||
|
}
|
||||||
|
|
||||||
target "update-vendor" {
|
target "update-vendor" {
|
||||||
inherits = ["_common"]
|
inherits = ["_common"]
|
||||||
dockerfile = "./hack/dockerfiles/vendor.Dockerfile"
|
dockerfile = "./hack/dockerfiles/vendor.Dockerfile"
|
||||||
|
@ -84,6 +91,13 @@ target "update-authors" {
|
||||||
output = ["."]
|
output = ["."]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
target "update-generated-files" {
|
||||||
|
inherits = ["_common"]
|
||||||
|
dockerfile = "./hack/dockerfiles/generated-files.Dockerfile"
|
||||||
|
target = "update"
|
||||||
|
output = ["."]
|
||||||
|
}
|
||||||
|
|
||||||
target "mod-outdated" {
|
target "mod-outdated" {
|
||||||
inherits = ["_common"]
|
inherits = ["_common"]
|
||||||
dockerfile = "./hack/dockerfiles/vendor.Dockerfile"
|
dockerfile = "./hack/dockerfiles/vendor.Dockerfile"
|
||||||
|
|
|
@ -9,22 +9,23 @@ Extended build capabilities with BuildKit
|
||||||
|
|
||||||
### Subcommands
|
### Subcommands
|
||||||
|
|
||||||
| Name | Description |
|
| Name | Description |
|
||||||
|:-------------------------------------|:-------------------------------------------|
|
|:-----------------------------------------------|:-------------------------------------------|
|
||||||
| [`bake`](buildx_bake.md) | Build from a file |
|
| [`_INTERNAL_SERVE`](buildx__INTERNAL_SERVE.md) | |
|
||||||
| [`build`](buildx_build.md) | Start a build |
|
| [`bake`](buildx_bake.md) | Build from a file |
|
||||||
| [`create`](buildx_create.md) | Create a new builder instance |
|
| [`build`](buildx_build.md) | Start a build |
|
||||||
| [`du`](buildx_du.md) | Disk usage |
|
| [`create`](buildx_create.md) | Create a new builder instance |
|
||||||
| [`imagetools`](buildx_imagetools.md) | Commands to work on images in registry |
|
| [`du`](buildx_du.md) | Disk usage |
|
||||||
| [`inspect`](buildx_inspect.md) | Inspect current builder instance |
|
| [`imagetools`](buildx_imagetools.md) | Commands to work on images in registry |
|
||||||
| [`install`](buildx_install.md) | Install buildx as a 'docker builder' alias |
|
| [`inspect`](buildx_inspect.md) | Inspect current builder instance |
|
||||||
| [`ls`](buildx_ls.md) | List builder instances |
|
| [`install`](buildx_install.md) | Install buildx as a 'docker builder' alias |
|
||||||
| [`prune`](buildx_prune.md) | Remove build cache |
|
| [`ls`](buildx_ls.md) | List builder instances |
|
||||||
| [`rm`](buildx_rm.md) | Remove a builder instance |
|
| [`prune`](buildx_prune.md) | Remove build cache |
|
||||||
| [`stop`](buildx_stop.md) | Stop builder instance |
|
| [`rm`](buildx_rm.md) | Remove a builder instance |
|
||||||
| [`uninstall`](buildx_uninstall.md) | Uninstall the 'docker builder' alias |
|
| [`stop`](buildx_stop.md) | Stop builder instance |
|
||||||
| [`use`](buildx_use.md) | Set the current builder instance |
|
| [`uninstall`](buildx_uninstall.md) | Uninstall the 'docker builder' alias |
|
||||||
| [`version`](buildx_version.md) | Show buildx version information |
|
| [`use`](buildx_use.md) | Set the current builder instance |
|
||||||
|
| [`version`](buildx_version.md) | Show buildx version information |
|
||||||
|
|
||||||
|
|
||||||
### Options
|
### Options
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
# docker buildx _INTERNAL_SERVE
|
||||||
|
|
||||||
|
<!---MARKER_GEN_START-->
|
||||||
|
### Options
|
||||||
|
|
||||||
|
| Name | Type | Default | Description |
|
||||||
|
|:------------|:---------|:--------|:-----------------------------------------|
|
||||||
|
| `--builder` | `string` | | Override the configured builder instance |
|
||||||
|
| `--config` | `string` | | Specify buildx server config file |
|
||||||
|
|
||||||
|
|
||||||
|
<!---MARKER_GEN_END-->
|
||||||
|
|
|
@ -24,6 +24,7 @@ Start a build
|
||||||
| [`--cache-from`](#cache-from) | `stringArray` | | External cache sources (e.g., `user/app:cache`, `type=local,src=path/to/dir`) |
|
| [`--cache-from`](#cache-from) | `stringArray` | | External cache sources (e.g., `user/app:cache`, `type=local,src=path/to/dir`) |
|
||||||
| [`--cache-to`](#cache-to) | `stringArray` | | Cache export destinations (e.g., `user/app:cache`, `type=local,dest=path/to/dir`) |
|
| [`--cache-to`](#cache-to) | `stringArray` | | Cache export destinations (e.g., `user/app:cache`, `type=local,dest=path/to/dir`) |
|
||||||
| [`--cgroup-parent`](https://docs.docker.com/engine/reference/commandline/build/#cgroup-parent) | `string` | | Optional parent cgroup for the container |
|
| [`--cgroup-parent`](https://docs.docker.com/engine/reference/commandline/build/#cgroup-parent) | `string` | | Optional parent cgroup for the container |
|
||||||
|
| `--detach` | | | Detach buildx server (supported only on linux) [experimental] |
|
||||||
| [`-f`](https://docs.docker.com/engine/reference/commandline/build/#file), [`--file`](https://docs.docker.com/engine/reference/commandline/build/#file) | `string` | | Name of the Dockerfile (default: `PATH/Dockerfile`) |
|
| [`-f`](https://docs.docker.com/engine/reference/commandline/build/#file), [`--file`](https://docs.docker.com/engine/reference/commandline/build/#file) | `string` | | Name of the Dockerfile (default: `PATH/Dockerfile`) |
|
||||||
| `--iidfile` | `string` | | Write the image ID to the file |
|
| `--iidfile` | `string` | | Write the image ID to the file |
|
||||||
| `--invoke` | `string` | | Invoke a command after the build [experimental] |
|
| `--invoke` | `string` | | Invoke a command after the build [experimental] |
|
||||||
|
@ -41,8 +42,10 @@ Start a build
|
||||||
| `--pull` | | | Always attempt to pull all referenced images |
|
| `--pull` | | | Always attempt to pull all referenced images |
|
||||||
| [`--push`](#push) | | | Shorthand for `--output=type=registry` |
|
| [`--push`](#push) | | | Shorthand for `--output=type=registry` |
|
||||||
| `-q`, `--quiet` | | | Suppress the build output and print image ID on success |
|
| `-q`, `--quiet` | | | Suppress the build output and print image ID on success |
|
||||||
|
| `--root` | `string` | | Specify root directory of server to connect [experimental] |
|
||||||
| [`--sbom`](#sbom) | `string` | | Shorthand for `--attest=type=sbom` |
|
| [`--sbom`](#sbom) | `string` | | Shorthand for `--attest=type=sbom` |
|
||||||
| [`--secret`](#secret) | `stringArray` | | Secret to expose to the build (format: `id=mysecret[,src=/local/secret]`) |
|
| [`--secret`](#secret) | `stringArray` | | Secret to expose to the build (format: `id=mysecret[,src=/local/secret]`) |
|
||||||
|
| `--server-config` | `string` | | Specify buildx server config file (used only when launching new server) [experimental] |
|
||||||
| [`--shm-size`](#shm-size) | `bytes` | `0` | Size of `/dev/shm` |
|
| [`--shm-size`](#shm-size) | `bytes` | `0` | Size of `/dev/shm` |
|
||||||
| [`--ssh`](#ssh) | `stringArray` | | SSH agent socket or keys to expose to the build (format: `default\|<id>[=<socket>\|<key>[,<key>]]`) |
|
| [`--ssh`](#ssh) | `stringArray` | | SSH agent socket or keys to expose to the build (format: `default\|<id>[=<socket>\|<key>[,<key>]]`) |
|
||||||
| [`-t`](https://docs.docker.com/engine/reference/commandline/build/#tag), [`--tag`](https://docs.docker.com/engine/reference/commandline/build/#tag) | `stringArray` | | Name and optionally a tag (format: `name:tag`) |
|
| [`-t`](https://docs.docker.com/engine/reference/commandline/build/#tag), [`--tag`](https://docs.docker.com/engine/reference/commandline/build/#tag) | `stringArray` | | Name and optionally a tag (format: `name:tag`) |
|
||||||
|
|
6
go.mod
6
go.mod
|
@ -13,11 +13,14 @@ require (
|
||||||
github.com/docker/docker v23.0.0-rc.1+incompatible
|
github.com/docker/docker v23.0.0-rc.1+incompatible
|
||||||
github.com/docker/go-units v0.5.0
|
github.com/docker/go-units v0.5.0
|
||||||
github.com/gofrs/flock v0.8.1
|
github.com/gofrs/flock v0.8.1
|
||||||
|
github.com/gogo/protobuf v1.3.2
|
||||||
|
github.com/golang/protobuf v1.5.2
|
||||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
||||||
github.com/hashicorp/go-cty-funcs v0.0.0-20200930094925-2721b1e36840
|
github.com/hashicorp/go-cty-funcs v0.0.0-20200930094925-2721b1e36840
|
||||||
github.com/hashicorp/hcl/v2 v2.8.2
|
github.com/hashicorp/hcl/v2 v2.8.2
|
||||||
github.com/moby/buildkit v0.11.1
|
github.com/moby/buildkit v0.11.1
|
||||||
github.com/moby/sys/mountinfo v0.6.2
|
github.com/moby/sys/mountinfo v0.6.2
|
||||||
|
github.com/moby/sys/signal v0.7.0
|
||||||
github.com/morikuni/aec v1.0.0
|
github.com/morikuni/aec v1.0.0
|
||||||
github.com/opencontainers/go-digest v1.0.0
|
github.com/opencontainers/go-digest v1.0.0
|
||||||
github.com/opencontainers/image-spec v1.0.3-0.20220303224323-02efb9a75ee1
|
github.com/opencontainers/image-spec v1.0.3-0.20220303224323-02efb9a75ee1
|
||||||
|
@ -90,9 +93,7 @@ require (
|
||||||
github.com/go-logr/stdr v1.2.2 // indirect
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
github.com/go-sql-driver/mysql v1.6.0 // indirect
|
github.com/go-sql-driver/mysql v1.6.0 // indirect
|
||||||
github.com/gogo/googleapis v1.4.1 // indirect
|
github.com/gogo/googleapis v1.4.1 // indirect
|
||||||
github.com/gogo/protobuf v1.3.2 // indirect
|
|
||||||
github.com/golang-jwt/jwt/v4 v4.4.2 // indirect
|
github.com/golang-jwt/jwt/v4 v4.4.2 // indirect
|
||||||
github.com/golang/protobuf v1.5.2 // indirect
|
|
||||||
github.com/google/certificate-transparency-go v1.0.21 // indirect
|
github.com/google/certificate-transparency-go v1.0.21 // indirect
|
||||||
github.com/google/go-cmp v0.5.9 // indirect
|
github.com/google/go-cmp v0.5.9 // indirect
|
||||||
github.com/google/gofuzz v1.2.0 // indirect
|
github.com/google/gofuzz v1.2.0 // indirect
|
||||||
|
@ -119,7 +120,6 @@ require (
|
||||||
github.com/moby/patternmatcher v0.5.0 // indirect
|
github.com/moby/patternmatcher v0.5.0 // indirect
|
||||||
github.com/moby/spdystream v0.2.0 // indirect
|
github.com/moby/spdystream v0.2.0 // indirect
|
||||||
github.com/moby/sys/sequential v0.5.0 // indirect
|
github.com/moby/sys/sequential v0.5.0 // indirect
|
||||||
github.com/moby/sys/signal v0.7.0 // indirect
|
|
||||||
github.com/moby/term v0.0.0-20221120202655-abb19827d345 // indirect
|
github.com/moby/term v0.0.0-20221120202655-abb19827d345 // indirect
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
# Forked from https://github.com/moby/buildkit/blob/e1b3b6c4abf7684f13e6391e5f7bc9210752687a/hack/dockerfiles/generated-files.Dockerfile
|
||||||
|
# Copyright The BuildKit Authors.
|
||||||
|
# Copyright The Buildx Authors.
|
||||||
|
# Licensed under the Apache License, Version 2.0
|
||||||
|
|
||||||
|
# syntax=docker/dockerfile-upstream:master
|
||||||
|
|
||||||
|
ARG GO_VERSION="1.19"
|
||||||
|
ARG PROTOC_VERSION="3.11.4"
|
||||||
|
|
||||||
|
# protoc is dynamically linked to glibc so can't use alpine base
|
||||||
|
FROM golang:${GO_VERSION}-buster AS base
|
||||||
|
RUN apt-get update && apt-get --no-install-recommends install -y git unzip
|
||||||
|
ARG PROTOC_VERSION
|
||||||
|
ARG TARGETOS
|
||||||
|
ARG TARGETARCH
|
||||||
|
RUN <<EOT
|
||||||
|
set -e
|
||||||
|
arch=$(echo $TARGETARCH | sed -e s/amd64/x86_64/ -e s/arm64/aarch_64/)
|
||||||
|
wget -q https://github.com/protocolbuffers/protobuf/releases/download/v${PROTOC_VERSION}/protoc-${PROTOC_VERSION}-${TARGETOS}-${arch}.zip
|
||||||
|
unzip protoc-${PROTOC_VERSION}-${TARGETOS}-${arch}.zip -d /usr/local
|
||||||
|
EOT
|
||||||
|
WORKDIR /go/src/github.com/docker/buildx
|
||||||
|
|
||||||
|
FROM base AS tools
|
||||||
|
RUN --mount=type=bind,target=.,rw \
|
||||||
|
--mount=type=cache,target=/root/.cache \
|
||||||
|
--mount=type=cache,target=/go/pkg/mod \
|
||||||
|
go install \
|
||||||
|
github.com/gogo/protobuf/protoc-gen-gogo \
|
||||||
|
github.com/gogo/protobuf/protoc-gen-gogofaster \
|
||||||
|
github.com/gogo/protobuf/protoc-gen-gogoslick \
|
||||||
|
github.com/golang/protobuf/protoc-gen-go
|
||||||
|
|
||||||
|
FROM tools AS generated
|
||||||
|
RUN --mount=type=bind,target=.,rw <<EOT
|
||||||
|
set -ex
|
||||||
|
go generate -mod=vendor -v ./...
|
||||||
|
mkdir /out
|
||||||
|
git ls-files -m --others -- ':!vendor' '**/*.pb.go' | tar -cf - --files-from - | tar -C /out -xf -
|
||||||
|
EOT
|
||||||
|
|
||||||
|
FROM scratch AS update
|
||||||
|
COPY --from=generated /out /
|
||||||
|
|
||||||
|
FROM base AS validate
|
||||||
|
RUN --mount=type=bind,target=.,rw \
|
||||||
|
--mount=type=bind,from=generated,source=/out,target=/generated-files <<EOT
|
||||||
|
set -e
|
||||||
|
git add -A
|
||||||
|
if [ "$(ls -A /generated-files)" ]; then
|
||||||
|
cp -rf /generated-files/* .
|
||||||
|
fi
|
||||||
|
diff=$(git status --porcelain -- ':!vendor' '**/*.pb.go')
|
||||||
|
if [ -n "$diff" ]; then
|
||||||
|
echo >&2 'ERROR: The result of "go generate" differs. Please update with "make generated-files"'
|
||||||
|
echo "$diff"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
EOT
|
|
@ -1,66 +1,103 @@
|
||||||
package monitor
|
package monitor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"text/tabwriter"
|
||||||
|
|
||||||
"github.com/docker/buildx/build"
|
"github.com/containerd/console"
|
||||||
"github.com/pkg/errors"
|
controllerapi "github.com/docker/buildx/commands/controller/pb"
|
||||||
|
"github.com/docker/buildx/util/ioset"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"golang.org/x/term"
|
"golang.org/x/term"
|
||||||
)
|
)
|
||||||
|
|
||||||
const helpMessage = `
|
const helpMessage = `
|
||||||
Available commads are:
|
Available commads are:
|
||||||
reload reloads the context and build it.
|
reload reloads the context and build it.
|
||||||
rollback re-runs the interactive container with initial rootfs contents.
|
rollback re-runs the interactive container with initial rootfs contents.
|
||||||
exit exits monitor.
|
list list buildx sessions.
|
||||||
help shows this message.
|
attach attach to a buildx server.
|
||||||
|
disconnect disconnect a client from a buildx server. Specific session ID can be specified an arg.
|
||||||
|
kill kill buildx server.
|
||||||
|
exit exits monitor.
|
||||||
|
help shows this message.
|
||||||
`
|
`
|
||||||
|
|
||||||
|
type BuildxController interface {
|
||||||
|
Invoke(ctx context.Context, ref string, options controllerapi.ContainerConfig, ioIn io.ReadCloser, ioOut io.WriteCloser, ioErr io.WriteCloser) error
|
||||||
|
Build(ctx context.Context, options controllerapi.BuildOptions, in io.ReadCloser, w io.Writer, out console.File, progressMode string) (ref string, err error)
|
||||||
|
Kill(ctx context.Context) error
|
||||||
|
Close() error
|
||||||
|
List(ctx context.Context) (res []string, _ error)
|
||||||
|
Disconnect(ctx context.Context, ref string) error
|
||||||
|
}
|
||||||
|
|
||||||
// RunMonitor provides an interactive session for running and managing containers via specified IO.
|
// RunMonitor provides an interactive session for running and managing containers via specified IO.
|
||||||
func RunMonitor(ctx context.Context, containerConfig build.ContainerConfig, reloadFunc func(context.Context) (*build.ResultContext, error), stdin io.ReadCloser, stdout, stderr io.WriteCloser) error {
|
func RunMonitor(ctx context.Context, curRef string, options controllerapi.BuildOptions, invokeConfig controllerapi.ContainerConfig, c BuildxController, progressMode string, stdin io.ReadCloser, stdout io.WriteCloser, stderr console.File) error {
|
||||||
monitorIn, monitorOut := ioSetPipe()
|
defer func() {
|
||||||
defer monitorIn.Close()
|
if err := c.Disconnect(ctx, curRef); err != nil {
|
||||||
|
logrus.Warnf("disconnect error: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
monitorIn, monitorOut := ioset.Pipe()
|
||||||
|
defer func() {
|
||||||
|
monitorIn.Close()
|
||||||
|
}()
|
||||||
monitorEnableCh := make(chan struct{})
|
monitorEnableCh := make(chan struct{})
|
||||||
monitorDisableCh := make(chan struct{})
|
monitorDisableCh := make(chan struct{})
|
||||||
monitorOutCtx := ioSetOutContext{monitorOut,
|
monitorOutCtx := ioset.MuxOut{
|
||||||
func() { monitorEnableCh <- struct{}{} },
|
Out: monitorOut,
|
||||||
func() { monitorDisableCh <- struct{}{} },
|
EnableHook: func() { monitorEnableCh <- struct{}{} },
|
||||||
|
DisableHook: func() { monitorDisableCh <- struct{}{} },
|
||||||
}
|
}
|
||||||
|
|
||||||
containerIn, containerOut := ioSetPipe()
|
containerIn, containerOut := ioset.Pipe()
|
||||||
defer containerIn.Close()
|
defer func() {
|
||||||
containerOutCtx := ioSetOutContext{containerOut,
|
containerIn.Close()
|
||||||
|
}()
|
||||||
|
containerOutCtx := ioset.MuxOut{
|
||||||
|
Out: containerOut,
|
||||||
// send newline to hopefully get the prompt; TODO: better UI (e.g. reprinting the last line)
|
// send newline to hopefully get the prompt; TODO: better UI (e.g. reprinting the last line)
|
||||||
func() { containerOut.stdin.Write([]byte("\n")) },
|
EnableHook: func() { containerOut.Stdin.Write([]byte("\n")) },
|
||||||
func() {},
|
DisableHook: func() {},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
invokeForwarder := ioset.NewForwarder()
|
||||||
|
invokeForwarder.SetIn(&containerIn)
|
||||||
m := &monitor{
|
m := &monitor{
|
||||||
invokeIO: newIOForwarder(containerIn),
|
invokeIO: invokeForwarder,
|
||||||
muxIO: newMuxIO(ioSetIn{stdin, stdout, stderr}, []ioSetOutContext{monitorOutCtx, containerOutCtx}, 1, func(prev int, res int) string {
|
muxIO: ioset.NewMuxIO(ioset.In{
|
||||||
|
Stdin: io.NopCloser(stdin),
|
||||||
|
Stdout: nopCloser{stdout},
|
||||||
|
Stderr: nopCloser{stderr},
|
||||||
|
}, []ioset.MuxOut{monitorOutCtx, containerOutCtx}, 1, func(prev int, res int) string {
|
||||||
if prev == 0 && res == 0 {
|
if prev == 0 && res == 0 {
|
||||||
// No toggle happened because container I/O isn't enabled.
|
// No toggle happened because container I/O isn't enabled.
|
||||||
return "No running interactive containers. You can start one by issuing rollback command\n"
|
return "No running interactive containers. You can start one by issuing rollback command\n"
|
||||||
}
|
}
|
||||||
return "Switched IO\n"
|
return "Switched IO\n"
|
||||||
}),
|
}),
|
||||||
|
invokeFunc: func(ctx context.Context, ref string, in io.ReadCloser, out io.WriteCloser, err io.WriteCloser) error {
|
||||||
|
return c.Invoke(ctx, ref, invokeConfig, in, out, err)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start container automatically
|
// Start container automatically
|
||||||
fmt.Fprintf(stdout, "Launching interactive container. Press Ctrl-a-c to switch to monitor console\n")
|
fmt.Fprintf(stdout, "Launching interactive container. Press Ctrl-a-c to switch to monitor console\n")
|
||||||
m.rollback(ctx, containerConfig)
|
m.rollback(ctx, curRef)
|
||||||
|
|
||||||
// Serve monitor commands
|
// Serve monitor commands
|
||||||
monitorForwarder := newIOForwarder(monitorIn)
|
monitorForwarder := ioset.NewForwarder()
|
||||||
|
monitorForwarder.SetIn(&monitorIn)
|
||||||
for {
|
for {
|
||||||
<-monitorEnableCh
|
<-monitorEnableCh
|
||||||
in, out := ioSetPipe()
|
in, out := ioset.Pipe()
|
||||||
monitorForwarder.setDestination(&out)
|
monitorForwarder.SetOut(&out)
|
||||||
doneCh, errCh := make(chan struct{}), make(chan error)
|
doneCh, errCh := make(chan struct{}), make(chan error)
|
||||||
go func() {
|
go func() {
|
||||||
defer close(doneCh)
|
defer close(doneCh)
|
||||||
|
@ -69,7 +106,7 @@ func RunMonitor(ctx context.Context, containerConfig build.ContainerConfig, relo
|
||||||
<-ctx.Done()
|
<-ctx.Done()
|
||||||
in.Close()
|
in.Close()
|
||||||
}()
|
}()
|
||||||
t := term.NewTerminal(readWriter{in.stdin, in.stdout}, "(buildx) ")
|
t := term.NewTerminal(readWriter{in.Stdin, in.Stdout}, "(buildx) ")
|
||||||
for {
|
for {
|
||||||
l, err := t.ReadLine()
|
l, err := t.ReadLine()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -79,22 +116,63 @@ func RunMonitor(ctx context.Context, containerConfig build.ContainerConfig, relo
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
switch l {
|
args := strings.Fields(l) // TODO: use shlex
|
||||||
|
if len(args) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
switch args[0] {
|
||||||
case "":
|
case "":
|
||||||
// nop
|
// nop
|
||||||
case "reload":
|
case "reload":
|
||||||
res, err := reloadFunc(ctx)
|
if curRef != "" {
|
||||||
|
if err := c.Disconnect(ctx, curRef); err != nil {
|
||||||
|
fmt.Println("disconnect error", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ref, err := c.Build(ctx, options, nil, stdout, stderr, progressMode) // TODO: support stdin, hold build ref
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("failed to reload: %v\n", err)
|
fmt.Printf("failed to reload: %v\n", err)
|
||||||
} else {
|
} else {
|
||||||
|
curRef = ref
|
||||||
// rollback the running container with the new result
|
// rollback the running container with the new result
|
||||||
containerConfig.ResultCtx = res
|
m.rollback(ctx, curRef)
|
||||||
m.rollback(ctx, containerConfig)
|
|
||||||
fmt.Fprint(stdout, "Interactive container was restarted. Press Ctrl-a-c to switch to the new container\n")
|
fmt.Fprint(stdout, "Interactive container was restarted. Press Ctrl-a-c to switch to the new container\n")
|
||||||
}
|
}
|
||||||
case "rollback":
|
case "rollback":
|
||||||
m.rollback(ctx, containerConfig)
|
m.rollback(ctx, curRef)
|
||||||
fmt.Fprint(stdout, "Interactive container was restarted. Press Ctrl-a-c to switch to the new container\n")
|
fmt.Fprint(stdout, "Interactive container was restarted. Press Ctrl-a-c to switch to the new container\n")
|
||||||
|
case "list":
|
||||||
|
refs, err := c.List(ctx)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("failed to list: %v\n", err)
|
||||||
|
}
|
||||||
|
sort.Strings(refs)
|
||||||
|
tw := tabwriter.NewWriter(stdout, 1, 8, 1, '\t', 0)
|
||||||
|
fmt.Fprintln(tw, "ID\tCURRENT_SESSION")
|
||||||
|
for _, k := range refs {
|
||||||
|
fmt.Fprintf(tw, "%-20s\t%v\n", k, k == curRef)
|
||||||
|
}
|
||||||
|
tw.Flush()
|
||||||
|
case "disconnect":
|
||||||
|
target := curRef
|
||||||
|
if len(args) >= 2 {
|
||||||
|
target = args[1]
|
||||||
|
}
|
||||||
|
if err := c.Disconnect(ctx, target); err != nil {
|
||||||
|
fmt.Println("disconnect error", err)
|
||||||
|
}
|
||||||
|
case "kill":
|
||||||
|
if err := c.Kill(ctx); err != nil {
|
||||||
|
fmt.Printf("failed to kill: %v\n", err)
|
||||||
|
}
|
||||||
|
case "attach":
|
||||||
|
if len(args) < 2 {
|
||||||
|
fmt.Println("attach: server name must be passed")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ref := args[1]
|
||||||
|
m.rollback(ctx, ref)
|
||||||
|
curRef = ref
|
||||||
case "exit":
|
case "exit":
|
||||||
return
|
return
|
||||||
case "help":
|
case "help":
|
||||||
|
@ -118,7 +196,7 @@ func RunMonitor(ctx context.Context, containerConfig build.ContainerConfig, relo
|
||||||
return err
|
return err
|
||||||
case <-monitorDisableCh:
|
case <-monitorDisableCh:
|
||||||
}
|
}
|
||||||
monitorForwarder.setDestination(nil)
|
monitorForwarder.SetOut(nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,36 +206,37 @@ type readWriter struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type monitor struct {
|
type monitor struct {
|
||||||
muxIO *muxIO
|
muxIO *ioset.MuxIO
|
||||||
invokeIO *ioForwarder
|
invokeIO *ioset.Forwarder
|
||||||
|
invokeFunc func(context.Context, string, io.ReadCloser, io.WriteCloser, io.WriteCloser) error
|
||||||
curInvokeCancel func()
|
curInvokeCancel func()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *monitor) rollback(ctx context.Context, cfg build.ContainerConfig) {
|
func (m *monitor) rollback(ctx context.Context, ref string) {
|
||||||
if m.curInvokeCancel != nil {
|
if m.curInvokeCancel != nil {
|
||||||
m.curInvokeCancel() // Finish the running container if exists
|
m.curInvokeCancel() // Finish the running container if exists
|
||||||
}
|
}
|
||||||
go func() {
|
go func() {
|
||||||
// Start a new container
|
// Start a new container
|
||||||
if err := m.invoke(ctx, cfg); err != nil {
|
if err := m.invoke(ctx, ref); err != nil {
|
||||||
logrus.Debugf("invoke error: %v", err)
|
logrus.Debugf("invoke error: %v", err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *monitor) invoke(ctx context.Context, cfg build.ContainerConfig) error {
|
func (m *monitor) invoke(ctx context.Context, ref string) error {
|
||||||
m.muxIO.enable(1)
|
m.muxIO.Enable(1)
|
||||||
defer m.muxIO.disable(1)
|
defer m.muxIO.Disable(1)
|
||||||
invokeCtx, invokeCancel := context.WithCancel(ctx)
|
invokeCtx, invokeCancel := context.WithCancel(ctx)
|
||||||
|
|
||||||
containerIn, containerOut := ioSetPipe()
|
containerIn, containerOut := ioset.Pipe()
|
||||||
m.invokeIO.setDestination(&containerOut)
|
m.invokeIO.SetOut(&containerOut)
|
||||||
waitInvokeDoneCh := make(chan struct{})
|
waitInvokeDoneCh := make(chan struct{})
|
||||||
var cancelOnce sync.Once
|
var cancelOnce sync.Once
|
||||||
curInvokeCancel := func() {
|
curInvokeCancel := func() {
|
||||||
cancelOnce.Do(func() {
|
cancelOnce.Do(func() {
|
||||||
containerIn.Close()
|
containerIn.Close()
|
||||||
m.invokeIO.setDestination(nil)
|
m.invokeIO.SetOut(nil)
|
||||||
invokeCancel()
|
invokeCancel()
|
||||||
})
|
})
|
||||||
<-waitInvokeDoneCh
|
<-waitInvokeDoneCh
|
||||||
|
@ -165,352 +244,14 @@ func (m *monitor) invoke(ctx context.Context, cfg build.ContainerConfig) error {
|
||||||
defer curInvokeCancel()
|
defer curInvokeCancel()
|
||||||
m.curInvokeCancel = curInvokeCancel
|
m.curInvokeCancel = curInvokeCancel
|
||||||
|
|
||||||
cfg.Stdin = containerIn.stdin
|
err := m.invokeFunc(invokeCtx, ref, containerIn.Stdin, containerIn.Stdout, containerIn.Stderr)
|
||||||
cfg.Stdout = containerIn.stdout
|
|
||||||
cfg.Stderr = containerIn.stderr
|
|
||||||
err := build.Invoke(invokeCtx, cfg)
|
|
||||||
close(waitInvokeDoneCh)
|
close(waitInvokeDoneCh)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
type ioForwarder struct {
|
type nopCloser struct {
|
||||||
curIO *ioSetOut
|
io.Writer
|
||||||
mu sync.Mutex
|
|
||||||
updateCh chan struct{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func newIOForwarder(in ioSetIn) *ioForwarder {
|
func (c nopCloser) Close() error { return nil }
|
||||||
f := &ioForwarder{
|
|
||||||
updateCh: make(chan struct{}),
|
|
||||||
}
|
|
||||||
doneCh := make(chan struct{})
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
f.mu.Lock()
|
|
||||||
w := f.curIO
|
|
||||||
f.mu.Unlock()
|
|
||||||
if w != nil && w.stdout != nil && w.stderr != nil {
|
|
||||||
go func() {
|
|
||||||
if _, err := io.Copy(in.stdout, w.stdout); err != nil && err != io.ErrClosedPipe {
|
|
||||||
// ErrClosedPipe is OK as we close this read end during setDestination.
|
|
||||||
logrus.WithError(err).Warnf("failed to forward stdout: %v", err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
go func() {
|
|
||||||
if _, err := io.Copy(in.stderr, w.stderr); err != nil && err != io.ErrClosedPipe {
|
|
||||||
// ErrClosedPipe is OK as we close this read end during setDestination.
|
|
||||||
logrus.WithError(err).Warnf("failed to forward stderr: %v", err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
select {
|
|
||||||
case <-f.updateCh:
|
|
||||||
case <-doneCh:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
go func() {
|
|
||||||
if err := copyToFunc(in.stdin, func() (io.Writer, error) {
|
|
||||||
f.mu.Lock()
|
|
||||||
w := f.curIO
|
|
||||||
f.mu.Unlock()
|
|
||||||
if w != nil {
|
|
||||||
return w.stdin, nil
|
|
||||||
}
|
|
||||||
return nil, nil
|
|
||||||
}); err != nil && err != io.ErrClosedPipe {
|
|
||||||
logrus.WithError(err).Warnf("failed to forward IO: %v", err)
|
|
||||||
}
|
|
||||||
close(doneCh)
|
|
||||||
|
|
||||||
if w := f.curIO; w != nil {
|
|
||||||
// Propagate close
|
|
||||||
if err := w.Close(); err != nil {
|
|
||||||
logrus.WithError(err).Warnf("failed to forwarded stdin IO: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *ioForwarder) setDestination(out *ioSetOut) {
|
|
||||||
f.mu.Lock()
|
|
||||||
if f.curIO != nil {
|
|
||||||
// close all stream on the current IO no to mix with the new IO
|
|
||||||
f.curIO.Close()
|
|
||||||
}
|
|
||||||
f.curIO = out
|
|
||||||
f.mu.Unlock()
|
|
||||||
f.updateCh <- struct{}{}
|
|
||||||
}
|
|
||||||
|
|
||||||
type ioSetOutContext struct {
|
|
||||||
ioSetOut
|
|
||||||
enableHook func()
|
|
||||||
disableHook func()
|
|
||||||
}
|
|
||||||
|
|
||||||
// newMuxIO forwards IO stream to/from "in" and "outs".
|
|
||||||
// "outs" are closed automatically when "in" reaches EOF.
|
|
||||||
// "in" doesn't closed automatically so the caller needs to explicitly close it.
|
|
||||||
func newMuxIO(in ioSetIn, out []ioSetOutContext, initIdx int, toggleMessage func(prev int, res int) string) *muxIO {
|
|
||||||
m := &muxIO{
|
|
||||||
enabled: make(map[int]struct{}),
|
|
||||||
in: in,
|
|
||||||
out: out,
|
|
||||||
closedCh: make(chan struct{}),
|
|
||||||
toggleMessage: toggleMessage,
|
|
||||||
}
|
|
||||||
for i := range out {
|
|
||||||
m.enabled[i] = struct{}{}
|
|
||||||
}
|
|
||||||
m.maxCur = len(out)
|
|
||||||
m.cur = initIdx
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
var mu sync.Mutex
|
|
||||||
for i, o := range out {
|
|
||||||
i, o := i, o
|
|
||||||
wg.Add(1)
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
if err := copyToFunc(o.stdout, func() (io.Writer, error) {
|
|
||||||
if m.cur == i {
|
|
||||||
return in.stdout, nil
|
|
||||||
}
|
|
||||||
return nil, nil
|
|
||||||
}); err != nil {
|
|
||||||
logrus.WithField("output index", i).WithError(err).Warnf("failed to write stdout")
|
|
||||||
}
|
|
||||||
if err := o.stdout.Close(); err != nil {
|
|
||||||
logrus.WithField("output index", i).WithError(err).Warnf("failed to close stdout")
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
wg.Add(1)
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
if err := copyToFunc(o.stderr, func() (io.Writer, error) {
|
|
||||||
if m.cur == i {
|
|
||||||
return in.stderr, nil
|
|
||||||
}
|
|
||||||
return nil, nil
|
|
||||||
}); err != nil {
|
|
||||||
logrus.WithField("output index", i).WithError(err).Warnf("failed to write stderr")
|
|
||||||
}
|
|
||||||
if err := o.stderr.Close(); err != nil {
|
|
||||||
logrus.WithField("output index", i).WithError(err).Warnf("failed to close stderr")
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
go func() {
|
|
||||||
errToggle := errors.Errorf("toggle IO")
|
|
||||||
for {
|
|
||||||
prevIsControlSequence := false
|
|
||||||
if err := copyToFunc(traceReader(in.stdin, func(r rune) (bool, error) {
|
|
||||||
// Toggle IO when it detects C-a-c
|
|
||||||
// TODO: make it configurable if needed
|
|
||||||
if int(r) == 1 {
|
|
||||||
prevIsControlSequence = true
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
defer func() { prevIsControlSequence = false }()
|
|
||||||
if prevIsControlSequence {
|
|
||||||
if string(r) == "c" {
|
|
||||||
return false, errToggle
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true, nil
|
|
||||||
}), func() (io.Writer, error) {
|
|
||||||
mu.Lock()
|
|
||||||
o := out[m.cur]
|
|
||||||
mu.Unlock()
|
|
||||||
return o.stdin, nil
|
|
||||||
}); !errors.Is(err, errToggle) {
|
|
||||||
if err != nil {
|
|
||||||
logrus.WithError(err).Warnf("failed to read stdin")
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
m.toggleIO()
|
|
||||||
}
|
|
||||||
|
|
||||||
// propagate stdin EOF
|
|
||||||
for i, o := range out {
|
|
||||||
if err := o.stdin.Close(); err != nil {
|
|
||||||
logrus.WithError(err).Warnf("failed to close stdin of %d", i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
close(m.closedCh)
|
|
||||||
}()
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|
||||||
type muxIO struct {
|
|
||||||
cur int
|
|
||||||
maxCur int
|
|
||||||
enabled map[int]struct{}
|
|
||||||
mu sync.Mutex
|
|
||||||
in ioSetIn
|
|
||||||
out []ioSetOutContext
|
|
||||||
closedCh chan struct{}
|
|
||||||
toggleMessage func(prev int, res int) string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *muxIO) waitClosed() {
|
|
||||||
<-m.closedCh
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *muxIO) enable(i int) {
|
|
||||||
m.mu.Lock()
|
|
||||||
defer m.mu.Unlock()
|
|
||||||
m.enabled[i] = struct{}{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *muxIO) disable(i int) error {
|
|
||||||
m.mu.Lock()
|
|
||||||
defer m.mu.Unlock()
|
|
||||||
if i == 0 {
|
|
||||||
return errors.Errorf("disabling 0th io is prohibited")
|
|
||||||
}
|
|
||||||
delete(m.enabled, i)
|
|
||||||
if m.cur == i {
|
|
||||||
m.toggleIO()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *muxIO) toggleIO() {
|
|
||||||
if m.out[m.cur].disableHook != nil {
|
|
||||||
m.out[m.cur].disableHook()
|
|
||||||
}
|
|
||||||
prev := m.cur
|
|
||||||
for {
|
|
||||||
if m.cur+1 >= m.maxCur {
|
|
||||||
m.cur = 0
|
|
||||||
} else {
|
|
||||||
m.cur++
|
|
||||||
}
|
|
||||||
if _, ok := m.enabled[m.cur]; !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
res := m.cur
|
|
||||||
if m.out[m.cur].enableHook != nil {
|
|
||||||
m.out[m.cur].enableHook()
|
|
||||||
}
|
|
||||||
fmt.Fprint(m.in.stdout, m.toggleMessage(prev, res))
|
|
||||||
}
|
|
||||||
|
|
||||||
func traceReader(r io.ReadCloser, f func(rune) (bool, error)) io.ReadCloser {
|
|
||||||
pr, pw := io.Pipe()
|
|
||||||
go func() {
|
|
||||||
br := bufio.NewReader(r)
|
|
||||||
for {
|
|
||||||
rn, _, err := br.ReadRune()
|
|
||||||
if err != nil {
|
|
||||||
if err == io.EOF {
|
|
||||||
pw.Close()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
pw.CloseWithError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if isWrite, err := f(rn); err != nil {
|
|
||||||
pw.CloseWithError(err)
|
|
||||||
return
|
|
||||||
} else if !isWrite {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if _, err := pw.Write([]byte(string(rn))); err != nil {
|
|
||||||
pw.CloseWithError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
return &readerWithClose{
|
|
||||||
Reader: pr,
|
|
||||||
closeFunc: func() error {
|
|
||||||
pr.Close()
|
|
||||||
return r.Close()
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func copyToFunc(r io.Reader, wFunc func() (io.Writer, error)) error {
|
|
||||||
buf := make([]byte, 4096)
|
|
||||||
for {
|
|
||||||
n, readErr := r.Read(buf)
|
|
||||||
if readErr != nil && readErr != io.EOF {
|
|
||||||
return readErr
|
|
||||||
}
|
|
||||||
w, err := wFunc()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if w != nil {
|
|
||||||
if _, err := w.Write(buf[:n]); err != nil {
|
|
||||||
logrus.WithError(err).Debugf("failed to copy")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if readErr == io.EOF {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ioSetPipe() (ioSetIn, ioSetOut) {
|
|
||||||
r1, w1 := io.Pipe()
|
|
||||||
r2, w2 := io.Pipe()
|
|
||||||
r3, w3 := io.Pipe()
|
|
||||||
return ioSetIn{r1, w2, w3}, ioSetOut{w1, r2, r3}
|
|
||||||
}
|
|
||||||
|
|
||||||
type ioSetIn struct {
|
|
||||||
stdin io.ReadCloser
|
|
||||||
stdout io.WriteCloser
|
|
||||||
stderr io.WriteCloser
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s ioSetIn) Close() (retErr error) {
|
|
||||||
if err := s.stdin.Close(); err != nil {
|
|
||||||
retErr = err
|
|
||||||
}
|
|
||||||
if err := s.stdout.Close(); err != nil {
|
|
||||||
retErr = err
|
|
||||||
}
|
|
||||||
if err := s.stderr.Close(); err != nil {
|
|
||||||
retErr = err
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
type ioSetOut struct {
|
|
||||||
stdin io.WriteCloser
|
|
||||||
stdout io.ReadCloser
|
|
||||||
stderr io.ReadCloser
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s ioSetOut) Close() (retErr error) {
|
|
||||||
if err := s.stdin.Close(); err != nil {
|
|
||||||
retErr = err
|
|
||||||
}
|
|
||||||
if err := s.stdout.Close(); err != nil {
|
|
||||||
retErr = err
|
|
||||||
}
|
|
||||||
if err := s.stderr.Close(); err != nil {
|
|
||||||
retErr = err
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
type readerWithClose struct {
|
|
||||||
io.Reader
|
|
||||||
closeFunc func() error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *readerWithClose) Close() error {
|
|
||||||
return r.closeFunc()
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
// Forked from https://github.com/moby/buildkit/blob/e1b3b6c4abf7684f13e6391e5f7bc9210752687a/tools/tools.go
|
||||||
|
//go:build tools
|
||||||
|
// +build tools
|
||||||
|
|
||||||
|
// Package tools tracks dependencies on binaries not referenced in this codebase.
|
||||||
|
// https://github.com/golang/go/wiki/Modules#how-can-i-track-tool-dependencies-for-a-module
|
||||||
|
// Disclaimer: Avoid adding tools that don't need to be inferred from go.mod
|
||||||
|
// like golangci-lint and check they don't import too many dependencies.
|
||||||
|
package tools
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "github.com/gogo/protobuf/protoc-gen-gogo"
|
||||||
|
_ "github.com/gogo/protobuf/protoc-gen-gogofaster"
|
||||||
|
_ "github.com/gogo/protobuf/protoc-gen-gogoslick"
|
||||||
|
_ "github.com/golang/protobuf/protoc-gen-go"
|
||||||
|
)
|
|
@ -0,0 +1,256 @@
|
||||||
|
package ioset
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Pipe returns a pair of piped readers and writers collection.
|
||||||
|
// They are useful for controlling stdio stream using Forwarder function.
|
||||||
|
func Pipe() (In, Out) {
|
||||||
|
r1, w1 := io.Pipe()
|
||||||
|
r2, w2 := io.Pipe()
|
||||||
|
r3, w3 := io.Pipe()
|
||||||
|
return In{r1, w2, w3}, Out{w1, r2, r3}
|
||||||
|
}
|
||||||
|
|
||||||
|
type In struct {
|
||||||
|
Stdin io.ReadCloser
|
||||||
|
Stdout io.WriteCloser
|
||||||
|
Stderr io.WriteCloser
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s In) Close() (retErr error) {
|
||||||
|
if err := s.Stdin.Close(); err != nil {
|
||||||
|
retErr = err
|
||||||
|
}
|
||||||
|
if err := s.Stdout.Close(); err != nil {
|
||||||
|
retErr = err
|
||||||
|
}
|
||||||
|
if err := s.Stderr.Close(); err != nil {
|
||||||
|
retErr = err
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type Out struct {
|
||||||
|
Stdin io.WriteCloser
|
||||||
|
Stdout io.ReadCloser
|
||||||
|
Stderr io.ReadCloser
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Out) Close() (retErr error) {
|
||||||
|
if err := s.Stdin.Close(); err != nil {
|
||||||
|
retErr = err
|
||||||
|
}
|
||||||
|
if err := s.Stdout.Close(); err != nil {
|
||||||
|
retErr = err
|
||||||
|
}
|
||||||
|
if err := s.Stderr.Close(); err != nil {
|
||||||
|
retErr = err
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Forwarder forwards IO between readers and writers contained
|
||||||
|
// in In and Out structs.
|
||||||
|
// In and Out can be changed during forwarding using SetIn and SetOut methods.
|
||||||
|
type Forwarder struct {
|
||||||
|
stdin *SingleForwarder
|
||||||
|
stdout *SingleForwarder
|
||||||
|
stderr *SingleForwarder
|
||||||
|
mu sync.Mutex
|
||||||
|
|
||||||
|
// PropagateStdinClose indicates whether EOF from Stdin of Out should be propagated.
|
||||||
|
// If this is true, EOF from Stdin (reader) of Out closes Stdin (writer) of In.
|
||||||
|
PropagateStdinClose bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewForwarder() *Forwarder {
|
||||||
|
return &Forwarder{
|
||||||
|
stdin: NewSingleForwarder(),
|
||||||
|
stdout: NewSingleForwarder(),
|
||||||
|
stderr: NewSingleForwarder(),
|
||||||
|
PropagateStdinClose: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Forwarder) Close() (retErr error) {
|
||||||
|
if err := f.stdin.Close(); err != nil {
|
||||||
|
retErr = err
|
||||||
|
}
|
||||||
|
if err := f.stdout.Close(); err != nil {
|
||||||
|
retErr = err
|
||||||
|
}
|
||||||
|
if err := f.stderr.Close(); err != nil {
|
||||||
|
retErr = err
|
||||||
|
}
|
||||||
|
return retErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Forwarder) SetOut(out *Out) {
|
||||||
|
f.mu.Lock()
|
||||||
|
if out == nil {
|
||||||
|
f.stdin.SetWriter(nil, func() io.WriteCloser { return nil })
|
||||||
|
f.stdout.SetReader(nil)
|
||||||
|
f.stderr.SetReader(nil)
|
||||||
|
} else {
|
||||||
|
f.stdin.SetWriter(out.Stdin, func() io.WriteCloser {
|
||||||
|
if f.PropagateStdinClose {
|
||||||
|
out.Stdin.Close() // propagate EOF
|
||||||
|
logrus.Debug("forwarder: propagating stdin close")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return out.Stdin
|
||||||
|
})
|
||||||
|
f.stdout.SetReader(out.Stdout)
|
||||||
|
f.stderr.SetReader(out.Stderr)
|
||||||
|
}
|
||||||
|
f.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Forwarder) SetIn(in *In) {
|
||||||
|
f.mu.Lock()
|
||||||
|
if in == nil {
|
||||||
|
f.stdin.SetReader(nil)
|
||||||
|
f.stdout.SetWriter(nil, func() io.WriteCloser { return nil })
|
||||||
|
f.stderr.SetWriter(nil, func() io.WriteCloser { return nil })
|
||||||
|
} else {
|
||||||
|
f.stdin.SetReader(in.Stdin)
|
||||||
|
f.stdout.SetWriter(in.Stdout, func() io.WriteCloser {
|
||||||
|
return in.Stdout // continue write; TODO: make it configurable if needed
|
||||||
|
})
|
||||||
|
f.stderr.SetWriter(in.Stderr, func() io.WriteCloser {
|
||||||
|
return in.Stderr // continue write; TODO: make it configurable if needed
|
||||||
|
})
|
||||||
|
}
|
||||||
|
f.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SingleForwarder forwards IO from a reader to a writer.
|
||||||
|
// The reader and writer can be changed during forwarding
|
||||||
|
// using SetReader and SetWriter methods.
|
||||||
|
type SingleForwarder struct {
|
||||||
|
curR io.ReadCloser // closed when set another reader
|
||||||
|
curRMu sync.Mutex
|
||||||
|
curW io.WriteCloser // closed when set another writer
|
||||||
|
curWEOFHandler func() io.WriteCloser
|
||||||
|
curWMu sync.Mutex
|
||||||
|
|
||||||
|
updateRCh chan io.ReadCloser
|
||||||
|
doneCh chan struct{}
|
||||||
|
|
||||||
|
closeOnce sync.Once
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSingleForwarder() *SingleForwarder {
|
||||||
|
f := &SingleForwarder{
|
||||||
|
updateRCh: make(chan io.ReadCloser),
|
||||||
|
doneCh: make(chan struct{}),
|
||||||
|
}
|
||||||
|
go f.doForward()
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *SingleForwarder) doForward() {
|
||||||
|
var r io.ReadCloser
|
||||||
|
for {
|
||||||
|
readerInvalid := false
|
||||||
|
if r != nil {
|
||||||
|
go func() {
|
||||||
|
buf := make([]byte, 4096)
|
||||||
|
for {
|
||||||
|
n, readErr := r.Read(buf)
|
||||||
|
if readErr != nil && !errors.Is(readErr, io.EOF) && !errors.Is(readErr, io.ErrClosedPipe) {
|
||||||
|
logrus.Debugf("single forwarder: reader error: %v", readErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
f.curWMu.Lock()
|
||||||
|
w := f.curW
|
||||||
|
f.curWMu.Unlock()
|
||||||
|
if w != nil {
|
||||||
|
if _, err := w.Write(buf[:n]); err != nil && !errors.Is(err, io.ErrClosedPipe) {
|
||||||
|
logrus.Debugf("single forwarder: writer error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if readerInvalid {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if readErr != io.EOF {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
f.curWMu.Lock()
|
||||||
|
var newW io.WriteCloser
|
||||||
|
if f.curWEOFHandler != nil {
|
||||||
|
newW = f.curWEOFHandler()
|
||||||
|
}
|
||||||
|
f.curW = newW
|
||||||
|
f.curWMu.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case newR := <-f.updateRCh:
|
||||||
|
f.curRMu.Lock()
|
||||||
|
if f.curR != nil {
|
||||||
|
f.curR.Close()
|
||||||
|
}
|
||||||
|
f.curR = newR
|
||||||
|
r = newR
|
||||||
|
readerInvalid = true
|
||||||
|
f.curRMu.Unlock()
|
||||||
|
case <-f.doneCh:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes the both of registered reader and writer and finishes the forwarder.
|
||||||
|
func (f *SingleForwarder) Close() (retErr error) {
|
||||||
|
f.closeOnce.Do(func() {
|
||||||
|
f.curRMu.Lock()
|
||||||
|
r := f.curR
|
||||||
|
f.curR = nil
|
||||||
|
f.curRMu.Unlock()
|
||||||
|
if r != nil {
|
||||||
|
if err := r.Close(); err != nil {
|
||||||
|
retErr = err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: Wait until read data fully written to the current writer if needed.
|
||||||
|
f.curWMu.Lock()
|
||||||
|
w := f.curW
|
||||||
|
f.curW = nil
|
||||||
|
f.curWMu.Unlock()
|
||||||
|
if w != nil {
|
||||||
|
if err := w.Close(); err != nil {
|
||||||
|
retErr = err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close(f.doneCh)
|
||||||
|
})
|
||||||
|
return retErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetWriter sets the specified writer as the forward destination.
|
||||||
|
// If curWEOFHandler isn't nil, this will be called when the current reader returns EOF.
|
||||||
|
func (f *SingleForwarder) SetWriter(w io.WriteCloser, curWEOFHandler func() io.WriteCloser) {
|
||||||
|
f.curWMu.Lock()
|
||||||
|
if f.curW != nil {
|
||||||
|
// close all stream on the current IO no to mix with the new IO
|
||||||
|
f.curW.Close()
|
||||||
|
}
|
||||||
|
f.curW = w
|
||||||
|
f.curWEOFHandler = curWEOFHandler
|
||||||
|
f.curWMu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetWriter sets the specified reader as the forward source.
|
||||||
|
func (f *SingleForwarder) SetReader(r io.ReadCloser) {
|
||||||
|
f.updateRCh <- r
|
||||||
|
}
|
|
@ -0,0 +1,236 @@
|
||||||
|
package ioset
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MuxOut struct {
|
||||||
|
Out
|
||||||
|
EnableHook func()
|
||||||
|
DisableHook func()
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMuxIO forwards IO stream to/from "in" and "outs".
|
||||||
|
// It toggles IO when it detects "C-a-c" key.
|
||||||
|
// "outs" are closed automatically when "in" reaches EOF.
|
||||||
|
// "in" doesn't closed automatically so the caller needs to explicitly close it.
|
||||||
|
func NewMuxIO(in In, outs []MuxOut, initIdx int, toggleMessage func(prev int, res int) string) *MuxIO {
|
||||||
|
m := &MuxIO{
|
||||||
|
enabled: make(map[int]struct{}),
|
||||||
|
in: in,
|
||||||
|
outs: outs,
|
||||||
|
closedCh: make(chan struct{}),
|
||||||
|
toggleMessage: toggleMessage,
|
||||||
|
}
|
||||||
|
for i := range outs {
|
||||||
|
m.enabled[i] = struct{}{}
|
||||||
|
}
|
||||||
|
m.maxCur = len(outs)
|
||||||
|
m.cur = initIdx
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
var mu sync.Mutex
|
||||||
|
for i, o := range outs {
|
||||||
|
i, o := i, o
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
if err := copyToFunc(o.Stdout, func() (io.Writer, error) {
|
||||||
|
if m.cur == i {
|
||||||
|
return in.Stdout, nil
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}); err != nil {
|
||||||
|
logrus.WithField("output index", i).WithError(err).Warnf("failed to write stdout")
|
||||||
|
}
|
||||||
|
if err := o.Stdout.Close(); err != nil {
|
||||||
|
logrus.WithField("output index", i).WithError(err).Warnf("failed to close stdout")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
if err := copyToFunc(o.Stderr, func() (io.Writer, error) {
|
||||||
|
if m.cur == i {
|
||||||
|
return in.Stderr, nil
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}); err != nil {
|
||||||
|
logrus.WithField("output index", i).WithError(err).Warnf("failed to write stderr")
|
||||||
|
}
|
||||||
|
if err := o.Stderr.Close(); err != nil {
|
||||||
|
logrus.WithField("output index", i).WithError(err).Warnf("failed to close stderr")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
errToggle := errors.Errorf("toggle IO")
|
||||||
|
for {
|
||||||
|
prevIsControlSequence := false
|
||||||
|
if err := copyToFunc(traceReader(in.Stdin, func(r rune) (bool, error) {
|
||||||
|
// Toggle IO when it detects C-a-c
|
||||||
|
// TODO: make it configurable if needed
|
||||||
|
if int(r) == 1 {
|
||||||
|
prevIsControlSequence = true
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
defer func() { prevIsControlSequence = false }()
|
||||||
|
if prevIsControlSequence {
|
||||||
|
if string(r) == "c" {
|
||||||
|
return false, errToggle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}), func() (io.Writer, error) {
|
||||||
|
mu.Lock()
|
||||||
|
o := outs[m.cur]
|
||||||
|
mu.Unlock()
|
||||||
|
return o.Stdin, nil
|
||||||
|
}); !errors.Is(err, errToggle) {
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithError(err).Warnf("failed to read stdin")
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
m.toggleIO()
|
||||||
|
}
|
||||||
|
|
||||||
|
// propagate stdin EOF
|
||||||
|
for i, o := range outs {
|
||||||
|
if err := o.Stdin.Close(); err != nil {
|
||||||
|
logrus.WithError(err).Warnf("failed to close stdin of %d", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
close(m.closedCh)
|
||||||
|
}()
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
type MuxIO struct {
|
||||||
|
cur int
|
||||||
|
maxCur int
|
||||||
|
enabled map[int]struct{}
|
||||||
|
mu sync.Mutex
|
||||||
|
in In
|
||||||
|
outs []MuxOut
|
||||||
|
closedCh chan struct{}
|
||||||
|
toggleMessage func(prev int, res int) string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MuxIO) waitClosed() {
|
||||||
|
<-m.closedCh
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MuxIO) Enable(i int) {
|
||||||
|
m.mu.Lock()
|
||||||
|
defer m.mu.Unlock()
|
||||||
|
m.enabled[i] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MuxIO) Disable(i int) error {
|
||||||
|
m.mu.Lock()
|
||||||
|
defer m.mu.Unlock()
|
||||||
|
if i == 0 {
|
||||||
|
return errors.Errorf("disabling 0th io is prohibited")
|
||||||
|
}
|
||||||
|
delete(m.enabled, i)
|
||||||
|
if m.cur == i {
|
||||||
|
m.toggleIO()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MuxIO) toggleIO() {
|
||||||
|
if m.outs[m.cur].DisableHook != nil {
|
||||||
|
m.outs[m.cur].DisableHook()
|
||||||
|
}
|
||||||
|
prev := m.cur
|
||||||
|
for {
|
||||||
|
if m.cur+1 >= m.maxCur {
|
||||||
|
m.cur = 0
|
||||||
|
} else {
|
||||||
|
m.cur++
|
||||||
|
}
|
||||||
|
if _, ok := m.enabled[m.cur]; !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
res := m.cur
|
||||||
|
if m.outs[m.cur].EnableHook != nil {
|
||||||
|
m.outs[m.cur].EnableHook()
|
||||||
|
}
|
||||||
|
fmt.Fprint(m.in.Stdout, m.toggleMessage(prev, res))
|
||||||
|
}
|
||||||
|
|
||||||
|
func traceReader(r io.ReadCloser, f func(rune) (bool, error)) io.ReadCloser {
|
||||||
|
pr, pw := io.Pipe()
|
||||||
|
go func() {
|
||||||
|
br := bufio.NewReader(r)
|
||||||
|
for {
|
||||||
|
rn, _, err := br.ReadRune()
|
||||||
|
if err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
pw.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pw.CloseWithError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if isWrite, err := f(rn); err != nil {
|
||||||
|
pw.CloseWithError(err)
|
||||||
|
return
|
||||||
|
} else if !isWrite {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, err := pw.Write([]byte(string(rn))); err != nil {
|
||||||
|
pw.CloseWithError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return &readerWithClose{
|
||||||
|
Reader: pr,
|
||||||
|
closeFunc: func() error {
|
||||||
|
pr.Close()
|
||||||
|
return r.Close()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyToFunc(r io.Reader, wFunc func() (io.Writer, error)) error {
|
||||||
|
buf := make([]byte, 4096)
|
||||||
|
for {
|
||||||
|
n, readErr := r.Read(buf)
|
||||||
|
if readErr != nil && readErr != io.EOF {
|
||||||
|
return readErr
|
||||||
|
}
|
||||||
|
w, err := wFunc()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if w != nil {
|
||||||
|
if _, err := w.Write(buf[:n]); err != nil {
|
||||||
|
logrus.WithError(err).Debugf("failed to copy")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if readErr == io.EOF {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type readerWithClose struct {
|
||||||
|
io.Reader
|
||||||
|
closeFunc func() error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *readerWithClose) Close() error {
|
||||||
|
return r.closeFunc()
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package monitor
|
package ioset
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
@ -11,7 +11,7 @@ import (
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestMuxIO tests muxIO
|
// TestMuxIO tests MuxIO
|
||||||
func TestMuxIO(t *testing.T) {
|
func TestMuxIO(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
@ -122,20 +122,20 @@ func TestMuxIO(t *testing.T) {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
inBuf, end, in := newTestIn(t)
|
inBuf, end, in := newTestIn(t)
|
||||||
var outBufs []*outBuf
|
var outBufs []*outBuf
|
||||||
var outs []ioSetOutContext
|
var outs []MuxOut
|
||||||
if tt.outputsNum != len(tt.wants) {
|
if tt.outputsNum != len(tt.wants) {
|
||||||
t.Fatalf("wants != outputsNum")
|
t.Fatalf("wants != outputsNum")
|
||||||
}
|
}
|
||||||
for i := 0; i < tt.outputsNum; i++ {
|
for i := 0; i < tt.outputsNum; i++ {
|
||||||
outBuf, out := newTestOut(t, i)
|
outBuf, out := newTestOut(t, i)
|
||||||
outBufs = append(outBufs, outBuf)
|
outBufs = append(outBufs, outBuf)
|
||||||
outs = append(outs, ioSetOutContext{out, nil, nil})
|
outs = append(outs, MuxOut{out, nil, nil})
|
||||||
}
|
}
|
||||||
mio := newMuxIO(in, outs, tt.initIdx, func(prev int, res int) string { return "" })
|
mio := NewMuxIO(in, outs, tt.initIdx, func(prev int, res int) string { return "" })
|
||||||
for _, i := range tt.inputs {
|
for _, i := range tt.inputs {
|
||||||
// Add input to muxIO
|
// Add input to MuxIO
|
||||||
istr, writeback := i(mio)
|
istr, writeback := i(mio)
|
||||||
if _, err := end.stdin.Write([]byte(istr)); err != nil {
|
if _, err := end.Stdin.Write([]byte(istr)); err != nil {
|
||||||
t.Fatalf("failed to write data to stdin: %v", err)
|
t.Fatalf("failed to write data to stdin: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,14 +143,14 @@ func TestMuxIO(t *testing.T) {
|
||||||
var eg errgroup.Group
|
var eg errgroup.Group
|
||||||
eg.Go(func() error {
|
eg.Go(func() error {
|
||||||
outbuf := make([]byte, len(writeback))
|
outbuf := make([]byte, len(writeback))
|
||||||
if _, err := io.ReadAtLeast(end.stdout, outbuf, len(outbuf)); err != nil {
|
if _, err := io.ReadAtLeast(end.Stdout, outbuf, len(outbuf)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
eg.Go(func() error {
|
eg.Go(func() error {
|
||||||
errbuf := make([]byte, len(writeback))
|
errbuf := make([]byte, len(writeback))
|
||||||
if _, err := io.ReadAtLeast(end.stderr, errbuf, len(errbuf)); err != nil {
|
if _, err := io.ReadAtLeast(end.Stderr, errbuf, len(errbuf)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -160,8 +160,8 @@ func TestMuxIO(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close stdin on this muxIO
|
// Close stdin on this MuxIO
|
||||||
end.stdin.Close()
|
end.Stdin.Close()
|
||||||
|
|
||||||
// Wait for all output ends reach EOF
|
// Wait for all output ends reach EOF
|
||||||
mio.waitClosed()
|
mio.waitClosed()
|
||||||
|
@ -189,30 +189,30 @@ func TestMuxIO(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type instruction func(m *muxIO) (intput string, writeBackView string)
|
type instruction func(m *MuxIO) (intput string, writeBackView string)
|
||||||
|
|
||||||
func input(s string) instruction {
|
func input(s string) instruction {
|
||||||
return func(m *muxIO) (string, string) {
|
return func(m *MuxIO) (string, string) {
|
||||||
return s, strings.ReplaceAll(s, string([]rune{rune(1)}), "")
|
return s, strings.ReplaceAll(s, string([]rune{rune(1)}), "")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func toggle() instruction {
|
func toggle() instruction {
|
||||||
return func(m *muxIO) (string, string) {
|
return func(m *MuxIO) (string, string) {
|
||||||
return string([]rune{rune(1)}) + "c", ""
|
return string([]rune{rune(1)}) + "c", ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func enable(i int) instruction {
|
func enable(i int) instruction {
|
||||||
return func(m *muxIO) (string, string) {
|
return func(m *MuxIO) (string, string) {
|
||||||
m.enable(i)
|
m.Enable(i)
|
||||||
return "", ""
|
return "", ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func disable(i int) instruction {
|
func disable(i int) instruction {
|
||||||
return func(m *muxIO) (string, string) {
|
return func(m *MuxIO) (string, string) {
|
||||||
m.disable(i)
|
m.Disable(i)
|
||||||
return "", ""
|
return "", ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -223,7 +223,7 @@ type inBuf struct {
|
||||||
doneCh chan struct{}
|
doneCh chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTestIn(t *testing.T) (*inBuf, ioSetOut, ioSetIn) {
|
func newTestIn(t *testing.T) (*inBuf, Out, In) {
|
||||||
ti := &inBuf{
|
ti := &inBuf{
|
||||||
doneCh: make(chan struct{}),
|
doneCh: make(chan struct{}),
|
||||||
}
|
}
|
||||||
|
@ -253,7 +253,7 @@ func newTestIn(t *testing.T) (*inBuf, ioSetOut, ioSetIn) {
|
||||||
close(ti.doneCh)
|
close(ti.doneCh)
|
||||||
}()
|
}()
|
||||||
inR, inW := io.Pipe()
|
inR, inW := io.Pipe()
|
||||||
return ti, ioSetOut{inW, gotOutR, gotErrR}, ioSetIn{inR, outW, errW}
|
return ti, Out{Stdin: inW, Stdout: gotOutR, Stderr: gotErrR}, In{Stdin: inR, Stdout: outW, Stderr: errW}
|
||||||
}
|
}
|
||||||
|
|
||||||
type outBuf struct {
|
type outBuf struct {
|
||||||
|
@ -262,7 +262,7 @@ type outBuf struct {
|
||||||
doneCh chan struct{}
|
doneCh chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTestOut(t *testing.T, idx int) (*outBuf, ioSetOut) {
|
func newTestOut(t *testing.T, idx int) (*outBuf, Out) {
|
||||||
to := &outBuf{
|
to := &outBuf{
|
||||||
idx: idx,
|
idx: idx,
|
||||||
doneCh: make(chan struct{}),
|
doneCh: make(chan struct{}),
|
||||||
|
@ -290,7 +290,7 @@ func newTestOut(t *testing.T, idx int) (*outBuf, ioSetOut) {
|
||||||
errW.Close()
|
errW.Close()
|
||||||
close(to.doneCh)
|
close(to.doneCh)
|
||||||
}()
|
}()
|
||||||
return to, ioSetOut{inW, outR, errR}
|
return to, Out{Stdin: inW, Stdout: outR, Stderr: errR}
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeMasked(w io.Writer, s string) io.Writer {
|
func writeMasked(w io.Writer, s string) io.Writer {
|
|
@ -68,3 +68,24 @@ func NewChannel(w Writer) (chan *client.SolveStatus, chan struct{}) {
|
||||||
}()
|
}()
|
||||||
return ch, done
|
return ch, done
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type tee struct {
|
||||||
|
Writer
|
||||||
|
ch chan *client.SolveStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tee) Write(v *client.SolveStatus) {
|
||||||
|
v2 := *v
|
||||||
|
t.ch <- &v2
|
||||||
|
t.Writer.Write(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Tee(w Writer, ch chan *client.SolveStatus) Writer {
|
||||||
|
if ch == nil {
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
return &tee{
|
||||||
|
Writer: w,
|
||||||
|
ch: ch,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package dialer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type dialResult struct {
|
||||||
|
c net.Conn
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContextDialer returns a GRPC net.Conn connected to the provided address
|
||||||
|
func ContextDialer(ctx context.Context, address string) (net.Conn, error) {
|
||||||
|
if deadline, ok := ctx.Deadline(); ok {
|
||||||
|
return timeoutDialer(address, time.Until(deadline))
|
||||||
|
}
|
||||||
|
return timeoutDialer(address, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dialer returns a GRPC net.Conn connected to the provided address
|
||||||
|
// Deprecated: use ContextDialer and grpc.WithContextDialer.
|
||||||
|
var Dialer = timeoutDialer
|
||||||
|
|
||||||
|
func timeoutDialer(address string, timeout time.Duration) (net.Conn, error) {
|
||||||
|
var (
|
||||||
|
stopC = make(chan struct{})
|
||||||
|
synC = make(chan *dialResult)
|
||||||
|
)
|
||||||
|
go func() {
|
||||||
|
defer close(synC)
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-stopC:
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
c, err := dialer(address, timeout)
|
||||||
|
if isNoent(err) {
|
||||||
|
<-time.After(10 * time.Millisecond)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
synC <- &dialResult{c, err}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
select {
|
||||||
|
case dr := <-synC:
|
||||||
|
return dr.c, dr.err
|
||||||
|
case <-time.After(timeout):
|
||||||
|
close(stopC)
|
||||||
|
go func() {
|
||||||
|
dr := <-synC
|
||||||
|
if dr != nil && dr.c != nil {
|
||||||
|
dr.c.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return nil, fmt.Errorf("dial %s: timeout", address)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
//go:build !windows
|
||||||
|
// +build !windows
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package dialer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DialAddress returns the address with unix:// prepended to the
|
||||||
|
// provided address
|
||||||
|
func DialAddress(address string) string {
|
||||||
|
return fmt.Sprintf("unix://%s", address)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isNoent(err error) bool {
|
||||||
|
if err != nil {
|
||||||
|
if nerr, ok := err.(*net.OpError); ok {
|
||||||
|
if serr, ok := nerr.Err.(*os.SyscallError); ok {
|
||||||
|
if serr.Err == syscall.ENOENT {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func dialer(address string, timeout time.Duration) (net.Conn, error) {
|
||||||
|
address = strings.TrimPrefix(address, "unix://")
|
||||||
|
return net.DialTimeout("unix", address, timeout)
|
||||||
|
}
|
38
vendor/github.com/containerd/containerd/pkg/dialer/dialer_windows.go
generated
vendored
Normal file
38
vendor/github.com/containerd/containerd/pkg/dialer/dialer_windows.go
generated
vendored
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package dialer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
winio "github.com/Microsoft/go-winio"
|
||||||
|
)
|
||||||
|
|
||||||
|
func isNoent(err error) bool {
|
||||||
|
return os.IsNotExist(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func dialer(address string, timeout time.Duration) (net.Conn, error) {
|
||||||
|
return winio.DialPipe(address, &timeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DialAddress returns the dial address
|
||||||
|
func DialAddress(address string) string {
|
||||||
|
return address
|
||||||
|
}
|
|
@ -0,0 +1,580 @@
|
||||||
|
// Protocol Buffers for Go with Gadgets
|
||||||
|
//
|
||||||
|
// Copyright (c) 2013, The GoGo Authors. All rights reserved.
|
||||||
|
// http://github.com/gogo/protobuf
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
package compare
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogo/protobuf/gogoproto"
|
||||||
|
"github.com/gogo/protobuf/proto"
|
||||||
|
descriptor "github.com/gogo/protobuf/protoc-gen-gogo/descriptor"
|
||||||
|
"github.com/gogo/protobuf/protoc-gen-gogo/generator"
|
||||||
|
"github.com/gogo/protobuf/vanity"
|
||||||
|
)
|
||||||
|
|
||||||
|
type plugin struct {
|
||||||
|
*generator.Generator
|
||||||
|
generator.PluginImports
|
||||||
|
fmtPkg generator.Single
|
||||||
|
bytesPkg generator.Single
|
||||||
|
sortkeysPkg generator.Single
|
||||||
|
protoPkg generator.Single
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPlugin() *plugin {
|
||||||
|
return &plugin{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *plugin) Name() string {
|
||||||
|
return "compare"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *plugin) Init(g *generator.Generator) {
|
||||||
|
p.Generator = g
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *plugin) Generate(file *generator.FileDescriptor) {
|
||||||
|
p.PluginImports = generator.NewPluginImports(p.Generator)
|
||||||
|
p.fmtPkg = p.NewImport("fmt")
|
||||||
|
p.bytesPkg = p.NewImport("bytes")
|
||||||
|
p.sortkeysPkg = p.NewImport("github.com/gogo/protobuf/sortkeys")
|
||||||
|
p.protoPkg = p.NewImport("github.com/gogo/protobuf/proto")
|
||||||
|
|
||||||
|
for _, msg := range file.Messages() {
|
||||||
|
if msg.DescriptorProto.GetOptions().GetMapEntry() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if gogoproto.HasCompare(file.FileDescriptorProto, msg.DescriptorProto) {
|
||||||
|
p.generateMessage(file, msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *plugin) generateNullableField(fieldname string) {
|
||||||
|
p.P(`if this.`, fieldname, ` != nil && that1.`, fieldname, ` != nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`if *this.`, fieldname, ` != *that1.`, fieldname, `{`)
|
||||||
|
p.In()
|
||||||
|
p.P(`if *this.`, fieldname, ` < *that1.`, fieldname, `{`)
|
||||||
|
p.In()
|
||||||
|
p.P(`return -1`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`return 1`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`} else if this.`, fieldname, ` != nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`return 1`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`} else if that1.`, fieldname, ` != nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`return -1`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *plugin) generateMsgNullAndTypeCheck(ccTypeName string) {
|
||||||
|
p.P(`if that == nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`if this == nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`return 0`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`return 1`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(``)
|
||||||
|
p.P(`that1, ok := that.(*`, ccTypeName, `)`)
|
||||||
|
p.P(`if !ok {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`that2, ok := that.(`, ccTypeName, `)`)
|
||||||
|
p.P(`if ok {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`that1 = &that2`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`} else {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`return 1`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`if that1 == nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`if this == nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`return 0`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`return 1`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`} else if this == nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`return -1`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *plugin) generateField(file *generator.FileDescriptor, message *generator.Descriptor, field *descriptor.FieldDescriptorProto) {
|
||||||
|
proto3 := gogoproto.IsProto3(file.FileDescriptorProto)
|
||||||
|
fieldname := p.GetOneOfFieldName(message, field)
|
||||||
|
repeated := field.IsRepeated()
|
||||||
|
ctype := gogoproto.IsCustomType(field)
|
||||||
|
nullable := gogoproto.IsNullable(field)
|
||||||
|
// oneof := field.OneofIndex != nil
|
||||||
|
if !repeated {
|
||||||
|
if ctype {
|
||||||
|
if nullable {
|
||||||
|
p.P(`if that1.`, fieldname, ` == nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`if this.`, fieldname, ` != nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`return 1`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`} else if this.`, fieldname, ` == nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`return -1`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`} else if c := this.`, fieldname, `.Compare(*that1.`, fieldname, `); c != 0 {`)
|
||||||
|
} else {
|
||||||
|
p.P(`if c := this.`, fieldname, `.Compare(that1.`, fieldname, `); c != 0 {`)
|
||||||
|
}
|
||||||
|
p.In()
|
||||||
|
p.P(`return c`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
} else {
|
||||||
|
if field.IsMessage() || p.IsGroup(field) {
|
||||||
|
if nullable {
|
||||||
|
p.P(`if c := this.`, fieldname, `.Compare(that1.`, fieldname, `); c != 0 {`)
|
||||||
|
} else {
|
||||||
|
p.P(`if c := this.`, fieldname, `.Compare(&that1.`, fieldname, `); c != 0 {`)
|
||||||
|
}
|
||||||
|
p.In()
|
||||||
|
p.P(`return c`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
} else if field.IsBytes() {
|
||||||
|
p.P(`if c := `, p.bytesPkg.Use(), `.Compare(this.`, fieldname, `, that1.`, fieldname, `); c != 0 {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`return c`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
} else if field.IsString() {
|
||||||
|
if nullable && !proto3 {
|
||||||
|
p.generateNullableField(fieldname)
|
||||||
|
} else {
|
||||||
|
p.P(`if this.`, fieldname, ` != that1.`, fieldname, `{`)
|
||||||
|
p.In()
|
||||||
|
p.P(`if this.`, fieldname, ` < that1.`, fieldname, `{`)
|
||||||
|
p.In()
|
||||||
|
p.P(`return -1`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`return 1`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
}
|
||||||
|
} else if field.IsBool() {
|
||||||
|
if nullable && !proto3 {
|
||||||
|
p.P(`if this.`, fieldname, ` != nil && that1.`, fieldname, ` != nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`if *this.`, fieldname, ` != *that1.`, fieldname, `{`)
|
||||||
|
p.In()
|
||||||
|
p.P(`if !*this.`, fieldname, ` {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`return -1`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`return 1`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`} else if this.`, fieldname, ` != nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`return 1`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`} else if that1.`, fieldname, ` != nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`return -1`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
} else {
|
||||||
|
p.P(`if this.`, fieldname, ` != that1.`, fieldname, `{`)
|
||||||
|
p.In()
|
||||||
|
p.P(`if !this.`, fieldname, ` {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`return -1`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`return 1`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if nullable && !proto3 {
|
||||||
|
p.generateNullableField(fieldname)
|
||||||
|
} else {
|
||||||
|
p.P(`if this.`, fieldname, ` != that1.`, fieldname, `{`)
|
||||||
|
p.In()
|
||||||
|
p.P(`if this.`, fieldname, ` < that1.`, fieldname, `{`)
|
||||||
|
p.In()
|
||||||
|
p.P(`return -1`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`return 1`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
p.P(`if len(this.`, fieldname, `) != len(that1.`, fieldname, `) {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`if len(this.`, fieldname, `) < len(that1.`, fieldname, `) {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`return -1`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`return 1`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`for i := range this.`, fieldname, ` {`)
|
||||||
|
p.In()
|
||||||
|
if ctype {
|
||||||
|
p.P(`if c := this.`, fieldname, `[i].Compare(that1.`, fieldname, `[i]); c != 0 {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`return c`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
} else {
|
||||||
|
if p.IsMap(field) {
|
||||||
|
m := p.GoMapType(nil, field)
|
||||||
|
valuegoTyp, _ := p.GoType(nil, m.ValueField)
|
||||||
|
valuegoAliasTyp, _ := p.GoType(nil, m.ValueAliasField)
|
||||||
|
nullable, valuegoTyp, valuegoAliasTyp = generator.GoMapValueTypes(field, m.ValueField, valuegoTyp, valuegoAliasTyp)
|
||||||
|
|
||||||
|
mapValue := m.ValueAliasField
|
||||||
|
if mapValue.IsMessage() || p.IsGroup(mapValue) {
|
||||||
|
if nullable && valuegoTyp == valuegoAliasTyp {
|
||||||
|
p.P(`if c := this.`, fieldname, `[i].Compare(that1.`, fieldname, `[i]); c != 0 {`)
|
||||||
|
} else {
|
||||||
|
// Compare() has a pointer receiver, but map value is a value type
|
||||||
|
a := `this.` + fieldname + `[i]`
|
||||||
|
b := `that1.` + fieldname + `[i]`
|
||||||
|
if valuegoTyp != valuegoAliasTyp {
|
||||||
|
// cast back to the type that has the generated methods on it
|
||||||
|
a = `(` + valuegoTyp + `)(` + a + `)`
|
||||||
|
b = `(` + valuegoTyp + `)(` + b + `)`
|
||||||
|
}
|
||||||
|
p.P(`a := `, a)
|
||||||
|
p.P(`b := `, b)
|
||||||
|
if nullable {
|
||||||
|
p.P(`if c := a.Compare(b); c != 0 {`)
|
||||||
|
} else {
|
||||||
|
p.P(`if c := (&a).Compare(&b); c != 0 {`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.In()
|
||||||
|
p.P(`return c`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
} else if mapValue.IsBytes() {
|
||||||
|
p.P(`if c := `, p.bytesPkg.Use(), `.Compare(this.`, fieldname, `[i], that1.`, fieldname, `[i]); c != 0 {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`return c`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
} else if mapValue.IsString() {
|
||||||
|
p.P(`if this.`, fieldname, `[i] != that1.`, fieldname, `[i] {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`if this.`, fieldname, `[i] < that1.`, fieldname, `[i] {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`return -1`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`return 1`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
} else {
|
||||||
|
p.P(`if this.`, fieldname, `[i] != that1.`, fieldname, `[i] {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`if this.`, fieldname, `[i] < that1.`, fieldname, `[i] {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`return -1`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`return 1`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
}
|
||||||
|
} else if field.IsMessage() || p.IsGroup(field) {
|
||||||
|
if nullable {
|
||||||
|
p.P(`if c := this.`, fieldname, `[i].Compare(that1.`, fieldname, `[i]); c != 0 {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`return c`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
} else {
|
||||||
|
p.P(`if c := this.`, fieldname, `[i].Compare(&that1.`, fieldname, `[i]); c != 0 {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`return c`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
}
|
||||||
|
} else if field.IsBytes() {
|
||||||
|
p.P(`if c := `, p.bytesPkg.Use(), `.Compare(this.`, fieldname, `[i], that1.`, fieldname, `[i]); c != 0 {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`return c`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
} else if field.IsString() {
|
||||||
|
p.P(`if this.`, fieldname, `[i] != that1.`, fieldname, `[i] {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`if this.`, fieldname, `[i] < that1.`, fieldname, `[i] {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`return -1`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`return 1`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
} else if field.IsBool() {
|
||||||
|
p.P(`if this.`, fieldname, `[i] != that1.`, fieldname, `[i] {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`if !this.`, fieldname, `[i] {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`return -1`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`return 1`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
} else {
|
||||||
|
p.P(`if this.`, fieldname, `[i] != that1.`, fieldname, `[i] {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`if this.`, fieldname, `[i] < that1.`, fieldname, `[i] {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`return -1`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`return 1`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *plugin) generateMessage(file *generator.FileDescriptor, message *generator.Descriptor) {
|
||||||
|
ccTypeName := generator.CamelCaseSlice(message.TypeName())
|
||||||
|
p.P(`func (this *`, ccTypeName, `) Compare(that interface{}) int {`)
|
||||||
|
p.In()
|
||||||
|
p.generateMsgNullAndTypeCheck(ccTypeName)
|
||||||
|
oneofs := make(map[string]struct{})
|
||||||
|
|
||||||
|
for _, field := range message.Field {
|
||||||
|
oneof := field.OneofIndex != nil
|
||||||
|
if oneof {
|
||||||
|
fieldname := p.GetFieldName(message, field)
|
||||||
|
if _, ok := oneofs[fieldname]; ok {
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
oneofs[fieldname] = struct{}{}
|
||||||
|
}
|
||||||
|
p.P(`if that1.`, fieldname, ` == nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`if this.`, fieldname, ` != nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`return 1`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`} else if this.`, fieldname, ` == nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`return -1`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`} else {`)
|
||||||
|
p.In()
|
||||||
|
|
||||||
|
// Generate two type switches in order to compare the
|
||||||
|
// types of the oneofs. If they are of the same type
|
||||||
|
// call Compare, otherwise return 1 or -1.
|
||||||
|
p.P(`thisType := -1`)
|
||||||
|
p.P(`switch this.`, fieldname, `.(type) {`)
|
||||||
|
for i, subfield := range message.Field {
|
||||||
|
if *subfield.OneofIndex == *field.OneofIndex {
|
||||||
|
ccTypeName := p.OneOfTypeName(message, subfield)
|
||||||
|
p.P(`case *`, ccTypeName, `:`)
|
||||||
|
p.In()
|
||||||
|
p.P(`thisType = `, i)
|
||||||
|
p.Out()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.P(`default:`)
|
||||||
|
p.In()
|
||||||
|
p.P(`panic(fmt.Sprintf("compare: unexpected type %T in oneof", this.`, fieldname, `))`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
|
||||||
|
p.P(`that1Type := -1`)
|
||||||
|
p.P(`switch that1.`, fieldname, `.(type) {`)
|
||||||
|
for i, subfield := range message.Field {
|
||||||
|
if *subfield.OneofIndex == *field.OneofIndex {
|
||||||
|
ccTypeName := p.OneOfTypeName(message, subfield)
|
||||||
|
p.P(`case *`, ccTypeName, `:`)
|
||||||
|
p.In()
|
||||||
|
p.P(`that1Type = `, i)
|
||||||
|
p.Out()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.P(`default:`)
|
||||||
|
p.In()
|
||||||
|
p.P(`panic(fmt.Sprintf("compare: unexpected type %T in oneof", that1.`, fieldname, `))`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
|
||||||
|
p.P(`if thisType == that1Type {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`if c := this.`, fieldname, `.Compare(that1.`, fieldname, `); c != 0 {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`return c`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`} else if thisType < that1Type {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`return -1`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`} else if thisType > that1Type {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`return 1`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
} else {
|
||||||
|
p.generateField(file, message, field)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if message.DescriptorProto.HasExtension() {
|
||||||
|
if gogoproto.HasExtensionsMap(file.FileDescriptorProto, message.DescriptorProto) {
|
||||||
|
p.P(`thismap := `, p.protoPkg.Use(), `.GetUnsafeExtensionsMap(this)`)
|
||||||
|
p.P(`thatmap := `, p.protoPkg.Use(), `.GetUnsafeExtensionsMap(that1)`)
|
||||||
|
p.P(`extkeys := make([]int32, 0, len(thismap)+len(thatmap))`)
|
||||||
|
p.P(`for k, _ := range thismap {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`extkeys = append(extkeys, k)`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`for k, _ := range thatmap {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`if _, ok := thismap[k]; !ok {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`extkeys = append(extkeys, k)`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(p.sortkeysPkg.Use(), `.Int32s(extkeys)`)
|
||||||
|
p.P(`for _, k := range extkeys {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`if v, ok := thismap[k]; ok {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`if v2, ok := thatmap[k]; ok {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`if c := v.Compare(&v2); c != 0 {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`return c`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`} else {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`return 1`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`} else {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`return -1`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
} else {
|
||||||
|
fieldname := "XXX_extensions"
|
||||||
|
p.P(`if c := `, p.bytesPkg.Use(), `.Compare(this.`, fieldname, `, that1.`, fieldname, `); c != 0 {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`return c`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if gogoproto.HasUnrecognized(file.FileDescriptorProto, message.DescriptorProto) {
|
||||||
|
fieldname := "XXX_unrecognized"
|
||||||
|
p.P(`if c := `, p.bytesPkg.Use(), `.Compare(this.`, fieldname, `, that1.`, fieldname, `); c != 0 {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`return c`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
}
|
||||||
|
p.P(`return 0`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
|
||||||
|
//Generate Compare methods for oneof fields
|
||||||
|
m := proto.Clone(message.DescriptorProto).(*descriptor.DescriptorProto)
|
||||||
|
for _, field := range m.Field {
|
||||||
|
oneof := field.OneofIndex != nil
|
||||||
|
if !oneof {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ccTypeName := p.OneOfTypeName(message, field)
|
||||||
|
p.P(`func (this *`, ccTypeName, `) Compare(that interface{}) int {`)
|
||||||
|
p.In()
|
||||||
|
|
||||||
|
p.generateMsgNullAndTypeCheck(ccTypeName)
|
||||||
|
vanity.TurnOffNullableForNativeTypes(field)
|
||||||
|
p.generateField(file, message, field)
|
||||||
|
|
||||||
|
p.P(`return 0`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
generator.RegisterPlugin(NewPlugin())
|
||||||
|
}
|
|
@ -0,0 +1,118 @@
|
||||||
|
// Protocol Buffers for Go with Gadgets
|
||||||
|
//
|
||||||
|
// Copyright (c) 2013, The GoGo Authors. All rights reserved.
|
||||||
|
// http://github.com/gogo/protobuf
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
package compare
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogo/protobuf/gogoproto"
|
||||||
|
"github.com/gogo/protobuf/plugin/testgen"
|
||||||
|
"github.com/gogo/protobuf/protoc-gen-gogo/generator"
|
||||||
|
)
|
||||||
|
|
||||||
|
type test struct {
|
||||||
|
*generator.Generator
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTest(g *generator.Generator) testgen.TestPlugin {
|
||||||
|
return &test{g}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *test) Generate(imports generator.PluginImports, file *generator.FileDescriptor) bool {
|
||||||
|
used := false
|
||||||
|
randPkg := imports.NewImport("math/rand")
|
||||||
|
timePkg := imports.NewImport("time")
|
||||||
|
testingPkg := imports.NewImport("testing")
|
||||||
|
protoPkg := imports.NewImport("github.com/gogo/protobuf/proto")
|
||||||
|
unsafePkg := imports.NewImport("unsafe")
|
||||||
|
if !gogoproto.ImportsGoGoProto(file.FileDescriptorProto) {
|
||||||
|
protoPkg = imports.NewImport("github.com/golang/protobuf/proto")
|
||||||
|
}
|
||||||
|
for _, message := range file.Messages() {
|
||||||
|
ccTypeName := generator.CamelCaseSlice(message.TypeName())
|
||||||
|
if !gogoproto.HasCompare(file.FileDescriptorProto, message.DescriptorProto) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if message.DescriptorProto.GetOptions().GetMapEntry() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if gogoproto.HasTestGen(file.FileDescriptorProto, message.DescriptorProto) {
|
||||||
|
used = true
|
||||||
|
hasUnsafe := gogoproto.IsUnsafeMarshaler(file.FileDescriptorProto, message.DescriptorProto) ||
|
||||||
|
gogoproto.IsUnsafeUnmarshaler(file.FileDescriptorProto, message.DescriptorProto)
|
||||||
|
p.P(`func Test`, ccTypeName, `Compare(t *`, testingPkg.Use(), `.T) {`)
|
||||||
|
p.In()
|
||||||
|
if hasUnsafe {
|
||||||
|
p.P(`var bigendian uint32 = 0x01020304`)
|
||||||
|
p.P(`if *(*byte)(`, unsafePkg.Use(), `.Pointer(&bigendian)) == 1 {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`t.Skip("unsafe does not work on big endian architectures")`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
}
|
||||||
|
p.P(`popr := `, randPkg.Use(), `.New(`, randPkg.Use(), `.NewSource(`, timePkg.Use(), `.Now().UnixNano()))`)
|
||||||
|
p.P(`p := NewPopulated`, ccTypeName, `(popr, false)`)
|
||||||
|
p.P(`dAtA, err := `, protoPkg.Use(), `.Marshal(p)`)
|
||||||
|
p.P(`if err != nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`panic(err)`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`msg := &`, ccTypeName, `{}`)
|
||||||
|
p.P(`if err := `, protoPkg.Use(), `.Unmarshal(dAtA, msg); err != nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`panic(err)`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`if c := p.Compare(msg); c != 0 {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`t.Fatalf("%#v !Compare %#v, since %d", msg, p, c)`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`p2 := NewPopulated`, ccTypeName, `(popr, false)`)
|
||||||
|
p.P(`c := p.Compare(p2)`)
|
||||||
|
p.P(`c2 := p2.Compare(p)`)
|
||||||
|
p.P(`if c != (-1 * c2) {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`t.Errorf("p.Compare(p2) = %d", c)`)
|
||||||
|
p.P(`t.Errorf("p2.Compare(p) = %d", c2)`)
|
||||||
|
p.P(`t.Errorf("p = %#v", p)`)
|
||||||
|
p.P(`t.Errorf("p2 = %#v", p2)`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return used
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
testgen.RegisterTestPlugin(NewTest)
|
||||||
|
}
|
133
vendor/github.com/gogo/protobuf/plugin/defaultcheck/defaultcheck.go
generated
vendored
Normal file
133
vendor/github.com/gogo/protobuf/plugin/defaultcheck/defaultcheck.go
generated
vendored
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
// Protocol Buffers for Go with Gadgets
|
||||||
|
//
|
||||||
|
// Copyright (c) 2013, The GoGo Authors. All rights reserved.
|
||||||
|
// http://github.com/gogo/protobuf
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
/*
|
||||||
|
The defaultcheck plugin is used to check whether nullable is not used incorrectly.
|
||||||
|
For instance:
|
||||||
|
An error is caused if a nullable field:
|
||||||
|
- has a default value,
|
||||||
|
- is an enum which does not start at zero,
|
||||||
|
- is used for an extension,
|
||||||
|
- is used for a native proto3 type,
|
||||||
|
- is used for a repeated native type.
|
||||||
|
|
||||||
|
An error is also caused if a field with a default value is used in a message:
|
||||||
|
- which is a face.
|
||||||
|
- without getters.
|
||||||
|
|
||||||
|
It is enabled by the following extensions:
|
||||||
|
|
||||||
|
- nullable
|
||||||
|
|
||||||
|
For incorrect usage of nullable with tests see:
|
||||||
|
|
||||||
|
github.com/gogo/protobuf/test/nullableconflict
|
||||||
|
|
||||||
|
*/
|
||||||
|
package defaultcheck
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/gogo/protobuf/gogoproto"
|
||||||
|
"github.com/gogo/protobuf/protoc-gen-gogo/generator"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
type plugin struct {
|
||||||
|
*generator.Generator
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPlugin() *plugin {
|
||||||
|
return &plugin{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *plugin) Name() string {
|
||||||
|
return "defaultcheck"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *plugin) Init(g *generator.Generator) {
|
||||||
|
p.Generator = g
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *plugin) Generate(file *generator.FileDescriptor) {
|
||||||
|
proto3 := gogoproto.IsProto3(file.FileDescriptorProto)
|
||||||
|
for _, msg := range file.Messages() {
|
||||||
|
getters := gogoproto.HasGoGetters(file.FileDescriptorProto, msg.DescriptorProto)
|
||||||
|
face := gogoproto.IsFace(file.FileDescriptorProto, msg.DescriptorProto)
|
||||||
|
for _, field := range msg.GetField() {
|
||||||
|
if len(field.GetDefaultValue()) > 0 {
|
||||||
|
if !getters {
|
||||||
|
fmt.Fprintf(os.Stderr, "ERROR: field %v.%v cannot have a default value and not have a getter method", generator.CamelCase(*msg.Name), generator.CamelCase(*field.Name))
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
if face {
|
||||||
|
fmt.Fprintf(os.Stderr, "ERROR: field %v.%v cannot have a default value be in a face", generator.CamelCase(*msg.Name), generator.CamelCase(*field.Name))
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if gogoproto.IsNullable(field) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if len(field.GetDefaultValue()) > 0 {
|
||||||
|
fmt.Fprintf(os.Stderr, "ERROR: field %v.%v cannot be non-nullable and have a default value", generator.CamelCase(*msg.Name), generator.CamelCase(*field.Name))
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
if !field.IsMessage() && !gogoproto.IsCustomType(field) {
|
||||||
|
if field.IsRepeated() {
|
||||||
|
fmt.Fprintf(os.Stderr, "WARNING: field %v.%v is a repeated non-nullable native type, nullable=false has no effect\n", generator.CamelCase(*msg.Name), generator.CamelCase(*field.Name))
|
||||||
|
} else if proto3 {
|
||||||
|
fmt.Fprintf(os.Stderr, "ERROR: field %v.%v is a native type and in proto3 syntax with nullable=false there exists conflicting implementations when encoding zero values", generator.CamelCase(*msg.Name), generator.CamelCase(*field.Name))
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
if field.IsBytes() {
|
||||||
|
fmt.Fprintf(os.Stderr, "WARNING: field %v.%v is a non-nullable bytes type, nullable=false has no effect\n", generator.CamelCase(*msg.Name), generator.CamelCase(*field.Name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !field.IsEnum() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
enum := p.ObjectNamed(field.GetTypeName()).(*generator.EnumDescriptor)
|
||||||
|
if len(enum.Value) == 0 || enum.Value[0].GetNumber() != 0 {
|
||||||
|
fmt.Fprintf(os.Stderr, "ERROR: field %v.%v cannot be non-nullable and be an enum type %v which does not start with zero", generator.CamelCase(*msg.Name), generator.CamelCase(*field.Name), enum.GetName())
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, e := range file.GetExtension() {
|
||||||
|
if !gogoproto.IsNullable(e) {
|
||||||
|
fmt.Fprintf(os.Stderr, "ERROR: extended field %v cannot be nullable %v", generator.CamelCase(e.GetName()), generator.CamelCase(*e.Name))
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *plugin) GenerateImports(*generator.FileDescriptor) {}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
generator.RegisterPlugin(NewPlugin())
|
||||||
|
}
|
201
vendor/github.com/gogo/protobuf/plugin/description/description.go
generated
vendored
Normal file
201
vendor/github.com/gogo/protobuf/plugin/description/description.go
generated
vendored
Normal file
|
@ -0,0 +1,201 @@
|
||||||
|
// Protocol Buffers for Go with Gadgets
|
||||||
|
//
|
||||||
|
// Copyright (c) 2013, The GoGo Authors. All rights reserved.
|
||||||
|
// http://github.com/gogo/protobuf
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
/*
|
||||||
|
The description (experimental) plugin generates a Description method for each message.
|
||||||
|
The Description method returns a populated google_protobuf.FileDescriptorSet struct.
|
||||||
|
This contains the description of the files used to generate this message.
|
||||||
|
|
||||||
|
It is enabled by the following extensions:
|
||||||
|
|
||||||
|
- description
|
||||||
|
- description_all
|
||||||
|
|
||||||
|
The description plugin also generates a test given it is enabled using one of the following extensions:
|
||||||
|
|
||||||
|
- testgen
|
||||||
|
- testgen_all
|
||||||
|
|
||||||
|
Let us look at:
|
||||||
|
|
||||||
|
github.com/gogo/protobuf/test/example/example.proto
|
||||||
|
|
||||||
|
Btw all the output can be seen at:
|
||||||
|
|
||||||
|
github.com/gogo/protobuf/test/example/*
|
||||||
|
|
||||||
|
The following message:
|
||||||
|
|
||||||
|
message B {
|
||||||
|
option (gogoproto.description) = true;
|
||||||
|
optional A A = 1 [(gogoproto.nullable) = false, (gogoproto.embed) = true];
|
||||||
|
repeated bytes G = 2 [(gogoproto.customtype) = "github.com/gogo/protobuf/test/custom.Uint128", (gogoproto.nullable) = false];
|
||||||
|
}
|
||||||
|
|
||||||
|
given to the description plugin, will generate the following code:
|
||||||
|
|
||||||
|
func (this *B) Description() (desc *google_protobuf.FileDescriptorSet) {
|
||||||
|
return ExampleDescription()
|
||||||
|
}
|
||||||
|
|
||||||
|
and the following test code:
|
||||||
|
|
||||||
|
func TestDescription(t *testing9.T) {
|
||||||
|
ExampleDescription()
|
||||||
|
}
|
||||||
|
|
||||||
|
The hope is to use this struct in some way instead of reflect.
|
||||||
|
This package is subject to change, since a use has not been figured out yet.
|
||||||
|
|
||||||
|
*/
|
||||||
|
package description
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"compress/gzip"
|
||||||
|
"fmt"
|
||||||
|
"github.com/gogo/protobuf/gogoproto"
|
||||||
|
"github.com/gogo/protobuf/proto"
|
||||||
|
descriptor "github.com/gogo/protobuf/protoc-gen-gogo/descriptor"
|
||||||
|
"github.com/gogo/protobuf/protoc-gen-gogo/generator"
|
||||||
|
)
|
||||||
|
|
||||||
|
type plugin struct {
|
||||||
|
*generator.Generator
|
||||||
|
generator.PluginImports
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPlugin() *plugin {
|
||||||
|
return &plugin{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *plugin) Name() string {
|
||||||
|
return "description"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *plugin) Init(g *generator.Generator) {
|
||||||
|
p.Generator = g
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *plugin) Generate(file *generator.FileDescriptor) {
|
||||||
|
used := false
|
||||||
|
localName := generator.FileName(file)
|
||||||
|
|
||||||
|
p.PluginImports = generator.NewPluginImports(p.Generator)
|
||||||
|
descriptorPkg := p.NewImport("github.com/gogo/protobuf/protoc-gen-gogo/descriptor")
|
||||||
|
protoPkg := p.NewImport("github.com/gogo/protobuf/proto")
|
||||||
|
gzipPkg := p.NewImport("compress/gzip")
|
||||||
|
bytesPkg := p.NewImport("bytes")
|
||||||
|
ioutilPkg := p.NewImport("io/ioutil")
|
||||||
|
|
||||||
|
for _, message := range file.Messages() {
|
||||||
|
if !gogoproto.HasDescription(file.FileDescriptorProto, message.DescriptorProto) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if message.DescriptorProto.GetOptions().GetMapEntry() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
used = true
|
||||||
|
ccTypeName := generator.CamelCaseSlice(message.TypeName())
|
||||||
|
p.P(`func (this *`, ccTypeName, `) Description() (desc *`, descriptorPkg.Use(), `.FileDescriptorSet) {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`return `, localName, `Description()`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if used {
|
||||||
|
|
||||||
|
p.P(`func `, localName, `Description() (desc *`, descriptorPkg.Use(), `.FileDescriptorSet) {`)
|
||||||
|
p.In()
|
||||||
|
//Don't generate SourceCodeInfo, since it will create too much code.
|
||||||
|
|
||||||
|
ss := make([]*descriptor.SourceCodeInfo, 0)
|
||||||
|
for _, f := range p.Generator.AllFiles().GetFile() {
|
||||||
|
ss = append(ss, f.SourceCodeInfo)
|
||||||
|
f.SourceCodeInfo = nil
|
||||||
|
}
|
||||||
|
b, err := proto.Marshal(p.Generator.AllFiles())
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
for i, f := range p.Generator.AllFiles().GetFile() {
|
||||||
|
f.SourceCodeInfo = ss[i]
|
||||||
|
}
|
||||||
|
p.P(`d := &`, descriptorPkg.Use(), `.FileDescriptorSet{}`)
|
||||||
|
var buf bytes.Buffer
|
||||||
|
w, _ := gzip.NewWriterLevel(&buf, gzip.BestCompression)
|
||||||
|
w.Write(b)
|
||||||
|
w.Close()
|
||||||
|
b = buf.Bytes()
|
||||||
|
p.P("var gzipped = []byte{")
|
||||||
|
p.In()
|
||||||
|
p.P("// ", len(b), " bytes of a gzipped FileDescriptorSet")
|
||||||
|
for len(b) > 0 {
|
||||||
|
n := 16
|
||||||
|
if n > len(b) {
|
||||||
|
n = len(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
s := ""
|
||||||
|
for _, c := range b[:n] {
|
||||||
|
s += fmt.Sprintf("0x%02x,", c)
|
||||||
|
}
|
||||||
|
p.P(s)
|
||||||
|
|
||||||
|
b = b[n:]
|
||||||
|
}
|
||||||
|
p.Out()
|
||||||
|
p.P("}")
|
||||||
|
p.P(`r := `, bytesPkg.Use(), `.NewReader(gzipped)`)
|
||||||
|
p.P(`gzipr, err := `, gzipPkg.Use(), `.NewReader(r)`)
|
||||||
|
p.P(`if err != nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`panic(err)`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`ungzipped, err := `, ioutilPkg.Use(), `.ReadAll(gzipr)`)
|
||||||
|
p.P(`if err != nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`panic(err)`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`if err := `, protoPkg.Use(), `.Unmarshal(ungzipped, d); err != nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`panic(err)`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`return d`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
generator.RegisterPlugin(NewPlugin())
|
||||||
|
}
|
73
vendor/github.com/gogo/protobuf/plugin/description/descriptiontest.go
generated
vendored
Normal file
73
vendor/github.com/gogo/protobuf/plugin/description/descriptiontest.go
generated
vendored
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
// Protocol Buffers for Go with Gadgets
|
||||||
|
//
|
||||||
|
// Copyright (c) 2013, The GoGo Authors. All rights reserved.
|
||||||
|
// http://github.com/gogo/protobuf
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
package description
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogo/protobuf/gogoproto"
|
||||||
|
"github.com/gogo/protobuf/plugin/testgen"
|
||||||
|
"github.com/gogo/protobuf/protoc-gen-gogo/generator"
|
||||||
|
)
|
||||||
|
|
||||||
|
type test struct {
|
||||||
|
*generator.Generator
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTest(g *generator.Generator) testgen.TestPlugin {
|
||||||
|
return &test{g}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *test) Generate(imports generator.PluginImports, file *generator.FileDescriptor) bool {
|
||||||
|
used := false
|
||||||
|
testingPkg := imports.NewImport("testing")
|
||||||
|
for _, message := range file.Messages() {
|
||||||
|
if !gogoproto.HasDescription(file.FileDescriptorProto, message.DescriptorProto) ||
|
||||||
|
!gogoproto.HasTestGen(file.FileDescriptorProto, message.DescriptorProto) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if message.DescriptorProto.GetOptions().GetMapEntry() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
used = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if used {
|
||||||
|
localName := generator.FileName(file)
|
||||||
|
p.P(`func Test`, localName, `Description(t *`, testingPkg.Use(), `.T) {`)
|
||||||
|
p.In()
|
||||||
|
p.P(localName, `Description()`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
|
||||||
|
}
|
||||||
|
return used
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
testgen.RegisterTestPlugin(NewTest)
|
||||||
|
}
|
|
@ -0,0 +1,200 @@
|
||||||
|
// Protocol Buffers for Go with Gadgets
|
||||||
|
//
|
||||||
|
// Copyright (c) 2013, The GoGo Authors. All rights reserved.
|
||||||
|
// http://github.com/gogo/protobuf
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
/*
|
||||||
|
The embedcheck plugin is used to check whether embed is not used incorrectly.
|
||||||
|
For instance:
|
||||||
|
An embedded message has a generated string method, but the is a member of a message which does not.
|
||||||
|
This causes a warning.
|
||||||
|
An error is caused by a namespace conflict.
|
||||||
|
|
||||||
|
It is enabled by the following extensions:
|
||||||
|
|
||||||
|
- embed
|
||||||
|
- embed_all
|
||||||
|
|
||||||
|
For incorrect usage of embed with tests see:
|
||||||
|
|
||||||
|
github.com/gogo/protobuf/test/embedconflict
|
||||||
|
|
||||||
|
*/
|
||||||
|
package embedcheck
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/gogo/protobuf/gogoproto"
|
||||||
|
"github.com/gogo/protobuf/protoc-gen-gogo/generator"
|
||||||
|
)
|
||||||
|
|
||||||
|
type plugin struct {
|
||||||
|
*generator.Generator
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPlugin() *plugin {
|
||||||
|
return &plugin{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *plugin) Name() string {
|
||||||
|
return "embedcheck"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *plugin) Init(g *generator.Generator) {
|
||||||
|
p.Generator = g
|
||||||
|
}
|
||||||
|
|
||||||
|
var overwriters []map[string]gogoproto.EnableFunc = []map[string]gogoproto.EnableFunc{
|
||||||
|
{
|
||||||
|
"stringer": gogoproto.IsStringer,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gostring": gogoproto.HasGoString,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"equal": gogoproto.HasEqual,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verboseequal": gogoproto.HasVerboseEqual,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size": gogoproto.IsSizer,
|
||||||
|
"protosizer": gogoproto.IsProtoSizer,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"unmarshaler": gogoproto.IsUnmarshaler,
|
||||||
|
"unsafe_unmarshaler": gogoproto.IsUnsafeUnmarshaler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"marshaler": gogoproto.IsMarshaler,
|
||||||
|
"unsafe_marshaler": gogoproto.IsUnsafeMarshaler,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *plugin) Generate(file *generator.FileDescriptor) {
|
||||||
|
for _, msg := range file.Messages() {
|
||||||
|
for _, os := range overwriters {
|
||||||
|
possible := true
|
||||||
|
for _, overwriter := range os {
|
||||||
|
if overwriter(file.FileDescriptorProto, msg.DescriptorProto) {
|
||||||
|
possible = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if possible {
|
||||||
|
p.checkOverwrite(msg, os)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.checkNameSpace(msg)
|
||||||
|
for _, field := range msg.GetField() {
|
||||||
|
if gogoproto.IsEmbed(field) && gogoproto.IsCustomName(field) {
|
||||||
|
fmt.Fprintf(os.Stderr, "ERROR: field %v with custom name %v cannot be embedded", *field.Name, gogoproto.GetCustomName(field))
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.checkRepeated(msg)
|
||||||
|
}
|
||||||
|
for _, e := range file.GetExtension() {
|
||||||
|
if gogoproto.IsEmbed(e) {
|
||||||
|
fmt.Fprintf(os.Stderr, "ERROR: extended field %v cannot be embedded", generator.CamelCase(*e.Name))
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *plugin) checkNameSpace(message *generator.Descriptor) map[string]bool {
|
||||||
|
ccTypeName := generator.CamelCaseSlice(message.TypeName())
|
||||||
|
names := make(map[string]bool)
|
||||||
|
for _, field := range message.Field {
|
||||||
|
fieldname := generator.CamelCase(*field.Name)
|
||||||
|
if field.IsMessage() && gogoproto.IsEmbed(field) {
|
||||||
|
desc := p.ObjectNamed(field.GetTypeName())
|
||||||
|
moreNames := p.checkNameSpace(desc.(*generator.Descriptor))
|
||||||
|
for another := range moreNames {
|
||||||
|
if names[another] {
|
||||||
|
fmt.Fprintf(os.Stderr, "ERROR: duplicate embedded fieldname %v in type %v\n", fieldname, ccTypeName)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
names[another] = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if names[fieldname] {
|
||||||
|
fmt.Fprintf(os.Stderr, "ERROR: duplicate embedded fieldname %v in type %v\n", fieldname, ccTypeName)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
names[fieldname] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return names
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *plugin) checkOverwrite(message *generator.Descriptor, enablers map[string]gogoproto.EnableFunc) {
|
||||||
|
ccTypeName := generator.CamelCaseSlice(message.TypeName())
|
||||||
|
names := []string{}
|
||||||
|
for name := range enablers {
|
||||||
|
names = append(names, name)
|
||||||
|
}
|
||||||
|
for _, field := range message.Field {
|
||||||
|
if field.IsMessage() && gogoproto.IsEmbed(field) {
|
||||||
|
fieldname := generator.CamelCase(*field.Name)
|
||||||
|
desc := p.ObjectNamed(field.GetTypeName())
|
||||||
|
msg := desc.(*generator.Descriptor)
|
||||||
|
for errStr, enabled := range enablers {
|
||||||
|
if enabled(msg.File().FileDescriptorProto, msg.DescriptorProto) {
|
||||||
|
fmt.Fprintf(os.Stderr, "WARNING: found non-%v %v with embedded %v %v\n", names, ccTypeName, errStr, fieldname)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.checkOverwrite(msg, enablers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *plugin) checkRepeated(message *generator.Descriptor) {
|
||||||
|
ccTypeName := generator.CamelCaseSlice(message.TypeName())
|
||||||
|
for _, field := range message.Field {
|
||||||
|
if !gogoproto.IsEmbed(field) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if field.IsBytes() {
|
||||||
|
fieldname := generator.CamelCase(*field.Name)
|
||||||
|
fmt.Fprintf(os.Stderr, "ERROR: found embedded bytes field %s in message %s\n", fieldname, ccTypeName)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
if !field.IsRepeated() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fieldname := generator.CamelCase(*field.Name)
|
||||||
|
fmt.Fprintf(os.Stderr, "ERROR: found repeated embedded field %s in message %s\n", fieldname, ccTypeName)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *plugin) GenerateImports(*generator.FileDescriptor) {}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
generator.RegisterPlugin(NewPlugin())
|
||||||
|
}
|
104
vendor/github.com/gogo/protobuf/plugin/enumstringer/enumstringer.go
generated
vendored
Normal file
104
vendor/github.com/gogo/protobuf/plugin/enumstringer/enumstringer.go
generated
vendored
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
// Protocol Buffers for Go with Gadgets
|
||||||
|
//
|
||||||
|
// Copyright (c) 2013, The GoGo Authors. All rights reserved.
|
||||||
|
// http://github.com/gogo/protobuf
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
/*
|
||||||
|
The enumstringer (experimental) plugin generates a String method for each enum.
|
||||||
|
|
||||||
|
It is enabled by the following extensions:
|
||||||
|
|
||||||
|
- enum_stringer
|
||||||
|
- enum_stringer_all
|
||||||
|
|
||||||
|
This package is subject to change.
|
||||||
|
|
||||||
|
*/
|
||||||
|
package enumstringer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogo/protobuf/gogoproto"
|
||||||
|
"github.com/gogo/protobuf/protoc-gen-gogo/generator"
|
||||||
|
)
|
||||||
|
|
||||||
|
type enumstringer struct {
|
||||||
|
*generator.Generator
|
||||||
|
generator.PluginImports
|
||||||
|
atleastOne bool
|
||||||
|
localName string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewEnumStringer() *enumstringer {
|
||||||
|
return &enumstringer{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *enumstringer) Name() string {
|
||||||
|
return "enumstringer"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *enumstringer) Init(g *generator.Generator) {
|
||||||
|
p.Generator = g
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *enumstringer) Generate(file *generator.FileDescriptor) {
|
||||||
|
p.PluginImports = generator.NewPluginImports(p.Generator)
|
||||||
|
p.atleastOne = false
|
||||||
|
|
||||||
|
p.localName = generator.FileName(file)
|
||||||
|
|
||||||
|
strconvPkg := p.NewImport("strconv")
|
||||||
|
|
||||||
|
for _, enum := range file.Enums() {
|
||||||
|
if !gogoproto.IsEnumStringer(file.FileDescriptorProto, enum.EnumDescriptorProto) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if gogoproto.IsGoEnumStringer(file.FileDescriptorProto, enum.EnumDescriptorProto) {
|
||||||
|
panic("Go enum stringer conflicts with new enumstringer plugin: please use gogoproto.goproto_enum_stringer or gogoproto.goproto_enum_string_all and set it to false")
|
||||||
|
}
|
||||||
|
p.atleastOne = true
|
||||||
|
ccTypeName := generator.CamelCaseSlice(enum.TypeName())
|
||||||
|
p.P("func (x ", ccTypeName, ") String() string {")
|
||||||
|
p.In()
|
||||||
|
p.P(`s, ok := `, ccTypeName, `_name[int32(x)]`)
|
||||||
|
p.P(`if ok {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`return s`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`return `, strconvPkg.Use(), `.Itoa(int(x))`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !p.atleastOne {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
generator.RegisterPlugin(NewEnumStringer())
|
||||||
|
}
|
|
@ -0,0 +1,694 @@
|
||||||
|
// Protocol Buffers for Go with Gadgets
|
||||||
|
//
|
||||||
|
// Copyright (c) 2013, The GoGo Authors. All rights reserved.
|
||||||
|
// http://github.com/gogo/protobuf
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
/*
|
||||||
|
The equal plugin generates an Equal and a VerboseEqual method for each message.
|
||||||
|
These equal methods are quite obvious.
|
||||||
|
The only difference is that VerboseEqual returns a non nil error if it is not equal.
|
||||||
|
This error contains more detail on exactly which part of the message was not equal to the other message.
|
||||||
|
The idea is that this is useful for debugging.
|
||||||
|
|
||||||
|
Equal is enabled using the following extensions:
|
||||||
|
|
||||||
|
- equal
|
||||||
|
- equal_all
|
||||||
|
|
||||||
|
While VerboseEqual is enable dusing the following extensions:
|
||||||
|
|
||||||
|
- verbose_equal
|
||||||
|
- verbose_equal_all
|
||||||
|
|
||||||
|
The equal plugin also generates a test given it is enabled using one of the following extensions:
|
||||||
|
|
||||||
|
- testgen
|
||||||
|
- testgen_all
|
||||||
|
|
||||||
|
Let us look at:
|
||||||
|
|
||||||
|
github.com/gogo/protobuf/test/example/example.proto
|
||||||
|
|
||||||
|
Btw all the output can be seen at:
|
||||||
|
|
||||||
|
github.com/gogo/protobuf/test/example/*
|
||||||
|
|
||||||
|
The following message:
|
||||||
|
|
||||||
|
option (gogoproto.equal_all) = true;
|
||||||
|
option (gogoproto.verbose_equal_all) = true;
|
||||||
|
|
||||||
|
message B {
|
||||||
|
optional A A = 1 [(gogoproto.nullable) = false, (gogoproto.embed) = true];
|
||||||
|
repeated bytes G = 2 [(gogoproto.customtype) = "github.com/gogo/protobuf/test/custom.Uint128", (gogoproto.nullable) = false];
|
||||||
|
}
|
||||||
|
|
||||||
|
given to the equal plugin, will generate the following code:
|
||||||
|
|
||||||
|
func (this *B) VerboseEqual(that interface{}) error {
|
||||||
|
if that == nil {
|
||||||
|
if this == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt2.Errorf("that == nil && this != nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
that1, ok := that.(*B)
|
||||||
|
if !ok {
|
||||||
|
return fmt2.Errorf("that is not of type *B")
|
||||||
|
}
|
||||||
|
if that1 == nil {
|
||||||
|
if this == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt2.Errorf("that is type *B but is nil && this != nil")
|
||||||
|
} else if this == nil {
|
||||||
|
return fmt2.Errorf("that is type *B but is not nil && this == nil")
|
||||||
|
}
|
||||||
|
if !this.A.Equal(&that1.A) {
|
||||||
|
return fmt2.Errorf("A this(%v) Not Equal that(%v)", this.A, that1.A)
|
||||||
|
}
|
||||||
|
if len(this.G) != len(that1.G) {
|
||||||
|
return fmt2.Errorf("G this(%v) Not Equal that(%v)", len(this.G), len(that1.G))
|
||||||
|
}
|
||||||
|
for i := range this.G {
|
||||||
|
if !this.G[i].Equal(that1.G[i]) {
|
||||||
|
return fmt2.Errorf("G this[%v](%v) Not Equal that[%v](%v)", i, this.G[i], i, that1.G[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) {
|
||||||
|
return fmt2.Errorf("XXX_unrecognized this(%v) Not Equal that(%v)", this.XXX_unrecognized, that1.XXX_unrecognized)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *B) Equal(that interface{}) bool {
|
||||||
|
if that == nil {
|
||||||
|
return this == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
that1, ok := that.(*B)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if that1 == nil {
|
||||||
|
return this == nil
|
||||||
|
} else if this == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !this.A.Equal(&that1.A) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if len(this.G) != len(that1.G) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i := range this.G {
|
||||||
|
if !this.G[i].Equal(that1.G[i]) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
and the following test code:
|
||||||
|
|
||||||
|
func TestBVerboseEqual(t *testing8.T) {
|
||||||
|
popr := math_rand8.New(math_rand8.NewSource(time8.Now().UnixNano()))
|
||||||
|
p := NewPopulatedB(popr, false)
|
||||||
|
dAtA, err := github_com_gogo_protobuf_proto2.Marshal(p)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
msg := &B{}
|
||||||
|
if err := github_com_gogo_protobuf_proto2.Unmarshal(dAtA, msg); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if err := p.VerboseEqual(msg); err != nil {
|
||||||
|
t.Fatalf("%#v !VerboseEqual %#v, since %v", msg, p, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
package equal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogo/protobuf/gogoproto"
|
||||||
|
"github.com/gogo/protobuf/proto"
|
||||||
|
descriptor "github.com/gogo/protobuf/protoc-gen-gogo/descriptor"
|
||||||
|
"github.com/gogo/protobuf/protoc-gen-gogo/generator"
|
||||||
|
"github.com/gogo/protobuf/vanity"
|
||||||
|
)
|
||||||
|
|
||||||
|
type plugin struct {
|
||||||
|
*generator.Generator
|
||||||
|
generator.PluginImports
|
||||||
|
fmtPkg generator.Single
|
||||||
|
bytesPkg generator.Single
|
||||||
|
protoPkg generator.Single
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPlugin() *plugin {
|
||||||
|
return &plugin{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *plugin) Name() string {
|
||||||
|
return "equal"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *plugin) Init(g *generator.Generator) {
|
||||||
|
p.Generator = g
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *plugin) Generate(file *generator.FileDescriptor) {
|
||||||
|
p.PluginImports = generator.NewPluginImports(p.Generator)
|
||||||
|
p.fmtPkg = p.NewImport("fmt")
|
||||||
|
p.bytesPkg = p.NewImport("bytes")
|
||||||
|
p.protoPkg = p.NewImport("github.com/gogo/protobuf/proto")
|
||||||
|
|
||||||
|
for _, msg := range file.Messages() {
|
||||||
|
if msg.DescriptorProto.GetOptions().GetMapEntry() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if gogoproto.HasVerboseEqual(file.FileDescriptorProto, msg.DescriptorProto) {
|
||||||
|
p.generateMessage(file, msg, true)
|
||||||
|
}
|
||||||
|
if gogoproto.HasEqual(file.FileDescriptorProto, msg.DescriptorProto) {
|
||||||
|
p.generateMessage(file, msg, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *plugin) generateNullableField(fieldname string, verbose bool) {
|
||||||
|
p.P(`if this.`, fieldname, ` != nil && that1.`, fieldname, ` != nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`if *this.`, fieldname, ` != *that1.`, fieldname, `{`)
|
||||||
|
p.In()
|
||||||
|
if verbose {
|
||||||
|
p.P(`return `, p.fmtPkg.Use(), `.Errorf("`, fieldname, ` this(%v) Not Equal that(%v)", *this.`, fieldname, `, *that1.`, fieldname, `)`)
|
||||||
|
} else {
|
||||||
|
p.P(`return false`)
|
||||||
|
}
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`} else if this.`, fieldname, ` != nil {`)
|
||||||
|
p.In()
|
||||||
|
if verbose {
|
||||||
|
p.P(`return `, p.fmtPkg.Use(), `.Errorf("this.`, fieldname, ` == nil && that.`, fieldname, ` != nil")`)
|
||||||
|
} else {
|
||||||
|
p.P(`return false`)
|
||||||
|
}
|
||||||
|
p.Out()
|
||||||
|
p.P(`} else if that1.`, fieldname, ` != nil {`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *plugin) generateMsgNullAndTypeCheck(ccTypeName string, verbose bool) {
|
||||||
|
p.P(`if that == nil {`)
|
||||||
|
p.In()
|
||||||
|
if verbose {
|
||||||
|
p.P(`if this == nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`return nil`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`return `, p.fmtPkg.Use(), `.Errorf("that == nil && this != nil")`)
|
||||||
|
} else {
|
||||||
|
p.P(`return this == nil`)
|
||||||
|
}
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(``)
|
||||||
|
p.P(`that1, ok := that.(*`, ccTypeName, `)`)
|
||||||
|
p.P(`if !ok {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`that2, ok := that.(`, ccTypeName, `)`)
|
||||||
|
p.P(`if ok {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`that1 = &that2`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`} else {`)
|
||||||
|
p.In()
|
||||||
|
if verbose {
|
||||||
|
p.P(`return `, p.fmtPkg.Use(), `.Errorf("that is not of type *`, ccTypeName, `")`)
|
||||||
|
} else {
|
||||||
|
p.P(`return false`)
|
||||||
|
}
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`if that1 == nil {`)
|
||||||
|
p.In()
|
||||||
|
if verbose {
|
||||||
|
p.P(`if this == nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`return nil`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`return `, p.fmtPkg.Use(), `.Errorf("that is type *`, ccTypeName, ` but is nil && this != nil")`)
|
||||||
|
} else {
|
||||||
|
p.P(`return this == nil`)
|
||||||
|
}
|
||||||
|
p.Out()
|
||||||
|
p.P(`} else if this == nil {`)
|
||||||
|
p.In()
|
||||||
|
if verbose {
|
||||||
|
p.P(`return `, p.fmtPkg.Use(), `.Errorf("that is type *`, ccTypeName, ` but is not nil && this == nil")`)
|
||||||
|
} else {
|
||||||
|
p.P(`return false`)
|
||||||
|
}
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *plugin) generateField(file *generator.FileDescriptor, message *generator.Descriptor, field *descriptor.FieldDescriptorProto, verbose bool) {
|
||||||
|
proto3 := gogoproto.IsProto3(file.FileDescriptorProto)
|
||||||
|
fieldname := p.GetOneOfFieldName(message, field)
|
||||||
|
repeated := field.IsRepeated()
|
||||||
|
ctype := gogoproto.IsCustomType(field)
|
||||||
|
nullable := gogoproto.IsNullable(field)
|
||||||
|
isNormal := (gogoproto.IsStdDuration(field) ||
|
||||||
|
gogoproto.IsStdDouble(field) ||
|
||||||
|
gogoproto.IsStdFloat(field) ||
|
||||||
|
gogoproto.IsStdInt64(field) ||
|
||||||
|
gogoproto.IsStdUInt64(field) ||
|
||||||
|
gogoproto.IsStdInt32(field) ||
|
||||||
|
gogoproto.IsStdUInt32(field) ||
|
||||||
|
gogoproto.IsStdBool(field) ||
|
||||||
|
gogoproto.IsStdString(field))
|
||||||
|
isBytes := gogoproto.IsStdBytes(field)
|
||||||
|
isTimestamp := gogoproto.IsStdTime(field)
|
||||||
|
// oneof := field.OneofIndex != nil
|
||||||
|
if !repeated {
|
||||||
|
if ctype || isTimestamp {
|
||||||
|
if nullable {
|
||||||
|
p.P(`if that1.`, fieldname, ` == nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`if this.`, fieldname, ` != nil {`)
|
||||||
|
p.In()
|
||||||
|
if verbose {
|
||||||
|
p.P(`return `, p.fmtPkg.Use(), `.Errorf("this.`, fieldname, ` != nil && that1.`, fieldname, ` == nil")`)
|
||||||
|
} else {
|
||||||
|
p.P(`return false`)
|
||||||
|
}
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`} else if !this.`, fieldname, `.Equal(*that1.`, fieldname, `) {`)
|
||||||
|
} else {
|
||||||
|
p.P(`if !this.`, fieldname, `.Equal(that1.`, fieldname, `) {`)
|
||||||
|
}
|
||||||
|
p.In()
|
||||||
|
if verbose {
|
||||||
|
p.P(`return `, p.fmtPkg.Use(), `.Errorf("`, fieldname, ` this(%v) Not Equal that(%v)", this.`, fieldname, `, that1.`, fieldname, `)`)
|
||||||
|
} else {
|
||||||
|
p.P(`return false`)
|
||||||
|
}
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
} else if isNormal {
|
||||||
|
if nullable {
|
||||||
|
p.generateNullableField(fieldname, verbose)
|
||||||
|
} else {
|
||||||
|
p.P(`if this.`, fieldname, ` != that1.`, fieldname, `{`)
|
||||||
|
}
|
||||||
|
p.In()
|
||||||
|
if verbose {
|
||||||
|
p.P(`return `, p.fmtPkg.Use(), `.Errorf("`, fieldname, ` this(%v) Not Equal that(%v)", this.`, fieldname, `, that1.`, fieldname, `)`)
|
||||||
|
} else {
|
||||||
|
p.P(`return false`)
|
||||||
|
}
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
} else if isBytes {
|
||||||
|
if nullable {
|
||||||
|
p.P(`if that1.`, fieldname, ` == nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`if this.`, fieldname, ` != nil {`)
|
||||||
|
p.In()
|
||||||
|
if verbose {
|
||||||
|
p.P(`return `, p.fmtPkg.Use(), `.Errorf("this.`, fieldname, ` != nil && that1.`, fieldname, ` == nil")`)
|
||||||
|
} else {
|
||||||
|
p.P(`return false`)
|
||||||
|
}
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`} else if !`, p.bytesPkg.Use(), `.Equal(*this.`, fieldname, `, *that1.`, fieldname, `) {`)
|
||||||
|
} else {
|
||||||
|
p.P(`if !`, p.bytesPkg.Use(), `.Equal(this.`, fieldname, `, that1.`, fieldname, `) {`)
|
||||||
|
}
|
||||||
|
p.In()
|
||||||
|
if verbose {
|
||||||
|
p.P(`return `, p.fmtPkg.Use(), `.Errorf("`, fieldname, ` this(%v) Not Equal that(%v)", this.`, fieldname, `, that1.`, fieldname, `)`)
|
||||||
|
} else {
|
||||||
|
p.P(`return false`)
|
||||||
|
}
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
} else {
|
||||||
|
if field.IsMessage() || p.IsGroup(field) {
|
||||||
|
if nullable {
|
||||||
|
p.P(`if !this.`, fieldname, `.Equal(that1.`, fieldname, `) {`)
|
||||||
|
} else {
|
||||||
|
p.P(`if !this.`, fieldname, `.Equal(&that1.`, fieldname, `) {`)
|
||||||
|
}
|
||||||
|
} else if field.IsBytes() {
|
||||||
|
p.P(`if !`, p.bytesPkg.Use(), `.Equal(this.`, fieldname, `, that1.`, fieldname, `) {`)
|
||||||
|
} else if field.IsString() {
|
||||||
|
if nullable && !proto3 {
|
||||||
|
p.generateNullableField(fieldname, verbose)
|
||||||
|
} else {
|
||||||
|
p.P(`if this.`, fieldname, ` != that1.`, fieldname, `{`)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if nullable && !proto3 {
|
||||||
|
p.generateNullableField(fieldname, verbose)
|
||||||
|
} else {
|
||||||
|
p.P(`if this.`, fieldname, ` != that1.`, fieldname, `{`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.In()
|
||||||
|
if verbose {
|
||||||
|
p.P(`return `, p.fmtPkg.Use(), `.Errorf("`, fieldname, ` this(%v) Not Equal that(%v)", this.`, fieldname, `, that1.`, fieldname, `)`)
|
||||||
|
} else {
|
||||||
|
p.P(`return false`)
|
||||||
|
}
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
p.P(`if len(this.`, fieldname, `) != len(that1.`, fieldname, `) {`)
|
||||||
|
p.In()
|
||||||
|
if verbose {
|
||||||
|
p.P(`return `, p.fmtPkg.Use(), `.Errorf("`, fieldname, ` this(%v) Not Equal that(%v)", len(this.`, fieldname, `), len(that1.`, fieldname, `))`)
|
||||||
|
} else {
|
||||||
|
p.P(`return false`)
|
||||||
|
}
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`for i := range this.`, fieldname, ` {`)
|
||||||
|
p.In()
|
||||||
|
if ctype && !p.IsMap(field) {
|
||||||
|
p.P(`if !this.`, fieldname, `[i].Equal(that1.`, fieldname, `[i]) {`)
|
||||||
|
} else if isTimestamp {
|
||||||
|
if nullable {
|
||||||
|
p.P(`if !this.`, fieldname, `[i].Equal(*that1.`, fieldname, `[i]) {`)
|
||||||
|
} else {
|
||||||
|
p.P(`if !this.`, fieldname, `[i].Equal(that1.`, fieldname, `[i]) {`)
|
||||||
|
}
|
||||||
|
} else if isNormal {
|
||||||
|
if nullable {
|
||||||
|
p.P(`if dthis, dthat := this.`, fieldname, `[i], that1.`, fieldname, `[i]; (dthis != nil && dthat != nil && *dthis != *dthat) || (dthis != nil && dthat == nil) || (dthis == nil && dthat != nil) {`)
|
||||||
|
} else {
|
||||||
|
p.P(`if this.`, fieldname, `[i] != that1.`, fieldname, `[i] {`)
|
||||||
|
}
|
||||||
|
} else if isBytes {
|
||||||
|
if nullable {
|
||||||
|
p.P(`if !`, p.bytesPkg.Use(), `.Equal(*this.`, fieldname, `[i], *that1.`, fieldname, `[i]) {`)
|
||||||
|
} else {
|
||||||
|
p.P(`if !`, p.bytesPkg.Use(), `.Equal(this.`, fieldname, `[i], that1.`, fieldname, `[i]) {`)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if p.IsMap(field) {
|
||||||
|
m := p.GoMapType(nil, field)
|
||||||
|
valuegoTyp, _ := p.GoType(nil, m.ValueField)
|
||||||
|
valuegoAliasTyp, _ := p.GoType(nil, m.ValueAliasField)
|
||||||
|
nullable, valuegoTyp, valuegoAliasTyp = generator.GoMapValueTypes(field, m.ValueField, valuegoTyp, valuegoAliasTyp)
|
||||||
|
|
||||||
|
mapValue := m.ValueAliasField
|
||||||
|
mapValueNormal := (gogoproto.IsStdDuration(mapValue) ||
|
||||||
|
gogoproto.IsStdDouble(mapValue) ||
|
||||||
|
gogoproto.IsStdFloat(mapValue) ||
|
||||||
|
gogoproto.IsStdInt64(mapValue) ||
|
||||||
|
gogoproto.IsStdUInt64(mapValue) ||
|
||||||
|
gogoproto.IsStdInt32(mapValue) ||
|
||||||
|
gogoproto.IsStdUInt32(mapValue) ||
|
||||||
|
gogoproto.IsStdBool(mapValue) ||
|
||||||
|
gogoproto.IsStdString(mapValue))
|
||||||
|
mapValueBytes := gogoproto.IsStdBytes(mapValue)
|
||||||
|
if mapValue.IsMessage() || p.IsGroup(mapValue) {
|
||||||
|
if nullable && valuegoTyp == valuegoAliasTyp {
|
||||||
|
p.P(`if !this.`, fieldname, `[i].Equal(that1.`, fieldname, `[i]) {`)
|
||||||
|
} else {
|
||||||
|
// Equal() has a pointer receiver, but map value is a value type
|
||||||
|
a := `this.` + fieldname + `[i]`
|
||||||
|
b := `that1.` + fieldname + `[i]`
|
||||||
|
if !mapValueNormal && !mapValueBytes && valuegoTyp != valuegoAliasTyp {
|
||||||
|
// cast back to the type that has the generated methods on it
|
||||||
|
a = `(` + valuegoTyp + `)(` + a + `)`
|
||||||
|
b = `(` + valuegoTyp + `)(` + b + `)`
|
||||||
|
}
|
||||||
|
p.P(`a := `, a)
|
||||||
|
p.P(`b := `, b)
|
||||||
|
if mapValueNormal {
|
||||||
|
if nullable {
|
||||||
|
p.P(`if *a != *b {`)
|
||||||
|
} else {
|
||||||
|
p.P(`if a != b {`)
|
||||||
|
}
|
||||||
|
} else if mapValueBytes {
|
||||||
|
if nullable {
|
||||||
|
p.P(`if !`, p.bytesPkg.Use(), `.Equal(*a, *b) {`)
|
||||||
|
} else {
|
||||||
|
p.P(`if !`, p.bytesPkg.Use(), `.Equal(a, b) {`)
|
||||||
|
}
|
||||||
|
} else if nullable {
|
||||||
|
p.P(`if !a.Equal(b) {`)
|
||||||
|
} else {
|
||||||
|
p.P(`if !(&a).Equal(&b) {`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if mapValue.IsBytes() {
|
||||||
|
if ctype {
|
||||||
|
if nullable {
|
||||||
|
p.P(`if !this.`, fieldname, `[i].Equal(*that1.`, fieldname, `[i]) { //nullable`)
|
||||||
|
} else {
|
||||||
|
p.P(`if !this.`, fieldname, `[i].Equal(that1.`, fieldname, `[i]) { //not nullable`)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
p.P(`if !`, p.bytesPkg.Use(), `.Equal(this.`, fieldname, `[i], that1.`, fieldname, `[i]) {`)
|
||||||
|
}
|
||||||
|
} else if mapValue.IsString() {
|
||||||
|
p.P(`if this.`, fieldname, `[i] != that1.`, fieldname, `[i] {`)
|
||||||
|
} else {
|
||||||
|
p.P(`if this.`, fieldname, `[i] != that1.`, fieldname, `[i] {`)
|
||||||
|
}
|
||||||
|
} else if field.IsMessage() || p.IsGroup(field) {
|
||||||
|
if nullable {
|
||||||
|
p.P(`if !this.`, fieldname, `[i].Equal(that1.`, fieldname, `[i]) {`)
|
||||||
|
} else {
|
||||||
|
p.P(`if !this.`, fieldname, `[i].Equal(&that1.`, fieldname, `[i]) {`)
|
||||||
|
}
|
||||||
|
} else if field.IsBytes() {
|
||||||
|
p.P(`if !`, p.bytesPkg.Use(), `.Equal(this.`, fieldname, `[i], that1.`, fieldname, `[i]) {`)
|
||||||
|
} else if field.IsString() {
|
||||||
|
p.P(`if this.`, fieldname, `[i] != that1.`, fieldname, `[i] {`)
|
||||||
|
} else {
|
||||||
|
p.P(`if this.`, fieldname, `[i] != that1.`, fieldname, `[i] {`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.In()
|
||||||
|
if verbose {
|
||||||
|
p.P(`return `, p.fmtPkg.Use(), `.Errorf("`, fieldname, ` this[%v](%v) Not Equal that[%v](%v)", i, this.`, fieldname, `[i], i, that1.`, fieldname, `[i])`)
|
||||||
|
} else {
|
||||||
|
p.P(`return false`)
|
||||||
|
}
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *plugin) generateMessage(file *generator.FileDescriptor, message *generator.Descriptor, verbose bool) {
|
||||||
|
ccTypeName := generator.CamelCaseSlice(message.TypeName())
|
||||||
|
if verbose {
|
||||||
|
p.P(`func (this *`, ccTypeName, `) VerboseEqual(that interface{}) error {`)
|
||||||
|
} else {
|
||||||
|
p.P(`func (this *`, ccTypeName, `) Equal(that interface{}) bool {`)
|
||||||
|
}
|
||||||
|
p.In()
|
||||||
|
p.generateMsgNullAndTypeCheck(ccTypeName, verbose)
|
||||||
|
oneofs := make(map[string]struct{})
|
||||||
|
|
||||||
|
for _, field := range message.Field {
|
||||||
|
oneof := field.OneofIndex != nil
|
||||||
|
if oneof {
|
||||||
|
fieldname := p.GetFieldName(message, field)
|
||||||
|
if _, ok := oneofs[fieldname]; ok {
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
oneofs[fieldname] = struct{}{}
|
||||||
|
}
|
||||||
|
p.P(`if that1.`, fieldname, ` == nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`if this.`, fieldname, ` != nil {`)
|
||||||
|
p.In()
|
||||||
|
if verbose {
|
||||||
|
p.P(`return `, p.fmtPkg.Use(), `.Errorf("this.`, fieldname, ` != nil && that1.`, fieldname, ` == nil")`)
|
||||||
|
} else {
|
||||||
|
p.P(`return false`)
|
||||||
|
}
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`} else if this.`, fieldname, ` == nil {`)
|
||||||
|
p.In()
|
||||||
|
if verbose {
|
||||||
|
p.P(`return `, p.fmtPkg.Use(), `.Errorf("this.`, fieldname, ` == nil && that1.`, fieldname, ` != nil")`)
|
||||||
|
} else {
|
||||||
|
p.P(`return false`)
|
||||||
|
}
|
||||||
|
p.Out()
|
||||||
|
if verbose {
|
||||||
|
p.P(`} else if err := this.`, fieldname, `.VerboseEqual(that1.`, fieldname, `); err != nil {`)
|
||||||
|
} else {
|
||||||
|
p.P(`} else if !this.`, fieldname, `.Equal(that1.`, fieldname, `) {`)
|
||||||
|
}
|
||||||
|
p.In()
|
||||||
|
if verbose {
|
||||||
|
p.P(`return err`)
|
||||||
|
} else {
|
||||||
|
p.P(`return false`)
|
||||||
|
}
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
} else {
|
||||||
|
p.generateField(file, message, field, verbose)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if message.DescriptorProto.HasExtension() {
|
||||||
|
if gogoproto.HasExtensionsMap(file.FileDescriptorProto, message.DescriptorProto) {
|
||||||
|
fieldname := "XXX_InternalExtensions"
|
||||||
|
p.P(`thismap := `, p.protoPkg.Use(), `.GetUnsafeExtensionsMap(this)`)
|
||||||
|
p.P(`thatmap := `, p.protoPkg.Use(), `.GetUnsafeExtensionsMap(that1)`)
|
||||||
|
p.P(`for k, v := range thismap {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`if v2, ok := thatmap[k]; ok {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`if !v.Equal(&v2) {`)
|
||||||
|
p.In()
|
||||||
|
if verbose {
|
||||||
|
p.P(`return `, p.fmtPkg.Use(), `.Errorf("`, fieldname, ` this[%v](%v) Not Equal that[%v](%v)", k, thismap[k], k, thatmap[k])`)
|
||||||
|
} else {
|
||||||
|
p.P(`return false`)
|
||||||
|
}
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`} else {`)
|
||||||
|
p.In()
|
||||||
|
if verbose {
|
||||||
|
p.P(`return `, p.fmtPkg.Use(), `.Errorf("`, fieldname, `[%v] Not In that", k)`)
|
||||||
|
} else {
|
||||||
|
p.P(`return false`)
|
||||||
|
}
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
|
||||||
|
p.P(`for k, _ := range thatmap {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`if _, ok := thismap[k]; !ok {`)
|
||||||
|
p.In()
|
||||||
|
if verbose {
|
||||||
|
p.P(`return `, p.fmtPkg.Use(), `.Errorf("`, fieldname, `[%v] Not In this", k)`)
|
||||||
|
} else {
|
||||||
|
p.P(`return false`)
|
||||||
|
}
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
} else {
|
||||||
|
fieldname := "XXX_extensions"
|
||||||
|
p.P(`if !`, p.bytesPkg.Use(), `.Equal(this.`, fieldname, `, that1.`, fieldname, `) {`)
|
||||||
|
p.In()
|
||||||
|
if verbose {
|
||||||
|
p.P(`return `, p.fmtPkg.Use(), `.Errorf("`, fieldname, ` this(%v) Not Equal that(%v)", this.`, fieldname, `, that1.`, fieldname, `)`)
|
||||||
|
} else {
|
||||||
|
p.P(`return false`)
|
||||||
|
}
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if gogoproto.HasUnrecognized(file.FileDescriptorProto, message.DescriptorProto) {
|
||||||
|
fieldname := "XXX_unrecognized"
|
||||||
|
p.P(`if !`, p.bytesPkg.Use(), `.Equal(this.`, fieldname, `, that1.`, fieldname, `) {`)
|
||||||
|
p.In()
|
||||||
|
if verbose {
|
||||||
|
p.P(`return `, p.fmtPkg.Use(), `.Errorf("`, fieldname, ` this(%v) Not Equal that(%v)", this.`, fieldname, `, that1.`, fieldname, `)`)
|
||||||
|
} else {
|
||||||
|
p.P(`return false`)
|
||||||
|
}
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
}
|
||||||
|
if verbose {
|
||||||
|
p.P(`return nil`)
|
||||||
|
} else {
|
||||||
|
p.P(`return true`)
|
||||||
|
}
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
|
||||||
|
//Generate Equal methods for oneof fields
|
||||||
|
m := proto.Clone(message.DescriptorProto).(*descriptor.DescriptorProto)
|
||||||
|
for _, field := range m.Field {
|
||||||
|
oneof := field.OneofIndex != nil
|
||||||
|
if !oneof {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ccTypeName := p.OneOfTypeName(message, field)
|
||||||
|
if verbose {
|
||||||
|
p.P(`func (this *`, ccTypeName, `) VerboseEqual(that interface{}) error {`)
|
||||||
|
} else {
|
||||||
|
p.P(`func (this *`, ccTypeName, `) Equal(that interface{}) bool {`)
|
||||||
|
}
|
||||||
|
p.In()
|
||||||
|
|
||||||
|
p.generateMsgNullAndTypeCheck(ccTypeName, verbose)
|
||||||
|
vanity.TurnOffNullableForNativeTypes(field)
|
||||||
|
p.generateField(file, message, field, verbose)
|
||||||
|
|
||||||
|
if verbose {
|
||||||
|
p.P(`return nil`)
|
||||||
|
} else {
|
||||||
|
p.P(`return true`)
|
||||||
|
}
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
generator.RegisterPlugin(NewPlugin())
|
||||||
|
}
|
|
@ -0,0 +1,109 @@
|
||||||
|
// Protocol Buffers for Go with Gadgets
|
||||||
|
//
|
||||||
|
// Copyright (c) 2013, The GoGo Authors. All rights reserved.
|
||||||
|
// http://github.com/gogo/protobuf
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
package equal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogo/protobuf/gogoproto"
|
||||||
|
"github.com/gogo/protobuf/plugin/testgen"
|
||||||
|
"github.com/gogo/protobuf/protoc-gen-gogo/generator"
|
||||||
|
)
|
||||||
|
|
||||||
|
type test struct {
|
||||||
|
*generator.Generator
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTest(g *generator.Generator) testgen.TestPlugin {
|
||||||
|
return &test{g}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *test) Generate(imports generator.PluginImports, file *generator.FileDescriptor) bool {
|
||||||
|
used := false
|
||||||
|
randPkg := imports.NewImport("math/rand")
|
||||||
|
timePkg := imports.NewImport("time")
|
||||||
|
testingPkg := imports.NewImport("testing")
|
||||||
|
protoPkg := imports.NewImport("github.com/gogo/protobuf/proto")
|
||||||
|
unsafePkg := imports.NewImport("unsafe")
|
||||||
|
if !gogoproto.ImportsGoGoProto(file.FileDescriptorProto) {
|
||||||
|
protoPkg = imports.NewImport("github.com/golang/protobuf/proto")
|
||||||
|
}
|
||||||
|
for _, message := range file.Messages() {
|
||||||
|
ccTypeName := generator.CamelCaseSlice(message.TypeName())
|
||||||
|
if !gogoproto.HasVerboseEqual(file.FileDescriptorProto, message.DescriptorProto) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if message.DescriptorProto.GetOptions().GetMapEntry() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if gogoproto.HasTestGen(file.FileDescriptorProto, message.DescriptorProto) {
|
||||||
|
used = true
|
||||||
|
hasUnsafe := gogoproto.IsUnsafeMarshaler(file.FileDescriptorProto, message.DescriptorProto) ||
|
||||||
|
gogoproto.IsUnsafeUnmarshaler(file.FileDescriptorProto, message.DescriptorProto)
|
||||||
|
p.P(`func Test`, ccTypeName, `VerboseEqual(t *`, testingPkg.Use(), `.T) {`)
|
||||||
|
p.In()
|
||||||
|
if hasUnsafe {
|
||||||
|
if hasUnsafe {
|
||||||
|
p.P(`var bigendian uint32 = 0x01020304`)
|
||||||
|
p.P(`if *(*byte)(`, unsafePkg.Use(), `.Pointer(&bigendian)) == 1 {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`t.Skip("unsafe does not work on big endian architectures")`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.P(`popr := `, randPkg.Use(), `.New(`, randPkg.Use(), `.NewSource(`, timePkg.Use(), `.Now().UnixNano()))`)
|
||||||
|
p.P(`p := NewPopulated`, ccTypeName, `(popr, false)`)
|
||||||
|
p.P(`dAtA, err := `, protoPkg.Use(), `.Marshal(p)`)
|
||||||
|
p.P(`if err != nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`panic(err)`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`msg := &`, ccTypeName, `{}`)
|
||||||
|
p.P(`if err := `, protoPkg.Use(), `.Unmarshal(dAtA, msg); err != nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`panic(err)`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`if err := p.VerboseEqual(msg); err != nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`t.Fatalf("%#v !VerboseEqual %#v, since %v", msg, p, err)`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return used
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
testgen.RegisterTestPlugin(NewTest)
|
||||||
|
}
|
|
@ -0,0 +1,233 @@
|
||||||
|
// Protocol Buffers for Go with Gadgets
|
||||||
|
//
|
||||||
|
// Copyright (c) 2013, The GoGo Authors. All rights reserved.
|
||||||
|
// http://github.com/gogo/protobuf
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
/*
|
||||||
|
The face plugin generates a function will be generated which can convert a structure which satisfies an interface (face) to the specified structure.
|
||||||
|
This interface contains getters for each of the fields in the struct.
|
||||||
|
The specified struct is also generated with the getters.
|
||||||
|
This means that getters should be turned off so as not to conflict with face getters.
|
||||||
|
This allows it to satisfy its own face.
|
||||||
|
|
||||||
|
It is enabled by the following extensions:
|
||||||
|
|
||||||
|
- face
|
||||||
|
- face_all
|
||||||
|
|
||||||
|
Turn off getters by using the following extensions:
|
||||||
|
|
||||||
|
- getters
|
||||||
|
- getters_all
|
||||||
|
|
||||||
|
The face plugin also generates a test given it is enabled using one of the following extensions:
|
||||||
|
|
||||||
|
- testgen
|
||||||
|
- testgen_all
|
||||||
|
|
||||||
|
Let us look at:
|
||||||
|
|
||||||
|
github.com/gogo/protobuf/test/example/example.proto
|
||||||
|
|
||||||
|
Btw all the output can be seen at:
|
||||||
|
|
||||||
|
github.com/gogo/protobuf/test/example/*
|
||||||
|
|
||||||
|
The following message:
|
||||||
|
|
||||||
|
message A {
|
||||||
|
option (gogoproto.face) = true;
|
||||||
|
option (gogoproto.goproto_getters) = false;
|
||||||
|
optional string Description = 1 [(gogoproto.nullable) = false];
|
||||||
|
optional int64 Number = 2 [(gogoproto.nullable) = false];
|
||||||
|
optional bytes Id = 3 [(gogoproto.customtype) = "github.com/gogo/protobuf/test/custom.Uuid", (gogoproto.nullable) = false];
|
||||||
|
}
|
||||||
|
|
||||||
|
given to the face plugin, will generate the following code:
|
||||||
|
|
||||||
|
type AFace interface {
|
||||||
|
Proto() github_com_gogo_protobuf_proto.Message
|
||||||
|
GetDescription() string
|
||||||
|
GetNumber() int64
|
||||||
|
GetId() github_com_gogo_protobuf_test_custom.Uuid
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *A) Proto() github_com_gogo_protobuf_proto.Message {
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *A) TestProto() github_com_gogo_protobuf_proto.Message {
|
||||||
|
return NewAFromFace(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *A) GetDescription() string {
|
||||||
|
return this.Description
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *A) GetNumber() int64 {
|
||||||
|
return this.Number
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *A) GetId() github_com_gogo_protobuf_test_custom.Uuid {
|
||||||
|
return this.Id
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAFromFace(that AFace) *A {
|
||||||
|
this := &A{}
|
||||||
|
this.Description = that.GetDescription()
|
||||||
|
this.Number = that.GetNumber()
|
||||||
|
this.Id = that.GetId()
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
and the following test code:
|
||||||
|
|
||||||
|
func TestAFace(t *testing7.T) {
|
||||||
|
popr := math_rand7.New(math_rand7.NewSource(time7.Now().UnixNano()))
|
||||||
|
p := NewPopulatedA(popr, true)
|
||||||
|
msg := p.TestProto()
|
||||||
|
if !p.Equal(msg) {
|
||||||
|
t.Fatalf("%#v !Face Equal %#v", msg, p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
The struct A, representing the message, will also be generated just like always.
|
||||||
|
As you can see A satisfies its own Face, AFace.
|
||||||
|
|
||||||
|
Creating another struct which satisfies AFace is very easy.
|
||||||
|
Simply create all these methods specified in AFace.
|
||||||
|
Implementing The Proto method is done with the helper function NewAFromFace:
|
||||||
|
|
||||||
|
func (this *MyStruct) Proto() proto.Message {
|
||||||
|
return NewAFromFace(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
just the like TestProto method which is used to test the NewAFromFace function.
|
||||||
|
|
||||||
|
*/
|
||||||
|
package face
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogo/protobuf/gogoproto"
|
||||||
|
"github.com/gogo/protobuf/protoc-gen-gogo/generator"
|
||||||
|
)
|
||||||
|
|
||||||
|
type plugin struct {
|
||||||
|
*generator.Generator
|
||||||
|
generator.PluginImports
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPlugin() *plugin {
|
||||||
|
return &plugin{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *plugin) Name() string {
|
||||||
|
return "face"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *plugin) Init(g *generator.Generator) {
|
||||||
|
p.Generator = g
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *plugin) Generate(file *generator.FileDescriptor) {
|
||||||
|
p.PluginImports = generator.NewPluginImports(p.Generator)
|
||||||
|
protoPkg := p.NewImport("github.com/gogo/protobuf/proto")
|
||||||
|
if !gogoproto.ImportsGoGoProto(file.FileDescriptorProto) {
|
||||||
|
protoPkg = p.NewImport("github.com/golang/protobuf/proto")
|
||||||
|
}
|
||||||
|
for _, message := range file.Messages() {
|
||||||
|
if !gogoproto.IsFace(file.FileDescriptorProto, message.DescriptorProto) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if message.DescriptorProto.GetOptions().GetMapEntry() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if message.DescriptorProto.HasExtension() {
|
||||||
|
panic("face does not support message with extensions")
|
||||||
|
}
|
||||||
|
if gogoproto.HasGoGetters(file.FileDescriptorProto, message.DescriptorProto) {
|
||||||
|
panic("face requires getters to be disabled please use gogoproto.getters or gogoproto.getters_all and set it to false")
|
||||||
|
}
|
||||||
|
ccTypeName := generator.CamelCaseSlice(message.TypeName())
|
||||||
|
p.P(`type `, ccTypeName, `Face interface{`)
|
||||||
|
p.In()
|
||||||
|
p.P(`Proto() `, protoPkg.Use(), `.Message`)
|
||||||
|
for _, field := range message.Field {
|
||||||
|
fieldname := p.GetFieldName(message, field)
|
||||||
|
goTyp, _ := p.GoType(message, field)
|
||||||
|
if p.IsMap(field) {
|
||||||
|
m := p.GoMapType(nil, field)
|
||||||
|
goTyp = m.GoType
|
||||||
|
}
|
||||||
|
p.P(`Get`, fieldname, `() `, goTyp)
|
||||||
|
}
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(``)
|
||||||
|
p.P(`func (this *`, ccTypeName, `) Proto() `, protoPkg.Use(), `.Message {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`return this`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(``)
|
||||||
|
p.P(`func (this *`, ccTypeName, `) TestProto() `, protoPkg.Use(), `.Message {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`return New`, ccTypeName, `FromFace(this)`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(``)
|
||||||
|
for _, field := range message.Field {
|
||||||
|
fieldname := p.GetFieldName(message, field)
|
||||||
|
goTyp, _ := p.GoType(message, field)
|
||||||
|
if p.IsMap(field) {
|
||||||
|
m := p.GoMapType(nil, field)
|
||||||
|
goTyp = m.GoType
|
||||||
|
}
|
||||||
|
p.P(`func (this *`, ccTypeName, `) Get`, fieldname, `() `, goTyp, `{`)
|
||||||
|
p.In()
|
||||||
|
p.P(` return this.`, fieldname)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(``)
|
||||||
|
}
|
||||||
|
p.P(``)
|
||||||
|
p.P(`func New`, ccTypeName, `FromFace(that `, ccTypeName, `Face) *`, ccTypeName, ` {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`this := &`, ccTypeName, `{}`)
|
||||||
|
for _, field := range message.Field {
|
||||||
|
fieldname := p.GetFieldName(message, field)
|
||||||
|
p.P(`this.`, fieldname, ` = that.Get`, fieldname, `()`)
|
||||||
|
}
|
||||||
|
p.P(`return this`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(``)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
generator.RegisterPlugin(NewPlugin())
|
||||||
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
// Protocol Buffers for Go with Gadgets
|
||||||
|
//
|
||||||
|
// Copyright (c) 2013, The GoGo Authors. All rights reserved.
|
||||||
|
// http://github.com/gogo/protobuf
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
package face
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogo/protobuf/gogoproto"
|
||||||
|
"github.com/gogo/protobuf/plugin/testgen"
|
||||||
|
"github.com/gogo/protobuf/protoc-gen-gogo/generator"
|
||||||
|
)
|
||||||
|
|
||||||
|
type test struct {
|
||||||
|
*generator.Generator
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTest(g *generator.Generator) testgen.TestPlugin {
|
||||||
|
return &test{g}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *test) Generate(imports generator.PluginImports, file *generator.FileDescriptor) bool {
|
||||||
|
used := false
|
||||||
|
randPkg := imports.NewImport("math/rand")
|
||||||
|
timePkg := imports.NewImport("time")
|
||||||
|
testingPkg := imports.NewImport("testing")
|
||||||
|
for _, message := range file.Messages() {
|
||||||
|
ccTypeName := generator.CamelCaseSlice(message.TypeName())
|
||||||
|
if !gogoproto.IsFace(file.FileDescriptorProto, message.DescriptorProto) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if message.DescriptorProto.GetOptions().GetMapEntry() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if gogoproto.HasTestGen(file.FileDescriptorProto, message.DescriptorProto) {
|
||||||
|
used = true
|
||||||
|
|
||||||
|
p.P(`func Test`, ccTypeName, `Face(t *`, testingPkg.Use(), `.T) {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`popr := `, randPkg.Use(), `.New(`, randPkg.Use(), `.NewSource(`, timePkg.Use(), `.Now().UnixNano()))`)
|
||||||
|
p.P(`p := NewPopulated`, ccTypeName, `(popr, true)`)
|
||||||
|
p.P(`msg := p.TestProto()`)
|
||||||
|
p.P(`if !p.Equal(msg) {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`t.Fatalf("%#v !Face Equal %#v", msg, p)`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return used
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
testgen.RegisterTestPlugin(NewTest)
|
||||||
|
}
|
|
@ -0,0 +1,386 @@
|
||||||
|
// Protocol Buffers for Go with Gadgets
|
||||||
|
//
|
||||||
|
// Copyright (c) 2013, The GoGo Authors. All rights reserved.
|
||||||
|
// http://github.com/gogo/protobuf
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
/*
|
||||||
|
The gostring plugin generates a GoString method for each message.
|
||||||
|
The GoString method is called whenever you use a fmt.Printf as such:
|
||||||
|
|
||||||
|
fmt.Printf("%#v", mymessage)
|
||||||
|
|
||||||
|
or whenever you actually call GoString()
|
||||||
|
The output produced by the GoString method can be copied from the output into code and used to set a variable.
|
||||||
|
It is totally valid Go Code and is populated exactly as the struct that was printed out.
|
||||||
|
|
||||||
|
It is enabled by the following extensions:
|
||||||
|
|
||||||
|
- gostring
|
||||||
|
- gostring_all
|
||||||
|
|
||||||
|
The gostring plugin also generates a test given it is enabled using one of the following extensions:
|
||||||
|
|
||||||
|
- testgen
|
||||||
|
- testgen_all
|
||||||
|
|
||||||
|
Let us look at:
|
||||||
|
|
||||||
|
github.com/gogo/protobuf/test/example/example.proto
|
||||||
|
|
||||||
|
Btw all the output can be seen at:
|
||||||
|
|
||||||
|
github.com/gogo/protobuf/test/example/*
|
||||||
|
|
||||||
|
The following message:
|
||||||
|
|
||||||
|
option (gogoproto.gostring_all) = true;
|
||||||
|
|
||||||
|
message A {
|
||||||
|
optional string Description = 1 [(gogoproto.nullable) = false];
|
||||||
|
optional int64 Number = 2 [(gogoproto.nullable) = false];
|
||||||
|
optional bytes Id = 3 [(gogoproto.customtype) = "github.com/gogo/protobuf/test/custom.Uuid", (gogoproto.nullable) = false];
|
||||||
|
}
|
||||||
|
|
||||||
|
given to the gostring plugin, will generate the following code:
|
||||||
|
|
||||||
|
func (this *A) GoString() string {
|
||||||
|
if this == nil {
|
||||||
|
return "nil"
|
||||||
|
}
|
||||||
|
s := strings1.Join([]string{`&test.A{` + `Description:` + fmt1.Sprintf("%#v", this.Description), `Number:` + fmt1.Sprintf("%#v", this.Number), `Id:` + fmt1.Sprintf("%#v", this.Id), `XXX_unrecognized:` + fmt1.Sprintf("%#v", this.XXX_unrecognized) + `}`}, ", ")
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
and the following test code:
|
||||||
|
|
||||||
|
func TestAGoString(t *testing6.T) {
|
||||||
|
popr := math_rand6.New(math_rand6.NewSource(time6.Now().UnixNano()))
|
||||||
|
p := NewPopulatedA(popr, false)
|
||||||
|
s1 := p.GoString()
|
||||||
|
s2 := fmt2.Sprintf("%#v", p)
|
||||||
|
if s1 != s2 {
|
||||||
|
t.Fatalf("GoString want %v got %v", s1, s2)
|
||||||
|
}
|
||||||
|
_, err := go_parser.ParseExpr(s1)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Typically fmt.Printf("%#v") will stop to print when it reaches a pointer and
|
||||||
|
not print their values, while the generated GoString method will always print all values, recursively.
|
||||||
|
|
||||||
|
*/
|
||||||
|
package gostring
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gogo/protobuf/gogoproto"
|
||||||
|
"github.com/gogo/protobuf/protoc-gen-gogo/generator"
|
||||||
|
)
|
||||||
|
|
||||||
|
type gostring struct {
|
||||||
|
*generator.Generator
|
||||||
|
generator.PluginImports
|
||||||
|
atleastOne bool
|
||||||
|
localName string
|
||||||
|
overwrite bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGoString() *gostring {
|
||||||
|
return &gostring{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *gostring) Name() string {
|
||||||
|
return "gostring"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *gostring) Overwrite() {
|
||||||
|
p.overwrite = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *gostring) Init(g *generator.Generator) {
|
||||||
|
p.Generator = g
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *gostring) Generate(file *generator.FileDescriptor) {
|
||||||
|
proto3 := gogoproto.IsProto3(file.FileDescriptorProto)
|
||||||
|
p.PluginImports = generator.NewPluginImports(p.Generator)
|
||||||
|
p.atleastOne = false
|
||||||
|
|
||||||
|
p.localName = generator.FileName(file)
|
||||||
|
|
||||||
|
fmtPkg := p.NewImport("fmt")
|
||||||
|
stringsPkg := p.NewImport("strings")
|
||||||
|
protoPkg := p.NewImport("github.com/gogo/protobuf/proto")
|
||||||
|
if !gogoproto.ImportsGoGoProto(file.FileDescriptorProto) {
|
||||||
|
protoPkg = p.NewImport("github.com/golang/protobuf/proto")
|
||||||
|
}
|
||||||
|
sortPkg := p.NewImport("sort")
|
||||||
|
strconvPkg := p.NewImport("strconv")
|
||||||
|
reflectPkg := p.NewImport("reflect")
|
||||||
|
sortKeysPkg := p.NewImport("github.com/gogo/protobuf/sortkeys")
|
||||||
|
|
||||||
|
extensionToGoStringUsed := false
|
||||||
|
for _, message := range file.Messages() {
|
||||||
|
if !p.overwrite && !gogoproto.HasGoString(file.FileDescriptorProto, message.DescriptorProto) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if message.DescriptorProto.GetOptions().GetMapEntry() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
p.atleastOne = true
|
||||||
|
packageName := file.GoPackageName()
|
||||||
|
|
||||||
|
ccTypeName := generator.CamelCaseSlice(message.TypeName())
|
||||||
|
p.P(`func (this *`, ccTypeName, `) GoString() string {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`if this == nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`return "nil"`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
|
||||||
|
p.P(`s := make([]string, 0, `, strconv.Itoa(len(message.Field)+4), `)`)
|
||||||
|
p.P(`s = append(s, "&`, packageName, ".", ccTypeName, `{")`)
|
||||||
|
|
||||||
|
oneofs := make(map[string]struct{})
|
||||||
|
for _, field := range message.Field {
|
||||||
|
nullable := gogoproto.IsNullable(field)
|
||||||
|
repeated := field.IsRepeated()
|
||||||
|
fieldname := p.GetFieldName(message, field)
|
||||||
|
oneof := field.OneofIndex != nil
|
||||||
|
if oneof {
|
||||||
|
if _, ok := oneofs[fieldname]; ok {
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
oneofs[fieldname] = struct{}{}
|
||||||
|
}
|
||||||
|
p.P(`if this.`, fieldname, ` != nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`s = append(s, "`, fieldname, `: " + `, fmtPkg.Use(), `.Sprintf("%#v", this.`, fieldname, `) + ",\n")`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
} else if p.IsMap(field) {
|
||||||
|
m := p.GoMapType(nil, field)
|
||||||
|
mapgoTyp, keyField, keyAliasField := m.GoType, m.KeyField, m.KeyAliasField
|
||||||
|
keysName := `keysFor` + fieldname
|
||||||
|
keygoTyp, _ := p.GoType(nil, keyField)
|
||||||
|
keygoTyp = strings.Replace(keygoTyp, "*", "", 1)
|
||||||
|
keygoAliasTyp, _ := p.GoType(nil, keyAliasField)
|
||||||
|
keygoAliasTyp = strings.Replace(keygoAliasTyp, "*", "", 1)
|
||||||
|
keyCapTyp := generator.CamelCase(keygoTyp)
|
||||||
|
p.P(keysName, ` := make([]`, keygoTyp, `, 0, len(this.`, fieldname, `))`)
|
||||||
|
p.P(`for k, _ := range this.`, fieldname, ` {`)
|
||||||
|
p.In()
|
||||||
|
if keygoAliasTyp == keygoTyp {
|
||||||
|
p.P(keysName, ` = append(`, keysName, `, k)`)
|
||||||
|
} else {
|
||||||
|
p.P(keysName, ` = append(`, keysName, `, `, keygoTyp, `(k))`)
|
||||||
|
}
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(sortKeysPkg.Use(), `.`, keyCapTyp, `s(`, keysName, `)`)
|
||||||
|
mapName := `mapStringFor` + fieldname
|
||||||
|
p.P(mapName, ` := "`, mapgoTyp, `{"`)
|
||||||
|
p.P(`for _, k := range `, keysName, ` {`)
|
||||||
|
p.In()
|
||||||
|
if keygoAliasTyp == keygoTyp {
|
||||||
|
p.P(mapName, ` += fmt.Sprintf("%#v: %#v,", k, this.`, fieldname, `[k])`)
|
||||||
|
} else {
|
||||||
|
p.P(mapName, ` += fmt.Sprintf("%#v: %#v,", k, this.`, fieldname, `[`, keygoAliasTyp, `(k)])`)
|
||||||
|
}
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(mapName, ` += "}"`)
|
||||||
|
p.P(`if this.`, fieldname, ` != nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`s = append(s, "`, fieldname, `: " + `, mapName, `+ ",\n")`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
} else if (field.IsMessage() && !gogoproto.IsCustomType(field) && !gogoproto.IsStdType(field)) || p.IsGroup(field) {
|
||||||
|
if nullable || repeated {
|
||||||
|
p.P(`if this.`, fieldname, ` != nil {`)
|
||||||
|
p.In()
|
||||||
|
}
|
||||||
|
if nullable {
|
||||||
|
p.P(`s = append(s, "`, fieldname, `: " + `, fmtPkg.Use(), `.Sprintf("%#v", this.`, fieldname, `) + ",\n")`)
|
||||||
|
} else if repeated {
|
||||||
|
if nullable {
|
||||||
|
p.P(`s = append(s, "`, fieldname, `: " + `, fmtPkg.Use(), `.Sprintf("%#v", this.`, fieldname, `) + ",\n")`)
|
||||||
|
} else {
|
||||||
|
goTyp, _ := p.GoType(message, field)
|
||||||
|
goTyp = strings.Replace(goTyp, "[]", "", 1)
|
||||||
|
p.P("vs := make([]", goTyp, ", len(this.", fieldname, "))")
|
||||||
|
p.P("for i := range vs {")
|
||||||
|
p.In()
|
||||||
|
p.P("vs[i] = this.", fieldname, "[i]")
|
||||||
|
p.Out()
|
||||||
|
p.P("}")
|
||||||
|
p.P(`s = append(s, "`, fieldname, `: " + `, fmtPkg.Use(), `.Sprintf("%#v", vs) + ",\n")`)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
p.P(`s = append(s, "`, fieldname, `: " + `, stringsPkg.Use(), `.Replace(this.`, fieldname, `.GoString()`, ",`&`,``,1)", ` + ",\n")`)
|
||||||
|
}
|
||||||
|
if nullable || repeated {
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if !proto3 && (nullable || repeated) {
|
||||||
|
p.P(`if this.`, fieldname, ` != nil {`)
|
||||||
|
p.In()
|
||||||
|
}
|
||||||
|
if field.IsEnum() {
|
||||||
|
if nullable && !repeated && !proto3 {
|
||||||
|
goTyp, _ := p.GoType(message, field)
|
||||||
|
p.P(`s = append(s, "`, fieldname, `: " + valueToGoString`, p.localName, `(this.`, fieldname, `,"`, generator.GoTypeToName(goTyp), `"`, `) + ",\n")`)
|
||||||
|
} else {
|
||||||
|
p.P(`s = append(s, "`, fieldname, `: " + `, fmtPkg.Use(), `.Sprintf("%#v", this.`, fieldname, `) + ",\n")`)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if nullable && !repeated && !proto3 {
|
||||||
|
goTyp, _ := p.GoType(message, field)
|
||||||
|
p.P(`s = append(s, "`, fieldname, `: " + valueToGoString`, p.localName, `(this.`, fieldname, `,"`, generator.GoTypeToName(goTyp), `"`, `) + ",\n")`)
|
||||||
|
} else {
|
||||||
|
p.P(`s = append(s, "`, fieldname, `: " + `, fmtPkg.Use(), `.Sprintf("%#v", this.`, fieldname, `) + ",\n")`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !proto3 && (nullable || repeated) {
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if message.DescriptorProto.HasExtension() {
|
||||||
|
if gogoproto.HasExtensionsMap(file.FileDescriptorProto, message.DescriptorProto) {
|
||||||
|
p.P(`s = append(s, "XXX_InternalExtensions: " + extensionToGoString`, p.localName, `(this) + ",\n")`)
|
||||||
|
extensionToGoStringUsed = true
|
||||||
|
} else {
|
||||||
|
p.P(`if this.XXX_extensions != nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`s = append(s, "XXX_extensions: " + `, fmtPkg.Use(), `.Sprintf("%#v", this.XXX_extensions) + ",\n")`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if gogoproto.HasUnrecognized(file.FileDescriptorProto, message.DescriptorProto) {
|
||||||
|
p.P(`if this.XXX_unrecognized != nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`s = append(s, "XXX_unrecognized:" + `, fmtPkg.Use(), `.Sprintf("%#v", this.XXX_unrecognized) + ",\n")`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
p.P(`s = append(s, "}")`)
|
||||||
|
p.P(`return `, stringsPkg.Use(), `.Join(s, "")`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
|
||||||
|
//Generate GoString methods for oneof fields
|
||||||
|
for _, field := range message.Field {
|
||||||
|
oneof := field.OneofIndex != nil
|
||||||
|
if !oneof {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ccTypeName := p.OneOfTypeName(message, field)
|
||||||
|
p.P(`func (this *`, ccTypeName, `) GoString() string {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`if this == nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`return "nil"`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
fieldname := p.GetOneOfFieldName(message, field)
|
||||||
|
outStr := strings.Join([]string{
|
||||||
|
"s := ",
|
||||||
|
stringsPkg.Use(), ".Join([]string{`&", packageName, ".", ccTypeName, "{` + \n",
|
||||||
|
"`", fieldname, ":` + ", fmtPkg.Use(), `.Sprintf("%#v", this.`, fieldname, `)`,
|
||||||
|
" + `}`",
|
||||||
|
`}`,
|
||||||
|
`,", "`,
|
||||||
|
`)`}, "")
|
||||||
|
p.P(outStr)
|
||||||
|
p.P(`return s`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !p.atleastOne {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
p.P(`func valueToGoString`, p.localName, `(v interface{}, typ string) string {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`rv := `, reflectPkg.Use(), `.ValueOf(v)`)
|
||||||
|
p.P(`if rv.IsNil() {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`return "nil"`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`pv := `, reflectPkg.Use(), `.Indirect(rv).Interface()`)
|
||||||
|
p.P(`return `, fmtPkg.Use(), `.Sprintf("func(v %v) *%v { return &v } ( %#v )", typ, typ, pv)`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
|
||||||
|
if extensionToGoStringUsed {
|
||||||
|
if !gogoproto.ImportsGoGoProto(file.FileDescriptorProto) {
|
||||||
|
fmt.Fprintf(os.Stderr, "The GoString plugin for messages with extensions requires importing gogoprotobuf. Please see file %s", file.GetName())
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
p.P(`func extensionToGoString`, p.localName, `(m `, protoPkg.Use(), `.Message) string {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`e := `, protoPkg.Use(), `.GetUnsafeExtensionsMap(m)`)
|
||||||
|
p.P(`if e == nil { return "nil" }`)
|
||||||
|
p.P(`s := "proto.NewUnsafeXXX_InternalExtensions(map[int32]proto.Extension{"`)
|
||||||
|
p.P(`keys := make([]int, 0, len(e))`)
|
||||||
|
p.P(`for k := range e {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`keys = append(keys, int(k))`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(sortPkg.Use(), `.Ints(keys)`)
|
||||||
|
p.P(`ss := []string{}`)
|
||||||
|
p.P(`for _, k := range keys {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`ss = append(ss, `, strconvPkg.Use(), `.Itoa(k) + ": " + e[int32(k)].GoString())`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`s+=`, stringsPkg.Use(), `.Join(ss, ",") + "})"`)
|
||||||
|
p.P(`return s`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
generator.RegisterPlugin(NewGoString())
|
||||||
|
}
|
|
@ -0,0 +1,90 @@
|
||||||
|
// Protocol Buffers for Go with Gadgets
|
||||||
|
//
|
||||||
|
// Copyright (c) 2013, The GoGo Authors. All rights reserved.
|
||||||
|
// http://github.com/gogo/protobuf
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
package gostring
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogo/protobuf/gogoproto"
|
||||||
|
"github.com/gogo/protobuf/plugin/testgen"
|
||||||
|
"github.com/gogo/protobuf/protoc-gen-gogo/generator"
|
||||||
|
)
|
||||||
|
|
||||||
|
type test struct {
|
||||||
|
*generator.Generator
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTest(g *generator.Generator) testgen.TestPlugin {
|
||||||
|
return &test{g}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *test) Generate(imports generator.PluginImports, file *generator.FileDescriptor) bool {
|
||||||
|
used := false
|
||||||
|
randPkg := imports.NewImport("math/rand")
|
||||||
|
timePkg := imports.NewImport("time")
|
||||||
|
testingPkg := imports.NewImport("testing")
|
||||||
|
fmtPkg := imports.NewImport("fmt")
|
||||||
|
parserPkg := imports.NewImport("go/parser")
|
||||||
|
for _, message := range file.Messages() {
|
||||||
|
ccTypeName := generator.CamelCaseSlice(message.TypeName())
|
||||||
|
if !gogoproto.HasGoString(file.FileDescriptorProto, message.DescriptorProto) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if message.DescriptorProto.GetOptions().GetMapEntry() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if gogoproto.HasTestGen(file.FileDescriptorProto, message.DescriptorProto) {
|
||||||
|
used = true
|
||||||
|
p.P(`func Test`, ccTypeName, `GoString(t *`, testingPkg.Use(), `.T) {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`popr := `, randPkg.Use(), `.New(`, randPkg.Use(), `.NewSource(`, timePkg.Use(), `.Now().UnixNano()))`)
|
||||||
|
p.P(`p := NewPopulated`, ccTypeName, `(popr, false)`)
|
||||||
|
p.P(`s1 := p.GoString()`)
|
||||||
|
p.P(`s2 := `, fmtPkg.Use(), `.Sprintf("%#v", p)`)
|
||||||
|
p.P(`if s1 != s2 {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`t.Fatalf("GoString want %v got %v", s1, s2)`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`_, err := `, parserPkg.Use(), `.ParseExpr(s1)`)
|
||||||
|
p.P(`if err != nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`t.Fatal(err)`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return used
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
testgen.RegisterTestPlugin(NewTest)
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,93 @@
|
||||||
|
// Protocol Buffers for Go with Gadgets
|
||||||
|
//
|
||||||
|
// Copyright (c) 2013, The GoGo Authors. All rights reserved.
|
||||||
|
// http://github.com/gogo/protobuf
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
/*
|
||||||
|
The oneofcheck plugin is used to check whether oneof is not used incorrectly.
|
||||||
|
For instance:
|
||||||
|
An error is caused if a oneof field:
|
||||||
|
- is used in a face
|
||||||
|
- is an embedded field
|
||||||
|
|
||||||
|
*/
|
||||||
|
package oneofcheck
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/gogo/protobuf/gogoproto"
|
||||||
|
"github.com/gogo/protobuf/protoc-gen-gogo/generator"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
type plugin struct {
|
||||||
|
*generator.Generator
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPlugin() *plugin {
|
||||||
|
return &plugin{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *plugin) Name() string {
|
||||||
|
return "oneofcheck"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *plugin) Init(g *generator.Generator) {
|
||||||
|
p.Generator = g
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *plugin) Generate(file *generator.FileDescriptor) {
|
||||||
|
for _, msg := range file.Messages() {
|
||||||
|
face := gogoproto.IsFace(file.FileDescriptorProto, msg.DescriptorProto)
|
||||||
|
for _, field := range msg.GetField() {
|
||||||
|
if field.OneofIndex == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if face {
|
||||||
|
fmt.Fprintf(os.Stderr, "ERROR: field %v.%v cannot be in a face and oneof\n", generator.CamelCase(*msg.Name), generator.CamelCase(*field.Name))
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
if gogoproto.IsEmbed(field) {
|
||||||
|
fmt.Fprintf(os.Stderr, "ERROR: field %v.%v cannot be in an oneof and an embedded field\n", generator.CamelCase(*msg.Name), generator.CamelCase(*field.Name))
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
if !gogoproto.IsNullable(field) {
|
||||||
|
fmt.Fprintf(os.Stderr, "ERROR: field %v.%v cannot be in an oneof and a non-nullable field\n", generator.CamelCase(*msg.Name), generator.CamelCase(*field.Name))
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
if gogoproto.IsUnion(file.FileDescriptorProto, msg.DescriptorProto) {
|
||||||
|
fmt.Fprintf(os.Stderr, "ERROR: field %v.%v cannot be in an oneof and in an union (deprecated)\n", generator.CamelCase(*msg.Name), generator.CamelCase(*field.Name))
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *plugin) GenerateImports(*generator.FileDescriptor) {}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
generator.RegisterPlugin(NewPlugin())
|
||||||
|
}
|
|
@ -0,0 +1,815 @@
|
||||||
|
// Protocol Buffers for Go with Gadgets
|
||||||
|
//
|
||||||
|
// Copyright (c) 2013, The GoGo Authors. All rights reserved.
|
||||||
|
// http://github.com/gogo/protobuf
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
/*
|
||||||
|
The populate plugin generates a NewPopulated function.
|
||||||
|
This function returns a newly populated structure.
|
||||||
|
|
||||||
|
It is enabled by the following extensions:
|
||||||
|
|
||||||
|
- populate
|
||||||
|
- populate_all
|
||||||
|
|
||||||
|
Let us look at:
|
||||||
|
|
||||||
|
github.com/gogo/protobuf/test/example/example.proto
|
||||||
|
|
||||||
|
Btw all the output can be seen at:
|
||||||
|
|
||||||
|
github.com/gogo/protobuf/test/example/*
|
||||||
|
|
||||||
|
The following message:
|
||||||
|
|
||||||
|
option (gogoproto.populate_all) = true;
|
||||||
|
|
||||||
|
message B {
|
||||||
|
optional A A = 1 [(gogoproto.nullable) = false, (gogoproto.embed) = true];
|
||||||
|
repeated bytes G = 2 [(gogoproto.customtype) = "github.com/gogo/protobuf/test/custom.Uint128", (gogoproto.nullable) = false];
|
||||||
|
}
|
||||||
|
|
||||||
|
given to the populate plugin, will generate code the following code:
|
||||||
|
|
||||||
|
func NewPopulatedB(r randyExample, easy bool) *B {
|
||||||
|
this := &B{}
|
||||||
|
v2 := NewPopulatedA(r, easy)
|
||||||
|
this.A = *v2
|
||||||
|
if r.Intn(10) != 0 {
|
||||||
|
v3 := r.Intn(10)
|
||||||
|
this.G = make([]github_com_gogo_protobuf_test_custom.Uint128, v3)
|
||||||
|
for i := 0; i < v3; i++ {
|
||||||
|
v4 := github_com_gogo_protobuf_test_custom.NewPopulatedUint128(r)
|
||||||
|
this.G[i] = *v4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !easy && r.Intn(10) != 0 {
|
||||||
|
this.XXX_unrecognized = randUnrecognizedExample(r, 3)
|
||||||
|
}
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
The idea that is useful for testing.
|
||||||
|
Most of the other plugins' generated test code uses it.
|
||||||
|
You will still be able to use the generated test code of other packages
|
||||||
|
if you turn off the popluate plugin and write your own custom NewPopulated function.
|
||||||
|
|
||||||
|
If the easy flag is not set the XXX_unrecognized and XXX_extensions fields are also populated.
|
||||||
|
These have caused problems with JSON marshalling and unmarshalling tests.
|
||||||
|
|
||||||
|
*/
|
||||||
|
package populate
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gogo/protobuf/gogoproto"
|
||||||
|
"github.com/gogo/protobuf/proto"
|
||||||
|
descriptor "github.com/gogo/protobuf/protoc-gen-gogo/descriptor"
|
||||||
|
"github.com/gogo/protobuf/protoc-gen-gogo/generator"
|
||||||
|
"github.com/gogo/protobuf/vanity"
|
||||||
|
)
|
||||||
|
|
||||||
|
type VarGen interface {
|
||||||
|
Next() string
|
||||||
|
Current() string
|
||||||
|
}
|
||||||
|
|
||||||
|
type varGen struct {
|
||||||
|
index int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewVarGen() VarGen {
|
||||||
|
return &varGen{0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *varGen) Next() string {
|
||||||
|
this.index++
|
||||||
|
return fmt.Sprintf("v%d", this.index)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *varGen) Current() string {
|
||||||
|
return fmt.Sprintf("v%d", this.index)
|
||||||
|
}
|
||||||
|
|
||||||
|
type plugin struct {
|
||||||
|
*generator.Generator
|
||||||
|
generator.PluginImports
|
||||||
|
varGen VarGen
|
||||||
|
atleastOne bool
|
||||||
|
localName string
|
||||||
|
typesPkg generator.Single
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPlugin() *plugin {
|
||||||
|
return &plugin{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *plugin) Name() string {
|
||||||
|
return "populate"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *plugin) Init(g *generator.Generator) {
|
||||||
|
p.Generator = g
|
||||||
|
}
|
||||||
|
|
||||||
|
func value(typeName string, fieldType descriptor.FieldDescriptorProto_Type) string {
|
||||||
|
switch fieldType {
|
||||||
|
case descriptor.FieldDescriptorProto_TYPE_DOUBLE:
|
||||||
|
return typeName + "(r.Float64())"
|
||||||
|
case descriptor.FieldDescriptorProto_TYPE_FLOAT:
|
||||||
|
return typeName + "(r.Float32())"
|
||||||
|
case descriptor.FieldDescriptorProto_TYPE_INT64,
|
||||||
|
descriptor.FieldDescriptorProto_TYPE_SFIXED64,
|
||||||
|
descriptor.FieldDescriptorProto_TYPE_SINT64:
|
||||||
|
return typeName + "(r.Int63())"
|
||||||
|
case descriptor.FieldDescriptorProto_TYPE_UINT64,
|
||||||
|
descriptor.FieldDescriptorProto_TYPE_FIXED64:
|
||||||
|
return typeName + "(uint64(r.Uint32()))"
|
||||||
|
case descriptor.FieldDescriptorProto_TYPE_INT32,
|
||||||
|
descriptor.FieldDescriptorProto_TYPE_SINT32,
|
||||||
|
descriptor.FieldDescriptorProto_TYPE_SFIXED32,
|
||||||
|
descriptor.FieldDescriptorProto_TYPE_ENUM:
|
||||||
|
return typeName + "(r.Int31())"
|
||||||
|
case descriptor.FieldDescriptorProto_TYPE_UINT32,
|
||||||
|
descriptor.FieldDescriptorProto_TYPE_FIXED32:
|
||||||
|
return typeName + "(r.Uint32())"
|
||||||
|
case descriptor.FieldDescriptorProto_TYPE_BOOL:
|
||||||
|
return typeName + `(bool(r.Intn(2) == 0))`
|
||||||
|
case descriptor.FieldDescriptorProto_TYPE_STRING,
|
||||||
|
descriptor.FieldDescriptorProto_TYPE_GROUP,
|
||||||
|
descriptor.FieldDescriptorProto_TYPE_MESSAGE,
|
||||||
|
descriptor.FieldDescriptorProto_TYPE_BYTES:
|
||||||
|
}
|
||||||
|
panic(fmt.Errorf("unexpected type %v", typeName))
|
||||||
|
}
|
||||||
|
|
||||||
|
func negative(fieldType descriptor.FieldDescriptorProto_Type) bool {
|
||||||
|
switch fieldType {
|
||||||
|
case descriptor.FieldDescriptorProto_TYPE_UINT64,
|
||||||
|
descriptor.FieldDescriptorProto_TYPE_FIXED64,
|
||||||
|
descriptor.FieldDescriptorProto_TYPE_UINT32,
|
||||||
|
descriptor.FieldDescriptorProto_TYPE_FIXED32,
|
||||||
|
descriptor.FieldDescriptorProto_TYPE_BOOL:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *plugin) getFuncName(goTypName string, field *descriptor.FieldDescriptorProto) string {
|
||||||
|
funcName := "NewPopulated" + goTypName
|
||||||
|
goTypNames := strings.Split(goTypName, ".")
|
||||||
|
if len(goTypNames) == 2 {
|
||||||
|
funcName = goTypNames[0] + ".NewPopulated" + goTypNames[1]
|
||||||
|
} else if len(goTypNames) != 1 {
|
||||||
|
panic(fmt.Errorf("unreachable: too many dots in %v", goTypName))
|
||||||
|
}
|
||||||
|
if field != nil {
|
||||||
|
switch {
|
||||||
|
case gogoproto.IsStdTime(field):
|
||||||
|
funcName = p.typesPkg.Use() + ".NewPopulatedStdTime"
|
||||||
|
case gogoproto.IsStdDuration(field):
|
||||||
|
funcName = p.typesPkg.Use() + ".NewPopulatedStdDuration"
|
||||||
|
case gogoproto.IsStdDouble(field):
|
||||||
|
funcName = p.typesPkg.Use() + ".NewPopulatedStdDouble"
|
||||||
|
case gogoproto.IsStdFloat(field):
|
||||||
|
funcName = p.typesPkg.Use() + ".NewPopulatedStdFloat"
|
||||||
|
case gogoproto.IsStdInt64(field):
|
||||||
|
funcName = p.typesPkg.Use() + ".NewPopulatedStdInt64"
|
||||||
|
case gogoproto.IsStdUInt64(field):
|
||||||
|
funcName = p.typesPkg.Use() + ".NewPopulatedStdUInt64"
|
||||||
|
case gogoproto.IsStdInt32(field):
|
||||||
|
funcName = p.typesPkg.Use() + ".NewPopulatedStdInt32"
|
||||||
|
case gogoproto.IsStdUInt32(field):
|
||||||
|
funcName = p.typesPkg.Use() + ".NewPopulatedStdUInt32"
|
||||||
|
case gogoproto.IsStdBool(field):
|
||||||
|
funcName = p.typesPkg.Use() + ".NewPopulatedStdBool"
|
||||||
|
case gogoproto.IsStdString(field):
|
||||||
|
funcName = p.typesPkg.Use() + ".NewPopulatedStdString"
|
||||||
|
case gogoproto.IsStdBytes(field):
|
||||||
|
funcName = p.typesPkg.Use() + ".NewPopulatedStdBytes"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return funcName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *plugin) getFuncCall(goTypName string, field *descriptor.FieldDescriptorProto) string {
|
||||||
|
funcName := p.getFuncName(goTypName, field)
|
||||||
|
funcCall := funcName + "(r, easy)"
|
||||||
|
return funcCall
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *plugin) getCustomFuncCall(goTypName string) string {
|
||||||
|
funcName := p.getFuncName(goTypName, nil)
|
||||||
|
funcCall := funcName + "(r)"
|
||||||
|
return funcCall
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *plugin) getEnumVal(field *descriptor.FieldDescriptorProto, goTyp string) string {
|
||||||
|
enum := p.ObjectNamed(field.GetTypeName()).(*generator.EnumDescriptor)
|
||||||
|
l := len(enum.Value)
|
||||||
|
values := make([]string, l)
|
||||||
|
for i := range enum.Value {
|
||||||
|
values[i] = strconv.Itoa(int(*enum.Value[i].Number))
|
||||||
|
}
|
||||||
|
arr := "[]int32{" + strings.Join(values, ",") + "}"
|
||||||
|
val := strings.Join([]string{generator.GoTypeToName(goTyp), `(`, arr, `[r.Intn(`, fmt.Sprintf("%d", l), `)])`}, "")
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *plugin) GenerateField(file *generator.FileDescriptor, message *generator.Descriptor, field *descriptor.FieldDescriptorProto) {
|
||||||
|
proto3 := gogoproto.IsProto3(file.FileDescriptorProto)
|
||||||
|
goTyp, _ := p.GoType(message, field)
|
||||||
|
fieldname := p.GetOneOfFieldName(message, field)
|
||||||
|
goTypName := generator.GoTypeToName(goTyp)
|
||||||
|
if p.IsMap(field) {
|
||||||
|
m := p.GoMapType(nil, field)
|
||||||
|
keygoTyp, _ := p.GoType(nil, m.KeyField)
|
||||||
|
keygoTyp = strings.Replace(keygoTyp, "*", "", 1)
|
||||||
|
keygoAliasTyp, _ := p.GoType(nil, m.KeyAliasField)
|
||||||
|
keygoAliasTyp = strings.Replace(keygoAliasTyp, "*", "", 1)
|
||||||
|
|
||||||
|
valuegoTyp, _ := p.GoType(nil, m.ValueField)
|
||||||
|
valuegoAliasTyp, _ := p.GoType(nil, m.ValueAliasField)
|
||||||
|
keytypName := generator.GoTypeToName(keygoTyp)
|
||||||
|
keygoAliasTyp = generator.GoTypeToName(keygoAliasTyp)
|
||||||
|
valuetypAliasName := generator.GoTypeToName(valuegoAliasTyp)
|
||||||
|
|
||||||
|
nullable, valuegoTyp, valuegoAliasTyp := generator.GoMapValueTypes(field, m.ValueField, valuegoTyp, valuegoAliasTyp)
|
||||||
|
|
||||||
|
p.P(p.varGen.Next(), ` := r.Intn(10)`)
|
||||||
|
p.P(`this.`, fieldname, ` = make(`, m.GoType, `)`)
|
||||||
|
p.P(`for i := 0; i < `, p.varGen.Current(), `; i++ {`)
|
||||||
|
p.In()
|
||||||
|
keyval := ""
|
||||||
|
if m.KeyField.IsString() {
|
||||||
|
keyval = fmt.Sprintf("randString%v(r)", p.localName)
|
||||||
|
} else {
|
||||||
|
keyval = value(keytypName, m.KeyField.GetType())
|
||||||
|
}
|
||||||
|
if keygoAliasTyp != keygoTyp {
|
||||||
|
keyval = keygoAliasTyp + `(` + keyval + `)`
|
||||||
|
}
|
||||||
|
if m.ValueField.IsMessage() || p.IsGroup(field) ||
|
||||||
|
(m.ValueField.IsBytes() && gogoproto.IsCustomType(field)) {
|
||||||
|
s := `this.` + fieldname + `[` + keyval + `] = `
|
||||||
|
if gogoproto.IsStdType(field) {
|
||||||
|
valuegoTyp = valuegoAliasTyp
|
||||||
|
}
|
||||||
|
funcCall := p.getCustomFuncCall(goTypName)
|
||||||
|
if !gogoproto.IsCustomType(field) {
|
||||||
|
goTypName = generator.GoTypeToName(valuegoTyp)
|
||||||
|
funcCall = p.getFuncCall(goTypName, m.ValueAliasField)
|
||||||
|
}
|
||||||
|
if !nullable {
|
||||||
|
funcCall = `*` + funcCall
|
||||||
|
}
|
||||||
|
if valuegoTyp != valuegoAliasTyp {
|
||||||
|
funcCall = `(` + valuegoAliasTyp + `)(` + funcCall + `)`
|
||||||
|
}
|
||||||
|
s += funcCall
|
||||||
|
p.P(s)
|
||||||
|
} else if m.ValueField.IsEnum() {
|
||||||
|
s := `this.` + fieldname + `[` + keyval + `]` + ` = ` + p.getEnumVal(m.ValueField, valuegoTyp)
|
||||||
|
p.P(s)
|
||||||
|
} else if m.ValueField.IsBytes() {
|
||||||
|
count := p.varGen.Next()
|
||||||
|
p.P(count, ` := r.Intn(100)`)
|
||||||
|
p.P(p.varGen.Next(), ` := `, keyval)
|
||||||
|
p.P(`this.`, fieldname, `[`, p.varGen.Current(), `] = make(`, valuegoTyp, `, `, count, `)`)
|
||||||
|
p.P(`for i := 0; i < `, count, `; i++ {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`this.`, fieldname, `[`, p.varGen.Current(), `][i] = byte(r.Intn(256))`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
} else if m.ValueField.IsString() {
|
||||||
|
s := `this.` + fieldname + `[` + keyval + `]` + ` = ` + fmt.Sprintf("randString%v(r)", p.localName)
|
||||||
|
p.P(s)
|
||||||
|
} else {
|
||||||
|
p.P(p.varGen.Next(), ` := `, keyval)
|
||||||
|
p.P(`this.`, fieldname, `[`, p.varGen.Current(), `] = `, value(valuetypAliasName, m.ValueField.GetType()))
|
||||||
|
if negative(m.ValueField.GetType()) {
|
||||||
|
p.P(`if r.Intn(2) == 0 {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`this.`, fieldname, `[`, p.varGen.Current(), `] *= -1`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
} else if gogoproto.IsCustomType(field) {
|
||||||
|
funcCall := p.getCustomFuncCall(goTypName)
|
||||||
|
if field.IsRepeated() {
|
||||||
|
p.P(p.varGen.Next(), ` := r.Intn(10)`)
|
||||||
|
p.P(`this.`, fieldname, ` = make(`, goTyp, `, `, p.varGen.Current(), `)`)
|
||||||
|
p.P(`for i := 0; i < `, p.varGen.Current(), `; i++ {`)
|
||||||
|
p.In()
|
||||||
|
p.P(p.varGen.Next(), `:= `, funcCall)
|
||||||
|
p.P(`this.`, fieldname, `[i] = *`, p.varGen.Current())
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
} else if gogoproto.IsNullable(field) {
|
||||||
|
p.P(`this.`, fieldname, ` = `, funcCall)
|
||||||
|
} else {
|
||||||
|
p.P(p.varGen.Next(), `:= `, funcCall)
|
||||||
|
p.P(`this.`, fieldname, ` = *`, p.varGen.Current())
|
||||||
|
}
|
||||||
|
} else if field.IsMessage() || p.IsGroup(field) {
|
||||||
|
funcCall := p.getFuncCall(goTypName, field)
|
||||||
|
if field.IsRepeated() {
|
||||||
|
p.P(p.varGen.Next(), ` := r.Intn(5)`)
|
||||||
|
p.P(`this.`, fieldname, ` = make(`, goTyp, `, `, p.varGen.Current(), `)`)
|
||||||
|
p.P(`for i := 0; i < `, p.varGen.Current(), `; i++ {`)
|
||||||
|
p.In()
|
||||||
|
if gogoproto.IsNullable(field) {
|
||||||
|
p.P(`this.`, fieldname, `[i] = `, funcCall)
|
||||||
|
} else {
|
||||||
|
p.P(p.varGen.Next(), `:= `, funcCall)
|
||||||
|
p.P(`this.`, fieldname, `[i] = *`, p.varGen.Current())
|
||||||
|
}
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
} else {
|
||||||
|
if gogoproto.IsNullable(field) {
|
||||||
|
p.P(`this.`, fieldname, ` = `, funcCall)
|
||||||
|
} else {
|
||||||
|
p.P(p.varGen.Next(), `:= `, funcCall)
|
||||||
|
p.P(`this.`, fieldname, ` = *`, p.varGen.Current())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if field.IsEnum() {
|
||||||
|
val := p.getEnumVal(field, goTyp)
|
||||||
|
if field.IsRepeated() {
|
||||||
|
p.P(p.varGen.Next(), ` := r.Intn(10)`)
|
||||||
|
p.P(`this.`, fieldname, ` = make(`, goTyp, `, `, p.varGen.Current(), `)`)
|
||||||
|
p.P(`for i := 0; i < `, p.varGen.Current(), `; i++ {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`this.`, fieldname, `[i] = `, val)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
} else if !gogoproto.IsNullable(field) || proto3 {
|
||||||
|
p.P(`this.`, fieldname, ` = `, val)
|
||||||
|
} else {
|
||||||
|
p.P(p.varGen.Next(), ` := `, val)
|
||||||
|
p.P(`this.`, fieldname, ` = &`, p.varGen.Current())
|
||||||
|
}
|
||||||
|
} else if field.IsBytes() {
|
||||||
|
if field.IsRepeated() {
|
||||||
|
p.P(p.varGen.Next(), ` := r.Intn(10)`)
|
||||||
|
p.P(`this.`, fieldname, ` = make(`, goTyp, `, `, p.varGen.Current(), `)`)
|
||||||
|
p.P(`for i := 0; i < `, p.varGen.Current(), `; i++ {`)
|
||||||
|
p.In()
|
||||||
|
p.P(p.varGen.Next(), ` := r.Intn(100)`)
|
||||||
|
p.P(`this.`, fieldname, `[i] = make([]byte,`, p.varGen.Current(), `)`)
|
||||||
|
p.P(`for j := 0; j < `, p.varGen.Current(), `; j++ {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`this.`, fieldname, `[i][j] = byte(r.Intn(256))`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
} else {
|
||||||
|
p.P(p.varGen.Next(), ` := r.Intn(100)`)
|
||||||
|
p.P(`this.`, fieldname, ` = make(`, goTyp, `, `, p.varGen.Current(), `)`)
|
||||||
|
p.P(`for i := 0; i < `, p.varGen.Current(), `; i++ {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`this.`, fieldname, `[i] = byte(r.Intn(256))`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
}
|
||||||
|
} else if field.IsString() {
|
||||||
|
typName := generator.GoTypeToName(goTyp)
|
||||||
|
val := fmt.Sprintf("%s(randString%v(r))", typName, p.localName)
|
||||||
|
if field.IsRepeated() {
|
||||||
|
p.P(p.varGen.Next(), ` := r.Intn(10)`)
|
||||||
|
p.P(`this.`, fieldname, ` = make(`, goTyp, `, `, p.varGen.Current(), `)`)
|
||||||
|
p.P(`for i := 0; i < `, p.varGen.Current(), `; i++ {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`this.`, fieldname, `[i] = `, val)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
} else if !gogoproto.IsNullable(field) || proto3 {
|
||||||
|
p.P(`this.`, fieldname, ` = `, val)
|
||||||
|
} else {
|
||||||
|
p.P(p.varGen.Next(), `:= `, val)
|
||||||
|
p.P(`this.`, fieldname, ` = &`, p.varGen.Current())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
typName := generator.GoTypeToName(goTyp)
|
||||||
|
if field.IsRepeated() {
|
||||||
|
p.P(p.varGen.Next(), ` := r.Intn(10)`)
|
||||||
|
p.P(`this.`, fieldname, ` = make(`, goTyp, `, `, p.varGen.Current(), `)`)
|
||||||
|
p.P(`for i := 0; i < `, p.varGen.Current(), `; i++ {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`this.`, fieldname, `[i] = `, value(typName, field.GetType()))
|
||||||
|
if negative(field.GetType()) {
|
||||||
|
p.P(`if r.Intn(2) == 0 {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`this.`, fieldname, `[i] *= -1`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
}
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
} else if !gogoproto.IsNullable(field) || proto3 {
|
||||||
|
p.P(`this.`, fieldname, ` = `, value(typName, field.GetType()))
|
||||||
|
if negative(field.GetType()) {
|
||||||
|
p.P(`if r.Intn(2) == 0 {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`this.`, fieldname, ` *= -1`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
p.P(p.varGen.Next(), ` := `, value(typName, field.GetType()))
|
||||||
|
if negative(field.GetType()) {
|
||||||
|
p.P(`if r.Intn(2) == 0 {`)
|
||||||
|
p.In()
|
||||||
|
p.P(p.varGen.Current(), ` *= -1`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
}
|
||||||
|
p.P(`this.`, fieldname, ` = &`, p.varGen.Current())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *plugin) hasLoop(pkg string, field *descriptor.FieldDescriptorProto, visited []*generator.Descriptor, excludes []*generator.Descriptor) *generator.Descriptor {
|
||||||
|
if field.IsMessage() || p.IsGroup(field) || p.IsMap(field) {
|
||||||
|
var fieldMessage *generator.Descriptor
|
||||||
|
if p.IsMap(field) {
|
||||||
|
m := p.GoMapType(nil, field)
|
||||||
|
if !m.ValueField.IsMessage() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
fieldMessage = p.ObjectNamed(m.ValueField.GetTypeName()).(*generator.Descriptor)
|
||||||
|
} else {
|
||||||
|
fieldMessage = p.ObjectNamed(field.GetTypeName()).(*generator.Descriptor)
|
||||||
|
}
|
||||||
|
fieldTypeName := generator.CamelCaseSlice(fieldMessage.TypeName())
|
||||||
|
for _, message := range visited {
|
||||||
|
messageTypeName := generator.CamelCaseSlice(message.TypeName())
|
||||||
|
if fieldTypeName == messageTypeName {
|
||||||
|
for _, e := range excludes {
|
||||||
|
if fieldTypeName == generator.CamelCaseSlice(e.TypeName()) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fieldMessage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, f := range fieldMessage.Field {
|
||||||
|
if strings.HasPrefix(f.GetTypeName(), "."+pkg) {
|
||||||
|
visited = append(visited, fieldMessage)
|
||||||
|
loopTo := p.hasLoop(pkg, f, visited, excludes)
|
||||||
|
if loopTo != nil {
|
||||||
|
return loopTo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *plugin) loops(pkg string, field *descriptor.FieldDescriptorProto, message *generator.Descriptor) int {
|
||||||
|
//fmt.Fprintf(os.Stderr, "loops %v %v\n", field.GetTypeName(), generator.CamelCaseSlice(message.TypeName()))
|
||||||
|
excludes := []*generator.Descriptor{}
|
||||||
|
loops := 0
|
||||||
|
for {
|
||||||
|
visited := []*generator.Descriptor{}
|
||||||
|
loopTo := p.hasLoop(pkg, field, visited, excludes)
|
||||||
|
if loopTo == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
//fmt.Fprintf(os.Stderr, "loopTo %v\n", generator.CamelCaseSlice(loopTo.TypeName()))
|
||||||
|
excludes = append(excludes, loopTo)
|
||||||
|
loops++
|
||||||
|
}
|
||||||
|
return loops
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *plugin) Generate(file *generator.FileDescriptor) {
|
||||||
|
p.atleastOne = false
|
||||||
|
p.PluginImports = generator.NewPluginImports(p.Generator)
|
||||||
|
p.varGen = NewVarGen()
|
||||||
|
proto3 := gogoproto.IsProto3(file.FileDescriptorProto)
|
||||||
|
p.typesPkg = p.NewImport("github.com/gogo/protobuf/types")
|
||||||
|
p.localName = generator.FileName(file)
|
||||||
|
protoPkg := p.NewImport("github.com/gogo/protobuf/proto")
|
||||||
|
if !gogoproto.ImportsGoGoProto(file.FileDescriptorProto) {
|
||||||
|
protoPkg = p.NewImport("github.com/golang/protobuf/proto")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, message := range file.Messages() {
|
||||||
|
if !gogoproto.HasPopulate(file.FileDescriptorProto, message.DescriptorProto) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if message.DescriptorProto.GetOptions().GetMapEntry() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
p.atleastOne = true
|
||||||
|
ccTypeName := generator.CamelCaseSlice(message.TypeName())
|
||||||
|
loopLevels := make([]int, len(message.Field))
|
||||||
|
maxLoopLevel := 0
|
||||||
|
for i, field := range message.Field {
|
||||||
|
loopLevels[i] = p.loops(file.GetPackage(), field, message)
|
||||||
|
if loopLevels[i] > maxLoopLevel {
|
||||||
|
maxLoopLevel = loopLevels[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ranTotal := 0
|
||||||
|
for i := range loopLevels {
|
||||||
|
ranTotal += int(math.Pow10(maxLoopLevel - loopLevels[i]))
|
||||||
|
}
|
||||||
|
p.P(`func NewPopulated`, ccTypeName, `(r randy`, p.localName, `, easy bool) *`, ccTypeName, ` {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`this := &`, ccTypeName, `{}`)
|
||||||
|
if gogoproto.IsUnion(message.File().FileDescriptorProto, message.DescriptorProto) && len(message.Field) > 0 {
|
||||||
|
p.P(`fieldNum := r.Intn(`, fmt.Sprintf("%d", ranTotal), `)`)
|
||||||
|
p.P(`switch fieldNum {`)
|
||||||
|
k := 0
|
||||||
|
for i, field := range message.Field {
|
||||||
|
is := []string{}
|
||||||
|
ran := int(math.Pow10(maxLoopLevel - loopLevels[i]))
|
||||||
|
for j := 0; j < ran; j++ {
|
||||||
|
is = append(is, fmt.Sprintf("%d", j+k))
|
||||||
|
}
|
||||||
|
k += ran
|
||||||
|
p.P(`case `, strings.Join(is, ","), `:`)
|
||||||
|
p.In()
|
||||||
|
p.GenerateField(file, message, field)
|
||||||
|
p.Out()
|
||||||
|
}
|
||||||
|
p.P(`}`)
|
||||||
|
} else {
|
||||||
|
var maxFieldNumber int32
|
||||||
|
oneofs := make(map[string]struct{})
|
||||||
|
for fieldIndex, field := range message.Field {
|
||||||
|
if field.GetNumber() > maxFieldNumber {
|
||||||
|
maxFieldNumber = field.GetNumber()
|
||||||
|
}
|
||||||
|
oneof := field.OneofIndex != nil
|
||||||
|
if !oneof {
|
||||||
|
if field.IsRequired() || (!gogoproto.IsNullable(field) && !field.IsRepeated()) || (proto3 && !field.IsMessage()) {
|
||||||
|
p.GenerateField(file, message, field)
|
||||||
|
} else {
|
||||||
|
if loopLevels[fieldIndex] > 0 {
|
||||||
|
p.P(`if r.Intn(5) == 0 {`)
|
||||||
|
} else {
|
||||||
|
p.P(`if r.Intn(5) != 0 {`)
|
||||||
|
}
|
||||||
|
p.In()
|
||||||
|
p.GenerateField(file, message, field)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fieldname := p.GetFieldName(message, field)
|
||||||
|
if _, ok := oneofs[fieldname]; ok {
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
oneofs[fieldname] = struct{}{}
|
||||||
|
}
|
||||||
|
fieldNumbers := []int32{}
|
||||||
|
for _, f := range message.Field {
|
||||||
|
fname := p.GetFieldName(message, f)
|
||||||
|
if fname == fieldname {
|
||||||
|
fieldNumbers = append(fieldNumbers, f.GetNumber())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p.P(`oneofNumber_`, fieldname, ` := `, fmt.Sprintf("%#v", fieldNumbers), `[r.Intn(`, strconv.Itoa(len(fieldNumbers)), `)]`)
|
||||||
|
p.P(`switch oneofNumber_`, fieldname, ` {`)
|
||||||
|
for _, f := range message.Field {
|
||||||
|
fname := p.GetFieldName(message, f)
|
||||||
|
if fname != fieldname {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
p.P(`case `, strconv.Itoa(int(f.GetNumber())), `:`)
|
||||||
|
p.In()
|
||||||
|
ccTypeName := p.OneOfTypeName(message, f)
|
||||||
|
p.P(`this.`, fname, ` = NewPopulated`, ccTypeName, `(r, easy)`)
|
||||||
|
p.Out()
|
||||||
|
}
|
||||||
|
p.P(`}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if message.DescriptorProto.HasExtension() {
|
||||||
|
p.P(`if !easy && r.Intn(10) != 0 {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`l := r.Intn(5)`)
|
||||||
|
p.P(`for i := 0; i < l; i++ {`)
|
||||||
|
p.In()
|
||||||
|
if len(message.DescriptorProto.GetExtensionRange()) > 1 {
|
||||||
|
p.P(`eIndex := r.Intn(`, strconv.Itoa(len(message.DescriptorProto.GetExtensionRange())), `)`)
|
||||||
|
p.P(`fieldNumber := 0`)
|
||||||
|
p.P(`switch eIndex {`)
|
||||||
|
for i, e := range message.DescriptorProto.GetExtensionRange() {
|
||||||
|
p.P(`case `, strconv.Itoa(i), `:`)
|
||||||
|
p.In()
|
||||||
|
p.P(`fieldNumber = r.Intn(`, strconv.Itoa(int(e.GetEnd()-e.GetStart())), `) + `, strconv.Itoa(int(e.GetStart())))
|
||||||
|
p.Out()
|
||||||
|
if e.GetEnd() > maxFieldNumber {
|
||||||
|
maxFieldNumber = e.GetEnd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.P(`}`)
|
||||||
|
} else {
|
||||||
|
e := message.DescriptorProto.GetExtensionRange()[0]
|
||||||
|
p.P(`fieldNumber := r.Intn(`, strconv.Itoa(int(e.GetEnd()-e.GetStart())), `) + `, strconv.Itoa(int(e.GetStart())))
|
||||||
|
if e.GetEnd() > maxFieldNumber {
|
||||||
|
maxFieldNumber = e.GetEnd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.P(`wire := r.Intn(4)`)
|
||||||
|
p.P(`if wire == 3 { wire = 5 }`)
|
||||||
|
p.P(`dAtA := randField`, p.localName, `(nil, r, fieldNumber, wire)`)
|
||||||
|
p.P(protoPkg.Use(), `.SetRawExtension(this, int32(fieldNumber), dAtA)`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if maxFieldNumber < (1 << 10) {
|
||||||
|
p.P(`if !easy && r.Intn(10) != 0 {`)
|
||||||
|
p.In()
|
||||||
|
if gogoproto.HasUnrecognized(file.FileDescriptorProto, message.DescriptorProto) {
|
||||||
|
p.P(`this.XXX_unrecognized = randUnrecognized`, p.localName, `(r, `, strconv.Itoa(int(maxFieldNumber+1)), `)`)
|
||||||
|
}
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.P(`return this`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(``)
|
||||||
|
|
||||||
|
//Generate NewPopulated functions for oneof fields
|
||||||
|
m := proto.Clone(message.DescriptorProto).(*descriptor.DescriptorProto)
|
||||||
|
for _, f := range m.Field {
|
||||||
|
oneof := f.OneofIndex != nil
|
||||||
|
if !oneof {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ccTypeName := p.OneOfTypeName(message, f)
|
||||||
|
p.P(`func NewPopulated`, ccTypeName, `(r randy`, p.localName, `, easy bool) *`, ccTypeName, ` {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`this := &`, ccTypeName, `{}`)
|
||||||
|
vanity.TurnOffNullableForNativeTypes(f)
|
||||||
|
p.GenerateField(file, message, f)
|
||||||
|
p.P(`return this`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !p.atleastOne {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
p.P(`type randy`, p.localName, ` interface {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`Float32() float32`)
|
||||||
|
p.P(`Float64() float64`)
|
||||||
|
p.P(`Int63() int64`)
|
||||||
|
p.P(`Int31() int32`)
|
||||||
|
p.P(`Uint32() uint32`)
|
||||||
|
p.P(`Intn(n int) int`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
|
||||||
|
p.P(`func randUTF8Rune`, p.localName, `(r randy`, p.localName, `) rune {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`ru := r.Intn(62)`)
|
||||||
|
p.P(`if ru < 10 {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`return rune(ru+48)`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`} else if ru < 36 {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`return rune(ru+55)`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`return rune(ru+61)`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
|
||||||
|
p.P(`func randString`, p.localName, `(r randy`, p.localName, `) string {`)
|
||||||
|
p.In()
|
||||||
|
p.P(p.varGen.Next(), ` := r.Intn(100)`)
|
||||||
|
p.P(`tmps := make([]rune, `, p.varGen.Current(), `)`)
|
||||||
|
p.P(`for i := 0; i < `, p.varGen.Current(), `; i++ {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`tmps[i] = randUTF8Rune`, p.localName, `(r)`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`return string(tmps)`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
|
||||||
|
p.P(`func randUnrecognized`, p.localName, `(r randy`, p.localName, `, maxFieldNumber int) (dAtA []byte) {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`l := r.Intn(5)`)
|
||||||
|
p.P(`for i := 0; i < l; i++ {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`wire := r.Intn(4)`)
|
||||||
|
p.P(`if wire == 3 { wire = 5 }`)
|
||||||
|
p.P(`fieldNumber := maxFieldNumber + r.Intn(100)`)
|
||||||
|
p.P(`dAtA = randField`, p.localName, `(dAtA, r, fieldNumber, wire)`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`return dAtA`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
|
||||||
|
p.P(`func randField`, p.localName, `(dAtA []byte, r randy`, p.localName, `, fieldNumber int, wire int) []byte {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`key := uint32(fieldNumber)<<3 | uint32(wire)`)
|
||||||
|
p.P(`switch wire {`)
|
||||||
|
p.P(`case 0:`)
|
||||||
|
p.In()
|
||||||
|
p.P(`dAtA = encodeVarintPopulate`, p.localName, `(dAtA, uint64(key))`)
|
||||||
|
p.P(p.varGen.Next(), ` := r.Int63()`)
|
||||||
|
p.P(`if r.Intn(2) == 0 {`)
|
||||||
|
p.In()
|
||||||
|
p.P(p.varGen.Current(), ` *= -1`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`dAtA = encodeVarintPopulate`, p.localName, `(dAtA, uint64(`, p.varGen.Current(), `))`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`case 1:`)
|
||||||
|
p.In()
|
||||||
|
p.P(`dAtA = encodeVarintPopulate`, p.localName, `(dAtA, uint64(key))`)
|
||||||
|
p.P(`dAtA = append(dAtA, byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)))`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`case 2:`)
|
||||||
|
p.In()
|
||||||
|
p.P(`dAtA = encodeVarintPopulate`, p.localName, `(dAtA, uint64(key))`)
|
||||||
|
p.P(`ll := r.Intn(100)`)
|
||||||
|
p.P(`dAtA = encodeVarintPopulate`, p.localName, `(dAtA, uint64(ll))`)
|
||||||
|
p.P(`for j := 0; j < ll; j++ {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`dAtA = append(dAtA, byte(r.Intn(256)))`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`default:`)
|
||||||
|
p.In()
|
||||||
|
p.P(`dAtA = encodeVarintPopulate`, p.localName, `(dAtA, uint64(key))`)
|
||||||
|
p.P(`dAtA = append(dAtA, byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)))`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`return dAtA`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
|
||||||
|
p.P(`func encodeVarintPopulate`, p.localName, `(dAtA []byte, v uint64) []byte {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`for v >= 1<<7 {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`dAtA = append(dAtA, uint8(uint64(v)&0x7f|0x80))`)
|
||||||
|
p.P(`v >>= 7`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`dAtA = append(dAtA, uint8(v))`)
|
||||||
|
p.P(`return dAtA`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
generator.RegisterPlugin(NewPlugin())
|
||||||
|
}
|
|
@ -0,0 +1,696 @@
|
||||||
|
// Protocol Buffers for Go with Gadgets
|
||||||
|
//
|
||||||
|
// Copyright (c) 2013, The GoGo Authors. All rights reserved.
|
||||||
|
// http://github.com/gogo/protobuf
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
/*
|
||||||
|
The size plugin generates a Size or ProtoSize method for each message.
|
||||||
|
This is useful with the MarshalTo method generated by the marshalto plugin and the
|
||||||
|
gogoproto.marshaler and gogoproto.marshaler_all extensions.
|
||||||
|
|
||||||
|
It is enabled by the following extensions:
|
||||||
|
|
||||||
|
- sizer
|
||||||
|
- sizer_all
|
||||||
|
- protosizer
|
||||||
|
- protosizer_all
|
||||||
|
|
||||||
|
The size plugin also generates a test given it is enabled using one of the following extensions:
|
||||||
|
|
||||||
|
- testgen
|
||||||
|
- testgen_all
|
||||||
|
|
||||||
|
And a benchmark given it is enabled using one of the following extensions:
|
||||||
|
|
||||||
|
- benchgen
|
||||||
|
- benchgen_all
|
||||||
|
|
||||||
|
Let us look at:
|
||||||
|
|
||||||
|
github.com/gogo/protobuf/test/example/example.proto
|
||||||
|
|
||||||
|
Btw all the output can be seen at:
|
||||||
|
|
||||||
|
github.com/gogo/protobuf/test/example/*
|
||||||
|
|
||||||
|
The following message:
|
||||||
|
|
||||||
|
option (gogoproto.sizer_all) = true;
|
||||||
|
|
||||||
|
message B {
|
||||||
|
option (gogoproto.description) = true;
|
||||||
|
optional A A = 1 [(gogoproto.nullable) = false, (gogoproto.embed) = true];
|
||||||
|
repeated bytes G = 2 [(gogoproto.customtype) = "github.com/gogo/protobuf/test/custom.Uint128", (gogoproto.nullable) = false];
|
||||||
|
}
|
||||||
|
|
||||||
|
given to the size plugin, will generate the following code:
|
||||||
|
|
||||||
|
func (m *B) Size() (n int) {
|
||||||
|
if m == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
var l int
|
||||||
|
_ = l
|
||||||
|
l = m.A.Size()
|
||||||
|
n += 1 + l + sovExample(uint64(l))
|
||||||
|
if len(m.G) > 0 {
|
||||||
|
for _, e := range m.G {
|
||||||
|
l = e.Size()
|
||||||
|
n += 1 + l + sovExample(uint64(l))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if m.XXX_unrecognized != nil {
|
||||||
|
n += len(m.XXX_unrecognized)
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
and the following test code:
|
||||||
|
|
||||||
|
func TestBSize(t *testing5.T) {
|
||||||
|
popr := math_rand5.New(math_rand5.NewSource(time5.Now().UnixNano()))
|
||||||
|
p := NewPopulatedB(popr, true)
|
||||||
|
dAtA, err := github_com_gogo_protobuf_proto2.Marshal(p)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
size := p.Size()
|
||||||
|
if len(dAtA) != size {
|
||||||
|
t.Fatalf("size %v != marshalled size %v", size, len(dAtA))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkBSize(b *testing5.B) {
|
||||||
|
popr := math_rand5.New(math_rand5.NewSource(616))
|
||||||
|
total := 0
|
||||||
|
pops := make([]*B, 1000)
|
||||||
|
for i := 0; i < 1000; i++ {
|
||||||
|
pops[i] = NewPopulatedB(popr, false)
|
||||||
|
}
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
total += pops[i%1000].Size()
|
||||||
|
}
|
||||||
|
b.SetBytes(int64(total / b.N))
|
||||||
|
}
|
||||||
|
|
||||||
|
The sovExample function is a size of varint function for the example.pb.go file.
|
||||||
|
|
||||||
|
*/
|
||||||
|
package size
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gogo/protobuf/gogoproto"
|
||||||
|
"github.com/gogo/protobuf/proto"
|
||||||
|
descriptor "github.com/gogo/protobuf/protoc-gen-gogo/descriptor"
|
||||||
|
"github.com/gogo/protobuf/protoc-gen-gogo/generator"
|
||||||
|
"github.com/gogo/protobuf/vanity"
|
||||||
|
)
|
||||||
|
|
||||||
|
type size struct {
|
||||||
|
*generator.Generator
|
||||||
|
generator.PluginImports
|
||||||
|
atleastOne bool
|
||||||
|
localName string
|
||||||
|
typesPkg generator.Single
|
||||||
|
bitsPkg generator.Single
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSize() *size {
|
||||||
|
return &size{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *size) Name() string {
|
||||||
|
return "size"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *size) Init(g *generator.Generator) {
|
||||||
|
p.Generator = g
|
||||||
|
}
|
||||||
|
|
||||||
|
func wireToType(wire string) int {
|
||||||
|
switch wire {
|
||||||
|
case "fixed64":
|
||||||
|
return proto.WireFixed64
|
||||||
|
case "fixed32":
|
||||||
|
return proto.WireFixed32
|
||||||
|
case "varint":
|
||||||
|
return proto.WireVarint
|
||||||
|
case "bytes":
|
||||||
|
return proto.WireBytes
|
||||||
|
case "group":
|
||||||
|
return proto.WireBytes
|
||||||
|
case "zigzag32":
|
||||||
|
return proto.WireVarint
|
||||||
|
case "zigzag64":
|
||||||
|
return proto.WireVarint
|
||||||
|
}
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
|
||||||
|
func keySize(fieldNumber int32, wireType int) int {
|
||||||
|
x := uint32(fieldNumber)<<3 | uint32(wireType)
|
||||||
|
size := 0
|
||||||
|
for size = 0; x > 127; size++ {
|
||||||
|
x >>= 7
|
||||||
|
}
|
||||||
|
size++
|
||||||
|
return size
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *size) sizeVarint() {
|
||||||
|
p.P(`
|
||||||
|
func sov`, p.localName, `(x uint64) (n int) {
|
||||||
|
return (`, p.bitsPkg.Use(), `.Len64(x | 1) + 6)/ 7
|
||||||
|
}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *size) sizeZigZag() {
|
||||||
|
p.P(`func soz`, p.localName, `(x uint64) (n int) {
|
||||||
|
return sov`, p.localName, `(uint64((x << 1) ^ uint64((int64(x) >> 63))))
|
||||||
|
}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *size) std(field *descriptor.FieldDescriptorProto, name string) (string, bool) {
|
||||||
|
ptr := ""
|
||||||
|
if gogoproto.IsNullable(field) {
|
||||||
|
ptr = "*"
|
||||||
|
}
|
||||||
|
if gogoproto.IsStdTime(field) {
|
||||||
|
return p.typesPkg.Use() + `.SizeOfStdTime(` + ptr + name + `)`, true
|
||||||
|
} else if gogoproto.IsStdDuration(field) {
|
||||||
|
return p.typesPkg.Use() + `.SizeOfStdDuration(` + ptr + name + `)`, true
|
||||||
|
} else if gogoproto.IsStdDouble(field) {
|
||||||
|
return p.typesPkg.Use() + `.SizeOfStdDouble(` + ptr + name + `)`, true
|
||||||
|
} else if gogoproto.IsStdFloat(field) {
|
||||||
|
return p.typesPkg.Use() + `.SizeOfStdFloat(` + ptr + name + `)`, true
|
||||||
|
} else if gogoproto.IsStdInt64(field) {
|
||||||
|
return p.typesPkg.Use() + `.SizeOfStdInt64(` + ptr + name + `)`, true
|
||||||
|
} else if gogoproto.IsStdUInt64(field) {
|
||||||
|
return p.typesPkg.Use() + `.SizeOfStdUInt64(` + ptr + name + `)`, true
|
||||||
|
} else if gogoproto.IsStdInt32(field) {
|
||||||
|
return p.typesPkg.Use() + `.SizeOfStdInt32(` + ptr + name + `)`, true
|
||||||
|
} else if gogoproto.IsStdUInt32(field) {
|
||||||
|
return p.typesPkg.Use() + `.SizeOfStdUInt32(` + ptr + name + `)`, true
|
||||||
|
} else if gogoproto.IsStdBool(field) {
|
||||||
|
return p.typesPkg.Use() + `.SizeOfStdBool(` + ptr + name + `)`, true
|
||||||
|
} else if gogoproto.IsStdString(field) {
|
||||||
|
return p.typesPkg.Use() + `.SizeOfStdString(` + ptr + name + `)`, true
|
||||||
|
} else if gogoproto.IsStdBytes(field) {
|
||||||
|
return p.typesPkg.Use() + `.SizeOfStdBytes(` + ptr + name + `)`, true
|
||||||
|
}
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *size) generateField(proto3 bool, file *generator.FileDescriptor, message *generator.Descriptor, field *descriptor.FieldDescriptorProto, sizeName string) {
|
||||||
|
fieldname := p.GetOneOfFieldName(message, field)
|
||||||
|
nullable := gogoproto.IsNullable(field)
|
||||||
|
repeated := field.IsRepeated()
|
||||||
|
doNilCheck := gogoproto.NeedsNilCheck(proto3, field)
|
||||||
|
if repeated {
|
||||||
|
p.P(`if len(m.`, fieldname, `) > 0 {`)
|
||||||
|
p.In()
|
||||||
|
} else if doNilCheck {
|
||||||
|
p.P(`if m.`, fieldname, ` != nil {`)
|
||||||
|
p.In()
|
||||||
|
}
|
||||||
|
packed := field.IsPacked() || (proto3 && field.IsPacked3())
|
||||||
|
_, wire := p.GoType(message, field)
|
||||||
|
wireType := wireToType(wire)
|
||||||
|
fieldNumber := field.GetNumber()
|
||||||
|
if packed {
|
||||||
|
wireType = proto.WireBytes
|
||||||
|
}
|
||||||
|
key := keySize(fieldNumber, wireType)
|
||||||
|
switch *field.Type {
|
||||||
|
case descriptor.FieldDescriptorProto_TYPE_DOUBLE,
|
||||||
|
descriptor.FieldDescriptorProto_TYPE_FIXED64,
|
||||||
|
descriptor.FieldDescriptorProto_TYPE_SFIXED64:
|
||||||
|
if packed {
|
||||||
|
p.P(`n+=`, strconv.Itoa(key), `+sov`, p.localName, `(uint64(len(m.`, fieldname, `)*8))`, `+len(m.`, fieldname, `)*8`)
|
||||||
|
} else if repeated {
|
||||||
|
p.P(`n+=`, strconv.Itoa(key+8), `*len(m.`, fieldname, `)`)
|
||||||
|
} else if proto3 {
|
||||||
|
p.P(`if m.`, fieldname, ` != 0 {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`n+=`, strconv.Itoa(key+8))
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
} else if nullable {
|
||||||
|
p.P(`n+=`, strconv.Itoa(key+8))
|
||||||
|
} else {
|
||||||
|
p.P(`n+=`, strconv.Itoa(key+8))
|
||||||
|
}
|
||||||
|
case descriptor.FieldDescriptorProto_TYPE_FLOAT,
|
||||||
|
descriptor.FieldDescriptorProto_TYPE_FIXED32,
|
||||||
|
descriptor.FieldDescriptorProto_TYPE_SFIXED32:
|
||||||
|
if packed {
|
||||||
|
p.P(`n+=`, strconv.Itoa(key), `+sov`, p.localName, `(uint64(len(m.`, fieldname, `)*4))`, `+len(m.`, fieldname, `)*4`)
|
||||||
|
} else if repeated {
|
||||||
|
p.P(`n+=`, strconv.Itoa(key+4), `*len(m.`, fieldname, `)`)
|
||||||
|
} else if proto3 {
|
||||||
|
p.P(`if m.`, fieldname, ` != 0 {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`n+=`, strconv.Itoa(key+4))
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
} else if nullable {
|
||||||
|
p.P(`n+=`, strconv.Itoa(key+4))
|
||||||
|
} else {
|
||||||
|
p.P(`n+=`, strconv.Itoa(key+4))
|
||||||
|
}
|
||||||
|
case descriptor.FieldDescriptorProto_TYPE_INT64,
|
||||||
|
descriptor.FieldDescriptorProto_TYPE_UINT64,
|
||||||
|
descriptor.FieldDescriptorProto_TYPE_UINT32,
|
||||||
|
descriptor.FieldDescriptorProto_TYPE_ENUM,
|
||||||
|
descriptor.FieldDescriptorProto_TYPE_INT32:
|
||||||
|
if packed {
|
||||||
|
p.P(`l = 0`)
|
||||||
|
p.P(`for _, e := range m.`, fieldname, ` {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`l+=sov`, p.localName, `(uint64(e))`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`n+=`, strconv.Itoa(key), `+sov`, p.localName, `(uint64(l))+l`)
|
||||||
|
} else if repeated {
|
||||||
|
p.P(`for _, e := range m.`, fieldname, ` {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`n+=`, strconv.Itoa(key), `+sov`, p.localName, `(uint64(e))`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
} else if proto3 {
|
||||||
|
p.P(`if m.`, fieldname, ` != 0 {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`n+=`, strconv.Itoa(key), `+sov`, p.localName, `(uint64(m.`, fieldname, `))`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
} else if nullable {
|
||||||
|
p.P(`n+=`, strconv.Itoa(key), `+sov`, p.localName, `(uint64(*m.`, fieldname, `))`)
|
||||||
|
} else {
|
||||||
|
p.P(`n+=`, strconv.Itoa(key), `+sov`, p.localName, `(uint64(m.`, fieldname, `))`)
|
||||||
|
}
|
||||||
|
case descriptor.FieldDescriptorProto_TYPE_BOOL:
|
||||||
|
if packed {
|
||||||
|
p.P(`n+=`, strconv.Itoa(key), `+sov`, p.localName, `(uint64(len(m.`, fieldname, `)))`, `+len(m.`, fieldname, `)*1`)
|
||||||
|
} else if repeated {
|
||||||
|
p.P(`n+=`, strconv.Itoa(key+1), `*len(m.`, fieldname, `)`)
|
||||||
|
} else if proto3 {
|
||||||
|
p.P(`if m.`, fieldname, ` {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`n+=`, strconv.Itoa(key+1))
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
} else if nullable {
|
||||||
|
p.P(`n+=`, strconv.Itoa(key+1))
|
||||||
|
} else {
|
||||||
|
p.P(`n+=`, strconv.Itoa(key+1))
|
||||||
|
}
|
||||||
|
case descriptor.FieldDescriptorProto_TYPE_STRING:
|
||||||
|
if repeated {
|
||||||
|
p.P(`for _, s := range m.`, fieldname, ` { `)
|
||||||
|
p.In()
|
||||||
|
p.P(`l = len(s)`)
|
||||||
|
p.P(`n+=`, strconv.Itoa(key), `+l+sov`, p.localName, `(uint64(l))`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
} else if proto3 {
|
||||||
|
p.P(`l=len(m.`, fieldname, `)`)
|
||||||
|
p.P(`if l > 0 {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`n+=`, strconv.Itoa(key), `+l+sov`, p.localName, `(uint64(l))`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
} else if nullable {
|
||||||
|
p.P(`l=len(*m.`, fieldname, `)`)
|
||||||
|
p.P(`n+=`, strconv.Itoa(key), `+l+sov`, p.localName, `(uint64(l))`)
|
||||||
|
} else {
|
||||||
|
p.P(`l=len(m.`, fieldname, `)`)
|
||||||
|
p.P(`n+=`, strconv.Itoa(key), `+l+sov`, p.localName, `(uint64(l))`)
|
||||||
|
}
|
||||||
|
case descriptor.FieldDescriptorProto_TYPE_GROUP:
|
||||||
|
panic(fmt.Errorf("size does not support group %v", fieldname))
|
||||||
|
case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
|
||||||
|
if p.IsMap(field) {
|
||||||
|
m := p.GoMapType(nil, field)
|
||||||
|
_, keywire := p.GoType(nil, m.KeyAliasField)
|
||||||
|
valuegoTyp, _ := p.GoType(nil, m.ValueField)
|
||||||
|
valuegoAliasTyp, valuewire := p.GoType(nil, m.ValueAliasField)
|
||||||
|
_, fieldwire := p.GoType(nil, field)
|
||||||
|
|
||||||
|
nullable, valuegoTyp, valuegoAliasTyp = generator.GoMapValueTypes(field, m.ValueField, valuegoTyp, valuegoAliasTyp)
|
||||||
|
|
||||||
|
fieldKeySize := keySize(field.GetNumber(), wireToType(fieldwire))
|
||||||
|
keyKeySize := keySize(1, wireToType(keywire))
|
||||||
|
valueKeySize := keySize(2, wireToType(valuewire))
|
||||||
|
p.P(`for k, v := range m.`, fieldname, ` { `)
|
||||||
|
p.In()
|
||||||
|
p.P(`_ = k`)
|
||||||
|
p.P(`_ = v`)
|
||||||
|
sum := []string{strconv.Itoa(keyKeySize)}
|
||||||
|
switch m.KeyField.GetType() {
|
||||||
|
case descriptor.FieldDescriptorProto_TYPE_DOUBLE,
|
||||||
|
descriptor.FieldDescriptorProto_TYPE_FIXED64,
|
||||||
|
descriptor.FieldDescriptorProto_TYPE_SFIXED64:
|
||||||
|
sum = append(sum, `8`)
|
||||||
|
case descriptor.FieldDescriptorProto_TYPE_FLOAT,
|
||||||
|
descriptor.FieldDescriptorProto_TYPE_FIXED32,
|
||||||
|
descriptor.FieldDescriptorProto_TYPE_SFIXED32:
|
||||||
|
sum = append(sum, `4`)
|
||||||
|
case descriptor.FieldDescriptorProto_TYPE_INT64,
|
||||||
|
descriptor.FieldDescriptorProto_TYPE_UINT64,
|
||||||
|
descriptor.FieldDescriptorProto_TYPE_UINT32,
|
||||||
|
descriptor.FieldDescriptorProto_TYPE_ENUM,
|
||||||
|
descriptor.FieldDescriptorProto_TYPE_INT32:
|
||||||
|
sum = append(sum, `sov`+p.localName+`(uint64(k))`)
|
||||||
|
case descriptor.FieldDescriptorProto_TYPE_BOOL:
|
||||||
|
sum = append(sum, `1`)
|
||||||
|
case descriptor.FieldDescriptorProto_TYPE_STRING,
|
||||||
|
descriptor.FieldDescriptorProto_TYPE_BYTES:
|
||||||
|
sum = append(sum, `len(k)+sov`+p.localName+`(uint64(len(k)))`)
|
||||||
|
case descriptor.FieldDescriptorProto_TYPE_SINT32,
|
||||||
|
descriptor.FieldDescriptorProto_TYPE_SINT64:
|
||||||
|
sum = append(sum, `soz`+p.localName+`(uint64(k))`)
|
||||||
|
}
|
||||||
|
switch m.ValueField.GetType() {
|
||||||
|
case descriptor.FieldDescriptorProto_TYPE_DOUBLE,
|
||||||
|
descriptor.FieldDescriptorProto_TYPE_FIXED64,
|
||||||
|
descriptor.FieldDescriptorProto_TYPE_SFIXED64:
|
||||||
|
sum = append(sum, strconv.Itoa(valueKeySize))
|
||||||
|
sum = append(sum, strconv.Itoa(8))
|
||||||
|
case descriptor.FieldDescriptorProto_TYPE_FLOAT,
|
||||||
|
descriptor.FieldDescriptorProto_TYPE_FIXED32,
|
||||||
|
descriptor.FieldDescriptorProto_TYPE_SFIXED32:
|
||||||
|
sum = append(sum, strconv.Itoa(valueKeySize))
|
||||||
|
sum = append(sum, strconv.Itoa(4))
|
||||||
|
case descriptor.FieldDescriptorProto_TYPE_INT64,
|
||||||
|
descriptor.FieldDescriptorProto_TYPE_UINT64,
|
||||||
|
descriptor.FieldDescriptorProto_TYPE_UINT32,
|
||||||
|
descriptor.FieldDescriptorProto_TYPE_ENUM,
|
||||||
|
descriptor.FieldDescriptorProto_TYPE_INT32:
|
||||||
|
sum = append(sum, strconv.Itoa(valueKeySize))
|
||||||
|
sum = append(sum, `sov`+p.localName+`(uint64(v))`)
|
||||||
|
case descriptor.FieldDescriptorProto_TYPE_BOOL:
|
||||||
|
sum = append(sum, strconv.Itoa(valueKeySize))
|
||||||
|
sum = append(sum, `1`)
|
||||||
|
case descriptor.FieldDescriptorProto_TYPE_STRING:
|
||||||
|
sum = append(sum, strconv.Itoa(valueKeySize))
|
||||||
|
sum = append(sum, `len(v)+sov`+p.localName+`(uint64(len(v)))`)
|
||||||
|
case descriptor.FieldDescriptorProto_TYPE_BYTES:
|
||||||
|
if gogoproto.IsCustomType(field) {
|
||||||
|
p.P(`l = 0`)
|
||||||
|
if nullable {
|
||||||
|
p.P(`if v != nil {`)
|
||||||
|
p.In()
|
||||||
|
}
|
||||||
|
p.P(`l = v.`, sizeName, `()`)
|
||||||
|
p.P(`l += `, strconv.Itoa(valueKeySize), ` + sov`+p.localName+`(uint64(l))`)
|
||||||
|
if nullable {
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
}
|
||||||
|
sum = append(sum, `l`)
|
||||||
|
} else {
|
||||||
|
p.P(`l = 0`)
|
||||||
|
if proto3 {
|
||||||
|
p.P(`if len(v) > 0 {`)
|
||||||
|
} else {
|
||||||
|
p.P(`if v != nil {`)
|
||||||
|
}
|
||||||
|
p.In()
|
||||||
|
p.P(`l = `, strconv.Itoa(valueKeySize), ` + len(v)+sov`+p.localName+`(uint64(len(v)))`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
sum = append(sum, `l`)
|
||||||
|
}
|
||||||
|
case descriptor.FieldDescriptorProto_TYPE_SINT32,
|
||||||
|
descriptor.FieldDescriptorProto_TYPE_SINT64:
|
||||||
|
sum = append(sum, strconv.Itoa(valueKeySize))
|
||||||
|
sum = append(sum, `soz`+p.localName+`(uint64(v))`)
|
||||||
|
case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
|
||||||
|
stdSizeCall, stdOk := p.std(m.ValueAliasField, "v")
|
||||||
|
if nullable {
|
||||||
|
p.P(`l = 0`)
|
||||||
|
p.P(`if v != nil {`)
|
||||||
|
p.In()
|
||||||
|
if stdOk {
|
||||||
|
p.P(`l = `, stdSizeCall)
|
||||||
|
} else if valuegoTyp != valuegoAliasTyp {
|
||||||
|
p.P(`l = ((`, valuegoTyp, `)(v)).`, sizeName, `()`)
|
||||||
|
} else {
|
||||||
|
p.P(`l = v.`, sizeName, `()`)
|
||||||
|
}
|
||||||
|
p.P(`l += `, strconv.Itoa(valueKeySize), ` + sov`+p.localName+`(uint64(l))`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
sum = append(sum, `l`)
|
||||||
|
} else {
|
||||||
|
if stdOk {
|
||||||
|
p.P(`l = `, stdSizeCall)
|
||||||
|
} else if valuegoTyp != valuegoAliasTyp {
|
||||||
|
p.P(`l = ((*`, valuegoTyp, `)(&v)).`, sizeName, `()`)
|
||||||
|
} else {
|
||||||
|
p.P(`l = v.`, sizeName, `()`)
|
||||||
|
}
|
||||||
|
sum = append(sum, strconv.Itoa(valueKeySize))
|
||||||
|
sum = append(sum, `l+sov`+p.localName+`(uint64(l))`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.P(`mapEntrySize := `, strings.Join(sum, "+"))
|
||||||
|
p.P(`n+=mapEntrySize+`, fieldKeySize, `+sov`, p.localName, `(uint64(mapEntrySize))`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
} else if repeated {
|
||||||
|
p.P(`for _, e := range m.`, fieldname, ` { `)
|
||||||
|
p.In()
|
||||||
|
stdSizeCall, stdOk := p.std(field, "e")
|
||||||
|
if stdOk {
|
||||||
|
p.P(`l=`, stdSizeCall)
|
||||||
|
} else {
|
||||||
|
p.P(`l=e.`, sizeName, `()`)
|
||||||
|
}
|
||||||
|
p.P(`n+=`, strconv.Itoa(key), `+l+sov`, p.localName, `(uint64(l))`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
} else {
|
||||||
|
stdSizeCall, stdOk := p.std(field, "m."+fieldname)
|
||||||
|
if stdOk {
|
||||||
|
p.P(`l=`, stdSizeCall)
|
||||||
|
} else {
|
||||||
|
p.P(`l=m.`, fieldname, `.`, sizeName, `()`)
|
||||||
|
}
|
||||||
|
p.P(`n+=`, strconv.Itoa(key), `+l+sov`, p.localName, `(uint64(l))`)
|
||||||
|
}
|
||||||
|
case descriptor.FieldDescriptorProto_TYPE_BYTES:
|
||||||
|
if !gogoproto.IsCustomType(field) {
|
||||||
|
if repeated {
|
||||||
|
p.P(`for _, b := range m.`, fieldname, ` { `)
|
||||||
|
p.In()
|
||||||
|
p.P(`l = len(b)`)
|
||||||
|
p.P(`n+=`, strconv.Itoa(key), `+l+sov`, p.localName, `(uint64(l))`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
} else if proto3 {
|
||||||
|
p.P(`l=len(m.`, fieldname, `)`)
|
||||||
|
p.P(`if l > 0 {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`n+=`, strconv.Itoa(key), `+l+sov`, p.localName, `(uint64(l))`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
} else {
|
||||||
|
p.P(`l=len(m.`, fieldname, `)`)
|
||||||
|
p.P(`n+=`, strconv.Itoa(key), `+l+sov`, p.localName, `(uint64(l))`)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if repeated {
|
||||||
|
p.P(`for _, e := range m.`, fieldname, ` { `)
|
||||||
|
p.In()
|
||||||
|
p.P(`l=e.`, sizeName, `()`)
|
||||||
|
p.P(`n+=`, strconv.Itoa(key), `+l+sov`, p.localName, `(uint64(l))`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
} else {
|
||||||
|
p.P(`l=m.`, fieldname, `.`, sizeName, `()`)
|
||||||
|
p.P(`n+=`, strconv.Itoa(key), `+l+sov`, p.localName, `(uint64(l))`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case descriptor.FieldDescriptorProto_TYPE_SINT32,
|
||||||
|
descriptor.FieldDescriptorProto_TYPE_SINT64:
|
||||||
|
if packed {
|
||||||
|
p.P(`l = 0`)
|
||||||
|
p.P(`for _, e := range m.`, fieldname, ` {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`l+=soz`, p.localName, `(uint64(e))`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`n+=`, strconv.Itoa(key), `+sov`, p.localName, `(uint64(l))+l`)
|
||||||
|
} else if repeated {
|
||||||
|
p.P(`for _, e := range m.`, fieldname, ` {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`n+=`, strconv.Itoa(key), `+soz`, p.localName, `(uint64(e))`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
} else if proto3 {
|
||||||
|
p.P(`if m.`, fieldname, ` != 0 {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`n+=`, strconv.Itoa(key), `+soz`, p.localName, `(uint64(m.`, fieldname, `))`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
} else if nullable {
|
||||||
|
p.P(`n+=`, strconv.Itoa(key), `+soz`, p.localName, `(uint64(*m.`, fieldname, `))`)
|
||||||
|
} else {
|
||||||
|
p.P(`n+=`, strconv.Itoa(key), `+soz`, p.localName, `(uint64(m.`, fieldname, `))`)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
panic("not implemented")
|
||||||
|
}
|
||||||
|
if repeated || doNilCheck {
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *size) Generate(file *generator.FileDescriptor) {
|
||||||
|
p.PluginImports = generator.NewPluginImports(p.Generator)
|
||||||
|
p.atleastOne = false
|
||||||
|
p.localName = generator.FileName(file)
|
||||||
|
p.typesPkg = p.NewImport("github.com/gogo/protobuf/types")
|
||||||
|
protoPkg := p.NewImport("github.com/gogo/protobuf/proto")
|
||||||
|
p.bitsPkg = p.NewImport("math/bits")
|
||||||
|
if !gogoproto.ImportsGoGoProto(file.FileDescriptorProto) {
|
||||||
|
protoPkg = p.NewImport("github.com/golang/protobuf/proto")
|
||||||
|
}
|
||||||
|
for _, message := range file.Messages() {
|
||||||
|
sizeName := ""
|
||||||
|
if gogoproto.IsSizer(file.FileDescriptorProto, message.DescriptorProto) && gogoproto.IsProtoSizer(file.FileDescriptorProto, message.DescriptorProto) {
|
||||||
|
fmt.Fprintf(os.Stderr, "ERROR: message %v cannot support both sizer and protosizer plugins\n", generator.CamelCase(*message.Name))
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
if gogoproto.IsSizer(file.FileDescriptorProto, message.DescriptorProto) {
|
||||||
|
sizeName = "Size"
|
||||||
|
} else if gogoproto.IsProtoSizer(file.FileDescriptorProto, message.DescriptorProto) {
|
||||||
|
sizeName = "ProtoSize"
|
||||||
|
} else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if message.DescriptorProto.GetOptions().GetMapEntry() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
p.atleastOne = true
|
||||||
|
ccTypeName := generator.CamelCaseSlice(message.TypeName())
|
||||||
|
p.P(`func (m *`, ccTypeName, `) `, sizeName, `() (n int) {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`if m == nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`return 0`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`var l int`)
|
||||||
|
p.P(`_ = l`)
|
||||||
|
oneofs := make(map[string]struct{})
|
||||||
|
for _, field := range message.Field {
|
||||||
|
oneof := field.OneofIndex != nil
|
||||||
|
if !oneof {
|
||||||
|
proto3 := gogoproto.IsProto3(file.FileDescriptorProto)
|
||||||
|
p.generateField(proto3, file, message, field, sizeName)
|
||||||
|
} else {
|
||||||
|
fieldname := p.GetFieldName(message, field)
|
||||||
|
if _, ok := oneofs[fieldname]; ok {
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
oneofs[fieldname] = struct{}{}
|
||||||
|
}
|
||||||
|
p.P(`if m.`, fieldname, ` != nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`n+=m.`, fieldname, `.`, sizeName, `()`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if message.DescriptorProto.HasExtension() {
|
||||||
|
if gogoproto.HasExtensionsMap(file.FileDescriptorProto, message.DescriptorProto) {
|
||||||
|
p.P(`n += `, protoPkg.Use(), `.SizeOfInternalExtension(m)`)
|
||||||
|
} else {
|
||||||
|
p.P(`if m.XXX_extensions != nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`n+=len(m.XXX_extensions)`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if gogoproto.HasUnrecognized(file.FileDescriptorProto, message.DescriptorProto) {
|
||||||
|
p.P(`if m.XXX_unrecognized != nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`n+=len(m.XXX_unrecognized)`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
}
|
||||||
|
p.P(`return n`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P()
|
||||||
|
|
||||||
|
//Generate Size methods for oneof fields
|
||||||
|
m := proto.Clone(message.DescriptorProto).(*descriptor.DescriptorProto)
|
||||||
|
for _, f := range m.Field {
|
||||||
|
oneof := f.OneofIndex != nil
|
||||||
|
if !oneof {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ccTypeName := p.OneOfTypeName(message, f)
|
||||||
|
p.P(`func (m *`, ccTypeName, `) `, sizeName, `() (n int) {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`if m == nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`return 0`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`var l int`)
|
||||||
|
p.P(`_ = l`)
|
||||||
|
vanity.TurnOffNullableForNativeTypes(f)
|
||||||
|
p.generateField(false, file, message, f, sizeName)
|
||||||
|
p.P(`return n`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !p.atleastOne {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
p.sizeVarint()
|
||||||
|
p.sizeZigZag()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
generator.RegisterPlugin(NewSize())
|
||||||
|
}
|
|
@ -0,0 +1,134 @@
|
||||||
|
// Protocol Buffers for Go with Gadgets
|
||||||
|
//
|
||||||
|
// Copyright (c) 2013, The GoGo Authors. All rights reserved.
|
||||||
|
// http://github.com/gogo/protobuf
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
package size
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogo/protobuf/gogoproto"
|
||||||
|
"github.com/gogo/protobuf/plugin/testgen"
|
||||||
|
"github.com/gogo/protobuf/protoc-gen-gogo/generator"
|
||||||
|
)
|
||||||
|
|
||||||
|
type test struct {
|
||||||
|
*generator.Generator
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTest(g *generator.Generator) testgen.TestPlugin {
|
||||||
|
return &test{g}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *test) Generate(imports generator.PluginImports, file *generator.FileDescriptor) bool {
|
||||||
|
used := false
|
||||||
|
randPkg := imports.NewImport("math/rand")
|
||||||
|
timePkg := imports.NewImport("time")
|
||||||
|
testingPkg := imports.NewImport("testing")
|
||||||
|
protoPkg := imports.NewImport("github.com/gogo/protobuf/proto")
|
||||||
|
if !gogoproto.ImportsGoGoProto(file.FileDescriptorProto) {
|
||||||
|
protoPkg = imports.NewImport("github.com/golang/protobuf/proto")
|
||||||
|
}
|
||||||
|
for _, message := range file.Messages() {
|
||||||
|
ccTypeName := generator.CamelCaseSlice(message.TypeName())
|
||||||
|
sizeName := ""
|
||||||
|
if gogoproto.IsSizer(file.FileDescriptorProto, message.DescriptorProto) {
|
||||||
|
sizeName = "Size"
|
||||||
|
} else if gogoproto.IsProtoSizer(file.FileDescriptorProto, message.DescriptorProto) {
|
||||||
|
sizeName = "ProtoSize"
|
||||||
|
} else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if message.DescriptorProto.GetOptions().GetMapEntry() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if gogoproto.HasTestGen(file.FileDescriptorProto, message.DescriptorProto) {
|
||||||
|
used = true
|
||||||
|
p.P(`func Test`, ccTypeName, sizeName, `(t *`, testingPkg.Use(), `.T) {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`seed := `, timePkg.Use(), `.Now().UnixNano()`)
|
||||||
|
p.P(`popr := `, randPkg.Use(), `.New(`, randPkg.Use(), `.NewSource(seed))`)
|
||||||
|
p.P(`p := NewPopulated`, ccTypeName, `(popr, true)`)
|
||||||
|
p.P(`size2 := `, protoPkg.Use(), `.Size(p)`)
|
||||||
|
p.P(`dAtA, err := `, protoPkg.Use(), `.Marshal(p)`)
|
||||||
|
p.P(`if err != nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`t.Fatalf("seed = %d, err = %v", seed, err)`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`size := p.`, sizeName, `()`)
|
||||||
|
p.P(`if len(dAtA) != size {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`t.Errorf("seed = %d, size %v != marshalled size %v", seed, size, len(dAtA))`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`if size2 != size {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`t.Errorf("seed = %d, size %v != before marshal proto.Size %v", seed, size, size2)`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`size3 := `, protoPkg.Use(), `.Size(p)`)
|
||||||
|
p.P(`if size3 != size {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`t.Errorf("seed = %d, size %v != after marshal proto.Size %v", seed, size, size3)`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P()
|
||||||
|
}
|
||||||
|
|
||||||
|
if gogoproto.HasBenchGen(file.FileDescriptorProto, message.DescriptorProto) {
|
||||||
|
used = true
|
||||||
|
p.P(`func Benchmark`, ccTypeName, sizeName, `(b *`, testingPkg.Use(), `.B) {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`popr := `, randPkg.Use(), `.New(`, randPkg.Use(), `.NewSource(616))`)
|
||||||
|
p.P(`total := 0`)
|
||||||
|
p.P(`pops := make([]*`, ccTypeName, `, 1000)`)
|
||||||
|
p.P(`for i := 0; i < 1000; i++ {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`pops[i] = NewPopulated`, ccTypeName, `(popr, false)`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`b.ResetTimer()`)
|
||||||
|
p.P(`for i := 0; i < b.N; i++ {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`total += pops[i%1000].`, sizeName, `()`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`b.SetBytes(int64(total / b.N))`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return used
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
testgen.RegisterTestPlugin(NewTest)
|
||||||
|
}
|
|
@ -0,0 +1,347 @@
|
||||||
|
// Protocol Buffers for Go with Gadgets
|
||||||
|
//
|
||||||
|
// Copyright (c) 2013, The GoGo Authors. All rights reserved.
|
||||||
|
// http://github.com/gogo/protobuf
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
/*
|
||||||
|
The stringer plugin generates a String method for each message.
|
||||||
|
|
||||||
|
It is enabled by the following extensions:
|
||||||
|
|
||||||
|
- stringer
|
||||||
|
- stringer_all
|
||||||
|
|
||||||
|
The stringer plugin also generates a test given it is enabled using one of the following extensions:
|
||||||
|
|
||||||
|
- testgen
|
||||||
|
- testgen_all
|
||||||
|
|
||||||
|
Let us look at:
|
||||||
|
|
||||||
|
github.com/gogo/protobuf/test/example/example.proto
|
||||||
|
|
||||||
|
Btw all the output can be seen at:
|
||||||
|
|
||||||
|
github.com/gogo/protobuf/test/example/*
|
||||||
|
|
||||||
|
The following message:
|
||||||
|
|
||||||
|
option (gogoproto.goproto_stringer_all) = false;
|
||||||
|
option (gogoproto.stringer_all) = true;
|
||||||
|
|
||||||
|
message A {
|
||||||
|
optional string Description = 1 [(gogoproto.nullable) = false];
|
||||||
|
optional int64 Number = 2 [(gogoproto.nullable) = false];
|
||||||
|
optional bytes Id = 3 [(gogoproto.customtype) = "github.com/gogo/protobuf/test/custom.Uuid", (gogoproto.nullable) = false];
|
||||||
|
}
|
||||||
|
|
||||||
|
given to the stringer stringer, will generate the following code:
|
||||||
|
|
||||||
|
func (this *A) String() string {
|
||||||
|
if this == nil {
|
||||||
|
return "nil"
|
||||||
|
}
|
||||||
|
s := strings.Join([]string{`&A{`,
|
||||||
|
`Description:` + fmt.Sprintf("%v", this.Description) + `,`,
|
||||||
|
`Number:` + fmt.Sprintf("%v", this.Number) + `,`,
|
||||||
|
`Id:` + fmt.Sprintf("%v", this.Id) + `,`,
|
||||||
|
`XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`,
|
||||||
|
`}`,
|
||||||
|
}, "")
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
and the following test code:
|
||||||
|
|
||||||
|
func TestAStringer(t *testing4.T) {
|
||||||
|
popr := math_rand4.New(math_rand4.NewSource(time4.Now().UnixNano()))
|
||||||
|
p := NewPopulatedA(popr, false)
|
||||||
|
s1 := p.String()
|
||||||
|
s2 := fmt1.Sprintf("%v", p)
|
||||||
|
if s1 != s2 {
|
||||||
|
t.Fatalf("String want %v got %v", s1, s2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Typically fmt.Printf("%v") will stop to print when it reaches a pointer and
|
||||||
|
not print their values, while the generated String method will always print all values, recursively.
|
||||||
|
|
||||||
|
*/
|
||||||
|
package stringer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogo/protobuf/gogoproto"
|
||||||
|
"github.com/gogo/protobuf/protoc-gen-gogo/generator"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type stringer struct {
|
||||||
|
*generator.Generator
|
||||||
|
generator.PluginImports
|
||||||
|
atleastOne bool
|
||||||
|
localName string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewStringer() *stringer {
|
||||||
|
return &stringer{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *stringer) Name() string {
|
||||||
|
return "stringer"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *stringer) Init(g *generator.Generator) {
|
||||||
|
p.Generator = g
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *stringer) Generate(file *generator.FileDescriptor) {
|
||||||
|
proto3 := gogoproto.IsProto3(file.FileDescriptorProto)
|
||||||
|
p.PluginImports = generator.NewPluginImports(p.Generator)
|
||||||
|
p.atleastOne = false
|
||||||
|
|
||||||
|
p.localName = generator.FileName(file)
|
||||||
|
|
||||||
|
fmtPkg := p.NewImport("fmt")
|
||||||
|
stringsPkg := p.NewImport("strings")
|
||||||
|
reflectPkg := p.NewImport("reflect")
|
||||||
|
sortKeysPkg := p.NewImport("github.com/gogo/protobuf/sortkeys")
|
||||||
|
protoPkg := p.NewImport("github.com/gogo/protobuf/proto")
|
||||||
|
for _, message := range file.Messages() {
|
||||||
|
if !gogoproto.IsStringer(file.FileDescriptorProto, message.DescriptorProto) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if gogoproto.EnabledGoStringer(file.FileDescriptorProto, message.DescriptorProto) {
|
||||||
|
panic("old string method needs to be disabled, please use gogoproto.goproto_stringer or gogoproto.goproto_stringer_all and set it to false")
|
||||||
|
}
|
||||||
|
if message.DescriptorProto.GetOptions().GetMapEntry() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
p.atleastOne = true
|
||||||
|
ccTypeName := generator.CamelCaseSlice(message.TypeName())
|
||||||
|
p.P(`func (this *`, ccTypeName, `) String() string {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`if this == nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`return "nil"`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
for _, field := range message.Field {
|
||||||
|
if p.IsMap(field) || !field.IsRepeated() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (field.IsMessage() && !gogoproto.IsCustomType(field)) || p.IsGroup(field) {
|
||||||
|
nullable := gogoproto.IsNullable(field)
|
||||||
|
desc := p.ObjectNamed(field.GetTypeName())
|
||||||
|
msgname := p.TypeName(desc)
|
||||||
|
msgnames := strings.Split(msgname, ".")
|
||||||
|
typeName := msgnames[len(msgnames)-1]
|
||||||
|
fieldMessageDesc := file.GetMessage(msgname)
|
||||||
|
gogoStringer := false
|
||||||
|
if fieldMessageDesc != nil {
|
||||||
|
gogoStringer = gogoproto.IsStringer(file.FileDescriptorProto, fieldMessageDesc)
|
||||||
|
}
|
||||||
|
fieldname := p.GetFieldName(message, field)
|
||||||
|
stringfunc := fmtPkg.Use() + `.Sprintf("%v", f)`
|
||||||
|
if gogoStringer {
|
||||||
|
stringfunc = `f.String()`
|
||||||
|
}
|
||||||
|
repeatedName := `repeatedStringFor` + fieldname
|
||||||
|
if nullable {
|
||||||
|
p.P(repeatedName, ` := "[]*`, typeName, `{"`)
|
||||||
|
} else {
|
||||||
|
p.P(repeatedName, ` := "[]`, typeName, `{"`)
|
||||||
|
}
|
||||||
|
|
||||||
|
p.P(`for _, f := range `, `this.`, fieldname, ` {`)
|
||||||
|
p.In()
|
||||||
|
if nullable {
|
||||||
|
p.P(repeatedName, " += ", stringsPkg.Use(), `.Replace(`, stringfunc, `, "`, typeName, `","`, msgname, `"`, ", 1)", ` + ","`)
|
||||||
|
} else if gogoStringer {
|
||||||
|
p.P(repeatedName, " += ", stringsPkg.Use(), `.Replace(`, stringsPkg.Use(), `.Replace(`, stringfunc, `, "`, typeName, `","`, msgname, `"`, ", 1),`&`,``,1)", ` + ","`)
|
||||||
|
} else {
|
||||||
|
p.P(repeatedName, " += ", stringfunc, ` + ","`)
|
||||||
|
}
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(repeatedName, ` += "}"`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, field := range message.Field {
|
||||||
|
if !p.IsMap(field) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fieldname := p.GetFieldName(message, field)
|
||||||
|
|
||||||
|
m := p.GoMapType(nil, field)
|
||||||
|
mapgoTyp, keyField, keyAliasField := m.GoType, m.KeyField, m.KeyAliasField
|
||||||
|
keysName := `keysFor` + fieldname
|
||||||
|
keygoTyp, _ := p.GoType(nil, keyField)
|
||||||
|
keygoTyp = strings.Replace(keygoTyp, "*", "", 1)
|
||||||
|
keygoAliasTyp, _ := p.GoType(nil, keyAliasField)
|
||||||
|
keygoAliasTyp = strings.Replace(keygoAliasTyp, "*", "", 1)
|
||||||
|
keyCapTyp := generator.CamelCase(keygoTyp)
|
||||||
|
p.P(keysName, ` := make([]`, keygoTyp, `, 0, len(this.`, fieldname, `))`)
|
||||||
|
p.P(`for k, _ := range this.`, fieldname, ` {`)
|
||||||
|
p.In()
|
||||||
|
if keygoAliasTyp == keygoTyp {
|
||||||
|
p.P(keysName, ` = append(`, keysName, `, k)`)
|
||||||
|
} else {
|
||||||
|
p.P(keysName, ` = append(`, keysName, `, `, keygoTyp, `(k))`)
|
||||||
|
}
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(sortKeysPkg.Use(), `.`, keyCapTyp, `s(`, keysName, `)`)
|
||||||
|
mapName := `mapStringFor` + fieldname
|
||||||
|
p.P(mapName, ` := "`, mapgoTyp, `{"`)
|
||||||
|
p.P(`for _, k := range `, keysName, ` {`)
|
||||||
|
p.In()
|
||||||
|
if keygoAliasTyp == keygoTyp {
|
||||||
|
p.P(mapName, ` += fmt.Sprintf("%v: %v,", k, this.`, fieldname, `[k])`)
|
||||||
|
} else {
|
||||||
|
p.P(mapName, ` += fmt.Sprintf("%v: %v,", k, this.`, fieldname, `[`, keygoAliasTyp, `(k)])`)
|
||||||
|
}
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(mapName, ` += "}"`)
|
||||||
|
}
|
||||||
|
p.P("s := ", stringsPkg.Use(), ".Join([]string{`&", ccTypeName, "{`,")
|
||||||
|
oneofs := make(map[string]struct{})
|
||||||
|
for _, field := range message.Field {
|
||||||
|
nullable := gogoproto.IsNullable(field)
|
||||||
|
repeated := field.IsRepeated()
|
||||||
|
fieldname := p.GetFieldName(message, field)
|
||||||
|
oneof := field.OneofIndex != nil
|
||||||
|
if oneof {
|
||||||
|
if _, ok := oneofs[fieldname]; ok {
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
oneofs[fieldname] = struct{}{}
|
||||||
|
}
|
||||||
|
p.P("`", fieldname, ":`", ` + `, fmtPkg.Use(), `.Sprintf("%v", this.`, fieldname, ") + `,", "`,")
|
||||||
|
} else if p.IsMap(field) {
|
||||||
|
mapName := `mapStringFor` + fieldname
|
||||||
|
p.P("`", fieldname, ":`", ` + `, mapName, " + `,", "`,")
|
||||||
|
} else if (field.IsMessage() && !gogoproto.IsCustomType(field)) || p.IsGroup(field) {
|
||||||
|
desc := p.ObjectNamed(field.GetTypeName())
|
||||||
|
msgname := p.TypeName(desc)
|
||||||
|
msgnames := strings.Split(msgname, ".")
|
||||||
|
typeName := msgnames[len(msgnames)-1]
|
||||||
|
fieldMessageDesc := file.GetMessage(msgname)
|
||||||
|
gogoStringer := false
|
||||||
|
if fieldMessageDesc != nil {
|
||||||
|
gogoStringer = gogoproto.IsStringer(file.FileDescriptorProto, fieldMessageDesc)
|
||||||
|
}
|
||||||
|
stringfunc := fmtPkg.Use() + `.Sprintf("%v", this.` + fieldname + `)`
|
||||||
|
if gogoStringer {
|
||||||
|
stringfunc = `this.` + fieldname + `.String()`
|
||||||
|
}
|
||||||
|
if nullable && !repeated {
|
||||||
|
p.P("`", fieldname, ":`", ` + `, stringsPkg.Use(), `.Replace(`, stringfunc, `, "`, typeName, `","`, msgname, `"`, ", 1) + `,", "`,")
|
||||||
|
} else if repeated {
|
||||||
|
repeatedName := `repeatedStringFor` + fieldname
|
||||||
|
p.P("`", fieldname, ":`", ` + `, repeatedName, " + `,", "`,")
|
||||||
|
} else {
|
||||||
|
p.P("`", fieldname, ":`", ` + `, stringsPkg.Use(), `.Replace(`, stringsPkg.Use(), `.Replace(`, stringfunc, `, "`, typeName, `","`, msgname, `"`, ", 1),`&`,``,1) + `,", "`,")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if nullable && !repeated && !proto3 {
|
||||||
|
p.P("`", fieldname, ":`", ` + valueToString`, p.localName, `(this.`, fieldname, ") + `,", "`,")
|
||||||
|
} else {
|
||||||
|
p.P("`", fieldname, ":`", ` + `, fmtPkg.Use(), `.Sprintf("%v", this.`, fieldname, ") + `,", "`,")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if message.DescriptorProto.HasExtension() {
|
||||||
|
if gogoproto.HasExtensionsMap(file.FileDescriptorProto, message.DescriptorProto) {
|
||||||
|
p.P("`XXX_InternalExtensions:` + ", protoPkg.Use(), ".StringFromInternalExtension(this) + `,`,")
|
||||||
|
} else {
|
||||||
|
p.P("`XXX_extensions:` + ", protoPkg.Use(), ".StringFromExtensionsBytes(this.XXX_extensions) + `,`,")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if gogoproto.HasUnrecognized(file.FileDescriptorProto, message.DescriptorProto) {
|
||||||
|
p.P("`XXX_unrecognized:` + ", fmtPkg.Use(), `.Sprintf("%v", this.XXX_unrecognized) + `, "`,`,")
|
||||||
|
}
|
||||||
|
p.P("`}`,")
|
||||||
|
p.P(`}`, `,""`, ")")
|
||||||
|
p.P(`return s`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
|
||||||
|
//Generate String methods for oneof fields
|
||||||
|
for _, field := range message.Field {
|
||||||
|
oneof := field.OneofIndex != nil
|
||||||
|
if !oneof {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ccTypeName := p.OneOfTypeName(message, field)
|
||||||
|
p.P(`func (this *`, ccTypeName, `) String() string {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`if this == nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`return "nil"`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P("s := ", stringsPkg.Use(), ".Join([]string{`&", ccTypeName, "{`,")
|
||||||
|
fieldname := p.GetOneOfFieldName(message, field)
|
||||||
|
if field.IsMessage() || p.IsGroup(field) {
|
||||||
|
desc := p.ObjectNamed(field.GetTypeName())
|
||||||
|
msgname := p.TypeName(desc)
|
||||||
|
msgnames := strings.Split(msgname, ".")
|
||||||
|
typeName := msgnames[len(msgnames)-1]
|
||||||
|
p.P("`", fieldname, ":`", ` + `, stringsPkg.Use(), `.Replace(`, fmtPkg.Use(), `.Sprintf("%v", this.`, fieldname, `), "`, typeName, `","`, msgname, `"`, ", 1) + `,", "`,")
|
||||||
|
} else {
|
||||||
|
p.P("`", fieldname, ":`", ` + `, fmtPkg.Use(), `.Sprintf("%v", this.`, fieldname, ") + `,", "`,")
|
||||||
|
}
|
||||||
|
p.P("`}`,")
|
||||||
|
p.P(`}`, `,""`, ")")
|
||||||
|
p.P(`return s`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !p.atleastOne {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
p.P(`func valueToString`, p.localName, `(v interface{}) string {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`rv := `, reflectPkg.Use(), `.ValueOf(v)`)
|
||||||
|
p.P(`if rv.IsNil() {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`return "nil"`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`pv := `, reflectPkg.Use(), `.Indirect(rv).Interface()`)
|
||||||
|
p.P(`return `, fmtPkg.Use(), `.Sprintf("*%v", pv)`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
generator.RegisterPlugin(NewStringer())
|
||||||
|
}
|
|
@ -0,0 +1,83 @@
|
||||||
|
// Protocol Buffers for Go with Gadgets
|
||||||
|
//
|
||||||
|
// Copyright (c) 2013, The GoGo Authors. All rights reserved.
|
||||||
|
// http://github.com/gogo/protobuf
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
package stringer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogo/protobuf/gogoproto"
|
||||||
|
"github.com/gogo/protobuf/plugin/testgen"
|
||||||
|
"github.com/gogo/protobuf/protoc-gen-gogo/generator"
|
||||||
|
)
|
||||||
|
|
||||||
|
type test struct {
|
||||||
|
*generator.Generator
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTest(g *generator.Generator) testgen.TestPlugin {
|
||||||
|
return &test{g}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *test) Generate(imports generator.PluginImports, file *generator.FileDescriptor) bool {
|
||||||
|
used := false
|
||||||
|
randPkg := imports.NewImport("math/rand")
|
||||||
|
timePkg := imports.NewImport("time")
|
||||||
|
testingPkg := imports.NewImport("testing")
|
||||||
|
fmtPkg := imports.NewImport("fmt")
|
||||||
|
for _, message := range file.Messages() {
|
||||||
|
ccTypeName := generator.CamelCaseSlice(message.TypeName())
|
||||||
|
if !gogoproto.IsStringer(file.FileDescriptorProto, message.DescriptorProto) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if message.DescriptorProto.GetOptions().GetMapEntry() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if gogoproto.HasTestGen(file.FileDescriptorProto, message.DescriptorProto) {
|
||||||
|
used = true
|
||||||
|
p.P(`func Test`, ccTypeName, `Stringer(t *`, testingPkg.Use(), `.T) {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`popr := `, randPkg.Use(), `.New(`, randPkg.Use(), `.NewSource(`, timePkg.Use(), `.Now().UnixNano()))`)
|
||||||
|
p.P(`p := NewPopulated`, ccTypeName, `(popr, false)`)
|
||||||
|
p.P(`s1 := p.String()`)
|
||||||
|
p.P(`s2 := `, fmtPkg.Use(), `.Sprintf("%v", p)`)
|
||||||
|
p.P(`if s1 != s2 {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`t.Fatalf("String want %v got %v", s1, s2)`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return used
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
testgen.RegisterTestPlugin(NewTest)
|
||||||
|
}
|
|
@ -0,0 +1,608 @@
|
||||||
|
// Protocol Buffers for Go with Gadgets
|
||||||
|
//
|
||||||
|
// Copyright (c) 2013, The GoGo Authors. All rights reserved.
|
||||||
|
// http://github.com/gogo/protobuf
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
/*
|
||||||
|
The testgen plugin generates Test and Benchmark functions for each message.
|
||||||
|
|
||||||
|
Tests are enabled using the following extensions:
|
||||||
|
|
||||||
|
- testgen
|
||||||
|
- testgen_all
|
||||||
|
|
||||||
|
Benchmarks are enabled using the following extensions:
|
||||||
|
|
||||||
|
- benchgen
|
||||||
|
- benchgen_all
|
||||||
|
|
||||||
|
Let us look at:
|
||||||
|
|
||||||
|
github.com/gogo/protobuf/test/example/example.proto
|
||||||
|
|
||||||
|
Btw all the output can be seen at:
|
||||||
|
|
||||||
|
github.com/gogo/protobuf/test/example/*
|
||||||
|
|
||||||
|
The following message:
|
||||||
|
|
||||||
|
option (gogoproto.testgen_all) = true;
|
||||||
|
option (gogoproto.benchgen_all) = true;
|
||||||
|
|
||||||
|
message A {
|
||||||
|
optional string Description = 1 [(gogoproto.nullable) = false];
|
||||||
|
optional int64 Number = 2 [(gogoproto.nullable) = false];
|
||||||
|
optional bytes Id = 3 [(gogoproto.customtype) = "github.com/gogo/protobuf/test/custom.Uuid", (gogoproto.nullable) = false];
|
||||||
|
}
|
||||||
|
|
||||||
|
given to the testgen plugin, will generate the following test code:
|
||||||
|
|
||||||
|
func TestAProto(t *testing.T) {
|
||||||
|
popr := math_rand.New(math_rand.NewSource(time.Now().UnixNano()))
|
||||||
|
p := NewPopulatedA(popr, false)
|
||||||
|
dAtA, err := github_com_gogo_protobuf_proto.Marshal(p)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
msg := &A{}
|
||||||
|
if err := github_com_gogo_protobuf_proto.Unmarshal(dAtA, msg); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
for i := range dAtA {
|
||||||
|
dAtA[i] = byte(popr.Intn(256))
|
||||||
|
}
|
||||||
|
if err := p.VerboseEqual(msg); err != nil {
|
||||||
|
t.Fatalf("%#v !VerboseProto %#v, since %v", msg, p, err)
|
||||||
|
}
|
||||||
|
if !p.Equal(msg) {
|
||||||
|
t.Fatalf("%#v !Proto %#v", msg, p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkAProtoMarshal(b *testing.B) {
|
||||||
|
popr := math_rand.New(math_rand.NewSource(616))
|
||||||
|
total := 0
|
||||||
|
pops := make([]*A, 10000)
|
||||||
|
for i := 0; i < 10000; i++ {
|
||||||
|
pops[i] = NewPopulatedA(popr, false)
|
||||||
|
}
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
dAtA, err := github_com_gogo_protobuf_proto.Marshal(pops[i%10000])
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
total += len(dAtA)
|
||||||
|
}
|
||||||
|
b.SetBytes(int64(total / b.N))
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkAProtoUnmarshal(b *testing.B) {
|
||||||
|
popr := math_rand.New(math_rand.NewSource(616))
|
||||||
|
total := 0
|
||||||
|
datas := make([][]byte, 10000)
|
||||||
|
for i := 0; i < 10000; i++ {
|
||||||
|
dAtA, err := github_com_gogo_protobuf_proto.Marshal(NewPopulatedA(popr, false))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
datas[i] = dAtA
|
||||||
|
}
|
||||||
|
msg := &A{}
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
total += len(datas[i%10000])
|
||||||
|
if err := github_com_gogo_protobuf_proto.Unmarshal(datas[i%10000], msg); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b.SetBytes(int64(total / b.N))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func TestAJSON(t *testing1.T) {
|
||||||
|
popr := math_rand1.New(math_rand1.NewSource(time1.Now().UnixNano()))
|
||||||
|
p := NewPopulatedA(popr, true)
|
||||||
|
jsondata, err := encoding_json.Marshal(p)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
msg := &A{}
|
||||||
|
err = encoding_json.Unmarshal(jsondata, msg)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if err := p.VerboseEqual(msg); err != nil {
|
||||||
|
t.Fatalf("%#v !VerboseProto %#v, since %v", msg, p, err)
|
||||||
|
}
|
||||||
|
if !p.Equal(msg) {
|
||||||
|
t.Fatalf("%#v !Json Equal %#v", msg, p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAProtoText(t *testing2.T) {
|
||||||
|
popr := math_rand2.New(math_rand2.NewSource(time2.Now().UnixNano()))
|
||||||
|
p := NewPopulatedA(popr, true)
|
||||||
|
dAtA := github_com_gogo_protobuf_proto1.MarshalTextString(p)
|
||||||
|
msg := &A{}
|
||||||
|
if err := github_com_gogo_protobuf_proto1.UnmarshalText(dAtA, msg); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if err := p.VerboseEqual(msg); err != nil {
|
||||||
|
t.Fatalf("%#v !VerboseProto %#v, since %v", msg, p, err)
|
||||||
|
}
|
||||||
|
if !p.Equal(msg) {
|
||||||
|
t.Fatalf("%#v !Proto %#v", msg, p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAProtoCompactText(t *testing2.T) {
|
||||||
|
popr := math_rand2.New(math_rand2.NewSource(time2.Now().UnixNano()))
|
||||||
|
p := NewPopulatedA(popr, true)
|
||||||
|
dAtA := github_com_gogo_protobuf_proto1.CompactTextString(p)
|
||||||
|
msg := &A{}
|
||||||
|
if err := github_com_gogo_protobuf_proto1.UnmarshalText(dAtA, msg); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if err := p.VerboseEqual(msg); err != nil {
|
||||||
|
t.Fatalf("%#v !VerboseProto %#v, since %v", msg, p, err)
|
||||||
|
}
|
||||||
|
if !p.Equal(msg) {
|
||||||
|
t.Fatalf("%#v !Proto %#v", msg, p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Other registered tests are also generated.
|
||||||
|
Tests are registered to this test plugin by calling the following function.
|
||||||
|
|
||||||
|
func RegisterTestPlugin(newFunc NewTestPlugin)
|
||||||
|
|
||||||
|
where NewTestPlugin is:
|
||||||
|
|
||||||
|
type NewTestPlugin func(g *generator.Generator) TestPlugin
|
||||||
|
|
||||||
|
and TestPlugin is an interface:
|
||||||
|
|
||||||
|
type TestPlugin interface {
|
||||||
|
Generate(imports generator.PluginImports, file *generator.FileDescriptor) (used bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
Plugins that use this interface include:
|
||||||
|
|
||||||
|
- populate
|
||||||
|
- gostring
|
||||||
|
- equal
|
||||||
|
- union
|
||||||
|
- and more
|
||||||
|
|
||||||
|
Please look at these plugins as examples of how to create your own.
|
||||||
|
A good idea is to let each plugin generate its own tests.
|
||||||
|
|
||||||
|
*/
|
||||||
|
package testgen
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogo/protobuf/gogoproto"
|
||||||
|
"github.com/gogo/protobuf/protoc-gen-gogo/generator"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TestPlugin interface {
|
||||||
|
Generate(imports generator.PluginImports, file *generator.FileDescriptor) (used bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
type NewTestPlugin func(g *generator.Generator) TestPlugin
|
||||||
|
|
||||||
|
var testplugins = make([]NewTestPlugin, 0)
|
||||||
|
|
||||||
|
func RegisterTestPlugin(newFunc NewTestPlugin) {
|
||||||
|
testplugins = append(testplugins, newFunc)
|
||||||
|
}
|
||||||
|
|
||||||
|
type plugin struct {
|
||||||
|
*generator.Generator
|
||||||
|
generator.PluginImports
|
||||||
|
tests []TestPlugin
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPlugin() *plugin {
|
||||||
|
return &plugin{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *plugin) Name() string {
|
||||||
|
return "testgen"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *plugin) Init(g *generator.Generator) {
|
||||||
|
p.Generator = g
|
||||||
|
p.tests = make([]TestPlugin, 0, len(testplugins))
|
||||||
|
for i := range testplugins {
|
||||||
|
p.tests = append(p.tests, testplugins[i](g))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *plugin) Generate(file *generator.FileDescriptor) {
|
||||||
|
p.PluginImports = generator.NewPluginImports(p.Generator)
|
||||||
|
atLeastOne := false
|
||||||
|
for i := range p.tests {
|
||||||
|
used := p.tests[i].Generate(p.PluginImports, file)
|
||||||
|
if used {
|
||||||
|
atLeastOne = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if atLeastOne {
|
||||||
|
p.P(`//These tests are generated by github.com/gogo/protobuf/plugin/testgen`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type testProto struct {
|
||||||
|
*generator.Generator
|
||||||
|
}
|
||||||
|
|
||||||
|
func newProto(g *generator.Generator) TestPlugin {
|
||||||
|
return &testProto{g}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *testProto) Generate(imports generator.PluginImports, file *generator.FileDescriptor) bool {
|
||||||
|
used := false
|
||||||
|
testingPkg := imports.NewImport("testing")
|
||||||
|
randPkg := imports.NewImport("math/rand")
|
||||||
|
timePkg := imports.NewImport("time")
|
||||||
|
protoPkg := imports.NewImport("github.com/gogo/protobuf/proto")
|
||||||
|
if !gogoproto.ImportsGoGoProto(file.FileDescriptorProto) {
|
||||||
|
protoPkg = imports.NewImport("github.com/golang/protobuf/proto")
|
||||||
|
}
|
||||||
|
for _, message := range file.Messages() {
|
||||||
|
ccTypeName := generator.CamelCaseSlice(message.TypeName())
|
||||||
|
if message.DescriptorProto.GetOptions().GetMapEntry() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if gogoproto.HasTestGen(file.FileDescriptorProto, message.DescriptorProto) {
|
||||||
|
used = true
|
||||||
|
|
||||||
|
p.P(`func Test`, ccTypeName, `Proto(t *`, testingPkg.Use(), `.T) {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`seed := `, timePkg.Use(), `.Now().UnixNano()`)
|
||||||
|
p.P(`popr := `, randPkg.Use(), `.New(`, randPkg.Use(), `.NewSource(seed))`)
|
||||||
|
p.P(`p := NewPopulated`, ccTypeName, `(popr, false)`)
|
||||||
|
p.P(`dAtA, err := `, protoPkg.Use(), `.Marshal(p)`)
|
||||||
|
p.P(`if err != nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`t.Fatalf("seed = %d, err = %v", seed, err)`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`msg := &`, ccTypeName, `{}`)
|
||||||
|
p.P(`if err := `, protoPkg.Use(), `.Unmarshal(dAtA, msg); err != nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`t.Fatalf("seed = %d, err = %v", seed, err)`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`littlefuzz := make([]byte, len(dAtA))`)
|
||||||
|
p.P(`copy(littlefuzz, dAtA)`)
|
||||||
|
p.P(`for i := range dAtA {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`dAtA[i] = byte(popr.Intn(256))`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
if gogoproto.HasVerboseEqual(file.FileDescriptorProto, message.DescriptorProto) {
|
||||||
|
p.P(`if err := p.VerboseEqual(msg); err != nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`t.Fatalf("seed = %d, %#v !VerboseProto %#v, since %v", seed, msg, p, err)`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
}
|
||||||
|
p.P(`if !p.Equal(msg) {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`t.Fatalf("seed = %d, %#v !Proto %#v", seed, msg, p)`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`if len(littlefuzz) > 0 {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`fuzzamount := 100`)
|
||||||
|
p.P(`for i := 0; i < fuzzamount; i++ {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`littlefuzz[popr.Intn(len(littlefuzz))] = byte(popr.Intn(256))`)
|
||||||
|
p.P(`littlefuzz = append(littlefuzz, byte(popr.Intn(256)))`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`// shouldn't panic`)
|
||||||
|
p.P(`_ = `, protoPkg.Use(), `.Unmarshal(littlefuzz, msg)`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P()
|
||||||
|
}
|
||||||
|
|
||||||
|
if gogoproto.HasTestGen(file.FileDescriptorProto, message.DescriptorProto) {
|
||||||
|
if gogoproto.IsMarshaler(file.FileDescriptorProto, message.DescriptorProto) || gogoproto.IsUnsafeMarshaler(file.FileDescriptorProto, message.DescriptorProto) {
|
||||||
|
p.P(`func Test`, ccTypeName, `MarshalTo(t *`, testingPkg.Use(), `.T) {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`seed := `, timePkg.Use(), `.Now().UnixNano()`)
|
||||||
|
p.P(`popr := `, randPkg.Use(), `.New(`, randPkg.Use(), `.NewSource(seed))`)
|
||||||
|
p.P(`p := NewPopulated`, ccTypeName, `(popr, false)`)
|
||||||
|
if gogoproto.IsProtoSizer(file.FileDescriptorProto, message.DescriptorProto) {
|
||||||
|
p.P(`size := p.ProtoSize()`)
|
||||||
|
} else {
|
||||||
|
p.P(`size := p.Size()`)
|
||||||
|
}
|
||||||
|
p.P(`dAtA := make([]byte, size)`)
|
||||||
|
p.P(`for i := range dAtA {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`dAtA[i] = byte(popr.Intn(256))`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`_, err := p.MarshalTo(dAtA)`)
|
||||||
|
p.P(`if err != nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`t.Fatalf("seed = %d, err = %v", seed, err)`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`msg := &`, ccTypeName, `{}`)
|
||||||
|
p.P(`if err := `, protoPkg.Use(), `.Unmarshal(dAtA, msg); err != nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`t.Fatalf("seed = %d, err = %v", seed, err)`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`for i := range dAtA {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`dAtA[i] = byte(popr.Intn(256))`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
if gogoproto.HasVerboseEqual(file.FileDescriptorProto, message.DescriptorProto) {
|
||||||
|
p.P(`if err := p.VerboseEqual(msg); err != nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`t.Fatalf("seed = %d, %#v !VerboseProto %#v, since %v", seed, msg, p, err)`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
}
|
||||||
|
p.P(`if !p.Equal(msg) {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`t.Fatalf("seed = %d, %#v !Proto %#v", seed, msg, p)`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if gogoproto.HasBenchGen(file.FileDescriptorProto, message.DescriptorProto) {
|
||||||
|
used = true
|
||||||
|
p.P(`func Benchmark`, ccTypeName, `ProtoMarshal(b *`, testingPkg.Use(), `.B) {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`popr := `, randPkg.Use(), `.New(`, randPkg.Use(), `.NewSource(616))`)
|
||||||
|
p.P(`total := 0`)
|
||||||
|
p.P(`pops := make([]*`, ccTypeName, `, 10000)`)
|
||||||
|
p.P(`for i := 0; i < 10000; i++ {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`pops[i] = NewPopulated`, ccTypeName, `(popr, false)`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`b.ResetTimer()`)
|
||||||
|
p.P(`for i := 0; i < b.N; i++ {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`dAtA, err := `, protoPkg.Use(), `.Marshal(pops[i%10000])`)
|
||||||
|
p.P(`if err != nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`panic(err)`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`total += len(dAtA)`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`b.SetBytes(int64(total / b.N))`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P()
|
||||||
|
|
||||||
|
p.P(`func Benchmark`, ccTypeName, `ProtoUnmarshal(b *`, testingPkg.Use(), `.B) {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`popr := `, randPkg.Use(), `.New(`, randPkg.Use(), `.NewSource(616))`)
|
||||||
|
p.P(`total := 0`)
|
||||||
|
p.P(`datas := make([][]byte, 10000)`)
|
||||||
|
p.P(`for i := 0; i < 10000; i++ {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`dAtA, err := `, protoPkg.Use(), `.Marshal(NewPopulated`, ccTypeName, `(popr, false))`)
|
||||||
|
p.P(`if err != nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`panic(err)`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`datas[i] = dAtA`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`msg := &`, ccTypeName, `{}`)
|
||||||
|
p.P(`b.ResetTimer()`)
|
||||||
|
p.P(`for i := 0; i < b.N; i++ {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`total += len(datas[i%10000])`)
|
||||||
|
p.P(`if err := `, protoPkg.Use(), `.Unmarshal(datas[i%10000], msg); err != nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`panic(err)`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`b.SetBytes(int64(total / b.N))`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return used
|
||||||
|
}
|
||||||
|
|
||||||
|
type testJson struct {
|
||||||
|
*generator.Generator
|
||||||
|
}
|
||||||
|
|
||||||
|
func newJson(g *generator.Generator) TestPlugin {
|
||||||
|
return &testJson{g}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *testJson) Generate(imports generator.PluginImports, file *generator.FileDescriptor) bool {
|
||||||
|
used := false
|
||||||
|
testingPkg := imports.NewImport("testing")
|
||||||
|
randPkg := imports.NewImport("math/rand")
|
||||||
|
timePkg := imports.NewImport("time")
|
||||||
|
jsonPkg := imports.NewImport("github.com/gogo/protobuf/jsonpb")
|
||||||
|
for _, message := range file.Messages() {
|
||||||
|
ccTypeName := generator.CamelCaseSlice(message.TypeName())
|
||||||
|
if message.DescriptorProto.GetOptions().GetMapEntry() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if gogoproto.HasTestGen(file.FileDescriptorProto, message.DescriptorProto) {
|
||||||
|
used = true
|
||||||
|
p.P(`func Test`, ccTypeName, `JSON(t *`, testingPkg.Use(), `.T) {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`seed := `, timePkg.Use(), `.Now().UnixNano()`)
|
||||||
|
p.P(`popr := `, randPkg.Use(), `.New(`, randPkg.Use(), `.NewSource(seed))`)
|
||||||
|
p.P(`p := NewPopulated`, ccTypeName, `(popr, true)`)
|
||||||
|
p.P(`marshaler := `, jsonPkg.Use(), `.Marshaler{}`)
|
||||||
|
p.P(`jsondata, err := marshaler.MarshalToString(p)`)
|
||||||
|
p.P(`if err != nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`t.Fatalf("seed = %d, err = %v", seed, err)`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`msg := &`, ccTypeName, `{}`)
|
||||||
|
p.P(`err = `, jsonPkg.Use(), `.UnmarshalString(jsondata, msg)`)
|
||||||
|
p.P(`if err != nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`t.Fatalf("seed = %d, err = %v", seed, err)`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
if gogoproto.HasVerboseEqual(file.FileDescriptorProto, message.DescriptorProto) {
|
||||||
|
p.P(`if err := p.VerboseEqual(msg); err != nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`t.Fatalf("seed = %d, %#v !VerboseProto %#v, since %v", seed, msg, p, err)`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
}
|
||||||
|
p.P(`if !p.Equal(msg) {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`t.Fatalf("seed = %d, %#v !Json Equal %#v", seed, msg, p)`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return used
|
||||||
|
}
|
||||||
|
|
||||||
|
type testText struct {
|
||||||
|
*generator.Generator
|
||||||
|
}
|
||||||
|
|
||||||
|
func newText(g *generator.Generator) TestPlugin {
|
||||||
|
return &testText{g}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *testText) Generate(imports generator.PluginImports, file *generator.FileDescriptor) bool {
|
||||||
|
used := false
|
||||||
|
testingPkg := imports.NewImport("testing")
|
||||||
|
randPkg := imports.NewImport("math/rand")
|
||||||
|
timePkg := imports.NewImport("time")
|
||||||
|
protoPkg := imports.NewImport("github.com/gogo/protobuf/proto")
|
||||||
|
if !gogoproto.ImportsGoGoProto(file.FileDescriptorProto) {
|
||||||
|
protoPkg = imports.NewImport("github.com/golang/protobuf/proto")
|
||||||
|
}
|
||||||
|
//fmtPkg := imports.NewImport("fmt")
|
||||||
|
for _, message := range file.Messages() {
|
||||||
|
ccTypeName := generator.CamelCaseSlice(message.TypeName())
|
||||||
|
if message.DescriptorProto.GetOptions().GetMapEntry() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if gogoproto.HasTestGen(file.FileDescriptorProto, message.DescriptorProto) {
|
||||||
|
used = true
|
||||||
|
|
||||||
|
p.P(`func Test`, ccTypeName, `ProtoText(t *`, testingPkg.Use(), `.T) {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`seed := `, timePkg.Use(), `.Now().UnixNano()`)
|
||||||
|
p.P(`popr := `, randPkg.Use(), `.New(`, randPkg.Use(), `.NewSource(seed))`)
|
||||||
|
p.P(`p := NewPopulated`, ccTypeName, `(popr, true)`)
|
||||||
|
p.P(`dAtA := `, protoPkg.Use(), `.MarshalTextString(p)`)
|
||||||
|
p.P(`msg := &`, ccTypeName, `{}`)
|
||||||
|
p.P(`if err := `, protoPkg.Use(), `.UnmarshalText(dAtA, msg); err != nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`t.Fatalf("seed = %d, err = %v", seed, err)`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
if gogoproto.HasVerboseEqual(file.FileDescriptorProto, message.DescriptorProto) {
|
||||||
|
p.P(`if err := p.VerboseEqual(msg); err != nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`t.Fatalf("seed = %d, %#v !VerboseProto %#v, since %v", seed, msg, p, err)`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
}
|
||||||
|
p.P(`if !p.Equal(msg) {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`t.Fatalf("seed = %d, %#v !Proto %#v", seed, msg, p)`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P()
|
||||||
|
|
||||||
|
p.P(`func Test`, ccTypeName, `ProtoCompactText(t *`, testingPkg.Use(), `.T) {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`seed := `, timePkg.Use(), `.Now().UnixNano()`)
|
||||||
|
p.P(`popr := `, randPkg.Use(), `.New(`, randPkg.Use(), `.NewSource(seed))`)
|
||||||
|
p.P(`p := NewPopulated`, ccTypeName, `(popr, true)`)
|
||||||
|
p.P(`dAtA := `, protoPkg.Use(), `.CompactTextString(p)`)
|
||||||
|
p.P(`msg := &`, ccTypeName, `{}`)
|
||||||
|
p.P(`if err := `, protoPkg.Use(), `.UnmarshalText(dAtA, msg); err != nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`t.Fatalf("seed = %d, err = %v", seed, err)`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
if gogoproto.HasVerboseEqual(file.FileDescriptorProto, message.DescriptorProto) {
|
||||||
|
p.P(`if err := p.VerboseEqual(msg); err != nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`t.Fatalf("seed = %d, %#v !VerboseProto %#v, since %v", seed, msg, p, err)`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
}
|
||||||
|
p.P(`if !p.Equal(msg) {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`t.Fatalf("seed = %d, %#v !Proto %#v", seed, msg, p)`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P()
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return used
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
RegisterTestPlugin(newProto)
|
||||||
|
RegisterTestPlugin(newJson)
|
||||||
|
RegisterTestPlugin(newText)
|
||||||
|
}
|
|
@ -0,0 +1,209 @@
|
||||||
|
// Protocol Buffers for Go with Gadgets
|
||||||
|
//
|
||||||
|
// Copyright (c) 2013, The GoGo Authors. All rights reserved.
|
||||||
|
// http://github.com/gogo/protobuf
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
/*
|
||||||
|
The onlyone plugin generates code for the onlyone extension.
|
||||||
|
All fields must be nullable and only one of the fields may be set, like a union.
|
||||||
|
Two methods are generated
|
||||||
|
|
||||||
|
GetValue() interface{}
|
||||||
|
|
||||||
|
and
|
||||||
|
|
||||||
|
SetValue(v interface{}) (set bool)
|
||||||
|
|
||||||
|
These provide easier interaction with a onlyone.
|
||||||
|
|
||||||
|
The onlyone extension is not called union as this causes compile errors in the C++ generated code.
|
||||||
|
There can only be one ;)
|
||||||
|
|
||||||
|
It is enabled by the following extensions:
|
||||||
|
|
||||||
|
- onlyone
|
||||||
|
- onlyone_all
|
||||||
|
|
||||||
|
The onlyone plugin also generates a test given it is enabled using one of the following extensions:
|
||||||
|
|
||||||
|
- testgen
|
||||||
|
- testgen_all
|
||||||
|
|
||||||
|
Lets look at:
|
||||||
|
|
||||||
|
github.com/gogo/protobuf/test/example/example.proto
|
||||||
|
|
||||||
|
Btw all the output can be seen at:
|
||||||
|
|
||||||
|
github.com/gogo/protobuf/test/example/*
|
||||||
|
|
||||||
|
The following message:
|
||||||
|
|
||||||
|
message U {
|
||||||
|
option (gogoproto.onlyone) = true;
|
||||||
|
optional A A = 1;
|
||||||
|
optional B B = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
given to the onlyone plugin, will generate code which looks a lot like this:
|
||||||
|
|
||||||
|
func (this *U) GetValue() interface{} {
|
||||||
|
if this.A != nil {
|
||||||
|
return this.A
|
||||||
|
}
|
||||||
|
if this.B != nil {
|
||||||
|
return this.B
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *U) SetValue(value interface{}) bool {
|
||||||
|
switch vt := value.(type) {
|
||||||
|
case *A:
|
||||||
|
this.A = vt
|
||||||
|
case *B:
|
||||||
|
this.B = vt
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
and the following test code:
|
||||||
|
|
||||||
|
func TestUUnion(t *testing.T) {
|
||||||
|
popr := math_rand.New(math_rand.NewSource(time.Now().UnixNano()))
|
||||||
|
p := NewPopulatedU(popr)
|
||||||
|
v := p.GetValue()
|
||||||
|
msg := &U{}
|
||||||
|
if !msg.SetValue(v) {
|
||||||
|
t.Fatalf("Union: Could not set Value")
|
||||||
|
}
|
||||||
|
if !p.Equal(msg) {
|
||||||
|
t.Fatalf("%#v !Union Equal %#v", msg, p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
package union
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogo/protobuf/gogoproto"
|
||||||
|
"github.com/gogo/protobuf/protoc-gen-gogo/generator"
|
||||||
|
)
|
||||||
|
|
||||||
|
type union struct {
|
||||||
|
*generator.Generator
|
||||||
|
generator.PluginImports
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUnion() *union {
|
||||||
|
return &union{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *union) Name() string {
|
||||||
|
return "union"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *union) Init(g *generator.Generator) {
|
||||||
|
p.Generator = g
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *union) Generate(file *generator.FileDescriptor) {
|
||||||
|
p.PluginImports = generator.NewPluginImports(p.Generator)
|
||||||
|
|
||||||
|
for _, message := range file.Messages() {
|
||||||
|
if !gogoproto.IsUnion(file.FileDescriptorProto, message.DescriptorProto) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if message.DescriptorProto.HasExtension() {
|
||||||
|
panic("onlyone does not currently support extensions")
|
||||||
|
}
|
||||||
|
if message.DescriptorProto.GetOptions().GetMapEntry() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ccTypeName := generator.CamelCaseSlice(message.TypeName())
|
||||||
|
p.P(`func (this *`, ccTypeName, `) GetValue() interface{} {`)
|
||||||
|
p.In()
|
||||||
|
for _, field := range message.Field {
|
||||||
|
fieldname := p.GetFieldName(message, field)
|
||||||
|
if fieldname == "Value" {
|
||||||
|
panic("cannot have a onlyone message " + ccTypeName + " with a field named Value")
|
||||||
|
}
|
||||||
|
p.P(`if this.`, fieldname, ` != nil {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`return this.`, fieldname)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
}
|
||||||
|
p.P(`return nil`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(``)
|
||||||
|
p.P(`func (this *`, ccTypeName, `) SetValue(value interface{}) bool {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`switch vt := value.(type) {`)
|
||||||
|
p.In()
|
||||||
|
for _, field := range message.Field {
|
||||||
|
fieldname := p.GetFieldName(message, field)
|
||||||
|
goTyp, _ := p.GoType(message, field)
|
||||||
|
p.P(`case `, goTyp, `:`)
|
||||||
|
p.In()
|
||||||
|
p.P(`this.`, fieldname, ` = vt`)
|
||||||
|
p.Out()
|
||||||
|
}
|
||||||
|
p.P(`default:`)
|
||||||
|
p.In()
|
||||||
|
for _, field := range message.Field {
|
||||||
|
fieldname := p.GetFieldName(message, field)
|
||||||
|
if field.IsMessage() {
|
||||||
|
goTyp, _ := p.GoType(message, field)
|
||||||
|
obj := p.ObjectNamed(field.GetTypeName()).(*generator.Descriptor)
|
||||||
|
|
||||||
|
if gogoproto.IsUnion(obj.File().FileDescriptorProto, obj.DescriptorProto) {
|
||||||
|
p.P(`this.`, fieldname, ` = new(`, generator.GoTypeToName(goTyp), `)`)
|
||||||
|
p.P(`if set := this.`, fieldname, `.SetValue(value); set {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`return true`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`this.`, fieldname, ` = nil`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.P(`return false`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`return true`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
generator.RegisterPlugin(NewUnion())
|
||||||
|
}
|
|
@ -0,0 +1,86 @@
|
||||||
|
// Protocol Buffers for Go with Gadgets
|
||||||
|
//
|
||||||
|
// Copyright (c) 2013, The GoGo Authors. All rights reserved.
|
||||||
|
// http://github.com/gogo/protobuf
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
package union
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogo/protobuf/gogoproto"
|
||||||
|
"github.com/gogo/protobuf/plugin/testgen"
|
||||||
|
"github.com/gogo/protobuf/protoc-gen-gogo/generator"
|
||||||
|
)
|
||||||
|
|
||||||
|
type test struct {
|
||||||
|
*generator.Generator
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTest(g *generator.Generator) testgen.TestPlugin {
|
||||||
|
return &test{g}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *test) Generate(imports generator.PluginImports, file *generator.FileDescriptor) bool {
|
||||||
|
used := false
|
||||||
|
randPkg := imports.NewImport("math/rand")
|
||||||
|
timePkg := imports.NewImport("time")
|
||||||
|
testingPkg := imports.NewImport("testing")
|
||||||
|
for _, message := range file.Messages() {
|
||||||
|
if !gogoproto.IsUnion(file.FileDescriptorProto, message.DescriptorProto) ||
|
||||||
|
!gogoproto.HasTestGen(file.FileDescriptorProto, message.DescriptorProto) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if message.DescriptorProto.GetOptions().GetMapEntry() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
used = true
|
||||||
|
ccTypeName := generator.CamelCaseSlice(message.TypeName())
|
||||||
|
|
||||||
|
p.P(`func Test`, ccTypeName, `OnlyOne(t *`, testingPkg.Use(), `.T) {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`popr := `, randPkg.Use(), `.New(`, randPkg.Use(), `.NewSource(`, timePkg.Use(), `.Now().UnixNano()))`)
|
||||||
|
p.P(`p := NewPopulated`, ccTypeName, `(popr, true)`)
|
||||||
|
p.P(`v := p.GetValue()`)
|
||||||
|
p.P(`msg := &`, ccTypeName, `{}`)
|
||||||
|
p.P(`if !msg.SetValue(v) {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`t.Fatalf("OnlyOne: Could not set Value")`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.P(`if !p.Equal(msg) {`)
|
||||||
|
p.In()
|
||||||
|
p.P(`t.Fatalf("%#v !OnlyOne Equal %#v", msg, p)`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
p.Out()
|
||||||
|
p.P(`}`)
|
||||||
|
|
||||||
|
}
|
||||||
|
return used
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
testgen.RegisterTestPlugin(NewTest)
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,41 @@
|
||||||
|
# Go support for Protocol Buffers - Google's data interchange format
|
||||||
|
#
|
||||||
|
# Copyright 2010 The Go Authors. All rights reserved.
|
||||||
|
# https://github.com/golang/protobuf
|
||||||
|
#
|
||||||
|
# Redistribution and use in source and binary forms, with or without
|
||||||
|
# modification, are permitted provided that the following conditions are
|
||||||
|
# met:
|
||||||
|
#
|
||||||
|
# * Redistributions of source code must retain the above copyright
|
||||||
|
# notice, this list of conditions and the following disclaimer.
|
||||||
|
# * Redistributions in binary form must reproduce the above
|
||||||
|
# copyright notice, this list of conditions and the following disclaimer
|
||||||
|
# in the documentation and/or other materials provided with the
|
||||||
|
# distribution.
|
||||||
|
# * Neither the name of Google Inc. nor the names of its
|
||||||
|
# contributors may be used to endorse or promote products derived from
|
||||||
|
# this software without specific prior written permission.
|
||||||
|
#
|
||||||
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
all: test
|
||||||
|
|
||||||
|
test:
|
||||||
|
go test
|
||||||
|
make -C testdata test
|
||||||
|
|
||||||
|
regenerate:
|
||||||
|
go test --regenerate
|
||||||
|
make -C descriptor regenerate
|
||||||
|
make -C plugin regenerate
|
|
@ -0,0 +1,51 @@
|
||||||
|
// Go support for Protocol Buffers - Google's data interchange format
|
||||||
|
//
|
||||||
|
// Copyright 2010 The Go Authors. All rights reserved.
|
||||||
|
// https://github.com/golang/protobuf
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
/*
|
||||||
|
A plugin for the Google protocol buffer compiler to generate Go code.
|
||||||
|
Run it by building this program and putting it in your path with the name
|
||||||
|
protoc-gen-gogo
|
||||||
|
That word 'gogo' at the end becomes part of the option string set for the
|
||||||
|
protocol compiler, so once the protocol compiler (protoc) is installed
|
||||||
|
you can run
|
||||||
|
protoc --gogo_out=output_directory input_directory/file.proto
|
||||||
|
to generate Go bindings for the protocol defined by file.proto.
|
||||||
|
With that input, the output will be written to
|
||||||
|
output_directory/go_package/file.pb.go
|
||||||
|
|
||||||
|
The generated code is documented in the package comment for
|
||||||
|
the library.
|
||||||
|
|
||||||
|
See the README and documentation for protocol buffers to learn more:
|
||||||
|
https://developers.google.com/protocol-buffers/
|
||||||
|
|
||||||
|
*/
|
||||||
|
package documentation
|
3444
vendor/github.com/gogo/protobuf/protoc-gen-gogo/generator/generator.go
generated
vendored
Normal file
3444
vendor/github.com/gogo/protobuf/protoc-gen-gogo/generator/generator.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
461
vendor/github.com/gogo/protobuf/protoc-gen-gogo/generator/helper.go
generated
vendored
Normal file
461
vendor/github.com/gogo/protobuf/protoc-gen-gogo/generator/helper.go
generated
vendored
Normal file
|
@ -0,0 +1,461 @@
|
||||||
|
// Protocol Buffers for Go with Gadgets
|
||||||
|
//
|
||||||
|
// Copyright (c) 2013, The GoGo Authors. All rights reserved.
|
||||||
|
// http://github.com/gogo/protobuf
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
package generator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"go/parser"
|
||||||
|
"go/printer"
|
||||||
|
"go/token"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gogo/protobuf/gogoproto"
|
||||||
|
"github.com/gogo/protobuf/proto"
|
||||||
|
descriptor "github.com/gogo/protobuf/protoc-gen-gogo/descriptor"
|
||||||
|
plugin "github.com/gogo/protobuf/protoc-gen-gogo/plugin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (d *FileDescriptor) Messages() []*Descriptor {
|
||||||
|
return d.desc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *FileDescriptor) Enums() []*EnumDescriptor {
|
||||||
|
return d.enum
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Descriptor) IsGroup() bool {
|
||||||
|
return d.group
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Generator) IsGroup(field *descriptor.FieldDescriptorProto) bool {
|
||||||
|
if d, ok := g.typeNameToObject[field.GetTypeName()].(*Descriptor); ok {
|
||||||
|
return d.IsGroup()
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Generator) TypeNameByObject(typeName string) Object {
|
||||||
|
o, ok := g.typeNameToObject[typeName]
|
||||||
|
if !ok {
|
||||||
|
g.Fail("can't find object with type", typeName)
|
||||||
|
}
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Generator) OneOfTypeName(message *Descriptor, field *descriptor.FieldDescriptorProto) string {
|
||||||
|
typeName := message.TypeName()
|
||||||
|
ccTypeName := CamelCaseSlice(typeName)
|
||||||
|
fieldName := g.GetOneOfFieldName(message, field)
|
||||||
|
tname := ccTypeName + "_" + fieldName
|
||||||
|
// It is possible for this to collide with a message or enum
|
||||||
|
// nested in this message. Check for collisions.
|
||||||
|
ok := true
|
||||||
|
for _, desc := range message.nested {
|
||||||
|
if strings.Join(desc.TypeName(), "_") == tname {
|
||||||
|
ok = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, enum := range message.enums {
|
||||||
|
if strings.Join(enum.TypeName(), "_") == tname {
|
||||||
|
ok = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
tname += "_"
|
||||||
|
}
|
||||||
|
return tname
|
||||||
|
}
|
||||||
|
|
||||||
|
type PluginImports interface {
|
||||||
|
NewImport(pkg string) Single
|
||||||
|
GenerateImports(file *FileDescriptor)
|
||||||
|
}
|
||||||
|
|
||||||
|
type pluginImports struct {
|
||||||
|
generator *Generator
|
||||||
|
singles []Single
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPluginImports(generator *Generator) *pluginImports {
|
||||||
|
return &pluginImports{generator, make([]Single, 0)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *pluginImports) NewImport(pkg string) Single {
|
||||||
|
imp := newImportedPackage(this.generator.ImportPrefix, pkg)
|
||||||
|
this.singles = append(this.singles, imp)
|
||||||
|
return imp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *pluginImports) GenerateImports(file *FileDescriptor) {
|
||||||
|
for _, s := range this.singles {
|
||||||
|
if s.IsUsed() {
|
||||||
|
this.generator.PrintImport(GoPackageName(s.Name()), GoImportPath(s.Location()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Single interface {
|
||||||
|
Use() string
|
||||||
|
IsUsed() bool
|
||||||
|
Name() string
|
||||||
|
Location() string
|
||||||
|
}
|
||||||
|
|
||||||
|
type importedPackage struct {
|
||||||
|
used bool
|
||||||
|
pkg string
|
||||||
|
name string
|
||||||
|
importPrefix string
|
||||||
|
}
|
||||||
|
|
||||||
|
func newImportedPackage(importPrefix string, pkg string) *importedPackage {
|
||||||
|
return &importedPackage{
|
||||||
|
pkg: pkg,
|
||||||
|
importPrefix: importPrefix,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *importedPackage) Use() string {
|
||||||
|
if !this.used {
|
||||||
|
this.name = string(cleanPackageName(this.pkg))
|
||||||
|
this.used = true
|
||||||
|
}
|
||||||
|
return this.name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *importedPackage) IsUsed() bool {
|
||||||
|
return this.used
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *importedPackage) Name() string {
|
||||||
|
return this.name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *importedPackage) Location() string {
|
||||||
|
return this.importPrefix + this.pkg
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Generator) GetFieldName(message *Descriptor, field *descriptor.FieldDescriptorProto) string {
|
||||||
|
goTyp, _ := g.GoType(message, field)
|
||||||
|
fieldname := CamelCase(*field.Name)
|
||||||
|
if gogoproto.IsCustomName(field) {
|
||||||
|
fieldname = gogoproto.GetCustomName(field)
|
||||||
|
}
|
||||||
|
if gogoproto.IsEmbed(field) {
|
||||||
|
fieldname = EmbedFieldName(goTyp)
|
||||||
|
}
|
||||||
|
if field.OneofIndex != nil {
|
||||||
|
fieldname = message.OneofDecl[int(*field.OneofIndex)].GetName()
|
||||||
|
fieldname = CamelCase(fieldname)
|
||||||
|
}
|
||||||
|
for _, f := range methodNames {
|
||||||
|
if f == fieldname {
|
||||||
|
return fieldname + "_"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !gogoproto.IsProtoSizer(message.file.FileDescriptorProto, message.DescriptorProto) {
|
||||||
|
if fieldname == "Size" {
|
||||||
|
return fieldname + "_"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fieldname
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Generator) GetOneOfFieldName(message *Descriptor, field *descriptor.FieldDescriptorProto) string {
|
||||||
|
goTyp, _ := g.GoType(message, field)
|
||||||
|
fieldname := CamelCase(*field.Name)
|
||||||
|
if gogoproto.IsCustomName(field) {
|
||||||
|
fieldname = gogoproto.GetCustomName(field)
|
||||||
|
}
|
||||||
|
if gogoproto.IsEmbed(field) {
|
||||||
|
fieldname = EmbedFieldName(goTyp)
|
||||||
|
}
|
||||||
|
for _, f := range methodNames {
|
||||||
|
if f == fieldname {
|
||||||
|
return fieldname + "_"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !gogoproto.IsProtoSizer(message.file.FileDescriptorProto, message.DescriptorProto) {
|
||||||
|
if fieldname == "Size" {
|
||||||
|
return fieldname + "_"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fieldname
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Generator) IsMap(field *descriptor.FieldDescriptorProto) bool {
|
||||||
|
if !field.IsMessage() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
byName := g.ObjectNamed(field.GetTypeName())
|
||||||
|
desc, ok := byName.(*Descriptor)
|
||||||
|
if byName == nil || !ok || !desc.GetOptions().GetMapEntry() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Generator) GetMapKeyField(field, keyField *descriptor.FieldDescriptorProto) *descriptor.FieldDescriptorProto {
|
||||||
|
if !gogoproto.IsCastKey(field) {
|
||||||
|
return keyField
|
||||||
|
}
|
||||||
|
keyField = proto.Clone(keyField).(*descriptor.FieldDescriptorProto)
|
||||||
|
if keyField.Options == nil {
|
||||||
|
keyField.Options = &descriptor.FieldOptions{}
|
||||||
|
}
|
||||||
|
keyType := gogoproto.GetCastKey(field)
|
||||||
|
if err := proto.SetExtension(keyField.Options, gogoproto.E_Casttype, &keyType); err != nil {
|
||||||
|
g.Fail(err.Error())
|
||||||
|
}
|
||||||
|
return keyField
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Generator) GetMapValueField(field, valField *descriptor.FieldDescriptorProto) *descriptor.FieldDescriptorProto {
|
||||||
|
if gogoproto.IsCustomType(field) && gogoproto.IsCastValue(field) {
|
||||||
|
g.Fail("cannot have a customtype and casttype: ", field.String())
|
||||||
|
}
|
||||||
|
valField = proto.Clone(valField).(*descriptor.FieldDescriptorProto)
|
||||||
|
if valField.Options == nil {
|
||||||
|
valField.Options = &descriptor.FieldOptions{}
|
||||||
|
}
|
||||||
|
|
||||||
|
stdtime := gogoproto.IsStdTime(field)
|
||||||
|
if stdtime {
|
||||||
|
if err := proto.SetExtension(valField.Options, gogoproto.E_Stdtime, &stdtime); err != nil {
|
||||||
|
g.Fail(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stddur := gogoproto.IsStdDuration(field)
|
||||||
|
if stddur {
|
||||||
|
if err := proto.SetExtension(valField.Options, gogoproto.E_Stdduration, &stddur); err != nil {
|
||||||
|
g.Fail(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wktptr := gogoproto.IsWktPtr(field)
|
||||||
|
if wktptr {
|
||||||
|
if err := proto.SetExtension(valField.Options, gogoproto.E_Wktpointer, &wktptr); err != nil {
|
||||||
|
g.Fail(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if valType := gogoproto.GetCastValue(field); len(valType) > 0 {
|
||||||
|
if err := proto.SetExtension(valField.Options, gogoproto.E_Casttype, &valType); err != nil {
|
||||||
|
g.Fail(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if valType := gogoproto.GetCustomType(field); len(valType) > 0 {
|
||||||
|
if err := proto.SetExtension(valField.Options, gogoproto.E_Customtype, &valType); err != nil {
|
||||||
|
g.Fail(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nullable := gogoproto.IsNullable(field)
|
||||||
|
if err := proto.SetExtension(valField.Options, gogoproto.E_Nullable, &nullable); err != nil {
|
||||||
|
g.Fail(err.Error())
|
||||||
|
}
|
||||||
|
return valField
|
||||||
|
}
|
||||||
|
|
||||||
|
// GoMapValueTypes returns the map value Go type and the alias map value Go type (for casting), taking into
|
||||||
|
// account whether the map is nullable or the value is a message.
|
||||||
|
func GoMapValueTypes(mapField, valueField *descriptor.FieldDescriptorProto, goValueType, goValueAliasType string) (nullable bool, outGoType string, outGoAliasType string) {
|
||||||
|
nullable = gogoproto.IsNullable(mapField) && (valueField.IsMessage() || gogoproto.IsCustomType(mapField))
|
||||||
|
if nullable {
|
||||||
|
// ensure the non-aliased Go value type is a pointer for consistency
|
||||||
|
if strings.HasPrefix(goValueType, "*") {
|
||||||
|
outGoType = goValueType
|
||||||
|
} else {
|
||||||
|
outGoType = "*" + goValueType
|
||||||
|
}
|
||||||
|
outGoAliasType = goValueAliasType
|
||||||
|
} else {
|
||||||
|
outGoType = strings.Replace(goValueType, "*", "", 1)
|
||||||
|
outGoAliasType = strings.Replace(goValueAliasType, "*", "", 1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func GoTypeToName(goTyp string) string {
|
||||||
|
return strings.Replace(strings.Replace(goTyp, "*", "", -1), "[]", "", -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func EmbedFieldName(goTyp string) string {
|
||||||
|
goTyp = GoTypeToName(goTyp)
|
||||||
|
goTyps := strings.Split(goTyp, ".")
|
||||||
|
if len(goTyps) == 1 {
|
||||||
|
return goTyp
|
||||||
|
}
|
||||||
|
if len(goTyps) == 2 {
|
||||||
|
return goTyps[1]
|
||||||
|
}
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Generator) GeneratePlugin(p Plugin) {
|
||||||
|
plugins = []Plugin{p}
|
||||||
|
p.Init(g)
|
||||||
|
// Generate the output. The generator runs for every file, even the files
|
||||||
|
// that we don't generate output for, so that we can collate the full list
|
||||||
|
// of exported symbols to support public imports.
|
||||||
|
genFileMap := make(map[*FileDescriptor]bool, len(g.genFiles))
|
||||||
|
for _, file := range g.genFiles {
|
||||||
|
genFileMap[file] = true
|
||||||
|
}
|
||||||
|
for _, file := range g.allFiles {
|
||||||
|
g.Reset()
|
||||||
|
g.writeOutput = genFileMap[file]
|
||||||
|
g.generatePlugin(file, p)
|
||||||
|
if !g.writeOutput {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
g.Response.File = append(g.Response.File, &plugin.CodeGeneratorResponse_File{
|
||||||
|
Name: proto.String(file.goFileName(g.pathType)),
|
||||||
|
Content: proto.String(g.String()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Generator) SetFile(filename string) {
|
||||||
|
g.file = g.fileByName(filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Generator) generatePlugin(file *FileDescriptor, p Plugin) {
|
||||||
|
g.writtenImports = make(map[string]bool)
|
||||||
|
g.usedPackages = make(map[GoImportPath]bool)
|
||||||
|
g.packageNames = make(map[GoImportPath]GoPackageName)
|
||||||
|
g.usedPackageNames = make(map[GoPackageName]bool)
|
||||||
|
g.addedImports = make(map[GoImportPath]bool)
|
||||||
|
g.file = file
|
||||||
|
|
||||||
|
// Run the plugins before the imports so we know which imports are necessary.
|
||||||
|
p.Generate(file)
|
||||||
|
|
||||||
|
// Generate header and imports last, though they appear first in the output.
|
||||||
|
rem := g.Buffer
|
||||||
|
g.Buffer = new(bytes.Buffer)
|
||||||
|
g.generateHeader()
|
||||||
|
// p.GenerateImports(g.file)
|
||||||
|
g.generateImports()
|
||||||
|
if !g.writeOutput {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
g.Write(rem.Bytes())
|
||||||
|
|
||||||
|
// Reformat generated code.
|
||||||
|
contents := string(g.Buffer.Bytes())
|
||||||
|
fset := token.NewFileSet()
|
||||||
|
ast, err := parser.ParseFile(fset, "", g, parser.ParseComments)
|
||||||
|
if err != nil {
|
||||||
|
g.Fail("bad Go source code was generated:", contents, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
g.Reset()
|
||||||
|
err = (&printer.Config{Mode: printer.TabIndent | printer.UseSpaces, Tabwidth: 8}).Fprint(g, fset, ast)
|
||||||
|
if err != nil {
|
||||||
|
g.Fail("generated Go source code could not be reformatted:", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetCustomType(field *descriptor.FieldDescriptorProto) (packageName string, typ string, err error) {
|
||||||
|
return getCustomType(field)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCustomType(field *descriptor.FieldDescriptorProto) (packageName string, typ string, err error) {
|
||||||
|
if field.Options != nil {
|
||||||
|
var v interface{}
|
||||||
|
v, err = proto.GetExtension(field.Options, gogoproto.E_Customtype)
|
||||||
|
if err == nil && v.(*string) != nil {
|
||||||
|
ctype := *(v.(*string))
|
||||||
|
packageName, typ = splitCPackageType(ctype)
|
||||||
|
return packageName, typ, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
func splitCPackageType(ctype string) (packageName string, typ string) {
|
||||||
|
ss := strings.Split(ctype, ".")
|
||||||
|
if len(ss) == 1 {
|
||||||
|
return "", ctype
|
||||||
|
}
|
||||||
|
packageName = strings.Join(ss[0:len(ss)-1], ".")
|
||||||
|
typeName := ss[len(ss)-1]
|
||||||
|
importStr := strings.Map(badToUnderscore, packageName)
|
||||||
|
typ = importStr + "." + typeName
|
||||||
|
return packageName, typ
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCastType(field *descriptor.FieldDescriptorProto) (packageName string, typ string, err error) {
|
||||||
|
if field.Options != nil {
|
||||||
|
var v interface{}
|
||||||
|
v, err = proto.GetExtension(field.Options, gogoproto.E_Casttype)
|
||||||
|
if err == nil && v.(*string) != nil {
|
||||||
|
ctype := *(v.(*string))
|
||||||
|
packageName, typ = splitCPackageType(ctype)
|
||||||
|
return packageName, typ, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
func FileName(file *FileDescriptor) string {
|
||||||
|
fname := path.Base(file.FileDescriptorProto.GetName())
|
||||||
|
fname = strings.Replace(fname, ".proto", "", -1)
|
||||||
|
fname = strings.Replace(fname, "-", "_", -1)
|
||||||
|
fname = strings.Replace(fname, ".", "_", -1)
|
||||||
|
return CamelCase(fname)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Generator) AllFiles() *descriptor.FileDescriptorSet {
|
||||||
|
set := &descriptor.FileDescriptorSet{}
|
||||||
|
set.File = make([]*descriptor.FileDescriptorProto, len(g.allFiles))
|
||||||
|
for i := range g.allFiles {
|
||||||
|
set.File[i] = g.allFiles[i].FileDescriptorProto
|
||||||
|
}
|
||||||
|
return set
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Descriptor) Path() string {
|
||||||
|
return d.path
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Generator) useTypes() string {
|
||||||
|
pkg := strings.Map(badToUnderscore, "github.com/gogo/protobuf/types")
|
||||||
|
g.customImports = append(g.customImports, "github.com/gogo/protobuf/types")
|
||||||
|
return pkg
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *FileDescriptor) GoPackageName() string {
|
||||||
|
return string(d.packageName)
|
||||||
|
}
|
117
vendor/github.com/gogo/protobuf/protoc-gen-gogo/generator/internal/remap/remap.go
generated
vendored
Normal file
117
vendor/github.com/gogo/protobuf/protoc-gen-gogo/generator/internal/remap/remap.go
generated
vendored
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
// Go support for Protocol Buffers - Google's data interchange format
|
||||||
|
//
|
||||||
|
// Copyright 2017 The Go Authors. All rights reserved.
|
||||||
|
// https://github.com/golang/protobuf
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
/*
|
||||||
|
Package remap handles tracking the locations of Go tokens in a source text
|
||||||
|
across a rewrite by the Go formatter.
|
||||||
|
*/
|
||||||
|
package remap
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"go/scanner"
|
||||||
|
"go/token"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Location represents a span of byte offsets in the source text.
|
||||||
|
type Location struct {
|
||||||
|
Pos, End int // End is exclusive
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Map represents a mapping between token locations in an input source text
|
||||||
|
// and locations in the correspnding output text.
|
||||||
|
type Map map[Location]Location
|
||||||
|
|
||||||
|
// Find reports whether the specified span is recorded by m, and if so returns
|
||||||
|
// the new location it was mapped to. If the input span was not found, the
|
||||||
|
// returned location is the same as the input.
|
||||||
|
func (m Map) Find(pos, end int) (Location, bool) {
|
||||||
|
key := Location{
|
||||||
|
Pos: pos,
|
||||||
|
End: end,
|
||||||
|
}
|
||||||
|
if loc, ok := m[key]; ok {
|
||||||
|
return loc, true
|
||||||
|
}
|
||||||
|
return key, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m Map) add(opos, oend, npos, nend int) {
|
||||||
|
m[Location{Pos: opos, End: oend}] = Location{Pos: npos, End: nend}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute constructs a location mapping from input to output. An error is
|
||||||
|
// reported if any of the tokens of output cannot be mapped.
|
||||||
|
func Compute(input, output []byte) (Map, error) {
|
||||||
|
itok := tokenize(input)
|
||||||
|
otok := tokenize(output)
|
||||||
|
if len(itok) != len(otok) {
|
||||||
|
return nil, fmt.Errorf("wrong number of tokens, %d ≠ %d", len(itok), len(otok))
|
||||||
|
}
|
||||||
|
m := make(Map)
|
||||||
|
for i, ti := range itok {
|
||||||
|
to := otok[i]
|
||||||
|
if ti.Token != to.Token {
|
||||||
|
return nil, fmt.Errorf("token %d type mismatch: %s ≠ %s", i+1, ti, to)
|
||||||
|
}
|
||||||
|
m.add(ti.pos, ti.end, to.pos, to.end)
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// tokinfo records the span and type of a source token.
|
||||||
|
type tokinfo struct {
|
||||||
|
pos, end int
|
||||||
|
token.Token
|
||||||
|
}
|
||||||
|
|
||||||
|
func tokenize(src []byte) []tokinfo {
|
||||||
|
fs := token.NewFileSet()
|
||||||
|
var s scanner.Scanner
|
||||||
|
s.Init(fs.AddFile("src", fs.Base(), len(src)), src, nil, scanner.ScanComments)
|
||||||
|
var info []tokinfo
|
||||||
|
for {
|
||||||
|
pos, next, lit := s.Scan()
|
||||||
|
switch next {
|
||||||
|
case token.SEMICOLON:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
info = append(info, tokinfo{
|
||||||
|
pos: int(pos - 1),
|
||||||
|
end: int(pos + token.Pos(len(lit)) - 1),
|
||||||
|
Token: next,
|
||||||
|
})
|
||||||
|
if next == token.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return info
|
||||||
|
}
|
|
@ -0,0 +1,536 @@
|
||||||
|
// Go support for Protocol Buffers - Google's data interchange format
|
||||||
|
//
|
||||||
|
// Copyright 2015 The Go Authors. All rights reserved.
|
||||||
|
// https://github.com/golang/protobuf
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
// Package grpc outputs gRPC service descriptions in Go code.
|
||||||
|
// It runs as a plugin for the Go protocol buffer compiler plugin.
|
||||||
|
// It is linked in to protoc-gen-go.
|
||||||
|
package grpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
pb "github.com/gogo/protobuf/protoc-gen-gogo/descriptor"
|
||||||
|
"github.com/gogo/protobuf/protoc-gen-gogo/generator"
|
||||||
|
)
|
||||||
|
|
||||||
|
// generatedCodeVersion indicates a version of the generated code.
|
||||||
|
// It is incremented whenever an incompatibility between the generated code and
|
||||||
|
// the grpc package is introduced; the generated code references
|
||||||
|
// a constant, grpc.SupportPackageIsVersionN (where N is generatedCodeVersion).
|
||||||
|
const generatedCodeVersion = 4
|
||||||
|
|
||||||
|
// Paths for packages used by code generated in this file,
|
||||||
|
// relative to the import_prefix of the generator.Generator.
|
||||||
|
const (
|
||||||
|
contextPkgPath = "context"
|
||||||
|
grpcPkgPath = "google.golang.org/grpc"
|
||||||
|
codePkgPath = "google.golang.org/grpc/codes"
|
||||||
|
statusPkgPath = "google.golang.org/grpc/status"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
generator.RegisterPlugin(new(grpc))
|
||||||
|
}
|
||||||
|
|
||||||
|
// grpc is an implementation of the Go protocol buffer compiler's
|
||||||
|
// plugin architecture. It generates bindings for gRPC support.
|
||||||
|
type grpc struct {
|
||||||
|
gen *generator.Generator
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name returns the name of this plugin, "grpc".
|
||||||
|
func (g *grpc) Name() string {
|
||||||
|
return "grpc"
|
||||||
|
}
|
||||||
|
|
||||||
|
// The names for packages imported in the generated code.
|
||||||
|
// They may vary from the final path component of the import path
|
||||||
|
// if the name is used by other packages.
|
||||||
|
var (
|
||||||
|
contextPkg string
|
||||||
|
grpcPkg string
|
||||||
|
)
|
||||||
|
|
||||||
|
// Init initializes the plugin.
|
||||||
|
func (g *grpc) Init(gen *generator.Generator) {
|
||||||
|
g.gen = gen
|
||||||
|
}
|
||||||
|
|
||||||
|
// Given a type name defined in a .proto, return its object.
|
||||||
|
// Also record that we're using it, to guarantee the associated import.
|
||||||
|
func (g *grpc) objectNamed(name string) generator.Object {
|
||||||
|
g.gen.RecordTypeUse(name)
|
||||||
|
return g.gen.ObjectNamed(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Given a type name defined in a .proto, return its name as we will print it.
|
||||||
|
func (g *grpc) typeName(str string) string {
|
||||||
|
return g.gen.TypeName(g.objectNamed(str))
|
||||||
|
}
|
||||||
|
|
||||||
|
// P forwards to g.gen.P.
|
||||||
|
func (g *grpc) P(args ...interface{}) { g.gen.P(args...) }
|
||||||
|
|
||||||
|
// Generate generates code for the services in the given file.
|
||||||
|
func (g *grpc) Generate(file *generator.FileDescriptor) {
|
||||||
|
if len(file.FileDescriptorProto.Service) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
contextPkg = string(g.gen.AddImport(contextPkgPath))
|
||||||
|
grpcPkg = string(g.gen.AddImport(grpcPkgPath))
|
||||||
|
|
||||||
|
g.P("// Reference imports to suppress errors if they are not otherwise used.")
|
||||||
|
g.P("var _ ", contextPkg, ".Context")
|
||||||
|
g.P("var _ ", grpcPkg, ".ClientConn")
|
||||||
|
g.P()
|
||||||
|
|
||||||
|
// Assert version compatibility.
|
||||||
|
g.P("// This is a compile-time assertion to ensure that this generated file")
|
||||||
|
g.P("// is compatible with the grpc package it is being compiled against.")
|
||||||
|
g.P("const _ = ", grpcPkg, ".SupportPackageIsVersion", generatedCodeVersion)
|
||||||
|
g.P()
|
||||||
|
|
||||||
|
for i, service := range file.FileDescriptorProto.Service {
|
||||||
|
g.generateService(file, service, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateImports generates the import declaration for this file.
|
||||||
|
func (g *grpc) GenerateImports(file *generator.FileDescriptor) {}
|
||||||
|
|
||||||
|
// reservedClientName records whether a client name is reserved on the client side.
|
||||||
|
var reservedClientName = map[string]bool{
|
||||||
|
// TODO: do we need any in gRPC?
|
||||||
|
}
|
||||||
|
|
||||||
|
func unexport(s string) string { return strings.ToLower(s[:1]) + s[1:] }
|
||||||
|
|
||||||
|
// deprecationComment is the standard comment added to deprecated
|
||||||
|
// messages, fields, enums, and enum values.
|
||||||
|
var deprecationComment = "// Deprecated: Do not use."
|
||||||
|
|
||||||
|
// generateService generates all the code for the named service.
|
||||||
|
func (g *grpc) generateService(file *generator.FileDescriptor, service *pb.ServiceDescriptorProto, index int) {
|
||||||
|
path := fmt.Sprintf("6,%d", index) // 6 means service.
|
||||||
|
|
||||||
|
origServName := service.GetName()
|
||||||
|
fullServName := origServName
|
||||||
|
if pkg := file.GetPackage(); pkg != "" {
|
||||||
|
fullServName = pkg + "." + fullServName
|
||||||
|
}
|
||||||
|
servName := generator.CamelCase(origServName)
|
||||||
|
deprecated := service.GetOptions().GetDeprecated()
|
||||||
|
|
||||||
|
g.P()
|
||||||
|
g.P(fmt.Sprintf(`// %sClient is the client API for %s service.
|
||||||
|
//
|
||||||
|
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.`, servName, servName))
|
||||||
|
|
||||||
|
// Client interface.
|
||||||
|
if deprecated {
|
||||||
|
g.P("//")
|
||||||
|
g.P(deprecationComment)
|
||||||
|
}
|
||||||
|
g.P("type ", servName, "Client interface {")
|
||||||
|
for i, method := range service.Method {
|
||||||
|
g.gen.PrintComments(fmt.Sprintf("%s,2,%d", path, i)) // 2 means method in a service.
|
||||||
|
g.P(g.generateClientSignature(servName, method))
|
||||||
|
}
|
||||||
|
g.P("}")
|
||||||
|
g.P()
|
||||||
|
|
||||||
|
// Client structure.
|
||||||
|
g.P("type ", unexport(servName), "Client struct {")
|
||||||
|
g.P("cc *", grpcPkg, ".ClientConn")
|
||||||
|
g.P("}")
|
||||||
|
g.P()
|
||||||
|
|
||||||
|
// NewClient factory.
|
||||||
|
if deprecated {
|
||||||
|
g.P(deprecationComment)
|
||||||
|
}
|
||||||
|
g.P("func New", servName, "Client (cc *", grpcPkg, ".ClientConn) ", servName, "Client {")
|
||||||
|
g.P("return &", unexport(servName), "Client{cc}")
|
||||||
|
g.P("}")
|
||||||
|
g.P()
|
||||||
|
|
||||||
|
var methodIndex, streamIndex int
|
||||||
|
serviceDescVar := "_" + servName + "_serviceDesc"
|
||||||
|
// Client method implementations.
|
||||||
|
for _, method := range service.Method {
|
||||||
|
var descExpr string
|
||||||
|
if !method.GetServerStreaming() && !method.GetClientStreaming() {
|
||||||
|
// Unary RPC method
|
||||||
|
descExpr = fmt.Sprintf("&%s.Methods[%d]", serviceDescVar, methodIndex)
|
||||||
|
methodIndex++
|
||||||
|
} else {
|
||||||
|
// Streaming RPC method
|
||||||
|
descExpr = fmt.Sprintf("&%s.Streams[%d]", serviceDescVar, streamIndex)
|
||||||
|
streamIndex++
|
||||||
|
}
|
||||||
|
g.generateClientMethod(servName, fullServName, serviceDescVar, method, descExpr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Server interface.
|
||||||
|
serverType := servName + "Server"
|
||||||
|
g.P("// ", serverType, " is the server API for ", servName, " service.")
|
||||||
|
if deprecated {
|
||||||
|
g.P("//")
|
||||||
|
g.P(deprecationComment)
|
||||||
|
}
|
||||||
|
g.P("type ", serverType, " interface {")
|
||||||
|
for i, method := range service.Method {
|
||||||
|
g.gen.PrintComments(fmt.Sprintf("%s,2,%d", path, i)) // 2 means method in a service.
|
||||||
|
g.P(g.generateServerSignature(servName, method))
|
||||||
|
}
|
||||||
|
g.P("}")
|
||||||
|
g.P()
|
||||||
|
|
||||||
|
// Server Unimplemented struct for forward compatability.
|
||||||
|
if deprecated {
|
||||||
|
g.P(deprecationComment)
|
||||||
|
}
|
||||||
|
g.generateUnimplementedServer(servName, service)
|
||||||
|
|
||||||
|
// Server registration.
|
||||||
|
if deprecated {
|
||||||
|
g.P(deprecationComment)
|
||||||
|
}
|
||||||
|
g.P("func Register", servName, "Server(s *", grpcPkg, ".Server, srv ", serverType, ") {")
|
||||||
|
g.P("s.RegisterService(&", serviceDescVar, `, srv)`)
|
||||||
|
g.P("}")
|
||||||
|
g.P()
|
||||||
|
|
||||||
|
// Server handler implementations.
|
||||||
|
var handlerNames []string
|
||||||
|
for _, method := range service.Method {
|
||||||
|
hname := g.generateServerMethod(servName, fullServName, method)
|
||||||
|
handlerNames = append(handlerNames, hname)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Service descriptor.
|
||||||
|
g.P("var ", serviceDescVar, " = ", grpcPkg, ".ServiceDesc {")
|
||||||
|
g.P("ServiceName: ", strconv.Quote(fullServName), ",")
|
||||||
|
g.P("HandlerType: (*", serverType, ")(nil),")
|
||||||
|
g.P("Methods: []", grpcPkg, ".MethodDesc{")
|
||||||
|
for i, method := range service.Method {
|
||||||
|
if method.GetServerStreaming() || method.GetClientStreaming() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
g.P("{")
|
||||||
|
g.P("MethodName: ", strconv.Quote(method.GetName()), ",")
|
||||||
|
g.P("Handler: ", handlerNames[i], ",")
|
||||||
|
g.P("},")
|
||||||
|
}
|
||||||
|
g.P("},")
|
||||||
|
g.P("Streams: []", grpcPkg, ".StreamDesc{")
|
||||||
|
for i, method := range service.Method {
|
||||||
|
if !method.GetServerStreaming() && !method.GetClientStreaming() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
g.P("{")
|
||||||
|
g.P("StreamName: ", strconv.Quote(method.GetName()), ",")
|
||||||
|
g.P("Handler: ", handlerNames[i], ",")
|
||||||
|
if method.GetServerStreaming() {
|
||||||
|
g.P("ServerStreams: true,")
|
||||||
|
}
|
||||||
|
if method.GetClientStreaming() {
|
||||||
|
g.P("ClientStreams: true,")
|
||||||
|
}
|
||||||
|
g.P("},")
|
||||||
|
}
|
||||||
|
g.P("},")
|
||||||
|
g.P("Metadata: \"", file.GetName(), "\",")
|
||||||
|
g.P("}")
|
||||||
|
g.P()
|
||||||
|
}
|
||||||
|
|
||||||
|
// generateUnimplementedServer creates the unimplemented server struct
|
||||||
|
func (g *grpc) generateUnimplementedServer(servName string, service *pb.ServiceDescriptorProto) {
|
||||||
|
serverType := servName + "Server"
|
||||||
|
g.P("// Unimplemented", serverType, " can be embedded to have forward compatible implementations.")
|
||||||
|
g.P("type Unimplemented", serverType, " struct {")
|
||||||
|
g.P("}")
|
||||||
|
g.P()
|
||||||
|
// Unimplemented<service_name>Server's concrete methods
|
||||||
|
for _, method := range service.Method {
|
||||||
|
g.generateServerMethodConcrete(servName, method)
|
||||||
|
}
|
||||||
|
g.P()
|
||||||
|
}
|
||||||
|
|
||||||
|
// generateServerMethodConcrete returns unimplemented methods which ensure forward compatibility
|
||||||
|
func (g *grpc) generateServerMethodConcrete(servName string, method *pb.MethodDescriptorProto) {
|
||||||
|
header := g.generateServerSignatureWithParamNames(servName, method)
|
||||||
|
g.P("func (*Unimplemented", servName, "Server) ", header, " {")
|
||||||
|
var nilArg string
|
||||||
|
if !method.GetServerStreaming() && !method.GetClientStreaming() {
|
||||||
|
nilArg = "nil, "
|
||||||
|
}
|
||||||
|
methName := generator.CamelCase(method.GetName())
|
||||||
|
statusPkg := string(g.gen.AddImport(statusPkgPath))
|
||||||
|
codePkg := string(g.gen.AddImport(codePkgPath))
|
||||||
|
g.P("return ", nilArg, statusPkg, `.Errorf(`, codePkg, `.Unimplemented, "method `, methName, ` not implemented")`)
|
||||||
|
g.P("}")
|
||||||
|
}
|
||||||
|
|
||||||
|
// generateClientSignature returns the client-side signature for a method.
|
||||||
|
func (g *grpc) generateClientSignature(servName string, method *pb.MethodDescriptorProto) string {
|
||||||
|
origMethName := method.GetName()
|
||||||
|
methName := generator.CamelCase(origMethName)
|
||||||
|
if reservedClientName[methName] {
|
||||||
|
methName += "_"
|
||||||
|
}
|
||||||
|
reqArg := ", in *" + g.typeName(method.GetInputType())
|
||||||
|
if method.GetClientStreaming() {
|
||||||
|
reqArg = ""
|
||||||
|
}
|
||||||
|
respName := "*" + g.typeName(method.GetOutputType())
|
||||||
|
if method.GetServerStreaming() || method.GetClientStreaming() {
|
||||||
|
respName = servName + "_" + generator.CamelCase(origMethName) + "Client"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s(ctx %s.Context%s, opts ...%s.CallOption) (%s, error)", methName, contextPkg, reqArg, grpcPkg, respName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *grpc) generateClientMethod(servName, fullServName, serviceDescVar string, method *pb.MethodDescriptorProto, descExpr string) {
|
||||||
|
sname := fmt.Sprintf("/%s/%s", fullServName, method.GetName())
|
||||||
|
methName := generator.CamelCase(method.GetName())
|
||||||
|
inType := g.typeName(method.GetInputType())
|
||||||
|
outType := g.typeName(method.GetOutputType())
|
||||||
|
|
||||||
|
if method.GetOptions().GetDeprecated() {
|
||||||
|
g.P(deprecationComment)
|
||||||
|
}
|
||||||
|
g.P("func (c *", unexport(servName), "Client) ", g.generateClientSignature(servName, method), "{")
|
||||||
|
if !method.GetServerStreaming() && !method.GetClientStreaming() {
|
||||||
|
g.P("out := new(", outType, ")")
|
||||||
|
// TODO: Pass descExpr to Invoke.
|
||||||
|
g.P(`err := c.cc.Invoke(ctx, "`, sname, `", in, out, opts...)`)
|
||||||
|
g.P("if err != nil { return nil, err }")
|
||||||
|
g.P("return out, nil")
|
||||||
|
g.P("}")
|
||||||
|
g.P()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
streamType := unexport(servName) + methName + "Client"
|
||||||
|
g.P("stream, err := c.cc.NewStream(ctx, ", descExpr, `, "`, sname, `", opts...)`)
|
||||||
|
g.P("if err != nil { return nil, err }")
|
||||||
|
g.P("x := &", streamType, "{stream}")
|
||||||
|
if !method.GetClientStreaming() {
|
||||||
|
g.P("if err := x.ClientStream.SendMsg(in); err != nil { return nil, err }")
|
||||||
|
g.P("if err := x.ClientStream.CloseSend(); err != nil { return nil, err }")
|
||||||
|
}
|
||||||
|
g.P("return x, nil")
|
||||||
|
g.P("}")
|
||||||
|
g.P()
|
||||||
|
|
||||||
|
genSend := method.GetClientStreaming()
|
||||||
|
genRecv := method.GetServerStreaming()
|
||||||
|
genCloseAndRecv := !method.GetServerStreaming()
|
||||||
|
|
||||||
|
// Stream auxiliary types and methods.
|
||||||
|
g.P("type ", servName, "_", methName, "Client interface {")
|
||||||
|
if genSend {
|
||||||
|
g.P("Send(*", inType, ") error")
|
||||||
|
}
|
||||||
|
if genRecv {
|
||||||
|
g.P("Recv() (*", outType, ", error)")
|
||||||
|
}
|
||||||
|
if genCloseAndRecv {
|
||||||
|
g.P("CloseAndRecv() (*", outType, ", error)")
|
||||||
|
}
|
||||||
|
g.P(grpcPkg, ".ClientStream")
|
||||||
|
g.P("}")
|
||||||
|
g.P()
|
||||||
|
|
||||||
|
g.P("type ", streamType, " struct {")
|
||||||
|
g.P(grpcPkg, ".ClientStream")
|
||||||
|
g.P("}")
|
||||||
|
g.P()
|
||||||
|
|
||||||
|
if genSend {
|
||||||
|
g.P("func (x *", streamType, ") Send(m *", inType, ") error {")
|
||||||
|
g.P("return x.ClientStream.SendMsg(m)")
|
||||||
|
g.P("}")
|
||||||
|
g.P()
|
||||||
|
}
|
||||||
|
if genRecv {
|
||||||
|
g.P("func (x *", streamType, ") Recv() (*", outType, ", error) {")
|
||||||
|
g.P("m := new(", outType, ")")
|
||||||
|
g.P("if err := x.ClientStream.RecvMsg(m); err != nil { return nil, err }")
|
||||||
|
g.P("return m, nil")
|
||||||
|
g.P("}")
|
||||||
|
g.P()
|
||||||
|
}
|
||||||
|
if genCloseAndRecv {
|
||||||
|
g.P("func (x *", streamType, ") CloseAndRecv() (*", outType, ", error) {")
|
||||||
|
g.P("if err := x.ClientStream.CloseSend(); err != nil { return nil, err }")
|
||||||
|
g.P("m := new(", outType, ")")
|
||||||
|
g.P("if err := x.ClientStream.RecvMsg(m); err != nil { return nil, err }")
|
||||||
|
g.P("return m, nil")
|
||||||
|
g.P("}")
|
||||||
|
g.P()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// generateServerSignatureWithParamNames returns the server-side signature for a method with parameter names.
|
||||||
|
func (g *grpc) generateServerSignatureWithParamNames(servName string, method *pb.MethodDescriptorProto) string {
|
||||||
|
origMethName := method.GetName()
|
||||||
|
methName := generator.CamelCase(origMethName)
|
||||||
|
if reservedClientName[methName] {
|
||||||
|
methName += "_"
|
||||||
|
}
|
||||||
|
|
||||||
|
var reqArgs []string
|
||||||
|
ret := "error"
|
||||||
|
if !method.GetServerStreaming() && !method.GetClientStreaming() {
|
||||||
|
reqArgs = append(reqArgs, "ctx "+contextPkg+".Context")
|
||||||
|
ret = "(*" + g.typeName(method.GetOutputType()) + ", error)"
|
||||||
|
}
|
||||||
|
if !method.GetClientStreaming() {
|
||||||
|
reqArgs = append(reqArgs, "req *"+g.typeName(method.GetInputType()))
|
||||||
|
}
|
||||||
|
if method.GetServerStreaming() || method.GetClientStreaming() {
|
||||||
|
reqArgs = append(reqArgs, "srv "+servName+"_"+generator.CamelCase(origMethName)+"Server")
|
||||||
|
}
|
||||||
|
|
||||||
|
return methName + "(" + strings.Join(reqArgs, ", ") + ") " + ret
|
||||||
|
}
|
||||||
|
|
||||||
|
// generateServerSignature returns the server-side signature for a method.
|
||||||
|
func (g *grpc) generateServerSignature(servName string, method *pb.MethodDescriptorProto) string {
|
||||||
|
origMethName := method.GetName()
|
||||||
|
methName := generator.CamelCase(origMethName)
|
||||||
|
if reservedClientName[methName] {
|
||||||
|
methName += "_"
|
||||||
|
}
|
||||||
|
|
||||||
|
var reqArgs []string
|
||||||
|
ret := "error"
|
||||||
|
if !method.GetServerStreaming() && !method.GetClientStreaming() {
|
||||||
|
reqArgs = append(reqArgs, contextPkg+".Context")
|
||||||
|
ret = "(*" + g.typeName(method.GetOutputType()) + ", error)"
|
||||||
|
}
|
||||||
|
if !method.GetClientStreaming() {
|
||||||
|
reqArgs = append(reqArgs, "*"+g.typeName(method.GetInputType()))
|
||||||
|
}
|
||||||
|
if method.GetServerStreaming() || method.GetClientStreaming() {
|
||||||
|
reqArgs = append(reqArgs, servName+"_"+generator.CamelCase(origMethName)+"Server")
|
||||||
|
}
|
||||||
|
|
||||||
|
return methName + "(" + strings.Join(reqArgs, ", ") + ") " + ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *grpc) generateServerMethod(servName, fullServName string, method *pb.MethodDescriptorProto) string {
|
||||||
|
methName := generator.CamelCase(method.GetName())
|
||||||
|
hname := fmt.Sprintf("_%s_%s_Handler", servName, methName)
|
||||||
|
inType := g.typeName(method.GetInputType())
|
||||||
|
outType := g.typeName(method.GetOutputType())
|
||||||
|
|
||||||
|
if !method.GetServerStreaming() && !method.GetClientStreaming() {
|
||||||
|
g.P("func ", hname, "(srv interface{}, ctx ", contextPkg, ".Context, dec func(interface{}) error, interceptor ", grpcPkg, ".UnaryServerInterceptor) (interface{}, error) {")
|
||||||
|
g.P("in := new(", inType, ")")
|
||||||
|
g.P("if err := dec(in); err != nil { return nil, err }")
|
||||||
|
g.P("if interceptor == nil { return srv.(", servName, "Server).", methName, "(ctx, in) }")
|
||||||
|
g.P("info := &", grpcPkg, ".UnaryServerInfo{")
|
||||||
|
g.P("Server: srv,")
|
||||||
|
g.P("FullMethod: ", strconv.Quote(fmt.Sprintf("/%s/%s", fullServName, methName)), ",")
|
||||||
|
g.P("}")
|
||||||
|
g.P("handler := func(ctx ", contextPkg, ".Context, req interface{}) (interface{}, error) {")
|
||||||
|
g.P("return srv.(", servName, "Server).", methName, "(ctx, req.(*", inType, "))")
|
||||||
|
g.P("}")
|
||||||
|
g.P("return interceptor(ctx, in, info, handler)")
|
||||||
|
g.P("}")
|
||||||
|
g.P()
|
||||||
|
return hname
|
||||||
|
}
|
||||||
|
streamType := unexport(servName) + methName + "Server"
|
||||||
|
g.P("func ", hname, "(srv interface{}, stream ", grpcPkg, ".ServerStream) error {")
|
||||||
|
if !method.GetClientStreaming() {
|
||||||
|
g.P("m := new(", inType, ")")
|
||||||
|
g.P("if err := stream.RecvMsg(m); err != nil { return err }")
|
||||||
|
g.P("return srv.(", servName, "Server).", methName, "(m, &", streamType, "{stream})")
|
||||||
|
} else {
|
||||||
|
g.P("return srv.(", servName, "Server).", methName, "(&", streamType, "{stream})")
|
||||||
|
}
|
||||||
|
g.P("}")
|
||||||
|
g.P()
|
||||||
|
|
||||||
|
genSend := method.GetServerStreaming()
|
||||||
|
genSendAndClose := !method.GetServerStreaming()
|
||||||
|
genRecv := method.GetClientStreaming()
|
||||||
|
|
||||||
|
// Stream auxiliary types and methods.
|
||||||
|
g.P("type ", servName, "_", methName, "Server interface {")
|
||||||
|
if genSend {
|
||||||
|
g.P("Send(*", outType, ") error")
|
||||||
|
}
|
||||||
|
if genSendAndClose {
|
||||||
|
g.P("SendAndClose(*", outType, ") error")
|
||||||
|
}
|
||||||
|
if genRecv {
|
||||||
|
g.P("Recv() (*", inType, ", error)")
|
||||||
|
}
|
||||||
|
g.P(grpcPkg, ".ServerStream")
|
||||||
|
g.P("}")
|
||||||
|
g.P()
|
||||||
|
|
||||||
|
g.P("type ", streamType, " struct {")
|
||||||
|
g.P(grpcPkg, ".ServerStream")
|
||||||
|
g.P("}")
|
||||||
|
g.P()
|
||||||
|
|
||||||
|
if genSend {
|
||||||
|
g.P("func (x *", streamType, ") Send(m *", outType, ") error {")
|
||||||
|
g.P("return x.ServerStream.SendMsg(m)")
|
||||||
|
g.P("}")
|
||||||
|
g.P()
|
||||||
|
}
|
||||||
|
if genSendAndClose {
|
||||||
|
g.P("func (x *", streamType, ") SendAndClose(m *", outType, ") error {")
|
||||||
|
g.P("return x.ServerStream.SendMsg(m)")
|
||||||
|
g.P("}")
|
||||||
|
g.P()
|
||||||
|
}
|
||||||
|
if genRecv {
|
||||||
|
g.P("func (x *", streamType, ") Recv() (*", inType, ", error) {")
|
||||||
|
g.P("m := new(", inType, ")")
|
||||||
|
g.P("if err := x.ServerStream.RecvMsg(m); err != nil { return nil, err }")
|
||||||
|
g.P("return m, nil")
|
||||||
|
g.P("}")
|
||||||
|
g.P()
|
||||||
|
}
|
||||||
|
|
||||||
|
return hname
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
// Go support for Protocol Buffers - Google's data interchange format
|
||||||
|
//
|
||||||
|
// Copyright 2010 The Go Authors. All rights reserved.
|
||||||
|
// https://github.com/golang/protobuf
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
// protoc-gen-go is a plugin for the Google protocol buffer compiler to generate
|
||||||
|
// Go code. Run it by building this program and putting it in your path with
|
||||||
|
// the name
|
||||||
|
// protoc-gen-gogo
|
||||||
|
// That word 'gogo' at the end becomes part of the option string set for the
|
||||||
|
// protocol compiler, so once the protocol compiler (protoc) is installed
|
||||||
|
// you can run
|
||||||
|
// protoc --gogo_out=output_directory input_directory/file.proto
|
||||||
|
// to generate Go bindings for the protocol defined by file.proto.
|
||||||
|
// With that input, the output will be written to
|
||||||
|
// output_directory/file.pb.go
|
||||||
|
//
|
||||||
|
// The generated code is documented in the package comment for
|
||||||
|
// the library.
|
||||||
|
//
|
||||||
|
// See the README and documentation for protocol buffers to learn more:
|
||||||
|
// https://developers.google.com/protocol-buffers/
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogo/protobuf/vanity/command"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
command.Write(command.Generate(command.Read()))
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
# Go support for Protocol Buffers - Google's data interchange format
|
||||||
|
#
|
||||||
|
# Copyright 2010 The Go Authors. All rights reserved.
|
||||||
|
# https://github.com/golang/protobuf
|
||||||
|
#
|
||||||
|
# Redistribution and use in source and binary forms, with or without
|
||||||
|
# modification, are permitted provided that the following conditions are
|
||||||
|
# met:
|
||||||
|
#
|
||||||
|
# * Redistributions of source code must retain the above copyright
|
||||||
|
# notice, this list of conditions and the following disclaimer.
|
||||||
|
# * Redistributions in binary form must reproduce the above
|
||||||
|
# copyright notice, this list of conditions and the following disclaimer
|
||||||
|
# in the documentation and/or other materials provided with the
|
||||||
|
# distribution.
|
||||||
|
# * Neither the name of Google Inc. nor the names of its
|
||||||
|
# contributors may be used to endorse or promote products derived from
|
||||||
|
# this software without specific prior written permission.
|
||||||
|
#
|
||||||
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
# Not stored here, but plugin.proto is in https://github.com/google/protobuf/
|
||||||
|
# at src/google/protobuf/compiler/plugin.proto
|
||||||
|
# Also we need to fix an import.
|
||||||
|
regenerate:
|
||||||
|
go install github.com/gogo/protobuf/protoc-gen-gogo
|
||||||
|
protoc --gogo_out=Mgoogle/protobuf/descriptor.proto=github.com/gogo/protobuf/protoc-gen-gogo/descriptor:. -I=../../protobuf/google/protobuf/compiler/:../../protobuf/ ../../protobuf/google/protobuf/compiler/plugin.proto
|
365
vendor/github.com/gogo/protobuf/protoc-gen-gogo/plugin/plugin.pb.go
generated
vendored
Normal file
365
vendor/github.com/gogo/protobuf/protoc-gen-gogo/plugin/plugin.pb.go
generated
vendored
Normal file
|
@ -0,0 +1,365 @@
|
||||||
|
// Code generated by protoc-gen-gogo. DO NOT EDIT.
|
||||||
|
// source: plugin.proto
|
||||||
|
|
||||||
|
package plugin_go
|
||||||
|
|
||||||
|
import (
|
||||||
|
fmt "fmt"
|
||||||
|
proto "github.com/gogo/protobuf/proto"
|
||||||
|
descriptor "github.com/gogo/protobuf/protoc-gen-gogo/descriptor"
|
||||||
|
math "math"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
|
var _ = proto.Marshal
|
||||||
|
var _ = fmt.Errorf
|
||||||
|
var _ = math.Inf
|
||||||
|
|
||||||
|
// This is a compile-time assertion to ensure that this generated file
|
||||||
|
// is compatible with the proto package it is being compiled against.
|
||||||
|
// A compilation error at this line likely means your copy of the
|
||||||
|
// proto package needs to be updated.
|
||||||
|
const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
|
||||||
|
|
||||||
|
// The version number of protocol compiler.
|
||||||
|
type Version struct {
|
||||||
|
Major *int32 `protobuf:"varint,1,opt,name=major" json:"major,omitempty"`
|
||||||
|
Minor *int32 `protobuf:"varint,2,opt,name=minor" json:"minor,omitempty"`
|
||||||
|
Patch *int32 `protobuf:"varint,3,opt,name=patch" json:"patch,omitempty"`
|
||||||
|
// A suffix for alpha, beta or rc release, e.g., "alpha-1", "rc2". It should
|
||||||
|
// be empty for mainline stable releases.
|
||||||
|
Suffix *string `protobuf:"bytes,4,opt,name=suffix" json:"suffix,omitempty"`
|
||||||
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
XXX_sizecache int32 `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Version) Reset() { *m = Version{} }
|
||||||
|
func (m *Version) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*Version) ProtoMessage() {}
|
||||||
|
func (*Version) Descriptor() ([]byte, []int) {
|
||||||
|
return fileDescriptor_22a625af4bc1cc87, []int{0}
|
||||||
|
}
|
||||||
|
func (m *Version) XXX_Unmarshal(b []byte) error {
|
||||||
|
return xxx_messageInfo_Version.Unmarshal(m, b)
|
||||||
|
}
|
||||||
|
func (m *Version) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
|
return xxx_messageInfo_Version.Marshal(b, m, deterministic)
|
||||||
|
}
|
||||||
|
func (m *Version) XXX_Merge(src proto.Message) {
|
||||||
|
xxx_messageInfo_Version.Merge(m, src)
|
||||||
|
}
|
||||||
|
func (m *Version) XXX_Size() int {
|
||||||
|
return xxx_messageInfo_Version.Size(m)
|
||||||
|
}
|
||||||
|
func (m *Version) XXX_DiscardUnknown() {
|
||||||
|
xxx_messageInfo_Version.DiscardUnknown(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
var xxx_messageInfo_Version proto.InternalMessageInfo
|
||||||
|
|
||||||
|
func (m *Version) GetMajor() int32 {
|
||||||
|
if m != nil && m.Major != nil {
|
||||||
|
return *m.Major
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Version) GetMinor() int32 {
|
||||||
|
if m != nil && m.Minor != nil {
|
||||||
|
return *m.Minor
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Version) GetPatch() int32 {
|
||||||
|
if m != nil && m.Patch != nil {
|
||||||
|
return *m.Patch
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Version) GetSuffix() string {
|
||||||
|
if m != nil && m.Suffix != nil {
|
||||||
|
return *m.Suffix
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// An encoded CodeGeneratorRequest is written to the plugin's stdin.
|
||||||
|
type CodeGeneratorRequest struct {
|
||||||
|
// The .proto files that were explicitly listed on the command-line. The
|
||||||
|
// code generator should generate code only for these files. Each file's
|
||||||
|
// descriptor will be included in proto_file, below.
|
||||||
|
FileToGenerate []string `protobuf:"bytes,1,rep,name=file_to_generate,json=fileToGenerate" json:"file_to_generate,omitempty"`
|
||||||
|
// The generator parameter passed on the command-line.
|
||||||
|
Parameter *string `protobuf:"bytes,2,opt,name=parameter" json:"parameter,omitempty"`
|
||||||
|
// FileDescriptorProtos for all files in files_to_generate and everything
|
||||||
|
// they import. The files will appear in topological order, so each file
|
||||||
|
// appears before any file that imports it.
|
||||||
|
//
|
||||||
|
// protoc guarantees that all proto_files will be written after
|
||||||
|
// the fields above, even though this is not technically guaranteed by the
|
||||||
|
// protobuf wire format. This theoretically could allow a plugin to stream
|
||||||
|
// in the FileDescriptorProtos and handle them one by one rather than read
|
||||||
|
// the entire set into memory at once. However, as of this writing, this
|
||||||
|
// is not similarly optimized on protoc's end -- it will store all fields in
|
||||||
|
// memory at once before sending them to the plugin.
|
||||||
|
//
|
||||||
|
// Type names of fields and extensions in the FileDescriptorProto are always
|
||||||
|
// fully qualified.
|
||||||
|
ProtoFile []*descriptor.FileDescriptorProto `protobuf:"bytes,15,rep,name=proto_file,json=protoFile" json:"proto_file,omitempty"`
|
||||||
|
// The version number of protocol compiler.
|
||||||
|
CompilerVersion *Version `protobuf:"bytes,3,opt,name=compiler_version,json=compilerVersion" json:"compiler_version,omitempty"`
|
||||||
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
XXX_sizecache int32 `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *CodeGeneratorRequest) Reset() { *m = CodeGeneratorRequest{} }
|
||||||
|
func (m *CodeGeneratorRequest) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*CodeGeneratorRequest) ProtoMessage() {}
|
||||||
|
func (*CodeGeneratorRequest) Descriptor() ([]byte, []int) {
|
||||||
|
return fileDescriptor_22a625af4bc1cc87, []int{1}
|
||||||
|
}
|
||||||
|
func (m *CodeGeneratorRequest) XXX_Unmarshal(b []byte) error {
|
||||||
|
return xxx_messageInfo_CodeGeneratorRequest.Unmarshal(m, b)
|
||||||
|
}
|
||||||
|
func (m *CodeGeneratorRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
|
return xxx_messageInfo_CodeGeneratorRequest.Marshal(b, m, deterministic)
|
||||||
|
}
|
||||||
|
func (m *CodeGeneratorRequest) XXX_Merge(src proto.Message) {
|
||||||
|
xxx_messageInfo_CodeGeneratorRequest.Merge(m, src)
|
||||||
|
}
|
||||||
|
func (m *CodeGeneratorRequest) XXX_Size() int {
|
||||||
|
return xxx_messageInfo_CodeGeneratorRequest.Size(m)
|
||||||
|
}
|
||||||
|
func (m *CodeGeneratorRequest) XXX_DiscardUnknown() {
|
||||||
|
xxx_messageInfo_CodeGeneratorRequest.DiscardUnknown(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
var xxx_messageInfo_CodeGeneratorRequest proto.InternalMessageInfo
|
||||||
|
|
||||||
|
func (m *CodeGeneratorRequest) GetFileToGenerate() []string {
|
||||||
|
if m != nil {
|
||||||
|
return m.FileToGenerate
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *CodeGeneratorRequest) GetParameter() string {
|
||||||
|
if m != nil && m.Parameter != nil {
|
||||||
|
return *m.Parameter
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *CodeGeneratorRequest) GetProtoFile() []*descriptor.FileDescriptorProto {
|
||||||
|
if m != nil {
|
||||||
|
return m.ProtoFile
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *CodeGeneratorRequest) GetCompilerVersion() *Version {
|
||||||
|
if m != nil {
|
||||||
|
return m.CompilerVersion
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// The plugin writes an encoded CodeGeneratorResponse to stdout.
|
||||||
|
type CodeGeneratorResponse struct {
|
||||||
|
// Error message. If non-empty, code generation failed. The plugin process
|
||||||
|
// should exit with status code zero even if it reports an error in this way.
|
||||||
|
//
|
||||||
|
// This should be used to indicate errors in .proto files which prevent the
|
||||||
|
// code generator from generating correct code. Errors which indicate a
|
||||||
|
// problem in protoc itself -- such as the input CodeGeneratorRequest being
|
||||||
|
// unparseable -- should be reported by writing a message to stderr and
|
||||||
|
// exiting with a non-zero status code.
|
||||||
|
Error *string `protobuf:"bytes,1,opt,name=error" json:"error,omitempty"`
|
||||||
|
File []*CodeGeneratorResponse_File `protobuf:"bytes,15,rep,name=file" json:"file,omitempty"`
|
||||||
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
XXX_sizecache int32 `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *CodeGeneratorResponse) Reset() { *m = CodeGeneratorResponse{} }
|
||||||
|
func (m *CodeGeneratorResponse) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*CodeGeneratorResponse) ProtoMessage() {}
|
||||||
|
func (*CodeGeneratorResponse) Descriptor() ([]byte, []int) {
|
||||||
|
return fileDescriptor_22a625af4bc1cc87, []int{2}
|
||||||
|
}
|
||||||
|
func (m *CodeGeneratorResponse) XXX_Unmarshal(b []byte) error {
|
||||||
|
return xxx_messageInfo_CodeGeneratorResponse.Unmarshal(m, b)
|
||||||
|
}
|
||||||
|
func (m *CodeGeneratorResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
|
return xxx_messageInfo_CodeGeneratorResponse.Marshal(b, m, deterministic)
|
||||||
|
}
|
||||||
|
func (m *CodeGeneratorResponse) XXX_Merge(src proto.Message) {
|
||||||
|
xxx_messageInfo_CodeGeneratorResponse.Merge(m, src)
|
||||||
|
}
|
||||||
|
func (m *CodeGeneratorResponse) XXX_Size() int {
|
||||||
|
return xxx_messageInfo_CodeGeneratorResponse.Size(m)
|
||||||
|
}
|
||||||
|
func (m *CodeGeneratorResponse) XXX_DiscardUnknown() {
|
||||||
|
xxx_messageInfo_CodeGeneratorResponse.DiscardUnknown(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
var xxx_messageInfo_CodeGeneratorResponse proto.InternalMessageInfo
|
||||||
|
|
||||||
|
func (m *CodeGeneratorResponse) GetError() string {
|
||||||
|
if m != nil && m.Error != nil {
|
||||||
|
return *m.Error
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *CodeGeneratorResponse) GetFile() []*CodeGeneratorResponse_File {
|
||||||
|
if m != nil {
|
||||||
|
return m.File
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Represents a single generated file.
|
||||||
|
type CodeGeneratorResponse_File struct {
|
||||||
|
// The file name, relative to the output directory. The name must not
|
||||||
|
// contain "." or ".." components and must be relative, not be absolute (so,
|
||||||
|
// the file cannot lie outside the output directory). "/" must be used as
|
||||||
|
// the path separator, not "\".
|
||||||
|
//
|
||||||
|
// If the name is omitted, the content will be appended to the previous
|
||||||
|
// file. This allows the generator to break large files into small chunks,
|
||||||
|
// and allows the generated text to be streamed back to protoc so that large
|
||||||
|
// files need not reside completely in memory at one time. Note that as of
|
||||||
|
// this writing protoc does not optimize for this -- it will read the entire
|
||||||
|
// CodeGeneratorResponse before writing files to disk.
|
||||||
|
Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
|
||||||
|
// If non-empty, indicates that the named file should already exist, and the
|
||||||
|
// content here is to be inserted into that file at a defined insertion
|
||||||
|
// point. This feature allows a code generator to extend the output
|
||||||
|
// produced by another code generator. The original generator may provide
|
||||||
|
// insertion points by placing special annotations in the file that look
|
||||||
|
// like:
|
||||||
|
// @@protoc_insertion_point(NAME)
|
||||||
|
// The annotation can have arbitrary text before and after it on the line,
|
||||||
|
// which allows it to be placed in a comment. NAME should be replaced with
|
||||||
|
// an identifier naming the point -- this is what other generators will use
|
||||||
|
// as the insertion_point. Code inserted at this point will be placed
|
||||||
|
// immediately above the line containing the insertion point (thus multiple
|
||||||
|
// insertions to the same point will come out in the order they were added).
|
||||||
|
// The double-@ is intended to make it unlikely that the generated code
|
||||||
|
// could contain things that look like insertion points by accident.
|
||||||
|
//
|
||||||
|
// For example, the C++ code generator places the following line in the
|
||||||
|
// .pb.h files that it generates:
|
||||||
|
// // @@protoc_insertion_point(namespace_scope)
|
||||||
|
// This line appears within the scope of the file's package namespace, but
|
||||||
|
// outside of any particular class. Another plugin can then specify the
|
||||||
|
// insertion_point "namespace_scope" to generate additional classes or
|
||||||
|
// other declarations that should be placed in this scope.
|
||||||
|
//
|
||||||
|
// Note that if the line containing the insertion point begins with
|
||||||
|
// whitespace, the same whitespace will be added to every line of the
|
||||||
|
// inserted text. This is useful for languages like Python, where
|
||||||
|
// indentation matters. In these languages, the insertion point comment
|
||||||
|
// should be indented the same amount as any inserted code will need to be
|
||||||
|
// in order to work correctly in that context.
|
||||||
|
//
|
||||||
|
// The code generator that generates the initial file and the one which
|
||||||
|
// inserts into it must both run as part of a single invocation of protoc.
|
||||||
|
// Code generators are executed in the order in which they appear on the
|
||||||
|
// command line.
|
||||||
|
//
|
||||||
|
// If |insertion_point| is present, |name| must also be present.
|
||||||
|
InsertionPoint *string `protobuf:"bytes,2,opt,name=insertion_point,json=insertionPoint" json:"insertion_point,omitempty"`
|
||||||
|
// The file contents.
|
||||||
|
Content *string `protobuf:"bytes,15,opt,name=content" json:"content,omitempty"`
|
||||||
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
XXX_sizecache int32 `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *CodeGeneratorResponse_File) Reset() { *m = CodeGeneratorResponse_File{} }
|
||||||
|
func (m *CodeGeneratorResponse_File) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*CodeGeneratorResponse_File) ProtoMessage() {}
|
||||||
|
func (*CodeGeneratorResponse_File) Descriptor() ([]byte, []int) {
|
||||||
|
return fileDescriptor_22a625af4bc1cc87, []int{2, 0}
|
||||||
|
}
|
||||||
|
func (m *CodeGeneratorResponse_File) XXX_Unmarshal(b []byte) error {
|
||||||
|
return xxx_messageInfo_CodeGeneratorResponse_File.Unmarshal(m, b)
|
||||||
|
}
|
||||||
|
func (m *CodeGeneratorResponse_File) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
|
return xxx_messageInfo_CodeGeneratorResponse_File.Marshal(b, m, deterministic)
|
||||||
|
}
|
||||||
|
func (m *CodeGeneratorResponse_File) XXX_Merge(src proto.Message) {
|
||||||
|
xxx_messageInfo_CodeGeneratorResponse_File.Merge(m, src)
|
||||||
|
}
|
||||||
|
func (m *CodeGeneratorResponse_File) XXX_Size() int {
|
||||||
|
return xxx_messageInfo_CodeGeneratorResponse_File.Size(m)
|
||||||
|
}
|
||||||
|
func (m *CodeGeneratorResponse_File) XXX_DiscardUnknown() {
|
||||||
|
xxx_messageInfo_CodeGeneratorResponse_File.DiscardUnknown(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
var xxx_messageInfo_CodeGeneratorResponse_File proto.InternalMessageInfo
|
||||||
|
|
||||||
|
func (m *CodeGeneratorResponse_File) GetName() string {
|
||||||
|
if m != nil && m.Name != nil {
|
||||||
|
return *m.Name
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *CodeGeneratorResponse_File) GetInsertionPoint() string {
|
||||||
|
if m != nil && m.InsertionPoint != nil {
|
||||||
|
return *m.InsertionPoint
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *CodeGeneratorResponse_File) GetContent() string {
|
||||||
|
if m != nil && m.Content != nil {
|
||||||
|
return *m.Content
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
proto.RegisterType((*Version)(nil), "google.protobuf.compiler.Version")
|
||||||
|
proto.RegisterType((*CodeGeneratorRequest)(nil), "google.protobuf.compiler.CodeGeneratorRequest")
|
||||||
|
proto.RegisterType((*CodeGeneratorResponse)(nil), "google.protobuf.compiler.CodeGeneratorResponse")
|
||||||
|
proto.RegisterType((*CodeGeneratorResponse_File)(nil), "google.protobuf.compiler.CodeGeneratorResponse.File")
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { proto.RegisterFile("plugin.proto", fileDescriptor_22a625af4bc1cc87) }
|
||||||
|
|
||||||
|
var fileDescriptor_22a625af4bc1cc87 = []byte{
|
||||||
|
// 383 bytes of a gzipped FileDescriptorProto
|
||||||
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x92, 0xcd, 0x6a, 0xd5, 0x40,
|
||||||
|
0x14, 0xc7, 0x89, 0x37, 0xb5, 0xe4, 0xb4, 0x34, 0x65, 0xa8, 0x32, 0x94, 0x2e, 0xe2, 0x45, 0x30,
|
||||||
|
0xab, 0x14, 0x8a, 0xe0, 0xbe, 0x15, 0x75, 0xe1, 0xe2, 0x32, 0x88, 0x0b, 0x41, 0x42, 0x4c, 0x4f,
|
||||||
|
0xe2, 0x48, 0x32, 0x67, 0x9c, 0x99, 0x88, 0x4f, 0xea, 0x7b, 0xf8, 0x06, 0x32, 0x1f, 0xa9, 0x72,
|
||||||
|
0xf1, 0xee, 0xe6, 0xff, 0x3b, 0xf3, 0x71, 0xce, 0x8f, 0x81, 0x53, 0x3d, 0x2d, 0xa3, 0x54, 0x8d,
|
||||||
|
0x36, 0xe4, 0x88, 0xf1, 0x91, 0x68, 0x9c, 0x30, 0xa6, 0x2f, 0xcb, 0xd0, 0xf4, 0x34, 0x6b, 0x39,
|
||||||
|
0xa1, 0xb9, 0xac, 0x62, 0xe5, 0x7a, 0xad, 0x5c, 0xdf, 0xa3, 0xed, 0x8d, 0xd4, 0x8e, 0x4c, 0xdc,
|
||||||
|
0xbd, 0xed, 0xe1, 0xf8, 0x23, 0x1a, 0x2b, 0x49, 0xb1, 0x0b, 0x38, 0x9a, 0xbb, 0x6f, 0x64, 0x78,
|
||||||
|
0x56, 0x65, 0xf5, 0x91, 0x88, 0x21, 0x50, 0xa9, 0xc8, 0xf0, 0x47, 0x89, 0xfa, 0xe0, 0xa9, 0xee,
|
||||||
|
0x5c, 0xff, 0x95, 0x6f, 0x22, 0x0d, 0x81, 0x3d, 0x85, 0xc7, 0x76, 0x19, 0x06, 0xf9, 0x93, 0xe7,
|
||||||
|
0x55, 0x56, 0x17, 0x22, 0xa5, 0xed, 0xef, 0x0c, 0x2e, 0xee, 0xe8, 0x1e, 0xdf, 0xa2, 0x42, 0xd3,
|
||||||
|
0x39, 0x32, 0x02, 0xbf, 0x2f, 0x68, 0x1d, 0xab, 0xe1, 0x7c, 0x90, 0x13, 0xb6, 0x8e, 0xda, 0x31,
|
||||||
|
0xd6, 0x90, 0x67, 0xd5, 0xa6, 0x2e, 0xc4, 0x99, 0xe7, 0x1f, 0x28, 0x9d, 0x40, 0x76, 0x05, 0x85,
|
||||||
|
0xee, 0x4c, 0x37, 0xa3, 0xc3, 0xd8, 0x4a, 0x21, 0xfe, 0x02, 0x76, 0x07, 0x10, 0xc6, 0x69, 0xfd,
|
||||||
|
0x29, 0x5e, 0x56, 0x9b, 0xfa, 0xe4, 0xe6, 0x79, 0xb3, 0xaf, 0xe5, 0x8d, 0x9c, 0xf0, 0xf5, 0x83,
|
||||||
|
0x80, 0x9d, 0xc7, 0xa2, 0x08, 0x55, 0x5f, 0x61, 0xef, 0xe1, 0x7c, 0x15, 0xd7, 0xfe, 0x88, 0x4e,
|
||||||
|
0xc2, 0x78, 0x27, 0x37, 0xcf, 0x9a, 0x43, 0x86, 0x9b, 0x24, 0x4f, 0x94, 0x2b, 0x49, 0x60, 0xfb,
|
||||||
|
0x2b, 0x83, 0x27, 0x7b, 0x33, 0x5b, 0x4d, 0xca, 0xa2, 0x77, 0x87, 0xc6, 0x24, 0xcf, 0x85, 0x88,
|
||||||
|
0x81, 0xbd, 0x83, 0xfc, 0x9f, 0xe6, 0x5f, 0x1e, 0x7e, 0xf1, 0xbf, 0x97, 0x86, 0xd9, 0x44, 0xb8,
|
||||||
|
0xe1, 0xf2, 0x33, 0xe4, 0x61, 0x1e, 0x06, 0xb9, 0xea, 0x66, 0x4c, 0xcf, 0x84, 0x35, 0x7b, 0x01,
|
||||||
|
0xa5, 0x54, 0x16, 0x8d, 0x93, 0xa4, 0x5a, 0x4d, 0x52, 0xb9, 0x24, 0xf3, 0xec, 0x01, 0xef, 0x3c,
|
||||||
|
0x65, 0x1c, 0x8e, 0x7b, 0x52, 0x0e, 0x95, 0xe3, 0x65, 0xd8, 0xb0, 0xc6, 0xdb, 0x57, 0x70, 0xd5,
|
||||||
|
0xd3, 0x7c, 0xb0, 0xbf, 0xdb, 0xd3, 0x5d, 0xf8, 0x9b, 0x41, 0xaf, 0xfd, 0x54, 0xc4, 0x9f, 0xda,
|
||||||
|
0x8e, 0xf4, 0x27, 0x00, 0x00, 0xff, 0xff, 0x7a, 0x72, 0x3d, 0x18, 0xb5, 0x02, 0x00, 0x00,
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
// Protocol Buffers for Go with Gadgets
|
||||||
|
//
|
||||||
|
// Copyright (c) 2015, The GoGo Authors. All rights reserved.
|
||||||
|
// http://github.com/gogo/protobuf
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogo/protobuf/vanity"
|
||||||
|
"github.com/gogo/protobuf/vanity/command"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
req := command.Read()
|
||||||
|
files := req.GetProtoFile()
|
||||||
|
files = vanity.FilterFiles(files, vanity.NotGoogleProtobufDescriptorProto)
|
||||||
|
|
||||||
|
vanity.ForEachFile(files, vanity.TurnOnMarshalerAll)
|
||||||
|
vanity.ForEachFile(files, vanity.TurnOnSizerAll)
|
||||||
|
vanity.ForEachFile(files, vanity.TurnOnUnmarshalerAll)
|
||||||
|
|
||||||
|
vanity.ForEachFieldInFilesExcludingExtensions(vanity.OnlyProto2(files), vanity.TurnOffNullableForNativeTypesWithoutDefaultsOnly)
|
||||||
|
vanity.ForEachFile(files, vanity.TurnOffGoUnrecognizedAll)
|
||||||
|
vanity.ForEachFile(files, vanity.TurnOffGoUnkeyedAll)
|
||||||
|
vanity.ForEachFile(files, vanity.TurnOffGoSizecacheAll)
|
||||||
|
|
||||||
|
resp := command.Generate(req)
|
||||||
|
command.Write(resp)
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
// Protocol Buffers for Go with Gadgets
|
||||||
|
//
|
||||||
|
// Copyright (c) 2015, The GoGo Authors. All rights reserved.
|
||||||
|
// http://github.com/gogo/protobuf
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogo/protobuf/vanity"
|
||||||
|
"github.com/gogo/protobuf/vanity/command"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
req := command.Read()
|
||||||
|
files := req.GetProtoFile()
|
||||||
|
files = vanity.FilterFiles(files, vanity.NotGoogleProtobufDescriptorProto)
|
||||||
|
|
||||||
|
vanity.ForEachFile(files, vanity.TurnOnMarshalerAll)
|
||||||
|
vanity.ForEachFile(files, vanity.TurnOnSizerAll)
|
||||||
|
vanity.ForEachFile(files, vanity.TurnOnUnmarshalerAll)
|
||||||
|
|
||||||
|
vanity.ForEachFieldInFilesExcludingExtensions(vanity.OnlyProto2(files), vanity.TurnOffNullableForNativeTypesWithoutDefaultsOnly)
|
||||||
|
vanity.ForEachFile(files, vanity.TurnOffGoUnrecognizedAll)
|
||||||
|
vanity.ForEachFile(files, vanity.TurnOffGoUnkeyedAll)
|
||||||
|
vanity.ForEachFile(files, vanity.TurnOffGoSizecacheAll)
|
||||||
|
|
||||||
|
vanity.ForEachFile(files, vanity.TurnOffGoEnumPrefixAll)
|
||||||
|
vanity.ForEachFile(files, vanity.TurnOffGoEnumStringerAll)
|
||||||
|
vanity.ForEachFile(files, vanity.TurnOnEnumStringerAll)
|
||||||
|
|
||||||
|
vanity.ForEachFile(files, vanity.TurnOnEqualAll)
|
||||||
|
vanity.ForEachFile(files, vanity.TurnOnGoStringAll)
|
||||||
|
vanity.ForEachFile(files, vanity.TurnOffGoStringerAll)
|
||||||
|
vanity.ForEachFile(files, vanity.TurnOnStringerAll)
|
||||||
|
|
||||||
|
resp := command.Generate(req)
|
||||||
|
command.Write(resp)
|
||||||
|
}
|
|
@ -0,0 +1,161 @@
|
||||||
|
// Protocol Buffers for Go with Gadgets
|
||||||
|
//
|
||||||
|
// Copyright (c) 2015, The GoGo Authors. All rights reserved.
|
||||||
|
// http://github.com/gogo/protobuf
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"go/format"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
_ "github.com/gogo/protobuf/plugin/compare"
|
||||||
|
_ "github.com/gogo/protobuf/plugin/defaultcheck"
|
||||||
|
_ "github.com/gogo/protobuf/plugin/description"
|
||||||
|
_ "github.com/gogo/protobuf/plugin/embedcheck"
|
||||||
|
_ "github.com/gogo/protobuf/plugin/enumstringer"
|
||||||
|
_ "github.com/gogo/protobuf/plugin/equal"
|
||||||
|
_ "github.com/gogo/protobuf/plugin/face"
|
||||||
|
_ "github.com/gogo/protobuf/plugin/gostring"
|
||||||
|
_ "github.com/gogo/protobuf/plugin/marshalto"
|
||||||
|
_ "github.com/gogo/protobuf/plugin/oneofcheck"
|
||||||
|
_ "github.com/gogo/protobuf/plugin/populate"
|
||||||
|
_ "github.com/gogo/protobuf/plugin/size"
|
||||||
|
_ "github.com/gogo/protobuf/plugin/stringer"
|
||||||
|
"github.com/gogo/protobuf/plugin/testgen"
|
||||||
|
_ "github.com/gogo/protobuf/plugin/union"
|
||||||
|
_ "github.com/gogo/protobuf/plugin/unmarshal"
|
||||||
|
"github.com/gogo/protobuf/proto"
|
||||||
|
"github.com/gogo/protobuf/protoc-gen-gogo/generator"
|
||||||
|
_ "github.com/gogo/protobuf/protoc-gen-gogo/grpc"
|
||||||
|
plugin "github.com/gogo/protobuf/protoc-gen-gogo/plugin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Read() *plugin.CodeGeneratorRequest {
|
||||||
|
g := generator.New()
|
||||||
|
data, err := ioutil.ReadAll(os.Stdin)
|
||||||
|
if err != nil {
|
||||||
|
g.Error(err, "reading input")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := proto.Unmarshal(data, g.Request); err != nil {
|
||||||
|
g.Error(err, "parsing input proto")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(g.Request.FileToGenerate) == 0 {
|
||||||
|
g.Fail("no files to generate")
|
||||||
|
}
|
||||||
|
return g.Request
|
||||||
|
}
|
||||||
|
|
||||||
|
// filenameSuffix replaces the .pb.go at the end of each filename.
|
||||||
|
func GeneratePlugin(req *plugin.CodeGeneratorRequest, p generator.Plugin, filenameSuffix string) *plugin.CodeGeneratorResponse {
|
||||||
|
g := generator.New()
|
||||||
|
g.Request = req
|
||||||
|
if len(g.Request.FileToGenerate) == 0 {
|
||||||
|
g.Fail("no files to generate")
|
||||||
|
}
|
||||||
|
|
||||||
|
g.CommandLineParameters(g.Request.GetParameter())
|
||||||
|
|
||||||
|
g.WrapTypes()
|
||||||
|
g.SetPackageNames()
|
||||||
|
g.BuildTypeNameMap()
|
||||||
|
g.GeneratePlugin(p)
|
||||||
|
|
||||||
|
for i := 0; i < len(g.Response.File); i++ {
|
||||||
|
g.Response.File[i].Name = proto.String(
|
||||||
|
strings.Replace(*g.Response.File[i].Name, ".pb.go", filenameSuffix, -1),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if err := goformat(g.Response); err != nil {
|
||||||
|
g.Error(err)
|
||||||
|
}
|
||||||
|
return g.Response
|
||||||
|
}
|
||||||
|
|
||||||
|
func goformat(resp *plugin.CodeGeneratorResponse) error {
|
||||||
|
for i := 0; i < len(resp.File); i++ {
|
||||||
|
formatted, err := format.Source([]byte(resp.File[i].GetContent()))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("go format error: %v", err)
|
||||||
|
}
|
||||||
|
fmts := string(formatted)
|
||||||
|
resp.File[i].Content = &fmts
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Generate(req *plugin.CodeGeneratorRequest) *plugin.CodeGeneratorResponse {
|
||||||
|
// Begin by allocating a generator. The request and response structures are stored there
|
||||||
|
// so we can do error handling easily - the response structure contains the field to
|
||||||
|
// report failure.
|
||||||
|
g := generator.New()
|
||||||
|
g.Request = req
|
||||||
|
|
||||||
|
g.CommandLineParameters(g.Request.GetParameter())
|
||||||
|
|
||||||
|
// Create a wrapped version of the Descriptors and EnumDescriptors that
|
||||||
|
// point to the file that defines them.
|
||||||
|
g.WrapTypes()
|
||||||
|
|
||||||
|
g.SetPackageNames()
|
||||||
|
g.BuildTypeNameMap()
|
||||||
|
|
||||||
|
g.GenerateAllFiles()
|
||||||
|
|
||||||
|
if err := goformat(g.Response); err != nil {
|
||||||
|
g.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
testReq := proto.Clone(req).(*plugin.CodeGeneratorRequest)
|
||||||
|
|
||||||
|
testResp := GeneratePlugin(testReq, testgen.NewPlugin(), "pb_test.go")
|
||||||
|
|
||||||
|
for i := 0; i < len(testResp.File); i++ {
|
||||||
|
if strings.Contains(*testResp.File[i].Content, `//These tests are generated by github.com/gogo/protobuf/plugin/testgen`) {
|
||||||
|
g.Response.File = append(g.Response.File, testResp.File[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return g.Response
|
||||||
|
}
|
||||||
|
|
||||||
|
func Write(resp *plugin.CodeGeneratorResponse) {
|
||||||
|
g := generator.New()
|
||||||
|
// Send back the results.
|
||||||
|
data, err := proto.Marshal(resp)
|
||||||
|
if err != nil {
|
||||||
|
g.Error(err, "failed to marshal output proto")
|
||||||
|
}
|
||||||
|
_, err = os.Stdout.Write(data)
|
||||||
|
if err != nil {
|
||||||
|
g.Error(err, "failed to write output proto")
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
// Protocol Buffers for Go with Gadgets
|
||||||
|
//
|
||||||
|
// Copyright (c) 2015, The GoGo Authors. rights reserved.
|
||||||
|
// http://github.com/gogo/protobuf
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
package vanity
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogo/protobuf/gogoproto"
|
||||||
|
"github.com/gogo/protobuf/proto"
|
||||||
|
descriptor "github.com/gogo/protobuf/protoc-gen-gogo/descriptor"
|
||||||
|
)
|
||||||
|
|
||||||
|
func EnumHasBoolExtension(enum *descriptor.EnumDescriptorProto, extension *proto.ExtensionDesc) bool {
|
||||||
|
if enum.Options == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
value, err := proto.GetExtension(enum.Options, extension)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if value == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if value.(*bool) == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetBoolEnumOption(extension *proto.ExtensionDesc, value bool) func(enum *descriptor.EnumDescriptorProto) {
|
||||||
|
return func(enum *descriptor.EnumDescriptorProto) {
|
||||||
|
if EnumHasBoolExtension(enum, extension) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if enum.Options == nil {
|
||||||
|
enum.Options = &descriptor.EnumOptions{}
|
||||||
|
}
|
||||||
|
if err := proto.SetExtension(enum.Options, extension, &value); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TurnOffGoEnumPrefix(enum *descriptor.EnumDescriptorProto) {
|
||||||
|
SetBoolEnumOption(gogoproto.E_GoprotoEnumPrefix, false)(enum)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TurnOffGoEnumStringer(enum *descriptor.EnumDescriptorProto) {
|
||||||
|
SetBoolEnumOption(gogoproto.E_GoprotoEnumStringer, false)(enum)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TurnOnEnumStringer(enum *descriptor.EnumDescriptorProto) {
|
||||||
|
SetBoolEnumOption(gogoproto.E_EnumStringer, true)(enum)
|
||||||
|
}
|
|
@ -0,0 +1,90 @@
|
||||||
|
// Protocol Buffers for Go with Gadgets
|
||||||
|
//
|
||||||
|
// Copyright (c) 2015, The GoGo Authors. rights reserved.
|
||||||
|
// http://github.com/gogo/protobuf
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
package vanity
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogo/protobuf/gogoproto"
|
||||||
|
"github.com/gogo/protobuf/proto"
|
||||||
|
descriptor "github.com/gogo/protobuf/protoc-gen-gogo/descriptor"
|
||||||
|
)
|
||||||
|
|
||||||
|
func FieldHasBoolExtension(field *descriptor.FieldDescriptorProto, extension *proto.ExtensionDesc) bool {
|
||||||
|
if field.Options == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
value, err := proto.GetExtension(field.Options, extension)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if value == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if value.(*bool) == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetBoolFieldOption(extension *proto.ExtensionDesc, value bool) func(field *descriptor.FieldDescriptorProto) {
|
||||||
|
return func(field *descriptor.FieldDescriptorProto) {
|
||||||
|
if FieldHasBoolExtension(field, extension) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if field.Options == nil {
|
||||||
|
field.Options = &descriptor.FieldOptions{}
|
||||||
|
}
|
||||||
|
if err := proto.SetExtension(field.Options, extension, &value); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TurnOffNullable(field *descriptor.FieldDescriptorProto) {
|
||||||
|
if field.IsRepeated() && !field.IsMessage() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
SetBoolFieldOption(gogoproto.E_Nullable, false)(field)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TurnOffNullableForNativeTypes(field *descriptor.FieldDescriptorProto) {
|
||||||
|
if field.IsRepeated() || field.IsMessage() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
SetBoolFieldOption(gogoproto.E_Nullable, false)(field)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TurnOffNullableForNativeTypesWithoutDefaultsOnly(field *descriptor.FieldDescriptorProto) {
|
||||||
|
if field.IsRepeated() || field.IsMessage() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if field.DefaultValue != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
SetBoolFieldOption(gogoproto.E_Nullable, false)(field)
|
||||||
|
}
|
|
@ -0,0 +1,197 @@
|
||||||
|
// Protocol Buffers for Go with Gadgets
|
||||||
|
//
|
||||||
|
// Copyright (c) 2015, The GoGo Authors. All rights reserved.
|
||||||
|
// http://github.com/gogo/protobuf
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
package vanity
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/gogo/protobuf/gogoproto"
|
||||||
|
"github.com/gogo/protobuf/proto"
|
||||||
|
descriptor "github.com/gogo/protobuf/protoc-gen-gogo/descriptor"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NotGoogleProtobufDescriptorProto(file *descriptor.FileDescriptorProto) bool {
|
||||||
|
// can not just check if file.GetName() == "google/protobuf/descriptor.proto" because we do not want to assume compile path
|
||||||
|
_, fileName := filepath.Split(file.GetName())
|
||||||
|
return !(file.GetPackage() == "google.protobuf" && fileName == "descriptor.proto")
|
||||||
|
}
|
||||||
|
|
||||||
|
func FilterFiles(files []*descriptor.FileDescriptorProto, f func(file *descriptor.FileDescriptorProto) bool) []*descriptor.FileDescriptorProto {
|
||||||
|
filtered := make([]*descriptor.FileDescriptorProto, 0, len(files))
|
||||||
|
for i := range files {
|
||||||
|
if !f(files[i]) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
filtered = append(filtered, files[i])
|
||||||
|
}
|
||||||
|
return filtered
|
||||||
|
}
|
||||||
|
|
||||||
|
func FileHasBoolExtension(file *descriptor.FileDescriptorProto, extension *proto.ExtensionDesc) bool {
|
||||||
|
if file.Options == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
value, err := proto.GetExtension(file.Options, extension)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if value == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if value.(*bool) == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetBoolFileOption(extension *proto.ExtensionDesc, value bool) func(file *descriptor.FileDescriptorProto) {
|
||||||
|
return func(file *descriptor.FileDescriptorProto) {
|
||||||
|
if FileHasBoolExtension(file, extension) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if file.Options == nil {
|
||||||
|
file.Options = &descriptor.FileOptions{}
|
||||||
|
}
|
||||||
|
if err := proto.SetExtension(file.Options, extension, &value); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TurnOffGoGettersAll(file *descriptor.FileDescriptorProto) {
|
||||||
|
SetBoolFileOption(gogoproto.E_GoprotoGettersAll, false)(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TurnOffGoEnumPrefixAll(file *descriptor.FileDescriptorProto) {
|
||||||
|
SetBoolFileOption(gogoproto.E_GoprotoEnumPrefixAll, false)(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TurnOffGoStringerAll(file *descriptor.FileDescriptorProto) {
|
||||||
|
SetBoolFileOption(gogoproto.E_GoprotoStringerAll, false)(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TurnOnVerboseEqualAll(file *descriptor.FileDescriptorProto) {
|
||||||
|
SetBoolFileOption(gogoproto.E_VerboseEqualAll, true)(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TurnOnFaceAll(file *descriptor.FileDescriptorProto) {
|
||||||
|
SetBoolFileOption(gogoproto.E_FaceAll, true)(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TurnOnGoStringAll(file *descriptor.FileDescriptorProto) {
|
||||||
|
SetBoolFileOption(gogoproto.E_GostringAll, true)(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TurnOnPopulateAll(file *descriptor.FileDescriptorProto) {
|
||||||
|
SetBoolFileOption(gogoproto.E_PopulateAll, true)(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TurnOnStringerAll(file *descriptor.FileDescriptorProto) {
|
||||||
|
SetBoolFileOption(gogoproto.E_StringerAll, true)(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TurnOnEqualAll(file *descriptor.FileDescriptorProto) {
|
||||||
|
SetBoolFileOption(gogoproto.E_EqualAll, true)(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TurnOnDescriptionAll(file *descriptor.FileDescriptorProto) {
|
||||||
|
SetBoolFileOption(gogoproto.E_DescriptionAll, true)(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TurnOnTestGenAll(file *descriptor.FileDescriptorProto) {
|
||||||
|
SetBoolFileOption(gogoproto.E_TestgenAll, true)(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TurnOnBenchGenAll(file *descriptor.FileDescriptorProto) {
|
||||||
|
SetBoolFileOption(gogoproto.E_BenchgenAll, true)(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TurnOnMarshalerAll(file *descriptor.FileDescriptorProto) {
|
||||||
|
SetBoolFileOption(gogoproto.E_MarshalerAll, true)(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TurnOnUnmarshalerAll(file *descriptor.FileDescriptorProto) {
|
||||||
|
SetBoolFileOption(gogoproto.E_UnmarshalerAll, true)(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TurnOnStable_MarshalerAll(file *descriptor.FileDescriptorProto) {
|
||||||
|
SetBoolFileOption(gogoproto.E_StableMarshalerAll, true)(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TurnOnSizerAll(file *descriptor.FileDescriptorProto) {
|
||||||
|
SetBoolFileOption(gogoproto.E_SizerAll, true)(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TurnOffGoEnumStringerAll(file *descriptor.FileDescriptorProto) {
|
||||||
|
SetBoolFileOption(gogoproto.E_GoprotoEnumStringerAll, false)(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TurnOnEnumStringerAll(file *descriptor.FileDescriptorProto) {
|
||||||
|
SetBoolFileOption(gogoproto.E_EnumStringerAll, true)(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TurnOnUnsafeUnmarshalerAll(file *descriptor.FileDescriptorProto) {
|
||||||
|
SetBoolFileOption(gogoproto.E_UnsafeUnmarshalerAll, true)(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TurnOnUnsafeMarshalerAll(file *descriptor.FileDescriptorProto) {
|
||||||
|
SetBoolFileOption(gogoproto.E_UnsafeMarshalerAll, true)(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TurnOffGoExtensionsMapAll(file *descriptor.FileDescriptorProto) {
|
||||||
|
SetBoolFileOption(gogoproto.E_GoprotoExtensionsMapAll, false)(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TurnOffGoUnrecognizedAll(file *descriptor.FileDescriptorProto) {
|
||||||
|
SetBoolFileOption(gogoproto.E_GoprotoUnrecognizedAll, false)(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TurnOffGoUnkeyedAll(file *descriptor.FileDescriptorProto) {
|
||||||
|
SetBoolFileOption(gogoproto.E_GoprotoUnkeyedAll, false)(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TurnOffGoSizecacheAll(file *descriptor.FileDescriptorProto) {
|
||||||
|
SetBoolFileOption(gogoproto.E_GoprotoSizecacheAll, false)(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TurnOffGogoImport(file *descriptor.FileDescriptorProto) {
|
||||||
|
SetBoolFileOption(gogoproto.E_GogoprotoImport, false)(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TurnOnCompareAll(file *descriptor.FileDescriptorProto) {
|
||||||
|
SetBoolFileOption(gogoproto.E_CompareAll, true)(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TurnOnMessageNameAll(file *descriptor.FileDescriptorProto) {
|
||||||
|
SetBoolFileOption(gogoproto.E_MessagenameAll, true)(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TurnOnGoRegistration(file *descriptor.FileDescriptorProto) {
|
||||||
|
SetBoolFileOption(gogoproto.E_GoprotoRegistration, true)(file)
|
||||||
|
}
|
|
@ -0,0 +1,125 @@
|
||||||
|
// Protocol Buffers for Go with Gadgets
|
||||||
|
//
|
||||||
|
// Copyright (c) 2015, The GoGo Authors. All rights reserved.
|
||||||
|
// http://github.com/gogo/protobuf
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
package vanity
|
||||||
|
|
||||||
|
import descriptor "github.com/gogo/protobuf/protoc-gen-gogo/descriptor"
|
||||||
|
|
||||||
|
func ForEachFile(files []*descriptor.FileDescriptorProto, f func(file *descriptor.FileDescriptorProto)) {
|
||||||
|
for _, file := range files {
|
||||||
|
f(file)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func OnlyProto2(files []*descriptor.FileDescriptorProto) []*descriptor.FileDescriptorProto {
|
||||||
|
outs := make([]*descriptor.FileDescriptorProto, 0, len(files))
|
||||||
|
for i, file := range files {
|
||||||
|
if file.GetSyntax() == "proto3" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
outs = append(outs, files[i])
|
||||||
|
}
|
||||||
|
return outs
|
||||||
|
}
|
||||||
|
|
||||||
|
func OnlyProto3(files []*descriptor.FileDescriptorProto) []*descriptor.FileDescriptorProto {
|
||||||
|
outs := make([]*descriptor.FileDescriptorProto, 0, len(files))
|
||||||
|
for i, file := range files {
|
||||||
|
if file.GetSyntax() != "proto3" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
outs = append(outs, files[i])
|
||||||
|
}
|
||||||
|
return outs
|
||||||
|
}
|
||||||
|
|
||||||
|
func ForEachMessageInFiles(files []*descriptor.FileDescriptorProto, f func(msg *descriptor.DescriptorProto)) {
|
||||||
|
for _, file := range files {
|
||||||
|
ForEachMessage(file.MessageType, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ForEachMessage(msgs []*descriptor.DescriptorProto, f func(msg *descriptor.DescriptorProto)) {
|
||||||
|
for _, msg := range msgs {
|
||||||
|
f(msg)
|
||||||
|
ForEachMessage(msg.NestedType, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ForEachFieldInFilesExcludingExtensions(files []*descriptor.FileDescriptorProto, f func(field *descriptor.FieldDescriptorProto)) {
|
||||||
|
for _, file := range files {
|
||||||
|
ForEachFieldExcludingExtensions(file.MessageType, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ForEachFieldInFiles(files []*descriptor.FileDescriptorProto, f func(field *descriptor.FieldDescriptorProto)) {
|
||||||
|
for _, file := range files {
|
||||||
|
for _, ext := range file.Extension {
|
||||||
|
f(ext)
|
||||||
|
}
|
||||||
|
ForEachField(file.MessageType, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ForEachFieldExcludingExtensions(msgs []*descriptor.DescriptorProto, f func(field *descriptor.FieldDescriptorProto)) {
|
||||||
|
for _, msg := range msgs {
|
||||||
|
for _, field := range msg.Field {
|
||||||
|
f(field)
|
||||||
|
}
|
||||||
|
ForEachField(msg.NestedType, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ForEachField(msgs []*descriptor.DescriptorProto, f func(field *descriptor.FieldDescriptorProto)) {
|
||||||
|
for _, msg := range msgs {
|
||||||
|
for _, field := range msg.Field {
|
||||||
|
f(field)
|
||||||
|
}
|
||||||
|
for _, ext := range msg.Extension {
|
||||||
|
f(ext)
|
||||||
|
}
|
||||||
|
ForEachField(msg.NestedType, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ForEachEnumInFiles(files []*descriptor.FileDescriptorProto, f func(enum *descriptor.EnumDescriptorProto)) {
|
||||||
|
for _, file := range files {
|
||||||
|
for _, enum := range file.EnumType {
|
||||||
|
f(enum)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ForEachEnum(msgs []*descriptor.DescriptorProto, f func(field *descriptor.EnumDescriptorProto)) {
|
||||||
|
for _, msg := range msgs {
|
||||||
|
for _, field := range msg.EnumType {
|
||||||
|
f(field)
|
||||||
|
}
|
||||||
|
ForEachEnum(msg.NestedType, f)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,154 @@
|
||||||
|
// Protocol Buffers for Go with Gadgets
|
||||||
|
//
|
||||||
|
// Copyright (c) 2015, The GoGo Authors. rights reserved.
|
||||||
|
// http://github.com/gogo/protobuf
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
package vanity
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogo/protobuf/gogoproto"
|
||||||
|
"github.com/gogo/protobuf/proto"
|
||||||
|
descriptor "github.com/gogo/protobuf/protoc-gen-gogo/descriptor"
|
||||||
|
)
|
||||||
|
|
||||||
|
func MessageHasBoolExtension(msg *descriptor.DescriptorProto, extension *proto.ExtensionDesc) bool {
|
||||||
|
if msg.Options == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
value, err := proto.GetExtension(msg.Options, extension)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if value == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if value.(*bool) == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetBoolMessageOption(extension *proto.ExtensionDesc, value bool) func(msg *descriptor.DescriptorProto) {
|
||||||
|
return func(msg *descriptor.DescriptorProto) {
|
||||||
|
if MessageHasBoolExtension(msg, extension) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if msg.Options == nil {
|
||||||
|
msg.Options = &descriptor.MessageOptions{}
|
||||||
|
}
|
||||||
|
if err := proto.SetExtension(msg.Options, extension, &value); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TurnOffGoGetters(msg *descriptor.DescriptorProto) {
|
||||||
|
SetBoolMessageOption(gogoproto.E_GoprotoGetters, false)(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TurnOffGoStringer(msg *descriptor.DescriptorProto) {
|
||||||
|
SetBoolMessageOption(gogoproto.E_GoprotoStringer, false)(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TurnOnVerboseEqual(msg *descriptor.DescriptorProto) {
|
||||||
|
SetBoolMessageOption(gogoproto.E_VerboseEqual, true)(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TurnOnFace(msg *descriptor.DescriptorProto) {
|
||||||
|
SetBoolMessageOption(gogoproto.E_Face, true)(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TurnOnGoString(msg *descriptor.DescriptorProto) {
|
||||||
|
SetBoolMessageOption(gogoproto.E_Face, true)(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TurnOnPopulate(msg *descriptor.DescriptorProto) {
|
||||||
|
SetBoolMessageOption(gogoproto.E_Populate, true)(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TurnOnStringer(msg *descriptor.DescriptorProto) {
|
||||||
|
SetBoolMessageOption(gogoproto.E_Stringer, true)(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TurnOnEqual(msg *descriptor.DescriptorProto) {
|
||||||
|
SetBoolMessageOption(gogoproto.E_Equal, true)(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TurnOnDescription(msg *descriptor.DescriptorProto) {
|
||||||
|
SetBoolMessageOption(gogoproto.E_Description, true)(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TurnOnTestGen(msg *descriptor.DescriptorProto) {
|
||||||
|
SetBoolMessageOption(gogoproto.E_Testgen, true)(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TurnOnBenchGen(msg *descriptor.DescriptorProto) {
|
||||||
|
SetBoolMessageOption(gogoproto.E_Benchgen, true)(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TurnOnMarshaler(msg *descriptor.DescriptorProto) {
|
||||||
|
SetBoolMessageOption(gogoproto.E_Marshaler, true)(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TurnOnUnmarshaler(msg *descriptor.DescriptorProto) {
|
||||||
|
SetBoolMessageOption(gogoproto.E_Unmarshaler, true)(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TurnOnSizer(msg *descriptor.DescriptorProto) {
|
||||||
|
SetBoolMessageOption(gogoproto.E_Sizer, true)(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TurnOnUnsafeUnmarshaler(msg *descriptor.DescriptorProto) {
|
||||||
|
SetBoolMessageOption(gogoproto.E_UnsafeUnmarshaler, true)(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TurnOnUnsafeMarshaler(msg *descriptor.DescriptorProto) {
|
||||||
|
SetBoolMessageOption(gogoproto.E_UnsafeMarshaler, true)(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TurnOffGoExtensionsMap(msg *descriptor.DescriptorProto) {
|
||||||
|
SetBoolMessageOption(gogoproto.E_GoprotoExtensionsMap, false)(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TurnOffGoUnrecognized(msg *descriptor.DescriptorProto) {
|
||||||
|
SetBoolMessageOption(gogoproto.E_GoprotoUnrecognized, false)(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TurnOffGoUnkeyed(msg *descriptor.DescriptorProto) {
|
||||||
|
SetBoolMessageOption(gogoproto.E_GoprotoUnkeyed, false)(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TurnOffGoSizecache(msg *descriptor.DescriptorProto) {
|
||||||
|
SetBoolMessageOption(gogoproto.E_GoprotoSizecache, false)(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TurnOnCompare(msg *descriptor.DescriptorProto) {
|
||||||
|
SetBoolMessageOption(gogoproto.E_Compare, true)(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TurnOnMessageName(msg *descriptor.DescriptorProto) {
|
||||||
|
SetBoolMessageOption(gogoproto.E_Messagename, true)(msg)
|
||||||
|
}
|
|
@ -0,0 +1,398 @@
|
||||||
|
// Copyright 2018 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package gengogrpc contains the gRPC code generator.
|
||||||
|
package gengogrpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"google.golang.org/protobuf/compiler/protogen"
|
||||||
|
|
||||||
|
"google.golang.org/protobuf/types/descriptorpb"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
contextPackage = protogen.GoImportPath("context")
|
||||||
|
grpcPackage = protogen.GoImportPath("google.golang.org/grpc")
|
||||||
|
codesPackage = protogen.GoImportPath("google.golang.org/grpc/codes")
|
||||||
|
statusPackage = protogen.GoImportPath("google.golang.org/grpc/status")
|
||||||
|
)
|
||||||
|
|
||||||
|
// GenerateFile generates a _grpc.pb.go file containing gRPC service definitions.
|
||||||
|
func GenerateFile(gen *protogen.Plugin, file *protogen.File) *protogen.GeneratedFile {
|
||||||
|
if len(file.Services) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
filename := file.GeneratedFilenamePrefix + "_grpc.pb.go"
|
||||||
|
g := gen.NewGeneratedFile(filename, file.GoImportPath)
|
||||||
|
g.P("// Code generated by protoc-gen-go-grpc. DO NOT EDIT.")
|
||||||
|
g.P()
|
||||||
|
g.P("package ", file.GoPackageName)
|
||||||
|
g.P()
|
||||||
|
GenerateFileContent(gen, file, g)
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateFileContent generates the gRPC service definitions, excluding the package statement.
|
||||||
|
func GenerateFileContent(gen *protogen.Plugin, file *protogen.File, g *protogen.GeneratedFile) {
|
||||||
|
if len(file.Services) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Remove this. We don't need to include these references any more.
|
||||||
|
g.P("// Reference imports to suppress errors if they are not otherwise used.")
|
||||||
|
g.P("var _ ", contextPackage.Ident("Context"))
|
||||||
|
g.P("var _ ", grpcPackage.Ident("ClientConnInterface"))
|
||||||
|
g.P()
|
||||||
|
|
||||||
|
g.P("// This is a compile-time assertion to ensure that this generated file")
|
||||||
|
g.P("// is compatible with the grpc package it is being compiled against.")
|
||||||
|
g.P("const _ = ", grpcPackage.Ident("SupportPackageIsVersion6"))
|
||||||
|
g.P()
|
||||||
|
for _, service := range file.Services {
|
||||||
|
genService(gen, file, g, service)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func genService(gen *protogen.Plugin, file *protogen.File, g *protogen.GeneratedFile, service *protogen.Service) {
|
||||||
|
clientName := service.GoName + "Client"
|
||||||
|
|
||||||
|
g.P("// ", clientName, " is the client API for ", service.GoName, " service.")
|
||||||
|
g.P("//")
|
||||||
|
g.P("// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.")
|
||||||
|
|
||||||
|
// Client interface.
|
||||||
|
if service.Desc.Options().(*descriptorpb.ServiceOptions).GetDeprecated() {
|
||||||
|
g.P("//")
|
||||||
|
g.P(deprecationComment)
|
||||||
|
}
|
||||||
|
g.Annotate(clientName, service.Location)
|
||||||
|
g.P("type ", clientName, " interface {")
|
||||||
|
for _, method := range service.Methods {
|
||||||
|
g.Annotate(clientName+"."+method.GoName, method.Location)
|
||||||
|
if method.Desc.Options().(*descriptorpb.MethodOptions).GetDeprecated() {
|
||||||
|
g.P(deprecationComment)
|
||||||
|
}
|
||||||
|
g.P(method.Comments.Leading,
|
||||||
|
clientSignature(g, method))
|
||||||
|
}
|
||||||
|
g.P("}")
|
||||||
|
g.P()
|
||||||
|
|
||||||
|
// Client structure.
|
||||||
|
g.P("type ", unexport(clientName), " struct {")
|
||||||
|
g.P("cc ", grpcPackage.Ident("ClientConnInterface"))
|
||||||
|
g.P("}")
|
||||||
|
g.P()
|
||||||
|
|
||||||
|
// NewClient factory.
|
||||||
|
if service.Desc.Options().(*descriptorpb.ServiceOptions).GetDeprecated() {
|
||||||
|
g.P(deprecationComment)
|
||||||
|
}
|
||||||
|
g.P("func New", clientName, " (cc ", grpcPackage.Ident("ClientConnInterface"), ") ", clientName, " {")
|
||||||
|
g.P("return &", unexport(clientName), "{cc}")
|
||||||
|
g.P("}")
|
||||||
|
g.P()
|
||||||
|
|
||||||
|
var methodIndex, streamIndex int
|
||||||
|
// Client method implementations.
|
||||||
|
for _, method := range service.Methods {
|
||||||
|
if !method.Desc.IsStreamingServer() && !method.Desc.IsStreamingClient() {
|
||||||
|
// Unary RPC method
|
||||||
|
genClientMethod(gen, file, g, method, methodIndex)
|
||||||
|
methodIndex++
|
||||||
|
} else {
|
||||||
|
// Streaming RPC method
|
||||||
|
genClientMethod(gen, file, g, method, streamIndex)
|
||||||
|
streamIndex++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Server interface.
|
||||||
|
serverType := service.GoName + "Server"
|
||||||
|
g.P("// ", serverType, " is the server API for ", service.GoName, " service.")
|
||||||
|
if service.Desc.Options().(*descriptorpb.ServiceOptions).GetDeprecated() {
|
||||||
|
g.P("//")
|
||||||
|
g.P(deprecationComment)
|
||||||
|
}
|
||||||
|
g.Annotate(serverType, service.Location)
|
||||||
|
g.P("type ", serverType, " interface {")
|
||||||
|
for _, method := range service.Methods {
|
||||||
|
g.Annotate(serverType+"."+method.GoName, method.Location)
|
||||||
|
if method.Desc.Options().(*descriptorpb.MethodOptions).GetDeprecated() {
|
||||||
|
g.P(deprecationComment)
|
||||||
|
}
|
||||||
|
g.P(method.Comments.Leading,
|
||||||
|
serverSignature(g, method))
|
||||||
|
}
|
||||||
|
g.P("}")
|
||||||
|
g.P()
|
||||||
|
|
||||||
|
// Server Unimplemented struct for forward compatibility.
|
||||||
|
g.P("// Unimplemented", serverType, " can be embedded to have forward compatible implementations.")
|
||||||
|
g.P("type Unimplemented", serverType, " struct {")
|
||||||
|
g.P("}")
|
||||||
|
g.P()
|
||||||
|
for _, method := range service.Methods {
|
||||||
|
nilArg := ""
|
||||||
|
if !method.Desc.IsStreamingClient() && !method.Desc.IsStreamingServer() {
|
||||||
|
nilArg = "nil,"
|
||||||
|
}
|
||||||
|
g.P("func (*Unimplemented", serverType, ") ", serverSignature(g, method), "{")
|
||||||
|
g.P("return ", nilArg, statusPackage.Ident("Errorf"), "(", codesPackage.Ident("Unimplemented"), `, "method `, method.GoName, ` not implemented")`)
|
||||||
|
g.P("}")
|
||||||
|
}
|
||||||
|
g.P()
|
||||||
|
|
||||||
|
// Server registration.
|
||||||
|
if service.Desc.Options().(*descriptorpb.ServiceOptions).GetDeprecated() {
|
||||||
|
g.P(deprecationComment)
|
||||||
|
}
|
||||||
|
serviceDescVar := "_" + service.GoName + "_serviceDesc"
|
||||||
|
g.P("func Register", service.GoName, "Server(s *", grpcPackage.Ident("Server"), ", srv ", serverType, ") {")
|
||||||
|
g.P("s.RegisterService(&", serviceDescVar, `, srv)`)
|
||||||
|
g.P("}")
|
||||||
|
g.P()
|
||||||
|
|
||||||
|
// Server handler implementations.
|
||||||
|
var handlerNames []string
|
||||||
|
for _, method := range service.Methods {
|
||||||
|
hname := genServerMethod(gen, file, g, method)
|
||||||
|
handlerNames = append(handlerNames, hname)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Service descriptor.
|
||||||
|
g.P("var ", serviceDescVar, " = ", grpcPackage.Ident("ServiceDesc"), " {")
|
||||||
|
g.P("ServiceName: ", strconv.Quote(string(service.Desc.FullName())), ",")
|
||||||
|
g.P("HandlerType: (*", serverType, ")(nil),")
|
||||||
|
g.P("Methods: []", grpcPackage.Ident("MethodDesc"), "{")
|
||||||
|
for i, method := range service.Methods {
|
||||||
|
if method.Desc.IsStreamingClient() || method.Desc.IsStreamingServer() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
g.P("{")
|
||||||
|
g.P("MethodName: ", strconv.Quote(string(method.Desc.Name())), ",")
|
||||||
|
g.P("Handler: ", handlerNames[i], ",")
|
||||||
|
g.P("},")
|
||||||
|
}
|
||||||
|
g.P("},")
|
||||||
|
g.P("Streams: []", grpcPackage.Ident("StreamDesc"), "{")
|
||||||
|
for i, method := range service.Methods {
|
||||||
|
if !method.Desc.IsStreamingClient() && !method.Desc.IsStreamingServer() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
g.P("{")
|
||||||
|
g.P("StreamName: ", strconv.Quote(string(method.Desc.Name())), ",")
|
||||||
|
g.P("Handler: ", handlerNames[i], ",")
|
||||||
|
if method.Desc.IsStreamingServer() {
|
||||||
|
g.P("ServerStreams: true,")
|
||||||
|
}
|
||||||
|
if method.Desc.IsStreamingClient() {
|
||||||
|
g.P("ClientStreams: true,")
|
||||||
|
}
|
||||||
|
g.P("},")
|
||||||
|
}
|
||||||
|
g.P("},")
|
||||||
|
g.P("Metadata: \"", file.Desc.Path(), "\",")
|
||||||
|
g.P("}")
|
||||||
|
g.P()
|
||||||
|
}
|
||||||
|
|
||||||
|
func clientSignature(g *protogen.GeneratedFile, method *protogen.Method) string {
|
||||||
|
s := method.GoName + "(ctx " + g.QualifiedGoIdent(contextPackage.Ident("Context"))
|
||||||
|
if !method.Desc.IsStreamingClient() {
|
||||||
|
s += ", in *" + g.QualifiedGoIdent(method.Input.GoIdent)
|
||||||
|
}
|
||||||
|
s += ", opts ..." + g.QualifiedGoIdent(grpcPackage.Ident("CallOption")) + ") ("
|
||||||
|
if !method.Desc.IsStreamingClient() && !method.Desc.IsStreamingServer() {
|
||||||
|
s += "*" + g.QualifiedGoIdent(method.Output.GoIdent)
|
||||||
|
} else {
|
||||||
|
s += method.Parent.GoName + "_" + method.GoName + "Client"
|
||||||
|
}
|
||||||
|
s += ", error)"
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func genClientMethod(gen *protogen.Plugin, file *protogen.File, g *protogen.GeneratedFile, method *protogen.Method, index int) {
|
||||||
|
service := method.Parent
|
||||||
|
sname := fmt.Sprintf("/%s/%s", service.Desc.FullName(), method.Desc.Name())
|
||||||
|
|
||||||
|
if method.Desc.Options().(*descriptorpb.MethodOptions).GetDeprecated() {
|
||||||
|
g.P(deprecationComment)
|
||||||
|
}
|
||||||
|
g.P("func (c *", unexport(service.GoName), "Client) ", clientSignature(g, method), "{")
|
||||||
|
if !method.Desc.IsStreamingServer() && !method.Desc.IsStreamingClient() {
|
||||||
|
g.P("out := new(", method.Output.GoIdent, ")")
|
||||||
|
g.P(`err := c.cc.Invoke(ctx, "`, sname, `", in, out, opts...)`)
|
||||||
|
g.P("if err != nil { return nil, err }")
|
||||||
|
g.P("return out, nil")
|
||||||
|
g.P("}")
|
||||||
|
g.P()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
streamType := unexport(service.GoName) + method.GoName + "Client"
|
||||||
|
serviceDescVar := "_" + service.GoName + "_serviceDesc"
|
||||||
|
g.P("stream, err := c.cc.NewStream(ctx, &", serviceDescVar, ".Streams[", index, `], "`, sname, `", opts...)`)
|
||||||
|
g.P("if err != nil { return nil, err }")
|
||||||
|
g.P("x := &", streamType, "{stream}")
|
||||||
|
if !method.Desc.IsStreamingClient() {
|
||||||
|
g.P("if err := x.ClientStream.SendMsg(in); err != nil { return nil, err }")
|
||||||
|
g.P("if err := x.ClientStream.CloseSend(); err != nil { return nil, err }")
|
||||||
|
}
|
||||||
|
g.P("return x, nil")
|
||||||
|
g.P("}")
|
||||||
|
g.P()
|
||||||
|
|
||||||
|
genSend := method.Desc.IsStreamingClient()
|
||||||
|
genRecv := method.Desc.IsStreamingServer()
|
||||||
|
genCloseAndRecv := !method.Desc.IsStreamingServer()
|
||||||
|
|
||||||
|
// Stream auxiliary types and methods.
|
||||||
|
g.P("type ", service.GoName, "_", method.GoName, "Client interface {")
|
||||||
|
if genSend {
|
||||||
|
g.P("Send(*", method.Input.GoIdent, ") error")
|
||||||
|
}
|
||||||
|
if genRecv {
|
||||||
|
g.P("Recv() (*", method.Output.GoIdent, ", error)")
|
||||||
|
}
|
||||||
|
if genCloseAndRecv {
|
||||||
|
g.P("CloseAndRecv() (*", method.Output.GoIdent, ", error)")
|
||||||
|
}
|
||||||
|
g.P(grpcPackage.Ident("ClientStream"))
|
||||||
|
g.P("}")
|
||||||
|
g.P()
|
||||||
|
|
||||||
|
g.P("type ", streamType, " struct {")
|
||||||
|
g.P(grpcPackage.Ident("ClientStream"))
|
||||||
|
g.P("}")
|
||||||
|
g.P()
|
||||||
|
|
||||||
|
if genSend {
|
||||||
|
g.P("func (x *", streamType, ") Send(m *", method.Input.GoIdent, ") error {")
|
||||||
|
g.P("return x.ClientStream.SendMsg(m)")
|
||||||
|
g.P("}")
|
||||||
|
g.P()
|
||||||
|
}
|
||||||
|
if genRecv {
|
||||||
|
g.P("func (x *", streamType, ") Recv() (*", method.Output.GoIdent, ", error) {")
|
||||||
|
g.P("m := new(", method.Output.GoIdent, ")")
|
||||||
|
g.P("if err := x.ClientStream.RecvMsg(m); err != nil { return nil, err }")
|
||||||
|
g.P("return m, nil")
|
||||||
|
g.P("}")
|
||||||
|
g.P()
|
||||||
|
}
|
||||||
|
if genCloseAndRecv {
|
||||||
|
g.P("func (x *", streamType, ") CloseAndRecv() (*", method.Output.GoIdent, ", error) {")
|
||||||
|
g.P("if err := x.ClientStream.CloseSend(); err != nil { return nil, err }")
|
||||||
|
g.P("m := new(", method.Output.GoIdent, ")")
|
||||||
|
g.P("if err := x.ClientStream.RecvMsg(m); err != nil { return nil, err }")
|
||||||
|
g.P("return m, nil")
|
||||||
|
g.P("}")
|
||||||
|
g.P()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func serverSignature(g *protogen.GeneratedFile, method *protogen.Method) string {
|
||||||
|
var reqArgs []string
|
||||||
|
ret := "error"
|
||||||
|
if !method.Desc.IsStreamingClient() && !method.Desc.IsStreamingServer() {
|
||||||
|
reqArgs = append(reqArgs, g.QualifiedGoIdent(contextPackage.Ident("Context")))
|
||||||
|
ret = "(*" + g.QualifiedGoIdent(method.Output.GoIdent) + ", error)"
|
||||||
|
}
|
||||||
|
if !method.Desc.IsStreamingClient() {
|
||||||
|
reqArgs = append(reqArgs, "*"+g.QualifiedGoIdent(method.Input.GoIdent))
|
||||||
|
}
|
||||||
|
if method.Desc.IsStreamingClient() || method.Desc.IsStreamingServer() {
|
||||||
|
reqArgs = append(reqArgs, method.Parent.GoName+"_"+method.GoName+"Server")
|
||||||
|
}
|
||||||
|
return method.GoName + "(" + strings.Join(reqArgs, ", ") + ") " + ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func genServerMethod(gen *protogen.Plugin, file *protogen.File, g *protogen.GeneratedFile, method *protogen.Method) string {
|
||||||
|
service := method.Parent
|
||||||
|
hname := fmt.Sprintf("_%s_%s_Handler", service.GoName, method.GoName)
|
||||||
|
|
||||||
|
if !method.Desc.IsStreamingClient() && !method.Desc.IsStreamingServer() {
|
||||||
|
g.P("func ", hname, "(srv interface{}, ctx ", contextPackage.Ident("Context"), ", dec func(interface{}) error, interceptor ", grpcPackage.Ident("UnaryServerInterceptor"), ") (interface{}, error) {")
|
||||||
|
g.P("in := new(", method.Input.GoIdent, ")")
|
||||||
|
g.P("if err := dec(in); err != nil { return nil, err }")
|
||||||
|
g.P("if interceptor == nil { return srv.(", service.GoName, "Server).", method.GoName, "(ctx, in) }")
|
||||||
|
g.P("info := &", grpcPackage.Ident("UnaryServerInfo"), "{")
|
||||||
|
g.P("Server: srv,")
|
||||||
|
g.P("FullMethod: ", strconv.Quote(fmt.Sprintf("/%s/%s", service.Desc.FullName(), method.GoName)), ",")
|
||||||
|
g.P("}")
|
||||||
|
g.P("handler := func(ctx ", contextPackage.Ident("Context"), ", req interface{}) (interface{}, error) {")
|
||||||
|
g.P("return srv.(", service.GoName, "Server).", method.GoName, "(ctx, req.(*", method.Input.GoIdent, "))")
|
||||||
|
g.P("}")
|
||||||
|
g.P("return interceptor(ctx, in, info, handler)")
|
||||||
|
g.P("}")
|
||||||
|
g.P()
|
||||||
|
return hname
|
||||||
|
}
|
||||||
|
streamType := unexport(service.GoName) + method.GoName + "Server"
|
||||||
|
g.P("func ", hname, "(srv interface{}, stream ", grpcPackage.Ident("ServerStream"), ") error {")
|
||||||
|
if !method.Desc.IsStreamingClient() {
|
||||||
|
g.P("m := new(", method.Input.GoIdent, ")")
|
||||||
|
g.P("if err := stream.RecvMsg(m); err != nil { return err }")
|
||||||
|
g.P("return srv.(", service.GoName, "Server).", method.GoName, "(m, &", streamType, "{stream})")
|
||||||
|
} else {
|
||||||
|
g.P("return srv.(", service.GoName, "Server).", method.GoName, "(&", streamType, "{stream})")
|
||||||
|
}
|
||||||
|
g.P("}")
|
||||||
|
g.P()
|
||||||
|
|
||||||
|
genSend := method.Desc.IsStreamingServer()
|
||||||
|
genSendAndClose := !method.Desc.IsStreamingServer()
|
||||||
|
genRecv := method.Desc.IsStreamingClient()
|
||||||
|
|
||||||
|
// Stream auxiliary types and methods.
|
||||||
|
g.P("type ", service.GoName, "_", method.GoName, "Server interface {")
|
||||||
|
if genSend {
|
||||||
|
g.P("Send(*", method.Output.GoIdent, ") error")
|
||||||
|
}
|
||||||
|
if genSendAndClose {
|
||||||
|
g.P("SendAndClose(*", method.Output.GoIdent, ") error")
|
||||||
|
}
|
||||||
|
if genRecv {
|
||||||
|
g.P("Recv() (*", method.Input.GoIdent, ", error)")
|
||||||
|
}
|
||||||
|
g.P(grpcPackage.Ident("ServerStream"))
|
||||||
|
g.P("}")
|
||||||
|
g.P()
|
||||||
|
|
||||||
|
g.P("type ", streamType, " struct {")
|
||||||
|
g.P(grpcPackage.Ident("ServerStream"))
|
||||||
|
g.P("}")
|
||||||
|
g.P()
|
||||||
|
|
||||||
|
if genSend {
|
||||||
|
g.P("func (x *", streamType, ") Send(m *", method.Output.GoIdent, ") error {")
|
||||||
|
g.P("return x.ServerStream.SendMsg(m)")
|
||||||
|
g.P("}")
|
||||||
|
g.P()
|
||||||
|
}
|
||||||
|
if genSendAndClose {
|
||||||
|
g.P("func (x *", streamType, ") SendAndClose(m *", method.Output.GoIdent, ") error {")
|
||||||
|
g.P("return x.ServerStream.SendMsg(m)")
|
||||||
|
g.P("}")
|
||||||
|
g.P()
|
||||||
|
}
|
||||||
|
if genRecv {
|
||||||
|
g.P("func (x *", streamType, ") Recv() (*", method.Input.GoIdent, ", error) {")
|
||||||
|
g.P("m := new(", method.Input.GoIdent, ")")
|
||||||
|
g.P("if err := x.ServerStream.RecvMsg(m); err != nil { return nil, err }")
|
||||||
|
g.P("return m, nil")
|
||||||
|
g.P("}")
|
||||||
|
g.P()
|
||||||
|
}
|
||||||
|
|
||||||
|
return hname
|
||||||
|
}
|
||||||
|
|
||||||
|
const deprecationComment = "// Deprecated: Do not use."
|
||||||
|
|
||||||
|
func unexport(s string) string { return strings.ToLower(s[:1]) + s[1:] }
|
|
@ -0,0 +1,74 @@
|
||||||
|
// Copyright 2010 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// protoc-gen-go is a plugin for the Google protocol buffer compiler to generate
|
||||||
|
// Go code. Install it by building this program and making it accessible within
|
||||||
|
// your PATH with the name:
|
||||||
|
// protoc-gen-go
|
||||||
|
//
|
||||||
|
// The 'go' suffix becomes part of the argument for the protocol compiler,
|
||||||
|
// such that it can be invoked as:
|
||||||
|
// protoc --go_out=paths=source_relative:. path/to/file.proto
|
||||||
|
//
|
||||||
|
// This generates Go bindings for the protocol buffer defined by file.proto.
|
||||||
|
// With that input, the output will be written to:
|
||||||
|
// path/to/file.pb.go
|
||||||
|
//
|
||||||
|
// See the README and documentation for protocol buffers to learn more:
|
||||||
|
// https://developers.google.com/protocol-buffers/
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/golang/protobuf/internal/gengogrpc"
|
||||||
|
gengo "google.golang.org/protobuf/cmd/protoc-gen-go/internal_gengo"
|
||||||
|
"google.golang.org/protobuf/compiler/protogen"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var (
|
||||||
|
flags flag.FlagSet
|
||||||
|
plugins = flags.String("plugins", "", "list of plugins to enable (supported values: grpc)")
|
||||||
|
importPrefix = flags.String("import_prefix", "", "prefix to prepend to import paths")
|
||||||
|
)
|
||||||
|
importRewriteFunc := func(importPath protogen.GoImportPath) protogen.GoImportPath {
|
||||||
|
switch importPath {
|
||||||
|
case "context", "fmt", "math":
|
||||||
|
return importPath
|
||||||
|
}
|
||||||
|
if *importPrefix != "" {
|
||||||
|
return protogen.GoImportPath(*importPrefix) + importPath
|
||||||
|
}
|
||||||
|
return importPath
|
||||||
|
}
|
||||||
|
protogen.Options{
|
||||||
|
ParamFunc: flags.Set,
|
||||||
|
ImportRewriteFunc: importRewriteFunc,
|
||||||
|
}.Run(func(gen *protogen.Plugin) error {
|
||||||
|
grpc := false
|
||||||
|
for _, plugin := range strings.Split(*plugins, ",") {
|
||||||
|
switch plugin {
|
||||||
|
case "grpc":
|
||||||
|
grpc = true
|
||||||
|
case "":
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("protoc-gen-go: unknown plugin %q", plugin)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, f := range gen.Files {
|
||||||
|
if !f.Generate {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
g := gengo.GenerateFile(gen, f)
|
||||||
|
if grpc {
|
||||||
|
gengogrpc.GenerateFileContent(gen, f, g)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gen.SupportedFeatures = gengo.SupportedFeatures
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
168
vendor/google.golang.org/protobuf/cmd/protoc-gen-go/internal_gengo/init.go
generated
vendored
Normal file
168
vendor/google.golang.org/protobuf/cmd/protoc-gen-go/internal_gengo/init.go
generated
vendored
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
// Copyright 2019 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package internal_gengo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"unicode"
|
||||||
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"google.golang.org/protobuf/compiler/protogen"
|
||||||
|
"google.golang.org/protobuf/encoding/protowire"
|
||||||
|
|
||||||
|
"google.golang.org/protobuf/types/descriptorpb"
|
||||||
|
)
|
||||||
|
|
||||||
|
type fileInfo struct {
|
||||||
|
*protogen.File
|
||||||
|
|
||||||
|
allEnums []*enumInfo
|
||||||
|
allMessages []*messageInfo
|
||||||
|
allExtensions []*extensionInfo
|
||||||
|
|
||||||
|
allEnumsByPtr map[*enumInfo]int // value is index into allEnums
|
||||||
|
allMessagesByPtr map[*messageInfo]int // value is index into allMessages
|
||||||
|
allMessageFieldsByPtr map[*messageInfo]*structFields
|
||||||
|
|
||||||
|
// needRawDesc specifies whether the generator should emit logic to provide
|
||||||
|
// the legacy raw descriptor in GZIP'd form.
|
||||||
|
// This is updated by enum and message generation logic as necessary,
|
||||||
|
// and checked at the end of file generation.
|
||||||
|
needRawDesc bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type structFields struct {
|
||||||
|
count int
|
||||||
|
unexported map[int]string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sf *structFields) append(name string) {
|
||||||
|
if r, _ := utf8.DecodeRuneInString(name); !unicode.IsUpper(r) {
|
||||||
|
if sf.unexported == nil {
|
||||||
|
sf.unexported = make(map[int]string)
|
||||||
|
}
|
||||||
|
sf.unexported[sf.count] = name
|
||||||
|
}
|
||||||
|
sf.count++
|
||||||
|
}
|
||||||
|
|
||||||
|
func newFileInfo(file *protogen.File) *fileInfo {
|
||||||
|
f := &fileInfo{File: file}
|
||||||
|
|
||||||
|
// Collect all enums, messages, and extensions in "flattened ordering".
|
||||||
|
// See filetype.TypeBuilder.
|
||||||
|
var walkMessages func([]*protogen.Message, func(*protogen.Message))
|
||||||
|
walkMessages = func(messages []*protogen.Message, f func(*protogen.Message)) {
|
||||||
|
for _, m := range messages {
|
||||||
|
f(m)
|
||||||
|
walkMessages(m.Messages, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
initEnumInfos := func(enums []*protogen.Enum) {
|
||||||
|
for _, enum := range enums {
|
||||||
|
f.allEnums = append(f.allEnums, newEnumInfo(f, enum))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
initMessageInfos := func(messages []*protogen.Message) {
|
||||||
|
for _, message := range messages {
|
||||||
|
f.allMessages = append(f.allMessages, newMessageInfo(f, message))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
initExtensionInfos := func(extensions []*protogen.Extension) {
|
||||||
|
for _, extension := range extensions {
|
||||||
|
f.allExtensions = append(f.allExtensions, newExtensionInfo(f, extension))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
initEnumInfos(f.Enums)
|
||||||
|
initMessageInfos(f.Messages)
|
||||||
|
initExtensionInfos(f.Extensions)
|
||||||
|
walkMessages(f.Messages, func(m *protogen.Message) {
|
||||||
|
initEnumInfos(m.Enums)
|
||||||
|
initMessageInfos(m.Messages)
|
||||||
|
initExtensionInfos(m.Extensions)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Derive a reverse mapping of enum and message pointers to their index
|
||||||
|
// in allEnums and allMessages.
|
||||||
|
if len(f.allEnums) > 0 {
|
||||||
|
f.allEnumsByPtr = make(map[*enumInfo]int)
|
||||||
|
for i, e := range f.allEnums {
|
||||||
|
f.allEnumsByPtr[e] = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(f.allMessages) > 0 {
|
||||||
|
f.allMessagesByPtr = make(map[*messageInfo]int)
|
||||||
|
f.allMessageFieldsByPtr = make(map[*messageInfo]*structFields)
|
||||||
|
for i, m := range f.allMessages {
|
||||||
|
f.allMessagesByPtr[m] = i
|
||||||
|
f.allMessageFieldsByPtr[m] = new(structFields)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
type enumInfo struct {
|
||||||
|
*protogen.Enum
|
||||||
|
|
||||||
|
genJSONMethod bool
|
||||||
|
genRawDescMethod bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func newEnumInfo(f *fileInfo, enum *protogen.Enum) *enumInfo {
|
||||||
|
e := &enumInfo{Enum: enum}
|
||||||
|
e.genJSONMethod = true
|
||||||
|
e.genRawDescMethod = true
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
type messageInfo struct {
|
||||||
|
*protogen.Message
|
||||||
|
|
||||||
|
genRawDescMethod bool
|
||||||
|
genExtRangeMethod bool
|
||||||
|
|
||||||
|
isTracked bool
|
||||||
|
hasWeak bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMessageInfo(f *fileInfo, message *protogen.Message) *messageInfo {
|
||||||
|
m := &messageInfo{Message: message}
|
||||||
|
m.genRawDescMethod = true
|
||||||
|
m.genExtRangeMethod = true
|
||||||
|
m.isTracked = isTrackedMessage(m)
|
||||||
|
for _, field := range m.Fields {
|
||||||
|
m.hasWeak = m.hasWeak || field.Desc.IsWeak()
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
// isTrackedMessage reports whether field tracking is enabled on the message.
|
||||||
|
func isTrackedMessage(m *messageInfo) (tracked bool) {
|
||||||
|
const trackFieldUse_fieldNumber = 37383685
|
||||||
|
|
||||||
|
// Decode the option from unknown fields to avoid a dependency on the
|
||||||
|
// annotation proto from protoc-gen-go.
|
||||||
|
b := m.Desc.Options().(*descriptorpb.MessageOptions).ProtoReflect().GetUnknown()
|
||||||
|
for len(b) > 0 {
|
||||||
|
num, typ, n := protowire.ConsumeTag(b)
|
||||||
|
b = b[n:]
|
||||||
|
if num == trackFieldUse_fieldNumber && typ == protowire.VarintType {
|
||||||
|
v, _ := protowire.ConsumeVarint(b)
|
||||||
|
tracked = protowire.DecodeBool(v)
|
||||||
|
}
|
||||||
|
m := protowire.ConsumeFieldValue(num, typ, b)
|
||||||
|
b = b[m:]
|
||||||
|
}
|
||||||
|
return tracked
|
||||||
|
}
|
||||||
|
|
||||||
|
type extensionInfo struct {
|
||||||
|
*protogen.Extension
|
||||||
|
}
|
||||||
|
|
||||||
|
func newExtensionInfo(f *fileInfo, extension *protogen.Extension) *extensionInfo {
|
||||||
|
x := &extensionInfo{Extension: extension}
|
||||||
|
return x
|
||||||
|
}
|
884
vendor/google.golang.org/protobuf/cmd/protoc-gen-go/internal_gengo/main.go
generated
vendored
Normal file
884
vendor/google.golang.org/protobuf/cmd/protoc-gen-go/internal_gengo/main.go
generated
vendored
Normal file
|
@ -0,0 +1,884 @@
|
||||||
|
// Copyright 2018 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package internal_gengo is internal to the protobuf module.
|
||||||
|
package internal_gengo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"go/ast"
|
||||||
|
"go/parser"
|
||||||
|
"go/token"
|
||||||
|
"math"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"google.golang.org/protobuf/compiler/protogen"
|
||||||
|
"google.golang.org/protobuf/internal/encoding/tag"
|
||||||
|
"google.golang.org/protobuf/internal/genid"
|
||||||
|
"google.golang.org/protobuf/internal/version"
|
||||||
|
"google.golang.org/protobuf/reflect/protoreflect"
|
||||||
|
"google.golang.org/protobuf/runtime/protoimpl"
|
||||||
|
|
||||||
|
"google.golang.org/protobuf/types/descriptorpb"
|
||||||
|
"google.golang.org/protobuf/types/pluginpb"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SupportedFeatures reports the set of supported protobuf language features.
|
||||||
|
var SupportedFeatures = uint64(pluginpb.CodeGeneratorResponse_FEATURE_PROTO3_OPTIONAL)
|
||||||
|
|
||||||
|
// GenerateVersionMarkers specifies whether to generate version markers.
|
||||||
|
var GenerateVersionMarkers = true
|
||||||
|
|
||||||
|
// Standard library dependencies.
|
||||||
|
const (
|
||||||
|
base64Package = protogen.GoImportPath("encoding/base64")
|
||||||
|
mathPackage = protogen.GoImportPath("math")
|
||||||
|
reflectPackage = protogen.GoImportPath("reflect")
|
||||||
|
sortPackage = protogen.GoImportPath("sort")
|
||||||
|
stringsPackage = protogen.GoImportPath("strings")
|
||||||
|
syncPackage = protogen.GoImportPath("sync")
|
||||||
|
timePackage = protogen.GoImportPath("time")
|
||||||
|
utf8Package = protogen.GoImportPath("unicode/utf8")
|
||||||
|
)
|
||||||
|
|
||||||
|
// Protobuf library dependencies.
|
||||||
|
//
|
||||||
|
// These are declared as an interface type so that they can be more easily
|
||||||
|
// patched to support unique build environments that impose restrictions
|
||||||
|
// on the dependencies of generated source code.
|
||||||
|
var (
|
||||||
|
protoPackage goImportPath = protogen.GoImportPath("google.golang.org/protobuf/proto")
|
||||||
|
protoifacePackage goImportPath = protogen.GoImportPath("google.golang.org/protobuf/runtime/protoiface")
|
||||||
|
protoimplPackage goImportPath = protogen.GoImportPath("google.golang.org/protobuf/runtime/protoimpl")
|
||||||
|
protojsonPackage goImportPath = protogen.GoImportPath("google.golang.org/protobuf/encoding/protojson")
|
||||||
|
protoreflectPackage goImportPath = protogen.GoImportPath("google.golang.org/protobuf/reflect/protoreflect")
|
||||||
|
protoregistryPackage goImportPath = protogen.GoImportPath("google.golang.org/protobuf/reflect/protoregistry")
|
||||||
|
)
|
||||||
|
|
||||||
|
type goImportPath interface {
|
||||||
|
String() string
|
||||||
|
Ident(string) protogen.GoIdent
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateFile generates the contents of a .pb.go file.
|
||||||
|
func GenerateFile(gen *protogen.Plugin, file *protogen.File) *protogen.GeneratedFile {
|
||||||
|
filename := file.GeneratedFilenamePrefix + ".pb.go"
|
||||||
|
g := gen.NewGeneratedFile(filename, file.GoImportPath)
|
||||||
|
f := newFileInfo(file)
|
||||||
|
|
||||||
|
genStandaloneComments(g, f, int32(genid.FileDescriptorProto_Syntax_field_number))
|
||||||
|
genGeneratedHeader(gen, g, f)
|
||||||
|
genStandaloneComments(g, f, int32(genid.FileDescriptorProto_Package_field_number))
|
||||||
|
|
||||||
|
packageDoc := genPackageKnownComment(f)
|
||||||
|
g.P(packageDoc, "package ", f.GoPackageName)
|
||||||
|
g.P()
|
||||||
|
|
||||||
|
// Emit a static check that enforces a minimum version of the proto package.
|
||||||
|
if GenerateVersionMarkers {
|
||||||
|
g.P("const (")
|
||||||
|
g.P("// Verify that this generated code is sufficiently up-to-date.")
|
||||||
|
g.P("_ = ", protoimplPackage.Ident("EnforceVersion"), "(", protoimpl.GenVersion, " - ", protoimplPackage.Ident("MinVersion"), ")")
|
||||||
|
g.P("// Verify that runtime/protoimpl is sufficiently up-to-date.")
|
||||||
|
g.P("_ = ", protoimplPackage.Ident("EnforceVersion"), "(", protoimplPackage.Ident("MaxVersion"), " - ", protoimpl.GenVersion, ")")
|
||||||
|
g.P(")")
|
||||||
|
g.P()
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, imps := 0, f.Desc.Imports(); i < imps.Len(); i++ {
|
||||||
|
genImport(gen, g, f, imps.Get(i))
|
||||||
|
}
|
||||||
|
for _, enum := range f.allEnums {
|
||||||
|
genEnum(g, f, enum)
|
||||||
|
}
|
||||||
|
for _, message := range f.allMessages {
|
||||||
|
genMessage(g, f, message)
|
||||||
|
}
|
||||||
|
genExtensions(g, f)
|
||||||
|
|
||||||
|
genReflectFileDescriptor(gen, g, f)
|
||||||
|
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
// genStandaloneComments prints all leading comments for a FileDescriptorProto
|
||||||
|
// location identified by the field number n.
|
||||||
|
func genStandaloneComments(g *protogen.GeneratedFile, f *fileInfo, n int32) {
|
||||||
|
loc := f.Desc.SourceLocations().ByPath(protoreflect.SourcePath{n})
|
||||||
|
for _, s := range loc.LeadingDetachedComments {
|
||||||
|
g.P(protogen.Comments(s))
|
||||||
|
g.P()
|
||||||
|
}
|
||||||
|
if s := loc.LeadingComments; s != "" {
|
||||||
|
g.P(protogen.Comments(s))
|
||||||
|
g.P()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func genGeneratedHeader(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo) {
|
||||||
|
g.P("// Code generated by protoc-gen-go. DO NOT EDIT.")
|
||||||
|
|
||||||
|
if GenerateVersionMarkers {
|
||||||
|
g.P("// versions:")
|
||||||
|
protocGenGoVersion := version.String()
|
||||||
|
protocVersion := "(unknown)"
|
||||||
|
if v := gen.Request.GetCompilerVersion(); v != nil {
|
||||||
|
protocVersion = fmt.Sprintf("v%v.%v.%v", v.GetMajor(), v.GetMinor(), v.GetPatch())
|
||||||
|
if s := v.GetSuffix(); s != "" {
|
||||||
|
protocVersion += "-" + s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g.P("// \tprotoc-gen-go ", protocGenGoVersion)
|
||||||
|
g.P("// \tprotoc ", protocVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.Proto.GetOptions().GetDeprecated() {
|
||||||
|
g.P("// ", f.Desc.Path(), " is a deprecated file.")
|
||||||
|
} else {
|
||||||
|
g.P("// source: ", f.Desc.Path())
|
||||||
|
}
|
||||||
|
g.P()
|
||||||
|
}
|
||||||
|
|
||||||
|
func genImport(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, imp protoreflect.FileImport) {
|
||||||
|
impFile, ok := gen.FilesByPath[imp.Path()]
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if impFile.GoImportPath == f.GoImportPath {
|
||||||
|
// Don't generate imports or aliases for types in the same Go package.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Generate imports for all non-weak dependencies, even if they are not
|
||||||
|
// referenced, because other code and tools depend on having the
|
||||||
|
// full transitive closure of protocol buffer types in the binary.
|
||||||
|
if !imp.IsWeak {
|
||||||
|
g.Import(impFile.GoImportPath)
|
||||||
|
}
|
||||||
|
if !imp.IsPublic {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate public imports by generating the imported file, parsing it,
|
||||||
|
// and extracting every symbol that should receive a forwarding declaration.
|
||||||
|
impGen := GenerateFile(gen, impFile)
|
||||||
|
impGen.Skip()
|
||||||
|
b, err := impGen.Content()
|
||||||
|
if err != nil {
|
||||||
|
gen.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fset := token.NewFileSet()
|
||||||
|
astFile, err := parser.ParseFile(fset, "", b, parser.ParseComments)
|
||||||
|
if err != nil {
|
||||||
|
gen.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
genForward := func(tok token.Token, name string, expr ast.Expr) {
|
||||||
|
// Don't import unexported symbols.
|
||||||
|
r, _ := utf8.DecodeRuneInString(name)
|
||||||
|
if !unicode.IsUpper(r) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Don't import the FileDescriptor.
|
||||||
|
if name == impFile.GoDescriptorIdent.GoName {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Don't import decls referencing a symbol defined in another package.
|
||||||
|
// i.e., don't import decls which are themselves public imports:
|
||||||
|
//
|
||||||
|
// type T = somepackage.T
|
||||||
|
if _, ok := expr.(*ast.SelectorExpr); ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
g.P(tok, " ", name, " = ", impFile.GoImportPath.Ident(name))
|
||||||
|
}
|
||||||
|
g.P("// Symbols defined in public import of ", imp.Path(), ".")
|
||||||
|
g.P()
|
||||||
|
for _, decl := range astFile.Decls {
|
||||||
|
switch decl := decl.(type) {
|
||||||
|
case *ast.GenDecl:
|
||||||
|
for _, spec := range decl.Specs {
|
||||||
|
switch spec := spec.(type) {
|
||||||
|
case *ast.TypeSpec:
|
||||||
|
genForward(decl.Tok, spec.Name.Name, spec.Type)
|
||||||
|
case *ast.ValueSpec:
|
||||||
|
for i, name := range spec.Names {
|
||||||
|
var expr ast.Expr
|
||||||
|
if i < len(spec.Values) {
|
||||||
|
expr = spec.Values[i]
|
||||||
|
}
|
||||||
|
genForward(decl.Tok, name.Name, expr)
|
||||||
|
}
|
||||||
|
case *ast.ImportSpec:
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("can't generate forward for spec type %T", spec))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g.P()
|
||||||
|
}
|
||||||
|
|
||||||
|
func genEnum(g *protogen.GeneratedFile, f *fileInfo, e *enumInfo) {
|
||||||
|
// Enum type declaration.
|
||||||
|
g.Annotate(e.GoIdent.GoName, e.Location)
|
||||||
|
leadingComments := appendDeprecationSuffix(e.Comments.Leading,
|
||||||
|
e.Desc.Options().(*descriptorpb.EnumOptions).GetDeprecated())
|
||||||
|
g.P(leadingComments,
|
||||||
|
"type ", e.GoIdent, " int32")
|
||||||
|
|
||||||
|
// Enum value constants.
|
||||||
|
g.P("const (")
|
||||||
|
for _, value := range e.Values {
|
||||||
|
g.Annotate(value.GoIdent.GoName, value.Location)
|
||||||
|
leadingComments := appendDeprecationSuffix(value.Comments.Leading,
|
||||||
|
value.Desc.Options().(*descriptorpb.EnumValueOptions).GetDeprecated())
|
||||||
|
g.P(leadingComments,
|
||||||
|
value.GoIdent, " ", e.GoIdent, " = ", value.Desc.Number(),
|
||||||
|
trailingComment(value.Comments.Trailing))
|
||||||
|
}
|
||||||
|
g.P(")")
|
||||||
|
g.P()
|
||||||
|
|
||||||
|
// Enum value maps.
|
||||||
|
g.P("// Enum value maps for ", e.GoIdent, ".")
|
||||||
|
g.P("var (")
|
||||||
|
g.P(e.GoIdent.GoName+"_name", " = map[int32]string{")
|
||||||
|
for _, value := range e.Values {
|
||||||
|
duplicate := ""
|
||||||
|
if value.Desc != e.Desc.Values().ByNumber(value.Desc.Number()) {
|
||||||
|
duplicate = "// Duplicate value: "
|
||||||
|
}
|
||||||
|
g.P(duplicate, value.Desc.Number(), ": ", strconv.Quote(string(value.Desc.Name())), ",")
|
||||||
|
}
|
||||||
|
g.P("}")
|
||||||
|
g.P(e.GoIdent.GoName+"_value", " = map[string]int32{")
|
||||||
|
for _, value := range e.Values {
|
||||||
|
g.P(strconv.Quote(string(value.Desc.Name())), ": ", value.Desc.Number(), ",")
|
||||||
|
}
|
||||||
|
g.P("}")
|
||||||
|
g.P(")")
|
||||||
|
g.P()
|
||||||
|
|
||||||
|
// Enum method.
|
||||||
|
//
|
||||||
|
// NOTE: A pointer value is needed to represent presence in proto2.
|
||||||
|
// Since a proto2 message can reference a proto3 enum, it is useful to
|
||||||
|
// always generate this method (even on proto3 enums) to support that case.
|
||||||
|
g.P("func (x ", e.GoIdent, ") Enum() *", e.GoIdent, " {")
|
||||||
|
g.P("p := new(", e.GoIdent, ")")
|
||||||
|
g.P("*p = x")
|
||||||
|
g.P("return p")
|
||||||
|
g.P("}")
|
||||||
|
g.P()
|
||||||
|
|
||||||
|
// String method.
|
||||||
|
g.P("func (x ", e.GoIdent, ") String() string {")
|
||||||
|
g.P("return ", protoimplPackage.Ident("X"), ".EnumStringOf(x.Descriptor(), ", protoreflectPackage.Ident("EnumNumber"), "(x))")
|
||||||
|
g.P("}")
|
||||||
|
g.P()
|
||||||
|
|
||||||
|
genEnumReflectMethods(g, f, e)
|
||||||
|
|
||||||
|
// UnmarshalJSON method.
|
||||||
|
if e.genJSONMethod && e.Desc.Syntax() == protoreflect.Proto2 {
|
||||||
|
g.P("// Deprecated: Do not use.")
|
||||||
|
g.P("func (x *", e.GoIdent, ") UnmarshalJSON(b []byte) error {")
|
||||||
|
g.P("num, err := ", protoimplPackage.Ident("X"), ".UnmarshalJSONEnum(x.Descriptor(), b)")
|
||||||
|
g.P("if err != nil {")
|
||||||
|
g.P("return err")
|
||||||
|
g.P("}")
|
||||||
|
g.P("*x = ", e.GoIdent, "(num)")
|
||||||
|
g.P("return nil")
|
||||||
|
g.P("}")
|
||||||
|
g.P()
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnumDescriptor method.
|
||||||
|
if e.genRawDescMethod {
|
||||||
|
var indexes []string
|
||||||
|
for i := 1; i < len(e.Location.Path); i += 2 {
|
||||||
|
indexes = append(indexes, strconv.Itoa(int(e.Location.Path[i])))
|
||||||
|
}
|
||||||
|
g.P("// Deprecated: Use ", e.GoIdent, ".Descriptor instead.")
|
||||||
|
g.P("func (", e.GoIdent, ") EnumDescriptor() ([]byte, []int) {")
|
||||||
|
g.P("return ", rawDescVarName(f), "GZIP(), []int{", strings.Join(indexes, ","), "}")
|
||||||
|
g.P("}")
|
||||||
|
g.P()
|
||||||
|
f.needRawDesc = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func genMessage(g *protogen.GeneratedFile, f *fileInfo, m *messageInfo) {
|
||||||
|
if m.Desc.IsMapEntry() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Message type declaration.
|
||||||
|
g.Annotate(m.GoIdent.GoName, m.Location)
|
||||||
|
leadingComments := appendDeprecationSuffix(m.Comments.Leading,
|
||||||
|
m.Desc.Options().(*descriptorpb.MessageOptions).GetDeprecated())
|
||||||
|
g.P(leadingComments,
|
||||||
|
"type ", m.GoIdent, " struct {")
|
||||||
|
genMessageFields(g, f, m)
|
||||||
|
g.P("}")
|
||||||
|
g.P()
|
||||||
|
|
||||||
|
genMessageKnownFunctions(g, f, m)
|
||||||
|
genMessageDefaultDecls(g, f, m)
|
||||||
|
genMessageMethods(g, f, m)
|
||||||
|
genMessageOneofWrapperTypes(g, f, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func genMessageFields(g *protogen.GeneratedFile, f *fileInfo, m *messageInfo) {
|
||||||
|
sf := f.allMessageFieldsByPtr[m]
|
||||||
|
genMessageInternalFields(g, f, m, sf)
|
||||||
|
for _, field := range m.Fields {
|
||||||
|
genMessageField(g, f, m, field, sf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func genMessageInternalFields(g *protogen.GeneratedFile, f *fileInfo, m *messageInfo, sf *structFields) {
|
||||||
|
g.P(genid.State_goname, " ", protoimplPackage.Ident("MessageState"))
|
||||||
|
sf.append(genid.State_goname)
|
||||||
|
g.P(genid.SizeCache_goname, " ", protoimplPackage.Ident("SizeCache"))
|
||||||
|
sf.append(genid.SizeCache_goname)
|
||||||
|
if m.hasWeak {
|
||||||
|
g.P(genid.WeakFields_goname, " ", protoimplPackage.Ident("WeakFields"))
|
||||||
|
sf.append(genid.WeakFields_goname)
|
||||||
|
}
|
||||||
|
g.P(genid.UnknownFields_goname, " ", protoimplPackage.Ident("UnknownFields"))
|
||||||
|
sf.append(genid.UnknownFields_goname)
|
||||||
|
if m.Desc.ExtensionRanges().Len() > 0 {
|
||||||
|
g.P(genid.ExtensionFields_goname, " ", protoimplPackage.Ident("ExtensionFields"))
|
||||||
|
sf.append(genid.ExtensionFields_goname)
|
||||||
|
}
|
||||||
|
if sf.count > 0 {
|
||||||
|
g.P()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func genMessageField(g *protogen.GeneratedFile, f *fileInfo, m *messageInfo, field *protogen.Field, sf *structFields) {
|
||||||
|
if oneof := field.Oneof; oneof != nil && !oneof.Desc.IsSynthetic() {
|
||||||
|
// It would be a bit simpler to iterate over the oneofs below,
|
||||||
|
// but generating the field here keeps the contents of the Go
|
||||||
|
// struct in the same order as the contents of the source
|
||||||
|
// .proto file.
|
||||||
|
if oneof.Fields[0] != field {
|
||||||
|
return // only generate for first appearance
|
||||||
|
}
|
||||||
|
|
||||||
|
tags := structTags{
|
||||||
|
{"protobuf_oneof", string(oneof.Desc.Name())},
|
||||||
|
}
|
||||||
|
if m.isTracked {
|
||||||
|
tags = append(tags, gotrackTags...)
|
||||||
|
}
|
||||||
|
|
||||||
|
g.Annotate(m.GoIdent.GoName+"."+oneof.GoName, oneof.Location)
|
||||||
|
leadingComments := oneof.Comments.Leading
|
||||||
|
if leadingComments != "" {
|
||||||
|
leadingComments += "\n"
|
||||||
|
}
|
||||||
|
ss := []string{fmt.Sprintf(" Types that are assignable to %s:\n", oneof.GoName)}
|
||||||
|
for _, field := range oneof.Fields {
|
||||||
|
ss = append(ss, "\t*"+field.GoIdent.GoName+"\n")
|
||||||
|
}
|
||||||
|
leadingComments += protogen.Comments(strings.Join(ss, ""))
|
||||||
|
g.P(leadingComments,
|
||||||
|
oneof.GoName, " ", oneofInterfaceName(oneof), tags)
|
||||||
|
sf.append(oneof.GoName)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
goType, pointer := fieldGoType(g, f, field)
|
||||||
|
if pointer {
|
||||||
|
goType = "*" + goType
|
||||||
|
}
|
||||||
|
tags := structTags{
|
||||||
|
{"protobuf", fieldProtobufTagValue(field)},
|
||||||
|
{"json", fieldJSONTagValue(field)},
|
||||||
|
}
|
||||||
|
if field.Desc.IsMap() {
|
||||||
|
key := field.Message.Fields[0]
|
||||||
|
val := field.Message.Fields[1]
|
||||||
|
tags = append(tags, structTags{
|
||||||
|
{"protobuf_key", fieldProtobufTagValue(key)},
|
||||||
|
{"protobuf_val", fieldProtobufTagValue(val)},
|
||||||
|
}...)
|
||||||
|
}
|
||||||
|
if m.isTracked {
|
||||||
|
tags = append(tags, gotrackTags...)
|
||||||
|
}
|
||||||
|
|
||||||
|
name := field.GoName
|
||||||
|
if field.Desc.IsWeak() {
|
||||||
|
name = genid.WeakFieldPrefix_goname + name
|
||||||
|
}
|
||||||
|
g.Annotate(m.GoIdent.GoName+"."+name, field.Location)
|
||||||
|
leadingComments := appendDeprecationSuffix(field.Comments.Leading,
|
||||||
|
field.Desc.Options().(*descriptorpb.FieldOptions).GetDeprecated())
|
||||||
|
g.P(leadingComments,
|
||||||
|
name, " ", goType, tags,
|
||||||
|
trailingComment(field.Comments.Trailing))
|
||||||
|
sf.append(field.GoName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// genMessageDefaultDecls generates consts and vars holding the default
|
||||||
|
// values of fields.
|
||||||
|
func genMessageDefaultDecls(g *protogen.GeneratedFile, f *fileInfo, m *messageInfo) {
|
||||||
|
var consts, vars []string
|
||||||
|
for _, field := range m.Fields {
|
||||||
|
if !field.Desc.HasDefault() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
name := "Default_" + m.GoIdent.GoName + "_" + field.GoName
|
||||||
|
goType, _ := fieldGoType(g, f, field)
|
||||||
|
defVal := field.Desc.Default()
|
||||||
|
switch field.Desc.Kind() {
|
||||||
|
case protoreflect.StringKind:
|
||||||
|
consts = append(consts, fmt.Sprintf("%s = %s(%q)", name, goType, defVal.String()))
|
||||||
|
case protoreflect.BytesKind:
|
||||||
|
vars = append(vars, fmt.Sprintf("%s = %s(%q)", name, goType, defVal.Bytes()))
|
||||||
|
case protoreflect.EnumKind:
|
||||||
|
idx := field.Desc.DefaultEnumValue().Index()
|
||||||
|
val := field.Enum.Values[idx]
|
||||||
|
if val.GoIdent.GoImportPath == f.GoImportPath {
|
||||||
|
consts = append(consts, fmt.Sprintf("%s = %s", name, g.QualifiedGoIdent(val.GoIdent)))
|
||||||
|
} else {
|
||||||
|
// If the enum value is declared in a different Go package,
|
||||||
|
// reference it by number since the name may not be correct.
|
||||||
|
// See https://github.com/golang/protobuf/issues/513.
|
||||||
|
consts = append(consts, fmt.Sprintf("%s = %s(%d) // %s",
|
||||||
|
name, g.QualifiedGoIdent(field.Enum.GoIdent), val.Desc.Number(), g.QualifiedGoIdent(val.GoIdent)))
|
||||||
|
}
|
||||||
|
case protoreflect.FloatKind, protoreflect.DoubleKind:
|
||||||
|
if f := defVal.Float(); math.IsNaN(f) || math.IsInf(f, 0) {
|
||||||
|
var fn, arg string
|
||||||
|
switch f := defVal.Float(); {
|
||||||
|
case math.IsInf(f, -1):
|
||||||
|
fn, arg = g.QualifiedGoIdent(mathPackage.Ident("Inf")), "-1"
|
||||||
|
case math.IsInf(f, +1):
|
||||||
|
fn, arg = g.QualifiedGoIdent(mathPackage.Ident("Inf")), "+1"
|
||||||
|
case math.IsNaN(f):
|
||||||
|
fn, arg = g.QualifiedGoIdent(mathPackage.Ident("NaN")), ""
|
||||||
|
}
|
||||||
|
vars = append(vars, fmt.Sprintf("%s = %s(%s(%s))", name, goType, fn, arg))
|
||||||
|
} else {
|
||||||
|
consts = append(consts, fmt.Sprintf("%s = %s(%v)", name, goType, f))
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
consts = append(consts, fmt.Sprintf("%s = %s(%v)", name, goType, defVal.Interface()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(consts) > 0 {
|
||||||
|
g.P("// Default values for ", m.GoIdent, " fields.")
|
||||||
|
g.P("const (")
|
||||||
|
for _, s := range consts {
|
||||||
|
g.P(s)
|
||||||
|
}
|
||||||
|
g.P(")")
|
||||||
|
}
|
||||||
|
if len(vars) > 0 {
|
||||||
|
g.P("// Default values for ", m.GoIdent, " fields.")
|
||||||
|
g.P("var (")
|
||||||
|
for _, s := range vars {
|
||||||
|
g.P(s)
|
||||||
|
}
|
||||||
|
g.P(")")
|
||||||
|
}
|
||||||
|
g.P()
|
||||||
|
}
|
||||||
|
|
||||||
|
func genMessageMethods(g *protogen.GeneratedFile, f *fileInfo, m *messageInfo) {
|
||||||
|
genMessageBaseMethods(g, f, m)
|
||||||
|
genMessageGetterMethods(g, f, m)
|
||||||
|
genMessageSetterMethods(g, f, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func genMessageBaseMethods(g *protogen.GeneratedFile, f *fileInfo, m *messageInfo) {
|
||||||
|
// Reset method.
|
||||||
|
g.P("func (x *", m.GoIdent, ") Reset() {")
|
||||||
|
g.P("*x = ", m.GoIdent, "{}")
|
||||||
|
g.P("if ", protoimplPackage.Ident("UnsafeEnabled"), " {")
|
||||||
|
g.P("mi := &", messageTypesVarName(f), "[", f.allMessagesByPtr[m], "]")
|
||||||
|
g.P("ms := ", protoimplPackage.Ident("X"), ".MessageStateOf(", protoimplPackage.Ident("Pointer"), "(x))")
|
||||||
|
g.P("ms.StoreMessageInfo(mi)")
|
||||||
|
g.P("}")
|
||||||
|
g.P("}")
|
||||||
|
g.P()
|
||||||
|
|
||||||
|
// String method.
|
||||||
|
g.P("func (x *", m.GoIdent, ") String() string {")
|
||||||
|
g.P("return ", protoimplPackage.Ident("X"), ".MessageStringOf(x)")
|
||||||
|
g.P("}")
|
||||||
|
g.P()
|
||||||
|
|
||||||
|
// ProtoMessage method.
|
||||||
|
g.P("func (*", m.GoIdent, ") ProtoMessage() {}")
|
||||||
|
g.P()
|
||||||
|
|
||||||
|
// ProtoReflect method.
|
||||||
|
genMessageReflectMethods(g, f, m)
|
||||||
|
|
||||||
|
// Descriptor method.
|
||||||
|
if m.genRawDescMethod {
|
||||||
|
var indexes []string
|
||||||
|
for i := 1; i < len(m.Location.Path); i += 2 {
|
||||||
|
indexes = append(indexes, strconv.Itoa(int(m.Location.Path[i])))
|
||||||
|
}
|
||||||
|
g.P("// Deprecated: Use ", m.GoIdent, ".ProtoReflect.Descriptor instead.")
|
||||||
|
g.P("func (*", m.GoIdent, ") Descriptor() ([]byte, []int) {")
|
||||||
|
g.P("return ", rawDescVarName(f), "GZIP(), []int{", strings.Join(indexes, ","), "}")
|
||||||
|
g.P("}")
|
||||||
|
g.P()
|
||||||
|
f.needRawDesc = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func genMessageGetterMethods(g *protogen.GeneratedFile, f *fileInfo, m *messageInfo) {
|
||||||
|
for _, field := range m.Fields {
|
||||||
|
genNoInterfacePragma(g, m.isTracked)
|
||||||
|
|
||||||
|
// Getter for parent oneof.
|
||||||
|
if oneof := field.Oneof; oneof != nil && oneof.Fields[0] == field && !oneof.Desc.IsSynthetic() {
|
||||||
|
g.Annotate(m.GoIdent.GoName+".Get"+oneof.GoName, oneof.Location)
|
||||||
|
g.P("func (m *", m.GoIdent.GoName, ") Get", oneof.GoName, "() ", oneofInterfaceName(oneof), " {")
|
||||||
|
g.P("if m != nil {")
|
||||||
|
g.P("return m.", oneof.GoName)
|
||||||
|
g.P("}")
|
||||||
|
g.P("return nil")
|
||||||
|
g.P("}")
|
||||||
|
g.P()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getter for message field.
|
||||||
|
goType, pointer := fieldGoType(g, f, field)
|
||||||
|
defaultValue := fieldDefaultValue(g, f, m, field)
|
||||||
|
g.Annotate(m.GoIdent.GoName+".Get"+field.GoName, field.Location)
|
||||||
|
leadingComments := appendDeprecationSuffix("",
|
||||||
|
field.Desc.Options().(*descriptorpb.FieldOptions).GetDeprecated())
|
||||||
|
switch {
|
||||||
|
case field.Desc.IsWeak():
|
||||||
|
g.P(leadingComments, "func (x *", m.GoIdent, ") Get", field.GoName, "() ", protoPackage.Ident("Message"), "{")
|
||||||
|
g.P("var w ", protoimplPackage.Ident("WeakFields"))
|
||||||
|
g.P("if x != nil {")
|
||||||
|
g.P("w = x.", genid.WeakFields_goname)
|
||||||
|
if m.isTracked {
|
||||||
|
g.P("_ = x.", genid.WeakFieldPrefix_goname+field.GoName)
|
||||||
|
}
|
||||||
|
g.P("}")
|
||||||
|
g.P("return ", protoimplPackage.Ident("X"), ".GetWeak(w, ", field.Desc.Number(), ", ", strconv.Quote(string(field.Message.Desc.FullName())), ")")
|
||||||
|
g.P("}")
|
||||||
|
case field.Oneof != nil && !field.Oneof.Desc.IsSynthetic():
|
||||||
|
g.P(leadingComments, "func (x *", m.GoIdent, ") Get", field.GoName, "() ", goType, " {")
|
||||||
|
g.P("if x, ok := x.Get", field.Oneof.GoName, "().(*", field.GoIdent, "); ok {")
|
||||||
|
g.P("return x.", field.GoName)
|
||||||
|
g.P("}")
|
||||||
|
g.P("return ", defaultValue)
|
||||||
|
g.P("}")
|
||||||
|
default:
|
||||||
|
g.P(leadingComments, "func (x *", m.GoIdent, ") Get", field.GoName, "() ", goType, " {")
|
||||||
|
if !field.Desc.HasPresence() || defaultValue == "nil" {
|
||||||
|
g.P("if x != nil {")
|
||||||
|
} else {
|
||||||
|
g.P("if x != nil && x.", field.GoName, " != nil {")
|
||||||
|
}
|
||||||
|
star := ""
|
||||||
|
if pointer {
|
||||||
|
star = "*"
|
||||||
|
}
|
||||||
|
g.P("return ", star, " x.", field.GoName)
|
||||||
|
g.P("}")
|
||||||
|
g.P("return ", defaultValue)
|
||||||
|
g.P("}")
|
||||||
|
}
|
||||||
|
g.P()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func genMessageSetterMethods(g *protogen.GeneratedFile, f *fileInfo, m *messageInfo) {
|
||||||
|
for _, field := range m.Fields {
|
||||||
|
if !field.Desc.IsWeak() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
genNoInterfacePragma(g, m.isTracked)
|
||||||
|
|
||||||
|
g.Annotate(m.GoIdent.GoName+".Set"+field.GoName, field.Location)
|
||||||
|
leadingComments := appendDeprecationSuffix("",
|
||||||
|
field.Desc.Options().(*descriptorpb.FieldOptions).GetDeprecated())
|
||||||
|
g.P(leadingComments, "func (x *", m.GoIdent, ") Set", field.GoName, "(v ", protoPackage.Ident("Message"), ") {")
|
||||||
|
g.P("var w *", protoimplPackage.Ident("WeakFields"))
|
||||||
|
g.P("if x != nil {")
|
||||||
|
g.P("w = &x.", genid.WeakFields_goname)
|
||||||
|
if m.isTracked {
|
||||||
|
g.P("_ = x.", genid.WeakFieldPrefix_goname+field.GoName)
|
||||||
|
}
|
||||||
|
g.P("}")
|
||||||
|
g.P(protoimplPackage.Ident("X"), ".SetWeak(w, ", field.Desc.Number(), ", ", strconv.Quote(string(field.Message.Desc.FullName())), ", v)")
|
||||||
|
g.P("}")
|
||||||
|
g.P()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fieldGoType returns the Go type used for a field.
|
||||||
|
//
|
||||||
|
// If it returns pointer=true, the struct field is a pointer to the type.
|
||||||
|
func fieldGoType(g *protogen.GeneratedFile, f *fileInfo, field *protogen.Field) (goType string, pointer bool) {
|
||||||
|
if field.Desc.IsWeak() {
|
||||||
|
return "struct{}", false
|
||||||
|
}
|
||||||
|
|
||||||
|
pointer = field.Desc.HasPresence()
|
||||||
|
switch field.Desc.Kind() {
|
||||||
|
case protoreflect.BoolKind:
|
||||||
|
goType = "bool"
|
||||||
|
case protoreflect.EnumKind:
|
||||||
|
goType = g.QualifiedGoIdent(field.Enum.GoIdent)
|
||||||
|
case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind:
|
||||||
|
goType = "int32"
|
||||||
|
case protoreflect.Uint32Kind, protoreflect.Fixed32Kind:
|
||||||
|
goType = "uint32"
|
||||||
|
case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind:
|
||||||
|
goType = "int64"
|
||||||
|
case protoreflect.Uint64Kind, protoreflect.Fixed64Kind:
|
||||||
|
goType = "uint64"
|
||||||
|
case protoreflect.FloatKind:
|
||||||
|
goType = "float32"
|
||||||
|
case protoreflect.DoubleKind:
|
||||||
|
goType = "float64"
|
||||||
|
case protoreflect.StringKind:
|
||||||
|
goType = "string"
|
||||||
|
case protoreflect.BytesKind:
|
||||||
|
goType = "[]byte"
|
||||||
|
pointer = false // rely on nullability of slices for presence
|
||||||
|
case protoreflect.MessageKind, protoreflect.GroupKind:
|
||||||
|
goType = "*" + g.QualifiedGoIdent(field.Message.GoIdent)
|
||||||
|
pointer = false // pointer captured as part of the type
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case field.Desc.IsList():
|
||||||
|
return "[]" + goType, false
|
||||||
|
case field.Desc.IsMap():
|
||||||
|
keyType, _ := fieldGoType(g, f, field.Message.Fields[0])
|
||||||
|
valType, _ := fieldGoType(g, f, field.Message.Fields[1])
|
||||||
|
return fmt.Sprintf("map[%v]%v", keyType, valType), false
|
||||||
|
}
|
||||||
|
return goType, pointer
|
||||||
|
}
|
||||||
|
|
||||||
|
func fieldProtobufTagValue(field *protogen.Field) string {
|
||||||
|
var enumName string
|
||||||
|
if field.Desc.Kind() == protoreflect.EnumKind {
|
||||||
|
enumName = protoimpl.X.LegacyEnumName(field.Enum.Desc)
|
||||||
|
}
|
||||||
|
return tag.Marshal(field.Desc, enumName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func fieldDefaultValue(g *protogen.GeneratedFile, f *fileInfo, m *messageInfo, field *protogen.Field) string {
|
||||||
|
if field.Desc.IsList() {
|
||||||
|
return "nil"
|
||||||
|
}
|
||||||
|
if field.Desc.HasDefault() {
|
||||||
|
defVarName := "Default_" + m.GoIdent.GoName + "_" + field.GoName
|
||||||
|
if field.Desc.Kind() == protoreflect.BytesKind {
|
||||||
|
return "append([]byte(nil), " + defVarName + "...)"
|
||||||
|
}
|
||||||
|
return defVarName
|
||||||
|
}
|
||||||
|
switch field.Desc.Kind() {
|
||||||
|
case protoreflect.BoolKind:
|
||||||
|
return "false"
|
||||||
|
case protoreflect.StringKind:
|
||||||
|
return `""`
|
||||||
|
case protoreflect.MessageKind, protoreflect.GroupKind, protoreflect.BytesKind:
|
||||||
|
return "nil"
|
||||||
|
case protoreflect.EnumKind:
|
||||||
|
val := field.Enum.Values[0]
|
||||||
|
if val.GoIdent.GoImportPath == f.GoImportPath {
|
||||||
|
return g.QualifiedGoIdent(val.GoIdent)
|
||||||
|
} else {
|
||||||
|
// If the enum value is declared in a different Go package,
|
||||||
|
// reference it by number since the name may not be correct.
|
||||||
|
// See https://github.com/golang/protobuf/issues/513.
|
||||||
|
return g.QualifiedGoIdent(field.Enum.GoIdent) + "(" + strconv.FormatInt(int64(val.Desc.Number()), 10) + ")"
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return "0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func fieldJSONTagValue(field *protogen.Field) string {
|
||||||
|
return string(field.Desc.Name()) + ",omitempty"
|
||||||
|
}
|
||||||
|
|
||||||
|
func genExtensions(g *protogen.GeneratedFile, f *fileInfo) {
|
||||||
|
if len(f.allExtensions) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
g.P("var ", extensionTypesVarName(f), " = []", protoimplPackage.Ident("ExtensionInfo"), "{")
|
||||||
|
for _, x := range f.allExtensions {
|
||||||
|
g.P("{")
|
||||||
|
g.P("ExtendedType: (*", x.Extendee.GoIdent, ")(nil),")
|
||||||
|
goType, pointer := fieldGoType(g, f, x.Extension)
|
||||||
|
if pointer {
|
||||||
|
goType = "*" + goType
|
||||||
|
}
|
||||||
|
g.P("ExtensionType: (", goType, ")(nil),")
|
||||||
|
g.P("Field: ", x.Desc.Number(), ",")
|
||||||
|
g.P("Name: ", strconv.Quote(string(x.Desc.FullName())), ",")
|
||||||
|
g.P("Tag: ", strconv.Quote(fieldProtobufTagValue(x.Extension)), ",")
|
||||||
|
g.P("Filename: ", strconv.Quote(f.Desc.Path()), ",")
|
||||||
|
g.P("},")
|
||||||
|
}
|
||||||
|
g.P("}")
|
||||||
|
g.P()
|
||||||
|
|
||||||
|
// Group extensions by the target message.
|
||||||
|
var orderedTargets []protogen.GoIdent
|
||||||
|
allExtensionsByTarget := make(map[protogen.GoIdent][]*extensionInfo)
|
||||||
|
allExtensionsByPtr := make(map[*extensionInfo]int)
|
||||||
|
for i, x := range f.allExtensions {
|
||||||
|
target := x.Extendee.GoIdent
|
||||||
|
if len(allExtensionsByTarget[target]) == 0 {
|
||||||
|
orderedTargets = append(orderedTargets, target)
|
||||||
|
}
|
||||||
|
allExtensionsByTarget[target] = append(allExtensionsByTarget[target], x)
|
||||||
|
allExtensionsByPtr[x] = i
|
||||||
|
}
|
||||||
|
for _, target := range orderedTargets {
|
||||||
|
g.P("// Extension fields to ", target, ".")
|
||||||
|
g.P("var (")
|
||||||
|
for _, x := range allExtensionsByTarget[target] {
|
||||||
|
xd := x.Desc
|
||||||
|
typeName := xd.Kind().String()
|
||||||
|
switch xd.Kind() {
|
||||||
|
case protoreflect.EnumKind:
|
||||||
|
typeName = string(xd.Enum().FullName())
|
||||||
|
case protoreflect.MessageKind, protoreflect.GroupKind:
|
||||||
|
typeName = string(xd.Message().FullName())
|
||||||
|
}
|
||||||
|
fieldName := string(xd.Name())
|
||||||
|
|
||||||
|
leadingComments := x.Comments.Leading
|
||||||
|
if leadingComments != "" {
|
||||||
|
leadingComments += "\n"
|
||||||
|
}
|
||||||
|
leadingComments += protogen.Comments(fmt.Sprintf(" %v %v %v = %v;\n",
|
||||||
|
xd.Cardinality(), typeName, fieldName, xd.Number()))
|
||||||
|
leadingComments = appendDeprecationSuffix(leadingComments,
|
||||||
|
x.Desc.Options().(*descriptorpb.FieldOptions).GetDeprecated())
|
||||||
|
g.P(leadingComments,
|
||||||
|
"E_", x.GoIdent, " = &", extensionTypesVarName(f), "[", allExtensionsByPtr[x], "]",
|
||||||
|
trailingComment(x.Comments.Trailing))
|
||||||
|
}
|
||||||
|
g.P(")")
|
||||||
|
g.P()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// genMessageOneofWrapperTypes generates the oneof wrapper types and
|
||||||
|
// associates the types with the parent message type.
|
||||||
|
func genMessageOneofWrapperTypes(g *protogen.GeneratedFile, f *fileInfo, m *messageInfo) {
|
||||||
|
for _, oneof := range m.Oneofs {
|
||||||
|
if oneof.Desc.IsSynthetic() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ifName := oneofInterfaceName(oneof)
|
||||||
|
g.P("type ", ifName, " interface {")
|
||||||
|
g.P(ifName, "()")
|
||||||
|
g.P("}")
|
||||||
|
g.P()
|
||||||
|
for _, field := range oneof.Fields {
|
||||||
|
g.Annotate(field.GoIdent.GoName, field.Location)
|
||||||
|
g.Annotate(field.GoIdent.GoName+"."+field.GoName, field.Location)
|
||||||
|
g.P("type ", field.GoIdent, " struct {")
|
||||||
|
goType, _ := fieldGoType(g, f, field)
|
||||||
|
tags := structTags{
|
||||||
|
{"protobuf", fieldProtobufTagValue(field)},
|
||||||
|
}
|
||||||
|
if m.isTracked {
|
||||||
|
tags = append(tags, gotrackTags...)
|
||||||
|
}
|
||||||
|
leadingComments := appendDeprecationSuffix(field.Comments.Leading,
|
||||||
|
field.Desc.Options().(*descriptorpb.FieldOptions).GetDeprecated())
|
||||||
|
g.P(leadingComments,
|
||||||
|
field.GoName, " ", goType, tags,
|
||||||
|
trailingComment(field.Comments.Trailing))
|
||||||
|
g.P("}")
|
||||||
|
g.P()
|
||||||
|
}
|
||||||
|
for _, field := range oneof.Fields {
|
||||||
|
g.P("func (*", field.GoIdent, ") ", ifName, "() {}")
|
||||||
|
g.P()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// oneofInterfaceName returns the name of the interface type implemented by
|
||||||
|
// the oneof field value types.
|
||||||
|
func oneofInterfaceName(oneof *protogen.Oneof) string {
|
||||||
|
return "is" + oneof.GoIdent.GoName
|
||||||
|
}
|
||||||
|
|
||||||
|
// genNoInterfacePragma generates a standalone "nointerface" pragma to
|
||||||
|
// decorate methods with field-tracking support.
|
||||||
|
func genNoInterfacePragma(g *protogen.GeneratedFile, tracked bool) {
|
||||||
|
if tracked {
|
||||||
|
g.P("//go:nointerface")
|
||||||
|
g.P()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var gotrackTags = structTags{{"go", "track"}}
|
||||||
|
|
||||||
|
// structTags is a data structure for build idiomatic Go struct tags.
|
||||||
|
// Each [2]string is a key-value pair, where value is the unescaped string.
|
||||||
|
//
|
||||||
|
// Example: structTags{{"key", "value"}}.String() -> `key:"value"`
|
||||||
|
type structTags [][2]string
|
||||||
|
|
||||||
|
func (tags structTags) String() string {
|
||||||
|
if len(tags) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
var ss []string
|
||||||
|
for _, tag := range tags {
|
||||||
|
// NOTE: When quoting the value, we need to make sure the backtick
|
||||||
|
// character does not appear. Convert all cases to the escaped hex form.
|
||||||
|
key := tag[0]
|
||||||
|
val := strings.Replace(strconv.Quote(tag[1]), "`", `\x60`, -1)
|
||||||
|
ss = append(ss, fmt.Sprintf("%s:%s", key, val))
|
||||||
|
}
|
||||||
|
return "`" + strings.Join(ss, " ") + "`"
|
||||||
|
}
|
||||||
|
|
||||||
|
// appendDeprecationSuffix optionally appends a deprecation notice as a suffix.
|
||||||
|
func appendDeprecationSuffix(prefix protogen.Comments, deprecated bool) protogen.Comments {
|
||||||
|
if !deprecated {
|
||||||
|
return prefix
|
||||||
|
}
|
||||||
|
if prefix != "" {
|
||||||
|
prefix += "\n"
|
||||||
|
}
|
||||||
|
return prefix + " Deprecated: Do not use.\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
// trailingComment is like protogen.Comments, but lacks a trailing newline.
|
||||||
|
type trailingComment protogen.Comments
|
||||||
|
|
||||||
|
func (c trailingComment) String() string {
|
||||||
|
s := strings.TrimSuffix(protogen.Comments(c).String(), "\n")
|
||||||
|
if strings.Contains(s, "\n") {
|
||||||
|
// We don't support multi-lined trailing comments as it is unclear
|
||||||
|
// how to best render them in the generated code.
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
351
vendor/google.golang.org/protobuf/cmd/protoc-gen-go/internal_gengo/reflect.go
generated
vendored
Normal file
351
vendor/google.golang.org/protobuf/cmd/protoc-gen-go/internal_gengo/reflect.go
generated
vendored
Normal file
|
@ -0,0 +1,351 @@
|
||||||
|
// Copyright 2018 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package internal_gengo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"strings"
|
||||||
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"google.golang.org/protobuf/compiler/protogen"
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
"google.golang.org/protobuf/reflect/protoreflect"
|
||||||
|
|
||||||
|
"google.golang.org/protobuf/types/descriptorpb"
|
||||||
|
)
|
||||||
|
|
||||||
|
func genReflectFileDescriptor(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo) {
|
||||||
|
g.P("var ", f.GoDescriptorIdent, " ", protoreflectPackage.Ident("FileDescriptor"))
|
||||||
|
g.P()
|
||||||
|
|
||||||
|
genFileDescriptor(gen, g, f)
|
||||||
|
if len(f.allEnums) > 0 {
|
||||||
|
g.P("var ", enumTypesVarName(f), " = make([]", protoimplPackage.Ident("EnumInfo"), ",", len(f.allEnums), ")")
|
||||||
|
}
|
||||||
|
if len(f.allMessages) > 0 {
|
||||||
|
g.P("var ", messageTypesVarName(f), " = make([]", protoimplPackage.Ident("MessageInfo"), ",", len(f.allMessages), ")")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate a unique list of Go types for all declarations and dependencies,
|
||||||
|
// and the associated index into the type list for all dependencies.
|
||||||
|
var goTypes []string
|
||||||
|
var depIdxs []string
|
||||||
|
seen := map[protoreflect.FullName]int{}
|
||||||
|
genDep := func(name protoreflect.FullName, depSource string) {
|
||||||
|
if depSource != "" {
|
||||||
|
line := fmt.Sprintf("%d, // %d: %s -> %s", seen[name], len(depIdxs), depSource, name)
|
||||||
|
depIdxs = append(depIdxs, line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
genEnum := func(e *protogen.Enum, depSource string) {
|
||||||
|
if e != nil {
|
||||||
|
name := e.Desc.FullName()
|
||||||
|
if _, ok := seen[name]; !ok {
|
||||||
|
line := fmt.Sprintf("(%s)(0), // %d: %s", g.QualifiedGoIdent(e.GoIdent), len(goTypes), name)
|
||||||
|
goTypes = append(goTypes, line)
|
||||||
|
seen[name] = len(seen)
|
||||||
|
}
|
||||||
|
if depSource != "" {
|
||||||
|
genDep(name, depSource)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
genMessage := func(m *protogen.Message, depSource string) {
|
||||||
|
if m != nil {
|
||||||
|
name := m.Desc.FullName()
|
||||||
|
if _, ok := seen[name]; !ok {
|
||||||
|
line := fmt.Sprintf("(*%s)(nil), // %d: %s", g.QualifiedGoIdent(m.GoIdent), len(goTypes), name)
|
||||||
|
if m.Desc.IsMapEntry() {
|
||||||
|
// Map entry messages have no associated Go type.
|
||||||
|
line = fmt.Sprintf("nil, // %d: %s", len(goTypes), name)
|
||||||
|
}
|
||||||
|
goTypes = append(goTypes, line)
|
||||||
|
seen[name] = len(seen)
|
||||||
|
}
|
||||||
|
if depSource != "" {
|
||||||
|
genDep(name, depSource)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This ordering is significant.
|
||||||
|
// See filetype.TypeBuilder.DependencyIndexes.
|
||||||
|
type offsetEntry struct {
|
||||||
|
start int
|
||||||
|
name string
|
||||||
|
}
|
||||||
|
var depOffsets []offsetEntry
|
||||||
|
for _, enum := range f.allEnums {
|
||||||
|
genEnum(enum.Enum, "")
|
||||||
|
}
|
||||||
|
for _, message := range f.allMessages {
|
||||||
|
genMessage(message.Message, "")
|
||||||
|
}
|
||||||
|
depOffsets = append(depOffsets, offsetEntry{len(depIdxs), "field type_name"})
|
||||||
|
for _, message := range f.allMessages {
|
||||||
|
for _, field := range message.Fields {
|
||||||
|
if field.Desc.IsWeak() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
source := string(field.Desc.FullName())
|
||||||
|
genEnum(field.Enum, source+":type_name")
|
||||||
|
genMessage(field.Message, source+":type_name")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
depOffsets = append(depOffsets, offsetEntry{len(depIdxs), "extension extendee"})
|
||||||
|
for _, extension := range f.allExtensions {
|
||||||
|
source := string(extension.Desc.FullName())
|
||||||
|
genMessage(extension.Extendee, source+":extendee")
|
||||||
|
}
|
||||||
|
depOffsets = append(depOffsets, offsetEntry{len(depIdxs), "extension type_name"})
|
||||||
|
for _, extension := range f.allExtensions {
|
||||||
|
source := string(extension.Desc.FullName())
|
||||||
|
genEnum(extension.Enum, source+":type_name")
|
||||||
|
genMessage(extension.Message, source+":type_name")
|
||||||
|
}
|
||||||
|
depOffsets = append(depOffsets, offsetEntry{len(depIdxs), "method input_type"})
|
||||||
|
for _, service := range f.Services {
|
||||||
|
for _, method := range service.Methods {
|
||||||
|
source := string(method.Desc.FullName())
|
||||||
|
genMessage(method.Input, source+":input_type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
depOffsets = append(depOffsets, offsetEntry{len(depIdxs), "method output_type"})
|
||||||
|
for _, service := range f.Services {
|
||||||
|
for _, method := range service.Methods {
|
||||||
|
source := string(method.Desc.FullName())
|
||||||
|
genMessage(method.Output, source+":output_type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
depOffsets = append(depOffsets, offsetEntry{len(depIdxs), ""})
|
||||||
|
for i := len(depOffsets) - 2; i >= 0; i-- {
|
||||||
|
curr, next := depOffsets[i], depOffsets[i+1]
|
||||||
|
depIdxs = append(depIdxs, fmt.Sprintf("%d, // [%d:%d] is the sub-list for %s",
|
||||||
|
curr.start, curr.start, next.start, curr.name))
|
||||||
|
}
|
||||||
|
if len(depIdxs) > math.MaxInt32 {
|
||||||
|
panic("too many dependencies") // sanity check
|
||||||
|
}
|
||||||
|
|
||||||
|
g.P("var ", goTypesVarName(f), " = []interface{}{")
|
||||||
|
for _, s := range goTypes {
|
||||||
|
g.P(s)
|
||||||
|
}
|
||||||
|
g.P("}")
|
||||||
|
|
||||||
|
g.P("var ", depIdxsVarName(f), " = []int32{")
|
||||||
|
for _, s := range depIdxs {
|
||||||
|
g.P(s)
|
||||||
|
}
|
||||||
|
g.P("}")
|
||||||
|
|
||||||
|
g.P("func init() { ", initFuncName(f.File), "() }")
|
||||||
|
|
||||||
|
g.P("func ", initFuncName(f.File), "() {")
|
||||||
|
g.P("if ", f.GoDescriptorIdent, " != nil {")
|
||||||
|
g.P("return")
|
||||||
|
g.P("}")
|
||||||
|
|
||||||
|
// Ensure that initialization functions for different files in the same Go
|
||||||
|
// package run in the correct order: Call the init funcs for every .proto file
|
||||||
|
// imported by this one that is in the same Go package.
|
||||||
|
for i, imps := 0, f.Desc.Imports(); i < imps.Len(); i++ {
|
||||||
|
impFile := gen.FilesByPath[imps.Get(i).Path()]
|
||||||
|
if impFile.GoImportPath != f.GoImportPath {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
g.P(initFuncName(impFile), "()")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(f.allMessages) > 0 {
|
||||||
|
// Populate MessageInfo.Exporters.
|
||||||
|
g.P("if !", protoimplPackage.Ident("UnsafeEnabled"), " {")
|
||||||
|
for _, message := range f.allMessages {
|
||||||
|
if sf := f.allMessageFieldsByPtr[message]; len(sf.unexported) > 0 {
|
||||||
|
idx := f.allMessagesByPtr[message]
|
||||||
|
typesVar := messageTypesVarName(f)
|
||||||
|
|
||||||
|
g.P(typesVar, "[", idx, "].Exporter = func(v interface{}, i int) interface{} {")
|
||||||
|
g.P("switch v := v.(*", message.GoIdent, "); i {")
|
||||||
|
for i := 0; i < sf.count; i++ {
|
||||||
|
if name := sf.unexported[i]; name != "" {
|
||||||
|
g.P("case ", i, ": return &v.", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g.P("default: return nil")
|
||||||
|
g.P("}")
|
||||||
|
g.P("}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g.P("}")
|
||||||
|
|
||||||
|
// Populate MessageInfo.OneofWrappers.
|
||||||
|
for _, message := range f.allMessages {
|
||||||
|
if len(message.Oneofs) > 0 {
|
||||||
|
idx := f.allMessagesByPtr[message]
|
||||||
|
typesVar := messageTypesVarName(f)
|
||||||
|
|
||||||
|
// Associate the wrapper types by directly passing them to the MessageInfo.
|
||||||
|
g.P(typesVar, "[", idx, "].OneofWrappers = []interface{} {")
|
||||||
|
for _, oneof := range message.Oneofs {
|
||||||
|
if !oneof.Desc.IsSynthetic() {
|
||||||
|
for _, field := range oneof.Fields {
|
||||||
|
g.P("(*", field.GoIdent, ")(nil),")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g.P("}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g.P("type x struct{}")
|
||||||
|
g.P("out := ", protoimplPackage.Ident("TypeBuilder"), "{")
|
||||||
|
g.P("File: ", protoimplPackage.Ident("DescBuilder"), "{")
|
||||||
|
g.P("GoPackagePath: ", reflectPackage.Ident("TypeOf"), "(x{}).PkgPath(),")
|
||||||
|
g.P("RawDescriptor: ", rawDescVarName(f), ",")
|
||||||
|
g.P("NumEnums: ", len(f.allEnums), ",")
|
||||||
|
g.P("NumMessages: ", len(f.allMessages), ",")
|
||||||
|
g.P("NumExtensions: ", len(f.allExtensions), ",")
|
||||||
|
g.P("NumServices: ", len(f.Services), ",")
|
||||||
|
g.P("},")
|
||||||
|
g.P("GoTypes: ", goTypesVarName(f), ",")
|
||||||
|
g.P("DependencyIndexes: ", depIdxsVarName(f), ",")
|
||||||
|
if len(f.allEnums) > 0 {
|
||||||
|
g.P("EnumInfos: ", enumTypesVarName(f), ",")
|
||||||
|
}
|
||||||
|
if len(f.allMessages) > 0 {
|
||||||
|
g.P("MessageInfos: ", messageTypesVarName(f), ",")
|
||||||
|
}
|
||||||
|
if len(f.allExtensions) > 0 {
|
||||||
|
g.P("ExtensionInfos: ", extensionTypesVarName(f), ",")
|
||||||
|
}
|
||||||
|
g.P("}.Build()")
|
||||||
|
g.P(f.GoDescriptorIdent, " = out.File")
|
||||||
|
|
||||||
|
// Set inputs to nil to allow GC to reclaim resources.
|
||||||
|
g.P(rawDescVarName(f), " = nil")
|
||||||
|
g.P(goTypesVarName(f), " = nil")
|
||||||
|
g.P(depIdxsVarName(f), " = nil")
|
||||||
|
g.P("}")
|
||||||
|
}
|
||||||
|
|
||||||
|
func genFileDescriptor(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo) {
|
||||||
|
descProto := proto.Clone(f.Proto).(*descriptorpb.FileDescriptorProto)
|
||||||
|
descProto.SourceCodeInfo = nil // drop source code information
|
||||||
|
|
||||||
|
b, err := proto.MarshalOptions{AllowPartial: true, Deterministic: true}.Marshal(descProto)
|
||||||
|
if err != nil {
|
||||||
|
gen.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
g.P("var ", rawDescVarName(f), " = []byte{")
|
||||||
|
for len(b) > 0 {
|
||||||
|
n := 16
|
||||||
|
if n > len(b) {
|
||||||
|
n = len(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
s := ""
|
||||||
|
for _, c := range b[:n] {
|
||||||
|
s += fmt.Sprintf("0x%02x,", c)
|
||||||
|
}
|
||||||
|
g.P(s)
|
||||||
|
|
||||||
|
b = b[n:]
|
||||||
|
}
|
||||||
|
g.P("}")
|
||||||
|
g.P()
|
||||||
|
|
||||||
|
if f.needRawDesc {
|
||||||
|
onceVar := rawDescVarName(f) + "Once"
|
||||||
|
dataVar := rawDescVarName(f) + "Data"
|
||||||
|
g.P("var (")
|
||||||
|
g.P(onceVar, " ", syncPackage.Ident("Once"))
|
||||||
|
g.P(dataVar, " = ", rawDescVarName(f))
|
||||||
|
g.P(")")
|
||||||
|
g.P()
|
||||||
|
|
||||||
|
g.P("func ", rawDescVarName(f), "GZIP() []byte {")
|
||||||
|
g.P(onceVar, ".Do(func() {")
|
||||||
|
g.P(dataVar, " = ", protoimplPackage.Ident("X"), ".CompressGZIP(", dataVar, ")")
|
||||||
|
g.P("})")
|
||||||
|
g.P("return ", dataVar)
|
||||||
|
g.P("}")
|
||||||
|
g.P()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func genEnumReflectMethods(g *protogen.GeneratedFile, f *fileInfo, e *enumInfo) {
|
||||||
|
idx := f.allEnumsByPtr[e]
|
||||||
|
typesVar := enumTypesVarName(f)
|
||||||
|
|
||||||
|
// Descriptor method.
|
||||||
|
g.P("func (", e.GoIdent, ") Descriptor() ", protoreflectPackage.Ident("EnumDescriptor"), " {")
|
||||||
|
g.P("return ", typesVar, "[", idx, "].Descriptor()")
|
||||||
|
g.P("}")
|
||||||
|
g.P()
|
||||||
|
|
||||||
|
// Type method.
|
||||||
|
g.P("func (", e.GoIdent, ") Type() ", protoreflectPackage.Ident("EnumType"), " {")
|
||||||
|
g.P("return &", typesVar, "[", idx, "]")
|
||||||
|
g.P("}")
|
||||||
|
g.P()
|
||||||
|
|
||||||
|
// Number method.
|
||||||
|
g.P("func (x ", e.GoIdent, ") Number() ", protoreflectPackage.Ident("EnumNumber"), " {")
|
||||||
|
g.P("return ", protoreflectPackage.Ident("EnumNumber"), "(x)")
|
||||||
|
g.P("}")
|
||||||
|
g.P()
|
||||||
|
}
|
||||||
|
|
||||||
|
func genMessageReflectMethods(g *protogen.GeneratedFile, f *fileInfo, m *messageInfo) {
|
||||||
|
idx := f.allMessagesByPtr[m]
|
||||||
|
typesVar := messageTypesVarName(f)
|
||||||
|
|
||||||
|
// ProtoReflect method.
|
||||||
|
g.P("func (x *", m.GoIdent, ") ProtoReflect() ", protoreflectPackage.Ident("Message"), " {")
|
||||||
|
g.P("mi := &", typesVar, "[", idx, "]")
|
||||||
|
g.P("if ", protoimplPackage.Ident("UnsafeEnabled"), " && x != nil {")
|
||||||
|
g.P("ms := ", protoimplPackage.Ident("X"), ".MessageStateOf(", protoimplPackage.Ident("Pointer"), "(x))")
|
||||||
|
g.P("if ms.LoadMessageInfo() == nil {")
|
||||||
|
g.P("ms.StoreMessageInfo(mi)")
|
||||||
|
g.P("}")
|
||||||
|
g.P("return ms")
|
||||||
|
g.P("}")
|
||||||
|
g.P("return mi.MessageOf(x)")
|
||||||
|
g.P("}")
|
||||||
|
g.P()
|
||||||
|
}
|
||||||
|
|
||||||
|
func fileVarName(f *protogen.File, suffix string) string {
|
||||||
|
prefix := f.GoDescriptorIdent.GoName
|
||||||
|
_, n := utf8.DecodeRuneInString(prefix)
|
||||||
|
prefix = strings.ToLower(prefix[:n]) + prefix[n:]
|
||||||
|
return prefix + "_" + suffix
|
||||||
|
}
|
||||||
|
func rawDescVarName(f *fileInfo) string {
|
||||||
|
return fileVarName(f.File, "rawDesc")
|
||||||
|
}
|
||||||
|
func goTypesVarName(f *fileInfo) string {
|
||||||
|
return fileVarName(f.File, "goTypes")
|
||||||
|
}
|
||||||
|
func depIdxsVarName(f *fileInfo) string {
|
||||||
|
return fileVarName(f.File, "depIdxs")
|
||||||
|
}
|
||||||
|
func enumTypesVarName(f *fileInfo) string {
|
||||||
|
return fileVarName(f.File, "enumTypes")
|
||||||
|
}
|
||||||
|
func messageTypesVarName(f *fileInfo) string {
|
||||||
|
return fileVarName(f.File, "msgTypes")
|
||||||
|
}
|
||||||
|
func extensionTypesVarName(f *fileInfo) string {
|
||||||
|
return fileVarName(f.File, "extTypes")
|
||||||
|
}
|
||||||
|
func initFuncName(f *protogen.File) string {
|
||||||
|
return fileVarName(f, "init")
|
||||||
|
}
|
1080
vendor/google.golang.org/protobuf/cmd/protoc-gen-go/internal_gengo/well_known_types.go
generated
vendored
Normal file
1080
vendor/google.golang.org/protobuf/cmd/protoc-gen-go/internal_gengo/well_known_types.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,653 @@
|
||||||
|
// Protocol Buffers - Google's data interchange format
|
||||||
|
// Copyright 2008 Google Inc. All rights reserved.
|
||||||
|
// https://developers.google.com/protocol-buffers/
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
// Author: kenton@google.com (Kenton Varda)
|
||||||
|
//
|
||||||
|
// WARNING: The plugin interface is currently EXPERIMENTAL and is subject to
|
||||||
|
// change.
|
||||||
|
//
|
||||||
|
// protoc (aka the Protocol Compiler) can be extended via plugins. A plugin is
|
||||||
|
// just a program that reads a CodeGeneratorRequest from stdin and writes a
|
||||||
|
// CodeGeneratorResponse to stdout.
|
||||||
|
//
|
||||||
|
// Plugins written using C++ can use google/protobuf/compiler/plugin.h instead
|
||||||
|
// of dealing with the raw protocol defined here.
|
||||||
|
//
|
||||||
|
// A plugin executable needs only to be placed somewhere in the path. The
|
||||||
|
// plugin should be named "protoc-gen-$NAME", and will then be used when the
|
||||||
|
// flag "--${NAME}_out" is passed to protoc.
|
||||||
|
|
||||||
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
|
// source: google/protobuf/compiler/plugin.proto
|
||||||
|
|
||||||
|
package pluginpb
|
||||||
|
|
||||||
|
import (
|
||||||
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
|
descriptorpb "google.golang.org/protobuf/types/descriptorpb"
|
||||||
|
reflect "reflect"
|
||||||
|
sync "sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Sync with code_generator.h.
|
||||||
|
type CodeGeneratorResponse_Feature int32
|
||||||
|
|
||||||
|
const (
|
||||||
|
CodeGeneratorResponse_FEATURE_NONE CodeGeneratorResponse_Feature = 0
|
||||||
|
CodeGeneratorResponse_FEATURE_PROTO3_OPTIONAL CodeGeneratorResponse_Feature = 1
|
||||||
|
)
|
||||||
|
|
||||||
|
// Enum value maps for CodeGeneratorResponse_Feature.
|
||||||
|
var (
|
||||||
|
CodeGeneratorResponse_Feature_name = map[int32]string{
|
||||||
|
0: "FEATURE_NONE",
|
||||||
|
1: "FEATURE_PROTO3_OPTIONAL",
|
||||||
|
}
|
||||||
|
CodeGeneratorResponse_Feature_value = map[string]int32{
|
||||||
|
"FEATURE_NONE": 0,
|
||||||
|
"FEATURE_PROTO3_OPTIONAL": 1,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (x CodeGeneratorResponse_Feature) Enum() *CodeGeneratorResponse_Feature {
|
||||||
|
p := new(CodeGeneratorResponse_Feature)
|
||||||
|
*p = x
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x CodeGeneratorResponse_Feature) String() string {
|
||||||
|
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (CodeGeneratorResponse_Feature) Descriptor() protoreflect.EnumDescriptor {
|
||||||
|
return file_google_protobuf_compiler_plugin_proto_enumTypes[0].Descriptor()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (CodeGeneratorResponse_Feature) Type() protoreflect.EnumType {
|
||||||
|
return &file_google_protobuf_compiler_plugin_proto_enumTypes[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x CodeGeneratorResponse_Feature) Number() protoreflect.EnumNumber {
|
||||||
|
return protoreflect.EnumNumber(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Do not use.
|
||||||
|
func (x *CodeGeneratorResponse_Feature) UnmarshalJSON(b []byte) error {
|
||||||
|
num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*x = CodeGeneratorResponse_Feature(num)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use CodeGeneratorResponse_Feature.Descriptor instead.
|
||||||
|
func (CodeGeneratorResponse_Feature) EnumDescriptor() ([]byte, []int) {
|
||||||
|
return file_google_protobuf_compiler_plugin_proto_rawDescGZIP(), []int{2, 0}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The version number of protocol compiler.
|
||||||
|
type Version struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
Major *int32 `protobuf:"varint,1,opt,name=major" json:"major,omitempty"`
|
||||||
|
Minor *int32 `protobuf:"varint,2,opt,name=minor" json:"minor,omitempty"`
|
||||||
|
Patch *int32 `protobuf:"varint,3,opt,name=patch" json:"patch,omitempty"`
|
||||||
|
// A suffix for alpha, beta or rc release, e.g., "alpha-1", "rc2". It should
|
||||||
|
// be empty for mainline stable releases.
|
||||||
|
Suffix *string `protobuf:"bytes,4,opt,name=suffix" json:"suffix,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Version) Reset() {
|
||||||
|
*x = Version{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_google_protobuf_compiler_plugin_proto_msgTypes[0]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Version) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Version) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *Version) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_google_protobuf_compiler_plugin_proto_msgTypes[0]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use Version.ProtoReflect.Descriptor instead.
|
||||||
|
func (*Version) Descriptor() ([]byte, []int) {
|
||||||
|
return file_google_protobuf_compiler_plugin_proto_rawDescGZIP(), []int{0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Version) GetMajor() int32 {
|
||||||
|
if x != nil && x.Major != nil {
|
||||||
|
return *x.Major
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Version) GetMinor() int32 {
|
||||||
|
if x != nil && x.Minor != nil {
|
||||||
|
return *x.Minor
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Version) GetPatch() int32 {
|
||||||
|
if x != nil && x.Patch != nil {
|
||||||
|
return *x.Patch
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Version) GetSuffix() string {
|
||||||
|
if x != nil && x.Suffix != nil {
|
||||||
|
return *x.Suffix
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// An encoded CodeGeneratorRequest is written to the plugin's stdin.
|
||||||
|
type CodeGeneratorRequest struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
// The .proto files that were explicitly listed on the command-line. The
|
||||||
|
// code generator should generate code only for these files. Each file's
|
||||||
|
// descriptor will be included in proto_file, below.
|
||||||
|
FileToGenerate []string `protobuf:"bytes,1,rep,name=file_to_generate,json=fileToGenerate" json:"file_to_generate,omitempty"`
|
||||||
|
// The generator parameter passed on the command-line.
|
||||||
|
Parameter *string `protobuf:"bytes,2,opt,name=parameter" json:"parameter,omitempty"`
|
||||||
|
// FileDescriptorProtos for all files in files_to_generate and everything
|
||||||
|
// they import. The files will appear in topological order, so each file
|
||||||
|
// appears before any file that imports it.
|
||||||
|
//
|
||||||
|
// protoc guarantees that all proto_files will be written after
|
||||||
|
// the fields above, even though this is not technically guaranteed by the
|
||||||
|
// protobuf wire format. This theoretically could allow a plugin to stream
|
||||||
|
// in the FileDescriptorProtos and handle them one by one rather than read
|
||||||
|
// the entire set into memory at once. However, as of this writing, this
|
||||||
|
// is not similarly optimized on protoc's end -- it will store all fields in
|
||||||
|
// memory at once before sending them to the plugin.
|
||||||
|
//
|
||||||
|
// Type names of fields and extensions in the FileDescriptorProto are always
|
||||||
|
// fully qualified.
|
||||||
|
ProtoFile []*descriptorpb.FileDescriptorProto `protobuf:"bytes,15,rep,name=proto_file,json=protoFile" json:"proto_file,omitempty"`
|
||||||
|
// The version number of protocol compiler.
|
||||||
|
CompilerVersion *Version `protobuf:"bytes,3,opt,name=compiler_version,json=compilerVersion" json:"compiler_version,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CodeGeneratorRequest) Reset() {
|
||||||
|
*x = CodeGeneratorRequest{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_google_protobuf_compiler_plugin_proto_msgTypes[1]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CodeGeneratorRequest) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*CodeGeneratorRequest) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *CodeGeneratorRequest) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_google_protobuf_compiler_plugin_proto_msgTypes[1]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use CodeGeneratorRequest.ProtoReflect.Descriptor instead.
|
||||||
|
func (*CodeGeneratorRequest) Descriptor() ([]byte, []int) {
|
||||||
|
return file_google_protobuf_compiler_plugin_proto_rawDescGZIP(), []int{1}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CodeGeneratorRequest) GetFileToGenerate() []string {
|
||||||
|
if x != nil {
|
||||||
|
return x.FileToGenerate
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CodeGeneratorRequest) GetParameter() string {
|
||||||
|
if x != nil && x.Parameter != nil {
|
||||||
|
return *x.Parameter
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CodeGeneratorRequest) GetProtoFile() []*descriptorpb.FileDescriptorProto {
|
||||||
|
if x != nil {
|
||||||
|
return x.ProtoFile
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CodeGeneratorRequest) GetCompilerVersion() *Version {
|
||||||
|
if x != nil {
|
||||||
|
return x.CompilerVersion
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// The plugin writes an encoded CodeGeneratorResponse to stdout.
|
||||||
|
type CodeGeneratorResponse struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
// Error message. If non-empty, code generation failed. The plugin process
|
||||||
|
// should exit with status code zero even if it reports an error in this way.
|
||||||
|
//
|
||||||
|
// This should be used to indicate errors in .proto files which prevent the
|
||||||
|
// code generator from generating correct code. Errors which indicate a
|
||||||
|
// problem in protoc itself -- such as the input CodeGeneratorRequest being
|
||||||
|
// unparseable -- should be reported by writing a message to stderr and
|
||||||
|
// exiting with a non-zero status code.
|
||||||
|
Error *string `protobuf:"bytes,1,opt,name=error" json:"error,omitempty"`
|
||||||
|
// A bitmask of supported features that the code generator supports.
|
||||||
|
// This is a bitwise "or" of values from the Feature enum.
|
||||||
|
SupportedFeatures *uint64 `protobuf:"varint,2,opt,name=supported_features,json=supportedFeatures" json:"supported_features,omitempty"`
|
||||||
|
File []*CodeGeneratorResponse_File `protobuf:"bytes,15,rep,name=file" json:"file,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CodeGeneratorResponse) Reset() {
|
||||||
|
*x = CodeGeneratorResponse{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_google_protobuf_compiler_plugin_proto_msgTypes[2]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CodeGeneratorResponse) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*CodeGeneratorResponse) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *CodeGeneratorResponse) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_google_protobuf_compiler_plugin_proto_msgTypes[2]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use CodeGeneratorResponse.ProtoReflect.Descriptor instead.
|
||||||
|
func (*CodeGeneratorResponse) Descriptor() ([]byte, []int) {
|
||||||
|
return file_google_protobuf_compiler_plugin_proto_rawDescGZIP(), []int{2}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CodeGeneratorResponse) GetError() string {
|
||||||
|
if x != nil && x.Error != nil {
|
||||||
|
return *x.Error
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CodeGeneratorResponse) GetSupportedFeatures() uint64 {
|
||||||
|
if x != nil && x.SupportedFeatures != nil {
|
||||||
|
return *x.SupportedFeatures
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CodeGeneratorResponse) GetFile() []*CodeGeneratorResponse_File {
|
||||||
|
if x != nil {
|
||||||
|
return x.File
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Represents a single generated file.
|
||||||
|
type CodeGeneratorResponse_File struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
// The file name, relative to the output directory. The name must not
|
||||||
|
// contain "." or ".." components and must be relative, not be absolute (so,
|
||||||
|
// the file cannot lie outside the output directory). "/" must be used as
|
||||||
|
// the path separator, not "\".
|
||||||
|
//
|
||||||
|
// If the name is omitted, the content will be appended to the previous
|
||||||
|
// file. This allows the generator to break large files into small chunks,
|
||||||
|
// and allows the generated text to be streamed back to protoc so that large
|
||||||
|
// files need not reside completely in memory at one time. Note that as of
|
||||||
|
// this writing protoc does not optimize for this -- it will read the entire
|
||||||
|
// CodeGeneratorResponse before writing files to disk.
|
||||||
|
Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
|
||||||
|
// If non-empty, indicates that the named file should already exist, and the
|
||||||
|
// content here is to be inserted into that file at a defined insertion
|
||||||
|
// point. This feature allows a code generator to extend the output
|
||||||
|
// produced by another code generator. The original generator may provide
|
||||||
|
// insertion points by placing special annotations in the file that look
|
||||||
|
// like:
|
||||||
|
// @@protoc_insertion_point(NAME)
|
||||||
|
// The annotation can have arbitrary text before and after it on the line,
|
||||||
|
// which allows it to be placed in a comment. NAME should be replaced with
|
||||||
|
// an identifier naming the point -- this is what other generators will use
|
||||||
|
// as the insertion_point. Code inserted at this point will be placed
|
||||||
|
// immediately above the line containing the insertion point (thus multiple
|
||||||
|
// insertions to the same point will come out in the order they were added).
|
||||||
|
// The double-@ is intended to make it unlikely that the generated code
|
||||||
|
// could contain things that look like insertion points by accident.
|
||||||
|
//
|
||||||
|
// For example, the C++ code generator places the following line in the
|
||||||
|
// .pb.h files that it generates:
|
||||||
|
// // @@protoc_insertion_point(namespace_scope)
|
||||||
|
// This line appears within the scope of the file's package namespace, but
|
||||||
|
// outside of any particular class. Another plugin can then specify the
|
||||||
|
// insertion_point "namespace_scope" to generate additional classes or
|
||||||
|
// other declarations that should be placed in this scope.
|
||||||
|
//
|
||||||
|
// Note that if the line containing the insertion point begins with
|
||||||
|
// whitespace, the same whitespace will be added to every line of the
|
||||||
|
// inserted text. This is useful for languages like Python, where
|
||||||
|
// indentation matters. In these languages, the insertion point comment
|
||||||
|
// should be indented the same amount as any inserted code will need to be
|
||||||
|
// in order to work correctly in that context.
|
||||||
|
//
|
||||||
|
// The code generator that generates the initial file and the one which
|
||||||
|
// inserts into it must both run as part of a single invocation of protoc.
|
||||||
|
// Code generators are executed in the order in which they appear on the
|
||||||
|
// command line.
|
||||||
|
//
|
||||||
|
// If |insertion_point| is present, |name| must also be present.
|
||||||
|
InsertionPoint *string `protobuf:"bytes,2,opt,name=insertion_point,json=insertionPoint" json:"insertion_point,omitempty"`
|
||||||
|
// The file contents.
|
||||||
|
Content *string `protobuf:"bytes,15,opt,name=content" json:"content,omitempty"`
|
||||||
|
// Information describing the file content being inserted. If an insertion
|
||||||
|
// point is used, this information will be appropriately offset and inserted
|
||||||
|
// into the code generation metadata for the generated files.
|
||||||
|
GeneratedCodeInfo *descriptorpb.GeneratedCodeInfo `protobuf:"bytes,16,opt,name=generated_code_info,json=generatedCodeInfo" json:"generated_code_info,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CodeGeneratorResponse_File) Reset() {
|
||||||
|
*x = CodeGeneratorResponse_File{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_google_protobuf_compiler_plugin_proto_msgTypes[3]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CodeGeneratorResponse_File) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*CodeGeneratorResponse_File) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *CodeGeneratorResponse_File) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_google_protobuf_compiler_plugin_proto_msgTypes[3]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use CodeGeneratorResponse_File.ProtoReflect.Descriptor instead.
|
||||||
|
func (*CodeGeneratorResponse_File) Descriptor() ([]byte, []int) {
|
||||||
|
return file_google_protobuf_compiler_plugin_proto_rawDescGZIP(), []int{2, 0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CodeGeneratorResponse_File) GetName() string {
|
||||||
|
if x != nil && x.Name != nil {
|
||||||
|
return *x.Name
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CodeGeneratorResponse_File) GetInsertionPoint() string {
|
||||||
|
if x != nil && x.InsertionPoint != nil {
|
||||||
|
return *x.InsertionPoint
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CodeGeneratorResponse_File) GetContent() string {
|
||||||
|
if x != nil && x.Content != nil {
|
||||||
|
return *x.Content
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CodeGeneratorResponse_File) GetGeneratedCodeInfo() *descriptorpb.GeneratedCodeInfo {
|
||||||
|
if x != nil {
|
||||||
|
return x.GeneratedCodeInfo
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var File_google_protobuf_compiler_plugin_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
|
var file_google_protobuf_compiler_plugin_proto_rawDesc = []byte{
|
||||||
|
0x0a, 0x25, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
|
||||||
|
0x66, 0x2f, 0x63, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69,
|
||||||
|
0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x18, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
|
||||||
|
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65,
|
||||||
|
0x72, 0x1a, 0x20, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
|
||||||
|
0x75, 0x66, 0x2f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x2e, 0x70, 0x72,
|
||||||
|
0x6f, 0x74, 0x6f, 0x22, 0x63, 0x0a, 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x14,
|
||||||
|
0x0a, 0x05, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6d,
|
||||||
|
0x61, 0x6a, 0x6f, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x18, 0x02, 0x20,
|
||||||
|
0x01, 0x28, 0x05, 0x52, 0x05, 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x61,
|
||||||
|
0x74, 0x63, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x70, 0x61, 0x74, 0x63, 0x68,
|
||||||
|
0x12, 0x16, 0x0a, 0x06, 0x73, 0x75, 0x66, 0x66, 0x69, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09,
|
||||||
|
0x52, 0x06, 0x73, 0x75, 0x66, 0x66, 0x69, 0x78, 0x22, 0xf1, 0x01, 0x0a, 0x14, 0x43, 0x6f, 0x64,
|
||||||
|
0x65, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||||
|
0x74, 0x12, 0x28, 0x0a, 0x10, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x74, 0x6f, 0x5f, 0x67, 0x65, 0x6e,
|
||||||
|
0x65, 0x72, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x66, 0x69, 0x6c,
|
||||||
|
0x65, 0x54, 0x6f, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x70,
|
||||||
|
0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09,
|
||||||
|
0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x12, 0x43, 0x0a, 0x0a, 0x70, 0x72, 0x6f,
|
||||||
|
0x74, 0x6f, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x0f, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e,
|
||||||
|
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
|
||||||
|
0x46, 0x69, 0x6c, 0x65, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x50, 0x72,
|
||||||
|
0x6f, 0x74, 0x6f, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x4c,
|
||||||
|
0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69,
|
||||||
|
0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
|
||||||
|
0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x69,
|
||||||
|
0x6c, 0x65, 0x72, 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x0f, 0x63, 0x6f, 0x6d,
|
||||||
|
0x70, 0x69, 0x6c, 0x65, 0x72, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x94, 0x03, 0x0a,
|
||||||
|
0x15, 0x43, 0x6f, 0x64, 0x65, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65,
|
||||||
|
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18,
|
||||||
|
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x2d, 0x0a, 0x12,
|
||||||
|
0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72,
|
||||||
|
0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x11, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72,
|
||||||
|
0x74, 0x65, 0x64, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x48, 0x0a, 0x04, 0x66,
|
||||||
|
0x69, 0x6c, 0x65, 0x18, 0x0f, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
|
||||||
|
0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x63, 0x6f, 0x6d, 0x70,
|
||||||
|
0x69, 0x6c, 0x65, 0x72, 0x2e, 0x43, 0x6f, 0x64, 0x65, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74,
|
||||||
|
0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x52,
|
||||||
|
0x04, 0x66, 0x69, 0x6c, 0x65, 0x1a, 0xb1, 0x01, 0x0a, 0x04, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x12,
|
||||||
|
0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61,
|
||||||
|
0x6d, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x69, 0x6f, 0x6e, 0x5f,
|
||||||
|
0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x69, 0x6e, 0x73,
|
||||||
|
0x65, 0x72, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x63,
|
||||||
|
0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f,
|
||||||
|
0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x52, 0x0a, 0x13, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74,
|
||||||
|
0x65, 0x64, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x10, 0x20, 0x01,
|
||||||
|
0x28, 0x0b, 0x32, 0x22, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
||||||
|
0x6f, 0x62, 0x75, 0x66, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x43, 0x6f,
|
||||||
|
0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x11, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65,
|
||||||
|
0x64, 0x43, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x38, 0x0a, 0x07, 0x46, 0x65, 0x61,
|
||||||
|
0x74, 0x75, 0x72, 0x65, 0x12, 0x10, 0x0a, 0x0c, 0x46, 0x45, 0x41, 0x54, 0x55, 0x52, 0x45, 0x5f,
|
||||||
|
0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, 0x46, 0x45, 0x41, 0x54, 0x55, 0x52,
|
||||||
|
0x45, 0x5f, 0x50, 0x52, 0x4f, 0x54, 0x4f, 0x33, 0x5f, 0x4f, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x41,
|
||||||
|
0x4c, 0x10, 0x01, 0x42, 0x57, 0x0a, 0x1c, 0x63, 0x6f, 0x6d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
|
||||||
|
0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x69,
|
||||||
|
0x6c, 0x65, 0x72, 0x42, 0x0c, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x50, 0x72, 0x6f, 0x74, 0x6f,
|
||||||
|
0x73, 0x5a, 0x29, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67,
|
||||||
|
0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x79,
|
||||||
|
0x70, 0x65, 0x73, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x70, 0x62,
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
file_google_protobuf_compiler_plugin_proto_rawDescOnce sync.Once
|
||||||
|
file_google_protobuf_compiler_plugin_proto_rawDescData = file_google_protobuf_compiler_plugin_proto_rawDesc
|
||||||
|
)
|
||||||
|
|
||||||
|
func file_google_protobuf_compiler_plugin_proto_rawDescGZIP() []byte {
|
||||||
|
file_google_protobuf_compiler_plugin_proto_rawDescOnce.Do(func() {
|
||||||
|
file_google_protobuf_compiler_plugin_proto_rawDescData = protoimpl.X.CompressGZIP(file_google_protobuf_compiler_plugin_proto_rawDescData)
|
||||||
|
})
|
||||||
|
return file_google_protobuf_compiler_plugin_proto_rawDescData
|
||||||
|
}
|
||||||
|
|
||||||
|
var file_google_protobuf_compiler_plugin_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
|
||||||
|
var file_google_protobuf_compiler_plugin_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
|
||||||
|
var file_google_protobuf_compiler_plugin_proto_goTypes = []interface{}{
|
||||||
|
(CodeGeneratorResponse_Feature)(0), // 0: google.protobuf.compiler.CodeGeneratorResponse.Feature
|
||||||
|
(*Version)(nil), // 1: google.protobuf.compiler.Version
|
||||||
|
(*CodeGeneratorRequest)(nil), // 2: google.protobuf.compiler.CodeGeneratorRequest
|
||||||
|
(*CodeGeneratorResponse)(nil), // 3: google.protobuf.compiler.CodeGeneratorResponse
|
||||||
|
(*CodeGeneratorResponse_File)(nil), // 4: google.protobuf.compiler.CodeGeneratorResponse.File
|
||||||
|
(*descriptorpb.FileDescriptorProto)(nil), // 5: google.protobuf.FileDescriptorProto
|
||||||
|
(*descriptorpb.GeneratedCodeInfo)(nil), // 6: google.protobuf.GeneratedCodeInfo
|
||||||
|
}
|
||||||
|
var file_google_protobuf_compiler_plugin_proto_depIdxs = []int32{
|
||||||
|
5, // 0: google.protobuf.compiler.CodeGeneratorRequest.proto_file:type_name -> google.protobuf.FileDescriptorProto
|
||||||
|
1, // 1: google.protobuf.compiler.CodeGeneratorRequest.compiler_version:type_name -> google.protobuf.compiler.Version
|
||||||
|
4, // 2: google.protobuf.compiler.CodeGeneratorResponse.file:type_name -> google.protobuf.compiler.CodeGeneratorResponse.File
|
||||||
|
6, // 3: google.protobuf.compiler.CodeGeneratorResponse.File.generated_code_info:type_name -> google.protobuf.GeneratedCodeInfo
|
||||||
|
4, // [4:4] is the sub-list for method output_type
|
||||||
|
4, // [4:4] is the sub-list for method input_type
|
||||||
|
4, // [4:4] is the sub-list for extension type_name
|
||||||
|
4, // [4:4] is the sub-list for extension extendee
|
||||||
|
0, // [0:4] is the sub-list for field type_name
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { file_google_protobuf_compiler_plugin_proto_init() }
|
||||||
|
func file_google_protobuf_compiler_plugin_proto_init() {
|
||||||
|
if File_google_protobuf_compiler_plugin_proto != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !protoimpl.UnsafeEnabled {
|
||||||
|
file_google_protobuf_compiler_plugin_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*Version); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_google_protobuf_compiler_plugin_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*CodeGeneratorRequest); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_google_protobuf_compiler_plugin_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*CodeGeneratorResponse); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_google_protobuf_compiler_plugin_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*CodeGeneratorResponse_File); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
type x struct{}
|
||||||
|
out := protoimpl.TypeBuilder{
|
||||||
|
File: protoimpl.DescBuilder{
|
||||||
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
|
RawDescriptor: file_google_protobuf_compiler_plugin_proto_rawDesc,
|
||||||
|
NumEnums: 1,
|
||||||
|
NumMessages: 4,
|
||||||
|
NumExtensions: 0,
|
||||||
|
NumServices: 0,
|
||||||
|
},
|
||||||
|
GoTypes: file_google_protobuf_compiler_plugin_proto_goTypes,
|
||||||
|
DependencyIndexes: file_google_protobuf_compiler_plugin_proto_depIdxs,
|
||||||
|
EnumInfos: file_google_protobuf_compiler_plugin_proto_enumTypes,
|
||||||
|
MessageInfos: file_google_protobuf_compiler_plugin_proto_msgTypes,
|
||||||
|
}.Build()
|
||||||
|
File_google_protobuf_compiler_plugin_proto = out.File
|
||||||
|
file_google_protobuf_compiler_plugin_proto_rawDesc = nil
|
||||||
|
file_google_protobuf_compiler_plugin_proto_goTypes = nil
|
||||||
|
file_google_protobuf_compiler_plugin_proto_depIdxs = nil
|
||||||
|
}
|
|
@ -166,6 +166,7 @@ github.com/containerd/containerd/labels
|
||||||
github.com/containerd/containerd/leases
|
github.com/containerd/containerd/leases
|
||||||
github.com/containerd/containerd/log
|
github.com/containerd/containerd/log
|
||||||
github.com/containerd/containerd/namespaces
|
github.com/containerd/containerd/namespaces
|
||||||
|
github.com/containerd/containerd/pkg/dialer
|
||||||
github.com/containerd/containerd/pkg/seed
|
github.com/containerd/containerd/pkg/seed
|
||||||
github.com/containerd/containerd/pkg/userns
|
github.com/containerd/containerd/pkg/userns
|
||||||
github.com/containerd/containerd/platforms
|
github.com/containerd/containerd/platforms
|
||||||
|
@ -319,18 +320,45 @@ github.com/gogo/googleapis/google/rpc
|
||||||
# github.com/gogo/protobuf v1.3.2
|
# github.com/gogo/protobuf v1.3.2
|
||||||
## explicit; go 1.15
|
## explicit; go 1.15
|
||||||
github.com/gogo/protobuf/gogoproto
|
github.com/gogo/protobuf/gogoproto
|
||||||
|
github.com/gogo/protobuf/plugin/compare
|
||||||
|
github.com/gogo/protobuf/plugin/defaultcheck
|
||||||
|
github.com/gogo/protobuf/plugin/description
|
||||||
|
github.com/gogo/protobuf/plugin/embedcheck
|
||||||
|
github.com/gogo/protobuf/plugin/enumstringer
|
||||||
|
github.com/gogo/protobuf/plugin/equal
|
||||||
|
github.com/gogo/protobuf/plugin/face
|
||||||
|
github.com/gogo/protobuf/plugin/gostring
|
||||||
|
github.com/gogo/protobuf/plugin/marshalto
|
||||||
|
github.com/gogo/protobuf/plugin/oneofcheck
|
||||||
|
github.com/gogo/protobuf/plugin/populate
|
||||||
|
github.com/gogo/protobuf/plugin/size
|
||||||
|
github.com/gogo/protobuf/plugin/stringer
|
||||||
|
github.com/gogo/protobuf/plugin/testgen
|
||||||
|
github.com/gogo/protobuf/plugin/union
|
||||||
|
github.com/gogo/protobuf/plugin/unmarshal
|
||||||
github.com/gogo/protobuf/proto
|
github.com/gogo/protobuf/proto
|
||||||
|
github.com/gogo/protobuf/protoc-gen-gogo
|
||||||
github.com/gogo/protobuf/protoc-gen-gogo/descriptor
|
github.com/gogo/protobuf/protoc-gen-gogo/descriptor
|
||||||
|
github.com/gogo/protobuf/protoc-gen-gogo/generator
|
||||||
|
github.com/gogo/protobuf/protoc-gen-gogo/generator/internal/remap
|
||||||
|
github.com/gogo/protobuf/protoc-gen-gogo/grpc
|
||||||
|
github.com/gogo/protobuf/protoc-gen-gogo/plugin
|
||||||
|
github.com/gogo/protobuf/protoc-gen-gogofaster
|
||||||
|
github.com/gogo/protobuf/protoc-gen-gogoslick
|
||||||
github.com/gogo/protobuf/sortkeys
|
github.com/gogo/protobuf/sortkeys
|
||||||
github.com/gogo/protobuf/types
|
github.com/gogo/protobuf/types
|
||||||
|
github.com/gogo/protobuf/vanity
|
||||||
|
github.com/gogo/protobuf/vanity/command
|
||||||
# github.com/golang-jwt/jwt/v4 v4.4.2
|
# github.com/golang-jwt/jwt/v4 v4.4.2
|
||||||
## explicit; go 1.16
|
## explicit; go 1.16
|
||||||
github.com/golang-jwt/jwt/v4
|
github.com/golang-jwt/jwt/v4
|
||||||
# github.com/golang/protobuf v1.5.2
|
# github.com/golang/protobuf v1.5.2
|
||||||
## explicit; go 1.9
|
## explicit; go 1.9
|
||||||
github.com/golang/protobuf/descriptor
|
github.com/golang/protobuf/descriptor
|
||||||
|
github.com/golang/protobuf/internal/gengogrpc
|
||||||
github.com/golang/protobuf/jsonpb
|
github.com/golang/protobuf/jsonpb
|
||||||
github.com/golang/protobuf/proto
|
github.com/golang/protobuf/proto
|
||||||
|
github.com/golang/protobuf/protoc-gen-go
|
||||||
github.com/golang/protobuf/protoc-gen-go/descriptor
|
github.com/golang/protobuf/protoc-gen-go/descriptor
|
||||||
github.com/golang/protobuf/ptypes
|
github.com/golang/protobuf/ptypes
|
||||||
github.com/golang/protobuf/ptypes/any
|
github.com/golang/protobuf/ptypes/any
|
||||||
|
@ -834,6 +862,8 @@ google.golang.org/grpc/status
|
||||||
google.golang.org/grpc/tap
|
google.golang.org/grpc/tap
|
||||||
# google.golang.org/protobuf v1.28.1
|
# google.golang.org/protobuf v1.28.1
|
||||||
## explicit; go 1.11
|
## explicit; go 1.11
|
||||||
|
google.golang.org/protobuf/cmd/protoc-gen-go/internal_gengo
|
||||||
|
google.golang.org/protobuf/compiler/protogen
|
||||||
google.golang.org/protobuf/encoding/protojson
|
google.golang.org/protobuf/encoding/protojson
|
||||||
google.golang.org/protobuf/encoding/prototext
|
google.golang.org/protobuf/encoding/prototext
|
||||||
google.golang.org/protobuf/encoding/protowire
|
google.golang.org/protobuf/encoding/protowire
|
||||||
|
@ -868,6 +898,7 @@ google.golang.org/protobuf/types/known/durationpb
|
||||||
google.golang.org/protobuf/types/known/fieldmaskpb
|
google.golang.org/protobuf/types/known/fieldmaskpb
|
||||||
google.golang.org/protobuf/types/known/timestamppb
|
google.golang.org/protobuf/types/known/timestamppb
|
||||||
google.golang.org/protobuf/types/known/wrapperspb
|
google.golang.org/protobuf/types/known/wrapperspb
|
||||||
|
google.golang.org/protobuf/types/pluginpb
|
||||||
# gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c
|
# gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c
|
||||||
## explicit; go 1.11
|
## explicit; go 1.11
|
||||||
# gopkg.in/dancannon/gorethink.v3 v3.0.5
|
# gopkg.in/dancannon/gorethink.v3 v3.0.5
|
||||||
|
|
Loading…
Reference in New Issue