普通文本  |  507行  |  17.86 KB

description = 'Conscrypt: OpenJdk'

ext {
    jniSourceDir = "$rootDir/common/src/jni"
    assert file("$jniSourceDir").exists()

    // Build the list of classifiers that will be used in the build.
    arch32Name = 'x86'
    arch64Name = 'x86_64'
    nativeClassifiers = []
    nativeClassifier64Bit = null
    nativeClassifier32Bit = null
    nativeConfiguration64Bit = null
    nativeConfiguration32Bit = null
    preferredNativeConfiguration = null
    preferredClassifier = null
    preferredSourceSet = null
    preferredNativeFileDir = null
    if (build64Bit) {
        // Add the 64-Bit classifier first, as the preferred classifier.
        nativeClassifier64Bit = classifierFor(osName, arch64Name)
        nativeClassifiers += nativeClassifier64Bit
        preferredClassifier = nativeClassifier64Bit
        preferredSourceSet = sourceSetName(preferredClassifier)
        preferredNativeFileDir = nativeResourcesDir(preferredClassifier)

        nativeConfiguration64Bit = compileConfigurationName(nativeClassifier64Bit)
        preferredNativeConfiguration = nativeConfiguration64Bit
    }
    if (build32Bit) {
        nativeClassifier32Bit = classifierFor(osName, arch32Name)
        nativeClassifiers += nativeClassifier32Bit
        if (preferredClassifier == null) {
            preferredClassifier = nativeClassifier32Bit
            preferredSourceSet = sourceSetName(preferredClassifier)
            preferredNativeFileDir = nativeResourcesDir(preferredClassifier)
        }

        nativeConfiguration32Bit = compileConfigurationName(nativeClassifier32Bit)
        if (preferredNativeConfiguration == null) {
            preferredNativeConfiguration = nativeConfiguration32Bit
        }
    }
}

sourceSets {

    main {
        java {
            srcDirs += "${rootDir}/common/src/main/java"
            srcDirs += project(':conscrypt-constants').sourceSets.main.java.srcDirs
        }
    }

    platform {
        java {
            srcDirs = [ "src/main/java" ]
            includes = [ "org/conscrypt/Platform.java" ]
        }
    }

    test {
        resources {
            // This shouldn't be needed but seems to help IntelliJ locate the native artifact.
            srcDirs += preferredNativeFileDir
        }
    }

    // Add the source sets for each of the native build
    nativeClassifiers.each { nativeClassifier ->
        def sourceSetName = sourceSetName(nativeClassifier)
        def testSourceSetName = testSourceSetName(nativeClassifier)

        // Main sources for the native build
        "$sourceSetName" {
            resources {
                srcDirs = [nativeResourcesDir(nativeClassifier)]
            }
        }

        // Test sources for the native build
        "${testSourceSetName}" {
            java {
                // Include the test source.
                srcDirs = test.java.srcDirs
            }
            resources {
                srcDirs = ["src/test/resources"]
                srcDirs += sourceSets["$sourceSetName"].resources.srcDirs
            }
        }
    }
}

task platformJar(type: Jar) {
    from sourceSets.platform.output
}

if (isExecutableOnPath('cpplint')) {
    task cpplint(type: Exec) {
        executable = 'cpplint'

        // TODO(nmittler): Is there a better way of getting the JNI sources?
        def pattern = ['**/*.cc', '**/*.h']
        def sourceFiles = fileTree(dir: jniSourceDir, includes: pattern).asPath.tokenize(':')
        // Adding roots so that class #ifdefs don't require full path from the project root.
        args = sourceFiles

        // Capture stderr from the process
        errorOutput = new ByteArrayOutputStream();

        // Need to ignore exit value so that doLast will execute.
        ignoreExitValue = true

        doLast {
            // Create the report file.
            def reportDir = file("${buildDir}/cpplint")
            reportDir.mkdirs();
            def reportFile = new File(reportDir, "report.txt")
            def reportStream = new FileOutputStream(reportFile)

            try {
                // Check for failure
                if (execResult != null) {
                    execResult.assertNormalExitValue()
                }
            } catch (Exception e) {
                // The process failed - get the error report from the stderr.
                String report = errorOutput.toString();

                // Write the report to the console.
                System.err.println(report)

                // Also write the report file.
                reportStream.write(report.bytes);

                // Extension method cpplint.output() can be used to obtain the report
                ext.output = {
                    return report
                }

                // Rethrow the exception to terminate the build.
                throw e;
            } finally {
                reportStream.close();
            }
        }
    }
    check.dependsOn cpplint
}

configurations {
    publicApiDocs
    platform
}

artifacts {
    platform platformJar
}

// Append the BoringSSL-Version to the manifest.
jar.manifest {
    attributes 'BoringSSL-Version' : boringSslVersion
}

dependencies {
    // This is used for the @hide annotation processing in JavaDoc
    publicApiDocs project(':conscrypt-api-doclet')

    compileOnly project(':conscrypt-constants'),
                configurations.publicApiDocs

    testCompile project(':conscrypt-constants'),
            project(':conscrypt-testing'),
            libraries.junit,
            libraries.mockito

    // Need to add the native artifact to classpath when running the tests.
    testRuntime configurations["${preferredNativeConfiguration}"]

    // Configure the dependencies for the native tests.
    nativeClassifiers.each { nativeClassifier ->
        def testCompileConfigName = testSourceSet(nativeClassifier).compileConfigurationName
        "${testCompileConfigName}" (
                sourceSets.main.output, // Explicitly add the main classes
                project(':conscrypt-constants'),
                project(':conscrypt-testing'),
                libraries.junit,
                libraries.mockito
        )
    }

    platformCompileOnly sourceSets.main.output
}

nativeClassifiers.each { nativeClassifier ->
    // Create the JAR task and add it's output to the published archives for this project
    addNativeJar(nativeClassifier)

    // Create the test task and have it auto run whenever the test task runs.
    addNativeTest(nativeClassifier)

    // Build the classes as part of the standard build.
    classes.dependsOn sourceSet(nativeClassifier).classesTaskName
    testClasses.dependsOn testSourceSet(nativeClassifier).classesTaskName
}

// Adds a JAR task for the native library.
def addNativeJar(nativeClassifier) {
    // Create a JAR for this configuration and add it to the output archives.
    SourceSet sourceSet = sourceSet(nativeClassifier)
    def jarTaskName = sourceSet.jarTaskName
    task "$jarTaskName"(type: Jar) {
        // Depend on the regular classes task
        dependsOn classes
        manifest = jar.manifest
        classifier = nativeClassifier

        from sourceSet.output + sourceSets.main.output
    }

    def jarTask = tasks["$jarTaskName"]

    // Add the jar task to the standard build.
    jar.dependsOn jarTask

    // Add it to the 'archives' configuration so that the artifact will be automatically built and
    // installed/deployed.
    artifacts.add('archives', jarTask)
}

// Optionally adds a test task for the given platform
def addNativeTest(nativeClassifier) {
    SourceSet testSourceSet = testSourceSet(nativeClassifier)

    // Just use the same name as the source set for the task.
    def testTaskName = "${testSourceSet.name}"
    def javaExecutable
    def javaArchFlag
    if (testSourceSet.name.endsWith("${arch32Name}Test")) {
        // 32-bit test
        javaExecutable = javaExecutable32 != null ? javaExecutable32 : test.executable
        javaArchFlag = '-d32'
    } else {
        // 64-bit test
        javaExecutable = javaExecutable64 != null ? javaExecutable64 : test.executable
        javaArchFlag = '-d64'
    }

    // Execute the java executable to see if it supports the architecture flag.
    def javaError = new ByteArrayOutputStream()
    exec {
        System.out.println("Running tests with java executable: " + javaExecutable + ".")
        executable javaExecutable
        args = ["$javaArchFlag", '-version']
        ignoreExitValue true
        errorOutput = javaError
    }

    // Only add the test if the javaArchFlag is supported for the selected JVM
    def archSupported = !javaError.toString().toLowerCase().contains('error')
    if (archSupported) {
        task "$testTaskName"(type: Test) {
            dependsOn testSourceSet.classesTaskName
            jvmArgs javaArchFlag
            executable = javaExecutable
            testClassesDir = testSourceSet.output.classesDir

            // TODO(nmittler): Is there a way to copy all properties of the test task?

            // Copy the logging configuration from the test task.
            org.codehaus.groovy.runtime.InvokerHelper.setProperties(testLogging,
                    test.testLogging.properties)

            // Copy heap settings.
            minHeapSize = test.minHeapSize
            maxHeapSize = test.maxHeapSize

            // Copy system properties
            systemProperties = test.systemProperties

            // Set the classpath just before we run the test so that the runtime classpath
            // is fully resolved.
            doFirst {
                classpath = testSourceSet.runtimeClasspath
            }
        }
        test.dependsOn "$testTaskName"
    }
}

// Exclude all test classes from the default test suite.
// We will test each available native artifact separately (see nativeClassifiers).
test.exclude("**")

javadoc {
    options.doclet = "org.conscrypt.doclet.FilterDoclet"
    options.docletpath = configurations.publicApiDocs.files as List
}

model {
    platforms {
        x86 {
            architecture arch32Name
        }
        x86_64 {
            architecture arch64Name
        }
    }

    buildTypes {
        release
    }

    components {
        // Builds the JNI library.
        conscrypt_openjdk_jni(NativeLibrarySpec) {
            if (build32Bit) { targetPlatform arch32Name }
            if (build64Bit) { targetPlatform arch64Name }

            sources {
                cpp {
                    source {
                        srcDirs "$jniSourceDir/main/cpp"
                        include "**/*.cc"
                    }
                }
            }

            binaries {
                // Build the JNI lib as a shared library.
                withType (SharedLibraryBinarySpec) {
                    cppCompiler.define "CONSCRYPT_OPENJDK"

                    // Set up 32-bit vs 64-bit build
                    def building64Bit = false
                    def libPath
                    if (targetPlatform.getArchitecture().getName() == "x86") {
                        libPath = "$boringssl32BuildDir"
                    } else if (targetPlatform.getArchitecture().getName() == "x86-64") {
                        libPath = "$boringssl64BuildDir"
                        building64Bit = true
                    } else {
                        throw new GradleException("Unknown architecture: " +
                                targetPlatform.getArchitecture().name)
                    }

                    if (toolChain in Clang || toolChain in Gcc) {
                        cppCompiler.args "-Wall",
                                "-fPIC",
                                "-O3",
                                "-std=c++11",
                                "-I$jniSourceDir/main/include",
                                "-I$jniSourceDir/unbundled/include",
                                "-I$boringsslIncludeDir",
                                "-I$jdkIncludeDir",
                                "-I$jdkIncludeDir/linux",
                                "-I$jdkIncludeDir/darwin",
                                "-I$jdkIncludeDir/win32"
                        if (rootProject.hasProperty('checkErrorQueue')) {
                            System.out.println("Compiling with error queue checking enabled")
                            cppCompiler.define "CONSCRYPT_CHECK_ERROR_QUEUE"
                        }

                        // Static link to BoringSSL
                        linker.args "-O3",
                                "-fvisibility=hidden",
                                "-lstdc++",
                                libPath + "/ssl/libssl.a",
                                libPath + "/crypto/libcrypto.a"
                    } else if (toolChain in VisualCpp) {
                        cppCompiler.define "DLL_EXPORT"
                        cppCompiler.define "WIN32_LEAN_AND_MEAN"
                        cppCompiler.define "NOMINMAX"
                        if (building64Bit) {
                            cppCompiler.define "WIN64"
                        }
                        cppCompiler.define "_WINDOWS"
                        cppCompiler.define "UNICODE"
                        cppCompiler.define "_UNICODE"
                        cppCompiler.define "NDEBUG"

                        cppCompiler.args "/nologo",
                                "/MT",
                                "/WX-",
                                "/Wall",
                                "/O2",
                                "/Oi",
                                "/Ot",
                                "/GL",
                                "/GS",
                                "/Gy",
                                "/fp:precise",
                                "-wd4514", // Unreferenced inline function removed
                                "-wd4548", // Expression before comma has no effect
                                "-wd4625", // Copy constructor was implicitly defined as deleted
                                "-wd4626", // Assignment operator was implicitly defined as deleted
                                "-wd4710", // function not inlined
                                "-wd4711", // function inlined
                                "-wd4820", // Extra padding added to struct
                                "-wd4946", // reinterpret_cast used between related classes:
                                "-wd4996", // Thread safety for strerror
                                "-wd5027", // Move assignment operator was implicitly defined as deleted
                                "-I$jniSourceDir/main/include",
                                "-I$jniSourceDir/unbundled/include",
                                "-I$boringsslIncludeDir",
                                "-I$jdkIncludeDir",
                                "-I$jdkIncludeDir/win32"

                        // Static link to BoringSSL
                        linker.args "-WX",
                                "ws2_32.lib",
                                "advapi32.lib",
                                "${libPath}\\ssl\\ssl.lib",
                                "${libPath}\\crypto\\crypto.lib"
                    }
                }

                // Never build a static library.
                withType(StaticLibraryBinarySpec) {
                    buildable = false
                }
            }
        }
    }

    tasks { t ->
        $.binaries.withType(SharedLibraryBinarySpec).each { binary ->
            // Build the native artifact classifier from the OS and architecture.
            def archName = binary.targetPlatform.architecture.name.replaceAll('-', '_')
            def classifier = classifierFor(osName, archName)
            def sourceSetName = sourceSetName("$classifier")
            def source = binary.sharedLibraryFile

            // Copies the native library to a resource location that will be included in the jar.
            def copyTaskName = "copyNativeLib${sourceSetName}"
            task "$copyTaskName"(type: Copy, dependsOn: binary.buildTask) {
                from source
                // Rename the artifact to include the generated classifier
                rename '(.+)(\\.[^\\.]+)', "\$1-$classifier\$2"
                // Everything under will be included in the native jar.
                into nativeResourcesDir(classifier) + '/META-INF/native'
            }

            // Make sure we build and copy the native library to the output directory.
            compileJava.dependsOn "$copyTaskName"

            // Now define a task to strip the release binary (linux only)
            if (osName == 'linux' && (!rootProject.hasProperty('nostrip') ||
                    !rootProject.nostrip.toBoolean())) {
                def stripTask = binary.tasks.taskName("strip")
                    task "$stripTask"(type: Exec) {
                        dependsOn binary.tasks.link
                        commandLine "strip", "${binary.tasks.link.outputFile}"
                    }
                binary.tasks.build.dependsOn stripTask
            }
        }
    }
}

boolean isExecutableOnPath(executable) {
    FilenameFilter filter = new FilenameFilter() {
        @Override
        boolean accept(File dir, String name) {
            return executable.equals(name);
        }
    }
    for(String folder : System.getenv('PATH').split("" + File.pathSeparatorChar)) {
        File[] files = file(folder).listFiles(filter)
        if (files != null && files.size() > 0) {
            return true;
        }
    }
    return false;
}

String nativeResourcesDir(nativeClassifier) {
    def sourceSetName = sourceSetName(nativeClassifier)
    "${buildDir}/${sourceSetName}/resources"
}

SourceSet sourceSet(classifier) {
    sourceSets[sourceSetName(classifier)]
}

SourceSet testSourceSet(classifier) {
    sourceSets[testSourceSetName(classifier)]
}

static String classifierFor(osName, archName) {
    "${osName}-${archName}"
}

static String sourceSetName(classifier) {
    classifier.replaceAll("-", "_")
}

static String testSourceSetName(classifier) {
    "${sourceSetName(classifier)}Test"
}

static String compileConfigurationName(classifier) {
    sourceSetName(classifier) + "Compile"
}