From a57b5d46d60acc12e9bd44c5d0cf8036abd94d28 Mon Sep 17 00:00:00 2001 From: Roger Hu Date: Wed, 26 Jul 2023 23:37:34 -0700 Subject: [PATCH] Allow manual tags to be synced to the project (#5085) * Adds a allow_manual_tags_sync in the .bazelproject to allow manual tags to be synced in the repo. * Add integration tests * Remove section --- .../BlazeQueryDirectoryToTargetProvider.java | 9 ++- .../DirectoryToTargetProvider.java | 4 +- .../section/sections/Sections.java | 1 + .../sections/SyncManualTargetsSection.java | 72 +++++++++++++++++ .../base/sync/SyncProjectTargetsHelper.java | 7 +- .../base/projectview/ProjectViewSetTest.java | 2 + .../java/sync/FakeBlazeQueryProvider.java | 45 +++++++++++ .../idea/blaze/java/sync/JavaSyncTest.java | 79 +++++++++++++++++++ 8 files changed, 215 insertions(+), 4 deletions(-) create mode 100644 base/src/com/google/idea/blaze/base/projectview/section/sections/SyncManualTargetsSection.java create mode 100644 java/tests/integrationtests/com/google/idea/blaze/java/sync/FakeBlazeQueryProvider.java diff --git a/base/src/com/google/idea/blaze/base/dependencies/BlazeQueryDirectoryToTargetProvider.java b/base/src/com/google/idea/blaze/base/dependencies/BlazeQueryDirectoryToTargetProvider.java index 8cbe8891382..6e71cfbf45d 100644 --- a/base/src/com/google/idea/blaze/base/dependencies/BlazeQueryDirectoryToTargetProvider.java +++ b/base/src/com/google/idea/blaze/base/dependencies/BlazeQueryDirectoryToTargetProvider.java @@ -51,13 +51,14 @@ public class BlazeQueryDirectoryToTargetProvider implements DirectoryToTargetPro @Override public List doExpandDirectoryTargets( Project project, + Boolean shouldManualTargetSync, ImportRoots directories, WorkspacePathResolver pathResolver, BlazeContext context) { - return runQuery(project, getQueryString(directories), context); + return runQuery(project, getQueryString(directories, shouldManualTargetSync), context); } - private static String getQueryString(ImportRoots directories) { + protected static String getQueryString(ImportRoots directories, boolean allowManualTargetsSync) { StringBuilder targets = new StringBuilder(); targets.append( directories.rootDirectories().stream() @@ -67,6 +68,10 @@ private static String getQueryString(ImportRoots directories) { targets.append(" - " + TargetExpression.allFromPackageRecursive(excluded).toString()); } + if (allowManualTargetsSync) { + return targets.toString(); + } + // exclude 'manual' targets, which shouldn't be built when expanding wildcard target patterns if (SystemInfo.isWindows) { // TODO(b/201974254): Windows support for Bazel sync (see diff --git a/base/src/com/google/idea/blaze/base/dependencies/DirectoryToTargetProvider.java b/base/src/com/google/idea/blaze/base/dependencies/DirectoryToTargetProvider.java index 8d772d00486..a41a0cdb303 100644 --- a/base/src/com/google/idea/blaze/base/dependencies/DirectoryToTargetProvider.java +++ b/base/src/com/google/idea/blaze/base/dependencies/DirectoryToTargetProvider.java @@ -46,11 +46,12 @@ static boolean hasProvider() { @Nullable static List expandDirectoryTargets( Project project, + Boolean shouldManualTargetSync, ImportRoots directories, WorkspacePathResolver pathResolver, BlazeContext context) { return Arrays.stream(EP_NAME.getExtensions()) - .map(p -> p.doExpandDirectoryTargets(project, directories, pathResolver, context)) + .map(p -> p.doExpandDirectoryTargets(project, shouldManualTargetSync, directories, pathResolver, context)) .filter(Objects::nonNull) .findFirst() .orElse(null); @@ -63,6 +64,7 @@ static List expandDirectoryTargets( @Nullable List doExpandDirectoryTargets( Project project, + Boolean shouldManualTargetSync, ImportRoots directories, WorkspacePathResolver pathResolver, BlazeContext context); diff --git a/base/src/com/google/idea/blaze/base/projectview/section/sections/Sections.java b/base/src/com/google/idea/blaze/base/projectview/section/sections/Sections.java index 10e9eccd94f..82dbce14e2d 100644 --- a/base/src/com/google/idea/blaze/base/projectview/section/sections/Sections.java +++ b/base/src/com/google/idea/blaze/base/projectview/section/sections/Sections.java @@ -30,6 +30,7 @@ public class Sections { ImportSection.PARSER, DirectorySection.PARSER, AutomaticallyDeriveTargetsSection.PARSER, + SyncManualTargetsSection.PARSER, TargetSection.PARSER, WorkspaceTypeSection.PARSER, AdditionalLanguagesSection.PARSER, diff --git a/base/src/com/google/idea/blaze/base/projectview/section/sections/SyncManualTargetsSection.java b/base/src/com/google/idea/blaze/base/projectview/section/sections/SyncManualTargetsSection.java new file mode 100644 index 00000000000..6fe11f7b6bf --- /dev/null +++ b/base/src/com/google/idea/blaze/base/projectview/section/sections/SyncManualTargetsSection.java @@ -0,0 +1,72 @@ +/* + * Copyright 2019 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.section.sections; + +import com.google.idea.blaze.base.projectview.ProjectView; +import com.google.idea.blaze.base.projectview.ProjectViewSet; +import com.google.idea.blaze.base.projectview.parser.ParseContext; +import com.google.idea.blaze.base.projectview.parser.ProjectViewParser; +import com.google.idea.blaze.base.projectview.section.ProjectViewDefaultValueProvider; +import com.google.idea.blaze.base.projectview.section.ScalarSection; +import com.google.idea.blaze.base.projectview.section.ScalarSectionParser; +import com.google.idea.blaze.base.projectview.section.SectionKey; +import com.google.idea.blaze.base.projectview.section.SectionParser; +import com.google.idea.blaze.base.settings.BuildSystemName; +import javax.annotation.Nullable; + +/** If set to true, automatically derives targets from the project directories. */ +public class SyncManualTargetsSection { + public static final SectionKey> KEY = + SectionKey.of("allow_manual_targets_sync"); + public static final SectionParser PARSER = new SyncManualTargetsParser(); + + private static class SyncManualTargetsParser + extends ScalarSectionParser { + SyncManualTargetsParser() { + super(KEY, ':'); + } + + @Override + @Nullable + protected Boolean parseItem(ProjectViewParser parser, ParseContext parseContext, String text) { + if (text.equals("true")) { + return true; + } + if (text.equals("false")) { + return false; + } + parseContext.addError( + "'allow_manual_tags_sync' must be set to 'true' or 'false' (e.g." + + " 'allow_manual_tags_sync: true')"); + return null; + } + + @Override + protected void printItem(StringBuilder sb, Boolean item) { + sb.append(item); + } + + @Override + public ItemType getItemType() { + return ItemType.Other; + } + + @Override + public String quickDocs() { + return "If set to true, project targets will be derived from the directories."; + } + } +} diff --git a/base/src/com/google/idea/blaze/base/sync/SyncProjectTargetsHelper.java b/base/src/com/google/idea/blaze/base/sync/SyncProjectTargetsHelper.java index f2834728055..86327a130f2 100644 --- a/base/src/com/google/idea/blaze/base/sync/SyncProjectTargetsHelper.java +++ b/base/src/com/google/idea/blaze/base/sync/SyncProjectTargetsHelper.java @@ -24,6 +24,7 @@ import com.google.idea.blaze.base.model.primitives.TargetExpression; import com.google.idea.blaze.base.projectview.ProjectViewSet; import com.google.idea.blaze.base.projectview.section.sections.AutomaticallyDeriveTargetsSection; +import com.google.idea.blaze.base.projectview.section.sections.SyncManualTargetsSection; import com.google.idea.blaze.base.projectview.section.sections.TargetSection; import com.google.idea.blaze.base.scope.BlazeContext; import com.google.idea.blaze.base.scope.Scope; @@ -88,6 +89,10 @@ private static boolean shouldDeriveSyncTargetsFromDirectories(ProjectViewSet vie return viewSet.getScalarValue(AutomaticallyDeriveTargetsSection.KEY).orElse(false); } + private static boolean shouldSyncManualTargets(ProjectViewSet viewSet) { + return viewSet.getScalarValue(SyncManualTargetsSection.KEY).orElse(false); + } + private static ImmutableList deriveTargetsFromDirectories( Project project, BlazeContext context, @@ -120,7 +125,7 @@ private static ImmutableList deriveTargetsFromDirectories( // We don't want blaze build errors to fail the whole sync childContext.setPropagatesErrors(false); return DirectoryToTargetProvider.expandDirectoryTargets( - project, importRoots, pathResolver, childContext); + project, shouldSyncManualTargets(projectViewSet), importRoots, pathResolver, childContext); }); if (context.isCancelled()) { throw new SyncCanceledException(); diff --git a/base/tests/unittests/com/google/idea/blaze/base/projectview/ProjectViewSetTest.java b/base/tests/unittests/com/google/idea/blaze/base/projectview/ProjectViewSetTest.java index d53e05fba5e..0a34dff9bf1 100644 --- a/base/tests/unittests/com/google/idea/blaze/base/projectview/ProjectViewSetTest.java +++ b/base/tests/unittests/com/google/idea/blaze/base/projectview/ProjectViewSetTest.java @@ -44,6 +44,7 @@ import com.google.idea.blaze.base.projectview.section.sections.Sections; import com.google.idea.blaze.base.projectview.section.sections.ShardBlazeBuildsSection; import com.google.idea.blaze.base.projectview.section.sections.SyncFlagsSection; +import com.google.idea.blaze.base.projectview.section.sections.SyncManualTargetsSection; import com.google.idea.blaze.base.projectview.section.sections.TargetSection; import com.google.idea.blaze.base.projectview.section.sections.TargetShardSizeSection; import com.google.idea.blaze.base.projectview.section.sections.TestFlagsSection; @@ -102,6 +103,7 @@ public void testProjectViewSetSerializable() { ListSection.builder(RunConfigurationsSection.KEY) .add(new WorkspacePath("test"))) .add(ScalarSection.builder(AutomaticallyDeriveTargetsSection.KEY).set(false)) + .add(ScalarSection.builder(SyncManualTargetsSection.KEY).set(false)) .add(ScalarSection.builder(ShardBlazeBuildsSection.KEY).set(false)) .add(ScalarSection.builder(TargetShardSizeSection.KEY).set(500)) .add( diff --git a/java/tests/integrationtests/com/google/idea/blaze/java/sync/FakeBlazeQueryProvider.java b/java/tests/integrationtests/com/google/idea/blaze/java/sync/FakeBlazeQueryProvider.java new file mode 100644 index 00000000000..785d4478cc2 --- /dev/null +++ b/java/tests/integrationtests/com/google/idea/blaze/java/sync/FakeBlazeQueryProvider.java @@ -0,0 +1,45 @@ +package com.google.idea.blaze.java.sync; + +import com.google.common.collect.ImmutableList; +import com.google.idea.blaze.base.dependencies.BlazeQueryDirectoryToTargetProvider; +import com.google.idea.blaze.base.dependencies.TargetInfo; +import com.google.idea.blaze.base.model.primitives.Label; +import com.google.idea.blaze.base.scope.BlazeContext; +import com.google.idea.blaze.base.sync.projectview.ImportRoots; +import com.google.idea.blaze.base.sync.workspace.WorkspacePathResolver; +import com.intellij.openapi.project.Project; + +import javax.annotation.Nullable; +import java.util.List; + +/** + * Need to mock out this target provider since it attempts to execute Bazel outside of the sandbox + * environment. Used primarily within JavaSyncTest to validate the behavior of the + * allow_manual_targets_sync: option. + */ +class FakeBlazeQueryProvider extends BlazeQueryDirectoryToTargetProvider { + + private static final String MANUAL_EXCLUDE_TAG = "((?!manual)"; + + // Need to override in order to be able to execute getQueryString(). + @Nullable + @Override + public List doExpandDirectoryTargets( + Project project, + Boolean shouldManualTargetSync, + ImportRoots directories, + WorkspacePathResolver pathResolver, + BlazeContext context) { + return runQuery(project, getQueryString(directories, shouldManualTargetSync), context); + } + + @Nullable + private static ImmutableList runQuery(Project project, String query, BlazeContext context) { + if (!query.contains(MANUAL_EXCLUDE_TAG)) { + TargetInfo targetInfo = TargetInfo.builder(Label.create("//java/com/google:lib"), "java_library").build(); + return ImmutableList.of(targetInfo); + } else { + return ImmutableList.of(); + } + } +} diff --git a/java/tests/integrationtests/com/google/idea/blaze/java/sync/JavaSyncTest.java b/java/tests/integrationtests/com/google/idea/blaze/java/sync/JavaSyncTest.java index 8889acbc6a5..ac2fb4a0334 100644 --- a/java/tests/integrationtests/com/google/idea/blaze/java/sync/JavaSyncTest.java +++ b/java/tests/integrationtests/com/google/idea/blaze/java/sync/JavaSyncTest.java @@ -18,6 +18,8 @@ import static com.google.common.truth.Truth.assertThat; import com.google.common.collect.ImmutableList; +import com.google.idea.blaze.base.dependencies.BlazeQueryDirectoryToTargetProvider; +import com.google.idea.blaze.base.dependencies.DirectoryToTargetProvider; import com.google.idea.blaze.base.ideinfo.JavaIdeInfo; import com.google.idea.blaze.base.ideinfo.TargetIdeInfo; import com.google.idea.blaze.base.ideinfo.TargetMap; @@ -155,6 +157,83 @@ public void testSimpleSync() throws Exception { .isEqualTo(WorkspaceType.JAVA); } + public void runWorkspaceSync() { + workspace.createFile( + new WorkspacePath("java/com/google/Source.java"), + "package com.google;", + "public class Source {}"); + + workspace.createFile( + new WorkspacePath("java/com/google/Other.java"), + "package com.google;", + "public class Other {}"); + + TargetMap targetMap = + TargetMapBuilder.builder() + .addTarget( + TargetIdeInfo.builder() + .setBuildFile(sourceRoot("java/com/google/BUILD")) + .setLabel("//java/com/google:lib") + .setKind("java_library") + .addTag("manual") + .addSource(sourceRoot("java/com/google/Source.java")) + .addSource(sourceRoot("java/com/google/Other.java"))) + .build(); + + setTargetMap(targetMap); + + runBlazeSync( + BlazeSyncParams.builder() + .setTitle("Sync") + .setSyncMode(SyncMode.INCREMENTAL) + .setSyncOrigin("test") + .setAddProjectViewTargets(true) + .build()); + } + + void mockBazelQueryDirectorProvider() { + DirectoryToTargetProvider.EP_NAME.getPoint().unregisterExtension(BlazeQueryDirectoryToTargetProvider.class); + registerExtensionFirst(DirectoryToTargetProvider.EP_NAME, new FakeBlazeQueryProvider()); + } + + @Test + public void testManualTargetSyncTrue() throws Exception { + mockBazelQueryDirectorProvider(); + + setProjectView( + "directories:", + " java/com/google", + "derive_targets_from_directories: true", + "allow_manual_targets_sync: true"); + runWorkspaceSync(); + + errorCollector.assertNoIssues(); + List syncStatsList = getSyncStats(); + SyncStats syncStats = syncStatsList.get(0); + + assertThat(syncStats.buildPhaseStats().get(0).targets()).hasSize(1); + } + + @Test + public void testManualTargetSyncFalse() throws Exception { + mockBazelQueryDirectorProvider(); + + setProjectView( + "directories:", + " java/com/google", + "derive_targets_from_directories: true", + "allow_manual_targets_sync: false"); + + runWorkspaceSync(); + + errorCollector.assertNoIssues(); + List syncStatsList = getSyncStats(); + SyncStats syncStats = syncStatsList.get(0); + + assertThat(syncStats.buildPhaseStats().get(0).targets()).hasSize(0); + } + + @Test public void testSimpleSyncLogging() throws Exception { setProjectView("directories:", " java/com/google", "targets:", " //java/com/google:lib");