Skip to content

Commit fa6bcb0

Browse files
committed
cmd/gomobile: concurrent gomobile-bind building for Android
This speeds up gomobile-bind for Android by concurrent building for each architecture. Before this change (on my MacBook Pro 2020): ``` $ time go run ./cmd/gomobile/ bind -target android ./example/bind/hello/ real 0m22.555s user 0m14.859s sys 0m10.232s ``` After this change: ``` $ time go run ./cmd/gomobile/ bind -target android ./example/bind/hello/ real 0m9.404s user 0m15.846s sys 0m11.044s ``` For #54770 Change-Id: I5a709dd4422a569e9244e924bd43ad2da1ede164 Reviewed-on: https://go-review.googlesource.com/c/mobile/+/426274 TryBot-Result: Gopher Robot <[email protected]> Reviewed-by: Bryan Mills <[email protected]> Reviewed-by: Dmitri Shuralyov <[email protected]> Reviewed-by: Changkun Ou <[email protected]> Reviewed-by: Dmitri Shuralyov <[email protected]> Run-TryBot: Hajime Hoshi <[email protected]>
1 parent aaac322 commit fa6bcb0

File tree

6 files changed

+90
-48
lines changed

6 files changed

+90
-48
lines changed

cmd/gomobile/bind.go

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"os/exec"
1616
"path/filepath"
1717
"strings"
18+
"sync"
1819

1920
"golang.org/x/mobile/internal/sdkpath"
2021
"golang.org/x/mod/modfile"
@@ -282,7 +283,7 @@ func getModuleVersions(targetPlatform string, targetArch string, src string) (*m
282283
return f, nil
283284
}
284285

285-
// writeGoMod writes go.mod file at $WORK/src when Go modules are used.
286+
// writeGoMod writes go.mod file at dir when Go modules are used.
286287
func writeGoMod(dir, targetPlatform, targetArch string) error {
287288
m, err := areGoModulesUsed()
288289
if err != nil {
@@ -293,7 +294,7 @@ func writeGoMod(dir, targetPlatform, targetArch string) error {
293294
return nil
294295
}
295296

296-
return writeFile(filepath.Join(dir, "src", "go.mod"), func(w io.Writer) error {
297+
return writeFile(filepath.Join(dir, "go.mod"), func(w io.Writer) error {
297298
f, err := getModuleVersions(targetPlatform, targetArch, ".")
298299
if err != nil {
299300
return err
@@ -312,14 +313,23 @@ func writeGoMod(dir, targetPlatform, targetArch string) error {
312313
})
313314
}
314315

315-
func areGoModulesUsed() (bool, error) {
316-
out, err := exec.Command("go", "env", "GOMOD").Output()
317-
if err != nil {
318-
return false, err
319-
}
320-
outstr := strings.TrimSpace(string(out))
321-
if outstr == "" {
322-
return false, nil
316+
var (
317+
areGoModulesUsedResult struct {
318+
used bool
319+
err error
323320
}
324-
return true, nil
321+
areGoModulesUsedOnce sync.Once
322+
)
323+
324+
func areGoModulesUsed() (bool, error) {
325+
areGoModulesUsedOnce.Do(func() {
326+
out, err := exec.Command("go", "env", "GOMOD").Output()
327+
if err != nil {
328+
areGoModulesUsedResult.err = err
329+
return
330+
}
331+
outstr := strings.TrimSpace(string(out))
332+
areGoModulesUsedResult.used = outstr != ""
333+
})
334+
return areGoModulesUsedResult.used, areGoModulesUsedResult.err
325335
}

cmd/gomobile/bind_androidapp.go

Lines changed: 62 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"strings"
1616

1717
"golang.org/x/mobile/internal/sdkpath"
18+
"golang.org/x/sync/errgroup"
1819
"golang.org/x/tools/go/packages"
1920
)
2021

@@ -52,41 +53,16 @@ func goAndroidBind(gobind string, pkgs []*packages.Package, targets []targetInfo
5253

5354
androidDir := filepath.Join(tmpdir, "android")
5455

55-
modulesUsed, err := areGoModulesUsed()
56-
if err != nil {
57-
return err
58-
}
59-
6056
// Generate binding code and java source code only when processing the first package.
57+
var wg errgroup.Group
6158
for _, t := range targets {
62-
if err := writeGoMod(tmpdir, "android", t.arch); err != nil {
63-
return err
64-
}
65-
66-
env := androidEnv[t.arch]
67-
// Add the generated packages to GOPATH for reverse bindings.
68-
gopath := fmt.Sprintf("GOPATH=%s%c%s", tmpdir, filepath.ListSeparator, goEnv("GOPATH"))
69-
env = append(env, gopath)
70-
71-
// Run `go mod tidy` to force to create go.sum.
72-
// Without go.sum, `go build` fails as of Go 1.16.
73-
if modulesUsed {
74-
if err := goModTidyAt(filepath.Join(tmpdir, "src"), env); err != nil {
75-
return err
76-
}
77-
}
78-
79-
toolchain := ndk.Toolchain(t.arch)
80-
err := goBuildAt(
81-
filepath.Join(tmpdir, "src"),
82-
"./gobind",
83-
env,
84-
"-buildmode=c-shared",
85-
"-o="+filepath.Join(androidDir, "src/main/jniLibs/"+toolchain.abi+"/libgojni.so"),
86-
)
87-
if err != nil {
88-
return err
89-
}
59+
t := t
60+
wg.Go(func() error {
61+
return buildAndroidSO(androidDir, t.arch)
62+
})
63+
}
64+
if err := wg.Wait(); err != nil {
65+
return err
9066
}
9167

9268
jsrc := filepath.Join(tmpdir, "java")
@@ -370,3 +346,56 @@ func writeJar(w io.Writer, dir string) error {
370346
}
371347
return jarw.Close()
372348
}
349+
350+
// buildAndroidSO generates an Android libgojni.so file to outputDir.
351+
// buildAndroidSO is concurrent-safe.
352+
func buildAndroidSO(outputDir string, arch string) error {
353+
// Copy the environment variables to make this function concurrent-safe.
354+
env := make([]string, len(androidEnv[arch]))
355+
copy(env, androidEnv[arch])
356+
357+
// Add the generated packages to GOPATH for reverse bindings.
358+
gopath := fmt.Sprintf("GOPATH=%s%c%s", tmpdir, filepath.ListSeparator, goEnv("GOPATH"))
359+
env = append(env, gopath)
360+
361+
modulesUsed, err := areGoModulesUsed()
362+
if err != nil {
363+
return err
364+
}
365+
366+
srcDir := filepath.Join(tmpdir, "src")
367+
368+
if modulesUsed {
369+
// Copy the source directory for each architecture for concurrent building.
370+
newSrcDir := filepath.Join(tmpdir, "src-android-"+arch)
371+
if !buildN {
372+
if err := doCopyAll(newSrcDir, srcDir); err != nil {
373+
return err
374+
}
375+
}
376+
srcDir = newSrcDir
377+
378+
if err := writeGoMod(srcDir, "android", arch); err != nil {
379+
return err
380+
}
381+
382+
// Run `go mod tidy` to force to create go.sum.
383+
// Without go.sum, `go build` fails as of Go 1.16.
384+
if err := goModTidyAt(srcDir, env); err != nil {
385+
return err
386+
}
387+
}
388+
389+
toolchain := ndk.Toolchain(arch)
390+
if err := goBuildAt(
391+
srcDir,
392+
"./gobind",
393+
env,
394+
"-buildmode=c-shared",
395+
"-o="+filepath.Join(outputDir, "src", "main", "jniLibs", toolchain.abi, "libgojni.so"),
396+
); err != nil {
397+
return err
398+
}
399+
400+
return nil
401+
}

cmd/gomobile/bind_iosapp.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ func goAppleBind(gobind string, pkgs []*packages.Package, targets []targetInfo)
9393
gopath := fmt.Sprintf("GOPATH=%s%c%s", outDir, filepath.ListSeparator, goEnv("GOPATH"))
9494
env = append(env, gopath)
9595

96-
if err := writeGoMod(outDir, t.platform, t.arch); err != nil {
96+
if err := writeGoMod(filepath.Join(outDir, "src"), t.platform, t.arch); err != nil {
9797
return err
9898
}
9999

cmd/gomobile/bind_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -187,9 +187,9 @@ func TestBindApple(t *testing.T) {
187187
var bindAndroidTmpl = template.Must(template.New("output").Parse(`GOMOBILE={{.GOPATH}}/pkg/gomobile
188188
WORK=$WORK
189189
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
190+
mkdir -p $WORK/src-android-arm
191+
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
192+
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
193193
PWD=$WORK/java javac -d $WORK/javac-output -source 1.7 -target 1.7 -bootclasspath {{.AndroidPlatform}}/android.jar *.java
194194
jar c -C $WORK/javac-output .
195195
`))

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@ require (
66
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56
77
golang.org/x/image v0.0.0-20190802002840-cff245a6509b
88
golang.org/x/mod v0.4.2
9+
golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde
910
golang.org/x/tools v0.1.8-0.20211022200916-316ba0b74098
1011
)

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
1818
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
1919
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
2020
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
21+
golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde h1:ejfdSekXMDxDLbRrJMwUk6KnSLZ2McaUCVcIKM+N6jc=
22+
golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
2123
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
2224
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
2325
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

0 commit comments

Comments
 (0)