From: Julien HENRY Date: Thu, 3 Apr 2014 14:04:49 +0000 (+0200) Subject: SONARPLUGINS-3575, SONARPLUGINS-3604, SONARPLUGINS-3574 X-Git-Tag: 2.5-rc1~108 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=8d1b122be50970df9772171621d8be7fc7837b90;p=sonar-scanner-cli.git SONARPLUGINS-3575, SONARPLUGINS-3604, SONARPLUGINS-3574 * Move parsing of sonar-project.properties files to SQ Runner * Trim properties read from configuration files * Add an official property to specify a project baseDir different than current directory --- diff --git a/sonar-runner-api/src/main/java/org/sonar/runner/api/Runner.java b/sonar-runner-api/src/main/java/org/sonar/runner/api/Runner.java index 19a9506..b7583de 100644 --- a/sonar-runner-api/src/main/java/org/sonar/runner/api/Runner.java +++ b/sonar-runner-api/src/main/java/org/sonar/runner/api/Runner.java @@ -94,7 +94,7 @@ public abstract class Runner { private void initDefaultValues() { setDefaultValue(RunnerProperties.HOST_URL, "http://localhost:9000"); - setDefaultValue(InternalProperties.RUNNER_APP, "SonarRunner"); + setDefaultValue(InternalProperties.RUNNER_APP, "SonarQubeRunner"); setDefaultValue(InternalProperties.RUNNER_APP_VERSION, RunnerVersion.version()); } diff --git a/sonar-runner-dist/src/main/java/org/sonar/runner/Conf.java b/sonar-runner-dist/src/main/java/org/sonar/runner/Conf.java index 2d68c02..c76176b 100644 --- a/sonar-runner-dist/src/main/java/org/sonar/runner/Conf.java +++ b/sonar-runner-dist/src/main/java/org/sonar/runner/Conf.java @@ -25,6 +25,8 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import java.util.Map; +import java.util.Map.Entry; import java.util.Properties; class Conf { @@ -32,6 +34,9 @@ class Conf { private static final String RUNNER_SETTINGS = "runner.settings"; private static final String PROJECT_HOME = "project.home"; private static final String PROJECT_SETTINGS = "project.settings"; + private static final String PROPERTY_MODULES = "sonar.modules"; + private static final String PROPERTY_PROJECT_BASEDIR = "sonar.projectBaseDir"; + private static final String PROPERTY_PROJECT_CONFIG_FILE = "sonar.projectConfigFile"; private final Cli cli; @@ -45,13 +50,7 @@ class Conf { result.putAll(loadProjectProperties()); result.putAll(System.getProperties()); result.putAll(cli.properties()); - - if (result.containsKey(PROJECT_HOME)) { - // the real property of the Sonar Runner is "sonar.projectBaseDir" - String baseDir = result.getProperty(PROJECT_HOME); - result.remove(PROJECT_HOME); - result.put("sonar.projectBaseDir", baseDir); - } + result.remove(PROJECT_HOME); return result; } @@ -66,15 +65,97 @@ class Conf { } private Properties loadProjectProperties() throws IOException { - File settingsFile = locatePropertiesFile(cli.properties(), PROJECT_HOME, "sonar-project.properties", PROJECT_SETTINGS); - if (settingsFile != null && settingsFile.isFile() && settingsFile.exists()) { - Logs.info("Project configuration file: " + settingsFile.getAbsolutePath()); - return toProperties(settingsFile); + Properties cliProps = cli.properties(); + File rootSettingsFile = locatePropertiesFile(cliProps, cliProps.containsKey(PROPERTY_PROJECT_BASEDIR) ? PROPERTY_PROJECT_BASEDIR : PROJECT_HOME, "sonar-project.properties", + PROJECT_SETTINGS); + if (rootSettingsFile != null && rootSettingsFile.isFile() && rootSettingsFile.exists()) { + Logs.info("Project configuration file: " + rootSettingsFile.getAbsolutePath()); + Properties projectProps = new Properties(); + Properties rootProps = toProperties(rootSettingsFile); + projectProps.putAll(rootProps); + initRootProjectBaseDir(cliProps, rootProps); + loadModulesProperties(rootProps, projectProps, ""); + return projectProps; } Logs.info("Project configuration file: NONE"); return new Properties(); } + private void initRootProjectBaseDir(Properties cliProps, Properties rootProps) { + if (!cliProps.containsKey(PROPERTY_PROJECT_BASEDIR)) { + String baseDir = cliProps.getProperty(PROJECT_HOME); + rootProps.put(PROPERTY_PROJECT_BASEDIR, baseDir); + } else { + rootProps.put(PROPERTY_PROJECT_BASEDIR, cliProps.getProperty(PROPERTY_PROJECT_BASEDIR)); + } + } + + private void loadModulesProperties(Properties parentProps, Properties projectProps, String prefix) { + File parentBaseDir = new File(parentProps.getProperty(PROPERTY_PROJECT_BASEDIR)); + if (parentProps.containsKey(PROPERTY_MODULES)) { + for (String module : getListFromProperty(parentProps, PROPERTY_MODULES)) { + Properties moduleProps = extractModuleProperties(module, parentProps); + moduleProps = loadChildConfigFile(parentBaseDir, moduleProps, module); + + // the child project may have children as well + loadModulesProperties(moduleProps, projectProps, module + "."); + // and finally add this child properties to global props + merge(projectProps, prefix, module, moduleProps); + } + } + + } + + private void merge(Properties projectProps, String prefix, String module, Properties moduleProps) { + for (Map.Entry entry : moduleProps.entrySet()) { + projectProps.put(prefix + module + "." + entry.getKey(), entry.getValue()); + } + } + + private Properties loadChildConfigFile(File parentBaseDir, Properties moduleProps, String moduleId) { + final File baseDir; + if (moduleProps.containsKey(PROPERTY_PROJECT_BASEDIR)) { + baseDir = getFileFromPath(moduleProps.getProperty(PROPERTY_PROJECT_BASEDIR), parentBaseDir); + setProjectBaseDir(baseDir, moduleProps, moduleId); + try { + if (!parentBaseDir.getCanonicalFile().equals(baseDir.getCanonicalFile())) { + tryToFindAndLoadPropsFile(baseDir, moduleProps, moduleId); + } + } catch (IOException e) { + throw new IllegalStateException("Error when resolving baseDir", e); + } + } else if (moduleProps.containsKey(PROPERTY_PROJECT_CONFIG_FILE)) { + baseDir = loadPropsFile(parentBaseDir, moduleProps, moduleId); + moduleProps.remove(PROPERTY_PROJECT_CONFIG_FILE); + } else { + baseDir = new File(parentBaseDir, moduleId); + setProjectBaseDir(baseDir, moduleProps, moduleId); + tryToFindAndLoadPropsFile(baseDir, moduleProps, moduleId); + } + + return moduleProps; + } + + private static void setProjectBaseDir(File baseDir, Properties childProps, String moduleId) { + if (!baseDir.isDirectory()) { + throw new IllegalStateException("The base directory of the module '" + moduleId + "' does not exist: " + baseDir.getAbsolutePath()); + } + childProps.put(PROPERTY_PROJECT_BASEDIR, baseDir.getAbsolutePath()); + } + + protected static Properties extractModuleProperties(String module, Properties properties) { + Properties moduleProps = new Properties(); + String propertyPrefix = module + "."; + int prefixLength = propertyPrefix.length(); + for (Map.Entry entry : properties.entrySet()) { + String key = (String) entry.getKey(); + if (key.startsWith(propertyPrefix)) { + moduleProps.put(key.substring(prefixLength), entry.getValue()); + } + } + return moduleProps; + } + private File locatePropertiesFile(Properties props, String homeKey, String relativePathFromHome, String settingsKey) { File settingsFile = null; String runnerHome = props.getProperty(homeKey, ""); @@ -91,7 +172,7 @@ class Conf { return settingsFile; } - private static Properties toProperties(File file) throws IOException { + private static Properties toProperties(File file) { InputStream in = null; try { Properties properties = new Properties(); @@ -108,8 +189,75 @@ class Conf { } finally { if (in != null) { - in.close(); + try { + in.close(); + } catch (IOException e) { + // Ignore errors + } + } + } + } + + /** + * @return baseDir + */ + protected File loadPropsFile(File parentBaseDir, Properties moduleProps, String moduleId) { + File propertyFile = getFileFromPath(moduleProps.getProperty(PROPERTY_PROJECT_CONFIG_FILE), parentBaseDir); + if (propertyFile.isFile()) { + Properties propsFromFile = toProperties(propertyFile); + for (Entry entry : propsFromFile.entrySet()) { + moduleProps.put(entry.getKey(), entry.getValue()); + } + File baseDir = null; + if (moduleProps.containsKey(PROPERTY_PROJECT_BASEDIR)) { + baseDir = getFileFromPath(moduleProps.getProperty(PROPERTY_PROJECT_BASEDIR), propertyFile.getParentFile()); + } else { + baseDir = propertyFile.getParentFile(); } + setProjectBaseDir(baseDir, moduleProps, moduleId); + return baseDir; + } else { + throw new IllegalStateException("The properties file of the module '" + moduleId + "' does not exist: " + propertyFile.getAbsolutePath()); + } + } + + private void tryToFindAndLoadPropsFile(File baseDir, Properties moduleProps, String moduleId) { + File propertyFile = new File(baseDir, "sonar-project.properties"); + if (propertyFile.isFile()) { + Properties propsFromFile = toProperties(propertyFile); + for (Entry entry : propsFromFile.entrySet()) { + moduleProps.put(entry.getKey(), entry.getValue()); + } + if (moduleProps.containsKey(PROPERTY_PROJECT_BASEDIR)) { + File overwrittenBaseDir = getFileFromPath(moduleProps.getProperty(PROPERTY_PROJECT_BASEDIR), propertyFile.getParentFile()); + setProjectBaseDir(overwrittenBaseDir, moduleProps, moduleId); + } + } + } + + /** + * Returns the file denoted by the given path, may this path be relative to "baseDir" or absolute. + */ + protected static File getFileFromPath(String path, File baseDir) { + File propertyFile = new File(path.trim()); + if (!propertyFile.isAbsolute()) { + propertyFile = new File(baseDir, propertyFile.getPath()); + } + return propertyFile; + } + + /** + * Transforms a comma-separated list String property in to a array of trimmed strings. + * + * This works even if they are separated by whitespace characters (space char, EOL, ...) + * + */ + static String[] getListFromProperty(Properties properties, String key) { + String value = properties.getProperty(key, ""); + String[] values = value.split(","); + for (int i = 0; i < values.length; i++) { + values[i] = values[i].trim(); } + return values; } } diff --git a/sonar-runner-dist/src/test/java/org/sonar/runner/ConfTest.java b/sonar-runner-dist/src/test/java/org/sonar/runner/ConfTest.java index d9ad17d..323be3b 100644 --- a/sonar-runner-dist/src/test/java/org/sonar/runner/ConfTest.java +++ b/sonar-runner-dist/src/test/java/org/sonar/runner/ConfTest.java @@ -20,7 +20,9 @@ package org.sonar.runner; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TemporaryFolder; import java.io.File; import java.util.Properties; @@ -31,6 +33,9 @@ import static org.mockito.Mockito.when; public class ConfTest { + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + Properties args = new Properties(); Cli cli = mock(Cli.class); Conf conf = new Conf(cli); @@ -61,20 +66,41 @@ public class ConfTest { assertThat(conf.properties().get("sonar.prop")).isEqualTo("otherValue"); } - // @Test - // public void shouldLoadCompleteConfiguration() throws Exception { - // File runnerHome = new File(getClass().getResource("/org/sonar/runner/MainTest/shouldLoadCompleteConfiguration/runner").toURI()); - // File projectHome = new File(getClass().getResource("/org/sonar/runner/MainTest/shouldLoadCompleteConfiguration/project").toURI()); - // Main main = new Main(); - // Properties args = main.parseArguments(new String[] { - // "-D", "runner.home=" + runnerHome.getCanonicalPath(), - // "-D", "project.home=" + projectHome.getCanonicalPath() - // }); - // main.loadProperties(args); - // - // assertThat(main.projectProperties.getProperty("project.prop")).isEqualTo("foo"); - // assertThat(main.projectProperties.getProperty("overridden.prop")).isEqualTo("project scope"); - // assertThat(main.globalProperties.getProperty("global.prop")).isEqualTo("jdbc:mysql:localhost/sonar"); - // } + @Test + public void shouldLoadCompleteConfiguration() throws Exception { + File runnerHome = new File(getClass().getResource("/org/sonar/runner/ConfTest/shouldLoadCompleteConfiguration/runner").toURI()); + File projectHome = new File(getClass().getResource("/org/sonar/runner/ConfTest/shouldLoadCompleteConfiguration/project").toURI()); + args.setProperty("runner.home", runnerHome.getCanonicalPath()); + args.setProperty("project.home", projectHome.getCanonicalPath()); + + Properties properties = conf.properties(); + + assertThat(properties.getProperty("project.prop")).isEqualTo("foo"); + assertThat(properties.getProperty("overridden.prop")).isEqualTo("project scope"); + assertThat(properties.getProperty("global.prop")).isEqualTo("jdbc:mysql:localhost/sonar"); + } + + @Test + public void shouldLoadModuleConfiguration() throws Exception { + File projectHome = new File(getClass().getResource("/org/sonar/runner/ConfTest/shouldLoadModuleConfiguration/project").toURI()); + args.setProperty("project.home", projectHome.getCanonicalPath()); + + Properties properties = conf.properties(); + + assertThat(properties.getProperty("module1.sonar.projectName")).isEqualTo("Module 1"); + assertThat(properties.getProperty("module2.sonar.projectName")).isEqualTo("Module 2"); + } + + @Test + public void shouldSupportSettingBaseDirFromCli() throws Exception { + File projectHome = new File(getClass().getResource("/org/sonar/runner/ConfTest/shouldLoadModuleConfiguration/project").toURI()); + args.setProperty("project.home", temp.newFolder().getCanonicalPath()); + args.setProperty("sonar.projectBaseDir", projectHome.getCanonicalPath()); + + Properties properties = conf.properties(); + + assertThat(properties.getProperty("module1.sonar.projectName")).isEqualTo("Module 1"); + assertThat(properties.getProperty("module2.sonar.projectName")).isEqualTo("Module 2"); + } } diff --git a/sonar-runner-dist/src/test/resources/org/sonar/runner/ConfTest/shouldLoadModuleConfiguration/project/module1/sonar-project.properties b/sonar-runner-dist/src/test/resources/org/sonar/runner/ConfTest/shouldLoadModuleConfiguration/project/module1/sonar-project.properties new file mode 100644 index 0000000..6803263 --- /dev/null +++ b/sonar-runner-dist/src/test/resources/org/sonar/runner/ConfTest/shouldLoadModuleConfiguration/project/module1/sonar-project.properties @@ -0,0 +1 @@ +sonar.projectName=Module 1 diff --git a/sonar-runner-dist/src/test/resources/org/sonar/runner/ConfTest/shouldLoadModuleConfiguration/project/module2/sonar-project.properties b/sonar-runner-dist/src/test/resources/org/sonar/runner/ConfTest/shouldLoadModuleConfiguration/project/module2/sonar-project.properties new file mode 100644 index 0000000..c12fad8 --- /dev/null +++ b/sonar-runner-dist/src/test/resources/org/sonar/runner/ConfTest/shouldLoadModuleConfiguration/project/module2/sonar-project.properties @@ -0,0 +1 @@ +sonar.projectName=Module 2 diff --git a/sonar-runner-dist/src/test/resources/org/sonar/runner/ConfTest/shouldLoadModuleConfiguration/project/sonar-project.properties b/sonar-runner-dist/src/test/resources/org/sonar/runner/ConfTest/shouldLoadModuleConfiguration/project/sonar-project.properties new file mode 100644 index 0000000..835124c --- /dev/null +++ b/sonar-runner-dist/src/test/resources/org/sonar/runner/ConfTest/shouldLoadModuleConfiguration/project/sonar-project.properties @@ -0,0 +1 @@ +sonar.modules=module1,module2