diff options
author | Ted Xie <tedx@google.com> | 2023-09-28 15:28:33 -0700 |
---|---|---|
committer | Copybara-Service <copybara-worker@google.com> | 2023-09-28 15:29:07 -0700 |
commit | 326d834b5a6c95556ca9029dcf33e7d5c176949b (patch) | |
tree | fd6a009b7780c6a60896e9d2025c07eb42dd05a0 | |
parent | e9fe88359fd1867a401df36a5b3d1fce535e0ba2 (diff) | |
download | bazelbuild-rules_android-326d834b5a6c95556ca9029dcf33e7d5c176949b.tar.gz |
Release src/tools/mi/deployment_oss
Part of #12
PiperOrigin-RevId: 569306937
Change-Id: Icb288200c6027501dd7e18a10acce0107273d7db
-rw-r--r-- | MODULE.bazel | 1 | ||||
-rw-r--r-- | defs.bzl | 7 | ||||
-rw-r--r-- | go.mod | 3 | ||||
-rw-r--r-- | go.sum | 2 | ||||
-rw-r--r-- | kokoro/presubmit/presubmit_main.sh | 1 | ||||
-rw-r--r-- | src/tools/mi/deployment_oss/BUILD | 36 | ||||
-rw-r--r-- | src/tools/mi/deployment_oss/deploy.go | 64 | ||||
-rw-r--r-- | src/tools/mi/deployment_oss/deploy_binary.go | 217 |
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", @@ -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() @@ -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 ) @@ -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()) + } + } +} |