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  }