cmd/gomobile: enable Go modules in gomobile-bind
This CL enables Go modules in gomobile-bind command. This CL generates go.mod at $WORK/src based on the modules state of the working directory, and use it when executing go-build. Updates golang/go#27234 Change-Id: I6958f29a317c0d2fb9ffa373f6e3c4cabdc4e898 Reviewed-on: https://go-review.googlesource.com/c/mobile/+/210380 Run-TryBot: Hajime Hoshi <hajimehoshi@gmail.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
This commit is contained in:
parent
1a1fef8273
commit
1d13e329d2
|
@ -5,6 +5,8 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
@ -15,6 +17,7 @@ import (
|
|||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/mod/modfile"
|
||||
"golang.org/x/tools/go/packages"
|
||||
)
|
||||
|
||||
|
@ -102,13 +105,10 @@ func runBind(cmd *command) error {
|
|||
gobind = "gobind"
|
||||
}
|
||||
|
||||
var pkgs []*packages.Package
|
||||
switch len(args) {
|
||||
case 0:
|
||||
pkgs, err = packages.Load(packagesConfig(targetOS), ".")
|
||||
default:
|
||||
pkgs, err = importPackages(args, targetOS)
|
||||
if len(args) == 0 {
|
||||
args = append(args, ".")
|
||||
}
|
||||
pkgs, err := importPackages(args, targetOS)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -196,8 +196,7 @@ func writeFile(filename string, generate func(io.Writer) error) error {
|
|||
fmt.Fprintf(os.Stderr, "write %s\n", filename)
|
||||
}
|
||||
|
||||
err := mkdir(filepath.Dir(filename))
|
||||
if err != nil {
|
||||
if err := mkdir(filepath.Dir(filename)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -230,3 +229,97 @@ func packagesConfig(targetOS string) *packages.Config {
|
|||
}
|
||||
return config
|
||||
}
|
||||
|
||||
// getModuleVersions returns a module information at the directory src.
|
||||
func getModuleVersions(targetOS string, targetArch string, src string) (*modfile.File, error) {
|
||||
cmd := exec.Command(goBin(), "list")
|
||||
cmd.Env = append(os.Environ(), "GOOS="+targetOS, "GOARCH="+targetArch)
|
||||
|
||||
tags := buildTags
|
||||
if targetOS == "darwin" {
|
||||
tags = append(tags, "ios")
|
||||
}
|
||||
cmd.Args = append(cmd.Args, "-m", "-json", "-tags="+strings.Join(tags, ","), "all")
|
||||
cmd.Dir = src
|
||||
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
// Module information is not available at src.
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type Module struct {
|
||||
Path string
|
||||
Version string
|
||||
Dir string
|
||||
Replace *Module
|
||||
}
|
||||
|
||||
f := &modfile.File{}
|
||||
f.AddModuleStmt("gobind")
|
||||
e := json.NewDecoder(bytes.NewReader(output))
|
||||
for {
|
||||
var mod *Module
|
||||
err := e.Decode(&mod)
|
||||
if err != nil && err != io.EOF {
|
||||
return nil, err
|
||||
}
|
||||
if mod != nil {
|
||||
switch {
|
||||
case mod.Replace != nil:
|
||||
f.AddReplace(mod.Path, mod.Version, mod.Replace.Path, mod.Replace.Version)
|
||||
case mod.Version == "":
|
||||
// When the version part is empty, the module is local and mod.Dir represents the location.
|
||||
f.AddReplace(mod.Path, "", mod.Dir, "")
|
||||
default:
|
||||
f.AddRequire(mod.Path, mod.Version)
|
||||
}
|
||||
}
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
|
||||
// writeGoMod writes go.mod file at $WORK/src when Go modules are used.
|
||||
func writeGoMod(targetOS string, targetArch string) error {
|
||||
m, err := areGoModulesUsed()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// If Go modules are not used, go.mod should not be created because the dependencies might not be compatible with Go modules.
|
||||
if !m {
|
||||
return nil
|
||||
}
|
||||
|
||||
return writeFile(filepath.Join(tmpdir, "src", "go.mod"), func(w io.Writer) error {
|
||||
f, err := getModuleVersions(targetOS, targetArch, ".")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if f == nil {
|
||||
return nil
|
||||
}
|
||||
bs, err := f.Format()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write(bs); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func areGoModulesUsed() (bool, error) {
|
||||
out, err := exec.Command(goBin(), "env", "GOMOD").Output()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
outstr := strings.TrimSpace(string(out))
|
||||
if outstr == "" {
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
|
|
@ -54,12 +54,14 @@ func goAndroidBind(gobind string, pkgs []*packages.Package, androidArchs []strin
|
|||
|
||||
// Generate binding code and java source code only when processing the first package.
|
||||
for _, arch := range androidArchs {
|
||||
if err := writeGoMod("android", arch); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
env := androidEnv[arch]
|
||||
// Add the generated packages to GOPATH for reverse bindings.
|
||||
gopath := fmt.Sprintf("GOPATH=%s%c%s", tmpdir, filepath.ListSeparator, goEnv("GOPATH"))
|
||||
env = append(env, gopath)
|
||||
// gomobile-bind does not support modules yet.
|
||||
env = append(env, "GO111MODULE=off")
|
||||
toolchain := ndk.Toolchain(arch)
|
||||
|
||||
err := goBuildAt(
|
||||
|
|
|
@ -57,6 +57,10 @@ func goIOSBind(gobind string, pkgs []*packages.Package, archs []string) error {
|
|||
cmd = exec.Command("xcrun", "lipo", "-create")
|
||||
|
||||
for _, arch := range archs {
|
||||
if err := writeGoMod("darwin", arch); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
env := darwinEnv[arch]
|
||||
// Add the generated packages to GOPATH for reverse bindings.
|
||||
gopath := fmt.Sprintf("GOPATH=%s%c%s", tmpdir, filepath.ListSeparator, goEnv("GOPATH"))
|
||||
|
@ -178,8 +182,6 @@ var iosModuleMapTmpl = template.Must(template.New("iosmmap").Parse(`framework mo
|
|||
func goIOSBindArchive(name string, env []string, gosrc string) (string, error) {
|
||||
arch := getenv(env, "GOARCH")
|
||||
archive := filepath.Join(tmpdir, name+"-"+arch+".a")
|
||||
// gobind-bind does not support modules yet.
|
||||
env = append(env, "GO111MODULE=off")
|
||||
err := goBuildAt(gosrc, "./gobind", env, "-buildmode=c-archive", "-o", archive)
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
|
|
@ -6,7 +6,9 @@ package main
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
|
@ -179,7 +181,8 @@ func TestBindIOS(t *testing.T) {
|
|||
var bindAndroidTmpl = template.Must(template.New("output").Parse(`GOMOBILE={{.GOPATH}}/pkg/gomobile
|
||||
WORK=$WORK
|
||||
GOOS=android CGO_ENABLED=1 gobind -lang=go,java -outdir=$WORK{{if .JavaPkg}} -javapkg={{.JavaPkg}}{{end}} golang.org/x/mobile/asset
|
||||
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 GO111MODULE=off go build -x -buildmode=c-shared -o=$WORK/android/src/main/jniLibs/armeabi-v7a/libgojni.so ./gobind
|
||||
mkdir -p $WORK/src
|
||||
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
|
||||
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 .
|
||||
`))
|
||||
|
@ -187,7 +190,8 @@ jar c -C $WORK/javac-output .
|
|||
var bindIOSTmpl = template.Must(template.New("output").Parse(`GOMOBILE={{.GOPATH}}/pkg/gomobile
|
||||
WORK=$WORK
|
||||
GOOS=darwin CGO_ENABLED=1 gobind -lang=go,objc -outdir=$WORK -tags=ios{{if .Prefix}} -prefix={{.Prefix}}{{end}} golang.org/x/mobile/asset
|
||||
PWD=$WORK/src GOARM=7 GOOS=darwin GOARCH=arm CC=iphoneos-clang CXX=iphoneos-clang++ CGO_CFLAGS=-isysroot=iphoneos -miphoneos-version-min=7.0 -fembed-bitcode -arch armv7 CGO_CXXFLAGS=-isysroot=iphoneos -miphoneos-version-min=7.0 -fembed-bitcode -arch armv7 CGO_LDFLAGS=-isysroot=iphoneos -miphoneos-version-min=7.0 -fembed-bitcode -arch armv7 CGO_ENABLED=1 GOPATH=$WORK:$GOPATH GO111MODULE=off go build -tags ios -x -buildmode=c-archive -o $WORK/asset-arm.a ./gobind
|
||||
mkdir -p $WORK/src
|
||||
PWD=$WORK/src GOARM=7 GOOS=darwin GOARCH=arm CC=iphoneos-clang CXX=iphoneos-clang++ CGO_CFLAGS=-isysroot=iphoneos -miphoneos-version-min=7.0 -fembed-bitcode -arch armv7 CGO_CXXFLAGS=-isysroot=iphoneos -miphoneos-version-min=7.0 -fembed-bitcode -arch armv7 CGO_LDFLAGS=-isysroot=iphoneos -miphoneos-version-min=7.0 -fembed-bitcode -arch armv7 CGO_ENABLED=1 GOPATH=$WORK:$GOPATH go build -tags ios -x -buildmode=c-archive -o $WORK/asset-arm.a ./gobind
|
||||
rm -r -f "Asset.framework"
|
||||
mkdir -p Asset.framework/Versions/A/Headers
|
||||
ln -s A Asset.framework/Versions/Current
|
||||
|
@ -207,3 +211,60 @@ mkdir -p Asset.framework/Resources
|
|||
mkdir -p Asset.framework/Versions/A/Modules
|
||||
ln -s Versions/Current/Modules Asset.framework/Modules
|
||||
`))
|
||||
|
||||
func TestBindWithGoModules(t *testing.T) {
|
||||
if runtime.GOOS == "android" {
|
||||
t.Skipf("gomobile and gobind are not available on %s", runtime.GOOS)
|
||||
}
|
||||
|
||||
dir, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
if out, err := exec.Command(goBin(), "build", "-o="+dir, "golang.org/x/mobile/cmd/gobind").CombinedOutput(); err != nil {
|
||||
t.Fatalf("%v: %s", err, string(out))
|
||||
}
|
||||
if out, err := exec.Command(goBin(), "build", "-o="+dir, "golang.org/x/mobile/cmd/gomobile").CombinedOutput(); err != nil {
|
||||
t.Fatalf("%v: %s", err, string(out))
|
||||
}
|
||||
path := dir
|
||||
if p := os.Getenv("PATH"); p != "" {
|
||||
path += string(filepath.ListSeparator) + p
|
||||
}
|
||||
|
||||
for _, target := range []string{"android", "ios"} {
|
||||
t.Run(target, func(t *testing.T) {
|
||||
switch target {
|
||||
case "android":
|
||||
androidHome := os.Getenv("ANDROID_HOME")
|
||||
if androidHome == "" {
|
||||
t.Skip("ANDROID_HOME not found, skipping bind")
|
||||
}
|
||||
if _, err := androidAPIPath(); err != nil {
|
||||
t.Skip("No android API platform found in $ANDROID_HOME, skipping bind")
|
||||
}
|
||||
case "ios":
|
||||
if !xcodeAvailable() {
|
||||
t.Skip("Xcode is missing")
|
||||
}
|
||||
}
|
||||
|
||||
var out string
|
||||
switch target {
|
||||
case "android":
|
||||
out = filepath.Join(dir, "cgopkg.aar")
|
||||
case "ios":
|
||||
out = filepath.Join(dir, "Cgopkg.framework")
|
||||
}
|
||||
cmd := exec.Command(filepath.Join(dir, "gomobile"), "bind", "-target="+target, "-o="+out, "golang.org/x/mobile/bind/testdata/cgopkg")
|
||||
cmd.Env = append(os.Environ(), "PATH="+path, "GO111MODULE=on")
|
||||
var b bytes.Buffer
|
||||
cmd.Stderr = &b
|
||||
if err := cmd.Run(); err != nil {
|
||||
t.Errorf("%v: %s", err, string(b.Bytes()))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
3
go.mod
3
go.mod
|
@ -5,5 +5,6 @@ go 1.11
|
|||
require (
|
||||
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b
|
||||
golang.org/x/tools v0.0.0-20190909214602-067311248421
|
||||
golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e
|
||||
)
|
||||
|
|
10
go.sum
10
go.sum
|
@ -1,13 +1,17 @@
|
|||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 h1:estk1glOnSVeJ9tdEZZc5mAMDZk5lNJNyJ6DvrBkTEU=
|
||||
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mod v0.1.0 h1:sfUMP1Gu8qASkorDVjnMuvgJzwFbTZSeXFiGBYAVdl4=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd h1:ePuNC7PZ6O5BzgPn9bZayERXBdfZjUYoXEf5BTfDfh8=
|
||||
golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
|
@ -17,6 +21,8 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5
|
|||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190909214602-067311248421 h1:NmmWqJbt02YJHmp4A4gBXvsXXIzzixjzE1y6PKUyIjk=
|
||||
golang.org/x/tools v0.0.0-20190909214602-067311248421/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e h1:aZzprAO9/8oim3qStq3wc1Xuxx4QmAGriC4VU4ojemQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
|
Loading…
Reference in New Issue