blob: 38568dcf09540c66ccd0f3a957e2170bed488b27 [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.base.sync.projectview;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.idea.blaze.base.bazel.BuildSystemProvider;
import com.google.idea.blaze.base.model.primitives.Label;
import com.google.idea.blaze.base.model.primitives.WorkspacePath;
import com.google.idea.blaze.base.model.primitives.WorkspaceRoot;
import com.google.idea.blaze.base.projectview.ProjectViewManager;
import com.google.idea.blaze.base.projectview.ProjectViewSet;
import com.google.idea.blaze.base.projectview.section.sections.DirectoryEntry;
import com.google.idea.blaze.base.projectview.section.sections.DirectorySection;
import com.google.idea.blaze.base.settings.Blaze;
import com.google.idea.blaze.base.settings.Blaze.BuildSystem;
import com.google.idea.blaze.base.util.WorkspacePathUtil;
import com.intellij.openapi.project.Project;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.util.Collection;
import java.util.Set;
import javax.annotation.Nullable;
/** The roots to import. Derived from project view. */
public final class ImportRoots {
/** Returns the ImportRoots for the project, or null if it's not a blaze project. */
@Nullable
public static ImportRoots forProjectSafe(Project project) {
WorkspaceRoot root = WorkspaceRoot.fromProjectSafe(project);
ProjectViewSet projectViewSet = ProjectViewManager.getInstance(project).getProjectViewSet();
if (root == null || projectViewSet == null) {
return null;
}
return ImportRoots.builder(root, Blaze.getBuildSystem(project)).add(projectViewSet).build();
}
/** Builder for import roots */
public static class Builder {
private final ImmutableCollection.Builder<WorkspacePath> rootDirectoriesBuilder =
ImmutableList.builder();
private final ImmutableSet.Builder<WorkspacePath> excludeDirectoriesBuilder =
ImmutableSet.builder();
private final WorkspaceRoot workspaceRoot;
private final BuildSystem buildSystem;
private Builder(WorkspaceRoot workspaceRoot, BuildSystem buildSystem) {
this.workspaceRoot = workspaceRoot;
this.buildSystem = buildSystem;
}
public Builder add(ProjectViewSet projectViewSet) {
for (DirectoryEntry entry : projectViewSet.listItems(DirectorySection.KEY)) {
add(entry);
}
return this;
}
@VisibleForTesting
public Builder add(DirectoryEntry entry) {
if (entry.included) {
rootDirectoriesBuilder.add(entry.directory);
} else {
excludeDirectoriesBuilder.add(entry.directory);
}
return this;
}
public ImportRoots build() {
// Remove any duplicates and any overlapping directories
ImmutableSet<WorkspacePath> minimalRootDirectories =
WorkspacePathUtil.calculateMinimalWorkspacePaths(rootDirectoriesBuilder.build());
// for bazel projects, if we're including the workspace root,
// we force-exclude the bazel artifact directories
// (e.g. bazel-bin, bazel-genfiles).
if (buildSystem == BuildSystem.Bazel && hasWorkspaceRoot(minimalRootDirectories)) {
excludeBuildSystemArtifacts();
}
ImmutableSet<WorkspacePath> minimalExcludes =
WorkspacePathUtil.calculateMinimalWorkspacePaths(excludeDirectoriesBuilder.build());
return new ImportRoots(minimalRootDirectories, minimalExcludes);
}
private void excludeBuildSystemArtifacts() {
for (String dir :
BuildSystemProvider.getBuildSystemProvider(buildSystem)
.buildArtifactDirectories(workspaceRoot)) {
excludeDirectoriesBuilder.add(new WorkspacePath(dir));
}
}
private static boolean hasWorkspaceRoot(ImmutableCollection<WorkspacePath> rootDirectories) {
return rootDirectories.stream().anyMatch(WorkspacePath::isWorkspaceRoot);
}
}
private final ImmutableCollection<WorkspacePath> rootDirectories;
private final ImmutableSet<WorkspacePath> excludeDirectories;
public static Builder builder(WorkspaceRoot workspaceRoot, BuildSystem buildSystem) {
return new Builder(workspaceRoot, buildSystem);
}
private ImportRoots(
ImmutableCollection<WorkspacePath> rootDirectories,
ImmutableSet<WorkspacePath> excludeDirectories) {
this.rootDirectories = rootDirectories;
this.excludeDirectories = excludeDirectories;
}
public Collection<WorkspacePath> rootDirectories() {
return rootDirectories;
}
public Set<WorkspacePath> excludeDirectories() {
return excludeDirectories;
}
/** Returns true if this rule should be imported as source. */
public boolean importAsSource(Label label) {
return containsLabel(label);
}
private boolean containsLabel(Label label) {
return !label.isExternal() && containsWorkspacePath(label.blazePackage());
}
public boolean containsWorkspacePath(WorkspacePath workspacePath) {
boolean included = false;
boolean excluded = false;
for (WorkspacePath rootDirectory : rootDirectories()) {
included = included || isSubdirectory(rootDirectory, workspacePath);
}
for (WorkspacePath excludeDirectory : excludeDirectories()) {
excluded = excluded || isSubdirectory(excludeDirectory, workspacePath);
}
return included && !excluded;
}
private static boolean isSubdirectory(WorkspacePath ancestor, WorkspacePath descendant) {
if (ancestor.isWorkspaceRoot()) {
return true;
}
Path ancestorPath = FileSystems.getDefault().getPath(ancestor.relativePath());
Path descendantPath = FileSystems.getDefault().getPath(descendant.relativePath());
return descendantPath.startsWith(ancestorPath);
}
}