diff --git a/build.gradle b/build.gradle index 9b7b1ad2..cc979fd4 100644 --- a/build.gradle +++ b/build.gradle @@ -1,380 +1,433 @@ +import org.gradle.api.tasks.testing.logging.TestLogEvent +import org.gradle.api.tasks.testing.logging.TestExceptionFormat +import java.lang.ProcessBuilder.Redirect +import java.util.concurrent.TimeUnit + plugins { - id 'com.github.johnrengelman.shadow' version '7.1.2' + java + id("com.github.johnrengelman.shadow") version "7.1.2" } -apply plugin: 'java' - // Note: For this setup to work you must follow the instructions outlined in the // checker manual Section 25.3 "Building from Source" // http://types.cs.washington.edu/checker-framework/current/checkers-manual.html#build-source -ext { - jsr308 = System.getenv('JSR308') ?: file(new File("..")).absolutePath - checkerFrameworkPath = System.getenv('CHECKERFRAMEWORK') ?: "${jsr308}/checker-framework" - checkerJar = "${checkerFrameworkPath}/checker/dist/checker.jar" - afu = "${jsr308}/annotation-tools/annotation-file-utilities" - z3Jar = "${projectDir}/lib/com.microsoft.z3.jar" - lingelingTar = "${projectDir}/lib/lingeling.tar.gz" - dljcScript = "${jsr308}/do-like-javac/dljc" - - // On a Java 8 JVM, use error-prone javac and source/target 8. - // On a Java 9+ JVM, use the host javac, default source/target, and required module flags. - isJava8 = JavaVersion.current() == JavaVersion.VERSION_1_8 - - errorproneJavacVersion = '9+181-r4173-1' -} - -println '====================================' -println ' Checker Framework Inference ' -println '====================================' -println '' -println '-------------------------------' -println 'Important Environment Variables' -println '-------------------------------' -println 'CHECKERFRAMEWORK: ' + checkerFrameworkPath +// Begin of extra project properties +val jsr308 by extra(System.getenv("JSR308") ?: file(File("..")).absolutePath) +val checkerFrameworkPath by extra(System.getenv("CHECKERFRAMEWORK") ?: "${jsr308}/checker-framework") +val checkerJar by extra("${checkerFrameworkPath}/checker/dist/checker.jar") +val afu by extra("${jsr308}/annotation-tools/annotation-file-utilities") +val z3Jar by extra("${projectDir}/lib/com.microsoft.z3.jar") +val lingelingTar by extra("${projectDir}/lib/lingeling.tar.gz") +val dljcScript by extra("${jsr308}/do-like-javac/dljc") + +// On a Java 8 JVM, use error-prone javac and source/target 8. +// On a Java 9+ JVM, use the host javac, default source/target, and required module flags. +val isJava8 by extra(JavaVersion.current() == JavaVersion.VERSION_1_8) + +val errorproneJavacVersion by extra("9+181-r4173-1") +// End of extra project properties + +println( +""" +==================================== + Checker Framework Inference +==================================== + +------------------------------- +Important Environment Variables +------------------------------- +CHECKERFRAMEWORK: $checkerFrameworkPath +""".trimIndent() +) repositories { mavenCentral() } -configurations { - javacJar -} +val javacJar by configurations.creating dependencies { if (isJava8) { - javacJar group: 'com.google.errorprone', name: 'javac', version: "$errorproneJavacVersion" + javacJar("com.google.errorprone:javac:$errorproneJavacVersion") } - implementation files("${checkerJar}") - implementation group: 'com.google.errorprone', name: 'javac', version: "$errorproneJavacVersion" + implementation(files(checkerJar)) + implementation("com.google.errorprone:javac:$errorproneJavacVersion") - implementation 'org.plumelib:options:1.0.5' - implementation 'org.plumelib:plume-util:1.5.8' + implementation("org.plumelib:options:1.0.5") + implementation("org.plumelib:plume-util:1.5.8") - implementation 'com.google.guava:guava:31.1-jre' + implementation("com.google.guava:guava:31.1-jre") // AFU is an "includedBuild" imported in settings.gradle, so the version number doesn't matter. // https://docs.gradle.org/current/userguide/composite_builds.html#settings_defined_composite - implementation('org.checkerframework:annotation-file-utilities:*') { - exclude group: 'com.google.errorprone', module: 'javac' + implementation("org.checkerframework:annotation-file-utilities:*") { + exclude(group = "com.google.errorprone", module = "javac") } // Serialize constraints - implementation 'com.googlecode.json-simple:json-simple:1.1.1' + implementation("com.googlecode.json-simple:json-simple:1.1.1") // Pretty print serialized constraints - implementation 'com.google.code.gson:gson:2.9.0' + implementation("com.google.code.gson:gson:2.9.0") - implementation 'org.ow2.sat4j:org.ow2.sat4j.core:2.3.6' - implementation 'org.ow2.sat4j:org.ow2.sat4j.maxsat:2.3.6' - implementation files(z3Jar) + implementation("org.ow2.sat4j:org.ow2.sat4j.core:2.3.6") + implementation("org.ow2.sat4j:org.ow2.sat4j.maxsat:2.3.6") + implementation(files(z3Jar)) - testImplementation fileTree(dir: "${checkerFrameworkPath}/framework-test/build/libs", include: "framework-test-*.jar") + testImplementation( + fileTree( + "dir" to "${checkerFrameworkPath}/framework-test/build/libs", + "include" to "framework-test-*.jar" + ) + ) // Mocking library. Used in a couple tests - testImplementation 'org.mockito:mockito-all:2.0.2-beta' - testImplementation 'junit:junit:4.13.2' + testImplementation("org.mockito:mockito-all:2.0.2-beta") + testImplementation("junit:junit:4.13.2") } sourceSets { main { java { - srcDirs = ["src"] + setSrcDirs(listOf("src")) } resources { - srcDir "src" - include "**/*.astub" + srcDir("src") + include("**/*.astub") } } test { java { - srcDirs = ["tests"] + setSrcDirs(listOf("tests")) } } } -task buildZ3(type: Exec) { - description 'Build Z3 solver' - onlyIf { !(new File(z3Jar).exists()) } - workingDir './scripts' - commandLine './buildZ3' +val buildZ3 by tasks.registering(Exec::class) { + description = "Build Z3 solver" + onlyIf { !(File(z3Jar).exists()) } + workingDir("./scripts") + commandLine("./buildZ3") } -task buildLingeling(type: Exec) { - description 'Build Lingeling solver' - onlyIf { !(new File(lingelingTar).exists()) } - workingDir './scripts' - commandLine './buildLingeling' +val buildLingeling by tasks.registering(Exec::class) { + description = "Build Lingeling solver" + onlyIf { !(File(lingelingTar).exists()) } + workingDir("./scripts") + commandLine("./buildLingeling") } -task buildDLJC(type: Exec) { - description 'Build DLJC Tool' - onlyIf { !(new File(dljcScript).exists()) } - workingDir './scripts' - commandLine './buildDLJC' +val buildDLJC by tasks.registering(Exec::class) { + description = "Build DLJC Tool" + onlyIf { !(File(dljcScript).exists()) } + workingDir("./scripts") + commandLine("./buildDLJC") } -test { - dependsOn(shadowJar) - dependsOn('dist') +// After each test, this listener will be notified to print a summary. +// See https://github.com/gradle/gradle/issues/5431 +val testSummaryPrinter = object : TestListener { + override fun beforeSuite(suite: TestDescriptor) {} + override fun beforeTest(testDescriptor: TestDescriptor) {} + override fun afterTest(testDescriptor: TestDescriptor, result: TestResult) {} + + override fun afterSuite(desc: TestDescriptor, result: TestResult) { + if (desc.className != null) { + val mils = result.endTime - result.startTime + val seconds = mils / 1000.0 + + println( + "Testsuite: ${desc.className}\n" + + "Tests run: ${result.testCount}, " + + "Failures: ${result.failedTestCount}, " + + "Skipped: ${result.skippedTestCount}, " + + "Time elapsed: $seconds sec\n" + ) + } + } +} - systemProperties 'path.afu.scripts': "${afu}/scripts", - 'use.hacks': true +tasks.test { + dependsOn(tasks.shadowJar) + dependsOn(dist) - systemProperties += [JDK_JAR: "${checkerFrameworkPath}/checker/dist/jdk8.jar"] + systemProperties( + "path.afu.scripts" to "${afu}/scripts", + "use.hacks" to true, + "JDK_JAR" to "${checkerFrameworkPath}/checker/dist/jdk8.jar" + ) - if (project.hasProperty('emit.test.debug')) { - systemProperties += ["emit.test.debug": 'true'] + if (project.hasProperty("emit.test.debug")) { + systemProperties("emit.test.debug" to "true") } if (isJava8) { - jvmArgs += ["-Xbootclasspath/p:${configurations.javacJar.asPath}"] + jvmArgs("-Xbootclasspath/p:${javacJar.asPath}") } else { // Without this, the test throw "java.lang.OutOfMemoryError: Java heap space" // Corresponding pull request: https://github.com/opprop/checker-framework-inference/pull/263 - forkEvery(1) + setForkEvery(1) } testLogging { // Always run the tests outputs.upToDateWhen { false } // The following prints out each time a test is passed. - // events "passed", "skipped", "failed", "standardOut", "standardError" + // events = mutableSetOf( + // TestLogEvent.PASSED, + // TestLogEvent.SKIPPED, + // TestLogEvent.FAILED, + // TestLogEvent.STANDARD_OUT, + // TestLogEvent.STANDARD_ERROR + // ) // Show the found unexpected diagnostics and expected diagnostics not found. - exceptionFormat "full" + exceptionFormat = TestExceptionFormat.FULL } - // After each test, print a summary. - afterSuite { desc, result -> - if (desc.getClassName() != null) { - long mils = result.getEndTime() - result.getStartTime() - double seconds = mils / 1000.0 - - println "Testsuite: ${desc.getClassName()}\n" + - "Tests run: ${result.testCount}, " + - "Failures: ${result.failedTestCount}, " + - "Skipped: ${result.skippedTestCount}, " + - "Time elapsed: ${seconds} sec\n" - } - - } + addTestListener(testSummaryPrinter) } -compileJava { +tasks.compileJava { dependsOn(buildZ3) dependsOn(buildLingeling) dependsOn(buildDLJC) - options.compilerArgs = [ - '-implicit:class', - '-Awarns', - '-Xmaxwarns', '10000', - // Can't use this because JSON library contains raw types: - // '-Xlint:unchecked', - '-Xlint:deprecation', - '-Werror', - ] + options.compilerArgs = mutableListOf( + "-implicit:class", + "-Awarns", + "-Xmaxwarns", "10000", + // Can"t use this because JSON library contains raw types: + // "-Xlint:unchecked", + "-Xlint:deprecation", + "-Werror", + ) } -// Exclude parts of the build directory that don't include classes from being packaged in +// Exclude parts of the build directory that don"t include classes from being packaged in // the jar file. // IMPORTANT: If "libs" is packaged in the JAR file you end up with an infinitely // recursive jar task that will fill up your hard drive (eventually) -jar { - description = 'Makes a jar with ONLY the classes compiled for checker ' + - 'framework inference and NONE of its dependencies' - archiveFileName = "checker-framework-inference.jar" - manifest.attributes("Main-Class": "checkers.inference.InferenceLauncher") +tasks.jar { + description = "Makes a jar with ONLY the classes compiled for checker " + + "framework inference and NONE of its dependencies" + archiveFileName.set("checker-framework-inference.jar") + manifest.attributes("Main-Class" to "checkers.inference.InferenceLauncher") exclude("dependency-cache", "libs", "tmp") } -shadowJar { +tasks.shadowJar { dependencies { exclude(dependency("junit:.*:.*")) } - description 'Creates the "fat" checker.jar in dist' - destinationDirectory = file("${projectDir}/dist") - archiveFileName = "checker-framework-inference.jar" + description = "Creates the \"fat\" checker.jar in dist" + destinationDirectory.set(file("${projectDir}/dist")) + archiveFileName.set("checker-framework-inference.jar") } -task dist(dependsOn: shadowJar, type: Copy) { +val dist by tasks.registering(Copy::class) { + dependsOn(tasks.shadowJar) + description = "If your Checker Framework project is fully built, this task " + "will build checker-framework-inference.jar, copy all the relevant runtime jars into " + "the dist directory." - from files( + from( + files( "${checkerFrameworkPath}/checker/dist/jdk8.jar", "${checkerFrameworkPath}/checker/dist/checker.jar", "${checkerFrameworkPath}/checker/dist/checker-qual.jar", "${checkerFrameworkPath}/checker/dist/checker-util.jar", "${checkerFrameworkPath}/checker/dist/javac.jar", + ) ) - into file('dist') + into(file("dist")) } -task dependenciesJar(dependsOn: dist, type: Jar) { - description 'Create a jar file with all the dependencies.' - duplicatesStrategy DuplicatesStrategy.EXCLUDE - from { - configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } - } - archiveFileName = 'dependencies.jar' - destinationDirectory = file("${projectDir}/dist/") +val dependenciesJar by tasks.registering(Jar::class) { + dependsOn(dist) + + description = "Create a jar file with all the dependencies." + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + from(configurations.runtimeClasspath.get().files.map { if (it.isDirectory) it else zipTree(it) }) + archiveFileName.set("dependencies.jar") + destinationDirectory.set(file("${projectDir}/dist/")) } -task testLibJar(dependsOn: dist, type: Jar) { - from sourceSets.test.output.classesDirs - include ('checkers/inference/test/**') - archiveFileName = 'inference-framework-test-lib.jar' - destinationDirectory = file("${projectDir}/dist/") +val testLibJar by tasks.registering(Jar::class) { + dependsOn(dist) + + from(sourceSets.test.get().output.classesDirs) + include("checkers/inference/test/**") + archiveFileName.set("inference-framework-test-lib.jar") + destinationDirectory.set(file("${projectDir}/dist/")) } tasks.clean { - delete += "build/libs/checker-framework-inference.zip" - delete += "jdk8.jar" - delete += "javac.jar" - delete += fileTree('dist') { - include '**/*.jar' - } - delete += "testdata/tmp" + delete("build/libs/checker-framework-inference.zip") + delete("jdk8.jar") + delete("javac.jar") + delete(fileTree("dist") { + include("**/*.jar") + }) + delete("testdata/tmp") } - -task release(type: Zip) { - from('src') { - into('release/src') +val release by tasks.registering(Zip::class) { + from("src") { + into("release/src") } - from('dist') { - into('release/dist') + from("dist") { + into("release/dist") } - from('scripts') { - into('release/scripts') - include '*.py' + from("scripts") { + into("release/scripts") + include("*.py") } - archiveBaseName = 'release' + archiveBaseName.set("release") } -task cloneAndBuildDependencies(type: Exec) { - description 'Clones (or updates) and builds all dependencies' - executable './.ci-build-without-test.sh' +val cloneAndBuildDependencies by tasks.registering(Exec::class) { + description = "Clones (or updates) and builds all dependencies" + executable = "./.ci-build-without-test.sh" } -task testCheckerInferenceScript(type: Exec, dependsOn: dist) { - description 'Basic sanity check of scripts/inference' - executable './scripts/inference' - args = ['--mode', 'TYPECHECK', - '--checker', 'ostrusted.OsTrustedChecker', - '--solver', 'checkers.inference.solver.PropagationSolver', - 'testdata/ostrusted/Test.java'] +val testCheckerInferenceScript by tasks.registering(Exec::class) { + dependsOn(dist) + + description = "Basic sanity check of scripts/inference" + executable = "./scripts/inference" + setArgs( + listOf( + "--mode", "TYPECHECK", + "--checker", "ostrusted.OsTrustedChecker", + "--solver", "checkers.inference.solver.PropagationSolver", + "testdata/ostrusted/Test.java" + ) + ) } -task testCheckerInferenceDevScript(type: Exec, dependsOn: [dist, dependenciesJar]) { - description 'Basic sanity check of scripts/inference-dev' - executable './scripts/inference-dev' - args = ['--mode', 'INFER', - '--checker', 'interning.InterningChecker', - '--solver', 'checkers.inference.solver.MaxSat2TypeSolver', - '--hacks=true', - 'testdata/interning/MapAssignment.java'] +val testCheckerInferenceDevScript by tasks.registering(Exec::class) { + dependsOn(dist, dependenciesJar) + + description = "Basic sanity check of scripts/inference-dev" + executable = "./scripts/inference-dev" + setArgs( + listOf( + "--mode", "INFER", + "--checker", "interning.InterningChecker", + "--solver", "checkers.inference.solver.MaxSat2TypeSolver", + "--hacks=true", + "testdata/interning/MapAssignment.java" + ) + ) } -task testDataflowExternalSolvers(type: Exec, dependsOn: [dist, dependenciesJar]) { - description 'Test Dataflow type system on its external solvers Lingeling and LogicBlox' - executable './testing/dataflowexample/ci-test.sh' +val testDataflowExternalSolvers by tasks.registering(Exec::class) { + dependsOn(dist, dependenciesJar) + + description = "Test Dataflow type system on its external solvers Lingeling and LogicBlox" + executable = "./testing/dataflowexample/ci-test.sh" } afterEvaluate { // Create a task for each test class whose name is the same as the class name. - sourceSets.test.java.filter { - it.path.contains('tests/checkers') && - it.path.endsWith('Test.java') && - !it.path.contains('CFInferenceTest') - }.forEach { file -> - String junitClassName = file.name.replaceAll(".java", "") - tasks.create(name: "${junitClassName}", type: Test, group: 'Verification') { - description "Run ${junitClassName} tests." - include "**/${name}.class" + sourceSets.test.get().java.filter { + it.path.contains("tests/checkers") && + it.path.endsWith("Test.java") && + !it.path.contains("CFInferenceTest") + }.forEach { file -> + val junitClassName = file.name.replace(".java", "") + tasks.create(name = junitClassName, type = Test::class) { + group = "Verification" + description = "Run $junitClassName tests." + include("**/${name}.class") } } // Configure Tests - tasks.withType(Test) { - dependsOn(shadowJar) - - systemProperties 'path.afu.scripts': "${afu}/scripts", - 'path.inference.script': "${projectDir}/scripts/inference", - 'use.hacks': true, - JDK_JAR: "${checkerFrameworkPath}/checker/dist/jdk8.jar" - - if (project.hasProperty('emit.test.debug')) { - systemProperties += ["emit.test.debug": 'true'] + tasks.withType(Test::class) { + dependsOn(tasks.shadowJar) + + systemProperties( + "path.afu.scripts" to "${afu}/scripts", + "path.inference.script" to "${projectDir}/scripts/inference", + "use.hacks" to true, + "JDK_JAR" to "${checkerFrameworkPath}/checker/dist/jdk8.jar" + ) + + if (project.hasProperty("emit.test.debug")) { + systemProperties("emit.test.debug" to "true") } if (isJava8) { - jvmArgs += ["-Xbootclasspath/p:${configurations.javacJar.asPath}"] + jvmArgs("-Xbootclasspath/p:${javacJar.asPath}") } testLogging { // Always run the tests outputs.upToDateWhen { false } // The following prints out each time a test is passed. - // events "passed", "skipped", "failed", "standardOut", "standardError" - events "failed" + events = mutableSetOf( + // TestLogEvent.PASSED, + // TestLogEvent.SKIPPED, + TestLogEvent.FAILED, + // TestLogEvent.STANDARD_OUT, + // TestLogEvent.STANDARD_ERROR + ) + // Show the found unexpected diagnostics and expected diagnostics not found. - exceptionFormat "full" + exceptionFormat = TestExceptionFormat.FULL } // After each test, print a summary. - afterSuite { desc, result -> - if (desc.getClassName() != null) { - long mils = result.getEndTime() - result.getStartTime() - double seconds = mils / 1000.0 - - println "Testsuite: ${desc.getClassName()}\n" + - "Tests run: ${result.testCount}, " + - "Failures: ${result.failedTestCount}, " + - "Skipped: ${result.skippedTestCount}, " + - "Time elapsed: ${seconds} sec\n" - } - } + // See https://github.com/gradle/kotlin-dsl/issues/836 + addTestListener(testSummaryPrinter) } } +// TODO: convert the commented out code to Kotlin /// Commented out because plugins section is commented out // /* Configuration for formatting */ // googleJavaFormat { -// // toolVersion '1.3' -// options style: 'AOSP' +// // toolVersion "1.3" +// options style: "AOSP" // } // tasks.googleJavaFormat { -// group 'Formatting' +// group "Formatting" // description = "Reformat Java source code with Google-Java-format" -// exclude 'testing' -// exclude 'testinputs' +// exclude "testing" +// exclude "testinputs" // } // tasks.verifyGoogleJavaFormat { -// group 'Formatting' +// group "Formatting" // description = "Check Java source code is in Google-Java-format" -// exclude 'testing' -// exclude 'testinputs' +// exclude "testing" +// exclude "testinputs" // } -task etags { +val etags by tasks.registering { doLast { - def sources = (sourceSets.main.java).getFiles().collect({ src -> src.getPath() }).sort() - def sourcesStr = sources.inject(null, { acc, source -> acc ? acc + " " + source : source }) - - def proc = "etags ${sourcesStr} ".execute() - proc.in.eachLine { line -> println line } - proc.err.eachLine { line -> println 'ERROR: ' + line } - proc.waitFor() + val sources = sourceSets.main.get().java.files.map { src -> src.path }.sorted() + val sourcesStr = sources.joinToString(" ") + + val proc = ProcessBuilder("etags", sourcesStr) + .directory(rootProject.projectDir) + .redirectOutput(Redirect.INHERIT) + .redirectError(Redirect.INHERIT) + .start() + proc.inputStream.bufferedReader().useLines { line -> println(line) } + proc.errorStream.bufferedReader().useLines { line -> println("ERROR: $line") } + proc.waitFor(10, TimeUnit.MINUTES) } } -task tags(dependsOn: etags) +val tags by tasks.registering { + dependsOn(etags) +} -task countHacks(type: Exec) { - commandLine "bash", "-c", "grep -r 'InferenceMain.isHackMode(' src/ | wc -l" +val countHacks by tasks.registering(Exec::class) { + commandLine("bash", "-c", "grep -r 'InferenceMain.isHackMode(' src/ | wc -l") } diff --git a/settings.gradle b/settings.gradle index 8b876e86..0b30ce80 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,7 +1,7 @@ -includeBuild ('../annotation-tools/annotation-file-utilities') { - if (!file('../annotation-tools/annotation-file-utilities').exists()) { +includeBuild ("../annotation-tools/annotation-file-utilities") { + if (!file("../annotation-tools/annotation-file-utilities").exists()) { exec { - executable './.ci-build-without-test.sh' + executable("./.ci-build-without-test.sh") } } }