github.com/aca02djr/gb@v0.4.1/cmd/gb/main.go (about) 1 package main 2 3 import ( 4 "flag" 5 "fmt" 6 "os" 7 "os/exec" 8 "path/filepath" 9 "runtime" 10 11 "github.com/constabulary/gb" 12 "github.com/constabulary/gb/cmd" 13 "github.com/constabulary/gb/debug" 14 ) 15 16 var ( 17 fs = flag.NewFlagSet(os.Args[0], flag.ExitOnError) 18 cwd string 19 ) 20 21 const ( 22 // disable to keep working directory 23 destroyContext = true 24 ) 25 26 func init() { 27 fs.StringVar(&cwd, "R", cmd.MustGetwd(), "set the project root") // actually the working directory to start the project root search 28 fs.Usage = usage 29 } 30 31 var commands = make(map[string]*cmd.Command) 32 33 // registerCommand registers a command for main. 34 // registerCommand should only be called from init(). 35 func registerCommand(command *cmd.Command) { 36 commands[command.Name] = command 37 } 38 39 // atExit functions are called in sequence at the exit of the program. 40 var atExit []func() error 41 42 // exit runs all atExit functions, then calls os.Exit(code). 43 func exit(code int) { 44 for _, fn := range atExit { 45 fn() 46 } 47 os.Exit(code) 48 } 49 50 func fatalf(format string, args ...interface{}) { 51 fmt.Fprintf(os.Stderr, "FATAL: "+format+"\n", args...) 52 exit(1) 53 } 54 55 func main() { 56 args := os.Args 57 if len(args) < 2 || args[1] == "-h" { 58 fs.Usage() // usage calles exit(2) 59 } 60 name := args[1] 61 if name == "help" { 62 help(args[2:]) 63 exit(0) 64 } 65 66 command, ok := commands[name] 67 if (command != nil && !command.Runnable()) || !ok { 68 plugin, err := lookupPlugin(name) 69 if err != nil { 70 fmt.Fprintf(os.Stderr, "FATAL: unknown command %q\n", name) 71 fs.Usage() // usage calles exit(2) 72 } 73 command = &cmd.Command{ 74 Run: func(ctx *gb.Context, args []string) error { 75 args = append([]string{plugin}, args...) 76 77 env := cmd.MergeEnv(os.Environ(), map[string]string{ 78 "GB_PROJECT_DIR": ctx.Projectdir(), 79 }) 80 81 cmd := exec.Cmd{ 82 Path: plugin, 83 Args: args, 84 Env: env, 85 86 Stdin: os.Stdin, 87 Stdout: os.Stdout, 88 Stderr: os.Stderr, 89 } 90 91 return cmd.Run() 92 }, 93 // plugin should not interpret arguments 94 SkipParseArgs: true, 95 } 96 } 97 98 // add extra flags if necessary 99 if command.AddFlags != nil { 100 command.AddFlags(fs) 101 } 102 103 var err error 104 if command.FlagParse != nil { 105 err = command.FlagParse(fs, args) 106 } else { 107 err = fs.Parse(args[2:]) 108 } 109 if err != nil { 110 fatalf("could not parse flags: %v", err) 111 } 112 113 args = fs.Args() // reset args to the leftovers from fs.Parse 114 115 debug.Debugf("args: %v", args) 116 117 if command == commands["plugin"] { 118 args = append([]string{name}, args...) 119 } 120 cwd, err := filepath.Abs(cwd) // if cwd was passed in via -R, make sure it is absolute 121 if err != nil { 122 fatalf("could not make project root absolute: %v", err) 123 } 124 125 ctx, err := cmd.NewContext( 126 cwd, // project root 127 gb.GcToolchain(), 128 gb.Gcflags(gcflags...), 129 gb.Ldflags(ldflags...), 130 gb.Tags(buildtags...), 131 func(c *gb.Context) error { 132 if !race { 133 return nil 134 } 135 136 // check this is a supported platform 137 if runtime.GOARCH != "amd64" { 138 fatalf("race detector not supported on %s/%s", runtime.GOOS, runtime.GOARCH) 139 } 140 switch runtime.GOOS { 141 case "linux", "windows", "darwin", "freebsd": 142 // supported 143 default: 144 fatalf("race detector not supported on %s/%s", runtime.GOOS, runtime.GOARCH) 145 } 146 147 // check the race runtime is built 148 _, err := os.Stat(filepath.Join(runtime.GOROOT(), "pkg", fmt.Sprintf("%s_%s_race", runtime.GOOS, runtime.GOARCH), "runtime.a")) 149 if os.IsNotExist(err) || err != nil { 150 fatalf("go installation at %s is missing race support. See https://getgb.io/faq/#missing-race-support", runtime.GOROOT()) 151 } 152 153 return gb.WithRace(c) 154 }, 155 ) 156 157 if err != nil { 158 fatalf("unable to construct context: %v", err) 159 } 160 161 if !command.SkipParseArgs { 162 args = importPaths(ctx, cwd, args) 163 } 164 165 debug.Debugf("args: %v", args) 166 167 if destroyContext { 168 atExit = append(atExit, ctx.Destroy) 169 } 170 171 if err := command.Run(ctx, args); err != nil { 172 fatalf("command %q failed: %v", name, err) 173 } 174 exit(0) 175 }