]> Cypherpunks.ru repositories - gostls13.git/commitdiff
misc/swig: restructure as a driver
authorAustin Clements <austin@google.com>
Mon, 8 May 2023 18:39:57 +0000 (14:39 -0400)
committerAustin Clements <austin@google.com>
Fri, 12 May 2023 12:00:05 +0000 (12:00 +0000)
Currently, the misc/swig tests directly use Swig and C++ and will fail
to build if either Swig or a C++ compiler are not present. Typically,
we hide this fact from users because dist test itself checks for Swig
and a C++ compiler before even attempting to run this test, though
users will see this is they try to go test ./... from misc.

However, we're about to move the misc/swig tests into the cmd module,
where they will be much more visible and much more likely to run
unintentionally. To prevent build errors, this CL restructures these
tests into a single pure Go test plus two test packages hidden in
testdata. This is relatively easy to do for this test because there
are only four test cases total. The pure Go test can check for the
necessary build tools before trying to build and run the tests in
testdata. This also gives us the opportunity to move the LTO variant
of these tests out of dist and into the test itself, simplifying dist.

For #37486.

Change-Id: Ibda089b4069e36866cb31867a7006c790be2d8b1
Reviewed-on: https://go-review.googlesource.com/c/go/+/493599
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Austin Clements <austin@google.com>
Reviewed-by: Bryan Mills <bcmills@google.com>
13 files changed:
misc/swig/callback/callback.go [deleted file]
misc/swig/callback/callback_test.go [deleted file]
misc/swig/nocgo_test.go [new file with mode: 0644]
misc/swig/stdio/file.go [deleted file]
misc/swig/stdio/file_test.go [deleted file]
misc/swig/swig_test.go [new file with mode: 0644]
misc/swig/testdata/callback/main.cc [moved from misc/swig/callback/callback.cc with 94% similarity]
misc/swig/testdata/callback/main.go [new file with mode: 0644]
misc/swig/testdata/callback/main.h [moved from misc/swig/callback/callback.h with 100% similarity]
misc/swig/testdata/callback/main.swigcxx [moved from misc/swig/callback/callback.swigcxx with 88% similarity]
misc/swig/testdata/stdio/main.go [new file with mode: 0644]
misc/swig/testdata/stdio/main.swig [moved from misc/swig/stdio/file.swig with 100% similarity]
src/cmd/dist/test.go

diff --git a/misc/swig/callback/callback.go b/misc/swig/callback/callback.go
deleted file mode 100644 (file)
index 0d6e97f..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-// Copyright 2012 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package callback
-
-type GoCallback struct{}
-
-func (p *GoCallback) Run() string {
-       return "GoCallback.Run"
-}
diff --git a/misc/swig/callback/callback_test.go b/misc/swig/callback/callback_test.go
deleted file mode 100644 (file)
index 0c8a300..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2012 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package callback
-
-import (
-       "testing"
-)
-
-func TestCall(t *testing.T) {
-       c := NewCaller()
-       cb := NewCallback()
-
-       c.SetCallback(cb)
-       s := c.Call()
-       if s != "Callback::run" {
-               t.Errorf("unexpected string from Call: %q", s)
-       }
-       c.DelCallback()
-}
-
-func TestCallback(t *testing.T) {
-       c := NewCaller()
-       cb := NewDirectorCallback(&GoCallback{})
-       c.SetCallback(cb)
-       s := c.Call()
-       if s != "GoCallback.Run" {
-               t.Errorf("unexpected string from Call with callback: %q", s)
-       }
-       c.DelCallback()
-       DeleteDirectorCallback(cb)
-}
diff --git a/misc/swig/nocgo_test.go b/misc/swig/nocgo_test.go
new file mode 100644 (file)
index 0000000..c68b97d
--- /dev/null
@@ -0,0 +1,7 @@
+// Copyright 2023 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file is just to avoid build errors if there's no cgo.
+
+package swig
diff --git a/misc/swig/stdio/file.go b/misc/swig/stdio/file.go
deleted file mode 100644 (file)
index a582f77..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright 2017 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// This file is here just to cause problems.
-// file.swig turns into a file also named file.go.
-// Make sure cmd/go keeps them separate
-// when both are passed to cgo.
-
-package file
-
-//int F(void) { return 1; }
-import "C"
-
-func F() int { return int(C.F()) }
diff --git a/misc/swig/stdio/file_test.go b/misc/swig/stdio/file_test.go
deleted file mode 100644 (file)
index aea92aa..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2012 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package file
-
-import "testing"
-
-// Open this file itself and verify that the first few characters are
-// as expected.
-func TestRead(t *testing.T) {
-       f := Fopen("file_test.go", "r")
-       if f.Swigcptr() == 0 {
-               t.Fatal("fopen failed")
-       }
-       if Fgetc(f) != '/' || Fgetc(f) != '/' || Fgetc(f) != ' ' || Fgetc(f) != 'C' {
-               t.Error("read unexpected characters")
-       }
-       if Fclose(f) != 0 {
-               t.Error("fclose failed")
-       }
-}
-
-func TestF(t *testing.T) {
-       if x := F(); x != 1 {
-               t.Fatalf("x = %d, want 1", x)
-       }
-}
diff --git a/misc/swig/swig_test.go b/misc/swig/swig_test.go
new file mode 100644 (file)
index 0000000..cbe062a
--- /dev/null
@@ -0,0 +1,149 @@
+// Copyright 2023 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build cgo
+
+package swig
+
+import (
+       "bytes"
+       "os"
+       "os/exec"
+       "path/filepath"
+       "regexp"
+       "strconv"
+       "strings"
+       "sync"
+       "testing"
+)
+
+func TestStdio(t *testing.T) {
+       mustHaveSwig(t)
+       run(t, "testdata/stdio", false)
+}
+
+func TestCall(t *testing.T) {
+       mustHaveSwig(t)
+       mustHaveCxx(t)
+       run(t, "testdata/callback", false, "Call")
+       t.Run("lto", func(t *testing.T) { run(t, "testdata/callback", true, "Call") })
+}
+
+func TestCallback(t *testing.T) {
+       mustHaveSwig(t)
+       mustHaveCxx(t)
+       run(t, "testdata/callback", false, "Callback")
+       t.Run("lto", func(t *testing.T) { run(t, "testdata/callback", true, "Callback") })
+}
+
+func run(t *testing.T, dir string, lto bool, args ...string) {
+       runArgs := append([]string{"run", "."}, args...)
+       cmd := exec.Command("go", runArgs...)
+       cmd.Dir = dir
+       if lto {
+               const cflags = "-flto -Wno-lto-type-mismatch -Wno-unknown-warning-option"
+               cmd.Env = append(cmd.Environ(),
+                       "CGO_CFLAGS="+cflags,
+                       "CGO_CXXFLAGS="+cflags,
+                       "CGO_LDFLAGS="+cflags)
+       }
+       out, err := cmd.CombinedOutput()
+       if string(out) != "OK\n" {
+               t.Errorf("%s", string(out))
+       }
+       if err != nil {
+               t.Errorf("%s", err)
+       }
+}
+
+func mustHaveCxx(t *testing.T) {
+       // Ask the go tool for the CXX it's configured to use.
+       cxx, err := exec.Command("go", "env", "CXX").CombinedOutput()
+       if err != nil {
+               t.Fatalf("go env CXX failed: %s", err)
+       }
+       cxx = bytes.TrimSuffix(cxx, []byte("\n"))
+       // TODO(austin): "go env CXX" can return a quoted list. Use quoted.Split.
+       p, err := exec.LookPath(string(cxx))
+       if p == "" {
+               t.Skipf("test requires C++ compiler, but failed to find %s: %s", string(cxx), err)
+       }
+}
+
+var (
+       swigOnce sync.Once
+       haveSwig bool
+)
+
+func mustHaveSwig(t *testing.T) {
+       swigOnce.Do(func() {
+               mustHaveSwigOnce(t)
+               haveSwig = true
+       })
+       // The first call will skip t with a nice message. On later calls, we just skip.
+       if !haveSwig {
+               t.Skip("swig not found")
+       }
+}
+
+func mustHaveSwigOnce(t *testing.T) {
+       swig, err := exec.LookPath("swig")
+       if err != nil {
+               t.Skipf("swig not in PATH: %s", err)
+       }
+
+       // Check that swig was installed with Go support by checking
+       // that a go directory exists inside the swiglib directory.
+       // See https://golang.org/issue/23469.
+       output, err := exec.Command(swig, "-go", "-swiglib").Output()
+       if err != nil {
+               t.Skip("swig is missing Go support")
+       }
+       swigDir := strings.TrimSpace(string(output))
+
+       _, err = os.Stat(filepath.Join(swigDir, "go"))
+       if err != nil {
+               t.Skip("swig is missing Go support")
+       }
+
+       // Check that swig has a new enough version.
+       // See https://golang.org/issue/22858.
+       out, err := exec.Command(swig, "-version").CombinedOutput()
+       if err != nil {
+               t.Skipf("failed to get swig version:%s\n%s", err, string(out))
+       }
+
+       re := regexp.MustCompile(`[vV]ersion +(\d+)([.]\d+)?([.]\d+)?`)
+       matches := re.FindSubmatch(out)
+       if matches == nil {
+               // Can't find version number; hope for the best.
+               t.Logf("failed to find swig version, continuing")
+               return
+       }
+
+       var parseError error
+       atoi := func(s string) int {
+               x, err := strconv.Atoi(s)
+               if err != nil && parseError == nil {
+                       parseError = err
+               }
+               return x
+       }
+       var major, minor, patch int
+       major = atoi(string(matches[1]))
+       if len(matches[2]) > 0 {
+               minor = atoi(string(matches[2][1:]))
+       }
+       if len(matches[3]) > 0 {
+               patch = atoi(string(matches[3][1:]))
+       }
+       if parseError != nil {
+               t.Logf("error parsing swig version %q, continuing anyway: %s", string(matches[0]), parseError)
+               return
+       }
+       t.Logf("found swig version %d.%d.%d", major, minor, patch)
+       if major < 3 || (major == 3 && minor == 0 && patch < 6) {
+               t.Skip("test requires swig 3.0.6 or later")
+       }
+}
similarity index 94%
rename from misc/swig/callback/callback.cc
rename to misc/swig/testdata/callback/main.cc
index 88bd49c57f8956d5e1a34b66c7e01ca9aa5cebda..7de917cde45782430734543cd15d1bf60f716c7e 100644 (file)
@@ -6,7 +6,7 @@
 // included in the package.
 
 #include <string>
-#include "callback.h"
+#include "main.h"
 
 std::string Caller::call() {
        if (callback_ != 0)
diff --git a/misc/swig/testdata/callback/main.go b/misc/swig/testdata/callback/main.go
new file mode 100644 (file)
index 0000000..73034a0
--- /dev/null
@@ -0,0 +1,60 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+       "fmt"
+       "os"
+)
+
+func main() {
+       if len(os.Args) != 2 {
+               fatal("usage: callback testname")
+       }
+       switch os.Args[1] {
+       default:
+               fatal("unknown test %q", os.Args[1])
+       case "Call":
+               testCall()
+       case "Callback":
+               testCallback()
+       }
+       println("OK")
+}
+
+func fatal(f string, args ...any) {
+       fmt.Fprintln(os.Stderr, fmt.Sprintf(f, args...))
+       os.Exit(1)
+}
+
+type GoCallback struct{}
+
+func (p *GoCallback) Run() string {
+       return "GoCallback.Run"
+}
+
+func testCall() {
+       c := NewCaller()
+       cb := NewCallback()
+
+       c.SetCallback(cb)
+       s := c.Call()
+       if s != "Callback::run" {
+               fatal("unexpected string from Call: %q", s)
+       }
+       c.DelCallback()
+}
+
+func testCallback() {
+       c := NewCaller()
+       cb := NewDirectorCallback(&GoCallback{})
+       c.SetCallback(cb)
+       s := c.Call()
+       if s != "GoCallback.Run" {
+               fatal("unexpected string from Call with callback: %q", s)
+       }
+       c.DelCallback()
+       DeleteDirectorCallback(cb)
+}
similarity index 88%
rename from misc/swig/callback/callback.swigcxx
rename to misc/swig/testdata/callback/main.swigcxx
index 6181fe9c7ea1a9f93a58ea40324d6c69380579a3..0fd73d63623126d995c62bb8a3bb13654b666081 100644 (file)
@@ -8,11 +8,11 @@
 
 %{
 #include <string>
-#include "callback.h"
+#include "main.h"
 %}
 
 %include "std_string.i"
 
 %feature("director");
 
-%include "callback.h"
+%include "main.h"
diff --git a/misc/swig/testdata/stdio/main.go b/misc/swig/testdata/stdio/main.go
new file mode 100644 (file)
index 0000000..0296dd3
--- /dev/null
@@ -0,0 +1,45 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file is here just to cause problems.
+// main.swig turns into a file also named main.go.
+// Make sure cmd/go keeps them separate
+// when both are passed to cgo.
+
+package main
+
+//int F(void) { return 1; }
+import "C"
+import (
+       "fmt"
+       "os"
+)
+
+func F() int { return int(C.F()) }
+
+func main() {
+       if x := int(C.F()); x != 1 {
+               fatal("x = %d, want 1", x)
+       }
+
+       // Open this file itself and verify that the first few characters are
+       // as expected.
+       f := Fopen("main.go", "r")
+       if f.Swigcptr() == 0 {
+               fatal("fopen failed")
+       }
+       if Fgetc(f) != '/' || Fgetc(f) != '/' || Fgetc(f) != ' ' || Fgetc(f) != 'C' {
+               fatal("read unexpected characters")
+       }
+       if Fclose(f) != 0 {
+               fatal("fclose failed")
+       }
+
+       println("OK")
+}
+
+func fatal(f string, args ...any) {
+       fmt.Fprintln(os.Stderr, fmt.Sprintf(f, args...))
+       os.Exit(1)
+}
index 2e0072f44fbbc0056f594ebb44a45a49724975fd..95c27ce32757f4e7e3807a3806c09be2967abeb1 100644 (file)
@@ -870,21 +870,8 @@ func (t *tester) registerTests() {
                if goos != "android" {
                        t.registerTest("cgo_testfortran", "", &goTest{dir: "cmd/cgo/internal/testfortran", timeout: 5 * time.Minute}, rtHostTest{})
                }
-               if t.hasSwig() && goos != "android" {
-                       t.registerTest("swig_stdio", "", &goTest{dir: "../misc/swig/stdio"})
-                       if t.hasCxx() {
-                               t.registerTest("swig_callback", "", &goTest{dir: "../misc/swig/callback"})
-                               const cflags = "-flto -Wno-lto-type-mismatch -Wno-unknown-warning-option"
-                               t.registerTest("swig_callback_lto", "",
-                                       &goTest{
-                                               dir: "../misc/swig/callback",
-                                               env: []string{
-                                                       "CGO_CFLAGS=" + cflags,
-                                                       "CGO_CXXFLAGS=" + cflags,
-                                                       "CGO_LDFLAGS=" + cflags,
-                                               },
-                                       })
-                       }
+               if goos != "android" {
+                       t.registerTest("swig", "", &goTest{dir: "../misc/swig"})
                }
        }
        if t.cgoEnabled {
@@ -1412,85 +1399,6 @@ func (t *tester) hasBash() bool {
        return true
 }
 
-func (t *tester) hasCxx() bool {
-       cxx, _ := exec.LookPath(compilerEnvLookup("CXX", defaultcxx, goos, goarch))
-       return cxx != ""
-}
-
-func (t *tester) hasSwig() bool {
-       swig, err := exec.LookPath("swig")
-       if err != nil {
-               return false
-       }
-
-       // Check that swig was installed with Go support by checking
-       // that a go directory exists inside the swiglib directory.
-       // See https://golang.org/issue/23469.
-       output, err := exec.Command(swig, "-go", "-swiglib").Output()
-       if err != nil {
-               return false
-       }
-       swigDir := strings.TrimSpace(string(output))
-
-       _, err = os.Stat(filepath.Join(swigDir, "go"))
-       if err != nil {
-               return false
-       }
-
-       // Check that swig has a new enough version.
-       // See https://golang.org/issue/22858.
-       out, err := exec.Command(swig, "-version").CombinedOutput()
-       if err != nil {
-               return false
-       }
-
-       re := regexp.MustCompile(`[vV]ersion +(\d+)([.]\d+)?([.]\d+)?`)
-       matches := re.FindSubmatch(out)
-       if matches == nil {
-               // Can't find version number; hope for the best.
-               return true
-       }
-
-       major, err := strconv.Atoi(string(matches[1]))
-       if err != nil {
-               // Can't find version number; hope for the best.
-               return true
-       }
-       if major < 3 {
-               return false
-       }
-       if major > 3 {
-               // 4.0 or later
-               return true
-       }
-
-       // We have SWIG version 3.x.
-       if len(matches[2]) > 0 {
-               minor, err := strconv.Atoi(string(matches[2][1:]))
-               if err != nil {
-                       return true
-               }
-               if minor > 0 {
-                       // 3.1 or later
-                       return true
-               }
-       }
-
-       // We have SWIG version 3.0.x.
-       if len(matches[3]) > 0 {
-               patch, err := strconv.Atoi(string(matches[3][1:]))
-               if err != nil {
-                       return true
-               }
-               if patch < 6 {
-                       // Before 3.0.6.
-                       return false
-               }
-       }
-
-       return true
-}
-
 // hasParallelism is a copy of the function
 // internal/testenv.HasParallelism, which can't be used here
 // because cmd/dist can not import internal packages during bootstrap.