import org.codehaus.groovy.runtime.InvokerHelper 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 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) } if (build32Bit) { nativeClassifier32Bit = classifierFor(osName, arch32Name) nativeClassifiers += nativeClassifier32Bit if (preferredClassifier == null) { preferredClassifier = nativeClassifier32Bit preferredSourceSet = sourceSetName(preferredClassifier) preferredNativeFileDir = nativeResourcesDir(preferredClassifier) } } } sourceSets { main { java { srcDirs += "${rootDir}/common/src/main/java" srcDirs += project(':conscrypt-constants').sourceSets.main.java.srcDirs } resources { srcDirs += "build/generated/resources" } } platform { java { srcDirs = [ "src/main/java" ] includes = [ "org/conscrypt/Platform.java" ] } } test { java { srcDirs += "${rootDir}/common/src/test/java" } 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" { output.dir(nativeResourcesDir(nativeClassifier), builtBy: "copyNativeLib${sourceSetName}") } // Test sources for the native build "$testSourceSetName" { java { // Include the test source. srcDirs = test.java.srcDirs } resources { srcDirs = ["src/test/resources"] } output.dir(nativeResourcesDir(nativeClassifier), builtBy: "copyNativeLib${sourceSetName}") } } } compileJava { dependsOn generateProperties } 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 } jar.manifest { attributes ('BoringSSL-Version' : boringSslVersion, 'Automatic-Module-Name' : 'org.conscrypt') } dependencies { // This is used for the @Internal annotation processing in JavaDoc publicApiDocs project(':conscrypt-api-doclet') compileOnly project(':conscrypt-constants'), configurations.publicApiDocs testImplementation project(':conscrypt-constants'), project(':conscrypt-testing'), libraries.junit, libraries.mockito testRuntimeClasspath sourceSets["$preferredSourceSet"].output // 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) { mustRunAfter test jvmArgs javaArchFlag executable = javaExecutable InvokerHelper.setProperties(testLogging, test.testLogging.properties) systemProperties = test.systemProperties } check.dependsOn "$testTaskName" } } // Exclude all test classes from the default test suite. // We will test each available native artifact separately (see nativeClassifiers). test.exclude("**") javadoc { dependsOn(configurations.publicApiDocs) 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++", "-lpthread", 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.tasks.link) { 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' } // 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 executable "strip" args binary.tasks.link.linkedFile.asFile.get() } project.tasks["$copyTaskName"].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}/native-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" }