mirror of
https://github.com/golang/go
synced 2024-11-25 04:47:57 -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:
parent
0e47460915
commit
4fd867b283
22
src/Make.pkg
22
src/Make.pkg
@ -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")
|
||||
|
Loading…
Reference in New Issue
Block a user