Home | History | Annotate | Download | only in bundletool
      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 
     42   abstract Path getOutputPath();
     43 
     44   abstract Optional<Path> getManifestPath();
     45 
     46   abstract Optional<Path> getManifestDirPath();
     47 
     48   abstract Optional<Path> getDexPath();
     49 
     50   abstract Optional<Path> getDexDirPath();
     51 
     52   abstract Optional<Path> getResourcesDirPath();
     53 
     54   abstract Optional<Path> getAssetsDirPath();
     55 
     56   abstract Optional<Path> getNativeDirPath();
     57 
     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 {
     65     abstract Builder setOutputPath(Path outputPath);
     66 
     67     abstract Builder setManifestPath(Path manifestPath);
     68 
     69     abstract Builder setManifestDirPath(Path manifestDirPath);
     70 
     71     abstract Builder setDexPath(Path dexPath);
     72 
     73     abstract Builder setDexDirPath(Path dexDirPath);
     74 
     75     abstract Builder setResourcesDirPath(Path resourcesDirPath);
     76 
     77     abstract Builder setAssetsDirPath(Path assetsDirPath);
     78 
     79     abstract Builder setNativeDirPath(Path nativeDirPath);
     80 
     81     abstract BuildModuleCommand build();
     82   }
     83 
     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 
     98   public void execute() {
     99     validateInput();
    100 
    101 
    102   }
    103 
    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 
    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 
    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 
    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