in go/tools/builders/compilepkg.go [149:458]
func compileArchive(
goenv *env,
importPath string,
packagePath string,
srcs archiveSrcs,
deps []archive,
coverMode string,
coverSrcs []string,
embedSrcs []string,
cgoEnabled bool,
cc string,
gcFlags []string,
asmFlags []string,
cppFlags []string,
cFlags []string,
cxxFlags []string,
objcFlags []string,
objcxxFlags []string,
ldFlags []string,
nogoPath string,
packageListPath string,
outPath string,
outXPath string,
cgoExportHPath string) error {
workDir, cleanup, err := goenv.workDir()
if err != nil {
return err
}
defer cleanup()
if len(srcs.goSrcs) == 0 {
emptyPath := filepath.Join(workDir, "_empty.go")
if err := ioutil.WriteFile(emptyPath, []byte("package empty\n"), 0666); err != nil {
return err
}
srcs.goSrcs = append(srcs.goSrcs, fileInfo{
filename: emptyPath,
ext: goExt,
matched: true,
pkg: "empty",
})
defer os.Remove(emptyPath)
}
packageName := srcs.goSrcs[0].pkg
var goSrcs, cgoSrcs []string
for _, src := range srcs.goSrcs {
if src.isCgo {
cgoSrcs = append(cgoSrcs, src.filename)
} else {
goSrcs = append(goSrcs, src.filename)
}
}
cSrcs := make([]string, len(srcs.cSrcs))
for i, src := range srcs.cSrcs {
cSrcs[i] = src.filename
}
cxxSrcs := make([]string, len(srcs.cxxSrcs))
for i, src := range srcs.cxxSrcs {
cxxSrcs[i] = src.filename
}
objcSrcs := make([]string, len(srcs.objcSrcs))
for i, src := range srcs.objcSrcs {
objcSrcs[i] = src.filename
}
objcxxSrcs := make([]string, len(srcs.objcxxSrcs))
for i, src := range srcs.objcxxSrcs {
objcxxSrcs[i] = src.filename
}
sSrcs := make([]string, len(srcs.sSrcs))
for i, src := range srcs.sSrcs {
sSrcs[i] = src.filename
}
hSrcs := make([]string, len(srcs.hSrcs))
for i, src := range srcs.hSrcs {
hSrcs[i] = src.filename
}
haveCgo := len(cgoSrcs)+len(cSrcs)+len(cxxSrcs)+len(objcSrcs)+len(objcxxSrcs) > 0
// Instrument source files for coverage.
if coverMode != "" {
shouldCover := make(map[string]bool)
for _, s := range coverSrcs {
shouldCover[s] = true
}
combined := append([]string{}, goSrcs...)
if cgoEnabled {
combined = append(combined, cgoSrcs...)
}
for i, origSrc := range combined {
if !shouldCover[origSrc] {
continue
}
srcName := origSrc
if importPath != "" {
srcName = path.Join(importPath, filepath.Base(origSrc))
}
stem := filepath.Base(origSrc)
if ext := filepath.Ext(stem); ext != "" {
stem = stem[:len(stem)-len(ext)]
}
coverVar := fmt.Sprintf("Cover_%s_%d_%s", sanitizePathForIdentifier(importPath), i, sanitizePathForIdentifier(stem))
coverVar = strings.ReplaceAll(coverVar, "_", "Z")
coverSrc := filepath.Join(workDir, fmt.Sprintf("cover_%d.go", i))
if err := instrumentForCoverage(goenv, origSrc, srcName, coverVar, coverMode, coverSrc); err != nil {
return err
}
if i < len(goSrcs) {
goSrcs[i] = coverSrc
} else {
cgoSrcs[i-len(goSrcs)] = coverSrc
}
}
}
// If we have cgo, generate separate C and go files, and compile the
// C files.
var objFiles []string
if cgoEnabled && haveCgo {
// TODO(#2006): Compile .s and .S files with cgo2, not the Go assembler.
// If cgo is not enabled or we don't have other cgo sources, don't
// compile .S files.
var srcDir string
srcDir, goSrcs, objFiles, err = cgo2(goenv, goSrcs, cgoSrcs, cSrcs, cxxSrcs, objcSrcs, objcxxSrcs, nil, hSrcs, packagePath, packageName, cc, cppFlags, cFlags, cxxFlags, objcFlags, objcxxFlags, ldFlags, cgoExportHPath)
if err != nil {
return err
}
gcFlags = append(gcFlags, createTrimPath(gcFlags, srcDir))
} else {
if cgoExportHPath != "" {
if err := ioutil.WriteFile(cgoExportHPath, nil, 0666); err != nil {
return err
}
}
gcFlags = append(gcFlags, createTrimPath(gcFlags, "."))
}
// Check that the filtered sources don't import anything outside of
// the standard library and the direct dependencies.
imports, err := checkImports(srcs.goSrcs, deps, packageListPath)
if err != nil {
return err
}
if cgoEnabled && len(cgoSrcs) != 0 {
// cgo generated code imports some extra packages.
imports["runtime/cgo"] = nil
imports["syscall"] = nil
imports["unsafe"] = nil
}
if coverMode != "" {
if coverMode == "atomic" {
imports["sync/atomic"] = nil
}
const coverdataPath = "github.com/bazelbuild/rules_go/go/tools/coverdata"
var coverdata *archive
for i := range deps {
if deps[i].importPath == coverdataPath {
coverdata = &deps[i]
break
}
}
if coverdata == nil {
return errors.New("coverage requested but coverdata dependency not provided")
}
imports[coverdataPath] = coverdata
}
// Build an importcfg file for the compiler.
importcfgPath, err := buildImportcfgFileForCompile(imports, goenv.installSuffix, filepath.Dir(outPath))
if err != nil {
return err
}
defer os.Remove(importcfgPath)
// Build an embedcfg file mapping embed patterns to filenames.
// Embed patterns are relative to any one of a list of root directories
// that may contain embeddable files. Source files containing embed patterns
// must be in one of these root directories so the pattern appears to be
// relative to the source file. Usually, there are two roots: the source
// directory, and the output directory (so that generated files are
// embeddable). There may be additional roots if sources are in multiple
// directories (like if there are are generated source files).
var srcDirs []string
srcDirs = append(srcDirs, filepath.Dir(outPath))
for _, src := range srcs.goSrcs {
srcDirs = append(srcDirs, filepath.Dir(src.filename))
}
sort.Strings(srcDirs) // group duplicates to uniq them below.
embedRootDirs := srcDirs[:1]
for _, dir := range srcDirs {
prev := embedRootDirs[len(embedRootDirs)-1]
if dir == prev || strings.HasPrefix(dir, prev+string(filepath.Separator)) {
// Skip duplicates.
continue
}
embedRootDirs = append(embedRootDirs, dir)
}
embedcfgPath, err := buildEmbedcfgFile(srcs.goSrcs, embedSrcs, embedRootDirs, workDir)
if err != nil {
return err
}
if embedcfgPath != "" {
defer os.Remove(embedcfgPath)
}
// Run nogo concurrently.
var nogoChan chan error
outFactsPath := filepath.Join(workDir, nogoFact)
if nogoPath != "" {
ctx, cancel := context.WithCancel(context.Background())
nogoChan = make(chan error)
go func() {
nogoChan <- runNogo(ctx, workDir, nogoPath, goSrcs, deps, packagePath, importcfgPath, outFactsPath)
}()
defer func() {
if nogoChan != nil {
cancel()
<-nogoChan
}
}()
}
// If there are assembly files, and this is go1.12+, generate symbol ABIs.
asmHdrPath := ""
if len(srcs.sSrcs) > 0 {
asmHdrPath = filepath.Join(workDir, "go_asm.h")
}
symabisPath, err := buildSymabisFile(goenv, srcs.sSrcs, srcs.hSrcs, asmHdrPath)
if symabisPath != "" {
defer os.Remove(symabisPath)
}
if err != nil {
return err
}
// Compile the filtered .go files.
if err := compileGo(goenv, goSrcs, packagePath, importcfgPath, embedcfgPath, asmHdrPath, symabisPath, gcFlags, outPath); err != nil {
return err
}
// Compile the .s files.
if len(srcs.sSrcs) > 0 {
includeSet := map[string]struct{}{
filepath.Join(os.Getenv("GOROOT"), "pkg", "include"): struct{}{},
workDir: struct{}{},
}
for _, hdr := range srcs.hSrcs {
includeSet[filepath.Dir(hdr.filename)] = struct{}{}
}
includes := make([]string, len(includeSet))
for inc := range includeSet {
includes = append(includes, inc)
}
sort.Strings(includes)
for _, inc := range includes {
asmFlags = append(asmFlags, "-I", inc)
}
for i, sSrc := range srcs.sSrcs {
obj := filepath.Join(workDir, fmt.Sprintf("s%d.o", i))
if err := asmFile(goenv, sSrc.filename, asmFlags, obj); err != nil {
return err
}
objFiles = append(objFiles, obj)
}
}
// Pack .o files into the archive. These may come from cgo generated code,
// cgo dependencies (cdeps), or assembly.
if len(objFiles) > 0 {
if err := appendFiles(goenv, outPath, objFiles); err != nil {
return err
}
}
// Check results from nogo.
nogoStatus := nogoNotRun
if nogoChan != nil {
err := <-nogoChan
nogoChan = nil // no cancellation needed
if err != nil {
nogoStatus = nogoFailed
// TODO: should we still create the .x file without nogo facts in this case?
return err
}
nogoStatus = nogoSucceeded
}
// Extract the export data file and pack it in an .x archive together with the
// nogo facts file (if there is one). This allows compile actions to depend
// on .x files only, so we don't need to recompile a package when one of its
// imports changes in a way that doesn't affect export data.
// TODO(golang/go#33820): After Go 1.16 is the minimum supported version,
// use -linkobj to tell the compiler to create separate .a and .x files for
// compiled code and export data. Before that version, the linker needed
// export data in the .a file when building a plugin. To work around that,
// we copy the export data into .x ourselves.
if err = extractFileFromArchive(outPath, workDir, pkgDef); err != nil {
return err
}
pkgDefPath := filepath.Join(workDir, pkgDef)
if nogoStatus == nogoSucceeded {
return appendFiles(goenv, outXPath, []string{pkgDefPath, outFactsPath})
}
return appendFiles(goenv, outXPath, []string{pkgDefPath})
}