diff --git a/base/src/META-INF/blaze-base.xml b/base/src/META-INF/blaze-base.xml index 6554b37379e..43036e9c301 100644 --- a/base/src/META-INF/blaze-base.xml +++ b/base/src/META-INF/blaze-base.xml @@ -360,6 +360,7 @@ + List listItems(SectionKey> key) { } /** Returns all values from all scalar sections in the project views, in order */ - public List listScalarItems(SectionKey> key) { + public List listScalarItems(SectionKey>... keys) { List result = Lists.newArrayList(); - for (ScalarSection section : getSections(key)) { - result.add(section.getValue()); + for (SectionKey> key: keys) { + for (ScalarSection section : getSections(key)) { + result.add(section.getValue()); + } } return result; } diff --git a/base/src/com/google/idea/blaze/base/projectview/section/sections/ImportSection.java b/base/src/com/google/idea/blaze/base/projectview/section/sections/ImportSection.java index 4b6a47a86f8..11d4dc5d022 100644 --- a/base/src/com/google/idea/blaze/base/projectview/section/sections/ImportSection.java +++ b/base/src/com/google/idea/blaze/base/projectview/section/sections/ImportSection.java @@ -22,6 +22,9 @@ 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.scope.output.IssueOutput; +import com.intellij.openapi.util.registry.Registry; + import java.io.File; import javax.annotation.Nullable; @@ -31,15 +34,25 @@ public class ImportSection { SectionKey.of("import"); public static final SectionParser PARSER = new ImportSectionParser(); - private static class ImportSectionParser extends ScalarSectionParser { + protected static class ImportSectionParser extends ScalarSectionParser { public ImportSectionParser() { - super(KEY, ' '); + this(KEY, ' '); + } + + protected ImportSectionParser(SectionKey> key, char divider) { + super(key, divider); } @Nullable @Override protected WorkspacePath parseItem( ProjectViewParser parser, ParseContext parseContext, String text) { + boolean projectViewImportsMandatory = !Registry.is("bazel.projectview.optional.imports"); + return parseItem(parser, parseContext, text, projectViewImportsMandatory); + } + + @Nullable + protected static WorkspacePath parseItem(ProjectViewParser parser, ParseContext parseContext, String text, boolean projectViewImportsMandatory) { String error = WorkspacePath.validate(text); if (error != null) { parseContext.addError(error); @@ -50,7 +63,13 @@ protected WorkspacePath parseItem( if (parser.isRecursive()) { File projectViewFile = parseContext.getWorkspacePathResolver().resolveToFile(workspacePath); if (projectViewFile != null) { - parser.parseProjectView(projectViewFile); + if (projectViewImportsMandatory || projectViewFile.exists()) { + parser.parseProjectView(projectViewFile); + } else { + IssueOutput.warn( + String.format("Could not load project view file: '%s'", projectViewFile.getPath())) + .submit(parseContext.getContext()); + } } else { parseContext.addError("Could not resolve import: " + workspacePath); } 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 1d73508f256..5aea4db980c 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 @@ -28,6 +28,7 @@ public class Sections { Lists.newArrayList( TextBlockSection.PARSER, ImportSection.PARSER, + TryImportSection.PARSER, DirectorySection.PARSER, AutomaticallyDeriveTargetsSection.PARSER, SyncManualTargetsSection.PARSER, diff --git a/base/src/com/google/idea/blaze/base/projectview/section/sections/TryImportSection.java b/base/src/com/google/idea/blaze/base/projectview/section/sections/TryImportSection.java new file mode 100644 index 00000000000..76cb76e2fd5 --- /dev/null +++ b/base/src/com/google/idea/blaze/base/projectview/section/sections/TryImportSection.java @@ -0,0 +1,33 @@ +package com.google.idea.blaze.base.projectview.section.sections; + +import com.google.idea.blaze.base.model.primitives.WorkspacePath; +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.ScalarSection; +import com.google.idea.blaze.base.projectview.section.SectionKey; +import com.google.idea.blaze.base.projectview.section.SectionParser; + +import javax.annotation.Nullable; + +/** "try_import" section. */ +public class TryImportSection extends ImportSection { + + public static final SectionKey> KEY = + SectionKey.of("try_import"); + + public static final SectionParser PARSER = new TryImportSectionParser(); + + private static class TryImportSectionParser extends ImportSectionParser { + + public TryImportSectionParser() { + super(KEY, ' '); + } + + @Nullable + @Override + protected WorkspacePath parseItem( + ProjectViewParser parser, ParseContext parseContext, String text) { + return parseItem(parser, parseContext, text, false); + } + } +} diff --git a/base/src/com/google/idea/blaze/base/qsync/ProjectStatsLogger.java b/base/src/com/google/idea/blaze/base/qsync/ProjectStatsLogger.java index 5f06b14f13e..1b70e5e51cd 100644 --- a/base/src/com/google/idea/blaze/base/qsync/ProjectStatsLogger.java +++ b/base/src/com/google/idea/blaze/base/qsync/ProjectStatsLogger.java @@ -21,6 +21,7 @@ import com.google.idea.blaze.base.model.primitives.WorkspacePath; import com.google.idea.blaze.base.projectview.ProjectViewSet; import com.google.idea.blaze.base.projectview.section.sections.ImportSection; +import com.google.idea.blaze.base.projectview.section.sections.TryImportSection; import com.google.idea.blaze.common.Context; import com.google.idea.blaze.qsync.BlazeProjectListener; import com.google.idea.blaze.qsync.project.BlazeProjectSnapshot; @@ -55,7 +56,7 @@ public void onNewProjectSnapshot(Context context, BlazeProjectSnapshot instan .getProjectInfoStatsBuilder() .setLanguagesActive(instance.queryData().projectDefinition().languageClasses()) .setBlazeProjectFiles( - projectViewSet.listScalarItems(ImportSection.KEY).stream() + projectViewSet.listScalarItems(ImportSection.KEY, TryImportSection.KEY).stream() .map(WorkspacePath::asPath) .collect(toImmutableSet())); scope diff --git a/base/src/com/google/idea/blaze/base/sync/SyncPhaseCoordinator.java b/base/src/com/google/idea/blaze/base/sync/SyncPhaseCoordinator.java index 48453fb3d69..61f2830d3d8 100644 --- a/base/src/com/google/idea/blaze/base/sync/SyncPhaseCoordinator.java +++ b/base/src/com/google/idea/blaze/base/sync/SyncPhaseCoordinator.java @@ -42,6 +42,7 @@ import com.google.idea.blaze.base.projectview.ProjectViewManager; import com.google.idea.blaze.base.projectview.ProjectViewSet; import com.google.idea.blaze.base.projectview.section.sections.ImportSection; +import com.google.idea.blaze.base.projectview.section.sections.TryImportSection; import com.google.idea.blaze.base.scope.BlazeContext; import com.google.idea.blaze.base.scope.Scope; import com.google.idea.blaze.base.scope.output.IssueOutput; @@ -722,7 +723,7 @@ private static void fillInBuildStats( .setWorkspaceType(projectState.getLanguageSettings().getWorkspaceType()) .setLanguagesActive(projectState.getLanguageSettings().getActiveLanguages()) .setBlazeProjectFiles( - projectState.getProjectViewSet().listScalarItems(ImportSection.KEY)); + projectState.getProjectViewSet().listScalarItems(ImportSection.KEY, TryImportSection.KEY)); } if (buildResult != null) { buildResult diff --git a/base/src/com/google/idea/blaze/base/wizard2/ui/BlazeEditProjectViewControl.java b/base/src/com/google/idea/blaze/base/wizard2/ui/BlazeEditProjectViewControl.java index a4174cb65ee..4630fdff3bc 100644 --- a/base/src/com/google/idea/blaze/base/wizard2/ui/BlazeEditProjectViewControl.java +++ b/base/src/com/google/idea/blaze/base/wizard2/ui/BlazeEditProjectViewControl.java @@ -37,6 +37,7 @@ import com.google.idea.blaze.base.projectview.section.sections.ImportSection; import com.google.idea.blaze.base.projectview.section.sections.Sections; import com.google.idea.blaze.base.projectview.section.sections.TargetSection; +import com.google.idea.blaze.base.projectview.section.sections.TryImportSection; import com.google.idea.blaze.base.scope.BlazeContext; import com.google.idea.blaze.base.scope.OutputSink.Propagation; import com.google.idea.blaze.base.scope.Scope; @@ -599,6 +600,9 @@ public void updateBuilder(BlazeNewProjectBuilder builder) { .add( ScalarSection.builder(ImportSection.KEY) .set(selectProjectViewOption.getSharedProjectView())) + .add( + ScalarSection.builder(TryImportSection.KEY) + .set(selectProjectViewOption.getSharedProjectView())) .build(); projectViewSet = ProjectViewSet.builder() diff --git a/base/tests/integrationtests/com/google/idea/blaze/base/lang/projectview/references/ProjectViewLabelReferenceTest.java b/base/tests/integrationtests/com/google/idea/blaze/base/lang/projectview/references/ProjectViewLabelReferenceTest.java index 179bd57a506..f16dd98054b 100644 --- a/base/tests/integrationtests/com/google/idea/blaze/base/lang/projectview/references/ProjectViewLabelReferenceTest.java +++ b/base/tests/integrationtests/com/google/idea/blaze/base/lang/projectview/references/ProjectViewLabelReferenceTest.java @@ -51,6 +51,24 @@ public void testFileReference() { assertThat(importItem.getReference().resolve()).isEqualTo(referencedFile); } + @Test + public void testFileReferenceTryImport() { + PsiFile referencedFile = + workspace.createPsiFile( + new WorkspacePath("ijwb.bazelproject"), + "directories:", + " java/com/google/foo", + "targets:", + " //java/com/google/foo/..."); + PsiFile file = + workspace.createPsiFile(new WorkspacePath(".bazelproject"), "try_import ijwb.bazelproject"); + + ProjectViewPsiSectionItem importItem = + PsiUtils.findFirstChildOfClassRecursive(file, ProjectViewPsiSectionItem.class); + assertThat(importItem).isNotNull(); + assertThat(importItem.getReference().resolve()).isEqualTo(referencedFile); + } + @Test public void testDirectoryReference() { PsiDirectory directory = workspace.createPsiDirectory(new WorkspacePath("foo/bar")); 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 6f7c4aa71d8..32d95c4e975 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 @@ -52,6 +52,7 @@ import com.google.idea.blaze.base.projectview.section.sections.TestSourceSection; import com.google.idea.blaze.base.projectview.section.sections.TextBlock; import com.google.idea.blaze.base.projectview.section.sections.TextBlockSection; +import com.google.idea.blaze.base.projectview.section.sections.TryImportSection; import com.google.idea.blaze.base.projectview.section.sections.WorkspaceTypeSection; import com.google.idea.blaze.base.sync.BlazeSyncPlugin; import com.google.idea.common.experiments.ExperimentService; @@ -85,6 +86,7 @@ public void testProjectViewSetSerializable() { ListSection.builder(TargetSection.KEY) .add(TargetExpression.fromStringSafe("//test:all"))) .add(ScalarSection.builder(ImportSection.KEY).set(new WorkspacePath("test"))) + .add(ScalarSection.builder(TryImportSection.KEY).set(new WorkspacePath("test-optional"))) .add(ListSection.builder(TestSourceSection.KEY).add(new Glob("javatests/*"))) .add(ListSection.builder(ExcludedSourceSection.KEY).add(new Glob("*.java"))) .add(ListSection.builder(BuildFlagsSection.KEY).add("--android_sdk=abcd")) diff --git a/base/tests/unittests/com/google/idea/blaze/base/projectview/parser/ProjectViewParserTest.java b/base/tests/unittests/com/google/idea/blaze/base/projectview/parser/ProjectViewParserTest.java index 80158007c6c..90313950d2b 100644 --- a/base/tests/unittests/com/google/idea/blaze/base/projectview/parser/ProjectViewParserTest.java +++ b/base/tests/unittests/com/google/idea/blaze/base/projectview/parser/ProjectViewParserTest.java @@ -38,6 +38,7 @@ import com.google.idea.blaze.base.projectview.section.sections.TestSourceSection; import com.google.idea.blaze.base.projectview.section.sections.TextBlock; import com.google.idea.blaze.base.projectview.section.sections.TextBlockSection; +import com.google.idea.blaze.base.projectview.section.sections.TryImportSection; import com.google.idea.blaze.base.projectview.section.sections.WorkspaceTypeSection; import com.google.idea.blaze.base.scope.BlazeContext; import com.google.idea.blaze.base.scope.ErrorCollector; @@ -51,6 +52,9 @@ import java.util.Collection; import java.util.Map; import javax.annotation.Nullable; + +import com.intellij.openapi.util.registry.Registry; +import com.intellij.openapi.util.registry.RegistryValue; import org.jetbrains.annotations.NotNull; import org.junit.Test; import org.junit.runner.RunWith; @@ -190,6 +194,9 @@ public void testPrint() { .add( ScalarSection.builder(ImportSection.KEY) .set(new WorkspacePath("some/file.blazeproject"))) + .add( + ScalarSection.builder(TryImportSection.KEY) + .set(new WorkspacePath("some/file.blazeproject.optional"))) .build(); String text = ProjectViewParser.projectViewToString(projectView); assertThat(text) @@ -202,7 +209,8 @@ public void testPrint() { "targets:", " //java/com/google:one", " //java/com/google:two", - "import some/file.blazeproject")); + "import some/file.blazeproject", + "try_import some/file.blazeproject.optional")); } @Test @@ -303,11 +311,34 @@ public void testCanParseWithMissingCarriageReturnAtEndOfSection() { @Test public void testImportMissingFileResultsInIssue() { + Registry.get("bazel.projectview.optional.imports").setValue(false); projectViewStorageManager.add(".blazeproject", "import parent.blazeproject"); projectViewParser.parseProjectView(new File(".blazeproject")); errorCollector.assertIssues("Could not load project view file: '/parent.blazeproject'"); } + @Test + public void testImportMissingFileResultsInWarning() { + RegistryValue importsTreatmentRegistryValue = Registry.get("bazel.projectview.optional.imports"); + try { + importsTreatmentRegistryValue.setValue(true); + projectViewStorageManager.add(".blazeproject", "import parent.blazeproject"); + projectViewParser.parseProjectView(new File(".blazeproject")); + errorCollector.assertIssues("Could not load project view file: '/parent.blazeproject'"); + errorCollector.assertHasNoErrors(); + } finally { + importsTreatmentRegistryValue.setValue(false); + } + } + + @Test + public void testTryImportMissingFileResultsInWarning() { + projectViewStorageManager.add(".blazeproject", "try_import parent.blazeproject"); + projectViewParser.parseProjectView(new File(".blazeproject")); + errorCollector.assertIssues("Could not load project view file: '/parent.blazeproject'"); + errorCollector.assertHasNoErrors(); + } + @Test public void testMissingSectionResultsInIssue() { projectViewStorageManager.add(".blazeproject", "nosuchsection:", " java/com/google"); diff --git a/base/tests/utils/unit/com/google/idea/blaze/base/scope/ErrorCollector.java b/base/tests/utils/unit/com/google/idea/blaze/base/scope/ErrorCollector.java index 9283e7d9d52..e3e02b8e9a8 100644 --- a/base/tests/utils/unit/com/google/idea/blaze/base/scope/ErrorCollector.java +++ b/base/tests/utils/unit/com/google/idea/blaze/base/scope/ErrorCollector.java @@ -55,6 +55,10 @@ public void assertHasErrors() { assertThat(issues.stream().anyMatch(i -> i.getCategory() == Category.ERROR)).isTrue(); } + public void assertHasNoErrors() { + assertThat(issues.stream().anyMatch(i -> i.getCategory() == Category.ERROR)).isFalse(); + } + public void assertIssues(String... requiredMessages) { List messages = Lists.newArrayList(); for (IssueOutput issue : issues) {