xref: /aosp_15_r20/external/bazelbuild-rules_go/go/tools/builders/cgo2.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// cgo2.go provides new cgo functionality for use by the GoCompilePkg action.
16*9bb1b549SSpandan Das// We can't use the functionality in cgo.go, since it relies too heavily
17*9bb1b549SSpandan Das// on logic in cgo.bzl. Ideally, we'd be able to replace cgo.go with this
18*9bb1b549SSpandan Das// file eventually, but not until Bazel gives us enough toolchain information
19*9bb1b549SSpandan Das// to compile ObjC files.
20*9bb1b549SSpandan Das
21*9bb1b549SSpandan Daspackage main
22*9bb1b549SSpandan Das
23*9bb1b549SSpandan Dasimport (
24*9bb1b549SSpandan Das	"bytes"
25*9bb1b549SSpandan Das	"fmt"
26*9bb1b549SSpandan Das	"io/ioutil"
27*9bb1b549SSpandan Das	"os"
28*9bb1b549SSpandan Das	"path/filepath"
29*9bb1b549SSpandan Das	"strings"
30*9bb1b549SSpandan Das)
31*9bb1b549SSpandan Das
32*9bb1b549SSpandan Das// cgo2 processes a set of mixed source files with cgo.
33*9bb1b549SSpandan Dasfunc 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) {
34*9bb1b549SSpandan Das	// Report an error if the C/C++ toolchain wasn't configured.
35*9bb1b549SSpandan Das	if cc == "" {
36*9bb1b549SSpandan Das		err := cgoError(cgoSrcs[:])
37*9bb1b549SSpandan Das		err = append(err, cSrcs...)
38*9bb1b549SSpandan Das		err = append(err, cxxSrcs...)
39*9bb1b549SSpandan Das		err = append(err, objcSrcs...)
40*9bb1b549SSpandan Das		err = append(err, objcxxSrcs...)
41*9bb1b549SSpandan Das		err = append(err, sSrcs...)
42*9bb1b549SSpandan Das		return "", nil, nil, err
43*9bb1b549SSpandan Das	}
44*9bb1b549SSpandan Das
45*9bb1b549SSpandan Das	// If we only have C/C++ sources without cgo, just compile and pack them
46*9bb1b549SSpandan Das	// without generating code. The Go command forbids this, but we've
47*9bb1b549SSpandan Das	// historically allowed it.
48*9bb1b549SSpandan Das	// TODO(jayconrod): this doesn't write CGO_LDFLAGS into the archive. We
49*9bb1b549SSpandan Das	// might miss dependencies like -lstdc++ if they aren't referenced in
50*9bb1b549SSpandan Das	// some other way.
51*9bb1b549SSpandan Das	if len(cgoSrcs) == 0 {
52*9bb1b549SSpandan Das		cObjs, err = compileCSources(goenv, cSrcs, cxxSrcs, objcSrcs, objcxxSrcs, sSrcs, hSrcs, cc, cppFlags, cFlags, cxxFlags, objcFlags, objcxxFlags)
53*9bb1b549SSpandan Das		return ".", nil, cObjs, err
54*9bb1b549SSpandan Das	}
55*9bb1b549SSpandan Das
56*9bb1b549SSpandan Das	workDir, cleanup, err := goenv.workDir()
57*9bb1b549SSpandan Das	if err != nil {
58*9bb1b549SSpandan Das		return "", nil, nil, err
59*9bb1b549SSpandan Das	}
60*9bb1b549SSpandan Das	defer cleanup()
61*9bb1b549SSpandan Das
62*9bb1b549SSpandan Das	// cgo2 will gather sources into a single temporary directory, since nogo
63*9bb1b549SSpandan Das	// scanners might want to include or exclude these sources we need to ensure
64*9bb1b549SSpandan Das	// that a fragment of the path is stable and human friendly enough to be
65*9bb1b549SSpandan Das	// referenced in nogo configuration.
66*9bb1b549SSpandan Das	workDir = filepath.Join(workDir, "cgo", packagePath)
67*9bb1b549SSpandan Das	if err := os.MkdirAll(workDir, 0700); err != nil {
68*9bb1b549SSpandan Das		return "", nil, nil, err
69*9bb1b549SSpandan Das	}
70*9bb1b549SSpandan Das
71*9bb1b549SSpandan Das	// Filter out -lstdc++ and -lc++ from ldflags if we don't have C++ sources,
72*9bb1b549SSpandan Das	// and set CGO_LDFLAGS. These flags get written as special comments into cgo
73*9bb1b549SSpandan Das	// generated sources. The compiler encodes those flags in the compiled .a
74*9bb1b549SSpandan Das	// file, and the linker passes them on to the external linker.
75*9bb1b549SSpandan Das	haveCxx := len(cxxSrcs)+len(objcxxSrcs) > 0
76*9bb1b549SSpandan Das	if !haveCxx {
77*9bb1b549SSpandan Das		for _, f := range ldFlags {
78*9bb1b549SSpandan Das			if strings.HasSuffix(f, ".a") {
79*9bb1b549SSpandan Das				// These flags come from cdeps options. Assume C++.
80*9bb1b549SSpandan Das				haveCxx = true
81*9bb1b549SSpandan Das				break
82*9bb1b549SSpandan Das			}
83*9bb1b549SSpandan Das		}
84*9bb1b549SSpandan Das	}
85*9bb1b549SSpandan Das	var combinedLdFlags []string
86*9bb1b549SSpandan Das	if haveCxx {
87*9bb1b549SSpandan Das		combinedLdFlags = append(combinedLdFlags, ldFlags...)
88*9bb1b549SSpandan Das	} else {
89*9bb1b549SSpandan Das		for _, f := range ldFlags {
90*9bb1b549SSpandan Das			if f != "-lc++" && f != "-lstdc++" {
91*9bb1b549SSpandan Das				combinedLdFlags = append(combinedLdFlags, f)
92*9bb1b549SSpandan Das			}
93*9bb1b549SSpandan Das		}
94*9bb1b549SSpandan Das	}
95*9bb1b549SSpandan Das	combinedLdFlags = append(combinedLdFlags, defaultLdFlags()...)
96*9bb1b549SSpandan Das	os.Setenv("CGO_LDFLAGS", strings.Join(combinedLdFlags, " "))
97*9bb1b549SSpandan Das
98*9bb1b549SSpandan Das	// If cgo sources are in different directories, gather them into a temporary
99*9bb1b549SSpandan Das	// directory so we can use -srcdir.
100*9bb1b549SSpandan Das	srcDir = filepath.Dir(cgoSrcs[0])
101*9bb1b549SSpandan Das	srcsInSingleDir := true
102*9bb1b549SSpandan Das	for _, src := range cgoSrcs[1:] {
103*9bb1b549SSpandan Das		if filepath.Dir(src) != srcDir {
104*9bb1b549SSpandan Das			srcsInSingleDir = false
105*9bb1b549SSpandan Das			break
106*9bb1b549SSpandan Das		}
107*9bb1b549SSpandan Das	}
108*9bb1b549SSpandan Das
109*9bb1b549SSpandan Das	if srcsInSingleDir {
110*9bb1b549SSpandan Das		for i := range cgoSrcs {
111*9bb1b549SSpandan Das			cgoSrcs[i] = filepath.Base(cgoSrcs[i])
112*9bb1b549SSpandan Das		}
113*9bb1b549SSpandan Das	} else {
114*9bb1b549SSpandan Das		srcDir = filepath.Join(workDir, "cgosrcs")
115*9bb1b549SSpandan Das		if err := os.Mkdir(srcDir, 0777); err != nil {
116*9bb1b549SSpandan Das			return "", nil, nil, err
117*9bb1b549SSpandan Das		}
118*9bb1b549SSpandan Das		copiedSrcs, err := gatherSrcs(srcDir, cgoSrcs)
119*9bb1b549SSpandan Das		if err != nil {
120*9bb1b549SSpandan Das			return "", nil, nil, err
121*9bb1b549SSpandan Das		}
122*9bb1b549SSpandan Das		cgoSrcs = copiedSrcs
123*9bb1b549SSpandan Das	}
124*9bb1b549SSpandan Das
125*9bb1b549SSpandan Das	// Generate Go and C code.
126*9bb1b549SSpandan Das	hdrDirs := map[string]bool{}
127*9bb1b549SSpandan Das	var hdrIncludes []string
128*9bb1b549SSpandan Das	for _, hdr := range hSrcs {
129*9bb1b549SSpandan Das		hdrDir := filepath.Dir(hdr)
130*9bb1b549SSpandan Das		if !hdrDirs[hdrDir] {
131*9bb1b549SSpandan Das			hdrDirs[hdrDir] = true
132*9bb1b549SSpandan Das			hdrIncludes = append(hdrIncludes, "-iquote", hdrDir)
133*9bb1b549SSpandan Das		}
134*9bb1b549SSpandan Das	}
135*9bb1b549SSpandan Das	hdrIncludes = append(hdrIncludes, "-iquote", workDir) // for _cgo_export.h
136*9bb1b549SSpandan Das
137*9bb1b549SSpandan Das	execRoot, err := bazelExecRoot()
138*9bb1b549SSpandan Das	if err != nil {
139*9bb1b549SSpandan Das		return "", nil, nil, err
140*9bb1b549SSpandan Das	}
141*9bb1b549SSpandan Das	// Trim the execroot from the //line comments emitted by cgo.
142*9bb1b549SSpandan Das	args := goenv.goTool("cgo", "-srcdir", srcDir, "-objdir", workDir, "-trimpath", execRoot)
143*9bb1b549SSpandan Das	if packagePath != "" {
144*9bb1b549SSpandan Das		args = append(args, "-importpath", packagePath)
145*9bb1b549SSpandan Das	}
146*9bb1b549SSpandan Das	args = append(args, "--")
147*9bb1b549SSpandan Das	args = append(args, cppFlags...)
148*9bb1b549SSpandan Das	args = append(args, hdrIncludes...)
149*9bb1b549SSpandan Das	args = append(args, cFlags...)
150*9bb1b549SSpandan Das	args = append(args, cgoSrcs...)
151*9bb1b549SSpandan Das	if err := goenv.runCommand(args); err != nil {
152*9bb1b549SSpandan Das		return "", nil, nil, err
153*9bb1b549SSpandan Das	}
154*9bb1b549SSpandan Das
155*9bb1b549SSpandan Das	if cgoExportHPath != "" {
156*9bb1b549SSpandan Das		if err := copyFile(filepath.Join(workDir, "_cgo_export.h"), cgoExportHPath); err != nil {
157*9bb1b549SSpandan Das			return "", nil, nil, err
158*9bb1b549SSpandan Das		}
159*9bb1b549SSpandan Das	}
160*9bb1b549SSpandan Das	genGoSrcs := make([]string, 1+len(cgoSrcs))
161*9bb1b549SSpandan Das	genGoSrcs[0] = filepath.Join(workDir, "_cgo_gotypes.go")
162*9bb1b549SSpandan Das	genCSrcs := make([]string, 1+len(cgoSrcs))
163*9bb1b549SSpandan Das	genCSrcs[0] = filepath.Join(workDir, "_cgo_export.c")
164*9bb1b549SSpandan Das	for i, src := range cgoSrcs {
165*9bb1b549SSpandan Das		stem := strings.TrimSuffix(filepath.Base(src), ".go")
166*9bb1b549SSpandan Das		genGoSrcs[i+1] = filepath.Join(workDir, stem+".cgo1.go")
167*9bb1b549SSpandan Das		genCSrcs[i+1] = filepath.Join(workDir, stem+".cgo2.c")
168*9bb1b549SSpandan Das	}
169*9bb1b549SSpandan Das	cgoMainC := filepath.Join(workDir, "_cgo_main.c")
170*9bb1b549SSpandan Das
171*9bb1b549SSpandan Das	// Compile C, C++, Objective-C/C++, and assembly code.
172*9bb1b549SSpandan Das	defaultCFlags := defaultCFlags(workDir)
173*9bb1b549SSpandan Das	combinedCFlags := combineFlags(cppFlags, hdrIncludes, cFlags, defaultCFlags)
174*9bb1b549SSpandan Das	for _, lang := range []struct{ srcs, flags []string }{
175*9bb1b549SSpandan Das		{genCSrcs, combinedCFlags},
176*9bb1b549SSpandan Das		{cSrcs, combinedCFlags},
177*9bb1b549SSpandan Das		{cxxSrcs, combineFlags(cppFlags, hdrIncludes, cxxFlags, defaultCFlags)},
178*9bb1b549SSpandan Das		{objcSrcs, combineFlags(cppFlags, hdrIncludes, objcFlags, defaultCFlags)},
179*9bb1b549SSpandan Das		{objcxxSrcs, combineFlags(cppFlags, hdrIncludes, objcxxFlags, defaultCFlags)},
180*9bb1b549SSpandan Das		{sSrcs, nil},
181*9bb1b549SSpandan Das	} {
182*9bb1b549SSpandan Das		for _, src := range lang.srcs {
183*9bb1b549SSpandan Das			obj := filepath.Join(workDir, fmt.Sprintf("_x%d.o", len(cObjs)))
184*9bb1b549SSpandan Das			cObjs = append(cObjs, obj)
185*9bb1b549SSpandan Das			if err := cCompile(goenv, src, cc, lang.flags, obj); err != nil {
186*9bb1b549SSpandan Das				return "", nil, nil, err
187*9bb1b549SSpandan Das			}
188*9bb1b549SSpandan Das		}
189*9bb1b549SSpandan Das	}
190*9bb1b549SSpandan Das
191*9bb1b549SSpandan Das	mainObj := filepath.Join(workDir, "_cgo_main.o")
192*9bb1b549SSpandan Das	if err := cCompile(goenv, cgoMainC, cc, combinedCFlags, mainObj); err != nil {
193*9bb1b549SSpandan Das		return "", nil, nil, err
194*9bb1b549SSpandan Das	}
195*9bb1b549SSpandan Das
196*9bb1b549SSpandan Das	// Link cgo binary and use the symbols to generate _cgo_import.go.
197*9bb1b549SSpandan Das	mainBin := filepath.Join(workDir, "_cgo_.o") // .o is a lie; it's an executable
198*9bb1b549SSpandan Das	args = append([]string{cc, "-o", mainBin, mainObj}, cObjs...)
199*9bb1b549SSpandan Das	args = append(args, combinedLdFlags...)
200*9bb1b549SSpandan Das	var originalErrBuf bytes.Buffer
201*9bb1b549SSpandan Das	if err := goenv.runCommandToFile(os.Stdout, &originalErrBuf, args); err != nil {
202*9bb1b549SSpandan Das		// If linking the binary for cgo fails, this is usually because the
203*9bb1b549SSpandan Das		// object files reference external symbols that can't be resolved yet.
204*9bb1b549SSpandan Das		// Since the binary is only produced to have its symbols read by the cgo
205*9bb1b549SSpandan Das		// command, there is no harm in trying to build it allowing unresolved
206*9bb1b549SSpandan Das		// symbols - the real link that happens at the end will fail if they
207*9bb1b549SSpandan Das		// rightfully can't be resolved.
208*9bb1b549SSpandan Das		var allowUnresolvedSymbolsLdFlag string
209*9bb1b549SSpandan Das		switch os.Getenv("GOOS") {
210*9bb1b549SSpandan Das		case "windows":
211*9bb1b549SSpandan Das			// MinGW's linker doesn't seem to support --unresolved-symbols
212*9bb1b549SSpandan Das			// and MSVC isn't supported at all.
213*9bb1b549SSpandan Das			return "", nil, nil, err
214*9bb1b549SSpandan Das		case "darwin", "ios":
215*9bb1b549SSpandan Das			allowUnresolvedSymbolsLdFlag = "-Wl,-undefined,dynamic_lookup"
216*9bb1b549SSpandan Das		default:
217*9bb1b549SSpandan Das			allowUnresolvedSymbolsLdFlag = "-Wl,--unresolved-symbols=ignore-all"
218*9bb1b549SSpandan Das		}
219*9bb1b549SSpandan Das		// Print and return the original error if we can't link the binary with
220*9bb1b549SSpandan Das		// the additional linker flags as they may simply be incorrect for the
221*9bb1b549SSpandan Das		// particular compiler/linker pair and would obscure the true reason for
222*9bb1b549SSpandan Das		// the failure of the original command.
223*9bb1b549SSpandan Das		if err2 := goenv.runCommandToFile(
224*9bb1b549SSpandan Das			os.Stdout,
225*9bb1b549SSpandan Das			ioutil.Discard,
226*9bb1b549SSpandan Das			append(args, allowUnresolvedSymbolsLdFlag),
227*9bb1b549SSpandan Das		); err2 != nil {
228*9bb1b549SSpandan Das			os.Stderr.Write(relativizePaths(originalErrBuf.Bytes()))
229*9bb1b549SSpandan Das			return "", nil, nil, err
230*9bb1b549SSpandan Das		}
231*9bb1b549SSpandan Das		// Do not print the original error - rerunning the command with the
232*9bb1b549SSpandan Das		// additional linker flag fixed it.
233*9bb1b549SSpandan Das	}
234*9bb1b549SSpandan Das
235*9bb1b549SSpandan Das	cgoImportsGo := filepath.Join(workDir, "_cgo_imports.go")
236*9bb1b549SSpandan Das	args = goenv.goTool("cgo", "-dynpackage", packageName, "-dynimport", mainBin, "-dynout", cgoImportsGo)
237*9bb1b549SSpandan Das	if err := goenv.runCommand(args); err != nil {
238*9bb1b549SSpandan Das		return "", nil, nil, err
239*9bb1b549SSpandan Das	}
240*9bb1b549SSpandan Das	genGoSrcs = append(genGoSrcs, cgoImportsGo)
241*9bb1b549SSpandan Das
242*9bb1b549SSpandan Das	// Copy regular Go source files into the work directory so that we can
243*9bb1b549SSpandan Das	// use -trimpath=workDir.
244*9bb1b549SSpandan Das	goBases, err := gatherSrcs(workDir, goSrcs)
245*9bb1b549SSpandan Das	if err != nil {
246*9bb1b549SSpandan Das		return "", nil, nil, err
247*9bb1b549SSpandan Das	}
248*9bb1b549SSpandan Das
249*9bb1b549SSpandan Das	allGoSrcs = make([]string, len(goSrcs)+len(genGoSrcs))
250*9bb1b549SSpandan Das	for i := range goSrcs {
251*9bb1b549SSpandan Das		allGoSrcs[i] = filepath.Join(workDir, goBases[i])
252*9bb1b549SSpandan Das	}
253*9bb1b549SSpandan Das	copy(allGoSrcs[len(goSrcs):], genGoSrcs)
254*9bb1b549SSpandan Das	return workDir, allGoSrcs, cObjs, nil
255*9bb1b549SSpandan Das}
256*9bb1b549SSpandan Das
257*9bb1b549SSpandan Das// compileCSources compiles a list of C, C++, Objective-C, Objective-C++,
258*9bb1b549SSpandan Das// and assembly sources into .o files to be packed into the archive.
259*9bb1b549SSpandan Das// It does not run cgo. This is used for packages with "cgo = True" but
260*9bb1b549SSpandan Das// without any .go files that import "C". The Go command forbids this,
261*9bb1b549SSpandan Das// but we have historically allowed it.
262*9bb1b549SSpandan Dasfunc compileCSources(goenv *env, cSrcs, cxxSrcs, objcSrcs, objcxxSrcs, sSrcs, hSrcs []string, cc string, cppFlags, cFlags, cxxFlags, objcFlags, objcxxFlags []string) (cObjs []string, err error) {
263*9bb1b549SSpandan Das	workDir, cleanup, err := goenv.workDir()
264*9bb1b549SSpandan Das	if err != nil {
265*9bb1b549SSpandan Das		return nil, err
266*9bb1b549SSpandan Das	}
267*9bb1b549SSpandan Das	defer cleanup()
268*9bb1b549SSpandan Das
269*9bb1b549SSpandan Das	hdrDirs := map[string]bool{}
270*9bb1b549SSpandan Das	var hdrIncludes []string
271*9bb1b549SSpandan Das	for _, hdr := range hSrcs {
272*9bb1b549SSpandan Das		hdrDir := filepath.Dir(hdr)
273*9bb1b549SSpandan Das		if !hdrDirs[hdrDir] {
274*9bb1b549SSpandan Das			hdrDirs[hdrDir] = true
275*9bb1b549SSpandan Das			hdrIncludes = append(hdrIncludes, "-iquote", hdrDir)
276*9bb1b549SSpandan Das		}
277*9bb1b549SSpandan Das	}
278*9bb1b549SSpandan Das
279*9bb1b549SSpandan Das	defaultCFlags := defaultCFlags(workDir)
280*9bb1b549SSpandan Das	for _, lang := range []struct{ srcs, flags []string }{
281*9bb1b549SSpandan Das		{cSrcs, combineFlags(cppFlags, hdrIncludes, cFlags, defaultCFlags)},
282*9bb1b549SSpandan Das		{cxxSrcs, combineFlags(cppFlags, hdrIncludes, cxxFlags, defaultCFlags)},
283*9bb1b549SSpandan Das		{objcSrcs, combineFlags(cppFlags, hdrIncludes, objcFlags, defaultCFlags)},
284*9bb1b549SSpandan Das		{objcxxSrcs, combineFlags(cppFlags, hdrIncludes, objcxxFlags, defaultCFlags)},
285*9bb1b549SSpandan Das		{sSrcs, nil},
286*9bb1b549SSpandan Das	} {
287*9bb1b549SSpandan Das		for _, src := range lang.srcs {
288*9bb1b549SSpandan Das			obj := filepath.Join(workDir, fmt.Sprintf("_x%d.o", len(cObjs)))
289*9bb1b549SSpandan Das			cObjs = append(cObjs, obj)
290*9bb1b549SSpandan Das			if err := cCompile(goenv, src, cc, lang.flags, obj); err != nil {
291*9bb1b549SSpandan Das				return nil, err
292*9bb1b549SSpandan Das			}
293*9bb1b549SSpandan Das		}
294*9bb1b549SSpandan Das	}
295*9bb1b549SSpandan Das	return cObjs, nil
296*9bb1b549SSpandan Das}
297*9bb1b549SSpandan Das
298*9bb1b549SSpandan Dasfunc combineFlags(lists ...[]string) []string {
299*9bb1b549SSpandan Das	n := 0
300*9bb1b549SSpandan Das	for _, list := range lists {
301*9bb1b549SSpandan Das		n += len(list)
302*9bb1b549SSpandan Das	}
303*9bb1b549SSpandan Das	flags := make([]string, 0, n)
304*9bb1b549SSpandan Das	for _, list := range lists {
305*9bb1b549SSpandan Das		flags = append(flags, list...)
306*9bb1b549SSpandan Das	}
307*9bb1b549SSpandan Das	return flags
308*9bb1b549SSpandan Das}
309*9bb1b549SSpandan Das
310*9bb1b549SSpandan Dasfunc cCompile(goenv *env, src, cc string, flags []string, out string) error {
311*9bb1b549SSpandan Das	args := []string{cc}
312*9bb1b549SSpandan Das	args = append(args, flags...)
313*9bb1b549SSpandan Das	args = append(args, "-c", src, "-o", out)
314*9bb1b549SSpandan Das	return goenv.runCommand(args)
315*9bb1b549SSpandan Das}
316*9bb1b549SSpandan Das
317*9bb1b549SSpandan Dasfunc defaultCFlags(workDir string) []string {
318*9bb1b549SSpandan Das	flags := []string{
319*9bb1b549SSpandan Das		"-fdebug-prefix-map=" + abs(".") + "=.",
320*9bb1b549SSpandan Das		"-fdebug-prefix-map=" + workDir + "=.",
321*9bb1b549SSpandan Das	}
322*9bb1b549SSpandan Das	goos, goarch := os.Getenv("GOOS"), os.Getenv("GOARCH")
323*9bb1b549SSpandan Das	switch {
324*9bb1b549SSpandan Das	case goos == "darwin" || goos == "ios":
325*9bb1b549SSpandan Das		return flags
326*9bb1b549SSpandan Das	case goos == "windows" && goarch == "amd64":
327*9bb1b549SSpandan Das		return append(flags, "-mthreads")
328*9bb1b549SSpandan Das	default:
329*9bb1b549SSpandan Das		return append(flags, "-pthread")
330*9bb1b549SSpandan Das	}
331*9bb1b549SSpandan Das}
332*9bb1b549SSpandan Das
333*9bb1b549SSpandan Dasfunc defaultLdFlags() []string {
334*9bb1b549SSpandan Das	goos, goarch := os.Getenv("GOOS"), os.Getenv("GOARCH")
335*9bb1b549SSpandan Das	switch {
336*9bb1b549SSpandan Das	case goos == "android":
337*9bb1b549SSpandan Das		return []string{"-llog", "-ldl"}
338*9bb1b549SSpandan Das	case goos == "darwin" || goos == "ios":
339*9bb1b549SSpandan Das		return nil
340*9bb1b549SSpandan Das	case goos == "windows" && goarch == "amd64":
341*9bb1b549SSpandan Das		return []string{"-mthreads"}
342*9bb1b549SSpandan Das	default:
343*9bb1b549SSpandan Das		return []string{"-pthread"}
344*9bb1b549SSpandan Das	}
345*9bb1b549SSpandan Das}
346*9bb1b549SSpandan Das
347*9bb1b549SSpandan Das// gatherSrcs copies or links files listed in srcs into dir. This is needed
348*9bb1b549SSpandan Das// to effectively use -trimpath with generated sources. It's also needed by cgo.
349*9bb1b549SSpandan Das//
350*9bb1b549SSpandan Das// gatherSrcs returns the basenames of copied files in the directory.
351*9bb1b549SSpandan Dasfunc gatherSrcs(dir string, srcs []string) ([]string, error) {
352*9bb1b549SSpandan Das	copiedBases := make([]string, len(srcs))
353*9bb1b549SSpandan Das	for i, src := range srcs {
354*9bb1b549SSpandan Das		base := filepath.Base(src)
355*9bb1b549SSpandan Das		ext := filepath.Ext(base)
356*9bb1b549SSpandan Das		stem := base[:len(base)-len(ext)]
357*9bb1b549SSpandan Das		var err error
358*9bb1b549SSpandan Das		for j := 1; j < 10000; j++ {
359*9bb1b549SSpandan Das			if err = copyOrLinkFile(src, filepath.Join(dir, base)); err == nil {
360*9bb1b549SSpandan Das				break
361*9bb1b549SSpandan Das			} else if !os.IsExist(err) {
362*9bb1b549SSpandan Das				return nil, err
363*9bb1b549SSpandan Das			} else {
364*9bb1b549SSpandan Das				base = fmt.Sprintf("%s_%d%s", stem, j, ext)
365*9bb1b549SSpandan Das			}
366*9bb1b549SSpandan Das		}
367*9bb1b549SSpandan Das		if err != nil {
368*9bb1b549SSpandan Das			return nil, fmt.Errorf("could not find unique name for file %s", src)
369*9bb1b549SSpandan Das		}
370*9bb1b549SSpandan Das		copiedBases[i] = base
371*9bb1b549SSpandan Das	}
372*9bb1b549SSpandan Das	return copiedBases, nil
373*9bb1b549SSpandan Das}
374*9bb1b549SSpandan Das
375*9bb1b549SSpandan Dasfunc bazelExecRoot() (string, error) {
376*9bb1b549SSpandan Das	// Bazel executes the builder with a working directory of the form
377*9bb1b549SSpandan Das	// .../execroot/<workspace name>. By stripping the last segment, we obtain a
378*9bb1b549SSpandan Das	// prefix of all possible source files, even when contained in external
379*9bb1b549SSpandan Das	// repositories.
380*9bb1b549SSpandan Das	cwd, err := os.Getwd()
381*9bb1b549SSpandan Das	if err != nil {
382*9bb1b549SSpandan Das		return "", err
383*9bb1b549SSpandan Das	}
384*9bb1b549SSpandan Das	return filepath.Dir(cwd), nil
385*9bb1b549SSpandan Das}
386*9bb1b549SSpandan Das
387*9bb1b549SSpandan Dastype cgoError []string
388*9bb1b549SSpandan Das
389*9bb1b549SSpandan Dasfunc (e cgoError) Error() string {
390*9bb1b549SSpandan Das	b := &bytes.Buffer{}
391*9bb1b549SSpandan Das	fmt.Fprint(b, "CC is not set and files need to be processed with cgo:\n")
392*9bb1b549SSpandan Das	for _, f := range e {
393*9bb1b549SSpandan Das		fmt.Fprintf(b, "\t%s\n", f)
394*9bb1b549SSpandan Das	}
395*9bb1b549SSpandan Das	fmt.Fprintf(b, "Ensure that 'cgo = True' is set and the C/C++ toolchain is configured.")
396*9bb1b549SSpandan Das	return b.String()
397*9bb1b549SSpandan Das}
398