For the 'application' plugin how to add a second run task which targets another mainClass

For a Java project I have set up Gradle 8.3 using the 'application' plugin.

In the build file I set

application {
    mainClass = 'com.mycompany.myproject.App1'
}

so that gradle run runs the main function in class App1.

In the same project I have a class

 com.mycompany.myproject.App2 

which I would like to run with another task, say runApp2. How do I “clone” the standard run task and then change for the cloned task the mainClass property, so that gradle runApp2 runs the main function in class App2?

Thanks heaps for any pointers!

What have I tried so far?

  • Inspired by “Run main method using gradle ‘run’ task” I have added

    task runApp2(type: JavaExec) {
      mainClass = 'com.mycompany.myproject.App2'
    }
    

    but with that I get a “Could not find or load main class” error. – It looks like the new task runApp2 does not inherited any info of the standard run task (which knows through the plugin where the sources, etc are.)


PS: For more details on build file and directory structrture see “How to define multiple run commands for different mainClasses in Gradle’s build file?”.

There is no way to “copy” or “clone” a task, other than copying or wiring its properties one-by-one.
If they are lazy properties, this might work, if they are not yet lazy properties, not so good.
But usually you just configure a new task as you need it.
So in your case something like:

tasks.register("runApp2", JavaExec) {
    group = ApplicationPlugin.APPLICATION_GROUP
    classpath = sourceSets.main.runtimeClasspath
    mainClass = 'com.mycompany.myproject.App2'
}
2 Likes

Cool. Thanks. Will try it out next week when I’m back at my Java machine!

1 Like

Thanks again, @Vampire. It works indeed. :slight_smile:

However I don’t understand how the task knows it is a run task which compiles the code before running it. – When I read up on JavaExec, I saw only that tasks of this type execute Java classes via the JVM, but not that they compile the source code as well.

That’s right, JavaExec runs Java classes, but does not compile them.
But you configured classpath = sourceSets.main.runtimeClasspath.
The classpath property of JavaExec is marked with @Classpath which effectively is a specialization of @InputFiles.
sourceSets.main.runtimeClasspath is a FileCollection. If that file collection is properly filled with properties that carry task-dependency information and not just with plain tasks, it knows which tasks need to run if its value is needed.

In Gradle you should almost never configure paths, but always wire task outputs to task inputs. By doing so, you get the necessary implicit task dependencies automatically. Like you have seen in this case where you wired a file collection that has task dependency information to an input property of a task and thus got automatically the necessary task dependency.

2 Likes

Makes total sense, @Vampire! Thanks for you detailed explanation!

I get the impression, Gradle is pretty cool in this regards. – Hard to understand for a newbie though. Will need to work through more of the doco…

1 Like