github.com/bir3/gocompiler@v0.3.205/src/cmd/gocmd/internal/work/init.go (about) 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. 4 5 // Build initialization (after flag parsing). 6 7 package work 8 9 import ( 10 "bytes" 11 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/base" 12 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/cfg" 13 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/fsys" 14 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/modload" 15 "github.com/bir3/gocompiler/src/cmd/internal/quoted" 16 "fmt" 17 "github.com/bir3/gocompiler/src/internal/platform" 18 "os" 19 "os/exec" 20 "path/filepath" 21 "regexp" 22 "runtime" 23 "strconv" 24 "sync" 25 ) 26 27 var buildInitStarted = false 28 29 func BuildInit() { 30 if buildInitStarted { 31 base.Fatalf("go: internal error: work.BuildInit called more than once") 32 } 33 buildInitStarted = true 34 base.AtExit(closeBuilders) 35 36 modload.Init() 37 instrumentInit() 38 buildModeInit() 39 if err := fsys.Init(base.Cwd()); err != nil { 40 base.Fatalf("go: %v", err) 41 } 42 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) 47 if err != nil { 48 fmt.Fprintf(os.Stderr, "go: evaluating -pkgdir: %v\n", err) 49 base.SetExitStatus(2) 50 base.Exit() 51 } 52 cfg.BuildPkgdir = p 53 } 54 55 if cfg.BuildP <= 0 { 56 base.Fatalf("go: -p must be a positive integer: %v\n", cfg.BuildP) 57 } 58 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) 63 if err != nil { 64 base.Fatalf("go: %s environment variable could not be parsed: %v", key, err) 65 } 66 if len(args) == 0 { 67 continue 68 } 69 path := args[0] 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) 72 } 73 } 74 75 // Set covermode if not already set. 76 // Ensure that -race and -covermode are compatible. 77 if cfg.BuildCoverMode == "" { 78 cfg.BuildCoverMode = "set" 79 if cfg.BuildRace { 80 // Default coverage mode is atomic when -race is set. 81 cfg.BuildCoverMode = "atomic" 82 } 83 } 84 if cfg.BuildRace && cfg.BuildCoverMode != "atomic" { 85 base.Fatalf(`-covermode must be "atomic", not %q, when -race is enabled`, cfg.BuildCoverMode) 86 } 87 } 88 89 // fuzzInstrumentFlags returns compiler flags that enable fuzzing instrumation 90 // on supported platforms. 91 // 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) { 97 return nil 98 } 99 return []string{"-d=libfuzzer"} 100 } 101 102 func instrumentInit() { 103 if !cfg.BuildRace && !cfg.BuildMSan && !cfg.BuildASan { 104 return 105 } 106 if cfg.BuildRace && cfg.BuildMSan { 107 fmt.Fprintf(os.Stderr, "go: may not use -race and -msan simultaneously\n") 108 base.SetExitStatus(2) 109 base.Exit() 110 } 111 if cfg.BuildRace && cfg.BuildASan { 112 fmt.Fprintf(os.Stderr, "go: may not use -race and -asan simultaneously\n") 113 base.SetExitStatus(2) 114 base.Exit() 115 } 116 if cfg.BuildMSan && cfg.BuildASan { 117 fmt.Fprintf(os.Stderr, "go: may not use -msan and -asan simultaneously\n") 118 base.SetExitStatus(2) 119 base.Exit() 120 } 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) 124 base.Exit() 125 } 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) 129 base.Exit() 130 } 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) 134 base.Exit() 135 } 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. 141 if cfg.BuildASan { 142 if err := compilerRequiredAsanVersion(); err != nil { 143 fmt.Fprintf(os.Stderr, "%v\n", err) 144 base.SetExitStatus(2) 145 base.Exit() 146 } 147 } 148 149 mode := "race" 150 if cfg.BuildMSan { 151 mode = "msan" 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" 156 } 157 } 158 if cfg.BuildASan { 159 mode = "asan" 160 } 161 modeFlag := "-" + mode 162 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) 168 } else { 169 fmt.Fprintf(os.Stderr, "go: %s requires cgo; enable cgo by setting CGO_ENABLED=1\n", modeFlag) 170 } 171 172 base.SetExitStatus(2) 173 base.Exit() 174 } 175 forcedGcflags = append(forcedGcflags, modeFlag) 176 forcedLdflags = append(forcedLdflags, modeFlag) 177 178 if cfg.BuildContext.InstallSuffix != "" { 179 cfg.BuildContext.InstallSuffix += "_" 180 } 181 cfg.BuildContext.InstallSuffix += mode 182 cfg.BuildContext.ToolTags = append(cfg.BuildContext.ToolTags, mode) 183 } 184 185 func buildModeInit() { 186 gccgo := cfg.BuildToolchainName == "gccgo" 187 var codegenArg string 188 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. 192 193 switch cfg.BuildBuildmode { 194 case "archive": 195 pkgsFilter = pkgsNotMain 196 case "c-archive": 197 pkgsFilter = oneMainPkg 198 if gccgo { 199 codegenArg = "-fPIC" 200 } else { 201 switch cfg.Goos { 202 case "darwin", "ios": 203 switch cfg.Goarch { 204 case "arm64": 205 codegenArg = "-shared" 206 } 207 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 211 // shared library. 212 codegenArg = "-shared" 213 } 214 } 215 cfg.ExeSuffix = ".a" 216 ldBuildmode = "c-archive" 217 case "c-shared": 218 pkgsFilter = oneMainPkg 219 if gccgo { 220 codegenArg = "-fPIC" 221 } else { 222 switch cfg.Goos { 223 case "linux", "android", "freebsd": 224 codegenArg = "-shared" 225 case "windows": 226 // Do not add usual .exe suffix to the .dll file. 227 cfg.ExeSuffix = "" 228 } 229 } 230 ldBuildmode = "c-shared" 231 case "default": 232 switch cfg.Goos { 233 case "android": 234 codegenArg = "-shared" 235 ldBuildmode = "pie" 236 case "windows": 237 if cfg.BuildRace { 238 ldBuildmode = "exe" 239 } else { 240 ldBuildmode = "pie" 241 } 242 case "ios": 243 codegenArg = "-shared" 244 ldBuildmode = "pie" 245 case "darwin": 246 switch cfg.Goarch { 247 case "arm64": 248 codegenArg = "-shared" 249 } 250 fallthrough 251 default: 252 ldBuildmode = "exe" 253 } 254 if gccgo { 255 codegenArg = "" 256 } 257 case "exe": 258 pkgsFilter = pkgsMain 259 ldBuildmode = "exe" 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. 262 // See issue #20017. 263 if cfg.BuildO != "" { 264 pkgsFilter = oneMainPkg 265 } 266 case "pie": 267 if cfg.BuildRace { 268 base.Fatalf("-buildmode=pie not supported when -race is enabled") 269 } 270 if gccgo { 271 codegenArg = "-fPIE" 272 } else { 273 switch cfg.Goos { 274 case "aix", "windows": 275 default: 276 codegenArg = "-shared" 277 } 278 } 279 ldBuildmode = "pie" 280 case "shared": 281 pkgsFilter = pkgsNotMain 282 if gccgo { 283 codegenArg = "-fPIC" 284 } else { 285 codegenArg = "-dynlink" 286 } 287 if cfg.BuildO != "" { 288 base.Fatalf("-buildmode=shared and -o not supported together") 289 } 290 ldBuildmode = "shared" 291 case "plugin": 292 pkgsFilter = oneMainPkg 293 if gccgo { 294 codegenArg = "-fPIC" 295 } else { 296 codegenArg = "-dynlink" 297 } 298 cfg.ExeSuffix = ".so" 299 ldBuildmode = "plugin" 300 default: 301 base.Fatalf("buildmode=%s not supported", cfg.BuildBuildmode) 302 } 303 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) 306 } 307 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) 311 } 312 if gccgo { 313 codegenArg = "-fPIC" 314 } else { 315 forcedAsmflags = append(forcedAsmflags, "-D=GOBUILDMODE_shared=1", 316 "-linkshared") 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") 321 } 322 } 323 if codegenArg != "" { 324 if gccgo { 325 forcedGccgoflags = append([]string{codegenArg}, forcedGccgoflags...) 326 } else { 327 forcedAsmflags = append([]string{codegenArg}, forcedAsmflags...) 328 forcedGcflags = append([]string{codegenArg}, forcedGcflags...) 329 } 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 += "_" 334 } 335 cfg.BuildContext.InstallSuffix += codegenArg[1:] 336 } 337 } 338 339 switch cfg.BuildMod { 340 case "": 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) 345 } 346 default: 347 base.Fatalf("-mod=%s not supported (can be '', 'mod', 'readonly', or 'vendor')", cfg.BuildMod) 348 } 349 if !cfg.ModulesEnabled { 350 if cfg.ModCacheRW && !base.InGOFLAGS("-modcacherw") { 351 base.Fatalf("build flag -modcacherw only valid when using modules") 352 } 353 if cfg.ModFile != "" && !base.InGOFLAGS("-mod") { 354 base.Fatalf("build flag -modfile only valid when using modules") 355 } 356 } 357 } 358 359 type version struct { 360 name string 361 major, minor int 362 } 363 364 var compiler struct { 365 sync.Once 366 version 367 err error 368 } 369 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() 379 if err != nil { 380 // Compiler does not support "--version" flag: not Clang or GCC. 381 return err 382 } 383 384 var match [][]byte 385 if bytes.HasPrefix(out, []byte("gcc")) { 386 compiler.name = "gcc" 387 out, err := exec.Command(cc, "-v").CombinedOutput() 388 if err != nil { 389 // gcc, but does not support gcc's "-v" flag?! 390 return err 391 } 392 gccRE := regexp.MustCompile(`gcc version (\d+)\.(\d+)`) 393 match = gccRE.FindSubmatch(out) 394 } else { 395 clangRE := regexp.MustCompile(`clang version (\d+)\.(\d+)`) 396 if match = clangRE.FindSubmatch(out); len(match) > 0 { 397 compiler.name = "clang" 398 } 399 } 400 401 if len(match) < 3 { 402 return nil // "unknown" 403 } 404 if compiler.major, err = strconv.Atoi(string(match[1])); err != nil { 405 return err 406 } 407 if compiler.minor, err = strconv.Atoi(string(match[2])); err != nil { 408 return err 409 } 410 return nil 411 }() 412 }) 413 return compiler.version, compiler.err 414 } 415 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 419 // required by Asan. 420 func compilerRequiredAsanVersion() error { 421 compiler, err := compilerVersion() 422 if err != nil { 423 return fmt.Errorf("-asan: the version of $(go env CC) could not be parsed") 424 } 425 426 switch compiler.name { 427 case "gcc": 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) 430 } 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) 433 } 434 case "clang": 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) 437 } 438 default: 439 return fmt.Errorf("-asan: C compiler is not gcc or clang") 440 } 441 return nil 442 }