// Copyright 2015 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 com.google.devtools.build.lib.rules.android; import com.google.common.collect.ImmutableList; import com.google.devtools.build.lib.actions.ActionExecutionContext; import com.google.devtools.build.lib.actions.ActionOwner; import com.google.devtools.build.lib.actions.Artifact; import com.google.devtools.build.lib.actions.ExecException; import com.google.devtools.build.lib.analysis.actions.AbstractFileWriteAction; import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; import com.google.devtools.build.lib.util.Fingerprint; import com.google.devtools.common.options.EnumConverter; import com.google.devtools.common.options.Option; import com.google.devtools.common.options.OptionsBase; import java.io.IOException; import java.io.OutputStream; import java.io.PrintStream; import java.util.List; /** * An action that writes the a parameter file to {@code incremental_install.py} based on the command * line arguments to {@code blaze mobile-install}. */ @Immutable // note that it accesses data non-hermetically during the execution phase public final class WriteAdbArgsAction extends AbstractFileWriteAction { private static final String GUID = "16720416-3c01-4b0a-a543-ead7e563a1ca"; /** * Options of the {@code mobile-install} command pertaining to the way {@code adb} is invoked. */ public static final class Options extends OptionsBase { @Option(name = "adb", category = "mobile-install", defaultValue = "", help = "adb binary to use for the 'mobile-install' command. If unspecified, the one in " + "the Android SDK specified by the --android_sdk command line option (or the default " + "SDK if --android_sdk is not specified) is used.") public String adb; @Option(name = "adb_arg", category = "mobile-install", allowMultiple = true, defaultValue = "", help = "Extra arguments to pass to adb. Usually used to designate a device to install to.") public List<String> adbArgs; @Option(name = "adb_jobs", category = "mobile-install", defaultValue = "2", help = "The number of instances of adb to use in parallel to update files on the device") public int adbJobs; @Option(name = "incremental_install_verbosity", category = "mobile-install", defaultValue = "", help = "The verbosity for incremental install. Set to 1 for debug logging.") public String incrementalInstallVerbosity; @Option(name = "start", category = "mobile-install", converter = StartTypeConverter.class, defaultValue = "NO", help = "How the app should be started after installing it. Set to WARM to preserve " + "and restore application state on incremental installs.") public StartType start; @Option(name = "start_app", category = "mobile-install", defaultValue = "null", help = "Whether to start the app after installing it.", expansion = {"--start=COLD"}) public Void startApp; @Option(name = "debug_app", category = "mobile-install", defaultValue = "null", help = "Whether to wait for the debugger before starting the app.", expansion = {"--start=DEBUG"}) public Void debugApp; } public WriteAdbArgsAction(ActionOwner owner, Artifact outputFile) { super(owner, ImmutableList.<Artifact>of(), outputFile, false); } @Override public DeterministicWriter newDeterministicWriter(ActionExecutionContext ctx) throws IOException, InterruptedException, ExecException { Options options = ctx.getExecutor().getOptions().getOptions(Options.class); final List<String> args = options.adbArgs; final String adb = options.adb; final int adbJobs = options.adbJobs; final String incrementalInstallVerbosity = options.incrementalInstallVerbosity; final StartType start = options.start; final String userHomeDirectory = ctx.getExecutor().getContext( WriteAdbArgsActionContext.class).getUserHomeDirectory(); return new DeterministicWriter() { @Override public void writeOutputFile(OutputStream out) throws IOException { PrintStream ps = new PrintStream(out, false, "UTF-8"); if (!adb.isEmpty()) { ps.printf("--adb=%s\n", adb); } for (String arg : args) { ps.printf("--extra_adb_arg=%s\n", arg); } ps.printf("--adb_jobs=%d\n", adbJobs); if (!incrementalInstallVerbosity.isEmpty()) { ps.printf("--verbosity=%s\n", incrementalInstallVerbosity); } ps.printf("--start=%s\n", start.name().toLowerCase()); if (userHomeDirectory != null) { ps.printf("--user_home_dir=%s\n", userHomeDirectory); } ps.flush(); } }; } @Override public boolean isVolatile() { return true; } @Override public boolean executeUnconditionally() { // In theory, we only need to re-execute if the --adb_args command line arg changes, but we // cannot express this. We also can't put the ADB args in the configuration, because that would // mean re-analysis on every change, and then the "build" command would also have this argument, // which is not optimal. return true; } @Override protected String computeKey() { return new Fingerprint() .addString(GUID) .hexDigestAndReset(); } /** Specifies how the app should be started/stopped. */ public enum StartType { /** The app will not be restarted after install. */ NO, /** The app will be restarted from a clean state after install. */ COLD, /** * The app will save its state before installing, and be restored from that state after * installing. */ WARM, /** The app will wait for debugger to attach before restarting from clean state after install */ DEBUG } /** Converter for the --start option. */ public static class StartTypeConverter extends EnumConverter<StartType> { public StartTypeConverter() { super(StartType.class, "start type"); } } }