Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion bind/seq/ref.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ func ToRefNum(obj interface{}) int32 {
}
refs.Unlock()

return int32(num)
return num
}

// FromRefNum returns the Ref for a refnum. If the refnum specifies a
Expand Down
6 changes: 5 additions & 1 deletion cmd/gobind/gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -376,10 +376,14 @@ func defaultFileName(lang string, pkg *types.Package) string {
}

func packageDir(path string) (string, error) {
pkgs, err := packages.Load(nil, path)
mode := packages.NeedFiles
pkgs, err := packages.Load(&packages.Config{Mode: mode}, path)
if err != nil {
return "", err
}
if len(pkgs) == 0 || len(pkgs[0].GoFiles) == 0 {
return "", fmt.Errorf("no Go package in %v", path)
}
pkg := pkgs[0]
if len(pkg.Errors) > 0 {
return "", fmt.Errorf("%v", pkg.Errors)
Expand Down
81 changes: 45 additions & 36 deletions cmd/gobind/gobind_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ package main
import (
"bytes"
"fmt"
"io/ioutil"
"log"
"os"
"os/exec"
"runtime"
Expand All @@ -18,6 +16,19 @@ import (
"golang.org/x/tools/go/packages/packagestest"
)

func TestMain(m *testing.M) {
// To avoid recompiling the gobind command (and to support compiler options
// like -race and -coverage), allow the test binary itself to re-exec itself
// as the gobind command by setting an environment variable.
if os.Getenv("GOBIND_TEST_IS_GOBIND") != "" {
main()
os.Exit(0)
}
os.Setenv("GOBIND_TEST_IS_GOBIND", "1")

os.Exit(m.Run())
}

var tests = []struct {
name string
lang string
Expand Down Expand Up @@ -64,55 +75,55 @@ var tests = []struct {
},
}

var gobindBin string

func TestMain(m *testing.M) {
os.Exit(testMain(m))
func mustHaveBindTestdata(t testing.TB) {
switch runtime.GOOS {
case "android", "ios":
t.Skipf("skipping: test cannot access ../../bind/testdata on %s/%s", runtime.GOOS, runtime.GOARCH)
}
}

func testMain(m *testing.M) int {
bin, err := ioutil.TempFile("", "*.exe")
if err != nil {
log.Fatal(err)
func gobindBin(t testing.TB) string {
switch runtime.GOOS {
case "js", "ios":
t.Skipf("skipping: cannot exec subprocess on %s/%s", runtime.GOOS, runtime.GOARCH)
}
bin.Close()
defer os.Remove(bin.Name())
if runtime.GOOS != "android" {
if out, err := exec.Command("go", "build", "-o", bin.Name(), "golang.org/x/mobile/cmd/gobind").CombinedOutput(); err != nil {
log.Fatalf("gobind build failed: %v: %s", err, out)
}
gobindBin = bin.Name()

p, err := os.Executable()
if err != nil {
t.Fatal(err)
}
return m.Run()
return p
}

func runGobind(t testing.TB, lang, pkg, goos string, exported *packagestest.Exported) error {
if gobindBin == "" {
t.Skipf("gobind is not available on %s", runtime.GOOS)
}
cmd := exec.Command(gobindBin, "-lang", lang, pkg)
cmd := exec.Command(gobindBin(t), "-lang", lang, pkg)
cmd.Dir = exported.Config.Dir
cmd.Env = exported.Config.Env
if goos != "" {
// Add CGO_ENABLED=1 explicitly since Cgo is disabled when GOOS is different from host OS.
cmd.Env = append(cmd.Env, "GOOS="+goos, "CGO_ENABLED=1")
}
if out, err := cmd.CombinedOutput(); err != nil {
var cmd string
for _, env := range exported.Config.Env {
if strings.HasPrefix(env, "GO111MODULE=") {
cmd = env + " "
break
}
}
cmd += fmt.Sprintf("gobind -lang %s %s", lang, pkg)
return fmt.Errorf("%s failed: %v: %s", cmd, err, out)
stderr := new(strings.Builder)
cmd.Stderr = stderr
stdout := new(strings.Builder)
cmd.Stdout = stdout
err := cmd.Run()
if testing.Verbose() && stdout.Len() > 0 {
t.Logf("stdout (%v):\n%s", cmd, stderr)
}
if stderr.Len() > 0 {
t.Logf("stderr (%v):\n%s", cmd, stderr)
}
if err != nil {
return fmt.Errorf("%v: %w", cmd, err)
}
return nil
}

func TestGobind(t *testing.T) { packagestest.TestAll(t, testGobind) }
func testGobind(t *testing.T, exporter packagestest.Exporter) {
mustHaveBindTestdata(t)

_, javapErr := exec.LookPath("javap")
exported := packagestest.Export(t, exporter, []packagestest.Module{{
Name: "golang.org/x/mobile",
Expand All @@ -137,9 +148,7 @@ func testGobind(t *testing.T, exporter packagestest.Exporter) {

func TestDocs(t *testing.T) { packagestest.TestAll(t, testDocs) }
func testDocs(t *testing.T, exporter packagestest.Exporter) {
if gobindBin == "" {
t.Skipf("gobind is not available on %s", runtime.GOOS)
}
mustHaveBindTestdata(t)

const docsrc = `
package doctest
Expand All @@ -165,7 +174,7 @@ type Struct struct{

const comment = "This is a comment."
for _, lang := range []string{"java", "objc"} {
cmd := exec.Command(gobindBin, "-lang", lang, "example.com/doctest")
cmd := exec.Command(gobindBin(t), "-lang", lang, "example.com/doctest")
cmd.Dir = exported.Config.Dir
cmd.Env = exported.Config.Env
out, err := cmd.CombinedOutput()
Expand Down
109 changes: 67 additions & 42 deletions cmd/gomobile/bind_iosapp.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,11 @@ func goAppleBind(gobind string, pkgs []*packages.Package, targets []targetInfo)
}

// Run the gobind command for each platform
var wg errgroup.Group
var gobindWG errgroup.Group
for platform, outDir := range outDirsForPlatform {
platform := platform
outDir := outDir
wg.Go(func() error {
gobindWG.Go(func() error {
// Catalyst support requires iOS 13+
v, _ := strconv.ParseFloat(buildIOSVersion, 64)
if platform == "maccatalyst" && v < 13.0 {
Expand Down Expand Up @@ -78,7 +78,7 @@ func goAppleBind(gobind string, pkgs []*packages.Package, targets []targetInfo)
return nil
})
}
if err := wg.Wait(); err != nil {
if err := gobindWG.Wait(); err != nil {
return err
}

Expand All @@ -87,12 +87,60 @@ func goAppleBind(gobind string, pkgs []*packages.Package, targets []targetInfo)
return err
}

// Build archive files.
var buildWG errgroup.Group
for _, t := range targets {
t := t
buildWG.Go(func() error {
outDir := outDirsForPlatform[t.platform]
outSrcDir := filepath.Join(outDir, "src")

if modulesUsed {
// Copy the source directory for each architecture for concurrent building.
newOutSrcDir := filepath.Join(outDir, "src-"+t.arch)
if !buildN {
if err := doCopyAll(newOutSrcDir, outSrcDir); err != nil {
return err
}
}
outSrcDir = newOutSrcDir
}

// Copy the environment variables to make this function concurrent-safe.
env := make([]string, len(appleEnv[t.String()]))
copy(env, appleEnv[t.String()])

// Add the generated packages to GOPATH for reverse bindings.
gopath := fmt.Sprintf("GOPATH=%s%c%s", outDir, filepath.ListSeparator, goEnv("GOPATH"))
env = append(env, gopath)

// Run `go mod tidy` to force to create go.sum.
// Without go.sum, `go build` fails as of Go 1.16.
if modulesUsed {
if err := writeGoMod(outSrcDir, t.platform, t.arch); err != nil {
return err
}
if err := goModTidyAt(outSrcDir, env); err != nil {
return err
}
}

if err := goAppleBindArchive(appleArchiveFilepath(name, t), env, outSrcDir); err != nil {
return fmt.Errorf("%s/%s: %v", t.platform, t.arch, err)
}

return nil
})
}
if err := buildWG.Wait(); err != nil {
return err
}

var frameworkDirs []string
frameworkArchCount := map[string]int{}
for _, t := range targets {
outDir := outDirsForPlatform[t.platform]
outSrcDir := filepath.Join(outDir, "src")
gobindDir := filepath.Join(outSrcDir, "gobind")
gobindDir := filepath.Join(outDir, "src", "gobind")

env := appleEnv[t.String()][:]
sdk := getenv(env, "DARWIN_SDK")
Expand All @@ -101,41 +149,14 @@ func goAppleBind(gobind string, pkgs []*packages.Package, targets []targetInfo)
frameworkDirs = append(frameworkDirs, frameworkDir)
frameworkArchCount[frameworkDir] = frameworkArchCount[frameworkDir] + 1

fileBases := make([]string, len(pkgs)+1)
for i, pkg := range pkgs {
fileBases[i] = bindPrefix + strings.Title(pkg.Name)
}
fileBases[len(fileBases)-1] = "Universe"

// Add the generated packages to GOPATH for reverse bindings.
gopath := fmt.Sprintf("GOPATH=%s%c%s", outDir, filepath.ListSeparator, goEnv("GOPATH"))
env = append(env, gopath)

if err := writeGoMod(filepath.Join(outDir, "src"), t.platform, t.arch); err != nil {
return err
}

// Run `go mod tidy` to force to create go.sum.
// Without go.sum, `go build` fails as of Go 1.16.
if modulesUsed {
if err := goModTidyAt(outSrcDir, env); err != nil {
return err
}
}

path, err := goAppleBindArchive(name+"-"+t.platform+"-"+t.arch, env, outSrcDir)
if err != nil {
return fmt.Errorf("%s/%s: %v", t.platform, t.arch, err)
}

versionsDir := filepath.Join(frameworkDir, "Versions")
versionsADir := filepath.Join(versionsDir, "A")
titlePath := filepath.Join(versionsADir, title)
if frameworkArchCount[frameworkDir] > 1 {
// Not the first static lib, attach to a fat library and skip create headers
fatCmd := exec.Command(
"xcrun",
"lipo", path, titlePath, "-create", "-output", titlePath,
"lipo", appleArchiveFilepath(name, t), titlePath, "-create", "-output", titlePath,
)
if err := runCmd(fatCmd); err != nil {
return err
Expand All @@ -159,12 +180,18 @@ func goAppleBind(gobind string, pkgs []*packages.Package, targets []targetInfo)

lipoCmd := exec.Command(
"xcrun",
"lipo", path, "-create", "-o", titlePath,
"lipo", appleArchiveFilepath(name, t), "-create", "-o", titlePath,
)
if err := runCmd(lipoCmd); err != nil {
return err
}

fileBases := make([]string, len(pkgs)+1)
for i, pkg := range pkgs {
fileBases[i] = bindPrefix + strings.Title(pkg.Name)
}
fileBases[len(fileBases)-1] = "Universe"

// Copy header file next to output archive.
var headerFiles []string
if len(fileBases) == 1 {
Expand Down Expand Up @@ -236,7 +263,6 @@ func goAppleBind(gobind string, pkgs []*packages.Package, targets []targetInfo)
if err != nil {
return err
}

}

// Finally combine all frameworks to an XCFramework
Expand Down Expand Up @@ -267,13 +293,12 @@ var appleModuleMapTmpl = template.Must(template.New("iosmmap").Parse(`framework
export *
}`))

func goAppleBindArchive(name string, env []string, gosrc string) (string, error) {
archive := filepath.Join(tmpdir, name+".a")
err := goBuildAt(gosrc, "./gobind", env, "-buildmode=c-archive", "-o", archive)
if err != nil {
return "", err
}
return archive, nil
func appleArchiveFilepath(name string, t targetInfo) string {
return filepath.Join(tmpdir, name+"-"+t.platform+"-"+t.arch+".a")
}

func goAppleBindArchive(out string, env []string, gosrc string) error {
return goBuildAt(gosrc, "./gobind", env, "-buildmode=c-archive", "-o", out)
}

var appleBindHeaderTmpl = template.Must(template.New("apple.h").Parse(`
Expand Down
10 changes: 5 additions & 5 deletions cmd/gomobile/bind_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,8 +188,8 @@ var bindAndroidTmpl = template.Must(template.New("output").Parse(`GOMOBILE={{.GO
WORK=$WORK
GOOS=android CGO_ENABLED=1 gobind -lang=go,java -outdir=$WORK{{if .JavaPkg}} -javapkg={{.JavaPkg}}{{end}} golang.org/x/mobile/asset
mkdir -p $WORK/src-android-arm
PWD=$WORK/src-android-arm 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
PWD=$WORK/src-android-arm 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
PWD=$WORK/src-android-arm GOMODCACHE=$GOPATH/pkg/mod 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
PWD=$WORK/src-android-arm GOMODCACHE=$GOPATH/pkg/mod 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
PWD=$WORK/java javac -d $WORK/javac-output -source 1.7 -target 1.7 -bootclasspath {{.AndroidPlatform}}/android.jar *.java
jar c -C $WORK/javac-output .
`))
Expand All @@ -198,9 +198,9 @@ var bindAppleTmpl = template.Must(template.New("output").Parse(`GOMOBILE={{.GOPA
WORK=$WORK
rm -r -f "{{.Output}}.xcframework"
GOOS=ios CGO_ENABLED=1 gobind -lang=go,objc -outdir=$WORK/ios -tags=ios{{if .Prefix}} -prefix={{.Prefix}}{{end}} golang.org/x/mobile/asset
mkdir -p $WORK/ios/src
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
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
mkdir -p $WORK/ios/src-arm64
PWD=$WORK/ios/src-arm64 GOMODCACHE=$GOPATH/pkg/mod 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
PWD=$WORK/ios/src-arm64 GOMODCACHE=$GOPATH/pkg/mod 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
mkdir -p $WORK/ios/iphoneos/{{.Output}}.framework/Versions/A/Headers
ln -s A $WORK/ios/iphoneos/{{.Output}}.framework/Versions/Current
ln -s Versions/Current/Headers $WORK/ios/iphoneos/{{.Output}}.framework/Headers
Expand Down
Loading