From a6a77ee19ae15393296c079ab9ddb3f976c4ea7f Mon Sep 17 00:00:00 2001 From: Simon Brandhof Date: Fri, 12 Apr 2013 12:12:16 +0200 Subject: [PATCH] SONARPLUGINS-2775 "sonar.libraries" should allow to specify folders --- .../org/sonar/runner/batch/FilePattern.java | 131 ------------------ .../runner/batch/ProjectReactorBuilder.java | 57 ++++++-- .../sonar/runner/batch/FilePatternTest.java | 81 ----------- .../batch/ProjectReactorBuilderTest.java | 15 +- .../lib/Fake.class | 3 + .../sonar-project.properties | 7 + .../sources/Fake.java | 3 + 7 files changed, 70 insertions(+), 227 deletions(-) delete mode 100644 sonar-runner-batch/src/main/java/org/sonar/runner/batch/FilePattern.java delete mode 100644 sonar-runner-batch/src/test/java/org/sonar/runner/batch/FilePatternTest.java create mode 100644 sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/simple-project-with-lib-dir/lib/Fake.class create mode 100644 sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/simple-project-with-lib-dir/sonar-project.properties create mode 100644 sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/simple-project-with-lib-dir/sources/Fake.java diff --git a/sonar-runner-batch/src/main/java/org/sonar/runner/batch/FilePattern.java b/sonar-runner-batch/src/main/java/org/sonar/runner/batch/FilePattern.java deleted file mode 100644 index ec7e339..0000000 --- a/sonar-runner-batch/src/main/java/org/sonar/runner/batch/FilePattern.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Sonar Runner - Batch - * Copyright (C) 2011 SonarSource - * dev@sonar.codehaus.org - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 - */ -package org.sonar.runner.batch; - -import com.google.common.base.Joiner; -import com.google.common.collect.Lists; -import org.apache.commons.io.FileUtils; -import org.apache.commons.io.FilenameUtils; -import org.apache.commons.io.filefilter.AndFileFilter; -import org.apache.commons.io.filefilter.FileFileFilter; -import org.apache.commons.io.filefilter.IOFileFilter; -import org.apache.commons.io.filefilter.TrueFileFilter; -import org.apache.commons.lang.StringUtils; -import org.sonar.api.utils.WildcardPattern; - -import java.io.File; -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -class FilePattern { - - Collection listFiles(File basedir, String pattern) { - File absoluteBasedir = absoluteBasedir(pattern); - IOFileFilter filter; - if (absoluteBasedir != null) { - filter = new AbsolutePathFilter(WildcardPattern.create(pattern, "/")); - basedir = absoluteBasedir.isFile() ? absoluteBasedir.getParentFile() : absoluteBasedir; - } else { - filter = new RelativePathFilter(basedir, WildcardPattern.create(pattern, "/")); - } - if (basedir.isDirectory() && basedir.exists()) { - return FileUtils.listFiles(basedir, new AndFileFilter(FileFileFilter.FILE, filter), TrueFileFilter.TRUE); - } - return Collections.emptyList(); - } - - - private File absoluteBasedir(String pattern) { - File absoluteBasedir = null; - int wildcard = StringUtils.indexOfAny(pattern, new char[]{'*', '?'}); - - if (wildcard == 0) { - // relative path - - } else if (wildcard == -1) { - absoluteBasedir = new File(pattern); - - } else { - int lastSlashBeforeWildcard = pattern.substring(0, wildcard).lastIndexOf("/"); - if (lastSlashBeforeWildcard >= 0) { - String path = pattern.substring(0, lastSlashBeforeWildcard); - absoluteBasedir = new File(path); - } - } - if (absoluteBasedir != null && !absoluteBasedir.isAbsolute()) { - absoluteBasedir = null; - } - return absoluteBasedir; - } - - private static class RelativePathFilter implements IOFileFilter { - private final File basedir; - private final WildcardPattern pattern; - - private RelativePathFilter(File basedir, WildcardPattern pattern) { - this.basedir = basedir; - this.pattern = pattern; - } - - public boolean accept(File file) { - return pattern.match(relativePath(file)); - } - - public boolean accept(File file, String filename) { - return true; - } - - String relativePath(File file) { - List stack = Lists.newArrayList(); - String path = FilenameUtils.normalize(file.getAbsolutePath()); - File cursor = new File(path); - while (cursor != null) { - if (containsFile(cursor)) { - return Joiner.on("/").join(stack); - } - stack.add(0, cursor.getName()); - cursor = cursor.getParentFile(); - } - return null; - } - - private boolean containsFile(File cursor) { - return FilenameUtils.equalsNormalizedOnSystem(basedir.getAbsolutePath(), cursor.getAbsolutePath()); - } - - } - - private static class AbsolutePathFilter implements IOFileFilter { - private final WildcardPattern pattern; - - private AbsolutePathFilter(WildcardPattern pattern) { - this.pattern = pattern; - } - - public boolean accept(File file) { - return pattern.match(FilenameUtils.separatorsToUnix(file.getAbsolutePath())); - } - - public boolean accept(File file, String filename) { - return true; - } - } -} diff --git a/sonar-runner-batch/src/main/java/org/sonar/runner/batch/ProjectReactorBuilder.java b/sonar-runner-batch/src/main/java/org/sonar/runner/batch/ProjectReactorBuilder.java index ab82f6a..53fb313 100644 --- a/sonar-runner-batch/src/main/java/org/sonar/runner/batch/ProjectReactorBuilder.java +++ b/sonar-runner-batch/src/main/java/org/sonar/runner/batch/ProjectReactorBuilder.java @@ -22,6 +22,7 @@ package org.sonar.runner.batch; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.Lists; import org.apache.commons.io.IOUtils; +import org.apache.commons.io.filefilter.*; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -29,6 +30,7 @@ import org.sonar.api.batch.bootstrap.ProjectDefinition; import org.sonar.api.batch.bootstrap.ProjectReactor; import java.io.File; +import java.io.FileFilter; import java.io.FileInputStream; import java.io.IOException; import java.util.*; @@ -136,8 +138,8 @@ class ProjectReactorBuilder { } ProjectDefinition definition = ProjectDefinition.create().setProperties(properties) - .setBaseDir(baseDir) - .setWorkDir(workDir); + .setBaseDir(baseDir) + .setWorkDir(workDir); return definition; } @@ -319,10 +321,10 @@ class ProjectReactorBuilder { // We need to resolve patterns that may have been used in "sonar.libraries" for (String pattern : Utils.getListFromProperty(props, PROPERTY_LIBRARIES)) { - Collection files = getLibraries(baseDir, pattern); - if (files.isEmpty()) { + File[] files = getLibraries(baseDir, pattern); + if (files == null || files.length == 0) { LOG.error("Invalid value of " + PROPERTY_LIBRARIES + " for " + projectId); - throw new IllegalStateException("No file matching pattern \"" + pattern + "\" in directory \"" + baseDir + "\""); + throw new IllegalStateException("No files nor directories matching '" + pattern + "' in directory " + baseDir); } } @@ -379,8 +381,8 @@ class ProjectReactorBuilder { File sourceFolder = getFileFromPath(path, project.getBaseDir()); if (sourceFolder.isDirectory()) { LOG.warn("/!\\ A multi-module project can't have source folders, so '{}' won't be used for the analysis. " + - "If you want to analyse files of this folder, you should create another sub-module and move them inside it.", - sourceFolder.toString()); + "If you want to analyse files of this folder, you should create another sub-module and move them inside it.", + sourceFolder.toString()); } } @@ -425,8 +427,8 @@ class ProjectReactorBuilder { for (Map.Entry entry : parentProps.entrySet()) { String key = (String) entry.getKey(); if (!childProps.containsKey(key) - && !NON_HERITED_PROPERTIES_FOR_CHILD.contains(key) - && !isKeyPrefixedByModuleId(key, moduleIds)) { + && !NON_HERITED_PROPERTIES_FOR_CHILD.contains(key) + && !isKeyPrefixedByModuleId(key, moduleIds)) { childProps.put(entry.getKey(), entry.getValue()); } } @@ -462,7 +464,7 @@ class ProjectReactorBuilder { if (!sourceFolder.isDirectory()) { LOG.error("Invalid value of " + propName + " for " + moduleRef); throw new IllegalStateException("The folder '" + path + "' does not exist for '" + moduleRef + - "' (base directory = " + baseDir.getAbsolutePath() + ")"); + "' (base directory = " + baseDir.getAbsolutePath() + ")"); } } @@ -472,8 +474,39 @@ class ProjectReactorBuilder { * Returns files matching specified pattern. */ @VisibleForTesting - protected static Collection getLibraries(File baseDir, String pattern) { - return new FilePattern().listFiles(baseDir, pattern); + protected static File[] getLibraries(File baseDir, String pattern) { + final int i = Math.max(pattern.lastIndexOf('/'), pattern.lastIndexOf('\\')); + final String dirPath, filePattern; + if (i == -1) { + dirPath = "."; + filePattern = pattern; + } else { + dirPath = pattern.substring(0, i); + filePattern = pattern.substring(i + 1); + } + List filters = new ArrayList(); + if (pattern.indexOf("*")>=0) { + filters.add(FileFileFilter.FILE); + } + filters.add(new WildcardFileFilter(filePattern)); + File dir = resolvePath(baseDir, dirPath); + File[] files = dir.listFiles((FileFilter) new AndFileFilter(filters)); + if (files == null) { + files = new File[0]; + } + return files; + } + + private static File resolvePath(File baseDir, String path) { + File file = new File(path); + if (!file.isAbsolute()) { + try { + file = new File(baseDir, path).getCanonicalFile(); + } catch (IOException e) { + throw new IllegalStateException("Unable to resolve path \"" + path + "\"", e); + } + } + return file; } /** diff --git a/sonar-runner-batch/src/test/java/org/sonar/runner/batch/FilePatternTest.java b/sonar-runner-batch/src/test/java/org/sonar/runner/batch/FilePatternTest.java deleted file mode 100644 index 64a366d..0000000 --- a/sonar-runner-batch/src/test/java/org/sonar/runner/batch/FilePatternTest.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Sonar Runner - Batch - * Copyright (C) 2011 SonarSource - * dev@sonar.codehaus.org - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 - */ -package org.sonar.runner.batch; - -import org.apache.commons.io.FilenameUtils; -import org.apache.commons.lang.StringUtils; -import org.junit.Before; -import org.junit.Test; - -import java.io.File; -import java.io.IOException; -import java.util.Collection; - -import static org.fest.assertions.Assertions.assertThat; - -public class FilePatternTest { - - File basedir, rootFile, subFile; - String basePath; - - @Before - public void init() throws Exception { - rootFile = new File(getClass().getResource("/org/sonar/runner/batch/FilePatternTest/root.txt").toURI()); - subFile = new File(getClass().getResource("/org/sonar/runner/batch/FilePatternTest/subdir/subfile.txt").toURI()); - basedir = rootFile.getParentFile(); - basePath = path(basedir); - } - - @Test - public void should_list_files_by_relative_path() throws Exception { - assertThat(new FilePattern().listFiles(basedir, "subdir/*.txt")).containsOnly(subFile); - assertThat(new FilePattern().listFiles(basedir, "*.txt")).containsOnly(rootFile); - assertThat(new FilePattern().listFiles(basedir, "root.txt")).containsOnly(rootFile); - assertThat(new FilePattern().listFiles(basedir, "ro*t.txt")).containsOnly(rootFile); - assertThat(new FilePattern().listFiles(basedir, "ro?t.txt")).containsOnly(rootFile); - assertThat(new FilePattern().listFiles(basedir, "r?t.txt")).isEmpty(); - assertThat(new FilePattern().listFiles(basedir, "*")).containsOnly(rootFile); - assertThat(new FilePattern().listFiles(basedir, "**/*")).containsOnly(rootFile, subFile); - assertThat(new FilePattern().listFiles(basedir, "**/*.txt")).containsOnly(subFile, rootFile); - assertThat(new FilePattern().listFiles(basedir, "**/*.jar")).isEmpty(); - assertThat(new FilePattern().listFiles(basedir, "elsewhere/root.txt")).isEmpty(); - assertThat(new FilePattern().listFiles(basedir, "elsewhere/subfile.txt")).isEmpty(); - } - - @Test - public void should_list_files_by_absolute_path() throws Exception { - assertOnly(new FilePattern().listFiles(basedir, basePath + "/*.txt"), rootFile); - assertOnly(new FilePattern().listFiles(basedir, basePath + "/**/subdir/*"), subFile); - assertOnly(new FilePattern().listFiles(basedir, path(rootFile)), rootFile); - assertOnly(new FilePattern().listFiles(basedir, path(basedir) + "/*/subfile.txt"), subFile); - assertThat(new FilePattern().listFiles(basedir, path(basedir) + "/**/*.txt")).containsOnly(subFile, rootFile); - assertThat(new FilePattern().listFiles(basedir, path(basedir) + "/ElseWhere/**/*.txt")).isEmpty(); - assertThat(new FilePattern().listFiles(basedir, "/ElseWhere/**/*.txt")).isEmpty(); - } - - private void assertOnly(Collection files, File file) throws Exception { - assertThat(files).hasSize(1); - assertThat(files.iterator().next().getCanonicalPath()).isEqualTo(file.getCanonicalPath()); - } - - private String path(File f) throws IOException { - return FilenameUtils.separatorsToUnix(f.getCanonicalPath()); - } -} diff --git a/sonar-runner-batch/src/test/java/org/sonar/runner/batch/ProjectReactorBuilderTest.java b/sonar-runner-batch/src/test/java/org/sonar/runner/batch/ProjectReactorBuilderTest.java index 1b35422..a8db88b 100644 --- a/sonar-runner-batch/src/test/java/org/sonar/runner/batch/ProjectReactorBuilderTest.java +++ b/sonar-runner-batch/src/test/java/org/sonar/runner/batch/ProjectReactorBuilderTest.java @@ -354,12 +354,21 @@ public class ProjectReactorBuilderTest { @Test public void shouldFailIfExplicitUnmatchingLibFolder() throws IOException { thrown.expect(IllegalStateException.class); - thrown.expectMessage("No file matching pattern \"libs/*.txt\" in directory \"" + thrown.expectMessage("No files nor directories matching 'libs/*.txt' in directory " + TestUtils.getResource(this.getClass(), "simple-project-with-unexisting-lib").getAbsolutePath()); loadProjectDefinition("simple-project-with-unexisting-lib"); } + @Test + public void shouldGetLibDirectory() throws IOException { + ProjectDefinition def = loadProjectDefinition("simple-project-with-lib-dir"); + assertThat(def.getLibraries()).hasSize(1); + File libDir = new File(def.getLibraries().get(0)); + assertThat(libDir).isDirectory().exists(); + assertThat(libDir.getName()).isEqualTo("lib"); + } + @Test public void shouldFailIfExplicitUnexistingTestFolderOnModule() throws IOException { thrown.expect(IllegalStateException.class); @@ -381,8 +390,8 @@ public class ProjectReactorBuilderTest { @Test public void shouldFailIfExplicitUnmatchingLibFolderOnModule() throws IOException { thrown.expect(IllegalStateException.class); - thrown.expectMessage("No file matching pattern \"lib/*.jar\" in directory \"" - + TestUtils.getResource(this.getClass(), "multi-module-with-explicit-unexisting-lib").getAbsolutePath() + File.separator + "module1\""); + thrown.expectMessage("No files nor directories matching 'lib/*.jar' in directory " + + TestUtils.getResource(this.getClass(), "multi-module-with-explicit-unexisting-lib").getAbsolutePath() + File.separator + "module1"); loadProjectDefinition("multi-module-with-explicit-unexisting-lib"); } diff --git a/sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/simple-project-with-lib-dir/lib/Fake.class b/sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/simple-project-with-lib-dir/lib/Fake.class new file mode 100644 index 0000000..bf2c3a0 --- /dev/null +++ b/sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/simple-project-with-lib-dir/lib/Fake.class @@ -0,0 +1,3 @@ +package org.sonar.runner.batch.ProjectReactorBuilderTest.simple + +Fake \ No newline at end of file diff --git a/sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/simple-project-with-lib-dir/sonar-project.properties b/sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/simple-project-with-lib-dir/sonar-project.properties new file mode 100644 index 0000000..0cada50 --- /dev/null +++ b/sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/simple-project-with-lib-dir/sonar-project.properties @@ -0,0 +1,7 @@ +sonar.projectKey=com.foo.project +sonar.projectName=Foo Project +sonar.projectVersion=1.0-SNAPSHOT +sonar.projectDescription=Description of Foo Project + +sonar.sources=sources +sonar.libraries=lib diff --git a/sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/simple-project-with-lib-dir/sources/Fake.java b/sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/simple-project-with-lib-dir/sources/Fake.java new file mode 100644 index 0000000..bf2c3a0 --- /dev/null +++ b/sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/simple-project-with-lib-dir/sources/Fake.java @@ -0,0 +1,3 @@ +package org.sonar.runner.batch.ProjectReactorBuilderTest.simple + +Fake \ No newline at end of file -- 2.39.5