Source file
src/cmd/cgo/main.go
Documentation: cmd/cgo
1
2
3
4
5
6
7
8
9
10
11 package main
12
13 import (
14 "crypto/md5"
15 "flag"
16 "fmt"
17 "go/ast"
18 "go/printer"
19 "go/token"
20 "internal/buildcfg"
21 "io"
22 "io/ioutil"
23 "os"
24 "os/exec"
25 "path/filepath"
26 "reflect"
27 "runtime"
28 "sort"
29 "strings"
30
31 "cmd/internal/edit"
32 "cmd/internal/objabi"
33 )
34
35
36 type Package struct {
37 PackageName string
38 PackagePath string
39 PtrSize int64
40 IntSize int64
41 GccOptions []string
42 GccIsClang bool
43 CgoFlags map[string][]string
44 Written map[string]bool
45 Name map[string]*Name
46 ExpFunc []*ExpFunc
47 Decl []ast.Decl
48 GoFiles []string
49 GccFiles []string
50 Preamble string
51 typedefs map[string]bool
52 typedefList []typedefInfo
53 }
54
55
56
57 type typedefInfo struct {
58 typedef string
59 pos token.Pos
60 }
61
62
63 type File struct {
64 AST *ast.File
65 Comments []*ast.CommentGroup
66 Package string
67 Preamble string
68 Ref []*Ref
69 Calls []*Call
70 ExpFunc []*ExpFunc
71 Name map[string]*Name
72 NamePos map[*Name]token.Pos
73 Edit *edit.Buffer
74 }
75
76 func (f *File) offset(p token.Pos) int {
77 return fset.Position(p).Offset
78 }
79
80 func nameKeys(m map[string]*Name) []string {
81 var ks []string
82 for k := range m {
83 ks = append(ks, k)
84 }
85 sort.Strings(ks)
86 return ks
87 }
88
89
90 type Call struct {
91 Call *ast.CallExpr
92 Deferred bool
93 Done bool
94 }
95
96
97 type Ref struct {
98 Name *Name
99 Expr *ast.Expr
100 Context astContext
101 Done bool
102 }
103
104 func (r *Ref) Pos() token.Pos {
105 return (*r.Expr).Pos()
106 }
107
108 var nameKinds = []string{"iconst", "fconst", "sconst", "type", "var", "fpvar", "func", "macro", "not-type"}
109
110
111 type Name struct {
112 Go string
113 Mangle string
114 C string
115 Define string
116 Kind string
117 Type *Type
118 FuncType *FuncType
119 AddError bool
120 Const string
121 }
122
123
124 func (n *Name) IsVar() bool {
125 return n.Kind == "var" || n.Kind == "fpvar"
126 }
127
128
129 func (n *Name) IsConst() bool {
130 return strings.HasSuffix(n.Kind, "const")
131 }
132
133
134
135
136 type ExpFunc struct {
137 Func *ast.FuncDecl
138 ExpName string
139 Doc string
140 }
141
142
143 type TypeRepr struct {
144 Repr string
145 FormatArgs []interface{}
146 }
147
148
149 type Type struct {
150 Size int64
151 Align int64
152 C *TypeRepr
153 Go ast.Expr
154 EnumValues map[string]int64
155 Typedef string
156 BadPointer bool
157 NotInHeap bool
158 }
159
160
161 type FuncType struct {
162 Params []*Type
163 Result *Type
164 Go *ast.FuncType
165 }
166
167 func usage() {
168 fmt.Fprint(os.Stderr, "usage: cgo -- [compiler options] file.go ...\n")
169 flag.PrintDefaults()
170 os.Exit(2)
171 }
172
173 var ptrSizeMap = map[string]int64{
174 "386": 4,
175 "alpha": 8,
176 "amd64": 8,
177 "arm": 4,
178 "arm64": 8,
179 "m68k": 4,
180 "mips": 4,
181 "mipsle": 4,
182 "mips64": 8,
183 "mips64le": 8,
184 "nios2": 4,
185 "ppc": 4,
186 "ppc64": 8,
187 "ppc64le": 8,
188 "riscv": 4,
189 "riscv64": 8,
190 "s390": 4,
191 "s390x": 8,
192 "sh": 4,
193 "shbe": 4,
194 "sparc": 4,
195 "sparc64": 8,
196 }
197
198 var intSizeMap = map[string]int64{
199 "386": 4,
200 "alpha": 8,
201 "amd64": 8,
202 "arm": 4,
203 "arm64": 8,
204 "m68k": 4,
205 "mips": 4,
206 "mipsle": 4,
207 "mips64": 8,
208 "mips64le": 8,
209 "nios2": 4,
210 "ppc": 4,
211 "ppc64": 8,
212 "ppc64le": 8,
213 "riscv": 4,
214 "riscv64": 8,
215 "s390": 4,
216 "s390x": 8,
217 "sh": 4,
218 "shbe": 4,
219 "sparc": 4,
220 "sparc64": 8,
221 }
222
223 var cPrefix string
224
225 var fset = token.NewFileSet()
226
227 var dynobj = flag.String("dynimport", "", "if non-empty, print dynamic import data for that file")
228 var dynout = flag.String("dynout", "", "write -dynimport output to this file")
229 var dynpackage = flag.String("dynpackage", "main", "set Go package for -dynimport output")
230 var dynlinker = flag.Bool("dynlinker", false, "record dynamic linker information in -dynimport mode")
231
232
233
234
235 var godefs = flag.Bool("godefs", false, "for bootstrap: write Go definitions for C file to standard output")
236
237 var srcDir = flag.String("srcdir", "", "source directory")
238 var objDir = flag.String("objdir", "", "object directory")
239 var importPath = flag.String("importpath", "", "import path of package being built (for comments in generated files)")
240 var exportHeader = flag.String("exportheader", "", "where to write export header if any exported functions")
241
242 var gccgo = flag.Bool("gccgo", false, "generate files for use with gccgo")
243 var gccgoprefix = flag.String("gccgoprefix", "", "-fgo-prefix option used with gccgo")
244 var gccgopkgpath = flag.String("gccgopkgpath", "", "-fgo-pkgpath option used with gccgo")
245 var gccgoMangler func(string) string
246 var importRuntimeCgo = flag.Bool("import_runtime_cgo", true, "import runtime/cgo in generated code")
247 var importSyscall = flag.Bool("import_syscall", true, "import syscall in generated code")
248 var trimpath = flag.String("trimpath", "", "applies supplied rewrites or trims prefixes to recorded source file paths")
249
250 var goarch, goos, gomips, gomips64 string
251
252 func main() {
253 objabi.AddVersionFlag()
254 flag.Usage = usage
255 flag.Parse()
256
257 if *dynobj != "" {
258
259
260
261
262
263
264
265
266 dynimport(*dynobj)
267 return
268 }
269
270 if *godefs {
271
272
273
274 conf.Mode &^= printer.SourcePos
275 }
276
277 args := flag.Args()
278 if len(args) < 1 {
279 usage()
280 }
281
282
283
284 var i int
285 for i = len(args); i > 0; i-- {
286 if !strings.HasSuffix(args[i-1], ".go") {
287 break
288 }
289 }
290 if i == len(args) {
291 usage()
292 }
293
294 goFiles := args[i:]
295
296 for _, arg := range args[:i] {
297 if arg == "-fsanitize=thread" {
298 tsanProlog = yesTsanProlog
299 }
300 if arg == "-fsanitize=memory" {
301 msanProlog = yesMsanProlog
302 }
303 }
304
305 p := newPackage(args[:i])
306
307
308 gccName := p.gccBaseCmd()[0]
309 _, err := exec.LookPath(gccName)
310 if err != nil {
311 fatalf("C compiler %q not found: %v", gccName, err)
312 os.Exit(2)
313 }
314
315
316 if ldflags := os.Getenv("CGO_LDFLAGS"); ldflags != "" {
317 args, err := splitQuoted(ldflags)
318 if err != nil {
319 fatalf("bad CGO_LDFLAGS: %q (%s)", ldflags, err)
320 }
321 p.addToFlag("LDFLAGS", args)
322 }
323
324
325
326
327
328
329 h := md5.New()
330 io.WriteString(h, *importPath)
331 fs := make([]*File, len(goFiles))
332 for i, input := range goFiles {
333 if *srcDir != "" {
334 input = filepath.Join(*srcDir, input)
335 }
336
337
338
339
340 if aname, err := filepath.Abs(input); err == nil {
341 input = aname
342 }
343
344 b, err := ioutil.ReadFile(input)
345 if err != nil {
346 fatalf("%s", err)
347 }
348 if _, err = h.Write(b); err != nil {
349 fatalf("%s", err)
350 }
351
352
353 input, _ = objabi.ApplyRewrites(input, *trimpath)
354 goFiles[i] = input
355
356 f := new(File)
357 f.Edit = edit.NewBuffer(b)
358 f.ParseGo(input, b)
359 f.DiscardCgoDirectives()
360 fs[i] = f
361 }
362
363 cPrefix = fmt.Sprintf("_%x", h.Sum(nil)[0:6])
364
365 if *objDir == "" {
366
367
368 os.Mkdir("_obj", 0777)
369 *objDir = "_obj"
370 }
371 *objDir += string(filepath.Separator)
372
373 for i, input := range goFiles {
374 f := fs[i]
375 p.Translate(f)
376 for _, cref := range f.Ref {
377 switch cref.Context {
378 case ctxCall, ctxCall2:
379 if cref.Name.Kind != "type" {
380 break
381 }
382 old := *cref.Expr
383 *cref.Expr = cref.Name.Type.Go
384 f.Edit.Replace(f.offset(old.Pos()), f.offset(old.End()), gofmt(cref.Name.Type.Go))
385 }
386 }
387 if nerrors > 0 {
388 os.Exit(2)
389 }
390 p.PackagePath = f.Package
391 p.Record(f)
392 if *godefs {
393 os.Stdout.WriteString(p.godefs(f))
394 } else {
395 p.writeOutput(f, input)
396 }
397 }
398
399 if !*godefs {
400 p.writeDefs()
401 }
402 if nerrors > 0 {
403 os.Exit(2)
404 }
405 }
406
407
408
409 func newPackage(args []string) *Package {
410 goarch = runtime.GOARCH
411 if s := os.Getenv("GOARCH"); s != "" {
412 goarch = s
413 }
414 goos = runtime.GOOS
415 if s := os.Getenv("GOOS"); s != "" {
416 goos = s
417 }
418 buildcfg.Check()
419 gomips = buildcfg.GOMIPS
420 gomips64 = buildcfg.GOMIPS64
421 ptrSize := ptrSizeMap[goarch]
422 if ptrSize == 0 {
423 fatalf("unknown ptrSize for $GOARCH %q", goarch)
424 }
425 intSize := intSizeMap[goarch]
426 if intSize == 0 {
427 fatalf("unknown intSize for $GOARCH %q", goarch)
428 }
429
430
431 os.Setenv("LANG", "en_US.UTF-8")
432 os.Setenv("LC_ALL", "C")
433
434 p := &Package{
435 PtrSize: ptrSize,
436 IntSize: intSize,
437 CgoFlags: make(map[string][]string),
438 Written: make(map[string]bool),
439 }
440 p.addToFlag("CFLAGS", args)
441 return p
442 }
443
444
445 func (p *Package) Record(f *File) {
446 if p.PackageName == "" {
447 p.PackageName = f.Package
448 } else if p.PackageName != f.Package {
449 error_(token.NoPos, "inconsistent package names: %s, %s", p.PackageName, f.Package)
450 }
451
452 if p.Name == nil {
453 p.Name = f.Name
454 } else {
455 for k, v := range f.Name {
456 if p.Name[k] == nil {
457 p.Name[k] = v
458 } else if p.incompleteTypedef(p.Name[k].Type) {
459 p.Name[k] = v
460 } else if p.incompleteTypedef(v.Type) {
461
462 } else if _, ok := nameToC[k]; ok {
463
464
465
466 } else if !reflect.DeepEqual(p.Name[k], v) {
467 error_(token.NoPos, "inconsistent definitions for C.%s", fixGo(k))
468 }
469 }
470 }
471
472 if f.ExpFunc != nil {
473 p.ExpFunc = append(p.ExpFunc, f.ExpFunc...)
474 p.Preamble += "\n" + f.Preamble
475 }
476 p.Decl = append(p.Decl, f.AST.Decls...)
477 }
478
479
480
481 func (p *Package) incompleteTypedef(t *Type) bool {
482 return t == nil || (t.Size == 0 && t.Align == -1)
483 }
484
View as plain text