]> Cypherpunks repositories - gostls13.git/commitdiff
cgo: support pkg-config for flags and libs
authorGustavo Niemeyer <gustavo@niemeyer.net>
Fri, 27 May 2011 01:19:23 +0000 (22:19 -0300)
committerGustavo Niemeyer <gustavo@niemeyer.net>
Fri, 27 May 2011 01:19:23 +0000 (22:19 -0300)
Fixes issue #1853.

R=golang-dev, mattn.jp, adg
CC=golang-dev
https://golang.org/cl/4550084

src/cmd/cgo/doc.go
src/cmd/cgo/gcc.go

index b3aa9aded29e2090689c6e562479f6400e93d23a..064725c1d5dbc43f26202ed8553034f6fab9ce33 100644 (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),
index ac656134598a944392f0c0fbc7893e7ef72dced3..1fa8dd166119bd479ec3c00fd36f00eb6f3a28da 100644 (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)
-               }
-
-               v := strings.TrimSpace(fields[1])
-               args, err := splitQuoted(v)
+               args, err := splitQuoted(fields[1])
                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
+                       fatalf("%s: bad #cgo option %s: %s", srcfile, k, err)
                }
-               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.