/* * Copyright 2012-present Facebook, Inc. * * 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.facebook.buck.cli; import com.facebook.buck.android.AdbHelper; import com.facebook.buck.android.HasInstallableApk; import com.facebook.buck.event.ConsoleEvent; import com.facebook.buck.json.BuildFileParseException; import com.facebook.buck.model.BuildTarget; import com.facebook.buck.model.BuildTargetException; import com.facebook.buck.parser.NoSuchBuildTargetException; import com.facebook.buck.rules.BuildRule; import com.facebook.buck.rules.BuildRuleResolver; import com.facebook.buck.rules.SourcePathResolver; import com.facebook.buck.rules.SourcePathRuleFinder; import com.facebook.buck.rules.TargetGraphAndBuildTargets; import com.facebook.buck.step.AdbOptions; import com.facebook.buck.step.ExecutionContext; import com.facebook.buck.step.TargetDeviceOptions; import com.facebook.buck.util.HumanReadableException; import com.facebook.buck.util.MoreExceptions; import com.facebook.infer.annotation.SuppressFieldNotInitialized; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import java.io.IOException; import java.util.ArrayList; import java.util.List; import org.kohsuke.args4j.Argument; import org.kohsuke.args4j.Option; public class UninstallCommand extends AbstractCommand { public static class UninstallOptions { @VisibleForTesting static final String KEEP_LONG_ARG = "--keep"; @VisibleForTesting static final String KEEP_SHORT_ARG = "-k"; @Option( name = KEEP_LONG_ARG, aliases = {KEEP_SHORT_ARG}, usage = "Keep user data when uninstalling." ) private boolean keepData = false; public boolean shouldKeepUserData() { return keepData; } } @AdditionalOptions @SuppressFieldNotInitialized private UninstallOptions uninstallOptions; @AdditionalOptions @SuppressFieldNotInitialized private AdbCommandLineOptions adbOptions; @AdditionalOptions @SuppressFieldNotInitialized private TargetDeviceCommandLineOptions deviceOptions; @Argument private List<String> arguments = new ArrayList<>(); public List<String> getArguments() { return arguments; } public UninstallOptions uninstallOptions() { return uninstallOptions; } public AdbOptions adbOptions(BuckConfig buckConfig) { return adbOptions.getAdbOptions(buckConfig); } public TargetDeviceOptions targetDeviceOptions() { return deviceOptions.getTargetDeviceOptions(); } @Override public int runWithoutHelp(CommandRunnerParams params) throws IOException, InterruptedException { // Parse all of the build targets specified by the user. BuildRuleResolver resolver; ImmutableSet<BuildTarget> buildTargets; try (CommandThreadManager pool = new CommandThreadManager("Uninstall", getConcurrencyLimit(params.getBuckConfig()))) { TargetGraphAndBuildTargets result = params .getParser() .buildTargetGraphForTargetNodeSpecs( params.getBuckEventBus(), params.getCell(), getEnableParserProfiling(), pool.getExecutor(), parseArgumentsAsTargetNodeSpecs(params.getBuckConfig(), getArguments())); buildTargets = result.getBuildTargets(); resolver = Preconditions.checkNotNull( params .getActionGraphCache() .getActionGraph( params.getBuckEventBus(), params.getBuckConfig().isActionGraphCheckingEnabled(), params.getBuckConfig().isSkipActionGraphCache(), result.getTargetGraph(), params.getBuckConfig().getKeySeed())) .getResolver(); } catch (BuildTargetException | BuildFileParseException e) { params .getBuckEventBus() .post(ConsoleEvent.severe(MoreExceptions.getHumanReadableOrLocalizedMessage(e))); return 1; } // Make sure that only one build target is specified. if (buildTargets.size() != 1) { params .getBuckEventBus() .post(ConsoleEvent.severe("Must specify exactly one android_binary() rule.")); return 1; } BuildTarget buildTarget = Iterables.get(buildTargets, 0); // Find the android_binary() rule from the parse. BuildRule buildRule; try { buildRule = resolver.requireRule(buildTarget); } catch (NoSuchBuildTargetException e) { throw new HumanReadableException(e.getHumanReadableErrorMessage()); } if (!(buildRule instanceof HasInstallableApk)) { params .getBuckEventBus() .post( ConsoleEvent.severe( String.format( "Specified rule %s must be of type android_binary() or apk_genrule() but was %s().\n", buildRule.getFullyQualifiedName(), buildRule.getType()))); return 1; } HasInstallableApk hasInstallableApk = (HasInstallableApk) buildRule; // We need this in case adb isn't already running. try (ExecutionContext context = createExecutionContext(params)) { final AdbHelper adbHelper = new AdbHelper( adbOptions(params.getBuckConfig()), targetDeviceOptions(), context, params.getConsole(), params.getBuckEventBus(), params.getBuckConfig().getRestartAdbOnFailure()); // Find application package name from manifest and uninstall from matching devices. SourcePathResolver pathResolver = new SourcePathResolver(new SourcePathRuleFinder(resolver)); String appId = AdbHelper.tryToExtractPackageNameFromManifest( pathResolver, hasInstallableApk.getApkInfo()); return adbHelper.uninstallApp(appId, uninstallOptions().shouldKeepUserData()) ? 0 : 1; } } @Override public String getShortDescription() { return "uninstalls an APK"; } @Override public boolean isReadOnly() { return false; } }