Skip to content

Commit

Permalink
Prepare for release 0.7.0.
Browse files Browse the repository at this point in the history
  • Loading branch information
Hitish Chappidi committed Nov 14, 2018
1 parent 324b913 commit 2f29336
Show file tree
Hide file tree
Showing 81 changed files with 6,086 additions and 3,042 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,4 @@ Read more about the App Bundle format and Bundletool's usage at

## Releases

Latest release: [0.6.2](https://github.com/google/bundletool/releases)
Latest release: [0.7.0](https://github.com/google/bundletool/releases)
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
release_version = 0.6.2
release_version = 0.7.0
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@
import com.android.tools.build.bundletool.commands.BuildApksCommand;
import com.android.tools.build.bundletool.commands.BuildBundleCommand;
import com.android.tools.build.bundletool.commands.CommandHelp;
import com.android.tools.build.bundletool.commands.DumpCommand;
import com.android.tools.build.bundletool.commands.ExtractApksCommand;
import com.android.tools.build.bundletool.commands.GetDeviceSpecCommand;
import com.android.tools.build.bundletool.commands.GetSizeCommand;
import com.android.tools.build.bundletool.commands.InstallApksCommand;
import com.android.tools.build.bundletool.commands.ValidateBundleCommand;
import com.android.tools.build.bundletool.commands.VersionCommand;
Expand Down Expand Up @@ -51,15 +53,15 @@ static void main(String[] args, Runtime runtime) {
try {
flags = new FlagParser().parse(args);
} catch (FlagParser.FlagParseException e) {
System.out.println("Error while parsing the flags: " + e.getMessage());
System.err.println("Error while parsing the flags: " + e.getMessage());
runtime.exit(1);
return;
}
List<String> commands = flags.getCommands();

Optional<String> command = flags.getMainCommand();
if (!command.isPresent()) {
System.out.println("Error: You have to specify a command.");
System.err.println("Error: You have to specify a command.");
help();
runtime.exit(1);
return;
Expand Down Expand Up @@ -92,6 +94,12 @@ static void main(String[] args, Runtime runtime) {
case ValidateBundleCommand.COMMAND_NAME:
ValidateBundleCommand.fromFlags(flags).execute();
break;
case DumpCommand.COMMAND_NAME:
DumpCommand.fromFlags(flags).execute();
break;
case GetSizeCommand.COMMAND_NAME:
GetSizeCommand.fromFlags(flags).execute();
break;
case VersionCommand.COMMAND_NAME:
VersionCommand.fromFlags(flags, System.out).execute();
break;
Expand All @@ -103,15 +111,15 @@ static void main(String[] args, Runtime runtime) {
}
break;
default:
System.out.printf("Error: Unrecognized command '%s'.%n%n%n", commands.get(0));
System.err.printf("Error: Unrecognized command '%s'.%n%n%n", commands.get(0));
help();
runtime.exit(1);
return;
}
} catch (Exception e) {
System.out.println(
System.err.println(
"[BT:" + BundleToolVersion.getCurrentVersion() + "] Error: " + e.getMessage());
e.printStackTrace(System.out);
e.printStackTrace();
runtime.exit(1);
return;
}
Expand All @@ -130,6 +138,8 @@ public static void help() {
GetDeviceSpecCommand.help(),
InstallApksCommand.help(),
ValidateBundleCommand.help(),
DumpCommand.help(),
GetSizeCommand.help(),
VersionCommand.help());

System.out.println("Synopsis: bundletool <command> ...");
Expand Down Expand Up @@ -161,8 +171,14 @@ public static void help(String commandName, Runtime runtime) {
case ValidateBundleCommand.COMMAND_NAME:
commandHelp = ValidateBundleCommand.help();
break;
case DumpCommand.COMMAND_NAME:
commandHelp = DumpCommand.help();
break;
case GetSizeCommand.COMMAND_NAME:
commandHelp = GetSizeCommand.help();
break;
default:
System.out.printf("Error: Unrecognized command '%s'.%n%n%n", commandName);
System.err.printf("Error: Unrecognized command '%s'.%n%n%n", commandName);
help();
runtime.exit(1);
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,7 @@ static BuildApksCommand fromFlags(
.ifPresent(buildApksCommand::setGenerateOnlyForConnectedDevice);

Optional<String> deviceSerialName = DEVICE_ID_FLAG.getValue(flags);
if (!deviceSerialName.isPresent()) {
if (connectedDeviceMode && !deviceSerialName.isPresent()) {
deviceSerialName = environmentVariableProvider.getVariable(ANDROID_SERIAL_VARIABLE);
}
deviceSerialName.ifPresent(buildApksCommand::setDeviceId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
@AutoValue
public abstract class CommandHelp {

private static final String LINE_SEPARATOR = System.getProperty("line.separator");
private static final String LINE_SEPARATOR = System.lineSeparator();
private static final int MAX_WIDTH = 80;
private static final int INDENT_SIZE = 4;

Expand Down Expand Up @@ -256,6 +256,18 @@ public int compareTo(FlagDescription o) {
@VisibleForTesting
@CheckReturnValue
static String wrap(String text, int maxWidth, int firstLineIndent, int otherLinesIndent) {
int newLineIdx = text.indexOf(LINE_SEPARATOR);
if (newLineIdx != -1) {
// If a line break is found in the sentence, then we wrap the text recursively for each part.
return wrap(text.substring(0, newLineIdx), maxWidth, firstLineIndent, otherLinesIndent)
+ LINE_SEPARATOR
+ wrap(
text.substring(newLineIdx + LINE_SEPARATOR.length()),
maxWidth,
firstLineIndent,
otherLinesIndent);
}

BreakIterator boundary = BreakIterator.getLineInstance(Locale.ENGLISH);
boundary.setText(text);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* 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.android.tools.build.bundletool.commands;

import static com.android.tools.build.bundletool.utils.files.FilePreconditions.checkFileExistsAndReadable;
import static java.util.stream.Collectors.toList;

import com.android.aapt.Resources.XmlNode;
import com.android.tools.build.bundletool.commands.CommandHelp.CommandDescription;
import com.android.tools.build.bundletool.commands.CommandHelp.FlagDescription;
import com.android.tools.build.bundletool.exceptions.ValidationException;
import com.android.tools.build.bundletool.model.BundleModule;
import com.android.tools.build.bundletool.model.BundleModuleName;
import com.android.tools.build.bundletool.model.ZipPath;
import com.android.tools.build.bundletool.utils.flags.Flag;
import com.android.tools.build.bundletool.utils.flags.ParsedFlags;
import com.android.tools.build.bundletool.utils.xmlproto.XmlProtoNode;
import com.android.tools.build.bundletool.xml.XPathResolver;
import com.android.tools.build.bundletool.xml.XPathResolver.XPathResult;
import com.android.tools.build.bundletool.xml.XmlNamespaceContext;
import com.android.tools.build.bundletool.xml.XmlProtoToXmlConverter;
import com.android.tools.build.bundletool.xml.XmlUtils;
import com.google.auto.value.AutoValue;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.io.UncheckedIOException;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Optional;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Document;

/** Command that prints information about a given Android App Bundle. */
@AutoValue
public abstract class DumpCommand {

public static final String COMMAND_NAME = "dump";

private static final Flag<Path> BUNDLE_LOCATION_FLAG = Flag.path("bundle");
private static final Flag<String> MODULE_FLAG = Flag.string("module");
private static final Flag<String> XPATH_FLAG = Flag.string("xpath");

public abstract Path getBundlePath();

public abstract PrintStream getOutputStream();

public abstract DumpTarget getDumpTarget();

public abstract String getModuleName();

public abstract Optional<String> getXPathExpression();

public static Builder builder() {
return new AutoValue_DumpCommand.Builder()
.setModuleName(BundleModuleName.BASE_MODULE_NAME)
.setOutputStream(System.out);
}

/** Builder for the {@link DumpCommand}. */
@AutoValue.Builder
public abstract static class Builder {
/** Sets the path to the bundle. */
public abstract Builder setBundlePath(Path bundlePath);

/** Sets the output stream where the dump should be printed. */
public abstract Builder setOutputStream(PrintStream outputStream);

/** Sets the target of the dump, e.g. the manifest. */
public abstract Builder setDumpTarget(DumpTarget dumpTarget);

/** Sets the module for the target of the dump. */
public abstract Builder setModuleName(String moduleName);

public abstract Builder setXPathExpression(String xPathExpression);

public abstract DumpCommand build();
}

public static DumpCommand fromFlags(ParsedFlags flags) {
DumpTarget dumpTarget = parseDumpTarget(flags);

Path bundlePath = BUNDLE_LOCATION_FLAG.getRequiredValue(flags);
String moduleName = MODULE_FLAG.getValue(flags).orElse(BundleModuleName.BASE_MODULE_NAME);
Optional<String> xPath = XPATH_FLAG.getValue(flags);

DumpCommand.Builder dumpCommand =
DumpCommand.builder()
.setBundlePath(bundlePath)
.setDumpTarget(dumpTarget)
.setModuleName(moduleName);

xPath.ifPresent(dumpCommand::setXPathExpression);

return dumpCommand.build();
}

public void execute() {
validateInput();

switch (getDumpTarget()) {
case MANIFEST:
printManifest(getXPathExpression());
break;
}
}

private void printManifest(Optional<String> xPathExpression) {
// Extract the manifest from the bundle.
XmlProtoNode manifestProto;
try (ZipFile zipFile = new ZipFile(getBundlePath().toFile())) {
ZipPath manifestPath = ZipPath.create(getModuleName()).resolve(BundleModule.MANIFEST_PATH);
ZipEntry manifestEntry = zipFile.getEntry(manifestPath.toString());
if (manifestEntry == null) {
throw ValidationException.builder()
.withMessage(
"No manifest found for module '%s'. Does the module exist?", getModuleName())
.build();
}

try (InputStream manifestInputStream = zipFile.getInputStream(manifestEntry)) {
manifestProto = new XmlProtoNode(XmlNode.parseFrom(manifestInputStream));
}
} catch (IOException e) {
throw new UncheckedIOException("Unable to read the manifest from the bundle.", e);
}

// Convert the proto to real XML.
Document document = XmlProtoToXmlConverter.convert(manifestProto);

// Select the output.
String output;
if (xPathExpression.isPresent()) {
try {
XPath xPath = XPathFactory.newInstance().newXPath();
xPath.setNamespaceContext(new XmlNamespaceContext(manifestProto));
XPathExpression compiledXPathExpression = xPath.compile(xPathExpression.get());
XPathResult xPathResult = XPathResolver.resolve(document, compiledXPathExpression);
output = xPathResult.toString();
} catch (XPathExpressionException e) {
throw new ValidationException("Error in the XPath expression: " + xPathExpression, e);
}
} else {
output = XmlUtils.documentToString(document);
}

// Print the output.
getOutputStream().println(output.trim());
}

private void validateInput() {
checkFileExistsAndReadable(getBundlePath());
}

private static DumpTarget parseDumpTarget(ParsedFlags flags) {
String subCommand =
flags
.getSubCommand()
.orElseThrow(() -> new ValidationException("Target of the dump not found."));

DumpTarget dumpTarget;
switch (subCommand) {
case "manifest":
dumpTarget = DumpTarget.MANIFEST;
break;
default:
throw ValidationException.builder()
.withMessage(
"Unrecognized dump target: '%s'. Accepted values are: %s",
subCommand,
Arrays.stream(DumpTarget.values())
.map(Enum::toString)
.map(String::toLowerCase)
.collect(toList()))
.build();
}
return dumpTarget;
}

/** Target of the dump. */
public enum DumpTarget {
MANIFEST,
}

public static CommandHelp help() {
return CommandHelp.builder()
.setCommandName(COMMAND_NAME)
.setCommandDescription(
CommandDescription.builder()
.setShortDescription(
"Prints files or extract values from the bundle in a human-readable form.")
.addAdditionalParagraph(
"To print the manifest, one can for example run: "
+ "bundletool dump manifest --bundle=/tmp/app.aab")
.build())
.addFlag(
FlagDescription.builder()
.setFlagName("bundle")
.setDescription("Path to the Android App Bundle.")
.setExampleValue("app.aab")
.build())
.addFlag(
FlagDescription.builder()
.setFlagName("module")
.setDescription("Name of the module to apply the dump for. Defaults to 'base'.")
.setExampleValue("base")
.setOptional(true)
.build())
.addFlag(
FlagDescription.builder()
.setFlagName("xpath")
.setDescription(
"XPath expression to extract the value of attributes from the XML file being "
+ "dumped.")
.setExampleValue("/manifest/@android:versionCode")
.setOptional(true)
.build())
.build();
}
}
Loading

0 comments on commit 2f29336

Please sign in to comment.