• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2019 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
5package so_test
6
7import (
8	"cmd/cgo/internal/cgotest"
9	"internal/testenv"
10	"log"
11	"os"
12	"os/exec"
13	"path/filepath"
14	"runtime"
15	"strings"
16	"testing"
17)
18
19func TestSO(t *testing.T) {
20	testSO(t, "so")
21}
22
23func TestSOVar(t *testing.T) {
24	testSO(t, "sovar")
25}
26
27func testSO(t *testing.T, dir string) {
28	if runtime.GOOS == "ios" {
29		t.Skip("iOS disallows dynamic loading of user libraries")
30	}
31	testenv.MustHaveGoBuild(t)
32	testenv.MustHaveExec(t)
33	testenv.MustHaveCGO(t)
34
35	GOPATH, err := os.MkdirTemp("", "cgosotest")
36	if err != nil {
37		log.Fatal(err)
38	}
39	defer os.RemoveAll(GOPATH)
40
41	modRoot := filepath.Join(GOPATH, "src", "cgosotest")
42	if err := cgotest.OverlayDir(modRoot, filepath.Join("testdata", dir)); err != nil {
43		log.Panic(err)
44	}
45	if err := os.WriteFile(filepath.Join(modRoot, "go.mod"), []byte("module cgosotest\n"), 0666); err != nil {
46		log.Panic(err)
47	}
48
49	cmd := exec.Command("go", "env", "CC", "GOGCCFLAGS")
50	cmd.Dir = modRoot
51	cmd.Stderr = new(strings.Builder)
52	cmd.Env = append(os.Environ(), "GOPATH="+GOPATH)
53	out, err := cmd.Output()
54	if err != nil {
55		t.Fatalf("%s: %v\n%s", strings.Join(cmd.Args, " "), err, cmd.Stderr)
56	}
57	lines := strings.Split(string(out), "\n")
58	if len(lines) != 3 || lines[2] != "" {
59		t.Fatalf("Unexpected output from %s:\n%s", strings.Join(cmd.Args, " "), lines)
60	}
61
62	cc := lines[0]
63	if cc == "" {
64		t.Fatal("CC environment variable (go env CC) cannot be empty")
65	}
66	gogccflags := strings.Split(lines[1], " ")
67
68	// build shared object
69	ext := "so"
70	args := append(gogccflags, "-shared")
71	switch runtime.GOOS {
72	case "darwin", "ios":
73		ext = "dylib"
74		args = append(args, "-undefined", "suppress", "-flat_namespace")
75	case "windows":
76		ext = "dll"
77		args = append(args, "-DEXPORT_DLL")
78		// At least in mingw-clang it is not permitted to just name a .dll
79		// on the command line. You must name the corresponding import
80		// library instead, even though the dll is used when the executable is run.
81		args = append(args, "-Wl,-out-implib,libcgosotest.a")
82	case "aix":
83		ext = "so.1"
84	}
85	sofname := "libcgosotest." + ext
86	args = append(args, "-o", sofname, "cgoso_c.c")
87
88	cmd = exec.Command(cc, args...)
89	cmd.Dir = modRoot
90	cmd.Env = append(os.Environ(), "GOPATH="+GOPATH)
91	out, err = cmd.CombinedOutput()
92	if err != nil {
93		t.Fatalf("%s: %s\n%s", strings.Join(cmd.Args, " "), err, out)
94	}
95	t.Logf("%s:\n%s", strings.Join(cmd.Args, " "), out)
96
97	if runtime.GOOS == "aix" {
98		// Shared object must be wrapped by an archive
99		cmd = exec.Command("ar", "-X64", "-q", "libcgosotest.a", "libcgosotest.so.1")
100		cmd.Dir = modRoot
101		out, err = cmd.CombinedOutput()
102		if err != nil {
103			t.Fatalf("%s: %s\n%s", strings.Join(cmd.Args, " "), err, out)
104		}
105	}
106
107	cmd = exec.Command("go", "build", "-o", "main.exe", "main.go")
108	cmd.Dir = modRoot
109	cmd.Env = append(os.Environ(), "GOPATH="+GOPATH)
110	out, err = cmd.CombinedOutput()
111	if err != nil {
112		t.Fatalf("%s: %s\n%s", strings.Join(cmd.Args, " "), err, out)
113	}
114	t.Logf("%s:\n%s", strings.Join(cmd.Args, " "), out)
115
116	cmd = exec.Command("./main.exe")
117	cmd.Dir = modRoot
118	cmd.Env = append(os.Environ(), "GOPATH="+GOPATH)
119	if runtime.GOOS != "windows" {
120		s := "LD_LIBRARY_PATH"
121		if runtime.GOOS == "darwin" || runtime.GOOS == "ios" {
122			s = "DYLD_LIBRARY_PATH"
123		}
124		cmd.Env = append(os.Environ(), s+"=.")
125
126		// On FreeBSD 64-bit architectures, the 32-bit linker looks for
127		// different environment variables.
128		if runtime.GOOS == "freebsd" && runtime.GOARCH == "386" {
129			cmd.Env = append(cmd.Env, "LD_32_LIBRARY_PATH=.")
130		}
131	}
132	out, err = cmd.CombinedOutput()
133	if err != nil {
134		t.Fatalf("%s: %s\n%s", strings.Join(cmd.Args, " "), err, out)
135	}
136	t.Logf("%s:\n%s", strings.Join(cmd.Args, " "), out)
137}
138