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 28 29 30 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 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 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 141
|
// 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 (
"log"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"testing"
)
func requireTestSOSupported(t *testing.T) {
t.Helper()
switch runtime.GOARCH {
case "arm64":
if runtime.GOOS == "darwin" || runtime.GOOS == "ios" {
t.Skip("No exec facility on iOS.")
}
case "ppc64":
if runtime.GOOS == "linux" {
t.Skip("External linking not implemented on linux/ppc64 (issue #8912).")
}
}
if runtime.GOOS == "android" {
t.Skip("No exec facility on Android.")
}
}
func TestSO(t *testing.T) {
requireTestSOSupported(t)
GOPATH, err := os.MkdirTemp("", "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 := os.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", "ios":
ext = "dylib"
args = append(args, "-undefined", "suppress", "-flat_namespace")
case "windows":
ext = "dll"
args = append(args, "-DEXPORT_DLL")
// At least in mingw-clang it is not permitted to just name a .dll
// on the command line. You must name the corresponding import
// library instead, even though the dll is used when the executable is run.
args = append(args, "-Wl,-out-implib,libcgosotest.a")
case "aix":
ext = "so.1"
}
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)
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)
}
}
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" || runtime.GOOS == "ios" {
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)
}
|