From: Quentin Smith Date: Thu, 3 Nov 2016 22:45:01 +0000 (-0400) Subject: cmd/go: handle escapes in pkg-config output X-Git-Tag: go1.8beta1~271 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=c2917af6280ea1bcc5ebf85224055ae1a1882af3;p=gostls13.git cmd/go: handle escapes in pkg-config output 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 TryBot-Result: Gobot Gobot Reviewed-by: Russ Cox --- diff --git a/src/cmd/go/build.go b/src/cmd/go/build.go index 4427a06ce5..2863d20d9c 100644 --- a/src/cmd/go/build.go +++ b/src/cmd/go/build.go @@ -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 { diff --git a/src/cmd/go/build_test.go b/src/cmd/go/build_test.go index d95bd0bc7e..79bbd54591 100644 --- a/src/cmd/go/build_test.go +++ b/src/cmd/go/build_test.go @@ -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) + } + } +} diff --git a/src/cmd/go/go_test.go b/src/cmd/go/go_test.go index 70fb81479e..caa8598885 100644 --- a/src/cmd/go/go_test.go +++ b/src/cmd/go/go_test.go @@ -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)