so_test.go 3.61 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
// Copyright 2019 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.

// +build cgo

package so_test

import (
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"testing"
)

func requireTestSOSupported(t *testing.T) {
	t.Helper()
	switch runtime.GOARCH {
	case "arm", "arm64":
		if runtime.GOOS == "darwin" {
			t.Skip("No exec facility on iOS.")
		}
	case "ppc64":
28 29 30
		if runtime.GOOS == "linux" {
			t.Skip("External linking not implemented on aix/ppc64 (issue #8912).")
		}
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
	case "mips64le", "mips64":
		t.Skip("External linking not implemented on mips64.")
	}
	if runtime.GOOS == "android" {
		t.Skip("No exec facility on Android.")
	}
}

func TestSO(t *testing.T) {
	requireTestSOSupported(t)

	GOPATH, err := ioutil.TempDir("", "cgosotest")
	if err != nil {
		log.Fatal(err)
	}
	defer os.RemoveAll(GOPATH)

	modRoot := filepath.Join(GOPATH, "src", "cgosotest")
	if err := overlayDir(modRoot, "testdata"); err != nil {
		log.Panic(err)
	}
	if err := ioutil.WriteFile(filepath.Join(modRoot, "go.mod"), []byte("module cgosotest\n"), 0666); err != nil {
		log.Panic(err)
	}

	cmd := exec.Command("go", "env", "CC", "GOGCCFLAGS")
	cmd.Dir = modRoot
	cmd.Stderr = new(strings.Builder)
	cmd.Env = append(os.Environ(), "GOPATH="+GOPATH)
	out, err := cmd.Output()
	if err != nil {
		t.Fatalf("%s: %v\n%s", strings.Join(cmd.Args, " "), err, cmd.Stderr)
	}
	lines := strings.Split(string(out), "\n")
	if len(lines) != 3 || lines[2] != "" {
		t.Fatalf("Unexpected output from %s:\n%s", strings.Join(cmd.Args, " "), lines)
	}

	cc := lines[0]
	if cc == "" {
		t.Fatal("CC environment variable (go env CC) cannot be empty")
	}
	gogccflags := strings.Split(lines[1], " ")

	// build shared object
	ext := "so"
	args := append(gogccflags, "-shared")
	switch runtime.GOOS {
	case "darwin":
		ext = "dylib"
		args = append(args, "-undefined", "suppress", "-flat_namespace")
	case "windows":
		ext = "dll"
		args = append(args, "-DEXPORT_DLL")
85 86
	case "aix":
		ext = "so.1"
87 88 89 90 91 92 93 94 95 96 97 98 99
	}
	sofname := "libcgosotest." + ext
	args = append(args, "-o", sofname, "cgoso_c.c")

	cmd = exec.Command(cc, args...)
	cmd.Dir = modRoot
	cmd.Env = append(os.Environ(), "GOPATH="+GOPATH)
	out, err = cmd.CombinedOutput()
	if err != nil {
		t.Fatalf("%s: %s\n%s", strings.Join(cmd.Args, " "), err, out)
	}
	t.Logf("%s:\n%s", strings.Join(cmd.Args, " "), out)

100 101 102 103 104 105 106 107 108 109
	if runtime.GOOS == "aix" {
		// Shared object must be wrapped by an archive
		cmd = exec.Command("ar", "-X64", "-q", "libcgosotest.a", "libcgosotest.so.1")
		cmd.Dir = modRoot
		out, err = cmd.CombinedOutput()
		if err != nil {
			t.Fatalf("%s: %s\n%s", strings.Join(cmd.Args, " "), err, out)
		}
	}

110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
	cmd = exec.Command("go", "build", "-o", "main.exe", "main.go")
	cmd.Dir = modRoot
	cmd.Env = append(os.Environ(), "GOPATH="+GOPATH)
	out, err = cmd.CombinedOutput()
	if err != nil {
		t.Fatalf("%s: %s\n%s", strings.Join(cmd.Args, " "), err, out)
	}
	t.Logf("%s:\n%s", strings.Join(cmd.Args, " "), out)

	cmd = exec.Command("./main.exe")
	cmd.Dir = modRoot
	cmd.Env = append(os.Environ(), "GOPATH="+GOPATH)
	if runtime.GOOS != "windows" {
		s := "LD_LIBRARY_PATH"
		if runtime.GOOS == "darwin" {
			s = "DYLD_LIBRARY_PATH"
		}
		cmd.Env = append(os.Environ(), s+"=.")

		// On FreeBSD 64-bit architectures, the 32-bit linker looks for
		// different environment variables.
		if runtime.GOOS == "freebsd" && runtime.GOARCH == "386" {
			cmd.Env = append(cmd.Env, "LD_32_LIBRARY_PATH=.")
		}
	}
	out, err = cmd.CombinedOutput()
	if err != nil {
		t.Fatalf("%s: %s\n%s", strings.Join(cmd.Args, " "), err, out)
	}
	t.Logf("%s:\n%s", strings.Join(cmd.Args, " "), out)
}