buildscript { repositories { mavenLocal() jcenter() mavenCentral() } dependencies { classpath 'org.junit.platform:junit-platform-gradle-plugin:1.0.0-M2' } } plugins { id 'com.github.johnrengelman.shadow' version '1.2.3' id 'me.champeau.gradle.jmh' version '0.3.1' } import org.gradle.internal.jvm.Jvm import org.apache.tools.ant.util.TeeOutputStream boolean debug = false class Tags { Boolean hasMainMethod = false Boolean compileTimeError = false Boolean throwsException = false Boolean errorOutputExpected = false Boolean validateByHand = false Boolean ignoreOutput = false // This tag isn't used in the build... String fileRoot String mainClass String javaCmd = null List args = [] List jVMArgs = [] String javap = null String runFirst = null String outputLine = null private String block def Tags(File file) { block = file.text hasMainMethod = block.contains('main(String[] args)') def firstLine = block.substring(0, block.indexOf("\n")) fileRoot = (firstLine.split("/")[-1] - ".java").trim() // Remove \r if it exists mainClass = fileRoot javaCmd = extract('java') if(javaCmd) { def pieces = javaCmd.split() mainClass = pieces[0] if(pieces.size() > 1) for(p in pieces[1..-1]) if(p.startsWith("-")) jVMArgs << p else args << p } compileTimeError = hasTag('CompileTimeError') throwsException = hasTag('ThrowsException') errorOutputExpected = hasTag('ErrorOutputExpected') validateByHand = hasTag('ValidateByHand') ignoreOutput = hasTag('IgnoreOutput') javap = extract('javap') // Includes only arguments to command runFirst = extract('RunFirst:') outputLine = extractOutputLine() } private def hasTag(String marker) { return block.contains("// {" + marker + "}") } def extractOutputLine() { def matcher = (block =~ /(?m)^(\/\* Output:.*)$/) if (matcher) { return matcher[0][1] } else { return null } } private def extract(String marker) { // Assume some whitespace is after marker if(!block.contains("// {${marker} ")) return null def matcher = (block =~ /\/\/ \{${marker}\s+([^}]+)/) if (matcher) { def matched = matcher[0][1].trim() return matched.replaceAll("\n?//", "") } else { println "Searching for: " + matcher println block System.exit(1) } } public boolean hasTags() { return compileTimeError || throwsException || errorOutputExpected || validateByHand || ignoreOutput || javaCmd || args || jVMArgs || javap || runFirst } public String toString() { String result = "" block.eachLine{ ln -> if(ln.startsWith("//") || ln.startsWith("package ")) result += ln + "\n" } """ hasMainMethod compileTimeError throwsException errorOutputExpected validateByHand ignoreOutput fileRoot mainClass javaCmd args jVMArgs javap runFirst """.split().each { str -> if(this[str]) result += str + ": " + this[str] + "\n" } result } } ext { junitJupiterVersion = '5.0.0-M2' } subprojects { apply plugin: 'com.github.johnrengelman.shadow' apply plugin: 'me.champeau.gradle.jmh' apply plugin: 'java' apply plugin: 'org.junit.platform.gradle.plugin' //apply plugin: 'checkstyle' //apply plugin: 'findbugs' sourceCompatibility = '1.8' targetCompatibility = '1.8' repositories { mavenLocal() jcenter() mavenCentral() } dependencies { // Logging: compile group: 'org.slf4j', name: 'slf4j-api', version: '1.7.+' compile group: 'ch.qos.logback', name: 'logback-classic', version: '1.+' // You can also use the JDK's built-in logging as the back end: // compile group: 'org.slf4j', name: 'slf4j-jdk14', version: '1.7.5' // JUnit testing: compile "org.junit.jupiter:junit-jupiter-api:${junitJupiterVersion}" testCompile "org.junit.jupiter:junit-jupiter-api:${junitJupiterVersion}" testRuntime "org.junit.jupiter:junit-jupiter-engine:${junitJupiterVersion}" } junitPlatform { includeClassNamePattern '.*' } // See: http://blog.jessitron.com/2012/07/using-checkstyle-in-gradle.html // https://github.com/checkstyle/checkstyle/blob/master/src/main/resources/google_checks.xml // http://checkstyle.sourceforge.net/reports/google-java-style.html /* checkstyle { // configFile = new File(rootDir, "checkstyle.xml") toolVersion = '6.7' }*/ /* findbugsMain { reports { xml.enabled = false html.enabled = true } ignoreFailures = true } findbugsJmh { reports { xml.enabled = false html.enabled = true } ignoreFailures = true } */ sourceSets { main { java { srcDir projectDir exclude "tests/**" } resources { srcDir projectDir include '*.xml' } } jmh { java { srcDir projectDir } } test { java { srcDir new File(projectDir, "tests") } } } jmh { jmhVersion = '1.13' duplicateClassesStrategy = 'warn' } List createdTasks = [] projectDir.eachFileRecurse { file -> if (file.name.endsWith('.java')) { Tags tags = new Tags(file) if(debug && tags.hasTags()) println tags // Exclude java sources that will not compile if (tags.compileTimeError) { sourceSets.main.java.excludes.add(file.name) } else { JavaExec javaTask = null // Add tasks for java sources with main methods if (tags.hasMainMethod || tags.javaCmd) { javaTask = tasks.create(name: tags.fileRoot, type: JavaExec, dependsOn: tags.runFirst) { main = tags.mainClass classpath = sourceSets.main.runtimeClasspath args = tags.args jvmArgs = tags.jVMArgs } } else if (tags.javap) { // Create task for running javap javaTask = tasks.create(name: "${tags.fileRoot}", type: JavaExec, dependsOn: tags.runFirst) { main = "com.sun.tools.javap.Main" classpath = sourceSets.main.runtimeClasspath + files(Jvm.current().toolsJar) // Assuming javap represents all the args and there's no need to jVMArgs args tags.javap.split() } } if (javaTask) { def baseName = file.name.substring(0, file.name.lastIndexOf('.')) File outFile = new File(file.parentFile, baseName + '.out') File errFile = new File(file.parentFile, baseName + '.err') javaTask.configure { ignoreExitValue = tags.validateByHand || tags.throwsException doFirst { if(outFile.exists()) outFile.delete() if(tags.outputLine) outFile << tags.outputLine + "\n" standardOutput = new TeeOutputStream(new FileOutputStream(outFile, true), System.out) errorOutput = new TeeOutputStream(new FileOutputStream(errFile), System.err) } doLast { if(outFile.size() == 0) outFile.delete() else if(!outFile.text.contains("/* Output:")) outFile.delete() if(errFile.size() == 0) errFile.delete() } } if (!tags.validateByHand) { // Only add tasks that we know we can run successfully to the task list createdTasks.add(javaTask) } } } } } task run(dependsOn: createdTasks) /* Store test output in $projectName/tests JUnit 5's junitPlatformTest runs as a "javaExec" rather than a "test", so we can't hook into the before/after test behavior. */ tasks.findByPath(":$name:junitPlatformTest").configure { File testDir = new File(project.name + "/tests") if(testDir.exists()) { File outFile = new File(testDir, 'report.txt') doFirst { standardOutput = new TeeOutputStream(new FileOutputStream(outFile, true), System.out) } doLast { if(outFile.size() == 0) outFile.delete() else if(outFile.text.contains("0 tests found")) outFile.delete() } } } } project(':validating') { jmh { include = 'validating.jmh.*' } } project(':understandingcollections') { dependencies { compile project(':typeinfo') compile project(':collections') } jmh { include = 'understandingcollections.jmh.*' } } project(':threads') { dependencies { compile project(':enums') } } project(':strings') { dependencies { compile project(':generics') } } project(':serialization') { configurations.all { resolutionStrategy { force 'xml-apis:xml-apis:1.0.b2' } } dependencies { compile 'com.io7m.xom:xom:1.2.10' } } project(':interfaces') { dependencies { compile project(':polymorphism') } } project(':hiding') { dependencies { compile project(':com') } } project(':generics') { dependencies { compile project(':typeinfo') } } project(':collections') { dependencies { compile project(':typeinfo') } } configure(subprojects - project(':onjava')) { dependencies { compile project(':onjava') compile group: 'com.google.guava', name: 'guava', version: '19.0' compile "org.openjdk.jmh:jmh-core:${jmh.jmhVersion}" compile 'org.junit.platform:junit-platform-gradle-plugin:1.0.0-M2' } } task verify(type:Exec) { description 'Uses Python tool to verify example output' commandLine 'python', '_verify_output.py' doFirst { println("execute 'gradlew run' first") } }