Is it possible to specify multiple main classes using gradle 'application' plugin - java

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.

Related

Gradle: jar task - override from

I need to customize my jar task so it would:
process all classes as usual;
include only particular resources and put them into custom folder inside the jar.
I've already made custom jar task, which does what I want:
task customJar(type: Jar) {
dependsOn classes
group = 'build'
// everything except resources process as usual
from sourceSets.main.output - sourceSets.main.output.resourcesDir
// and process resources to custom place
from(sourceSets.main.output.resourcesDir) {
include 'docs/**'
include 'messages*.properties'
into 'custom-folder'
}
}
But I still need to replace built-in jar with a new one.
Replacing it with create
tasks.create(name: "jar", type: Jar, overwrite: true) {
// ... custom jar spec
}
...yields
Replacing an existing task that may have already been used by other plugins is not supported
and simple configuration of the jar task doesn't work, because it is already configured in JavaPlugin:
jar {
// jar is already configured
// with `from sourceSets.main.output`
// so it will include everything
// AND create a custom folder
// does nothing
from sourceSets.main.output - sourceSets.main.output.resourcesDir
// adds processed resources into 'custom-folder'
// in addition to all resources processed by default behaviour
from(sourceSets.main.output.resourcesDir) {
include 'docs/**'
include 'messages*.properties'
into 'custom-folder'
}
}
So, what I need is to rewrite (override) default from configuration of jar. Is it possible?
you were there almost ..
tasks.create(name: "myJar", type: Jar) {
// ... custom jar spec
}
jar.enabled = false //if you want to disable the default
build.dependsOn myJar //ensure this always runs

gradle javaexec error "'apiElements' directly is not allowed"- Gradle 5.4.1

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.

How to use BuildConfig.DEBUG as task variable in build.gradle on Android Studio 4.0?

I have a custom task that required to check if the current build is debug or release, and then use the corresponding classpath.
The normal task definition:
task custom_java_task(type: JavaExec) {
classpath "build/intermediates/javac/debug/classes/"
main = "com.testapp.JavaTaskTest"
args "test", "${projectDir}"
}
The task definition needs to check BuildConfig.DEBUG and set the different value for classpath:
task custom_java_task(type: JavaExec) {
classpath BuildConfig.DEBUG ? "build/intermediates/javac/debug/classes/" : "build/intermediates/javac/release/classes/"
main = "com.testapp.JavaTaskTest"
args "test", "${projectDir}"
}
The build will fail with the following error:
Could not get unknown property 'BuildConfig' for task ':app:custom_java_task' of type org.gradle.api.tasks.JavaExec.
Thanks.
BuildConfig is generated set of properties that cannot be used from within gradle scripts. It is actually generated based on your build.gradle
But there are some ways to achieve what you want for example like this:
First in project level build.gradle under buildscript you can define the function / method and name ti however you want (eg. doStuff)
buildscript {
...
ext.doStuff = { buildType ->
// do stuff based on buildType param
}
...
}
Than in your module level build.gradle, simply do
android {
...
buildTypes {
debug {
...
doStuff("debug")
}
release {
...
doStuff("release")
}
}
...
}
Now this is just to give you a hint on the abilities of build.gradle files, you can build further from this.
Cheers

Gradle strange behavior while extending sourceSets with Map variable

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

Gradle: how can I use dynamic properties on multiple tasks to fail a build?

One of my build tasks pulls information on the current SVN branch. On builds from a tag I want to be more strict and fail the build when e.g., a link checker finds dead links for the online help files. On regular builds from branches or trunk this should not break the build.
I have the following code, where the mentioned Perl script creates a properties file:
task generateSvnInfo(type: Exec) {
outputs.files "generated/svninfo"
executable "perl"
args "..."
}
Properties buildProps = new Properties()
task svninfo() {
inputs.files generateSvnInfo.outputs.files
outputs.upToDateWhen { false }
buildProps.load(new FileInputStream(inputs.files.getSingleFile()))
}
Now my other targets depend on svninfo (and the fact that it populates buildProps).
task checkHelpLinks(type: Exec) {
dependsOn "svninfo"
executable "perl"
args "..."
}
This will always fail if it finds dead help links. As far as I understand it, ignoreExitValue is false by default. To set it to true on non-tag builds, I can add this to the checkHelpLinks task:
ignoreExitValue = true
doLast {
ignoreExitValue = buildProps.FROM_TAG == "false"
}
This works, but I have four or five of these check tasks and would like to not duplicate that code around. So I tried
tasks.grep(~ /^check.+/).each { task ->
task.ignoreExitValue = true
task.doLast {
task.ignoreExitValue = buildProps.FROM_TAG == "false"
}
}
This code does not seem to get executed. I thought that may be because I compare the Task object to a String in grep, but using
tasks.grep(it.name =~ /^check.+/).each { task ->
gets me a build script error ("Could not find property 'it' on root project 'foo'.)
How can I add my tag check to all check tasks?
Is there a better way to load the properties from a file that is created as part of the build process?
Is there a SVN plugin that would do the work for me?

Categories