/*
* SonarLint for Eclipse
* Copyright (C) 2015-2017 SonarSource SA
* sonarlint@sonarsource.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.sonarlint.eclipse.cdt.internal;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import javax.annotation.CheckForNull;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.model.CoreModel;
import org.eclipse.cdt.core.parser.IScannerInfo;
import org.eclipse.cdt.core.parser.IScannerInfoProvider;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.content.IContentType;
import org.sonarlint.eclipse.core.SonarLintLogger;
import org.sonarlint.eclipse.core.analysis.IPreAnalysisContext;
import org.sonarlint.eclipse.core.internal.jobs.DefaultPreAnalysisContext;
import org.sonarlint.eclipse.core.resource.ISonarLintFile;
import org.sonarlint.eclipse.core.resource.ISonarLintProject;
public class CdtUtils {
private static final String CFAMILY_USE_CACHE = "sonar.cfamily.useCache";
private static final String BUILD_WRAPPER_OUTPUT_PROP = "sonar.cfamily.build-wrapper-output";
private static final String BUILD_WRAPPER_OUTPUT_FILENAME = "build-wrapper-dump.json";
private static final Charset BUILD_WRAPPER_OUTPUT_CHARSET = StandardCharsets.UTF_8;
private final BuildWrapperJsonFactory jsonFactory;
private final CCorePlugin cCorePlugin;
private final Predicate<IFile> fileValidator;
private final SonarLintLogger logger;
private final BiFunction<IProject, String, IContentType> contentTypeResolver;
public CdtUtils() {
this(new BuildWrapperJsonFactory(), CCorePlugin.getDefault(), CoreModel::isTranslationUnit,
CCorePlugin::getContentType, SonarLintLogger.get());
}
public CdtUtils(BuildWrapperJsonFactory jsonFactory, CCorePlugin cCorePlugin, Predicate<IFile> fileValidator,
BiFunction<IProject, String, IContentType> contentTypeResolver, SonarLintLogger logger) {
this.jsonFactory = jsonFactory;
this.cCorePlugin = cCorePlugin;
this.fileValidator = fileValidator;
this.logger = logger;
this.contentTypeResolver = contentTypeResolver;
}
public void configure(IPreAnalysisContext context, IProgressMonitor monitor) {
Collection<ISonarLintFile> filesToAnalyze = context.getFilesToAnalyze()
.stream()
.filter(f -> f.getResource() instanceof IFile && fileValidator.test((IFile) f.getResource()))
.collect(Collectors.toList());
try {
Collection<ConfiguredFile> configuredFiles = configureCProject(context, context.getProject(), filesToAnalyze);
Path jsonPath = writeJson(context, context.getProject(), configuredFiles);
logger.debug("Wrote build info to: " + jsonPath.toString());
context.setAnalysisProperty(CFAMILY_USE_CACHE, Boolean.FALSE.toString());
context.setAnalysisProperty(BUILD_WRAPPER_OUTPUT_PROP, jsonPath.getParent().toString());
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
private Collection<ConfiguredFile> configureCProject(IPreAnalysisContext context, ISonarLintProject project, Collection<ISonarLintFile> filesToAnalyze) {
List<ConfiguredFile> files = new LinkedList<>();
IScannerInfoProvider infoProvider = cCorePlugin.getScannerInfoProvider((IProject) project.getResource());
for (ISonarLintFile file : filesToAnalyze) {
ConfiguredFile.Builder builder = new ConfiguredFile.Builder((IFile) file.getResource());
String path = ((DefaultPreAnalysisContext) context).getLocalPath(file);
IScannerInfo fileInfo = infoProvider.getScannerInformation(file.getResource());
builder.includes(fileInfo.getIncludePaths() != null ? fileInfo.getIncludePaths() : new String[0])
.symbols(fileInfo.getDefinedSymbols() != null ? fileInfo.getDefinedSymbols() : Collections.emptyMap())
.path(path);
files.add(builder.build());
}
return files;
}
private Path writeJson(IPreAnalysisContext context, ISonarLintProject project, Collection<ConfiguredFile> files) throws IOException {
String json = jsonFactory.create(files, getBaseDir(context, project));
return createJsonFile(context.getAnalysisTemporaryFolder(), json);
}
private static String getBaseDir(IPreAnalysisContext context, ISonarLintProject project) {
IPath projectLocation = project.getResource().getLocation();
if (projectLocation != null) {
return projectLocation.toFile().toString();
}
// In some unfrequent cases the project may be virtual and don't have physical location
// so fallback to use analysis work dir (where physical file copy should be created anyway)
return context.getAnalysisTemporaryFolder().toString();
}
@CheckForNull
private String getFileLanguage(IProject project, IFile file) {
IPath location = file.getLocation();
if (location == null) {
return null;
}
IContentType contentType = contentTypeResolver.apply(project, location.toOSString());
if (contentType == null) {
return null;
}
switch (contentType.getId()) {
case CCorePlugin.CONTENT_TYPE_CHEADER:
case CCorePlugin.CONTENT_TYPE_CSOURCE:
return "c";
case CCorePlugin.CONTENT_TYPE_CXXHEADER:
case CCorePlugin.CONTENT_TYPE_CXXSOURCE:
return "cpp";
default:
return null;
}
}
private static Path createJsonFile(Path workDir, String content) throws IOException {
Path jsonFilePath = workDir.resolve(BUILD_WRAPPER_OUTPUT_FILENAME);
Files.createDirectories(workDir);
Files.write(jsonFilePath, content.getBytes(BUILD_WRAPPER_OUTPUT_CHARSET));
return jsonFilePath;
}
public String language(IFile iFile) {
return getFileLanguage(iFile.getProject(), iFile);
}
}