You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ProjectFileIndexer.java 8.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2024 SonarSource SA
  4. * mailto:info AT sonarsource DOT com
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 3 of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this program; if not, write to the Free Software Foundation,
  18. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19. */
  20. package org.sonar.scanner.scan.filesystem;
  21. import java.io.IOException;
  22. import java.nio.file.FileVisitOption;
  23. import java.nio.file.Files;
  24. import java.nio.file.Path;
  25. import java.util.Collections;
  26. import java.util.Comparator;
  27. import java.util.Iterator;
  28. import java.util.List;
  29. import java.util.Optional;
  30. import java.util.concurrent.TimeUnit;
  31. import org.apache.commons.lang.StringUtils;
  32. import org.slf4j.Logger;
  33. import org.slf4j.LoggerFactory;
  34. import org.sonar.api.batch.fs.InputFile.Type;
  35. import org.sonar.api.batch.fs.internal.DefaultInputModule;
  36. import org.sonar.api.notifications.AnalysisWarnings;
  37. import org.sonar.api.scan.filesystem.PathResolver;
  38. import org.sonar.scanner.bootstrap.GlobalConfiguration;
  39. import org.sonar.scanner.bootstrap.GlobalServerSettings;
  40. import org.sonar.scanner.fs.InputModuleHierarchy;
  41. import org.sonar.scanner.scan.ModuleConfiguration;
  42. import org.sonar.scanner.scan.ModuleConfigurationProvider;
  43. import org.sonar.scanner.scan.ProjectServerSettings;
  44. import org.sonar.scanner.scan.SonarGlobalPropertiesFilter;
  45. import org.sonar.scanner.util.ProgressReport;
  46. /**
  47. * Index project input files into {@link InputComponentStore}.
  48. */
  49. public class ProjectFileIndexer {
  50. private static final Logger LOG = LoggerFactory.getLogger(ProjectFileIndexer.class);
  51. private final ProjectExclusionFilters projectExclusionFilters;
  52. private final SonarGlobalPropertiesFilter sonarGlobalPropertiesFilter;
  53. private final ProjectCoverageAndDuplicationExclusions projectCoverageAndDuplicationExclusions;
  54. private final InputComponentStore componentStore;
  55. private final InputModuleHierarchy inputModuleHierarchy;
  56. private final GlobalConfiguration globalConfig;
  57. private final GlobalServerSettings globalServerSettings;
  58. private final ProjectServerSettings projectServerSettings;
  59. private final FileIndexer fileIndexer;
  60. private final ProjectFilePreprocessor projectFilePreprocessor;
  61. private final AnalysisWarnings analysisWarnings;
  62. private ProgressReport progressReport;
  63. public ProjectFileIndexer(InputComponentStore componentStore, ProjectExclusionFilters exclusionFilters,
  64. SonarGlobalPropertiesFilter sonarGlobalPropertiesFilter, InputModuleHierarchy inputModuleHierarchy,
  65. GlobalConfiguration globalConfig, GlobalServerSettings globalServerSettings, ProjectServerSettings projectServerSettings,
  66. FileIndexer fileIndexer, ProjectCoverageAndDuplicationExclusions projectCoverageAndDuplicationExclusions,
  67. ProjectFilePreprocessor projectFilePreprocessor, AnalysisWarnings analysisWarnings) {
  68. this.componentStore = componentStore;
  69. this.sonarGlobalPropertiesFilter = sonarGlobalPropertiesFilter;
  70. this.inputModuleHierarchy = inputModuleHierarchy;
  71. this.globalConfig = globalConfig;
  72. this.globalServerSettings = globalServerSettings;
  73. this.projectServerSettings = projectServerSettings;
  74. this.fileIndexer = fileIndexer;
  75. this.projectExclusionFilters = exclusionFilters;
  76. this.projectCoverageAndDuplicationExclusions = projectCoverageAndDuplicationExclusions;
  77. this.projectFilePreprocessor = projectFilePreprocessor;
  78. this.analysisWarnings = analysisWarnings;
  79. }
  80. public void index() {
  81. progressReport = new ProgressReport("Report about progress of file indexing", TimeUnit.SECONDS.toMillis(10));
  82. progressReport.start("Indexing files...");
  83. LOG.info("Project configuration:");
  84. projectExclusionFilters.log(" ");
  85. projectCoverageAndDuplicationExclusions.log(" ");
  86. indexModulesRecursively(inputModuleHierarchy.root());
  87. int totalIndexed = componentStore.inputFiles().size();
  88. progressReport.stop(totalIndexed + " " + pluralizeFiles(totalIndexed) + " indexed");
  89. }
  90. private void indexModulesRecursively(DefaultInputModule module) {
  91. inputModuleHierarchy.children(module).stream()
  92. .sorted(Comparator.comparing(DefaultInputModule::key))
  93. .forEach(this::indexModulesRecursively);
  94. index(module);
  95. }
  96. private void index(DefaultInputModule module) {
  97. // Emulate creation of module level settings
  98. ModuleConfiguration moduleConfig = new ModuleConfigurationProvider(sonarGlobalPropertiesFilter).provide(globalConfig, module, globalServerSettings, projectServerSettings);
  99. ModuleExclusionFilters moduleExclusionFilters = new ModuleExclusionFilters(moduleConfig, analysisWarnings);
  100. ModuleCoverageAndDuplicationExclusions moduleCoverageAndDuplicationExclusions = new ModuleCoverageAndDuplicationExclusions(moduleConfig);
  101. if (componentStore.allModules().size() > 1) {
  102. LOG.info("Indexing files of module '{}'", module.getName());
  103. LOG.info(" Base dir: {}", module.getBaseDir().toAbsolutePath());
  104. module.getSourceDirsOrFiles().ifPresent(srcs -> logPaths(" Source paths: ", module.getBaseDir(), srcs));
  105. module.getTestDirsOrFiles().ifPresent(tests -> logPaths(" Test paths: ", module.getBaseDir(), tests));
  106. moduleExclusionFilters.log(" ");
  107. moduleCoverageAndDuplicationExclusions.log(" ");
  108. }
  109. List<Path> mainSourceDirsOrFiles = projectFilePreprocessor.getMainSourcesByModule(module);
  110. indexFiles(module, moduleExclusionFilters, moduleCoverageAndDuplicationExclusions, mainSourceDirsOrFiles, Type.MAIN);
  111. projectFilePreprocessor.getTestSourcesByModule(module)
  112. .ifPresent(tests -> indexFiles(module, moduleExclusionFilters, moduleCoverageAndDuplicationExclusions, tests, Type.TEST));
  113. }
  114. private static void logPaths(String label, Path baseDir, List<Path> paths) {
  115. if (!paths.isEmpty()) {
  116. StringBuilder sb = new StringBuilder(label);
  117. for (Iterator<Path> it = paths.iterator(); it.hasNext(); ) {
  118. Path file = it.next();
  119. Optional<String> relativePathToBaseDir = PathResolver.relativize(baseDir, file);
  120. if (relativePathToBaseDir.isEmpty()) {
  121. sb.append(file);
  122. } else if (StringUtils.isBlank(relativePathToBaseDir.get())) {
  123. sb.append(".");
  124. } else {
  125. sb.append(relativePathToBaseDir.get());
  126. }
  127. if (it.hasNext()) {
  128. sb.append(", ");
  129. }
  130. }
  131. if (LOG.isDebugEnabled()) {
  132. LOG.debug(sb.toString());
  133. } else {
  134. LOG.info(StringUtils.abbreviate(sb.toString(), 80));
  135. }
  136. }
  137. }
  138. private void indexFiles(DefaultInputModule module, ModuleExclusionFilters moduleExclusionFilters, ModuleCoverageAndDuplicationExclusions moduleCoverageAndDuplicationExclusions,
  139. List<Path> sources, Type type) {
  140. try {
  141. for (Path dirOrFile : sources) {
  142. if (dirOrFile.toFile().isDirectory()) {
  143. indexDirectory(module, moduleExclusionFilters, moduleCoverageAndDuplicationExclusions, dirOrFile, type);
  144. } else {
  145. fileIndexer.indexFile(module, moduleCoverageAndDuplicationExclusions, dirOrFile, type, progressReport);
  146. }
  147. }
  148. } catch (IOException e) {
  149. throw new IllegalStateException("Failed to index files", e);
  150. }
  151. }
  152. private void indexDirectory(DefaultInputModule module, ModuleExclusionFilters moduleExclusionFilters,
  153. ModuleCoverageAndDuplicationExclusions moduleCoverageAndDuplicationExclusions,
  154. Path dirToIndex, Type type) throws IOException {
  155. Files.walkFileTree(dirToIndex.normalize(), Collections.singleton(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE,
  156. new DirectoryFileVisitor(file -> fileIndexer.indexFile(module, moduleCoverageAndDuplicationExclusions, file, type, progressReport),
  157. module, moduleExclusionFilters, inputModuleHierarchy, type));
  158. }
  159. private static String pluralizeFiles(int count) {
  160. return count == 1 ? "file" : "files";
  161. }
  162. }