func cgo2()

in go/tools/builders/cgo2.go [32:223]


func cgo2(goenv *env, goSrcs, cgoSrcs, cSrcs, cxxSrcs, objcSrcs, objcxxSrcs, sSrcs, hSrcs []string, packagePath, packageName string, cc string, cppFlags, cFlags, cxxFlags, objcFlags, objcxxFlags, ldFlags []string, cgoExportHPath string) (srcDir string, allGoSrcs, cObjs []string, err error) {
	// Report an error if the C/C++ toolchain wasn't configured.
	if cc == "" {
		err := cgoError(cgoSrcs[:])
		err = append(err, cSrcs...)
		err = append(err, cxxSrcs...)
		err = append(err, objcSrcs...)
		err = append(err, objcxxSrcs...)
		err = append(err, sSrcs...)
		return "", nil, nil, err
	}

	// If we only have C/C++ sources without cgo, just compile and pack them
	// without generating code. The Go command forbids this, but we've
	// historically allowed it.
	// TODO(jayconrod): this doesn't write CGO_LDFLAGS into the archive. We
	// might miss dependencies like -lstdc++ if they aren't referenced in
	// some other way.
	if len(cgoSrcs) == 0 {
		cObjs, err = compileCSources(goenv, cSrcs, cxxSrcs, objcSrcs, objcxxSrcs, sSrcs, hSrcs, cc, cppFlags, cFlags, cxxFlags, objcFlags, objcxxFlags)
		return ".", nil, cObjs, err
	}

	workDir, cleanup, err := goenv.workDir()
	if err != nil {
		return "", nil, nil, err
	}
	defer cleanup()

	// cgo2 will gather sources into a single temporary directory, since nogo
	// scanners might want to include or exclude these sources we need to ensure
	// that a fragment of the path is stable and human friendly enough to be
	// referenced in nogo configuration.
	workDir = filepath.Join(workDir, "cgo", packagePath)
	if err := os.MkdirAll(workDir, 0700); err != nil {
		return "", nil, nil, err
	}

	// Filter out -lstdc++ and -lc++ from ldflags if we don't have C++ sources,
	// and set CGO_LDFLAGS. These flags get written as special comments into cgo
	// generated sources. The compiler encodes those flags in the compiled .a
	// file, and the linker passes them on to the external linker.
	haveCxx := len(cxxSrcs)+len(objcxxSrcs) > 0
	if !haveCxx {
		for _, f := range ldFlags {
			if strings.HasSuffix(f, ".a") {
				// These flags come from cdeps options. Assume C++.
				haveCxx = true
				break
			}
		}
	}
	var combinedLdFlags []string
	if haveCxx {
		combinedLdFlags = append(combinedLdFlags, ldFlags...)
	} else {
		for _, f := range ldFlags {
			if f != "-lc++" && f != "-lstdc++" {
				combinedLdFlags = append(combinedLdFlags, f)
			}
		}
	}
	combinedLdFlags = append(combinedLdFlags, defaultLdFlags()...)
	os.Setenv("CGO_LDFLAGS", strings.Join(combinedLdFlags, " "))

	// If cgo sources are in different directories, gather them into a temporary
	// directory so we can use -srcdir.
	srcDir = filepath.Dir(cgoSrcs[0])
	srcsInSingleDir := true
	for _, src := range cgoSrcs[1:] {
		if filepath.Dir(src) != srcDir {
			srcsInSingleDir = false
			break
		}
	}

	if srcsInSingleDir {
		for i := range cgoSrcs {
			cgoSrcs[i] = filepath.Base(cgoSrcs[i])
		}
	} else {
		srcDir = filepath.Join(workDir, "cgosrcs")
		if err := os.Mkdir(srcDir, 0777); err != nil {
			return "", nil, nil, err
		}
		copiedSrcs, err := gatherSrcs(srcDir, cgoSrcs)
		if err != nil {
			return "", nil, nil, err
		}
		cgoSrcs = copiedSrcs
	}

	// Generate Go and C code.
	hdrDirs := map[string]bool{}
	var hdrIncludes []string
	for _, hdr := range hSrcs {
		hdrDir := filepath.Dir(hdr)
		if !hdrDirs[hdrDir] {
			hdrDirs[hdrDir] = true
			hdrIncludes = append(hdrIncludes, "-iquote", hdrDir)
		}
	}
	hdrIncludes = append(hdrIncludes, "-iquote", workDir) // for _cgo_export.h

	execRoot, err := bazelExecRoot()
	if err != nil {
		return "", nil, nil, err
	}
	// Trim the execroot from the //line comments emitted by cgo.
	args := goenv.goTool("cgo", "-srcdir", srcDir, "-objdir", workDir, "-trimpath", execRoot)
	if packagePath != "" {
		args = append(args, "-importpath", packagePath)
	}
	args = append(args, "--")
	args = append(args, cppFlags...)
	args = append(args, hdrIncludes...)
	args = append(args, cFlags...)
	args = append(args, cgoSrcs...)
	if err := goenv.runCommand(args); err != nil {
		return "", nil, nil, err
	}

	if cgoExportHPath != "" {
		if err := copyFile(filepath.Join(workDir, "_cgo_export.h"), cgoExportHPath); err != nil {
			return "", nil, nil, err
		}
	}
	genGoSrcs := make([]string, 1+len(cgoSrcs))
	genGoSrcs[0] = filepath.Join(workDir, "_cgo_gotypes.go")
	genCSrcs := make([]string, 1+len(cgoSrcs))
	genCSrcs[0] = filepath.Join(workDir, "_cgo_export.c")
	for i, src := range cgoSrcs {
		stem := strings.TrimSuffix(filepath.Base(src), ".go")
		genGoSrcs[i+1] = filepath.Join(workDir, stem+".cgo1.go")
		genCSrcs[i+1] = filepath.Join(workDir, stem+".cgo2.c")
	}
	cgoMainC := filepath.Join(workDir, "_cgo_main.c")

	// Compile C, C++, Objective-C/C++, and assembly code.
	defaultCFlags := defaultCFlags(workDir)
	combinedCFlags := combineFlags(cppFlags, hdrIncludes, cFlags, defaultCFlags)
	for _, lang := range []struct{ srcs, flags []string }{
		{genCSrcs, combinedCFlags},
		{cSrcs, combinedCFlags},
		{cxxSrcs, combineFlags(cppFlags, hdrIncludes, cxxFlags, defaultCFlags)},
		{objcSrcs, combineFlags(cppFlags, hdrIncludes, objcFlags, defaultCFlags)},
		{objcxxSrcs, combineFlags(cppFlags, hdrIncludes, objcxxFlags, defaultCFlags)},
		{sSrcs, nil},
	} {
		for _, src := range lang.srcs {
			obj := filepath.Join(workDir, fmt.Sprintf("_x%d.o", len(cObjs)))
			cObjs = append(cObjs, obj)
			if err := cCompile(goenv, src, cc, lang.flags, obj); err != nil {
				return "", nil, nil, err
			}
		}
	}

	mainObj := filepath.Join(workDir, "_cgo_main.o")
	if err := cCompile(goenv, cgoMainC, cc, combinedCFlags, mainObj); err != nil {
		return "", nil, nil, err
	}

	// Link cgo binary and use the symbols to generate _cgo_import.go.
	mainBin := filepath.Join(workDir, "_cgo_.o") // .o is a lie; it's an executable
	args = append([]string{cc, "-o", mainBin, mainObj}, cObjs...)
	args = append(args, combinedLdFlags...)
	if err := goenv.runCommand(args); err != nil {
		return "", nil, nil, err
	}

	cgoImportsGo := filepath.Join(workDir, "_cgo_imports.go")
	args = goenv.goTool("cgo", "-dynpackage", packageName, "-dynimport", mainBin, "-dynout", cgoImportsGo)
	if err := goenv.runCommand(args); err != nil {
		return "", nil, nil, err
	}
	genGoSrcs = append(genGoSrcs, cgoImportsGo)

	// Copy regular Go source files into the work directory so that we can
	// use -trimpath=workDir.
	goBases, err := gatherSrcs(workDir, goSrcs)
	if err != nil {
		return "", nil, nil, err
	}

	allGoSrcs = make([]string, len(goSrcs)+len(genGoSrcs))
	for i := range goSrcs {
		allGoSrcs[i] = filepath.Join(workDir, goBases[i])
	}
	copy(allGoSrcs[len(goSrcs):], genGoSrcs)
	return workDir, allGoSrcs, cObjs, nil
}