]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/go/internal/work/init.go
cd99a33a21216eb3242a7338a297f443723ade0d
[gostls13.git] / src / cmd / go / internal / work / init.go
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         "cmd/go/internal/base"
12         "cmd/go/internal/cfg"
13         "cmd/go/internal/fsys"
14         "cmd/go/internal/modload"
15         "cmd/internal/quoted"
16         "fmt"
17         "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 }