1
2
3
4
5 package swig
6
7 import (
8 "cmd/internal/quoted"
9 "internal/testenv"
10 "os"
11 "os/exec"
12 "path/filepath"
13 "regexp"
14 "strconv"
15 "strings"
16 "sync"
17 "testing"
18 )
19
20 func TestStdio(t *testing.T) {
21 testenv.MustHaveCGO(t)
22 mustHaveSwig(t)
23 run(t, "testdata/stdio", false)
24 }
25
26 func TestCall(t *testing.T) {
27 testenv.MustHaveCGO(t)
28 mustHaveSwig(t)
29 mustHaveCxx(t)
30 run(t, "testdata/callback", false, "Call")
31 t.Run("lto", func(t *testing.T) { run(t, "testdata/callback", true, "Call") })
32 }
33
34 func TestCallback(t *testing.T) {
35 testenv.MustHaveCGO(t)
36 mustHaveSwig(t)
37 mustHaveCxx(t)
38 run(t, "testdata/callback", false, "Callback")
39 t.Run("lto", func(t *testing.T) { run(t, "testdata/callback", true, "Callback") })
40 }
41
42 func run(t *testing.T, dir string, lto bool, args ...string) {
43 testenv.MustHaveGoRun(t)
44 runArgs := append([]string{"run", "."}, args...)
45 cmd := exec.Command(testenv.GoToolPath(t), runArgs...)
46 cmd.Dir = dir
47 if lto {
48
49
50
51 extraLDFlags := ""
52 if strings.Contains(testenv.Builder(), "clang") {
53 extraLDFlags += " -fuse-ld=lld"
54 }
55 const cflags = "-flto -Wno-lto-type-mismatch -Wno-unknown-warning-option"
56 cmd.Env = append(cmd.Environ(),
57 "CGO_CFLAGS="+cflags,
58 "CGO_CXXFLAGS="+cflags,
59 "CGO_LDFLAGS="+cflags+extraLDFlags)
60 }
61 out, err := cmd.CombinedOutput()
62 if string(out) != "OK\n" {
63 t.Errorf("%s", string(out))
64 }
65 if err != nil {
66 t.Errorf("%s", err)
67 }
68 }
69
70 func mustHaveCxx(t *testing.T) {
71
72 cxx, err := exec.Command(testenv.GoToolPath(t), "env", "CXX").CombinedOutput()
73 if err != nil {
74 t.Fatalf("go env CXX failed: %s", err)
75 }
76 args, err := quoted.Split(string(cxx))
77 if err != nil {
78 t.Skipf("could not parse 'go env CXX' output %q: %s", string(cxx), err)
79 }
80 if len(args) == 0 {
81 t.Skip("no C++ compiler")
82 }
83 testenv.MustHaveExecPath(t, string(args[0]))
84 }
85
86 var (
87 swigOnce sync.Once
88 haveSwig bool
89 )
90
91 func mustHaveSwig(t *testing.T) {
92 swigOnce.Do(func() {
93 mustHaveSwigOnce(t)
94 haveSwig = true
95 })
96
97 if !haveSwig {
98 t.Skip("swig not found")
99 }
100 }
101
102 func mustHaveSwigOnce(t *testing.T) {
103 swig, err := exec.LookPath("swig")
104 if err != nil {
105 t.Skipf("swig not in PATH: %s", err)
106 }
107
108
109
110
111 output, err := exec.Command(swig, "-go", "-swiglib").Output()
112 if err != nil {
113 t.Skip("swig is missing Go support")
114 }
115 swigDir := strings.TrimSpace(string(output))
116
117 _, err = os.Stat(filepath.Join(swigDir, "go"))
118 if err != nil {
119 t.Skip("swig is missing Go support")
120 }
121
122
123
124 out, err := exec.Command(swig, "-version").CombinedOutput()
125 if err != nil {
126 t.Skipf("failed to get swig version:%s\n%s", err, string(out))
127 }
128
129 re := regexp.MustCompile(`[vV]ersion +(\d+)([.]\d+)?([.]\d+)?`)
130 matches := re.FindSubmatch(out)
131 if matches == nil {
132
133 t.Logf("failed to find swig version, continuing")
134 return
135 }
136
137 var parseError error
138 atoi := func(s string) int {
139 x, err := strconv.Atoi(s)
140 if err != nil && parseError == nil {
141 parseError = err
142 }
143 return x
144 }
145 var major, minor, patch int
146 major = atoi(string(matches[1]))
147 if len(matches[2]) > 0 {
148 minor = atoi(string(matches[2][1:]))
149 }
150 if len(matches[3]) > 0 {
151 patch = atoi(string(matches[3][1:]))
152 }
153 if parseError != nil {
154 t.Logf("error parsing swig version %q, continuing anyway: %s", string(matches[0]), parseError)
155 return
156 }
157 t.Logf("found swig version %d.%d.%d", major, minor, patch)
158 if major < 3 || (major == 3 && minor == 0 && patch < 6) {
159 t.Skip("test requires swig 3.0.6 or later")
160 }
161 }
162
View as plain text