From d63af7844d206b05b558a18976aae40583b05b53 Mon Sep 17 00:00:00 2001 From: Paolo Di Tommaso Date: Sun, 11 Jun 2023 21:37:26 +0200 Subject: [PATCH] Fix matching symlink files 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 --- .../src/test/groovy/nextflow/NextflowTest.groovy | 10 +++++++--- .../src/main/nextflow/file/FileHelper.groovy | 15 ++++++++------- .../src/test/nextflow/file/FileHelperTest.groovy | 16 ++++++++++++---- 3 files changed, 27 insertions(+), 14 deletions(-) diff --git a/modules/nextflow/src/test/groovy/nextflow/NextflowTest.groovy b/modules/nextflow/src/test/groovy/nextflow/NextflowTest.groovy index 5452a4e41f..1ecee5f052 100644 --- a/modules/nextflow/src/test/groovy/nextflow/NextflowTest.groovy +++ b/modules/nextflow/src/test/groovy/nextflow/NextflowTest.groovy @@ -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) @@ -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') @@ -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() diff --git a/modules/nf-commons/src/main/nextflow/file/FileHelper.groovy b/modules/nf-commons/src/main/nextflow/file/FileHelper.groovy index ae2774a959..a846a86565 100644 --- a/modules/nf-commons/src/main/nextflow/file/FileHelper.groovy +++ b/modules/nf-commons/src/main/nextflow/file/FileHelper.groovy @@ -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 @@ -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) } diff --git a/modules/nf-commons/src/test/nextflow/file/FileHelperTest.groovy b/modules/nf-commons/src/test/nextflow/file/FileHelperTest.groovy index 74ae49c029..a6a2542d0c 100644 --- a/modules/nf-commons/src/test/nextflow/file/FileHelperTest.groovy +++ b/modules/nf-commons/src/test/nextflow/file/FileHelperTest.groovy @@ -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()