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  	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  		// On the builders we're using the default /usr/bin/ld, but
    49  		// that has problems when asking for LTO in particular. Force
    50  		// use of lld, which ships with our clang installation.
    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  	// Ask the go tool for the CXX it's configured to use.
    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  	// The first call will skip t with a nice message. On later calls, we just skip.
    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  	// Check that swig was installed with Go support by checking
   109  	// that a go directory exists inside the swiglib directory.
   110  	// See https://golang.org/issue/23469.
   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  	// Check that swig has a new enough version.
   123  	// See https://golang.org/issue/22858.
   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  		// Can't find version number; hope for the best.
   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