All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.android.build.gradle.ndk.internal.NdkConfiguration Maven / Gradle / Ivy

There is a newer version: 0.9.0
Show newest version
/*
 * 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.ndk.internal;

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.core.Abi;
import com.android.build.gradle.internal.dependency.AndroidNativeDependencySpec;
import com.android.build.gradle.internal.dependency.NativeDependencyResolveResult;
import com.android.build.gradle.internal.dependency.NativeDependencyResolver;
import com.android.build.gradle.internal.dependency.NativeLibraryArtifact;
import com.android.build.gradle.internal.dependency.NativeLibraryArtifactAdaptor;
import com.android.build.gradle.managed.NdkConfig;
import com.android.build.gradle.model.NativeSourceSet;
import com.android.build.gradle.tasks.StripDebugSymbolTask;
import com.android.utils.StringHelper;
import com.google.common.base.Objects;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;

import org.gradle.api.Action;
import org.gradle.api.Task;
import org.gradle.api.file.SourceDirectorySet;
import org.gradle.internal.service.ServiceRegistry;
import org.gradle.language.base.FunctionalSourceSet;
import org.gradle.language.c.CSourceSet;
import org.gradle.language.c.tasks.CCompile;
import org.gradle.language.cpp.CppSourceSet;
import org.gradle.language.cpp.tasks.CppCompile;
import org.gradle.language.nativeplatform.DependentSourceSet;
import org.gradle.model.ModelMap;
import org.gradle.nativeplatform.NativeBinarySpec;
import org.gradle.nativeplatform.NativeLibraryBinarySpec;
import org.gradle.nativeplatform.NativeLibrarySpec;
import org.gradle.nativeplatform.SharedLibraryBinarySpec;
import org.gradle.nativeplatform.StaticLibraryBinarySpec;
import org.gradle.nativeplatform.internal.resolve.DefaultNativeDependencySet;
import org.gradle.platform.base.BinarySpec;
import org.gradle.platform.base.internal.BinarySpecInternal;

import java.io.File;
import java.util.Collection;
import java.util.List;
import java.util.Map;

/**
 * Configure settings used by the native binaries.
 */
public class NdkConfiguration {
    public static final Collection C_FILE_EXTENSIONS = ImmutableList.of("c");
    public static final Collection CPP_FILE_EXTENSIONS = ImmutableList.of(
            "C", "CPP", "c++", "cc", "cp", "cpp", "cxx");

    public static void configureProperties(
            NativeLibrarySpec library,
            final ModelMap sources,
            final File buildDir,
            final NdkHandler ndkHandler,
            final ServiceRegistry serviceRegistry) {
        for (Abi abi : ndkHandler.getSupportedAbis()) {
            library.targetPlatform(abi.getName());
        }

        // Setting each native binary to not buildable to prevent the native tasks to be
        // automatically added to the "assemble" task.
        library.getBinaries().beforeEach(
                new Action() {
                    @Override
                    public void execute(BinarySpec binary) {
                        ((BinarySpecInternal) binary).setBuildable(false);
                    }
                });

        library.getBinaries().withType(
                NativeLibraryBinarySpec.class,
                new Action() {
                    @Override
                    public void execute(final NativeLibraryBinarySpec binary) {
                        Map jniSources =
                                findNativeSourceSets(binary, sources);
                        for (Map.Entry entry : jniSources.entrySet()) {
                            addNativeSourceSets(binary, entry.getKey(), entry.getValue());
                        }

                        binary.getcCompiler().define("ANDROID");
                        binary.getCppCompiler().define("ANDROID");
                        binary.getcCompiler().define("ANDROID_NDK");
                        binary.getCppCompiler().define("ANDROID_NDK");

                        // Replace output directory of compile tasks.
                        binary.getTasks().withType(CCompile.class, new Action() {
                            @Override
                            public void execute(CCompile task) {
                                String sourceSetName = task.getObjectFileDir().getName();
                                task.setObjectFileDir(
                                        NdkNamingScheme.getObjectFilesOutputDirectory(
                                                binary,
                                                buildDir,
                                                sourceSetName));
                            }
                        });
                        binary.getTasks().withType(CppCompile.class, new Action() {
                            @Override
                            public void execute(CppCompile task) {
                                String sourceSetName = task.getObjectFileDir().getName();
                                task.setObjectFileDir(
                                        NdkNamingScheme.getObjectFilesOutputDirectory(
                                                binary,
                                                buildDir,
                                                sourceSetName));
                            }
                        });

                        new DefaultNativeToolSpecification().apply(binary);

                        String sysroot = ndkHandler.getSysroot(
                                Abi.getByName(binary.getTargetPlatform().getName()));

                        binary.getcCompiler().args("--sysroot=" + sysroot);
                        binary.getCppCompiler().args("--sysroot=" + sysroot);
                        binary.getLinker().args("--sysroot=" + sysroot);
                        binary.getLinker().args("-Wl,--build-id");

                        for (NativeSourceSet jniSource : jniSources.values()) {
                            handleDependencies(
                                    binary,
                                    resolveDependency(serviceRegistry, binary, jniSource));
                        }
                    }
                });
    }


    /**
     * Configure output file of a native library binary.
     */
    public static void configureNativeBinaryOutputFile(
            NativeLibraryBinarySpec binary,
            final File buildDir,
            final String moduleName) {
        // Set output library filename.
        if (binary instanceof SharedLibraryBinarySpec) {
            ((SharedLibraryBinarySpec) binary).setSharedLibraryFile(
                    new File(
                            buildDir,
                            NdkNamingScheme.getDebugLibraryDirectoryName(binary)
                                    + "/"
                                    + NdkNamingScheme.getSharedLibraryFileName(
                                    moduleName)));
            ((SharedLibraryBinarySpec) binary).setSharedLibraryLinkFile(
                    new File(
                            buildDir,
                            NdkNamingScheme.getDebugLibraryDirectoryName(binary)
                                    + "/"
                                    + NdkNamingScheme.getSharedLibraryFileName(
                                    moduleName)));
        } else if (binary instanceof StaticLibraryBinarySpec) {
            ((StaticLibraryBinarySpec) binary).setStaticLibraryFile(
                    new File(
                            buildDir,
                            NdkNamingScheme.getDebugLibraryDirectoryName(binary)
                                    + "/"
                                    + NdkNamingScheme.getStaticLibraryFileName(
                                    moduleName)));
        } else {
            throw new AssertionError("Should be unreachable");
        }
    }

    /**
     * Configure native binary with variant specific options.
     */
    public static void configureBinary(
            NativeLibraryBinarySpec binary,
            final NdkConfig ndkConfig,
            final NdkHandler ndkHandler) {
        String sysroot = ndkHandler.getSysroot(
                Abi.getByName(binary.getTargetPlatform().getName()));

        if (ndkConfig.getRenderscriptNdkMode()) {
            binary.getcCompiler().args("-I" + sysroot + "/usr/include/rs");
            binary.getcCompiler().args("-I" + sysroot + "/usr/include/rs/cpp");
            binary.getCppCompiler().args("-I" + sysroot + "/usr/include/rs");
            binary.getCppCompiler().args("-I" + sysroot + "/usr/include/rs/cpp");
            binary.getLinker().args("-L" + sysroot + "/usr/lib/rs");
        }

        // STL flags must be applied before user defined flags to resolve possible undefined symbols
        // in the STL library.
        StlNativeToolSpecification stlConfig = new StlNativeToolSpecification(
                ndkHandler,
                ndkConfig.getStl(),
                ndkConfig.getStlVersion(),
                binary.getTargetPlatform());
        stlConfig.apply(binary);

        NativeToolSpecificationFactory.create(
                ndkHandler,
                binary.getTargetPlatform(),
                Objects.firstNonNull(ndkConfig.getDebuggable(), false)).apply(
                binary);

        // Add flags defined in NdkConfig
        for (String flag : ndkConfig.getCFlags()) {
            binary.getcCompiler().args(flag.trim());
        }

        for (String flag : ndkConfig.getCppFlags()) {
            binary.getCppCompiler().args(flag.trim());
        }

        for (String flag : ndkConfig.getLdFlags()) {
            binary.getLinker().args(flag.trim());
        }

        for (String ldLib : ndkConfig.getLdLibs()) {
            binary.getLinker().args("-l" + ldLib.trim());
        }
    }


    public static NativeDependencyResolveResult resolveDependency(
            @NonNull ServiceRegistry serviceRegistry,
            @NonNull NativeBinarySpec binary,
            @NonNull NativeSourceSet jniSource) {
        return new NativeDependencyResolver(
                serviceRegistry,
                jniSource.getDependencies(),
                new AndroidNativeDependencySpec(
                        null,
                        null,
                        binary.getBuildType().getName(),
                        binary.getFlavor().getName(),
                        NativeDependencyLinkage.SHARED)).resolve();
    }

    private static void handleDependencies(
            @NonNull NativeBinarySpec binary,
            @NonNull NativeDependencyResolveResult dependency) {
        for (final NativeLibraryArtifact artifacts: dependency.getNativeArtifacts()) {
            final String abi = artifacts.getAbi();
            if (binary.getTargetPlatform().getName().equals(abi)) {
                binary.getTasks().all(
                        new Action() {
                            @Override
                            public void execute(Task task) {
                                task.dependsOn(artifacts.getBuiltBy());
                            }
                        });
                binary.lib(new DefaultNativeDependencySet(new NativeLibraryArtifactAdaptor(artifacts)));
            }
        }
    }

    /**
     * Find all JNI source sets that should be added the a native binary.
     *
     * @return a map from the name of the FunctionalSourceSet containing the JNI source set, to the
     * JNI source set.
     */
    public static Map findNativeSourceSets(
            NativeBinarySpec binary,
            ModelMap projectSourceSet) {
        Map sourceSetMap = Maps.newHashMap();
        addSourceIfExist(sourceSetMap, projectSourceSet, "main");
        addSourceIfExist(sourceSetMap, projectSourceSet, binary.getFlavor().getName());
        addSourceIfExist(sourceSetMap, projectSourceSet, binary.getBuildType().getName());
        addSourceIfExist(sourceSetMap, projectSourceSet,
                binary.getFlavor().getName()
                        + StringHelper.capitalize(binary.getBuildType().getName()));
        return sourceSetMap;
    }

    private static void addSourceIfExist(
            @NonNull Map sourceSetMap,
            @NonNull ModelMap projectSourceSet,
            @NonNull String sourceSetName) {
        FunctionalSourceSet sourceSet = projectSourceSet.get(sourceSetName);
        if (sourceSet != null) {
            sourceSetMap.put(sourceSetName, (NativeSourceSet) sourceSet.get("jni"));
        }
    }

    /**
     * Add the sourceSet with the specified name to the binary.
     */
    private static void addNativeSourceSets(
            @NonNull BinarySpec binary,
            @NonNull final String sourceSetName,
            @NonNull final NativeSourceSet jni) {
        // Hardcode the acceptable extension until we find a suitable DSL for user to modify.
        binary.getSources().create(
                        sourceSetName + "C",
                        CSourceSet.class,
                        new Action() {
                            @Override
                            public void execute(CSourceSet source) {
                                source.getSource().setSrcDirs(jni.getSource().getSrcDirs());
                                addInclude(source.getSource(), C_FILE_EXTENSIONS);
                                source.getSource().exclude(jni.getSource().getExcludes());
                                source.getExportedHeaders().source(jni.getExportedHeaders());
                                configurePrebuiltDependency(source, jni);
                            }
                        });
        binary.getSources().create(
                        sourceSetName + "Cpp",
                        CppSourceSet.class,
                        new Action() {
                            @Override
                            public void execute(CppSourceSet source) {
                                source.getSource().setSrcDirs(jni.getSource().getSrcDirs());
                                addInclude(source.getSource(), CPP_FILE_EXTENSIONS);
                                source.getSource().exclude(jni.getSource().getExcludes());
                                source.getExportedHeaders().source(jni.getExportedHeaders());
                                configurePrebuiltDependency(source, jni);
                            }
                        });
    }

    private static void addInclude(
            @NonNull SourceDirectorySet sourceDirSet,
            @NonNull Iterable fileExtensions) {
        for (String ext : fileExtensions) {
            sourceDirSet.include("**/*." + ext);
        }
    }

    private static void configurePrebuiltDependency(
            DependentSourceSet source,
            NativeSourceSet jni) {
        for(AndroidNativeDependencySpec dependencySpec :
                jni.getDependencies().getDependencies()) {
            if (dependencySpec.getLibraryPath() != null) {
                ImmutableMap.Builder builder = ImmutableMap.builder();
                builder.put("library", dependencySpec.getLibraryPath());
                if (dependencySpec.getLinkage() != null) {
                    builder.put("linkage", dependencySpec.getLinkage().getName());
                }
                source.lib(builder.build());
            }
        }
    }

    public static void createTasks(
            @NonNull ModelMap tasks,
            @NonNull final NativeBinarySpec binary,
            @NonNull final File buildDir,
            @NonNull NdkConfig ndkConfig,
            @NonNull NdkHandler ndkHandler,
            @NonNull final Multimap dependencyMap) {
        String compileNdkTaskName = NdkNamingScheme.getNdkBuildTaskName(binary);
        tasks.create(compileNdkTaskName);

        if (binary instanceof SharedLibraryBinarySpec) {
            StlConfiguration.createStlCopyTask(
                    tasks,
                    binary,
                    buildDir,
                    ndkHandler,
                    ndkConfig.getStl(),
                    ndkConfig.getStlVersion(),
                    compileNdkTaskName);

            createStripDebugTask(
                    tasks,
                    (SharedLibraryBinarySpec) binary,
                    dependencyMap,
                    buildDir,
                    ndkHandler,
                    compileNdkTaskName);
        }
    }

    private static void createStripDebugTask(
            ModelMap tasks,
            final SharedLibraryBinarySpec binary,
            @NonNull final Multimap dependencyMap,
            final File buildDir,
            final NdkHandler handler,
            String buildTaskName) {

        final String taskName = NdkNamingScheme.getTaskName(binary, "stripSymbols");

        List libs = Lists.newArrayList();
        final Collection dependencies =
                dependencyMap.get(binary.getName());
        for (NativeDependencyResolveResult dependency : dependencies) {
            Collection artifacts = dependency.getNativeArtifacts();
            for (NativeLibraryArtifact artifact : artifacts) {
                if (binary.getTargetPlatform().getName().equals(artifact.getAbi())) {
                    for (File lib : artifact.getLibraries()) {
                        if (lib.getName().endsWith(".so")) {
                            libs.add(lib);
                        }
                    }
                }
            }
        }
        tasks.create(
                taskName,
                StripDebugSymbolTask.class,
                new StripDebugSymbolTask.ConfigAction(
                        binary,
                        new File(buildDir, NdkNamingScheme.getDebugLibraryDirectoryName(binary)),
                        libs,
                        buildDir,
                        handler));
        tasks.named(buildTaskName, new Action() {
            @Override
            public void execute(Task task) {
                task.dependsOn(taskName);
            }
        });
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy