123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383 |
- // Copyright 2009 The Go Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- // Cgo; see gmp.go for an overview.
- // TODO(rsc):
- // Emit correct line number annotations.
- // Make 6g understand the annotations.
- package main
- import (
- "crypto/md5"
- "flag"
- "fmt"
- "go/ast"
- "go/printer"
- "go/token"
- "io"
- "os"
- "path/filepath"
- "reflect"
- "runtime"
- "sort"
- "strings"
- )
- // A Package collects information about the package we're going to write.
- type Package struct {
- PackageName string // name of package
- PackagePath string
- PtrSize int64
- IntSize int64
- GccOptions []string
- CgoFlags map[string][]string // #cgo flags (CFLAGS, LDFLAGS)
- Written map[string]bool
- Name map[string]*Name // accumulated Name from Files
- ExpFunc []*ExpFunc // accumulated ExpFunc from Files
- Decl []ast.Decl
- GoFiles []string // list of Go files
- GccFiles []string // list of gcc output files
- Preamble string // collected preamble for _cgo_export.h
- }
- // A File collects information about a single Go input file.
- type File struct {
- AST *ast.File // parsed AST
- Comments []*ast.CommentGroup // comments from file
- Package string // Package name
- Preamble string // C preamble (doc comment on import "C")
- Ref []*Ref // all references to C.xxx in AST
- ExpFunc []*ExpFunc // exported functions for this file
- Name map[string]*Name // map from Go name to Name
- }
- func nameKeys(m map[string]*Name) []string {
- var ks []string
- for k := range m {
- ks = append(ks, k)
- }
- sort.Strings(ks)
- return ks
- }
- // A Ref refers to an expression of the form C.xxx in the AST.
- type Ref struct {
- Name *Name
- Expr *ast.Expr
- Context string // "type", "expr", "call", or "call2"
- }
- func (r *Ref) Pos() token.Pos {
- return (*r.Expr).Pos()
- }
- // A Name collects information about C.xxx.
- type Name struct {
- Go string // name used in Go referring to package C
- Mangle string // name used in generated Go
- C string // name used in C
- Define string // #define expansion
- Kind string // "const", "type", "var", "fpvar", "func", "not-type"
- Type *Type // the type of xxx
- FuncType *FuncType
- AddError bool
- Const string // constant definition
- }
- // IsVar returns true if Kind is either "var" or "fpvar"
- func (n *Name) IsVar() bool {
- return n.Kind == "var" || n.Kind == "fpvar"
- }
- // A ExpFunc is an exported function, callable from C.
- // Such functions are identified in the Go input file
- // by doc comments containing the line //export ExpName
- type ExpFunc struct {
- Func *ast.FuncDecl
- ExpName string // name to use from C
- }
- // A TypeRepr contains the string representation of a type.
- type TypeRepr struct {
- Repr string
- FormatArgs []interface{}
- }
- // A Type collects information about a type in both the C and Go worlds.
- type Type struct {
- Size int64
- Align int64
- C *TypeRepr
- Go ast.Expr
- EnumValues map[string]int64
- Typedef string
- }
- // A FuncType collects information about a function type in both the C and Go worlds.
- type FuncType struct {
- Params []*Type
- Result *Type
- Go *ast.FuncType
- }
- func usage() {
- fmt.Fprint(os.Stderr, "usage: cgo -- [compiler options] file.go ...\n")
- flag.PrintDefaults()
- os.Exit(2)
- }
- var ptrSizeMap = map[string]int64{
- "386": 4,
- "alpha": 8,
- "amd64": 8,
- "arm": 4,
- "arm64": 8,
- "m68k": 4,
- "mipso32": 4,
- "mipsn32": 4,
- "mipso64": 8,
- "mipsn64": 8,
- "ppc": 4,
- "ppc64": 8,
- "ppc64le": 8,
- "s390": 4,
- "s390x": 8,
- "sparc": 4,
- "sparc64": 8,
- }
- var intSizeMap = map[string]int64{
- "386": 4,
- "alpha": 8,
- "amd64": 8,
- "arm": 4,
- "arm64": 8,
- "m68k": 4,
- "mipso32": 4,
- "mipsn32": 4,
- "mipso64": 8,
- "mipsn64": 8,
- "ppc": 4,
- "ppc64": 8,
- "ppc64le": 8,
- "s390": 4,
- "s390x": 8,
- "sparc": 4,
- "sparc64": 8,
- }
- var cPrefix string
- var fset = token.NewFileSet()
- var dynobj = flag.String("dynimport", "", "if non-empty, print dynamic import data for that file")
- var dynout = flag.String("dynout", "", "write -dynobj output to this file")
- var dynlinker = flag.Bool("dynlinker", false, "record dynamic linker information in dynimport mode")
- // These flags are for bootstrapping a new Go implementation,
- // to generate Go and C headers that match the data layout and
- // constant values used in the host's C libraries and system calls.
- var godefs = flag.Bool("godefs", false, "for bootstrap: write Go definitions for C file to standard output")
- var cdefs = flag.Bool("cdefs", false, "for bootstrap: write C definitions for C file to standard output")
- var objDir = flag.String("objdir", "", "object directory")
- var gccgo = flag.Bool("gccgo", false, "generate files for use with gccgo")
- var gccgoprefix = flag.String("gccgoprefix", "", "-fgo-prefix option used with gccgo")
- var gccgopkgpath = flag.String("gccgopkgpath", "", "-fgo-pkgpath option used with gccgo")
- var importRuntimeCgo = flag.Bool("import_runtime_cgo", true, "import runtime/cgo in generated code")
- var importSyscall = flag.Bool("import_syscall", true, "import syscall in generated code")
- var goarch, goos string
- func main() {
- flag.Usage = usage
- flag.Parse()
- if *dynobj != "" {
- // cgo -dynimport is essentially a separate helper command
- // built into the cgo binary. It scans a gcc-produced executable
- // and dumps information about the imported symbols and the
- // imported libraries. The 'go build' rules for cgo prepare an
- // appropriate executable and then use its import information
- // instead of needing to make the linkers duplicate all the
- // specialized knowledge gcc has about where to look for imported
- // symbols and which ones to use.
- dynimport(*dynobj)
- return
- }
- if *godefs && *cdefs {
- fmt.Fprintf(os.Stderr, "cgo: cannot use -cdefs and -godefs together\n")
- os.Exit(2)
- }
- if *godefs || *cdefs {
- // Generating definitions pulled from header files,
- // to be checked into Go repositories.
- // Line numbers are just noise.
- conf.Mode &^= printer.SourcePos
- }
- args := flag.Args()
- if len(args) < 1 {
- usage()
- }
- // Find first arg that looks like a go file and assume everything before
- // that are options to pass to gcc.
- var i int
- for i = len(args); i > 0; i-- {
- if !strings.HasSuffix(args[i-1], ".go") {
- break
- }
- }
- if i == len(args) {
- usage()
- }
- goFiles := args[i:]
- p := newPackage(args[:i])
- // Record CGO_LDFLAGS from the environment for external linking.
- if ldflags := os.Getenv("CGO_LDFLAGS"); ldflags != "" {
- args, err := splitQuoted(ldflags)
- if err != nil {
- fatalf("bad CGO_LDFLAGS: %q (%s)", ldflags, err)
- }
- p.addToFlag("LDFLAGS", args)
- }
- // Need a unique prefix for the global C symbols that
- // we use to coordinate between gcc and ourselves.
- // We already put _cgo_ at the beginning, so the main
- // concern is other cgo wrappers for the same functions.
- // Use the beginning of the md5 of the input to disambiguate.
- h := md5.New()
- for _, input := range goFiles {
- f, err := os.Open(input)
- if err != nil {
- fatalf("%s", err)
- }
- io.Copy(h, f)
- f.Close()
- }
- cPrefix = fmt.Sprintf("_%x", h.Sum(nil)[0:6])
- fs := make([]*File, len(goFiles))
- for i, input := range goFiles {
- f := new(File)
- f.ReadGo(input)
- f.DiscardCgoDirectives()
- fs[i] = f
- }
- if *objDir == "" {
- // make sure that _obj directory exists, so that we can write
- // all the output files there.
- os.Mkdir("_obj", 0777)
- *objDir = "_obj"
- }
- *objDir += string(filepath.Separator)
- for i, input := range goFiles {
- f := fs[i]
- p.Translate(f)
- for _, cref := range f.Ref {
- switch cref.Context {
- case "call", "call2":
- if cref.Name.Kind != "type" {
- break
- }
- *cref.Expr = cref.Name.Type.Go
- }
- }
- if nerrors > 0 {
- os.Exit(2)
- }
- pkg := f.Package
- if dir := os.Getenv("CGOPKGPATH"); dir != "" {
- pkg = filepath.Join(dir, pkg)
- }
- p.PackagePath = pkg
- p.Record(f)
- if *godefs {
- os.Stdout.WriteString(p.godefs(f, input))
- } else if *cdefs {
- os.Stdout.WriteString(p.cdefs(f, input))
- } else {
- p.writeOutput(f, input)
- }
- }
- if !*godefs && !*cdefs {
- p.writeDefs()
- }
- if nerrors > 0 {
- os.Exit(2)
- }
- }
- // newPackage returns a new Package that will invoke
- // gcc with the additional arguments specified in args.
- func newPackage(args []string) *Package {
- goarch = runtime.GOARCH
- if s := os.Getenv("GOARCH"); s != "" {
- goarch = s
- }
- goos = runtime.GOOS
- if s := os.Getenv("GOOS"); s != "" {
- goos = s
- }
- ptrSize := ptrSizeMap[goarch]
- if ptrSize == 0 {
- fatalf("unknown ptrSize for $GOARCH %q", goarch)
- }
- intSize := intSizeMap[goarch]
- if intSize == 0 {
- fatalf("unknown intSize for $GOARCH %q", goarch)
- }
- // Reset locale variables so gcc emits English errors [sic].
- os.Setenv("LANG", "en_US.UTF-8")
- os.Setenv("LC_ALL", "C")
- p := &Package{
- PtrSize: ptrSize,
- IntSize: intSize,
- CgoFlags: make(map[string][]string),
- Written: make(map[string]bool),
- }
- p.addToFlag("CFLAGS", args)
- return p
- }
- // Record what needs to be recorded about f.
- func (p *Package) Record(f *File) {
- if p.PackageName == "" {
- p.PackageName = f.Package
- } else if p.PackageName != f.Package {
- error_(token.NoPos, "inconsistent package names: %s, %s", p.PackageName, f.Package)
- }
- if p.Name == nil {
- p.Name = f.Name
- } else {
- for k, v := range f.Name {
- if p.Name[k] == nil {
- p.Name[k] = v
- } else if !reflect.DeepEqual(p.Name[k], v) {
- error_(token.NoPos, "inconsistent definitions for C.%s", fixGo(k))
- }
- }
- }
- if f.ExpFunc != nil {
- p.ExpFunc = append(p.ExpFunc, f.ExpFunc...)
- p.Preamble += "\n" + f.Preamble
- }
- p.Decl = append(p.Decl, f.AST.Decls...)
- }
|