Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

build in Gradle 2.3 runs more tasks than in Gradle 2.2 #2679

Closed
wilkinsona opened this issue Mar 18, 2015 · 21 comments
Closed

build in Gradle 2.3 runs more tasks than in Gradle 2.2 #2679

wilkinsona opened this issue Mar 18, 2015 · 21 comments
Assignees
Milestone

Comments

@wilkinsona
Copy link
Member

I'm not sure if this is entirely due to Gradle, and there's nothing we can do about it, or if our plugin's involved.

With Gradle 2.2:

$ ./gradlew build
:compileJava
:processResources UP-TO-DATE
:classes
:jar
:bootRepackage
:assemble
:compileTestJava UP-TO-DATE
:processTestResources UP-TO-DATE
:testClasses UP-TO-DATE
:test UP-TO-DATE
:check UP-TO-DATE
:build

With Gradle 2.3:

$ ./gradlew build
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:jar
:findMainClass
:startScripts
:distTar
:distZip
:bootRepackage
:assemble
:compileTestJava UP-TO-DATE
:processTestResources UP-TO-DATE
:testClasses UP-TO-DATE
:test UP-TO-DATE
:check UP-TO-DATE
:build

Note the additional findMainClass, startScripts, distTar, and distZip tasks that have been run with Gradle 2.3

Question on StackOverflow that brought this to my attention.

@wilkinsona
Copy link
Member Author

Removing Boot's plugin, and making no other changes, makes Gradle 2.3 behave the same as Gradle 2.2, i.e. it's our plugin that's causing the different behaviour. I wonder if this could be related to bootRepackage depending on the distribution tasks (#2622), although that doesn't appear to explain the behaviour change between Gradle 2.2 and 2.3.

@philwebb philwebb added this to the 1.3.0 milestone Mar 22, 2015
@jeffbswope
Copy link

This appears to break builds which rely on bootRepackage.enabled = false. The :startScripts task is still queued and fails for lack of a main class.

@wilkinsona
Copy link
Member Author

@philwebb I think we should tackle this in 1.2.x. Do you agree?

@philwebb philwebb modified the milestones: 1.2.3, 1.3.0 Mar 25, 2015
@philwebb
Copy link
Member

Sounds good.

@wilkinsona wilkinsona self-assigned this Mar 26, 2015
@wilkinsona
Copy link
Member Author

The change is due to a change in the application plugin's behaviour between Gradle 2.2 and 2.3.

In my comment above I stated that the behaviour in Gradle 2.3 was the same as Gradle 2.2 when you removed the Spring Boot plugin. This wasn't really a fair comparison as removing the Spring Boot plugin also removed Gradle's application plugin.

Consider this build script:

apply plugin: 'application'

It fails with Gradle 2.3:

gradle build
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:jar UP-TO-DATE
:startScripts FAILED

FAILURE: Build failed with an exception.

* What went wrong:
A problem was found with the configuration of task ':startScripts'.
> No value has been specified for property 'mainClassName'.

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.

BUILD FAILED

Total time: 3.124 secs

But works fine with Gradle 2.2.1:

gradle build
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:jar
:assemble
:compileTestJava UP-TO-DATE
:processTestResources UP-TO-DATE
:testClasses UP-TO-DATE
:test UP-TO-DATE
:check UP-TO-DATE
:build

BUILD SUCCESSFUL

Total time: 3.332 secs

I'm not sure that there's anything that we can do, particularly in 1.2.x, to address this pretty fundamental change in Gradle's behaviour

@wilkinsona
Copy link
Member Author

@jeffbswope You can make Gradle 2.3 happy by configuring mainClassName with any value. For example, this build script works fine:

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'org.springframework.boot:spring-boot-gradle-plugin:1.2.2.RELEASE'
    }
}

apply plugin: 'spring-boot'

bootRepackage {
    enabled = false
}

mainClassName = 'com.foo.Bar'

You'll end up with start scripts that don't work (as they point to a non-existent main class) but, as I said above, I'm not sure what we can do about that given the change in Gradle's behaviour.

@jeffbswope
Copy link

Thanks @wilkinsona

This is entirely too bound to the specifics of the problem but also seems to work as a workaround without creating invalid start scripts:

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'org.springframework.boot:spring-boot-gradle-plugin:1.2.2.RELEASE'
    }
}

apply plugin: 'spring-boot'

bootRepackage {
    enabled = false
}

gradle.taskGraph.whenReady { graph ->
    def badTasks = ['findMainClass', 'startScripts', 'distTar', 'distZip']
    graph.allTasks.findAll {it.name in badTasks}.each { task ->
        task.enabled = false
    }
}

The reality is that the dependency management plugin being spun off likely eliminates our need/desire to use the boot plugin with bootRepackage disabled for our libs, but have not dug into making that change yet.

@wilkinsona
Copy link
Member Author

I've posted on the Gradle forum about the regression caused by the new behaviour in Gradle 2.3. Given that we have a workaround, I'd like to wait and see what the response is before deciding what, if anything, we should do about this.

@wilkinsona wilkinsona removed this from the 1.2.3 milestone Mar 26, 2015
@wilkinsona
Copy link
Member Author

So I don't forget, I prototyped this as a solution:

project.afterEvaluate {
    if (!project.plugins.hasPlugin(ApplicationPlugin)) {
        project.plugins.apply(ApplicationPlugin)
        project.tasks.withType(CreateStartScripts) { CreateStartScripts task ->
            task.enabled = false
        }
    }
}

The idea was to switch off the problematic pieces of the application plugin unless the user has explicitly applied the application plugin. I'm not sure it's a good solution, but it did get the build working.

@stuartstevenson
Copy link

Can I ask why there is a dependency on the application plugin? It makes assumptions about how the distributions plugin works, that means that an app using the spring-boot gradle plugin has to also inherit the immutable CopySpec from the application plugin, and cannot create it's own unique distributions.

There is an outstanding pull request to make this configurable: gradle/gradle#264.

Would it be actually be better to find a way to remove the dependency to reduce coupling and hidden behaviours?

@wilkinsona
Copy link
Member Author

@stuartstevenson With hindsight, and particularly in light of the regression/behaviour change in the application plugin in Gradle 2.3, that's probably what we'd choose to do now. And it may well be what we end up doing to resolve this issue and #2622 in Spring Boot 1.3

@stuartstevenson
Copy link

thanks for the quick response @wilkinsona , is there anything I can do to help out?

@wilkinsona
Copy link
Member Author

Thanks for the offer. The first step is probably for us to reach agreement on the solution. At first glance, moving away from the application plugin seems like the right thing to do, but I'm not sure how much of its functionality we'd end up duplicating in order to continue to support the bootRun task. Some exploration in that direction would be much appreciated.

@stuartstevenson
Copy link

I've had a look through the spring-boot-gradle-plugin and seen that there are two usages of the application plugin.

  1. In RunPluginFeatures.mainClassNameFinder it loops over all tasks and when it finds CreateStartScripts in the application plugin, it adds a dependency on that task on the FindMainClassTask.
  2. In FindMainClassTask.findMainClass the code checks the SpringBootExtension for the main class name but also the application plugin ApplicationPluginConvention to try to get the main class name, and then it tries to find a task named 'run' from the application plugin to extract the main class name from that. At the end it sets the mainClassName on botht he SpringBootExtension and the runTask.

I've tried deleting the used code on my fork and the project builds. What I'm not sure about is to what extent the FindMainClassTask relies upon the configuration of the application plugin's task in order to work. The run task also has to be configured by the user as the default value for the main class name is null. It seems like the code is trying to work with the application plugin, rather than rely on it for any unique functionality that isn't provided by SpringBootExtension.

@wilkinsona
Copy link
Member Author

From Luke Daley on the Gradle forum (I'd link to it, but the thread seems to have been lost in the migration to Discourse):

If you're able to make the decision not to set the main class name, you can remove the distribution archives at this time with:

[distZip, distTar].each { task ->  configurations.archives.artifacts.removeAll { it.class.simpleName == "ArchivePublishArtifact" && it.archiveTask == task }
  task.enabled = false
}

Unfortunately, ArchivePublishArtifact is internal so this is touching internals. That said, this type has leaked in other ways so it will be a long time before it moves/changes if it ever does at all.

The only alternatives I can come up with for you (that aren't conditionally applying the application plugin in the first place) are a bit too loose and risk messing with the user's adhoc configuration.

@DanailMinchev
Copy link

I have the same issue and was wondering if this issue will be resolved soon, any news?

I've started new Spring Cloud + Spring Boot project until hit this issue and now I'm considering to implement it with Spring Framework only.
It is not good idea to downgrade Gradle from 2.5 to 2.2 and the proposed fix is not working, because we use subprojects with ApplicationPlugin enabled.

Any plans to make this issue a major priority?

Thank you!

@wilkinsona
Copy link
Member Author

@DanailMinchev Have you tried the suggestion from Luke Daley?

Also, if the solution that I proposed does not work, then your problem is with Gradle, not Spring Boot as the application plugin is now integrated with the distribution plugin. You're applying the ApplicationPlugin directly so you're getting Gradle's standard behaviour for 2.3 and later.

@wilkinsona wilkinsona added this to the 1.3.0.RC1 milestone Jul 22, 2015
@wilkinsona wilkinsona reopened this Jul 22, 2015
wilkinsona added a commit that referenced this issue Jul 22, 2015
8673250 updated the plugin so that the application plugin is no longer
applied by default. This exposed three problems:

 1. bootRepackage may run before findMainClass has run, leaving it with
    an unknown main class.
 2. findMainClass may run before the classes have been built, making it
    unable to find the main class by examining the class files
 3. The project's mainClassName property was still being used as a
    convention for the bootRun task's main property. If the application
    plugin has not be applied, then this property does not exist.

The first problem has been addressed by configuring bootRepackage to
depend on findMainClass.

The second problem has been addressed by configuring the main source
set's output as an input of findMainClass, and configuring findMainClass
to depend on the tasks that build the output.

The third problem has been addressed by only using the mainClassName
property if it exists and its value is not null. We then fallback to
using the mainClassName property on the project's extra properties in
the same way. 

See gh-2679
@DanailMinchev
Copy link

@wilkinsona thank you for the fix and the information!

@btiernay
Copy link

btiernay commented Sep 9, 2015

With these new changes, it would be great to get some guidance on how to use bootRepackage in conjunction with distTar since I believe creating a distribution tarball with a repackaged jar with wrapper scripts is a common use case. If you agree, perhaps we can open another ticket? Much thanks!

@wilkinsona
Copy link
Member Author

That's really orthogonal to this issue. While we've changed the dependencies of the bootRepackage task, I don't think there's any difference in what distTar will generate between 1.2 and 1.3.

I'm somewhat reluctant to try and cover this in Boot's reference guide as Gradle's distribution plugin is still incubating so we'd be documenting a moving target.

That said, feel free to open a new issue. If someone figures out the configuration that's required (I think it'll involve configuring the contents CopySpec and, possibly, customising the startup scripts), and it seems stable across a number of version of Gradle, then we could consider adding it to the docs.

@btiernay
Copy link

btiernay commented Sep 9, 2015

Perhaps a StackOverflow question is best. The reason I mention it here is that I was hit with this issue upgrading from Gradle 2.2 to 2.5 today. Between M5 of 1.3.0 and Gradle 2.5, I'm not really sure what the recommended course is for my setup. Thanks for your comment!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants