// 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.lib.rules.android; import com.google.common.collect.ImmutableList; import com.google.devtools.build.lib.actions.Artifact; import com.google.devtools.build.lib.actions.MutableActionGraph.ActionConflictException; import com.google.devtools.build.lib.analysis.ConfiguredTarget; import com.google.devtools.build.lib.analysis.OutputGroupInfo; import com.google.devtools.build.lib.analysis.RuleConfiguredTargetBuilder; import com.google.devtools.build.lib.analysis.RuleConfiguredTargetFactory; import com.google.devtools.build.lib.analysis.RuleContext; import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget.Mode; import com.google.devtools.build.lib.collect.nestedset.NestedSet; import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; import com.google.devtools.build.lib.packages.AttributeMap; import com.google.devtools.build.lib.packages.BuildType; import com.google.devtools.build.lib.packages.TriState; import com.google.devtools.build.lib.rules.android.AndroidConfiguration.AndroidAaptVersion; import com.google.devtools.build.lib.rules.android.AndroidLibraryAarInfo.Aar; import com.google.devtools.build.lib.rules.java.JavaCommon; import com.google.devtools.build.lib.rules.java.JavaSemantics; import com.google.devtools.build.lib.rules.java.JavaSourceInfoProvider; import com.google.devtools.build.lib.rules.java.JavaTargetAttributes; import com.google.devtools.build.lib.rules.java.ProguardLibrary; import com.google.devtools.build.lib.rules.java.ProguardSpecProvider; import com.google.devtools.build.lib.syntax.Type; /** An implementation for the "android_library" rule. */ public abstract class AndroidLibrary implements RuleConfiguredTargetFactory { protected abstract JavaSemantics createJavaSemantics(); protected abstract AndroidSemantics createAndroidSemantics(); protected abstract AndroidMigrationSemantics createAndroidMigrationSemantics(); /** Checks expected rule invariants, throws rule errors if anything is set wrong. */ private static void validateRuleContext(RuleContext ruleContext) throws InterruptedException, RuleErrorException { /** * TODO(b/14473160): Remove when deps are no longer implicitly exported. * *

Warn if android_library rule contains deps without srcs or locally-used resources. Such * deps are implicitly exported (deprecated behavior), and will soon be disallowed entirely. */ if (usesDeprecatedImplicitExport(ruleContext)) { String message = "android_library will be deprecating the use of deps to export " + "targets implicitly. Please use android_library.exports to explicitly specify " + "targets this rule exports"; AndroidConfiguration androidConfig = ruleContext.getFragment(AndroidConfiguration.class); if (androidConfig.allowSrcsLessAndroidLibraryDeps(ruleContext)) { ruleContext.attributeWarning("deps", message); } else { ruleContext.attributeError("deps", message); } } } /** * TODO(b/14473160): Remove when deps are no longer implicitly exported. * *

Returns true if the rule (possibly) relies on the implicit dep exports behavior. * *

If this returns true, then the rule *is* exporting deps implicitly, and does not have any * srcs or locally-used resources consuming the deps. * *

Else, this rule either is not using deps or has another deps-consuming attribute (src, * locally-used resources) */ private static boolean usesDeprecatedImplicitExport(RuleContext ruleContext) throws RuleErrorException { AttributeMap attrs = ruleContext.attributes(); if (!attrs.isAttributeValueExplicitlySpecified("deps") || attrs.get("deps", BuildType.LABEL_LIST).isEmpty()) { return false; } String[] labelListAttrs = {"srcs", "idl_srcs", "assets", "resource_files"}; for (String attr : labelListAttrs) { if (attrs.isAttributeValueExplicitlySpecified(attr) && !attrs.get(attr, BuildType.LABEL_LIST).isEmpty()) { return false; } } boolean hasManifest = attrs.isAttributeValueExplicitlySpecified("manifest"); boolean hasAssetsDir = attrs.isAttributeValueExplicitlySpecified("assets_dir"); boolean hasInlineConsts = attrs.isAttributeValueExplicitlySpecified("inline_constants") && attrs.get("inline_constants", Type.BOOLEAN); boolean hasExportsManifest = attrs.isAttributeValueExplicitlySpecified("exports_manifest") && attrs.get("exports_manifest", BuildType.TRISTATE) == TriState.YES; return !(hasManifest || hasInlineConsts || hasAssetsDir || hasExportsManifest); } @Override public ConfiguredTarget create(RuleContext ruleContext) throws InterruptedException, RuleErrorException, ActionConflictException { validateRuleContext(ruleContext); // Create semantics objects, which are different between Blaze and Bazel. JavaSemantics javaSemantics = createJavaSemantics(); AndroidSemantics androidSemantics = createAndroidSemantics(); androidSemantics.validateAndroidLibraryRuleContext(ruleContext); createAndroidMigrationSemantics().validateRuleContext(ruleContext); AndroidSdkProvider.verifyPresence(ruleContext); // Create wrappers for the ProGuard configuration files. NestedSetBuilder proguardConfigsbuilder = NestedSetBuilder.stableOrder(); ProguardLibrary proguardLibrary = new ProguardLibrary(ruleContext); proguardConfigsbuilder.addTransitive(proguardLibrary.collectProguardSpecs()); // If there are idl srcs, we'll need the transitive proguard configurations too. AndroidIdlHelper.maybeAddSupportLibProguardConfigs(ruleContext, proguardConfigsbuilder); NestedSet transitiveProguardConfigs = proguardConfigsbuilder.build(); AndroidConfiguration androidConfig = AndroidCommon.getAndroidConfig(ruleContext); // "Resources" here include actual resources (xmls, drawables, etc), assets, and the manifest. boolean definesLocalResources = AndroidResources.definesAndroidResources(ruleContext.attributes()); if (definesLocalResources) { AndroidResources.validateRuleContext(ruleContext); } // TODO(b/69668042): Always correctly apply neverlinking for resources boolean isNeverLink = JavaCommon.isNeverLink(ruleContext) && (definesLocalResources || androidConfig.fixedResourceNeverlinking()); ResourceDependencies resourceDeps = ResourceDependencies.fromRuleDeps(ruleContext, isNeverLink); AssetDependencies assetDeps = AssetDependencies.fromRuleDeps(ruleContext, isNeverLink); // Start processing Android data via the AndroidDataContext. // "Data" is a collective term for manifest, resources, and assets. final AndroidDataContext dataContext = androidSemantics.makeContextForNative(ruleContext); // Use a standalone resource-only APK (with extension ".ap_") to decouple Android data from the // other artifacts. final ResourceApk resourceApk; if (definesLocalResources) { StampedAndroidManifest manifest = AndroidManifest.fromAttributes(ruleContext, dataContext, androidSemantics) .stamp(dataContext); ValidatedAndroidResources resources = AndroidResources.from(ruleContext, "resource_files") .process( ruleContext, dataContext, manifest, DataBinding.contextFrom(ruleContext, dataContext.getAndroidConfig()), isNeverLink); MergedAndroidAssets assets = AndroidAssets.from(ruleContext) .process( dataContext, assetDeps, AndroidAaptVersion.chooseTargetAaptVersion(ruleContext)); resourceApk = ResourceApk.of(resources, assets, null, null); if (ruleContext.hasErrors()) { return null; } } else { // No local resources, but we still need to process transitive resources in order to export an // aar. resourceApk = ResourceApk.processFromTransitiveLibraryData( dataContext, DataBinding.contextFrom(ruleContext, dataContext.getAndroidConfig()), resourceDeps, assetDeps, StampedAndroidManifest.createEmpty(ruleContext, /* exported = */ false)); } // JavaCommon and AndroidCommon contain shared helper classes between java_* and android_* // rules respectively. JavaCommon javaCommon = AndroidCommon.createJavaCommonWithAndroidDataBinding( ruleContext, javaSemantics, resourceApk.asDataBindingContext(), /* isLibrary */ true); javaSemantics.checkRule(ruleContext, javaCommon); AndroidCommon androidCommon = new AndroidCommon(javaCommon); // As android_library makes use of the Java rule compilation pipeline, we collect all // Java-related information here to be passed into the JavaSourceInfoProvider later. JavaTargetAttributes javaTargetAttributes = androidCommon.init( javaSemantics, androidSemantics, resourceApk, /* addCoverageSupport= */ false, /* collectJavaCompilationArgs= */ true, /* isBinary= */ false, /* excludedRuntimeArtifacts= */ null, /* generateExtensionRegistry= */ false); if (javaTargetAttributes == null) { return null; } // Create the implicit AAR output artifact which contains non-transitive resources, proguard // specs and class jar. final Aar aar = Aar.makeAar( dataContext, resourceApk, proguardLibrary.collectLocalProguardSpecs(), androidCommon.getClassJar()); // Start building the configured target by adding all the necessary transitive providers/infos. // Includes databinding, IDE, Java. Also declares the output groups and sets the files to build. RuleConfiguredTargetBuilder builder = new RuleConfiguredTargetBuilder(ruleContext); androidCommon.addTransitiveInfoProviders( builder, aar.getAar(), resourceApk, null, ImmutableList.of(), NativeLibs.EMPTY, // TODO(elenairina): Use JavaCommon.isNeverlink(ruleContext) for consistency among rules. androidCommon.isNeverLink(), /* isLibrary = */ true); NestedSetBuilder transitiveResourcesJars = collectTransitiveResourceJars(ruleContext); if (resourceApk.getResourceJavaClassJar() != null) { transitiveResourcesJars.add(resourceApk.getResourceJavaClassJar()); } builder // android_library doesn't do any cc steps, so we'll just pass native libs along. .addNativeDeclaredProvider( new AndroidNativeLibsInfo( AndroidCommon.collectTransitiveNativeLibs(ruleContext).build())) .addNativeDeclaredProvider( new AndroidCcLinkParamsProvider(androidCommon.getCcLinkingInfo())) .add( JavaSourceInfoProvider.class, JavaSourceInfoProvider.fromJavaTargetAttributes(javaTargetAttributes, javaSemantics)) .addNativeDeclaredProvider(new ProguardSpecProvider(transitiveProguardConfigs)) .addNativeDeclaredProvider( new AndroidProguardInfo(proguardLibrary.collectLocalProguardSpecs())) .addOutputGroup(OutputGroupInfo.HIDDEN_TOP_LEVEL, transitiveProguardConfigs) .addNativeDeclaredProvider( AndroidLibraryResourceClassJarProvider.create(transitiveResourcesJars.build())); // If this isn't a neverlink target, we'll provide the artifacts in the AAR too. if (!JavaCommon.isNeverLink(ruleContext)) { builder.addNativeDeclaredProvider(aar.toProvider(ruleContext, definesLocalResources)); } return builder.build(); } private NestedSetBuilder collectTransitiveResourceJars(RuleContext ruleContext) { NestedSetBuilder builder = NestedSetBuilder.naiveLinkOrder(); Iterable providers = AndroidCommon.getTransitivePrerequisites( ruleContext, Mode.TARGET, AndroidLibraryResourceClassJarProvider.PROVIDER); for (AndroidLibraryResourceClassJarProvider resourceJarProvider : providers) { builder.addTransitive(resourceJarProvider.getResourceClassJars()); } return builder; } }