Tar task follows symbolic links and duplicates files

I have a situation where I have several symbolic links to files within a directory. When I define a task using the ‘Tar Type’ to tar up the directory it actually follows the symlink and duplicates the file. How can override this so that the links are maintained.

When asking this kind of question, please always state the OS and Gradle version.

No problem! We are using gradle-1.0-milestone-6 on CenOS 5.7. Is there an option to tell gradle not to follow the links like the ‘followsymlinks’ attribute does for ant? If not, what would you suggest for a workaround?

There is currently no such option. The usual workaround is to use the Ant task.

Unfortunately, the ant task does not support symlinks, too. Is there any other workaround besides using native tar implementations?

Considering that Java7 have support for symbolic links what is the prospect of resolving that matter so that it at least works with the latest version of Java?

Internally, Gradle already has sophisticated symlink support, using NIO2 where appropriate. This story hasn’t bubbled up yet, but I’ll export it to JIRA so that it gets some attention. (A good pull request would help to get this feature implemented sooner rather than later.)

I hear what you are saying and will see if we can help with that.

As we normally run with Java6 I went through to revalidate if it was still happening on Java7 and it still doesn’t operate as expected by the normal “tar” behaviour.

In this respect I am guessing that a jar should behave as a Jar pre-Java6 (unless overridden) meaning symbolic links are followed while a Tar should behave like “tar”, ie. not follow and resolve a symbolic links while extracting or creating.

Let me know what the Jira is and I’ll have a look at it.

It’s GRADLE-2685 (you should see it at the top of the screen).

The behavior you are requesting will need to be implemented explicitly. Because it isn’t currently implemented, it doesn’t matter which Java version you run.

I have a solution but it is very ugly… Gradle or groovy doesn’t yet provide full access to nio so we can’t use some of the nicer methods. We can still detect sym links using apache’s trick.

Using OS tools, we unpack the tar file into a directory We walk the files in the directory and replace the link with its concrete source. At the end we have unpacked tar with no symlinks. Larger on repack but ant/maven/gradle will be able to unpack it properly.

This isn’t ideal. In my case, I’m automating repacking of a vendor kit into a maven accessible dependency and I discovered that neither ant, nor maven not gradle handle sym links in tar files.

/**

  • Unpack tar file a replace symlinks with concrete copies of files

  • @param src

  • @param dest

  • @return

*/ def osUnpackAndResolveSymlinks(File src, File dest) {

dest.mkdirs()

def opts = “xvf”

// Determine Compression

if (src =~ /.*tar(.gz)?/) {

opts = “${opts}z”

}

// Process Unpack

def command=“tar ${opts} ${src.getAbsoluteFile()}”

def proc = command.execute(null, dest)

StringBuffer output = new StringBuffer()

proc.consumeProcessOutput(output,output)

proc.waitFor()

if (proc.exitValue()) {

throw new GradleScriptException(“Failed to unpack ${src} to ${dest}. Exit value: ${proc.exitValue()},${output.toString()}”)

}

replaceSymLinks(dest) }

/**

  • Replace Symlinks with Concrete copies of files

  • @param base

  • @return

*/ def replaceSymLinks(File base) {

FileTree tree = fileTree(dir: base.getAbsolutePath())

tree.each { File file ->

println “Check ${file}”

if (isSymlink(file)) {

println “** copy ${file.getCanonicalPath()} to ${file.getAbsolutePath()}”

File src = new File(file.getCanonicalPath())

File dest = new File(file.getAbsolutePath())

println “** ${dest.getAbsolutePath()} ${dest.getCanonicalPath()}”

dest.delete()

copy { from(src)

into(src.getParentFile())

rename { String fileName -> fileName = dest.getName()}

}

}

} }

/**

*/ public static boolean isSymlink(File file) throws IOException {

if (file == null)

throw new NullPointerException(“File must not be null”);

File canon;

if (file.getParent() == null) {

canon = file;

} else {

File canonDir = file.getParentFile().getCanonicalFile();

canon = new File(canonDir, file.getName());

}

return !canon.getCanonicalFile().equals(canon.getAbsoluteFile()); } def isTarFile(def name) {

return name =~ /.*tar(.gz)?/ } def isZipFile(def name) {

return name =~ /.*zip$/