github.com/F4RD1N/gomobile@v1.0.1/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 19 func TestBindAndroid(t *testing.T) { 20 androidHome := os.Getenv("ANDROID_HOME") 21 if androidHome == "" { 22 t.Skip("ANDROID_HOME not found, skipping bind") 23 } 24 platform, err := androidAPIPath() 25 if err != nil { 26 t.Skip("No android API platform found in $ANDROID_HOME, skipping bind") 27 } 28 platform = strings.Replace(platform, androidHome, "$ANDROID_HOME", -1) 29 30 defer func() { 31 xout = os.Stderr 32 buildN = false 33 buildX = false 34 buildO = "" 35 buildTarget = "" 36 bindJavaPkg = "" 37 }() 38 buildN = true 39 buildX = true 40 buildO = "asset.aar" 41 buildTarget = "android/arm" 42 43 tests := []struct { 44 javaPkg string 45 }{ 46 { 47 // Empty javaPkg 48 }, 49 { 50 javaPkg: "com.example.foo", 51 }, 52 } 53 for _, tc := range tests { 54 bindJavaPkg = tc.javaPkg 55 56 buf := new(bytes.Buffer) 57 xout = buf 58 gopath = filepath.SplitList(goEnv("GOPATH"))[0] 59 if goos == "windows" { 60 os.Setenv("HOMEDRIVE", "C:") 61 } 62 cmdBind.flag.Parse([]string{"github.com/F4RD1N/gomobile/asset"}) 63 err := runBind(cmdBind) 64 if err != nil { 65 t.Log(buf.String()) 66 t.Fatal(err) 67 } 68 got := filepath.ToSlash(buf.String()) 69 70 output, err := defaultOutputData() 71 if err != nil { 72 t.Fatal(err) 73 } 74 data := struct { 75 outputData 76 AndroidPlatform string 77 JavaPkg string 78 }{ 79 outputData: output, 80 AndroidPlatform: platform, 81 JavaPkg: tc.javaPkg, 82 } 83 84 wantBuf := new(bytes.Buffer) 85 if err := bindAndroidTmpl.Execute(wantBuf, data); err != nil { 86 t.Errorf("%+v: computing diff failed: %v", tc, err) 87 continue 88 } 89 90 diff, err := diff(got, wantBuf.String()) 91 if err != nil { 92 t.Errorf("%+v: computing diff failed: %v", tc, err) 93 continue 94 } 95 if diff != "" { 96 t.Errorf("%+v: unexpected output:\n%s", tc, diff) 97 } 98 } 99 } 100 101 func TestBindIOS(t *testing.T) { 102 if !xcodeAvailable() { 103 t.Skip("Xcode is missing") 104 } 105 defer func() { 106 xout = os.Stderr 107 buildN = false 108 buildX = false 109 buildO = "" 110 buildTarget = "" 111 bindPrefix = "" 112 }() 113 buildN = true 114 buildX = true 115 buildO = "Asset.framework" 116 buildTarget = "ios/arm64" 117 118 tests := []struct { 119 prefix string 120 out string 121 }{ 122 { 123 // empty prefix 124 }, 125 { 126 prefix: "Foo", 127 }, 128 { 129 out: "Abcde.framework", 130 }, 131 } 132 for _, tc := range tests { 133 bindPrefix = tc.prefix 134 if tc.out != "" { 135 buildO = tc.out 136 } 137 138 buf := new(bytes.Buffer) 139 xout = buf 140 gopath = filepath.SplitList(goEnv("GOPATH"))[0] 141 if goos == "windows" { 142 os.Setenv("HOMEDRIVE", "C:") 143 } 144 cmdBind.flag.Parse([]string{"github.com/F4RD1N/gomobile/asset"}) 145 if err := runBind(cmdBind); err != nil { 146 t.Log(buf.String()) 147 t.Fatal(err) 148 } 149 got := filepath.ToSlash(buf.String()) 150 151 output, err := defaultOutputData() 152 if err != nil { 153 t.Fatal(err) 154 } 155 156 data := struct { 157 outputData 158 Output string 159 Prefix string 160 BitcodeEnabled bool 161 }{ 162 outputData: output, 163 Output: buildO[:len(buildO)-len(".framework")], 164 Prefix: tc.prefix, 165 BitcodeEnabled: bitcodeEnabled, 166 } 167 168 wantBuf := new(bytes.Buffer) 169 if err := bindIOSTmpl.Execute(wantBuf, data); err != nil { 170 t.Errorf("%+v: computing diff failed: %v", tc, err) 171 continue 172 } 173 174 diff, err := diff(got, wantBuf.String()) 175 if err != nil { 176 t.Errorf("%+v: computing diff failed: %v", tc, err) 177 continue 178 } 179 if diff != "" { 180 t.Errorf("%+v: unexpected output:\n%s", tc, diff) 181 } 182 } 183 } 184 185 var bindAndroidTmpl = template.Must(template.New("output").Parse(`GOMOBILE={{.GOPATH}}/pkg/gomobile 186 WORK=$WORK 187 GOOS=android CGO_ENABLED=1 gobind -lang=go,java -outdir=$WORK{{if .JavaPkg}} -javapkg={{.JavaPkg}}{{end}} github.com/F4RD1N/gomobile/asset 188 mkdir -p $WORK/src 189 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 190 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 191 PWD=$WORK/java javac -d $WORK/javac-output -source 1.7 -target 1.7 -bootclasspath {{.AndroidPlatform}}/android.jar *.java 192 jar c -C $WORK/javac-output . 193 `)) 194 195 var bindIOSTmpl = template.Must(template.New("output").Parse(`GOMOBILE={{.GOPATH}}/pkg/gomobile 196 WORK=$WORK 197 GOOS=darwin CGO_ENABLED=1 gobind -lang=go,objc -outdir=$WORK -tags=ios{{if .Prefix}} -prefix={{.Prefix}}{{end}} github.com/F4RD1N/gomobile/asset 198 mkdir -p $WORK/src 199 PWD=$WORK/src GOOS=darwin GOARCH=arm64 CC=iphoneos-clang CXX=iphoneos-clang++ CGO_CFLAGS=-isysroot=iphoneos -miphoneos-version-min=7.0 {{if .BitcodeEnabled}}-fembed-bitcode {{end}}-arch arm64 CGO_CXXFLAGS=-isysroot=iphoneos -miphoneos-version-min=7.0 {{if .BitcodeEnabled}}-fembed-bitcode {{end}}-arch arm64 CGO_LDFLAGS=-isysroot=iphoneos -miphoneos-version-min=7.0 {{if .BitcodeEnabled}}-fembed-bitcode {{end}}-arch arm64 CGO_ENABLED=1 GOPATH=$WORK:$GOPATH go build -tags ios -x -buildmode=c-archive -o $WORK/{{.Output}}-arm64.a ./gobind 200 rm -r -f "{{.Output}}.framework" 201 mkdir -p {{.Output}}.framework/Versions/A/Headers 202 ln -s A {{.Output}}.framework/Versions/Current 203 ln -s Versions/Current/Headers {{.Output}}.framework/Headers 204 ln -s Versions/Current/{{.Output}} {{.Output}}.framework/{{.Output}} 205 xcrun lipo -create -arch arm64 $WORK/{{.Output}}-arm64.a -o {{.Output}}.framework/Versions/A/{{.Output}} 206 cp $WORK/src/gobind/{{.Prefix}}Asset.objc.h {{.Output}}.framework/Versions/A/Headers/{{.Prefix}}Asset.objc.h 207 mkdir -p {{.Output}}.framework/Versions/A/Headers 208 cp $WORK/src/gobind/Universe.objc.h {{.Output}}.framework/Versions/A/Headers/Universe.objc.h 209 mkdir -p {{.Output}}.framework/Versions/A/Headers 210 cp $WORK/src/gobind/ref.h {{.Output}}.framework/Versions/A/Headers/ref.h 211 mkdir -p {{.Output}}.framework/Versions/A/Headers 212 mkdir -p {{.Output}}.framework/Versions/A/Headers 213 mkdir -p {{.Output}}.framework/Versions/A/Resources 214 ln -s Versions/Current/Resources {{.Output}}.framework/Resources 215 mkdir -p {{.Output}}.framework/Resources 216 mkdir -p {{.Output}}.framework/Versions/A/Modules 217 ln -s Versions/Current/Modules {{.Output}}.framework/Modules 218 `)) 219 220 func TestBindIOSAll(t *testing.T) { 221 if !xcodeAvailable() { 222 t.Skip("Xcode is missing") 223 } 224 defer func() { 225 xout = os.Stderr 226 buildN = false 227 buildX = false 228 buildO = "" 229 buildTarget = "" 230 bindPrefix = "" 231 }() 232 buildN = true 233 buildX = true 234 buildO = "Asset.framework" 235 buildTarget = "ios" 236 237 buf := new(bytes.Buffer) 238 xout = buf 239 gopath = filepath.SplitList(goEnv("GOPATH"))[0] 240 if goos == "windows" { 241 os.Setenv("HOMEDRIVE", "C:") 242 } 243 cmdBind.flag.Parse([]string{"github.com/F4RD1N/gomobile/asset"}) 244 if err := runBind(cmdBind); err != nil { 245 t.Log(buf.String()) 246 t.Fatal(err) 247 } 248 } 249 250 func TestBindWithGoModules(t *testing.T) { 251 if runtime.GOOS == "android" { 252 t.Skipf("gomobile and gobind are not available on %s", runtime.GOOS) 253 } 254 255 dir, err := ioutil.TempDir("", "gomobile-test") 256 if err != nil { 257 t.Fatal(err) 258 } 259 defer os.RemoveAll(dir) 260 261 if out, err := exec.Command("go", "build", "-o="+dir, "github.com/F4RD1N/gomobile/cmd/gobind").CombinedOutput(); err != nil { 262 t.Fatalf("%v: %s", err, string(out)) 263 } 264 if out, err := exec.Command("go", "build", "-o="+dir, "github.com/F4RD1N/gomobile/cmd/gomobile").CombinedOutput(); err != nil { 265 t.Fatalf("%v: %s", err, string(out)) 266 } 267 path := dir 268 if p := os.Getenv("PATH"); p != "" { 269 path += string(filepath.ListSeparator) + p 270 } 271 272 for _, target := range []string{"android", "ios"} { 273 t.Run(target, func(t *testing.T) { 274 switch target { 275 case "android": 276 androidHome := os.Getenv("ANDROID_HOME") 277 if androidHome == "" { 278 t.Skip("ANDROID_HOME not found, skipping bind") 279 } 280 if _, err := androidAPIPath(); err != nil { 281 t.Skip("No android API platform found in $ANDROID_HOME, 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.framework") 295 } 296 297 tests := []struct { 298 Name string 299 Path string 300 Dir string 301 }{ 302 { 303 Name: "Absolute Path", 304 Path: "github.com/F4RD1N/gomobile/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 }