]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-11465 Move post-job at project level
authorJulien HENRY <julien.henry@sonarsource.com>
Thu, 29 Nov 2018 16:07:21 +0000 (17:07 +0100)
committersonartech <sonartech@sonarsource.com>
Wed, 16 Jan 2019 08:43:04 +0000 (09:43 +0100)
43 files changed:
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/SensorStrategy.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/postjob/PostJob.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/scm/BlameCommand.java
sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/DefaultInputFileTest.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/AbstractExtensionDictionnary.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/BatchComponents.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/PostJobExtensionDictionnary.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/ScannerExtensionDictionnary.java [deleted file]
sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/SensorExtensionDictionnary.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/scanner/mediumtest/AnalysisResult.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/AbstractCoverageExclusions.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/AbstractModulePhaseExecutor.java [deleted file]
sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/IssuesPhaseExecutor.java [deleted file]
sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/ModuleCoverageExclusions.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/PostJobsExecutor.java [deleted file]
sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/ProjectCoverageExclusions.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/PublishPhaseExecutor.java [deleted file]
sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/SensorsExecutor.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/postjob/PostJobWrapper.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/postjob/PostJobsExecutor.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/DefaultProjectRepositoriesLoader.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/QProfileVerifier.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ModuleScanContainer.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectScanContainer.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/DefaultProjectFileSystem.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/FileIndexer.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/InputComponentStore.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/ModuleInputComponentStore.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/ProjectFileIndexer.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/report/IssuesReports.java [deleted file]
sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/report/JSONReport.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/report/Reporter.java [deleted file]
sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/ScmConfiguration.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/ScmPublisher.java
sonar-scanner-engine/src/test/java/org/sonar/scanner/bootstrap/PostJobExtensionDictionnaryTest.java [new file with mode: 0644]
sonar-scanner-engine/src/test/java/org/sonar/scanner/bootstrap/ScannerExtensionDictionnaryTest.java [deleted file]
sonar-scanner-engine/src/test/java/org/sonar/scanner/bootstrap/SensorExtensionDictionnaryTest.java [new file with mode: 0644]
sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/fs/FileSystemMediumTest.java
sonar-scanner-engine/src/test/java/org/sonar/scanner/phases/ModuleCoverageExclusionsTest.java
sonar-scanner-engine/src/test/java/org/sonar/scanner/phases/PostJobsExecutorTest.java
sonar-scanner-engine/src/test/java/org/sonar/scanner/phases/SensorsExecutorTest.java
sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/InputComponentStoreTest.java
sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/ModuleInputComponentStoreTest.java

index 62de7d120f82ceba95caa7266b037dfe5a90ce67..adde73809f62dc752e0c76c69d2f874d79fcbaa8 100644 (file)
@@ -29,7 +29,7 @@ import org.sonar.api.batch.fs.InputFile;
  */
 public class SensorStrategy {
 
-  private boolean global = false;
+  private boolean global = true;
 
   public boolean isGlobal() {
     return global;
index 659cdbf88e8666e9ff23170bda90f8182d8dae28..facf976fc6f39dda2122ec71e785ad6c8b889fae 100644 (file)
@@ -20,7 +20,7 @@
 package org.sonar.api.batch.postjob;
 
 import org.sonar.api.ExtensionPoint;
-import org.sonar.api.batch.ScannerSide;
+import org.sonar.api.scanner.ScannerSide;
 
 /**
  * PostJobs are executed at the very end of scanner analysis. A PostJob can't do any modification
@@ -29,6 +29,7 @@ import org.sonar.api.batch.ScannerSide;
  * asynchronous process to compute data on server side in 5.x series.
  *
  * @since 5.2
+ * @since 7.6 postjobs are loaded in the project container
  */
 @ScannerSide
 @ExtensionPoint
index 5fd89b5bb55f06e09f447d40bb5348fca34d1e5a..c31fe6c8b34f5e3765223380e2952dc16b686665 100644 (file)
  */
 package org.sonar.api.batch.scm;
 
-import org.sonar.api.batch.ScannerSide;
-import org.sonar.api.batch.InstantiationStrategy;
+import java.util.List;
+import org.sonar.api.scanner.ScannerSide;
 import org.sonar.api.batch.fs.FileSystem;
 import org.sonar.api.batch.fs.InputFile;
 
-import java.util.List;
-
 /**
  * This class should be implemented by SCM providers.
  * @since 5.0
  */
-@InstantiationStrategy(InstantiationStrategy.PER_BATCH)
 @ScannerSide
 public abstract class BlameCommand {
 
index c5e84f19f332ddf764ff6382cab6b2de920d228b..420f433557b5c5ef1630310a18d99d13618a38df 100644 (file)
@@ -87,6 +87,7 @@ public class DefaultInputFileTest {
     assertThat(inputFile.getModuleRelativePath()).isEqualTo(MODULE_RELATIVE_PATH);
     assertThat(inputFile.getProjectRelativePath()).isEqualTo(PROJECT_RELATIVE_PATH);
 
+    sensorStrategy.setGlobal(false);
     assertThat(inputFile.relativePath()).isEqualTo(MODULE_RELATIVE_PATH);
     assertThat(new File(inputFile.relativePath())).isRelative();
     sensorStrategy.setGlobal(true);
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/AbstractExtensionDictionnary.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/AbstractExtensionDictionnary.java
new file mode 100644 (file)
index 0000000..811a656
--- /dev/null
@@ -0,0 +1,206 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * 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  02110-1301, USA.
+ */
+package org.sonar.scanner.bootstrap;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Array;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.stream.Collectors;
+import javax.annotation.Nullable;
+import org.apache.commons.lang.ClassUtils;
+import org.sonar.api.batch.DependedUpon;
+import org.sonar.api.batch.DependsUpon;
+import org.sonar.api.batch.Phase;
+import org.sonar.api.utils.AnnotationUtils;
+import org.sonar.api.utils.dag.DirectAcyclicGraph;
+import org.sonar.core.platform.ComponentContainer;
+
+public abstract class AbstractExtensionDictionnary {
+
+  private final ComponentContainer componentContainer;
+
+  public AbstractExtensionDictionnary(ComponentContainer componentContainer) {
+    this.componentContainer = componentContainer;
+  }
+
+  public <T> Collection<T> select(Class<T> type, boolean sort, @Nullable ExtensionMatcher matcher) {
+    List<T> result = getFilteredExtensions(type, matcher);
+    if (sort) {
+      return sort(result);
+    }
+    return result;
+  }
+
+  private static Phase.Name evaluatePhase(Object extension) {
+    Phase phaseAnnotation = AnnotationUtils.getAnnotation(extension, Phase.class);
+    if (phaseAnnotation != null) {
+      return phaseAnnotation.name();
+    }
+    return Phase.Name.DEFAULT;
+  }
+
+  protected <T> List<T> getFilteredExtensions(Class<T> type, @Nullable ExtensionMatcher matcher) {
+    List<T> result = new ArrayList<>();
+
+    for (Object extension : getExtensions(type)) {
+      if (shouldKeep(type, extension, matcher)) {
+        result.add((T) extension);
+      }
+    }
+    return result;
+  }
+
+  private <T> List<T> getExtensions(Class<T> type) {
+    List<T> extensions = new ArrayList<>();
+    completeScannerExtensions(componentContainer, extensions, type);
+    return extensions;
+  }
+
+  private static <T> void completeScannerExtensions(ComponentContainer container, List<T> extensions, Class<T> type) {
+    extensions.addAll(container.getComponentsByType(type));
+    ComponentContainer parentContainer = container.getParent();
+    if (parentContainer != null) {
+      completeScannerExtensions(parentContainer, extensions, type);
+    }
+  }
+
+  protected <T> Collection<T> sort(Collection<T> extensions) {
+    DirectAcyclicGraph dag = new DirectAcyclicGraph();
+
+    for (T extension : extensions) {
+      dag.add(extension);
+      for (Object dependency : getDependencies(extension)) {
+        dag.add(extension, dependency);
+      }
+      for (Object generates : getDependents(extension)) {
+        dag.add(generates, extension);
+      }
+      completePhaseDependencies(dag, extension);
+    }
+    List<?> sortedList = dag.sort();
+
+    return (Collection<T>) sortedList.stream()
+      .filter(extensions::contains)
+      .collect(Collectors.toList());
+  }
+
+  /**
+   * Extension dependencies
+   */
+  private <T> List<Object> getDependencies(T extension) {
+    return new ArrayList<>(evaluateAnnotatedClasses(extension, DependsUpon.class));
+  }
+
+  /**
+   * Objects that depend upon this extension.
+   */
+  private <T> List<Object> getDependents(T extension) {
+    return new ArrayList<>(evaluateAnnotatedClasses(extension, DependedUpon.class));
+  }
+
+  private static void completePhaseDependencies(DirectAcyclicGraph dag, Object extension) {
+    Phase.Name phase = evaluatePhase(extension);
+    dag.add(extension, phase);
+    for (Phase.Name name : Phase.Name.values()) {
+      if (phase.compareTo(name) < 0) {
+        dag.add(name, extension);
+      } else if (phase.compareTo(name) > 0) {
+        dag.add(extension, name);
+      }
+    }
+  }
+
+  List<Object> evaluateAnnotatedClasses(Object extension, Class<? extends Annotation> annotation) {
+    List<Object> results = new ArrayList<>();
+    Class<?> aClass = extension.getClass();
+    while (aClass != null) {
+      evaluateClass(aClass, annotation, results);
+
+      for (Method method : aClass.getDeclaredMethods()) {
+        if (method.getAnnotation(annotation) != null) {
+          checkAnnotatedMethod(method);
+          evaluateMethod(extension, method, results);
+        }
+      }
+      aClass = aClass.getSuperclass();
+    }
+
+    return results;
+  }
+
+  private static void evaluateClass(Class<?> extensionClass, Class<? extends Annotation> annotationClass, List<Object> results) {
+    Annotation annotation = extensionClass.getAnnotation(annotationClass);
+    if (annotation != null) {
+      if (annotation.annotationType().isAssignableFrom(DependsUpon.class)) {
+        results.addAll(Arrays.asList(((DependsUpon) annotation).value()));
+
+      } else if (annotation.annotationType().isAssignableFrom(DependedUpon.class)) {
+        results.addAll(Arrays.asList(((DependedUpon) annotation).value()));
+      }
+    }
+
+    Class<?>[] interfaces = extensionClass.getInterfaces();
+    for (Class<?> anInterface : interfaces) {
+      evaluateClass(anInterface, annotationClass, results);
+    }
+  }
+
+  private void evaluateMethod(Object extension, Method method, List<Object> results) {
+    try {
+      Object result = method.invoke(extension);
+      if (result != null) {
+        if (result instanceof Class<?>) {
+          results.addAll(componentContainer.getComponentsByType((Class<?>) result));
+
+        } else if (result instanceof Collection<?>) {
+          results.addAll((Collection<?>) result);
+
+        } else if (result.getClass().isArray()) {
+          for (int i = 0; i < Array.getLength(result); i++) {
+            results.add(Array.get(result, i));
+          }
+
+        } else {
+          results.add(result);
+        }
+      }
+    } catch (Exception e) {
+      throw new IllegalStateException("Can not invoke method " + method, e);
+    }
+  }
+
+  private static void checkAnnotatedMethod(Method method) {
+    if (!Modifier.isPublic(method.getModifiers())) {
+      throw new IllegalStateException("Annotated method must be public:" + method);
+    }
+    if (method.getParameterTypes().length > 0) {
+      throw new IllegalStateException("Annotated method must not have parameters:" + method);
+    }
+  }
+
+  private static boolean shouldKeep(Class<?> type, Object extension, @Nullable ExtensionMatcher matcher) {
+    return ClassUtils.isAssignable(extension.getClass(), type) && (matcher == null || matcher.accept(extension));
+  }
+}
index c6b844a78c3b9075b86c6e29a20432a063f8edb4..d5487c990cac1cd57dc6cd8557185f9d5d3fbfe0 100644 (file)
@@ -31,8 +31,6 @@ import org.sonar.scanner.genericcoverage.GenericTestExecutionSensor;
 import org.sonar.scanner.issue.tracking.ServerIssueFromWs;
 import org.sonar.scanner.issue.tracking.TrackedIssue;
 import org.sonar.scanner.scan.report.JSONReport;
-import org.sonar.scanner.scm.ScmConfiguration;
-import org.sonar.scanner.scm.ScmPublisher;
 import org.sonar.scanner.source.ZeroCoverageSensor;
 
 public class BatchComponents {
@@ -45,10 +43,6 @@ public class BatchComponents {
       DefaultResourceTypes.get());
     components.addAll(CorePropertyDefinitions.all());
     if (!analysisMode.isIssues()) {
-      // SCM
-      components.add(ScmConfiguration.class);
-      components.add(ScmPublisher.class);
-
       components.add(ZeroCoverageSensor.class);
 
       // Generic coverage
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/PostJobExtensionDictionnary.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/PostJobExtensionDictionnary.java
new file mode 100644 (file)
index 0000000..3994149
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * 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  02110-1301, USA.
+ */
+package org.sonar.scanner.bootstrap;
+
+import java.util.Collection;
+import java.util.stream.Collectors;
+import org.sonar.api.batch.postjob.PostJob;
+import org.sonar.api.batch.postjob.PostJobContext;
+import org.sonar.core.platform.ComponentContainer;
+import org.sonar.scanner.postjob.PostJobOptimizer;
+import org.sonar.scanner.postjob.PostJobWrapper;
+
+public class PostJobExtensionDictionnary extends AbstractExtensionDictionnary {
+
+  private final PostJobContext postJobContext;
+  private final PostJobOptimizer postJobOptimizer;
+
+  public PostJobExtensionDictionnary(ComponentContainer componentContainer, PostJobOptimizer postJobOptimizer, PostJobContext postJobContext) {
+    super(componentContainer);
+    this.postJobOptimizer = postJobOptimizer;
+    this.postJobContext = postJobContext;
+  }
+
+  public Collection<PostJobWrapper> selectPostJobs() {
+    Collection<PostJob> result = sort(getFilteredExtensions(PostJob.class, null));
+    return result.stream()
+      .map(j -> new PostJobWrapper(j, postJobContext, postJobOptimizer))
+      .filter(PostJobWrapper::shouldExecute)
+      .collect(Collectors.toList());
+  }
+}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/ScannerExtensionDictionnary.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/ScannerExtensionDictionnary.java
deleted file mode 100644 (file)
index e8e9d81..0000000
+++ /dev/null
@@ -1,242 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2019 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * 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  02110-1301, USA.
- */
-package org.sonar.scanner.bootstrap;
-
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Array;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-import java.util.stream.Collectors;
-import javax.annotation.Nullable;
-import org.apache.commons.lang.ClassUtils;
-import org.sonar.api.batch.DependedUpon;
-import org.sonar.api.batch.DependsUpon;
-import org.sonar.api.batch.Phase;
-import org.sonar.api.batch.postjob.PostJob;
-import org.sonar.api.batch.postjob.PostJobContext;
-import org.sonar.api.batch.sensor.Sensor;
-import org.sonar.api.batch.sensor.SensorContext;
-import org.sonar.api.utils.AnnotationUtils;
-import org.sonar.api.utils.dag.DirectAcyclicGraph;
-import org.sonar.core.platform.ComponentContainer;
-import org.sonar.scanner.postjob.PostJobOptimizer;
-import org.sonar.scanner.postjob.PostJobWrapper;
-import org.sonar.scanner.sensor.SensorOptimizer;
-import org.sonar.scanner.sensor.SensorWrapper;
-
-/**
- * @since 2.6
- */
-public class ScannerExtensionDictionnary {
-
-  private final ComponentContainer componentContainer;
-  private final SensorContext sensorContext;
-  private final SensorOptimizer sensorOptimizer;
-  private final PostJobOptimizer postJobOptimizer;
-  private final PostJobContext postJobContext;
-
-  public ScannerExtensionDictionnary(ComponentContainer componentContainer, SensorContext sensorContext, SensorOptimizer sensorOptimizer,
-    PostJobOptimizer postJobOptimizer, PostJobContext postJobContext) {
-    this.componentContainer = componentContainer;
-    this.sensorContext = sensorContext;
-    this.sensorOptimizer = sensorOptimizer;
-    this.postJobOptimizer = postJobOptimizer;
-    this.postJobContext = postJobContext;
-  }
-
-  public <T> Collection<T> select(Class<T> type, boolean sort, @Nullable ExtensionMatcher matcher) {
-    List<T> result = getFilteredExtensions(type, matcher);
-    if (sort) {
-      return sort(result);
-    }
-    return result;
-  }
-
-  public Collection<SensorWrapper> selectSensors(boolean global) {
-    Collection<Sensor> result = sort(getFilteredExtensions(Sensor.class, null));
-    return result.stream()
-      .map(s -> new SensorWrapper(s, sensorContext, sensorOptimizer))
-      .filter(s -> global == s.isGlobal() && s.shouldExecute())
-      .collect(Collectors.toList());
-  }
-
-  public Collection<PostJobWrapper> selectPostJobs() {
-    Collection<PostJob> result = sort(getFilteredExtensions(PostJob.class, null));
-    return result.stream()
-      .map(j -> new PostJobWrapper(j, postJobContext, postJobOptimizer))
-      .filter(PostJobWrapper::shouldExecute)
-      .collect(Collectors.toList());
-  }
-
-  private static Phase.Name evaluatePhase(Object extension) {
-    Phase phaseAnnotation = AnnotationUtils.getAnnotation(extension, Phase.class);
-    if (phaseAnnotation != null) {
-      return phaseAnnotation.name();
-    }
-    return Phase.Name.DEFAULT;
-  }
-
-  private <T> List<T> getFilteredExtensions(Class<T> type, @Nullable ExtensionMatcher matcher) {
-    List<T> result = new ArrayList<>();
-
-    for (Object extension : getExtensions(type)) {
-      if (shouldKeep(type, extension, matcher)) {
-        result.add((T) extension);
-      }
-    }
-    return result;
-  }
-
-  private <T> List<T> getExtensions(Class<T> type) {
-    List<T> extensions = new ArrayList<>();
-    completeScannerExtensions(componentContainer, extensions, type);
-    return extensions;
-  }
-
-  private static <T> void completeScannerExtensions(ComponentContainer container, List<T> extensions, Class<T> type) {
-    extensions.addAll(container.getComponentsByType(type));
-    ComponentContainer parentContainer = container.getParent();
-    if (parentContainer != null) {
-      completeScannerExtensions(parentContainer, extensions, type);
-    }
-  }
-
-  private <T> Collection<T> sort(Collection<T> extensions) {
-    DirectAcyclicGraph dag = new DirectAcyclicGraph();
-
-    for (T extension : extensions) {
-      dag.add(extension);
-      for (Object dependency : getDependencies(extension)) {
-        dag.add(extension, dependency);
-      }
-      for (Object generates : getDependents(extension)) {
-        dag.add(generates, extension);
-      }
-      completePhaseDependencies(dag, extension);
-    }
-    List<?> sortedList = dag.sort();
-
-    return (Collection<T>) sortedList.stream()
-      .filter(extensions::contains)
-      .collect(Collectors.toList());
-  }
-
-  /**
-   * Extension dependencies
-   */
-  private <T> List<Object> getDependencies(T extension) {
-    return new ArrayList<>(evaluateAnnotatedClasses(extension, DependsUpon.class));
-  }
-
-  /**
-   * Objects that depend upon this extension.
-   */
-  private <T> List<Object> getDependents(T extension) {
-    return new ArrayList<>(evaluateAnnotatedClasses(extension, DependedUpon.class));
-  }
-
-  private static void completePhaseDependencies(DirectAcyclicGraph dag, Object extension) {
-    Phase.Name phase = evaluatePhase(extension);
-    dag.add(extension, phase);
-    for (Phase.Name name : Phase.Name.values()) {
-      if (phase.compareTo(name) < 0) {
-        dag.add(name, extension);
-      } else if (phase.compareTo(name) > 0) {
-        dag.add(extension, name);
-      }
-    }
-  }
-
-  List<Object> evaluateAnnotatedClasses(Object extension, Class<? extends Annotation> annotation) {
-    List<Object> results = new ArrayList<>();
-    Class<?> aClass = extension.getClass();
-    while (aClass != null) {
-      evaluateClass(aClass, annotation, results);
-
-      for (Method method : aClass.getDeclaredMethods()) {
-        if (method.getAnnotation(annotation) != null) {
-          checkAnnotatedMethod(method);
-          evaluateMethod(extension, method, results);
-        }
-      }
-      aClass = aClass.getSuperclass();
-    }
-
-    return results;
-  }
-
-  private static void evaluateClass(Class<?> extensionClass, Class<? extends Annotation> annotationClass, List<Object> results) {
-    Annotation annotation = extensionClass.getAnnotation(annotationClass);
-    if (annotation != null) {
-      if (annotation.annotationType().isAssignableFrom(DependsUpon.class)) {
-        results.addAll(Arrays.asList(((DependsUpon) annotation).value()));
-
-      } else if (annotation.annotationType().isAssignableFrom(DependedUpon.class)) {
-        results.addAll(Arrays.asList(((DependedUpon) annotation).value()));
-      }
-    }
-
-    Class<?>[] interfaces = extensionClass.getInterfaces();
-    for (Class<?> anInterface : interfaces) {
-      evaluateClass(anInterface, annotationClass, results);
-    }
-  }
-
-  private void evaluateMethod(Object extension, Method method, List<Object> results) {
-    try {
-      Object result = method.invoke(extension);
-      if (result != null) {
-        if (result instanceof Class<?>) {
-          results.addAll(componentContainer.getComponentsByType((Class<?>) result));
-
-        } else if (result instanceof Collection<?>) {
-          results.addAll((Collection<?>) result);
-
-        } else if (result.getClass().isArray()) {
-          for (int i = 0; i < Array.getLength(result); i++) {
-            results.add(Array.get(result, i));
-          }
-
-        } else {
-          results.add(result);
-        }
-      }
-    } catch (Exception e) {
-      throw new IllegalStateException("Can not invoke method " + method, e);
-    }
-  }
-
-  private static void checkAnnotatedMethod(Method method) {
-    if (!Modifier.isPublic(method.getModifiers())) {
-      throw new IllegalStateException("Annotated method must be public:" + method);
-    }
-    if (method.getParameterTypes().length > 0) {
-      throw new IllegalStateException("Annotated method must not have parameters:" + method);
-    }
-  }
-
-  private static boolean shouldKeep(Class<?> type, Object extension, @Nullable ExtensionMatcher matcher) {
-    return ClassUtils.isAssignable(extension.getClass(), type) && (matcher == null || matcher.accept(extension));
-  }
-}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/SensorExtensionDictionnary.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/SensorExtensionDictionnary.java
new file mode 100644 (file)
index 0000000..34ac2dd
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * 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  02110-1301, USA.
+ */
+package org.sonar.scanner.bootstrap;
+
+import java.util.Collection;
+import java.util.stream.Collectors;
+import org.sonar.api.batch.sensor.Sensor;
+import org.sonar.api.batch.sensor.SensorContext;
+import org.sonar.core.platform.ComponentContainer;
+import org.sonar.scanner.sensor.SensorOptimizer;
+import org.sonar.scanner.sensor.SensorWrapper;
+
+public class SensorExtensionDictionnary extends AbstractExtensionDictionnary {
+
+  private final SensorContext sensorContext;
+  private final SensorOptimizer sensorOptimizer;
+
+  public SensorExtensionDictionnary(ComponentContainer componentContainer, SensorContext sensorContext, SensorOptimizer sensorOptimizer) {
+    super(componentContainer);
+    this.sensorContext = sensorContext;
+    this.sensorOptimizer = sensorOptimizer;
+  }
+
+  public Collection<SensorWrapper> selectSensors(boolean global) {
+    Collection<Sensor> result = sort(getFilteredExtensions(Sensor.class, null));
+    return result.stream()
+      .map(s -> new SensorWrapper(s, sensorContext, sensorOptimizer))
+      .filter(s -> global == s.isGlobal() && s.shouldExecute())
+      .collect(Collectors.toList());
+  }
+}
index db4f0787172ffa0d68fd2768876aaff3d1d55fdd..5f15ca7bd3192b628ef7fe4ddf28cf9fbfe838e1 100644 (file)
@@ -82,7 +82,7 @@ public class AnalysisResult implements AnalysisObserver {
 
   private void storeFs(ProjectScanContainer container) {
     InputComponentStore inputFileCache = container.getComponentByType(InputComponentStore.class);
-    for (InputFile inputPath : inputFileCache.allFiles()) {
+    for (InputFile inputPath : inputFileCache.inputFiles()) {
       inputFilesByKeys.put(((DefaultInputFile) inputPath).getProjectRelativePath(), inputPath);
     }
   }
index 0690f02f539a4293b3318f3ebc57e1b9ee62e9b4..a47155b9b99eaf304d7379c04423cf432ee180aa 100644 (file)
@@ -29,7 +29,6 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.sonar.api.CoreProperties;
 import org.sonar.api.batch.fs.internal.DefaultInputFile;
-import org.sonar.api.config.Configuration;
 import org.sonar.api.utils.WildcardPattern;
 
 @Immutable
@@ -40,10 +39,10 @@ public abstract class AbstractCoverageExclusions {
 
   private Collection<WildcardPattern> exclusionPatterns;
 
-  public AbstractCoverageExclusions(Configuration config, Function<DefaultInputFile, String> pathExtractor) {
+  public AbstractCoverageExclusions(Function<String, String[]> configProvider, Function<DefaultInputFile, String> pathExtractor) {
     this.pathExtractor = pathExtractor;
     Builder<WildcardPattern> builder = ImmutableList.builder();
-    coverageExclusionConfig = config.getStringArray(CoreProperties.PROJECT_COVERAGE_EXCLUSIONS_PROPERTY);
+    coverageExclusionConfig = configProvider.apply(CoreProperties.PROJECT_COVERAGE_EXCLUSIONS_PROPERTY);
     for (String pattern : coverageExclusionConfig) {
       builder.add(WildcardPattern.create(pattern));
     }
@@ -60,7 +59,7 @@ public abstract class AbstractCoverageExclusions {
     }
   }
 
-  boolean isExcluded(DefaultInputFile file) {
+  public boolean isExcluded(DefaultInputFile file) {
     boolean found = false;
     Iterator<WildcardPattern> iterator = exclusionPatterns.iterator();
     while (!found && iterator.hasNext()) {
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/AbstractModulePhaseExecutor.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/AbstractModulePhaseExecutor.java
deleted file mode 100644 (file)
index 0737c2e..0000000
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2018 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * 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  02110-1301, USA.
- */
-package org.sonar.scanner.phases;
-
-import java.util.Arrays;
-import org.sonar.api.CoreProperties;
-import org.sonar.api.batch.fs.InputFile;
-import org.sonar.api.batch.fs.internal.AbstractProjectOrModule;
-import org.sonar.api.batch.fs.internal.DefaultInputFile;
-import org.sonar.api.batch.fs.internal.DefaultInputModule;
-import org.sonar.api.batch.fs.internal.InputModuleHierarchy;
-import org.sonar.api.notifications.AnalysisWarnings;
-import org.sonar.api.utils.log.Logger;
-import org.sonar.api.utils.log.Loggers;
-import org.sonar.scanner.issue.ignore.scanner.IssueExclusionsLoader;
-import org.sonar.scanner.scan.filesystem.DefaultModuleFileSystem;
-
-public abstract class AbstractModulePhaseExecutor {
-
-  private static final Logger LOG = Loggers.get(AbstractModulePhaseExecutor.class);
-
-  private final PostJobsExecutor postJobsExecutor;
-  private final SensorsExecutor sensorsExecutor;
-  private final DefaultModuleFileSystem fs;
-  private final IssueExclusionsLoader issueExclusionsLoader;
-  private final InputModuleHierarchy hierarchy;
-  private final ModuleCoverageExclusions moduleCoverageExclusions;
-  private final ProjectCoverageExclusions projectCoverageExclusions;
-  private final AnalysisWarnings analysisWarnings;
-  private boolean warnCoverageAlreadyLogged;
-
-  public AbstractModulePhaseExecutor(PostJobsExecutor postJobsExecutor, SensorsExecutor sensorsExecutor, InputModuleHierarchy hierarchy, DefaultModuleFileSystem fs,
-                                     IssueExclusionsLoader issueExclusionsLoader,
-                                     ModuleCoverageExclusions moduleCoverageExclusions, ProjectCoverageExclusions projectCoverageExclusions,
-                                     AnalysisWarnings analysisWarnings) {
-    this.postJobsExecutor = postJobsExecutor;
-    this.sensorsExecutor = sensorsExecutor;
-    this.fs = fs;
-    this.issueExclusionsLoader = issueExclusionsLoader;
-    this.hierarchy = hierarchy;
-    this.moduleCoverageExclusions = moduleCoverageExclusions;
-    this.projectCoverageExclusions = projectCoverageExclusions;
-    this.analysisWarnings = analysisWarnings;
-  }
-
-  /**
-   * Executed on each module
-   */
-  public final void execute(DefaultInputModule module) {
-    // Initialize coverage exclusions
-    evaluateCoverageExclusions(module);
-
-    sensorsExecutor.execute();
-
-    afterSensors();
-
-    if (hierarchy.isRoot(module)) {
-      executeOnRoot();
-      postJobsExecutor.execute();
-    }
-  }
-
-  private void evaluateCoverageExclusions(AbstractProjectOrModule module) {
-    if (!Arrays.equals(moduleCoverageExclusions.getCoverageExclusionConfig(), projectCoverageExclusions.getCoverageExclusionConfig())) {
-      moduleCoverageExclusions.log();
-    }
-    for (InputFile inputFile : fs.inputFiles(fs.predicates().all())) {
-      boolean excludedByProjectConfiguration = projectCoverageExclusions.isExcluded((DefaultInputFile) inputFile);
-      if (excludedByProjectConfiguration) {
-        ((DefaultInputFile) inputFile).setExcludedForCoverage(true);
-        LOG.debug("File {} excluded for coverage", inputFile);
-        continue;
-      }
-      boolean excludedByModuleConfig = moduleCoverageExclusions.isExcluded((DefaultInputFile) inputFile);
-      if (excludedByModuleConfig) {
-        ((DefaultInputFile) inputFile).setExcludedForCoverage(true);
-        if (Arrays.equals(moduleCoverageExclusions.getCoverageExclusionConfig(), projectCoverageExclusions.getCoverageExclusionConfig())) {
-          warnOnce("File '" + inputFile + "' was excluded from coverage because patterns are still evaluated using module relative paths but this is deprecated. " +
-            "Please update '" + CoreProperties.PROJECT_COVERAGE_EXCLUSIONS_PROPERTY + "' configuration so that patterns refer to project relative paths");
-        } else {
-          warnOnce("Defining coverage exclusions at module level is deprecated. " +
-            "Move '" + CoreProperties.PROJECT_COVERAGE_EXCLUSIONS_PROPERTY + "' from module '" + module.getName() + "' " +
-            "to the root project and update patterns to refer to project relative paths");
-        }
-        LOG.debug("File {} excluded for coverage", inputFile);
-      }
-    }
-
-  }
-
-  private void warnOnce(String msg) {
-    if (!warnCoverageAlreadyLogged) {
-      LOG.warn(msg);
-      analysisWarnings.addUnique(msg);
-      warnCoverageAlreadyLogged = true;
-    }
-  }
-
-  protected void afterSensors() {
-  }
-
-  protected abstract void executeOnRoot();
-
-}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/IssuesPhaseExecutor.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/IssuesPhaseExecutor.java
deleted file mode 100644 (file)
index 9c7e956..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2019 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * 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  02110-1301, USA.
- */
-package org.sonar.scanner.phases;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.sonar.api.batch.fs.internal.InputModuleHierarchy;
-import org.sonar.api.notifications.AnalysisWarnings;
-import org.sonar.scanner.issue.ignore.scanner.IssueExclusionsLoader;
-import org.sonar.scanner.issue.tracking.IssueTransition;
-import org.sonar.scanner.scan.filesystem.DefaultModuleFileSystem;
-import org.sonar.scanner.scan.report.IssuesReports;
-
-public final class IssuesPhaseExecutor extends AbstractModulePhaseExecutor {
-
-  private static final Logger LOG = LoggerFactory.getLogger(IssuesPhaseExecutor.class);
-
-  private final IssuesReports issuesReport;
-  private final IssueTransition localIssueTracking;
-
-  public IssuesPhaseExecutor(PostJobsExecutor postJobsExecutor, SensorsExecutor sensorsExecutor,
-    IssuesReports jsonReport, DefaultModuleFileSystem fs,
-    IssueExclusionsLoader issueExclusionsLoader, IssueTransition localIssueTracking, InputModuleHierarchy moduleHierarchy,
-    ModuleCoverageExclusions moduleCoverageExclusions, ProjectCoverageExclusions projectCoverageExclusions,
-    AnalysisWarnings analysisWarnings) {
-    super(postJobsExecutor, sensorsExecutor, moduleHierarchy, fs, issueExclusionsLoader,
-      moduleCoverageExclusions, projectCoverageExclusions, analysisWarnings);
-    this.issuesReport = jsonReport;
-    this.localIssueTracking = localIssueTracking;
-  }
-
-  @Override
-  protected void executeOnRoot() {
-    localIssueTracking.execute();
-    issuesReport.execute();
-    LOG.info("ANALYSIS SUCCESSFUL");
-  }
-}
index dd00766cfe79545f356f89f93a8eec49d8e0346a..ae264222e1956c623a279a05e436164caae9cbcd 100644 (file)
@@ -21,12 +21,17 @@ package org.sonar.scanner.phases;
 
 import javax.annotation.concurrent.Immutable;
 import org.sonar.api.batch.fs.internal.DefaultInputFile;
-import org.sonar.scanner.scan.ModuleConfiguration;
+import org.sonar.api.batch.fs.internal.DefaultInputModule;
+
+import static org.sonar.api.config.internal.MultivalueProperty.parseAsCsv;
 
 @Immutable
 public class ModuleCoverageExclusions extends AbstractCoverageExclusions {
 
-  public ModuleCoverageExclusions(ModuleConfiguration config) {
-    super(config, DefaultInputFile::getModuleRelativePath);
+  public ModuleCoverageExclusions(DefaultInputModule module) {
+    super(k -> {
+      String value = module.properties().get(k);
+      return value != null ? parseAsCsv(k, value) : new String[0];
+    }, DefaultInputFile::getModuleRelativePath);
   }
 }
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/PostJobsExecutor.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/PostJobsExecutor.java
deleted file mode 100644 (file)
index 0ceea00..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2019 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * 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  02110-1301, USA.
- */
-package org.sonar.scanner.phases;
-
-import java.util.Collection;
-import java.util.stream.Collectors;
-import org.sonar.api.batch.ScannerSide;
-import org.sonar.api.utils.log.Logger;
-import org.sonar.api.utils.log.Loggers;
-import org.sonar.scanner.bootstrap.ScannerExtensionDictionnary;
-import org.sonar.scanner.postjob.PostJobWrapper;
-
-@ScannerSide
-public class PostJobsExecutor {
-  private static final Logger LOG = Loggers.get(PostJobsExecutor.class);
-
-  private final ScannerExtensionDictionnary selector;
-
-  public PostJobsExecutor(ScannerExtensionDictionnary selector) {
-    this.selector = selector;
-  }
-
-  public void execute() {
-    Collection<PostJobWrapper> postJobs = selector.selectPostJobs();
-    execute(postJobs);
-  }
-
-  private static void execute(Collection<PostJobWrapper> postJobs) {
-    logPostJobs(postJobs);
-
-    for (PostJobWrapper postJob : postJobs) {
-      if (postJob.shouldExecute()) {
-        LOG.info("Executing post-job '{}'", postJob);
-        postJob.execute();
-      }
-    }
-  }
-
-  private static void logPostJobs(Collection<PostJobWrapper> postJobs) {
-    if (LOG.isDebugEnabled()) {
-      LOG.debug(() -> "Post-jobs : " + postJobs.stream().map(Object::toString).collect(Collectors.joining(" -> ")));
-    }
-  }
-}
index c3167b2adcdc5e7375d7c52afb341b921b6a3d1f..e6ba6a1075372b5de03ea7f9aeab989044396465 100644 (file)
@@ -27,7 +27,7 @@ import org.sonar.scanner.scan.ProjectConfiguration;
 public class ProjectCoverageExclusions extends AbstractCoverageExclusions {
 
   public ProjectCoverageExclusions(ProjectConfiguration projectConfig) {
-    super(projectConfig, DefaultInputFile::getProjectRelativePath);
+    super(projectConfig::getStringArray, DefaultInputFile::getProjectRelativePath);
     log();
   }
 }
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/PublishPhaseExecutor.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/PublishPhaseExecutor.java
deleted file mode 100644 (file)
index 6411b5b..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2019 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * 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  02110-1301, USA.
- */
-package org.sonar.scanner.phases;
-
-import org.sonar.api.batch.fs.internal.InputModuleHierarchy;
-import org.sonar.api.notifications.AnalysisWarnings;
-import org.sonar.scanner.cpd.CpdExecutor;
-import org.sonar.scanner.issue.ignore.scanner.IssueExclusionsLoader;
-import org.sonar.scanner.report.ReportPublisher;
-import org.sonar.scanner.scan.filesystem.DefaultModuleFileSystem;
-import org.sonar.scanner.scm.ScmPublisher;
-
-public final class PublishPhaseExecutor extends AbstractModulePhaseExecutor {
-
-  private final ReportPublisher reportPublisher;
-  private final CpdExecutor cpdExecutor;
-  private final ScmPublisher scm;
-
-  public PublishPhaseExecutor(PostJobsExecutor postJobsExecutor, SensorsExecutor sensorsExecutor,
-    ReportPublisher reportPublisher, DefaultModuleFileSystem fs, IssueExclusionsLoader issueExclusionsLoader,
-    CpdExecutor cpdExecutor, ScmPublisher scm, InputModuleHierarchy hierarchy,
-    ModuleCoverageExclusions moduleCoverageExclusions, ProjectCoverageExclusions projectCoverageExclusions,
-    AnalysisWarnings analysisWarnings) {
-    super(postJobsExecutor, sensorsExecutor, hierarchy, fs, issueExclusionsLoader, moduleCoverageExclusions,
-      projectCoverageExclusions, analysisWarnings);
-    this.reportPublisher = reportPublisher;
-    this.cpdExecutor = cpdExecutor;
-    this.scm = scm;
-  }
-
-  @Override
-  protected void executeOnRoot() {
-    cpdExecutor.execute();
-    reportPublisher.execute();
-  }
-
-  @Override
-  protected void afterSensors() {
-    scm.publish();
-  }
-}
index 94515124d0c46de6fe4922c377832458f50cc6a9..002ebf4207a33c83aee32a8647b527b4be136649 100644 (file)
@@ -23,27 +23,25 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
-import org.sonar.api.batch.ScannerSide;
 import org.sonar.api.batch.fs.internal.DefaultInputModule;
 import org.sonar.api.batch.fs.internal.InputModuleHierarchy;
 import org.sonar.api.batch.fs.internal.SensorStrategy;
 import org.sonar.api.utils.log.Logger;
 import org.sonar.api.utils.log.Loggers;
 import org.sonar.core.util.logs.Profiler;
-import org.sonar.scanner.bootstrap.ScannerExtensionDictionnary;
+import org.sonar.scanner.bootstrap.SensorExtensionDictionnary;
 import org.sonar.scanner.bootstrap.ScannerPluginRepository;
 import org.sonar.scanner.sensor.SensorWrapper;
 
-@ScannerSide
 public class SensorsExecutor {
   private static final Logger LOG = Loggers.get(SensorsExecutor.class);
   private static final Profiler profiler = Profiler.create(LOG);
-  private final ScannerExtensionDictionnary selector;
+  private final SensorExtensionDictionnary selector;
   private final SensorStrategy strategy;
   private final ScannerPluginRepository pluginRepo;
   private final boolean isRoot;
 
-  public SensorsExecutor(ScannerExtensionDictionnary selector, DefaultInputModule module, InputModuleHierarchy hierarchy,
+  public SensorsExecutor(SensorExtensionDictionnary selector, DefaultInputModule module, InputModuleHierarchy hierarchy,
                          SensorStrategy strategy, ScannerPluginRepository pluginRepo) {
     this.selector = selector;
     this.strategy = strategy;
@@ -52,17 +50,18 @@ public class SensorsExecutor {
   }
 
   public void execute() {
-    Collection<SensorWrapper> moduleSensors = selector.selectSensors(false);
+    Collection<SensorWrapper> moduleSensors = new ArrayList<>();
+    withModuleStrategy(() -> moduleSensors.addAll(selector.selectSensors(false)));
     Collection<SensorWrapper> globalSensors = new ArrayList<>();
     if (isRoot) {
-      withGlobalStrategy(() -> globalSensors.addAll(selector.selectSensors(true)));
+      globalSensors.addAll(selector.selectSensors(true));
     }
 
     printSensors(moduleSensors, globalSensors);
-    execute(moduleSensors);
+    withModuleStrategy(() -> execute(moduleSensors));
 
     if (isRoot) {
-      withGlobalStrategy(() -> execute(globalSensors));
+      execute(globalSensors);
     }
   }
 
@@ -74,9 +73,9 @@ public class SensorsExecutor {
     LOG.debug("Sensors : {}", sensors);
   }
 
-  private void withGlobalStrategy(Runnable r) {
+  private void withModuleStrategy(Runnable r) {
     boolean orig = strategy.isGlobal();
-    strategy.setGlobal(true);
+    strategy.setGlobal(false);
     r.run();
     strategy.setGlobal(orig);
   }
index 4b8e7b3772f42681ede918c8461c8c926566c8d2..408ad6385c0a14c88e4bef0896aa104105c5b553 100644 (file)
@@ -35,6 +35,9 @@ public class PostJobWrapper {
     this.optimizer = optimizer;
     this.descriptor = new DefaultPostJobDescriptor();
     newPostJob.describe(descriptor);
+    if (descriptor.name() == null) {
+      descriptor.name(newPostJob.getClass().getName());
+    }
     this.adaptor = adaptor;
   }
 
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/postjob/PostJobsExecutor.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/postjob/PostJobsExecutor.java
new file mode 100644 (file)
index 0000000..0b5a760
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * 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  02110-1301, USA.
+ */
+package org.sonar.scanner.postjob;
+
+import java.util.Collection;
+import java.util.stream.Collectors;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
+import org.sonar.scanner.bootstrap.PostJobExtensionDictionnary;
+
+public class PostJobsExecutor {
+  private static final Logger LOG = Loggers.get(PostJobsExecutor.class);
+
+  private final PostJobExtensionDictionnary selector;
+
+  public PostJobsExecutor(PostJobExtensionDictionnary selector) {
+    this.selector = selector;
+  }
+
+  public void execute() {
+    Collection<PostJobWrapper> postJobs = selector.selectPostJobs();
+    execute(postJobs);
+  }
+
+  private static void execute(Collection<PostJobWrapper> postJobs) {
+    logPostJobs(postJobs);
+
+    for (PostJobWrapper postJob : postJobs) {
+      if (postJob.shouldExecute()) {
+        LOG.info("Executing post-job '{}'", postJob);
+        postJob.execute();
+      }
+    }
+  }
+
+  private static void logPostJobs(Collection<PostJobWrapper> postJobs) {
+    if (LOG.isDebugEnabled()) {
+      LOG.debug(() -> "Post-jobs : " + postJobs.stream().map(Object::toString).collect(Collectors.joining(" -> ")));
+    }
+  }
+}
index 271b84bceeea79d208e18297fc4f078c96893b18..8a7d8c7f1e0842bee3a09d684da6d248e974e81e 100644 (file)
@@ -41,7 +41,7 @@ import org.sonarqube.ws.client.WsResponse;
 public class DefaultProjectRepositoriesLoader implements ProjectRepositoriesLoader {
   private static final Logger LOG = LoggerFactory.getLogger(DefaultProjectRepositoriesLoader.class);
   private static final String BATCH_PROJECT_URL = "/batch/project.protobuf";
-  private ScannerWsClient wsClient;
+  private final ScannerWsClient wsClient;
 
   public DefaultProjectRepositoriesLoader(ScannerWsClient wsClient) {
     this.wsClient = wsClient;
@@ -91,7 +91,7 @@ public class DefaultProjectRepositoriesLoader implements ProjectRepositoriesLoad
     return false;
   }
 
-  private static ProjectRepositories processStream(InputStream is, String projectKey) {
+  private ProjectRepositories processStream(InputStream is, String projectKey) {
     try {
       WsProjectResponse response = WsProjectResponse.parseFrom(is);
       if (response.getFileDataByModuleAndPathCount() == 0) {
index b7ca12593964d0c28af222b9b836fae051a2738c..3d86c58e4763db658e7d1005d0b987dba08cfec8 100644 (file)
@@ -51,7 +51,7 @@ public class QProfileVerifier {
   void execute(Logger logger) {
     String defaultName = config.get(QualityProfiles.SONAR_PROFILE_PROP).orElse(null);
     boolean defaultNameUsed = StringUtils.isBlank(defaultName);
-    for (String lang : store.getLanguages()) {
+    for (String lang : store.languages()) {
       QProfile profile = profiles.findByLanguage(lang);
       if (profile == null) {
         logger.warn("No Quality profile found for language {}", lang);
@@ -62,7 +62,7 @@ public class QProfileVerifier {
         }
       }
     }
-    if (!defaultNameUsed && !store.getLanguages().isEmpty()) {
+    if (!defaultNameUsed && !store.languages().isEmpty()) {
       throw MessageException.of("sonar.profile was set to '" + defaultName + "' but didn't match any profile for any language. Please check your configuration.");
     }
   }
index 1530412636a90744d24d5b4a004f42b59b73a3b1..7ae65e8419c3ccb582339c843a30dc59ab070e52 100644 (file)
@@ -27,23 +27,14 @@ import org.sonar.core.extension.CoreExtensionsInstaller;
 import org.sonar.core.platform.ComponentContainer;
 import org.sonar.scanner.DefaultFileLinesContextFactory;
 import org.sonar.scanner.bootstrap.ExtensionInstaller;
-import org.sonar.scanner.bootstrap.GlobalAnalysisMode;
-import org.sonar.scanner.bootstrap.ScannerExtensionDictionnary;
+import org.sonar.scanner.bootstrap.SensorExtensionDictionnary;
 import org.sonar.scanner.deprecated.perspectives.ScannerPerspectives;
 import org.sonar.scanner.issue.ModuleIssueFilters;
 import org.sonar.scanner.issue.ModuleIssues;
-import org.sonar.scanner.phases.AbstractModulePhaseExecutor;
-import org.sonar.scanner.phases.IssuesPhaseExecutor;
 import org.sonar.scanner.phases.ModuleCoverageExclusions;
-import org.sonar.scanner.phases.PostJobsExecutor;
-import org.sonar.scanner.phases.PublishPhaseExecutor;
 import org.sonar.scanner.phases.SensorsExecutor;
-import org.sonar.scanner.postjob.DefaultPostJobContext;
-import org.sonar.scanner.postjob.PostJobOptimizer;
-import org.sonar.scanner.rule.QProfileVerifier;
 import org.sonar.scanner.scan.filesystem.DefaultModuleFileSystem;
 import org.sonar.scanner.scan.filesystem.ModuleInputComponentStore;
-import org.sonar.scanner.scan.report.IssuesReports;
 import org.sonar.scanner.sensor.DefaultSensorContext;
 import org.sonar.scanner.sensor.DefaultSensorStorage;
 import org.sonar.scanner.sensor.SensorOptimizer;
@@ -56,12 +47,10 @@ import static org.sonar.scanner.bootstrap.ExtensionUtils.isInstantiationStrategy
 public class ModuleScanContainer extends ComponentContainer {
   private static final Logger LOG = LoggerFactory.getLogger(ModuleScanContainer.class);
   private final DefaultInputModule module;
-  private final GlobalAnalysisMode analysisMode;
 
-  public ModuleScanContainer(ProjectScanContainer parent, DefaultInputModule module, GlobalAnalysisMode analysisMode) {
+  public ModuleScanContainer(ProjectScanContainer parent, DefaultInputModule module) {
     super(parent);
     this.module = module;
-    this.analysisMode = analysisMode;
   }
 
   @Override
@@ -76,19 +65,8 @@ public class ModuleScanContainer extends ComponentContainer {
       module.definition(),
       module,
       MutableModuleSettings.class,
-      new ModuleConfigurationProvider());
+      new ModuleConfigurationProvider(),
 
-    if (analysisMode.isIssues()) {
-      add(
-        IssuesPhaseExecutor.class,
-        IssuesReports.class);
-    } else {
-      add(
-        PublishPhaseExecutor.class);
-    }
-
-    add(
-      PostJobsExecutor.class,
       SensorsExecutor.class,
 
       // file system
@@ -97,12 +75,10 @@ public class ModuleScanContainer extends ComponentContainer {
       DefaultModuleFileSystem.class,
 
       SensorOptimizer.class,
-      PostJobOptimizer.class,
 
-      DefaultPostJobContext.class,
       DefaultSensorStorage.class,
       DefaultSensorContext.class,
-      ScannerExtensionDictionnary.class,
+      SensorExtensionDictionnary.class,
       ModuleIssueFilters.class,
       ModuleCoverageExclusions.class,
 
@@ -124,7 +100,7 @@ public class ModuleScanContainer extends ComponentContainer {
 
   @Override
   protected void doAfterStart() {
-    getComponentByType(AbstractModulePhaseExecutor.class).execute(module);
+    getComponentByType(SensorsExecutor.class).execute();
   }
 
 }
index baef9c2cac480aecd489ec77c247467aa25eba46..318e6af23d075a988a97b1a94277046d707f3ef6 100644 (file)
@@ -43,6 +43,7 @@ import org.sonar.scanner.bootstrap.ExtensionInstaller;
 import org.sonar.scanner.bootstrap.ExtensionMatcher;
 import org.sonar.scanner.bootstrap.GlobalAnalysisMode;
 import org.sonar.scanner.bootstrap.MetricProvider;
+import org.sonar.scanner.bootstrap.PostJobExtensionDictionnary;
 import org.sonar.scanner.cpd.CpdExecutor;
 import org.sonar.scanner.cpd.CpdSettings;
 import org.sonar.scanner.cpd.JavaCpdBlockIndexerSensor;
@@ -65,6 +66,9 @@ import org.sonar.scanner.issue.tracking.ServerLineHashesLoader;
 import org.sonar.scanner.mediumtest.AnalysisObservers;
 import org.sonar.scanner.notifications.DefaultAnalysisWarnings;
 import org.sonar.scanner.phases.ProjectCoverageExclusions;
+import org.sonar.scanner.postjob.DefaultPostJobContext;
+import org.sonar.scanner.postjob.PostJobOptimizer;
+import org.sonar.scanner.postjob.PostJobsExecutor;
 import org.sonar.scanner.report.ActiveRulesPublisher;
 import org.sonar.scanner.report.AnalysisContextReportPublisher;
 import org.sonar.scanner.report.AnalysisWarningsPublisher;
@@ -100,6 +104,7 @@ import org.sonar.scanner.scan.branch.BranchConfigurationProvider;
 import org.sonar.scanner.scan.branch.BranchType;
 import org.sonar.scanner.scan.branch.ProjectBranchesProvider;
 import org.sonar.scanner.scan.branch.ProjectPullRequestsProvider;
+import org.sonar.scanner.scan.filesystem.DefaultProjectFileSystem;
 import org.sonar.scanner.scan.filesystem.FileIndexer;
 import org.sonar.scanner.scan.filesystem.InputComponentStore;
 import org.sonar.scanner.scan.filesystem.LanguageDetection;
@@ -110,7 +115,10 @@ import org.sonar.scanner.scan.filesystem.ScannerComponentIdGenerator;
 import org.sonar.scanner.scan.filesystem.StatusDetection;
 import org.sonar.scanner.scan.measure.DefaultMetricFinder;
 import org.sonar.scanner.scan.measure.MeasureCache;
+import org.sonar.scanner.scan.report.JSONReport;
 import org.sonar.scanner.scm.ScmChangedFilesProvider;
+import org.sonar.scanner.scm.ScmConfiguration;
+import org.sonar.scanner.scm.ScmPublisher;
 import org.sonar.scanner.storage.Storages;
 
 import static org.sonar.api.batch.InstantiationStrategy.PER_BATCH;
@@ -183,7 +191,6 @@ public class ProjectScanContainer extends ComponentContainer {
       ProjectFileIndexer.class,
       ProjectExclusionFilters.class,
 
-
       // rules
       new ActiveRulesProvider(),
       new QualityProfilesProvider(),
@@ -246,6 +253,19 @@ public class ProjectScanContainer extends ComponentContainer {
       SonarCpdBlockIndex.class,
       JavaCpdBlockIndexerSensor.class,
 
+      // PostJobs
+      PostJobsExecutor.class,
+      PostJobOptimizer.class,
+      DefaultPostJobContext.class,
+      PostJobExtensionDictionnary.class,
+
+      // SCM
+      ScmConfiguration.class,
+      ScmPublisher.class,
+
+      // Filesystem
+      DefaultProjectFileSystem.class,
+
       AnalysisObservers.class);
 
     addIfMissing(DefaultRulesLoader.class, RulesLoader.class);
@@ -323,7 +343,21 @@ public class ProjectScanContainer extends ComponentContainer {
     getComponentByType(QProfileVerifier.class).execute();
 
     LOG.debug("Start recursive analysis of project modules");
-    scanRecursively(tree, tree.root(), analysisMode);
+    scanRecursively(tree, tree.root());
+
+    getComponentByType(ScmPublisher.class).publish();
+
+    if (analysisMode.isIssues()) {
+      getComponentByType(IssueTransition.class).execute();
+      getComponentByType(JSONReport.class).execute();
+    } else {
+      getComponentByType(CpdExecutor.class).execute();
+      getComponentByType(ReportPublisher.class).execute();
+    }
+
+    getComponentByType(PostJobsExecutor.class).execute();
+
+    LOG.info("ANALYSIS SUCCESSFUL");
 
     if (analysisMode.isMediumTest()) {
       getComponentByType(AnalysisObservers.class).notifyEndOfScanTask();
@@ -345,16 +379,16 @@ public class ProjectScanContainer extends ComponentContainer {
     }
   }
 
-  private void scanRecursively(InputModuleHierarchy tree, DefaultInputModule module, GlobalAnalysisMode analysisMode) {
+  private void scanRecursively(InputModuleHierarchy tree, DefaultInputModule module) {
     for (DefaultInputModule child : tree.children(module)) {
-      scanRecursively(tree, child, analysisMode);
+      scanRecursively(tree, child);
     }
-    scan(module, analysisMode);
+    scan(module);
   }
 
   @VisibleForTesting
-  void scan(DefaultInputModule module, GlobalAnalysisMode analysisMode) {
-    new ModuleScanContainer(this, module, analysisMode).execute();
+  void scan(DefaultInputModule module) {
+    new ModuleScanContainer(this, module).execute();
   }
 
 }
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/DefaultProjectFileSystem.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/DefaultProjectFileSystem.java
new file mode 100644 (file)
index 0000000..5c42922
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * 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  02110-1301, USA.
+ */
+package org.sonar.scanner.scan.filesystem;
+
+import com.google.common.annotations.VisibleForTesting;
+import org.sonar.api.batch.fs.internal.DefaultFileSystem;
+import org.sonar.api.batch.fs.internal.DefaultInputProject;
+import org.sonar.scanner.analysis.DefaultAnalysisMode;
+
+public class DefaultProjectFileSystem extends DefaultFileSystem {
+
+  public DefaultProjectFileSystem(InputComponentStore inputComponentStore, DefaultInputProject project, DefaultAnalysisMode mode,
+    StatusDetection statusDetection) {
+    super(project.getBaseDir(), inputComponentStore);
+    setFields(project, mode, statusDetection);
+  }
+
+  @VisibleForTesting
+  public DefaultProjectFileSystem(DefaultInputProject project, DefaultAnalysisMode mode, StatusDetection statusDetection) {
+    super(project.getBaseDir());
+    setFields(project, mode, statusDetection);
+  }
+
+  private void setFields(DefaultInputProject project, DefaultAnalysisMode mode, StatusDetection statusDetection) {
+    setWorkDir(project.getWorkDir());
+    setEncoding(project.getEncoding());
+
+    // filter the files sensors have access to
+    if (!mode.scanAllFiles()) {
+      setDefaultPredicate(p -> new SameInputFilePredicate(p, statusDetection, project.definition().getKeyWithBranch()));
+    }
+  }
+
+}
index 86c11738f1bf90427893bbe95bc5031580736960..801a68e4070733ef09a36ad581f12f4a6d02113f 100644 (file)
@@ -22,7 +22,9 @@ package org.sonar.scanner.scan.filesystem;
 import java.io.IOException;
 import java.nio.file.LinkOption;
 import java.nio.file.Path;
+import java.util.Arrays;
 import java.util.concurrent.atomic.AtomicInteger;
+import org.sonar.api.CoreProperties;
 import org.sonar.api.batch.fs.InputFile;
 import org.sonar.api.batch.fs.InputFile.Type;
 import org.sonar.api.batch.fs.InputFileFilter;
@@ -36,6 +38,8 @@ import org.sonar.api.utils.MessageException;
 import org.sonar.api.utils.log.Logger;
 import org.sonar.api.utils.log.Loggers;
 import org.sonar.scanner.issue.ignore.scanner.IssueExclusionsLoader;
+import org.sonar.scanner.phases.ModuleCoverageExclusions;
+import org.sonar.scanner.phases.ProjectCoverageExclusions;
 import org.sonar.scanner.scan.ScanProperties;
 import org.sonar.scanner.util.ProgressReport;
 
@@ -49,6 +53,7 @@ public class FileIndexer {
   private final ScanProperties properties;
   private final InputFileFilter[] filters;
   private final ProjectExclusionFilters projectExclusionFilters;
+  private final ProjectCoverageExclusions projectCoverageExclusions;
   private final IssueExclusionsLoader issueExclusionsLoader;
   private final MetadataGenerator metadataGenerator;
   private final DefaultInputProject project;
@@ -58,15 +63,16 @@ public class FileIndexer {
   private final LanguageDetection langDetection;
 
   private boolean warnExclusionsAlreadyLogged;
+  private boolean warnCoverageExclusionsAlreadyLogged;
 
-  public FileIndexer(DefaultInputProject project, ScannerComponentIdGenerator scannerComponentIdGenerator, InputComponentStore componentStore, ProjectExclusionFilters projectExclusionFilters,
-    IssueExclusionsLoader issueExclusionsLoader,
-    MetadataGenerator metadataGenerator, SensorStrategy sensorStrategy,
-    LanguageDetection languageDetection, AnalysisWarnings analysisWarnings, ScanProperties properties,
+  public FileIndexer(DefaultInputProject project, ScannerComponentIdGenerator scannerComponentIdGenerator, InputComponentStore componentStore,
+    ProjectExclusionFilters projectExclusionFilters, ProjectCoverageExclusions projectCoverageExclusions, IssueExclusionsLoader issueExclusionsLoader,
+    MetadataGenerator metadataGenerator, SensorStrategy sensorStrategy, LanguageDetection languageDetection, AnalysisWarnings analysisWarnings, ScanProperties properties,
     InputFileFilter[] filters) {
     this.project = project;
     this.scannerComponentIdGenerator = scannerComponentIdGenerator;
     this.componentStore = componentStore;
+    this.projectCoverageExclusions = projectCoverageExclusions;
     this.issueExclusionsLoader = issueExclusionsLoader;
     this.metadataGenerator = metadataGenerator;
     this.sensorStrategy = sensorStrategy;
@@ -77,15 +83,18 @@ public class FileIndexer {
     this.projectExclusionFilters = projectExclusionFilters;
   }
 
-  public FileIndexer(DefaultInputProject project, ScannerComponentIdGenerator scannerComponentIdGenerator, InputComponentStore componentStore, ProjectExclusionFilters projectExclusionFilters,
-    IssueExclusionsLoader issueExclusionsLoader,
+  public FileIndexer(DefaultInputProject project, ScannerComponentIdGenerator scannerComponentIdGenerator, InputComponentStore componentStore,
+    ProjectExclusionFilters projectExclusionFilters, ProjectCoverageExclusions projectCoverageExclusions, IssueExclusionsLoader issueExclusionsLoader,
     MetadataGenerator metadataGenerator, SensorStrategy sensorStrategy, LanguageDetection languageDetection, AnalysisWarnings analysisWarnings, ScanProperties properties) {
-    this(project, scannerComponentIdGenerator, componentStore, projectExclusionFilters, issueExclusionsLoader, metadataGenerator, sensorStrategy, languageDetection, analysisWarnings,
+    this(project, scannerComponentIdGenerator, componentStore, projectExclusionFilters, projectCoverageExclusions, issueExclusionsLoader, metadataGenerator, sensorStrategy,
+      languageDetection,
+      analysisWarnings,
       properties, new InputFileFilter[0]);
   }
 
-  public void indexFile(DefaultInputModule module, AbstractExclusionFilters moduleExclusionFilters, Path sourceFile, InputFile.Type type, ProgressReport progressReport,
-                        AtomicInteger excludedByPatternsCount)
+  public void indexFile(DefaultInputModule module, ModuleExclusionFilters moduleExclusionFilters, ModuleCoverageExclusions moduleCoverageExclusions, Path sourceFile,
+    InputFile.Type type, ProgressReport progressReport,
+    AtomicInteger excludedByPatternsCount)
     throws IOException {
     // get case of real file without resolving link
     Path realAbsoluteFile = sourceFile.toRealPath(LinkOption.NOFOLLOW_LINKS).toAbsolutePath().normalize();
@@ -101,10 +110,10 @@ public class FileIndexer {
     }
     if (!moduleExclusionFilters.accept(realAbsoluteFile, moduleRelativePath, type)) {
       if (projectExclusionFilters.equals(moduleExclusionFilters)) {
-        warnOnce("File '" + projectRelativePath + "' was excluded because patterns are still evaluated using module relative paths but this is deprecated. " +
+        warnOnceDeprecatedExclusion("File '" + projectRelativePath + "' was excluded because patterns are still evaluated using module relative paths but this is deprecated. " +
           "Please update file inclusion/exclusion configuration so that patterns refer to project relative paths.");
       } else {
-        warnOnce("Defining inclusion/exclusions at module level is deprecated. " +
+        warnOnceDeprecatedExclusion("Defining inclusion/exclusions at module level is deprecated. " +
           "Move file inclusion/exclusion configuration from module '" + module.getName() + "' " +
           "to the root project and update patterns to refer to project relative paths.");
       }
@@ -127,23 +136,48 @@ public class FileIndexer {
     if (!accept(inputFile)) {
       return;
     }
-    if (componentStore.getFile(inputFile.getProjectRelativePath()) != null) {
-      throw MessageException.of("File " + inputFile + " can't be indexed twice. Please check that inclusion/exclusion patterns produce "
-        + "disjoint sets for main and test files");
-    }
+    checkIfAlreadyIndexed(inputFile);
     componentStore.put(module.key(), inputFile);
     if (issueExclusionsLoader.shouldExecute()) {
       issueExclusionsLoader.addMulticriteriaPatterns(inputFile.getProjectRelativePath(), inputFile.key());
     }
     LOG.debug("'{}' indexed {}with language '{}'", projectRelativePath, type == Type.TEST ? "as test " : "", inputFile.language());
+    evaluateCoverageExclusions(module, moduleCoverageExclusions, inputFile);
     if (properties.preloadFileMetadata()) {
       inputFile.checkMetadata();
     }
-    int count = componentStore.allFiles().size();
+    int count = componentStore.inputFiles().size();
     progressReport.message(count + " " + pluralizeFiles(count) + " indexed...  (last one was " + inputFile.getProjectRelativePath() + ")");
   }
 
-  private void warnOnce(String msg) {
+  private void checkIfAlreadyIndexed(DefaultInputFile inputFile) {
+    if (componentStore.inputFile(inputFile.getProjectRelativePath()) != null) {
+      throw MessageException.of("File " + inputFile + " can't be indexed twice. Please check that inclusion/exclusion patterns produce "
+        + "disjoint sets for main and test files");
+    }
+  }
+
+  private void evaluateCoverageExclusions(DefaultInputModule module, ModuleCoverageExclusions moduleCoverageExclusions, DefaultInputFile inputFile) {
+    boolean excludedByProjectConfiguration = projectCoverageExclusions.isExcluded(inputFile);
+    if (excludedByProjectConfiguration) {
+      inputFile.setExcludedForCoverage(true);
+      LOG.debug("File {} excluded for coverage", inputFile);
+    } else if (moduleCoverageExclusions.isExcluded(inputFile)) {
+      inputFile.setExcludedForCoverage(true);
+      if (Arrays.equals(moduleCoverageExclusions.getCoverageExclusionConfig(), projectCoverageExclusions.getCoverageExclusionConfig())) {
+        warnOnceDeprecatedCoverageExclusion(
+          "File '" + inputFile + "' was excluded from coverage because patterns are still evaluated using module relative paths but this is deprecated. " +
+            "Please update '" + CoreProperties.PROJECT_COVERAGE_EXCLUSIONS_PROPERTY + "' configuration so that patterns refer to project relative paths");
+      } else {
+        warnOnceDeprecatedCoverageExclusion("Defining coverage exclusions at module level is deprecated. " +
+          "Move '" + CoreProperties.PROJECT_COVERAGE_EXCLUSIONS_PROPERTY + "' from module '" + module.getName() + "' " +
+          "to the root project and update patterns to refer to project relative paths");
+      }
+      LOG.debug("File {} excluded for coverage", inputFile);
+    }
+  }
+
+  private void warnOnceDeprecatedExclusion(String msg) {
     if (!warnExclusionsAlreadyLogged) {
       LOG.warn(msg);
       analysisWarnings.addUnique(msg);
@@ -151,6 +185,14 @@ public class FileIndexer {
     }
   }
 
+  private void warnOnceDeprecatedCoverageExclusion(String msg) {
+    if (!warnCoverageExclusionsAlreadyLogged) {
+      LOG.warn(msg);
+      analysisWarnings.addUnique(msg);
+      warnCoverageExclusionsAlreadyLogged = true;
+    }
+  }
+
   private boolean accept(InputFile indexedFile) {
     // InputFileFilter extensions. Might trigger generation of metadata
     for (InputFileFilter filter : filters) {
index 6cc08a44ad45db6f5f1748061c5a7477b1e1960f..4a92f584a307c259990c1d224aea779db15cc6f9 100644 (file)
@@ -34,6 +34,7 @@ import java.util.stream.Stream;
 import javax.annotation.CheckForNull;
 import org.sonar.api.batch.fs.InputComponent;
 import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.internal.DefaultFileSystem;
 import org.sonar.api.batch.fs.internal.DefaultInputFile;
 import org.sonar.api.batch.fs.internal.DefaultInputModule;
 import org.sonar.api.batch.fs.internal.FileExtensionPredicate;
@@ -43,7 +44,7 @@ import org.sonar.scanner.scan.branch.BranchConfiguration;
  * Store of all files and dirs. Inclusion and
  * exclusion patterns are already applied.
  */
-public class InputComponentStore {
+public class InputComponentStore extends DefaultFileSystem.Cache {
 
   private final SortedSet<String> globalLanguagesCache = new TreeSet<>();
   private final Map<String, SortedSet<String>> languagesCache = new HashMap<>();
@@ -65,7 +66,7 @@ public class InputComponentStore {
   }
 
   private Stream<DefaultInputFile> allFilesToPublishStream() {
-    return inputFileByModuleCache.values().stream()
+    return globalInputFileCache.values().stream()
       .map(f -> (DefaultInputFile) f)
       .filter(DefaultInputFile::isPublished);
   }
@@ -80,7 +81,8 @@ public class InputComponentStore {
       ::iterator;
   }
 
-  public Collection<InputFile> allFiles() {
+  @Override
+  public Collection<InputFile> inputFiles() {
     return globalInputFileCache.values();
   }
 
@@ -116,8 +118,9 @@ public class InputComponentStore {
     return inputFileByModuleCache.get(moduleKey, relativePath);
   }
 
+  @Override
   @CheckForNull
-  public InputFile getFile(String relativePath) {
+  public InputFile inputFile(String relativePath) {
     return globalInputFileCache.get(relativePath);
   }
 
@@ -126,6 +129,17 @@ public class InputComponentStore {
     return inputModuleCache.get(moduleKeyWithBranch);
   }
 
+  @CheckForNull
+  public DefaultInputModule findModule(DefaultInputFile file) {
+    return inputFileByModuleCache
+      .cellSet()
+      .stream()
+      .filter(c -> c.getValue().equals(file))
+      .findFirst()
+      .map(c -> (DefaultInputModule) inputComponents.get(c.getRowKey()))
+      .orElse(null);
+  }
+
   public void put(DefaultInputModule inputModule) {
     String key = inputModule.key();
     String keyWithBranch = inputModule.getKeyWithBranch();
@@ -136,23 +150,32 @@ public class InputComponentStore {
     inputModuleCache.put(keyWithBranch, inputModule);
   }
 
+  @Override
   public Iterable<InputFile> getFilesByName(String filename) {
     return filesByNameCache.get(filename);
   }
 
+  @Override
   public Iterable<InputFile> getFilesByExtension(String extension) {
     return filesByExtensionCache.get(extension);
   }
 
-  public SortedSet<String> getLanguages() {
+  @Override
+  public SortedSet<String> languages() {
     return globalLanguagesCache;
   }
 
-  public SortedSet<String> getLanguages(String moduleKey) {
+  public SortedSet<String> languages(String moduleKey) {
     return languagesCache.getOrDefault(moduleKey, Collections.emptySortedSet());
   }
 
   public Collection<DefaultInputModule> allModules() {
     return inputModuleCache.values();
   }
+
+  @Override
+  protected void doAdd(InputFile inputFile) {
+    throw new UnsupportedOperationException();
+  }
+
 }
index 6bb17759043805c7b341f71d5c30e06d42ef0dfa..c9d1d3c115ea2f04e2d543693b0d009597e07e4e 100644 (file)
@@ -42,7 +42,7 @@ public class ModuleInputComponentStore extends DefaultFileSystem.Cache {
   @Override
   public Iterable<InputFile> inputFiles() {
     if (strategy.isGlobal()) {
-      return inputComponentStore.allFiles();
+      return inputComponentStore.inputFiles();
     } else {
       return inputComponentStore.filesByModule(moduleKey);
     }
@@ -51,7 +51,7 @@ public class ModuleInputComponentStore extends DefaultFileSystem.Cache {
   @Override
   public InputFile inputFile(String relativePath) {
     if (strategy.isGlobal()) {
-      return inputComponentStore.getFile(relativePath);
+      return inputComponentStore.inputFile(relativePath);
     } else {
       return inputComponentStore.getFile(moduleKey, relativePath);
     }
@@ -60,9 +60,9 @@ public class ModuleInputComponentStore extends DefaultFileSystem.Cache {
   @Override
   public SortedSet<String> languages() {
     if (strategy.isGlobal()) {
-      return inputComponentStore.getLanguages();
+      return inputComponentStore.languages();
     } else {
-      return inputComponentStore.getLanguages(moduleKey);
+      return inputComponentStore.languages(moduleKey);
     }
   }
 
index 4d51181e2c891d959ac247e0e825ce36dae4c0e6..bae2a44d47cc6ce183ab7472f4ef9ed96143dd06 100644 (file)
@@ -28,6 +28,7 @@ import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.attribute.BasicFileAttributes;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -37,6 +38,8 @@ import org.sonar.api.batch.fs.InputFile.Type;
 import org.sonar.api.batch.fs.internal.DefaultInputModule;
 import org.sonar.api.batch.fs.internal.DefaultInputProject;
 import org.sonar.api.batch.fs.internal.InputModuleHierarchy;
+import org.sonar.scanner.phases.ModuleCoverageExclusions;
+import org.sonar.scanner.phases.ProjectCoverageExclusions;
 import org.sonar.scanner.util.ProgressReport;
 
 /**
@@ -49,16 +52,18 @@ public class ProjectFileIndexer {
   private final DefaultInputProject project;
   private final InputComponentStore componentStore;
   private final InputModuleHierarchy inputModuleHierarchy;
+  private final ProjectCoverageExclusions projectCoverageExclusions;
   private final FileIndexer fileIndexer;
 
   private ProgressReport progressReport;
 
   public ProjectFileIndexer(DefaultInputProject project, InputComponentStore componentStore, AbstractExclusionFilters exclusionFilters,
-    InputModuleHierarchy inputModuleHierarchy,
+    InputModuleHierarchy inputModuleHierarchy, ProjectCoverageExclusions projectCoverageExclusions,
     FileIndexer fileIndexer) {
     this.project = project;
     this.componentStore = componentStore;
     this.inputModuleHierarchy = inputModuleHierarchy;
+    this.projectCoverageExclusions = projectCoverageExclusions;
     this.fileIndexer = fileIndexer;
     this.projectExclusionFilters = exclusionFilters;
   }
@@ -71,7 +76,7 @@ public class ProjectFileIndexer {
 
     indexModulesRecursively(inputModuleHierarchy.root(), excludedByPatternsCount);
 
-    int totalIndexed = componentStore.allFiles().size();
+    int totalIndexed = componentStore.inputFiles().size();
     progressReport.stop(totalIndexed + " " + pluralizeFiles(totalIndexed) + " indexed");
 
     if (projectExclusionFilters.hasPattern()) {
@@ -81,27 +86,32 @@ public class ProjectFileIndexer {
   }
 
   private void indexModulesRecursively(DefaultInputModule module, AtomicInteger excludedByPatternsCount) {
-    inputModuleHierarchy.children(module).forEach(m -> indexModulesRecursively(m, excludedByPatternsCount));
+    inputModuleHierarchy.children(module).stream().sorted(Comparator.comparing(DefaultInputModule::key)).forEach(m -> indexModulesRecursively(m, excludedByPatternsCount));
     index(module, excludedByPatternsCount);
   }
 
   private void index(DefaultInputModule module, AtomicInteger excludedByPatternsCount) {
+    if (componentStore.allModules().size() > 1) {
+      LOG.info("Indexing files from module {}", module.getName());
+    }
     ModuleExclusionFilters moduleExclusionFilters = new ModuleExclusionFilters(module);
-    indexFiles(module, moduleExclusionFilters, module.getSourceDirsOrFiles(), Type.MAIN, excludedByPatternsCount);
-    indexFiles(module, moduleExclusionFilters, module.getTestDirsOrFiles(), Type.TEST, excludedByPatternsCount);
+    ModuleCoverageExclusions moduleCoverageExclusions = new ModuleCoverageExclusions(module);
+    indexFiles(module, moduleExclusionFilters, moduleCoverageExclusions, module.getSourceDirsOrFiles(), Type.MAIN, excludedByPatternsCount);
+    indexFiles(module, moduleExclusionFilters, moduleCoverageExclusions, module.getTestDirsOrFiles(), Type.TEST, excludedByPatternsCount);
   }
 
   private static String pluralizeFiles(int count) {
     return count == 1 ? "file" : "files";
   }
 
-  private void indexFiles(DefaultInputModule module, AbstractExclusionFilters moduleExclusionFilters, List<Path> sources, Type type, AtomicInteger excludedByPatternsCount) {
+  private void indexFiles(DefaultInputModule module, ModuleExclusionFilters moduleExclusionFilters, ModuleCoverageExclusions moduleCoverageExclusions, List<Path> sources,
+    Type type, AtomicInteger excludedByPatternsCount) {
     try {
       for (Path dirOrFile : sources) {
         if (dirOrFile.toFile().isDirectory()) {
-          indexDirectory(module, moduleExclusionFilters, dirOrFile, type, excludedByPatternsCount);
+          indexDirectory(module, moduleExclusionFilters, moduleCoverageExclusions, dirOrFile, type, excludedByPatternsCount);
         } else {
-          fileIndexer.indexFile(module, moduleExclusionFilters, dirOrFile, type, progressReport, excludedByPatternsCount);
+          fileIndexer.indexFile(module, moduleExclusionFilters, moduleCoverageExclusions, dirOrFile, type, progressReport, excludedByPatternsCount);
         }
       }
     } catch (IOException e) {
@@ -109,21 +119,25 @@ public class ProjectFileIndexer {
     }
   }
 
-  private void indexDirectory(DefaultInputModule module, AbstractExclusionFilters moduleExclusionFilters, Path dirToIndex, Type type, AtomicInteger excludedByPatternsCount)
+  private void indexDirectory(DefaultInputModule module, ModuleExclusionFilters moduleExclusionFilters, ModuleCoverageExclusions moduleCoverageExclusions, Path dirToIndex,
+    Type type, AtomicInteger excludedByPatternsCount)
     throws IOException {
     Files.walkFileTree(dirToIndex.normalize(), Collections.singleton(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE,
-      new IndexFileVisitor(module, moduleExclusionFilters, type, excludedByPatternsCount));
+      new IndexFileVisitor(module, moduleExclusionFilters, moduleCoverageExclusions, type, excludedByPatternsCount));
   }
 
   private class IndexFileVisitor implements FileVisitor<Path> {
     private final DefaultInputModule module;
-    private final AbstractExclusionFilters moduleExclusionFilters;
+    private final ModuleExclusionFilters moduleExclusionFilters;
+    private final ModuleCoverageExclusions moduleCoverageExclusions;
     private final Type type;
     private final AtomicInteger excludedByPatternsCount;
 
-    IndexFileVisitor(DefaultInputModule module, AbstractExclusionFilters moduleExclusionFilters, Type type, AtomicInteger excludedByPatternsCount) {
+    IndexFileVisitor(DefaultInputModule module, ModuleExclusionFilters moduleExclusionFilters, ModuleCoverageExclusions moduleCoverageExclusions, Type type,
+      AtomicInteger excludedByPatternsCount) {
       this.module = module;
       this.moduleExclusionFilters = moduleExclusionFilters;
+      this.moduleCoverageExclusions = moduleCoverageExclusions;
       this.type = type;
       this.excludedByPatternsCount = excludedByPatternsCount;
     }
@@ -144,7 +158,7 @@ public class ProjectFileIndexer {
     @Override
     public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
       if (!Files.isHidden(file)) {
-        fileIndexer.indexFile(module, moduleExclusionFilters, file, type, progressReport, excludedByPatternsCount);
+        fileIndexer.indexFile(module, moduleExclusionFilters, moduleCoverageExclusions, file, type, progressReport, excludedByPatternsCount);
       }
       return FileVisitResult.CONTINUE;
     }
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/report/IssuesReports.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/report/IssuesReports.java
deleted file mode 100644 (file)
index b321aab..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2019 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * 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  02110-1301, USA.
- */
-package org.sonar.scanner.scan.report;
-
-import org.sonar.api.batch.ScannerSide;
-
-@ScannerSide
-public class IssuesReports {
-
-  private final Reporter[] reporters;
-
-  public IssuesReports(Reporter... reporters) {
-    this.reporters = reporters;
-  }
-
-  public void execute() {
-    for (Reporter reporter : reporters) {
-      reporter.execute();
-    }
-  }
-}
index d965bb3e90859467abd479d18bf6cff74450e632..50f53f047bf93902dedc07784cbe1254b5601372 100644 (file)
@@ -45,6 +45,7 @@ import org.sonar.api.batch.rule.Rules;
 import org.sonar.api.config.Configuration;
 import org.sonar.api.platform.Server;
 import org.sonar.api.rule.RuleKey;
+import org.sonar.api.scanner.ScannerSide;
 import org.sonar.api.utils.text.JsonWriter;
 import org.sonar.core.component.ComponentKeys;
 import org.sonar.scanner.issue.IssueCache;
@@ -57,7 +58,8 @@ import org.sonar.scanner.scan.filesystem.InputComponentStore;
     name = "Report Results Export File",
     type = PropertyType.STRING,
     global = false, project = false)})
-public class JSONReport implements Reporter {
+@ScannerSide
+public class JSONReport {
 
   static final String SONAR_REPORT_EXPORT_PATH = "sonar.report.export.path";
   private static final Logger LOG = LoggerFactory.getLogger(JSONReport.class);
@@ -80,7 +82,6 @@ public class JSONReport implements Reporter {
     this.componentStore = componentStore;
   }
 
-  @Override
   public void execute() {
     settings.get(SONAR_REPORT_EXPORT_PATH).ifPresent(this::exportResults);
   }
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/report/Reporter.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/report/Reporter.java
deleted file mode 100644 (file)
index 63babb6..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2019 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * 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  02110-1301, USA.
- */
-package org.sonar.scanner.scan.report;
-
-import org.sonar.api.batch.ScannerSide;
-
-@ScannerSide
-@FunctionalInterface
-public interface Reporter {
-
-  void execute();
-
-}
index dcdb8909a21ee509ff288d8a590cb8d71be43045..05737631ae2de39fabe3f3aaa4ec1a8cf8536f43 100644 (file)
@@ -30,8 +30,6 @@ import org.sonar.api.Properties;
 import org.sonar.api.Property;
 import org.sonar.api.PropertyType;
 import org.sonar.api.batch.AnalysisMode;
-import org.sonar.api.batch.InstantiationStrategy;
-import org.sonar.api.batch.ScannerSide;
 import org.sonar.api.batch.fs.internal.InputModuleHierarchy;
 import org.sonar.api.batch.scm.ScmProvider;
 import org.sonar.api.config.Configuration;
@@ -55,8 +53,6 @@ import static org.sonar.api.CoreProperties.SCM_PROVIDER_KEY;
     global = false,
     type = PropertyType.BOOLEAN)
 })
-@InstantiationStrategy(InstantiationStrategy.PER_BATCH)
-@ScannerSide
 public class ScmConfiguration implements Startable {
   private static final Logger LOG = Loggers.get(ScmConfiguration.class);
 
index 424f3ee1459715b28eae6b9462243748b058f6b6..4abb86427d5c238e779e324547bdc1903d300ddf 100644 (file)
@@ -23,11 +23,9 @@ import java.util.LinkedList;
 import java.util.List;
 import org.apache.commons.lang.StringUtils;
 import org.sonar.api.CoreProperties;
-import org.sonar.api.batch.InstantiationStrategy;
-import org.sonar.api.batch.ScannerSide;
+import org.sonar.api.batch.fs.FileSystem;
 import org.sonar.api.batch.fs.InputFile;
 import org.sonar.api.batch.fs.InputFile.Status;
-import org.sonar.api.batch.fs.internal.AbstractProjectOrModule;
 import org.sonar.api.batch.fs.internal.DefaultInputFile;
 import org.sonar.api.batch.scm.ScmProvider;
 import org.sonar.api.utils.log.Logger;
@@ -39,26 +37,21 @@ import org.sonar.scanner.report.ReportPublisher;
 import org.sonar.scanner.repository.FileData;
 import org.sonar.scanner.repository.ProjectRepositories;
 import org.sonar.scanner.scan.branch.BranchConfiguration;
-import org.sonar.scanner.scan.filesystem.DefaultModuleFileSystem;
-import org.sonar.scanner.scan.filesystem.ModuleInputComponentStore;
+import org.sonar.scanner.scan.filesystem.InputComponentStore;
 
-@InstantiationStrategy(InstantiationStrategy.PER_PROJECT)
-@ScannerSide
 public final class ScmPublisher {
 
   private static final Logger LOG = Loggers.get(ScmPublisher.class);
 
-  private final AbstractProjectOrModule inputModule;
   private final ScmConfiguration configuration;
   private final ProjectRepositories projectRepositories;
-  private final ModuleInputComponentStore componentStore;
-  private final DefaultModuleFileSystem fs;
+  private final InputComponentStore componentStore;
+  private final FileSystem fs;
   private final ScannerReportWriter writer;
   private final BranchConfiguration branchConfiguration;
 
-  public ScmPublisher(AbstractProjectOrModule inputModule, ScmConfiguration configuration, ProjectRepositories projectRepositories,
-    ModuleInputComponentStore componentStore, DefaultModuleFileSystem fs, ReportPublisher reportPublisher, BranchConfiguration branchConfiguration) {
-    this.inputModule = inputModule;
+  public ScmPublisher(ScmConfiguration configuration, ProjectRepositories projectRepositories,
+                      InputComponentStore componentStore, FileSystem fs, ReportPublisher reportPublisher, BranchConfiguration branchConfiguration) {
     this.configuration = configuration;
     this.projectRepositories = projectRepositories;
     this.componentStore = componentStore;
@@ -99,16 +92,12 @@ public final class ScmPublisher {
       LOG.warn("Forced reloading of SCM data for all files.");
     }
     List<InputFile> filesToBlame = new LinkedList<>();
-    for (InputFile f : componentStore.inputFiles()) {
-      DefaultInputFile inputFile = (DefaultInputFile) f;
-      if (!inputFile.isPublished()) {
-        continue;
-      }
+    for (DefaultInputFile f : componentStore.allFilesToPublish()) {
       if (configuration.forceReloadAll() || f.status() != Status.SAME) {
         addIfNotEmpty(filesToBlame, f);
       } else if (!branchConfiguration.isShortOrPullRequest()) {
         // File status is SAME so that mean fileData exists
-        FileData fileData = projectRepositories.fileData(inputModule.definition().getKeyWithBranch(), inputFile);
+        FileData fileData = projectRepositories.fileData(componentStore.findModule(f).getKeyWithBranch(), f);
         if (StringUtils.isEmpty(fileData.revision())) {
           addIfNotEmpty(filesToBlame, f);
         } else {
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/bootstrap/PostJobExtensionDictionnaryTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/bootstrap/PostJobExtensionDictionnaryTest.java
new file mode 100644 (file)
index 0000000..a3b55c6
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * 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  02110-1301, USA.
+ */
+package org.sonar.scanner.bootstrap;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.api.batch.Phase;
+import org.sonar.api.batch.postjob.PostJob;
+import org.sonar.api.batch.postjob.PostJobContext;
+import org.sonar.api.batch.postjob.PostJobDescriptor;
+import org.sonar.api.batch.postjob.internal.DefaultPostJobDescriptor;
+import org.sonar.api.batch.sensor.Sensor;
+import org.sonar.api.batch.sensor.SensorContext;
+import org.sonar.api.batch.sensor.SensorDescriptor;
+import org.sonar.core.platform.ComponentContainer;
+import org.sonar.scanner.postjob.PostJobOptimizer;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class PostJobExtensionDictionnaryTest {
+  private PostJobOptimizer postJobOptimizer = mock(PostJobOptimizer.class);
+
+  @Before
+  public void setUp() {
+    when(postJobOptimizer.shouldExecute(any(DefaultPostJobDescriptor.class))).thenReturn(true);
+  }
+
+  private PostJobExtensionDictionnary newSelector(Object... extensions) {
+    ComponentContainer iocContainer = new ComponentContainer();
+    for (Object extension : extensions) {
+      iocContainer.addSingleton(extension);
+    }
+    return new PostJobExtensionDictionnary(iocContainer, postJobOptimizer, mock(PostJobContext.class));
+  }
+
+  @Test
+  public void dependsUponPhaseForPostJob() {
+    PrePostJob pre = new PrePostJob();
+    NormalPostJob normal = new NormalPostJob();
+
+    PostJobExtensionDictionnary selector = newSelector(normal, pre);
+    assertThat(selector.selectPostJobs()).extracting("wrappedPostJob").containsExactly(pre, normal);
+  }
+
+  interface Marker {
+
+  }
+
+  @Phase(name = Phase.Name.POST)
+  class PostSensor implements Sensor {
+
+    @Override
+    public void describe(SensorDescriptor descriptor) {
+    }
+
+    @Override
+    public void execute(SensorContext context) {
+    }
+
+  }
+
+  class PostSensorSubclass extends PostSensor {
+
+  }
+
+  class NormalPostJob implements PostJob {
+
+    @Override
+    public void describe(PostJobDescriptor descriptor) {
+    }
+
+    @Override
+    public void execute(PostJobContext context) {
+    }
+
+  }
+
+  @Phase(name = Phase.Name.PRE)
+  class PrePostJob implements PostJob {
+
+    @Override
+    public void describe(PostJobDescriptor descriptor) {
+    }
+
+    @Override
+    public void execute(PostJobContext context) {
+    }
+
+  }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/bootstrap/ScannerExtensionDictionnaryTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/bootstrap/ScannerExtensionDictionnaryTest.java
deleted file mode 100644 (file)
index 7ddf235..0000000
+++ /dev/null
@@ -1,459 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2019 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * 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  02110-1301, USA.
- */
-package org.sonar.scanner.bootstrap;
-
-import com.google.common.collect.Lists;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-import org.junit.Before;
-import org.junit.Test;
-import org.picocontainer.behaviors.FieldDecorated;
-import org.sonar.api.batch.DependedUpon;
-import org.sonar.api.batch.DependsUpon;
-import org.sonar.api.batch.Phase;
-import org.sonar.api.batch.ScannerSide;
-import org.sonar.api.batch.postjob.PostJob;
-import org.sonar.api.batch.postjob.PostJobContext;
-import org.sonar.api.batch.postjob.PostJobDescriptor;
-import org.sonar.api.batch.postjob.internal.DefaultPostJobDescriptor;
-import org.sonar.api.batch.sensor.Sensor;
-import org.sonar.api.batch.sensor.SensorContext;
-import org.sonar.api.batch.sensor.SensorDescriptor;
-import org.sonar.api.batch.sensor.internal.DefaultSensorDescriptor;
-import org.sonar.core.platform.ComponentContainer;
-import org.sonar.scanner.postjob.PostJobOptimizer;
-import org.sonar.scanner.sensor.DefaultSensorContext;
-import org.sonar.scanner.sensor.SensorOptimizer;
-import org.sonar.scanner.sensor.SensorWrapper;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.Assert.assertEquals;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-public class ScannerExtensionDictionnaryTest {
-  private SensorOptimizer sensorOptimizer = mock(SensorOptimizer.class);
-  private PostJobOptimizer postJobOptimizer = mock(PostJobOptimizer.class);
-
-  @Before
-  public void setUp() {
-    when(sensorOptimizer.shouldExecute(any(DefaultSensorDescriptor.class))).thenReturn(true);
-    when(postJobOptimizer.shouldExecute(any(DefaultPostJobDescriptor.class))).thenReturn(true);
-  }
-
-  private ScannerExtensionDictionnary newSelector(Object... extensions) {
-    ComponentContainer iocContainer = new ComponentContainer();
-    for (Object extension : extensions) {
-      iocContainer.addSingleton(extension);
-    }
-    return new ScannerExtensionDictionnary(iocContainer, mock(DefaultSensorContext.class), sensorOptimizer, postJobOptimizer, mock(PostJobContext.class));
-  }
-
-  @Test
-  public void testGetFilteredExtensionWithExtensionMatcher() {
-    final Sensor sensor1 = new FakeSensor();
-    final Sensor sensor2 = new FakeSensor();
-
-    ScannerExtensionDictionnary selector = newSelector(sensor1, sensor2);
-    Collection<Sensor> sensors = selector.select(Sensor.class, true, extension -> extension.equals(sensor1));
-    assertThat(sensors).contains(sensor1);
-    assertEquals(1, sensors.size());
-  }
-
-  @Test
-  public void testGetFilteredExtensions() {
-    Sensor sensor1 = new FakeSensor();
-    Sensor sensor2 = new FakeSensor();
-    FieldDecorated.Decorator decorator = mock(FieldDecorated.Decorator.class);
-
-    ScannerExtensionDictionnary selector = newSelector(sensor1, sensor2, decorator);
-    Collection<Sensor> sensors = selector.select(Sensor.class, false, null);
-
-    assertThat(sensors).containsOnly(sensor1, sensor2);
-  }
-
-  @Test
-  public void shouldSearchInParentContainers() {
-    Sensor a = new FakeSensor();
-    Sensor b = new FakeSensor();
-    Sensor c = new FakeSensor();
-
-    ComponentContainer grandParent = new ComponentContainer();
-    grandParent.addSingleton(a);
-
-    ComponentContainer parent = grandParent.createChild();
-    parent.addSingleton(b);
-
-    ComponentContainer child = parent.createChild();
-    child.addSingleton(c);
-
-    ScannerExtensionDictionnary dictionnary = new ScannerExtensionDictionnary(child, mock(DefaultSensorContext.class),
-      mock(SensorOptimizer.class), mock(PostJobOptimizer.class), mock(PostJobContext.class));
-    assertThat(dictionnary.select(Sensor.class, true, null)).containsOnly(a, b, c);
-  }
-
-  @Test
-  public void sortExtensionsByDependency() {
-    Object a = new MethodDependentOf(null);
-    Object b = new MethodDependentOf(a);
-    Object c = new MethodDependentOf(b);
-
-    ScannerExtensionDictionnary selector = newSelector(b, c, a);
-    List<Object> extensions = Lists.newArrayList(selector.select(Marker.class, true, null));
-
-    assertThat(extensions).hasSize(3);
-    assertThat(extensions.get(0)).isEqualTo(a);
-    assertThat(extensions.get(1)).isEqualTo(b);
-    assertThat(extensions.get(2)).isEqualTo(c);
-  }
-
-  @Test
-  public void useMethodAnnotationsToSortExtensions() {
-    Object a = new GeneratesSomething("foo");
-    Object b = new MethodDependentOf("foo");
-
-    ScannerExtensionDictionnary selector = newSelector(a, b);
-    List<Object> extensions = Lists.newArrayList(selector.select(Marker.class, true, null));
-
-    assertThat(extensions.size()).isEqualTo(2);
-    assertThat(extensions.get(0)).isEqualTo(a);
-    assertThat(extensions.get(1)).isEqualTo(b);
-
-    // different initial order
-    selector = newSelector(b, a);
-    extensions = Lists.newArrayList(selector.select(Marker.class, true, null));
-
-    assertThat(extensions).hasSize(2);
-    assertThat(extensions.get(0)).isEqualTo(a);
-    assertThat(extensions.get(1)).isEqualTo(b);
-  }
-
-  @Test
-  public void methodDependsUponCollection() {
-    Object a = new GeneratesSomething("foo");
-    Object b = new MethodDependentOf(Arrays.asList("foo"));
-
-    ScannerExtensionDictionnary selector = newSelector(a, b);
-    List<Object> extensions = Lists.newArrayList(selector.select(Marker.class, true, null));
-
-    assertThat(extensions).hasSize(2);
-    assertThat(extensions.get(0)).isEqualTo(a);
-    assertThat(extensions.get(1)).isEqualTo(b);
-
-    // different initial order
-    selector = newSelector(b, a);
-    extensions = Lists.newArrayList(selector.select(Marker.class, true, null));
-
-    assertThat(extensions).hasSize(2);
-    assertThat(extensions.get(0)).isEqualTo(a);
-    assertThat(extensions.get(1)).isEqualTo(b);
-  }
-
-  @Test
-  public void methodDependsUponArray() {
-    Object a = new GeneratesSomething("foo");
-    Object b = new MethodDependentOf(new String[] {"foo"});
-
-    ScannerExtensionDictionnary selector = newSelector(a, b);
-    List<Object> extensions = Lists.newArrayList(selector.select(Marker.class, true, null));
-
-    assertThat(extensions).hasSize(2);
-    assertThat(extensions.get(0)).isEqualTo(a);
-    assertThat(extensions.get(1)).isEqualTo(b);
-
-    // different initial order
-    selector = newSelector(b, a);
-    extensions = Lists.newArrayList(selector.select(Marker.class, true, null));
-
-    assertThat(extensions).hasSize(2);
-    assertThat(extensions.get(0)).isEqualTo(a);
-    assertThat(extensions.get(1)).isEqualTo(b);
-  }
-
-  @Test
-  public void useClassAnnotationsToSortExtensions() {
-    Object a = new ClassDependedUpon();
-    Object b = new ClassDependsUpon();
-
-    ScannerExtensionDictionnary selector = newSelector(a, b);
-    List<Object> extensions = Lists.newArrayList(selector.select(Marker.class, true, null));
-
-    assertThat(extensions).hasSize(2);
-    assertThat(extensions.get(0)).isEqualTo(a);
-    assertThat(extensions.get(1)).isEqualTo(b);
-
-    // different initial order
-    selector = newSelector(b, a);
-    extensions = Lists.newArrayList(selector.select(Marker.class, true, null));
-
-    assertThat(extensions).hasSize(2);
-    assertThat(extensions.get(0)).isEqualTo(a);
-    assertThat(extensions.get(1)).isEqualTo(b);
-  }
-
-  @Test
-  public void useClassAnnotationsOnInterfaces() {
-    Object a = new InterfaceDependedUpon() {
-    };
-    Object b = new InterfaceDependsUpon() {
-    };
-
-    ScannerExtensionDictionnary selector = newSelector(a, b);
-    List<Object> extensions = Lists.newArrayList(selector.select(Marker.class, true, null));
-
-    assertThat(extensions).hasSize(2);
-    assertThat(extensions.get(0)).isEqualTo(a);
-    assertThat(extensions.get(1)).isEqualTo(b);
-
-    // different initial order
-    selector = newSelector(b, a);
-    extensions = Lists.newArrayList(selector.select(Marker.class, true, null));
-
-    assertThat(extensions).hasSize(2);
-    assertThat(extensions.get(0)).isEqualTo(a);
-    assertThat(extensions.get(1)).isEqualTo(b);
-  }
-
-  @Test
-  public void inheritAnnotations() {
-    Object a = new SubClass("foo");
-    Object b = new MethodDependentOf("foo");
-
-    ScannerExtensionDictionnary selector = newSelector(b, a);
-    List<Object> extensions = Lists.newArrayList(selector.select(Marker.class, true, null));
-
-    assertThat(extensions).hasSize(2);
-    assertThat(extensions.get(0)).isEqualTo(a);
-    assertThat(extensions.get(1)).isEqualTo(b);
-
-    // change initial order
-    selector = newSelector(a, b);
-    extensions = Lists.newArrayList(selector.select(Marker.class, true, null));
-
-    assertThat(extensions).hasSize(2);
-    assertThat(extensions.get(0)).isEqualTo(a);
-    assertThat(extensions.get(1)).isEqualTo(b);
-  }
-
-  @Test(expected = IllegalStateException.class)
-  public void annotatedMethodsCanNotBePrivate() {
-    ScannerExtensionDictionnary selector = newSelector();
-    Object wrong = new Object() {
-      @DependsUpon
-      private Object foo() {
-        return "foo";
-      }
-    };
-    selector.evaluateAnnotatedClasses(wrong, DependsUpon.class);
-  }
-
-  @Test
-  public void dependsUponPhaseForSensors() {
-    PreSensor pre = new PreSensor();
-    NormalSensor normal = new NormalSensor();
-    PostSensor post = new PostSensor();
-
-    ScannerExtensionDictionnary selector = newSelector(normal, post, pre);
-    assertThat(selector.selectSensors(false)).extracting("wrappedSensor").containsExactly(pre, normal, post);
-  }
-
-  @Test
-  public void dependsUponPhaseForPostJob() {
-    PrePostJob pre = new PrePostJob();
-    NormalPostJob normal = new NormalPostJob();
-
-    ScannerExtensionDictionnary selector = newSelector(normal, pre);
-    assertThat(selector.selectPostJobs()).extracting("wrappedPostJob").containsExactly(pre, normal);
-  }
-
-  @Test
-  public void dependsUponInheritedPhase() {
-    PreSensorSubclass pre = new PreSensorSubclass();
-    NormalSensor normal = new NormalSensor();
-    PostSensorSubclass post = new PostSensorSubclass();
-
-    ScannerExtensionDictionnary selector = newSelector(normal, post, pre);
-    List extensions = Lists.newArrayList(selector.select(Sensor.class, true, null));
-
-    assertThat(extensions).containsExactly(pre, normal, post);
-  }
-
-  @Test
-  public void selectSensors() {
-    FakeSensor nonGlobalSensor = new FakeSensor();
-    FakeGlobalSensor globalSensor = new FakeGlobalSensor();
-    ScannerExtensionDictionnary selector = newSelector(nonGlobalSensor, globalSensor);
-
-    // verify non-global sensor
-    Collection<SensorWrapper> extensions = selector.selectSensors(false);
-    assertThat(extensions).hasSize(1);
-    assertThat(extensions).extracting("wrappedSensor").containsExactly(nonGlobalSensor);
-
-    // verify global sensor
-    extensions = selector.selectSensors(true);
-    assertThat(extensions).extracting("wrappedSensor").containsExactly(globalSensor);
-  }
-
-  interface Marker {
-
-  }
-
-  class FakeSensor implements Sensor {
-
-    @Override public void describe(SensorDescriptor descriptor) {
-
-    }
-
-    @Override public void execute(SensorContext context) {
-
-    }
-  }
-
-  class FakeGlobalSensor implements Sensor {
-
-    @Override
-    public void describe(SensorDescriptor descriptor) {
-      descriptor.global();
-    }
-
-    @Override
-    public void execute(SensorContext context) {
-    }
-
-  }
-
-  @ScannerSide class MethodDependentOf implements Marker {
-    private Object dep;
-
-    MethodDependentOf(Object o) {
-      this.dep = o;
-    }
-
-    @DependsUpon
-    public Object dependsUponObject() {
-      return dep;
-    }
-  }
-
-  @ScannerSide
-  @DependsUpon("flag") class ClassDependsUpon implements Marker {
-  }
-
-  @ScannerSide
-  @DependedUpon("flag") class ClassDependedUpon implements Marker {
-  }
-
-  @ScannerSide
-  @DependsUpon("flag") interface InterfaceDependsUpon extends Marker {
-  }
-
-  @ScannerSide
-  @DependedUpon("flag") interface InterfaceDependedUpon extends Marker {
-  }
-
-  @ScannerSide class GeneratesSomething implements Marker {
-    private Object gen;
-
-    GeneratesSomething(Object o) {
-      this.gen = o;
-    }
-
-    @DependedUpon
-    public Object generates() {
-      return gen;
-    }
-  }
-
-  class SubClass extends GeneratesSomething implements Marker {
-    SubClass(Object o) {
-      super(o);
-    }
-  }
-
-  class NormalSensor implements Sensor {
-
-    @Override
-    public void describe(SensorDescriptor descriptor) {
-    }
-
-    @Override
-    public void execute(SensorContext context) {
-    }
-
-  }
-
-  @Phase(name = Phase.Name.PRE) class PreSensor implements Sensor {
-
-    @Override
-    public void describe(SensorDescriptor descriptor) {
-    }
-
-    @Override
-    public void execute(SensorContext context) {
-    }
-
-  }
-
-  class PreSensorSubclass extends PreSensor {
-
-  }
-
-  @Phase(name = Phase.Name.POST) class PostSensor implements Sensor {
-
-    @Override
-    public void describe(SensorDescriptor descriptor) {
-    }
-
-    @Override
-    public void execute(SensorContext context) {
-    }
-
-  }
-
-  class PostSensorSubclass extends PostSensor {
-
-  }
-
-  class NormalPostJob implements PostJob {
-
-    @Override
-    public void describe(PostJobDescriptor descriptor) {
-    }
-
-    @Override
-    public void execute(PostJobContext context) {
-    }
-
-  }
-
-  @Phase(name = Phase.Name.PRE) class PrePostJob implements PostJob {
-
-    @Override
-    public void describe(PostJobDescriptor descriptor) {
-    }
-
-    @Override
-    public void execute(PostJobContext context) {
-    }
-
-  }
-}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/bootstrap/SensorExtensionDictionnaryTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/bootstrap/SensorExtensionDictionnaryTest.java
new file mode 100644 (file)
index 0000000..b922b3a
--- /dev/null
@@ -0,0 +1,429 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * 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  02110-1301, USA.
+ */
+package org.sonar.scanner.bootstrap;
+
+import com.google.common.collect.Lists;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import org.junit.Before;
+import org.junit.Test;
+import org.picocontainer.behaviors.FieldDecorated;
+import org.sonar.api.batch.DependedUpon;
+import org.sonar.api.batch.DependsUpon;
+import org.sonar.api.batch.Phase;
+import org.sonar.api.batch.ScannerSide;
+import org.sonar.api.batch.sensor.Sensor;
+import org.sonar.api.batch.sensor.SensorContext;
+import org.sonar.api.batch.sensor.SensorDescriptor;
+import org.sonar.api.batch.sensor.internal.DefaultSensorDescriptor;
+import org.sonar.core.platform.ComponentContainer;
+import org.sonar.scanner.sensor.DefaultSensorContext;
+import org.sonar.scanner.sensor.SensorOptimizer;
+import org.sonar.scanner.sensor.SensorWrapper;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class SensorExtensionDictionnaryTest {
+  private SensorOptimizer sensorOptimizer = mock(SensorOptimizer.class);
+
+  @Before
+  public void setUp() {
+    when(sensorOptimizer.shouldExecute(any(DefaultSensorDescriptor.class))).thenReturn(true);
+  }
+
+  private SensorExtensionDictionnary newSelector(Object... extensions) {
+    ComponentContainer iocContainer = new ComponentContainer();
+    for (Object extension : extensions) {
+      iocContainer.addSingleton(extension);
+    }
+    return new SensorExtensionDictionnary(iocContainer, mock(DefaultSensorContext.class), sensorOptimizer);
+  }
+
+  @Test
+  public void testGetFilteredExtensionWithExtensionMatcher() {
+    final Sensor sensor1 = new FakeSensor();
+    final Sensor sensor2 = new FakeSensor();
+
+    SensorExtensionDictionnary selector = newSelector(sensor1, sensor2);
+    Collection<Sensor> sensors = selector.select(Sensor.class, true, extension -> extension.equals(sensor1));
+    assertThat(sensors).contains(sensor1);
+    assertEquals(1, sensors.size());
+  }
+
+  @Test
+  public void testGetFilteredExtensions() {
+    Sensor sensor1 = new FakeSensor();
+    Sensor sensor2 = new FakeSensor();
+    FieldDecorated.Decorator decorator = mock(FieldDecorated.Decorator.class);
+
+    SensorExtensionDictionnary selector = newSelector(sensor1, sensor2, decorator);
+    Collection<Sensor> sensors = selector.select(Sensor.class, false, null);
+
+    assertThat(sensors).containsOnly(sensor1, sensor2);
+  }
+
+  @Test
+  public void shouldSearchInParentContainers() {
+    Sensor a = new FakeSensor();
+    Sensor b = new FakeSensor();
+    Sensor c = new FakeSensor();
+
+    ComponentContainer grandParent = new ComponentContainer();
+    grandParent.addSingleton(a);
+
+    ComponentContainer parent = grandParent.createChild();
+    parent.addSingleton(b);
+
+    ComponentContainer child = parent.createChild();
+    child.addSingleton(c);
+
+    SensorExtensionDictionnary dictionnary = new SensorExtensionDictionnary(child, mock(DefaultSensorContext.class), mock(SensorOptimizer.class));
+    assertThat(dictionnary.select(Sensor.class, true, null)).containsOnly(a, b, c);
+  }
+
+  @Test
+  public void sortExtensionsByDependency() {
+    Object a = new MethodDependentOf(null);
+    Object b = new MethodDependentOf(a);
+    Object c = new MethodDependentOf(b);
+
+    SensorExtensionDictionnary selector = newSelector(b, c, a);
+    List<Object> extensions = Lists.newArrayList(selector.select(Marker.class, true, null));
+
+    assertThat(extensions).hasSize(3);
+    assertThat(extensions.get(0)).isEqualTo(a);
+    assertThat(extensions.get(1)).isEqualTo(b);
+    assertThat(extensions.get(2)).isEqualTo(c);
+  }
+
+  @Test
+  public void useMethodAnnotationsToSortExtensions() {
+    Object a = new GeneratesSomething("foo");
+    Object b = new MethodDependentOf("foo");
+
+    SensorExtensionDictionnary selector = newSelector(a, b);
+    List<Object> extensions = Lists.newArrayList(selector.select(Marker.class, true, null));
+
+    assertThat(extensions.size()).isEqualTo(2);
+    assertThat(extensions.get(0)).isEqualTo(a);
+    assertThat(extensions.get(1)).isEqualTo(b);
+
+    // different initial order
+    selector = newSelector(b, a);
+    extensions = Lists.newArrayList(selector.select(Marker.class, true, null));
+
+    assertThat(extensions).hasSize(2);
+    assertThat(extensions.get(0)).isEqualTo(a);
+    assertThat(extensions.get(1)).isEqualTo(b);
+  }
+
+  @Test
+  public void methodDependsUponCollection() {
+    Object a = new GeneratesSomething("foo");
+    Object b = new MethodDependentOf(Arrays.asList("foo"));
+
+    SensorExtensionDictionnary selector = newSelector(a, b);
+    List<Object> extensions = Lists.newArrayList(selector.select(Marker.class, true, null));
+
+    assertThat(extensions).hasSize(2);
+    assertThat(extensions.get(0)).isEqualTo(a);
+    assertThat(extensions.get(1)).isEqualTo(b);
+
+    // different initial order
+    selector = newSelector(b, a);
+    extensions = Lists.newArrayList(selector.select(Marker.class, true, null));
+
+    assertThat(extensions).hasSize(2);
+    assertThat(extensions.get(0)).isEqualTo(a);
+    assertThat(extensions.get(1)).isEqualTo(b);
+  }
+
+  @Test
+  public void methodDependsUponArray() {
+    Object a = new GeneratesSomething("foo");
+    Object b = new MethodDependentOf(new String[] {"foo"});
+
+    SensorExtensionDictionnary selector = newSelector(a, b);
+    List<Object> extensions = Lists.newArrayList(selector.select(Marker.class, true, null));
+
+    assertThat(extensions).hasSize(2);
+    assertThat(extensions.get(0)).isEqualTo(a);
+    assertThat(extensions.get(1)).isEqualTo(b);
+
+    // different initial order
+    selector = newSelector(b, a);
+    extensions = Lists.newArrayList(selector.select(Marker.class, true, null));
+
+    assertThat(extensions).hasSize(2);
+    assertThat(extensions.get(0)).isEqualTo(a);
+    assertThat(extensions.get(1)).isEqualTo(b);
+  }
+
+  @Test
+  public void useClassAnnotationsToSortExtensions() {
+    Object a = new ClassDependedUpon();
+    Object b = new ClassDependsUpon();
+
+    SensorExtensionDictionnary selector = newSelector(a, b);
+    List<Object> extensions = Lists.newArrayList(selector.select(Marker.class, true, null));
+
+    assertThat(extensions).hasSize(2);
+    assertThat(extensions.get(0)).isEqualTo(a);
+    assertThat(extensions.get(1)).isEqualTo(b);
+
+    // different initial order
+    selector = newSelector(b, a);
+    extensions = Lists.newArrayList(selector.select(Marker.class, true, null));
+
+    assertThat(extensions).hasSize(2);
+    assertThat(extensions.get(0)).isEqualTo(a);
+    assertThat(extensions.get(1)).isEqualTo(b);
+  }
+
+  @Test
+  public void useClassAnnotationsOnInterfaces() {
+    Object a = new InterfaceDependedUpon() {
+    };
+    Object b = new InterfaceDependsUpon() {
+    };
+
+    SensorExtensionDictionnary selector = newSelector(a, b);
+    List<Object> extensions = Lists.newArrayList(selector.select(Marker.class, true, null));
+
+    assertThat(extensions).hasSize(2);
+    assertThat(extensions.get(0)).isEqualTo(a);
+    assertThat(extensions.get(1)).isEqualTo(b);
+
+    // different initial order
+    selector = newSelector(b, a);
+    extensions = Lists.newArrayList(selector.select(Marker.class, true, null));
+
+    assertThat(extensions).hasSize(2);
+    assertThat(extensions.get(0)).isEqualTo(a);
+    assertThat(extensions.get(1)).isEqualTo(b);
+  }
+
+  @Test
+  public void inheritAnnotations() {
+    Object a = new SubClass("foo");
+    Object b = new MethodDependentOf("foo");
+
+    SensorExtensionDictionnary selector = newSelector(b, a);
+    List<Object> extensions = Lists.newArrayList(selector.select(Marker.class, true, null));
+
+    assertThat(extensions).hasSize(2);
+    assertThat(extensions.get(0)).isEqualTo(a);
+    assertThat(extensions.get(1)).isEqualTo(b);
+
+    // change initial order
+    selector = newSelector(a, b);
+    extensions = Lists.newArrayList(selector.select(Marker.class, true, null));
+
+    assertThat(extensions).hasSize(2);
+    assertThat(extensions.get(0)).isEqualTo(a);
+    assertThat(extensions.get(1)).isEqualTo(b);
+  }
+
+  @Test(expected = IllegalStateException.class)
+  public void annotatedMethodsCanNotBePrivate() {
+    SensorExtensionDictionnary selector = newSelector();
+    Object wrong = new Object() {
+      @DependsUpon
+      private Object foo() {
+        return "foo";
+      }
+    };
+    selector.evaluateAnnotatedClasses(wrong, DependsUpon.class);
+  }
+
+  @Test
+  public void dependsUponPhaseForSensors() {
+    PreSensor pre = new PreSensor();
+    NormalSensor normal = new NormalSensor();
+    PostSensor post = new PostSensor();
+
+    SensorExtensionDictionnary selector = newSelector(normal, post, pre);
+    assertThat(selector.selectSensors(false)).extracting("wrappedSensor").containsExactly(pre, normal, post);
+  }
+
+  @Test
+  public void dependsUponInheritedPhase() {
+    PreSensorSubclass pre = new PreSensorSubclass();
+    NormalSensor normal = new NormalSensor();
+    PostSensorSubclass post = new PostSensorSubclass();
+
+    SensorExtensionDictionnary selector = newSelector(normal, post, pre);
+    List extensions = Lists.newArrayList(selector.select(Sensor.class, true, null));
+
+    assertThat(extensions).containsExactly(pre, normal, post);
+  }
+
+  @Test
+  public void selectSensors() {
+    FakeSensor nonGlobalSensor = new FakeSensor();
+    FakeGlobalSensor globalSensor = new FakeGlobalSensor();
+    SensorExtensionDictionnary selector = newSelector(nonGlobalSensor, globalSensor);
+
+    // verify non-global sensor
+    Collection<SensorWrapper> extensions = selector.selectSensors(false);
+    assertThat(extensions).hasSize(1);
+    assertThat(extensions).extracting("wrappedSensor").containsExactly(nonGlobalSensor);
+
+    // verify global sensor
+    extensions = selector.selectSensors(true);
+    assertThat(extensions).extracting("wrappedSensor").containsExactly(globalSensor);
+  }
+
+  interface Marker {
+
+  }
+
+  class FakeSensor implements Sensor {
+
+    @Override
+    public void describe(SensorDescriptor descriptor) {
+
+    }
+
+    @Override
+    public void execute(SensorContext context) {
+
+    }
+  }
+
+  class FakeGlobalSensor implements Sensor {
+
+    @Override
+    public void describe(SensorDescriptor descriptor) {
+      descriptor.global();
+    }
+
+    @Override
+    public void execute(SensorContext context) {
+    }
+
+  }
+
+  @ScannerSide
+  class MethodDependentOf implements Marker {
+    private Object dep;
+
+    MethodDependentOf(Object o) {
+      this.dep = o;
+    }
+
+    @DependsUpon
+    public Object dependsUponObject() {
+      return dep;
+    }
+  }
+
+  @ScannerSide
+  @DependsUpon("flag")
+  class ClassDependsUpon implements Marker {
+  }
+
+  @ScannerSide
+  @DependedUpon("flag")
+  class ClassDependedUpon implements Marker {
+  }
+
+  @ScannerSide
+  @DependsUpon("flag")
+  interface InterfaceDependsUpon extends Marker {
+  }
+
+  @ScannerSide
+  @DependedUpon("flag")
+  interface InterfaceDependedUpon extends Marker {
+  }
+
+  @ScannerSide
+  class GeneratesSomething implements Marker {
+    private Object gen;
+
+    GeneratesSomething(Object o) {
+      this.gen = o;
+    }
+
+    @DependedUpon
+    public Object generates() {
+      return gen;
+    }
+  }
+
+  class SubClass extends GeneratesSomething implements Marker {
+    SubClass(Object o) {
+      super(o);
+    }
+  }
+
+  class NormalSensor implements Sensor {
+
+    @Override
+    public void describe(SensorDescriptor descriptor) {
+    }
+
+    @Override
+    public void execute(SensorContext context) {
+    }
+
+  }
+
+  @Phase(name = Phase.Name.PRE)
+  class PreSensor implements Sensor {
+
+    @Override
+    public void describe(SensorDescriptor descriptor) {
+    }
+
+    @Override
+    public void execute(SensorContext context) {
+    }
+
+  }
+
+  class PreSensorSubclass extends PreSensor {
+
+  }
+
+  @Phase(name = Phase.Name.POST)
+  class PostSensor implements Sensor {
+
+    @Override
+    public void describe(SensorDescriptor descriptor) {
+    }
+
+    @Override
+    public void execute(SensorContext context) {
+    }
+
+  }
+
+  class PostSensorSubclass extends PostSensor {
+
+  }
+
+}
index bcc778dc7242fcd18c37dc4910a763711d7b377c..6c2f47ff34c2b0eeafd83da6444692c457844174 100644 (file)
@@ -574,7 +574,7 @@ public class FileSystemMediumTest {
     assertThat(fileB).isNull();
 
     assertThat(logTester.logs(LoggerLevel.WARN))
-      .contains("File 'moduleB/src/sample.xoo' was excluded because patterns are still evaluated using module relative paths but this is deprecated. " +
+      .contains("File 'moduleA/src/sample.xoo' was excluded because patterns are still evaluated using module relative paths but this is deprecated. " +
       "Please update file inclusion/exclusion configuration so that patterns refer to project relative paths.");
   }
 
index 6d95e2ff8286db5bedcfc1af20bbfe4f5b18be6a..6be5efa45a953f59bd9a443c2dab79c764d2a517 100644 (file)
 package org.sonar.scanner.phases;
 
 import java.io.File;
+import java.util.Arrays;
+import java.util.stream.Collectors;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
+import org.sonar.api.batch.bootstrap.ProjectDefinition;
 import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.fs.internal.DefaultInputModule;
 import org.sonar.api.batch.fs.internal.TestInputFileBuilder;
 import org.sonar.scanner.scan.ModuleConfiguration;
 
@@ -63,9 +67,12 @@ public class ModuleCoverageExclusionsTest {
     assertThat(coverageExclusions.isExcluded(file)).isFalse();
   }
 
-  private ModuleConfiguration mockConfig(String... values) {
+  private DefaultInputModule mockConfig(String... values) {
     ModuleConfiguration config = mock(ModuleConfiguration.class);
     when(config.getStringArray("sonar.coverage.exclusions")).thenReturn(values);
-    return config;
+    return new DefaultInputModule(ProjectDefinition.create()
+      .setBaseDir(baseDir)
+      .setWorkDir(baseDir)
+      .setProperty("sonar.coverage.exclusions", Arrays.asList(values).stream().collect(Collectors.joining(","))));
   }
 }
index f4a8ed15f927f5b62dec81e49004871d4f154b58..92e7bc2a8ce648cfa47d0543b2cc07a6d7ab8698 100644 (file)
@@ -22,8 +22,9 @@ package org.sonar.scanner.phases;
 import java.util.Arrays;
 import org.junit.Before;
 import org.junit.Test;
-import org.sonar.scanner.bootstrap.ScannerExtensionDictionnary;
+import org.sonar.scanner.bootstrap.PostJobExtensionDictionnary;
 import org.sonar.scanner.postjob.PostJobWrapper;
+import org.sonar.scanner.postjob.PostJobsExecutor;
 
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
@@ -33,7 +34,7 @@ import static org.mockito.Mockito.when;
 public class PostJobsExecutorTest {
   private PostJobsExecutor executor;
 
-  private ScannerExtensionDictionnary selector = mock(ScannerExtensionDictionnary.class);
+  private PostJobExtensionDictionnary selector = mock(PostJobExtensionDictionnary.class);
   private PostJobWrapper job1 = mock(PostJobWrapper.class);
   private PostJobWrapper job2 = mock(PostJobWrapper.class);
 
index ca97a46a6fc701353acd01534c97e704bec98c5f..de31e20e3c342c61856c74c11d95089912bd9ed4 100644 (file)
@@ -31,7 +31,7 @@ import org.sonar.api.batch.fs.internal.InputModuleHierarchy;
 import org.sonar.api.batch.fs.internal.SensorStrategy;
 import org.sonar.api.batch.fs.internal.TestInputFileBuilder;
 import org.sonar.api.batch.sensor.Sensor;
-import org.sonar.scanner.bootstrap.ScannerExtensionDictionnary;
+import org.sonar.scanner.bootstrap.SensorExtensionDictionnary;
 import org.sonar.scanner.bootstrap.ScannerPluginRepository;
 import org.sonar.scanner.sensor.SensorWrapper;
 
@@ -64,7 +64,7 @@ public class SensorsExecutorTest {
     when(globalSensor.shouldExecute()).thenReturn(true);
     when(globalSensor.wrappedSensor()).thenReturn(mock(Sensor.class));
 
-    ScannerExtensionDictionnary selector = mock(ScannerExtensionDictionnary.class);
+    SensorExtensionDictionnary selector = mock(SensorExtensionDictionnary.class);
     when(selector.selectSensors(false)).thenReturn(Collections.singleton(perModuleSensor));
     when(selector.selectSensors(true)).thenReturn(Collections.singleton(globalSensor));
 
index ee736b0578f8178a2e603c6ee618465dd359d42f..98785e06e62febbcc052ba5815ee975fe59197a2 100644 (file)
@@ -84,8 +84,8 @@ public class InputComponentStoreTest {
 
     assertThat(store.filesByModule(rootModuleKey)).hasSize(1);
     assertThat(store.filesByModule(subModuleKey)).hasSize(1);
-    assertThat(store.allFiles()).hasSize(2);
-    for (InputPath inputPath : store.allFiles()) {
+    assertThat(store.inputFiles()).hasSize(2);
+    for (InputPath inputPath : store.inputFiles()) {
       assertThat(inputPath.relativePath()).startsWith("src/main/java/");
     }
 
@@ -118,9 +118,9 @@ public class InputComponentStoreTest {
     String mod2Key = "mod2";
     tester.addFile(mod2Key, "src/main/groovy/Foo.groovy", "groovy");
 
-    assertThat(tester.getLanguages(mod1Key)).containsExactly("java");
-    assertThat(tester.getLanguages(mod2Key)).containsExactly("groovy");
-    assertThat(tester.getLanguages()).containsExactlyInAnyOrder("java", "groovy");
+    assertThat(tester.languages(mod1Key)).containsExactly("java");
+    assertThat(tester.languages(mod2Key)).containsExactly("groovy");
+    assertThat(tester.languages()).containsExactlyInAnyOrder("java", "groovy");
   }
 
   @Test
@@ -135,6 +135,6 @@ public class InputComponentStoreTest {
 
     assertThat(tester.filesByModule(mod1Key)).containsExactly(mod1File);
     assertThat(tester.filesByModule(mod2Key)).containsExactly(mod2File);
-    assertThat(tester.allFiles()).containsExactlyInAnyOrder(mod1File, mod2File);
+    assertThat(tester.inputFiles()).containsExactlyInAnyOrder(mod1File, mod2File);
   }
 }
index cb1b4793940743a55ed02ebe6c643564e118e352..29064edd51b8c977836b276014011fe2953012e7 100644 (file)
@@ -127,6 +127,8 @@ public class ModuleInputComponentStoreTest {
     when(module.key()).thenReturn("foo");
     ModuleInputComponentStore store = new ModuleInputComponentStore(module, inputComponentStore, strategy);
 
+    strategy.setGlobal(false);
+
     store.inputFiles();
     verify(inputComponentStore).filesByModule("foo");
 
@@ -135,7 +137,7 @@ public class ModuleInputComponentStoreTest {
     verify(inputComponentStore).getFile(any(String.class), eq(relativePath));
 
     store.languages();
-    verify(inputComponentStore).getLanguages(any(String.class));
+    verify(inputComponentStore).languages(any(String.class));
   }
 
   @Test
@@ -147,13 +149,13 @@ public class ModuleInputComponentStoreTest {
     strategy.setGlobal(true);
 
     store.inputFiles();
-    verify(inputComponentStore).allFiles();
+    verify(inputComponentStore).inputFiles();
 
     String relativePath = "somepath";
     store.inputFile(relativePath);
-    verify(inputComponentStore).getFile(relativePath);
+    verify(inputComponentStore).inputFile(relativePath);
 
     store.languages();
-    verify(inputComponentStore).getLanguages();
+    verify(inputComponentStore).languages();
   }
 }