Home | History | Annotate | Download | only in cgo
      1 // Copyright 2009 The Go Authors.  All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 // Cgo; see gmp.go for an overview.
      6 
      7 // TODO(rsc):
      8 //	Emit correct line number annotations.
      9 //	Make gc understand the annotations.
     10 
     11 package main
     12 
     13 import (
     14 	"crypto/md5"
     15 	"flag"
     16 	"fmt"
     17 	"go/ast"
     18 	"go/printer"
     19 	"go/token"
     20 	"io"
     21 	"os"
     22 	"path/filepath"
     23 	"reflect"
     24 	"runtime"
     25 	"sort"
     26 	"strings"
     27 )
     28 
     29 // A Package collects information about the package we're going to write.
     30 type Package struct {
     31 	PackageName string // name of package
     32 	PackagePath string
     33 	PtrSize     int64
     34 	IntSize     int64
     35 	GccOptions  []string
     36 	GccIsClang  bool
     37 	CgoFlags    map[string][]string // #cgo flags (CFLAGS, LDFLAGS)
     38 	Written     map[string]bool
     39 	Name        map[string]*Name // accumulated Name from Files
     40 	ExpFunc     []*ExpFunc       // accumulated ExpFunc from Files
     41 	Decl        []ast.Decl
     42 	GoFiles     []string // list of Go files
     43 	GccFiles    []string // list of gcc output files
     44 	Preamble    string   // collected preamble for _cgo_export.h
     45 }
     46 
     47 // A File collects information about a single Go input file.
     48 type File struct {
     49 	AST      *ast.File           // parsed AST
     50 	Comments []*ast.CommentGroup // comments from file
     51 	Package  string              // Package name
     52 	Preamble string              // C preamble (doc comment on import "C")
     53 	Ref      []*Ref              // all references to C.xxx in AST
     54 	ExpFunc  []*ExpFunc          // exported functions for this file
     55 	Name     map[string]*Name    // map from Go name to Name
     56 }
     57 
     58 func nameKeys(m map[string]*Name) []string {
     59 	var ks []string
     60 	for k := range m {
     61 		ks = append(ks, k)
     62 	}
     63 	sort.Strings(ks)
     64 	return ks
     65 }
     66 
     67 // A Ref refers to an expression of the form C.xxx in the AST.
     68 type Ref struct {
     69 	Name    *Name
     70 	Expr    *ast.Expr
     71 	Context string // "type", "expr", "call", or "call2"
     72 }
     73 
     74 func (r *Ref) Pos() token.Pos {
     75 	return (*r.Expr).Pos()
     76 }
     77 
     78 // A Name collects information about C.xxx.
     79 type Name struct {
     80 	Go       string // name used in Go referring to package C
     81 	Mangle   string // name used in generated Go
     82 	C        string // name used in C
     83 	Define   string // #define expansion
     84 	Kind     string // "const", "type", "var", "fpvar", "func", "not-type"
     85 	Type     *Type  // the type of xxx
     86 	FuncType *FuncType
     87 	AddError bool
     88 	Const    string // constant definition
     89 }
     90 
     91 // IsVar reports whether Kind is either "var" or "fpvar"
     92 func (n *Name) IsVar() bool {
     93 	return n.Kind == "var" || n.Kind == "fpvar"
     94 }
     95 
     96 // A ExpFunc is an exported function, callable from C.
     97 // Such functions are identified in the Go input file
     98 // by doc comments containing the line //export ExpName
     99 type ExpFunc struct {
    100 	Func    *ast.FuncDecl
    101 	ExpName string // name to use from C
    102 	Doc     string
    103 }
    104 
    105 // A TypeRepr contains the string representation of a type.
    106 type TypeRepr struct {
    107 	Repr       string
    108 	FormatArgs []interface{}
    109 }
    110 
    111 // A Type collects information about a type in both the C and Go worlds.
    112 type Type struct {
    113 	Size       int64
    114 	Align      int64
    115 	C          *TypeRepr
    116 	Go         ast.Expr
    117 	EnumValues map[string]int64
    118 	Typedef    string
    119 }
    120 
    121 // A FuncType collects information about a function type in both the C and Go worlds.
    122 type FuncType struct {
    123 	Params []*Type
    124 	Result *Type
    125 	Go     *ast.FuncType
    126 }
    127 
    128 func usage() {
    129 	fmt.Fprint(os.Stderr, "usage: cgo -- [compiler options] file.go ...\n")
    130 	flag.PrintDefaults()
    131 	os.Exit(2)
    132 }
    133 
    134 var ptrSizeMap = map[string]int64{
    135 	"386":     4,
    136 	"amd64":   8,
    137 	"arm":     4,
    138 	"arm64":   8,
    139 	"ppc64":   8,
    140 	"ppc64le": 8,
    141 	"s390":    4,
    142 	"s390x":   8,
    143 }
    144 
    145 var intSizeMap = map[string]int64{
    146 	"386":     4,
    147 	"amd64":   8,
    148 	"arm":     4,
    149 	"arm64":   8,
    150 	"ppc64":   8,
    151 	"ppc64le": 8,
    152 	"s390":    4,
    153 	"s390x":   4,
    154 }
    155 
    156 var cPrefix string
    157 
    158 var fset = token.NewFileSet()
    159 
    160 var dynobj = flag.String("dynimport", "", "if non-empty, print dynamic import data for that file")
    161 var dynout = flag.String("dynout", "", "write -dynimport output to this file")
    162 var dynpackage = flag.String("dynpackage", "main", "set Go package for -dynimport output")
    163 var dynlinker = flag.Bool("dynlinker", false, "record dynamic linker information in -dynimport mode")
    164 
    165 // This flag is for bootstrapping a new Go implementation,
    166 // to generate Go types that match the data layout and
    167 // constant values used in the host's C libraries and system calls.
    168 var godefs = flag.Bool("godefs", false, "for bootstrap: write Go definitions for C file to standard output")
    169 
    170 var objDir = flag.String("objdir", "", "object directory")
    171 var importPath = flag.String("importpath", "", "import path of package being built (for comments in generated files)")
    172 var exportHeader = flag.String("exportheader", "", "where to write export header if any exported functions")
    173 
    174 var gccgo = flag.Bool("gccgo", false, "generate files for use with gccgo")
    175 var gccgoprefix = flag.String("gccgoprefix", "", "-fgo-prefix option used with gccgo")
    176 var gccgopkgpath = flag.String("gccgopkgpath", "", "-fgo-pkgpath option used with gccgo")
    177 var importRuntimeCgo = flag.Bool("import_runtime_cgo", true, "import runtime/cgo in generated code")
    178 var importSyscall = flag.Bool("import_syscall", true, "import syscall in generated code")
    179 var goarch, goos string
    180 
    181 func main() {
    182 	flag.Usage = usage
    183 	flag.Parse()
    184 
    185 	if *dynobj != "" {
    186 		// cgo -dynimport is essentially a separate helper command
    187 		// built into the cgo binary.  It scans a gcc-produced executable
    188 		// and dumps information about the imported symbols and the
    189 		// imported libraries.  The 'go build' rules for cgo prepare an
    190 		// appropriate executable and then use its import information
    191 		// instead of needing to make the linkers duplicate all the
    192 		// specialized knowledge gcc has about where to look for imported
    193 		// symbols and which ones to use.
    194 		dynimport(*dynobj)
    195 		return
    196 	}
    197 
    198 	if *godefs {
    199 		// Generating definitions pulled from header files,
    200 		// to be checked into Go repositories.
    201 		// Line numbers are just noise.
    202 		conf.Mode &^= printer.SourcePos
    203 	}
    204 
    205 	args := flag.Args()
    206 	if len(args) < 1 {
    207 		usage()
    208 	}
    209 
    210 	// Find first arg that looks like a go file and assume everything before
    211 	// that are options to pass to gcc.
    212 	var i int
    213 	for i = len(args); i > 0; i-- {
    214 		if !strings.HasSuffix(args[i-1], ".go") {
    215 			break
    216 		}
    217 	}
    218 	if i == len(args) {
    219 		usage()
    220 	}
    221 
    222 	goFiles := args[i:]
    223 
    224 	p := newPackage(args[:i])
    225 
    226 	// Record CGO_LDFLAGS from the environment for external linking.
    227 	if ldflags := os.Getenv("CGO_LDFLAGS"); ldflags != "" {
    228 		args, err := splitQuoted(ldflags)
    229 		if err != nil {
    230 			fatalf("bad CGO_LDFLAGS: %q (%s)", ldflags, err)
    231 		}
    232 		p.addToFlag("LDFLAGS", args)
    233 	}
    234 
    235 	// Need a unique prefix for the global C symbols that
    236 	// we use to coordinate between gcc and ourselves.
    237 	// We already put _cgo_ at the beginning, so the main
    238 	// concern is other cgo wrappers for the same functions.
    239 	// Use the beginning of the md5 of the input to disambiguate.
    240 	h := md5.New()
    241 	for _, input := range goFiles {
    242 		f, err := os.Open(input)
    243 		if err != nil {
    244 			fatalf("%s", err)
    245 		}
    246 		io.Copy(h, f)
    247 		f.Close()
    248 	}
    249 	cPrefix = fmt.Sprintf("_%x", h.Sum(nil)[0:6])
    250 
    251 	fs := make([]*File, len(goFiles))
    252 	for i, input := range goFiles {
    253 		f := new(File)
    254 		f.ReadGo(input)
    255 		f.DiscardCgoDirectives()
    256 		fs[i] = f
    257 	}
    258 
    259 	if *objDir == "" {
    260 		// make sure that _obj directory exists, so that we can write
    261 		// all the output files there.
    262 		os.Mkdir("_obj", 0777)
    263 		*objDir = "_obj"
    264 	}
    265 	*objDir += string(filepath.Separator)
    266 
    267 	for i, input := range goFiles {
    268 		f := fs[i]
    269 		p.Translate(f)
    270 		for _, cref := range f.Ref {
    271 			switch cref.Context {
    272 			case "call", "call2":
    273 				if cref.Name.Kind != "type" {
    274 					break
    275 				}
    276 				*cref.Expr = cref.Name.Type.Go
    277 			}
    278 		}
    279 		if nerrors > 0 {
    280 			os.Exit(2)
    281 		}
    282 		pkg := f.Package
    283 		if dir := os.Getenv("CGOPKGPATH"); dir != "" {
    284 			pkg = filepath.Join(dir, pkg)
    285 		}
    286 		p.PackagePath = pkg
    287 		p.Record(f)
    288 		if *godefs {
    289 			os.Stdout.WriteString(p.godefs(f, input))
    290 		} else {
    291 			p.writeOutput(f, input)
    292 		}
    293 	}
    294 
    295 	if !*godefs {
    296 		p.writeDefs()
    297 	}
    298 	if nerrors > 0 {
    299 		os.Exit(2)
    300 	}
    301 }
    302 
    303 // newPackage returns a new Package that will invoke
    304 // gcc with the additional arguments specified in args.
    305 func newPackage(args []string) *Package {
    306 	goarch = runtime.GOARCH
    307 	if s := os.Getenv("GOARCH"); s != "" {
    308 		goarch = s
    309 	}
    310 	goos = runtime.GOOS
    311 	if s := os.Getenv("GOOS"); s != "" {
    312 		goos = s
    313 	}
    314 	ptrSize := ptrSizeMap[goarch]
    315 	if ptrSize == 0 {
    316 		fatalf("unknown ptrSize for $GOARCH %q", goarch)
    317 	}
    318 	intSize := intSizeMap[goarch]
    319 	if intSize == 0 {
    320 		fatalf("unknown intSize for $GOARCH %q", goarch)
    321 	}
    322 
    323 	// Reset locale variables so gcc emits English errors [sic].
    324 	os.Setenv("LANG", "en_US.UTF-8")
    325 	os.Setenv("LC_ALL", "C")
    326 
    327 	p := &Package{
    328 		PtrSize:  ptrSize,
    329 		IntSize:  intSize,
    330 		CgoFlags: make(map[string][]string),
    331 		Written:  make(map[string]bool),
    332 	}
    333 	p.addToFlag("CFLAGS", args)
    334 	return p
    335 }
    336 
    337 // Record what needs to be recorded about f.
    338 func (p *Package) Record(f *File) {
    339 	if p.PackageName == "" {
    340 		p.PackageName = f.Package
    341 	} else if p.PackageName != f.Package {
    342 		error_(token.NoPos, "inconsistent package names: %s, %s", p.PackageName, f.Package)
    343 	}
    344 
    345 	if p.Name == nil {
    346 		p.Name = f.Name
    347 	} else {
    348 		for k, v := range f.Name {
    349 			if p.Name[k] == nil {
    350 				p.Name[k] = v
    351 			} else if !reflect.DeepEqual(p.Name[k], v) {
    352 				error_(token.NoPos, "inconsistent definitions for C.%s", fixGo(k))
    353 			}
    354 		}
    355 	}
    356 
    357 	if f.ExpFunc != nil {
    358 		p.ExpFunc = append(p.ExpFunc, f.ExpFunc...)
    359 		p.Preamble += "\n" + f.Preamble
    360 	}
    361 	p.Decl = append(p.Decl, f.AST.Decls...)
    362 }
    363