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