blob: 705d832d9b8f629321a9807f6e79d9c538fb0f83 [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.projectview;
import com.google.common.collect.Lists;
import com.google.idea.blaze.base.io.WorkspaceScanner;
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.section.ListSection;
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.projectview.section.sections.ExcludedSourceSection;
import com.google.idea.blaze.base.scope.BlazeContext;
import com.google.idea.blaze.base.scope.output.IssueOutput;
import com.google.idea.blaze.base.sync.BlazeSyncPlugin;
import com.google.idea.blaze.base.sync.projectview.WorkspaceLanguageSettings;
import com.intellij.openapi.util.io.FileUtil;
import java.util.List;
/**
* Verifies project views.
*/
public class ProjectViewVerifier {
public static class MissingDirectoryIssueData extends IssueOutput.IssueData {
public final WorkspacePath workspacePath;
public MissingDirectoryIssueData(WorkspacePath workspacePath) {
this.workspacePath = workspacePath;
}
}
/**
* Verifies the project view. Any errors are output to the context as issues.
*/
public static boolean verifyProjectView(
BlazeContext context,
WorkspaceRoot workspaceRoot,
ProjectViewSet projectViewSet,
WorkspaceLanguageSettings workspaceLanguageSettings) {
if (!verifyIncludedPackagesExistOnDisk(context, workspaceRoot, projectViewSet)) {
return false;
}
if (!verifyDirectoriesAreNonOverlapping(context, projectViewSet)) {
return false;
}
if (!verifyIncludedPackagesAreNotExcluded(context, projectViewSet)) {
return false;
}
for (BlazeSyncPlugin syncPlugin : BlazeSyncPlugin.EP_NAME.getExtensions()) {
if (!syncPlugin.validateProjectView(context, projectViewSet, workspaceLanguageSettings)) {
return false;
}
}
if (!projectViewSet.listItems(ExcludedSourceSection.KEY).isEmpty()) {
IssueOutput
.warn("excluded_sources is deprecated and has no effect.")
.inFile(projectViewSet.getTopLevelProjectViewFile().projectViewFile)
.submit(context);
}
return true;
}
private static boolean verifyDirectoriesAreNonOverlapping(
BlazeContext context,
ProjectViewSet projectViewSet) {
boolean ok = true;
List<WorkspacePath> includedDirectories = getIncludedDirectories(projectViewSet);
for (WorkspacePath includedDirectory : includedDirectories) {
for (ProjectViewSet.ProjectViewFile projectViewFile : projectViewSet.getProjectViewFiles()) {
ListSection<DirectoryEntry> directorySection = projectViewFile.projectView.getSectionOfType(DirectorySection.KEY);
if (directorySection == null) {
continue;
}
for (DirectoryEntry entry : directorySection.items()) {
if (!entry.included) {
continue;
}
if (isAncestor(includedDirectory.relativePath(), entry.directory.relativePath())) {
IssueOutput
.error(String.format("Overlapping directories: %s already included by %s",
entry.directory.toString(),
includedDirectory.toString()))
.inFile(projectViewFile.projectViewFile)
.submit(context);
ok = false;
}
}
}
}
return ok;
}
/**
* Returns true if 'path' is a strict child of 'ancestorPath'.
*/
private static boolean isAncestor(String ancestorPath, String path) {
// FileUtil.isAncestor has a bug in its handling of equal, empty paths (it ignores the 'strict' flag in this case).
if (ancestorPath.equals(path)) {
return false;
}
return FileUtil.isAncestor(ancestorPath, path, true);
}
private static boolean verifyIncludedPackagesAreNotExcluded(
BlazeContext context,
ProjectViewSet projectViewSet) {
boolean ok = true;
List<WorkspacePath> includedDirectories = getIncludedDirectories(projectViewSet);
for (WorkspacePath includedDirectory : includedDirectories) {
for (ProjectViewSet.ProjectViewFile projectViewFile : projectViewSet.getProjectViewFiles()) {
ListSection<DirectoryEntry> directorySection = projectViewFile.projectView.getSectionOfType(DirectorySection.KEY);
if (directorySection == null) {
continue;
}
for (DirectoryEntry entry : directorySection.items()) {
if (entry.included) {
continue;
}
WorkspacePath excludedDirectory = entry.directory;
if (FileUtil.isAncestor(
excludedDirectory.relativePath(),
includedDirectory.relativePath(),
false)) {
IssueOutput
.error(String.format("%s is included, but that contradicts %s which was excluded",
includedDirectory.toString(),
excludedDirectory.toString()))
.inFile(projectViewFile.projectViewFile)
.submit(context);
ok = false;
}
}
}
}
return ok;
}
private static List<WorkspacePath> getIncludedDirectories(ProjectViewSet projectViewSet) {
List<WorkspacePath> includedDirectories = Lists.newArrayList();
for (DirectoryEntry entry : projectViewSet.listItems(DirectorySection.KEY)) {
if (entry.included) {
includedDirectories.add(entry.directory);
}
}
return includedDirectories;
}
private static boolean verifyIncludedPackagesExistOnDisk(
BlazeContext context,
WorkspaceRoot workspaceRoot,
ProjectViewSet projectViewSet) {
boolean ok = true;
WorkspaceScanner workspaceScanner = WorkspaceScanner.getInstance();
for (ProjectViewSet.ProjectViewFile projectViewFile : projectViewSet.getProjectViewFiles()) {
ListSection<DirectoryEntry> directorySection = projectViewFile.projectView.getSectionOfType(DirectorySection.KEY);
if (directorySection == null) {
continue;
}
for (DirectoryEntry entry : directorySection.items()) {
if (!entry.included) {
continue;
}
WorkspacePath workspacePath = entry.directory;
if (!workspaceScanner.exists(workspaceRoot, workspacePath)) {
IssueOutput
.error(String.format("Directory '%s' specified in import roots not found under workspace root '%s'",
workspacePath, workspaceRoot))
.inFile(projectViewFile.projectViewFile)
.withData(new MissingDirectoryIssueData(workspacePath))
.submit(context);
ok = false;
}
}
}
return ok;
}
}