xref: /aosp_15_r20/external/bazelbuild-rules_go/go/tools/builders/compilepkg.go (revision 9bb1b549b6a84214c53be0924760be030e66b93a)
1*9bb1b549SSpandan Das// Copyright 2019 The Bazel Authors. All rights reserved.
2*9bb1b549SSpandan Das//
3*9bb1b549SSpandan Das// Licensed under the Apache License, Version 2.0 (the "License");
4*9bb1b549SSpandan Das// you may not use this file except in compliance with the License.
5*9bb1b549SSpandan Das// You may obtain a copy of the License at
6*9bb1b549SSpandan Das//
7*9bb1b549SSpandan Das//    http://www.apache.org/licenses/LICENSE-2.0
8*9bb1b549SSpandan Das//
9*9bb1b549SSpandan Das// Unless required by applicable law or agreed to in writing, software
10*9bb1b549SSpandan Das// distributed under the License is distributed on an "AS IS" BASIS,
11*9bb1b549SSpandan Das// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*9bb1b549SSpandan Das// See the License for the specific language governing permissions and
13*9bb1b549SSpandan Das// limitations under the License.
14*9bb1b549SSpandan Das
15*9bb1b549SSpandan Das// compilepkg compiles a complete Go package from Go, C, and assembly files.  It
16*9bb1b549SSpandan Das// supports cgo, coverage, and nogo. It is invoked by the Go rules as an action.
17*9bb1b549SSpandan Daspackage main
18*9bb1b549SSpandan Das
19*9bb1b549SSpandan Dasimport (
20*9bb1b549SSpandan Das	"bytes"
21*9bb1b549SSpandan Das	"context"
22*9bb1b549SSpandan Das	"errors"
23*9bb1b549SSpandan Das	"flag"
24*9bb1b549SSpandan Das	"fmt"
25*9bb1b549SSpandan Das	"io/ioutil"
26*9bb1b549SSpandan Das	"os"
27*9bb1b549SSpandan Das	"os/exec"
28*9bb1b549SSpandan Das	"path"
29*9bb1b549SSpandan Das	"path/filepath"
30*9bb1b549SSpandan Das	"sort"
31*9bb1b549SSpandan Das	"strings"
32*9bb1b549SSpandan Das)
33*9bb1b549SSpandan Das
34*9bb1b549SSpandan Dastype nogoResult int
35*9bb1b549SSpandan Das
36*9bb1b549SSpandan Dasconst (
37*9bb1b549SSpandan Das	nogoNotRun nogoResult = iota
38*9bb1b549SSpandan Das	nogoError
39*9bb1b549SSpandan Das	nogoFailed
40*9bb1b549SSpandan Das	nogoSucceeded
41*9bb1b549SSpandan Das)
42*9bb1b549SSpandan Das
43*9bb1b549SSpandan Dasfunc compilePkg(args []string) error {
44*9bb1b549SSpandan Das	// Parse arguments.
45*9bb1b549SSpandan Das	args, _, err := expandParamsFiles(args)
46*9bb1b549SSpandan Das	if err != nil {
47*9bb1b549SSpandan Das		return err
48*9bb1b549SSpandan Das	}
49*9bb1b549SSpandan Das
50*9bb1b549SSpandan Das	fs := flag.NewFlagSet("GoCompilePkg", flag.ExitOnError)
51*9bb1b549SSpandan Das	goenv := envFlags(fs)
52*9bb1b549SSpandan Das	var unfilteredSrcs, coverSrcs, embedSrcs, embedLookupDirs, embedRoots, recompileInternalDeps multiFlag
53*9bb1b549SSpandan Das	var deps archiveMultiFlag
54*9bb1b549SSpandan Das	var importPath, packagePath, nogoPath, packageListPath, coverMode string
55*9bb1b549SSpandan Das	var outPath, outFactsPath, cgoExportHPath string
56*9bb1b549SSpandan Das	var testFilter string
57*9bb1b549SSpandan Das	var gcFlags, asmFlags, cppFlags, cFlags, cxxFlags, objcFlags, objcxxFlags, ldFlags quoteMultiFlag
58*9bb1b549SSpandan Das	var coverFormat string
59*9bb1b549SSpandan Das	fs.Var(&unfilteredSrcs, "src", ".go, .c, .cc, .m, .mm, .s, or .S file to be filtered and compiled")
60*9bb1b549SSpandan Das	fs.Var(&coverSrcs, "cover", ".go file that should be instrumented for coverage (must also be a -src)")
61*9bb1b549SSpandan Das	fs.Var(&embedSrcs, "embedsrc", "file that may be compiled into the package with a //go:embed directive")
62*9bb1b549SSpandan Das	fs.Var(&embedLookupDirs, "embedlookupdir", "Root-relative paths to directories relative to which //go:embed directives are resolved")
63*9bb1b549SSpandan Das	fs.Var(&embedRoots, "embedroot", "Bazel output root under which a file passed via -embedsrc resides")
64*9bb1b549SSpandan Das	fs.Var(&deps, "arc", "Import path, package path, and file name of a direct dependency, separated by '='")
65*9bb1b549SSpandan Das	fs.StringVar(&importPath, "importpath", "", "The import path of the package being compiled. Not passed to the compiler, but may be displayed in debug data.")
66*9bb1b549SSpandan Das	fs.StringVar(&packagePath, "p", "", "The package path (importmap) of the package being compiled")
67*9bb1b549SSpandan Das	fs.Var(&gcFlags, "gcflags", "Go compiler flags")
68*9bb1b549SSpandan Das	fs.Var(&asmFlags, "asmflags", "Go assembler flags")
69*9bb1b549SSpandan Das	fs.Var(&cppFlags, "cppflags", "C preprocessor flags")
70*9bb1b549SSpandan Das	fs.Var(&cFlags, "cflags", "C compiler flags")
71*9bb1b549SSpandan Das	fs.Var(&cxxFlags, "cxxflags", "C++ compiler flags")
72*9bb1b549SSpandan Das	fs.Var(&objcFlags, "objcflags", "Objective-C compiler flags")
73*9bb1b549SSpandan Das	fs.Var(&objcxxFlags, "objcxxflags", "Objective-C++ compiler flags")
74*9bb1b549SSpandan Das	fs.Var(&ldFlags, "ldflags", "C linker flags")
75*9bb1b549SSpandan Das	fs.StringVar(&nogoPath, "nogo", "", "The nogo binary. If unset, nogo will not be run.")
76*9bb1b549SSpandan Das	fs.StringVar(&packageListPath, "package_list", "", "The file containing the list of standard library packages")
77*9bb1b549SSpandan Das	fs.StringVar(&coverMode, "cover_mode", "", "The coverage mode to use. Empty if coverage instrumentation should not be added.")
78*9bb1b549SSpandan Das	fs.StringVar(&outPath, "o", "", "The output archive file to write compiled code")
79*9bb1b549SSpandan Das	fs.StringVar(&outFactsPath, "x", "", "The output archive file to write export data and nogo facts")
80*9bb1b549SSpandan Das	fs.StringVar(&cgoExportHPath, "cgoexport", "", "The _cgo_exports.h file to write")
81*9bb1b549SSpandan Das	fs.StringVar(&testFilter, "testfilter", "off", "Controls test package filtering")
82*9bb1b549SSpandan Das	fs.StringVar(&coverFormat, "cover_format", "", "Emit source file paths in coverage instrumentation suitable for the specified coverage format")
83*9bb1b549SSpandan Das	fs.Var(&recompileInternalDeps, "recompile_internal_deps", "The import path of the direct dependencies that needs to be recompiled.")
84*9bb1b549SSpandan Das	if err := fs.Parse(args); err != nil {
85*9bb1b549SSpandan Das		return err
86*9bb1b549SSpandan Das	}
87*9bb1b549SSpandan Das	if err := goenv.checkFlags(); err != nil {
88*9bb1b549SSpandan Das		return err
89*9bb1b549SSpandan Das	}
90*9bb1b549SSpandan Das	if importPath == "" {
91*9bb1b549SSpandan Das		importPath = packagePath
92*9bb1b549SSpandan Das	}
93*9bb1b549SSpandan Das	cgoEnabled := os.Getenv("CGO_ENABLED") == "1"
94*9bb1b549SSpandan Das	cc := os.Getenv("CC")
95*9bb1b549SSpandan Das	outPath = abs(outPath)
96*9bb1b549SSpandan Das	for i := range unfilteredSrcs {
97*9bb1b549SSpandan Das		unfilteredSrcs[i] = abs(unfilteredSrcs[i])
98*9bb1b549SSpandan Das	}
99*9bb1b549SSpandan Das	for i := range embedSrcs {
100*9bb1b549SSpandan Das		embedSrcs[i] = abs(embedSrcs[i])
101*9bb1b549SSpandan Das	}
102*9bb1b549SSpandan Das
103*9bb1b549SSpandan Das	// Filter sources.
104*9bb1b549SSpandan Das	srcs, err := filterAndSplitFiles(unfilteredSrcs)
105*9bb1b549SSpandan Das	if err != nil {
106*9bb1b549SSpandan Das		return err
107*9bb1b549SSpandan Das	}
108*9bb1b549SSpandan Das
109*9bb1b549SSpandan Das	// TODO(jayconrod): remove -testfilter flag. The test action should compile
110*9bb1b549SSpandan Das	// the main, internal, and external packages by calling compileArchive
111*9bb1b549SSpandan Das	// with the correct sources for each.
112*9bb1b549SSpandan Das	switch testFilter {
113*9bb1b549SSpandan Das	case "off":
114*9bb1b549SSpandan Das	case "only":
115*9bb1b549SSpandan Das		testSrcs := make([]fileInfo, 0, len(srcs.goSrcs))
116*9bb1b549SSpandan Das		for _, f := range srcs.goSrcs {
117*9bb1b549SSpandan Das			if strings.HasSuffix(f.pkg, "_test") {
118*9bb1b549SSpandan Das				testSrcs = append(testSrcs, f)
119*9bb1b549SSpandan Das			}
120*9bb1b549SSpandan Das		}
121*9bb1b549SSpandan Das		srcs.goSrcs = testSrcs
122*9bb1b549SSpandan Das	case "exclude":
123*9bb1b549SSpandan Das		libSrcs := make([]fileInfo, 0, len(srcs.goSrcs))
124*9bb1b549SSpandan Das		for _, f := range srcs.goSrcs {
125*9bb1b549SSpandan Das			if !strings.HasSuffix(f.pkg, "_test") {
126*9bb1b549SSpandan Das				libSrcs = append(libSrcs, f)
127*9bb1b549SSpandan Das			}
128*9bb1b549SSpandan Das		}
129*9bb1b549SSpandan Das		srcs.goSrcs = libSrcs
130*9bb1b549SSpandan Das	default:
131*9bb1b549SSpandan Das		return fmt.Errorf("invalid test filter %q", testFilter)
132*9bb1b549SSpandan Das	}
133*9bb1b549SSpandan Das
134*9bb1b549SSpandan Das	return compileArchive(
135*9bb1b549SSpandan Das		goenv,
136*9bb1b549SSpandan Das		importPath,
137*9bb1b549SSpandan Das		packagePath,
138*9bb1b549SSpandan Das		srcs,
139*9bb1b549SSpandan Das		deps,
140*9bb1b549SSpandan Das		coverMode,
141*9bb1b549SSpandan Das		coverSrcs,
142*9bb1b549SSpandan Das		embedSrcs,
143*9bb1b549SSpandan Das		embedLookupDirs,
144*9bb1b549SSpandan Das		embedRoots,
145*9bb1b549SSpandan Das		cgoEnabled,
146*9bb1b549SSpandan Das		cc,
147*9bb1b549SSpandan Das		gcFlags,
148*9bb1b549SSpandan Das		asmFlags,
149*9bb1b549SSpandan Das		cppFlags,
150*9bb1b549SSpandan Das		cFlags,
151*9bb1b549SSpandan Das		cxxFlags,
152*9bb1b549SSpandan Das		objcFlags,
153*9bb1b549SSpandan Das		objcxxFlags,
154*9bb1b549SSpandan Das		ldFlags,
155*9bb1b549SSpandan Das		nogoPath,
156*9bb1b549SSpandan Das		packageListPath,
157*9bb1b549SSpandan Das		outPath,
158*9bb1b549SSpandan Das		outFactsPath,
159*9bb1b549SSpandan Das		cgoExportHPath,
160*9bb1b549SSpandan Das		coverFormat,
161*9bb1b549SSpandan Das		recompileInternalDeps)
162*9bb1b549SSpandan Das}
163*9bb1b549SSpandan Das
164*9bb1b549SSpandan Dasfunc compileArchive(
165*9bb1b549SSpandan Das	goenv *env,
166*9bb1b549SSpandan Das	importPath string,
167*9bb1b549SSpandan Das	packagePath string,
168*9bb1b549SSpandan Das	srcs archiveSrcs,
169*9bb1b549SSpandan Das	deps []archive,
170*9bb1b549SSpandan Das	coverMode string,
171*9bb1b549SSpandan Das	coverSrcs []string,
172*9bb1b549SSpandan Das	embedSrcs []string,
173*9bb1b549SSpandan Das	embedLookupDirs []string,
174*9bb1b549SSpandan Das	embedRoots []string,
175*9bb1b549SSpandan Das	cgoEnabled bool,
176*9bb1b549SSpandan Das	cc string,
177*9bb1b549SSpandan Das	gcFlags []string,
178*9bb1b549SSpandan Das	asmFlags []string,
179*9bb1b549SSpandan Das	cppFlags []string,
180*9bb1b549SSpandan Das	cFlags []string,
181*9bb1b549SSpandan Das	cxxFlags []string,
182*9bb1b549SSpandan Das	objcFlags []string,
183*9bb1b549SSpandan Das	objcxxFlags []string,
184*9bb1b549SSpandan Das	ldFlags []string,
185*9bb1b549SSpandan Das	nogoPath string,
186*9bb1b549SSpandan Das	packageListPath string,
187*9bb1b549SSpandan Das	outPath string,
188*9bb1b549SSpandan Das	outXPath string,
189*9bb1b549SSpandan Das	cgoExportHPath string,
190*9bb1b549SSpandan Das	coverFormat string,
191*9bb1b549SSpandan Das	recompileInternalDeps []string,
192*9bb1b549SSpandan Das) error {
193*9bb1b549SSpandan Das	workDir, cleanup, err := goenv.workDir()
194*9bb1b549SSpandan Das	if err != nil {
195*9bb1b549SSpandan Das		return err
196*9bb1b549SSpandan Das	}
197*9bb1b549SSpandan Das	defer cleanup()
198*9bb1b549SSpandan Das
199*9bb1b549SSpandan Das	// As part of compilation process, rules_go does generate and/or rewrite code
200*9bb1b549SSpandan Das	// based on the original source files.  We should only run static analysis
201*9bb1b549SSpandan Das	// over original source files and not the generated source as end users have
202*9bb1b549SSpandan Das	// little control over the generated source.
203*9bb1b549SSpandan Das	//
204*9bb1b549SSpandan Das	// nogoSrcsOrigin maps generated/rewritten source files back to original source.
205*9bb1b549SSpandan Das	// If the original source path is an empty string, exclude generated source from nogo run.
206*9bb1b549SSpandan Das	nogoSrcsOrigin := make(map[string]string)
207*9bb1b549SSpandan Das
208*9bb1b549SSpandan Das	if len(srcs.goSrcs) == 0 {
209*9bb1b549SSpandan Das		// We need to run the compiler to create a valid archive, even if there's nothing in it.
210*9bb1b549SSpandan Das		// Otherwise, GoPack will complain if we try to add assembly or cgo objects.
211*9bb1b549SSpandan Das		// A truly empty archive does not include any references to source file paths, which
212*9bb1b549SSpandan Das		// ensures hermeticity even though the temp file path is random.
213*9bb1b549SSpandan Das		emptyGoFile, err := os.CreateTemp(filepath.Dir(outPath), "*.go")
214*9bb1b549SSpandan Das		if err != nil {
215*9bb1b549SSpandan Das			return err
216*9bb1b549SSpandan Das		}
217*9bb1b549SSpandan Das		defer os.Remove(emptyGoFile.Name())
218*9bb1b549SSpandan Das		defer emptyGoFile.Close()
219*9bb1b549SSpandan Das		if _, err := emptyGoFile.WriteString("package empty\n"); err != nil {
220*9bb1b549SSpandan Das			return err
221*9bb1b549SSpandan Das		}
222*9bb1b549SSpandan Das		if err := emptyGoFile.Close(); err != nil {
223*9bb1b549SSpandan Das			return err
224*9bb1b549SSpandan Das		}
225*9bb1b549SSpandan Das
226*9bb1b549SSpandan Das		srcs.goSrcs = append(srcs.goSrcs, fileInfo{
227*9bb1b549SSpandan Das			filename: emptyGoFile.Name(),
228*9bb1b549SSpandan Das			ext:      goExt,
229*9bb1b549SSpandan Das			matched:  true,
230*9bb1b549SSpandan Das			pkg:      "empty",
231*9bb1b549SSpandan Das		})
232*9bb1b549SSpandan Das
233*9bb1b549SSpandan Das		nogoSrcsOrigin[emptyGoFile.Name()] = ""
234*9bb1b549SSpandan Das	}
235*9bb1b549SSpandan Das	packageName := srcs.goSrcs[0].pkg
236*9bb1b549SSpandan Das	var goSrcs, cgoSrcs []string
237*9bb1b549SSpandan Das	for _, src := range srcs.goSrcs {
238*9bb1b549SSpandan Das		if src.isCgo {
239*9bb1b549SSpandan Das			cgoSrcs = append(cgoSrcs, src.filename)
240*9bb1b549SSpandan Das		} else {
241*9bb1b549SSpandan Das			goSrcs = append(goSrcs, src.filename)
242*9bb1b549SSpandan Das		}
243*9bb1b549SSpandan Das	}
244*9bb1b549SSpandan Das	cSrcs := make([]string, len(srcs.cSrcs))
245*9bb1b549SSpandan Das	for i, src := range srcs.cSrcs {
246*9bb1b549SSpandan Das		cSrcs[i] = src.filename
247*9bb1b549SSpandan Das	}
248*9bb1b549SSpandan Das	cxxSrcs := make([]string, len(srcs.cxxSrcs))
249*9bb1b549SSpandan Das	for i, src := range srcs.cxxSrcs {
250*9bb1b549SSpandan Das		cxxSrcs[i] = src.filename
251*9bb1b549SSpandan Das	}
252*9bb1b549SSpandan Das	objcSrcs := make([]string, len(srcs.objcSrcs))
253*9bb1b549SSpandan Das	for i, src := range srcs.objcSrcs {
254*9bb1b549SSpandan Das		objcSrcs[i] = src.filename
255*9bb1b549SSpandan Das	}
256*9bb1b549SSpandan Das	objcxxSrcs := make([]string, len(srcs.objcxxSrcs))
257*9bb1b549SSpandan Das	for i, src := range srcs.objcxxSrcs {
258*9bb1b549SSpandan Das		objcxxSrcs[i] = src.filename
259*9bb1b549SSpandan Das	}
260*9bb1b549SSpandan Das	sSrcs := make([]string, len(srcs.sSrcs))
261*9bb1b549SSpandan Das	for i, src := range srcs.sSrcs {
262*9bb1b549SSpandan Das		sSrcs[i] = src.filename
263*9bb1b549SSpandan Das	}
264*9bb1b549SSpandan Das	hSrcs := make([]string, len(srcs.hSrcs))
265*9bb1b549SSpandan Das	for i, src := range srcs.hSrcs {
266*9bb1b549SSpandan Das		hSrcs[i] = src.filename
267*9bb1b549SSpandan Das	}
268*9bb1b549SSpandan Das	haveCgo := len(cgoSrcs)+len(cSrcs)+len(cxxSrcs)+len(objcSrcs)+len(objcxxSrcs) > 0
269*9bb1b549SSpandan Das
270*9bb1b549SSpandan Das	// Instrument source files for coverage.
271*9bb1b549SSpandan Das	if coverMode != "" {
272*9bb1b549SSpandan Das		relCoverPath := make(map[string]string)
273*9bb1b549SSpandan Das		for _, s := range coverSrcs {
274*9bb1b549SSpandan Das			relCoverPath[abs(s)] = s
275*9bb1b549SSpandan Das		}
276*9bb1b549SSpandan Das
277*9bb1b549SSpandan Das		combined := append([]string{}, goSrcs...)
278*9bb1b549SSpandan Das		if cgoEnabled {
279*9bb1b549SSpandan Das			combined = append(combined, cgoSrcs...)
280*9bb1b549SSpandan Das		}
281*9bb1b549SSpandan Das		for i, origSrc := range combined {
282*9bb1b549SSpandan Das			if _, ok := relCoverPath[origSrc]; !ok {
283*9bb1b549SSpandan Das				continue
284*9bb1b549SSpandan Das			}
285*9bb1b549SSpandan Das
286*9bb1b549SSpandan Das			var srcName string
287*9bb1b549SSpandan Das			switch coverFormat {
288*9bb1b549SSpandan Das			case "go_cover":
289*9bb1b549SSpandan Das				srcName = origSrc
290*9bb1b549SSpandan Das				if importPath != "" {
291*9bb1b549SSpandan Das					srcName = path.Join(importPath, filepath.Base(origSrc))
292*9bb1b549SSpandan Das				}
293*9bb1b549SSpandan Das			case "lcov":
294*9bb1b549SSpandan Das				// Bazel merges lcov reports across languages and thus assumes
295*9bb1b549SSpandan Das				// that the source file paths are relative to the exec root.
296*9bb1b549SSpandan Das				srcName = relCoverPath[origSrc]
297*9bb1b549SSpandan Das			default:
298*9bb1b549SSpandan Das				return fmt.Errorf("invalid value for -cover_format: %q", coverFormat)
299*9bb1b549SSpandan Das			}
300*9bb1b549SSpandan Das
301*9bb1b549SSpandan Das			stem := filepath.Base(origSrc)
302*9bb1b549SSpandan Das			if ext := filepath.Ext(stem); ext != "" {
303*9bb1b549SSpandan Das				stem = stem[:len(stem)-len(ext)]
304*9bb1b549SSpandan Das			}
305*9bb1b549SSpandan Das			coverVar := fmt.Sprintf("Cover_%s_%d_%s", sanitizePathForIdentifier(importPath), i, sanitizePathForIdentifier(stem))
306*9bb1b549SSpandan Das			coverVar = strings.ReplaceAll(coverVar, "_", "Z")
307*9bb1b549SSpandan Das			coverSrc := filepath.Join(workDir, fmt.Sprintf("cover_%d.go", i))
308*9bb1b549SSpandan Das			if err := instrumentForCoverage(goenv, origSrc, srcName, coverVar, coverMode, coverSrc); err != nil {
309*9bb1b549SSpandan Das				return err
310*9bb1b549SSpandan Das			}
311*9bb1b549SSpandan Das
312*9bb1b549SSpandan Das			if i < len(goSrcs) {
313*9bb1b549SSpandan Das				goSrcs[i] = coverSrc
314*9bb1b549SSpandan Das				nogoSrcsOrigin[coverSrc] = origSrc
315*9bb1b549SSpandan Das				continue
316*9bb1b549SSpandan Das			}
317*9bb1b549SSpandan Das
318*9bb1b549SSpandan Das			cgoSrcs[i-len(goSrcs)] = coverSrc
319*9bb1b549SSpandan Das		}
320*9bb1b549SSpandan Das	}
321*9bb1b549SSpandan Das
322*9bb1b549SSpandan Das	// If we have cgo, generate separate C and go files, and compile the
323*9bb1b549SSpandan Das	// C files.
324*9bb1b549SSpandan Das	var objFiles []string
325*9bb1b549SSpandan Das	if cgoEnabled && haveCgo {
326*9bb1b549SSpandan Das		// TODO(#2006): Compile .s and .S files with cgo2, not the Go assembler.
327*9bb1b549SSpandan Das		// If cgo is not enabled or we don't have other cgo sources, don't
328*9bb1b549SSpandan Das		// compile .S files.
329*9bb1b549SSpandan Das		var srcDir string
330*9bb1b549SSpandan Das		srcDir, goSrcs, objFiles, err = cgo2(goenv, goSrcs, cgoSrcs, cSrcs, cxxSrcs, objcSrcs, objcxxSrcs, nil, hSrcs, packagePath, packageName, cc, cppFlags, cFlags, cxxFlags, objcFlags, objcxxFlags, ldFlags, cgoExportHPath)
331*9bb1b549SSpandan Das		if err != nil {
332*9bb1b549SSpandan Das			return err
333*9bb1b549SSpandan Das		}
334*9bb1b549SSpandan Das
335*9bb1b549SSpandan Das		gcFlags = append(gcFlags, createTrimPath(gcFlags, srcDir))
336*9bb1b549SSpandan Das	} else {
337*9bb1b549SSpandan Das		if cgoExportHPath != "" {
338*9bb1b549SSpandan Das			if err := ioutil.WriteFile(cgoExportHPath, nil, 0o666); err != nil {
339*9bb1b549SSpandan Das				return err
340*9bb1b549SSpandan Das			}
341*9bb1b549SSpandan Das		}
342*9bb1b549SSpandan Das		gcFlags = append(gcFlags, createTrimPath(gcFlags, "."))
343*9bb1b549SSpandan Das	}
344*9bb1b549SSpandan Das
345*9bb1b549SSpandan Das	// Check that the filtered sources don't import anything outside of
346*9bb1b549SSpandan Das	// the standard library and the direct dependencies.
347*9bb1b549SSpandan Das	imports, err := checkImports(srcs.goSrcs, deps, packageListPath, importPath, recompileInternalDeps)
348*9bb1b549SSpandan Das	if err != nil {
349*9bb1b549SSpandan Das		return err
350*9bb1b549SSpandan Das	}
351*9bb1b549SSpandan Das	if cgoEnabled && len(cgoSrcs) != 0 {
352*9bb1b549SSpandan Das		// cgo generated code imports some extra packages.
353*9bb1b549SSpandan Das		imports["runtime/cgo"] = nil
354*9bb1b549SSpandan Das		imports["syscall"] = nil
355*9bb1b549SSpandan Das		imports["unsafe"] = nil
356*9bb1b549SSpandan Das	}
357*9bb1b549SSpandan Das	if coverMode != "" {
358*9bb1b549SSpandan Das		if coverMode == "atomic" {
359*9bb1b549SSpandan Das			imports["sync/atomic"] = nil
360*9bb1b549SSpandan Das		}
361*9bb1b549SSpandan Das		const coverdataPath = "github.com/bazelbuild/rules_go/go/tools/coverdata"
362*9bb1b549SSpandan Das		var coverdata *archive
363*9bb1b549SSpandan Das		for i := range deps {
364*9bb1b549SSpandan Das			if deps[i].importPath == coverdataPath {
365*9bb1b549SSpandan Das				coverdata = &deps[i]
366*9bb1b549SSpandan Das				break
367*9bb1b549SSpandan Das			}
368*9bb1b549SSpandan Das		}
369*9bb1b549SSpandan Das		if coverdata == nil {
370*9bb1b549SSpandan Das			return errors.New("coverage requested but coverdata dependency not provided")
371*9bb1b549SSpandan Das		}
372*9bb1b549SSpandan Das		imports[coverdataPath] = coverdata
373*9bb1b549SSpandan Das	}
374*9bb1b549SSpandan Das
375*9bb1b549SSpandan Das	// Build an importcfg file for the compiler.
376*9bb1b549SSpandan Das	importcfgPath, err := buildImportcfgFileForCompile(imports, goenv.installSuffix, filepath.Dir(outPath))
377*9bb1b549SSpandan Das	if err != nil {
378*9bb1b549SSpandan Das		return err
379*9bb1b549SSpandan Das	}
380*9bb1b549SSpandan Das	if !goenv.shouldPreserveWorkDir {
381*9bb1b549SSpandan Das		defer os.Remove(importcfgPath)
382*9bb1b549SSpandan Das	}
383*9bb1b549SSpandan Das
384*9bb1b549SSpandan Das	// Build an embedcfg file mapping embed patterns to filenames.
385*9bb1b549SSpandan Das	// Embed patterns are relative to any one of a list of root directories
386*9bb1b549SSpandan Das	// that may contain embeddable files. Source files containing embed patterns
387*9bb1b549SSpandan Das	// must be in one of these root directories so the pattern appears to be
388*9bb1b549SSpandan Das	// relative to the source file. Due to transitions, source files can reside
389*9bb1b549SSpandan Das	// under Bazel roots different from both those of the go srcs and those of
390*9bb1b549SSpandan Das	// the compilation output. Thus, we have to consider all combinations of
391*9bb1b549SSpandan Das	// Bazel roots embedsrcs and root-relative paths of source files and the
392*9bb1b549SSpandan Das	// output binary.
393*9bb1b549SSpandan Das	var embedRootDirs []string
394*9bb1b549SSpandan Das	for _, root := range embedRoots {
395*9bb1b549SSpandan Das		for _, lookupDir := range embedLookupDirs {
396*9bb1b549SSpandan Das			embedRootDir := abs(filepath.Join(root, lookupDir))
397*9bb1b549SSpandan Das			// Since we are iterating over all combinations of roots and
398*9bb1b549SSpandan Das			// root-relative paths, some resulting paths may not exist and
399*9bb1b549SSpandan Das			// should be filtered out before being passed to buildEmbedcfgFile.
400*9bb1b549SSpandan Das			// Since Bazel uniquified both the roots and the root-relative
401*9bb1b549SSpandan Das			// paths, the combinations are automatically unique.
402*9bb1b549SSpandan Das			if _, err := os.Stat(embedRootDir); err == nil {
403*9bb1b549SSpandan Das				embedRootDirs = append(embedRootDirs, embedRootDir)
404*9bb1b549SSpandan Das			}
405*9bb1b549SSpandan Das		}
406*9bb1b549SSpandan Das	}
407*9bb1b549SSpandan Das	embedcfgPath, err := buildEmbedcfgFile(srcs.goSrcs, embedSrcs, embedRootDirs, workDir)
408*9bb1b549SSpandan Das	if err != nil {
409*9bb1b549SSpandan Das		return err
410*9bb1b549SSpandan Das	}
411*9bb1b549SSpandan Das	if embedcfgPath != "" {
412*9bb1b549SSpandan Das		if !goenv.shouldPreserveWorkDir {
413*9bb1b549SSpandan Das			defer os.Remove(embedcfgPath)
414*9bb1b549SSpandan Das		}
415*9bb1b549SSpandan Das	}
416*9bb1b549SSpandan Das
417*9bb1b549SSpandan Das	// Run nogo concurrently.
418*9bb1b549SSpandan Das	var nogoChan chan error
419*9bb1b549SSpandan Das	outFactsPath := filepath.Join(workDir, nogoFact)
420*9bb1b549SSpandan Das	nogoSrcs := make([]string, 0, len(goSrcs))
421*9bb1b549SSpandan Das	for _, goSrc := range goSrcs {
422*9bb1b549SSpandan Das		// If source is found in the origin map, that means it's likely to be a generated source file
423*9bb1b549SSpandan Das		// so feed the original source file to static analyzers instead of the generated one.
424*9bb1b549SSpandan Das		//
425*9bb1b549SSpandan Das		// If origin is empty, that means the generated source file is not based on a user-provided source file
426*9bb1b549SSpandan Das		// thus ignore that entry entirely.
427*9bb1b549SSpandan Das		if originSrc, ok := nogoSrcsOrigin[goSrc]; ok {
428*9bb1b549SSpandan Das			if originSrc != "" {
429*9bb1b549SSpandan Das				nogoSrcs = append(nogoSrcs, originSrc)
430*9bb1b549SSpandan Das			}
431*9bb1b549SSpandan Das			continue
432*9bb1b549SSpandan Das		}
433*9bb1b549SSpandan Das
434*9bb1b549SSpandan Das		// TODO(sluongng): most likely what remains here are CGO-generated source files as the result of calling cgo2()
435*9bb1b549SSpandan Das		// Need to determine whether we want to feed these CGO-generated files into static analyzers.
436*9bb1b549SSpandan Das		//
437*9bb1b549SSpandan Das		// Add unknown origin source files into the mix.
438*9bb1b549SSpandan Das		nogoSrcs = append(nogoSrcs, goSrc)
439*9bb1b549SSpandan Das	}
440*9bb1b549SSpandan Das	if nogoPath != "" && len(nogoSrcs) > 0 {
441*9bb1b549SSpandan Das		ctx, cancel := context.WithCancel(context.Background())
442*9bb1b549SSpandan Das		nogoChan = make(chan error)
443*9bb1b549SSpandan Das		go func() {
444*9bb1b549SSpandan Das			nogoChan <- runNogo(ctx, workDir, nogoPath, nogoSrcs, deps, packagePath, importcfgPath, outFactsPath)
445*9bb1b549SSpandan Das		}()
446*9bb1b549SSpandan Das		defer func() {
447*9bb1b549SSpandan Das			if nogoChan != nil {
448*9bb1b549SSpandan Das				cancel()
449*9bb1b549SSpandan Das				<-nogoChan
450*9bb1b549SSpandan Das			}
451*9bb1b549SSpandan Das		}()
452*9bb1b549SSpandan Das	}
453*9bb1b549SSpandan Das
454*9bb1b549SSpandan Das	// If there are assembly files, and this is go1.12+, generate symbol ABIs.
455*9bb1b549SSpandan Das	asmHdrPath := ""
456*9bb1b549SSpandan Das	if len(srcs.sSrcs) > 0 {
457*9bb1b549SSpandan Das		asmHdrPath = filepath.Join(workDir, "go_asm.h")
458*9bb1b549SSpandan Das	}
459*9bb1b549SSpandan Das	symabisPath, err := buildSymabisFile(goenv, srcs.sSrcs, srcs.hSrcs, asmHdrPath)
460*9bb1b549SSpandan Das	if symabisPath != "" {
461*9bb1b549SSpandan Das		if !goenv.shouldPreserveWorkDir {
462*9bb1b549SSpandan Das			defer os.Remove(symabisPath)
463*9bb1b549SSpandan Das		}
464*9bb1b549SSpandan Das	}
465*9bb1b549SSpandan Das	if err != nil {
466*9bb1b549SSpandan Das		return err
467*9bb1b549SSpandan Das	}
468*9bb1b549SSpandan Das
469*9bb1b549SSpandan Das	// Compile the filtered .go files.
470*9bb1b549SSpandan Das	if err := compileGo(goenv, goSrcs, packagePath, importcfgPath, embedcfgPath, asmHdrPath, symabisPath, gcFlags, outPath); err != nil {
471*9bb1b549SSpandan Das		return err
472*9bb1b549SSpandan Das	}
473*9bb1b549SSpandan Das
474*9bb1b549SSpandan Das	// Compile the .s files.
475*9bb1b549SSpandan Das	if len(srcs.sSrcs) > 0 {
476*9bb1b549SSpandan Das		includeSet := map[string]struct{}{
477*9bb1b549SSpandan Das			filepath.Join(os.Getenv("GOROOT"), "pkg", "include"): {},
478*9bb1b549SSpandan Das			workDir: {},
479*9bb1b549SSpandan Das		}
480*9bb1b549SSpandan Das		for _, hdr := range srcs.hSrcs {
481*9bb1b549SSpandan Das			includeSet[filepath.Dir(hdr.filename)] = struct{}{}
482*9bb1b549SSpandan Das		}
483*9bb1b549SSpandan Das		includes := make([]string, len(includeSet))
484*9bb1b549SSpandan Das		for inc := range includeSet {
485*9bb1b549SSpandan Das			includes = append(includes, inc)
486*9bb1b549SSpandan Das		}
487*9bb1b549SSpandan Das		sort.Strings(includes)
488*9bb1b549SSpandan Das		for _, inc := range includes {
489*9bb1b549SSpandan Das			asmFlags = append(asmFlags, "-I", inc)
490*9bb1b549SSpandan Das		}
491*9bb1b549SSpandan Das		for i, sSrc := range srcs.sSrcs {
492*9bb1b549SSpandan Das			obj := filepath.Join(workDir, fmt.Sprintf("s%d.o", i))
493*9bb1b549SSpandan Das			if err := asmFile(goenv, sSrc.filename, packagePath, asmFlags, obj); err != nil {
494*9bb1b549SSpandan Das				return err
495*9bb1b549SSpandan Das			}
496*9bb1b549SSpandan Das			objFiles = append(objFiles, obj)
497*9bb1b549SSpandan Das		}
498*9bb1b549SSpandan Das	}
499*9bb1b549SSpandan Das
500*9bb1b549SSpandan Das	// Pack .o files into the archive. These may come from cgo generated code,
501*9bb1b549SSpandan Das	// cgo dependencies (cdeps), or assembly.
502*9bb1b549SSpandan Das	if len(objFiles) > 0 {
503*9bb1b549SSpandan Das		if err := appendFiles(goenv, outPath, objFiles); err != nil {
504*9bb1b549SSpandan Das			return err
505*9bb1b549SSpandan Das		}
506*9bb1b549SSpandan Das	}
507*9bb1b549SSpandan Das
508*9bb1b549SSpandan Das	// Check results from nogo.
509*9bb1b549SSpandan Das	nogoStatus := nogoNotRun
510*9bb1b549SSpandan Das	if nogoChan != nil {
511*9bb1b549SSpandan Das		err := <-nogoChan
512*9bb1b549SSpandan Das		nogoChan = nil // no cancellation needed
513*9bb1b549SSpandan Das		if err != nil {
514*9bb1b549SSpandan Das			nogoStatus = nogoFailed
515*9bb1b549SSpandan Das			// TODO: should we still create the .x file without nogo facts in this case?
516*9bb1b549SSpandan Das			return err
517*9bb1b549SSpandan Das		}
518*9bb1b549SSpandan Das		nogoStatus = nogoSucceeded
519*9bb1b549SSpandan Das	}
520*9bb1b549SSpandan Das
521*9bb1b549SSpandan Das	// Extract the export data file and pack it in an .x archive together with the
522*9bb1b549SSpandan Das	// nogo facts file (if there is one). This allows compile actions to depend
523*9bb1b549SSpandan Das	// on .x files only, so we don't need to recompile a package when one of its
524*9bb1b549SSpandan Das	// imports changes in a way that doesn't affect export data.
525*9bb1b549SSpandan Das	// TODO(golang/go#33820): After Go 1.16 is the minimum supported version,
526*9bb1b549SSpandan Das	// use -linkobj to tell the compiler to create separate .a and .x files for
527*9bb1b549SSpandan Das	// compiled code and export data. Before that version, the linker needed
528*9bb1b549SSpandan Das	// export data in the .a file when building a plugin. To work around that,
529*9bb1b549SSpandan Das	// we copy the export data into .x ourselves.
530*9bb1b549SSpandan Das	if err = extractFileFromArchive(outPath, workDir, pkgDef); err != nil {
531*9bb1b549SSpandan Das		return err
532*9bb1b549SSpandan Das	}
533*9bb1b549SSpandan Das	pkgDefPath := filepath.Join(workDir, pkgDef)
534*9bb1b549SSpandan Das	if nogoStatus == nogoSucceeded {
535*9bb1b549SSpandan Das		return appendFiles(goenv, outXPath, []string{pkgDefPath, outFactsPath})
536*9bb1b549SSpandan Das	}
537*9bb1b549SSpandan Das	return appendFiles(goenv, outXPath, []string{pkgDefPath})
538*9bb1b549SSpandan Das}
539*9bb1b549SSpandan Das
540*9bb1b549SSpandan Dasfunc compileGo(goenv *env, srcs []string, packagePath, importcfgPath, embedcfgPath, asmHdrPath, symabisPath string, gcFlags []string, outPath string) error {
541*9bb1b549SSpandan Das	args := goenv.goTool("compile")
542*9bb1b549SSpandan Das	args = append(args, "-p", packagePath, "-importcfg", importcfgPath, "-pack")
543*9bb1b549SSpandan Das	if embedcfgPath != "" {
544*9bb1b549SSpandan Das		args = append(args, "-embedcfg", embedcfgPath)
545*9bb1b549SSpandan Das	}
546*9bb1b549SSpandan Das	if asmHdrPath != "" {
547*9bb1b549SSpandan Das		args = append(args, "-asmhdr", asmHdrPath)
548*9bb1b549SSpandan Das	}
549*9bb1b549SSpandan Das	if symabisPath != "" {
550*9bb1b549SSpandan Das		args = append(args, "-symabis", symabisPath)
551*9bb1b549SSpandan Das	}
552*9bb1b549SSpandan Das	args = append(args, gcFlags...)
553*9bb1b549SSpandan Das	args = append(args, "-o", outPath)
554*9bb1b549SSpandan Das	args = append(args, "--")
555*9bb1b549SSpandan Das	args = append(args, srcs...)
556*9bb1b549SSpandan Das	absArgs(args, []string{"-I", "-o", "-trimpath", "-importcfg"})
557*9bb1b549SSpandan Das	return goenv.runCommand(args)
558*9bb1b549SSpandan Das}
559*9bb1b549SSpandan Das
560*9bb1b549SSpandan Dasfunc runNogo(ctx context.Context, workDir string, nogoPath string, srcs []string, deps []archive, packagePath, importcfgPath, outFactsPath string) error {
561*9bb1b549SSpandan Das	args := []string{nogoPath}
562*9bb1b549SSpandan Das	args = append(args, "-p", packagePath)
563*9bb1b549SSpandan Das	args = append(args, "-importcfg", importcfgPath)
564*9bb1b549SSpandan Das	for _, dep := range deps {
565*9bb1b549SSpandan Das		args = append(args, "-fact", fmt.Sprintf("%s=%s", dep.importPath, dep.file))
566*9bb1b549SSpandan Das	}
567*9bb1b549SSpandan Das	args = append(args, "-x", outFactsPath)
568*9bb1b549SSpandan Das	args = append(args, srcs...)
569*9bb1b549SSpandan Das
570*9bb1b549SSpandan Das	paramsFile := filepath.Join(workDir, "nogo.param")
571*9bb1b549SSpandan Das	if err := writeParamsFile(paramsFile, args[1:]); err != nil {
572*9bb1b549SSpandan Das		return fmt.Errorf("error writing nogo params file: %v", err)
573*9bb1b549SSpandan Das	}
574*9bb1b549SSpandan Das
575*9bb1b549SSpandan Das	cmd := exec.CommandContext(ctx, args[0], "-param="+paramsFile)
576*9bb1b549SSpandan Das	out := &bytes.Buffer{}
577*9bb1b549SSpandan Das	cmd.Stdout, cmd.Stderr = out, out
578*9bb1b549SSpandan Das	if err := cmd.Run(); err != nil {
579*9bb1b549SSpandan Das		if exitErr, ok := err.(*exec.ExitError); ok {
580*9bb1b549SSpandan Das			if !exitErr.Exited() {
581*9bb1b549SSpandan Das				cmdLine := strings.Join(args, " ")
582*9bb1b549SSpandan Das				return fmt.Errorf("nogo command '%s' exited unexpectedly: %s", cmdLine, exitErr.String())
583*9bb1b549SSpandan Das			}
584*9bb1b549SSpandan Das			return errors.New(string(relativizePaths(out.Bytes())))
585*9bb1b549SSpandan Das		} else {
586*9bb1b549SSpandan Das			if out.Len() != 0 {
587*9bb1b549SSpandan Das				fmt.Fprintln(os.Stderr, out.String())
588*9bb1b549SSpandan Das			}
589*9bb1b549SSpandan Das			return fmt.Errorf("error running nogo: %v", err)
590*9bb1b549SSpandan Das		}
591*9bb1b549SSpandan Das	}
592*9bb1b549SSpandan Das	return nil
593*9bb1b549SSpandan Das}
594*9bb1b549SSpandan Das
595*9bb1b549SSpandan Dasfunc createTrimPath(gcFlags []string, path string) string {
596*9bb1b549SSpandan Das	for _, flag := range gcFlags {
597*9bb1b549SSpandan Das		if strings.HasPrefix(flag, "-trimpath=") {
598*9bb1b549SSpandan Das			return flag + ":" + path
599*9bb1b549SSpandan Das		}
600*9bb1b549SSpandan Das	}
601*9bb1b549SSpandan Das
602*9bb1b549SSpandan Das	return "-trimpath=" + path
603*9bb1b549SSpandan Das}
604*9bb1b549SSpandan Das
605*9bb1b549SSpandan Dasfunc sanitizePathForIdentifier(path string) string {
606*9bb1b549SSpandan Das	return strings.Map(func(r rune) rune {
607*9bb1b549SSpandan Das		if 'A' <= r && r <= 'Z' ||
608*9bb1b549SSpandan Das			'a' <= r && r <= 'z' ||
609*9bb1b549SSpandan Das			'0' <= r && r <= '9' ||
610*9bb1b549SSpandan Das			r == '_' {
611*9bb1b549SSpandan Das			return r
612*9bb1b549SSpandan Das		}
613*9bb1b549SSpandan Das		return '_'
614*9bb1b549SSpandan Das	}, path)
615*9bb1b549SSpandan Das}
616