github.com/coming-chat/gomobile@v0.0.0-20220601074111-56995f7d7aba/cmd/gomobile/bind_test.go (about) 1 // Copyright 2015 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 5 package main 6 7 import ( 8 "bytes" 9 "io/ioutil" 10 "os" 11 "os/exec" 12 "path/filepath" 13 "runtime" 14 "strings" 15 "testing" 16 "text/template" 17 18 "golang.org/x/mobile/internal/sdkpath" 19 ) 20 21 func TestBindAndroid(t *testing.T) { 22 platform, err := sdkpath.AndroidAPIPath(minAndroidAPI) 23 if err != nil { 24 t.Skip("No compatible Android API platform found, skipping bind") 25 } 26 // platform is a path like "/path/to/Android/sdk/platforms/android-32" 27 components := strings.Split(platform, string(filepath.Separator)) 28 if len(components) < 2 { 29 t.Fatalf("API path is too short: %s", platform) 30 } 31 components = components[len(components)-2:] 32 platformRel := filepath.Join("$ANDROID_HOME", components[0], components[1]) 33 34 defer func() { 35 xout = os.Stderr 36 buildN = false 37 buildX = false 38 buildO = "" 39 buildTarget = "" 40 bindJavaPkg = "" 41 }() 42 buildN = true 43 buildX = true 44 buildO = "asset.aar" 45 buildTarget = "android/arm" 46 47 tests := []struct { 48 javaPkg string 49 }{ 50 { 51 // Empty javaPkg 52 }, 53 { 54 javaPkg: "com.example.foo", 55 }, 56 } 57 for _, tc := range tests { 58 bindJavaPkg = tc.javaPkg 59 60 buf := new(bytes.Buffer) 61 xout = buf 62 gopath = filepath.SplitList(goEnv("GOPATH"))[0] 63 if goos == "windows" { 64 os.Setenv("HOMEDRIVE", "C:") 65 } 66 cmdBind.flag.Parse([]string{"golang.org/x/mobile/asset"}) 67 err := runBind(cmdBind) 68 if err != nil { 69 t.Log(buf.String()) 70 t.Fatal(err) 71 } 72 got := filepath.ToSlash(buf.String()) 73 74 output, err := defaultOutputData("") 75 if err != nil { 76 t.Fatal(err) 77 } 78 data := struct { 79 outputData 80 AndroidPlatform string 81 JavaPkg string 82 }{ 83 outputData: output, 84 AndroidPlatform: platformRel, 85 JavaPkg: tc.javaPkg, 86 } 87 88 wantBuf := new(bytes.Buffer) 89 if err := bindAndroidTmpl.Execute(wantBuf, data); err != nil { 90 t.Errorf("%+v: computing diff failed: %v", tc, err) 91 continue 92 } 93 94 diff, err := diff(got, wantBuf.String()) 95 if err != nil { 96 t.Errorf("%+v: computing diff failed: %v", tc, err) 97 continue 98 } 99 if diff != "" { 100 t.Errorf("%+v: unexpected output:\n%s", tc, diff) 101 } 102 } 103 } 104 105 func TestBindApple(t *testing.T) { 106 if !xcodeAvailable() { 107 t.Skip("Xcode is missing") 108 } 109 defer func() { 110 xout = os.Stderr 111 buildN = false 112 buildX = false 113 buildO = "" 114 buildTarget = "" 115 bindPrefix = "" 116 }() 117 buildN = true 118 buildX = true 119 buildO = "Asset.xcframework" 120 buildTarget = "ios/arm64" 121 122 tests := []struct { 123 prefix string 124 out string 125 }{ 126 { 127 // empty prefix 128 }, 129 { 130 prefix: "Foo", 131 }, 132 { 133 out: "Abcde.xcframework", 134 }, 135 } 136 for _, tc := range tests { 137 bindPrefix = tc.prefix 138 if tc.out != "" { 139 buildO = tc.out 140 } 141 142 buf := new(bytes.Buffer) 143 xout = buf 144 gopath = filepath.SplitList(goEnv("GOPATH"))[0] 145 if goos == "windows" { 146 os.Setenv("HOMEDRIVE", "C:") 147 } 148 cmdBind.flag.Parse([]string{"golang.org/x/mobile/asset"}) 149 if err := runBind(cmdBind); err != nil { 150 t.Log(buf.String()) 151 t.Fatal(err) 152 } 153 got := filepath.ToSlash(buf.String()) 154 155 output, err := defaultOutputData("") 156 if err != nil { 157 t.Fatal(err) 158 } 159 160 data := struct { 161 outputData 162 Output string 163 Prefix string 164 }{ 165 outputData: output, 166 Output: buildO[:len(buildO)-len(".xcframework")], 167 Prefix: tc.prefix, 168 } 169 170 wantBuf := new(bytes.Buffer) 171 if err := bindAppleTmpl.Execute(wantBuf, data); err != nil { 172 t.Errorf("%+v: computing diff failed: %v", tc, err) 173 continue 174 } 175 176 diff, err := diff(got, wantBuf.String()) 177 if err != nil { 178 t.Errorf("%+v: computing diff failed: %v", tc, err) 179 continue 180 } 181 if diff != "" { 182 t.Errorf("%+v: unexpected output:\n%s", tc, diff) 183 } 184 } 185 } 186 187 var bindAndroidTmpl = template.Must(template.New("output").Parse(`GOMOBILE={{.GOPATH}}/pkg/gomobile 188 WORK=$WORK 189 GOOS=android CGO_ENABLED=1 gobind -lang=go,java -outdir=$WORK{{if .JavaPkg}} -javapkg={{.JavaPkg}}{{end}} golang.org/x/mobile/asset 190 mkdir -p $WORK/src 191 PWD=$WORK/src GOOS=android GOARCH=arm CC=$NDK_PATH/toolchains/llvm/prebuilt/{{.NDKARCH}}/bin/armv7a-linux-androideabi16-clang CXX=$NDK_PATH/toolchains/llvm/prebuilt/{{.NDKARCH}}/bin/armv7a-linux-androideabi16-clang++ CGO_ENABLED=1 GOARM=7 GOPATH=$WORK:$GOPATH go mod tidy 192 PWD=$WORK/src GOOS=android GOARCH=arm CC=$NDK_PATH/toolchains/llvm/prebuilt/{{.NDKARCH}}/bin/armv7a-linux-androideabi16-clang CXX=$NDK_PATH/toolchains/llvm/prebuilt/{{.NDKARCH}}/bin/armv7a-linux-androideabi16-clang++ CGO_ENABLED=1 GOARM=7 GOPATH=$WORK:$GOPATH go build -x -buildmode=c-shared -o=$WORK/android/src/main/jniLibs/armeabi-v7a/libgojni.so ./gobind 193 PWD=$WORK/java javac -d $WORK/javac-output -source 1.7 -target 1.7 -bootclasspath {{.AndroidPlatform}}/android.jar *.java 194 jar c -C $WORK/javac-output . 195 `)) 196 197 var bindAppleTmpl = template.Must(template.New("output").Parse(`GOMOBILE={{.GOPATH}}/pkg/gomobile 198 WORK=$WORK 199 rm -r -f "{{.Output}}.xcframework" 200 GOOS=ios CGO_ENABLED=1 gobind -lang=go,objc -outdir=$WORK/ios -tags=ios{{if .Prefix}} -prefix={{.Prefix}}{{end}} golang.org/x/mobile/asset 201 mkdir -p $WORK/ios/src 202 PWD=$WORK/ios/src GOOS=ios GOARCH=arm64 GOFLAGS=-tags=ios CC=iphoneos-clang CXX=iphoneos-clang++ CGO_CFLAGS=-isysroot iphoneos -miphoneos-version-min=13.0 -fembed-bitcode -arch arm64 CGO_CXXFLAGS=-isysroot iphoneos -miphoneos-version-min=13.0 -fembed-bitcode -arch arm64 CGO_LDFLAGS=-isysroot iphoneos -miphoneos-version-min=13.0 -fembed-bitcode -arch arm64 CGO_ENABLED=1 DARWIN_SDK=iphoneos GOPATH=$WORK/ios:$GOPATH go mod tidy 203 PWD=$WORK/ios/src GOOS=ios GOARCH=arm64 GOFLAGS=-tags=ios CC=iphoneos-clang CXX=iphoneos-clang++ CGO_CFLAGS=-isysroot iphoneos -miphoneos-version-min=13.0 -fembed-bitcode -arch arm64 CGO_CXXFLAGS=-isysroot iphoneos -miphoneos-version-min=13.0 -fembed-bitcode -arch arm64 CGO_LDFLAGS=-isysroot iphoneos -miphoneos-version-min=13.0 -fembed-bitcode -arch arm64 CGO_ENABLED=1 DARWIN_SDK=iphoneos GOPATH=$WORK/ios:$GOPATH go build -x -buildmode=c-archive -o $WORK/{{.Output}}-ios-arm64.a ./gobind 204 mkdir -p $WORK/ios/iphoneos/{{.Output}}.framework/Versions/A/Headers 205 ln -s A $WORK/ios/iphoneos/{{.Output}}.framework/Versions/Current 206 ln -s Versions/Current/Headers $WORK/ios/iphoneos/{{.Output}}.framework/Headers 207 ln -s Versions/Current/{{.Output}} $WORK/ios/iphoneos/{{.Output}}.framework/{{.Output}} 208 xcrun lipo $WORK/{{.Output}}-ios-arm64.a -create -o $WORK/ios/iphoneos/{{.Output}}.framework/Versions/A/{{.Output}} 209 cp $WORK/ios/src/gobind/{{.Prefix}}Asset.objc.h $WORK/ios/iphoneos/{{.Output}}.framework/Versions/A/Headers/{{.Prefix}}Asset.objc.h 210 mkdir -p $WORK/ios/iphoneos/{{.Output}}.framework/Versions/A/Headers 211 cp $WORK/ios/src/gobind/Universe.objc.h $WORK/ios/iphoneos/{{.Output}}.framework/Versions/A/Headers/Universe.objc.h 212 mkdir -p $WORK/ios/iphoneos/{{.Output}}.framework/Versions/A/Headers 213 cp $WORK/ios/src/gobind/ref.h $WORK/ios/iphoneos/{{.Output}}.framework/Versions/A/Headers/ref.h 214 mkdir -p $WORK/ios/iphoneos/{{.Output}}.framework/Versions/A/Headers 215 mkdir -p $WORK/ios/iphoneos/{{.Output}}.framework/Versions/A/Headers 216 mkdir -p $WORK/ios/iphoneos/{{.Output}}.framework/Versions/A/Resources 217 ln -s Versions/Current/Resources $WORK/ios/iphoneos/{{.Output}}.framework/Resources 218 mkdir -p $WORK/ios/iphoneos/{{.Output}}.framework/Resources 219 mkdir -p $WORK/ios/iphoneos/{{.Output}}.framework/Versions/A/Modules 220 ln -s Versions/Current/Modules $WORK/ios/iphoneos/{{.Output}}.framework/Modules 221 xcodebuild -create-xcframework -framework $WORK/ios/iphoneos/{{.Output}}.framework -output {{.Output}}.xcframework 222 `)) 223 224 func TestBindAppleAll(t *testing.T) { 225 if !xcodeAvailable() { 226 t.Skip("Xcode is missing") 227 } 228 defer func() { 229 xout = os.Stderr 230 buildN = false 231 buildX = false 232 buildO = "" 233 buildTarget = "" 234 bindPrefix = "" 235 }() 236 buildN = true 237 buildX = true 238 buildO = "Asset.xcframework" 239 buildTarget = "ios" 240 241 buf := new(bytes.Buffer) 242 xout = buf 243 gopath = filepath.SplitList(goEnv("GOPATH"))[0] 244 if goos == "windows" { 245 os.Setenv("HOMEDRIVE", "C:") 246 } 247 cmdBind.flag.Parse([]string{"golang.org/x/mobile/asset"}) 248 if err := runBind(cmdBind); err != nil { 249 t.Log(buf.String()) 250 t.Fatal(err) 251 } 252 } 253 254 func TestBindWithGoModules(t *testing.T) { 255 if runtime.GOOS == "android" || runtime.GOOS == "ios" { 256 t.Skipf("gomobile and gobind are not available on %s", runtime.GOOS) 257 } 258 259 dir, err := ioutil.TempDir("", "gomobile-test") 260 if err != nil { 261 t.Fatal(err) 262 } 263 defer os.RemoveAll(dir) 264 265 if out, err := exec.Command("go", "build", "-o="+dir, "golang.org/x/mobile/cmd/gobind").CombinedOutput(); err != nil { 266 t.Fatalf("%v: %s", err, string(out)) 267 } 268 if out, err := exec.Command("go", "build", "-o="+dir, "golang.org/x/mobile/cmd/gomobile").CombinedOutput(); err != nil { 269 t.Fatalf("%v: %s", err, string(out)) 270 } 271 path := dir 272 if p := os.Getenv("PATH"); p != "" { 273 path += string(filepath.ListSeparator) + p 274 } 275 276 for _, target := range []string{"android", "ios"} { 277 t.Run(target, func(t *testing.T) { 278 switch target { 279 case "android": 280 if _, err := sdkpath.AndroidAPIPath(minAndroidAPI); err != nil { 281 t.Skip("No compatible Android API platform found, skipping bind") 282 } 283 case "ios": 284 if !xcodeAvailable() { 285 t.Skip("Xcode is missing") 286 } 287 } 288 289 var out string 290 switch target { 291 case "android": 292 out = filepath.Join(dir, "cgopkg.aar") 293 case "ios": 294 out = filepath.Join(dir, "Cgopkg.xcframework") 295 } 296 297 tests := []struct { 298 Name string 299 Path string 300 Dir string 301 }{ 302 { 303 Name: "Absolute Path", 304 Path: "golang.org/x/mobile/bind/testdata/cgopkg", 305 }, 306 { 307 Name: "Relative Path", 308 Path: "./bind/testdata/cgopkg", 309 Dir: filepath.Join("..", ".."), 310 }, 311 } 312 313 for _, tc := range tests { 314 tc := tc 315 t.Run(tc.Name, func(t *testing.T) { 316 cmd := exec.Command(filepath.Join(dir, "gomobile"), "bind", "-target="+target, "-o="+out, tc.Path) 317 cmd.Env = append(os.Environ(), "PATH="+path, "GO111MODULE=on") 318 cmd.Dir = tc.Dir 319 if out, err := cmd.CombinedOutput(); err != nil { 320 t.Errorf("gomobile bind failed: %v\n%s", err, string(out)) 321 } 322 }) 323 } 324 }) 325 } 326 }