1
0
mirror of https://github.com/golang/go synced 2024-11-25 04:57:56 -07:00

cgo: support pkg-config for flags and libs

Fixes issue #1853.

R=golang-dev, mattn.jp, adg
CC=golang-dev
https://golang.org/cl/4550084
This commit is contained in:
Gustavo Niemeyer 2011-05-26 22:19:23 -03:00
parent 12376c93ef
commit bddb75127f
2 changed files with 88 additions and 17 deletions

View File

@ -35,9 +35,17 @@ systems. For example:
// #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.
Alternatively, CFLAGS and LDFLAGS may be obtained via the pkg-config
tool using a '#cgo pkg-config:' directive followed by the package names.
For example:
// #cgo pkg-config: png cairo
// #include <png.h>
import "C"
Within the Go file, 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.
The standard C numeric types are available under the names
C.char, C.schar (signed char), C.uchar (unsigned char),

View File

@ -100,27 +100,76 @@ NextLine:
fatalf("%s: bad #cgo option: %s", srcfile, fields[0])
}
if k != "CFLAGS" && k != "LDFLAGS" {
fatalf("%s: unsupported #cgo option %s", srcfile, k)
args, err := splitQuoted(fields[1])
if err != nil {
fatalf("%s: bad #cgo option %s: %s", srcfile, k, err)
}
v := strings.TrimSpace(fields[1])
args, err := splitQuoted(v)
if err != nil {
fatalf("%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...)
switch k {
case "CFLAGS", "LDFLAGS":
p.addToFlag(k, args)
case "pkg-config":
cflags, ldflags, err := pkgConfig(args)
if err != nil {
fatalf("%s: bad #cgo option %s: %s", srcfile, k, err)
}
p.addToFlag("CFLAGS", cflags)
p.addToFlag("LDFLAGS", ldflags)
default:
fatalf("%s: unsupported #cgo option %s", srcfile, k)
}
}
f.Preamble = strings.Join(linesOut, "\n")
}
// addToFlag appends args to flag. All flags are later written out onto the
// _cgo_flags file for the build system to use.
func (p *Package) addToFlag(flag string, args []string) {
if oldv, ok := p.CgoFlags[flag]; ok {
p.CgoFlags[flag] = oldv + " " + strings.Join(args, " ")
} else {
p.CgoFlags[flag] = strings.Join(args, " ")
}
if flag == "CFLAGS" {
// We'll also need these when preprocessing for dwarf information.
p.GccOptions = append(p.GccOptions, args...)
}
}
// pkgConfig runs pkg-config and extracts --libs and --cflags information
// for packages.
func pkgConfig(packages []string) (cflags, ldflags []string, err os.Error) {
for _, name := range packages {
if len(name) == 0 || !safeName(name) || name[0] == '-' {
return nil, nil, os.NewError(fmt.Sprintf("invalid name: %q", name))
}
}
args := append([]string{"pkg-config", "--cflags"}, packages...)
stdout, stderr, ok := run(nil, args)
if !ok {
os.Stderr.Write(stderr)
return nil, nil, os.NewError("pkg-config failed")
}
cflags, err = splitQuoted(string(stdout))
if err != nil {
return
}
args = append([]string{"pkg-config", "--libs"}, packages...)
stdout, stderr, ok = run(nil, args)
if !ok {
os.Stderr.Write(stderr)
return nil, nil, os.NewError("pkg-config failed")
}
ldflags, err = splitQuoted(string(stdout))
return
}
// 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.
@ -182,6 +231,20 @@ func splitQuoted(s string) (r []string, err os.Error) {
return args, err
}
var safeBytes = []byte("+-./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz")
func safeName(s string) bool {
if s == "" {
return false
}
for i := 0; i < len(s); i++ {
if c := s[i]; c < 0x80 && bytes.IndexByte(safeBytes, c) < 0 {
return false
}
}
return true
}
// 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.