@@ -97,7 +97,7 @@ class DefaultProjectBootstrapper implements ProjectBootstrapper { | |||
/** | |||
* Array of all mandatory properties required for a child project before its properties get merged with its parent ones. | |||
*/ | |||
private static final String[] MANDATORY_PROPERTIES_FOR_CHILD = {CoreProperties.PROJECT_KEY_PROPERTY, CoreProperties.PROJECT_NAME_PROPERTY}; | |||
private static final String[] MANDATORY_PROPERTIES_FOR_CHILD = {CoreProperties.MODULE_KEY_PROPERTY, CoreProperties.PROJECT_NAME_PROPERTY}; | |||
/** | |||
* Properties that must not be passed from the parent project to its children. | |||
@@ -181,8 +181,6 @@ class DefaultProjectBootstrapper implements ProjectBootstrapper { | |||
} | |||
private ProjectDefinition loadChildProject(ProjectDefinition parentProject, Properties moduleProps, String moduleId) { | |||
setProjectKeyAndNameIfNotDefined(moduleProps, moduleId); | |||
final File baseDir; | |||
if (moduleProps.containsKey(PROPERTY_PROJECT_BASEDIR)) { | |||
baseDir = getFileFromPath(moduleProps.getProperty(PROPERTY_PROJECT_BASEDIR), parentProject.getBaseDir()); | |||
@@ -202,14 +200,14 @@ class DefaultProjectBootstrapper implements ProjectBootstrapper { | |||
tryToFindAndLoadPropsFile(baseDir, moduleProps, moduleId); | |||
} | |||
setModuleKeyAndNameIfNotDefined(moduleProps, moduleId, parentProject.getKey()); | |||
// and finish | |||
checkMandatoryProperties(moduleProps, MANDATORY_PROPERTIES_FOR_CHILD); | |||
validateDirectories(moduleProps, baseDir, moduleId); | |||
mergeParentProperties(moduleProps, parentProject.getProperties()); | |||
prefixProjectKeyWithParentKey(moduleProps, parentProject.getKey()); | |||
return defineProject(moduleProps, parentProject); | |||
} | |||
@@ -266,13 +264,20 @@ class DefaultProjectBootstrapper implements ProjectBootstrapper { | |||
} | |||
@VisibleForTesting | |||
protected static void setProjectKeyAndNameIfNotDefined(Properties childProps, String moduleId) { | |||
if (!childProps.containsKey(CoreProperties.PROJECT_KEY_PROPERTY)) { | |||
childProps.put(CoreProperties.PROJECT_KEY_PROPERTY, moduleId); | |||
protected static void setModuleKeyAndNameIfNotDefined(Properties childProps, String moduleId, String parentKey) { | |||
if (!childProps.containsKey(CoreProperties.MODULE_KEY_PROPERTY)) { | |||
if (!childProps.containsKey(CoreProperties.PROJECT_KEY_PROPERTY)) { | |||
childProps.put(CoreProperties.MODULE_KEY_PROPERTY, parentKey + ":" + moduleId); | |||
} else { | |||
String childKey = childProps.getProperty(CoreProperties.PROJECT_KEY_PROPERTY); | |||
childProps.put(CoreProperties.MODULE_KEY_PROPERTY, parentKey + ":" + childKey); | |||
} | |||
} | |||
if (!childProps.containsKey(CoreProperties.PROJECT_NAME_PROPERTY)) { | |||
childProps.put(CoreProperties.PROJECT_NAME_PROPERTY, moduleId); | |||
} | |||
// For backward compatibility with ProjectDefinition | |||
childProps.put(CoreProperties.PROJECT_KEY_PROPERTY, childProps.getProperty(CoreProperties.MODULE_KEY_PROPERTY)); | |||
} | |||
@VisibleForTesting | |||
@@ -284,12 +289,6 @@ class DefaultProjectBootstrapper implements ProjectBootstrapper { | |||
} | |||
} | |||
@VisibleForTesting | |||
protected static void prefixProjectKeyWithParentKey(Properties childProps, String parentKey) { | |||
String childKey = childProps.getProperty(CoreProperties.PROJECT_KEY_PROPERTY); | |||
childProps.put(CoreProperties.PROJECT_KEY_PROPERTY, parentKey + ":" + childKey); | |||
} | |||
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()); | |||
@@ -309,9 +308,9 @@ class DefaultProjectBootstrapper implements ProjectBootstrapper { | |||
missing.append(mandatoryProperty); | |||
} | |||
} | |||
String projectKey = props.getProperty(CoreProperties.PROJECT_KEY_PROPERTY); | |||
String moduleKey = StringUtils.defaultIfBlank(props.getProperty(CoreProperties.MODULE_KEY_PROPERTY), props.getProperty(CoreProperties.PROJECT_KEY_PROPERTY)); | |||
if (missing.length() != 0) { | |||
throw new IllegalStateException("You must define the following mandatory properties for '" + (projectKey == null ? "Unknown" : projectKey) + "': " + missing); | |||
throw new IllegalStateException("You must define the following mandatory properties for '" + (moduleKey == null ? "Unknown" : moduleKey) + "': " + missing); | |||
} | |||
} | |||
@@ -207,6 +207,25 @@ public class DefaultProjectBootstrapperTest { | |||
assertThat(module2.getProperties().getProperty("module2.sonar.projectKey")).isNull(); | |||
} | |||
// SONAR-4876 | |||
@Test | |||
public void shouldDefineMultiModuleProjectWithModuleKey() throws IOException { | |||
ProjectDefinition rootProject = loadProjectDefinition("multi-module-definitions-moduleKey"); | |||
// CHECK ROOT | |||
// module properties must have been cleaned | |||
assertThat(rootProject.getProperties().getProperty("module1.sonar.moduleKey")).isNull(); | |||
assertThat(rootProject.getProperties().getProperty("module2.sonar.moduleKey")).isNull(); | |||
// CHECK MODULES | |||
List<ProjectDefinition> modules = rootProject.getSubProjects(); | |||
assertThat(modules.size()).isEqualTo(2); | |||
// Module 2 | |||
ProjectDefinition module2 = modules.get(1); | |||
assertThat(module2.getKey()).isEqualTo("com.foo.project.module2"); | |||
} | |||
@Test | |||
public void shouldDefineMultiModuleProjectWithDefinitionsModule1Inherited() throws IOException { | |||
ProjectDefinition rootProject = loadProjectDefinition("multi-module-definitions-in-each-module-inherited"); | |||
@@ -530,7 +549,7 @@ public class DefaultProjectBootstrapperTest { | |||
@Test | |||
public void shouldInitRootWorkDir() { | |||
DefaultProjectBootstrapper builder = new DefaultProjectBootstrapper(new BootstrapSettings(new BootstrapProperties(Maps.<String, String> newHashMap()))); | |||
DefaultProjectBootstrapper builder = new DefaultProjectBootstrapper(new BootstrapSettings(new BootstrapProperties(Maps.<String, String>newHashMap()))); | |||
File baseDir = new File("target/tmp/baseDir"); | |||
File workDir = builder.initRootProjectWorkDir(baseDir); | |||
@@ -540,7 +559,7 @@ public class DefaultProjectBootstrapperTest { | |||
@Test | |||
public void shouldInitWorkDirWithCustomRelativeFolder() { | |||
Map<String, String> props = Maps.<String, String> newHashMap(); | |||
Map<String, String> props = Maps.<String, String>newHashMap(); | |||
props.put("sonar.working.directory", ".foo"); | |||
DefaultProjectBootstrapper builder = new DefaultProjectBootstrapper(new BootstrapSettings(new BootstrapProperties(props))); | |||
File baseDir = new File("target/tmp/baseDir"); | |||
@@ -552,7 +571,7 @@ public class DefaultProjectBootstrapperTest { | |||
@Test | |||
public void shouldInitRootWorkDirWithCustomAbsoluteFolder() { | |||
Map<String, String> props = Maps.<String, String> newHashMap(); | |||
Map<String, String> props = Maps.<String, String>newHashMap(); | |||
props.put("sonar.working.directory", new File("src").getAbsolutePath()); | |||
DefaultProjectBootstrapper builder = new DefaultProjectBootstrapper(new BootstrapSettings(new BootstrapProperties(props))); | |||
File baseDir = new File("target/tmp/baseDir"); | |||
@@ -562,15 +581,6 @@ public class DefaultProjectBootstrapperTest { | |||
assertThat(workDir).isEqualTo(new File("src").getAbsoluteFile()); | |||
} | |||
@Test | |||
public void shouldReturnPrefixedKey() { | |||
Properties props = new Properties(); | |||
props.put("sonar.projectKey", "my-module-key"); | |||
DefaultProjectBootstrapper.prefixProjectKeyWithParentKey(props, "my-parent-key"); | |||
assertThat(props.getProperty("sonar.projectKey")).isEqualTo("my-parent-key:my-module-key"); | |||
} | |||
@Test | |||
public void shouldFailIf2ModulesWithSameKey() { | |||
Properties props = new Properties(); | |||
@@ -597,18 +607,18 @@ public class DefaultProjectBootstrapperTest { | |||
} | |||
@Test | |||
public void shouldSetProjectKeyIfNotPresent() { | |||
public void shouldSetModuleKeyIfNotPresent() { | |||
Properties props = new Properties(); | |||
props.put("sonar.projectVersion", "1.0"); | |||
// should be set | |||
DefaultProjectBootstrapper.setProjectKeyAndNameIfNotDefined(props, "foo"); | |||
assertThat(props.getProperty("sonar.projectKey")).isEqualTo("foo"); | |||
DefaultProjectBootstrapper.setModuleKeyAndNameIfNotDefined(props, "foo", "parent"); | |||
assertThat(props.getProperty("sonar.moduleKey")).isEqualTo("parent:foo"); | |||
assertThat(props.getProperty("sonar.projectName")).isEqualTo("foo"); | |||
// but not this 2nd time | |||
DefaultProjectBootstrapper.setProjectKeyAndNameIfNotDefined(props, "bar"); | |||
assertThat(props.getProperty("sonar.projectKey")).isEqualTo("foo"); | |||
DefaultProjectBootstrapper.setModuleKeyAndNameIfNotDefined(props, "bar", "parent"); | |||
assertThat(props.getProperty("sonar.moduleKey")).isEqualTo("parent:foo"); | |||
assertThat(props.getProperty("sonar.projectName")).isEqualTo("foo"); | |||
} | |||
@@ -621,7 +631,7 @@ public class DefaultProjectBootstrapperTest { | |||
} | |||
private ProjectDefinition loadProjectDefinition(String projectFolder) throws IOException { | |||
Map<String, String> props = Maps.<String, String> newHashMap(); | |||
Map<String, String> props = Maps.<String, String>newHashMap(); | |||
Properties runnerProps = DefaultProjectBootstrapper.toProperties(TestUtils.getResource(this.getClass(), projectFolder + "/sonar-project.properties")); | |||
for (final String name : runnerProps.stringPropertyNames()) { | |||
props.put(name, runnerProps.getProperty(name)); |
@@ -0,0 +1 @@ | |||
Fake |
@@ -0,0 +1 @@ | |||
Fake |
@@ -0,0 +1,19 @@ | |||
sonar.projectKey=com.foo.project | |||
sonar.projectName=Foo Project | |||
sonar.projectVersion=1.0-SNAPSHOT | |||
sonar.projectDescription=Description of Foo Project | |||
sonar.sources=sources | |||
sonar.tests=tests | |||
sonar.binaries=target/classes | |||
sonar.modules=module1,\ | |||
module2 | |||
# Mandatory properties for module1 are all inferred from the module ID | |||
module2.sonar.moduleKey=com.foo.project.module2 | |||
module2.sonar.projectName=Foo Module 2 | |||
# redefine some properties | |||
module2.sonar.projectDescription=Description of Module 2 | |||
module2.sonar.sources=src |
@@ -136,6 +136,11 @@ public interface CoreProperties { | |||
*/ | |||
String PROJECT_KEY_PROPERTY = "sonar.projectKey"; | |||
/** | |||
* @since 4.1 | |||
*/ | |||
String MODULE_KEY_PROPERTY = "sonar.moduleKey"; | |||
/** | |||
* @since 2.6 | |||
*/ |