1
0
mirror of https://github.com/golang/go synced 2024-11-22 01:14:40 -07:00

cgo: define CGO_CFLAGS and CGO_LDFLAGS in Go files

R=rsc, binet
CC=golang-dev
https://golang.org/cl/3921043
This commit is contained in:
Gustavo Niemeyer 2011-02-01 08:44:18 -05:00 committed by Russ Cox
parent 0e47460915
commit 4fd867b283
5 changed files with 150 additions and 10 deletions

View File

@ -48,7 +48,7 @@ coverage:
6cov -g $(shell pwd) $O.out | grep -v '_test\.go:' 6cov -g $(shell pwd) $O.out | grep -v '_test\.go:'
CLEANFILES+=*.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* CLEANFILES+=*.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.*
CLEANFILES+=_cgo_.c _cgo_import.c _cgo_main.c CLEANFILES+=_cgo_.c _cgo_import.c _cgo_main.c _cgo_flags _cgo_run
CLEANFILES+=*.so _obj _test _testmain.go *.exe CLEANFILES+=*.so _obj _test _testmain.go *.exe
test: test:
@ -112,11 +112,21 @@ dir:
# #
ifdef CGOFILES ifdef CGOFILES
_cgo_defun.c: $(CGOFILES) _cgo_run: $(CGOFILES)
@touch _cgo_run
CGOPKGPATH=$(dir) cgo -- $(CGO_CFLAGS) $(CGOFILES) CGOPKGPATH=$(dir) cgo -- $(CGO_CFLAGS) $(CGOFILES)
# _CGO_CFLAGS and _CGO_LDFLAGS are defined via the evaluation of _cgo_flags.
# The include happens before the commands in the recipe run,
# so it cannot be done in the same recipe that runs cgo.
_cgo_flags: _cgo_run
$(eval include _cgo_flags)
# Include any previous flags in case cgo files are up to date.
-include _cgo_flags
# Ugly but necessary - cgo writes these files too. # Ugly but necessary - cgo writes these files too.
_cgo_gotypes.go _cgo_export.c _cgo_export.h _cgo_main.c: _cgo_defun.c _cgo_gotypes.go _cgo_export.c _cgo_export.h _cgo_main.c _cgo_defun.c: _cgo_flags
@true @true
%.cgo1.go %.cgo2.c: _cgo_defun.c %.cgo1.go %.cgo2.c: _cgo_defun.c
@ -125,7 +135,7 @@ endif
# Compile rules for gcc source files. # Compile rules for gcc source files.
%.o: %.c %.o: %.c
$(HOST_CC) $(_CGO_CFLAGS_$(GOARCH)) -g -fPIC -O2 -o $@ -c $(CGO_CFLAGS) $*.c $(HOST_CC) $(_CGO_CFLAGS_$(GOARCH)) -g -fPIC -O2 -o $@ -c $(CGO_CFLAGS) $(_CGO_CFLAGS) $*.c
# To find out which symbols are needed from external libraries # To find out which symbols are needed from external libraries
# and which libraries are needed, we build a simple a.out that # and which libraries are needed, we build a simple a.out that
@ -136,10 +146,10 @@ endif
# by Go code. That's crosscall2 and any exported symbols. # by Go code. That's crosscall2 and any exported symbols.
_cgo_main.o: _cgo_main.c _cgo_main.o: _cgo_main.c
$(HOST_CC) $(_CGO_CFLAGS_$(GOARCH)) -g -fPIC -O2 -o $@ -c $(CGO_CFLAGS) _cgo_main.c $(HOST_CC) $(_CGO_CFLAGS_$(GOARCH)) -g -fPIC -O2 -o $@ -c $(CGO_CFLAGS) $(_CGO_CFLAGS) _cgo_main.c
_cgo1_.o: _cgo_main.o $(CGO_OFILES) _cgo1_.o: _cgo_main.o $(CGO_OFILES)
$(HOST_CC) $(_CGO_CFLAGS_$(GOARCH)) -g -fPIC -O2 -o $@ $^ $(CGO_LDFLAGS) $(HOST_CC) $(_CGO_CFLAGS_$(GOARCH)) -g -fPIC -O2 -o $@ $^ $(CGO_LDFLAGS) $(_CGO_LDFLAGS)
_cgo_import.c: _cgo1_.o _cgo_import.c: _cgo1_.o
cgo -dynimport _cgo1_.o >_$@ && mv -f _$@ $@ cgo -dynimport _cgo1_.o >_$@ && mv -f _$@ $@

View File

@ -23,6 +23,15 @@ the package. For example:
// #include <errno.h> // #include <errno.h>
import "C" import "C"
CFLAGS and LDFLAGS may be defined with pseudo #cgo directives
within these comments to tweak the behavior of gcc. Values defined
in multiple directives are concatenated together. For example:
// #cgo CFLAGS: -DPNG_DEBUG=1
// #cgo LDFLAGS: -lpng
// #include <png.h>
import "C"
C identifiers or field names that are keywords in Go can be C identifiers or field names that are keywords in Go can be
accessed by prefixing them with an underscore: if x points at accessed by prefixing them with an underscore: if x points at
a C struct with a field named "type", x._type accesses the field. a C struct with a field named "type", x._type accesses the field.

View File

@ -21,6 +21,7 @@ import (
"os" "os"
"strconv" "strconv"
"strings" "strings"
"unicode"
) )
var debugDefine = flag.Bool("debug-define", false, "print relevant #defines") var debugDefine = flag.Bool("debug-define", false, "print relevant #defines")
@ -59,6 +60,107 @@ func cname(s string) string {
return s return s
} }
// ParseFlags extracts #cgo CFLAGS and LDFLAGS options from the file
// preamble. Multiple occurrences are concatenated with a separating space,
// even across files.
func (p *Package) ParseFlags(f *File, srcfile string) {
linesIn := strings.Split(f.Preamble, "\n", -1)
linesOut := make([]string, 0, len(linesIn))
for _, line := range linesIn {
l := strings.TrimSpace(line)
if len(l) < 5 || l[:4] != "#cgo" || !unicode.IsSpace(int(l[4])) {
linesOut = append(linesOut, line)
continue
}
l = strings.TrimSpace(l[4:])
fields := strings.Split(l, ":", 2)
if len(fields) != 2 {
fatal("%s: bad #cgo line: %s", srcfile, line)
}
k := fields[0]
v := strings.TrimSpace(fields[1])
if k != "CFLAGS" && k != "LDFLAGS" {
fatal("%s: unsupported #cgo option %s", srcfile, k)
}
args, err := splitQuoted(v)
if err != nil {
fatal("%s: bad #cgo option %s: %s", srcfile, k, err.String())
}
if oldv, ok := p.CgoFlags[k]; ok {
p.CgoFlags[k] = oldv + " " + v
} else {
p.CgoFlags[k] = v
}
if k == "CFLAGS" {
p.GccOptions = append(p.GccOptions, args...)
}
}
f.Preamble = strings.Join(linesOut, "\n")
}
// splitQuoted splits the string s around each instance of one or more consecutive
// white space characters while taking into account quotes and escaping, and
// returns an array of substrings of s or an empty list if s contains only white space.
// Single quotes and double quotes are recognized to prevent splitting within the
// quoted region, and are removed from the resulting substrings. If a quote in s
// isn't closed err will be set and r will have the unclosed argument as the
// last element. The backslash is used for escaping.
//
// For example, the following string:
//
// `a b:"c d" 'e''f' "g\""`
//
// Would be parsed as:
//
// []string{"a", "b:c d", "ef", `g"`}
//
func splitQuoted(s string) (r []string, err os.Error) {
var args []string
arg := make([]int, len(s))
escaped := false
quoted := false
quote := 0
i := 0
for _, rune := range s {
switch {
case escaped:
escaped = false
case rune == '\\':
escaped = true
continue
case quote != 0:
if rune == quote {
quote = 0
continue
}
case rune == '"' || rune == '\'':
quoted = true
quote = rune
continue
case unicode.IsSpace(rune):
if quoted || i > 0 {
quoted = false
args = append(args, string(arg[:i]))
i = 0
}
continue
}
arg[i] = rune
i++
}
if quoted || i > 0 {
args = append(args, string(arg[:i]))
}
if quote != 0 {
err = os.ErrorString("unclosed quote")
} else if escaped {
err = os.ErrorString("unfinished escaping")
}
return args, err
}
// Translate rewrites f.AST, the original Go input, to remove // Translate rewrites f.AST, the original Go input, to remove
// references to the imported package C, replacing them with // references to the imported package C, replacing them with
// references to the equivalent Go types, functions, and variables. // references to the equivalent Go types, functions, and variables.

View File

@ -29,6 +29,7 @@ type Package struct {
PackagePath string PackagePath string
PtrSize int64 PtrSize int64
GccOptions []string GccOptions []string
CgoFlags map[string]string // #cgo flags (CFLAGS, LDFLAGS)
Written map[string]bool Written map[string]bool
Name map[string]*Name // accumulated Name from Files Name map[string]*Name // accumulated Name from Files
Typedef map[string]ast.Expr // accumulated Typedef from Files Typedef map[string]ast.Expr // accumulated Typedef from Files
@ -161,7 +162,12 @@ func main() {
if i == len(args) { if i == len(args) {
usage() usage()
} }
gccOptions, goFiles := args[0:i], args[i:]
// Copy it to a new slice so it can grow.
gccOptions := make([]string, i)
copy(gccOptions, args[0:i])
goFiles := args[i:]
arch := os.Getenv("GOARCH") arch := os.Getenv("GOARCH")
if arch == "" { if arch == "" {
@ -180,6 +186,7 @@ func main() {
p := &Package{ p := &Package{
PtrSize: ptrSize, PtrSize: ptrSize,
GccOptions: gccOptions, GccOptions: gccOptions,
CgoFlags: make(map[string]string),
Written: make(map[string]bool), Written: make(map[string]bool),
} }
@ -199,11 +206,17 @@ func main() {
} }
cPrefix = fmt.Sprintf("_%x", h.Sum()[0:6]) cPrefix = fmt.Sprintf("_%x", h.Sum()[0:6])
for _, input := range goFiles { fs := make([]*File, len(goFiles))
for i, input := range goFiles {
// Parse flags for all files before translating due to CFLAGS.
f := new(File) f := new(File)
// Reset f.Preamble so that we don't end up with conflicting headers / defines
f.Preamble = ""
f.ReadGo(input) f.ReadGo(input)
p.ParseFlags(f, input)
fs[i] = f
}
for i, input := range goFiles {
f := fs[i]
p.Translate(f) p.Translate(f)
for _, cref := range f.Ref { for _, cref := range f.Ref {
switch cref.Context { switch cref.Context {

View File

@ -33,6 +33,12 @@ func (p *Package) writeDefs() {
fc := creat("_cgo_defun.c") fc := creat("_cgo_defun.c")
fm := creat("_cgo_main.c") fm := creat("_cgo_main.c")
fflg := creat("_cgo_flags")
for k, v := range p.CgoFlags {
fmt.Fprintf(fflg, "_CGO_%s=%s\n", k, v)
}
fflg.Close()
// Write C main file for using gcc to resolve imports. // Write C main file for using gcc to resolve imports.
fmt.Fprintf(fm, "int main() { return 0; }\n") fmt.Fprintf(fm, "int main() { return 0; }\n")
fmt.Fprintf(fm, "void crosscall2(void(*fn)(void*, int), void *a, int c) { }\n") fmt.Fprintf(fm, "void crosscall2(void(*fn)(void*, int), void *a, int c) { }\n")