Running Gradle root project task from subproject - gradle

I am facing the following problem. I have multi module Gradle project. One module is my root project and the second module are integration tests.
In order for my integration tests to run, a copy task needs to run first in order to move some assets to a folder on the root directory of the project.
I already have such a task defined on my root project which when I try to invoke does nothing (I have tried several different way of calling it).
Since this was failing I went ahead and created the following task on the subproject itself:
task prepareTestAssets(type: Copy) {
description = "Copies the needed jars from the root project output build dir"
copy {
from rootProject.configurations.compileClasspath
into ("${rootProject.rootDir}/classes")
rename { fileName -> fileName.replace '-internal', '' }
rename 'ads-(.+).jar', 'ads.jar'
fileMode 0755
}
copy {
from ("${rootProject.buildDir}/libs")
into ("${rootProject.rootDir}/classes")
fileMode 0755
}
}
Which I by having another task depend on it. My goal for this is to have it copy the root project classes as well as the jar generated under build/libs into a single directory (needed by the integration tests to run).
My problem is that when this runs, it seemingly finds no source and keeps failing.
Can anyone help me troubleshoot why I cannot copy the root project's assets from the context of the subproject

It seems you should leave out the copy{} closure and specify its contents directly.
The configuration intended for the task is otherwise given to the closure, so the task thinks it has no configuration (it doesn't know to look for a closure called "copy").

Related

Gradle: Copy one specific file from main project directory into subproject/build/libs

Searching in vain for examples on this humble task, none worked, I need to learn how to use gradle to copy one specific file from the main directory of a gradle project (containing subprojects) and into the /build/libs folder of one of the subprojects. Structure is this:
mainProject folder
file.txt
Subproject folder
build folder
libs folder
(I want to copy file.txt here)
This copy process shall be in the build.gradle (for the Subproject) file. I am using Android Studio and the subproject is a pure java application.
Thanks for any help.
You need to implement a custom task of type Copy to copy the file to the target directory. To access the file in the root project, you may use the rootProject property of the subproject.
task copyFileFromRootProject(type: Copy) {
from "${rootProject.projectDir}/file.txt"
into 'build/libs'
}

Collect files in build directory of parent project

I have a multi-module project in Gradle
root
-- ProjectA
-- ProjectB
Both ProjectA and ProjectB use the application plugin to create a zip in "ProjectA/build/distributions" and "ProjectB/build/distributions" respectively.
Now I want to copy the two zip files into "root/build/distributions".
I have tried various approaches, e.g. adding this in the build.gradle of the root project:
subprojects {
task copyFiles(type: Copy) {
includeEmptyDirs = true
from "$buildDir/distributions"
include '*.zip'
into "$parent.buildDir/distributions"
}
copyFiles.dependsOn(build)
}
or just adding a task to the root project:
task copyFiles(type: Copy) {
from "ProjectA/build/distributions"
from "ProjectB/build/distributions"
include "*.zip"
into "$buildDir/distributions"
}
build.dependsOn(copyFiles)
However, in both cases, nothing happens. No file gets copied.
What am I doing wrong?
I can see two things you are doing wrong:
You have relative paths to the subprojects. This is discouraged as it means you will always have to invoke Gradle from the root folder. And if a Gradle daemon was started from somehere else, it will fail. You could fix it by using the rootDir property (e.g. `from "$rootDir/ProjectA/...") but there is a better way...
The other problem is that you have no dependencies from your copyFiles task in your root project to the required distZip tasks in the sub-projects. So if the distributions have not already been built previously, there are no guarantees that it will work (which it apparently doesn't).
To fix it, you can have a look at the question "Referencing the outputs of a task in another project in Gradle", which covers the more general use case of what you ask. There are currently two answers, both of which are good.
So in your case, you can probably do either this:
task copyFiles(type: Copy) {
from tasks.getByPath(":ProjectA:distZip").outputs
from tasks.getByPath(":ProjectB:distZip").outputs
into "$buildDir/distributions"
}
or this:
task copyFiles(type: Copy) {
subprojects {
from(tasks.withType(Zip)) {
include "*.zip"
}
}
into "$buildDir/distributions"
}
Gradle will implicitly make the copy task depend on the other tasks automatically, so you don't need to do that yourself.
Also note that the currently accepted answer to the question I referenced is about configuration variants, and this is probably the most correct way to model the relationships (see here for more documentation on the topic). But I prefer the simplicity of the direct access to the tasks over the verbose and arguably more complex way to model it through configurations. Let's hope it gets simpler in a later release.

Gradle - Error Copying Files To Project Root

I’m getting the following error whenever I attempt to use a Copy task to copy a file into the root of a project (the same folder I’m running gradle from):
Failed to create MD5 hash for file content.
I thought this was related to the artifacts I was pulling from Artifactory, but that seems to be unrelated. I was able to get the same results with a minimal script.
Is there something obviously wrong with what I’m doing, or does Gradle intentionally disallow such things?
task fails(type:Copy) {
from 'build/someFile.txt'
into new File('.').absolutePath
}
task works(type:Copy) {
from 'build/someFile.txt'
into new File('.').absolutePath + '/output'
}
Short Answer: Don't copy into the project directory, you are best to use into "$buildDir/someFolder" so that the folder is isolated to this single task, and also so that it will be cleaned by gradle clean
Long Answer: At it's core, Gradle has the concept of an "UP-TO-DATE" check for every single task. If Gradle sees that nothing has changed since last time a task was executed it will use the old result instead of executing again.
UP-TO-DATE checking is implemented by taking a "hash" of the task inputs and task outputs. Since you are using into '.' that means that the entire contents of the project directory is considered a task output (bad)
Gradle uses the .gradle folder for temp files (eg task hashes) It's likely some of these files are locked for writing as Gradle is trying to also read the same files (to calculate the "hash" of the task outputs) causing the error you are seeing
* EDIT *
If you need to copy into the project directory for legacy reasons, you might use Project.copy(...) directly instead of a Copy task. You could manually manage the task inputs/outputs in this case
Eg
task customCopy {
inputs.file "$buildDir/someFile.txt"
outputs.file 'someFile.txt'
doLast {
copy {
from "$buildDir/someFile.txt"
into '.'
}
}
}
Can you believe it, the following works
task myCopy(type: Copy) {
from "$rootDir/app1/src/main/resources/db"
into "$rootDir/app2/src/test/resources/db"
}
test.dependsOn myCopy
and the following doesn't 🤦
task myCopy(type: Copy) {
from '$rootDir/app1/src/main/resources'
into '$rootDir/app2/src/test/resources'
}
test.dependsOn myCopy

trying to understand the "from" in the "into" closure about a gradle sample

When learning from the tutorial, I encountered a sample below:
task dist(type: Zip) {
dependsOn spiJar
from 'src/dist'
into('libs') {
from spiJar.archivePath // what's meaning
from configurations.runtime // what's meaning
}
}
artifacts {
archives dist
}
As a newbie to gradle, how to understand this into(...){ from ...}?
In this particular case:
from spiJar.archivePath
is probably (since I don't know what exactly spiJar is) resolved to the output of spiJar task - namely a jar archive - particular file.
When it comes to the second question configurations is (simplifying) is a map that matches given name - runtime in this case - with a group of dependencies (jar files in this case).
When:
from configurations.runtime
is used is copies all the dependencies from runtime configuration into given destination.
In addition to the previews answer, to, possibly, clarify a little. Due to dsl reference, Zip task provide the into(destPath, configureClosure) method, which:
Creates and configures a child CopySpec with a destination directory inside the archive for the files.
This means, that it could create an additional directory with the some content in it. In your case, script creates a libs directory within archive and specifies the resources, which should be copied into this directory. This resources could be out of the src/dist directory, which will be fully zipped into the archive's root.
Here is a dsl reference for CopySpec task, which is configured by the into method of the Zip task. As you can see, from just:
Specifies source files or directories for a copy.

How do I prevent Gradle from building a non-project directory?

In the Gradle samples (included with version 2.2.1) there is a java/multiproject project.
The settings.gradle file defines the following projects:
include "shared", "api", "services:webservice", "services:shared"
Note that services is not itself a project, merely a directory which contains the webservice and shared projects.
When I run the command gradle build from the root directory, I notice that after gradle successfully builds it creates inside the /services directory a /build directory containing /lib and a /tmp directories.
Inside of /services/build/lib is a jar: services-1.0.jar which contains very little; specifically just a META-INF/MANIFEST.MF file containing:
Manifest-Version: 1.0
provider: gradle
So what is causing Gradle to build a jar for this non-project? And how can I prevent this behavior in my similarly structured multiproject project?
/services isn't a project, I don't want to create anything inside /build folder at all. Yes I could just delete it, but I would like to avoid the unnecessary work of building this jar/running any tasks on this non-project in the first place.
To be honest I've no reasonable idea why gradle builds this folder. I guess that because it's a kind of a transient folder. However it can be excluded by adding the following piece of code to main build.gradle script:
project(':services').jar { onlyIf { false } }
Desired effect (services.jar elimination) can be also obtained with the following settings.gradle content:
include "shared", "api", "services/webservice", "services/shared"
File instead of project paths are included.
My guess would be that this is a combination of the next 2 gradle rules:
When you're including subprojects in the build.settings file using the include keyword according to Gradle Documentation here:
the inclusion of the path 'services:hotels:api' will result in
creating 3 projects: 'services', 'services:hotels' and
'services:hotels:api'.
In simple words, this means that the inclusion of services::webservice will also build the services project
The bulid.gradle file in your root that applies the 'java' plugin. According to Gradle Documentation here every configuration defined in the root.gradle takes effect for all sub projects. This means that it will also hold as the default configuration for the services project. As the 'java' plugin was applied a jar will be created, but as there is no src/main folder under the services directory nothing will be compiled and the jar will include only a META-INF/MANIFEST.MF file.

Resources