1 // Copyright 2017 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.
5 // Build initialization (after flag parsing).
11 "cmd/go/internal/base"
13 "cmd/go/internal/fsys"
14 "cmd/go/internal/modload"
27 var buildInitStarted = false
31 base.Fatalf("go: internal error: work.BuildInit called more than once")
33 buildInitStarted = true
34 base.AtExit(closeBuilders)
39 if err := fsys.Init(base.Cwd()); err != nil {
40 base.Fatalf("go: %v", err)
43 // Make sure -pkgdir is absolute, because we run commands
44 // in different directories.
45 if cfg.BuildPkgdir != "" && !filepath.IsAbs(cfg.BuildPkgdir) {
46 p, err := filepath.Abs(cfg.BuildPkgdir)
48 fmt.Fprintf(os.Stderr, "go: evaluating -pkgdir: %v\n", err)
56 base.Fatalf("go: -p must be a positive integer: %v\n", cfg.BuildP)
59 // Make sure CC, CXX, and FC are absolute paths.
60 for _, key := range []string{"CC", "CXX", "FC"} {
61 value := cfg.Getenv(key)
62 args, err := quoted.Split(value)
64 base.Fatalf("go: %s environment variable could not be parsed: %v", key, err)
70 if !filepath.IsAbs(path) && path != filepath.Base(path) {
71 base.Fatalf("go: %s environment variable is relative; must be absolute path: %s\n", key, path)
75 // Set covermode if not already set.
76 // Ensure that -race and -covermode are compatible.
77 if cfg.BuildCoverMode == "" {
78 cfg.BuildCoverMode = "set"
80 // Default coverage mode is atomic when -race is set.
81 cfg.BuildCoverMode = "atomic"
84 if cfg.BuildRace && cfg.BuildCoverMode != "atomic" {
85 base.Fatalf(`-covermode must be "atomic", not %q, when -race is enabled`, cfg.BuildCoverMode)
89 // fuzzInstrumentFlags returns compiler flags that enable fuzzing instrumation
90 // on supported platforms.
92 // On unsupported platforms, fuzzInstrumentFlags returns nil, meaning no
93 // instrumentation is added. 'go test -fuzz' still works without coverage,
94 // but it generates random inputs without guidance, so it's much less effective.
95 func fuzzInstrumentFlags() []string {
96 if !platform.FuzzInstrumented(cfg.Goos, cfg.Goarch) {
99 return []string{"-d=libfuzzer"}
102 func instrumentInit() {
103 if !cfg.BuildRace && !cfg.BuildMSan && !cfg.BuildASan {
106 if cfg.BuildRace && cfg.BuildMSan {
107 fmt.Fprintf(os.Stderr, "go: may not use -race and -msan simultaneously\n")
108 base.SetExitStatus(2)
111 if cfg.BuildRace && cfg.BuildASan {
112 fmt.Fprintf(os.Stderr, "go: may not use -race and -asan simultaneously\n")
113 base.SetExitStatus(2)
116 if cfg.BuildMSan && cfg.BuildASan {
117 fmt.Fprintf(os.Stderr, "go: may not use -msan and -asan simultaneously\n")
118 base.SetExitStatus(2)
121 if cfg.BuildMSan && !platform.MSanSupported(cfg.Goos, cfg.Goarch) {
122 fmt.Fprintf(os.Stderr, "-msan is not supported on %s/%s\n", cfg.Goos, cfg.Goarch)
123 base.SetExitStatus(2)
126 if cfg.BuildRace && !platform.RaceDetectorSupported(cfg.Goos, cfg.Goarch) {
127 fmt.Fprintf(os.Stderr, "-race is not supported on %s/%s\n", cfg.Goos, cfg.Goarch)
128 base.SetExitStatus(2)
131 if cfg.BuildASan && !platform.ASanSupported(cfg.Goos, cfg.Goarch) {
132 fmt.Fprintf(os.Stderr, "-asan is not supported on %s/%s\n", cfg.Goos, cfg.Goarch)
133 base.SetExitStatus(2)
136 // The current implementation is only compatible with the ASan library from version
137 // v7 to v9 (See the description in src/runtime/asan/asan.go). Therefore, using the
138 // -asan option must use a compatible version of ASan library, which requires that
139 // the gcc version is not less than 7 and the clang version is not less than 9,
140 // otherwise a segmentation fault will occur.
142 if err := compilerRequiredAsanVersion(); err != nil {
143 fmt.Fprintf(os.Stderr, "%v\n", err)
144 base.SetExitStatus(2)
152 // MSAN needs PIE on all platforms except linux/amd64.
153 // https://github.com/llvm/llvm-project/blob/llvmorg-13.0.1/clang/lib/Driver/SanitizerArgs.cpp#L621
154 if cfg.BuildBuildmode == "default" && (cfg.Goos != "linux" || cfg.Goarch != "amd64") {
155 cfg.BuildBuildmode = "pie"
161 modeFlag := "-" + mode
163 // Check that cgo is enabled.
164 // Note: On macOS, -race does not require cgo. -asan and -msan still do.
165 if !cfg.BuildContext.CgoEnabled && (cfg.Goos != "darwin" || cfg.BuildASan || cfg.BuildMSan) {
166 if runtime.GOOS != cfg.Goos || runtime.GOARCH != cfg.Goarch {
167 fmt.Fprintf(os.Stderr, "go: %s requires cgo\n", modeFlag)
169 fmt.Fprintf(os.Stderr, "go: %s requires cgo; enable cgo by setting CGO_ENABLED=1\n", modeFlag)
172 base.SetExitStatus(2)
175 forcedGcflags = append(forcedGcflags, modeFlag)
176 forcedLdflags = append(forcedLdflags, modeFlag)
178 if cfg.BuildContext.InstallSuffix != "" {
179 cfg.BuildContext.InstallSuffix += "_"
181 cfg.BuildContext.InstallSuffix += mode
182 cfg.BuildContext.ToolTags = append(cfg.BuildContext.ToolTags, mode)
185 func buildModeInit() {
186 gccgo := cfg.BuildToolchainName == "gccgo"
187 var codegenArg string
189 // Configure the build mode first, then verify that it is supported.
190 // That way, if the flag is completely bogus we will prefer to error out with
191 // "-buildmode=%s not supported" instead of naming the specific platform.
193 switch cfg.BuildBuildmode {
195 pkgsFilter = pkgsNotMain
197 pkgsFilter = oneMainPkg
202 case "darwin", "ios":
205 codegenArg = "-shared"
208 case "dragonfly", "freebsd", "illumos", "linux", "netbsd", "openbsd", "solaris":
209 // Use -shared so that the result is
210 // suitable for inclusion in a PIE or
212 codegenArg = "-shared"
216 ldBuildmode = "c-archive"
218 pkgsFilter = oneMainPkg
223 case "linux", "android", "freebsd":
224 codegenArg = "-shared"
226 // Do not add usual .exe suffix to the .dll file.
230 ldBuildmode = "c-shared"
234 codegenArg = "-shared"
243 codegenArg = "-shared"
248 codegenArg = "-shared"
258 pkgsFilter = pkgsMain
260 // Set the pkgsFilter to oneMainPkg if the user passed a specific binary output
261 // and is using buildmode=exe for a better error message.
263 if cfg.BuildO != "" {
264 pkgsFilter = oneMainPkg
268 base.Fatalf("-buildmode=pie not supported when -race is enabled")
274 case "aix", "windows":
276 codegenArg = "-shared"
281 pkgsFilter = pkgsNotMain
285 codegenArg = "-dynlink"
287 if cfg.BuildO != "" {
288 base.Fatalf("-buildmode=shared and -o not supported together")
290 ldBuildmode = "shared"
292 pkgsFilter = oneMainPkg
296 codegenArg = "-dynlink"
298 cfg.ExeSuffix = ".so"
299 ldBuildmode = "plugin"
301 base.Fatalf("buildmode=%s not supported", cfg.BuildBuildmode)
304 if !platform.BuildModeSupported(cfg.BuildToolchainName, cfg.BuildBuildmode, cfg.Goos, cfg.Goarch) {
305 base.Fatalf("-buildmode=%s not supported on %s/%s\n", cfg.BuildBuildmode, cfg.Goos, cfg.Goarch)
308 if cfg.BuildLinkshared {
309 if !platform.BuildModeSupported(cfg.BuildToolchainName, "shared", cfg.Goos, cfg.Goarch) {
310 base.Fatalf("-linkshared not supported on %s/%s\n", cfg.Goos, cfg.Goarch)
315 forcedAsmflags = append(forcedAsmflags, "-D=GOBUILDMODE_shared=1",
317 codegenArg = "-dynlink"
318 forcedGcflags = append(forcedGcflags, "-linkshared")
319 // TODO(mwhudson): remove -w when that gets fixed in linker.
320 forcedLdflags = append(forcedLdflags, "-linkshared", "-w")
323 if codegenArg != "" {
325 forcedGccgoflags = append([]string{codegenArg}, forcedGccgoflags...)
327 forcedAsmflags = append([]string{codegenArg}, forcedAsmflags...)
328 forcedGcflags = append([]string{codegenArg}, forcedGcflags...)
330 // Don't alter InstallSuffix when modifying default codegen args.
331 if cfg.BuildBuildmode != "default" || cfg.BuildLinkshared {
332 if cfg.BuildContext.InstallSuffix != "" {
333 cfg.BuildContext.InstallSuffix += "_"
335 cfg.BuildContext.InstallSuffix += codegenArg[1:]
339 switch cfg.BuildMod {
341 // Behavior will be determined automatically, as if no flag were passed.
342 case "readonly", "vendor", "mod":
343 if !cfg.ModulesEnabled && !base.InGOFLAGS("-mod") {
344 base.Fatalf("build flag -mod=%s only valid when using modules", cfg.BuildMod)
347 base.Fatalf("-mod=%s not supported (can be '', 'mod', 'readonly', or 'vendor')", cfg.BuildMod)
349 if !cfg.ModulesEnabled {
350 if cfg.ModCacheRW && !base.InGOFLAGS("-modcacherw") {
351 base.Fatalf("build flag -modcacherw only valid when using modules")
353 if cfg.ModFile != "" && !base.InGOFLAGS("-mod") {
354 base.Fatalf("build flag -modfile only valid when using modules")
359 type version struct {
364 var compiler struct {
370 // compilerVersion detects the version of $(go env CC).
371 // It returns a non-nil error if the compiler matches a known version schema but
372 // the version could not be parsed, or if $(go env CC) could not be determined.
373 func compilerVersion() (version, error) {
374 compiler.Once.Do(func() {
375 compiler.err = func() error {
376 compiler.name = "unknown"
377 cc := os.Getenv("CC")
378 out, err := exec.Command(cc, "--version").Output()
380 // Compiler does not support "--version" flag: not Clang or GCC.
385 if bytes.HasPrefix(out, []byte("gcc")) {
386 compiler.name = "gcc"
387 out, err := exec.Command(cc, "-v").CombinedOutput()
389 // gcc, but does not support gcc's "-v" flag?!
392 gccRE := regexp.MustCompile(`gcc version (\d+)\.(\d+)`)
393 match = gccRE.FindSubmatch(out)
395 clangRE := regexp.MustCompile(`clang version (\d+)\.(\d+)`)
396 if match = clangRE.FindSubmatch(out); len(match) > 0 {
397 compiler.name = "clang"
402 return nil // "unknown"
404 if compiler.major, err = strconv.Atoi(string(match[1])); err != nil {
407 if compiler.minor, err = strconv.Atoi(string(match[2])); err != nil {
413 return compiler.version, compiler.err
416 // compilerRequiredAsanVersion is a copy of the function defined in
417 // misc/cgo/testsanitizers/cc_test.go
418 // compilerRequiredAsanVersion reports whether the compiler is the version
420 func compilerRequiredAsanVersion() error {
421 compiler, err := compilerVersion()
423 return fmt.Errorf("-asan: the version of $(go env CC) could not be parsed")
426 switch compiler.name {
428 if runtime.GOARCH == "ppc64le" && compiler.major < 9 {
429 return fmt.Errorf("-asan is not supported with %s compiler %d.%d\n", compiler.name, compiler.major, compiler.minor)
431 if compiler.major < 7 {
432 return fmt.Errorf("-asan is not supported with %s compiler %d.%d\n", compiler.name, compiler.major, compiler.minor)
435 if compiler.major < 9 {
436 return fmt.Errorf("-asan is not supported with %s compiler %d.%d\n", compiler.name, compiler.major, compiler.minor)
439 return fmt.Errorf("-asan: C compiler is not gcc or clang")