Build logic can grow to be complex as application logic and good tooling is key for a productive build-logic authoring and maintenance experience.
The Gradle Kotlin DSL leverages the Kotlin language and its tooling to provide just this. The static nature of Kotlin enables both compile time checking and authoring assistance from your IDE, from auto-completion to refactoring and everything in-between.
This webcast introduces:
- Creating a Gradle Kotlin DSL build from scratch
- Customizing the build with plugins, configuring their DSLs and tasks
- Using non-statically typed APIs and DSLs from a statically typed language
- Understanding patterns for organizing build logic
- Authoring plugins that take advantage of the Gradle Kotlin DSL
- Authoring DSLs that play well with both Kotlin and Groovy
If you want to take a look at the slides, you will find them here as well as the sample projects.
Thanks to everyone that attended the webinar and thanks for your questions! Learn more about upcoming webinars here.
Paul: Welcome, everyone. Welcome to this webinar on getting started with the Gradle Kotlin DSL. I’m Paul, and this is Rodrigo, and we are the Kotlin DSL team at Gradle.
We’ll get started by asking you a couple of questions to get to know the audience. The first one is, “How are you using Gradle?”. You’ll see the question displayed. And we’ll wait just a bit to get some answers.
Rodrigo: Interesting, lots of Groovy DSL users in the house.
Paul: A bit more than a half so far, and something like 10% not using Gradle yet. That’s interesting, too. And 15% already using the Kotlin DSL, and 20% mixing both languages. That’s good. Very interesting, thank you.
A similar question is, “What is your primary programming language?”. So you’ll see a few options. We only had five slots, so please, if your primary language isn’t in the options, send us the details in the question box. It looks like it’s mostly Java, as expected. Well, a bit more than half. Mm, Kotlin going up, so it’s 35% Kotlin almost, a bit of Groovy, a bit of native, and some others. Good, thank you.
Before going to the Gradle Kotlin DSL itself, we’ll look a bit about what is Gradle? And we’ll start with Gradle’s purpose. It’s a build tool and automation tool. It’s JVM based, meaning it runs on the JVM. It’s implemented in Java. And it’s open-source, licensed with the Apache license.
So we said it’s an automation tool before because it’s agnostic of the target ecosystem. Meaning, you can build for the JVM ecosystem, for many JVM languages, Java, Groovy, Kotlin, Scala. There are plugins for others, like Closure and so on. The native ecosystem is quite well-supported, too, and of course, Android, which is a large user base. But it’s not limited to that. We have community plugins, again, for Python, Go. There are some for REST and Asciidoctor, and JavaScript. And this very presentation is actually built with Gradle using Asciidoctor.
Gradle also added a commercial product named Develocity, which is all about developer productivity and it’s mostly split into two feature sets. One is build scans, which are permanent records of what happened during a build. So we can collect them and very easily share them with your colleagues to troubleshoot issues and so on. And the build cache is mostly about reusing work across your company. And it not only supports the Gradle build tool but also the Maven build tool.
We saw that Gradle is agnostic of the ecosystem, so we’ll run another question to get to know you. The question is, “What type of applications you are building?”. So again, only 5 slots, so please send us details. Looks like it is web services competing with Android applications, mostly. OK, so half of the people building web services, which make sense today. And mobile apps, mostly Android, again, it makes sense given the official support is made with Gradle. Yeah, and 10% on desktop applications, interesting. Good, thank you.
So now we’ll go more details into the Kotlin DSL, starting by creating a build using it from scratch, and then customizing it, configuring things. And then growing it, and then identifying patterns to organize the build logic, and then we’ll talk a bit about good practices toward the plugins and DSLs.
Rodrigo: Before we go into that, and before we start with the Kotlin DSL, it’s interesting to get to know a bit more about the Gradle basics. We talked about how Gradle is this automation tool that can automate the execution of work. And that mainly happens through the automation of tasks. Tasks are the main thing you’ll be dealing with or executing in your Gradle build. So you’ll be configuring tasks and registering tasks, creating tasks. That’s how a Gradle build works.
Those tasks can be configured using Groovy DSL scripts. As we saw, we have a lot of Groovy users today. But now, we also have the Kotlin DSL, which is using the Kotlin language as a base for configuring those tasks. In the end, we can see that both these DSLs are like the sugar on top of a rich Java API for automation, for automating tasks. So we have the Groovy DSL, the Kotlin DSL, built on top of this rich Java API for automation of work. And what Gradle actually does, in the end, right, besides automating all that work and tasks, is to make sure it avoids work. It does the dependency management resolution and everything else that goes with it, like downloading artifacts, et cetera. The main way we reuse and package functionality in Gradle is by packaging those tasks and their configuration as plugins. So that’s how you mainly interact with Gradle, by using plugins from either the plugin portal or from repositories or from your local disk. That’s how we use Gradle.
So this, just to give a bit of an introduction in what a Gradle build looks like, it’s a Java library, or a build script written using the Kotlin DSL for a Java library. So we can see, we apply plugins. We can configure how that plugin will work. And we can see how we can add some dependencies to control how our library will be viewed.
We can also build a native app. So it’s not only about Java, as we said, Gradle is agnostic of the platform. Here we see a native app written in C++. And then we can actually go and configure the two chains like you’re using GCC, or maybe you’re using Visual Studio on Windows, and that is how you would interact with that plugin.
Paul: All right, so let’s get into practical things. So we’ll get started by creating a Gradle Kotlin DSL enabled build from scratch. And you can do that in two ways. The first one is, if you already have Gradle locally installed on your machine, you can use this any task. It’s a task that provides an interactive workflow, that suggests a set of template builds. Here, we selected the Kotlin application build for the sake of the example. And of course, the Kotlin DSL for the build scripts, the name, and that’s it. It’s that simple.
The second way you could use to create a Kotlin DSL build without having Gradle locally installed is from a web UI with this Gradle initializer project, which is basically driving the gradle-init task. It’s exactly the same thing, but you don’t need Gradle installed. So we’ll do the same, choose the Kotlin application, choose the Kotlin DSL for scripts, set of project name and package a name for the source code. Then click Generate, and you get a zip file that contains the same thing that gradle-init creates.
This can be automated with the command line if that’s your thing. But in both cases, what we get is exactly the same, and that’s the file tree you currently see. The first thing to notice here is the Gradle script file extensions. It says .gradle.kts, where kts stands for Kotlin script. And that’s basically how you obtain to the Gradle Kotlin DSL. Like .gradle files will use the Groovy DSL, and gradle.kts file will use the Kotlin DSL. The first file we see is the setting script. The setting script controls the settings for the whole build. The most common thing you control there is the project structure. Here we have only one project. And we just set its name, and that’s it. There are other global build configurations you can do in a setting script, like configuring the build cache, configuring how build plugins are resolved, et cetera.
The second file of the build.gradle.kts is the root project. We’ll create the build script for the root project. We’ll have a quick look at it. So we see here that we apply plugins. And if we just read that, it said we apply the plugin with the Kotlin JVM ID and the version and the application plugin. We’re basically configuring Kotlin JVM application, as we selected with the Gradle init. Then you would set up a repository, where you fetch dependencies, and then declare some implementation and test implementation dependencies.
If you want to know more about this important topic, please see the Dependency Management Fundamental webinar that was done recently.
And the last block is setting up our application main entry point. Then there are a few files that are very important, like the gradlew, gradlew.bat, and the gradle folder with the wrapper file, gradle-wrapper.jar, and gradle-wrapper.properties. The wrapper is a way for a build to declare and specify the version of Gradle it uses. So it’s a way to lock the version of Gradle you used to build that build. In the script, the gradlew and gradlew.bat scripts, they will automatically download the declared distribution. It means that it makes it very easy for people to get the build up and running on their developer machine or on CI. Just fetch the sources from somewhere, run the script. It will download Gradle and run the build. There is only one requirement, which is you need to have a JVM installed to bootstrap the whole build.
And then we see an application without sources will not make sense. So we see the sources for the application and its tests. And we see it follow the standard layout. Then we get to run that build. But first, from the command line.
Rodrigo: Running the build is as easy as using the Gradle script that is provided by the init plugin. So you really want to use the Gradle script to make sure you’re using the Gradle version that you selected during the setup process. So as I said, you run tasks with Gradle. That’s your main interface with Gradle. So here we are going to run our application. So I’m telling Gradle to run my application. And this is a very simple application. So it says, hello, world. And that’s how you run it. The next thing you might want to do with the build is to actually work with it using an IDE.
Paul: Yeah, speaking of which, we’ll do another quick poll about, “What IDEs are you using?” before going further. All right, so JetBrains IDEs are the clear winner, it seems. Some Eclipse and some Apache Netbeans users, not much, but still.
Rodrigo: I guess Eclipse users will be happy to know that JetBrains is now committed to improving the Kotlin experience in Eclipse, including the Gradle Kotlin DSL.
Paul: Yes, the issue went to in progress recently.
Rodrigo: After you execute your build, you want to actually import it into an IDE. Here, I’m using IntelliJ, the free community edition, the latest stable version, 2019.1. And I basically selected the folder we just saw, that we just generated. And I’m saying import, and I’m going to open it. I want to select the Gradle model, and I’m going to run it with the Java 11 version. We can use all the default settings. And then press Finish. So then, as we can see here, we get basically the same structure. It is the same structure we just saw. We have our script here with syntax highlighting. Of course, the first time you open the script, you might have to wait just a bit as dependencies are resolved, downloaded. And the highlighting works. Yeah, that was fast.
Talking about running the build again, you have a few options here, with IntelliJ, in particular. We have our faithful terminal. We can just, again, interact with Gradle using the terminal here. Just run a different task. I’m going to run the check task that executes your test. And maybe you have code quality tasks that will inspect your source code. So that’s a way to do it. We can also use a very nice feature in IntelliJ that I like it a lot. It’s the run anything and it actually supports Gradle. So we can just say Gradle and run again, and then there we have it.
A very interesting thing is that Gradle is actually now the default runner. When you import a Gradle project and you want to say, execute a test, I’m going to go to my app test. And if I choose to run the test now, IntelliJ will actually use the Gradle test task to run that build. So again, if you have any sort of deep dependencies for your test, you have code quality, other sort of inspections, you have code generation in the mix, that will all be honored now by IntelliJ, which is pretty cool.
Paul: So now we have first build, and we want to customize it. That’s why we’re here. So we’re looking at this build, the same example called JVM application. And now let’s say we want to change where this application is run. For example, we want to limit the amount of memory given to the Java process to only 4 megabytes. That should be plenty for hello world. It’s better than a quarter of your memory, which is the default. With the JVM, we can achieve that by using the -xmx command-line argument. We should be able to tell the application plugin how to do just that.
So let’s take a closer look at this application block we have in the configuration. This application block is what we call an extension configuration block because plugins, like the application plugin that is applied here, they contribute extensions to the DSL. They extend the DSL to add more language elements to your scripts, and this one was contributed by the application plugin. So we should be able to configure it, and what we want to configure here is the argument. Let’s ask the IDE if there is something. Yes, there is something named applicationDefaultJvmArgs. That’s exactly what we want to set. And I saw it’s an interval of string. This was in the documentation showing up, right?
Rodrigo: Yes.
Paul: So a list of strings will do. And want to set “-Xmx4m”. And that should do it. So here, the IDE provided us with a lot of help. And that’s thanks to the type safety of the Kotlin language.
Rodrigo: Yeah and at this point, we actually changed the configuration. We changed the way our application is supposed to run. Then we actually have to import those changes so the IDE can actually run it in the proper way, so just go ahead and do it. At this point, you should be able to just run it, I guess.
Paul: Yeah, let’s run it and let’s check that it had an effect and that the new configuration is taken into account. And let’s run with “-i”, which means information, so we get a bit more logs. And we should get the command line. Yeah, good. Starting process, and that’s it. So it did work. We could also look at the distribution. Like what about when I distribute this application? Will these settings also be taken into account?
Rodrigo: So you have to view the application first.
Paul: Yeah, like run Gradle assemble. And now we should be able to look at the archives in distributions, like the zip, and then in bin, the script. And then here we should be able to find the same. So yeah, that’s it. We configured the application plugin, and it does the expected effect. The IDE helped us and we used this application block contributed by the application plugin.
Rodrigo: As we saw, we can run it in both ways. And another thing we might want to do with our application is to make sure, for instance, that we don’t introduce unintended warnings into the codebase. Warnings can actually be signs of bugs in the codebase. So we want to make sure we always fail the application or fail the compilation of the application when any warnings are introduced. And the Kotlin compiler is actually able to do just that. It can actually inspect your code and make sure no warnings are there. So we should be able to go and configure the Kotlin compiler.
Paul: Like we said, Gradle does work by running tasks. So there must be a compilation task.
Rodrigo: To configure tasks, the tasks are also available in the Kotlin DSL, in this task block. Here we can actually configure any of the tasks that are now available in my view. So if it’s something about compile, I should be able to then inspect such tasks. We can see there’s a few of them. There’s a compileKotlin task and a compileJava task. There is a compile test Java, and a compile test Kotlin. They are all existing tasks, as the documentation says. I’m going to do the inspection. I want the inspection of warnings, just for my production sources. And that’s the compileKotlin task. If warnings are introduced in the test, I’m not as concerned.
So I’m going to do this for this task. And again, the way we configure a task is by entering the task block, this task configuration block, just like we did for extensions. We want to change the way the compiler works, so we want to change the compiler options. We should be able to see something about options. And there we go. This is the block that lets us configure the Kotlin options. And there should be something here about warnings. Maybe yeah, let’s start with just that, warnings.
Paul: That should be what we want.
Rodrigo: So it’s just a Boolean flag. I’ll go there and just say, well, all warnings as errors. And again, since I changed the configuration of the compiler, I should go ahead and let IntelliJ know about those changes so we can execute the build in the right way. OK, so now we should be able to just run the application again, so just to make sure everything still works. Yes, it still works.
Paul: We should also check that it actually fails when we introduce a warning.
Rodrigo: We should. We definitely should, let’s do that. Let’s go to our app, and maybe just introduce a random warning here. I’m going to just duplicate the line. And as you can see, the IDE already flags that as an error. You can see, it’s unreachable code. So again, if I run in the console, I should see that fail. And we do see it fail. And it’s the same warning we saw before.
Paul: Yeah, and it says warnings found, and the warning was specified.
Rodrigo: So let’s just fix that warning, and maybe run the application again. And there we go.
Paul: So we learned a few things so far. We can change the configuration and the behavior of the build by configuring extensions and tasks so that tasks are units of work. Like we said, that’s how the Gradle model works. And you can configure a specific task directly like we did to configure the Kotlin compiler, or you can also configure extensions. Like the application, the plugin has these application extensions where we set the JVM arguments. And we saw that it influenced several tasks, like the run task and the task to create the application scripts and so on. Both are contributed by plugins. So plugins, specifically, enhance the Gradle DSL through extensions and tasks. And you use scripts in DSLs to configure that.
Going a bit further, our build was really simple, it’s just a single project. But in reality, most people don’t do so simple builds. And even like Android developers, when you start to build like if you use Android Studio and create a new build, you will already have a modularized build, because there is a subproject. So let’s take our simple application and split it into two. And we want to have a call library and then a UI, which will be the command line application. We keep our Hello, World example, but we did split it. And that involved a bit of manual work. But we’ll show you the results, and it’s very simple.
The first thing we do is use the settings file, the settings script. As we saw, it’s purpose is to control the structure of the build. And we say we want to include two projects, one named Call and one named App. It means we have two subfolders, the Call subfolder, the App subfolder. As a first step, we just duplicate it, and now we have two build scripts for the subprojects. And we basically duplicated all the configuration logic, like the repositories declaration, the dependencies, the task configuration. So it’s not ideal at all, but it works, and it’s a good first step.
Rodrigo: So we can actually see that it works, and it’s the exact same behavior as before. So I’m going to run the task in a quiet mode so we only see the output of the actual application. And it’s the same app.
Paul: So we will now go into the process of making this a bit better. Because of course, duplicated build logic, it’s like production code. You don’t want to repeat yourself. And if you need to change something, you don’t want to have to change it at all the places you duplicated things. So let’s factor all this logic. We now have a build with a project structure, where we have the root project. Can we show the root project build script? It’s empty, right? So we had this root project, and then we have two children, the app and the call. Gradle evaluates your build script, following the hierarchy. So it evaluates first the root, and then the subprojects. And it’s important to keep in mind, to know how you can articulate your build logic.
In this example, if we want to dedupe some things, it will only be about, like, pulling things up from the subproject to the root project. And we’ll start with the beginning of the script. So both the Call and the App script, they declare that they use the Kotlin JVM plugin with version 1.3.21 here. But we don’t want to duplicate that, we want to move that up to the root project. So let’s do that. Good. All right, so now we changed the script, and IntelliJ is telling us that our new script dependency is available and that’s because, when you request a plugin, it will download it. And it will make it available to the classpath of this script. And so the IDE editor context must be refreshed. So you can say, apply dependencies. And it says that the build configuration failed. Well, let’s follow his advice and run Gradle task to know what’s happening. So it says that the plugin request for the Kotlin JVM plugin here must not include the version. Of course, we moved that up into the root project. And the root project is now the one who is specifying the version. Meaning, we should remove it from the two subproject scripts.
Rodrigo: Here we move it to there, and remove it. And now we should be able to run the view.
Paul: Mhm. And let’s go back to the root base create. So now, this one is the only one having the version declared.
Rodrigo: But this project is, in fact, not a Kotlin project. So it doesn’t make sense to actually apply.
Paul: Right, apply the Kotlin JVM into it. So we should specify that we only want to declare that we will use this plugin, this version, but we won’t apply it to this old project. And that’s done using “apply_false”.
Rodrigo: We’re good to go. I guess we could, again, run the application at this point, just to see everything still works. Yeah, good.
Paul: OK, so one step at a time, we extracted, we removed the Kotlin JVM plugin version. But we have more things duplicated. Still, the plugins block is duplicated in between the two things. So the root project is now declaring what version of plugin we do. But we could also declare that all of our subprojects, they use the same plugin.
Rodrigo: Right. That sounds like a good start. So we take the ID of the plugin.
Paul: Yeah, we’ll need the ID of the plugin of course. And then we want to configure subprojects. So this is named cost configuring project. Like from one project, we configure others. And we do that following the evaluation hierarchy of scripts. What we want to do here is use the apply function, and we apply the plugin, the Kotlin JVM plugin to all subprojects.
Rodrigo: And now we could actually remove the plugins from every subproject.
Paul: Yeah, let’s remove duplication. So here, we will only remove the Kotlin JVM plugin, because we want to keep the application plugin here. Wait a second, IntelliJ keeps asking us when we make changes to the scripts, it keeps asking us about applying dependencies when we edit. We could have an auto-reload here because it’s cheap. It’s like much, much cheaper than re-importing the whole project, and we actually want the dependencies dated.
Rodrigo: So should I go and do that?
Paul: So let’s enable, yeah.
Rodrigo: OK, you’re the boss.
Paul: Did you remove the plugins from the other project yet?
Rodrigo: I did not.
Paul: No, let’s do that.
Rodrigo: So we can remove everything at this point, right?
Paul: Yeah.
Rodrigo: The plugins blocker.
Paul: Yes. Good. Of course, it needs to reload the dependencies.
Rodrigo: We could also import changes because we changed the structure. Yeah, so let’s do that.
Paul: Yeah, good. So both projects have the repositories, so let’s move it up.
Rodrigo: So go here. And then we go into app and do the same.
Paul: And the dependencies are mostly the same. The only difference between our core library and our app is, one depends on the other. But the three other dependencies, we could pull them up.
Rodrigo: Which is, basically, the dependencies block from the core module. So we can just go there and move it.
Paul: Good. So here, something is red in the IDE and it says the configuration failed. And white says that it couldn’t reference the implementation world. So here we are in a subproject block and we are cross-configuring subprojects. But the Kotlin DSL didn’t have a chance to know that those configurations actually exist and to provide type-safe accessors.
Rodrigo: Yeah, because the plugin is not really applied to the root project and this is still the root project script. So even though we are saying that we are configuring a subproject, the Kotlin DSL gets loaded in the context of the root project. So that’s the model it inspects.
Paul: Yeah. Another way to put it is that the Kotlin DSL can provide type-safe, static accessors to dynamic elements, only if your applied plugin dependencies are specified declaratively using the plugins block, or from a project above in the hierarchy, because of the evaluation order. Here, we don’t have access to those types of accessors. So what we need here, like implementation. Implementation is a configuration, a Gradle configuration, to which we want to add this dependency. And configuration can be addressed by name. And with the Kotlin DSL, the way you do that is by using a string for the name.
Rodrigo: Yeah, so that’s one way to do it.
Paul: And there’s another way, and we’ll show it with this example because we have two instances of this reference to this dynamic thing. And we don’t want to have a typo in the string. So we want to tell the Kotlin DSL to bring into scope a reference to that existing configuration. That’s the two ways to do that.
Rodrigo: Yeah, and with that now in place, we can actually import the project again, so the configuration runs. I’m not sure I actually deleted all the dependencies I should have.
Paul: I guess you didn’t delete this one here. Yes, this block should be only, like, the app, depending on call.
Rodrigo: Right, and now on the core, yeah, we moved everything. Well, let’s import one more time because I did do one change. And now we should be able to, again, run the application, and the effect should be exactly the same. Let’s run the application.
Paul: Yes. And there is still one more duplication.
Rodrigo: In fact, there is one more duplication.
Paul: Which is the Kotlin compiler configuration we did earlier. Both projects have this task, compile Kotlin, Kotlin option block. And we should move that up, too.
Rodrigo: Yeah, so at this point, I guess, I could take the whole test and move it.
Paul: Yeah, the core script has nothing special. So let’s put that here. And now again, we see some red code and configuration failing and it’s the same ID. It says that the compile Kotlin name cannot be resolved and it’s the same idea. Like the Kotlin DSL didn’t have a chance to provide you with a type-safe static accessor here. But fear not, the Gradle DSLs and API provide everything you need to do that properly. So what we want to do here is we want to configure the tasks named compile Kotlin. And this gives us a block to configure the task with the generic task type. But it doesn’t have the Kotlin option, of course. So what we want to configure is a task named compile Kotlin of the Kotlin compile type. And we get the completion in the ID.
Rodrigo: Well, I still have to delete this same block from app.
Paul: Now this script is only about the specificities of this subproject. And we have extracted all the duplicated logic up to the old project. This would be a good time to report.
Rodrigo: Yeah, run it, just to make sure.
Paul: It should still run. Yeah, so let’s run the app. Good. OK, so let’s take a quick step back. We learned a few things by doing that. We learned that the project evaluation order follows a hierarchy of projects. That’s a key thing to keep in mind. We also learned that the script classpath is inherited along this same hierarchy. We require the Kotlin JVM script in the old project. And then the types of it were available.
Rodrigo: In the Kotlin JVM plugin.
Paul: Yeah. And we also learned that cross-configuring requires dynamic references, and we learned how to do it. Well, in other words, type-safe accessors are not always available. In our case, they are not. And you can refer to the Kotlin DSL Primer in the Gradle User Manual. And it details all the cases where the type-safe accessors are available or not.
Rodrigo: Another thing that you might bump into as you are configuring your build, instead you might want to reuse some pre-existing logic, some pre-existing conventions from your company. Maybe another team, or there’s a build team in the company that wants to standardize some sort of conventions. Like maybe the licensing of the projects, they all should be of a certain way, or they should be declared.
Let’s say we have this standard licensing plugin or script that is distributed for all the teams in my company, and now I want to use that in my build. The only catch is that maybe this script is actually a Groovy script. And we want to use that Groovy script in the Kotlin DSL. So here we have a very simple Groovy script, a Gradle Groovy script, that defines a licensing model, with two licenses, the only true two licenses, the GPL and LGPL. By default, we assume every project is licensed under the LGPL. And that’s how the plugin works. The plugin also lets projects to declare their licensing options via an extension.
Paul: So it creates an extension, contributing to the DSL.
Rodrigo: Exactly, and for the purpose of this demo, we removed all the complex validation of license and all that. And it simply prints which license was chosen by a particular project. So that’s the script we want to apply. In our example, the script is actually living inside our codebase. But it could be coming from the network, anywhere reachable, from your build. So this is still the same build. We’re just extending. So we can see, this is just like we had before. We have the repositories, the plugins, and the configurations.
Paul: We want to apply it to all subprojects.
Rodrigo: We want to apply this script to all subprojects. So again, we use this verb apply, so there must be something about applying here. And this time, we actually want to apply a script from a certain location.
Paul: Yeah, we don’t have a plugin ID. We only have a location.
Rodrigo: It’s just a file that is based on my root project. So we can just go and find it, starting with the root project. There is the file, and we can import changes to make sure we applied. So let’s run it and I’m going to use the faithful terminal for this one. I know what’s going on. So I’m going to use this one. So as we run the script, when it actually configures my build, we can see that the script plugin was actually applied. And it configured my app to use the LGPL license and the core project, the LGPL. Turns out I don’t actually want to license my app under the GPL, because it’s an app. I want to license under the terms of the GPL. So I should be able now to go to the particular build script that controls the settings for that project and change it. So let’s do that. So again, I’m going to go to the app build script.
Paul: We saw that the Groovy script was contributing an extension to the DSL. It was named Licensing.
Rodrigo: So there should be something about licensing, and indeed there is. Now we can go and configure the license.
Paul: It looks like we cannot. And it looks like it’s typed any.
Rodrigo: Right. We can actually look at the docs and see what’s going on here. So because this is a Groovy script, that was applied to a project, and it defines types that should be visible here. But they actually are not, as the Kotlin DSL says here. The type of this extension licensing is actually not accessible here, because the type is simply not available. The type is not available because of the way the Gradle isolates the classpath of applied scripts. So even though the type does indeed exist right, in that class loader, in that classpath, it’s not available to my script. So we are stuck, I guess.
Paul: Or we could use some reflection.
Rodrigo: We could use some reflection or we could use the Kotlin DSL. The Kotlin DSL actually has a few interpretability helpers. The one we want to use here is the GroovyBuilder interpretability helper. What this helper lets you do is basically opt into these dynamic semantics of Groovy, and actually use a bit of dynamic logic inside, or a statically typed. So as we saw in the licensing script, we have the licensing extension. And it provides this license function that lets me actually pass a license. So if I go back to my script, I should be able to call this method by using the dynamic invocation syntax. The thing is, we saw the function actually takes an enum. And again, that enum lives in this class loader, in this classpath, that we don’t have access to.
Paul: Non-reachable classpath.
Rodrigo: So what we can do at this point, is pass a string, because Groovy is happy enough to simply go and convert a script to an enum, so let’s do that. So I want to use the GPL, and this should work at this point. So I’m going to run the app again and maybe I import changes, just to make sure everything’s still good. Let’s run it, and now I should see that the app is actually licensed under the GPL. So maybe now we step back a bit and see what we have learned. So we learned that, in fact, you can just mix Kotlin and Groovy scripts in the same build. Both are equally supported, different strokes for different folks, I guess. They can just work together.
Paul: Or the best of both worlds.
Rodrigo: Yes. So we also learned that, when the types are not available for whatever reason, in this case, it was because they were coming from this classpath or this class loader with this classpath that was isolated from my build script. We can use withGroovyBuilder. And we have other facilities in the Kotlin DSL. You should refer to the Kotlin DSL primer in the User Manual. And there we describe all the different ways you can actually interoperate with Groovy from Kotlin.
Paul: All right, so now that we saw several aspects of customizing a build, we look at some patterns for organizing the build logic. So we saw cross-configuration first, like moving things up in the project hierarchy to remove duplication and so on. But the build we used as an example is really simple, and with time and complexity, you will add more functionality to your build. And the build script logic can get unwieldy and gets into this unmanageable state, with these 1,000-line root project build script. You don’t want to go there. So we’ll see how two patterns help with dealing with this growing complexity.
The first one is build source, and the second one will be about sharing the logic between builds. So build source, it’s a convention supported by Gradle that allows you to extract build logic further up, before the root project, into a separate build. But it’s a separate build that is tied to your auto-build. So it’s for going further organizing your build logic inside your build. OK, so here we can see the layout. So we see the same build again. Like if we start from the bottom, we have our setting script, our build script, the Gradle wrapper, our call module, our app module. And then we have this thing, the build source. We can already spot that it’s a separate build because it has a settings script. And it’s a build, so it has a build script and sources.
Rodrigo: That means that it also actually have a rich project structure of its own. So it’s a build.
Paul: Yeah, it’s a build. And it builds things in source, src/main, Java, Kotlin, Groovy, anything. Meaning, you will have more tooling than just scripts because you’ll be able to organize your coding packages, create types, use the language, have tests, and so on. So let’s move the build logic we have to build source.
So here, we skipped a few steps to manual step, creating directories. But it was basically creating the directories we saw on the previous slide. And so the build script you can see here is the one for the build source project. And it’s applying the Kotlin DSL plugin. And this plugin is about bringing the features of the Gradle Kotlin DSL to Kotlin source set, so to an src/main/kotlin. So build source is a build that will build the src/main/kotlin sources. But because we use the Kotlin DSL plugin, it gets not only the Kotlin language features, but also the Gradle Kotlin DSL features and we leverage that.
The first thing we did when we moved things up in the old project is moving the declaration of the Kotlin JVM plugin version. And here, we extracted it further up, declared the dependency as a regular dependency. Because actually, build source is, like any other JVM project, building an artifact, a jar. And this is that jar that will go into the classpath of your root project and all the child project scripts. In build logic, not in the prediction, it’s only for build logic.
Rodrigo: So in fact, the root script is basically the same as we had before in the previous step, except for the plugin.
Paul: Except for declaring the Kotlin JVM plugin version. All right, so let’s imagine our root build script is starting to get big, with lots of logic and so on, and we want to make it more manageable. So we said that in Gradle, you package chunks of functionality as plugins. So that’s exactly what we’ll do. We’ll extract this build logic as a Gradle plugin into build source. So we’ll do that by creating a Gradle Kotlin DSL script into an src/main/kotlin. Let’s say kotlin-conventions because we will capture our Kotlin conventions, like the dependencies and the configuration of the Kotlin compiler. So we can create a Gradle KTS script in the Kotlin subset, thanks to the config DSL plugin we applied to the build script. Yeah, and let’s start moving things.
Rodrigo: Just deploying to start with?
Paul: Yeah
Rodrigo: Let’s just move that. Or in fact, we can basically say, we’re going to now apply the Kotlin conventions plugin.
Paul: Yeah, so one thing to note here is that the plugin ID is derived from the name of the script file in the Kotlin subset here. So here, it’s in the default package, so it’s just kotlin-convention. But packages are taken into account. Like if your script has a package declaration, then it will be namespaced. But let’s keep it simple. And so here what we want then is to request the Kotlin JVM plugin.
Rodrigo: There’s actually a shortcut for that in the Kotlin DSL that we could use at this point. So the Kotlin DSL also makes it easy or easier to work with Kotlin. And so you can actually use the Kotlin plugin spec builder to apply any Kotlin plugin.
Paul: Like the JVM, the Android one, the JavaScript one, the native one.
Rodrigo: So as the documentation says, you can visit the plugin portal to see which plugins are available here.
Paul: And we don’t need a version, because it’s already declared in the build source build script. So that should be it.
Rodrigo: Yeah, so this is exactly the same as doing this, just in case you were wondering. It’s exactly the same, except that it’s a bit less characters to type and to read.
Paul: And again, IntelliJ is asking us to apply dependency. So earlier, we clicked on an auto-reload. This setting is per imported build. And here we are working on separate ones for each step, so that explains that if we want the new dependencies. So let’s just check if it works. So what we did is, we replaced applying the Kotlin JVM plugin by applying our conventional plugin. That itself applies the Kotlin JVM plugin. Small steps.
Rodrigo: So it’s compiling. Let’s apply. Yeah, so some state got stale there. But we can actually see everything still works. It should work, if we still do run, in fact.
Paul: Right, that’s live coding. All right, so now let’s get to the meaty part and actually extract the build logic, like dependencies and the Kotlin compiler configuration.
Rodrigo: Right, so we could just take anything.
Paul: That’s our Kotlin convention.
Rodrigo: Yeah, let’s do that. So we can take just that, go to the Kotlin combinations plugin. OK, it just works.
Paul: Great. But actually, there is an opportunity for simplification here.
Rodrigo: Just a second. Let’s just see if it actually works. There’s something going on there. It’s still loading. But we can actually run the build in the meantime, and it’s all good, it still runs as expected. So we might wonder if the configurations, for instance, that we applied to the compile task are actually being taken into account. If I go to the app again and introduce a warning, say, we should still get the same guarantees that we had before. And in fact, we do. So I’m going to fix that and run again. It has to be in a good state. Yes, it works. And you were saying?
Paul: I was saying that our new Kotlin convention plugin written as a script could be simplified. Because when we were doing cross-configuration, we didn’t have the type-safe static accessor that Kotlin DSL can provide. But here, we should have them. Because we have the plugins block, declaratively, requesting the plugin.
Rodrigo: Yeah, the Kotlin DSL should be able to figure out what is that the Kotlin JVM plugin is providing to the Gradle model. And so this implementation configuration should just be there. And the task implementation should just be there.
Paul: So it means we could replace this all, like name a dynamic reference to compile Kotlin typed.
Rodrigo: Oh, yeah, it should be there. And at this point, we can actually just organize imports, because the Kotlin compile task is no longer used. And we are back to a very self-contained conventions plugin. And let’s go back to the root script, see if everything’s still fine. It keeps saying that. But in fact, we know that it can actually run. It will just take a bit of time the first time, as everything warms up. Let’s import changes. Maybe that will satisfy the IDE.
Paul: It’ll be fine eventually. OK, good. So again, let’s take a quick step back. So we learned that build source, the build is a conventional build for build logic in Gradle. And it allows you to extract common build logic to your whole build inside a single place, where you can benefit from source sets, meaning your whole language tools, like growing interfaces, types, and so on, and also the Kotlin DSL features. Like we saw we can put Gradle Kotlin DSL script there and benefit from its goodness with the plugins block and type-safe accessors. And this script gets exposed as a plugin with the ID derived from the name, we can just apply them. Again, this is all documented in the Kotlin DSL Primer in the User Manual. So go read it.
Rodrigo: Another interesting scenario is sharing build logic not only inside a single build but across different builds in an organization. It doesn’t really matter if it’s a mono-repo or multi-repo. But let’s say a mono-repo with multiple builds. And you want to make sure you have the same set of conventions across all those builds, so you want to share. You don’t want to duplicate your build logic again. You don’t want to duplicate your build logic. For whatever reason, you might not want to make all those projects or all of those builds part of a single build, and have like a multi-project Gradle build. So you actually want to keep all the builds separated for the different parts for different reasons.
Paul: It could be because you’ll release them at a different pace or whatever.
Rodrigo: So the solution to that is to go one level up, so another level up. So basically, I guess, as they say, any problem in computer science can be solved by another level of interaction. And sharing build logic can be solved by just going one level up. And here we are going one level up. So in this structure, we basically have the same structure we had before, except that now, what we had here as build source has been extracted up as a sibling of my build. And I just renamed it build logic. It could be any name because it’s a build. It’s a Gradle build. You can control the name. You can control its version. Now it can be versioned separately, even from your project. And that’s really as simple as that. With a bit of configuration of the connections between those builds, and that’s all you have.
Paul: Yeah, naturally, you have to declare that connection somehow.
Rodrigo: So let’s see how that actually works. Here we have this extraction already imported into my IDE. And what I did, I simply imported the same app as I had before. But now, because I actually declare, in my settings Gradle file, that I’m including a build, there is a sibling.
Paul: So is that the Gradle composite builds?
Rodrigo: Yeah, we’re talking about Gradle composite builds here. Yeah, so you should also read the docs because this is really, really great. And what you get automatically also from idea is that that project gets imported. So you can still use the same tricks. You can navigate to your build logic. So the Kotlin conventions plugin is still there, same logic. Eventually, you will light up. So the first thing you want to do when you move build source up is to include a build. And then you have to also say it. Because we want to include that build as build logic into my build. What I’m doing here is saying that, for my root project in this consuming build, I want all my build scripts to have that build logic built in their classpath.
Paul: So the job built by that other build will end up in the classpath of my root project, and hence be available to all my subproject build scripts. So everybody would be able to see it, and I could use it from any build script.
Rodrigo: Yes, any build script. So the decision of repositories, where to download, for instance, the Kotlin Gradle plugin belongs to the consuming build, because one build might be using one repository, another build a different repository, for whatever reasons, whatever conventions. So this is basically so the Kotlin Gradle plugin can be found. It’s not about finding the build logic. Finding the build logic is enough to just make this connection. I include the build, and I declare dependency. But here’s the one catch. So I’m using these coordinates to connect these two builds. I’m saying, oh, this is the webinar group, the build logic name, and the 1.0 version.
Paul: Like group, name, version. Like regular dependency coordinates.
Rodrigo: So that means that my build logic build actually has to declare that. And the way we make sure, first, we declare the name. We declare the name of the build in the root script and build logic, and in the root build script for my build logic.
Paul: I think it’s the first one.
Rodrigo: I actually add a group and add a version. And the rest is, basically, or exactly the same as before.
Paul: Like as before in build source.
Rodrigo: In build source, yeah, exactly. And that’s it. So we should be able, again, to run the build and see the exact same effect.
Paul: OK, so here the idea is that we had our build with some build logic inside that was in build source. But it was constrained to that build. And then we moved it as a separate build, like a sibling further. We include it. But it means that we could have lots of builds, like, five Android apps that include the same guy providing build logic.
Rodrigo: Yes, that’s exactly right. Good. And the Kotlin DSL works all the way. Like you can navigate from your build logic types. From your build script, you can navigate to the types, from the build logic, and so on.
Paul: OK, so we talked about how to organize the build logic. But the build logic is mainly comprised of plugins. So we’ve talked a bit about how to author all those plugins, and of the challenges that sharing them with a wider audience. For example, writing a plugin for people in your company to use, other teams in your company, or open-source plugins you share to a community, this can cause some challenges. So the first step will be, of course, about publishing those plugins. We’ll talk a bit about how to extend the Gradle DSL in ways that play well with both the Kotlin and Groovy scripts. And those are JVM languages. And how to make your plugin work across Gradle versions. So publishing plugins will make it quick. The idea is, you can publish to the Gradle Plugin Portal or an internal repository. But this is all well-covered in the Plugin Development guides.
One thing to note is that publishing to a repository doesn’t prevent the included build workflow we just saw. Because you can be using included build for build logic for when you are making changes to a plugin, but you have some tests. Of course, you have some tests in your plugin code base. But you want to test it in a real build. So you take that build, you include your plugin. You declare the dependency. And then you can make changes in your ID. You have both in context. And you can make changes to your plugin, change to your Kotlin build scripts, see it reflected live. So it provides a very good development feedback loop. And this thing about, as you said, including builds, it’s orthogonal to choosing mono-repo architectures or multi-repo architectures. It’s builds, the important boundary is builds. We won’t go into much more details.
Rodrigo: So another important concern as you write those plugins is to make sure that those plugins play well with the Gradle ecosystem, which is comprised of many different languages. Groovy is historically the language that started it all. So there are tons of Groovy plugins out there. And you want to be able to reuse that. Also the build scripts within Groovy, you want to make sure that your plugins work well with any build script out there, be it Groovy, be it Kotlin. And there is also the question of other plugins using our plugin. So plugins can also reuse plugins.
Paul: Like we did a Kotlin convention plugin that configures the Kotlin JVM plugin.
Rodrigo: Right. And we could be writing a plugin Java to configure a plugin written in Kotlin that configures a plugin written in Groovy. And we want to make sure this all works nicely together.
Paul: Or closure.
Rodrigo: Yes. So yeah, there are diverse plugin implementation languages, so just keep that in mind. And I guess the important message is, the common denominator across all those languages is that they all support some sort of static typing. So they can all call into statically-defined, statically-typed APIs. So it’s much easier to call. Or let’s say it’s always easier to use statically-typed APIs from dynamically-typed languages than the other way around. So we should always prefer to actually put type constraints into your APIs.
Paul: So if you want to make it easy for people to use your plugin, make it statically-typed, whatever is the language you use.
Rodrigo: Yes. So some examples of what would be, for instance, to always prefer exposing a statically-typed APIs over unityped APIs. So for instance, instead of actually doing a single method that takes any or object, behind the things you are actually using as either an enum or some string. Provide both options, like these strongly-typed options.
Paul: It will also provide a better IDE experience, because people interacting with this API will see in the IDE that, oh, OK, it takes this type, or this type. Because the object is like, OK, I can try to give it something.
Rodrigo: And then actually provide all the enums upfront for you to choose, for instance, in this example. So the other thing you want to do is to always prefer the Gradle model types, like the Gradle types or the Gradle API types, over any language-specific type, when they are available. The most common example, and that happens a lot, is the action type, the type for configuration blocks or the type for actions that act on a specific object. So the best way to do it, be it in a Groovy plugin, a Kotlin plugin, or a Java plugin is to use the Gradle.
Paul: Here it’s Java.
Rodrigo: Java, so this is a Java plugin API that is declaring some method to use the action Gradle type. If this was a Kotlin API, this would be bad, even though it’s typed. So OK, so it’s a strongly-typed API because we actually declare the actual type of my configuration object. I’m limiting the audience somehow. Now I’m forcing Java plugins to actually talk to this Kotlin API, that, although it might be simple to use, it’s not as usual. It’s not as friendly.
Paul: Yeah, you will have to import Kotlin types when you are in Java, dealing with the Kotlin function. But the Gradle action type will unify all these.
Rodrigo: Yeah. And an extreme example would be a Groovy plugin that decides to actually expose closures.
Paul: An untyped closure.
Rodrigo: Yeah, so it’s even an untyped closure. So we don’t know. This closure can return anything. There are no notations to tell me what actually it expects. So that makes it hard to use those plugins from statically typed languages. So that will be an important advice.
Paul: The last thing we will go over quickly about sharing plugins with a wider audience, being inside your company or outside the community. For example, when you have plugins like build logic in your build, you know what Gradle version is used, thanks to the Gradle wrapper, because it’s locked. But as soon as you start sharing your plugin across builds, they may use different Gradle versions.
So just a few pieces of advice to make that not painful. So the first will be to always aim. If you have an existing plugin, always aim for the latest Gradle version, supporting the latest. Or if you’re starting, start with the latest, so you can take advantage of all the goodness the new Gradle release provides. And apply the best practices, because using the latest will make it easier. And then do cross-version testing. And that can be as simple as just a JUnit parameterized test. This is just a quick example. It’s in Kotlin, but you should get the idea. If you look at the two arrows on the right, there is a list of Gradle versions tested. And this is using the Gradle test kit that is a way to integration test Gradle plugins. And you set the version, and that’s it.
And then with that in place, you can incrementally support the Gradle version. Like you start with the latest, make it work, and then you add the Gradle version to the test metrics. You fix it as needed. The Upgrading Gradle section of the User Manual will be very useful doing that, like down version, version. And repeat. And you stop when satisfied. Like OK, I supported this range of Gradle versions. That’s enough for my company, or that’s enough for the community. Or OK, now I went back to three dot X Gradle, I’m overwhelmed. I just want to stop. It’s good. And here is a small example using Kotlin to cross 4.9 Gradle boundary, where support for lazy task registration was done. Just an example, but that’s like what we said about how to create DSLs that work well in statically typed DSLs like Kotlin, plus that, it’s the main two things to get plugins working in most cases.
So again, what did we learn? We saw how to create a Gradle build using the Gradle Kotlin DSL, with or without having Gradle installed. We saw how to customize this build with the Kotlin DSL, configuring extensions and tasks that are configured by the plugins you apply. There was a plugins block, the extensions, like the application block, and task and the task block, and with the type-safe access, also, and so on. We saw some mixing of Kotlin and Groovy build logic, which might be interesting for people migrating builds or dealing with existing build logic. We saw how to modularize a build into projects and how to organize your build logic by pulling build logic up, doing cross-configuration further up into build source, and then further up into shared plugins. You can share between builds and between organizations and to the community. And we talked a bit about how to author a plugin that plays well with different DSL languages, and then multiple Gradle versions.
Here are some documentation links. You’ll get the clickable versions when we send the slides to you. And that’s it for today. And so now we have a bit of time for questions.
Rodrigo: I hope we do.
Paul:
So is build tools evaluated before the root script?
Yes, that’s correct. It’s even evaluated before the setting script. Meaning, you could have, in build source, build logic that controls the settings of your build. A regular Gradle plugin is a plugin of project. But you could also write a plugin of settings.
Are the shown examples somewhere available?
Yes. So we’ll provide you with all the example builds alongside the slides and the video. Good question.
Can I use everything you showed today using Gradle 4.10?
Unfortunately. not everything.
Rodrigo: Most of the pre-compiles to plugin support.
Paul: Yeah, like the Gradle Kotlin DSL script inside Kotlin source set, they cannot be used. In 4.10, it also lacks the type-safe accessors for tasks. So it will be more work on your side. Because you could see, like go back to these referencing dynamic elements if it’s only a name using a string. And if it’s name and type, you need the name in a string and the type.
Rodrigo: Not everything.
So do we have Groovy DSL to Kotlin DSL converter?
We don’t have one yet. Yeah, there are plans, but nothing concrete yet. We have some Java users here who are excited I see.
Can Gradle cache the configuration?
That’s a very interesting question, and it’s one we hope to answer as soon as possible.
Paul: That’s another interesting question.
Do changes in the compile Kotlin task, such as the one we did, like to change warnings to errors, also affect the Kotlin DSL script, as well?
And the answer is no. Why? Because when we change the compile Kotlin task configuration, we change the configuration of the Kotlin compiler, compiling your prediction code, not compiling the build logic.
So do you think the Groovy and Kotlin DSL will live together, or will Kotlin replace Groovy as a new feature?
So both DSLs are equally supported. And we have ongoing efforts to make progress on both, basically.
Rodrigo: Yeah, so to go with that question, Josh asks,
What are the limitations of the Kotlin DSL compared to Groovy at the moment? And what’s on the roadmap for the Kotlin DSL?
So limitation you run into, and we actually ran into it, is when you have a script supplied to some project in some context, and the types are simply not available to the Kotlin script. So Groovy can do the dynamic dispatching magic, so types are irrelevant or mostly irrelevant. But Kotlin cannot do that. Of course, they Kotlin DSL provides the withGroovyBuilder for that exact use case. But that’s the main limitation, is when you need to cross that boundary where types are no longer available, no longer visible.
What’s Gradle wrapper exactly, and when to use it?
Would you like to answer that, Paul?
Paul: Sure. So the Gradle wrapper, it’s a bunch of files, like the mechanical parts. It’s a bunch of files that you store alongside your source code. Like if you’re using Git, you will put those files, the Gradle wrapper files, into check it into source version control. And those files, they allow you to declare and lock what version of the build tool must be used to build that project.
Like for example, if I look at one of the projects I have on my shelf that I did two years ago, it was using Gradle 3.x. I just get the source, and I use the wrapper. And it will download the exact Gradle version that is required to build that project, and it will just work. So it’s like looking for a Gradle version, and it’s also providing a way to automatically download the Gradle version.
So how to access gradle.properties in a Kotlin Gradle script?
So you can do that using the Kotlin DSL provides some shortcuts to do that. And it’s Kotlin delegated properties. So say, if, in your gradle.properties file, you have a property named foo with the value bar, and you know it’s a string, you can type Val foo by project. And this will actually bring the foo. And you need to declare it, to declare a type. You need to say it’s a string. And it will create a Kotlin reference to that property. So basically like that.
Rodrigo: Yes, yes, exactly. And if the property might not be there, you can declare it as a type, and then it’s optional. And then you have to actually check if it’s there.
So are there advantages to using Kotlin for build scripts over Groovy?
I hope you should be able to answer that question by now. Or I hope we answered that question.
Paul: The IDEs report is the main thing. Because with the type-safety of Kotlin and the IDE tooling, it means the IDE will help you a lot with source code navigation, refactorings, like compiler messages, write as you type, and so on. And of course, the fact that the language is statically typed means that you will get more feedback at compilation time, and earlier. With Groovy, which is dynamic, you will get, like, runtime errors.
Rodrigo: It’s basically the choice between dynamic typing and static typing, and with the great IDE support you get for Kotlin coming from JetBrains.
So it is a good idea to add the deploy scripts to the Gradle?
I’m not sure I get completely the question. But if this is about automating deploying apps, absolutely.
Paul: Yeah, you should do that.
Rodrigo: You should do that. So Gradle is built specifically for automation. What’s the actual automated work you use it for is up to you. And you get all the benefits of work avoidance and all that and built cache.
So I have trouble using the Kotlin DSL with an internal artifactory Maven repository. Is there a way to pull in plugins from an internal repository instead of the plugin portal?
Yes.
Paul: Yes. And I will say, refer to the Dependency Management documentation.
Rodrigo: Yeah, it’s basically declaring where the plugins are coming from with the plugin management block in a certain script. But again, yeah, that’s a good idea. Go to the Dependence Management Fundamentals. It’s pretty good.
Paul: Yeah, another question on dependency management, so please go to this other webinar our colleagues did from the Dependency Management team, Cedric and Luis. They use the Kotlin DSL in the webinar. And they show how to manage dependencies.
Rodrigo: So,
Can we use extension functions from Kotlin?
You can use Kotlin extension functions. If you mean Groovy extension functions because I recall Groovy also having some sort of extension functions.
Paul: Yeah, it’s extension modules. So yes, you can. And if you look at the Kotlin DSL Primer, our documentation, there are explanations on how to call Kotlin extensions from Groovy, and how to call Groovy extensions from Kotlin.
Any difference between by project and by extra?
So by project is about referencing Gradle properties, like from the command line or Gradle project properties, from the command line or from the Gradle properties file. And by extra is something else. By extra is extra properties. And you should look at the documentation for all the properties that are available in Gradle, it’s pretty neat. It’s just a different bucket. And the extra are dynamic and set by build logic, whereas the Gradle properties, they are set from the Gradle property file of the command line. That’s the boundary. We could say Gradle properties come from the outside, and extra property comes from the inside of the build logic.
Rodrigo: So another question here.
Is there a way to reference variables for versions in the plugins block yet, or is that planned?
So the plugins block is very limited. Like the API you can use there for a reason. You can use static APIs from that. So you could use like system dot something, but it’s not recommended. So is that planned? Yes, it is planned that we’re going to have a solution for a global version management of plugins. It might not be exactly what you think it would be, like just reference variables. But yes, it’s something that we very much would like to have soon.
Paul: Another one,
What support is there for Kotlin script in Eclipse?
For now, close to none. Like you can open the file and change it. But there’s no IDE inside. You cannot refactor and all that. But like we said earlier, JetBrains, like the issue of working on the Gradle Kotlin DSL script support in Eclipse just went into in progress. So it’s moving. No ETA, sorry.
Rodrigo: So another question about dependence management. Again, I would refer to the webinar on Dependence Management Fundamentals. It’s really great.
Is it best practice to use build source instead of external Gradle scripts, but lose the benefit from class loader separation?
I think it’s better to use build sources rather than scripts. Also because of all the benefits, Paul talked about earlier. You get to use the whole set of patterns we’re used to with the production code. You can have packages. You can segregate your dependencies and have those packages.
Paul: You can unit test your build logic. You can integration test your build logic. You can have things like check time or whatever.
Rodrigo: Yeah, and the class builder separation is something that, it might eventually go away. So if you really need the class loader separation, yeah, so there is no choice. But I would say, as a way to just organize build logic, absolutely.
Paul: Yeah, and it’s a pass to sharing some build logic across builds. So it has a resources benefit.
Rodrigo: So Paul Sharp asks,
What training resources do you recommend for migrating from Groovy to Kotlin?
Paul: So when it comes to Gradle, we have this migration guide. But it’s specific to migrating Groovy builds like Gradle builds written in Groovy to Gradle builds written in Kotlin. But there you will be able to find links to good resources on Kotlin. But I don’t know of a guide specific to moving from the Groovy language to the Kotlin language.
Rodrigo: There are some blog posts you’ll find on Medium. But yeah, I haven’t seen the official guide or material. So refer to the migrating build logic from Groovy to Kotlin, and there are some tips there.
Paul: Yeah, and there are a few questions about having an automatic converter from Groovy to Kotlin, or an automated way in IDE to migrate. There is none, for now.
Rodrigo: So there’s a question about,
How to report third-party plugins, like Android into build source?
So basically, that’s by adding a dependency tribute source. But to add a dependency, you need to know the coordinates of the artifact. You can find the coordinates of the artifact on the plugin portal page of that plugin. And then if you refer to the page, you’ll see both syntaxes, with the plugin block and the build script block. The coordinates you want to use are those for the build script block. That’s basically how you do it. So Frederick asks,
Are there issues with plugins built-in later versions of Gradle running on earlier versions– for example, the embedded Kotlin version?
Paul: There might be. For the embedded Kotlin version, the backward compatibility characteristics of Kotlin apply. So you should refer to like, on kotlinlang.org, there is this matrix of which components of Kotlin are compatible with, according to the version and all that.
I will say, the best way to actually answer the question is to do cross-version testing. Have a test, and then make it one with Gradle versions, and then you’ll see. And like if you are a plugin author, when we pull an out, you just add that to your test metrics, run, and then you’ll see if there is any trouble. Come back to us. We may have made a change we shouldn’t have.
Rodrigo: Yeah, especially things like introducing nullability to APIs, that’s something that might happen.
Paul: Yeah, because Kotlin is straight. That’s good.
All right, thank you all for your attendance and the questions and the thanks. So have a good one, and see you soon.