Source file src/cmd/compile/internal/base/flag.go

     1  // Copyright 2009 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package base
     6  
     7  import (
     8  	"encoding/json"
     9  	"flag"
    10  	"fmt"
    11  	"internal/buildcfg"
    12  	"io/ioutil"
    13  	"log"
    14  	"os"
    15  	"reflect"
    16  	"runtime"
    17  	"strings"
    18  
    19  	"cmd/internal/objabi"
    20  	"cmd/internal/sys"
    21  )
    22  
    23  func usage() {
    24  	fmt.Fprintf(os.Stderr, "usage: compile [options] file.go...\n")
    25  	objabi.Flagprint(os.Stderr)
    26  	Exit(2)
    27  }
    28  
    29  // Flag holds the parsed command-line flags.
    30  // See ParseFlag for non-zero defaults.
    31  var Flag CmdFlags
    32  
    33  // A CountFlag is a counting integer flag.
    34  // It accepts -name=value to set the value directly,
    35  // but it also accepts -name with no =value to increment the count.
    36  type CountFlag int
    37  
    38  // CmdFlags defines the command-line flags (see var Flag).
    39  // Each struct field is a different flag, by default named for the lower-case of the field name.
    40  // If the flag name is a single letter, the default flag name is left upper-case.
    41  // If the flag name is "Lower" followed by a single letter, the default flag name is the lower-case of the last letter.
    42  //
    43  // If this default flag name can't be made right, the `flag` struct tag can be used to replace it,
    44  // but this should be done only in exceptional circumstances: it helps everyone if the flag name
    45  // is obvious from the field name when the flag is used elsewhere in the compiler sources.
    46  // The `flag:"-"` struct tag makes a field invisible to the flag logic and should also be used sparingly.
    47  //
    48  // Each field must have a `help` struct tag giving the flag help message.
    49  //
    50  // The allowed field types are bool, int, string, pointers to those (for values stored elsewhere),
    51  // CountFlag (for a counting flag), and func(string) (for a flag that uses special code for parsing).
    52  type CmdFlags struct {
    53  	// Single letters
    54  	B CountFlag    "help:\"disable bounds checking\""
    55  	C CountFlag    "help:\"disable printing of columns in error messages\""
    56  	D string       "help:\"set relative `path` for local imports\""
    57  	E CountFlag    "help:\"debug symbol export\""
    58  	G CountFlag    "help:\"accept generic code\""
    59  	I func(string) "help:\"add `directory` to import search path\""
    60  	K CountFlag    "help:\"debug missing line numbers\""
    61  	L CountFlag    "help:\"show full file names in error messages\""
    62  	N CountFlag    "help:\"disable optimizations\""
    63  	S CountFlag    "help:\"print assembly listing\""
    64  	// V is added by objabi.AddVersionFlag
    65  	W CountFlag "help:\"debug parse tree after type checking\""
    66  
    67  	LowerC int        "help:\"concurrency during compilation (1 means no concurrency)\""
    68  	LowerD flag.Value "help:\"enable debugging settings; try -d help\""
    69  	LowerE CountFlag  "help:\"no limit on number of errors reported\""
    70  	LowerH CountFlag  "help:\"halt on error\""
    71  	LowerJ CountFlag  "help:\"debug runtime-initialized variables\""
    72  	LowerL CountFlag  "help:\"disable inlining\""
    73  	LowerM CountFlag  "help:\"print optimization decisions\""
    74  	LowerO string     "help:\"write output to `file`\""
    75  	LowerP *string    "help:\"set expected package import `path`\"" // &Ctxt.Pkgpath, set below
    76  	LowerR CountFlag  "help:\"debug generated wrappers\""
    77  	LowerT bool       "help:\"enable tracing for debugging the compiler\""
    78  	LowerW CountFlag  "help:\"debug type checking\""
    79  	LowerV *bool      "help:\"increase debug verbosity\""
    80  
    81  	// Special characters
    82  	Percent          int  "flag:\"%\" help:\"debug non-static initializers\""
    83  	CompilingRuntime bool "flag:\"+\" help:\"compiling runtime\""
    84  
    85  	// Longer names
    86  	AsmHdr             string       "help:\"write assembly header to `file`\""
    87  	ASan               bool         "help:\"build code compatible with C/C++ address sanitizer\""
    88  	Bench              string       "help:\"append benchmark times to `file`\""
    89  	BlockProfile       string       "help:\"write block profile to `file`\""
    90  	BuildID            string       "help:\"record `id` as the build id in the export metadata\""
    91  	CPUProfile         string       "help:\"write cpu profile to `file`\""
    92  	Complete           bool         "help:\"compiling complete package (no C or assembly)\""
    93  	ClobberDead        bool         "help:\"clobber dead stack slots (for debugging)\""
    94  	ClobberDeadReg     bool         "help:\"clobber dead registers (for debugging)\""
    95  	Dwarf              bool         "help:\"generate DWARF symbols\""
    96  	DwarfBASEntries    *bool        "help:\"use base address selection entries in DWARF\""                        // &Ctxt.UseBASEntries, set below
    97  	DwarfLocationLists *bool        "help:\"add location lists to DWARF in optimized mode\""                      // &Ctxt.Flag_locationlists, set below
    98  	Dynlink            *bool        "help:\"support references to Go symbols defined in other shared libraries\"" // &Ctxt.Flag_dynlink, set below
    99  	EmbedCfg           func(string) "help:\"read go:embed configuration from `file`\""
   100  	GenDwarfInl        int          "help:\"generate DWARF inline info records\"" // 0=disabled, 1=funcs, 2=funcs+formals/locals
   101  	GoVersion          string       "help:\"required version of the runtime\""
   102  	ImportCfg          func(string) "help:\"read import configuration from `file`\""
   103  	ImportMap          func(string) "help:\"add `definition` of the form source=actual to import map\""
   104  	InstallSuffix      string       "help:\"set pkg directory `suffix`\""
   105  	JSON               string       "help:\"version,file for JSON compiler/optimizer detail output\""
   106  	Lang               string       "help:\"Go language version source code expects\""
   107  	LinkObj            string       "help:\"write linker-specific object to `file`\""
   108  	LinkShared         *bool        "help:\"generate code that will be linked against Go shared libraries\"" // &Ctxt.Flag_linkshared, set below
   109  	Live               CountFlag    "help:\"debug liveness analysis\""
   110  	MSan               bool         "help:\"build code compatible with C/C++ memory sanitizer\""
   111  	MemProfile         string       "help:\"write memory profile to `file`\""
   112  	MemProfileRate     int          "help:\"set runtime.MemProfileRate to `rate`\""
   113  	MutexProfile       string       "help:\"write mutex profile to `file`\""
   114  	NoLocalImports     bool         "help:\"reject local (relative) imports\""
   115  	Pack               bool         "help:\"write to file.a instead of file.o\""
   116  	Race               bool         "help:\"enable race detector\""
   117  	Shared             *bool        "help:\"generate code that can be linked into a shared library\"" // &Ctxt.Flag_shared, set below
   118  	SmallFrames        bool         "help:\"reduce the size limit for stack allocated objects\""      // small stacks, to diagnose GC latency; see golang.org/issue/27732
   119  	Spectre            string       "help:\"enable spectre mitigations in `list` (all, index, ret)\""
   120  	Std                bool         "help:\"compiling standard library\""
   121  	SymABIs            string       "help:\"read symbol ABIs from `file`\""
   122  	TraceProfile       string       "help:\"write an execution trace to `file`\""
   123  	TrimPath           string       "help:\"remove `prefix` from recorded source file paths\""
   124  	WB                 bool         "help:\"enable write barrier\"" // TODO: remove
   125  
   126  	// Configuration derived from flags; not a flag itself.
   127  	Cfg struct {
   128  		Embed struct { // set by -embedcfg
   129  			Patterns map[string][]string
   130  			Files    map[string]string
   131  		}
   132  		ImportDirs   []string          // appended to by -I
   133  		ImportMap    map[string]string // set by -importmap OR -importcfg
   134  		PackageFile  map[string]string // set by -importcfg; nil means not in use
   135  		SpectreIndex bool              // set by -spectre=index or -spectre=all
   136  		// Whether we are adding any sort of code instrumentation, such as
   137  		// when the race detector is enabled.
   138  		Instrumenting bool
   139  	}
   140  }
   141  
   142  // ParseFlags parses the command-line flags into Flag.
   143  func ParseFlags() {
   144  	Flag.G = 3
   145  	Flag.I = addImportDir
   146  
   147  	Flag.LowerC = 1
   148  	Flag.LowerD = objabi.NewDebugFlag(&Debug, DebugSSA)
   149  	Flag.LowerP = &Ctxt.Pkgpath
   150  	Flag.LowerV = &Ctxt.Debugvlog
   151  
   152  	Flag.Dwarf = buildcfg.GOARCH != "wasm"
   153  	Flag.DwarfBASEntries = &Ctxt.UseBASEntries
   154  	Flag.DwarfLocationLists = &Ctxt.Flag_locationlists
   155  	*Flag.DwarfLocationLists = true
   156  	Flag.Dynlink = &Ctxt.Flag_dynlink
   157  	Flag.EmbedCfg = readEmbedCfg
   158  	Flag.GenDwarfInl = 2
   159  	Flag.ImportCfg = readImportCfg
   160  	Flag.ImportMap = addImportMap
   161  	Flag.LinkShared = &Ctxt.Flag_linkshared
   162  	Flag.Shared = &Ctxt.Flag_shared
   163  	Flag.WB = true
   164  
   165  	Debug.InlFuncsWithClosures = 1
   166  	if buildcfg.Experiment.Unified {
   167  		Debug.Unified = 1
   168  	}
   169  
   170  	Debug.Checkptr = -1 // so we can tell whether it is set explicitly
   171  
   172  	Flag.Cfg.ImportMap = make(map[string]string)
   173  
   174  	objabi.AddVersionFlag() // -V
   175  	registerFlags()
   176  	objabi.Flagparse(usage)
   177  
   178  	if Flag.MSan && !sys.MSanSupported(buildcfg.GOOS, buildcfg.GOARCH) {
   179  		log.Fatalf("%s/%s does not support -msan", buildcfg.GOOS, buildcfg.GOARCH)
   180  	}
   181  	if Flag.ASan && !sys.ASanSupported(buildcfg.GOOS, buildcfg.GOARCH) {
   182  		log.Fatalf("%s/%s does not support -asan", buildcfg.GOOS, buildcfg.GOARCH)
   183  	}
   184  	if Flag.Race && !sys.RaceDetectorSupported(buildcfg.GOOS, buildcfg.GOARCH) {
   185  		log.Fatalf("%s/%s does not support -race", buildcfg.GOOS, buildcfg.GOARCH)
   186  	}
   187  	if (*Flag.Shared || *Flag.Dynlink || *Flag.LinkShared) && !Ctxt.Arch.InFamily(sys.AMD64, sys.ARM, sys.ARM64, sys.I386, sys.PPC64, sys.RISCV64, sys.S390X) {
   188  		log.Fatalf("%s/%s does not support -shared", buildcfg.GOOS, buildcfg.GOARCH)
   189  	}
   190  	parseSpectre(Flag.Spectre) // left as string for RecordFlags
   191  
   192  	Ctxt.Flag_shared = Ctxt.Flag_dynlink || Ctxt.Flag_shared
   193  	Ctxt.Flag_optimize = Flag.N == 0
   194  	Ctxt.Debugasm = int(Flag.S)
   195  	Ctxt.Flag_maymorestack = Debug.MayMoreStack
   196  
   197  	if flag.NArg() < 1 {
   198  		usage()
   199  	}
   200  
   201  	if Flag.GoVersion != "" && Flag.GoVersion != runtime.Version() {
   202  		fmt.Printf("compile: version %q does not match go tool version %q\n", runtime.Version(), Flag.GoVersion)
   203  		Exit(2)
   204  	}
   205  
   206  	if Flag.LowerO == "" {
   207  		p := flag.Arg(0)
   208  		if i := strings.LastIndex(p, "/"); i >= 0 {
   209  			p = p[i+1:]
   210  		}
   211  		if runtime.GOOS == "windows" {
   212  			if i := strings.LastIndex(p, `\`); i >= 0 {
   213  				p = p[i+1:]
   214  			}
   215  		}
   216  		if i := strings.LastIndex(p, "."); i >= 0 {
   217  			p = p[:i]
   218  		}
   219  		suffix := ".o"
   220  		if Flag.Pack {
   221  			suffix = ".a"
   222  		}
   223  		Flag.LowerO = p + suffix
   224  	}
   225  	switch {
   226  	case Flag.Race && Flag.MSan:
   227  		log.Fatal("cannot use both -race and -msan")
   228  	case Flag.Race && Flag.ASan:
   229  		log.Fatal("cannot use both -race and -asan")
   230  	case Flag.MSan && Flag.ASan:
   231  		log.Fatal("cannot use both -msan and -asan")
   232  	}
   233  	if Flag.Race || Flag.MSan || Flag.ASan {
   234  		// -race, -msan and -asan imply -d=checkptr for now.
   235  		if Debug.Checkptr == -1 { // if not set explicitly
   236  			Debug.Checkptr = 1
   237  		}
   238  	}
   239  
   240  	if Flag.CompilingRuntime && Flag.N != 0 {
   241  		log.Fatal("cannot disable optimizations while compiling runtime")
   242  	}
   243  	if Flag.LowerC < 1 {
   244  		log.Fatalf("-c must be at least 1, got %d", Flag.LowerC)
   245  	}
   246  	if Flag.LowerC > 1 && !concurrentBackendAllowed() {
   247  		log.Fatalf("cannot use concurrent backend compilation with provided flags; invoked as %v", os.Args)
   248  	}
   249  
   250  	if Flag.CompilingRuntime {
   251  		// Runtime can't use -d=checkptr, at least not yet.
   252  		Debug.Checkptr = 0
   253  
   254  		// Fuzzing the runtime isn't interesting either.
   255  		Debug.Libfuzzer = 0
   256  	}
   257  
   258  	if Debug.Checkptr == -1 { // if not set explicitly
   259  		Debug.Checkptr = 0
   260  	}
   261  
   262  	// set via a -d flag
   263  	Ctxt.Debugpcln = Debug.PCTab
   264  }
   265  
   266  // registerFlags adds flag registrations for all the fields in Flag.
   267  // See the comment on type CmdFlags for the rules.
   268  func registerFlags() {
   269  	var (
   270  		boolType      = reflect.TypeOf(bool(false))
   271  		intType       = reflect.TypeOf(int(0))
   272  		stringType    = reflect.TypeOf(string(""))
   273  		ptrBoolType   = reflect.TypeOf(new(bool))
   274  		ptrIntType    = reflect.TypeOf(new(int))
   275  		ptrStringType = reflect.TypeOf(new(string))
   276  		countType     = reflect.TypeOf(CountFlag(0))
   277  		funcType      = reflect.TypeOf((func(string))(nil))
   278  	)
   279  
   280  	v := reflect.ValueOf(&Flag).Elem()
   281  	t := v.Type()
   282  	for i := 0; i < t.NumField(); i++ {
   283  		f := t.Field(i)
   284  		if f.Name == "Cfg" {
   285  			continue
   286  		}
   287  
   288  		var name string
   289  		if len(f.Name) == 1 {
   290  			name = f.Name
   291  		} else if len(f.Name) == 6 && f.Name[:5] == "Lower" && 'A' <= f.Name[5] && f.Name[5] <= 'Z' {
   292  			name = string(rune(f.Name[5] + 'a' - 'A'))
   293  		} else {
   294  			name = strings.ToLower(f.Name)
   295  		}
   296  		if tag := f.Tag.Get("flag"); tag != "" {
   297  			name = tag
   298  		}
   299  
   300  		help := f.Tag.Get("help")
   301  		if help == "" {
   302  			panic(fmt.Sprintf("base.Flag.%s is missing help text", f.Name))
   303  		}
   304  
   305  		if k := f.Type.Kind(); (k == reflect.Ptr || k == reflect.Func) && v.Field(i).IsNil() {
   306  			panic(fmt.Sprintf("base.Flag.%s is uninitialized %v", f.Name, f.Type))
   307  		}
   308  
   309  		switch f.Type {
   310  		case boolType:
   311  			p := v.Field(i).Addr().Interface().(*bool)
   312  			flag.BoolVar(p, name, *p, help)
   313  		case intType:
   314  			p := v.Field(i).Addr().Interface().(*int)
   315  			flag.IntVar(p, name, *p, help)
   316  		case stringType:
   317  			p := v.Field(i).Addr().Interface().(*string)
   318  			flag.StringVar(p, name, *p, help)
   319  		case ptrBoolType:
   320  			p := v.Field(i).Interface().(*bool)
   321  			flag.BoolVar(p, name, *p, help)
   322  		case ptrIntType:
   323  			p := v.Field(i).Interface().(*int)
   324  			flag.IntVar(p, name, *p, help)
   325  		case ptrStringType:
   326  			p := v.Field(i).Interface().(*string)
   327  			flag.StringVar(p, name, *p, help)
   328  		case countType:
   329  			p := (*int)(v.Field(i).Addr().Interface().(*CountFlag))
   330  			objabi.Flagcount(name, help, p)
   331  		case funcType:
   332  			f := v.Field(i).Interface().(func(string))
   333  			objabi.Flagfn1(name, help, f)
   334  		default:
   335  			if val, ok := v.Field(i).Interface().(flag.Value); ok {
   336  				flag.Var(val, name, help)
   337  			} else {
   338  				panic(fmt.Sprintf("base.Flag.%s has unexpected type %s", f.Name, f.Type))
   339  			}
   340  		}
   341  	}
   342  }
   343  
   344  // concurrentFlagOk reports whether the current compiler flags
   345  // are compatible with concurrent compilation.
   346  func concurrentFlagOk() bool {
   347  	// TODO(rsc): Many of these are fine. Remove them.
   348  	return Flag.Percent == 0 &&
   349  		Flag.E == 0 &&
   350  		Flag.K == 0 &&
   351  		Flag.L == 0 &&
   352  		Flag.LowerH == 0 &&
   353  		Flag.LowerJ == 0 &&
   354  		Flag.LowerM == 0 &&
   355  		Flag.LowerR == 0
   356  }
   357  
   358  func concurrentBackendAllowed() bool {
   359  	if !concurrentFlagOk() {
   360  		return false
   361  	}
   362  
   363  	// Debug.S by itself is ok, because all printing occurs
   364  	// while writing the object file, and that is non-concurrent.
   365  	// Adding Debug_vlog, however, causes Debug.S to also print
   366  	// while flushing the plist, which happens concurrently.
   367  	if Ctxt.Debugvlog || Debug.Any || Flag.Live > 0 {
   368  		return false
   369  	}
   370  	// TODO: Test and delete this condition.
   371  	if buildcfg.Experiment.FieldTrack {
   372  		return false
   373  	}
   374  	// TODO: fix races and enable the following flags
   375  	if Ctxt.Flag_dynlink || Flag.Race {
   376  		return false
   377  	}
   378  	return true
   379  }
   380  
   381  func addImportDir(dir string) {
   382  	if dir != "" {
   383  		Flag.Cfg.ImportDirs = append(Flag.Cfg.ImportDirs, dir)
   384  	}
   385  }
   386  
   387  func addImportMap(s string) {
   388  	if Flag.Cfg.ImportMap == nil {
   389  		Flag.Cfg.ImportMap = make(map[string]string)
   390  	}
   391  	if strings.Count(s, "=") != 1 {
   392  		log.Fatal("-importmap argument must be of the form source=actual")
   393  	}
   394  	i := strings.Index(s, "=")
   395  	source, actual := s[:i], s[i+1:]
   396  	if source == "" || actual == "" {
   397  		log.Fatal("-importmap argument must be of the form source=actual; source and actual must be non-empty")
   398  	}
   399  	Flag.Cfg.ImportMap[source] = actual
   400  }
   401  
   402  func readImportCfg(file string) {
   403  	if Flag.Cfg.ImportMap == nil {
   404  		Flag.Cfg.ImportMap = make(map[string]string)
   405  	}
   406  	Flag.Cfg.PackageFile = map[string]string{}
   407  	data, err := ioutil.ReadFile(file)
   408  	if err != nil {
   409  		log.Fatalf("-importcfg: %v", err)
   410  	}
   411  
   412  	for lineNum, line := range strings.Split(string(data), "\n") {
   413  		lineNum++ // 1-based
   414  		line = strings.TrimSpace(line)
   415  		if line == "" || strings.HasPrefix(line, "#") {
   416  			continue
   417  		}
   418  
   419  		var verb, args string
   420  		if i := strings.Index(line, " "); i < 0 {
   421  			verb = line
   422  		} else {
   423  			verb, args = line[:i], strings.TrimSpace(line[i+1:])
   424  		}
   425  		var before, after string
   426  		if i := strings.Index(args, "="); i >= 0 {
   427  			before, after = args[:i], args[i+1:]
   428  		}
   429  		switch verb {
   430  		default:
   431  			log.Fatalf("%s:%d: unknown directive %q", file, lineNum, verb)
   432  		case "importmap":
   433  			if before == "" || after == "" {
   434  				log.Fatalf(`%s:%d: invalid importmap: syntax is "importmap old=new"`, file, lineNum)
   435  			}
   436  			Flag.Cfg.ImportMap[before] = after
   437  		case "packagefile":
   438  			if before == "" || after == "" {
   439  				log.Fatalf(`%s:%d: invalid packagefile: syntax is "packagefile path=filename"`, file, lineNum)
   440  			}
   441  			Flag.Cfg.PackageFile[before] = after
   442  		}
   443  	}
   444  }
   445  
   446  func readEmbedCfg(file string) {
   447  	data, err := ioutil.ReadFile(file)
   448  	if err != nil {
   449  		log.Fatalf("-embedcfg: %v", err)
   450  	}
   451  	if err := json.Unmarshal(data, &Flag.Cfg.Embed); err != nil {
   452  		log.Fatalf("%s: %v", file, err)
   453  	}
   454  	if Flag.Cfg.Embed.Patterns == nil {
   455  		log.Fatalf("%s: invalid embedcfg: missing Patterns", file)
   456  	}
   457  	if Flag.Cfg.Embed.Files == nil {
   458  		log.Fatalf("%s: invalid embedcfg: missing Files", file)
   459  	}
   460  }
   461  
   462  // parseSpectre parses the spectre configuration from the string s.
   463  func parseSpectre(s string) {
   464  	for _, f := range strings.Split(s, ",") {
   465  		f = strings.TrimSpace(f)
   466  		switch f {
   467  		default:
   468  			log.Fatalf("unknown setting -spectre=%s", f)
   469  		case "":
   470  			// nothing
   471  		case "all":
   472  			Flag.Cfg.SpectreIndex = true
   473  			Ctxt.Retpoline = true
   474  		case "index":
   475  			Flag.Cfg.SpectreIndex = true
   476  		case "ret":
   477  			Ctxt.Retpoline = true
   478  		}
   479  	}
   480  
   481  	if Flag.Cfg.SpectreIndex {
   482  		switch buildcfg.GOARCH {
   483  		case "amd64":
   484  			// ok
   485  		default:
   486  			log.Fatalf("GOARCH=%s does not support -spectre=index", buildcfg.GOARCH)
   487  		}
   488  	}
   489  }
   490  

View as plain text