Commit 4fd867b2 authored by Gustavo Niemeyer's avatar Gustavo Niemeyer Committed by Russ Cox

cgo: define CGO_CFLAGS and CGO_LDFLAGS in Go files

R=rsc, binet
CC=golang-dev
https://golang.org/cl/3921043
parent 0e474609
......@@ -48,7 +48,7 @@ coverage:
6cov -g $(shell pwd) $O.out | grep -v '_test\.go:'
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
test:
......@@ -112,11 +112,21 @@ dir:
#
ifdef CGOFILES
_cgo_defun.c: $(CGOFILES)
_cgo_run: $(CGOFILES)
@touch _cgo_run
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.
_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
%.cgo1.go %.cgo2.c: _cgo_defun.c
......@@ -125,7 +135,7 @@ endif
# Compile rules for gcc source files.
%.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
# 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.
_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)
$(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 -dynimport _cgo1_.o >_$@ && mv -f _$@ $@
......
......@@ -23,6 +23,15 @@ the package. For example:
// #include <errno.h>
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
accessed by prefixing them with an underscore: if x points at
a C struct with a field named "type", x._type accesses the field.
......
......@@ -21,6 +21,7 @@ import (
"os"
"strconv"
"strings"
"unicode"
)
var debugDefine = flag.Bool("debug-define", false, "print relevant #defines")
......@@ -59,6 +60,107 @@ func cname(s string) string {
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
// references to the imported package C, replacing them with
// references to the equivalent Go types, functions, and variables.
......
......@@ -29,6 +29,7 @@ type Package struct {
PackagePath string
PtrSize int64
GccOptions []string
CgoFlags map[string]string // #cgo flags (CFLAGS, LDFLAGS)
Written map[string]bool
Name map[string]*Name // accumulated Name from Files
Typedef map[string]ast.Expr // accumulated Typedef from Files
......@@ -161,7 +162,12 @@ func main() {
if i == len(args) {
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")
if arch == "" {
......@@ -180,6 +186,7 @@ func main() {
p := &Package{
PtrSize: ptrSize,
GccOptions: gccOptions,
CgoFlags: make(map[string]string),
Written: make(map[string]bool),
}
......@@ -199,11 +206,17 @@ func main() {
}
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)
// Reset f.Preamble so that we don't end up with conflicting headers / defines
f.Preamble = ""
f.ReadGo(input)
p.ParseFlags(f, input)
fs[i] = f
}
for i, input := range goFiles {
f := fs[i]
p.Translate(f)
for _, cref := range f.Ref {
switch cref.Context {
......
......@@ -33,6 +33,12 @@ func (p *Package) writeDefs() {
fc := creat("_cgo_defun.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.
fmt.Fprintf(fm, "int main() { return 0; }\n")
fmt.Fprintf(fm, "void crosscall2(void(*fn)(void*, int), void *a, int c) { }\n")
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment