1plugins { 2 id 'com.github.johnrengelman.shadow' version '6.0.0' 3} 4 5import aQute.bnd.gradle.BundleTaskConvention 6import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar 7import org.codehaus.groovy.runtime.InvokerHelper 8 9apply plugin: 'biz.aQute.bnd.builder' 10 11description = 'Conscrypt: OpenJdk' 12 13ext { 14 jniSourceDir = "$rootDir/common/src/jni" 15 assert file("$jniSourceDir").exists() 16 17 // Build the list of classifiers that will be used in the build. 18 arch32Name = 'x86' 19 arch64Name = 'x86_64' 20 nativeClassifiers = [] 21 nativeClassifier64Bit = null 22 nativeClassifier32Bit = null 23 preferredClassifier = null 24 preferredSourceSet = null 25 preferredNativeFileDir = null 26 if (build64Bit) { 27 // Add the 64-Bit classifier first, as the preferred classifier. 28 nativeClassifier64Bit = classifierFor(osName, arch64Name) 29 nativeClassifiers += nativeClassifier64Bit 30 preferredClassifier = nativeClassifier64Bit 31 preferredSourceSet = sourceSetName(preferredClassifier) 32 preferredNativeFileDir = nativeResourcesDir(preferredClassifier) 33 } 34 if (build32Bit) { 35 nativeClassifier32Bit = classifierFor(osName, arch32Name) 36 nativeClassifiers += nativeClassifier32Bit 37 if (preferredClassifier == null) { 38 preferredClassifier = nativeClassifier32Bit 39 preferredSourceSet = sourceSetName(preferredClassifier) 40 preferredNativeFileDir = nativeResourcesDir(preferredClassifier) 41 } 42 } 43} 44 45sourceSets { 46 47 main { 48 java { 49 srcDirs += "${rootDir}/common/src/main/java" 50 srcDirs += project(':conscrypt-constants').sourceSets.main.java.srcDirs 51 } 52 resources { 53 srcDirs += "build/generated/resources" 54 } 55 } 56 57 platform { 58 java { 59 srcDirs = [ "src/main/java" ] 60 includes = [ "org/conscrypt/Platform.java" ] 61 } 62 } 63 64 test { 65 java { 66 srcDirs += "${rootDir}/common/src/test/java" 67 } 68 resources { 69 srcDirs += "${rootDir}/common/src/test/resources" 70 // This shouldn't be needed but seems to help IntelliJ locate the native artifact. 71 srcDirs += preferredNativeFileDir 72 } 73 } 74 75 // Add the source sets for each of the native build 76 nativeClassifiers.each { nativeClassifier -> 77 def sourceSetName = sourceSetName(nativeClassifier) 78 79 // Main sources for the native build 80 "$sourceSetName" { 81 output.dir(nativeResourcesDir(nativeClassifier), builtBy: "copyNativeLib${sourceSetName}") 82 } 83 } 84} 85 86compileJava { 87 dependsOn generateProperties 88} 89 90tasks.register("platformJar", Jar) { 91 from sourceSets.platform.output 92} 93 94tasks.register("testJar", ShadowJar) { 95 classifier = 'tests' 96 configurations = [project.configurations.testRuntime] 97 from sourceSets.test.output 98} 99 100if (isExecutableOnPath('cpplint')) { 101 def cpplint = tasks.register("cpplint", Exec) { 102 executable = 'cpplint' 103 104 // TODO(nmittler): Is there a better way of getting the JNI sources? 105 def pattern = ['**/*.cc', '**/*.h'] 106 def sourceFiles = fileTree(dir: jniSourceDir, includes: pattern).asPath.tokenize(':') 107 // Adding roots so that class #ifdefs don't require full path from the project root. 108 args = sourceFiles 109 110 // Capture stderr from the process 111 errorOutput = new ByteArrayOutputStream(); 112 113 // Need to ignore exit value so that doLast will execute. 114 ignoreExitValue = true 115 116 doLast { 117 // Create the report file. 118 def reportDir = file("${buildDir}/cpplint") 119 reportDir.mkdirs(); 120 def reportFile = new File(reportDir, "report.txt") 121 def reportStream = new FileOutputStream(reportFile) 122 123 try { 124 // Check for failure 125 if (execResult != null) { 126 execResult.assertNormalExitValue() 127 } 128 } catch (Exception e) { 129 // The process failed - get the error report from the stderr. 130 String report = errorOutput.toString(); 131 132 // Write the report to the console. 133 System.err.println(report) 134 135 // Also write the report file. 136 reportStream.write(report.bytes); 137 138 // Extension method cpplint.output() can be used to obtain the report 139 ext.output = { 140 return report 141 } 142 143 // Rethrow the exception to terminate the build. 144 throw e; 145 } finally { 146 reportStream.close(); 147 } 148 } 149 } 150 check.dependsOn cpplint 151} 152 153configurations { 154 publicApiDocs 155 platform 156} 157 158artifacts { 159 platform platformJar 160} 161 162apply from: "$rootDir/gradle/publishing.gradle" 163publishing.publications.maven { 164 artifact sourcesJar 165 artifact javadocJar 166} 167 168jar.manifest { 169 attributes ('BoringSSL-Version' : boringSslVersion, 170 'Automatic-Module-Name' : 'org.conscrypt', 171 'Bundle-SymbolicName': 'org.conscrypt', 172 '-exportcontents': 'org.conscrypt.*') 173} 174 175dependencies { 176 // This is used for the @Internal annotation processing in JavaDoc 177 publicApiDocs project(':conscrypt-api-doclet') 178 179 // This is listed as compile-only, but we absorb its contents. 180 compileOnly project(':conscrypt-constants') 181 182 testCompile project(':conscrypt-constants'), 183 project(path: ':conscrypt-testing', configuration: 'runtime'), 184 libraries.junit, 185 libraries.mockito 186 187 testRuntimeClasspath sourceSets["$preferredSourceSet"].output 188 189 platformCompileOnly sourceSets.main.output 190} 191 192nativeClassifiers.each { nativeClassifier -> 193 // Create the JAR task and add it's output to the published archives for this project 194 addNativeJar(nativeClassifier) 195 196 // Build the classes as part of the standard build. 197 classes.dependsOn sourceSet(nativeClassifier).classesTaskName 198} 199 200// Adds a JAR task for the native library. 201def addNativeJar(nativeClassifier) { 202 // Create a JAR for this configuration and add it to the output archives. 203 SourceSet sourceSet = sourceSet(nativeClassifier) 204 def jarTask = tasks.register(sourceSet.jarTaskName, Jar) { Jar t -> 205 // Depend on the regular classes task 206 dependsOn classes 207 manifest = jar.manifest 208 classifier = nativeClassifier 209 210 from sourceSet.output + sourceSets.main.output 211 212 // add OSGI headers 213 t.convention.plugins.bundle = new BundleTaskConvention(t) 214 t.doLast { 215 t.buildBundle() 216 } 217 } 218 219 // Add the jar task to the standard build. 220 jar.dependsOn jarTask 221 222 // Add it to the publishing archives list. 223 publishing.publications.maven.artifact jarTask.get() 224} 225 226 227// Check which version 228def javaError = new ByteArrayOutputStream() 229exec { 230 executable test.executable 231 System.out.println("Running tests with java executable: " + test.executable + ".") 232 args = ['-version'] 233 ignoreExitValue true 234 errorOutput = javaError 235} 236 237def suiteClass = (javaError.toString() =~ /"1[.]7[.].*"/) ? 238 "org/conscrypt/ConscryptJava7Suite.class" : "org/conscrypt/ConscryptSuite.class"; 239 240test { 241 include suiteClass, "org/conscrypt/ConscryptOpenJdkSuite.class" 242} 243 244def testFdSocket = tasks.register("testFdSocket", Test) { 245 include suiteClass, "org/conscrypt/ConscryptOpenJdkSuite.class" 246 InvokerHelper.setProperties(testLogging, test.testLogging.properties) 247 systemProperties = test.systemProperties 248 systemProperty "org.conscrypt.useEngineSocketByDefault", false 249} 250check.dependsOn testFdSocket 251 252// Tests that involve interoperation with the OpenJDK TLS provider (generally to 253// test renegotiation, since we don't support initiating renegotiation but do 254// support peer-initiated renegotiation). The JDK TLS provider doesn't work 255// if Conscrypt is installed as the default provider, so these need to live in 256// a different task than the other tests, most of which need Conscrypt to be 257// installed to function. 258def testInterop = tasks.register("testInterop", Test) { 259 include "org/conscrypt/ConscryptEngineTest.class" 260 include "org/conscrypt/RenegotiationTest.class" 261} 262check.dependsOn testInterop 263 264jacocoTestReport { 265 additionalSourceDirs.from files("$rootDir/openjdk/src/test/java", "$rootDir/common/src/main/java") 266 executionData tasks.withType(Test) 267} 268 269javadoc { 270 dependsOn(configurations.publicApiDocs) 271 options.doclet = "org.conscrypt.doclet.FilterDoclet" 272 options.docletpath = configurations.publicApiDocs.files as List 273} 274 275model { 276 platforms { 277 x86 { 278 architecture arch32Name 279 } 280 x86_64 { 281 architecture arch64Name 282 } 283 } 284 285 buildTypes { 286 release 287 } 288 289 components { 290 // Builds the JNI library. 291 conscrypt_openjdk_jni(NativeLibrarySpec) { 292 if (build32Bit) { targetPlatform arch32Name } 293 if (build64Bit) { targetPlatform arch64Name } 294 295 sources { 296 cpp { 297 source { 298 srcDirs "$jniSourceDir/main/cpp" 299 include "**/*.cc" 300 } 301 } 302 } 303 304 binaries { 305 // Build the JNI lib as a shared library. 306 withType (SharedLibraryBinarySpec) { 307 cppCompiler.define "CONSCRYPT_OPENJDK" 308 309 // Set up 32-bit vs 64-bit build 310 def building64Bit = false 311 def libPath 312 if (targetPlatform.getArchitecture().getName() == "x86") { 313 libPath = "$boringssl32BuildDir" 314 } else if (targetPlatform.getArchitecture().getName() == "x86-64") { 315 libPath = "$boringssl64BuildDir" 316 building64Bit = true 317 } else { 318 throw new GradleException("Unknown architecture: " + 319 targetPlatform.getArchitecture().name) 320 } 321 322 if (toolChain in Clang || toolChain in Gcc) { 323 cppCompiler.args "-Wall", 324 "-fPIC", 325 "-O3", 326 "-std=c++11", 327 "-I$jniSourceDir/main/include", 328 "-I$jniSourceDir/unbundled/include", 329 "-I$boringsslIncludeDir", 330 "-I$jdkIncludeDir", 331 "-I$jdkIncludeDir/linux", 332 "-I$jdkIncludeDir/darwin", 333 "-I$jdkIncludeDir/win32" 334 if (rootProject.hasProperty('checkErrorQueue')) { 335 System.out.println("Compiling with error queue checking enabled") 336 cppCompiler.define "CONSCRYPT_CHECK_ERROR_QUEUE" 337 } 338 339 // Static link to BoringSSL 340 linker.args "-O3", 341 "-fvisibility=hidden", 342 "-lpthread", 343 libPath + "/ssl/libssl.a", 344 libPath + "/crypto/libcrypto.a" 345 if (targetPlatform.operatingSystem.isLinux()) { 346 // Static link libstdc++ and libgcc because 347 // they are not available in some restrictive Linux 348 // environments. 349 linker.args "-static-libstdc++", 350 "-static-libgcc" 351 } else { 352 linker.args "-lstdc++" 353 } 354 } else if (toolChain in VisualCpp) { 355 cppCompiler.define "DLL_EXPORT" 356 cppCompiler.define "WIN32_LEAN_AND_MEAN" 357 cppCompiler.define "NOMINMAX" 358 if (building64Bit) { 359 cppCompiler.define "WIN64" 360 } 361 cppCompiler.define "_WINDOWS" 362 cppCompiler.define "UNICODE" 363 cppCompiler.define "_UNICODE" 364 cppCompiler.define "NDEBUG" 365 366 cppCompiler.args "/nologo", 367 "/MT", 368 "/WX-", 369 "/Wall", 370 "/O2", 371 "/Oi", 372 "/Ot", 373 "/GL", 374 "/GS", 375 "/Gy", 376 "/fp:precise", 377 "-wd4514", // Unreferenced inline function removed 378 "-wd4548", // Expression before comma has no effect 379 "-wd4625", // Copy constructor was implicitly defined as deleted 380 "-wd4626", // Assignment operator was implicitly defined as deleted 381 "-wd4710", // function not inlined 382 "-wd4711", // function inlined 383 "-wd4820", // Extra padding added to struct 384 "-wd4946", // reinterpret_cast used between related classes: 385 "-wd4996", // Thread safety for strerror 386 "-wd5027", // Move assignment operator was implicitly defined as deleted 387 "-I$jniSourceDir/main/include", 388 "-I$jniSourceDir/unbundled/include", 389 "-I$boringsslIncludeDir", 390 "-I$jdkIncludeDir", 391 "-I$jdkIncludeDir/win32" 392 393 // Static link to BoringSSL 394 linker.args "-WX", 395 "ws2_32.lib", 396 "advapi32.lib", 397 "${libPath}\\ssl\\ssl.lib", 398 "${libPath}\\crypto\\crypto.lib" 399 } 400 } 401 402 // Never build a static library. 403 withType(StaticLibraryBinarySpec) { 404 buildable = false 405 } 406 } 407 } 408 } 409 410 tasks { t -> 411 $.binaries.withType(SharedLibraryBinarySpec).each { binary -> 412 // Build the native artifact classifier from the OS and architecture. 413 def archName = binary.targetPlatform.architecture.name.replaceAll('-', '_') 414 def classifier = classifierFor(osName, archName) 415 def sourceSetName = sourceSetName("$classifier") 416 def source = binary.sharedLibraryFile 417 418 // Copies the native library to a resource location that will be included in the jar. 419 def copyTask = project.tasks.register("copyNativeLib${sourceSetName}", Copy) { 420 dependsOn binary.tasks.link 421 from source 422 // Rename the artifact to include the generated classifier 423 rename '(.+)(\\.[^\\.]+)', "\$1-$classifier\$2" 424 // Everything under will be included in the native jar. 425 into nativeResourcesDir(classifier) + '/META-INF/native' 426 } 427 428 // Now define a task to strip the release binary (linux only) 429 if (osName == 'linux' && (!rootProject.hasProperty('nostrip') || 430 !rootProject.nostrip.toBoolean())) { 431 def stripTask = binary.tasks.taskName("strip") 432 project.tasks.register(stripTask as String, Exec) { 433 dependsOn binary.tasks.link 434 executable "strip" 435 args binary.tasks.link.linkedFile.asFile.get() 436 } 437 copyTask.configure { 438 dependsOn stripTask 439 } 440 } 441 } 442 } 443} 444 445boolean isExecutableOnPath(executable) { 446 FilenameFilter filter = new FilenameFilter() { 447 @Override 448 boolean accept(File dir, String name) { 449 return executable.equals(name); 450 } 451 } 452 for(String folder : System.getenv('PATH').split("" + File.pathSeparatorChar)) { 453 File[] files = file(folder).listFiles(filter) 454 if (files != null && files.size() > 0) { 455 return true; 456 } 457 } 458 return false; 459} 460 461String nativeResourcesDir(nativeClassifier) { 462 def sourceSetName = sourceSetName(nativeClassifier) 463 "${buildDir}/${sourceSetName}/native-resources" 464} 465 466SourceSet sourceSet(classifier) { 467 sourceSets[sourceSetName(classifier)] 468} 469 470static String classifierFor(osName, archName) { 471 "${osName}-${archName}" 472} 473 474static String sourceSetName(classifier) { 475 classifier.replaceAll("-", "_") 476} 477 478