Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Resolve symbolic links from external workspace to workspace root #5228

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import com.google.idea.blaze.base.ideinfo.Tags;
import com.google.idea.blaze.base.ideinfo.TargetIdeInfo;
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.ProjectViewSet;
import com.google.idea.blaze.base.projectview.section.sections.ExcludeTargetSection;
Expand All @@ -45,6 +46,10 @@ public boolean isSourceTarget(TargetIdeInfo target) {
return importRoots.importAsSource(target.getKey().getLabel()) && !importTargetOutput(target);
}

public boolean containsWorkspacePath(WorkspacePath workspacePath) {
return importRoots.containsWorkspacePath(workspacePath);
}

private boolean importTargetOutput(TargetIdeInfo target) {
return target.getTags().contains(Tags.TARGET_TAG_IMPORT_TARGET_OUTPUT)
|| target.getTags().contains(Tags.TARGET_TAG_IMPORT_AS_LIBRARY_LEGACY)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,17 @@
import com.google.idea.blaze.base.command.info.BlazeInfo;
import com.google.idea.blaze.base.ideinfo.ArtifactLocation;
import com.google.idea.blaze.base.model.RemoteOutputArtifacts;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.io.FileUtil;
import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.Objects;
import javax.annotation.Nullable;

/** Decodes intellij_ide_info.proto ArtifactLocation file paths */
public final class ArtifactLocationDecoderImpl implements ArtifactLocationDecoder {
private static final Logger LOG = Logger.getInstance(ArtifactLocationDecoderImpl.class);
private final BlazeInfo blazeInfo;
private final WorkspacePathResolver pathResolver;
private final RemoteOutputArtifacts remoteOutputs;
Expand Down Expand Up @@ -55,6 +59,11 @@ public BlazeArtifact resolveOutput(ArtifactLocation artifact) {

@Override
public File resolveSource(ArtifactLocation artifact) {
File mainWorkspaceFile = tryToResolveExternalArtifactToMainWorkspace(artifact);
if (mainWorkspaceFile != null) {
return mainWorkspaceFile;
}

return artifact.isMainWorkspaceSourceArtifact()
? pathResolver.resolveToFile(artifact.getRelativePath())
: null;
Expand All @@ -65,6 +74,12 @@ public File decode(ArtifactLocation artifactLocation) {
if (artifactLocation.isMainWorkspaceSourceArtifact()) {
return pathResolver.resolveToFile(artifactLocation.getRelativePath());
}

File mainWorkspaceFile = tryToResolveExternalArtifactToMainWorkspace(artifactLocation);
if (mainWorkspaceFile != null) {
return mainWorkspaceFile;
}

String path =
Paths.get(
blazeInfo.getExecutionRoot().getPath(),
Expand All @@ -74,6 +89,22 @@ public File decode(ArtifactLocation artifactLocation) {
return new File(FileUtil.toCanonicalPath(path));
}

private @Nullable File tryToResolveExternalArtifactToMainWorkspace(ArtifactLocation artifactLocation) {
if (artifactLocation.isExternal()) {
try {
File realFile = blazeInfo.getExecutionRoot().toPath()
.resolve(artifactLocation.getExecutionRootRelativePath()).toRealPath().toFile();
if (pathResolver.getWorkspacePath(realFile) != null) {
return realFile;
}
} catch (IOException ioException) {
LOG.warn("Failed to resolve real path for " + artifactLocation.getExecutionRootRelativePath(),
ioException);
}
}
return null;
}

@Override
public boolean equals(Object o) {
if (this == o) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.registry.Registry;
import java.io.File;
import java.io.IOException;
import javax.annotation.Nullable;
import org.jetbrains.annotations.NotNull;

/**
* Converts execution-root-relative paths to absolute files with a minimum of file system calls
Expand Down Expand Up @@ -135,7 +137,7 @@ public ImmutableList<File> resolveToIncludeDirectories(ExecutionRootPath path) {
if (firstPathComponent.equals(externalPrefix)) { // In external workspace
// External workspaces accumulate under the output base.
// The symlinks to them under the execution root are unstable, and only linked per build.
return ImmutableList.of(path.getFileRootedAt(outputBase));
return resolveToExternalWorkspaceWithSymbolicLinkResolution(path);
}
// Else, in main workspace
WorkspacePath workspacePath =
Expand All @@ -147,6 +149,27 @@ public ImmutableList<File> resolveToIncludeDirectories(ExecutionRootPath path) {
}
}

/**
* Resolves ExecutionRootPath to external workspace location and in case if item in external
* workspace is a link to workspace root then follows it and returns a path to workspace root
*/
@NotNull
private ImmutableList<File> resolveToExternalWorkspaceWithSymbolicLinkResolution(
ExecutionRootPath path) {
File fileInExecutionRoot = path.getFileRootedAt(outputBase);

try {
File realPath = fileInExecutionRoot.toPath().toRealPath().toFile();
if (workspacePathResolver.getWorkspacePath(realPath) != null) {
return ImmutableList.of(realPath);
}
} catch (IOException ioException) {
LOG.warn("Failed to resolve real path for " + fileInExecutionRoot, ioException);
}

return ImmutableList.of(fileInExecutionRoot);
}

public File getExecutionRoot() {
return executionRoot;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,18 @@
package com.google.idea.blaze.base.sync.workspace;

import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import com.google.devtools.intellij.aspect.Common;
import com.google.idea.blaze.base.BlazeTestCase;
import com.google.idea.blaze.base.command.info.BlazeInfo;
import com.google.idea.blaze.base.ideinfo.ArtifactLocation;
import com.google.idea.blaze.base.model.RemoteOutputArtifacts;
import com.google.idea.blaze.base.model.primitives.WorkspaceRoot;
import java.io.File;
import java.io.IOException;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
Expand Down Expand Up @@ -115,4 +121,38 @@ public void testExternalDerivedArtifact() {
assertThat(decoder.decode(artifactLocation).getPath())
.isEqualTo(OUTPUT_BASE + "/execroot/repo_name/blaze-out/crosstool/bin/com/google/Bla.java");
}

@Test
public void testResolveSourceToProjectWorkspace() throws IOException {
ArtifactLocation artifactLocation =
ArtifactLocation.fromProto(
Common.ArtifactLocation.newBuilder()
.setRelativePath("something.h")
.setRootExecutionPathFragment("external/repo_name")
.setIsSource(true)
.setIsExternal(true)
.build());

BlazeInfo blazeInfo = mock(BlazeInfo.class, RETURNS_DEEP_STUBS);

File workspaceRootFile = new File("/workspace/root");
WorkspacePathResolver resolver =
new WorkspacePathResolverImpl(new WorkspaceRoot(workspaceRootFile));

ArtifactLocationDecoder decoder =
new ArtifactLocationDecoderImpl(
blazeInfo,
resolver,
RemoteOutputArtifacts.EMPTY);

when(blazeInfo.getExecutionRoot().toPath()
.resolve(artifactLocation.getExecutionRootRelativePath()).toRealPath()).thenReturn(
workspaceRootFile.toPath().resolve("something.h"));

assertThat(decoder.resolveSource(artifactLocation))
.isEqualTo(workspaceRootFile.toPath().resolve("something.h").toFile());

assertThat(decoder.decode(artifactLocation))
.isEqualTo(workspaceRootFile.toPath().resolve("something.h").toFile());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
package com.google.idea.blaze.base.sync.workspace;

import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
Expand All @@ -27,6 +30,7 @@
import com.google.idea.blaze.base.ideinfo.TargetMap;
import com.google.idea.blaze.base.model.primitives.*;
import com.intellij.openapi.util.registry.Registry;
import java.io.IOException;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.nio.file.Path;
Expand Down Expand Up @@ -224,4 +228,25 @@ public void testVirtualIncludesWithIncludePrefix() {
assertThat(files).containsExactly(
new File(EXECUTION_ROOT, fileToBeResolved.toString()));
}

@Test
public void testExternalWorkspaceSymlinkToProject() throws IOException {
Path expectedPath = Path.of(WORKSPACE_ROOT.toString(), "guava", "src");

Path pathMock = mock(Path.class);
when(pathMock.toRealPath()).thenReturn(expectedPath);

File output = spy(new File("external/guava/src"));

// some hacky mocking to bypass several ifs
when(output.isAbsolute()).thenReturn(false, true);
when(output.toPath()).thenReturn(pathMock);

ExecutionRootPath pathUnderTest = new ExecutionRootPath(output);

ImmutableList<File> files =
pathResolver.resolveToIncludeDirectories(pathUnderTest);

assertThat(files).containsExactly(WORKSPACE_ROOT.fileForPath(new WorkspacePath("guava/src")));
}
}
1 change: 1 addition & 0 deletions cpp/src/META-INF/blaze-cpp.xml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
overrides="true"
/>
<registryKey defaultValue="false" description="Disable absolute path trimming in debug clang builds" key="bazel.trim.absolute.path.disabled"/>
<registryKey defaultValue="true" description="Allow external targets from source directories be imported in" key="bazel.cpp.sync.external.targets.from.directories"/>
<applicationService serviceInterface="com.google.idea.blaze.cpp.CompilerVersionChecker"
serviceImplementation="com.google.idea.blaze.cpp.CompilerVersionCheckerImpl"/>
<applicationService serviceInterface="com.google.idea.blaze.cpp.CompilerWrapperProvider"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import com.google.idea.blaze.base.ideinfo.TargetKey;
import com.google.idea.blaze.base.model.BlazeProjectData;
import com.google.idea.blaze.base.model.primitives.ExecutionRootPath;
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.ProjectViewSet;
import com.google.idea.blaze.base.scope.BlazeContext;
Expand All @@ -41,10 +42,15 @@
import com.google.idea.blaze.base.settings.Blaze;
import com.google.idea.blaze.base.sync.projectview.ProjectViewTargetImportFilter;
import com.google.idea.blaze.base.sync.workspace.ExecutionRootPathResolver;
import com.google.idea.blaze.base.sync.workspace.WorkspaceHelper;
import com.google.idea.blaze.base.sync.workspace.WorkspacePathResolver;
import com.google.idea.blaze.common.PrintOutput;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.io.FileUtilRt;
import com.intellij.openapi.util.registry.Registry;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Optional;
import org.jetbrains.annotations.NotNull;

Expand All @@ -61,6 +67,8 @@
import java.util.stream.Collectors;

final class BlazeConfigurationResolver {
static final String SYNC_EXTERNAL_TARGETS_FROM_DIRECTORIES_KEY = "bazel.cpp.sync.external.targets.from.directories";

private static final Logger logger = Logger.getInstance(BlazeConfigurationResolver.class);

private final Project project;
Expand Down Expand Up @@ -104,7 +112,8 @@ public BlazeConfigurationResolverResult update(
ProjectViewTargetImportFilter projectViewFilter =
new ProjectViewTargetImportFilter(
Blaze.getBuildSystemName(project), workspaceRoot, projectViewSet);
Predicate<TargetIdeInfo> targetFilter = getTargetFilter(projectViewFilter);
Predicate<TargetIdeInfo> targetFilter =
getTargetFilter(projectViewFilter, project, blazeProjectData.getWorkspacePathResolver());
BlazeConfigurationResolverResult.Builder builder = BlazeConfigurationResolverResult.builder();
buildBlazeConfigurationData(
context, blazeProjectData, toolchainLookupMap, compilerSettings, targetFilter, builder);
Expand Down Expand Up @@ -136,11 +145,40 @@ private static ImmutableMap<String, String> getTargetToVersionMap(ImmutableMap<T
}

private static Predicate<TargetIdeInfo> getTargetFilter(
ProjectViewTargetImportFilter projectViewFilter) {
return target ->
target.getcIdeInfo() != null
&& projectViewFilter.isSourceTarget(target)
&& containsCompiledSources(target);
ProjectViewTargetImportFilter projectViewFilter,
Project project,
WorkspacePathResolver workspacePathResolver) {
return target -> {
WorkspacePath pathForExternalTarget = getWorkspacePathForExternalTarget(target, project, workspacePathResolver);

boolean allowExternalTargetSync =
Registry.is(SYNC_EXTERNAL_TARGETS_FROM_DIRECTORIES_KEY) && pathForExternalTarget != null;

return target.getcIdeInfo() != null
&& (projectViewFilter.isSourceTarget(target) ||
allowExternalTargetSync && projectViewFilter.containsWorkspacePath(pathForExternalTarget))
&& containsCompiledSources(target);
};
}

private static WorkspacePath getWorkspacePathForExternalTarget(
TargetIdeInfo target,
Project project,
WorkspacePathResolver workspacePathResolver) {
if (target.toTargetInfo().getLabel().isExternal()) {
WorkspaceRoot externalWorkspace = WorkspaceHelper.resolveExternalWorkspace(project,
target.getKey().getLabel().externalWorkspaceName());

if (externalWorkspace != null) {
try {
Path externalWorkspaceRealPath = externalWorkspace.directory().toPath().toRealPath();
return workspacePathResolver.getWorkspacePath(externalWorkspaceRealPath.toFile());
} catch (IOException ioException) {
logger.warn("Failed to resolve real external workspace location", ioException);
}
}
}
return null;
}

private static boolean containsCompiledSources(TargetIdeInfo target) {
Expand Down
Loading
Loading