Gradle plugin copy directory tree with files from resources

I’m totally new to gradle and i’m trying to create a plugin.
I added a directory tree with files from my resources plugin and create a scaffold task to copy that tree in my project, i tried several methods from FileUtils (copyDirectory) but with no success.
Any idea to achieve this?


How about creating Copy type task?

task scaffold(type: Copy) {
  from fileTree(templateDir)
  into scaffoldDestDir

I tried this but got a error when i try to get my templateDir from the JAR plugin

i tried to get it using :
File input = new File ( getClass().getResource( “templateDir” ).getPath() )
In this case input is ok but when i try to copy it i get a malformed url (looks like because it’s in a jar file)

and i also tried :
InputStream input = getClass().getResourceAsStream( “templateDir” )
In this case input is always null

Why do you use Class#getResource(String) or Class#getResourceAsStream(String)?

I think I don’t understand the situation you are in. Because Class#getResource(String) is merely used in build.gradle.

Please give me more information on what you want to do
(ex. build script(build.gradle), or plugin’s project structure).

I’m developping a plugin for gradle, i’m not working in build.gradle file :wink:
I don’t have sources right now (i’m at work) but i will post more details tonight

so here are more details :

my plugin sources structure :


here is my Scaffold task class :

class Scaffold extends DefaultTask
	public Scaffold()
		group = "scaffold"
		description = ""

	public void generateProject()
		File input = new File( getClass().getResource( "/scaffold" ).getPath(  ) )
		File output = new File( "${ project.projectDir }/app/" )

		project.copy {
			from input
			into output

So my goal here is to copy all scaffold directory from resources to my project, to test this i build my jar plugin and upload into to mavenlocal and then i create a new gradle project applying my plugin and running scaffold task.
Unfortunalty i got this error :

> Could not normalize path for file 'W:\Projects\testflair\file:\C:\Users\SamYStudiO\.m2\repository\com\flair\flair\0.1-beta\flair-0.1-beta.jar!\scaffold'.

I don’t know why my project path is concat with it???

I also tried to zip my scaffold directory in ordder to extract it afterward but when i try to getResourceAsStream from it, i get a null object though zip is included in my plugin

public void generateProject()
	InputStream input = getClass().getResourceAsStream( "/" )
	ZipInputStream zip = new ZipInputStream( input )

> in is null

I don’t know what i’m doing wrong here, any idea?

  1. Class#getResource(String) can retrieve only files which belongs to the same package (for example if it is called from class, only the files in src/main/java/com/foo can be retrieved). So if you want to get files from resources, use ClassLoader#getResource(String).

  2. The purpose of your task is to copy files, so it’s better to use Copy class, which Gradle provides in default.

For example I wrote a sample plugin, which copies files in scaffold directory located in resources.

package org.sample;

import org.gradle.api.Action;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.tasks.Copy;

public class ScaffoldPlugin implements Plugin<Project> {

    public static final String PLUGIN_TASK_NAME = "scaffold";

    private final ClassLoader loader = getClass().getClassLoader();

    public void apply(final Project project) {
        project.getTasks().create(PLUGIN_TASK_NAME, Copy.class, new Action<Copy>() {
            public void execute(Copy copy) {
                // in the case of buildSrc dir
                copy.into(String.format("%s/scaffold", project.getBuildDir()));
                // TODO: in the case of plugin is provided as jar.

And the execution result of this task is as follows.

$ gradle clean scaffold
:buildSrc:compileJava UP-TO-DATE
:buildSrc:compileGroovy UP-TO-DATE
:buildSrc:processResources UP-TO-DATE
:buildSrc:classes UP-TO-DATE
:buildSrc:jar UP-TO-DATE
:buildSrc:assemble UP-TO-DATE
:buildSrc:compileTestJava UP-TO-DATE
:buildSrc:compileTestGroovy UP-TO-DATE
:buildSrc:processTestResources UP-TO-DATE
:buildSrc:testClasses UP-TO-DATE
:buildSrc:test UP-TO-DATE
:buildSrc:check UP-TO-DATE
:buildSrc:build UP-TO-DATE


Total time: 0.956 secs
$ $ tree build
└── scaffold
    ├── assets
    │   ├── create.html
    │   ├── css
    │   │   └── page.css
    │   ├── js
    │   │   └── page.js
    │   ├── list.html
    │   └── update.html
    └── sql

5 directories, 5 files

Please note that this plugin is written in buildSrc project, so I don’t consider the case of jar distribution as a public plugin.

Works great with buildSrc thank you!
And what about a jar distribution? What do i need to change to make it work as well?

Thanks again!

I think that the easiest way to distribute your plugin as jar is to zip scaffold directory to zip before publish.

And use Project#zipTree method at from's argument. For example zip scaffold directory into before distribution, then change code of copy.from to …


The method zipTree works as nearly same as fileTree. The difference is zipTree takes zip file as argument, while fileTree takes directory as argument.

The following code is to judge the plugin is buildSrc project or jar distribution.


If this code returns false, it is buildSrc project, and if true it’s jar distribution. So if it’s buildSrc project, use fileTree and it’s jar distribution use zipTree.

Of course in buildSrc project the way to zipping scaffold directory is useful.

Sorry to ask you again but when i try this :

copy.from( project.zipTree( loader.getResource( "" ).getPath(  ) ) ); // need to add getPath() to make it works

i got this error :

Cannot expand ZIP 'C:\Users\SamYStudiO\.m2\repository\com\flair\flair\0.1-beta\flair-0.1-beta.jar!\' as it does not exist.

I checked several times, is at my jar root but looks it can’t find it.

Ok i made it work using :

copy.from( project.zipTree( getClass().getProtectionDomain().getCodeSource().getLocation().toExternalForm() )

Dunno if its the right way but it works :smile:
Thanks again!