xref: /aosp_15_r20/external/bazelbuild-rules_go/go/tools/builders/stdlib.go (revision 9bb1b549b6a84214c53be0924760be030e66b93a)
1// Copyright 2018 The Bazel Authors. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//    http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package main
16
17import (
18	"flag"
19	"fmt"
20	"go/build"
21	"os"
22	"path/filepath"
23	"regexp"
24	"strings"
25)
26
27// stdlib builds the standard library in the appropriate mode into a new goroot.
28func stdlib(args []string) error {
29	// process the args
30	flags := flag.NewFlagSet("stdlib", flag.ExitOnError)
31	goenv := envFlags(flags)
32	out := flags.String("out", "", "Path to output go root")
33	race := flags.Bool("race", false, "Build in race mode")
34	shared := flags.Bool("shared", false, "Build in shared mode")
35	dynlink := flags.Bool("dynlink", false, "Build in dynlink mode")
36	var packages multiFlag
37	flags.Var(&packages, "package", "Packages to build")
38	var gcflags quoteMultiFlag
39	flags.Var(&gcflags, "gcflags", "Go compiler flags")
40	if err := flags.Parse(args); err != nil {
41		return err
42	}
43	if err := goenv.checkFlags(); err != nil {
44		return err
45	}
46	goroot := os.Getenv("GOROOT")
47	if goroot == "" {
48		return fmt.Errorf("GOROOT not set")
49	}
50	output := abs(*out)
51
52	// Fail fast if cgo is required but a toolchain is not configured.
53	if os.Getenv("CGO_ENABLED") == "1" && filepath.Base(os.Getenv("CC")) == "vc_installation_error.bat" {
54		return fmt.Errorf(`cgo is required, but a C toolchain has not been configured.
55You may need to use the flags --cpu=x64_windows --compiler=mingw-gcc.`)
56	}
57
58	// Link in the bare minimum needed to the new GOROOT
59	if err := replicate(goroot, output, replicatePaths("src", "pkg/tool", "pkg/include")); err != nil {
60		return err
61	}
62
63	output, err := processPath(output)
64	if err != nil {
65		return err
66	}
67
68	// Now switch to the newly created GOROOT
69	os.Setenv("GOROOT", output)
70
71	// Create a temporary cache directory. "go build" requires this starting
72	// in Go 1.12.
73	cachePath := filepath.Join(output, ".gocache")
74	os.Setenv("GOCACHE", cachePath)
75	defer os.RemoveAll(cachePath)
76
77	// Disable modules for the 'go install' command. Depending on the sandboxing
78	// mode, there may be a go.mod file in a parent directory which will turn
79	// modules on in "auto" mode.
80	os.Setenv("GO111MODULE", "off")
81
82	// Make sure we have an absolute path to the C compiler.
83	// TODO(#1357): also take absolute paths of includes and other paths in flags.
84	os.Setenv("CC", quotePathIfNeeded(abs(os.Getenv("CC"))))
85
86	// Ensure paths are absolute.
87	absPaths := []string{}
88	for _, path := range filepath.SplitList(os.Getenv("PATH")) {
89		absPaths = append(absPaths, abs(path))
90	}
91	os.Setenv("PATH", strings.Join(absPaths, string(os.PathListSeparator)))
92
93	sandboxPath := abs(".")
94
95	// Strip path prefix from source files in debug information.
96	os.Setenv("CGO_CFLAGS", os.Getenv("CGO_CFLAGS")+" "+strings.Join(defaultCFlags(output), " "))
97	os.Setenv("CGO_LDFLAGS", os.Getenv("CGO_LDFLAGS")+" "+strings.Join(defaultLdFlags(), " "))
98
99	// Allow flags in CGO_LDFLAGS that wouldn't pass the security check.
100	// Workaround for golang.org/issue/42565.
101	var b strings.Builder
102	sep := ""
103	cgoLdflags, _ := splitQuoted(os.Getenv("CGO_LDFLAGS"))
104	for _, f := range cgoLdflags {
105		b.WriteString(sep)
106		sep = "|"
107		b.WriteString(regexp.QuoteMeta(f))
108		// If the flag if -framework, the flag value needs to be in the same
109		// condition.
110		if f == "-framework" {
111			sep = " "
112		}
113	}
114	os.Setenv("CGO_LDFLAGS_ALLOW", b.String())
115	os.Setenv("GODEBUG", "installgoroot=all")
116
117	// Build the commands needed to build the std library in the right mode
118	// NOTE: the go command stamps compiled .a files with build ids, which are
119	// cryptographic sums derived from the inputs. This prevents us from
120	// creating reproducible builds because the build ids are hashed from
121	// CGO_CFLAGS, which frequently contains absolute paths. As a workaround,
122	// we strip the build ids, since they won't be used after this.
123	installArgs := goenv.goCmd("install", "-toolexec", abs(os.Args[0])+" filterbuildid")
124	if len(build.Default.BuildTags) > 0 {
125		installArgs = append(installArgs, "-tags", strings.Join(build.Default.BuildTags, ","))
126	}
127
128	ldflags := []string{"-trimpath", sandboxPath}
129	asmflags := []string{"-trimpath", output}
130	if *race {
131		installArgs = append(installArgs, "-race")
132	}
133	if *shared {
134		gcflags = append(gcflags, "-shared")
135		ldflags = append(ldflags, "-shared")
136		asmflags = append(asmflags, "-shared")
137	}
138	if *dynlink {
139		gcflags = append(gcflags, "-dynlink")
140		ldflags = append(ldflags, "-dynlink")
141		asmflags = append(asmflags, "-dynlink")
142	}
143
144	// Since Go 1.10, an all= prefix indicates the flags should apply to the package
145	// and its dependencies, rather than just the package itself. This was the
146	// default behavior before Go 1.10.
147	allSlug := ""
148	for _, t := range build.Default.ReleaseTags {
149		if t == "go1.10" {
150			allSlug = "all="
151			break
152		}
153	}
154	installArgs = append(installArgs, "-gcflags="+allSlug+strings.Join(gcflags, " "))
155	installArgs = append(installArgs, "-ldflags="+allSlug+strings.Join(ldflags, " "))
156	installArgs = append(installArgs, "-asmflags="+allSlug+strings.Join(asmflags, " "))
157
158	// Modifying CGO flags to use only absolute path
159	// because go is having its own sandbox, all CGO flags must use absolute path
160	if err := absEnv(cgoEnvVars, cgoAbsEnvFlags); err != nil {
161		return fmt.Errorf("error modifying cgo environment to absolute path: %v", err)
162	}
163
164	installArgs = append(installArgs, packages...)
165	if err := goenv.runCommand(installArgs); err != nil {
166		return err
167	}
168	return nil
169}
170