1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 package com.android.tools.appbundle.bundletool; 17 18 import static com.google.common.base.Preconditions.checkArgument; 19 20 import com.android.tools.appbundle.bundletool.utils.FlagParser; 21 import com.google.auto.value.AutoValue; 22 import java.nio.file.Files; 23 import java.nio.file.Path; 24 import java.nio.file.Paths; 25 import java.util.Optional; 26 27 /** Command responsible for building an App Bundle module. */ 28 @AutoValue 29 public abstract class BuildModuleCommand { 30 31 public static final String COMMAND_NAME = "build-module"; 32 33 private static final String OUTPUT_FLAG = "output"; 34 private static final String MANIFEST_FLAG = "manifest"; 35 private static final String MANIFEST_DIR_FLAG = "manifest-dir"; 36 private static final String DEX_FLAG = "dex"; 37 private static final String DEX_DIR_FLAG = "dex-dir"; 38 private static final String RESOURCES_DIR_FLAG = "resources-dir"; 39 private static final String ASSETS_DIR_FLAG = "assets-dir"; 40 private static final String NATIVE_DIR_FLAG = "native-dir"; 41 getOutputPath()42 abstract Path getOutputPath(); 43 getManifestPath()44 abstract Optional<Path> getManifestPath(); 45 getManifestDirPath()46 abstract Optional<Path> getManifestDirPath(); 47 getDexPath()48 abstract Optional<Path> getDexPath(); 49 getDexDirPath()50 abstract Optional<Path> getDexDirPath(); 51 getResourcesDirPath()52 abstract Optional<Path> getResourcesDirPath(); 53 getAssetsDirPath()54 abstract Optional<Path> getAssetsDirPath(); 55 getNativeDirPath()56 abstract Optional<Path> getNativeDirPath(); 57 builder()58 public static Builder builder() { 59 return new AutoValue_BuildModuleCommand.Builder(); 60 } 61 62 /** Builder for the {@link BuildModuleCommand} */ 63 @AutoValue.Builder 64 public abstract static class Builder { setOutputPath(Path outputPath)65 abstract Builder setOutputPath(Path outputPath); 66 setManifestPath(Path manifestPath)67 abstract Builder setManifestPath(Path manifestPath); 68 setManifestDirPath(Path manifestDirPath)69 abstract Builder setManifestDirPath(Path manifestDirPath); 70 setDexPath(Path dexPath)71 abstract Builder setDexPath(Path dexPath); 72 setDexDirPath(Path dexDirPath)73 abstract Builder setDexDirPath(Path dexDirPath); 74 setResourcesDirPath(Path resourcesDirPath)75 abstract Builder setResourcesDirPath(Path resourcesDirPath); 76 setAssetsDirPath(Path assetsDirPath)77 abstract Builder setAssetsDirPath(Path assetsDirPath); 78 setNativeDirPath(Path nativeDirPath)79 abstract Builder setNativeDirPath(Path nativeDirPath); 80 build()81 abstract BuildModuleCommand build(); 82 } 83 fromFlags(FlagParser flagParser)84 static BuildModuleCommand fromFlags(FlagParser flagParser) { 85 Builder builder = 86 builder().setOutputPath(Paths.get(flagParser.getRequiredFlagValue(OUTPUT_FLAG))); 87 flagParser.getFlagValueAsPath(MANIFEST_FLAG).ifPresent(builder::setManifestPath); 88 flagParser.getFlagValueAsPath(MANIFEST_DIR_FLAG).ifPresent(builder::setManifestDirPath); 89 flagParser.getFlagValueAsPath(DEX_FLAG).ifPresent(builder::setDexPath); 90 flagParser.getFlagValueAsPath(DEX_DIR_FLAG).ifPresent(builder::setDexDirPath); 91 flagParser.getFlagValueAsPath(RESOURCES_DIR_FLAG).ifPresent(builder::setResourcesDirPath); 92 flagParser.getFlagValueAsPath(ASSETS_DIR_FLAG).ifPresent(builder::setAssetsDirPath); 93 flagParser.getFlagValueAsPath(NATIVE_DIR_FLAG).ifPresent(builder::setNativeDirPath); 94 95 return builder.build(); 96 } 97 execute()98 public void execute() { 99 validateInput(); 100 101 102 } 103 validateInput()104 private void validateInput() { 105 checkArgument( 106 getManifestPath().isPresent() || getManifestDirPath().isPresent(), 107 "One of --%s or --%s is required.", 108 MANIFEST_FLAG, 109 MANIFEST_DIR_FLAG); 110 checkArgument( 111 !getManifestPath().isPresent() || !getManifestDirPath().isPresent(), 112 "Cannot set both --%s and --%s flags.", 113 MANIFEST_FLAG, 114 MANIFEST_DIR_FLAG); 115 checkArgument( 116 !getDexPath().isPresent() || !getDexDirPath().isPresent(), 117 "Cannot set both --%s and --%s flags.", 118 DEX_FLAG, 119 DEX_DIR_FLAG); 120 121 checkArgument(!Files.exists(getOutputPath()), "File %s already exists.", getOutputPath()); 122 checkFileExistsAndReadable(getManifestPath()); 123 checkDirectoryExists(getManifestDirPath()); 124 checkFileExistsAndReadable(getDexPath()); 125 checkDirectoryExists(getDexDirPath()); 126 checkDirectoryExists(getResourcesDirPath()); 127 checkDirectoryExists(getAssetsDirPath()); 128 checkDirectoryExists(getNativeDirPath()); 129 } 130 checkFileExistsAndReadable(Optional<Path> pathOptional)131 private static void checkFileExistsAndReadable(Optional<Path> pathOptional) { 132 if (pathOptional.isPresent()) { 133 Path path = pathOptional.get(); 134 checkArgument(Files.exists(path), "File '%s' was not found.", path); 135 checkArgument(Files.isReadable(path), "File '%s' is not readable.", path); 136 } 137 } 138 checkDirectoryExists(Optional<Path> pathOptional)139 private static void checkDirectoryExists(Optional<Path> pathOptional) { 140 if (pathOptional.isPresent()) { 141 Path path = pathOptional.get(); 142 checkArgument(Files.exists(path), "Directory '%s' was not found.", path); 143 checkArgument(Files.isDirectory(path), "'%s' is not a directory."); 144 } 145 } 146 help()147 public static void help() { 148 System.out.println( 149 String.format( 150 "bundletool %s --output=<path/to/module.zip> " 151 + "[--%s=<path/to/AndroidManifest.flat>|--%s=<path/to/manifest-dir/>] " 152 + "[--%s=<path/to/classes.dex>|--%s=<path/to/dex-dir/>] " 153 + "[--%s=<path/to/res/>] " 154 + "[--%s=<path/to/assets/>] " 155 + "[--%s=<path/to/lib/>] ", 156 COMMAND_NAME, 157 MANIFEST_FLAG, 158 MANIFEST_DIR_FLAG, 159 DEX_FLAG, 160 DEX_DIR_FLAG, 161 RESOURCES_DIR_FLAG, 162 ASSETS_DIR_FLAG, 163 NATIVE_DIR_FLAG)); 164 System.out.println(); 165 System.out.println( 166 "Builds a module as a zip from an app's project. Note that the resources and the " 167 + "AndroidManifest.xml must already have been compiled with aapt2."); 168 System.out.println(); 169 System.out.println("--output: Path to the zip file to build."); 170 System.out.printf( 171 "--%s: Path to the AndroidManifest.flat compiled by aapt2. Use --%s if there " 172 + "are more than one.\n", 173 MANIFEST_FLAG, MANIFEST_DIR_FLAG); 174 System.out.printf( 175 "--%s: Path to the directory containing multiple Android manifests compiled by aapt2. " 176 + "A file named 'manifest-targeting.xml' must be present in the directory " 177 + "describing the targeting of each manifest present.\n", 178 MANIFEST_DIR_FLAG); 179 System.out.printf( 180 "--%s: Path to the dex file. Use --%s if there are more than one.\n", 181 DEX_FLAG, DEX_DIR_FLAG); 182 System.out.printf( 183 "--%s: Path to the directory containing multiple dex files. Unless all dex files must " 184 + "be included in the generated APKs (for MultiDex), a file named " 185 + "'dex-targeting.xml' must be present in the directory describing the targeting " 186 + "of the different dex files.\n", 187 DEX_DIR_FLAG); 188 System.out.printf( 189 "--%s: Path to the directory containing the resources file(s). A file named " 190 + "'resources.flat' must be present in that directory corresponding to the output " 191 + "of the aapt2 compilation of the resources.\n", 192 RESOURCES_DIR_FLAG); 193 System.out.printf("--%s: Path to the directory containing the assets.\n", ASSETS_DIR_FLAG); 194 System.out.printf( 195 "--%s: Path to the directory containing the native libraries.\n", NATIVE_DIR_FLAG); 196 } 197 } 198