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 }