Coding With Fun
Home Docker Django Node.js Articles Python pip guide FAQ Policy

Gradle uses files


May 25, 2021 Gradle


Table of contents


Use the file

Most of the build work is done with files. Gradle adds concepts and APIs to help you achieve this.

Locate the file

You can use the Project.file() method to find a file relative to the project directory.

Find the file

build.gradle

// Using a relative path
File configFile = file('src/config.xml')
// Using an absolute path
configFile = file(configFile.absolutePath)
// Using a File object with a relative path
configFile = file(new File('src/config.xml'))  

You can pass any object to the file() method, and it will try to convert it to a File object with an absolute path. T ypically, you pass it on to an instance of String or File. T he value of the tostring() method of the object provided will act as the file path. I f this path is an absolute path, it is used to construct a File instance. O therwise, the File instance is constructed by first calculating the relative path of the path provided relative to the project directory. This file() method can also identify URLs, such as file:/some/path.xml

This is a useful way to convert some user-provided values into a File object with a relative path. Because the file() method always calculates the path provided relative to the project directory, it is best to use new File (somePath), because it is a fixed path and does not change because the user runs the specific working directory of Gradle.

The collection of files

A collection of files simply represents a set of files. I t is represented by the FileCollection interface. M any objects in the Gradle API implement this interface. For example, relying on configuration implements fileCollection as an interface.

Using Project.files() is one of the ways to get an instance of FileCollection. Y ou can pass any object to this method, and they are converted to a set of File objects. T his Files() method accepts any type of object as its argument. A ccording to the description of the file() method in chapter 16.1, "Locating files," its results are calculated as relative paths relative to the project directory. Y ou can also pass collections, iterative variables, map, and arrays to the files() method. They are expanded and the content is converted to a File instance.

Create a collection of files

build.gradle

FileCollection collection = files('src/file1.txt', new File('src/file2.txt'), ['src/file3.txt', 'src/file4.txt'])  

A collection of files is iterative and can be converted to other types of object collections using the as operator. Y ou can also add two file collections using the plus operator, or use the - operator to subtract a file collection. Here are some examples of using file collections.

Use a collection of files

build.gradle

// Iterate over the files in the collection
collection.each {File file ->
    println file.name
}
// Convert the collection to various types
Set set = collection.files
Set set2 = collection as Set
List list = collection as List
String path = collection.asPath
File file = collection.singleFile
File file2 = collection as File
// Add and subtract collections
def union = collection + files('src/file3.txt')
def different = collection - files('src/file3.txt')  

You can also pass a closure or a Callable instance to the files() method. I t is called when the contents of the collection are queried and its return value is converted to a set of file instances. T he return value of this closure or Callable instance can be any type of object supported by the files() method. This is an easy way to "implement" the FileCollection interface.

Implement a collection of files

build.gradle

task list << {
    File srcDir
    // Create a file collection using a closure
    collection = files { srcDir.listFiles() }
    srcDir = file('src')
    println "Contents of $srcDir.name"
    collection.collect { relativePath(it) }.sort().each { println it }
    srcDir = file('src2')
    println "Contents of $srcDir.name"
    collection.collect { relativePath(it) }.sort().each { println it }
}  

The output of the gradle -q list

> gradle -q list
Contents of src
src/dir1
src/file1.txt
Contents of src2
src2/dir1
src2/dir2  

You can pass in some other types of objects to files():

FileCollection

They are expanded and the content is included in the file collection.

Task

The output file of the task is included in the file collection.

TaskOutputs

TaskOutputs' output files are included in the file collection.

One thing to note is that the contents of a collection of files are slowly calculated, and it is calculated only when needed. This means that you can, for example, create a FileCollection object and the files inside will not be created until later, for example, in some tasks.

File tree

A file tree is a collection of files sorted hierarchically. F or example, a file tree might represent the contents of a tree or ZIP file. I t is represented by the FileTree interface. T he FileTree interface inherits from FileCollection, so you can treat the file tree the same way you treat file collections. Several objects in Gradle implement the FileTree interface, such as source sets.

Using Project.fileTree() is one way to get a FileTree instance. It defines a base directory to create FileTree objects, and can choose to add some Ant-style inclusion and exclusion patterns.

Create a file tree

build.gradle

// Create a file tree with a base directory
FileTree tree = fileTree(dir: 'src/main')
// Add include and exclude patterns to the tree
tree.include '**/*.java'
tree.exclude '**/Abstract*'
// Create a tree using path
tree = fileTree('src').include('**/*.java')
// Create a tree using closure
tree = fileTree('src') {
    include '**/*.java'
}
// Create a tree using a map
tree = fileTree(dir: 'src', include: '**/*.java')
tree = fileTree(dir: 'src', includes: ['**/*.java', '**/*.xml'])
tree = fileTree(dir: 'src', include: '**/*.java', exclude: '**/*test*/**')  

You can use a file tree as if you were using a collection of files. You can also use Ant-style mode to access the contents of the file tree or select a sub-tree:

Use the file tree

build.gradle

// Iterate over the contents of a tree
tree.each {File file ->
    println file
}
// Filter a tree
FileTree filtered = tree.matching {
    include 'org/gradle/api/**'
}
// Add trees together
FileTree sum = tree + fileTree(dir: 'src/test')
// Visit the elements of the tree
tree.visit {element ->
    println "$element.relativePath => $element.file"
}  

Use the contents of the archive as a file tree

You can use the contents of an archive, such as a ZIP or TAR file, as a file tree. Y ou can do this by using the Project.zipTree() or Project.tarTree() approach. T hese methods return a FileTree instance that you can use just like any other file tree or collection of files. For example, you can use it to expand an archive by copying content, or to merge some archives into another archive.

Use the archive as a file tree

build.gradle

// Create a ZIP file tree using path
FileTree zip = zipTree('someFile.zip')
// Create a TAR file tree using path
FileTree tar = tarTree('someFile.tar')
//tar tree attempts to guess the compression based on the file extension
//however if you must specify the compression explicitly you can:
FileTree someTar = tarTree(resources.gzip('someTar.ext'))  

Specify a set of input files

Many objects in Gradle have properties that accept a set of input files. F or example, the JavaCompile task has a source property that defines the source code file to compile. Y ou can set this property using any type of object supported by the files() method shown above. T his means that you can set this property by, for example, File, String, Collection, FileCollection object, or even a closure. Here are some examples:

Specify a set of files

build.gradle

// Use a File object to specify the source directory
compile {
    source = file('src/main/java')
}
// Use a String path to specify the source directory
compile {
    source = 'src/main/java'
}
// Use a collection to specify multiple source directories
compile {
    source = ['src/main/java', '../shared/java']
}
// Use a FileCollection (or FileTree in this case) to specify the source files
compile {
    source = fileTree(dir: 'src/main/java').matching { include 'org/gradle/api/**' }
}
// Using a closure to specify the source files.
compile {
    source = {
        // Use the contents of each zip file in the src dir
        file('src').listFiles().findAll {it.name.endsWith('.zip')}.collect { zipTree(it) }
    }
}  

Typically, there is a method with the same name as the property, which you can append to this collection of files. Further, this method accepts any type of argument supported by the files() method.

Specify a set of files

build.gradle

compile {
    // Add some source directories use String paths
    source 'src/main/java', 'src/main/groovy'
    // Add a source directory using a File object
    source file('../shared/java')
    // Add some source directories using a closure
    source { file('src/test/').listFiles() }
}  

Copy the file

You can use the Copy task to copy files. The replication task is very flexible and allows you to do things like filter the contents of the file you want to copy, or map the name of the file.

To use the Copy task, you must provide the source files and destination directories for replication. Y ou can also specify how to convert a file when you copy it. Y ou can do this with a replication specification. A replication specification is represented by the CopySpec interface. T he Copy task implements this interface. You can specify the source file using the CopySpec.from() method and the target directory using the CopySpec.into() method.

Copy the file with the copy task

build.gradle

task copyTask(type: Copy) {
    from 'src/main/webapp'
    into 'build/explodedWar'
}  

The from() method accepts any parameters that are the same as the files() method. W hen parameters are resolved to a directory, all files in that directory (without the directory itself) are recursively copied to the target directory. W hen parameters are resolved to a file, the file is copied to the target directory. W hen an argument is resolved to a file that does not exist, the argument is ignored. I f the parameter is a task, the output file of the task (that is, the file created by the task) is copied and the task is automatically added as a dependency of the Copy task. T he into() method accepts any parameters that are the same as the files() method. Here's another example:

Example 16.11. Specify the source file and destination directory for the replication task

build.gradle

task anotherCopyTask(type: Copy) {
    // Copy everything under src/main/webapp
    from 'src/main/webapp'
    // Copy a single file
    from 'src/staging/index.html'
    // Copy the output of a task
    from copyTask
    // Copy the output of a task using Task outputs explicitly.
    from copyTaskWithPatterns.outputs
    // Copy the contents of a Zip file
    from zipTree('src/main/assets.zip')
    // Determine the destination directory later
    into { getDestDir() }
}  

You can use Ant-style include or exclude modes, or use a closure to select the files to copy:

Select the file to copy

build.gradle

task copyTaskWithPatterns(type: Copy) {
    from 'src/main/webapp'
    into 'build/explodedWar'
    include '**/*.html'
    include '**/*.jsp'
    exclude { details -> details.file.name.endsWith('.html') && details.file.text.contains('staging') }
}  

In addition, you can use the Project.copy() method to copy files. I t works the same way as a task, although it has some major limitations. First, copy() cannot do incremental operations.

Copy the file using the copy() method, which does not have an up-to-date status check

build.gradle

task copyMethod << {
    copy {
        from 'src/main/webapp'
        into 'build/explodedWar'
        include '**/*.html'
        include '**/*.jsp'
    }
}  

Second, when a task is used as a replication source (that is, as an argument to from(), the copy() method cannot establish a task dependency because it is a method, not a task. Therefore, if you use the copy() method in the action of a task, you must explicitly declare all inputs and outputs to get the correct behavior.

Copy files using the copy() method with the latest status checks

build.gradle

task copyMethodWithExplicitDependencies{
    inputs.file copyTask // up-to-date check for inputs, plus add copyTask as dependency
    outputs.dir 'some-dir' // up-to-date check for outputs
    doLast{
        copy {
            // Copy the output of copyTask
            from copyTask
            into 'some-dir'
        }
    }
}  

Where possible, it's best to use the Copy task because it supports incremental builds and task dependency reasoning without your extra effort. T he copy() method can copy files as part of a task's execution. T hat is, this copy() method is intended for custom tasks when file replication is required as part of its functionality. In this case, the custom task should fully declare the inputs/outputs related to the replication operation.

Rename the file

Rename the copied file

build.gradle

task rename(type: Copy) {
    from 'src/main/webapp'
    into 'build/explodedWar'
    // Use a closure to map the file name
    rename { String fileName ->
        fileName.replace('-staging-', '')
    }
    // Use a regular expression to map the file name
    rename '(.+)-staging-(.+)', '$1$2'
    rename(/(.+)-staging-(.+)/, '$1$2')
}  

Filter the file

Filter the files to copy

build.gradle

import org.apache.tools.ant.filters.FixCrLfFilter
import org.apache.tools.ant.filters.ReplaceTokens
task filter(type: Copy) {
    from 'src/main/webapp'
    into 'build/explodedWar'
    // Substitute property references in files
    expand(copyright: '2009', version: '2.3.1')
    expand(project.properties)
    // Use some of the filters provided by Ant
    filter(FixCrLfFilter)
    filter(ReplaceTokens, tokens: [copyright: '2009', version: '2.3.1'])
    // Use a closure to filter each line
    filter { String line ->
        "[$line]"
    }
}  

Use the CopySpec class

The replication specification is used to organize a hierarchy. A replication specification inherits its target path, including patterns, exclusion patterns, replication operations, name maps, and filters.

Nested replication specifications

build.gradle

task nestedSpecs(type: Copy) {
    into 'build/explodedWar'
    exclude '**/*staging*'
    from('src/dist') {
        include '**/*.html'
    }
    into('libs') {
        from configurations.runtime
    }
}  

Use sync tasks

The Sync task inherits the Copy task. W hen it executes, it copies the source file to the target directory, and then removes all files from the target directory that are not copied by it. This can be used to do things like install your application, create an exploded copy of your archive, or maintain a copy of the project's dependencies.

Here is an example of maintaining a copy that is dependent on the runtime of an item in the build/libs directory.

Copy dependencies with synchronous tasks

build.gradle

task libs(type: Sync) {
    from configurations.runtime
    into "$buildDir/libs"
}  

Create an archive

A project can have as many JAR files as you want. Y ou can also add WAR, ZIP, and TAG files to your project. U se a variety of archiving tasks to create the following archives: Zip, Tar, Jar, War, and Ear. They all work the same way, so let's see how to create a ZIP file.

Create a ZIP file

build.gradle

apply plugin: 'java'
task zip(type: Zip) {
    from 'src/dist'
    into('libs') {
        from configurations.runtime
    }
}  

Why java plug-ins?

The Java plug-in adds some default values to the archive task. I f you prefer, you can use archiving tasks without the need for a Java plug-in. Y ou need to provide some values to attached properties.

Archive tasks work the same way as Copy tasks and implement the same CopySpec interface. A s with copy tasks, you need to specify the entered files using the from() method, and you can choose whether to specify the final location in the archive through the into() method. You can filter the contents of a file, rename the file, and do other things you can through a copy specification.

Archive naming

The default name for the generated archive is projectName-version.type. For example:

Create a ZIP file

build.gradle

apply plugin: 'java'
version = 1.0
task myZip(type: Zip) {
    from 'somedir'
}
println myZip.archiveName
println relativePath(myZip.destinationDir)
println relativePath(myZip.archivePath)  

The output of gradle -q myZip

> gradle -q myZip
zipProject-1.0.zip
build/distributions
build/distributions/zipProject-1.0.zip  

It adds a ZIP archive task called myZip, which results in a ZIP file zipProject 1.0 .zip. I t is important to distinguish between the name of the archive task and the name of the archive file generated by the archive task. T he default name of the archive can be changed by the project property archiveBaseName. You can also change the name of the archive at any later time.

There are many properties you can set in the archive task. T hey are listed in Table 16.1 below, Archive Task-Name Properties. You can, say, change the name of the archive:

Configure the archive task - customize the archive name

build.gradle

apply plugin: 'java'
version = 1.0
task myZip(type: Zip) {
    from 'somedir'
    baseName = 'customName'
}
println myZip.archiveName  

The output of gradle -q myZip

gradle -q myZip customName-1.0.zip

You can further customize the archive name:

Configure the archive task - appendix and classifier

build.gradle


apply plugin: 'java'
archivesBaseName = 'gradle'
version = 1.0
task myZip(type: Zip) {
    appendix = 'wrapper'
    classifier = 'src'
    from 'somedir'
}
println myZip.archiveName  

The output of gradle -q myZip

> gradle -q myZip
gradle-wrapper-1.0-src.zip  

Table 16.1. Archive task - Name the property

The name of the property Type The default Describe
archiveName String extension

If any of these properties are empty, the later - not be added to the name.

The base file name of the generated archive
archivePath File archiveName The absolute path to the generated archive.
destinationDir File Depends on the type of archive. J AR and WAR packages are generated project.buildDir ZIP files and TAR files are generated project.buildDir A directory that holds the generated archive
baseName String project.name The basic name part of the archive's name.
appendix String null The appendix section in the name of the archive.
version String project.version The version portion of the archive's name.
classifier String null The classification part of the name of the archive.
extension String Depending on the type of archive used for TAR files, it can be the following compression type: tbz2 . The extended name section of the archive's name.

Share content between multiple archives

You can use the Project.copySpec() method to share content between archives.

You often want to publish an archive so that you can use it from another project.