| plugins { |
| id 'com.github.johnrengelman.shadow' version '7.1.2' |
| } |
| |
| import aQute.bnd.gradle.BundleTaskConvention |
| import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar |
| import org.codehaus.groovy.runtime.InvokerHelper |
| |
| apply plugin: 'biz.aQute.bnd.builder' |
| |
| description = 'Conscrypt: OpenJdk' |
| |
| // Gradle mostly uses Java os.arch names for architectures which feeds into default |
| // targetPlatform names. Notable exception Gradle 6.9.x reports MacOS/ARM as |
| // arm-v8. |
| // |
| // The Maven osdetector plugin (which we recommend to developers) uses different |
| // arch names, so that's what we need for artifacts. |
| // |
| // This class encapsulates both naming schemes as well as other per-platform information |
| // about native builds, more of which will migrate in here over time. |
| enum NativeBuildInfo { |
| WINDOWS_X86_64("windows", "x86_64"), |
| LINUX_X86_64("linux", "x86_64"), |
| MAC_X86_64("osx", "x86_64") { |
| String libDir() { |
| "build.x86" |
| } |
| }, |
| MAC_AARCH64("osx", "aarch_64") { |
| String libDir() { |
| "build.arm" |
| } |
| }; |
| |
| static String buildDir = "FIXME" // See below |
| |
| public final String os |
| public final String arch |
| |
| // Maps osdetector arch to Gradle equivalent. |
| private static final gradleArchMap = [ |
| "aarch_64": "aarch64", |
| "x86_64" : "x86-64", |
| ] |
| |
| NativeBuildInfo(String os, String arch) { |
| this.os = os |
| this.arch = arch |
| } |
| |
| // Classifier as generated by Maven osdetector. |
| String mavenClassifier() { |
| "${os}-${arch}" |
| } |
| |
| // Gradle equivalent to Maven arch |
| String gradleArch() { |
| gradleArch(arch) |
| } |
| |
| // Output directory for native resources |
| String nativeResourcesDir() { |
| "$buildDir/${mavenClassifier()}/native-resources" |
| } |
| |
| // Directory for native resources inside final jar. |
| String jarNativeResourcesDir() { |
| nativeResourcesDir() + '/META-INF/native' |
| } |
| |
| // Target platform identifier as used by Gradle |
| String targetPlatform() { |
| "${os}_${gradleArch()}" |
| } |
| |
| String libDir() { |
| "build64" |
| } |
| |
| static String gradleArch(String arch) { |
| gradleArchMap.get(arch) |
| } |
| |
| static NativeBuildInfo findForGradle(String os, String arch) { |
| values().find { |
| it.os == os && it.gradleArch() == arch |
| } |
| } |
| |
| static NativeBuildInfo find(String os, String arch) { |
| values().find { |
| it.os == os && it.arch == arch |
| } |
| } |
| |
| static NativeBuildInfo find(NativePlatform targetPlatform) { |
| String targetOS = targetPlatform.operatingSystem.name |
| String targetArch = targetPlatform.architecture.name |
| def result = findForGradle(targetOS, targetArch) |
| assert result != null : "Unknown target platform: ${targetOS}-${targetArch}" |
| result |
| } |
| |
| static findAll(String os) { |
| values().findAll { |
| it.os == os |
| } |
| } |
| } |
| |
| // TODO: There has to be a better way of accessing Gradle properties from Groovy code than this |
| NativeBuildInfo.buildDir = "$buildDir" |
| |
| ext { |
| jniSourceDir = "$rootDir/common/src/jni" |
| assert file("$jniSourceDir").exists() |
| |
| // Decide which targets we should build and test |
| nativeBuilds = NativeBuildInfo.findAll("${osdetector.os}") |
| buildToTest = NativeBuildInfo.find("${osdetector.os}", "${osdetector.arch}") |
| |
| assert !nativeBuilds.isEmpty() : "No native builds selected." |
| assert buildToTest != null : "No test build selected for os.arch = ${osdetector.arch}" |
| |
| // Compatibility with other sub-projects |
| preferredSourceSet = buildToTest.mavenClassifier() |
| preferredNativeFileDir = buildToTest.nativeResourcesDir() |
| } |
| |
| 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 |
| srcDirs += buildToTest.nativeResourcesDir() |
| } |
| } |
| |
| // Add the source sets for each of the native builds |
| nativeBuilds.each { nativeBuild -> |
| String sourceSetName = nativeBuild.mavenClassifier() |
| String nativeDir = nativeBuild.nativeResourcesDir() |
| |
| // Main sources for the native build |
| "$sourceSetName" { |
| output.dir(nativeDir, builtBy: "copyNativeLib${sourceSetName}") |
| } |
| } |
| } |
| |
| compileJava { |
| dependsOn generateProperties |
| } |
| |
| processResources { |
| dependsOn generateProperties |
| } |
| |
| tasks.register("platformJar", Jar) { |
| from sourceSets.platform.output |
| } |
| |
| tasks.register("testJar", ShadowJar) { |
| classifier = 'tests' |
| configurations = [project.configurations.testRuntimeClasspath] |
| from sourceSets.test.output |
| } |
| |
| if (isExecutableOnPath('cpplint')) { |
| def cpplint = tasks.register("cpplint", 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(path: ':conscrypt-testing', configuration: 'shadow'), |
| libraries.junit, |
| libraries.mockito |
| |
| testRuntimeOnly sourceSets["$preferredSourceSet"].output |
| |
| platformCompileOnly sourceSets.main.output |
| } |
| |
| nativeBuilds.each { nativeBuild -> |
| // Create the JAR task and add it's output to the published archives for this project |
| addNativeJar(nativeBuild) |
| |
| // Build the classes as part of the standard build. |
| classes.dependsOn sourceSets[nativeBuild.mavenClassifier()].classesTaskName |
| } |
| |
| // Adds a JAR task for the native library. |
| def addNativeJar(NativeBuildInfo nativeBuild) { |
| // Create a JAR for this configuration and add it to the output archives. |
| SourceSet sourceSet = sourceSets[nativeBuild.mavenClassifier()] |
| def jarTask = tasks.register(sourceSet.jarTaskName, Jar) { Jar t -> |
| // Depend on the regular classes task |
| dependsOn classes |
| manifest = jar.manifest |
| classifier = nativeBuild.mavenClassifier() |
| |
| from sourceSet.output + sourceSets.main.output |
| |
| // add OSGI headers |
| t.convention.plugins.bundle = new BundleTaskConvention(t) |
| t.doLast { |
| t.buildBundle() |
| } |
| } |
| |
| // Add the jar task to the standard build. |
| jar.dependsOn jarTask |
| |
| // Add it to the publishing archives list. |
| publishing.publications.maven.artifact jarTask.get() |
| } |
| |
| |
| // TODO(prb) Still provide a mechanism for testing on Java 7? |
| // 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"; |
| def suiteClass = "org/conscrypt/ConscryptSuite.class"; |
| |
| test { |
| include suiteClass, "org/conscrypt/ConscryptOpenJdkSuite.class" |
| } |
| |
| def testFdSocket = tasks.register("testFdSocket", Test) { |
| include suiteClass, "org/conscrypt/ConscryptOpenJdkSuite.class" |
| InvokerHelper.setProperties(testLogging, test.testLogging.properties) |
| systemProperties = test.systemProperties |
| systemProperty "org.conscrypt.useEngineSocketByDefault", false |
| } |
| check.dependsOn testFdSocket |
| |
| // 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. |
| def testInterop = tasks.register("testInterop", 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) |
| // TODO(prb): Update doclet to Java 11. |
| // options.doclet = "org.conscrypt.doclet.FilterDoclet" |
| // options.docletpath = configurations.publicApiDocs.files as List |
| } |
| |
| def jniIncludeDir() { |
| def result = "" |
| java { |
| def jdkHome = javaToolchains.compilerFor(toolchain).get().metadata.getInstallationPath() |
| result = jdkHome.file("include").toString() |
| } |
| result |
| } |
| |
| model { |
| buildTypes { |
| release |
| } |
| |
| components { |
| // Builds the JNI library. |
| conscrypt_openjdk_jni(NativeLibrarySpec) { |
| nativeBuilds.each { nativeBuild -> |
| targetPlatform nativeBuild.targetPlatform() |
| } |
| |
| sources { |
| cpp { |
| source { |
| srcDirs "$jniSourceDir/main/cpp" |
| include "**/*.cc" |
| } |
| } |
| } |
| |
| binaries { |
| // Build the JNI lib as a shared library. |
| withType (SharedLibraryBinarySpec) { |
| cppCompiler.define "CONSCRYPT_OPENJDK" |
| def jdkIncludeDir = jniIncludeDir() |
| def nativeBuild = NativeBuildInfo.find(targetPlatform) |
| String libPath = "$boringsslHome/${nativeBuild.libDir()}" |
| |
| if (toolChain in Clang || toolChain in Gcc) { |
| cppCompiler.args "-Wall", |
| "-fPIC", |
| "-O3", |
| "-std=c++17", |
| "-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", |
| "-lpthread", |
| libPath + "/ssl/libssl.a", |
| libPath + "/crypto/libcrypto.a" |
| if (targetPlatform.operatingSystem.isLinux()) { |
| // Static link libstdc++ and libgcc because |
| // they are not available in some restrictive Linux |
| // environments. |
| linker.args "-static-libstdc++", |
| "-static-libgcc" |
| } else { |
| linker.args "-lstdc++" |
| } |
| } else if (toolChain in VisualCpp) { |
| cppCompiler.define "DLL_EXPORT" |
| cppCompiler.define "WIN32_LEAN_AND_MEAN" |
| cppCompiler.define "NOMINMAX" |
| 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 -> |
| def nativeBuild = NativeBuildInfo.find(binary.targetPlatform) |
| def classifier = nativeBuild.mavenClassifier() |
| def source = binary.sharedLibraryFile |
| |
| // Copies the native library to a resource location that will be included in the jar. |
| def copyTask = project.tasks.register("copyNativeLib${classifier}", 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 nativeBuild.jarNativeResourcesDir() |
| } |
| processResources { |
| dependsOn copyTask |
| } |
| processTestResources { |
| dependsOn copyTask |
| } |
| |
| // Now define a task to strip the release binary (linux only) |
| if (osdetector.os == 'linux' && (!rootProject.hasProperty('nostrip') || |
| !rootProject.nostrip.toBoolean())) { |
| def stripTask = binary.tasks.taskName("strip") |
| project.tasks.register(stripTask as String, Exec) { |
| dependsOn binary.tasks.link |
| executable "strip" |
| args binary.tasks.link.linkedFile.asFile.get() |
| } |
| copyTask.configure { |
| 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; |
| } |
| |