blob: b903ebec66d762e6e1843ef5ca9d944c109e8e82 [file] [log] [blame]
// 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.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 com.google.devtools.common.options.TriState;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
/**
* 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\
* --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 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");
ExecutorServiceCloser executorService = ExecutorServiceCloser.createWithFixedPoolOf(15)) {
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"));
profiler.recordEndOf("setup").startTask("merging");
AndroidDataDeserializer dataDeserializer =
aaptConfigOptions.useCompiledResourcesForMerge
? AndroidCompiledDataDeserializer.withFilteredResources(options.prefilteredResources)
: AndroidParsedDataDeserializer.withFilteredResources(options.prefilteredResources);
// Checks for merge conflicts.
MergedAndroidData mergedAndroidData =
AndroidResourceMerger.mergeData(
ParsedAndroidData.from(options.primaryData),
options.primaryData.getManifest(),
options.directData,
options.transitiveData,
mergedResources,
mergedAssets,
null /* cruncher. Aapt2 automatically chooses to crunch or not. */,
options.packageType,
options.symbolsOut,
/* rclassWriter= */ null,
dataDeserializer,
options.throwOnResourceConflict,
executorService)
.filter(
new DensitySpecificResourceFilter(
options.densities, filteredResources, mergedResources),
new DensitySpecificManifestProcessor(options.densities, densityManifest));
profiler.recordEndOf("merging");
profiler.startTask("compile");
final ResourceCompiler compiler =
ResourceCompiler.create(
executorService,
compiledResources,
aaptConfigOptions.aapt2,
aaptConfigOptions.buildToolsVersion,
aaptConfigOptions.generatePseudoLocale);
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(options.densities, 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, executorService, linkedOut)
.profileUsing(profiler)
.customPackage(options.packageForR)
.outputAsProto(aaptConfigOptions.resourceTableAsProto)
.dependencies(ImmutableList.of(StaticLibrary.from(aaptConfigOptions.androidJar)))
.include(compiledResourceDeps)
.withAssets(assetDirs)
.buildVersion(aaptConfigOptions.buildToolsVersion)
.conditionalKeepRules(aaptConfigOptions.conditionalKeepRules == TriState.YES)
.filterToDensity(options.densities)
.debug(aaptConfigOptions.debug)
.includeGeneratedLocales(aaptConfigOptions.generatePseudoLocale)
.includeOnlyConfigs(aaptConfigOptions.resourceConfigs)
.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.fromApk(
mergedAndroidData.getResourceDir(),
packagedResources.getApk(),
packagedResources.getResourceIds())
.writeTo(options.resourcesOutput, /* compress= */ false);
profiler.recordEndOf("package");
}
}
}
}