summaryrefslogtreecommitdiff
path: root/src/cmd/cgo/main.go
blob: 070146c9acb2e955c6c5b8a3a34db924eb7b604c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
// 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 (
	"fmt"
	"go/ast"
	"os"
	"strings"
)

func usage() { fmt.Fprint(os.Stderr, "usage: cgo [compiler options] file.go ...\n") }

var ptrSizeMap = map[string]int64{
	"386":   4,
	"amd64": 8,
	"arm":   4,
}

var expandName = map[string]string{
	"schar":     "signed char",
	"uchar":     "unsigned char",
	"ushort":    "unsigned short",
	"uint":      "unsigned int",
	"ulong":     "unsigned long",
	"longlong":  "long long",
	"ulonglong": "unsigned long long",
}

func main() {
	args := os.Args
	if len(args) < 2 {
		usage()
		os.Exit(2)
	}

	// 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) - 1; i > 0; i-- {
		if !strings.HasSuffix(args[i], ".go") {
			break
		}
	}

	i += 1

	gccOptions, goFiles := args[1:i], args[i:]

	arch := os.Getenv("GOARCH")
	if arch == "" {
		fatal("$GOARCH is not set")
	}
	ptrSize, ok := ptrSizeMap[arch]
	if !ok {
		fatal("unknown architecture %s", arch)
	}

	// Clear locale variables so gcc emits English errors [sic].
	os.Setenv("LANG", "en_US.UTF-8")
	os.Setenv("LC_ALL", "C")
	os.Setenv("LC_CTYPE", "C")

	p := new(Prog)

	p.PtrSize = ptrSize
	p.GccOptions = gccOptions
	p.Vardef = make(map[string]*Type)
	p.Funcdef = make(map[string]*FuncType)
	p.Enumdef = make(map[string]int64)
	p.Constdef = make(map[string]string)
	p.OutDefs = make(map[string]bool)

	for _, input := range goFiles {
		// Reset p.Preamble so that we don't end up with conflicting headers / defines
		p.Preamble = builtinProlog
		openProg(input, p)
		for _, cref := range p.Crefs {
			// Convert C.ulong to C.unsigned long, etc.
			if expand, ok := expandName[cref.Name]; ok {
				cref.Name = expand
			}
		}
		p.loadDebugInfo()
		for _, cref := range p.Crefs {
			switch cref.Context {
			case "const":
				// This came from a #define and we'll output it later.
				*cref.Expr = ast.NewIdent(cref.Name)
				break
			case "call":
				if !cref.TypeName {
					// Is an actual function call.
					pos := (*cref.Expr).Pos()
					*cref.Expr = &ast.Ident{Position: pos, Obj: ast.NewObj(ast.Err, pos, "_C_"+cref.Name)}
					p.Funcdef[cref.Name] = cref.FuncType
					break
				}
				*cref.Expr = cref.Type.Go
			case "expr":
				if cref.TypeName {
					error((*cref.Expr).Pos(), "type C.%s used as expression", cref.Name)
				}
				// If the expression refers to an enumerated value, then
				// place the identifier for the value and add it to Enumdef so
				// it will be declared as a constant in the later stage.
				if cref.Type.EnumValues != nil {
					*cref.Expr = ast.NewIdent(cref.Name)
					p.Enumdef[cref.Name] = cref.Type.EnumValues[cref.Name]
					break
				}
				// Reference to C variable.
				// We declare a pointer and arrange to have it filled in.
				*cref.Expr = &ast.StarExpr{X: ast.NewIdent("_C_" + cref.Name)}
				p.Vardef[cref.Name] = cref.Type
			case "type":
				if !cref.TypeName {
					error((*cref.Expr).Pos(), "expression C.%s used as type", cref.Name)
				}
				*cref.Expr = cref.Type.Go
			}
		}
		if nerrors > 0 {
			os.Exit(2)
		}
		pkg := p.Package
		if dir := os.Getenv("CGOPKGPATH"); dir != "" {
			pkg = dir + "/" + pkg
		}
		p.PackagePath = pkg
		p.writeOutput(input)
	}

	p.writeDefs()
}