aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTed Xie <tedx@google.com>2023-09-28 15:28:33 -0700
committerCopybara-Service <copybara-worker@google.com>2023-09-28 15:29:07 -0700
commit326d834b5a6c95556ca9029dcf33e7d5c176949b (patch)
treefd6a009b7780c6a60896e9d2025c07eb42dd05a0
parente9fe88359fd1867a401df36a5b3d1fce535e0ba2 (diff)
downloadbazelbuild-rules_android-326d834b5a6c95556ca9029dcf33e7d5c176949b.tar.gz
Release src/tools/mi/deployment_oss
Part of #12 PiperOrigin-RevId: 569306937 Change-Id: Icb288200c6027501dd7e18a10acce0107273d7db
-rw-r--r--MODULE.bazel1
-rw-r--r--defs.bzl7
-rw-r--r--go.mod3
-rw-r--r--go.sum2
-rw-r--r--kokoro/presubmit/presubmit_main.sh1
-rw-r--r--src/tools/mi/deployment_oss/BUILD36
-rw-r--r--src/tools/mi/deployment_oss/deploy.go64
-rw-r--r--src/tools/mi/deployment_oss/deploy_binary.go217
8 files changed, 330 insertions, 1 deletions
diff --git a/MODULE.bazel b/MODULE.bazel
index 82c2a0d..39c7032 100644
--- a/MODULE.bazel
+++ b/MODULE.bazel
@@ -32,6 +32,7 @@ go_deps = use_extension("@bazel_gazelle//:extensions.bzl", "go_deps")
go_deps.from_file(go_mod = "//:go.mod")
use_repo(
go_deps,
+ "com_github_golang_glog",
"com_github_google_go_cmp",
"org_golang_google_protobuf",
"org_golang_x_sync",
diff --git a/defs.bzl b/defs.bzl
index fae7a9b..5ae144a 100644
--- a/defs.bzl
+++ b/defs.bzl
@@ -81,6 +81,13 @@ def rules_android_workspace():
version = "v0.0.0-20210220032951-036812b2e83c",
)
+ go_repository(
+ name = "com_github_golang_glog",
+ importpath = "github.com/golang/glog",
+ version = "v1.1.2",
+ sum = "h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo=",
+ )
+
robolectric_repositories()
rules_java_dependencies()
diff --git a/go.mod b/go.mod
index 68f0c90..bd31721 100644
--- a/go.mod
+++ b/go.mod
@@ -1,9 +1,10 @@
module github.com/bazelbuild/rules_android
-go 1.18
+go 1.19
require (
github.com/google/go-cmp v0.5.9
+ github.com/golang/glog v1.1.2
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
google.golang.org/protobuf v1.28.1
)
diff --git a/go.sum b/go.sum
index 23ba436..7c6ca1b 100644
--- a/go.sum
+++ b/go.sum
@@ -1,3 +1,5 @@
+github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo=
+github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
diff --git a/kokoro/presubmit/presubmit_main.sh b/kokoro/presubmit/presubmit_main.sh
index 48d39ff..c47237b 100644
--- a/kokoro/presubmit/presubmit_main.sh
+++ b/kokoro/presubmit/presubmit_main.sh
@@ -109,6 +109,7 @@ function main() {
//src/tools/javatests/... \
//src/tools/jdeps/... \
//src/tools/java/... \
+ //src/tools/mi/... \
//test/...
# Go to basic app workspace in the source tree
diff --git a/src/tools/mi/deployment_oss/BUILD b/src/tools/mi/deployment_oss/BUILD
new file mode 100644
index 0000000..b078ff2
--- /dev/null
+++ b/src/tools/mi/deployment_oss/BUILD
@@ -0,0 +1,36 @@
+load("@bazel_skylib//rules:build_test.bzl", "build_test")
+load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
+
+package(
+ default_applicable_licenses = ["//:license"],
+ default_visibility = ["//src/tools/mi/deployment_oss:__pkg__"],
+)
+
+go_binary(
+ name = "deploy_binary",
+ srcs = ["deploy_binary.go"],
+ visibility = ["//visibility:public"],
+ deps = [
+ "//src/common/golang:flagfile",
+ "//src/common/golang:flags",
+ "//src/common/golang:pprint",
+ "//src/tools/mi/deployment_oss:deployment",
+ "@com_github_golang_glog//:glog",
+ ],
+)
+
+go_library(
+ name = "deployment",
+ importpath = "src/tools/mi/deployment_oss/deployment",
+
+ srcs = [
+ "deploy.go",
+ ],
+ visibility = ["//src/tools/mi:__subpackages__"],
+ deps = ["//src/common/golang:pprint"],
+)
+
+build_test(
+ name = "deploy_binary_build_test",
+ targets = [":deploy_binary"]
+) \ No newline at end of file
diff --git a/src/tools/mi/deployment_oss/deploy.go b/src/tools/mi/deployment_oss/deploy.go
new file mode 100644
index 0000000..1446e58
--- /dev/null
+++ b/src/tools/mi/deployment_oss/deploy.go
@@ -0,0 +1,64 @@
+// Copyright 2018 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package deployment has utilities to sync mobile-install build outputs with a device.
+package deployment
+
+import (
+ "context"
+ "fmt"
+ "os"
+ "os/exec"
+ "strconv"
+
+ "src/common/golang/pprint"
+)
+
+// AndroidStudioSync calls to the Studio deployer with splits.
+func AndroidStudioSync(ctx context.Context, deviceSerial, port, pkg string, splits []string, deployer, adbPath, jdk string, optimisticInstall bool, studioVerboseLog bool, userID int, useADBRoot bool) error {
+ args := []string{"-jar", deployer, "install", pkg}
+ if deviceSerial != "" {
+ args = append(args, fmt.Sprintf("--device=%s", deviceSerial))
+ }
+ args = append(args, "--skip-post-install", "--no-jdwp-client-support")
+ if optimisticInstall {
+ args = append(args, "--optimistic-install")
+ }
+ if useADBRoot {
+ args = append(args, "--use-root-push-install")
+ }
+ if studioVerboseLog {
+ args = append(args, "--log-level=VERBOSE")
+ }
+ if adbPath != "" {
+ args = append(args, fmt.Sprintf("--adb=%s", adbPath))
+ }
+ if userID != 0 {
+ args = append(args, fmt.Sprintf("--user=%s", strconv.Itoa(userID)))
+ }
+ args = append(args, splits...)
+ cmd := exec.Command(jdk, args...)
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+ if port != "" {
+ cmd.Env = append(os.Environ(), fmt.Sprintf("ANDROID_ADB_SERVER_PORT=%s", port))
+ }
+ if studioVerboseLog {
+ pprint.Info("device: %s", deviceSerial)
+ pprint.Info("port: %s", port)
+ pprint.Info("Env: %s", cmd.Env)
+ pprint.Info("Cmd: %s", cmd.String())
+ }
+ return cmd.Run()
+}
diff --git a/src/tools/mi/deployment_oss/deploy_binary.go b/src/tools/mi/deployment_oss/deploy_binary.go
new file mode 100644
index 0000000..503c81a
--- /dev/null
+++ b/src/tools/mi/deployment_oss/deploy_binary.go
@@ -0,0 +1,217 @@
+// Copyright 2018 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// The deploy_binary command unpacks a workspace and deploys it to a device.
+package main
+
+import (
+ "context"
+ "flag"
+ "io/ioutil"
+ "log"
+ "os"
+ "os/exec"
+ "strings"
+ "time"
+
+ glog "github.com/golang/glog"
+
+ _ "src/common/golang/flagfile"
+ "src/common/golang/flags"
+ "src/common/golang/pprint"
+ "src/tools/mi/deployment_oss/deployment"
+)
+
+var (
+ adbArgs = flags.NewStringList("adb_arg", "Options for the adb binary.")
+ adbPath = flag.String("adb", "/usr/bin/adb", "Path to the adb binary to use with mobile-install.")
+ device = flag.String("device", "", "The adb device serial number.")
+ javaHome = flag.String("java_home", "", "Path to JDK.")
+ launchActivity = flag.String("launch_activity", "", "Activity to launch via am start -n package/.activity_to_launch.")
+ appPackagePath = flag.String("manifest_package_name_path", "", "Path to file containing the manifest package name.")
+ splits = flags.NewStringList("splits", "The list of split apk paths.")
+ start = flag.String("start", "", "start_type from mobile-install.")
+ startType = flag.String("start_type", "", "start_type (deprecated, use --start).")
+ useADBRoot = flag.Bool("use_adb_root", true, "whether (true) or not (false) to use root permissions.")
+ userID = flag.Int("user", 0, "User id to install the app for.")
+
+ // Studio deployer args
+ studioDeployerPath = flag.String("studio_deployer", "", "Path to the Android Studio deployer.")
+ optimisticInstall = flag.Bool("optimistic-install", false, "If true, try to push changes to the device without installing.")
+ studioVerboseLog = flag.Bool("studio-verbose-log", false, "If true, enable verbose logging for the Android Studio deployer")
+
+ // Need to double up on launch_app due as the built-in flag module does not support noXX for bool flags.
+ // Some users are using --nolaunch_app, so we need to explicitly declare this flag
+ launchApp = flag.Bool("launch_app", true, "Launch the app after the sync is done.")
+ noLaunchApp = flag.Bool("nolaunch_app", false, "Don't launch the app after the sync is done.")
+ noDeploy = flag.Bool("nodeploy", false, "Don't deploy or launch the app, useful for testing.")
+
+ // Unused flags: Relevant only for Google-internal use cases, but need to exist in the flag parser
+ buildID = flag.String("build_id", "", "The id of the build. Set by Bazel, the user should not use this flag.")
+)
+
+func resolveDeviceSerialAndPort(ctx context.Context, device string) (deviceSerialFlag, port string) {
+ switch {
+ case strings.Contains(device, ":tcp:"):
+ parts := strings.SplitN(device, ":tcp:", 2)
+ deviceSerialFlag = parts[0]
+ port = parts[1]
+ case strings.HasPrefix(device, "tcp:"):
+ port = strings.TrimPrefix(device, "tcp:")
+ default:
+ deviceSerialFlag = device
+ }
+ return deviceSerialFlag, port
+}
+
+func determineDeviceSerial(deviceSerialFlag, deviceSerialEnv, deviceSerialADBArg string) string {
+ var deviceSerial string
+ switch {
+ case deviceSerialFlag != "":
+ deviceSerial = deviceSerialFlag
+ case deviceSerialEnv != "":
+ deviceSerial = deviceSerialEnv
+ case deviceSerialADBArg != "":
+ deviceSerial = deviceSerialADBArg
+ }
+ return deviceSerial
+}
+
+// ReadFile reads file from a given path
+func readFile(path string) []byte {
+ data, err := ioutil.ReadFile(path)
+ if err != nil {
+ log.Fatalf("Error reading file %q: %v", path, err)
+ }
+ return data
+}
+
+func parseRepeatedFlag(n string, a *flags.StringList) {
+ var l []string
+ for _, f := range os.Args {
+ if strings.HasPrefix(f, n) {
+ l = append(l, strings.TrimPrefix(f, n))
+ }
+ }
+ if len(l) > 1 {
+ *a = l
+ }
+}
+
+// Flush all the metrics to Streamz before the program exits.
+func flushAndExitf(ctx context.Context, unused1, unused2, unused3, unused4, format string, args ...any) {
+ glog.Exitf(format, args...)
+}
+
+func main() {
+ ctx := context.Background()
+
+ flag.Parse()
+
+ pprint.Info("Deploying using OSS mobile-install!")
+
+ if *noDeploy {
+ pprint.Warning("--nodeploy set, not deploying application.")
+ return
+ }
+
+ // Override --launch_app if --nolaunch_app is passed
+ if *noLaunchApp {
+ *launchApp = false
+ }
+
+ if *appPackagePath == "" {
+ glog.Exitf("--manifest_package_name is required")
+ }
+
+ // Resolve the device serial and port.
+ var deviceSerialFlag, port string
+ if *device != "" {
+ deviceSerialFlag, port = resolveDeviceSerialAndPort(ctx, *device)
+ }
+ deviceSerialEnv := os.Getenv("ANDROID_SERIAL")
+
+ // TODO(b/66490815): Remove once adb_arg flag is deprecated.
+ // Check for a device serial in adb_arg. If deviceSerial has not been specified, the value
+ // found here will become the deviceSerial. If the deviceSerial has been specified the value
+ // found here will be ignored but we will notify the user the device chosen.
+ var deviceSerialADBArg string
+ for i, arg := range *adbArgs {
+ if strings.TrimSpace(arg) == "-s" && len(*adbArgs) > i+1 {
+ deviceSerialADBArg = (*adbArgs)[i+1]
+ }
+ }
+
+ // TODO(timpeut): Delete after the next blaze release
+ // Ignore the device passed by --adb_arg if it matches the device passed by --device.
+ if deviceSerialADBArg == *device {
+ deviceSerialADBArg = ""
+ }
+
+ // Determine which value to use as the deviceSerial.
+ deviceSerial := determineDeviceSerial(deviceSerialFlag, deviceSerialEnv, deviceSerialADBArg)
+
+ // Warn user of the multiple device serial specification, that is not equal to the first.
+ if (deviceSerialEnv != "" && deviceSerialEnv != deviceSerial) ||
+ (deviceSerialADBArg != "" && deviceSerialADBArg != deviceSerial) {
+ pprint.Warning("A device serial was specified more than once with --device, $ANDROID_SERIAL or --adb_arg, using %s.", deviceSerial)
+ }
+
+ appPackage := strings.TrimSpace(string(readFile(*appPackagePath)))
+
+ startTime := time.Now()
+
+ pprint.Info("Installing application using the Android Studio deployer ...")
+ if err := deployment.AndroidStudioSync(ctx, deviceSerial, port, appPackage, *splits, *studioDeployerPath, *adbPath, *javaHome, *optimisticInstall, *studioVerboseLog, *userID, *useADBRoot); err != nil {
+ flushAndExitf(ctx, "", "", "", "", "Got error installing using the Android Studio deployer: %v", err)
+ }
+
+ deploymentTime := time.Since(startTime)
+ pprint.Info("Took %.2f seconds to sync changes", deploymentTime.Seconds())
+
+ if *startType != "" {
+ *start = *startType
+ }
+
+ // Wait for the debugger if debug mode selected
+ if *start == "DEBUG" {
+ waitCmd := exec.Command(*adbPath, "shell", "am", "set-debug-app", "-w", appPackage)
+ if err := waitCmd.Wait(); err != nil {
+ pprint.Error("Unable to wait for debugger: %s", err.Error())
+ }
+ }
+
+ if *launchApp {
+ pprint.Info("Finished deploying changes. Launching app")
+ var launchCmd *exec.Cmd
+ if *launchActivity != "" {
+ launchCmd = exec.Command(*adbPath, "shell", "am", "start", "-a", "android.intent.action.MAIN", "-n", appPackage+"/"+*launchActivity)
+ } else {
+ launchCmd = exec.Command(*adbPath, "shell", "monkey", "-p", appPackage, "1")
+ pprint.Warning(
+ "No or multiple main activities found, falling back to Monkey launcher. Specify the activity you want with `-- --launch_activity` or `-- --nolaunch_app` to launch nothing.")
+ }
+
+ if err := launchCmd.Run(); err != nil {
+ pprint.Warning("Unable to launch app. Specify an activity with --launch_activity.")
+ pprint.Warning("Original error: %s", err.Error())
+ }
+ } else {
+ // Always stop the app since classloader needs to be reloaded.
+ stopCmd := exec.Command(*adbPath, "shell", "am", "force-stop", appPackage)
+ if err := stopCmd.Run(); err != nil {
+ pprint.Error("Unable to stop app: %s", err.Error())
+ }
+ }
+}