I have a bunch of sql files which I want to execute in my gradle build. I came up with this.
def sqlDir = new File('src/main/resources/')
task testSql << {
sqlDir.eachDirRecurse() { dir ->
dir.eachFileMatch(~/.*\.sql$/) {
// sql.execute(it.text)
println it
}
}
}
I however, also want to exclude some files, and I can probably do that using if blocks and hardcoding filenames inside the code. Can someone give me a cleaner solution to exclude files?
As you pointed, there is a [fileTree] solution in Gradle
You can use it somehow like that. Please refer link to details.
def sqlDir = file 'src/main/resources/'
task testSql << {
fileTree(dir: sqlDir, include: "**.sql", exclude: ["somefilename.sql", "otherfilename.sql"]).each {
sql.execute(it.text)
}
}
Related
I can copy all dependencies of a module in a multimodule gradle project with a task like
tasks.register<Sync>("copyResources") {
from(configurations.runtimeClasspath)
into(layout.buildDirectory.dir("extraResources"))
}
But actually, I only need to copy the project dependencies by applying a filter with project group id.
Something like
tasks.register<Sync>("copyResources") {
from(configurations.runtimeClasspath) {
include {
group "this.project.group" // NOT WORKING.
}
}
into(layout.buildDirectory.dir("extraResources"))
}
What is the right way to do this in Gradle with Kotlin DSL?
The following works.
But I feel there must be a simpler solution.
This solution uses regular expressions to filter out dependencies. The regex match is true if the absolute path of the dependency file contains the multimodule project root in it.
val copyImplemtations by configurations.creating {
extendsFrom(configurations.implementation.get())
}
tasks.register<Sync>("copyResources") {
val regex = Regex(".*[\\\\/]kotlin-spring-demo[\\\\/].*")
from(copyImplemtations.filter { regex.matches(it.absolutePath) })
into(layout.buildDirectory.dir("extraResources"))
}
I have a package in my app and I'd like to go over all classes in that package to then generate some JSON schema automatically.
I'd like to create a gradle task and with some sort of build-time dependency would allow me to do:
tasks.register("my fancy task") {
doLast {
"my.package.name".readKotlinFiles().classes.forEach { klass ->
klass.properties["id"]... and do something here
}
}
}
How can you do such thing easily?
I was doing something similar, loading classes form the project, but not as a Gradle task or plugin but in Java application itself. To execute this logic as Gradle task / plugin I would do it in the following way:
First you generate jar package from your Kotlin project as a standard process of Kotlin-Gradle build.
As Gradle build is not a part of your project source code you have to load classes of the actual project with properly configured ClassLoader.
Then you can read those classes and generate the report.
Now that would translate into the pseudocode of a Gradle task:
tasks.register("processClasses") {
dependsOn tasks.named("assemble") // generate jar
doLast {
// Initiate classloader of assembled project
// Read target package from gradle properties or other kind of parameters
// Get list of all classes under the specific package
// Load the list of classes from classloader
// Execute custom logic upon the list of classes
// Generate report in specific format (JSON, YAML, HTML, ...)
}
}
I tried to validate the logic and created a POC as the following GitHub project. There are two flavors of build scripts:
Groovy build script is in master branch
Kotlin build script version is in kotlin branch. Note multiple
aspects should be improved and adapted to the specific goal you have
in mind (really curious actually). You are welcome to make any
suggestions, fork the code, do changes..
Excerpt of the code below:
// Pack all the dependencies into jar
tasks.jar {
from(configurations.runtimeClasspath.map { configuration ->
configuration.asFileTree.fold(files().asFileTree) { collection, file ->
if (file.isDirectory) collection else collection.plus(zipTree(file))
}
})
}
val packageToProcess: String by project
tasks.register("processClasses") {
group = "process"
description = "Process classes form a specific package"
dependsOn(tasks.named("assemble"))
doLast {
// Instantiate classloader from jar as you will need other dependencies for loading classes
val file: File = project.projectDir.toPath().resolve(tasks.jar.get().archiveFile.get().toString()).toFile()
println("Packaged Kotlin project jar file: $file")
val classloader: ClassLoader = URLClassLoader(arrayOf(file.toURI().toURL()))
// Iterate through all of the class files in the specified package
val packageToScan: String = packageToProcess.replace(".", File.separator)
val path: java.nio.file.Path = project.file("build/classes/kotlin/main/").toPath().resolve(packageToScan)
val classesFromSpecificPackage = Files.walk(path)
.filter {
Files.isRegularFile(it)
}
.map {
project.file("build/classes/kotlin/main/").toPath().relativize(it)
}
.map { it.toString().substring(0, it.toString().lastIndexOf(".")).replace(File.separator, ".") }
.map {
//println("Class = $it") // print class if necessary before loading it
classloader.loadClass(it)
}
// Do something with the classes form specific package
// Now just basic information is printed directly to the console
println("=======================================")
println("Classes from package: $packageToScan")
println("")
classesFromSpecificPackage.forEach {
println(" Class: ${it.canonicalName}")
val fields = it.declaredFields
for (element in fields) {
println(" - Declared field: $element")
}
println("")
}
println("=======================================")
}
}
I'm trying to write a task named stage like this:
plugins {
base
}
val clean = "clean"
val build = "build"
tasks.register("stage") {
dependsOn(clean, build)
}
tasks[build].dependsOn(clean)
The problem is that when I run ./gradlew stage it doesn't run clean, nor build. This is a multiproject build and I have 3 subprojects with Kotlin code.
How do I get Gradle to run ./gradlew clean build whenever I type ./gradlew stage? This is clearly not a solution, and I don't see what I'm doing wrong.
I also tried this, but it didn't work either. It runs the tasks I want but at the end it runs clean and I end up with no build folder:
tasks.register("stage") {
subprojects.forEach { project ->
val clean = project.tasks.first { it.name.contains("clean") }
val build = project.tasks.first { it.name.contains("build") }
build.dependsOn(clean)
dependsOn(build)
}
}
I think one of the issues in your code is running the command:
build.dependsOn(clean)
Instead try:
tasks{
register("stage"){
dependsOn(clean, build)
}
}
Remember that tasks{} is a container and by scoping the code inside we have access to the other tasks as well.
You likely need to define the ordering using mustRunAfter
tasks.register("stage") {
subprojects.forEach { project ->
val clean = project.tasks.first { it.name.contains("clean") }
val build = project.tasks.first { it.name.contains("build") }
build.dependsOn(clean)
build.mustRunAfter(clean)
dependsOn(build)
}
}
From the docs of Task Dependencies and Task Ordering:
Dependencies to a task are controlled using Task.dependsOn(java.lang.Object[]) or Task.setDependsOn(java.lang.Iterable), and Task.mustRunAfter(java.lang.Object[]), Task.setMustRunAfter(java.lang.Iterable), Task.shouldRunAfter(java.lang.Object[]) and Task.setShouldRunAfter(java.lang.Iterable) are used to specify ordering between tasks.
This documentation in Gradle about ordering tasks can explain tasks ordering, and why you need a combination of dependsOn and mustRunAfter for your use case:
Note that “B.mustRunAfter(A)” or “B.shouldRunAfter(A)” does not imply any execution dependency between the tasks:
It is possible to execute tasks A and B independently. The ordering rule only has an effect when both tasks are scheduled for execution.
its much simpler
plugins {
base
}
allprojects {
task stage() {
dependsOn(clean, build)
}
}
so when you run stage - it should do clean and build
I wanted to execute gradle task from my plugin code.
Any one can suggest me, how can I programmatically execute gradle task from code.
Thanks,
Sumeet.
You can do it as follows
task a {
doLast {
println 'test'
}
}
task b {
doLast {
a.execute()
}
}
So in plugin code it might be something similar to
project.tasks.<taskname>.execute()
But this might be changed in the future. You should rely on the chaining of the tasks rather then invoking them directly.
The answer provided by Martin Linha does not work anymore with recent versions of Gradle, for instance Gradle 7. The Task class does not have an execute method anymore. Instead, the activities have to be executed. In addition, you might want to execute the dependencies as well:
void executeTask(Task task) {
task.taskDependencies.getDependencies(task).each {
subTask -> executeTask(subTask)
}
task.actions.each { it.execute(task) }
}
Note that this is still a hack and not guaranteed to work.
Here's what I'm trying to do:
Copy archive (zip) from buildscript dependencies to the temp directory
Unzip archive to another directory within build
Here's task to copy archive (works)
task copyTpcds(type: Copy) {
file('build/zip').mkdirs()
from buildscript.configurations.classpath
include 'tpcds*'
into 'build/zip'
}
And the task to unzip and then delete archive
task extractTpcds(type: Copy) {
def names = new FileNameFinder().getFileNames('build/zip', 'tpcds*')
def outDir = file('build/cmd/tpcds')
outDir.mkdirs() // make sure the directory exists
from zipTree(file(names[0])) // generates error when
into outDir
// now remove copied zip file
//zipFile.delete() // deletes file before the extractions completes?
}
Here are few scenarios:
If I put both tasks into build.gradle and try to run anything, even just gradle tasks then I get this error: Neither path nor baseDir may be null or empty string. path='null' basedir='C:\dev\code\td\pdo\tpcds-tpg' from this code in the task #2: file(names[0])
If I comment out code in the 2nd task, 1st task would run and copy zip file to build/zip
Now I can uncomment code in the 2nd task (except deletion) and execute gradle extractTpcds it will run and extract the archive
So it seems to me that
File operations are evaluated across all tasks regardless of which tasks is executed
If I have a code that copies the file with some follow up code that tries to operate on the file being copied then that code will not wait for copy process to complete and will fail because file is not there yet
I'm at loss on how to deal with this and would greatly appreciate your suggestions
The following works for me, using Gradle 2.12 (and assuming that the zip file resides in files):
buildscript {
configurations {
classpath
}
dependencies {
classpath files("files/tpcds.zip")
}
}
def copyFiles = { ->
ant.mkdir(dir: "build/zip")
buildscript.configurations.classpath.each { def thisFile ->
if (thisFile.name ==~ /tpcds.*/) {
ant.copy(file: thisFile.absolutePath, todir: "build/zip")
}
}
}
tasks.whenTaskAdded { task ->
if (task.name == "extractTpcds") {
copyFiles()
}
}
task copyTpcds << {
copyFiles()
}
task extractTpcds(type: Copy) {
def names = new FileNameFinder().getFileNames('build/zip', 'tpcds*')
def outDir = file('build/cmd/tpcds')
outDir.mkdirs() // make sure the directory exists
from zipTree(file(names[0])) // generates error when
into outDir
// now remove copied zip file
//zipFile.delete() // deletes file before the extractions completes?
}
An issue with the original involves an impedance mismatch vis-a-vis the ICE rule: Initialization phase, Configuration phase, and Execution phase. In particular, the specification of the Copy task is in Configuration phase; typically, task dependencies are enforced (e.g. dependsOn) during the Execution phase. The extractTpcds task has a dependency on other code during the Configuration phase.
In my example, there are are 2 cases:
gradle copyTpcds will call the copyFiles method during Execution phase. The << means "do this stuff last [during the Execution phase]".
gradle tasks will trigger the code in whenTaskAdded, during Configuration phase, and call copyFiles. Similarly for gradle extractTpcds
An alternative to this is simply to use the AntBuilder in both tasks, and avoid Type: Copy completely, as shown here:
buildscript {
configurations {
classpath
}
dependencies {
classpath files("files/tpcds.zip")
}
}
task copyTpcds << {
ant.mkdir(dir: "build/zip")
buildscript.configurations.classpath.each { def thisFile ->
if (thisFile.name ==~ /tpcds.*/) {
ant.copy(file: thisFile.absolutePath, todir: "build/zip")
}
}
}
task extractTpcds(dependsOn: 'copyTpcds') << {
def outDir = "build/cmd/tpcds"
ant.mkdir(dir: outDir)
def names = new FileNameFinder().getFileNames('build/zip', 'tpcds*')
names.eachWithIndex { zipFile, index ->
if (index == 0) {
ant.unzip(src: zipFile, dest: outDir)
}
}
}
Something like this should work, but why do you need to delete the zip? chaining the tasks with dependsOn means you only need to run the second task and the copyTpcds will automatically run unless the inputs or outputs have changed (deleted)
task extractTpcds(type: Copy) {
dependsOn(copyTpcds) //make this execute after
def names = new FileNameFinder().getFileNames('build/zip', 'tpcds*')
def outDir = file('build/cmd/tpcds')
outDir.mkdirs() // make sure the directory exists
from zipTree(file(names[0])) // generates error when
into outDir
// now remove copied zip file
doLast { zipFile.delete() }// deletes file after the extractions completes
}