blob: 81ade0926909d225e0b432e7b3526766d2e70998 [file] [log] [blame]
/*
* Copyright 2017 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.workspace;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableMap;
import com.google.idea.blaze.base.bazel.BuildSystemProvider;
import com.google.idea.blaze.base.io.FileAttributeProvider;
import com.google.idea.blaze.base.model.BlazeProjectData;
import com.google.idea.blaze.base.model.primitives.Label;
import com.google.idea.blaze.base.model.primitives.TargetName;
import com.google.idea.blaze.base.model.primitives.WorkspacePath;
import com.google.idea.blaze.base.model.primitives.WorkspaceRoot;
import com.google.idea.blaze.base.settings.Blaze;
import com.google.idea.blaze.base.settings.Blaze.BuildSystem;
import com.google.idea.blaze.base.sync.SyncCache;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.io.FileUtil;
import java.io.File;
import java.util.Arrays;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
/** External-workspace-aware resolution of workspace paths. */
public class WorkspaceHelper {
private static class Workspace {
private final WorkspaceRoot root;
@Nullable private final String externalWorkspaceName;
private Workspace(WorkspaceRoot root, @Nullable String externalWorkspaceName) {
this.root = root;
this.externalWorkspaceName = externalWorkspaceName;
}
}
@Nullable
public static WorkspaceRoot resolveExternalWorkspace(Project project, String workspaceName) {
Map<String, WorkspaceRoot> externalWorkspaces = getExternalWorkspaceRoots(project);
return externalWorkspaces != null ? externalWorkspaces.get(workspaceName) : null;
}
/** Resolves the parent blaze package corresponding to this label. */
@Nullable
public static File resolveBlazePackage(Project project, Label label) {
if (!label.isExternal()) {
WorkspacePathResolver pathResolver =
WorkspacePathResolverProvider.getInstance(project).getPathResolver();
return pathResolver != null ? pathResolver.resolveToFile(label.blazePackage()) : null;
}
Map<String, WorkspaceRoot> externalWorkspaces = getExternalWorkspaceRoots(project);
if (externalWorkspaces == null) {
return null;
}
WorkspaceRoot root = externalWorkspaces.get(label.externalWorkspaceName());
return root != null ? root.fileForPath(label.blazePackage()) : null;
}
@Nullable
public static WorkspacePath resolveWorkspacePath(Project project, File absoluteFile) {
Workspace workspace = resolveWorkspace(project, absoluteFile);
return workspace != null ? workspace.root.workspacePathForSafe(absoluteFile) : null;
}
/** Converts a file to the corresponding BUILD label for this project, if valid. */
@Nullable
public static Label getBuildLabel(Project project, File absoluteFile) {
Workspace workspace = resolveWorkspace(project, absoluteFile);
if (workspace == null) {
return null;
}
WorkspacePath workspacePath = workspace.root.workspacePathForSafe(absoluteFile);
if (workspacePath == null) {
return null;
}
return deriveLabel(project, workspace, workspacePath);
}
@Nullable
private static Workspace resolveWorkspace(Project project, File absoluteFile) {
WorkspacePathResolver pathResolver =
WorkspacePathResolverProvider.getInstance(project).getPathResolver();
if (pathResolver == null) {
return null;
}
// try project workspace first
WorkspaceRoot root = pathResolver.findWorkspaceRoot(absoluteFile);
if (root != null) {
return new Workspace(root, null);
}
Map<String, WorkspaceRoot> externalWorkspaces = getExternalWorkspaceRoots(project);
if (externalWorkspaces == null) {
return null;
}
for (Entry<String, WorkspaceRoot> entry : externalWorkspaces.entrySet()) {
root = entry.getValue();
WorkspacePath workspacePath = root.workspacePathForSafe(absoluteFile);
if (workspacePath != null) {
return new Workspace(root, entry.getKey());
}
}
return null;
}
private static Label deriveLabel(
Project project, Workspace workspace, WorkspacePath workspacePath) {
BuildSystemProvider provider = Blaze.getBuildSystemProvider(project);
File file = workspace.root.fileForPath(workspacePath);
if (provider.isBuildFile(file.getName())) {
return Label.create(
workspace.externalWorkspaceName,
workspace.root.workspacePathFor(file.getParentFile()),
TargetName.create("__pkg__"));
}
WorkspacePath packagePath = getPackagePath(provider, workspace.root, workspacePath);
if (packagePath == null) {
return null;
}
TargetName targetName =
TargetName.createIfValid(
FileUtil.getRelativePath(workspace.root.fileForPath(packagePath), file));
return targetName != null
? Label.create(workspace.externalWorkspaceName, packagePath, targetName)
: null;
}
private static WorkspacePath getPackagePath(
BuildSystemProvider provider, WorkspaceRoot root, WorkspacePath workspacePath) {
File file = root.fileForPath(workspacePath).getParentFile();
while (file != null && FileUtil.isAncestor(root.directory(), file, false)) {
if (provider.findBuildFileInDirectory(file) != null) {
return root.workspacePathFor(file);
}
file = file.getParentFile();
}
return null;
}
@Nullable
private static synchronized Map<String, WorkspaceRoot> getExternalWorkspaceRoots(
Project project) {
if (Blaze.getBuildSystem(project) == BuildSystem.Blaze) {
return ImmutableMap.of();
}
return SyncCache.getInstance(project)
.get(WorkspaceHelper.class, WorkspaceHelper::enumerateExternalWorkspaces);
}
@SuppressWarnings("unused")
private static Map<String, WorkspaceRoot> enumerateExternalWorkspaces(
Project project, BlazeProjectData blazeProjectData) {
FileAttributeProvider provider = FileAttributeProvider.getInstance();
File[] children = provider.listFiles(getExternalSourceRoot(blazeProjectData));
if (children == null) {
return ImmutableMap.of();
}
return Arrays.stream(children)
.filter(provider::isDirectory)
.collect(Collectors.toMap(File::getName, WorkspaceRoot::new));
}
@VisibleForTesting
public static File getExternalSourceRoot(BlazeProjectData projectData) {
return new File(projectData.blazeInfo.getOutputBase(), "external");
}
}