// Copyright 2017 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. // Copyright 2017 The Bazel Authors. All rights reserved. package com.google.devtools.build.android; import com.android.builder.core.VariantConfiguration; import com.google.common.base.Preconditions; import com.google.common.base.Strings; import com.google.devtools.build.android.aapt2.Aapt2ConfigOptions; import com.google.devtools.build.android.aapt2.CompiledResources; import com.google.devtools.build.android.aapt2.ResourceLinker; import com.google.devtools.build.android.aapt2.StaticLibrary; import com.google.devtools.common.options.Option; import com.google.devtools.common.options.OptionDocumentationCategory; import com.google.devtools.common.options.OptionEffectTag; import com.google.devtools.common.options.OptionsBase; import com.google.devtools.common.options.OptionsParser; import com.google.devtools.common.options.ShellQuotedParamsFilePreProcessor; import java.nio.file.FileSystems; import java.nio.file.Path; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; /** Performs resource validation and static linking for compiled android resources. */ public class ValidateAndLinkResourcesAction { /** Action configuration options. */ public static class Options extends OptionsBase { @Option( name = "compiled", documentationCategory = OptionDocumentationCategory.UNCATEGORIZED, effectTags = {OptionEffectTag.UNKNOWN}, defaultValue = "null", converter = Converters.ExistingPathConverter.class, category = "input", help = "Compiled resources to link.", deprecationWarning = "Use --resources.") // TODO(b/64570523): Still used by blaze. Will be removed as part of the command line cleanup. @Deprecated public Path compiled; @Option( name = "compiledDep", documentationCategory = OptionDocumentationCategory.UNCATEGORIZED, effectTags = {OptionEffectTag.UNKNOWN}, defaultValue = "", converter = Converters.PathListConverter.class, category = "input", allowMultiple = true, help = "Compiled resource dependencies to link.") public List compiledDeps; @Option( name = "manifest", documentationCategory = OptionDocumentationCategory.UNCATEGORIZED, effectTags = {OptionEffectTag.UNKNOWN}, defaultValue = "null", converter = Converters.ExistingPathConverter.class, category = "input", help = "Manifest for the library.", deprecationWarning = "Use --resources.") // TODO(b/64570523): Still used by blaze. Will be removed as part of the command line cleanup. @Deprecated public Path manifest; @Option( name = "resources", documentationCategory = OptionDocumentationCategory.UNCATEGORIZED, effectTags = {OptionEffectTag.UNKNOWN}, defaultValue = "null", converter = Converters.CompiledResourcesConverter.class, category = "input", help = "Compiled resources to link.") public CompiledResources resources; // TODO(b/64570523): remove this flag when it is no longer used. @Option( name = "libraries", documentationCategory = OptionDocumentationCategory.UNCATEGORIZED, effectTags = {OptionEffectTag.UNKNOWN}, defaultValue = "null", converter = Converters.StaticLibraryListConverter.class, category = "input", help = "Static libraries to link against. Deprecated, use --library") public List deprecatedLibraries; @Option( name = "library", documentationCategory = OptionDocumentationCategory.UNCATEGORIZED, effectTags = {OptionEffectTag.UNKNOWN}, defaultValue = "null", converter = Converters.StaticLibraryConverter.class, category = "input", allowMultiple = true, help = "Static libraries to link against.") public List libraries; @Option( name = "packageForR", documentationCategory = OptionDocumentationCategory.UNCATEGORIZED, effectTags = {OptionEffectTag.UNKNOWN}, defaultValue = "null", category = "input", help = "Package for the resources.") public String packageForR; @Option( name = "staticLibraryOut", documentationCategory = OptionDocumentationCategory.UNCATEGORIZED, effectTags = {OptionEffectTag.UNKNOWN}, defaultValue = "null", converter = Converters.PathConverter.class, category = "output", help = "Static library produced.") public Path staticLibraryOut; @Option( name = "rTxtOut", documentationCategory = OptionDocumentationCategory.UNCATEGORIZED, effectTags = {OptionEffectTag.UNKNOWN}, defaultValue = "null", converter = Converters.PathConverter.class, category = "output", help = "R.txt out.") public Path rTxtOut; @Option( name = "sourceJarOut", documentationCategory = OptionDocumentationCategory.UNCATEGORIZED, effectTags = {OptionEffectTag.UNKNOWN}, converter = Converters.PathConverter.class, defaultValue = "null", category = "output", help = "Generated java classes from the resources.") public Path sourceJarOut; } public static void main(String[] args) throws Exception { final OptionsParser optionsParser = OptionsParser.newOptionsParser(Options.class, Aapt2ConfigOptions.class); optionsParser.enableParamsFileSupport( new ShellQuotedParamsFilePreProcessor(FileSystems.getDefault())); optionsParser.parse(args); Options options = optionsParser.getOptions(Options.class); final Aapt2ConfigOptions aapt2Options = optionsParser.getOptions(Aapt2ConfigOptions.class); final Profiler profiler = LoggingProfiler.createAndStart("manifest"); try (ScopedTemporaryDirectory scopedTmp = new ScopedTemporaryDirectory("android_resources_tmp"); ExecutorServiceCloser executorService = ExecutorServiceCloser.createWithFixedPoolOf(15)) { CompiledResources resources = // TODO(b/64570523): Remove when the flags are standardized. Optional.ofNullable(options.resources) .orElseGet( () -> CompiledResources.from( Preconditions.checkNotNull(options.compiled), Preconditions.checkNotNull(options.manifest))) // We need to make the manifest aapt safe (w.r.t., placeholders). For now, just stub // it out. .processManifest( manifest -> { final String packageForR = Strings.isNullOrEmpty(options.packageForR) ? VariantConfiguration.getManifestPackage(manifest.toFile()) : options.packageForR; return AndroidManifestProcessor.writeDummyManifestForAapt( scopedTmp.getPath().resolve("manifest-aapt-dummy/AndroidManifest.xml"), packageForR); }); profiler.recordEndOf("manifest").startTask("link"); ResourceLinker.create(aapt2Options.aapt2, executorService, scopedTmp.getPath()) .profileUsing(profiler) .dependencies(Optional.ofNullable(options.deprecatedLibraries).orElse(options.libraries)) .include( options .compiledDeps .stream() .map(CompiledResources::from) .collect(Collectors.toList())) .buildVersion(aapt2Options.buildToolsVersion) .outputAsProto(aapt2Options.resourceTableAsProto) .linkStatically(resources) .copyLibraryTo(options.staticLibraryOut) .copySourceJarTo(options.sourceJarOut) .copyRTxtTo(options.rTxtOut); profiler.recordEndOf("link"); } } }