This is probably more a Gradle question than a Caliper question, but I am still rather new to Gradle. I am interested in providing a task in my build that can run some benchmarks using Caliper. I have already added Caliper to my testCompile dependencies, and that works and pulls everything down. I would like to know how to provide a task that will actually run the benchmarks.
Btw, I already know about caliper-ci. I do have a Jenkins build, but it's on a cloud service that doesn't yet allow me to configure usage of caliper-ci, and besides, I want to be able to run locally before committing changes to the cloud.
It turns out it was a simple case of using JavaExec (I was new to that anyway):
task runBenchmark(type: JavaExec, dependsOn: test) {
def vmVer = System.getProperty('java.version')
def osName = System.getProperty('os.name').replaceAll('\\s','')
def osArch = System.getProperty('os.arch')
def fnameBase = "ver${version}_${osName}-${osArch}_jvm${vmVer}"
def benchMarksDir = "${project.buildDir}/benchmarks"
ant.mkdir(dir: benchMarksDir)
def outStream = new FileOutputStream("${benchMarksDir}/${fnameBase}-out.txt")
standardOutput = outStream
main = 'org.funcito.benchmarks.MyBenchmark'
classpath = sourceSets.test.runtimeClasspath
args = ['--saveResults', "${benchMarksDir}/${fnameBase}.json", '-Jmode=-server,-client']
}
Related
I have a separate task to run the test that generates open-api.yml specification for my application. Here is the Gradle configuration below:
task generateOpenApiYml(type: Test) {
group = 'verification'
useJUnitPlatform {
includeTags 'openApi'
}
testLogging {
showExceptions true
showStandardStreams = false
showCauses true
showStackTraces true
exceptionFormat "full"
events("skipped", "failed", "passed")
}
}
So, I have one test with openApi JUnit tag. It works very well, but there is a slight thing I want to approve.
The whole idea of this test is that the result open-api.yml file is used to generate Java client to invoke REST endpoints. This client is used in other tests in the project.
I want to put those generated Java classes to .gitgnore because they are generated anyway and there is no need to index those files. The problem is that the generateOpenApiYml task compiles all the tests in src/test/java directory. Therefore, some of them use generated classes. Which leads to compiling errors.
Is it possible to tune the generateOpenApiYml task to make it compile and run only the single test with openApi JUnit tag? Then I could easily put generated Java classes to .gitignore and don't bother about their temporary absence, because other tests won't be compiled.
I figured out the solution. Firstly, I installed the gradle-testsets-plugin.
Then I configured a separate source set like this:
testSets {
generateOpenApiYml
}
So, generateOpenApiYml is the new Gradle task that looks for sources in src/generatedOpenApiYml/java directory.
Afterwards, we need to tune all tasks of test type to bind them with JUnit 5 platform.
tasks.withType(Test) {
group = 'verification'
useJUnitPlatform()
testLogging {
showExceptions true
showStandardStreams = false
showCauses true
showStackTraces true
exceptionFormat "full"
events("skipped", "failed", "passed")
}
}
generateOpenApiYml.outputs.upToDateWhen { false }
I put the upToDateWhen option for convenience to make sure the generateOpenApiYml task is always run on demand and never cached.
Then I have the open-api-generator.
openApiGenerate {
inputSpec = "$buildDir/classes/java/generateOpenApiYml/open-api.json".toString()
outputDir = "$buildDir/generated/openapi".toString()
apiPackage = "..."
invokerPackage = "..."
modelPackage = "..."
configOptions = [
dateLibrary : "java8",
openApiNullable: "false",
]
generatorName = 'java'
groupId = "..."
globalProperties = [
modelDocs: "false"
]
additionalProperties = [
hideGenerationTimestamp: true
]
}
tasks.named('openApiGenerate') {
dependsOn generateOpenApiYml
}
Finally, I just need to run two commands to build and run tests for my whole project.
./gradlew openApiGenerate
./gradlew build
The first one creates the open-api.yml file and generates Java client according to the provided specification. The second one runs tests and build the project normally. The tests running during the build phase uses classes generated by openApiGenerate task. Therefore, I can put them to .gitignore safely.
Hope this will be helpful.
I am new to Gradle and trying to migrate an existing system build from ant to Gradle.
As part of this I need to run a java program on every file in a directory. Directory contains xml files and the java code will parse and convert .xml to .java files (and these Java files would be build to generate class and package in final jar) after performing some business specific transformation.
below is a function I wrote in Gradle
private runJavaFile(String dirPath) {
FileTree tree = fileTree(dir: dirPath, include: '**/*.xml')
tree.each {
def xmlfile = it.path
def javaFile = it.path.replaceFirst(".xml", ".java")
javaexec { //// getting error on this line
classpath configurations.all
main = 'XmlToJavaParser'
args = ["$xmlfile", "$javaFile", 'Java']
}
}
}
I am calling this function from a Gradle task by passing the dir path which contains the xml files to be parsed.
While running the task, I am getting below error:
> Resolving configuration 'apiElements' directly is not allowed
Any help would be appreciated.
Let me know if any more information is needed.
In Gradle, a configuration represents a group of artifacts and their dependencies. You typically have several configurations depending on what you want to do. For instance, you could have one where you declare which dependencies are needed for compilation, which are only needed at runtime, or which are needed for running a particular Java application.
In your case, you are saying that the classpath to the XmlToJavaParser class is "all configurations combined" and that doesn't really make sense. You are also not allowed to do that as some configurations from the Java plugin are not resolvable like this, which is why you get an error.
So to fix it, you should declare your own configuration for XmlToJavaParser. You can then declare dependencies for it like you normally do. Example (using the Groovy DSL):
configurations {
xmlJavaParser {
canBeResolved = true
canBeConsumed = false
}
}
dependencies {
xmlJavaParser "org.example:xml-java-parser:1.0" // or whatever you need
}
private runJavaFile(String dirPath) {
// ...
javaexec {
classpath = configurations.xmlJavaParser // The configuration is referenced here
main = 'XmlToJavaParser'
args = ["$xmlfile", "$javaFile", 'Java']
}
}
There are also other ways to go about it. But the main point is to not use configurations.all as a classpath.
Is it possible to perform some high-level operation at Unit testing end?
Like grabbing all test results and logs and sending them by email to developer?
Would like solution either in JUnit or with Gradle.
A pure JUnit approach could be to implement your own RunListener to be able to grab the information of each of your tests.
To invoke your listener, you need to run your tests through JUnitCore.
public void main(String... args) {
JUnitCore core= new JUnitCore();
core.addListener(new MyRunListener());
core.run(MyTestClass.class);
}
A pure Gradle approach could be to implement your own TestListener to be able to grab the information of each of your tests and send the final report.
In build.gradle you will need to add gradle.addListener(new my.package.MyTestListener()). Your listener must be in the build source directory also known as buildSrc.
test.finalizedBy(someHighLevelOperationTask)
Example: Here is a task that you can call gradle emailTestResults that would depend on 'zipTestResults' which depends on test. I have not done the email part of it, there's a question that covers that: Sending email using Gradle
So this just spits out a println in place of the email task:
apply plugin: 'java'
task zipTestResults(dependsOn: 'test', type: Zip){
from 'build/reports/tests'
baseName 'TestReport'
destinationDir file('build/reports')
}
task emailTestResults(dependsOn: 'zipTestResults') << {
println 'Emailing...' + file('build/reports/TestReport.zip')
}
repositories{
jcenter()
}
dependencies{
testCompile 'junit:junit:4.12'
}
But that won't work if the test fails, so, there maybe a better way, but you can add this:
test.ignoreFailures = true
Then if you then always want to email the test results add:
test.finalizedBy(emailTestResults)
Then a gradle test would finish by emailing the results.
We are developing a Java project that is able to instrument (change) class files at build time. We defined a Gradle task that invokes a java based Ant task which takes an inputDir (e.g. build/classes) and an outputDir (e.g. build/classes-instrumented) and possible other parameters. The task gets invoked separately for main and test class files after compilation. Since the "normal" java sourceSet is not a good fit, our first thought was to implement our own sourceSet but couldn't find an easy way. A reasonable alternative, similar to ANTLR etc, seemed to be extra variables. Since I needed several, I went for a Map.
sourceSets.all { ext.instrumentation = [:] }
sourceSets.all {
instrumentation.inputDir = null
instrumentation.outputDir = null
instrumentation.classPath = null
}
def postfix = '-instrumented'
Below you see how we initialize the variables.
sourceSets {
main {
instrumentation.inputDir = sourceSets.main.output.classesDir
instrumentation.outputDir = instrumentation.inputDir + postfix
instrumentation.classPath = sourceSets.main.output + configurations.compile
}
test {
instrumentation.inputDir = sourceSets.test.output.classesDir
instrumentation.outputDir = instrumentation.inputDir + postfix
}
}
However it fails with "Could not find method main() for arguments [build_f2cvmoa3v4hnjefifhpuk6ira$_run_closure5_closure23#12a14b74] on root
project 'Continuations'."
We are using Gradle 2.1
I have the following questions:
any idea why the first one fails?
Is the extra variable a reasonable solution to approach the problem?
Thanks a lot for your help
solution: install last version.
I had the same problem, I read gradle documentation of gradle 3, but gradle 2.7 was installed.
checked gradle version 2.7
then read gradle 2.7 doc https://docs.gradle.org/2.7/userguide/tutorial_java_projects.html#N103CD , but found no info about sourceSet in java plugin for that version
installed gradle 3 --> problem solved
I would like to use the Gradle "application" plugin to create startScripts for a second mainClass. Is this possible? Even if the application plugin doesn't have this functionality built in, is it possible to leverage the startScripts task to create a second pair of scripts for a different mainClass?
Add something like this to your root build.gradle:
// Creates scripts for entry points
// Subproject must apply application plugin to be able to call this method.
def createScript(project, mainClass, name) {
project.tasks.create(name: name, type: CreateStartScripts) {
outputDir = new File(project.buildDir, 'scripts')
mainClassName = mainClass
applicationName = name
classpath = project.tasks[JavaPlugin.JAR_TASK_NAME].outputs.files + project.configurations.runtimeClasspath
}
project.tasks[name].dependsOn(project.jar)
project.applicationDistribution.with {
into("bin") {
from(project.tasks[name])
fileMode = 0755
}
}
}
Then call it as follows either from the root or from subprojects:
// The next two lines disable the tasks for the primary main which by default
// generates a script with a name matching the project name.
// You can leave them enabled but if so you'll need to define mainClassName
// And you'll be creating your application scripts two different ways which
// could lead to confusion
startScripts.enabled = false
run.enabled = false
// Call this for each Main class you want to expose with an app script
createScript(project, 'com.foo.MyDriver', 'driver')
I combined parts of both of these answers to arrive at the relatively simple solution:
task otherStartScripts(type: CreateStartScripts) {
description "Creates OS specific scripts to call the 'other' entry point"
classpath = startScripts.classpath
outputDir = startScripts.outputDir
mainClassName = 'some.package.app.Other'
applicationName = 'other'
}
distZip {
baseName = archivesBaseName
classifier = 'app'
//include our extra start script
//this is a bit weird, I'm open to suggestions on how to do this better
into("${baseName}-${version}-${classifier}/bin") {
from otherStartScripts
fileMode = 0755
}
}
startScripts is created when the application plugin is applied.
You can create multiple tasks of type CreateStartScripts and in each task you configure a different mainClassName. for convenience, you can do this in a loop.