Skip to content

Commit

Permalink
Create unnormalized symlinks with ctx.actions.symlink(target_path=...)
Browse files Browse the repository at this point in the history
  • Loading branch information
fmeum committed Jul 5, 2022
1 parent 1f1e6e5 commit 856b812
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 17 deletions.
17 changes: 17 additions & 0 deletions src/main/java/com/google/devtools/build/lib/analysis/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@ java_library(
":actions/substitution",
":actions/symlink_action",
":actions/template_expansion_action",
":actions/unresolved_symlink_action",
":actions_provider",
":aspect_aware_attribute_mapper",
":aspect_collection",
Expand Down Expand Up @@ -1513,6 +1514,22 @@ java_library(
],
)

java_library(
name = "actions/unresolved_symlink_action",
srcs = ["actions/UnresolvedSymlinkAction.java"],
deps = [
"//src/main/java/com/google/devtools/build/lib/actions",
"//src/main/java/com/google/devtools/build/lib/actions:artifacts",
"//src/main/java/com/google/devtools/build/lib/collect/nestedset",
"//src/main/java/com/google/devtools/build/lib/util",
"//src/main/java/com/google/devtools/build/lib/util:detailed_exit_code",
"//src/main/java/com/google/devtools/build/lib/vfs",
"//src/main/protobuf:failure_details_java_proto",
"//third_party:guava",
"//third_party:jsr305",
],
)

java_library(
name = "buildinfo/build_info_key",
srcs = ["buildinfo/BuildInfoKey.java"],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,13 @@
import java.io.IOException;
import javax.annotation.Nullable;

/** Action to create a symbolic link. */
/**
* Action to create a symlink to a known-to-exist target with alias semantics similar to a true copy
* of the input (if any).
*
* To create a possibly unresolved symlink to a raw path, use {@link UnresolvedSymlinkAction}
* instead.
*/
public final class SymlinkAction extends AbstractAction {
private static final String GUID = "7f4fab4d-d0a7-4f0f-8649-1d0337a21fee";

Expand Down Expand Up @@ -152,13 +158,6 @@ public static SymlinkAction toFileset(
owner, execPath, primaryInput, primaryOutput, progressMessage, TargetType.FILESET);
}

public static SymlinkAction createUnresolved(
ActionOwner owner, Artifact primaryOutput, PathFragment targetPath, String progressMessage) {
Preconditions.checkArgument(primaryOutput.isSymlink());
return new SymlinkAction(
owner, targetPath, null, primaryOutput, progressMessage, TargetType.OTHER);
}

/**
* Creates a new SymlinkAction instance, where an input artifact is not present. This is useful
* when dealing with special cases where input paths that are outside the exec root directory
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// Copyright 2022 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.devtools.build.lib.analysis.actions;

import com.google.common.collect.ImmutableSet;
import com.google.devtools.build.lib.actions.AbstractAction;
import com.google.devtools.build.lib.actions.ActionExecutionContext;
import com.google.devtools.build.lib.actions.ActionExecutionException;
import com.google.devtools.build.lib.actions.ActionKeyContext;
import com.google.devtools.build.lib.actions.ActionOwner;
import com.google.devtools.build.lib.actions.ActionResult;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.Artifact.ArtifactExpander;
import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
import com.google.devtools.build.lib.collect.nestedset.Order;
import com.google.devtools.build.lib.server.FailureDetails;
import com.google.devtools.build.lib.server.FailureDetails.FailureDetail;
import com.google.devtools.build.lib.server.FailureDetails.SymlinkAction.Code;
import com.google.devtools.build.lib.util.DetailedExitCode;
import com.google.devtools.build.lib.util.Fingerprint;
import com.google.devtools.build.lib.vfs.Path;
import java.io.IOException;
import javax.annotation.Nullable;

/**
* Action to create a possibly unresolved symbolic link to a raw path.
*
* To create a symlink to a known-to-exist target with alias semantics similar to a true copy of the
* input, use {@link SymlinkAction} instead.
*/
public final class UnresolvedSymlinkAction extends AbstractAction {
private static final String GUID = "0f302651-602c-404b-881c-58913193cfe7";

private final String target;
private final String progressMessage;

public UnresolvedSymlinkAction(
ActionOwner owner,
Artifact primaryOutput,
String target,
String progressMessage) {
super(
owner,
NestedSetBuilder.emptySet(Order.STABLE_ORDER),
ImmutableSet.of(primaryOutput));
this.target = target;
this.progressMessage = progressMessage;
}

@Override
public ActionResult execute(ActionExecutionContext actionExecutionContext)
throws ActionExecutionException {

Path outputPath = actionExecutionContext.getInputPath(getPrimaryOutput());
try {
outputPath.createSymbolicLink(target);
} catch (IOException e) {
String message =
String.format(
"failed to create symbolic link '%s' to '%s' due to I/O error: %s",
getPrimaryOutput().getExecPathString(), target, e.getMessage());
DetailedExitCode code = createDetailedExitCode(message, Code.LINK_CREATION_IO_EXCEPTION);
throw new ActionExecutionException(message, e, this, false, code);
}

return ActionResult.EMPTY;
}

@Override
protected void computeKey(
ActionKeyContext actionKeyContext,
@Nullable ArtifactExpander artifactExpander,
Fingerprint fp) {
fp.addString(GUID);
fp.addString(target);
}

@Override
public String getMnemonic() {
return "UnresolvedSymlink";
}

@Override
protected String getRawProgressMessage() {
return progressMessage;
}

private static DetailedExitCode createDetailedExitCode(String message, Code detailedCode) {
return DetailedExitCode.of(
FailureDetail.newBuilder()
.setMessage(message)
.setSymlinkAction(FailureDetails.SymlinkAction.newBuilder().setCode(detailedCode))
.build());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
import com.google.devtools.build.lib.analysis.actions.Substitution;
import com.google.devtools.build.lib.analysis.actions.SymlinkAction;
import com.google.devtools.build.lib.analysis.actions.TemplateExpansionAction;
import com.google.devtools.build.lib.analysis.actions.UnresolvedSymlinkAction;
import com.google.devtools.build.lib.analysis.platform.PlatformInfo;
import com.google.devtools.build.lib.collect.nestedset.Depset;
import com.google.devtools.build.lib.collect.nestedset.Depset.TypeException;
Expand Down Expand Up @@ -273,7 +274,7 @@ public void symlink(
? (String) progressMessageUnchecked
: "Creating symlink " + outputArtifact.getExecPathString();

SymlinkAction action;
Action action;
if (targetFile != Starlark.NONE) {
if (outputArtifact.isSymlink()) {
throw Starlark.errorf(
Expand Down Expand Up @@ -315,12 +316,11 @@ public void symlink(
throw Starlark.errorf("\"is_executable\" cannot be True when using \"target_path\"");
}

action =
SymlinkAction.createUnresolved(
ruleContext.getActionOwner(),
outputArtifact,
PathFragment.create((String) targetPath),
progressMessage);
action = new UnresolvedSymlinkAction(
ruleContext.getActionOwner(),
outputArtifact,
(String) targetPath,
progressMessage);
}
registerAction(action);
}
Expand Down
4 changes: 2 additions & 2 deletions src/test/shell/bazel/bazel_symlink_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -428,11 +428,11 @@ EOF
cat > a/BUILD <<'EOF'
load(":a.bzl", "a")
a(name="a", link_target="somewhere/in/my/heart")
a(name="a", link_target="../somewhere/../in/my/heart/..")
EOF

bazel --windows_enable_symlinks build --experimental_allow_unresolved_symlinks //a:a || fail "build failed"
assert_contains "input link is .*[/\\]somewhere/in/my/heart" bazel-bin/a/a.file
assert_contains "input link is ../somewhere/../in/my/heart/.." bazel-bin/a/a.file
}

function test_symlink_created_from_symlink_action() {
Expand Down

0 comments on commit 856b812

Please sign in to comment.