Using a wildcard in gradle copy task - gradle

I'd like to copy a directory using a wildcard, however the from method of the Gradle copy task doesn't accept a wildcard.
// this doesn't work
task copyDirectory(type: Copy) {
from "/path/to/folder-*/"
into "/target"
}
// this does
task copyDirectory(type: Copy) {
from "/path/to/folder-1.0/"
into "/target"
}

Just use 'include' task property to specify exact files ot directories you need to copy, something like this:
task copyDirectory(type: Copy) {
from "/path/to/"
include 'test-*/'
into "/target"
}
Update: if you want to copy only directories content, then you have to deal with every file separately, something like this:
task copyDirectory(type: Copy) {
from "/path/to/"
include 'test-*/'
into "/target"
eachFile {
def segments = it.getRelativePath().getSegments() as List
it.setPath(segments.tail().join("/"))
return it
}
includeEmptyDirs = false
}

Related

How do I get a task to execute of type Zip using 'dependsOn'?

I am trying to make my task zipGui execute on build target, but the only way I seem to be able to execute a task is if I remove the (type: Zip) from the task definition.
This snippet fails to execute the zipGui task at all:
task zipGui(type: Zip) {
doFirst {
println "==================== Zipping GUI components"
}
doLast {
FileTree zip = zipTree('assets/htdocs/gui.zip')
from 'assets/htdocs'
}
}
build.dependsOn zipGui
And this executes the zipGui but it doesn't know anything about how to zip files:
task zipGui {
doFirst {
println "==================== Zipping GUI components"
}
doLast {
FileTree zip = zipTree('assets/htdocs/gui.zip')
from 'assets/htdocs'
}
}
build.dependsOn zipGui
This is a stripped down fragment of the overall build.gradle.
How can I get zipGui to execute as a dependency of the build?
EDIT: here is more of the real build.gradle without me stripping things out to simplify the question:
task copyCert(type: Copy) {
from '../../../install'
into 'assets/certs/root'
include 'ca.pem'
doFirst {
println "==================== Copying root cert into assets"
}
}
task copyGui(dependsOn: copyCert, type: Copy) {
from '../../web/gui'
into 'assets/htdocs'
include '**/*.html'
include '**/*.css'
include '**/*.js'
include '**/*.wav'
include '**/*.tmpl'
include '**/*.png'
include '**/*.gif'
include '**/*.jpg'
exclude '**/*.DS_Store'
exclude '**/.gitignore'
exclude '**/.thumb'
exclude '**/build'
doFirst {
println "==================== Copying gui components into assets"
}
}
task zipGui(dependsOn: copyGui, type: Zip) {
FileTree zip = zipTree('assets/htdocs/gui.zip')
from 'assets/htdocs'
doFirst {
println "==================== Zipping GUI components"
}
}
Check details of zip task here: https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Zip.html
task zipGui(type: Zip) {
archiveName = 'gui.zip'
from("$projectDir/../../../install") {
into 'assets/certs/root'
include 'ca.pem'
}
from("$projectDir/../../web/gui"){
into 'assets/htdocs'
exclude '**/*.DS_Store'
exclude '**/.gitignore'
exclude '**/.thumb'
exclude '**/build'
} // Just include or exclude is enough here as you indicated.
destinationDir(file("$buildDir/libs"))
}
Your task simply has nothing to do.
Gradle tasks are only executed, if they have something to do. If there is nothing to do (e.g. no files to zip), a task is skipped. There are multiple reasons for a task to have nothing to do. The main reason are up-to-date checks to prevent Gradle from doing the same thing what was done during the last invocation, at least as long as the task outputs are still available and the task inputs did not change.
However, in your specific case, the task has nothing to do, because at the time the task executes, no input files are specified at all. Files to include into the zip file can be added via from, which you do use. But, you use it inside a doLast closure, which is executed after the task actions (where the zipping takes place) were executed. Therefor, when running the zip process, the configuration did not take place.
You could use the regular configuration closure of the task or even the doFirst closure to configure your task:
task zipGui(type: Zip) {
from 'assets/htdocs'
}
// OR
task zipGui(type: Zip) {
doFirst {
from 'assets/htdocs'
}
}
Some more remarks:
The line FileTree zip = zipTree('assets/htdocs/gui.zip') does absolutely nothing, because the created file tree is not queried. You need to either access the files of the file tree manually or pass it to a task, e.g. your zip task: from zipTree('assets/htdocs/gui.zip')
In the second example, the task is executed because for normal tasks there are no automatic checks if there is any work to do, since a normal task can basically do everything the user makes it do.
I'm not quite sure, but I think the second example should throw an exception, because a normal task without a type does not provide a from method.
To get the status of each executed task and more insight generally, use the command line parameter --console=plain. The parameters -d and --stacktrace / --full-stacktrace may be used to get more information on errors.

Gradle zip: how to include and rename one file easily?

Create a zip adding file "hello/world.xml" under directory "foo/bar" as "hello/universe.xml"
task myZip(type: Zip) {
from ("foo/bar") {
include "hello/world.xml"
filesMatching("hello/*") {
it.path = "hello/universe.xml"
}
}
}
filesMatching(...) will impact performance obviously.
What is a better way? like:
task myZip(type: Zip) {
from ("foo/bar") {
include ("hello/world.xml") {
rename "hello/universe.xml"
}
}
}
But rename is not supported with include.
I don't get why you are using filesMatching at all. You are only including one single file in your child CopySpec. Simply rename it and everything is fine:
task myZip(type: Zip) {
from ('foo/bar') {
include 'hello/world.xml'
rename { 'hello/universe.xml' }
}
}
If you want to include multiple files (or even copy all), but only want to rename one of them, specify which file(s) to rename with a regular expression as first argument:
task myZip(type: Zip) {
from 'foo/bar'
rename 'hello/world.xml' 'hello/universe.xml'
}
If the last one did not work, try:
rename ('a.java', 'b.java')

Weird behaviour of a simple gradle copy task

I am completely baffled by the gradle behaviour of a Copy task into multiple directories.
I intend to copy all files from src/common into
target/dir1/ps_modules
target/dir2/ps_modules
target/dir3/ps_modules
Below is how my build.gradle looks:
project.ext.dirs = ["dir1", "dir2", "dir3"]
// ensures that ps_modules directory is created before copying
def ensurePsModulesDir() {
dirs.each {
def psModules = file("target/$it/ps_modules")
if (!(psModules.exists())) {
println("Creating ps_modules directory $psModules as it doesn't exist yet")
mkdir psModules
}
}
}
task copyCommons(type: Copy) {
doFirst {
ensurePsModulesDir()
}
from("src/common")
dirs.each {
into "target/$it/ps_modules"
}
}
The result of running the command ./gradlew copyCommons is completely weird.
The folder creation works as expected, however, the contents/files are copied only in the target/dir3/ps_modules directory. The rest two directories remain empty.
Any help would be appreciated.
Below is the screen grab of target directory tree once the job is run:
I think you want to do something like:
task copyCommons(type: Copy) {
dirs.each {
with copySpec {
from "src/common"
into "target/$it/ps_modules"
}
}
}
I think you can get rid of the ensurePsModulesDir() with this change
* edit *
it seems that the copy task is forcing us to set a destination dir. You might think that setting destinationDir = '.' is ok but it's used in up-to-date checking so likely the task will NEVER be considered up-to-date so will always run. I suggest you use project.copy(...) instead of a Copy task. Eg
task copyCommons {
// setup inputs and outputs manually
inputs.dir "src/common"
dirs.each {
outputs.dir "target/$it/ps_modules"
}
doLast {
dirs.each { dir ->
project.copy {
from "src/common"
into "target/$dir/ps_modules"
}
}
}
}
You can configure a single into for a task of type Copy. In this particular example gradle behaves as expected. Since dir3 is the last element on the list it is finally configured as a destination. Please have a look at this question - which you can find helpful. Also this thread might be helpful as well.

What is the best way to parameterize a task on Gradle?

I need to create two different .properties files from two different .properties.dist if they don't exist, so I'm using a Copy task and I'm specifying the from and the into accordingly.
At the moment I had to create two different tasks, each of which is creating a file like this:
task copyAndRenameDialling(type: Copy){
if(!file("./properties/dialling.properties").exists()){
from './dist/dialling.properties.dist'
into './properties/'
rename{ String fileName ->
fileName.replace('.dist','')
}
}
}
task copyAndRenameFiles(type: Copy){
if(!file("./properties/file.properties").exists()){
from './dist/files.properties.dist'
into './properties/'
rename{ String fileName ->
fileName.replace('.dist','')
}
}
}
task copyAndRenameProperties {
dependsOn << copyAndRenameDialling
dependsOn << copyAndRenameFiles
}
and I run the task with gradle copyAndRenameProperties.
Is it possible to make the two Copy tasks parameterized based on the name of the file, so that I have only one generic copyAndRename?
If so, how can I pass the parameter to the task?
I'd do it like this:
task copyAndRenameProperties(type: Copy) {
from 'dist'
include '*.properties.dist'
into 'properties'
rename { it - ~/\.dist$/ }
eachFile { if (file("properties/$it.name").file) it.exclude() }
}
or if you really only want those two specific files
task copyAndRenameProperties(type: Copy) {
from 'dist/dialling.properties.dist'
from 'dist/file.properties.dist'
into 'properties'
rename { it - ~/\.dist$/ }
eachFile { if (file("properties/$it.name").file) it.exclude() }
}
or
task copyAndRenameProperties(type: Copy) {
from 'dist'
include 'dialling.properties.dist', 'file.properties.dist'
into 'properties'
rename { it - ~/\.dist$/ }
eachFile { if (file("properties/$it.name").file) it.exclude() }
}

Gradle copy without overwrite

Is there a way to avoid overwriting files, when using task type:Copy?
This is my task:
task unpack1(type:Copy)
{
duplicatesStrategy= DuplicatesStrategy.EXCLUDE
delete(rootDir.getPath()+"/tmp")
from zipTree(rootDir.getPath()+"/app-war/app.war")
into rootDir.getPath()+"/tmp"
duplicatesStrategy= DuplicatesStrategy.EXCLUDE
from rootDir.getPath()+"/tmp"
into "WebContent"
}
I want to avoid to specify all the files using exclude 'file/file*'.
It looks like that duplicatesStrategy= DuplicatesStrategy.EXCLUDE doesn't work. I read about an issue on gradle 0.9 but I'm using Gradle 2.1.
Is this problem still there?
Or am I misunderstanding how this task should be used properly?
Thanks
A further refinement of BugOrFeature's answer. It's using simple strings for the from and into parameters, uses the CopySpec's destinationDir property to resolve the destination file's relative path to a File:
task ensureLocalTestProperties(type: Copy) {
from zipTree('/app-war/app.war')
into 'WebContent'
eachFile {
if (it.relativePath.getFile(destinationDir).exists()) {
it.exclude()
}
}
}
You can always check first if the file exists in the destination directory:
task copyFileIfNotExists << {
if (!file('destination/directory/myFile').exists())
copy {
from("source/directory")
into("destination/directory")
include("myFile")
}
}
Sample based on Peter's comment:
task unpack1(type: Copy) {
def destination = project.file("WebContent")
from rootDir.getPath() + "/tmp"
into destination
eachFile {
if (it.getRelativePath().getFile(destination).exists()) {
it.exclude()
}
}
}

Resources