mirror of
https://github.com/sliverarmory/reflektor
synced 2026-08-25 09:14:48 +00:00
177 lines
4.4 KiB
Go
177 lines
4.4 KiB
Go
package reflektor_test
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func buildOneGoSharedLib(t *testing.T, outDir string, goos string, goarch string) string {
|
|
t.Helper()
|
|
|
|
ext, err := sharedLibExt(goos)
|
|
if err != nil {
|
|
t.Fatalf("build go shared library target=%s/%s: %v", goos, goarch, err)
|
|
}
|
|
|
|
outputPath := filepath.Join(outDir, fmt.Sprintf("basic_go_%s-%s.%s", goos, goarch, ext))
|
|
sourcePath := "./testdata/go/basic"
|
|
|
|
args := []string{
|
|
"build",
|
|
"-buildmode=c-shared",
|
|
"-trimpath",
|
|
"-o", outputPath,
|
|
sourcePath,
|
|
}
|
|
|
|
baseEnv := overrideEnv(os.Environ(), map[string]string{
|
|
"GOOS": goos,
|
|
"GOARCH": goarch,
|
|
"CGO_ENABLED": "1",
|
|
"GOCACHE": filepath.Join(os.TempDir(), "reflektor-go-build-cache"),
|
|
})
|
|
|
|
var (
|
|
out []byte
|
|
)
|
|
if goos == runtime.GOOS && goarch == runtime.GOARCH {
|
|
// Build native fixtures with the platform compiler. Apart from matching
|
|
// the production ABI most closely, this keeps a cross-linker defect from
|
|
// masquerading as a loader failure. In particular, Zig/LLD's linux/386
|
|
// Go c-shared output crashes even when loaded by the native dlopen.
|
|
cmd := exec.Command("go", args...)
|
|
cmd.Env = withoutEnv(baseEnv, "CC", "CXX")
|
|
out, err = cmd.CombinedOutput()
|
|
if err == nil {
|
|
cleanupGoSharedSidecars(outputPath, ext)
|
|
return outputPath
|
|
}
|
|
t.Logf("go build with native compiler failed for %s/%s, retrying with zig cc: %v\n%s", goos, goarch, err, out)
|
|
}
|
|
|
|
if _, err := exec.LookPath("zig"); err == nil {
|
|
cmd := exec.Command("go", args...)
|
|
cc := "zig cc"
|
|
cxx := "zig c++"
|
|
if target, ok := zigTargetFor(goos, goarch); ok {
|
|
cc = "zig cc -target " + target
|
|
cxx = "zig c++ -target " + target
|
|
}
|
|
cmd.Env = overrideEnv(baseEnv, map[string]string{
|
|
"CC": cc,
|
|
"CXX": cxx,
|
|
})
|
|
out, err = cmd.CombinedOutput()
|
|
if err == nil {
|
|
cleanupGoSharedSidecars(outputPath, ext)
|
|
return outputPath
|
|
}
|
|
t.Logf("go build with zig cc failed for %s/%s, retrying with default compiler: %v\n%s", goos, goarch, err, out)
|
|
}
|
|
|
|
cmd := exec.Command("go", args...)
|
|
cmd.Env = baseEnv
|
|
out, err = cmd.CombinedOutput()
|
|
if err != nil {
|
|
t.Fatalf("build go shared lib target=%s/%s: %v\n%s", goos, goarch, err, out)
|
|
}
|
|
|
|
cleanupGoSharedSidecars(outputPath, ext)
|
|
return outputPath
|
|
}
|
|
|
|
func zigTargetFor(goos string, goarch string) (string, bool) {
|
|
switch {
|
|
case goos == "darwin" && goarch == "amd64":
|
|
return "x86_64-macos", true
|
|
case goos == "darwin" && goarch == "arm64":
|
|
return "aarch64-macos", true
|
|
case goos == "linux" && goarch == "386":
|
|
return "x86-linux-gnu", true
|
|
case goos == "linux" && goarch == "amd64":
|
|
return "x86_64-linux-gnu", true
|
|
case goos == "linux" && goarch == "arm64":
|
|
return "aarch64-linux-gnu", true
|
|
case goos == "windows" && goarch == "386":
|
|
return "x86-windows-gnu", true
|
|
case goos == "windows" && goarch == "amd64":
|
|
return "x86_64-windows-gnu", true
|
|
case goos == "windows" && goarch == "arm64":
|
|
return "aarch64-windows-gnu", true
|
|
default:
|
|
return "", false
|
|
}
|
|
}
|
|
|
|
func sharedLibExt(goos string) (string, error) {
|
|
switch goos {
|
|
case "darwin":
|
|
return "dylib", nil
|
|
case "linux":
|
|
return "so", nil
|
|
case "windows":
|
|
return "dll", nil
|
|
default:
|
|
return "", fmt.Errorf("unsupported target os: %s", goos)
|
|
}
|
|
}
|
|
|
|
func cleanupGoSharedSidecars(outputPath string, ext string) {
|
|
base := strings.TrimSuffix(outputPath, "."+ext)
|
|
_ = os.Remove(base + ".h")
|
|
if runtime.GOOS == "windows" || strings.EqualFold(ext, "dll") {
|
|
_ = os.Remove(base + ".lib")
|
|
_ = os.Remove(base + ".exp")
|
|
_ = os.Remove(base + ".pdb")
|
|
}
|
|
}
|
|
|
|
func overrideEnv(base []string, overrides map[string]string) []string {
|
|
block := make(map[string]struct{}, len(overrides))
|
|
for key := range overrides {
|
|
block[key] = struct{}{}
|
|
}
|
|
|
|
out := make([]string, 0, len(base)+len(overrides))
|
|
for _, kv := range base {
|
|
eq := strings.IndexByte(kv, '=')
|
|
if eq <= 0 {
|
|
continue
|
|
}
|
|
if _, drop := block[kv[:eq]]; drop {
|
|
continue
|
|
}
|
|
out = append(out, kv)
|
|
}
|
|
|
|
for key, value := range overrides {
|
|
out = append(out, key+"="+value)
|
|
}
|
|
return out
|
|
}
|
|
|
|
func withoutEnv(base []string, keys ...string) []string {
|
|
remove := make(map[string]struct{}, len(keys))
|
|
for _, key := range keys {
|
|
remove[key] = struct{}{}
|
|
}
|
|
|
|
out := make([]string, 0, len(base))
|
|
for _, kv := range base {
|
|
eq := strings.IndexByte(kv, '=')
|
|
if eq <= 0 {
|
|
continue
|
|
}
|
|
if _, drop := remove[kv[:eq]]; drop {
|
|
continue
|
|
}
|
|
out = append(out, kv)
|
|
}
|
|
return out
|
|
}
|