
com.android.build.gradle.model.NdkComponentModelPlugin Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gradle-experimental Show documentation
Show all versions of gradle-experimental Show documentation
Gradle plug-in to build Android applications.
/*
* 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 static com.android.build.gradle.model.AndroidComponentModelPlugin.COMPONENT_NAME;
import static com.android.build.gradle.model.ModelConstants.ARTIFACTS;
import static com.android.build.gradle.model.ModelConstants.EXTERNAL_BUILD_CONFIG;
import static com.android.build.gradle.model.ModelConstants.NATIVE_DEPENDENCIES;
import com.android.annotations.NonNull;
import com.android.build.gradle.internal.NativeDependencyLinkage;
import com.android.build.gradle.internal.NdkHandler;
import com.android.build.gradle.internal.ProductFlavorCombo;
import com.android.build.gradle.internal.core.Abi;
import com.android.build.gradle.internal.dependency.ArtifactContainer;
import com.android.build.gradle.internal.dependency.NativeDependencyResolveResult;
import com.android.build.gradle.internal.dependency.NativeLibraryArtifact;
import com.android.build.gradle.internal.scope.VariantScope;
import com.android.build.gradle.managed.BuildType;
import com.android.build.gradle.managed.NativeBuildConfig;
import com.android.build.gradle.managed.NativeLibrary;
import com.android.build.gradle.managed.NativeSourceFolder;
import com.android.build.gradle.managed.NativeToolchain;
import com.android.build.gradle.managed.NdkAbiOptions;
import com.android.build.gradle.managed.NdkConfig;
import com.android.build.gradle.managed.ProductFlavor;
import com.android.build.gradle.model.internal.AndroidBinaryInternal;
import com.android.build.gradle.model.internal.AndroidComponentSpecInternal;
import com.android.build.gradle.model.internal.DefaultNativeSourceSet;
import com.android.build.gradle.ndk.internal.NdkConfiguration;
import com.android.build.gradle.ndk.internal.NdkExtensionConvention;
import com.android.build.gradle.ndk.internal.NdkNamingScheme;
import com.android.build.gradle.ndk.internal.StlNativeToolSpecification;
import com.android.build.gradle.ndk.internal.ToolchainConfiguration;
import com.android.builder.core.BuilderConstants;
import com.android.builder.core.VariantConfiguration;
import com.google.common.base.Joiner;
import com.google.common.base.Objects;
import com.google.common.base.Predicate;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import org.gradle.api.Action;
import org.gradle.api.BuildableModelElement;
import org.gradle.api.InvalidUserDataException;
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.service.ServiceRegistry;
import org.gradle.language.base.FunctionalSourceSet;
import org.gradle.language.base.LanguageSourceSet;
import org.gradle.language.c.plugins.CPlugin;
import org.gradle.language.cpp.plugins.CppPlugin;
import org.gradle.language.nativeplatform.HeaderExportingSourceSet;
import org.gradle.model.Defaults;
import org.gradle.model.Finalize;
import org.gradle.model.Model;
import org.gradle.model.ModelMap;
import org.gradle.model.Mutate;
import org.gradle.model.Path;
import org.gradle.model.RuleSource;
import org.gradle.model.Validate;
import org.gradle.model.internal.core.ModelPath;
import org.gradle.model.internal.registry.ModelRegistry;
import org.gradle.model.internal.type.ModelType;
import org.gradle.model.internal.type.ModelTypes;
import org.gradle.nativeplatform.BuildTypeContainer;
import org.gradle.nativeplatform.FlavorContainer;
import org.gradle.nativeplatform.NativeBinarySpec;
import org.gradle.nativeplatform.NativeDependencySet;
import org.gradle.nativeplatform.NativeLibraryBinary;
import org.gradle.nativeplatform.NativeLibraryBinarySpec;
import org.gradle.nativeplatform.NativeLibrarySpec;
import org.gradle.nativeplatform.SharedLibraryBinary;
import org.gradle.nativeplatform.SharedLibraryBinarySpec;
import org.gradle.nativeplatform.StaticLibraryBinarySpec;
import org.gradle.nativeplatform.platform.NativePlatform;
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.LanguageType;
import org.gradle.platform.base.LanguageTypeBuilder;
import org.gradle.platform.base.PlatformContainer;
import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry;
import java.io.File;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import javax.inject.Inject;
/**
* Plugin for Android NDK applications.
*/
public class NdkComponentModelPlugin implements Plugin {
@NonNull
private final ToolingModelBuilderRegistry toolingRegistry;
@NonNull
private final ModelRegistry modelRegistry;
@Inject
private NdkComponentModelPlugin(
@NonNull ToolingModelBuilderRegistry toolingRegistry,
@NonNull ModelRegistry modelRegistry) {
this.toolingRegistry = toolingRegistry;
this.modelRegistry = modelRegistry;
}
@Override
public void apply(Project project) {
project.getPluginManager().apply(AndroidComponentModelPlugin.class);
project.getPluginManager().apply(CPlugin.class);
project.getPluginManager().apply(CppPlugin.class);
toolingRegistry.register(new NativeComponentModelBuilder(modelRegistry));
}
public static class Rules extends RuleSource {
@LanguageType
public static void registerNativeSourceSet(LanguageTypeBuilder builder) {
builder.setLanguageName("jni");
builder.defaultImplementation(DefaultNativeSourceSet.class);
}
@Defaults
public static void initializeNdkConfig(@Path("android.ndk") NdkConfig ndk) {
ndk.setModuleName("");
ndk.setToolchain("");
ndk.setToolchainVersion("");
ndk.setStl("");
ndk.setRenderscriptNdkMode(false);
}
@Finalize
public static void setDefaultNdkExtensionValue(@Path("android.ndk") NdkConfig ndkConfig) {
NdkExtensionConvention.setExtensionDefault(ndkConfig);
}
@Validate
public static void checkNdkDir(
NdkHandler ndkHandler,
@Path("android.ndk") NdkConfig ndkConfig) {
if (!ndkConfig.getModuleName().isEmpty() && !ndkHandler.isNdkDirConfigured()) {
throw new InvalidUserDataException(
"NDK location not found. Define location with ndk.dir in the "
+ "local.properties file or with an ANDROID_NDK_HOME environment "
+ "variable.");
}
if (ndkHandler.isNdkDirConfigured()) {
//noinspection ConstantConditions - isNdkDirConfigured ensures getNdkDirectory is not null
if (!ndkHandler.getNdkDirectory().exists()) {
throw new InvalidUserDataException(
"Specified NDK location does not exists. Please ensure ndk.dir in "
+ "local.properties file or ANDROID_NDK_HOME is configured "
+ "correctly.");
}
}
}
@Defaults
public static void addDefaultNativeSourceSet(
@Path("android.sources") ModelMap sources) {
sources.beforeEach(new Action() {
@Override
public void execute(FunctionalSourceSet sourceSet) {
sourceSet.create("jni", NativeSourceSet.class, new Action() {
@Override
public void execute(NativeSourceSet nativeSourceSet) {
nativeSourceSet.getSource().srcDir("src/" + nativeSourceSet.getParentName() + "/" + "jni");
}
});
}
});
}
@Model(ModelConstants.NDK_HANDLER)
public static NdkHandler ndkHandler(
ProjectIdentifier projectId,
@Path("android.compileSdkVersion") String compileSdkVersion,
@Path("android.ndk") NdkConfig ndkConfig) {
while (projectId.getParentIdentifier() != null) {
projectId = projectId.getParentIdentifier();
}
return new NdkHandler(
projectId.getProjectDir(),
Objects.firstNonNull(ndkConfig.getPlatformVersion(), compileSdkVersion),
ndkConfig.getToolchain(),
ndkConfig.getToolchainVersion());
}
@Defaults
public static void initBuildTypeNdk(
@Path("android.buildTypes") ModelMap buildTypes) {
buildTypes.named(
BuilderConstants.DEBUG,
new Action() {
@Override
public void execute(BuildType buildType) {
if (buildType.getNdk().getDebuggable() == null) {
buildType.getNdk().setDebuggable(true);
}
}
});
}
@Mutate
public static void createAndroidPlatforms(PlatformContainer platforms,
NdkHandler ndkHandler) {
if (!ndkHandler.isNdkDirConfigured()) {
return;
}
// Create android platforms.
ToolchainConfiguration.configurePlatforms(platforms, ndkHandler);
}
@Validate
public static void validateAbi(@Path("android.abis") ModelMap abiConfigs) {
abiConfigs.afterEach(new Action() {
@Override
public void execute(NdkAbiOptions abiOptions) {
if (Abi.getByName(abiOptions.getName()) == null) {
throw new InvalidUserDataException("Target ABI '" + abiOptions.getName()
+ "' is not supported.");
}
}
});
}
@Mutate
public static void createToolchains(
NativeToolChainRegistry toolchainRegistry,
@Path("android.abis") ModelMap abis,
@Path("android.ndk") NdkConfig ndkConfig,
NdkHandler ndkHandler) {
if (!ndkHandler.isNdkDirConfigured()) {
return;
}
// Create toolchain for each ABI.
ToolchainConfiguration.configureToolchain(
toolchainRegistry,
ndkConfig.getToolchain(),
abis,
ndkHandler);
}
@Mutate
public static void createNativeBuildTypes(BuildTypeContainer nativeBuildTypes,
@Path("android.buildTypes") ModelMap androidBuildTypes) {
for (BuildType buildType : androidBuildTypes.values()) {
nativeBuildTypes.maybeCreate(buildType.getName());
}
}
@Mutate
public static void createNativeFlavors(
FlavorContainer nativeFlavors,
List> androidFlavorGroups) {
if (androidFlavorGroups.isEmpty()) {
// Create empty native flavor to override Gradle's default name.
nativeFlavors.maybeCreate("");
} else {
for (ProductFlavorCombo group : androidFlavorGroups) {
nativeFlavors.maybeCreate(group.getName());
}
}
}
@Mutate
public static void createNativeLibrary(
final ComponentSpecContainer specs,
@Path("android.ndk") final NdkConfig ndkConfig,
final NdkHandler ndkHandler,
@Path("android.sources") final ModelMap sources,
@Path("buildDir") final File buildDir,
final ServiceRegistry serviceRegistry) {
if (!ndkHandler.isNdkDirConfigured()) {
return;
}
if (!ndkConfig.getModuleName().isEmpty()) {
specs.create(
ndkConfig.getModuleName(),
NativeLibrarySpec.class,
new Action() {
@Override
public void execute(final NativeLibrarySpec nativeLib) {
((AndroidComponentSpecInternal) specs.get(COMPONENT_NAME))
.setNativeLibrary(nativeLib);
NdkConfiguration.configureProperties(
nativeLib,
sources,
buildDir,
ndkHandler,
serviceRegistry);
nativeLib.getBinaries().all(new Action() {
@Override
public void execute(BinarySpec binarySpec) {
NdkConfiguration.configureNativeBinaryOutputFile(
(NativeLibraryBinarySpec)binarySpec,
buildDir,
ndkConfig.getModuleName());
}
});
}
});
AndroidComponentSpecInternal androidSpecs =
(AndroidComponentSpecInternal) specs.get(COMPONENT_NAME);
androidSpecs.setNativeLibrary(
(NativeLibrarySpec) specs.get(ndkConfig.getModuleName()));
}
}
/**
* Find the native dependency for each native binaries.
*
* TODO: Remove duplication of functionality with NdkConfiguration.configureProperties.
* We need to predict the NativeBinarySpec that will be produce instead of creating this map
* after the binaries are created.
*/
@Model(NATIVE_DEPENDENCIES)
public static Multimap resolveNativeDependencies(
ModelMap nativeBinaries,
@Path("android.sources") final ModelMap sources,
final ServiceRegistry serviceRegistry) {
Multimap dependencies =
ArrayListMultimap.create();
for (NativeLibraryBinarySpec nativeBinary : nativeBinaries.values()) {
Collection jniSources =
NdkConfiguration.findNativeSourceSets(nativeBinary, sources).values();
for (NativeSourceSet jniSource : jniSources) {
dependencies.put(
nativeBinary.getName(),
NdkConfiguration.resolveDependency(
serviceRegistry,
nativeBinary,
jniSource));
}
}
return dependencies;
}
@Mutate
public static void createAdditionalTasksForNatives(
final ModelMap tasks,
ModelMap specs,
@Path("android.ndk") final NdkConfig ndkConfig,
@Path(NATIVE_DEPENDENCIES) final Multimap dependencies,
final NdkHandler ndkHandler,
ModelMap binaries,
@Path("buildDir") final File buildDir) {
if (!ndkHandler.isNdkDirConfigured()) {
return;
}
final AndroidComponentSpecInternal androidSpec =
(AndroidComponentSpecInternal) specs.get(COMPONENT_NAME);
if (androidSpec.getNativeLibrary() != null) {
for (AndroidBinaryInternal binary : binaries.values()) {
for (NativeBinarySpec nativeBinary : binary.getNativeBinaries()) {
NdkConfiguration.createTasks(
tasks,
nativeBinary,
buildDir,
binary.getMergedNdkConfig(),
ndkHandler,
dependencies);
}
}
}
}
@Mutate
public static void configureNativeBinary(
BinaryContainer binaries,
ComponentSpecContainer specs,
@Path("android.ndk") final NdkConfig ndkConfig,
final NdkHandler ndkHandler) {
final NativeLibrarySpec library = specs.withType(NativeLibrarySpec.class)
.get(ndkConfig.getModuleName());
final ImmutableSet.Builder builder = ImmutableSet.builder();
for(Abi abi : NdkHandler.getAbiList()) {
builder.add(abi.getName());
}
final ImmutableSet supportedAbis = builder.build();
binaries.withType(
AndroidBinaryInternal.class,
new Action() {
@Override
public void execute(AndroidBinaryInternal binary) {
binary.computeMergedNdk(
ndkConfig,
binary.getProductFlavors(),
binary.getBuildType());
if (binary.getMergedNdkConfig().getAbiFilters().isEmpty()) {
binary.getMergedNdkConfig().getAbiFilters().addAll(supportedAbis);
}
if (library != null) {
Collection nativeBinaries =
getNativeBinaries(
library,
binary.getBuildType(),
binary.getProductFlavors());
for (NativeLibraryBinarySpec nativeBin : nativeBinaries) {
if (binary.getMergedNdkConfig().getAbiFilters().contains(
nativeBin.getTargetPlatform().getName())) {
NdkConfiguration.configureBinary(
nativeBin,
binary.getMergedNdkConfig(),
ndkHandler);
binary.getNativeBinaries().add(nativeBin);
}
}
}
}
});
}
/**
* Create external build config to allow NativeComponentModelBuilder to create native model.
*/
@Model(EXTERNAL_BUILD_CONFIG)
public static void createNativeBuildModel(
NativeBuildConfig config,
ModelMap binaries,
final NdkHandler ndkHandler) {
if (!ndkHandler.isNdkDirConfigured()) {
return;
}
config.getCFileExtensions().addAll(NdkConfiguration.C_FILE_EXTENSIONS);
config.getCppFileExtensions().addAll(NdkConfiguration.CPP_FILE_EXTENSIONS);
for (final AndroidBinaryInternal binary : binaries.values()) {
for (final NativeLibraryBinarySpec nativeBinary : binary.getNativeBinaries()) {
if (!(nativeBinary instanceof SharedLibraryBinarySpec)) {
continue;
}
final String abi = nativeBinary.getTargetPlatform().getName();
config.getLibraries().create(
binary.getName() + '-' + abi,
new CreateNativeLibraryAction(
binary,
(SharedLibraryBinarySpec) nativeBinary));
}
}
for (final Abi abi : ndkHandler.getSupportedAbis()) {
config.getToolchains().create("ndk-" + abi.getName(),
new Action() {
@Override
public void execute(NativeToolchain nativeToolchain) {
nativeToolchain.setCCompilerExecutable(
ndkHandler.getCCompiler(abi));
nativeToolchain.setCppCompilerExecutable(
ndkHandler.getCppCompiler(abi));
}
});
}
}
private static class CreateNativeLibraryAction implements Action {
@NonNull
private final AndroidBinaryInternal binary;
@NonNull
private final SharedLibraryBinarySpec nativeBinary;
public CreateNativeLibraryAction(
@NonNull AndroidBinaryInternal binary,
@NonNull SharedLibraryBinarySpec nativeBinary) {
this.binary = binary;
this.nativeBinary = nativeBinary;
}
@Override
public void execute(NativeLibrary nativeLibrary) {
final String abi = nativeBinary.getTargetPlatform().getName();
nativeLibrary.setOutput(nativeBinary.getSharedLibraryFile());
Set srcFolders = Sets.newHashSet();
nativeLibrary.setGroupName(binary.getName());
final List cFlags = nativeBinary.getcCompiler().getArgs();
final List cppFlags = nativeBinary.getCppCompiler().getArgs();
for (LanguageSourceSet sourceSet : nativeBinary.getSources()) {
srcFolders.addAll(sourceSet.getSource().getSrcDirs());
if (sourceSet instanceof HeaderExportingSourceSet) {
Set headerDirs =
((HeaderExportingSourceSet) sourceSet).getExportedHeaders()
.getSrcDirs();
for (File headerDir : headerDirs) {
if (!nativeLibrary.getExportedHeaders().contains(headerDir)) {
nativeLibrary.getExportedHeaders().add(headerDir);
// Exported headers are not part of the binary's flag. Need to add
// it manually.
cFlags.add("-I" + headerDir);
cppFlags.add("-I" + headerDir);
}
}
}
}
for (NativeDependencySet dependency : nativeBinary.getLibs()) {
for (File includeDir : dependency.getIncludeRoots()) {
cFlags.add("-I" + includeDir);
cppFlags.add("-I" + includeDir);
}
}
for (final File srcFolder : srcFolders) {
nativeLibrary.getFolders().create(
new Action() {
@Override
public void execute(NativeSourceFolder nativeSourceFolder) {
nativeSourceFolder.setSrc(srcFolder);
nativeSourceFolder.getCFlags().addAll(cFlags);
nativeSourceFolder.getCppFlags().addAll(cppFlags);
}
});
}
nativeLibrary.setToolchain("ndk-" + abi);
}
}
@Finalize
public static void attachNativeTasksToAndroidBinary(ModelMap binaries) {
binaries.afterEach(new Action() {
@Override
public void execute(AndroidBinaryInternal binary) {
for (NativeLibraryBinarySpec nativeBinary : binary.getNativeBinaries()) {
if (binary.getTargetAbi().isEmpty() || binary.getTargetAbi().contains(
nativeBinary.getTargetPlatform().getName())) {
binary.getBuildTask().dependsOn(
NdkNamingScheme.getNdkBuildTaskName(nativeBinary));
}
}
}
});
}
@Model(ARTIFACTS)
public static void createNativeLibraryArtifacts(
ArtifactContainer artifactContainer,
ModelMap binaries,
@Path("android.sources") final ModelMap sources,
final ModelMap tasks,
@Path(NATIVE_DEPENDENCIES) final Multimap dependencies,
NdkHandler ndkHandler,
ProjectIdentifier project) {
if (!ndkHandler.isNdkDirConfigured()) {
return;
}
for(final AndroidBinaryInternal binary : binaries.values()) {
for (final NativeLibraryBinarySpec nativeBinary : binary.getNativeBinaries()) {
final String linkage = nativeBinary instanceof SharedLibraryBinarySpec
? "shared"
: "static";
String name = Joiner.on('-').join(
binary.getName(),
nativeBinary.getTargetPlatform().getName(),
linkage);
artifactContainer.getNativeArtifacts().create(
name,
new CreateNativeLibraryArtifactAction(
binary,
nativeBinary,
dependencies.get(nativeBinary.getName()),
ndkHandler,
project));
}
}
}
private static class CreateNativeLibraryArtifactAction
implements Action {
private final AndroidBinaryInternal binary;
private final NativeLibraryBinarySpec nativeBinary;
private final Collection dependencies;
private final NdkHandler ndkHandler;
private final ProjectIdentifier projectId;
public CreateNativeLibraryArtifactAction(AndroidBinaryInternal binary,
NativeLibraryBinarySpec nativeBinary,
Collection dependencies,
NdkHandler ndkHandler,
ProjectIdentifier projectId) {
this.binary = binary;
this.nativeBinary = nativeBinary;
this.dependencies = dependencies;
this.ndkHandler = ndkHandler;
this.projectId = projectId;
}
@Override
public void execute(NativeLibraryArtifact artifact) {
final NativeDependencyLinkage linkage =
nativeBinary instanceof SharedLibraryBinarySpec
? NativeDependencyLinkage.SHARED
: NativeDependencyLinkage.STATIC;
File output = nativeBinary instanceof SharedLibraryBinarySpec
? ((SharedLibraryBinarySpec) nativeBinary).getSharedLibraryFile()
: ((StaticLibraryBinarySpec) nativeBinary).getStaticLibraryFile();
artifact.getLibraries().add(output);
artifact.setBuildType(binary.getBuildType().getName());
for (ProductFlavor flavor : binary.getProductFlavors()) {
artifact.getProductFlavors().add(flavor.getName());
}
artifact.setVariantName(binary.getName());
artifact.setAbi(nativeBinary.getTargetPlatform().getName());
artifact.setLinkage(linkage);
List
© 2015 - 2025 Weber Informatics LLC | Privacy Policy