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

[6.3.0] Adjust --top_level_targets_for_symlinks #18916

Merged
merged 2 commits into from
Jul 12, 2023
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 @@ -707,7 +707,10 @@ public void handleConvenienceSymlinks(AnalysisResult analysisResult) {
ImmutableList<ConvenienceSymlink> convenienceSymlinks = ImmutableList.of();
if (request.getBuildOptions().experimentalConvenienceSymlinks
!= ConvenienceSymlinksMode.IGNORE) {
convenienceSymlinks = createConvenienceSymlinks(request.getBuildOptions(), analysisResult);
convenienceSymlinks = createConvenienceSymlinks(
request.getBuildOptions(),
analysisResult.getTargetsToBuild(),
analysisResult.getConfigurationCollection().getTargetConfiguration());
}
if (request.getBuildOptions().experimentalConvenienceSymlinksBepEvent) {
env.getEventBus().post(new ConvenienceSymlinksIdentifiedEvent(convenienceSymlinks));
Expand All @@ -729,22 +732,46 @@ public void handleConvenienceSymlinks(AnalysisResult analysisResult) {
* in fact gets removed if it was already present from a previous invocation.
*/
private ImmutableList<ConvenienceSymlink> createConvenienceSymlinks(
BuildRequestOptions buildRequestOptions, AnalysisResult analysisResult) {
BuildRequestOptions buildRequestOptions,
ImmutableSet<ConfiguredTarget> targetsToBuild,
BuildConfigurationValue configuration) {
SkyframeExecutor executor = env.getSkyframeExecutor();
Reporter reporter = env.getReporter();

// Gather configurations to consider.
Set<BuildConfigurationValue> targetConfigurations =
buildRequestOptions.useTopLevelTargetsForSymlinks()
&& !analysisResult.getTargetsToBuild().isEmpty()
? analysisResult.getTargetsToBuild().stream()
.map(ConfiguredTarget::getActual)
.map(ConfiguredTarget::getConfigurationKey)
.filter(Objects::nonNull)
.distinct()
.map((key) -> executor.getConfiguration(reporter, key))
.collect(toImmutableSet())
: ImmutableSet.of(analysisResult.getConfigurationCollection().getTargetConfiguration());
ImmutableSet<BuildConfigurationValue> targetConfigs;
if (buildRequestOptions.useTopLevelTargetsForSymlinks() && !targetsToBuild.isEmpty()) {
// Collect the configuration of each top-level requested target. These may be different than
// the build's top-level configuration because of self-transitions.
ImmutableSet<BuildConfigurationValue> requestedTargetConfigs =
targetsToBuild.stream()
.map(ConfiguredTarget::getActual)
.map(ConfiguredTarget::getConfigurationKey)
.filter(Objects::nonNull)
.distinct()
.map((key) -> executor.getConfiguration(reporter, key))
.collect(toImmutableSet());
if (requestedTargetConfigs.size() == 1) {
// All top-level targets have the same configuration, so use that one.
targetConfigs = requestedTargetConfigs;
} else if (requestedTargetConfigs.stream()
.anyMatch(
c -> c.getOutputDirectoryName().equals(configuration.getOutputDirectoryName()))) {
// Mixed configs but at least one matches the top-level config's output path (this doesn't
// mean it's the same as the top-level config: --trim_test_configuration means non-test
// targets use the default output path but lack the top-level config's TestOptions). Set
// symlinks to the top-level config so at least non-transitioned targets resolve. See
// https://github.com/bazelbuild/bazel/issues/17081.
targetConfigs = ImmutableSet.of(configuration);
} else {
// Mixed configs, none of which include the top-level config. Delete the symlinks because
// they won't contain any relevant data. This is handled in the
// createOutputDirectorySymlinks call below.
targetConfigs = requestedTargetConfigs;
}
} else {
targetConfigs = ImmutableSet.of(configuration);
}

String productName = runtime.getProductName();
try (SilentCloseable c =
Expand All @@ -756,7 +783,7 @@ private ImmutableList<ConvenienceSymlink> createConvenienceSymlinks(
env.getWorkspace(),
env.getDirectories(),
getReporter(),
targetConfigurations,
targetConfigs,
options -> getConfiguration(executor, reporter, options),
productName);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,9 @@ static ImmutableList<ConvenienceSymlink> createOutputDirectoryLinks(
eventHandler.handle(
Event.warn(
String.format(
"cleared convenience symlink(s) %s because their destinations would be ambiguous",
"cleared convenience symlink(s) %s because they wouldn't contain "
+ "requested targets' outputs. Those targets self-transition to multiple "
+ "distinct configurations",
Joiner.on(", ").join(ambiguousLinks))));
}
return convenienceSymlinksBuilder.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -417,7 +417,8 @@ public void buildingOnlyTargetsWithNullConfigurations_unsetsSymlinks() throws Ex
}

@Test
public void buildingTargetsWithDifferentOutputDirectories_unsetsSymlinks() throws Exception {
public void buildingTargetsWithDifferentOutputDirectories_unsetsSymlinksIfNoneAreTopLevel()
throws Exception {
addOptions("--symlink_prefix=ambiguous-", "--incompatible_skip_genfiles_symlink=false");

Path config = getOutputPath().getRelative("some-imaginary-config");
Expand All @@ -431,10 +432,9 @@ public void buildingTargetsWithDifferentOutputDirectories_unsetsSymlinks() throw

write(
"targets/BUILD",
"basic_rule(name='default')",
"incoming_transition_rule(name='config1', path='set_from_config1')",
"incoming_transition_rule(name='config2', path='set_from_config2')");
buildTarget("//targets:default", "//targets:config1", "//targets:config2");
buildTarget("//targets:config1", "//targets:config2");

// there should be nothing at any of the convenience symlinks which depend on configuration -
// the symlinks put there during the simulated prior build should have been deleted
Expand All @@ -454,6 +454,49 @@ public void buildingTargetsWithDifferentOutputDirectories_unsetsSymlinks() throw
getOutputPath());
}

@Test
public void buildingTargetsWithDifferentOutputDirectories_setsSymlinksIfAnyAreTopLevel()
throws Exception {
addOptions(
"--symlink_prefix=ambiguous-",
"--incompatible_skip_genfiles_symlink=false",
"--incompatible_merge_genfiles_directory=false",
"--incompatible_skip_genfiles_symlink=false");

Path config = getOutputPath().getRelative("some-imaginary-config");
// put symlinks at the convenience symlinks spots to simulate a prior build
Path binLink = getWorkspace().getChild("ambiguous-bin");
binLink.createSymbolicLink(config.getChild("bin"));
Path genfilesLink = getWorkspace().getChild("ambiguous-genfiles");
genfilesLink.createSymbolicLink(config.getChild("genfiles"));
Path testlogsLink = getWorkspace().getChild("ambiguous-testlogs");
testlogsLink.createSymbolicLink(config.getChild("testlogs"));

write(
"targets/BUILD",
"basic_rule(name='default')",
"incoming_transition_rule(name='config1', path='set_from_config1')");
buildTarget("//targets:default", "//targets:config1");

assertThat(getConvenienceSymlinks())
.containsExactly(
"ambiguous-bin",
getOutputPath()
.getRelative("default-" + getTargetConfiguration().getCpu() + "-fastbuild/bin"),
"ambiguous-genfiles",
getOutputPath()
.getRelative(
"default-" + getTargetConfiguration().getCpu() + "-fastbuild/genfiles"),
"ambiguous-testlogs",
getOutputPath()
.getRelative(
"default-" + getTargetConfiguration().getCpu() + "-fastbuild/testlogs"),
"ambiguous-" + TestConstants.WORKSPACE_NAME,
getExecRoot(),
"ambiguous-out",
getOutputPath());
}

@Test
public void buildingTargetsWithSameConfiguration_setsSymlinks() throws Exception {
addOptions(
Expand Down