Source file src/cmd/cgo/internal/swig/swig_test.go

     1  // Copyright 2023 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     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  	runArgs := append([]string{"run", "."}, args...)
    44  	cmd := exec.Command("go", runArgs...)
    45  	cmd.Dir = dir
    46  	if lto {
    47  		// On the builders we're using the default /usr/bin/ld, but
    48  		// that has problems when asking for LTO in particular. Force
    49  		// use of lld, which ships with our clang installation.
    50  		extraLDFlags := ""
    51  		if strings.Contains(testenv.Builder(), "clang") {
    52  			extraLDFlags += " -fuse-ld=lld"
    53  		}
    54  		const cflags = "-flto -Wno-lto-type-mismatch -Wno-unknown-warning-option"
    55  		cmd.Env = append(cmd.Environ(),
    56  			"CGO_CFLAGS="+cflags,
    57  			"CGO_CXXFLAGS="+cflags,
    58  			"CGO_LDFLAGS="+cflags+extraLDFlags)
    59  	}
    60  	out, err := cmd.CombinedOutput()
    61  	if string(out) != "OK\n" {
    62  		t.Errorf("%s", string(out))
    63  	}
    64  	if err != nil {
    65  		t.Errorf("%s", err)
    66  	}
    67  }
    68  
    69  func mustHaveCxx(t *testing.T) {
    70  	// Ask the go tool for the CXX it's configured to use.
    71  	cxx, err := exec.Command("go", "env", "CXX").CombinedOutput()
    72  	if err != nil {
    73  		t.Fatalf("go env CXX failed: %s", err)
    74  	}
    75  	args, err := quoted.Split(string(cxx))
    76  	if err != nil {
    77  		t.Skipf("could not parse 'go env CXX' output %q: %s", string(cxx), err)
    78  	}
    79  	if len(args) == 0 {
    80  		t.Skip("no C++ compiler")
    81  	}
    82  	testenv.MustHaveExecPath(t, string(args[0]))
    83  }
    84  
    85  var (
    86  	swigOnce sync.Once
    87  	haveSwig bool
    88  )
    89  
    90  func mustHaveSwig(t *testing.T) {
    91  	swigOnce.Do(func() {
    92  		mustHaveSwigOnce(t)
    93  		haveSwig = true
    94  	})
    95  	// The first call will skip t with a nice message. On later calls, we just skip.
    96  	if !haveSwig {
    97  		t.Skip("swig not found")
    98  	}
    99  }
   100  
   101  func mustHaveSwigOnce(t *testing.T) {
   102  	swig, err := exec.LookPath("swig")
   103  	if err != nil {
   104  		t.Skipf("swig not in PATH: %s", err)
   105  	}
   106  
   107  	// Check that swig was installed with Go support by checking
   108  	// that a go directory exists inside the swiglib directory.
   109  	// See https://golang.org/issue/23469.
   110  	output, err := exec.Command(swig, "-go", "-swiglib").Output()
   111  	if err != nil {
   112  		t.Skip("swig is missing Go support")
   113  	}
   114  	swigDir := strings.TrimSpace(string(output))
   115  
   116  	_, err = os.Stat(filepath.Join(swigDir, "go"))
   117  	if err != nil {
   118  		t.Skip("swig is missing Go support")
   119  	}
   120  
   121  	// Check that swig has a new enough version.
   122  	// See https://golang.org/issue/22858.
   123  	out, err := exec.Command(swig, "-version").CombinedOutput()
   124  	if err != nil {
   125  		t.Skipf("failed to get swig version:%s\n%s", err, string(out))
   126  	}
   127  
   128  	re := regexp.MustCompile(`[vV]ersion +(\d+)([.]\d+)?([.]\d+)?`)
   129  	matches := re.FindSubmatch(out)
   130  	if matches == nil {
   131  		// Can't find version number; hope for the best.
   132  		t.Logf("failed to find swig version, continuing")
   133  		return
   134  	}
   135  
   136  	var parseError error
   137  	atoi := func(s string) int {
   138  		x, err := strconv.Atoi(s)
   139  		if err != nil && parseError == nil {
   140  			parseError = err
   141  		}
   142  		return x
   143  	}
   144  	var major, minor, patch int
   145  	major = atoi(string(matches[1]))
   146  	if len(matches[2]) > 0 {
   147  		minor = atoi(string(matches[2][1:]))
   148  	}
   149  	if len(matches[3]) > 0 {
   150  		patch = atoi(string(matches[3][1:]))
   151  	}
   152  	if parseError != nil {
   153  		t.Logf("error parsing swig version %q, continuing anyway: %s", string(matches[0]), parseError)
   154  		return
   155  	}
   156  	t.Logf("found swig version %d.%d.%d", major, minor, patch)
   157  	if major < 3 || (major == 3 && minor == 0 && patch < 6) {
   158  		t.Skip("test requires swig 3.0.6 or later")
   159  	}
   160  }
   161  

View as plain text