github.com/bir3/gocompiler@v0.9.2202/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 "github.com/bir3/gocompiler/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.Fatal(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 instrumentation 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 ldBuildmode = "exe" 233 if platform.DefaultPIE(cfg.Goos, cfg.Goarch, cfg.BuildRace) { 234 ldBuildmode = "pie" 235 if cfg.Goos != "windows" && !gccgo { 236 codegenArg = "-shared" 237 } 238 } 239 case "exe": 240 pkgsFilter = pkgsMain 241 ldBuildmode = "exe" 242 // Set the pkgsFilter to oneMainPkg if the user passed a specific binary output 243 // and is using buildmode=exe for a better error message. 244 // See issue #20017. 245 if cfg.BuildO != "" { 246 pkgsFilter = oneMainPkg 247 } 248 case "pie": 249 if cfg.BuildRace && !platform.DefaultPIE(cfg.Goos, cfg.Goarch, cfg.BuildRace) { 250 base.Fatalf("-buildmode=pie not supported when -race is enabled on %s/%s", cfg.Goos, cfg.Goarch) 251 } 252 if gccgo { 253 codegenArg = "-fPIE" 254 } else { 255 switch cfg.Goos { 256 case "aix", "windows": 257 default: 258 codegenArg = "-shared" 259 } 260 } 261 ldBuildmode = "pie" 262 case "shared": 263 pkgsFilter = pkgsNotMain 264 if gccgo { 265 codegenArg = "-fPIC" 266 } else { 267 codegenArg = "-dynlink" 268 } 269 if cfg.BuildO != "" { 270 base.Fatalf("-buildmode=shared and -o not supported together") 271 } 272 ldBuildmode = "shared" 273 case "plugin": 274 pkgsFilter = oneMainPkg 275 if gccgo { 276 codegenArg = "-fPIC" 277 } else { 278 codegenArg = "-dynlink" 279 } 280 cfg.ExeSuffix = ".so" 281 ldBuildmode = "plugin" 282 default: 283 base.Fatalf("buildmode=%s not supported", cfg.BuildBuildmode) 284 } 285 286 if cfg.BuildBuildmode != "default" && !platform.BuildModeSupported(cfg.BuildToolchainName, cfg.BuildBuildmode, cfg.Goos, cfg.Goarch) { 287 base.Fatalf("-buildmode=%s not supported on %s/%s\n", cfg.BuildBuildmode, cfg.Goos, cfg.Goarch) 288 } 289 290 if cfg.BuildLinkshared { 291 if !platform.BuildModeSupported(cfg.BuildToolchainName, "shared", cfg.Goos, cfg.Goarch) { 292 base.Fatalf("-linkshared not supported on %s/%s\n", cfg.Goos, cfg.Goarch) 293 } 294 if gccgo { 295 codegenArg = "-fPIC" 296 } else { 297 forcedAsmflags = append(forcedAsmflags, "-D=GOBUILDMODE_shared=1", 298 "-linkshared") 299 codegenArg = "-dynlink" 300 forcedGcflags = append(forcedGcflags, "-linkshared") 301 // TODO(mwhudson): remove -w when that gets fixed in linker. 302 forcedLdflags = append(forcedLdflags, "-linkshared", "-w") 303 } 304 } 305 if codegenArg != "" { 306 if gccgo { 307 forcedGccgoflags = append([]string{codegenArg}, forcedGccgoflags...) 308 } else { 309 forcedAsmflags = append([]string{codegenArg}, forcedAsmflags...) 310 forcedGcflags = append([]string{codegenArg}, forcedGcflags...) 311 } 312 // Don't alter InstallSuffix when modifying default codegen args. 313 if cfg.BuildBuildmode != "default" || cfg.BuildLinkshared { 314 if cfg.BuildContext.InstallSuffix != "" { 315 cfg.BuildContext.InstallSuffix += "_" 316 } 317 cfg.BuildContext.InstallSuffix += codegenArg[1:] 318 } 319 } 320 321 switch cfg.BuildMod { 322 case "": 323 // Behavior will be determined automatically, as if no flag were passed. 324 case "readonly", "vendor", "mod": 325 if !cfg.ModulesEnabled && !base.InGOFLAGS("-mod") { 326 base.Fatalf("build flag -mod=%s only valid when using modules", cfg.BuildMod) 327 } 328 default: 329 base.Fatalf("-mod=%s not supported (can be '', 'mod', 'readonly', or 'vendor')", cfg.BuildMod) 330 } 331 if !cfg.ModulesEnabled { 332 if cfg.ModCacheRW && !base.InGOFLAGS("-modcacherw") { 333 base.Fatalf("build flag -modcacherw only valid when using modules") 334 } 335 if cfg.ModFile != "" && !base.InGOFLAGS("-mod") { 336 base.Fatalf("build flag -modfile only valid when using modules") 337 } 338 } 339 } 340 341 type version struct { 342 name string 343 major, minor int 344 } 345 346 var compiler struct { 347 sync.Once 348 version 349 err error 350 } 351 352 // compilerVersion detects the version of $(go env CC). 353 // It returns a non-nil error if the compiler matches a known version schema but 354 // the version could not be parsed, or if $(go env CC) could not be determined. 355 func compilerVersion() (version, error) { 356 compiler.Once.Do(func() { 357 compiler.err = func() error { 358 compiler.name = "unknown" 359 cc := os.Getenv("CC") 360 out, err := exec.Command(cc, "--version").Output() 361 if err != nil { 362 // Compiler does not support "--version" flag: not Clang or GCC. 363 return err 364 } 365 366 var match [][]byte 367 if bytes.HasPrefix(out, []byte("gcc")) { 368 compiler.name = "gcc" 369 out, err := exec.Command(cc, "-v").CombinedOutput() 370 if err != nil { 371 // gcc, but does not support gcc's "-v" flag?! 372 return err 373 } 374 gccRE := regexp.MustCompile(`gcc version (\d+)\.(\d+)`) 375 match = gccRE.FindSubmatch(out) 376 } else { 377 clangRE := regexp.MustCompile(`clang version (\d+)\.(\d+)`) 378 if match = clangRE.FindSubmatch(out); len(match) > 0 { 379 compiler.name = "clang" 380 } 381 } 382 383 if len(match) < 3 { 384 return nil // "unknown" 385 } 386 if compiler.major, err = strconv.Atoi(string(match[1])); err != nil { 387 return err 388 } 389 if compiler.minor, err = strconv.Atoi(string(match[2])); err != nil { 390 return err 391 } 392 return nil 393 }() 394 }) 395 return compiler.version, compiler.err 396 } 397 398 // compilerRequiredAsanVersion is a copy of the function defined in 399 // cmd/cgo/internal/testsanitizers/cc_test.go 400 // compilerRequiredAsanVersion reports whether the compiler is the version 401 // required by Asan. 402 func compilerRequiredAsanVersion() error { 403 compiler, err := compilerVersion() 404 if err != nil { 405 return fmt.Errorf("-asan: the version of $(go env CC) could not be parsed") 406 } 407 408 switch compiler.name { 409 case "gcc": 410 if runtime.GOARCH == "ppc64le" && compiler.major < 9 { 411 return fmt.Errorf("-asan is not supported with %s compiler %d.%d\n", compiler.name, compiler.major, compiler.minor) 412 } 413 if compiler.major < 7 { 414 return fmt.Errorf("-asan is not supported with %s compiler %d.%d\n", compiler.name, compiler.major, compiler.minor) 415 } 416 case "clang": 417 if compiler.major < 9 { 418 return fmt.Errorf("-asan is not supported with %s compiler %d.%d\n", compiler.name, compiler.major, compiler.minor) 419 } 420 default: 421 return fmt.Errorf("-asan: C compiler is not gcc or clang") 422 } 423 return nil 424 }