1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package main
22
23 import (
24 "bytes"
25 "fmt"
26 "io"
27 "io/ioutil"
28 "os"
29 "path/filepath"
30 "runtime"
31 "strings"
32 )
33
34
35 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) {
36
37 if cc == "" {
38 err := cgoError(cgoSrcs[:])
39 err = append(err, cSrcs...)
40 err = append(err, cxxSrcs...)
41 err = append(err, objcSrcs...)
42 err = append(err, objcxxSrcs...)
43 err = append(err, sSrcs...)
44 return "", nil, nil, err
45 }
46
47
48
49
50
51
52
53 if len(cgoSrcs) == 0 {
54 cObjs, err = compileCSources(goenv, cSrcs, cxxSrcs, objcSrcs, objcxxSrcs, sSrcs, hSrcs, cc, cppFlags, cFlags, cxxFlags, objcFlags, objcxxFlags)
55 return ".", nil, cObjs, err
56 }
57
58 workDir, cleanup, err := goenv.workDir()
59 if err != nil {
60 return "", nil, nil, err
61 }
62 defer cleanup()
63
64
65
66
67
68 workDir = filepath.Join(workDir, "cgo", packagePath)
69 if err := os.MkdirAll(workDir, 0700); err != nil {
70 return "", nil, nil, err
71 }
72
73
74
75
76
77 haveCxx := len(cxxSrcs)+len(objcxxSrcs) > 0
78 if !haveCxx {
79 for _, f := range ldFlags {
80 if strings.HasSuffix(f, ".a") {
81
82 haveCxx = true
83 break
84 }
85 }
86 }
87 var combinedLdFlags []string
88 if haveCxx {
89 combinedLdFlags = append(combinedLdFlags, ldFlags...)
90 } else {
91 for _, f := range ldFlags {
92 if f != "-lc++" && f != "-lstdc++" {
93 combinedLdFlags = append(combinedLdFlags, f)
94 }
95 }
96 }
97 combinedLdFlags = append(combinedLdFlags, defaultLdFlags()...)
98 os.Setenv("CGO_LDFLAGS", strings.Join(combinedLdFlags, " "))
99
100
101
102 srcDir = filepath.Dir(cgoSrcs[0])
103 srcsInSingleDir := true
104 for _, src := range cgoSrcs[1:] {
105 if filepath.Dir(src) != srcDir {
106 srcsInSingleDir = false
107 break
108 }
109 }
110
111 if srcsInSingleDir {
112 for i := range cgoSrcs {
113 cgoSrcs[i] = filepath.Base(cgoSrcs[i])
114 }
115 } else {
116 srcDir = filepath.Join(workDir, "cgosrcs")
117 if err := os.Mkdir(srcDir, 0777); err != nil {
118 return "", nil, nil, err
119 }
120 copiedSrcs, err := gatherSrcs(srcDir, cgoSrcs)
121 if err != nil {
122 return "", nil, nil, err
123 }
124 cgoSrcs = copiedSrcs
125 }
126
127
128 hdrDirs := map[string]bool{}
129 var hdrIncludes []string
130 for _, hdr := range hSrcs {
131 hdrDir := filepath.Dir(hdr)
132 if !hdrDirs[hdrDir] {
133 hdrDirs[hdrDir] = true
134 hdrIncludes = append(hdrIncludes, "-iquote", hdrDir)
135 }
136 }
137 hdrIncludes = append(hdrIncludes, "-iquote", workDir)
138
139 execRoot, err := bazelExecRoot()
140 if err != nil {
141 return "", nil, nil, err
142 }
143
144 args := goenv.goTool("cgo", "-srcdir", srcDir, "-objdir", workDir, "-trimpath", execRoot)
145 if packagePath != "" {
146 args = append(args, "-importpath", packagePath)
147 }
148 args = append(args, "--")
149 args = append(args, cppFlags...)
150 args = append(args, hdrIncludes...)
151 args = append(args, cFlags...)
152 args = append(args, cgoSrcs...)
153 if err := goenv.runCommand(args); err != nil {
154 return "", nil, nil, err
155 }
156
157 if cgoExportHPath != "" {
158 if err := copyFile(filepath.Join(workDir, "_cgo_export.h"), cgoExportHPath); err != nil {
159 return "", nil, nil, err
160 }
161 }
162 genGoSrcs := make([]string, 1+len(cgoSrcs))
163 genGoSrcs[0] = filepath.Join(workDir, "_cgo_gotypes.go")
164 genCSrcs := make([]string, 1+len(cgoSrcs))
165 genCSrcs[0] = filepath.Join(workDir, "_cgo_export.c")
166 for i, src := range cgoSrcs {
167 stem := strings.TrimSuffix(filepath.Base(src), ".go")
168 genGoSrcs[i+1] = filepath.Join(workDir, stem+".cgo1.go")
169 genCSrcs[i+1] = filepath.Join(workDir, stem+".cgo2.c")
170 }
171 cgoMainC := filepath.Join(workDir, "_cgo_main.c")
172
173
174 defaultCFlags := defaultCFlags(workDir)
175 combinedCFlags := combineFlags(cppFlags, hdrIncludes, cFlags, defaultCFlags)
176 for _, lang := range []struct{ srcs, flags []string }{
177 {genCSrcs, combinedCFlags},
178 {cSrcs, combinedCFlags},
179 {cxxSrcs, combineFlags(cppFlags, hdrIncludes, cxxFlags, defaultCFlags)},
180 {objcSrcs, combineFlags(cppFlags, hdrIncludes, objcFlags, defaultCFlags)},
181 {objcxxSrcs, combineFlags(cppFlags, hdrIncludes, objcxxFlags, defaultCFlags)},
182 {sSrcs, nil},
183 } {
184 for _, src := range lang.srcs {
185 obj := filepath.Join(workDir, fmt.Sprintf("_x%d.o", len(cObjs)))
186 cObjs = append(cObjs, obj)
187 if err := cCompile(goenv, src, cc, lang.flags, obj); err != nil {
188 return "", nil, nil, err
189 }
190 }
191 }
192
193 mainObj := filepath.Join(workDir, "_cgo_main.o")
194 if err := cCompile(goenv, cgoMainC, cc, combinedCFlags, mainObj); err != nil {
195 return "", nil, nil, err
196 }
197
198
199 mainBin := filepath.Join(workDir, "_cgo_.o")
200 args = append([]string{cc, "-o", mainBin, mainObj}, cObjs...)
201 args = append(args, combinedLdFlags...)
202 var originalErrBuf bytes.Buffer
203 if err := goenv.runCommandToFile(os.Stdout, &originalErrBuf, args); err != nil {
204
205
206
207
208
209
210 var allowUnresolvedSymbolsLdFlag string
211 switch os.Getenv("GOOS") {
212 case "windows":
213
214
215 return "", nil, nil, err
216 case "darwin", "ios":
217 allowUnresolvedSymbolsLdFlag = "-Wl,-undefined,dynamic_lookup"
218 default:
219 allowUnresolvedSymbolsLdFlag = "-Wl,--unresolved-symbols=ignore-all"
220 }
221
222
223
224
225 if err2 := goenv.runCommandToFile(
226 os.Stdout,
227 ioutil.Discard,
228 append(args, allowUnresolvedSymbolsLdFlag),
229 ); err2 != nil {
230 os.Stderr.Write(relativizePaths(originalErrBuf.Bytes()))
231 return "", nil, nil, err
232 }
233
234
235 }
236
237 cgoImportsGo := filepath.Join(workDir, "_cgo_imports.go")
238 args = goenv.goTool("cgo", "-dynpackage", packageName, "-dynimport", mainBin, "-dynout", cgoImportsGo)
239 if err := goenv.runCommand(args); err != nil {
240 return "", nil, nil, err
241 }
242 genGoSrcs = append(genGoSrcs, cgoImportsGo)
243
244
245
246 goBases, err := gatherSrcs(workDir, goSrcs)
247 if err != nil {
248 return "", nil, nil, err
249 }
250
251 allGoSrcs = make([]string, len(goSrcs)+len(genGoSrcs))
252 for i := range goSrcs {
253 allGoSrcs[i] = filepath.Join(workDir, goBases[i])
254 }
255 copy(allGoSrcs[len(goSrcs):], genGoSrcs)
256 return workDir, allGoSrcs, cObjs, nil
257 }
258
259
260
261
262
263
264 func compileCSources(goenv *env, cSrcs, cxxSrcs, objcSrcs, objcxxSrcs, sSrcs, hSrcs []string, cc string, cppFlags, cFlags, cxxFlags, objcFlags, objcxxFlags []string) (cObjs []string, err error) {
265 workDir, cleanup, err := goenv.workDir()
266 if err != nil {
267 return nil, err
268 }
269 defer cleanup()
270
271 hdrDirs := map[string]bool{}
272 var hdrIncludes []string
273 for _, hdr := range hSrcs {
274 hdrDir := filepath.Dir(hdr)
275 if !hdrDirs[hdrDir] {
276 hdrDirs[hdrDir] = true
277 hdrIncludes = append(hdrIncludes, "-iquote", hdrDir)
278 }
279 }
280
281 defaultCFlags := defaultCFlags(workDir)
282 for _, lang := range []struct{ srcs, flags []string }{
283 {cSrcs, combineFlags(cppFlags, hdrIncludes, cFlags, defaultCFlags)},
284 {cxxSrcs, combineFlags(cppFlags, hdrIncludes, cxxFlags, defaultCFlags)},
285 {objcSrcs, combineFlags(cppFlags, hdrIncludes, objcFlags, defaultCFlags)},
286 {objcxxSrcs, combineFlags(cppFlags, hdrIncludes, objcxxFlags, defaultCFlags)},
287 {sSrcs, nil},
288 } {
289 for _, src := range lang.srcs {
290 obj := filepath.Join(workDir, fmt.Sprintf("_x%d.o", len(cObjs)))
291 cObjs = append(cObjs, obj)
292 if err := cCompile(goenv, src, cc, lang.flags, obj); err != nil {
293 return nil, err
294 }
295 }
296 }
297 return cObjs, nil
298 }
299
300 func combineFlags(lists ...[]string) []string {
301 n := 0
302 for _, list := range lists {
303 n += len(list)
304 }
305 flags := make([]string, 0, n)
306 for _, list := range lists {
307 flags = append(flags, list...)
308 }
309 return flags
310 }
311
312 func cCompile(goenv *env, src, cc string, flags []string, out string) error {
313 args := []string{cc}
314 args = append(args, flags...)
315 args = append(args, "-c", src, "-o", out)
316 return goenv.runCommand(args)
317 }
318
319 func defaultCFlags(workDir string) []string {
320 flags := []string{
321 "-fdebug-prefix-map=" + abs(".") + "=.",
322 "-fdebug-prefix-map=" + workDir + "=.",
323 }
324 goos, goarch := os.Getenv("GOOS"), os.Getenv("GOARCH")
325 switch {
326 case goos == "darwin" || goos == "ios":
327 return flags
328 case goos == "windows" && goarch == "amd64":
329 return append(flags, "-mthreads")
330 default:
331 return append(flags, "-pthread")
332 }
333 }
334
335 func defaultLdFlags() []string {
336 goos, goarch := os.Getenv("GOOS"), os.Getenv("GOARCH")
337 switch {
338 case goos == "android":
339 return []string{"-llog", "-ldl"}
340 case goos == "darwin" || goos == "ios":
341 return nil
342 case goos == "windows" && goarch == "amd64":
343 return []string{"-mthreads"}
344 default:
345 return []string{"-pthread"}
346 }
347 }
348
349
350
351
352
353 func gatherSrcs(dir string, srcs []string) ([]string, error) {
354 copiedBases := make([]string, len(srcs))
355 for i, src := range srcs {
356 base := filepath.Base(src)
357 ext := filepath.Ext(base)
358 stem := base[:len(base)-len(ext)]
359 var err error
360 for j := 1; j < 10000; j++ {
361 if err = copyOrLinkFile(src, filepath.Join(dir, base)); err == nil {
362 break
363 } else if !os.IsExist(err) {
364 return nil, err
365 } else {
366 base = fmt.Sprintf("%s_%d%s", stem, j, ext)
367 }
368 }
369 if err != nil {
370 return nil, fmt.Errorf("could not find unique name for file %s", src)
371 }
372 copiedBases[i] = base
373 }
374 return copiedBases, nil
375 }
376
377 func bazelExecRoot() (string, error) {
378
379
380
381
382 cwd, err := os.Getwd()
383 if err != nil {
384 return "", err
385 }
386 return filepath.Dir(cwd), nil
387 }
388
389 type cgoError []string
390
391 func (e cgoError) Error() string {
392 b := &bytes.Buffer{}
393 fmt.Fprint(b, "CC is not set and files need to be processed with cgo:\n")
394 for _, f := range e {
395 fmt.Fprintf(b, "\t%s\n", f)
396 }
397 fmt.Fprintf(b, "Ensure that 'cgo = True' is set and the C/C++ toolchain is configured.")
398 return b.String()
399 }
400
401 func copyFile(inPath, outPath string) error {
402 inFile, err := os.Open(inPath)
403 if err != nil {
404 return err
405 }
406 defer inFile.Close()
407 outFile, err := os.OpenFile(outPath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0666)
408 if err != nil {
409 return err
410 }
411 defer outFile.Close()
412 _, err = io.Copy(outFile, inFile)
413 return err
414 }
415
416 func linkFile(inPath, outPath string) error {
417 inPath, err := filepath.Abs(inPath)
418 if err != nil {
419 return err
420 }
421 return os.Symlink(inPath, outPath)
422 }
423
424 func copyOrLinkFile(inPath, outPath string) error {
425 if runtime.GOOS == "windows" {
426 return copyFile(inPath, outPath)
427 } else {
428 return linkFile(inPath, outPath)
429 }
430 }
431
View as plain text