All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.android.builder.core.AaptPackageCommandBuilder Maven / Gradle / Ivy

/*
 * Copyright (C) 2014 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.builder.core;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.builder.dependency.SymbolFileProvider;
import com.android.builder.model.AaptOptions;
import com.android.sdklib.BuildToolInfo;
import com.android.sdklib.IAndroidTarget;
import com.android.utils.ILogger;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;

import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/** AMAZON_EDIT_BEGIN */
import java.util.jar.JarFile;
import java.util.TreeMap;
import java.lang.NumberFormatException;
/** AMAZON_EDIT_END */

/**
 * Builds the command line necessary for an aapt package invocation
 */
public class AaptPackageCommandBuilder {

    @NonNull private final File mManifestFile;
    @NonNull private final AaptOptions mOptions;
    @Nullable private File mResFolder;
    @Nullable private File mAssetsFolder;
    private boolean mVerboseExec = false;
    @Nullable private String mSourceOutputDir;
    @Nullable private String mSymbolOutputDir;
    @Nullable private List mLibraries;
    @Nullable private String mResPackageOutput;
    @Nullable private String mProguardOutput;
    @Nullable private VariantConfiguration.Type mType;
    private boolean mDebuggable = false;
    private boolean mPseudoLocalesEnabled = false;
    @Nullable private Collection mResourceConfigs;
    @Nullable Collection mSplits;
    @Nullable String mPackageForR;

    /**
     *
     * @param manifestFile the location of the manifest file
     * @param options the {@link com.android.builder.model.AaptOptions}
     */
    public AaptPackageCommandBuilder(
            @NonNull File manifestFile,
            @NonNull AaptOptions options) {
        checkNotNull(manifestFile, "manifestFile cannot be null.");
        checkNotNull(options, "options cannot be null.");
        mManifestFile = manifestFile;
        mOptions = options;
    }

    public File getManifestFile() {
        return mManifestFile;
    }

    /**
     * @param resFolder the merged res folder
     * @return itself
     */
    public AaptPackageCommandBuilder setResFolder(@NonNull File resFolder) {
        if (!resFolder.isDirectory()) {
            throw new RuntimeException("resFolder parameter is not a directory");
        }
        mResFolder = resFolder;
        return this;
    }

    /**
     * @param assetsFolder the merged asset folder
     * @return itself
     */
    public AaptPackageCommandBuilder setAssetsFolder(@NonNull File assetsFolder) {
        if (!assetsFolder.isDirectory()) {
            throw new RuntimeException("assetsFolder parameter is not a directory");
        }
        mAssetsFolder = assetsFolder;
        return this;
    }

    /**
     * @param sourceOutputDir optional source folder to generate R.java
     * @return itself
     */
    public AaptPackageCommandBuilder setSourceOutputDir(@Nullable String sourceOutputDir) {
        mSourceOutputDir = sourceOutputDir;
        return this;
    }

    @Nullable
    public String getSourceOutputDir() {
        return mSourceOutputDir;
    }

    /**
     * @param symbolOutputDir the folder to write symbols into
     * @ itself
     */
    public AaptPackageCommandBuilder setSymbolOutputDir(@Nullable String symbolOutputDir) {
        mSymbolOutputDir = symbolOutputDir;
        return this;
    }

    @Nullable
    public String getSymbolOutputDir() {
        return mSymbolOutputDir;
    }

    /**
     * @param libraries the flat list of libraries
     * @return itself
     */
    public AaptPackageCommandBuilder setLibraries(
            @NonNull List libraries) {
        mLibraries = libraries;
        return this;
    }

    @NonNull
    public List getLibraries() {
        return mLibraries == null ? ImmutableList.of() : mLibraries;
    }

    /**
     * @param resPackageOutput optional filepath for packaged resources
     * @return itself
     */
    public AaptPackageCommandBuilder setResPackageOutput(@Nullable String resPackageOutput) {
        mResPackageOutput = resPackageOutput;
        return this;
    }

    /**
     * @param proguardOutput optional filepath for proguard file to generate
     * @return itself
     */
    public AaptPackageCommandBuilder setProguardOutput(@Nullable String proguardOutput) {
        mProguardOutput = proguardOutput;
        return this;
    }

    /**
     * @param type the type of the variant being built
     * @return itself
     */
    public AaptPackageCommandBuilder setType(@NonNull VariantConfiguration.Type type) {
        this.mType = type;
        return this;
    }

    @Nullable
    public VariantConfiguration.Type getType() {
        return mType;
    }

    /**
     * @param debuggable whether the app is debuggable
     * @return itself
     */
    public AaptPackageCommandBuilder setDebuggable(boolean debuggable) {
        this.mDebuggable = debuggable;
        return this;
    }

    /**
     * @param resourceConfigs a list of resource config filters to pass to aapt.
     * @return itself
     */
    public AaptPackageCommandBuilder setResourceConfigs(@NonNull Collection resourceConfigs) {
        this.mResourceConfigs = resourceConfigs;
        return this;
    }

    /**
     * @param splits optional list of split dimensions values (like a density or an abi). This
     *               will be used by aapt to generate the corresponding pure split apks.
     * @return itself
     */
    public AaptPackageCommandBuilder setSplits(@NonNull Collection splits) {
        this.mSplits = splits;
        return this;
    }


    public AaptPackageCommandBuilder setVerbose() {
        mVerboseExec = true;
        return this;
    }

    /**
     * @param packageForR Package override to generate the R class in a different package.
     * @return itself
     */
    public AaptPackageCommandBuilder setPackageForR(@NonNull String packageForR) {
        this.mPackageForR = packageForR;
        return this;
    }

    public AaptPackageCommandBuilder setPseudoLocalesEnabled(boolean pseudoLocalesEnabled) {
        mPseudoLocalesEnabled = pseudoLocalesEnabled;
        return this;
    }

    @Nullable
    String getPackageForR() {
        return mPackageForR;
    }

    public List build(@NonNull BuildToolInfo buildToolInfo, @NonNull IAndroidTarget target, @NonNull ILogger logger) {

        // if both output types are empty, then there's nothing to do and this is an error
        checkArgument(mSourceOutputDir != null || mResPackageOutput != null,
                "No output provided for aapt task");
        if (mSymbolOutputDir != null || mSourceOutputDir != null) {
            checkNotNull(mLibraries, "libraries cannot be null if symbolOutputDir or sourceOutputDir is non-null");
        }

        // launch aapt: create the command line
        ArrayList command = Lists.newArrayList();

        String aapt = buildToolInfo.getPath(BuildToolInfo.PathId.AAPT);
        if (aapt == null || !new File(aapt).isFile()) {
            throw new IllegalStateException("aapt is missing");
        }

        command.add(aapt);
        command.add("package");

        if (mVerboseExec) {
            command.add("-v");
        }

        command.add("-f");
        command.add("--no-crunch");

        // inputs
        command.add("-I");
        command.add(target.getPath(IAndroidTarget.ANDROID_JAR));

        command.add("-M");
        command.add(mManifestFile.getAbsolutePath());

        /** AMAZON_EDIT_BEGIN */
        // Include resources in addon jars if appropriate.
        if (!target.isPlatform()) {
          final IAndroidTarget.IOptionalLibrary[] addonLibs = target.getOptionalLibraries();

          if (addonLibs != null) {

            // Contains a listing of any addon libraries that contain resources.
            // Since AAPT enforces base packages to be provided in order of ascending
            // package id, a TreeMap is used to ensure entries are sorted accordingly
            // which allows for easy processing later on.
            //    < (Integer) package_id, (String) library_path >
            final TreeMap resourceLibs = new TreeMap();

            // Iterate through each library checking the manifest for resource information.
            for (IAndroidTarget.IOptionalLibrary lib : addonLibs) {
              final String jarPath = lib.getJarPath();
              String pkgId;

              try {
                // Get the resource package id from the manifest if it exists.
                final JarFile jar = new JarFile(jarPath);
                pkgId = jar.getManifest().getMainAttributes().getValue("Resource-Package-Id");
              } catch (Exception e) {
                logger.error(e, "Unable to read addon library manifest.");
                continue;
              }

              // If the library manifest contains resource information, save the package id and
              // library path in a map to be processed later.
              if (pkgId != null) {
                try {
                  final int id = Integer.parseInt(pkgId);
                  resourceLibs.put(id, jarPath);
                } catch (NumberFormatException e) {
                  logger.error(e, "The provided resource id is not a valid integer: " + pkgId);
                }
              }
            }

            if (resourceLibs.size() > 0) {
              // Only pass libraries into AAPT if they are listed in the map. Iterating over the
              // key set is guaranteed to provide the libraries in order of ascending package id.
              for (Integer id : resourceLibs.keySet()) {
                  command.add("-I");
                  command.add(resourceLibs.get(id));
              }
            } else {
              // If no resource information was found in any of the addon libraries, pass
              // all of the libraries into AAPT to maintain support for versions of the SDK
              // that do not contain resource information in library manifests.
              for (IAndroidTarget.IOptionalLibrary lib : addonLibs) {
                  command.add("-I");
                  command.add(lib.getJarPath());
              }
            }
          }
        }
        /** AMAZON_EDIT_END */

        if (mResFolder != null) {
            command.add("-S");
            command.add(mResFolder.getAbsolutePath());
        }

        if (mAssetsFolder != null) {
            command.add("-A");
            command.add(mAssetsFolder.getAbsolutePath());
        }

        // outputs

        if (mSourceOutputDir != null) {
            command.add("-m");
            command.add("-J");
            command.add(mSourceOutputDir);
        }

        if (mResPackageOutput != null) {
            command.add("-F");
            command.add(mResPackageOutput);
        }

        if (mProguardOutput != null) {
            command.add("-G");
            command.add(mProguardOutput);
        }

        if (mSplits != null) {
            for (String split : mSplits) {

                command.add("--split");
                command.add(split);
            }
        }

        // options controlled by build variants

        if (mDebuggable) {
            command.add("--debug-mode");
        }


        if (mType != VariantConfiguration.Type.TEST) {
            if (mPackageForR != null) {
                command.add("--custom-package");
                command.add(mPackageForR);
                logger.verbose("Custom package for R class: '%s'", mPackageForR);
            }
        }

        if (mPseudoLocalesEnabled) {
            if (buildToolInfo.getRevision().getMajor() >= 21) {
                command.add("--pseudo-localize");
            } else {
                throw new RuntimeException(
                        "Pseudolocalization is only available since Build Tools version 21.0.0,"
                                + " please upgrade or turn it off.");
            }
        }

        // library specific options
        if (mType == VariantConfiguration.Type.LIBRARY) {
            command.add("--non-constant-id");
        }

        // AAPT options
        String ignoreAssets = mOptions.getIgnoreAssets();
        if (ignoreAssets != null) {
            command.add("--ignore-assets");
            command.add(ignoreAssets);
        }

        if (mOptions.getFailOnMissingConfigEntry()) {
            if (buildToolInfo.getRevision().getMajor() > 20) {
                command.add("--error-on-missing-config-entry");
            } else {
                throw new IllegalStateException("aaptOptions:failOnMissingConfigEntry cannot be used"
                        + " with SDK Build Tools revision earlier than 21.0.0");
            }
        }

        // never compress apks.
        command.add("-0");
        command.add("apk");

        // add custom no-compress extensions
        Collection noCompressList = mOptions.getNoCompress();
        if (noCompressList != null) {
            for (String noCompress : noCompressList) {
                command.add("-0");
                command.add(noCompress);
            }
        }

        if (mResourceConfigs!=null && !mResourceConfigs.isEmpty()) {
            command.add("-c");

            Joiner joiner = Joiner.on(',');
            command.add(joiner.join(mResourceConfigs));
        }

        if (mSymbolOutputDir != null &&
                (mType == VariantConfiguration.Type.LIBRARY || !mLibraries.isEmpty())) {
            command.add("--output-text-symbols");
            command.add(mSymbolOutputDir);
        }
        return command;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy