Skip to content

Commit

Permalink
Fix matching symlink files
Browse files Browse the repository at this point in the history
This commit fixes that matches of symlink files when the output
followLinks option is set to false.

Prior this fix symlinks where discarded when glob pattern was
matching the name of the link file.

However the correct behavior is that the file should be included
in the result set as file the represents the symlink.

Signed-off-by: Paolo Di Tommaso <[email protected]>
  • Loading branch information
pditommaso committed Jun 11, 2023
1 parent 4da0442 commit d63af78
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 14 deletions.
10 changes: 7 additions & 3 deletions modules/nextflow/src/test/groovy/nextflow/NextflowTest.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ class NextflowTest extends Specification {
when:
result = Nextflow.files("$folder/**.fa", relative: true, followLinks: false)
then:
result.collect { it.toString() } .sort() == ['dir1/dir2/file4.fa', 'file2.fa']
result.collect { it.toString() } .sort() == ['dir1/dir2/file4.fa', 'file2.fa', 'file_link.fa']

when:
result = Nextflow.files("$folder/**.fa", relative: true, maxDepth: 1)
Expand All @@ -227,8 +227,10 @@ class NextflowTest extends Specification {
then:
result.collect { it.toString() } .sort() == ['dir1/dir2/file4.fa',
'dir1/file3.txt',
'dir_link',
'file1.txt',
'file2.fa']
'file2.fa',
'file_link.fa']

when:
result = Nextflow.files("$folder/**", relative: true, type:'dir')
Expand All @@ -252,8 +254,10 @@ class NextflowTest extends Specification {
'dir1/dir2',
'dir1/dir2/file4.fa',
'dir1/file3.txt',
'dir_link',
'file1.txt',
'file2.fa']
'file2.fa',
'file_link.fa']

cleanup:
folder?.deleteDir()
Expand Down
15 changes: 8 additions & 7 deletions modules/nf-commons/src/main/nextflow/file/FileHelper.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -795,14 +795,15 @@ class FileHelper {
assert filePattern
assert action

final type = options?.type ?: 'any'
final walkOptions = options?.followLinks == false ? EnumSet.noneOf(FileVisitOption.class) : EnumSet.of(FileVisitOption.FOLLOW_LINKS)
final int maxDepth = getMaxDepth(options?.maxDepth, filePattern)
final includeHidden = options?.hidden as Boolean ?: filePattern.startsWith('.')
if( options==null ) options = Map.of()
final type = options.type ?: 'any'
final walkOptions = options.followLinks == false ? EnumSet.noneOf(FileVisitOption.class) : EnumSet.of(FileVisitOption.FOLLOW_LINKS)
final int maxDepth = getMaxDepth(options.maxDepth, filePattern)
final includeHidden = options.hidden as Boolean ?: filePattern.startsWith('.')
final includeDir = type in ['dir','any']
final includeFile = type in ['file','any']
final syntax = options?.syntax ?: 'glob'
final relative = options?.relative == true
final syntax = options.syntax ?: 'glob'
final relative = options.relative == true

final matcher = getPathMatcherFor("$syntax:${filePattern}", folder.fileSystem)
final singleParam = action.getMaximumNumberOfParameters() == 1
Expand All @@ -828,7 +829,7 @@ class FileHelper {
final path = folder.relativize(fullPath)
log.trace "visitFiles > file=$path; includeFile=$includeFile; matches=${matcher.matches(path)}; isRegularFile=${attrs.isRegularFile()}"

if (includeFile && matcher.matches(path) && attrs.isRegularFile() && (includeHidden || !isHidden(fullPath))) {
if (includeFile && matcher.matches(path) && (attrs.isRegularFile() || (options.followLinks == false && attrs.isSymbolicLink())) && (includeHidden || !isHidden(fullPath))) {
def result = relative ? path : fullPath
singleParam ? action.call(result) : action.call(result,attrs)
}
Expand Down
16 changes: 12 additions & 4 deletions modules/nf-commons/src/test/nextflow/file/FileHelperTest.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -463,18 +463,26 @@ class FileHelperTest extends Specification {
Files.createSymbolicLink(
folder.resolve('file4.fa'),
folder.resolve('file3.bam'))
and:
folder.resolve('subdir_real').mkdirs()
folder.resolve('subdir_real/file5.fa').text = 'file 5'
and:
Files.createSymbolicLink(
folder.resolve('subdir_link'),
folder.resolve('subdir_real'))

when:
def result = []
FileHelper.visitFiles(folder, '*.fa', relative: true) { result << it.toString() }
FileHelper.visitFiles(folder, '**.fa', relative: true) { result << it.toString() }
then:
result.sort() == ['file2.fa','file4.fa']
result.sort() == ['file2.fa','file4.fa','subdir_link/file5.fa', 'subdir_real/file5.fa']

when:
result = []
FileHelper.visitFiles(folder, '*.fa', relative: true, followLinks: false) { result << it.toString() }
FileHelper.visitFiles(folder, '**.fa', relative: true, followLinks: false) { result << it.toString() }
then:
result.sort() == ['file2.fa']
// note: "file4.fa" is included in the result because it matches the name of the symlink file
result.sort() == ['file2.fa','file4.fa','subdir_real/file5.fa']

cleanup:
folder?.deleteDir()
Expand Down

0 comments on commit d63af78

Please sign in to comment.