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
|
||||
- validate-vendor
|
||||
- validate-docs
|
||||
- validate-generated-files
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
|
|
10
Makefile
10
Makefile
|
@ -37,7 +37,7 @@ release:
|
|||
./hack/release
|
||||
|
||||
.PHONY: validate-all
|
||||
validate-all: lint test validate-vendor validate-docs
|
||||
validate-all: lint test validate-vendor validate-docs validate-generated-files
|
||||
|
||||
.PHONY: lint
|
||||
lint:
|
||||
|
@ -59,6 +59,10 @@ validate-docs:
|
|||
validate-authors:
|
||||
$(BUILDX_CMD) bake validate-authors
|
||||
|
||||
.PHONY: validate-generated-files
|
||||
validate-generated-files:
|
||||
$(BUILDX_CMD) bake validate-generated-files
|
||||
|
||||
.PHONY: test-driver
|
||||
test-driver:
|
||||
./hack/test-driver
|
||||
|
@ -78,3 +82,7 @@ authors:
|
|||
.PHONY: mod-outdated
|
||||
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) {
|
||||
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 {
|
||||
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)
|
||||
}
|
||||
}
|
||||
if len(noOutputTargets) > 0 && !allowNoOutput {
|
||||
if len(noOutputTargets) > 0 {
|
||||
var warnNoOutputBuf bytes.Buffer
|
||||
warnNoOutputBuf.WriteString("No output specified ")
|
||||
if len(noOutputTargets) == 1 && noOutputTargets[0] == "default" {
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"github.com/docker/buildx/bake"
|
||||
"github.com/docker/buildx/build"
|
||||
"github.com/docker/buildx/builder"
|
||||
controllerapi "github.com/docker/buildx/commands/controller/pb"
|
||||
"github.com/docker/buildx/util/buildflags"
|
||||
"github.com/docker/buildx/util/confutil"
|
||||
"github.com/docker/buildx/util/dockerutil"
|
||||
|
@ -25,10 +26,10 @@ type bakeOptions struct {
|
|||
files []string
|
||||
overrides []string
|
||||
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, end, err := tracing.TraceCurrentCommand(ctx, "bake")
|
||||
|
@ -60,31 +61,31 @@ func runBake(dockerCli command.Cli, targets []string, in bakeOptions) (err error
|
|||
}
|
||||
|
||||
overrides := in.overrides
|
||||
if in.exportPush {
|
||||
if in.exportLoad {
|
||||
if in.ExportPush {
|
||||
if in.ExportLoad {
|
||||
return errors.Errorf("push and load may not be set together at the moment")
|
||||
}
|
||||
overrides = append(overrides, "*.push=true")
|
||||
} else if in.exportLoad {
|
||||
} else if in.ExportLoad {
|
||||
overrides = append(overrides, "*.output=type=docker")
|
||||
}
|
||||
if in.noCache != nil {
|
||||
overrides = append(overrides, fmt.Sprintf("*.no-cache=%t", *in.noCache))
|
||||
if cFlags.noCache != nil {
|
||||
overrides = append(overrides, fmt.Sprintf("*.no-cache=%t", *cFlags.noCache))
|
||||
}
|
||||
if in.pull != nil {
|
||||
overrides = append(overrides, fmt.Sprintf("*.pull=%t", *in.pull))
|
||||
if cFlags.pull != nil {
|
||||
overrides = append(overrides, fmt.Sprintf("*.pull=%t", *cFlags.pull))
|
||||
}
|
||||
if in.sbom != "" {
|
||||
overrides = append(overrides, fmt.Sprintf("*.attest=%s", buildflags.CanonicalizeAttest("sbom", in.sbom)))
|
||||
if in.SBOM != "" {
|
||||
overrides = append(overrides, fmt.Sprintf("*.attest=%s", buildflags.CanonicalizeAttest("sbom", in.SBOM)))
|
||||
}
|
||||
if in.provenance != "" {
|
||||
overrides = append(overrides, fmt.Sprintf("*.attest=%s", buildflags.CanonicalizeAttest("provenance", in.provenance)))
|
||||
if in.Provenance != "" {
|
||||
overrides = append(overrides, fmt.Sprintf("*.attest=%s", buildflags.CanonicalizeAttest("provenance", in.Provenance)))
|
||||
}
|
||||
contextPathHash, _ := os.Getwd()
|
||||
|
||||
ctx2, cancel := context.WithCancel(context.TODO())
|
||||
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 {
|
||||
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
|
||||
if url != "" || !in.printOnly {
|
||||
b, err := builder.New(dockerCli,
|
||||
builder.WithName(in.builder),
|
||||
builder.WithName(in.Builder),
|
||||
builder.WithContextPathHash(contextPathHash),
|
||||
)
|
||||
if err != nil {
|
||||
|
@ -170,12 +171,12 @@ func runBake(dockerCli command.Cli, targets []string, in bakeOptions) (err error
|
|||
return wrapBuildError(err, true)
|
||||
}
|
||||
|
||||
if len(in.metadataFile) > 0 {
|
||||
if len(in.MetadataFile) > 0 {
|
||||
dt := make(map[string]interface{})
|
||||
for t, r := range resp {
|
||||
dt[t] = decodeExporterResponse(r.ExporterResponse)
|
||||
}
|
||||
if err := writeMetadataFile(in.metadataFile, dt); err != nil {
|
||||
if err := writeMetadataFile(in.MetadataFile, dt); err != nil {
|
||||
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 {
|
||||
var options bakeOptions
|
||||
var cFlags commonFlags
|
||||
|
||||
cmd := &cobra.Command{
|
||||
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 {
|
||||
// reset to nil to avoid override is unset
|
||||
if !cmd.Flags().Lookup("no-cache").Changed {
|
||||
options.noCache = nil
|
||||
cFlags.noCache = nil
|
||||
}
|
||||
if !cmd.Flags().Lookup("pull").Changed {
|
||||
options.pull = nil
|
||||
cFlags.pull = nil
|
||||
}
|
||||
options.commonOptions.builder = rootOpts.builder
|
||||
return runBake(dockerCli, args, options)
|
||||
options.Builder = rootOpts.builder
|
||||
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.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.exportPush, "push", false, `Shorthand for "--set=*.output=type=registry"`)
|
||||
flags.StringVar(&options.sbom, "sbom", "", `Shorthand for "--set=*.attest=type=sbom"`)
|
||||
flags.StringVar(&options.provenance, "provenance", "", `Shorthand for "--set=*.attest=type=provenance"`)
|
||||
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.Provenance, "provenance", "", `Shorthand for "--set=*.attest=type=provenance"`)
|
||||
flags.StringArrayVar(&options.overrides, "set", nil, `Override target value (e.g., "targetpattern.key=value")`)
|
||||
|
||||
commonBuildFlags(&options.commonOptions, flags)
|
||||
commonBuildFlags(&cFlags, flags)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
@ -17,12 +18,14 @@ import (
|
|||
"github.com/containerd/console"
|
||||
"github.com/docker/buildx/build"
|
||||
"github.com/docker/buildx/builder"
|
||||
controllerapi "github.com/docker/buildx/commands/controller/pb"
|
||||
"github.com/docker/buildx/monitor"
|
||||
"github.com/docker/buildx/store"
|
||||
"github.com/docker/buildx/store/storeutil"
|
||||
"github.com/docker/buildx/util/buildflags"
|
||||
"github.com/docker/buildx/util/confutil"
|
||||
"github.com/docker/buildx/util/dockerutil"
|
||||
"github.com/docker/buildx/util/ioset"
|
||||
"github.com/docker/buildx/util/platformutil"
|
||||
"github.com/docker/buildx/util/progress"
|
||||
"github.com/docker/buildx/util/tracing"
|
||||
|
@ -50,50 +53,15 @@ import (
|
|||
const defaultTargetName = "default"
|
||||
|
||||
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
|
||||
pull *bool
|
||||
|
||||
exportPush bool
|
||||
exportLoad bool
|
||||
|
||||
sbom string
|
||||
provenance string
|
||||
invoke string
|
||||
serverConfig string
|
||||
root string
|
||||
detach bool
|
||||
controllerapi.BuildOptions
|
||||
}
|
||||
|
||||
func runBuild(dockerCli command.Cli, in buildOptions) (err error) {
|
||||
func runBuild(dockerCli command.Cli, in buildOptions) error {
|
||||
ctx := appcontext.Context()
|
||||
|
||||
ctx, end, err := tracing.TraceCurrentCommand(ctx, "build")
|
||||
|
@ -104,89 +72,85 @@ func runBuild(dockerCli command.Cli, in buildOptions) (err error) {
|
|||
end(err)
|
||||
}()
|
||||
|
||||
noCache := false
|
||||
if in.noCache != nil {
|
||||
noCache = *in.noCache
|
||||
}
|
||||
pull := false
|
||||
if in.pull != nil {
|
||||
pull = *in.pull
|
||||
_, err = runBuildWithContext(ctx, dockerCli, in.BuildOptions, os.Stdin, in.progress, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
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.Opts.NoCache && len(in.NoCacheFilter) > 0 {
|
||||
return nil, errors.Errorf("--no-cache and --no-cache-filter cannot currently be used together")
|
||||
}
|
||||
|
||||
if noCache && len(in.noCacheFilter) > 0 {
|
||||
return errors.Errorf("--no-cache and --no-cache-filter cannot currently be used together")
|
||||
if in.Quiet && progressMode != progress.PrinterModeAuto && progressMode != progress.PrinterModeQuiet {
|
||||
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 {
|
||||
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)
|
||||
contexts, err := parseContextNames(in.Contexts)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
printFunc, err := parsePrintFunc(in.printFunc)
|
||||
printFunc, err := parsePrintFunc(in.PrintFunc)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
opts := build.Options{
|
||||
Inputs: build.Inputs{
|
||||
ContextPath: in.contextPath,
|
||||
DockerfilePath: in.dockerfileName,
|
||||
InStream: os.Stdin,
|
||||
ContextPath: in.ContextPath,
|
||||
DockerfilePath: in.DockerfileName,
|
||||
InStream: inStream,
|
||||
NamedContexts: contexts,
|
||||
},
|
||||
BuildArgs: listToMap(in.buildArgs, true),
|
||||
ExtraHosts: in.extraHosts,
|
||||
ImageIDFile: in.imageIDFile,
|
||||
Labels: listToMap(in.labels, false),
|
||||
NetworkMode: in.networkMode,
|
||||
NoCache: noCache,
|
||||
NoCacheFilter: in.noCacheFilter,
|
||||
Pull: pull,
|
||||
ShmSize: in.shmSize,
|
||||
Tags: in.tags,
|
||||
Target: in.target,
|
||||
Ulimits: in.ulimits,
|
||||
BuildArgs: listToMap(in.BuildArgs, true),
|
||||
ExtraHosts: in.ExtraHosts,
|
||||
ImageIDFile: in.ImageIDFile,
|
||||
Labels: listToMap(in.Labels, false),
|
||||
NetworkMode: in.NetworkMode,
|
||||
NoCache: in.Opts.NoCache,
|
||||
NoCacheFilter: in.NoCacheFilter,
|
||||
Pull: in.Opts.Pull,
|
||||
ShmSize: dockeropts.MemBytes(in.ShmSize),
|
||||
Tags: in.Tags,
|
||||
Target: in.Target,
|
||||
Ulimits: controllerUlimitOpt2DockerUlimit(in.Ulimits),
|
||||
PrintFunc: printFunc,
|
||||
}
|
||||
|
||||
platforms, err := platformutil.Parse(in.platforms)
|
||||
platforms, err := platformutil.Parse(in.Platforms)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
opts.Platforms = platforms
|
||||
|
||||
dockerConfig := config.LoadDefaultConfigFile(os.Stderr)
|
||||
opts.Session = append(opts.Session, authprovider.NewDockerAuthProvider(dockerConfig))
|
||||
|
||||
secrets, err := buildflags.ParseSecretSpecs(in.secrets)
|
||||
secrets, err := buildflags.ParseSecretSpecs(in.Secrets)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
opts.Session = append(opts.Session, secrets)
|
||||
|
||||
sshSpecs := in.ssh
|
||||
if len(sshSpecs) == 0 && buildflags.IsGitSSH(in.contextPath) {
|
||||
sshSpecs := in.SSH
|
||||
if len(sshSpecs) == 0 && buildflags.IsGitSSH(in.ContextPath) {
|
||||
sshSpecs = []string{"default"}
|
||||
}
|
||||
ssh, err := buildflags.ParseSSHSpecs(sshSpecs)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
opts.Session = append(opts.Session, ssh)
|
||||
|
||||
outputs, err := buildflags.ParseOutputs(in.outputs)
|
||||
outputs, err := buildflags.ParseOutputs(in.Outputs)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
if in.exportPush {
|
||||
if in.exportLoad {
|
||||
return errors.Errorf("push and load may not be set together at the moment")
|
||||
if in.Opts.ExportPush {
|
||||
if in.Opts.ExportLoad {
|
||||
return nil, errors.Errorf("push and load may not be set together at the moment")
|
||||
}
|
||||
if len(outputs) == 0 {
|
||||
outputs = []client.ExportEntry{{
|
||||
|
@ -200,11 +164,11 @@ func runBuild(dockerCli command.Cli, in buildOptions) (err error) {
|
|||
case "image":
|
||||
outputs[0].Attrs["push"] = "true"
|
||||
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 {
|
||||
outputs = []client.ExportEntry{{
|
||||
Type: "docker",
|
||||
|
@ -214,102 +178,76 @@ func runBuild(dockerCli command.Cli, in buildOptions) (err error) {
|
|||
switch outputs[0].Type {
|
||||
case "docker":
|
||||
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
|
||||
|
||||
inAttests := append([]string{}, in.attests...)
|
||||
if in.provenance != "" {
|
||||
inAttests = append(inAttests, buildflags.CanonicalizeAttest("provenance", in.provenance))
|
||||
inAttests := append([]string{}, in.Attests...)
|
||||
if in.Opts.Provenance != "" {
|
||||
inAttests = append(inAttests, buildflags.CanonicalizeAttest("provenance", in.Opts.Provenance))
|
||||
}
|
||||
if in.sbom != "" {
|
||||
inAttests = append(inAttests, buildflags.CanonicalizeAttest("sbom", in.sbom))
|
||||
if in.Opts.SBOM != "" {
|
||||
inAttests = append(inAttests, buildflags.CanonicalizeAttest("sbom", in.Opts.SBOM))
|
||||
}
|
||||
opts.Attests, err = buildflags.ParseAttests(inAttests)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cacheImports, err := buildflags.ParseCacheEntry(in.cacheFrom)
|
||||
cacheImports, err := buildflags.ParseCacheEntry(in.CacheFrom)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
opts.CacheFrom = cacheImports
|
||||
|
||||
cacheExports, err := buildflags.ParseCacheEntry(in.cacheTo)
|
||||
cacheExports, err := buildflags.ParseCacheEntry(in.CacheTo)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
opts.CacheTo = cacheExports
|
||||
|
||||
allow, err := buildflags.ParseEntitlements(in.allow)
|
||||
allow, err := buildflags.ParseEntitlements(in.Allow)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
opts.Allow = allow
|
||||
|
||||
// key string used for kubernetes "sticky" mode
|
||||
contextPathHash, err := filepath.Abs(in.contextPath)
|
||||
contextPathHash, err := filepath.Abs(in.ContextPath)
|
||||
if err != nil {
|
||||
contextPathHash = in.contextPath
|
||||
contextPathHash = in.ContextPath
|
||||
}
|
||||
|
||||
b, err := builder.New(dockerCli,
|
||||
builder.WithName(in.builder),
|
||||
builder.WithName(in.Opts.Builder),
|
||||
builder.WithContextPathHash(contextPathHash),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
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)
|
||||
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)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if in.invoke != "" {
|
||||
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 {
|
||||
if in.Quiet {
|
||||
fmt.Println(imageID)
|
||||
}
|
||||
return nil
|
||||
return res, nil
|
||||
}
|
||||
|
||||
type nopCloser struct {
|
||||
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) {
|
||||
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) {
|
||||
ctx2, cancel := context.WithCancel(context.TODO())
|
||||
defer cancel()
|
||||
|
||||
|
@ -320,13 +258,13 @@ func buildTargets(ctx context.Context, dockerCli command.Cli, nodes []builder.No
|
|||
|
||||
var mu sync.Mutex
|
||||
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()
|
||||
defer mu.Unlock()
|
||||
if res == nil || driverIndex < idx {
|
||||
idx, res = driverIndex, gotRes
|
||||
}
|
||||
}, allowNoOutput)
|
||||
})
|
||||
err1 := printer.Wait()
|
||||
if err == nil {
|
||||
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
|
||||
}
|
||||
|
||||
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) {
|
||||
if len(warnings) == 0 || mode == progress.PrinterModeQuiet {
|
||||
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 {
|
||||
options := newBuildOptions()
|
||||
cFlags := &commonFlags{}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "build [OPTIONS] PATH | URL | -",
|
||||
|
@ -455,9 +342,22 @@ func buildCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
|
|||
Short: "Start a build",
|
||||
Args: cli.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
options.contextPath = args[0]
|
||||
options.builder = rootOpts.builder
|
||||
options.ContextPath = args[0]
|
||||
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)
|
||||
if isExperimental() {
|
||||
return launchControllerAndRunBuild(dockerCli, options)
|
||||
}
|
||||
return runBuild(dockerCli, options)
|
||||
},
|
||||
}
|
||||
|
@ -469,67 +369,70 @@ func buildCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
|
|||
|
||||
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.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.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.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() {
|
||||
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.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.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.StringVar(&options.sbom, "sbom", "", `Shorthand for "--attest=type=sbom"`)
|
||||
flags.StringVar(&options.provenance, "provenance", "", `Shortand for "--attest=type=provenance"`)
|
||||
flags.StringArrayVar(&options.Attests, "attest", []string{}, `Attestation parameters (format: "type=sbom,generator=image")`)
|
||||
flags.StringVar(&options.Opts.SBOM, "sbom", "", `Shorthand for "--attest=type=sbom"`)
|
||||
flags.StringVar(&options.Opts.Provenance, "provenance", "", `Shortand for "--attest=type=provenance"`)
|
||||
|
||||
if isExperimental() {
|
||||
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
|
||||
|
@ -580,11 +483,19 @@ func buildCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
|
|||
flags.BoolVar(&ignoreBool, "force-rm", false, "Always remove intermediate containers")
|
||||
flags.MarkHidden("force-rm")
|
||||
|
||||
commonBuildFlags(&options.commonOptions, flags)
|
||||
commonBuildFlags(cFlags, flags)
|
||||
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")
|
||||
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")
|
||||
|
@ -744,3 +655,235 @@ func updateLastActivity(dockerCli command.Cli, ng *store.NodeGroup) error {
|
|||
defer release()
|
||||
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),
|
||||
imagetoolscmd.RootCmd(dockerCli, imagetoolscmd.RootOptions{Builder: &opts.builder}),
|
||||
)
|
||||
if isExperimental() {
|
||||
addControllerCommands(cmd, dockerCli, opts)
|
||||
}
|
||||
}
|
||||
|
||||
func rootFlags(options *rootOptions, flags *pflag.FlagSet) {
|
||||
|
|
|
@ -59,6 +59,13 @@ target "validate-authors" {
|
|||
output = ["type=cacheonly"]
|
||||
}
|
||||
|
||||
target "validate-generated-files" {
|
||||
inherits = ["_common"]
|
||||
dockerfile = "./hack/dockerfiles/generated-files.Dockerfile"
|
||||
target = "validate"
|
||||
output = ["type=cacheonly"]
|
||||
}
|
||||
|
||||
target "update-vendor" {
|
||||
inherits = ["_common"]
|
||||
dockerfile = "./hack/dockerfiles/vendor.Dockerfile"
|
||||
|
@ -84,6 +91,13 @@ target "update-authors" {
|
|||
output = ["."]
|
||||
}
|
||||
|
||||
target "update-generated-files" {
|
||||
inherits = ["_common"]
|
||||
dockerfile = "./hack/dockerfiles/generated-files.Dockerfile"
|
||||
target = "update"
|
||||
output = ["."]
|
||||
}
|
||||
|
||||
target "mod-outdated" {
|
||||
inherits = ["_common"]
|
||||
dockerfile = "./hack/dockerfiles/vendor.Dockerfile"
|
||||
|
|
|
@ -9,22 +9,23 @@ Extended build capabilities with BuildKit
|
|||
|
||||
### Subcommands
|
||||
|
||||
| Name | Description |
|
||||
|:-------------------------------------|:-------------------------------------------|
|
||||
| [`bake`](buildx_bake.md) | Build from a file |
|
||||
| [`build`](buildx_build.md) | Start a build |
|
||||
| [`create`](buildx_create.md) | Create a new builder instance |
|
||||
| [`du`](buildx_du.md) | Disk usage |
|
||||
| [`imagetools`](buildx_imagetools.md) | Commands to work on images in registry |
|
||||
| [`inspect`](buildx_inspect.md) | Inspect current builder instance |
|
||||
| [`install`](buildx_install.md) | Install buildx as a 'docker builder' alias |
|
||||
| [`ls`](buildx_ls.md) | List builder instances |
|
||||
| [`prune`](buildx_prune.md) | Remove build cache |
|
||||
| [`rm`](buildx_rm.md) | Remove a builder instance |
|
||||
| [`stop`](buildx_stop.md) | Stop builder instance |
|
||||
| [`uninstall`](buildx_uninstall.md) | Uninstall the 'docker builder' alias |
|
||||
| [`use`](buildx_use.md) | Set the current builder instance |
|
||||
| [`version`](buildx_version.md) | Show buildx version information |
|
||||
| Name | Description |
|
||||
|:-----------------------------------------------|:-------------------------------------------|
|
||||
| [`_INTERNAL_SERVE`](buildx__INTERNAL_SERVE.md) | |
|
||||
| [`bake`](buildx_bake.md) | Build from a file |
|
||||
| [`build`](buildx_build.md) | Start a build |
|
||||
| [`create`](buildx_create.md) | Create a new builder instance |
|
||||
| [`du`](buildx_du.md) | Disk usage |
|
||||
| [`imagetools`](buildx_imagetools.md) | Commands to work on images in registry |
|
||||
| [`inspect`](buildx_inspect.md) | Inspect current builder instance |
|
||||
| [`install`](buildx_install.md) | Install buildx as a 'docker builder' alias |
|
||||
| [`ls`](buildx_ls.md) | List builder instances |
|
||||
| [`prune`](buildx_prune.md) | Remove build cache |
|
||||
| [`rm`](buildx_rm.md) | Remove a builder instance |
|
||||
| [`stop`](buildx_stop.md) | Stop builder instance |
|
||||
| [`uninstall`](buildx_uninstall.md) | Uninstall the 'docker builder' alias |
|
||||
| [`use`](buildx_use.md) | Set the current builder instance |
|
||||
| [`version`](buildx_version.md) | Show buildx version information |
|
||||
|
||||
|
||||
### 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-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 |
|
||||
| `--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`) |
|
||||
| `--iidfile` | `string` | | Write the image ID to the file |
|
||||
| `--invoke` | `string` | | Invoke a command after the build [experimental] |
|
||||
|
@ -41,8 +42,10 @@ Start a build
|
|||
| `--pull` | | | Always attempt to pull all referenced images |
|
||||
| [`--push`](#push) | | | Shorthand for `--output=type=registry` |
|
||||
| `-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` |
|
||||
| [`--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` |
|
||||
| [`--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`) |
|
||||
|
|
6
go.mod
6
go.mod
|
@ -13,11 +13,14 @@ require (
|
|||
github.com/docker/docker v23.0.0-rc.1+incompatible
|
||||
github.com/docker/go-units v0.5.0
|
||||
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/hashicorp/go-cty-funcs v0.0.0-20200930094925-2721b1e36840
|
||||
github.com/hashicorp/hcl/v2 v2.8.2
|
||||
github.com/moby/buildkit v0.11.1
|
||||
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/opencontainers/go-digest v1.0.0
|
||||
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-sql-driver/mysql v1.6.0 // 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/protobuf v1.5.2 // indirect
|
||||
github.com/google/certificate-transparency-go v1.0.21 // indirect
|
||||
github.com/google/go-cmp v0.5.9 // 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/spdystream v0.2.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/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // 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
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/docker/buildx/build"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/containerd/console"
|
||||
controllerapi "github.com/docker/buildx/commands/controller/pb"
|
||||
"github.com/docker/buildx/util/ioset"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
const helpMessage = `
|
||||
Available commads are:
|
||||
reload reloads the context and build it.
|
||||
rollback re-runs the interactive container with initial rootfs contents.
|
||||
exit exits monitor.
|
||||
help shows this message.
|
||||
reload reloads the context and build it.
|
||||
rollback re-runs the interactive container with initial rootfs contents.
|
||||
list list buildx sessions.
|
||||
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.
|
||||
func RunMonitor(ctx context.Context, containerConfig build.ContainerConfig, reloadFunc func(context.Context) (*build.ResultContext, error), stdin io.ReadCloser, stdout, stderr io.WriteCloser) error {
|
||||
monitorIn, monitorOut := ioSetPipe()
|
||||
defer monitorIn.Close()
|
||||
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 {
|
||||
defer func() {
|
||||
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{})
|
||||
monitorDisableCh := make(chan struct{})
|
||||
monitorOutCtx := ioSetOutContext{monitorOut,
|
||||
func() { monitorEnableCh <- struct{}{} },
|
||||
func() { monitorDisableCh <- struct{}{} },
|
||||
monitorOutCtx := ioset.MuxOut{
|
||||
Out: monitorOut,
|
||||
EnableHook: func() { monitorEnableCh <- struct{}{} },
|
||||
DisableHook: func() { monitorDisableCh <- struct{}{} },
|
||||
}
|
||||
|
||||
containerIn, containerOut := ioSetPipe()
|
||||
defer containerIn.Close()
|
||||
containerOutCtx := ioSetOutContext{containerOut,
|
||||
containerIn, containerOut := ioset.Pipe()
|
||||
defer func() {
|
||||
containerIn.Close()
|
||||
}()
|
||||
containerOutCtx := ioset.MuxOut{
|
||||
Out: containerOut,
|
||||
// send newline to hopefully get the prompt; TODO: better UI (e.g. reprinting the last line)
|
||||
func() { containerOut.stdin.Write([]byte("\n")) },
|
||||
func() {},
|
||||
EnableHook: func() { containerOut.Stdin.Write([]byte("\n")) },
|
||||
DisableHook: func() {},
|
||||
}
|
||||
|
||||
invokeForwarder := ioset.NewForwarder()
|
||||
invokeForwarder.SetIn(&containerIn)
|
||||
m := &monitor{
|
||||
invokeIO: newIOForwarder(containerIn),
|
||||
muxIO: newMuxIO(ioSetIn{stdin, stdout, stderr}, []ioSetOutContext{monitorOutCtx, containerOutCtx}, 1, func(prev int, res int) string {
|
||||
invokeIO: invokeForwarder,
|
||||
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 {
|
||||
// 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 "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
|
||||
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
|
||||
monitorForwarder := newIOForwarder(monitorIn)
|
||||
monitorForwarder := ioset.NewForwarder()
|
||||
monitorForwarder.SetIn(&monitorIn)
|
||||
for {
|
||||
<-monitorEnableCh
|
||||
in, out := ioSetPipe()
|
||||
monitorForwarder.setDestination(&out)
|
||||
in, out := ioset.Pipe()
|
||||
monitorForwarder.SetOut(&out)
|
||||
doneCh, errCh := make(chan struct{}), make(chan error)
|
||||
go func() {
|
||||
defer close(doneCh)
|
||||
|
@ -69,7 +106,7 @@ func RunMonitor(ctx context.Context, containerConfig build.ContainerConfig, relo
|
|||
<-ctx.Done()
|
||||
in.Close()
|
||||
}()
|
||||
t := term.NewTerminal(readWriter{in.stdin, in.stdout}, "(buildx) ")
|
||||
t := term.NewTerminal(readWriter{in.Stdin, in.Stdout}, "(buildx) ")
|
||||
for {
|
||||
l, err := t.ReadLine()
|
||||
if err != nil {
|
||||
|
@ -79,22 +116,63 @@ func RunMonitor(ctx context.Context, containerConfig build.ContainerConfig, relo
|
|||
}
|
||||
return
|
||||
}
|
||||
switch l {
|
||||
args := strings.Fields(l) // TODO: use shlex
|
||||
if len(args) == 0 {
|
||||
continue
|
||||
}
|
||||
switch args[0] {
|
||||
case "":
|
||||
// nop
|
||||
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 {
|
||||
fmt.Printf("failed to reload: %v\n", err)
|
||||
} else {
|
||||
curRef = ref
|
||||
// rollback the running container with the new result
|
||||
containerConfig.ResultCtx = res
|
||||
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")
|
||||
}
|
||||
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")
|
||||
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":
|
||||
return
|
||||
case "help":
|
||||
|
@ -118,7 +196,7 @@ func RunMonitor(ctx context.Context, containerConfig build.ContainerConfig, relo
|
|||
return err
|
||||
case <-monitorDisableCh:
|
||||
}
|
||||
monitorForwarder.setDestination(nil)
|
||||
monitorForwarder.SetOut(nil)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -128,36 +206,37 @@ type readWriter struct {
|
|||
}
|
||||
|
||||
type monitor struct {
|
||||
muxIO *muxIO
|
||||
invokeIO *ioForwarder
|
||||
muxIO *ioset.MuxIO
|
||||
invokeIO *ioset.Forwarder
|
||||
invokeFunc func(context.Context, string, io.ReadCloser, io.WriteCloser, io.WriteCloser) error
|
||||
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 {
|
||||
m.curInvokeCancel() // Finish the running container if exists
|
||||
}
|
||||
go func() {
|
||||
// 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)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (m *monitor) invoke(ctx context.Context, cfg build.ContainerConfig) error {
|
||||
m.muxIO.enable(1)
|
||||
defer m.muxIO.disable(1)
|
||||
func (m *monitor) invoke(ctx context.Context, ref string) error {
|
||||
m.muxIO.Enable(1)
|
||||
defer m.muxIO.Disable(1)
|
||||
invokeCtx, invokeCancel := context.WithCancel(ctx)
|
||||
|
||||
containerIn, containerOut := ioSetPipe()
|
||||
m.invokeIO.setDestination(&containerOut)
|
||||
containerIn, containerOut := ioset.Pipe()
|
||||
m.invokeIO.SetOut(&containerOut)
|
||||
waitInvokeDoneCh := make(chan struct{})
|
||||
var cancelOnce sync.Once
|
||||
curInvokeCancel := func() {
|
||||
cancelOnce.Do(func() {
|
||||
containerIn.Close()
|
||||
m.invokeIO.setDestination(nil)
|
||||
m.invokeIO.SetOut(nil)
|
||||
invokeCancel()
|
||||
})
|
||||
<-waitInvokeDoneCh
|
||||
|
@ -165,352 +244,14 @@ func (m *monitor) invoke(ctx context.Context, cfg build.ContainerConfig) error {
|
|||
defer curInvokeCancel()
|
||||
m.curInvokeCancel = curInvokeCancel
|
||||
|
||||
cfg.Stdin = containerIn.stdin
|
||||
cfg.Stdout = containerIn.stdout
|
||||
cfg.Stderr = containerIn.stderr
|
||||
err := build.Invoke(invokeCtx, cfg)
|
||||
err := m.invokeFunc(invokeCtx, ref, containerIn.Stdin, containerIn.Stdout, containerIn.Stderr)
|
||||
close(waitInvokeDoneCh)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
type ioForwarder struct {
|
||||
curIO *ioSetOut
|
||||
mu sync.Mutex
|
||||
updateCh chan struct{}
|
||||
type nopCloser struct {
|
||||
io.Writer
|
||||
}
|
||||
|
||||
func newIOForwarder(in ioSetIn) *ioForwarder {
|
||||
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()
|
||||
}
|
||||
func (c nopCloser) Close() error { return nil }
|
||||
|
|
|
@ -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 (
|
||||
"bytes"
|
||||
|
@ -11,7 +11,7 @@ import (
|
|||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
// TestMuxIO tests muxIO
|
||||
// TestMuxIO tests MuxIO
|
||||
func TestMuxIO(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
|
@ -122,20 +122,20 @@ func TestMuxIO(t *testing.T) {
|
|||
t.Run(tt.name, func(t *testing.T) {
|
||||
inBuf, end, in := newTestIn(t)
|
||||
var outBufs []*outBuf
|
||||
var outs []ioSetOutContext
|
||||
var outs []MuxOut
|
||||
if tt.outputsNum != len(tt.wants) {
|
||||
t.Fatalf("wants != outputsNum")
|
||||
}
|
||||
for i := 0; i < tt.outputsNum; i++ {
|
||||
outBuf, out := newTestOut(t, i)
|
||||
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 {
|
||||
// Add input to muxIO
|
||||
// Add input to MuxIO
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -143,14 +143,14 @@ func TestMuxIO(t *testing.T) {
|
|||
var eg errgroup.Group
|
||||
eg.Go(func() error {
|
||||
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 nil
|
||||
})
|
||||
eg.Go(func() error {
|
||||
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 nil
|
||||
|
@ -160,8 +160,8 @@ func TestMuxIO(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// Close stdin on this muxIO
|
||||
end.stdin.Close()
|
||||
// Close stdin on this MuxIO
|
||||
end.Stdin.Close()
|
||||
|
||||
// Wait for all output ends reach EOF
|
||||
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 {
|
||||
return func(m *muxIO) (string, string) {
|
||||
return func(m *MuxIO) (string, string) {
|
||||
return s, strings.ReplaceAll(s, string([]rune{rune(1)}), "")
|
||||
}
|
||||
}
|
||||
|
||||
func toggle() instruction {
|
||||
return func(m *muxIO) (string, string) {
|
||||
return func(m *MuxIO) (string, string) {
|
||||
return string([]rune{rune(1)}) + "c", ""
|
||||
}
|
||||
}
|
||||
|
||||
func enable(i int) instruction {
|
||||
return func(m *muxIO) (string, string) {
|
||||
m.enable(i)
|
||||
return func(m *MuxIO) (string, string) {
|
||||
m.Enable(i)
|
||||
return "", ""
|
||||
}
|
||||
}
|
||||
|
||||
func disable(i int) instruction {
|
||||
return func(m *muxIO) (string, string) {
|
||||
m.disable(i)
|
||||
return func(m *MuxIO) (string, string) {
|
||||
m.Disable(i)
|
||||
return "", ""
|
||||
}
|
||||
}
|
||||
|
@ -223,7 +223,7 @@ type inBuf struct {
|
|||
doneCh chan struct{}
|
||||
}
|
||||
|
||||
func newTestIn(t *testing.T) (*inBuf, ioSetOut, ioSetIn) {
|
||||
func newTestIn(t *testing.T) (*inBuf, Out, In) {
|
||||
ti := &inBuf{
|
||||
doneCh: make(chan struct{}),
|
||||
}
|
||||
|
@ -253,7 +253,7 @@ func newTestIn(t *testing.T) (*inBuf, ioSetOut, ioSetIn) {
|
|||
close(ti.doneCh)
|
||||
}()
|
||||
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 {
|
||||
|
@ -262,7 +262,7 @@ type outBuf struct {
|
|||
doneCh chan struct{}
|
||||
}
|
||||
|
||||
func newTestOut(t *testing.T, idx int) (*outBuf, ioSetOut) {
|
||||
func newTestOut(t *testing.T, idx int) (*outBuf, Out) {
|
||||
to := &outBuf{
|
||||
idx: idx,
|
||||
doneCh: make(chan struct{}),
|
||||
|
@ -290,7 +290,7 @@ func newTestOut(t *testing.T, idx int) (*outBuf, ioSetOut) {
|
|||
errW.Close()
|
||||
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 {
|
|
@ -68,3 +68,24 @@ func NewChannel(w Writer) (chan *client.SolveStatus, chan struct{}) {
|
|||
}()
|
||||
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/log
|
||||
github.com/containerd/containerd/namespaces
|
||||
github.com/containerd/containerd/pkg/dialer
|
||||
github.com/containerd/containerd/pkg/seed
|
||||
github.com/containerd/containerd/pkg/userns
|
||||
github.com/containerd/containerd/platforms
|
||||
|
@ -319,18 +320,45 @@ github.com/gogo/googleapis/google/rpc
|
|||
# github.com/gogo/protobuf v1.3.2
|
||||
## explicit; go 1.15
|
||||
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/protoc-gen-gogo
|
||||
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/types
|
||||
github.com/gogo/protobuf/vanity
|
||||
github.com/gogo/protobuf/vanity/command
|
||||
# github.com/golang-jwt/jwt/v4 v4.4.2
|
||||
## explicit; go 1.16
|
||||
github.com/golang-jwt/jwt/v4
|
||||
# github.com/golang/protobuf v1.5.2
|
||||
## explicit; go 1.9
|
||||
github.com/golang/protobuf/descriptor
|
||||
github.com/golang/protobuf/internal/gengogrpc
|
||||
github.com/golang/protobuf/jsonpb
|
||||
github.com/golang/protobuf/proto
|
||||
github.com/golang/protobuf/protoc-gen-go
|
||||
github.com/golang/protobuf/protoc-gen-go/descriptor
|
||||
github.com/golang/protobuf/ptypes
|
||||
github.com/golang/protobuf/ptypes/any
|
||||
|
@ -834,6 +862,8 @@ google.golang.org/grpc/status
|
|||
google.golang.org/grpc/tap
|
||||
# google.golang.org/protobuf v1.28.1
|
||||
## 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/prototext
|
||||
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/timestamppb
|
||||
google.golang.org/protobuf/types/known/wrapperspb
|
||||
google.golang.org/protobuf/types/pluginpb
|
||||
# gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c
|
||||
## explicit; go 1.11
|
||||
# gopkg.in/dancannon/gorethink.v3 v3.0.5
|
||||
|
|
Loading…
Reference in New Issue