github.com/Minish144/prototool-arm64@v1.3.0/internal/exec/runner.go (about)

     1  // Copyright (c) 2018 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package exec
    22  
    23  import (
    24  	"bufio"
    25  	"bytes"
    26  	"encoding/json"
    27  	"errors"
    28  	"fmt"
    29  	"io"
    30  	"io/ioutil"
    31  	"os"
    32  	"path/filepath"
    33  	"runtime"
    34  	"sort"
    35  	"strings"
    36  	"text/scanner"
    37  	"text/tabwriter"
    38  	"time"
    39  
    40  	"github.com/golang/protobuf/jsonpb"
    41  	"github.com/golang/protobuf/protoc-gen-go/descriptor"
    42  	"github.com/uber/prototool/internal/cfginit"
    43  	"github.com/uber/prototool/internal/create"
    44  	"github.com/uber/prototool/internal/diff"
    45  	"github.com/uber/prototool/internal/extract"
    46  	"github.com/uber/prototool/internal/file"
    47  	"github.com/uber/prototool/internal/format"
    48  	"github.com/uber/prototool/internal/grpc"
    49  	"github.com/uber/prototool/internal/lint"
    50  	"github.com/uber/prototool/internal/protoc"
    51  	"github.com/uber/prototool/internal/reflect"
    52  	"github.com/uber/prototool/internal/settings"
    53  	"github.com/uber/prototool/internal/text"
    54  	"github.com/uber/prototool/internal/vars"
    55  	"go.uber.org/zap"
    56  )
    57  
    58  var jsonMarshaler = &jsonpb.Marshaler{Indent: "  "}
    59  
    60  type runner struct {
    61  	configProvider   settings.ConfigProvider
    62  	protoSetProvider file.ProtoSetProvider
    63  
    64  	workDirPath string
    65  	input       io.Reader
    66  	output      io.Writer
    67  
    68  	logger        *zap.Logger
    69  	cachePath     string
    70  	configData    string
    71  	protocBinPath string
    72  	protocWKTPath string
    73  	protocURL     string
    74  	printFields   string
    75  	json          bool
    76  }
    77  
    78  func newRunner(workDirPath string, input io.Reader, output io.Writer, options ...RunnerOption) *runner {
    79  	runner := &runner{
    80  		workDirPath: workDirPath,
    81  		input:       input,
    82  		output:      output,
    83  	}
    84  	for _, option := range options {
    85  		option(runner)
    86  	}
    87  	runner.configProvider = settings.NewConfigProvider(
    88  		settings.ConfigProviderWithLogger(runner.logger),
    89  	)
    90  	protoSetProviderOptions := []file.ProtoSetProviderOption{
    91  		file.ProtoSetProviderWithLogger(runner.logger),
    92  	}
    93  	if runner.configData != "" {
    94  		protoSetProviderOptions = append(
    95  			protoSetProviderOptions,
    96  			file.ProtoSetProviderWithConfigData(runner.configData),
    97  		)
    98  	}
    99  	runner.protoSetProvider = file.NewProtoSetProvider(protoSetProviderOptions...)
   100  	return runner
   101  }
   102  
   103  func (r *runner) Version() error {
   104  	out := struct {
   105  		Version              string `json:"version,omitempty"`
   106  		DefaultProtocVersion string `json:"default_protoc_version,omitempty"`
   107  		GoVersion            string `json:"go_version,omitempty"`
   108  		GitCommit            string `json:"git_commit,omitempty"`
   109  		BuiltTimestamp       string `json:"built_timestamp,omitempty"`
   110  		GOOS                 string `json:"goos,omitempty"`
   111  		GOARCH               string `json:"goarch,omitempty"`
   112  	}{
   113  		Version:              vars.Version,
   114  		DefaultProtocVersion: vars.DefaultProtocVersion,
   115  		GoVersion:            runtime.Version(),
   116  		GitCommit:            vars.GitCommit,
   117  		BuiltTimestamp:       vars.BuiltTimestamp,
   118  		GOOS:                 runtime.GOOS,
   119  		GOARCH:               runtime.GOARCH,
   120  	}
   121  
   122  	if r.json {
   123  		enc := json.NewEncoder(r.output)
   124  		enc.SetIndent("", "  ")
   125  
   126  		if err := enc.Encode(out); err != nil {
   127  			return err
   128  		}
   129  		return nil
   130  	}
   131  
   132  	tabWriter := newTabWriter(r.output)
   133  	if _, err := fmt.Fprintf(tabWriter, "Version:\t%s\n", out.Version); err != nil {
   134  		return err
   135  	}
   136  	if _, err := fmt.Fprintf(tabWriter, "Default protoc version:\t%s\n", out.DefaultProtocVersion); err != nil {
   137  		return err
   138  	}
   139  	if _, err := fmt.Fprintf(tabWriter, "Go version:\t%s\n", out.GoVersion); err != nil {
   140  		return err
   141  	}
   142  	if out.GitCommit != "" {
   143  		if _, err := fmt.Fprintf(tabWriter, "Git commit:\t%s\n", out.GitCommit); err != nil {
   144  			return err
   145  		}
   146  	}
   147  	if out.BuiltTimestamp != "" {
   148  		if _, err := fmt.Fprintf(tabWriter, "Built:\t%s\n", out.BuiltTimestamp); err != nil {
   149  			return err
   150  		}
   151  	}
   152  	if _, err := fmt.Fprintf(tabWriter, "OS/Arch:\t%s/%s\n", out.GOOS, out.GOARCH); err != nil {
   153  		return err
   154  	}
   155  	return tabWriter.Flush()
   156  }
   157  
   158  func (r *runner) Init(args []string, uncomment bool) error {
   159  	if len(args) > 1 {
   160  		return errors.New("must provide one arg dirPath")
   161  	}
   162  	// TODO(pedge): cleanup
   163  	dirPath := r.workDirPath
   164  	if len(args) == 1 {
   165  		dirPath = args[0]
   166  		if err := os.MkdirAll(dirPath, 0755); err != nil {
   167  			return err
   168  		}
   169  	}
   170  	filePath := filepath.Join(dirPath, settings.DefaultConfigFilename)
   171  	if _, err := os.Stat(filePath); err == nil {
   172  		return fmt.Errorf("%s already exists", filePath)
   173  	}
   174  	data, err := cfginit.Generate(vars.DefaultProtocVersion, uncomment)
   175  	if err != nil {
   176  		return err
   177  	}
   178  	return ioutil.WriteFile(filePath, data, 0644)
   179  }
   180  
   181  func (r *runner) Create(args []string, pkg string) error {
   182  	return r.newCreateHandler(pkg).Create(args...)
   183  }
   184  
   185  func (r *runner) Download() error {
   186  	config, err := r.getConfig(r.workDirPath)
   187  	if err != nil {
   188  		return err
   189  	}
   190  	d, err := r.newDownloader(config)
   191  	if err != nil {
   192  		return err
   193  	}
   194  	path, err := d.Download()
   195  	if err != nil {
   196  		return err
   197  	}
   198  	return r.println(path)
   199  }
   200  
   201  func (r *runner) Clean() error {
   202  	config, err := r.getConfig(r.workDirPath)
   203  	if err != nil {
   204  		return err
   205  	}
   206  	d, err := r.newDownloader(config)
   207  	if err != nil {
   208  		return err
   209  	}
   210  	return d.Delete()
   211  }
   212  
   213  func (r *runner) Files(args []string) error {
   214  	meta, err := r.getMeta(args, 1)
   215  	if err != nil {
   216  		return err
   217  	}
   218  	for _, files := range meta.ProtoSet.DirPathToFiles {
   219  		for _, file := range files {
   220  			if err := r.println(file.DisplayPath); err != nil {
   221  				return err
   222  			}
   223  		}
   224  	}
   225  	return nil
   226  }
   227  
   228  func (r *runner) Compile(args []string, dryRun bool) error {
   229  	meta, err := r.getMeta(args, 1)
   230  	if err != nil {
   231  		return err
   232  	}
   233  	r.printAffectedFiles(meta)
   234  	_, err = r.compile(false, false, dryRun, meta)
   235  	return err
   236  }
   237  
   238  func (r *runner) Gen(args []string, dryRun bool) error {
   239  	meta, err := r.getMeta(args, 1)
   240  	if err != nil {
   241  		return err
   242  	}
   243  	r.printAffectedFiles(meta)
   244  	_, err = r.compile(true, false, dryRun, meta)
   245  	return err
   246  }
   247  
   248  func (r *runner) DescriptorProto(args []string) error {
   249  	path := args[len(args)-1]
   250  	meta, err := r.getMeta(args, 2)
   251  	if err != nil {
   252  		return err
   253  	}
   254  	r.printAffectedFiles(meta)
   255  	fileDescriptorSets, err := r.compile(false, true, false, meta)
   256  	if err != nil {
   257  		return err
   258  	}
   259  	if len(fileDescriptorSets) == 0 {
   260  		return fmt.Errorf("no FileDescriptorSets returned")
   261  	}
   262  	message, err := r.newGetter().GetMessage(fileDescriptorSets, path)
   263  	if err != nil {
   264  		return err
   265  	}
   266  	data, err := jsonMarshaler.MarshalToString(message.DescriptorProto)
   267  	if err != nil {
   268  		return err
   269  	}
   270  	return r.println(data)
   271  }
   272  
   273  func (r *runner) FieldDescriptorProto(args []string) error {
   274  	path := args[len(args)-1]
   275  	meta, err := r.getMeta(args, 2)
   276  	if err != nil {
   277  		return err
   278  	}
   279  	r.printAffectedFiles(meta)
   280  	fileDescriptorSets, err := r.compile(false, true, false, meta)
   281  	if err != nil {
   282  		return err
   283  	}
   284  	if len(fileDescriptorSets) == 0 {
   285  		return fmt.Errorf("no FileDescriptorSets returned")
   286  	}
   287  	field, err := r.newGetter().GetField(fileDescriptorSets, path)
   288  	if err != nil {
   289  		return err
   290  	}
   291  	data, err := jsonMarshaler.MarshalToString(field.FieldDescriptorProto)
   292  	if err != nil {
   293  		return err
   294  	}
   295  	return r.println(data)
   296  }
   297  
   298  func (r *runner) ServiceDescriptorProto(args []string) error {
   299  	path := args[len(args)-1]
   300  	meta, err := r.getMeta(args, 2)
   301  	if err != nil {
   302  		return err
   303  	}
   304  	r.printAffectedFiles(meta)
   305  	fileDescriptorSets, err := r.compile(false, true, false, meta)
   306  	if err != nil {
   307  		return err
   308  	}
   309  	if len(fileDescriptorSets) == 0 {
   310  		return fmt.Errorf("no FileDescriptorSets returned")
   311  	}
   312  	service, err := r.newGetter().GetService(fileDescriptorSets, path)
   313  	if err != nil {
   314  		return err
   315  	}
   316  	data, err := jsonMarshaler.MarshalToString(service.ServiceDescriptorProto)
   317  	if err != nil {
   318  		return err
   319  	}
   320  	return r.println(data)
   321  }
   322  
   323  func (r *runner) compile(doGen, doFileDescriptorSet, dryRun bool, meta *meta) ([]*descriptor.FileDescriptorSet, error) {
   324  	if dryRun {
   325  		return nil, r.printCommands(doGen, meta.ProtoSet)
   326  	}
   327  	compileResult, err := r.newCompiler(doGen, doFileDescriptorSet).Compile(meta.ProtoSet)
   328  	if err != nil {
   329  		return nil, err
   330  	}
   331  	if err := r.printFailures("", meta, compileResult.Failures...); err != nil {
   332  		return nil, err
   333  	}
   334  	if len(compileResult.Failures) > 0 {
   335  		return nil, newExitErrorf(255, "")
   336  	}
   337  	r.logger.Debug("protoc command exited without errors")
   338  	return compileResult.FileDescriptorSets, nil
   339  }
   340  
   341  func (r *runner) printCommands(doGen bool, protoSet *file.ProtoSet) error {
   342  	commands, err := r.newCompiler(doGen, false).ProtocCommands(protoSet)
   343  	if err != nil {
   344  		return err
   345  	}
   346  	for _, command := range commands {
   347  		if err := r.println(command); err != nil {
   348  			return err
   349  		}
   350  	}
   351  	return nil
   352  }
   353  
   354  func (r *runner) Lint(args []string, listAllLinters bool, listLinters bool) error {
   355  	if listAllLinters && listLinters {
   356  		return newExitErrorf(255, "can only set one of list-all-linters, list-linters")
   357  	}
   358  	if listAllLinters {
   359  		return r.listAllLinters()
   360  	}
   361  	if listLinters {
   362  		return r.listLinters()
   363  	}
   364  	meta, err := r.getMeta(args, 1)
   365  	if err != nil {
   366  		return err
   367  	}
   368  	r.printAffectedFiles(meta)
   369  	if _, err := r.compile(false, false, false, meta); err != nil {
   370  		return err
   371  	}
   372  	return r.lint(meta)
   373  }
   374  
   375  func (r *runner) lint(meta *meta) error {
   376  	r.logger.Debug("calling LintRunner")
   377  	failures, err := r.newLintRunner().Run(meta.ProtoSet)
   378  	if err != nil {
   379  		return err
   380  	}
   381  	if err := r.printFailures("", meta, failures...); err != nil {
   382  		return err
   383  	}
   384  	if len(failures) > 0 {
   385  		return newExitErrorf(255, "")
   386  	}
   387  	return nil
   388  }
   389  
   390  func (r *runner) listLinters() error {
   391  	config, err := r.getConfig(r.workDirPath)
   392  	if err != nil {
   393  		return err
   394  	}
   395  	linters, err := lint.GetLinters(config.Lint)
   396  	if err != nil {
   397  		return err
   398  	}
   399  	return r.printLinters(linters)
   400  }
   401  
   402  func (r *runner) listAllLinters() error {
   403  	return r.printLinters(lint.AllLinters)
   404  }
   405  
   406  func (r *runner) ListLintGroup(group string) error {
   407  	linters, ok := lint.GroupToLinters[strings.ToLower(group)]
   408  	if !ok {
   409  		return newExitErrorf(255, "unknown lint group: %s", strings.ToLower(group))
   410  	}
   411  	return r.printLinters(linters)
   412  }
   413  
   414  func (r *runner) ListAllLintGroups() error {
   415  	groups := make([]string, 0, len(lint.GroupToLinters))
   416  	for group := range lint.GroupToLinters {
   417  		groups = append(groups, group)
   418  	}
   419  	sort.Strings(groups)
   420  	for _, group := range groups {
   421  		if err := r.println(group); err != nil {
   422  			return err
   423  		}
   424  	}
   425  	return nil
   426  }
   427  
   428  func (r *runner) Format(args []string, overwrite, diffMode, lintMode, fix bool) error {
   429  	if (overwrite && diffMode) || (overwrite && lintMode) || (diffMode && lintMode) {
   430  		return newExitErrorf(255, "can only set one of overwrite, diff, lint")
   431  	}
   432  	meta, err := r.getMeta(args, 1)
   433  	if err != nil {
   434  		return err
   435  	}
   436  	r.printAffectedFiles(meta)
   437  	if _, err := r.compile(false, false, false, meta); err != nil {
   438  		return err
   439  	}
   440  	return r.format(overwrite, diffMode, lintMode, fix, meta)
   441  }
   442  
   443  func (r *runner) format(overwrite, diffMode, lintMode, fix bool, meta *meta) error {
   444  	success := true
   445  	for _, protoFiles := range meta.ProtoSet.DirPathToFiles {
   446  		for _, protoFile := range protoFiles {
   447  			fileSuccess, err := r.formatFile(overwrite, diffMode, lintMode, fix, meta, protoFile)
   448  			if err != nil {
   449  				return err
   450  			}
   451  			if !fileSuccess {
   452  				success = false
   453  			}
   454  		}
   455  	}
   456  	if !success {
   457  		return newExitErrorf(255, "")
   458  	}
   459  	return nil
   460  }
   461  
   462  // return true if there was no unexpected diff and we should exit with 0
   463  // return false if we should exit with non-zero
   464  // if false and nil error, we will return an ExitError outside of this function
   465  func (r *runner) formatFile(overwrite bool, diffMode bool, lintMode bool, fix bool, meta *meta, protoFile *file.ProtoFile) (bool, error) {
   466  	absSingleFilename, err := file.AbsClean(meta.SingleFilename)
   467  	if err != nil {
   468  		return false, err
   469  	}
   470  	// we are not concerned with the current file
   471  	if meta.SingleFilename != "" && protoFile.Path != absSingleFilename {
   472  		return true, nil
   473  	}
   474  	input, err := ioutil.ReadFile(protoFile.Path)
   475  	if err != nil {
   476  		return false, err
   477  	}
   478  	data, failures, err := r.newTransformer(fix).Transform(protoFile.Path, input)
   479  	if err != nil {
   480  		return false, err
   481  	}
   482  	if len(failures) > 0 {
   483  		return false, r.printFailures(protoFile.DisplayPath, meta, failures...)
   484  	}
   485  	if !bytes.Equal(input, data) {
   486  		if overwrite {
   487  			// 0 exit code in overwrite case
   488  			return true, ioutil.WriteFile(protoFile.Path, data, os.ModePerm)
   489  		}
   490  		if lintMode {
   491  			return false, r.printFailures("", meta, text.NewFailuref(scanner.Position{
   492  				Filename: protoFile.DisplayPath,
   493  			}, "FORMAT_DIFF", "Format returned a diff."))
   494  		}
   495  		if diffMode {
   496  			d, err := diff.Do(input, data, protoFile.DisplayPath)
   497  			if err != nil {
   498  				return false, err
   499  			}
   500  			if _, err := io.Copy(r.output, bytes.NewReader(d)); err != nil {
   501  				return false, err
   502  			}
   503  			return false, nil
   504  		}
   505  		//below is !overwrite && !lintMode && !diffMode
   506  		if _, err := io.Copy(r.output, bytes.NewReader(data)); err != nil {
   507  			return false, err
   508  		}
   509  		// there was a diff, return non-zero exit code
   510  		return false, nil
   511  	}
   512  	// we still print the formatted file to stdout
   513  	if !overwrite && !lintMode && !diffMode {
   514  		if _, err := io.Copy(r.output, bytes.NewReader(data)); err != nil {
   515  			return false, err
   516  		}
   517  	}
   518  	return true, nil
   519  }
   520  
   521  func (r *runner) BinaryToJSON(args []string) error {
   522  	path := args[len(args)-2]
   523  	data, err := r.getInputData(args[len(args)-1])
   524  	if err != nil {
   525  		return err
   526  	}
   527  	meta, err := r.getMeta(args, 3)
   528  	if err != nil {
   529  		return err
   530  	}
   531  	r.printAffectedFiles(meta)
   532  	fileDescriptorSets, err := r.compile(false, true, false, meta)
   533  	if err != nil {
   534  		return err
   535  	}
   536  	if len(fileDescriptorSets) == 0 {
   537  		return fmt.Errorf("no FileDescriptorSets returned")
   538  	}
   539  	out, err := r.newReflectHandler().BinaryToJSON(fileDescriptorSets, path, data)
   540  	if err != nil {
   541  		return err
   542  	}
   543  	_, err = r.output.Write(out)
   544  	return err
   545  }
   546  
   547  func (r *runner) JSONToBinary(args []string) error {
   548  	path := args[len(args)-2]
   549  	data, err := r.getInputData(args[len(args)-1])
   550  	if err != nil {
   551  		return err
   552  	}
   553  	meta, err := r.getMeta(args, 3)
   554  	if err != nil {
   555  		return err
   556  	}
   557  	r.printAffectedFiles(meta)
   558  	fileDescriptorSets, err := r.compile(false, true, false, meta)
   559  	if err != nil {
   560  		return err
   561  	}
   562  	if len(fileDescriptorSets) == 0 {
   563  		return fmt.Errorf("no FileDescriptorSets returned")
   564  	}
   565  	out, err := r.newReflectHandler().JSONToBinary(fileDescriptorSets, path, data)
   566  	if err != nil {
   567  		return err
   568  	}
   569  	_, err = r.output.Write(out)
   570  	return err
   571  }
   572  
   573  func (r *runner) All(args []string, disableFormat, disableLint, fix bool) error {
   574  	meta, err := r.getMeta(args, 1)
   575  	if err != nil {
   576  		return err
   577  	}
   578  	r.printAffectedFiles(meta)
   579  	if _, err := r.compile(false, false, false, meta); err != nil {
   580  		return err
   581  	}
   582  	if !disableFormat {
   583  		if err := r.format(true, false, false, fix, meta); err != nil {
   584  			return err
   585  		}
   586  	}
   587  	if _, err := r.compile(true, false, false, meta); err != nil {
   588  		return err
   589  	}
   590  	if !disableLint {
   591  		return r.lint(meta)
   592  	}
   593  	return nil
   594  }
   595  
   596  func (r *runner) GRPC(args, headers []string, address, method, data, callTimeout, connectTimeout, keepaliveTime string, stdin bool) error {
   597  	if address == "" {
   598  		return newExitErrorf(255, "must set address")
   599  	}
   600  	if method == "" {
   601  		return newExitErrorf(255, "must set method")
   602  	}
   603  	if data == "" && !stdin {
   604  		return newExitErrorf(255, "must set one of data or stdin")
   605  	}
   606  	if data != "" && stdin {
   607  		return newExitErrorf(255, "must set only one of data or stdin")
   608  	}
   609  	reader := r.getInputReader(data, stdin)
   610  
   611  	parsedHeaders := make(map[string]string)
   612  	for _, header := range headers {
   613  		split := strings.SplitN(header, ":", 2)
   614  		if len(split) != 2 {
   615  			return fmt.Errorf("headers must be key:value but got %s", header)
   616  		}
   617  		parsedHeaders[split[0]] = split[1]
   618  	}
   619  	var parsedCallTimeout time.Duration
   620  	var parsedConnectTimeout time.Duration
   621  	var parsedKeepaliveTime time.Duration
   622  	var err error
   623  	if callTimeout != "" {
   624  		parsedCallTimeout, err = time.ParseDuration(callTimeout)
   625  		if err != nil {
   626  			return err
   627  		}
   628  	}
   629  	if connectTimeout != "" {
   630  		parsedConnectTimeout, err = time.ParseDuration(connectTimeout)
   631  		if err != nil {
   632  			return err
   633  		}
   634  	}
   635  	if keepaliveTime != "" {
   636  		parsedKeepaliveTime, err = time.ParseDuration(keepaliveTime)
   637  		if err != nil {
   638  			return err
   639  		}
   640  	}
   641  
   642  	meta, err := r.getMeta(args, 1)
   643  	if err != nil {
   644  		return err
   645  	}
   646  	r.printAffectedFiles(meta)
   647  	fileDescriptorSets, err := r.compile(false, true, false, meta)
   648  	if err != nil {
   649  		return err
   650  	}
   651  	if len(fileDescriptorSets) == 0 {
   652  		return fmt.Errorf("no FileDescriptorSets returned")
   653  	}
   654  	return r.newGRPCHandler(
   655  		parsedHeaders,
   656  		parsedCallTimeout,
   657  		parsedConnectTimeout,
   658  		parsedKeepaliveTime,
   659  	).Invoke(fileDescriptorSets, address, method, reader, r.output)
   660  }
   661  
   662  func (r *runner) newDownloader(config settings.Config) (protoc.Downloader, error) {
   663  	downloaderOptions := []protoc.DownloaderOption{
   664  		protoc.DownloaderWithLogger(r.logger),
   665  	}
   666  	if r.cachePath != "" {
   667  		downloaderOptions = append(
   668  			downloaderOptions,
   669  			protoc.DownloaderWithCachePath(r.cachePath),
   670  		)
   671  	}
   672  	if r.protocBinPath != "" {
   673  		downloaderOptions = append(
   674  			downloaderOptions,
   675  			protoc.DownloaderWithProtocBinPath(r.protocBinPath),
   676  		)
   677  	}
   678  	if r.protocWKTPath != "" {
   679  		downloaderOptions = append(
   680  			downloaderOptions,
   681  			protoc.DownloaderWithProtocWKTPath(r.protocWKTPath),
   682  		)
   683  	}
   684  	if r.protocURL != "" {
   685  		downloaderOptions = append(
   686  			downloaderOptions,
   687  			protoc.DownloaderWithProtocURL(r.protocURL),
   688  		)
   689  	}
   690  	return protoc.NewDownloader(config, downloaderOptions...)
   691  }
   692  
   693  func (r *runner) newCompiler(doGen bool, doFileDescriptorSet bool) protoc.Compiler {
   694  	compilerOptions := []protoc.CompilerOption{
   695  		protoc.CompilerWithLogger(r.logger),
   696  	}
   697  	if r.cachePath != "" {
   698  		compilerOptions = append(
   699  			compilerOptions,
   700  			protoc.CompilerWithCachePath(r.cachePath),
   701  		)
   702  	}
   703  	if r.protocBinPath != "" {
   704  		compilerOptions = append(
   705  			compilerOptions,
   706  			protoc.CompilerWithProtocBinPath(r.protocBinPath),
   707  		)
   708  	}
   709  	if r.protocWKTPath != "" {
   710  		compilerOptions = append(
   711  			compilerOptions,
   712  			protoc.CompilerWithProtocWKTPath(r.protocWKTPath),
   713  		)
   714  	}
   715  	if r.protocURL != "" {
   716  		compilerOptions = append(
   717  			compilerOptions,
   718  			protoc.CompilerWithProtocURL(r.protocURL),
   719  		)
   720  	}
   721  	if doGen {
   722  		compilerOptions = append(
   723  			compilerOptions,
   724  			protoc.CompilerWithGen(),
   725  		)
   726  	}
   727  	if doFileDescriptorSet {
   728  		compilerOptions = append(
   729  			compilerOptions,
   730  			protoc.CompilerWithFileDescriptorSet(),
   731  		)
   732  	}
   733  	return protoc.NewCompiler(compilerOptions...)
   734  }
   735  
   736  func (r *runner) newLintRunner() lint.Runner {
   737  	return lint.NewRunner(
   738  		lint.RunnerWithLogger(r.logger),
   739  	)
   740  }
   741  
   742  func (r *runner) newTransformer(fix bool) format.Transformer {
   743  	transformerOptions := []format.TransformerOption{format.TransformerWithLogger(r.logger)}
   744  	if fix {
   745  		transformerOptions = append(transformerOptions, format.TransformerWithFix())
   746  	}
   747  	return format.NewTransformer(transformerOptions...)
   748  }
   749  
   750  func (r *runner) newGetter() extract.Getter {
   751  	return extract.NewGetter(
   752  		extract.GetterWithLogger(r.logger),
   753  	)
   754  }
   755  
   756  func (r *runner) newReflectHandler() reflect.Handler {
   757  	return reflect.NewHandler(
   758  		reflect.HandlerWithLogger(r.logger),
   759  	)
   760  }
   761  
   762  func (r *runner) newCreateHandler(pkg string) create.Handler {
   763  	handlerOptions := []create.HandlerOption{create.HandlerWithLogger(r.logger)}
   764  	if pkg != "" {
   765  		handlerOptions = append(handlerOptions, create.HandlerWithPackage(pkg))
   766  	}
   767  	return create.NewHandler(handlerOptions...)
   768  }
   769  
   770  func (r *runner) newGRPCHandler(
   771  	headers map[string]string,
   772  	callTimeout time.Duration,
   773  	connectTimeout time.Duration,
   774  	keepaliveTime time.Duration,
   775  ) grpc.Handler {
   776  	handlerOptions := []grpc.HandlerOption{
   777  		grpc.HandlerWithLogger(r.logger),
   778  	}
   779  	for key, value := range headers {
   780  		handlerOptions = append(handlerOptions, grpc.HandlerWithHeader(key, value))
   781  	}
   782  	if callTimeout != 0 {
   783  		handlerOptions = append(handlerOptions, grpc.HandlerWithCallTimeout(callTimeout))
   784  	}
   785  	if connectTimeout != 0 {
   786  		handlerOptions = append(handlerOptions, grpc.HandlerWithConnectTimeout(connectTimeout))
   787  	}
   788  	if keepaliveTime != 0 {
   789  		handlerOptions = append(handlerOptions, grpc.HandlerWithKeepaliveTime(keepaliveTime))
   790  	}
   791  	return grpc.NewHandler(handlerOptions...)
   792  }
   793  
   794  func (r *runner) getConfig(dirPath string) (settings.Config, error) {
   795  	if r.configData != "" {
   796  		return r.configProvider.GetForData(dirPath, r.configData)
   797  	}
   798  	return r.configProvider.GetForDir(dirPath)
   799  }
   800  
   801  type meta struct {
   802  	ProtoSet *file.ProtoSet
   803  	// this will be empty if not in dir mode
   804  	// if in dir mode, this will be the single filename that we want to return errors for
   805  	SingleFilename string
   806  }
   807  
   808  // lenOfArgsIfSpecified makes this function more convenient when calling above.
   809  // It says "if the length of args is equal to lenOfArgsIfSpecified, then args
   810  // contains the dirOrFile parameter as it's first argument." For example, for
   811  // "prototool compile [dirOrFile]", lenOfArgsIfSpecified is 1, saying that if
   812  // we have 1 argument, the first argument is dirOrFile, if we have zero arguments,
   813  // we do not and assume the current directory is the dirOrFile.
   814  func (r *runner) getMeta(args []string, lenOfArgsIfSpecified int) (*meta, error) {
   815  	// TODO: does not fit in with workDirPath paradigm
   816  	fileOrDir := "."
   817  	if len(args) == lenOfArgsIfSpecified {
   818  		fileOrDir = args[0]
   819  	}
   820  	fileInfo, err := os.Stat(fileOrDir)
   821  	if err != nil {
   822  		return nil, err
   823  	}
   824  	if fileInfo.Mode().IsDir() {
   825  		protoSet, err := r.protoSetProvider.GetForDir(r.workDirPath, fileOrDir)
   826  		if err != nil {
   827  			return nil, err
   828  		}
   829  		return &meta{
   830  			ProtoSet: protoSet,
   831  		}, nil
   832  	}
   833  	// TODO: allow symlinks?
   834  	if fileInfo.Mode().IsRegular() {
   835  		protoSet, err := r.protoSetProvider.GetForDir(r.workDirPath, filepath.Dir(fileOrDir))
   836  		if err != nil {
   837  			return nil, err
   838  		}
   839  		return &meta{
   840  			ProtoSet:       protoSet,
   841  			SingleFilename: fileOrDir,
   842  		}, nil
   843  	}
   844  	return nil, fmt.Errorf("%s is not a directory or a regular file", fileOrDir)
   845  }
   846  
   847  // TODO: we filter failures in dir mode in printFailures but above we count any failure
   848  // as an error with a non-zero exit code, seems inconsistent, this needs refactoring
   849  
   850  // filename is optional
   851  // if set, it will update the Failures to have this filename
   852  // will be sorted
   853  func (r *runner) printFailures(filename string, meta *meta, failures ...*text.Failure) error {
   854  	for _, failure := range failures {
   855  		if filename != "" {
   856  			failure.Filename = filename
   857  		}
   858  	}
   859  	failureFields, err := text.ParseColonSeparatedFailureFields(r.printFields)
   860  	if err != nil {
   861  		return err
   862  	}
   863  	text.SortFailures(failures)
   864  	bufWriter := bufio.NewWriter(r.output)
   865  	for _, failure := range failures {
   866  		shouldPrint := false
   867  		if meta.SingleFilename == "" || meta.SingleFilename == failure.Filename {
   868  			shouldPrint = true
   869  		} else if meta.SingleFilename != "" {
   870  			// TODO: the compiler may not return the rel path due to logic in bestFilePath
   871  			absSingleFilename, err := file.AbsClean(meta.SingleFilename)
   872  			if err != nil {
   873  				return err
   874  			}
   875  			absFailureFilename, err := file.AbsClean(failure.Filename)
   876  			if err != nil {
   877  				return err
   878  			}
   879  			if absSingleFilename == absFailureFilename {
   880  				shouldPrint = true
   881  			}
   882  		}
   883  		if shouldPrint {
   884  			if r.json {
   885  				data, err := json.Marshal(failure)
   886  				if err != nil {
   887  					return err
   888  				}
   889  				if _, err := fmt.Fprintln(bufWriter, string(data)); err != nil {
   890  					return err
   891  				}
   892  			} else if err := failure.Fprintln(bufWriter, failureFields...); err != nil {
   893  				return err
   894  			}
   895  		}
   896  	}
   897  	return bufWriter.Flush()
   898  }
   899  
   900  func (r *runner) printLinters(linters []lint.Linter) error {
   901  	sort.Slice(linters, func(i int, j int) bool { return linters[i].ID() < linters[j].ID() })
   902  	tabWriter := newTabWriter(r.output)
   903  	for _, linter := range linters {
   904  		if _, err := fmt.Fprintf(tabWriter, "%s\t%s\n", linter.ID(), linter.Purpose()); err != nil {
   905  			return err
   906  		}
   907  	}
   908  	return tabWriter.Flush()
   909  }
   910  
   911  func (r *runner) printAffectedFiles(meta *meta) {
   912  	for _, files := range meta.ProtoSet.DirPathToFiles {
   913  		for _, file := range files {
   914  			r.logger.Debug("using file", zap.String("file", file.DisplayPath))
   915  		}
   916  	}
   917  }
   918  
   919  func (r *runner) println(s string) error {
   920  	if s == "" {
   921  		return nil
   922  	}
   923  	_, err := fmt.Fprintln(r.output, s)
   924  	return err
   925  }
   926  
   927  func (r *runner) getInputData(arg string) ([]byte, error) {
   928  	if arg == "-" {
   929  		return ioutil.ReadAll(r.input)
   930  	}
   931  	return []byte(arg), nil
   932  }
   933  
   934  func (r *runner) getInputReader(data string, stdin bool) io.Reader {
   935  	if stdin {
   936  		return r.input
   937  	}
   938  	return bytes.NewReader([]byte(data))
   939  }
   940  
   941  func newExitErrorf(code int, format string, args ...interface{}) *ExitError {
   942  	return &ExitError{
   943  		Code:    code,
   944  		Message: fmt.Sprintf(format, args...),
   945  	}
   946  }
   947  
   948  func newTabWriter(writer io.Writer) *tabwriter.Writer {
   949  	return tabwriter.NewWriter(writer, 0, 0, 2, ' ', 0)
   950  }