blob: 78c489086d3e27b57a2b712fd688282de31c9605 [file] [log] [blame]
/*
* Copyright 2016 The Bazel Authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.idea.blaze.cpp;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.idea.blaze.base.ideinfo.TargetKey;
import com.google.idea.blaze.base.sync.workspace.WorkspacePathResolver;
import com.google.idea.sdkcompat.cidr.OCCompilerMacrosAdapter;
import com.google.idea.sdkcompat.cidr.OCResolveConfigurationAdapter;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vfs.VirtualFile;
import com.jetbrains.cidr.lang.OCFileTypeHelpers;
import com.jetbrains.cidr.lang.OCLanguageKind;
import com.jetbrains.cidr.lang.preprocessor.OCImportGraph;
import com.jetbrains.cidr.lang.workspace.OCLanguageKindCalculator;
import com.jetbrains.cidr.lang.workspace.OCResolveConfiguration;
import com.jetbrains.cidr.lang.workspace.OCResolveRootAndConfiguration;
import com.jetbrains.cidr.lang.workspace.OCWorkspaceUtil;
import com.jetbrains.cidr.lang.workspace.compiler.CidrCompilerResult;
import com.jetbrains.cidr.lang.workspace.compiler.OCCompilerSettings;
import com.jetbrains.cidr.lang.workspace.headerRoots.HeaderRoots;
import com.jetbrains.cidr.lang.workspace.headerRoots.HeadersSearchRoot;
import com.jetbrains.cidr.toolchains.CompilerInfoCache;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import javax.annotation.Nullable;
/** Blaze implementation of {@link OCResolveConfiguration}. */
final class BlazeResolveConfiguration extends OCResolveConfigurationAdapter {
private final Project project;
private final ConcurrentMap<Pair<OCLanguageKind, VirtualFile>, HeaderRoots>
libraryIncludeRootsCache = new ConcurrentHashMap<>();
private final WorkspacePathResolver workspacePathResolver;
private final BlazeResolveConfigurationData configurationData;
private String displayNameIdentifier;
private BlazeResolveConfiguration(
Project project,
WorkspacePathResolver workspacePathResolver,
BlazeResolveConfigurationData configurationData) {
this.project = project;
this.workspacePathResolver = workspacePathResolver;
this.configurationData = configurationData;
}
static BlazeResolveConfiguration createForTargets(
Project project,
WorkspacePathResolver workspacePathResolver,
BlazeResolveConfigurationData configurationData,
Collection<TargetKey> targets) {
BlazeResolveConfiguration result =
new BlazeResolveConfiguration(project, workspacePathResolver, configurationData);
result.representMultipleTargets(targets);
return result;
}
/**
* Indicate that this single configuration represents N other targets. NOTE: this changes the
* identifier used by {@link #compareTo}, so any data structures using compareTo must be
* invalidated when this changes.
*/
void representMultipleTargets(Collection<TargetKey> targets) {
TargetKey minTargetKey = targets.stream().min(TargetKey::compareTo).orElse(null);
Preconditions.checkNotNull(minTargetKey);
String minTarget = minTargetKey.toString();
if (targets.size() == 1) {
displayNameIdentifier = minTarget;
} else {
displayNameIdentifier =
String.format("%s and %d other target(s)", minTarget, targets.size() - 1);
}
}
@Override
public Project getProject() {
return project;
}
public WorkspacePathResolver getWorkspacePathResolver() {
return workspacePathResolver;
}
@Override
public String getDisplayName(boolean shorten) {
return displayNameIdentifier;
}
@Override
public int compareTo(OCResolveConfiguration other) {
// This is a bit of a weak comparison -- it just uses the display name (ignoring case)
// and doesn't compare the actual fields of the configuration.
// It should only be used for simple things like sorting for the UI.
return OCWorkspaceUtil.compareConfigurations(this, other);
}
@Override
public int hashCode() {
// There should only be one configuration per target, and the display name is derived
// from a target
return Objects.hashCode(displayNameIdentifier);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof BlazeResolveConfiguration)) {
return false;
}
BlazeResolveConfiguration that = (BlazeResolveConfiguration) obj;
return compareTo(that) == 0;
}
@Nullable
@Override
public OCLanguageKind getDeclaredLanguageKind(VirtualFile sourceOrHeaderFile) {
String fileName = sourceOrHeaderFile.getName();
if (OCFileTypeHelpers.isSourceFile(fileName)) {
return getLanguageKind(sourceOrHeaderFile);
}
if (OCFileTypeHelpers.isHeaderFile(fileName)) {
return getLanguageKind(getSourceFileForHeaderFile(sourceOrHeaderFile));
}
return null;
}
private OCLanguageKind getLanguageKind(@Nullable VirtualFile sourceFile) {
OCLanguageKind kind = OCLanguageKindCalculator.tryFileTypeAndExtension(project, sourceFile);
return kind != null ? kind : getMaximumLanguageKind();
}
@Nullable
private VirtualFile getSourceFileForHeaderFile(VirtualFile headerFile) {
Collection<VirtualFile> roots = OCImportGraph.getAllHeaderRoots(project, headerFile);
final String headerNameWithoutExtension = headerFile.getNameWithoutExtension();
for (VirtualFile root : roots) {
if (root.getNameWithoutExtension().equals(headerNameWithoutExtension)) {
return root;
}
}
return null;
}
@Override
public OCLanguageKind getMaximumLanguageKind() {
return OCLanguageKind.CPP;
}
@Override
public HeaderRoots getProjectHeadersRoots() {
return configurationData.projectIncludeRoots;
}
@Override
public HeaderRoots getLibraryHeadersRoots(OCResolveRootAndConfiguration headerContext) {
OCLanguageKind languageKind = headerContext.getKind();
VirtualFile sourceFile = headerContext.getRootFile();
if (languageKind == null) {
languageKind = getLanguageKind(sourceFile);
}
Pair<OCLanguageKind, VirtualFile> cacheKey = Pair.create(languageKind, sourceFile);
return libraryIncludeRootsCache.computeIfAbsent(
cacheKey,
key -> {
OCLanguageKind lang = key.first;
VirtualFile source = key.second;
ImmutableSet.Builder<HeadersSearchRoot> roots = ImmutableSet.builder();
if (lang == OCLanguageKind.C) {
roots.addAll(configurationData.cLibraryIncludeRoots);
} else {
roots.addAll(configurationData.cppLibraryIncludeRoots);
}
CidrCompilerResult<CompilerInfoCache.Entry> compilerInfoCacheHolder =
configurationData.compilerSettings.getCompilerInfo(lang, source);
CompilerInfoCache.Entry compilerInfo = compilerInfoCacheHolder.getResult();
if (compilerInfo != null) {
roots.addAll(compilerInfo.headerSearchPaths);
}
return new HeaderRoots(roots.build().asList());
});
}
@Override
public OCCompilerMacrosAdapter getCompilerMacros() {
return configurationData.compilerMacros;
}
@Override
public OCCompilerSettings getCompilerSettings() {
return configurationData.compilerSettings;
}
@Override
public Object getIndexingCluster() {
return configurationData.toolchainIdeInfo;
}
/* #api163 */
@Nullable
@Override
public VirtualFile getPrecompiledHeader() {
return null;
}
/* #api163 */
@Override
public OCLanguageKind getPrecompiledLanguageKind() {
return getMaximumLanguageKind();
}
/* #api171 */
@Override
public Set<VirtualFile> getPrecompiledHeaders() {
return ImmutableSet.of();
}
/* #api171 */
@Override
public List<VirtualFile> getPrecompiledHeaders(OCLanguageKind kind, VirtualFile sourceFile) {
return ImmutableList.of();
}
/* #api171 */
@Override
public Collection<VirtualFile> getSources() {
return ImmutableList.of();
}
/* #api172 */
@Override
public String getPreprocessorDefines(OCLanguageKind kind, VirtualFile virtualFile) {
return configurationData.compilerMacros.getAllDefines(kind, virtualFile);
}
}