Is there something like the effective pom (Maven) in Gradle? - gradle

I've started using Gradle several months ago and I sometimes bump into a problem with my build.gradle files. For example if I add something like this to my file:
apply plugin: 'kotlin'
I can't tell what that will expand to. In my case I figured out that it pulls in the java plugin as well and the java plugin itself sets up some configuration. How do I know what these statements will expand to? Do Gradle has something like an effective build.gradle ?
Clarification: what I really wish to know is what each apply plugin X statement does behind the scenes without looking up documentations etc.

Since Gradle 2.10 you could try using gradle buildEnvironment. Also see this answer, this blog article, or the official docs.

Try gradle dependencies. The output is similar to Maven's mvn dependency:tree.
See docs here: https://docs.gradle.org/current/userguide/inspecting_dependencies.html#example_rendering_the_dependency_report_for_a_custom_configuration

You could probably list the plugins with a custom task
task pluginReport {
doLast {
allprojects.each { Project p ->
PluginContainer plugins = p.plugins
println "Project: ${p.name} has ${plugins.size()} plugins
plugins.each { Plugin plugin ->
println " - ${plugin.class.name}"
}
}
}
}

Gradle plugins, while they can exist in the form of gradle scripts, a great many of them are binary plugins: java code that happens to be executed by the jvm at build time. This means that "build.gradle" is the effective build.gradle, unless you really want to go looking at bytecode/plugin source code.
The best way to know what a plugin is bringing into your project is to read the documentation, or if insufficient documentation exists, try to reach out to the Kotlin devs/a Kotlin specific support forum.
Edit in response to clarification: No, there is no way know what a plugin is doing without looking at documentation; plugins are frequently Groovy/Java programs that run with your build. It'd be like asking your computer to surmise what "xyz.exe" does without googling or running it.
Choosing gradle plugins is a very big and deliberate choice for your build procedure, and it needs to be done carefully and with consideration for what functionality they bring and what settings to use for each plugin to ensure your build delivers to results you need. Even if we had SciFi computers that could magically say what a random binary does in plain English, you'd be better served by doing your legwork, reading the docs, and figuring out what your plugins do and how to actually use your plugins effectively anyway.

Related

Use of plugins in gradle

I have a question on the usage of plugins in gradle in general. What features do they provide?
For example, why should I use an Idea plugin if my IDE is intelliJ. What if I don't use that will there be any difference.
If you want to know about Gradle plugins in general, you should read the relevant part of the user guide. It does a good job of explaining the concept.
As for the IntelliJ plugin in particular: no you should generally not add it. IntelliJ can import Gradle projects just fine without it. The only time it makes sense to add it is if you need to customize the import process in a way that differs from the default. But even that is often better done through adding the specific IntelliJ configuration files to your VCS repository. If you like to know more about best practices for this, the Gradle forums are a better match for discussing the different approaches.

Is there a way to define property to be used in both settings.gradle.kts and projects/subprojects build.gradle.kts with gradle 6.0?

We have multi-module android app with build logic written in gradle kotlin dsl. We use buildSrc to extract common logic like dependencies versions. We have something like:
buildSrc/src/main/kotlin/Dependencies.kt:
object Versions {
const val fooVersion = "1.2.3"
const val barVersion = "4.5.6"
}
object Libraries {
val foo = "com.example.foo:foo:$fooVersion"
val bar = "com.example.bar:bar:$barVersion"
}
object Modules {
const val app = ":app"
const val base = ":base"
const val baz = ":baz"
}
Then we can use these in modules' dependencies block to avoid hardcoded/duplicated values:
app/build.gradle.kts:
dependencies {
implementation(Libs.foo)
implementation(Libs.bar)
implementation(project(Modules.base))
implementation(project(Modules.baz))
}
And we also use it in settings.gradle.kts:
settings.gradle.kts:
include(
Modules.app,
Modules.base,
Modules.baz
)
This works ok with gradle 5.6. When I upgrade to 6.0, I get Unresolved reference: Modules in settings.gradle.kts file. I found it mentioned in migration guide:
Previously, the buildSrc project was built before applying the project’s settings script and its classes were visible within the script. Now, buildSrc is built after the settings script and its classes are not visible to it. The buildSrc classes remain visible to project build scripts and script plugins.
Custom logic can be used from a settings script by declaring external dependencies.
So I know what broke the build and I can fix the build by using hardcoded values in settings.gradle.kts:
include(
":app",
":base",
":baz"
)
Is it possible to avoid this duplication with gradle 6.0?
Please make sure you read the updates down below.
Original answer
See ticket #11090 "Definitions from buildSrc/ not found in settings.gradle.kts using gradle 6.0-rc-1". As you already noticed this changed recently:
This has changed in 6.0, and was deprecated in 5.6. Please see: https://docs.gradle.org/current/userguide/upgrading_version_5.html#buildsrc_usage_in_gradle_settings
-- https://github.com/gradle/gradle/issues/11090#issuecomment-544473179
One of the maintainers describes the reasons behind the decision:
Unfortunately, there are pros and cons to both arrangements (settings-then-buildSrc and buildSrc-then-settings), and we opted for the former after considering.
(...)
The pros that compelled us to make the change:
Settings plugins can influence buildSrc and main build (i.e. apply a build plugin to both)
Build cache configuration is applied to buildSrc
buildSrc behaves more like a regular included build
-- https://github.com/gradle/gradle/issues/11090#issuecomment-545697268
And finally some bad news:
We won't be changing the behaviour back to the pre Gradle 6 arrangement. Please let us know if you would like more detail on how to use one of the alternative mechanisms for using complex logic in a settings script.
-- https://github.com/gradle/gradle/issues/11090#issuecomment-545697268
Workarounds
In the aforementioned post the author proposes some workarounds:
The con of this is exactly what you have hit. It's now less convenient to use complex logic in your settings script. Now, you have to either:
Inline the logic into the settings file
Move the logic to a shared script that can be used where it needs to
Move the logic to a pre-built binary that you load in the settings file (i.e. a settings plugin)
-- https://github.com/gradle/gradle/issues/11090#issuecomment-545697268
#1 is pretty straightforward, but I can only assume what #2 and #3 mean. I come from the Groovy world and only recently started making friends with Kotlin DSL. Having said that let's give it a try.
In #3 the author might be talking about developing an external plugin and applying it in both scripts. I'm not really sure if this is something that would make sense to implement (it gives you strong typing though).
"#2 Move the logic to a shared script that can be used where it needs to"
I think it's about having a common script plugin and including it in both settings.gradle and build.gradle files. The plugin would put the static information in the ExtraPropertiesExtension of the ExtensionAware in scope (Settings in case of a settings.gradle script plugin and Project in case of build.gradle). This is described in this answer to "Include scripts with Gradle Kotlin DSL":
How can I put all common constants (such as dependency versions) to the separate file to include them just by using something like springBootVersion or Constants.springBootVersion with compile-time checks?
There is no good way to do it currently. You can use extra properties, but it won't guarantee compile time checks. Something like that:
// $rootDir/dependencies.gradle.kts
// this will try to take configuration from existing ones
val compile by configurations
val api by configurations
dependencies {
compile("commons-io:commons-io:1.2.3")
api("some.dep")
}
// This will put your version into extra extension
extra["springBootVersion"] = "1.2.3"
And you can use it like this:
// $rootDir/build.gradle.kts
subprojects {
apply {
plugin<JavaLibraryPlugin>()
from("$rootDir/dependencies.gradle.kts")
}
And in your module:
// $rootDir/module/build.gradle.kts
// This will take existing dependency from extra
val springBootVersion: String by extra
dependencies {
compile("org.spring:boot:$springBootVersion")
}
-- Include scripts with Gradle Kotlin DSL
UPDATE 1
The issue has become popular and gained attraction from Gradle maintainers:
Since there are so many comments still on this issue, let me clarify a few things about where we are and where we are actively moving to right now.
For the general topic "I want buildSrc to be done before anything else" there will be a solution soon with included builds and setting plugins. Most likely in the next release. Then you will be able to include a build, using a new DSL method, that is available earlier. This build can then contain a settings plugin which you can apply in settings. This makes the Jar containing the plugin available on the settings classpath. (Although ideally you would define a plugin extension and not use classes/static methods directly.)
settings.gradle.kts
pluginManagement {
includeBuildEarly("my-build-logic") //<- WIP - new API under development
}
plugins {
apply("my.settings.plugin) // this is defined in the "my-build-logic" build
}
For the topic of sharing dependencies and versions, we are also working on general improvements that might make some "custom solutions" unnecessary in the future.
Generally, you should try to avoid using buildscript {} or using resolutionStrategy {} in settings. Instead you can define plugins in included builds. You can then manage all dependencies (also to plugins) in the build files of these builds.
If you want to share constants between all these builds, you can have one build only for these constants that you include in all others. Like the libraries build in this example..
See also the sample about sharing convention plugins with build logic build.
Hope these are some helpful pointers.
-- https://github.com/gradle/gradle/issues/11090#issuecomment-734795353
UPDATE 2
Nowadays (Gradle 7+) you could use Version Catalogs to share dependency versions across buildSrc and the regular build. Please refer to the official documentation and this answer.
AFAIK - no, and that is what aforementioned migration guide says: settings.gradle is the first to be evaluated, thus, at that stage, objects defined in buildSrc don't even exist yet.
I can image some hacky workaround - just for the science, but in real project it would smell so bad that, IMO, not worth it.
P. S.: I guess you could reverse it and try to create instance of some enumeration by going through submodules in buildSrc code. But again, ways to achieve this might be quite exotic and should only be used for the sole purpose of proving this can work )

Gradle dependency autocompletion in IntelliJ IDEA

I've been searching around for a while and can't seem to find anything regarding this issue. I'm new to Java in general and for the last half a year I've been learning Maven. I use IntelliJ IDEA and I'm already used to the fact that it can autocomplete dependency coordinates (groupId, artifactId, version) if you sync the repository. It works nicely and it feels natural when you decide to add some dependency after the initial project generation.
Now I'm trying to switch to Gradle (which is a requirement on my new job) and I'm studying it. And I can't get over the first roadblock that I've encountered with it: there is no autocompletion and it even seems like IntelliJ IDEA's support for this kind of feature has degraded over time (I saw a video of old version of IDEA where there used to be a "Generate" -> "Add maven dependency" option, which I cannot find in the latest version). I even tried a bunch of plugins with names like "Gradle Dependency Helper" and non of them worked.
Are we supposed to type the whole dependency coordinates to add them to gradle build? Because that feels like a big downside of Gradle with IDEA. Or is there some well hidden feature that I missed?
You may manage Gradle dependencies in Intellij IDEA using View→Tool windows→Dependencies.
It allows to add or remove dependencies from dependencies section in your build.gradle and gives full-text search for available dependencies.

How to download a non-dependency artefact as part of buildScript block for Semantic Versioning

First off, I have no code to show since I'm completely stumped with this one. This is bad form for an SO question so I really do apologize - I worked entire yesterday on the related build script but couldn't get anything useful to show for this.
I am currently working on a build script that as part of the jar task (or rather its doLast {} closure) would verify the current JAR against previous published JAR from my own Artifactory using the SemVer API. Everything else that I have works except for actually downloading the previous version of the project; I can't seem to be able to devise a working script.
My approach so far was based on the reasoning that as Gradle uses Ivy as its dependency management system I should be able to just call some of Ivy's Ant tasks with the right parameters - the same as current project so I actually have access to group, name and current version easily - and then get the path to downloaded artefact file and use it as input for the aforementioned SemVer API. Being a bit of Gradle newbie and not actually have used Ivy for a few years even my struggle revealed to me that at this point I have no idea how to really do this in a clean way. One of my major hurdles with this has also so far been that Gradle's documentation is too extensive making it difficult to find things when I don't even remember the right terms for certain bits I want to have.
As I'm not providing any code/build script samples of what I have so far, I don't mind if your answers are just nudges to right direction.
Gradle has not used Ivy internally for some time, so I doubt very much if your approach of using Ivy ant tasks will work.
I would probably do this in a separate task, rather than add actions to the jar task. There is probably a more elegant way, but you could try something like:
configurations { lastPublishedVersion }
configurations.lastPublishedVersion.transitive=false
dependencies {
lastPublishedVersion group: group, name: name, version: "+"
}
task checkSemVer {
dependsOn jar
dependsOn configurations.lastPublishedVersion
doLast {
println configurations.lastPublishedVersion.getSingleFile()
}
}
PS If you get this working it would make for a very interesting gradle plugin if you wanted to release it :)

Eclipse plugin for Gradle - group libraries? (annoyance)

I've been digging into Gradle and loving it so far! I was delighted to find that apply plugin: 'eclipse' would easily generate an Eclipse project, but I'm irked by the fact that the dependency libraries show up under the root of the project in the Project Explorer view, like so:
That seems unsustainable as the list of libraries expands. Yeah, it doesn't affect the functionality, but it's sure ugly. I'd rather group them into a Libraries "folder" or something like that. Does anyone know of an easy way? I'm thinking I can use withXml to monkey with the project definition, but it seems like someone else has probably thought of this and there must be a better way.
Bonus if I can easily see the subset of libraries that get packaged in (vs e.g. testCompile or providedCompile) when using the war plugin!
If you use Gradle Plugin for Eclipse developed by Pivotal folks there is an option to use classpath container that does what you want - it replaces individual classpath entries with one (expandable).
To enable this feature Right click on the project and select 'Gradle >> Enable Dependency Management'

Resources