Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,8 @@ public void invalid_binaries_dir_should_fail_analysis() {
SonarScanner scanner = ditProjectSonarScanner();
scanner.setProperty("sonar.java.binaries", "target/dummy__Dir");
BuildResult buildResult = ORCHESTRATOR.executeBuildQuietly(scanner);
assertThat(buildResult.getLastStatus()).isNotZero();
assertThat(buildResult.getLogs()).contains("No files nor directories matching 'target/dummy__Dir'");
assertThat(buildResult.getLastStatus()).isZero();
assertThat(buildResult.getLogs()).contains("WARN Invalid value for 'sonar.java.binaries', no files nor directories matching 'target/dummy__Dir'.");
}

@Test
Expand Down Expand Up @@ -154,27 +154,13 @@ public void should_keep_order_libs() {
assertThat(getNumberOfViolations(projectKey)).isEqualTo(1);
}

@Test
public void should_support_the_old_binaries_and_libraries_properties() {
SonarScanner scanner = ditProjectSonarScanner();
scanner.setProperty("sonar.binaries", "target/classes");
scanner.setProperty("sonar.libraries", guavaJarPathEscaped);
BuildResult buildResult = ORCHESTRATOR.executeBuildQuietly(scanner);

assertThat(buildResult.getLogs()).contains(
"sonar.binaries and sonar.libraries are not supported since version 4.0 of the SonarSource Java Analyzer," +
" please use sonar.java.binaries and sonar.java.libraries instead");
assertThat(buildResult.isSuccess()).isFalse();
}

@Test
public void should_log_warnings_if_binaries_missing() {
SonarScanner scanner = ditProjectSonarScanner();
BuildResult buildResult = ORCHESTRATOR.executeBuildQuietly(scanner);
String logs = buildResult.getLogs();
assertThat(logs).contains("Your project contains .java files, please provide compiled classes with sonar.java.binaries property,"
+ " or exclude them from the analysis with sonar.exclusions property.");
assertThat(buildResult.isSuccess()).isFalse();
assertThat(logs).contains("WARN Missing 'sonar.java.binaries' and 'sonar.java.libraries' properties. You might end up with less precise analysis results.");
assertThat(buildResult.isSuccess()).isTrue();
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -616,11 +616,11 @@ public void logUndefinedTypes() {
if (problemsToFilePaths.isEmpty()) {
return;
}
javaClasspath.logSuspiciousEmptyLibraries();
javaClasspath.logClasspathWarnings();
if (!isAutoScan()) {
// In autoscan, test + main code are analyzed in the same batch, and we do not make the distinction between
// test and main libraries, everything is inside "sonar.java.libraries", it is expected to let the test property empty.
javaTestClasspath.logSuspiciousEmptyLibraries();
javaTestClasspath.logClasspathWarnings();
}
logUndefinedTypes(LOGGED_MAX_NUMBER_UNDEFINED_TYPES);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Optional;
Expand All @@ -42,10 +43,12 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.batch.ScannerSide;
import org.sonar.api.batch.fs.FilePredicate;
import org.sonar.api.batch.fs.FileSystem;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.config.Configuration;
import org.sonar.java.collections.CollectionUtils;
import org.sonar.java.AnalysisWarningsWrapper;
import org.sonar.java.SonarComponents;
import org.sonarsource.api.sonarlint.SonarLintSide;

@ScannerSide
Expand All @@ -58,26 +61,67 @@ public abstract class AbstractClasspath {
protected final Configuration settings;
protected final FileSystem fs;
private final InputFile.Type fileType;
protected final String binariesProperty;
protected final String librariesProperty;
private static final Path[] STANDARD_CLASSES_DIRS = {Paths.get("target", "classes"), Paths.get("target", "test-classes")};

protected final List<File> binaries;
protected final List<File> elements;
protected List<File> elements;
protected boolean validateLibraries;
protected boolean initialized;
private boolean inAndroidContext = false;
private final Set<String> classpathWarnings;
protected final AnalysisWarningsWrapper analysisWarnings;

protected AbstractClasspath(Configuration settings, FileSystem fs, InputFile.Type fileType) {
protected AbstractClasspath(Configuration settings, FileSystem fs, InputFile.Type fileType, String binariesProperty, String librariesProperty,
AnalysisWarningsWrapper analysisWarnings) {
this.settings = settings;
this.fs = fs;
this.fileType = fileType;
this.binariesProperty = binariesProperty;
this.librariesProperty = librariesProperty;
this.binaries = new ArrayList<>();
this.elements = new ArrayList<>();
this.elements = List.of();
this.analysisWarnings = analysisWarnings;
classpathWarnings = new LinkedHashSet<>();
initialized = false;
}

protected void init() {
if (!initialized) {
initialized = true;
validateLibraries = hasJavaFiles();
validatePropertiesPresence();
binaries.addAll(getFilesFromProperty(binariesProperty));
Set<File> libraries = new LinkedHashSet<>(getJdkJars());
Set<File> extraLibraries = getFilesFromProperty(librariesProperty);
libraries.addAll(extraLibraries);
logResolvedFiles(binariesProperty, binaries);
logResolvedFiles(librariesProperty, libraries);
Set<File> all = new LinkedHashSet<>(binaries);
all.addAll(libraries);
elements = List.copyOf(all);
}
}

protected void validatePropertiesPresence() {
if (settings.getBoolean(SonarComponents.SONAR_AUTOSCAN).orElse(false)) {
return;
}
boolean missingBinary = !settings.hasKey(binariesProperty) && hasMoreThanOneJavaFile();
boolean missingLibraries = !settings.hasKey(librariesProperty) && hasJavaFiles();
if (missingBinary && missingLibraries) {
classpathWarnings.add(String.format("Missing '%s' and '%s' properties. You might end up with less precise analysis results.", binariesProperty, librariesProperty));
} else if (missingBinary) {
classpathWarnings.add(String.format("Missing '%s' property. You might end up with less precise analysis results.", binariesProperty));
} else if (missingLibraries) {
classpathWarnings.add(String.format("Missing '%s' property. You might end up with less precise analysis results.", librariesProperty));
}
}

protected List<File> getJdkJars() {
List<File> jdkClassesRoots = settings.get(ClasspathProperties.SONAR_JAVA_JDK_HOME)
.flatMap(AbstractClasspath::existingDirectoryOrLog)
.flatMap(this::existingDirectoryOrLog)
.map(File::toPath)
.map(JavaSdkUtil::getJdkClassesRoots)
.orElse(Collections.emptyList());
Expand All @@ -87,42 +131,43 @@ protected List<File> getJdkJars() {

static void logResolvedFiles(String property, Collection<File> files) {
if (LOG.isDebugEnabled()) {
LOG.debug(String.format("Property '%s' resolved with:%n%s", property, files.stream()
LOG.debug(String.format("Property '%s' resolved with: %s", property, files.stream()
.map(File::getAbsolutePath)
.collect(Collectors.joining("," + System.lineSeparator(), "[", "]"))));
.collect(Collectors.joining(",", "[", "]"))));
}
}

private static Optional<File> existingDirectoryOrLog(String path) {
private Optional<File> existingDirectoryOrLog(String path) {
LOG.debug("Property '{}' set with: {}", ClasspathProperties.SONAR_JAVA_JDK_HOME, path);
File file = new File(path);
if (!file.exists() || !file.isDirectory()) {
LOG.warn("Invalid value for '{}' property, defaulting to runtime JDK.{}Configured location does not exists: '{}'",
ClasspathProperties.SONAR_JAVA_JDK_HOME, System.lineSeparator(), file.getAbsolutePath());
classpathWarnings.add(String.format("Invalid value '%s' for '%s' property, defaulting to runtime JDK.", file.getAbsolutePath(), ClasspathProperties.SONAR_JAVA_JDK_HOME));
return Optional.empty();
}
return Optional.of(file);
}

protected abstract void init();

public abstract void logSuspiciousEmptyLibraries();
public void logClasspathWarnings() {
for (String warning : classpathWarnings) {
LOG.warn(warning);
analysisWarnings.addUnique(warning);
}
Comment thread
alban-auzeill marked this conversation as resolved.
classpathWarnings.clear();
}
Comment thread
alban-auzeill marked this conversation as resolved.

protected Set<File> getFilesFromProperty(String property) {
Set<File> result = new LinkedHashSet<>();
if (settings.hasKey(property)) {
List<String> fileNames = Arrays.stream(settings.getStringArray(property))
.filter(s -> !s.isEmpty()).toList();
File baseDir = fs.baseDir();
boolean hasJavaSources = hasJavaSources();
boolean hasJavaFiles = hasJavaFiles();
boolean validateLibs = validateLibraries;
boolean isLibraryProperty = property.endsWith("libraries");
for (String pathPattern : fileNames) {
Set<File> libraryFilesForPattern = getFilesForPattern(baseDir.toPath(), pathPattern, isLibraryProperty);
if (validateLibraries && libraryFilesForPattern.isEmpty() && hasJavaSources) {
LOG.error("Invalid value for '{}' property.", property);
String message = "No files nor directories matching '" + pathPattern + "'";
throw new IllegalStateException(message);
if (validateLibraries && libraryFilesForPattern.isEmpty() && hasJavaFiles) {
classpathWarnings.add(String.format("Invalid value for '%s', no files nor directories matching '%s'.", property, pathPattern));
}
validateLibraries = validateLibs;
result.addAll(libraryFilesForPattern);
Expand All @@ -134,12 +179,22 @@ protected Set<File> getFilesFromProperty(String property) {
return result;
}

protected boolean hasJavaSources() {
return fs.hasFiles(fs.predicates().and(fs.predicates().hasLanguage("java"), fs.predicates().hasType(fileType)));
protected boolean hasMoreThanOneJavaFile() {
// No need to iterate over the entire collection, checking that there are two elements is enough
Iterator<InputFile> iterator = fs.inputFiles(sourcePredicate()).iterator();
if (iterator.hasNext()) {
iterator.next();
return iterator.hasNext();
}
return false;
}

protected boolean hasMoreThanOneJavaFile() {
return CollectionUtils.size(fs.inputFiles(fs.predicates().and(fs.predicates().hasLanguage("java"), fs.predicates().hasType(fileType)))) > 1;
protected boolean hasJavaFiles() {
Comment thread
alban-auzeill marked this conversation as resolved.
return fs.hasFiles(sourcePredicate());
}

private FilePredicate sourcePredicate() {
return fs.predicates().and(fs.predicates().hasLanguage("java"), fs.predicates().hasType(fileType));
}

private Set<File> getFilesForPattern(Path baseDir, String pathPattern, boolean libraryProperty) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,86 +16,22 @@
*/
package org.sonar.java.classpath;

import java.io.File;
import java.util.LinkedHashSet;
import java.util.Set;
import javax.annotation.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.batch.fs.FileSystem;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.config.Configuration;
import org.sonar.java.AnalysisException;
import org.sonar.java.AnalysisWarningsWrapper;

public class ClasspathForMain extends AbstractClasspath {

private static final Logger LOG = LoggerFactory.getLogger(ClasspathForMain.class);
import static org.sonar.java.classpath.ClasspathProperties.SONAR_JAVA_BINARIES;
import static org.sonar.java.classpath.ClasspathProperties.SONAR_JAVA_LIBRARIES;

private final AnalysisWarningsWrapper analysisWarnings;
private boolean hasSuspiciousEmptyLibraries = false;
private boolean alreadyReported = false;
public class ClasspathForMain extends AbstractClasspath {

public ClasspathForMain(Configuration settings, FileSystem fs, AnalysisWarningsWrapper analysisWarnings) {
super(settings, fs, InputFile.Type.MAIN);
this.analysisWarnings = analysisWarnings;
super(settings, fs, InputFile.Type.MAIN, SONAR_JAVA_BINARIES, SONAR_JAVA_LIBRARIES, analysisWarnings);
}

public ClasspathForMain(Configuration settings, FileSystem fs) {
this(settings, fs, AnalysisWarningsWrapper.NOOP_ANALYSIS_WARNINGS);
}

@Override
protected void init() {
if (!initialized) {
validateLibraries = fs.hasFiles(fs.predicates().all());
initialized = true;
binaries.addAll(getFilesFromProperty(ClasspathProperties.SONAR_JAVA_BINARIES));

Set<File> libraries = new LinkedHashSet<>(getJdkJars());
Set<File> extraLibraries = getFilesFromProperty(ClasspathProperties.SONAR_JAVA_LIBRARIES);
logResolvedFiles(ClasspathProperties.SONAR_JAVA_LIBRARIES, extraLibraries);
libraries.addAll(extraLibraries);
if (binaries.isEmpty() && libraries.isEmpty() && useDeprecatedProperties()) {
throw new AnalysisException(
"sonar.binaries and sonar.libraries are not supported since version 4.0 of the SonarSource Java Analyzer,"
+ " please use sonar.java.binaries and sonar.java.libraries instead");
}
hasSuspiciousEmptyLibraries = libraries.isEmpty() && hasJavaSources();

if (binaries.isEmpty() && hasMoreThanOneJavaFile()) {
if(isSonarLint()) {
LOG.warn("sonar.java.binaries is empty, please double check your configuration");
} else {
throw new AnalysisException("Your project contains .java files, please provide compiled classes with sonar.java.binaries property,"
+ " or exclude them from the analysis with sonar.exclusions property.");
}
}

elements.addAll(binaries);
elements.addAll(libraries);
}
}

protected boolean isSonarLint() {
return false;
}

private boolean useDeprecatedProperties() {
return isNotNullOrEmpty(settings.get("sonar.binaries").orElse(null)) && isNotNullOrEmpty(settings.get("sonar.libraries").orElse(null));
}

private static boolean isNotNullOrEmpty(@Nullable String string) {
return string != null && !string.isEmpty();
}

@Override
public void logSuspiciousEmptyLibraries() {
if (hasSuspiciousEmptyLibraries && !alreadyReported) {
String warning = String.format(ClasspathProperties.EMPTY_LIBRARIES_WARNING_TEMPLATE, "SOURCE", ClasspathProperties.SONAR_JAVA_LIBRARIES);
LOG.warn(warning);
analysisWarnings.addUnique(warning);
alreadyReported = true;
}
}
}

This file was deleted.

Loading
Loading