github.com/joshdk/godel@v0.0.0-20170529232908-862138a45aee/apps/distgo/cmd/run/run.go (about) 1 // Copyright 2016 Palantir Technologies, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package run 16 17 import ( 18 "fmt" 19 "go/ast" 20 "go/parser" 21 "go/token" 22 "io" 23 "io/ioutil" 24 "os" 25 "os/exec" 26 "path" 27 "strings" 28 29 "github.com/pkg/errors" 30 31 "github.com/palantir/godel/apps/distgo/params" 32 ) 33 34 func DoRun(buildSpec params.ProductBuildSpec, runArgs []string, stdout, stderr io.Writer) error { 35 mainPkgDir := path.Join(buildSpec.ProjectDir, buildSpec.Build.MainPkg) 36 mainPkgFileNames, err := mainPkgFileNames(mainPkgDir) 37 if err != nil { 38 return errors.Wrapf(err, "failed to find main file") 39 } 40 41 cmd := exec.Command("go") 42 args := []string{cmd.Path, "run"} 43 for _, name := range mainPkgFileNames { 44 args = append(args, path.Join(buildSpec.ProjectDir, buildSpec.Build.MainPkg, name)) 45 } 46 args = append(args, buildSpec.Run.Args...) 47 args = append(args, transformRunFlagArgs(runArgs)...) 48 cmd.Args = args 49 50 cmd.Stdout = stdout 51 cmd.Stderr = stderr 52 cmd.Stdin = os.Stdin 53 54 fmt.Fprintln(stdout, strings.Join(args, " ")) 55 if err := cmd.Run(); err != nil { 56 return errors.Wrapf(err, "go run failed") 57 } 58 return nil 59 } 60 61 func transformRunFlagArgs(args []string) []string { 62 const flagPrefix = "flag:" 63 var newArgs []string 64 for _, currArg := range args { 65 // if current argument has flag prefix and has content after the prefix, trim the prefix 66 if strings.HasPrefix(currArg, flagPrefix) && len(currArg) > len(flagPrefix) { 67 currArg = strings.TrimPrefix(currArg, flagPrefix) 68 } 69 newArgs = append(newArgs, currArg) 70 } 71 return newArgs 72 } 73 74 // getMainPkgFiles returns the names of all of the files in the "main" pkg of the specified directory. Returns an error 75 // if there are no files in the "main" package that declares a "main" function (or if there are multiple such files). 76 func mainPkgFileNames(mainPkgDir string) ([]string, error) { 77 fileInfos, err := ioutil.ReadDir(mainPkgDir) 78 if err != nil { 79 return nil, errors.Wrapf(err, "failed to list files in directory %v", mainPkgDir) 80 } 81 var mainPkgFileNames []string 82 var mainFuncFileNames []string 83 for _, currFile := range fileInfos { 84 currFilePath := path.Join(mainPkgDir, currFile.Name()) 85 if !currFile.IsDir() && strings.HasSuffix(currFile.Name(), ".go") && !strings.HasSuffix(currFile.Name(), "_test.go") { 86 fset := token.NewFileSet() 87 fnode, err := parser.ParseFile(fset, currFilePath, nil, parser.ParseComments) 88 if err != nil { 89 return nil, errors.Wrapf(err, "failed to parse file %v", currFilePath) 90 } 91 92 // find main package 93 if fnode.Name.Name == "main" { 94 mainPkgFileNames = append(mainPkgFileNames, currFile.Name()) 95 if hasMainFunc(fnode) { 96 mainFuncFileNames = append(mainFuncFileNames, currFile.Name()) 97 } 98 } 99 } 100 } 101 102 switch len(mainFuncFileNames) { 103 case 0: 104 return nil, errors.Errorf("no go file with main package and main function exists in directory %v", mainPkgDir) 105 case 1: 106 return mainPkgFileNames, nil 107 default: 108 return nil, errors.Errorf("directory %v contain multiple files that have main package and main function: %v", mainPkgDir, mainFuncFileNames) 109 } 110 } 111 112 func hasMainFunc(node *ast.File) bool { 113 for _, currDecl := range node.Decls { 114 switch t := currDecl.(type) { 115 case *ast.FuncDecl: 116 if t.Name.Name == "main" { 117 return true 118 } 119 } 120 } 121 return false 122 }