import aQute.bnd.gradle.BundleTaskConvention import org.codehaus.groovy.runtime.InvokerHelper apply plugin: 'biz.aQute.bnd.builder' 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 { srcDirs += "${rootDir}/common/src/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) // Main sources for the native build "$sourceSetName" { 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 } apply from: "$rootDir/gradle/publishing.gradle" publishing.publications.maven { artifact sourcesJar artifact javadocJar } jar.manifest { attributes ('BoringSSL-Version' : boringSslVersion, 'Automatic-Module-Name' : 'org.conscrypt', 'Bundle-SymbolicName': 'org.conscrypt', '-exportcontents': 'org.conscrypt.*') } dependencies { // This is used for the @Internal annotation processing in JavaDoc publicApiDocs project(':conscrypt-api-doclet') // This is listed as compile-only, but we absorb its contents. compileOnly project(':conscrypt-constants') testImplementation project(':conscrypt-constants'), project(':conscrypt-testing'), libraries.junit, libraries.mockito testRuntimeClasspath sourceSets["$preferredSourceSet"].output 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) // Build the classes as part of the standard build. classes.dependsOn sourceSet(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) { Jar t -> // Depend on the regular classes task dependsOn classes manifest = jar.manifest classifier = nativeClassifier from sourceSet.output + sourceSets.main.output // add OSGI headers t.convention.plugins.bundle = new BundleTaskConvention(t) t.doLast { t.buildBundle() } } def jarTask = tasks["$jarTaskName"] // Add the jar task to the standard build. jar.dependsOn jarTask // Add it to the publishing archives list. publishing.publications.maven.artifact jarTask } // Check which version def javaError = new ByteArrayOutputStream() exec { executable test.executable System.out.println("Running tests with java executable: " + test.executable + ".") args = ['-version'] ignoreExitValue true errorOutput = javaError } def suiteClass = (javaError.toString() =~ /"1[.]7[.].*"/) ? "org/conscrypt/ConscryptJava7Suite.class" : "org/conscrypt/ConscryptSuite.class"; test { include suiteClass, "org/conscrypt/ConscryptOpenJdkSuite.class" } task testEngineSocket(type: Test) { include suiteClass, "org/conscrypt/ConscryptOpenJdkSuite.class" InvokerHelper.setProperties(testLogging, test.testLogging.properties) systemProperties = test.systemProperties systemProperty "org.conscrypt.useEngineSocketByDefault", true } check.dependsOn testEngineSocket // Tests that involve interoperation with the OpenJDK TLS provider (generally to // test renegotiation, since we don't support initiating renegotiation but do // support peer-initiated renegotiation). The JDK TLS provider doesn't work // if Conscrypt is installed as the default provider, so these need to live in // a different task than the other tests, most of which need Conscrypt to be // installed to function. task testInterop(type: Test) { include "org/conscrypt/ConscryptEngineTest.class" include "org/conscrypt/RenegotiationTest.class" } check.dependsOn testInterop jacocoTestReport { additionalSourceDirs.from files("$rootDir/openjdk/src/test/java", "$rootDir/common/src/main/java") executionData tasks.withType(Test) } 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)] } static String classifierFor(osName, archName) { "${osName}-${archName}" } static String sourceSetName(classifier) { classifier.replaceAll("-", "_") }