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-goenv_version.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 versionParse func(uintptr, int32, uintptr, uintptr, uintptr) int32
  31  	purego.RegisterLibFunc(&versionParse, lib, "moxie_version_parse")
  32  
  33  	var versionCompare func(uintptr, int32, uintptr, int32) int32
  34  	purego.RegisterLibFunc(&versionCompare, lib, "moxie_version_compare")
  35  
  36  	// Test Parse: "mx1.2.19"
  37  	testParse(versionParse, "mx1.2.19", 1, 2, 19)
  38  	testParse(versionParse, "go1.24.0", 1, 24, 0)
  39  	testParse(versionParse, "mx1.3", 1, 3, 0)
  40  	testParse(versionParse, "devel mx1.4.5 abc", 1, 4, 5)
  41  
  42  	// Test Parse: invalid inputs
  43  	testParseFail(versionParse, "v1.2.3")
  44  	testParseFail(versionParse, "x")
  45  	testParseFail(versionParse, "mx")
  46  
  47  	// Test Compare
  48  	testCompare(versionCompare, "mx1.2.19", "mx1.2.19", 0)
  49  	testCompare(versionCompare, "mx1.2.18", "mx1.2.19", -1)
  50  	testCompare(versionCompare, "mx1.3.0", "mx1.2.19", 1)
  51  	testCompare(versionCompare, "mx2.0.0", "mx1.9.9", 1)
  52  	testCompare(versionCompare, "go1.24.0", "mx1.24.0", 0)
  53  
  54  	fmt.Println("\nAll goenv version roundtrip tests passed.")
  55  }
  56  
  57  func testParse(fn func(uintptr, int32, uintptr, uintptr, uintptr) int32, version string, wantMaj, wantMin, wantPat int32) {
  58  	data := []byte(version)
  59  	var maj, min, pat int32
  60  	rc := fn(uintptr(unsafe.Pointer(&data[0])), int32(len(data)),
  61  		uintptr(unsafe.Pointer(&maj)), uintptr(unsafe.Pointer(&min)), uintptr(unsafe.Pointer(&pat)))
  62  	if rc != 0 {
  63  		fmt.Fprintf(os.Stderr, "FAIL: parse(%q) returned %d, want 0\n", version, rc)
  64  		os.Exit(1)
  65  	}
  66  	if maj != wantMaj || min != wantMin || pat != wantPat {
  67  		fmt.Fprintf(os.Stderr, "FAIL: parse(%q) = (%d,%d,%d), want (%d,%d,%d)\n",
  68  			version, maj, min, pat, wantMaj, wantMin, wantPat)
  69  		os.Exit(1)
  70  	}
  71  	fmt.Printf("PASS: parse(%q) = (%d,%d,%d)\n", version, maj, min, pat)
  72  }
  73  
  74  func testParseFail(fn func(uintptr, int32, uintptr, uintptr, uintptr) int32, version string) {
  75  	data := []byte(version)
  76  	var maj, min, pat int32
  77  	rc := fn(uintptr(unsafe.Pointer(&data[0])), int32(len(data)),
  78  		uintptr(unsafe.Pointer(&maj)), uintptr(unsafe.Pointer(&min)), uintptr(unsafe.Pointer(&pat)))
  79  	if rc == 0 {
  80  		fmt.Fprintf(os.Stderr, "FAIL: parse(%q) succeeded, want failure\n", version)
  81  		os.Exit(1)
  82  	}
  83  	fmt.Printf("PASS: parse(%q) correctly rejected\n", version)
  84  }
  85  
  86  func testCompare(fn func(uintptr, int32, uintptr, int32) int32, a, b string, want int32) {
  87  	da := []byte(a)
  88  	db := []byte(b)
  89  	got := fn(uintptr(unsafe.Pointer(&da[0])), int32(len(da)),
  90  		uintptr(unsafe.Pointer(&db[0])), int32(len(db)))
  91  	if got != want {
  92  		fmt.Fprintf(os.Stderr, "FAIL: compare(%q, %q) = %d, want %d\n", a, b, got, want)
  93  		os.Exit(1)
  94  	}
  95  	fmt.Printf("PASS: compare(%q, %q) = %d\n", a, b, got)
  96  }
  97  
  98  func openLib(path string) (uintptr, error) {
  99  	switch runtime.GOOS {
 100  	case "linux", "darwin":
 101  		return purego.Dlopen(path, purego.RTLD_NOW|purego.RTLD_GLOBAL)
 102  	default:
 103  		return 0, fmt.Errorf("unsupported OS: %s", runtime.GOOS)
 104  	}
 105  }
 106