com.google.devtools.build.android.Aapt2ResourcePackagingAction.java Source code

Java tutorial

Introduction

Here is the source code for com.google.devtools.build.android.Aapt2ResourcePackagingAction.java

Source

// Copyright 2015 The Bazel Authors. All rights reserved.
//
// 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.google.devtools.build.android;

import static com.google.common.collect.Streams.concat;
import static java.util.stream.Collectors.toList;

import com.android.utils.StdLogger;
import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.devtools.build.android.AndroidResourceMerger.MergingException;
import com.google.devtools.build.android.AndroidResourceProcessingAction.Options;
import com.google.devtools.build.android.aapt2.Aapt2ConfigOptions;
import com.google.devtools.build.android.aapt2.CompiledResources;
import com.google.devtools.build.android.aapt2.PackagedResources;
import com.google.devtools.build.android.aapt2.ResourceCompiler;
import com.google.devtools.build.android.aapt2.ResourceLinker;
import com.google.devtools.build.android.aapt2.StaticLibrary;
import com.google.devtools.common.options.OptionsParser;
import com.google.devtools.common.options.ShellQuotedParamsFilePreProcessor;
import java.io.Closeable;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.List;
import java.util.logging.Logger;

/**
 * Provides an entry point for the resource processing using the AOSP build tools.
 *
 * <pre>
 * Example Usage:
 *   java/com/google/build/android/Aapt2ResourcePackagingAction\
 *      --sdkRoot path/to/sdk\
 *      --aapt path/to/sdk/aapt\
 *      --annotationJar path/to/sdk/annotationJar\
 *      --adb path/to/sdk/adb\
 *      --zipAlign path/to/sdk/zipAlign\
 *      --androidJar path/to/sdk/androidJar\
 *      --manifestOutput path/to/manifest\
 *      --primaryData path/to/resources:path/to/assets:path/to/manifest\
 *      --data p/t/res1:p/t/assets1:p/t/1/AndroidManifest.xml:p/t/1/R.txt:symbols,\
 *             p/t/res2:p/t/assets2:p/t/2/AndroidManifest.xml:p/t/2/R.txt:symbols\
 *      --packagePath path/to/write/archive.ap_\
 *      --srcJarOutput path/to/write/archive.srcjar
 * </pre>
 */
public class Aapt2ResourcePackagingAction {

    private static final StdLogger STD_LOGGER = new StdLogger(StdLogger.Level.WARNING);

    private static final Logger logger = Logger.getLogger(Aapt2ResourcePackagingAction.class.getName());

    private static Aapt2ConfigOptions aaptConfigOptions;
    private static Options options;

    public static void main(String[] args) throws Exception {
        Profiler profiler = LoggingProfiler.createAndStart("setup");
        OptionsParser optionsParser = OptionsParser.newOptionsParser(Options.class, Aapt2ConfigOptions.class);
        optionsParser.enableParamsFileSupport(new ShellQuotedParamsFilePreProcessor(FileSystems.getDefault()));
        optionsParser.parseAndExitUponError(args);
        aaptConfigOptions = optionsParser.getOptions(Aapt2ConfigOptions.class);
        options = optionsParser.getOptions(Options.class);

        try (ScopedTemporaryDirectory scopedTmp = new ScopedTemporaryDirectory("android_resources_tmp")) {
            final Path tmp = scopedTmp.getPath();
            final Path mergedAssets = tmp.resolve("merged_assets");
            final Path mergedResources = tmp.resolve("merged_resources");
            final Path filteredResources = tmp.resolve("filtered_resources");

            final Path densityManifest = tmp.resolve("manifest-filtered/AndroidManifest.xml");

            final Path processedManifest = tmp.resolve("manifest-processed/AndroidManifest.xml");
            final Path databindingResourcesRoot = Files
                    .createDirectories(tmp.resolve("android_data_binding_resources"));
            final Path compiledResources = Files.createDirectories(tmp.resolve("compiled"));
            final Path linkedOut = Files.createDirectories(tmp.resolve("linked"));

            final List<String> densitiesToFilter = options.prefilteredResources.isEmpty() ? options.densities
                    : Collections.<String>emptyList();

            final List<String> densitiesForManifest = densitiesToFilter.isEmpty() ? options.densitiesForManifest
                    : densitiesToFilter;

            profiler.recordEndOf("setup").startTask("merging");

            // Checks for merge conflicts.
            MergedAndroidData mergedAndroidData = AndroidResourceMerger
                    .mergeData(options.primaryData, options.directData, options.transitiveData, mergedResources,
                            mergedAssets, null /* cruncher. Aapt2 automatically chooses to crunch or not. */,
                            options.packageType, options.symbolsOut, options.prefilteredResources,
                            false /* throwOnResourceConflict */)
                    .filter(new DensitySpecificResourceFilter(densitiesToFilter, filteredResources,
                            mergedResources),
                            new DensitySpecificManifestProcessor(densitiesForManifest, densityManifest));

            profiler.recordEndOf("merging");

            final ListeningExecutorService executorService = ExecutorServiceCloser.createDefaultService();
            try (final Closeable closeable = ExecutorServiceCloser.createWith(executorService)) {
                profiler.startTask("compile");
                final ResourceCompiler compiler = ResourceCompiler.create(executorService, compiledResources,
                        aaptConfigOptions.aapt2, aaptConfigOptions.buildToolsVersion);

                CompiledResources compiled = options.primaryData
                        .processDataBindings(
                                options.dataBindingInfoOut, options.packageForR, databindingResourcesRoot)
                        .compile(compiler, compiledResources)
                        .processManifest(manifest -> AndroidManifestProcessor.with(STD_LOGGER).processManifest(
                                options.applicationId, options.versionCode, options.versionName, manifest,
                                processedManifest))
                        .processManifest(manifest -> new DensitySpecificManifestProcessor(densitiesForManifest,
                                densityManifest).process(manifest));
                profiler.recordEndOf("compile").startTask("link");
                // Write manifestOutput now before the dummy manifest is created.
                if (options.manifestOutput != null) {
                    AndroidResourceOutputs.copyManifestToOutput(compiled, options.manifestOutput);
                }

                List<CompiledResources> compiledResourceDeps =
                        // Last defined dependencies will overwrite previous one, so always place direct
                        // after transitive.
                        concat(options.transitiveData.stream(), options.directData.stream())
                                .map(DependencyAndroidData::getCompiledSymbols).collect(toList());

                List<Path> assetDirs = concat(options.transitiveData.stream(), options.directData.stream())
                        .flatMap(dep -> dep.assetDirs.stream()).collect(toList());
                assetDirs.addAll(options.primaryData.assetDirs);

                final PackagedResources packagedResources = ResourceLinker
                        .create(aaptConfigOptions.aapt2, linkedOut).profileUsing(profiler)
                        .dependencies(ImmutableList.of(StaticLibrary.from(aaptConfigOptions.androidJar)))
                        .include(compiledResourceDeps).withAssets(assetDirs)
                        .buildVersion(aaptConfigOptions.buildToolsVersion).filterToDensity(densitiesToFilter)
                        .link(compiled).copyPackageTo(options.packagePath).copyProguardTo(options.proguardOutput)
                        .copyMainDexProguardTo(options.mainDexProguardOutput).createSourceJar(options.srcJarOutput)
                        .copyRTxtTo(options.rOutput);
                profiler.recordEndOf("link");
                if (options.resourcesOutput != null) {
                    profiler.startTask("package");
                    // The compiled resources and the merged resources should be the same.
                    // TODO(corysmith): Decompile or otherwise provide the exact resources in the apk.
                    ResourcesZip
                            .from(mergedAndroidData.getResourceDir(), mergedAndroidData.getAssetDir(),
                                    packagedResources.resourceIds())
                            .writeTo(options.resourcesOutput, false /* compress */);
                    profiler.recordEndOf("package");
                }
            }
        } catch (MergingException e) {
            logger.severe("Merging exception: " + e.getMessage());
            // throw an error, as system.exit will fail tests.
            throw new RuntimeException();
        } catch (IOException e) {
            logger.severe("File error: " + e.getMessage());
            // throw an error, as system.exit will fail tests.
            throw new RuntimeException();
        } catch (Exception e) {
            throw e;
        }
    }
}