549 lines
18 KiB
Groovy
549 lines
18 KiB
Groovy
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;
|
|
}
|
|
|