| /* |
| * 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; |
| } |
| } |