main.go raw
1 package main
2
3 import (
4 "fmt"
5 "os"
6 "path/filepath"
7 "runtime"
8 "unsafe"
9
10 "github.com/ebitengine/purego"
11 )
12
13 func main() {
14 if len(os.Args) < 2 {
15 fmt.Fprintf(os.Stderr, "usage: %s <path-to-compileopts.so>\n", os.Args[0])
16 os.Exit(1)
17 }
18 soPath := os.Args[1]
19 if !filepath.IsAbs(soPath) {
20 wd, _ := os.Getwd()
21 soPath = filepath.Join(wd, soPath)
22 }
23
24 lib, err := openLib(soPath)
25 if err != nil {
26 fmt.Fprintf(os.Stderr, "dlopen %s: %v\n", soPath, err)
27 os.Exit(1)
28 }
29
30 var targetLoad func(uintptr, int32, uintptr, int32, int32) int32
31 purego.RegisterLibFunc(&targetLoad, lib, "moxie_target_load")
32
33 var targetTriple func(int32, uintptr, int32) int32
34 purego.RegisterLibFunc(&targetTriple, lib, "moxie_target_triple")
35
36 var targetCPU func(int32, uintptr, int32) int32
37 purego.RegisterLibFunc(&targetCPU, lib, "moxie_target_cpu")
38
39 var targetFeatures func(int32, uintptr, int32) int32
40 purego.RegisterLibFunc(&targetFeatures, lib, "moxie_target_features")
41
42 var targetLinker func(int32, uintptr, int32) int32
43 purego.RegisterLibFunc(&targetLinker, lib, "moxie_target_linker")
44
45 var targetLibc func(int32, uintptr, int32) int32
46 purego.RegisterLibFunc(&targetLibc, lib, "moxie_target_libc")
47
48 var targetStackSize func(int32) int64
49 purego.RegisterLibFunc(&targetStackSize, lib, "moxie_target_stack_size")
50
51 var targetLdflagCount func(int32) int32
52 purego.RegisterLibFunc(&targetLdflagCount, lib, "moxie_target_ldflag_count")
53
54 var targetLdflag func(int32, int32, uintptr, int32) int32
55 purego.RegisterLibFunc(&targetLdflag, lib, "moxie_target_ldflag")
56
57 var targetExtraFileCount func(int32) int32
58 purego.RegisterLibFunc(&targetExtraFileCount, lib, "moxie_target_extra_file_count")
59
60 var targetExtraFile func(int32, int32, uintptr, int32) int32
61 purego.RegisterLibFunc(&targetExtraFile, lib, "moxie_target_extra_file")
62
63 var targetBuildTagCount func(int32) int32
64 purego.RegisterLibFunc(&targetBuildTagCount, lib, "moxie_target_build_tag_count")
65
66 var targetBuildTag func(int32, int32, uintptr, int32) int32
67 purego.RegisterLibFunc(&targetBuildTag, lib, "moxie_target_build_tag")
68
69 var targetOptLevel func(uintptr, int32, uintptr, uintptr) int32
70 purego.RegisterLibFunc(&targetOptLevel, lib, "moxie_target_opt_level")
71
72 var targetCanonicalArch func(uintptr, int32, uintptr, int32) int32
73 purego.RegisterLibFunc(&targetCanonicalArch, lib, "moxie_target_canonical_arch")
74
75 var targetFree func(int32)
76 purego.RegisterLibFunc(&targetFree, lib, "moxie_target_free")
77
78 // Test linux/amd64
79 h := loadTarget(targetLoad, "linux", "amd64", 0)
80 expectS(targetTriple, h, "x86_64-unknown-linux-musleabihf", "linux/amd64 triple")
81 expectS(targetCPU, h, "x86-64", "linux/amd64 cpu")
82
83 expectS(targetLinker, h, "ld.lld", "linux/amd64 linker")
84 expectS(targetLibc, h, "musl", "linux/amd64 libc")
85 expectI64(targetStackSize(h), 65536, "linux/amd64 stack_size")
86 expectContainsFile(targetExtraFile, targetExtraFileCount, h, "src/runtime/asm_amd64.S", "linux/amd64 asm file")
87 targetFree(h)
88
89 // Test darwin/arm64
90 h = loadTarget(targetLoad, "darwin", "arm64", 0)
91 expectS(targetTriple, h, "arm64-apple-macosx11.0.0", "darwin/arm64 triple")
92 expectS(targetCPU, h, "generic", "darwin/arm64 cpu")
93 expectS(targetLibc, h, "darwin-libSystem", "darwin/arm64 libc")
94 targetFree(h)
95
96 // Test js/wasm
97 h = loadTarget(targetLoad, "js", "wasm", 0)
98 expectS(targetTriple, h, "wasm32-unknown-js", "js/wasm triple")
99 expectS(targetCPU, h, "generic", "js/wasm cpu")
100 expectS(targetLinker, h, "wasm-ld", "js/wasm linker")
101 targetFree(h)
102
103 // Test c-shared mode adds --shared ldflag
104 h = loadTarget(targetLoad, "linux", "amd64", 1)
105 expectContainsLdflag(targetLdflag, targetLdflagCount, h, "--shared", "c-shared --shared ldflag")
106 targetFree(h)
107
108 // Test opt levels
109 testOptLevel(targetOptLevel, "0", 0, 0)
110 testOptLevel(targetOptLevel, "1", 1, 0)
111 testOptLevel(targetOptLevel, "2", 2, 0)
112 testOptLevel(targetOptLevel, "s", 2, 1)
113 testOptLevel(targetOptLevel, "z", 2, 2)
114 testOptLevel(targetOptLevel, "none", 0, 0)
115
116 // Test canonical arch
117 testCanonicalArch(targetCanonicalArch, "x86_64-unknown-linux-musleabihf", "x86_64")
118 testCanonicalArch(targetCanonicalArch, "arm64-apple-macosx11.0.0", "aarch64")
119 testCanonicalArch(targetCanonicalArch, "wasm32-unknown-js", "wasm32")
120
121 // Test invalid inputs
122 goos := []byte("windows")
123 goarch := []byte("amd64")
124 bad := targetLoad(uintptr(unsafe.Pointer(&goos[0])), int32(len(goos)),
125 uintptr(unsafe.Pointer(&goarch[0])), int32(len(goarch)), 0)
126 if bad != -1 {
127 fmt.Fprintf(os.Stderr, "FAIL: windows/amd64 should fail\n")
128 os.Exit(1)
129 }
130 fmt.Println("PASS: windows/amd64 correctly rejected")
131
132 fmt.Println("\nAll compileopts roundtrip tests passed.")
133 }
134
135 func loadTarget(fn func(uintptr, int32, uintptr, int32, int32) int32, goos, goarch string, buildMode int32) int32 {
136 g := []byte(goos)
137 a := []byte(goarch)
138 h := fn(uintptr(unsafe.Pointer(&g[0])), int32(len(g)),
139 uintptr(unsafe.Pointer(&a[0])), int32(len(a)), buildMode)
140 if h < 0 {
141 fmt.Fprintf(os.Stderr, "FAIL: target_load(%s, %s) returned %d\n", goos, goarch, h)
142 os.Exit(1)
143 }
144 fmt.Printf("PASS: target_load(%s/%s) = handle %d\n", goos, goarch, h)
145 return h
146 }
147
148 func getString(fn func(int32, uintptr, int32) int32, h int32) string {
149 buf := make([]byte, 512)
150 n := fn(h, uintptr(unsafe.Pointer(&buf[0])), int32(len(buf)))
151 if n < 0 {
152 return ""
153 }
154 return string(buf[:n])
155 }
156
157 func getStringIdx(fn func(int32, int32, uintptr, int32) int32, h, idx int32) string {
158 buf := make([]byte, 512)
159 n := fn(h, idx, uintptr(unsafe.Pointer(&buf[0])), int32(len(buf)))
160 if n < 0 {
161 return ""
162 }
163 return string(buf[:n])
164 }
165
166 func expectS(fn func(int32, uintptr, int32) int32, h int32, want, label string) {
167 got := getString(fn, h)
168 if got != want {
169 fmt.Fprintf(os.Stderr, "FAIL: %s = %q, want %q\n", label, got, want)
170 os.Exit(1)
171 }
172 fmt.Printf("PASS: %s = %q\n", label, got)
173 }
174
175 func expectI64(got, want int64, label string) {
176 if got != want {
177 fmt.Fprintf(os.Stderr, "FAIL: %s = %d, want %d\n", label, got, want)
178 os.Exit(1)
179 }
180 fmt.Printf("PASS: %s = %d\n", label, got)
181 }
182
183 func expectContainsFile(fileFn func(int32, int32, uintptr, int32) int32, countFn func(int32) int32, h int32, want, label string) {
184 count := countFn(h)
185 for i := int32(0); i < count; i++ {
186 got := getStringIdx(fileFn, h, i)
187 if got == want {
188 fmt.Printf("PASS: %s found at index %d\n", label, i)
189 return
190 }
191 }
192 fmt.Fprintf(os.Stderr, "FAIL: %s not found in %d extra files\n", label, count)
193 os.Exit(1)
194 }
195
196 func expectContainsLdflag(flagFn func(int32, int32, uintptr, int32) int32, countFn func(int32) int32, h int32, want, label string) {
197 count := countFn(h)
198 for i := int32(0); i < count; i++ {
199 got := getStringIdx(flagFn, h, i)
200 if got == want {
201 fmt.Printf("PASS: %s found at index %d\n", label, i)
202 return
203 }
204 }
205 fmt.Fprintf(os.Stderr, "FAIL: %s not found in %d ldflags\n", label, count)
206 os.Exit(1)
207 }
208
209 func testOptLevel(fn func(uintptr, int32, uintptr, uintptr) int32, opt string, wantSpeed, wantSize int32) {
210 data := []byte(opt)
211 var speed, size int32
212 rc := fn(uintptr(unsafe.Pointer(&data[0])), int32(len(data)),
213 uintptr(unsafe.Pointer(&speed)), uintptr(unsafe.Pointer(&size)))
214 if rc != 0 {
215 fmt.Fprintf(os.Stderr, "FAIL: opt_level(%q) returned %d\n", opt, rc)
216 os.Exit(1)
217 }
218 if speed != wantSpeed || size != wantSize {
219 fmt.Fprintf(os.Stderr, "FAIL: opt_level(%q) = (%d,%d), want (%d,%d)\n", opt, speed, size, wantSpeed, wantSize)
220 os.Exit(1)
221 }
222 fmt.Printf("PASS: opt_level(%q) = speed=%d, size=%d\n", opt, speed, size)
223 }
224
225 func testCanonicalArch(fn func(uintptr, int32, uintptr, int32) int32, triple, want string) {
226 data := []byte(triple)
227 buf := make([]byte, 128)
228 n := fn(uintptr(unsafe.Pointer(&data[0])), int32(len(data)),
229 uintptr(unsafe.Pointer(&buf[0])), int32(len(buf)))
230 got := string(buf[:n])
231 if got != want {
232 fmt.Fprintf(os.Stderr, "FAIL: canonical_arch(%q) = %q, want %q\n", triple, got, want)
233 os.Exit(1)
234 }
235 fmt.Printf("PASS: canonical_arch(%q) = %q\n", triple, got)
236 }
237
238 func openLib(path string) (uintptr, error) {
239 switch runtime.GOOS {
240 case "linux", "darwin":
241 return purego.Dlopen(path, purego.RTLD_NOW|purego.RTLD_GLOBAL)
242 default:
243 return 0, fmt.Errorf("unsupported OS: %s", runtime.GOOS)
244 }
245 }
246