blob: 409b8b15b8305f4c858977566bfd8339931b1c96 [file] [log] [blame]
/*
* 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.build.gradle.model
import com.android.build.gradle.BaseExtension
import com.android.build.gradle.internal.ProductFlavorCombo
import com.android.build.gradle.internal.dsl.BuildType
import com.android.build.gradle.ndk.NdkExtension
import com.android.build.gradle.ndk.internal.NdkConfiguration
import com.android.build.gradle.ndk.internal.NdkExtensionConvention
import com.android.build.gradle.ndk.internal.NdkHandler
import com.android.build.gradle.ndk.internal.ToolchainConfiguration
import com.android.builder.core.VariantConfiguration
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.internal.project.ProjectIdentifier
import org.gradle.api.tasks.TaskContainer
import org.gradle.internal.reflect.Instantiator
import org.gradle.internal.service.ServiceRegistry
import org.gradle.language.c.CSourceSet
import org.gradle.language.cpp.CppSourceSet
import org.gradle.model.Finalize
import org.gradle.model.Model
import org.gradle.model.Mutate
import org.gradle.model.Path
import org.gradle.model.RuleSource
import org.gradle.nativeplatform.BuildTypeContainer
import org.gradle.nativeplatform.FlavorContainer
import org.gradle.nativeplatform.NativeLibraryBinarySpec
import org.gradle.nativeplatform.NativeLibrarySpec
import org.gradle.nativeplatform.SharedLibraryBinarySpec
import org.gradle.nativeplatform.StaticLibraryBinary
import org.gradle.nativeplatform.toolchain.NativeToolChainRegistry
import org.gradle.platform.base.BinaryContainer
import org.gradle.platform.base.BinarySpec
import org.gradle.platform.base.ComponentSpecContainer
import org.gradle.platform.base.PlatformContainer
/**
* Plugin for Android NDK applications.
*/
class NdkComponentModelPlugin implements Plugin<Project> {
private Project project
void apply(Project project) {
this.project = project
project.apply plugin: AndroidComponentModelPlugin
project.apply plugin: 'c'
project.apply plugin: 'cpp'
// Remove static library tasks from assemble
project.binaries.withType(StaticLibraryBinary) {
it.buildable = false
}
}
static class Rules extends RuleSource {
@Mutate
void configureAndroidModel(
AndroidModel androidModel,
@Path("androidNdk") NdkExtension ndk) {
androidModel.ndk = ndk
}
@Model("androidNdk")
NdkExtension createAndroidNdk(ServiceRegistry serviceRegistry) {
Instantiator instantiator = serviceRegistry.get(Instantiator.class)
return instantiator.newInstance(NdkExtension)
}
@Mutate
void addDefaultNativeSourceSet(AndroidComponentModelSourceSet sources) {
sources.addDefaultSourceSet("c", CSourceSet.class);
sources.addDefaultSourceSet("cpp", CppSourceSet.class);
}
@Model
NdkHandler ndkHandler(ProjectIdentifier projectId, NdkExtension extension) {
while (projectId.parentIdentifier != null) {
projectId = projectId.parentIdentifier
}
return new NdkHandler(projectId.projectDir, extension)
}
@Finalize
void setDefaultNdkExtensionValue(NdkExtension extension) {
NdkExtensionConvention.setExtensionDefault(extension)
}
@Mutate
void createAndroidPlatforms(PlatformContainer platforms, NdkHandler ndkHandler) {
// Create android platforms.
ToolchainConfiguration.configurePlatforms(platforms, ndkHandler)
}
@Mutate
void createToolchains(
NativeToolChainRegistry toolchains,
NdkExtension ndkExtension,
NdkHandler ndkHandler) {
// Create toolchain for each ABI.
ToolchainConfiguration.configureToolchain(
toolchains,
ndkExtension.getToolchain(),
ndkExtension.getToolchainVersion(),
ndkHandler)
}
@Mutate
void createNativeBuildTypes(
BuildTypeContainer nativeBuildTypes,
@Path("android.buildTypes") NamedDomainObjectContainer<BuildType> androidBuildTypes) {
for (def buildType : androidBuildTypes) {
nativeBuildTypes.maybeCreate(buildType.name)
}
}
@Mutate
void createNativeFlavors(
FlavorContainer nativeFlavors,
List<ProductFlavorCombo> androidFlavorGroups) {
for (def group : androidFlavorGroups) {
nativeFlavors.maybeCreate(group.name)
}
}
@Mutate
void createNativeLibrary(
ComponentSpecContainer specs,
@Path("android.ndk") NdkExtension extension,
NdkHandler ndkHandler,
@Path("android.sources") AndroidComponentModelSourceSet sources,
@Path("buildDir") File buildDir) {
if (!extension.moduleName.isEmpty()) {
NativeLibrarySpec library = specs.create(extension.moduleName, NativeLibrarySpec)
specs.withType(DefaultAndroidComponentSpec) { androidSpec ->
androidSpec.nativeLibrary = library
NdkConfiguration.configureProperties(
library, sources, buildDir, extension, ndkHandler)
}
}
}
@Mutate
void createAdditionalTasksForNatives(
TaskContainer tasks,
ComponentSpecContainer specs,
NdkExtension extension,
NdkHandler ndkHandler,
@Path("buildDir") File buildDir) {
specs.withType(DefaultAndroidComponentSpec) { androidSpec ->
if (androidSpec.nativeLibrary != null) {
androidSpec.nativeLibrary.binaries.withType(SharedLibraryBinarySpec) { binary ->
NdkConfiguration.createTasks(
tasks, binary, buildDir, extension, ndkHandler)
}
}
}
}
@Mutate
void attachNativeBinaryToAndroid(
BinaryContainer binaries,
ComponentSpecContainer specs,
AndroidComponentSpec androidSpec,
@Path("android.ndk") NdkExtension extension) {
if (!extension.moduleName.isEmpty()) {
NativeLibrarySpec library =
specs.withType(NativeLibrarySpec).getByName(extension.moduleName);
binaries.withType(DefaultAndroidBinary).each { binary ->
def nativeBinaries = getNativeBinaries(library, binary.buildType, binary.productFlavors)
binary.getNativeBinaries().addAll(nativeBinaries)
}
}
}
@Finalize
void attachNativeTasksToAssembleTasks(BinaryContainer binaries) {
binaries.withType(DefaultAndroidBinary) { binary ->
if (binary.targetAbi.isEmpty()) {
binary.builtBy(binary.nativeBinaries)
} else {
for (NativeLibraryBinarySpec nativeBinary : binary.nativeBinaries) {
if (binary.targetAbi.contains(nativeBinary.targetPlatform.name)) {
binary.builtBy(nativeBinary)
}
}
}
}
}
/**
* Remove unintended tasks created by Gradle native plugin from task list.
*
* Gradle native plugins creates static library tasks automatically. This method removes them
* to avoid cluttering the task list.
*/
@Mutate
void hideNativeTasks(TaskContainer tasks, BinaryContainer binaries) {
// Gradle do not support a way to remove created tasks. The best workaround is to clear the
// group of the task and have another task depends on it. Therefore, we have to create
// a dummy task to depend on all the tasks that we do not want to show up on the task
// list. The dummy task dependsOn itself, effectively making it non-executable and
// invisible unless the --all option is use.
Task nonExecutableTask = tasks.create("nonExecutableTask")
nonExecutableTask.dependsOn nonExecutableTask
nonExecutableTask.description =
"Dummy task to hide other unwanted tasks in the task list."
binaries.withType(NativeLibraryBinarySpec) { binary ->
Task buildTask = binary.getBuildTask()
nonExecutableTask.dependsOn buildTask
buildTask.group = null
}
}
}
private static Collection<SharedLibraryBinarySpec> getNativeBinaries(
NativeLibrarySpec library,
com.android.builder.model.BuildType buildType,
List<? extends com.android.build.gradle.api.GroupableProductFlavor> productFlavors) {
ProductFlavorCombo flavorGroup = new ProductFlavorCombo(productFlavors);
library.binaries.withType(SharedLibraryBinarySpec).matching { binary ->
(binary.buildType.name.equals(buildType.name)
&& ((productFlavors.isEmpty() && binary.flavor.name.equals("default"))
|| binary.flavor.name.equals(flavorGroup.name)))
}
}
/**
* Return library binaries for a VariantConfiguration.
*/
public Collection<BinarySpec> getBinaries(VariantConfiguration variantConfig) {
if (variantConfig.getType().isForTesting()) {
// Do not return binaries for test variants as test source set is not supported at the
// moment.
return []
}
project.binaries.withType(SharedLibraryBinarySpec).matching { binary ->
(binary.buildType.name.equals(variantConfig.getBuildType().getName())
&& (binary.flavor.name.equals(variantConfig.getFlavorName())
|| (binary.flavor.name.equals("default")
&& variantConfig.getFlavorName().isEmpty()))
&& (variantConfig.getNdkConfig().getAbiFilters() == null
|| variantConfig.getNdkConfig().getAbiFilters().contains(
binary.targetPlatform.name)))
}
}
/**
* Return the output directory of the native binary tasks for a VariantConfiguration.
*/
public Collection<File> getOutputDirectories(VariantConfiguration variantConfig) {
// Return the parent's parent directory of the binaries' output.
// A binary's output file is set to something in the form of
// "/path/to/lib/platformName/libmodulename.so". We want to return "/path/to/lib".
(getBinaries(variantConfig)
*.getPrimaryOutput()
*.getParentFile()
*.getParentFile()
.unique())
}
}