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