]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/go: handle escapes in pkg-config output
authorQuentin Smith <quentin@golang.org>
Thu, 3 Nov 2016 22:45:01 +0000 (18:45 -0400)
committerQuentin Smith <quentin@golang.org>
Mon, 7 Nov 2016 21:50:26 +0000 (21:50 +0000)
This commit also adds a test for pkg-config usage in cgo.

Fixes #16455.

Change-Id: I95fb6a288a4d19093c4613c93878017d95cbe4a2
Reviewed-on: https://go-review.googlesource.com/32735
Run-TryBot: Quentin Smith <quentin@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Russ Cox <rsc@golang.org>
src/cmd/go/build.go
src/cmd/go/build_test.go
src/cmd/go/go_test.go

index 4427a06ce5f6ae00ef80174152af573c41909f34..2863d20d9c8e0d0afcfc332350e2873a95327653 100644 (file)
@@ -1635,6 +1635,39 @@ func (b *builder) pkgconfigCmd() string {
        return envList("PKG_CONFIG", defaultPkgConfig)[0]
 }
 
+// splitPkgConfigOutput parses the pkg-config output into a slice of
+// flags. pkg-config always uses \ to escape special characters.
+func splitPkgConfigOutput(out []byte) []string {
+       if len(out) == 0 {
+               return nil
+       }
+       var flags []string
+       flag := make([]byte, len(out))
+       r, w := 0, 0
+       for r < len(out) {
+               switch out[r] {
+               case ' ', '\t', '\r', '\n':
+                       if w > 0 {
+                               flags = append(flags, string(flag[:w]))
+                       }
+                       w = 0
+               case '\\':
+                       r++
+                       fallthrough
+               default:
+                       if r < len(out) {
+                               flag[w] = out[r]
+                               w++
+                       }
+               }
+               r++
+       }
+       if w > 0 {
+               flags = append(flags, string(flag[:w]))
+       }
+       return flags
+}
+
 // Calls pkg-config if needed and returns the cflags/ldflags needed to build the package.
 func (b *builder) getPkgConfigFlags(p *Package) (cflags, ldflags []string, err error) {
        if pkgs := p.CgoPkgConfig; len(pkgs) > 0 {
@@ -1647,7 +1680,7 @@ func (b *builder) getPkgConfigFlags(p *Package) (cflags, ldflags []string, err e
                        return
                }
                if len(out) > 0 {
-                       cflags = strings.Fields(string(out))
+                       cflags = splitPkgConfigOutput(out)
                }
                out, err = b.runOut(p.Dir, p.ImportPath, nil, b.pkgconfigCmd(), "--libs", pkgs)
                if err != nil {
index d95bd0bc7ec16de6d6b840e0e39fb345cd85f93f..79bbd545915badb95a7b60a876946451873b802b 100644 (file)
@@ -6,6 +6,7 @@ package main
 
 import (
        "os"
+       "reflect"
        "testing"
 )
 
@@ -23,3 +24,21 @@ func TestRemoveDevNull(t *testing.T) {
                t.Errorf("mayberemovefile(%s) did remove it; oops", os.DevNull)
        }
 }
+
+func TestSplitPkgConfigOutput(t *testing.T) {
+       for _, test := range []struct {
+               in   []byte
+               want []string
+       }{
+               {[]byte(`-r:foo -L/usr/white\ space/lib -lfoo\ bar -lbar\ baz`), []string{"-r:foo", "-L/usr/white space/lib", "-lfoo bar", "-lbar baz"}},
+               {[]byte(`-lextra\ fun\ arg\\`), []string{`-lextra fun arg\`}},
+               {[]byte(`broken flag\`), []string{"broken", "flag"}},
+               {[]byte("\textra     whitespace\r\n"), []string{"extra", "whitespace"}},
+               {[]byte("     \r\n      "), nil},
+       } {
+               got := splitPkgConfigOutput(test.in)
+               if !reflect.DeepEqual(got, test.want) {
+                       t.Errorf("splitPkgConfigOutput(%v) = %v; want %v", test.in, got, test.want)
+               }
+       }
+}
index 70fb81479e31f8af408cc1922fa37b453ecdf736..caa8598885717481c89703b5373a013f64ee3647 100644 (file)
@@ -2201,6 +2201,51 @@ func TestCgoHandlesWlORIGIN(t *testing.T) {
        tg.run("build", "origin")
 }
 
+func TestCgoPkgConfig(t *testing.T) {
+       if !canCgo {
+               t.Skip("skipping because cgo not enabled")
+       }
+       tg := testgo(t)
+       defer tg.cleanup()
+       tg.parallel()
+
+       tg.run("env", "PKG_CONFIG")
+       if _, err := exec.LookPath(strings.TrimSpace(tg.getStdout())); err != nil {
+               t.Skip("skipping because pkg-config could not be found")
+       }
+
+       // OpenBSD's pkg-config is strict about whitespace and only
+       // supports backslash-escaped whitespace. It does not support
+       // quotes, which the normal freedesktop.org pkg-config does
+       // support. See http://man.openbsd.org/pkg-config.1
+       tg.tempFile("foo.pc", `
+Name: foo
+Description: The foo library
+Version: 1.0.0
+Cflags: -Dhello=10 -Dworld=+32 -DDEFINED_FROM_PKG_CONFIG=hello\ world
+`)
+       tg.tempFile("foo.go", `package main
+
+/*
+#cgo pkg-config: foo
+int value() {
+       return DEFINED_FROM_PKG_CONFIG;
+}
+*/
+import "C"
+import "os"
+
+func main() {
+       if C.value() != 42 {
+               println("value() =", C.value(), "wanted 42")
+               os.Exit(1)
+       }
+}
+`)
+       tg.setenv("PKG_CONFIG_PATH", tg.path("."))
+       tg.run("run", tg.path("foo.go"))
+}
+
 // "go test -c -test.bench=XXX errors" should not hang
 func TestIssue6480(t *testing.T) {
        tg := testgo(t)