Merge pull request #53 from brendandouglas/master
Import of bazel plugin using copybara
diff --git a/README.md b/README.md
index 3d0a727..c8dc1bf 100644
--- a/README.md
+++ b/README.md
@@ -25,4 +25,4 @@
Install Bazel, then run 'bazel build //ijwb:ijwb_bazel --define=ij_product=intellij-latest'
from the project root. This will create a plugin jar in
-'bazel-genfiles/ijwb/ijwb_bazel.jar'.
+'bazel-genfiles/ijwb/ijwb_bazel.jar'.
\ No newline at end of file
diff --git a/aswb/2.3/src/com/google/idea/blaze/android/project/BlazeBuildSystemService.java b/aswb/2.3/src/com/google/idea/blaze/android/project/BlazeBuildSystemService.java
index 36923d7..ac68a65 100644
--- a/aswb/2.3/src/com/google/idea/blaze/android/project/BlazeBuildSystemService.java
+++ b/aswb/2.3/src/com/google/idea/blaze/android/project/BlazeBuildSystemService.java
@@ -19,19 +19,17 @@
import com.google.idea.blaze.android.sync.model.AndroidResourceModuleRegistry;
import com.google.idea.blaze.base.actions.BlazeBuildService;
import com.google.idea.blaze.base.ideinfo.TargetIdeInfo;
+import com.google.idea.blaze.base.io.VirtualFileSystemProvider;
import com.google.idea.blaze.base.lang.buildfile.references.BuildReferenceManager;
import com.google.idea.blaze.base.model.BlazeProjectData;
import com.google.idea.blaze.base.settings.Blaze;
import com.google.idea.blaze.base.sync.BlazeSyncManager;
import com.google.idea.blaze.base.sync.data.BlazeProjectDataManager;
-import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
-import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
-import com.intellij.openapi.vfs.ex.temp.TempFileSystem;
import com.intellij.psi.PsiElement;
import java.io.File;
@@ -84,16 +82,11 @@
} else {
// If not, just the build file is good enough.
File buildIoFile = blazeProjectData.artifactLocationDecoder.decode(targetIdeInfo.buildFile);
- VirtualFile buildVirtualFile = findFileByIoFile(buildIoFile);
+ VirtualFile buildVirtualFile =
+ VirtualFileSystemProvider.findFileByIoFileRefreshIfNeeded(buildIoFile);
if (buildVirtualFile != null) {
fileEditorManager.openFile(buildVirtualFile, true);
}
}
}
-
- private static VirtualFile findFileByIoFile(File file) {
- return ApplicationManager.getApplication().isUnitTestMode()
- ? TempFileSystem.getInstance().findFileByIoFile(file)
- : VfsUtil.findFileByIoFile(file, true);
- }
}
diff --git a/aswb/2.3/src/com/google/idea/blaze/android/sync/model/idea/BlazeClassJarProvider.java b/aswb/2.3/src/com/google/idea/blaze/android/sync/model/idea/BlazeClassJarProvider.java
index 15c2add..9641ced 100644
--- a/aswb/2.3/src/com/google/idea/blaze/android/sync/model/idea/BlazeClassJarProvider.java
+++ b/aswb/2.3/src/com/google/idea/blaze/android/sync/model/idea/BlazeClassJarProvider.java
@@ -27,6 +27,7 @@
import com.google.idea.blaze.base.ideinfo.TargetIdeInfo;
import com.google.idea.blaze.base.ideinfo.TargetKey;
import com.google.idea.blaze.base.ideinfo.TargetMap;
+import com.google.idea.blaze.base.io.VirtualFileSystemProvider;
import com.google.idea.blaze.base.model.BlazeProjectData;
import com.google.idea.blaze.base.sync.data.BlazeProjectDataManager;
import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoder;
@@ -89,7 +90,8 @@
continue;
}
File classJarFile = decoder.decode(jar.classJar);
- VirtualFile classJarVF = findFileByIoFile(classJarFile);
+ VirtualFile classJarVF =
+ VirtualFileSystemProvider.getInstance().getSystem().findFileByIoFile(classJarFile);
if (classJarVF == null) {
if (classJarFile.exists()) {
missingClassJars.add(classJarFile);
@@ -161,7 +163,8 @@
// TODO: benchmark to see if optimization is worthwhile.
if (jar.classJar != null) {
File classJarFile = decoder.decode(jar.classJar);
- VirtualFile classJar = findFileByIoFile(classJarFile);
+ VirtualFile classJar =
+ VirtualFileSystemProvider.getInstance().getSystem().findFileByIoFile(classJarFile);
if (classJar != null) {
results.add(classJar);
} else if (classJarFile.exists()) {
@@ -192,12 +195,6 @@
return results;
}
- private static VirtualFile findFileByIoFile(File file) {
- return ApplicationManager.getApplication().isUnitTestMode()
- ? TempFileSystem.getInstance().findFileByIoFile(file)
- : LocalFileSystem.getInstance().findFileByIoFile(file);
- }
-
private static void maybeRefreshJars(Collection<File> missingJars, AtomicBoolean pendingRefresh) {
// We probably need to refresh the virtual file system to find these files, but we can't refresh
// here because we're in a read action. We also can't use the async refreshIoFiles since it
diff --git a/aswb/2.3/tests/unittests/com/google/idea/blaze/android/project/BlazeBuildSystemServiceTest.java b/aswb/2.3/tests/unittests/com/google/idea/blaze/android/project/BlazeBuildSystemServiceTest.java
index 9075d66..fbd398d 100755
--- a/aswb/2.3/tests/unittests/com/google/idea/blaze/android/project/BlazeBuildSystemServiceTest.java
+++ b/aswb/2.3/tests/unittests/com/google/idea/blaze/android/project/BlazeBuildSystemServiceTest.java
@@ -33,6 +33,7 @@
import com.google.idea.blaze.base.ideinfo.TargetKey;
import com.google.idea.blaze.base.ideinfo.TargetMap;
import com.google.idea.blaze.base.ideinfo.TargetMapBuilder;
+import com.google.idea.blaze.base.io.VirtualFileSystemProvider;
import com.google.idea.blaze.base.lang.buildfile.references.BuildReferenceManager;
import com.google.idea.blaze.base.model.BlazeProjectData;
import com.google.idea.blaze.base.model.MockBlazeProjectDataBuilder;
@@ -58,6 +59,7 @@
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
import com.intellij.openapi.module.Module;
+import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.ex.temp.TempFileSystem;
import com.intellij.psi.PsiElement;
@@ -124,7 +126,8 @@
when(buildTargetPsi.getContainingFile()).thenReturn(psiFile);
when(buildTargetPsi.getTextOffset()).thenReturn(1337);
- VirtualFile buildFile = TempFileSystem.getInstance().findFileByPath("/foo/BUILD");
+ VirtualFile buildFile =
+ VirtualFileSystemProvider.getInstance().getSystem().findFileByPath("/foo/BUILD");
assertThat(buildFile).isNotNull();
when(psiFile.getVirtualFile()).thenReturn(buildFile);
@@ -149,7 +152,8 @@
when(BuildReferenceManager.getInstance(project).resolveLabel(new Label("//foo:bar")))
.thenReturn(null);
- VirtualFile buildFile = TempFileSystem.getInstance().findFileByPath("/foo/BUILD");
+ VirtualFile buildFile =
+ VirtualFileSystemProvider.getInstance().getSystem().findFileByPath("/foo/BUILD");
assertThat(buildFile).isNotNull();
String dependency = "com.android.foo:bar"; // Doesn't matter.
@@ -185,7 +189,8 @@
projectServices.register(BuildReferenceManager.class, mock(BuildReferenceManager.class));
projectServices.register(LazyRangeMarkerFactory.class, mock(LazyRangeMarkerFactoryImpl.class));
- applicationServices.register(TempFileSystem.class, new MockFileSystem("/foo/BUILD"));
+ applicationServices.register(
+ VirtualFileSystemProvider.class, new MockVirtualFileSystemProvider("/foo/BUILD"));
AndroidResourceModuleRegistry moduleRegistry = new AndroidResourceModuleRegistry();
moduleRegistry.put(
@@ -251,4 +256,18 @@
return findFileByPath(file.getPath());
}
}
+
+ private static class MockVirtualFileSystemProvider implements VirtualFileSystemProvider {
+
+ private final LocalFileSystem fileSystem;
+
+ MockVirtualFileSystemProvider(String... paths) {
+ fileSystem = new MockFileSystem(paths);
+ }
+
+ @Override
+ public LocalFileSystem getSystem() {
+ return fileSystem;
+ }
+ }
}
diff --git a/aswb/src/META-INF/aswb.xml b/aswb/src/META-INF/aswb.xml
index 48f701d..d59d935 100644
--- a/aswb/src/META-INF/aswb.xml
+++ b/aswb/src/META-INF/aswb.xml
@@ -62,6 +62,7 @@
<BlazeCommandRunConfigurationHandlerProvider implementation="com.google.idea.blaze.android.run.binary.BlazeAndroidBinaryRunConfigurationHandlerProvider"/>
<BlazeCommandRunConfigurationHandlerProvider implementation="com.google.idea.blaze.android.run.test.BlazeAndroidTestRunConfigurationHandlerProvider"/>
<BuildSystemAndroidJdkProvider implementation="com.google.idea.blaze.android.sync.BazelAndroidJdkProvider"/>
+ <BlazeTestEventsHandler implementation="com.google.idea.blaze.android.run.test.smrunner.BlazeAndroidTestEventsHandler"/>
</extensions>
<extensions defaultExtensionNs="com.android.ide">
diff --git a/aswb/src/com/google/idea/blaze/android/projectview/AndroidMinSdkSection.java b/aswb/src/com/google/idea/blaze/android/projectview/AndroidMinSdkSection.java
new file mode 100644
index 0000000..84b5ece
--- /dev/null
+++ b/aswb/src/com/google/idea/blaze/android/projectview/AndroidMinSdkSection.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2017 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.android.projectview;
+
+import com.google.common.primitives.Ints;
+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.ScalarSectionParser;
+import com.google.idea.blaze.base.projectview.section.SectionKey;
+import com.google.idea.blaze.base.projectview.section.SectionParser;
+import org.jetbrains.annotations.Nullable;
+
+/** Allows project wide min sdk. */
+public class AndroidMinSdkSection {
+ public static final SectionKey<Integer, ScalarSection<Integer>> KEY =
+ SectionKey.of("android_min_sdk");
+ public static final SectionParser PARSER = new AndroidMinSdkParser();
+
+ private static class AndroidMinSdkParser extends ScalarSectionParser<Integer> {
+ AndroidMinSdkParser() {
+ super(KEY, ':');
+ }
+
+ @Nullable
+ @Override
+ protected Integer parseItem(ProjectViewParser parser, ParseContext parseContext, String rest) {
+ return Ints.tryParse(rest);
+ }
+
+ @Override
+ protected void printItem(StringBuilder sb, Integer value) {
+ sb.append(value);
+ }
+
+ @Override
+ public ItemType getItemType() {
+ return ItemType.Other;
+ }
+ }
+}
diff --git a/aswb/src/com/google/idea/blaze/android/projectview/AndroidSdkPlatformSection.java b/aswb/src/com/google/idea/blaze/android/projectview/AndroidSdkPlatformSection.java
index 0c14f79..fe75735 100644
--- a/aswb/src/com/google/idea/blaze/android/projectview/AndroidSdkPlatformSection.java
+++ b/aswb/src/com/google/idea/blaze/android/projectview/AndroidSdkPlatformSection.java
@@ -15,6 +15,9 @@
*/
package com.google.idea.blaze.android.projectview;
+import static java.util.stream.Collectors.toList;
+
+import com.google.common.collect.ImmutableList;
import com.google.idea.blaze.android.compatibility.Compatibility.AndroidSdkUtils;
import com.google.idea.blaze.android.sync.sdk.AndroidSdkFromProjectView;
import com.google.idea.blaze.base.projectview.ProjectView;
@@ -28,7 +31,7 @@
import com.google.idea.blaze.base.projectview.section.sections.TextBlockSection;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.util.text.StringUtil;
-import java.util.Collection;
+import java.util.List;
import org.jetbrains.annotations.Nullable;
/** Allows manual override of the android sdk. */
@@ -63,19 +66,34 @@
if (!projectView.getSectionsOfType(KEY).isEmpty()) {
return projectView;
}
- Collection<Sdk> sdks = AndroidSdkUtils.getAllAndroidSdks();
- return ProjectView.builder(projectView)
- .add(TextBlockSection.of(TextBlock.newLine()))
- .add(TextBlockSection.of(TextBlock.of("# Please set to an android SDK platform")))
- .add(
- TextBlockSection.of(
- TextBlock.of(
- sdks.isEmpty()
- ? "# You currently have no SDKs. Please use the SDK manager first."
- : "# Available SDKs are: "
- + AndroidSdkFromProjectView.getAvailableSdkPlatforms(sdks))))
- .add(ScalarSection.builder(KEY).set("<android sdk platform>"))
- .build();
+ List<Sdk> sdks = AndroidSdkUtils.getAllAndroidSdks();
+ ProjectView.Builder builder =
+ ProjectView.builder(projectView).add(TextBlockSection.of(TextBlock.newLine()));
+
+ if (sdks.isEmpty()) {
+ builder
+ .add(TextBlockSection.of(TextBlock.of("# Please set to an android SDK platform")))
+ .add(
+ TextBlockSection.of(
+ TextBlock.of(
+ "# You currently have no SDKs. Please use the SDK manager first.")))
+ .add(ScalarSection.builder(KEY).set("(android sdk goes here)"));
+ } else if (sdks.size() == 1) {
+ builder.add(
+ ScalarSection.builder(KEY)
+ .set(AndroidSdkFromProjectView.getSdkTargetHash(sdks.get(0))));
+ } else {
+ builder.add(
+ TextBlockSection.of(
+ TextBlock.of("# Please uncomment an android-SDK platform. Available SDKs are:")));
+ List<String> sdkOptions =
+ AndroidSdkFromProjectView.getAvailableSdkTargetHashes(sdks)
+ .stream()
+ .map(androidSdk -> "# android_sdk_platform: " + androidSdk)
+ .collect(toList());
+ builder.add(TextBlockSection.of(new TextBlock(ImmutableList.copyOf(sdkOptions))));
+ }
+ return builder.build();
}
}
}
diff --git a/aswb/src/com/google/idea/blaze/android/run/runner/BlazeAndroidRunConfigurationRunner.java b/aswb/src/com/google/idea/blaze/android/run/runner/BlazeAndroidRunConfigurationRunner.java
index 6d8feb4..bf56acb 100644
--- a/aswb/src/com/google/idea/blaze/android/run/runner/BlazeAndroidRunConfigurationRunner.java
+++ b/aswb/src/com/google/idea/blaze/android/run/runner/BlazeAndroidRunConfigurationRunner.java
@@ -34,7 +34,6 @@
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.idea.blaze.base.experiments.ExperimentScope;
-import com.google.idea.blaze.base.metrics.Action;
import com.google.idea.blaze.base.run.confighandler.BlazeCommandRunConfigurationRunner;
import com.google.idea.blaze.base.run.smrunner.SmRunnerUtils;
import com.google.idea.blaze.base.scope.Scope;
@@ -42,7 +41,6 @@
import com.google.idea.blaze.base.scope.scopes.BlazeConsoleScope;
import com.google.idea.blaze.base.scope.scopes.IdeaLogScope;
import com.google.idea.blaze.base.scope.scopes.IssuesScope;
-import com.google.idea.blaze.base.scope.scopes.LoggedTimingScope;
import com.google.idea.blaze.base.settings.BlazeUserSettings;
import com.intellij.execution.DefaultExecutionResult;
import com.intellij.execution.ExecutionException;
@@ -202,8 +200,7 @@
new BlazeConsoleScope.Builder(project)
.setSuppressConsole(suppressConsole)
.build())
- .push(new IdeaLogScope())
- .push(new LoggedTimingScope(project, Action.APK_BUILD_AND_INSTALL));
+ .push(new IdeaLogScope());
BlazeAndroidRunContext runContext = env.getCopyableUserData(RUN_CONTEXT_KEY);
if (runContext == null) {
diff --git a/aswb/src/com/google/idea/blaze/android/run/runner/BlazeApkBuildStepNormalBuild.java b/aswb/src/com/google/idea/blaze/android/run/runner/BlazeApkBuildStepNormalBuild.java
index 9c18824..835aa1d 100644
--- a/aswb/src/com/google/idea/blaze/android/run/runner/BlazeApkBuildStepNormalBuild.java
+++ b/aswb/src/com/google/idea/blaze/android/run/runner/BlazeApkBuildStepNormalBuild.java
@@ -29,13 +29,11 @@
import com.google.idea.blaze.base.command.BlazeFlags;
import com.google.idea.blaze.base.filecache.FileCaches;
import com.google.idea.blaze.base.issueparser.IssueOutputLineProcessor;
-import com.google.idea.blaze.base.metrics.Action;
import com.google.idea.blaze.base.model.primitives.Label;
import com.google.idea.blaze.base.model.primitives.WorkspaceRoot;
import com.google.idea.blaze.base.scope.BlazeContext;
import com.google.idea.blaze.base.scope.ScopedTask;
import com.google.idea.blaze.base.scope.output.IssueOutput;
-import com.google.idea.blaze.base.scope.scopes.LoggedTimingScope;
import com.google.idea.blaze.base.settings.Blaze;
import com.google.idea.blaze.base.util.SaveUtil;
import com.intellij.execution.ExecutionException;
@@ -87,7 +85,7 @@
deployInfoHelper.getLineProcessor(),
new IssueOutputLineProcessor(project, context, workspaceRoot)))
.build()
- .run(new LoggedTimingScope(project, Action.BLAZE_BUILD));
+ .run();
FileCaches.refresh(project);
if (retVal != 0) {
diff --git a/aswb/src/com/google/idea/blaze/android/run/test/AndroidTestConsoleProvider.java b/aswb/src/com/google/idea/blaze/android/run/test/AndroidTestConsoleProvider.java
index d91419a..c0cbb24 100644
--- a/aswb/src/com/google/idea/blaze/android/run/test/AndroidTestConsoleProvider.java
+++ b/aswb/src/com/google/idea/blaze/android/run/test/AndroidTestConsoleProvider.java
@@ -17,7 +17,7 @@
import com.android.tools.idea.run.ConsoleProvider;
import com.google.idea.blaze.android.compatibility.Compatibility.AndroidTestConsoleProperties;
-import com.google.idea.blaze.android.run.test.smrunner.BlazeAndroidTestEventsHandler;
+import com.google.idea.blaze.base.run.smrunner.BlazeTestEventsHandler;
import com.google.idea.blaze.base.run.smrunner.SmRunnerUtils;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.Executor;
@@ -37,13 +37,13 @@
private final Project project;
private final RunConfiguration runConfiguration;
private final BlazeAndroidTestRunConfigurationState configState;
- @Nullable private final BlazeAndroidTestEventsHandler testEventsHandler;
+ @Nullable private final BlazeTestEventsHandler testEventsHandler;
AndroidTestConsoleProvider(
Project project,
RunConfiguration runConfiguration,
BlazeAndroidTestRunConfigurationState configState,
- @Nullable BlazeAndroidTestEventsHandler testEventsHandler) {
+ @Nullable BlazeTestEventsHandler testEventsHandler) {
this.project = project;
this.runConfiguration = runConfiguration;
this.configState = configState;
diff --git a/aswb/src/com/google/idea/blaze/android/run/test/BlazeAndroidTestClassRunConfigurationProducer.java b/aswb/src/com/google/idea/blaze/android/run/test/BlazeAndroidTestClassRunConfigurationProducer.java
index 8d08ec0..a9b9515 100644
--- a/aswb/src/com/google/idea/blaze/android/run/test/BlazeAndroidTestClassRunConfigurationProducer.java
+++ b/aswb/src/com/google/idea/blaze/android/run/test/BlazeAndroidTestClassRunConfigurationProducer.java
@@ -22,6 +22,7 @@
import com.google.idea.blaze.base.run.BlazeCommandRunConfiguration;
import com.google.idea.blaze.base.run.BlazeCommandRunConfigurationType;
import com.google.idea.blaze.base.run.producers.BlazeRunConfigurationProducer;
+import com.google.idea.blaze.base.run.smrunner.SmRunnerUtils;
import com.google.idea.blaze.java.run.RunUtil;
import com.google.idea.blaze.java.run.producers.JUnitConfigurationUtil;
import com.google.idea.blaze.java.run.producers.ProducerUtils;
@@ -33,7 +34,6 @@
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiMethod;
-import org.jetbrains.annotations.NotNull;
/**
* Producer for run configurations related to Android test classes in Blaze.
@@ -50,9 +50,9 @@
@Override
protected boolean doSetupConfigFromContext(
- @NotNull BlazeCommandRunConfiguration configuration,
- @NotNull ConfigurationContext context,
- @NotNull Ref<PsiElement> sourceElement) {
+ BlazeCommandRunConfiguration configuration,
+ ConfigurationContext context,
+ Ref<PsiElement> sourceElement) {
final Location contextLocation = context.getLocation();
assert contextLocation != null;
@@ -61,6 +61,10 @@
return false;
}
+ if (!SmRunnerUtils.getSelectedSmRunnerTreeElements(context).isEmpty()) {
+ // handled by a different producer
+ return false;
+ }
if (JUnitConfigurationUtil.isMultipleElementsSelected(context)) {
return false;
}
@@ -93,7 +97,7 @@
@Override
protected boolean doIsConfigFromContext(
- @NotNull BlazeCommandRunConfiguration configuration, @NotNull ConfigurationContext context) {
+ BlazeCommandRunConfiguration configuration, ConfigurationContext context) {
final Location contextLocation = context.getLocation();
assert contextLocation != null;
@@ -102,6 +106,10 @@
return false;
}
+ if (!SmRunnerUtils.getSelectedSmRunnerTreeElements(context).isEmpty()) {
+ // handled by a different producer
+ return false;
+ }
if (JUnitConfigurationUtil.isMultipleElementsSelected(context)) {
return false;
}
diff --git a/aswb/src/com/google/idea/blaze/android/run/test/BlazeAndroidTestMethodRunConfigurationProducer.java b/aswb/src/com/google/idea/blaze/android/run/test/BlazeAndroidTestMethodRunConfigurationProducer.java
index e37f07f..a0d4878 100644
--- a/aswb/src/com/google/idea/blaze/android/run/test/BlazeAndroidTestMethodRunConfigurationProducer.java
+++ b/aswb/src/com/google/idea/blaze/android/run/test/BlazeAndroidTestMethodRunConfigurationProducer.java
@@ -22,6 +22,7 @@
import com.google.idea.blaze.base.run.BlazeCommandRunConfiguration;
import com.google.idea.blaze.base.run.BlazeCommandRunConfigurationType;
import com.google.idea.blaze.base.run.producers.BlazeRunConfigurationProducer;
+import com.google.idea.blaze.base.run.smrunner.SmRunnerUtils;
import com.google.idea.blaze.java.run.RunUtil;
import com.google.idea.blaze.java.run.producers.JUnitConfigurationUtil;
import com.google.idea.blaze.java.run.producers.ProducerUtils;
@@ -31,7 +32,6 @@
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiMethod;
-import org.jetbrains.annotations.NotNull;
/**
* Producer for run configurations related to Android test methods in Blaze.
@@ -48,10 +48,13 @@
@Override
protected boolean doSetupConfigFromContext(
- @NotNull BlazeCommandRunConfiguration configuration,
- @NotNull ConfigurationContext context,
- @NotNull Ref<PsiElement> sourceElement) {
-
+ BlazeCommandRunConfiguration configuration,
+ ConfigurationContext context,
+ Ref<PsiElement> sourceElement) {
+ if (!SmRunnerUtils.getSelectedSmRunnerTreeElements(context).isEmpty()) {
+ // handled by a different producer
+ return false;
+ }
if (JUnitConfigurationUtil.isMultipleElementsSelected(context)) {
return false;
}
@@ -94,8 +97,11 @@
@Override
protected boolean doIsConfigFromContext(
- @NotNull BlazeCommandRunConfiguration configuration, @NotNull ConfigurationContext context) {
-
+ BlazeCommandRunConfiguration configuration, ConfigurationContext context) {
+ if (!SmRunnerUtils.getSelectedSmRunnerTreeElements(context).isEmpty()) {
+ // handled by a different producer
+ return false;
+ }
if (JUnitConfigurationUtil.isMultipleElementsSelected(context)) {
return false;
}
diff --git a/aswb/src/com/google/idea/blaze/android/run/test/BlazeAndroidTestRunContext.java b/aswb/src/com/google/idea/blaze/android/run/test/BlazeAndroidTestRunContext.java
index cf418e9..7f5fb95 100644
--- a/aswb/src/com/google/idea/blaze/android/run/test/BlazeAndroidTestRunContext.java
+++ b/aswb/src/com/google/idea/blaze/android/run/test/BlazeAndroidTestRunContext.java
@@ -42,12 +42,12 @@
import com.google.idea.blaze.android.run.runner.BlazeAndroidRunContext;
import com.google.idea.blaze.android.run.runner.BlazeApkBuildStep;
import com.google.idea.blaze.android.run.runner.BlazeApkBuildStepNormalBuild;
-import com.google.idea.blaze.android.run.test.smrunner.BlazeAndroidTestEventsHandler;
import com.google.idea.blaze.base.model.primitives.Label;
+import com.google.idea.blaze.base.run.BlazeCommandRunConfiguration;
+import com.google.idea.blaze.base.run.smrunner.BlazeTestEventsHandler;
import com.google.idea.common.experiments.BoolExperiment;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.Executor;
-import com.intellij.execution.configurations.RunConfiguration;
import com.intellij.execution.executors.DefaultDebugExecutor;
import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.openapi.project.Project;
@@ -61,12 +61,12 @@
/** Run context for android_test. */
class BlazeAndroidTestRunContext implements BlazeAndroidRunContext {
- static final BoolExperiment smRunnerUiEnabled =
+ private static final BoolExperiment smRunnerUiEnabled =
new BoolExperiment("use.smrunner.ui.android", true);
private final Project project;
private final AndroidFacet facet;
- private final RunConfiguration runConfiguration;
+ private final BlazeCommandRunConfiguration runConfiguration;
private final ExecutionEnvironment env;
private final BlazeAndroidTestRunConfigurationState configState;
private final Label label;
@@ -80,7 +80,7 @@
public BlazeAndroidTestRunContext(
Project project,
AndroidFacet facet,
- RunConfiguration runConfiguration,
+ BlazeCommandRunConfiguration runConfiguration,
ExecutionEnvironment env,
BlazeAndroidTestRunConfigurationState configState,
Label label,
@@ -96,12 +96,14 @@
new BlazeAndroidTestApplicationIdProvider(project, buildStep.getDeployInfo());
this.apkProvider = new BlazeApkProvider(project, buildStep.getDeployInfo());
- BlazeAndroidTestEventsHandler testEventsHandler = null;
+ BlazeTestEventsHandler testEventsHandler = null;
if (smRunnerUiEnabled.getValue() && !isDebugging(env.getExecutor())) {
- testEventsHandler = new BlazeAndroidTestEventsHandler();
+ testEventsHandler =
+ BlazeTestEventsHandler.getHandlerForTarget(project, runConfiguration.getTarget());
+ assert (testEventsHandler != null);
this.buildFlags =
ImmutableList.<String>builder()
- .addAll(testEventsHandler.getBlazeFlags())
+ .addAll(BlazeTestEventsHandler.getBlazeFlags(project))
.addAll(buildFlags)
.build();
} else {
diff --git a/aswb/src/com/google/idea/blaze/android/run/test/smrunner/BlazeAndroidTestEventsHandler.java b/aswb/src/com/google/idea/blaze/android/run/test/smrunner/BlazeAndroidTestEventsHandler.java
index f37359a..947a070 100644
--- a/aswb/src/com/google/idea/blaze/android/run/test/smrunner/BlazeAndroidTestEventsHandler.java
+++ b/aswb/src/com/google/idea/blaze/android/run/test/smrunner/BlazeAndroidTestEventsHandler.java
@@ -16,29 +16,33 @@
package com.google.idea.blaze.android.run.test.smrunner;
import com.google.idea.blaze.base.command.BlazeFlags;
+import com.google.idea.blaze.base.model.primitives.Kind;
import com.google.idea.blaze.base.run.smrunner.BlazeTestEventsHandler;
import com.google.idea.blaze.base.run.smrunner.SmRunnerUtils;
import com.google.idea.blaze.java.run.producers.BlazeJUnitTestFilterFlags;
import com.google.idea.blaze.java.run.producers.BlazeJUnitTestFilterFlags.JUnitVersion;
import com.intellij.execution.Location;
-import com.intellij.execution.testframework.AbstractTestProxy;
import com.intellij.execution.testframework.sm.runner.SMTestLocator;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiMethod;
-import com.intellij.psi.search.GlobalSearchScope;
-import com.intellij.util.containers.MultiMap;
import com.intellij.util.io.URLUtil;
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import javax.annotation.Nullable;
/** Provides java-specific methods needed by the SM-runner test UI. */
public class BlazeAndroidTestEventsHandler extends BlazeTestEventsHandler {
- public BlazeAndroidTestEventsHandler() {
- super("Blaze Android Test");
+ @Override
+ protected EnumSet<Kind> handledKinds() {
+ return EnumSet.of(Kind.ANDROID_TEST);
}
@Override
@@ -47,12 +51,13 @@
}
@Override
- public String suiteLocationUrl(String name) {
+ public String suiteLocationUrl(@Nullable Kind kind, String name) {
return SmRunnerUtils.GENERIC_SUITE_PROTOCOL + URLUtil.SCHEME_SEPARATOR + name;
}
@Override
- public String testLocationUrl(String name, @Nullable String className) {
+ public String testLocationUrl(
+ @Nullable Kind kind, String parentSuite, String name, @Nullable String className) {
// ignore initial value of className -- it's the test runner class.
name = StringUtil.trimTrailing(name, '-');
if (!name.contains("-")) {
@@ -69,7 +74,7 @@
}
@Override
- public String testDisplayName(String rawName) {
+ public String testDisplayName(@Nullable Kind kind, String rawName) {
String name = StringUtil.trimTrailing(rawName, '-');
if (name.contains("-")) {
int ix = name.lastIndexOf('-');
@@ -80,31 +85,31 @@
@Nullable
@Override
- public String getTestFilter(Project project, List<AbstractTestProxy> failedTests) {
- GlobalSearchScope projectScope = GlobalSearchScope.allScope(project);
- MultiMap<PsiClass, PsiMethod> failedMethodsPerClass = new MultiMap<>();
- for (AbstractTestProxy test : failedTests) {
- appendTest(failedMethodsPerClass, test.getLocation(project, projectScope));
+ public String getTestFilter(Project project, List<Location<?>> testLocations) {
+ Map<PsiClass, Collection<Location<?>>> failedClassesAndMethods = new HashMap<>();
+ for (Location<?> location : testLocations) {
+ appendTest(failedClassesAndMethods, location);
}
// the android test runner always runs with JUnit4
String filter =
BlazeJUnitTestFilterFlags.testFilterForClassesAndMethods(
- failedMethodsPerClass, JUnitVersion.JUNIT_4);
+ failedClassesAndMethods, JUnitVersion.JUNIT_4);
return filter != null ? BlazeFlags.TEST_FILTER + "=" + filter : null;
}
- private void appendTest(
- MultiMap<PsiClass, PsiMethod> testMap, @Nullable Location<?> testLocation) {
- if (testLocation == null) {
+ private static void appendTest(Map<PsiClass, Collection<Location<?>>> map, Location<?> location) {
+ PsiElement psi = location.getPsiElement();
+ if (psi instanceof PsiClass) {
+ map.computeIfAbsent((PsiClass) psi, k -> new HashSet<>());
return;
}
- PsiElement method = testLocation.getPsiElement();
- if (!(method instanceof PsiMethod)) {
+ if (!(psi instanceof PsiMethod)) {
return;
}
- PsiClass psiClass = ((PsiMethod) method).getContainingClass();
- if (psiClass != null) {
- testMap.putValue(psiClass, (PsiMethod) method);
+ PsiClass psiClass = ((PsiMethod) psi).getContainingClass();
+ if (psiClass == null) {
+ return;
}
+ map.computeIfAbsent(psiClass, k -> new HashSet<>()).add(location);
}
}
diff --git a/aswb/src/com/google/idea/blaze/android/sync/BlazeAndroidSyncPlugin.java b/aswb/src/com/google/idea/blaze/android/sync/BlazeAndroidSyncPlugin.java
index 74d41e7..bee4d9a 100644
--- a/aswb/src/com/google/idea/blaze/android/sync/BlazeAndroidSyncPlugin.java
+++ b/aswb/src/com/google/idea/blaze/android/sync/BlazeAndroidSyncPlugin.java
@@ -20,6 +20,7 @@
import com.google.idea.blaze.android.compatibility.Compatibility.AndroidSdkUtils;
import com.google.idea.blaze.android.compatibility.Compatibility.IdeSdks;
import com.google.idea.blaze.android.cppapi.NdkSupport;
+import com.google.idea.blaze.android.projectview.AndroidMinSdkSection;
import com.google.idea.blaze.android.projectview.AndroidSdkPlatformSection;
import com.google.idea.blaze.android.projectview.GeneratedAndroidResourcesSection;
import com.google.idea.blaze.android.sync.importer.BlazeAndroidWorkspaceImporter;
@@ -298,7 +299,9 @@
@Override
public Collection<SectionParser> getSections() {
return ImmutableList.of(
- AndroidSdkPlatformSection.PARSER, GeneratedAndroidResourcesSection.PARSER);
+ AndroidMinSdkSection.PARSER,
+ AndroidSdkPlatformSection.PARSER,
+ GeneratedAndroidResourcesSection.PARSER);
}
@Nullable
diff --git a/aswb/src/com/google/idea/blaze/android/sync/model/AndroidSdkPlatform.java b/aswb/src/com/google/idea/blaze/android/sync/model/AndroidSdkPlatform.java
index 644d749..5aed572 100644
--- a/aswb/src/com/google/idea/blaze/android/sync/model/AndroidSdkPlatform.java
+++ b/aswb/src/com/google/idea/blaze/android/sync/model/AndroidSdkPlatform.java
@@ -22,10 +22,10 @@
@Immutable
public class AndroidSdkPlatform implements Serializable {
public final String androidSdk;
- public final int androidSdkLevel;
+ public final int androidMinSdkLevel;
- public AndroidSdkPlatform(String androidSdk, int androidSdkLevel) {
+ public AndroidSdkPlatform(String androidSdk, int androidMinSdkLevel) {
this.androidSdk = androidSdk;
- this.androidSdkLevel = androidSdkLevel;
+ this.androidMinSdkLevel = androidMinSdkLevel;
}
}
diff --git a/aswb/src/com/google/idea/blaze/android/sync/model/idea/BlazeAndroidModel.java b/aswb/src/com/google/idea/blaze/android/sync/model/idea/BlazeAndroidModel.java
index 8e19be7..b89365f 100644
--- a/aswb/src/com/google/idea/blaze/android/sync/model/idea/BlazeAndroidModel.java
+++ b/aswb/src/com/google/idea/blaze/android/sync/model/idea/BlazeAndroidModel.java
@@ -45,7 +45,7 @@
private final List<SourceProvider> sourceProviders; // Singleton list of sourceProvider
private final File moduleManifest;
private final String resourceJavaPackage;
- private final int androidSdkApiLevel;
+ private final int minSdkVersion;
/** Creates a new {@link BlazeAndroidModel}. */
public BlazeAndroidModel(
@@ -55,14 +55,14 @@
SourceProvider sourceProvider,
File moduleManifest,
String resourceJavaPackage,
- int androidSdkApiLevel) {
+ int minSdkVersion) {
this.project = project;
this.rootDirPath = rootDirPath;
this.sourceProvider = sourceProvider;
this.sourceProviders = ImmutableList.of(sourceProvider);
this.moduleManifest = moduleManifest;
this.resourceJavaPackage = resourceJavaPackage;
- this.androidSdkApiLevel = androidSdkApiLevel;
+ this.minSdkVersion = minSdkVersion;
}
@Override
@@ -122,7 +122,7 @@
@Override
@Nullable
public AndroidVersion getMinSdkVersion() {
- return new AndroidVersion(androidSdkApiLevel, null);
+ return new AndroidVersion(minSdkVersion, null);
}
@Nullable
diff --git a/aswb/src/com/google/idea/blaze/android/sync/projectstructure/BlazeAndroidProjectStructureSyncer.java b/aswb/src/com/google/idea/blaze/android/sync/projectstructure/BlazeAndroidProjectStructureSyncer.java
index 1b55ff1..66b020f 100644
--- a/aswb/src/com/google/idea/blaze/android/sync/projectstructure/BlazeAndroidProjectStructureSyncer.java
+++ b/aswb/src/com/google/idea/blaze/android/sync/projectstructure/BlazeAndroidProjectStructureSyncer.java
@@ -415,7 +415,7 @@
sourceProvider,
manifest,
resourceJavaPackage,
- androidSdkPlatform.androidSdkLevel);
+ androidSdkPlatform.androidMinSdkLevel);
AndroidFacet facet = AndroidFacet.getInstance(module);
if (facet != null) {
facet.setAndroidModel(androidModel);
diff --git a/aswb/src/com/google/idea/blaze/android/sync/sdk/AndroidSdkFromProjectView.java b/aswb/src/com/google/idea/blaze/android/sync/sdk/AndroidSdkFromProjectView.java
index e4ae1b5..b47e238 100644
--- a/aswb/src/com/google/idea/blaze/android/sync/sdk/AndroidSdkFromProjectView.java
+++ b/aswb/src/com/google/idea/blaze/android/sync/sdk/AndroidSdkFromProjectView.java
@@ -17,16 +17,20 @@
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
import com.google.idea.blaze.android.compatibility.Compatibility.AndroidSdkUtils;
+import com.google.idea.blaze.android.projectview.AndroidMinSdkSection;
import com.google.idea.blaze.android.projectview.AndroidSdkPlatformSection;
import com.google.idea.blaze.android.sync.model.AndroidSdkPlatform;
import com.google.idea.blaze.base.projectview.ProjectViewSet;
+import com.google.idea.blaze.base.projectview.ProjectViewSet.ProjectViewFile;
import com.google.idea.blaze.base.scope.BlazeContext;
import com.google.idea.blaze.base.scope.output.IssueOutput;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.pom.Navigatable;
import java.util.Collection;
import java.util.List;
+import java.util.Set;
import javax.annotation.Nullable;
import org.jetbrains.android.sdk.AndroidPlatform;
import org.jetbrains.android.sdk.AndroidSdkAdditionalData;
@@ -59,52 +63,71 @@
.submit(context);
return null;
}
- String androidSdk = null;
- if (projectViewSet != null) {
- androidSdk = projectViewSet.getScalarValue(AndroidSdkPlatformSection.KEY);
+ if (projectViewSet == null) {
+ return null;
}
+ String androidSdk = projectViewSet.getScalarValue(AndroidSdkPlatformSection.KEY);
+ Integer androidMinSdk = projectViewSet.getScalarValue(AndroidMinSdkSection.KEY);
+
if (androidSdk == null) {
+ ProjectViewFile projectViewFile = projectViewSet.getTopLevelProjectViewFile();
IssueOutput.error(
("No android_sdk_platform set. Please set to an android platform. "
+ "Available android_sdk_platforms are: "
- + getAvailableSdkPlatforms(sdks)))
- .inFile(projectViewSet.getTopLevelProjectViewFile().projectViewFile)
+ + getAvailableTargetHashesAsList(sdks)))
+ .inFile(projectViewFile != null ? projectViewFile.projectViewFile : null)
.submit(context);
return null;
}
Sdk sdk = AndroidSdkUtils.findSuitableAndroidSdk(androidSdk);
if (sdk == null) {
+ ProjectViewFile projectViewFile = projectViewSet.getTopLevelProjectViewFile();
IssueOutput.error(
("No such android_sdk_platform: '"
+ androidSdk
+ "'. "
+ "Available android_sdk_platforms are: "
- + getAvailableSdkPlatforms(sdks)
+ + getAvailableTargetHashesAsList(sdks)
+ ". "
+ "Please change android_sdk_platform or run SDK manager "
+ "to download missing SDK platforms."))
- .inFile(projectViewSet.getTopLevelProjectViewFile().projectViewFile)
+ .inFile(projectViewFile != null ? projectViewFile.projectViewFile : null)
.submit(context);
return null;
}
- int androidSdkApiLevel = getAndroidSdkApiLevel(sdk);
- return new AndroidSdkPlatform(androidSdk, androidSdkApiLevel);
+ if (androidMinSdk == null) {
+ androidMinSdk = getAndroidSdkApiLevel(sdk);
+ }
+ return new AndroidSdkPlatform(androidSdk, androidMinSdk);
}
- public static String getAvailableSdkPlatforms(Collection<Sdk> sdks) {
- List<String> names = Lists.newArrayList();
- for (Sdk sdk : sdks) {
- AndroidSdkAdditionalData additionalData = AndroidSdkUtils.getAndroidSdkAdditionalData(sdk);
- if (additionalData == null) {
- continue;
- }
- String targetHash = additionalData.getBuildTargetHashString();
- names.add(targetHash);
+ @Nullable
+ public static String getSdkTargetHash(Sdk sdk) {
+ AndroidSdkAdditionalData additionalData = AndroidSdkUtils.getAndroidSdkAdditionalData(sdk);
+ if (additionalData == null) {
+ return null;
}
- return "{" + Joiner.on(", ").join(names) + "}";
+ return additionalData.getBuildTargetHashString();
+ }
+
+ public static List<String> getAvailableSdkTargetHashes(Collection<Sdk> sdks) {
+ Set<String> names = Sets.newHashSet();
+ for (Sdk sdk : sdks) {
+ String targetHash = getSdkTargetHash(sdk);
+ if (targetHash != null) {
+ names.add(targetHash);
+ }
+ }
+ List<String> result = Lists.newArrayList(names);
+ result.sort(String::compareTo);
+ return result;
+ }
+
+ private static String getAvailableTargetHashesAsList(Collection<Sdk> sdks) {
+ return Joiner.on(", ").join(getAvailableSdkTargetHashes(sdks));
}
private static int getAndroidSdkApiLevel(Sdk sdk) {
diff --git a/aswb/tests/integrationtests/com/google/idea/blaze/android/run/test/smrunner/BlazeAndroidTestEventsHandlerTest.java b/aswb/tests/integrationtests/com/google/idea/blaze/android/run/test/smrunner/BlazeAndroidTestEventsHandlerTest.java
new file mode 100644
index 0000000..474e1b4
--- /dev/null
+++ b/aswb/tests/integrationtests/com/google/idea/blaze/android/run/test/smrunner/BlazeAndroidTestEventsHandlerTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2017 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.android.run.test.smrunner;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.collect.Iterables;
+import com.google.idea.blaze.android.BlazeAndroidIntegrationTestCase;
+import com.google.idea.blaze.base.model.primitives.WorkspacePath;
+import com.intellij.execution.Location;
+import com.intellij.openapi.vfs.VirtualFileManager;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiClassOwner;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.search.GlobalSearchScope;
+import javax.annotation.Nullable;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Integration tests for {@link BlazeAndroidTestEventsHandler}. */
+@RunWith(JUnit4.class)
+public class BlazeAndroidTestEventsHandlerTest extends BlazeAndroidIntegrationTestCase {
+
+ private final BlazeAndroidTestEventsHandler handler = new BlazeAndroidTestEventsHandler();
+
+ @Before
+ public final void doSetup() {
+ // BlazeAndroidTestLocator calls JUnitUtil::isTestClass, which requires junit classes.
+ workspace.createPsiFile(
+ new WorkspacePath("org/junit/runner/RunWith.java"),
+ "package org.junit.runner;"
+ + "public @interface RunWith {"
+ + " Class<? extends Runner> value();"
+ + "}");
+ workspace.createPsiFile(
+ new WorkspacePath("org/junit/Test"), "package org.junit;", "public @interface Test {}");
+ workspace.createPsiFile(
+ new WorkspacePath("org/junit/runners/JUnit4"),
+ "package org.junit.runners;",
+ "public class JUnit4 {}");
+ }
+
+ @Test
+ public void testSuiteLocationResolves() {
+ PsiFile javaFile =
+ workspace.createPsiFile(
+ new WorkspacePath("java/com/google/lib/JavaClass.java"),
+ "package com.google.lib;",
+ "import org.junit.Test;",
+ "import org.junit.runner.RunWith;",
+ "import org.junit.runners.JUnit4;",
+ "@RunWith(JUnit4.class)",
+ "public class JavaClass {",
+ " @Test",
+ " public void testMethod() {}",
+ "}");
+ PsiClass javaClass = ((PsiClassOwner) javaFile).getClasses()[0];
+ assertThat(javaClass).isNotNull();
+
+ String url = handler.suiteLocationUrl(null, "JavaClass");
+ Location<?> location = getLocation(url);
+ assertThat(location.getPsiElement()).isEqualTo(javaClass);
+ }
+
+ @Test
+ public void testMethodLocationResolves() {
+ PsiFile javaFile =
+ workspace.createPsiFile(
+ new WorkspacePath("java/com/google/lib/JavaClass.java"),
+ "package com.google.lib;",
+ "import org.junit.Test;",
+ "import org.junit.runner.RunWith;",
+ "import org.junit.runners.JUnit4;",
+ "@RunWith(JUnit4.class)",
+ "public class JavaClass {",
+ " @Test",
+ " public void testMethod() {}",
+ "}");
+ PsiClass javaClass = ((PsiClassOwner) javaFile).getClasses()[0];
+ PsiMethod method = javaClass.findMethodsByName("testMethod", false)[0];
+ assertThat(method).isNotNull();
+
+ String url =
+ handler.testLocationUrl(
+ null, null, "JavaClass-testMethod", "com.google.test.AndroidTestBase");
+ Location<?> location = getLocation(url);
+ assertThat(location.getPsiElement()).isEqualTo(method);
+ }
+
+ @Nullable
+ private Location<?> getLocation(String url) {
+ String protocol = VirtualFileManager.extractProtocol(url);
+ String path = VirtualFileManager.extractPath(url);
+ if (protocol == null) {
+ return null;
+ }
+ return Iterables.getFirst(
+ handler
+ .getTestLocator()
+ .getLocation(protocol, path, getProject(), GlobalSearchScope.allScope(getProject())),
+ null);
+ }
+}
diff --git a/base/src/META-INF/blaze-base.xml b/base/src/META-INF/blaze-base.xml
index 34d33f4..913bdb5 100644
--- a/base/src/META-INF/blaze-base.xml
+++ b/base/src/META-INF/blaze-base.xml
@@ -49,8 +49,12 @@
text="Show Performance Warnings">
</action>
<action id="Blaze.EditProjectView"
- class="com.google.idea.blaze.base.settings.ui.EditProjectViewAction"
- text="Open Project View Files">
+ class="com.google.idea.blaze.base.settings.ui.OpenAllProjectViewsAction"
+ text="Open All Project View Files">
+ </action>
+ <action id="Blaze.EditLocalProjectView"
+ class="com.google.idea.blaze.base.settings.ui.OpenLocalProjectViewAction"
+ text="Open Local Project View File">
</action>
<action class="com.google.idea.blaze.base.buildmap.OpenCorrespondingBuildFile"
id="Blaze.OpenCorrespondingBuildFile"
@@ -76,7 +80,9 @@
<group id="Blaze.MainMenuActionGroup" class="com.google.idea.blaze.base.actions.BlazeMenuGroup">
<add-to-group group-id="MainMenu" anchor="before" relative-to-action="HelpMenu"/>
+ <reference id="Blaze.EditLocalProjectView"/>
<reference id="Blaze.EditProjectView"/>
+ <separator/>
<group id ="Blaze.SyncMenuGroup" text="Sync" popup="true">
<reference id="Blaze.IncrementalSyncProject"/>
<reference id="Blaze.FullSyncProject"/>
@@ -110,15 +116,10 @@
<separator/>
</group>
- <group id="Blaze.ProjectViewPopupMenu">
+ <group id="Blaze.PerFileContextMenu">
<add-to-group anchor="after" group-id="ProjectViewPopupMenu" relative-to-action="EditSource"/>
- <separator/>
- <reference ref="Blaze.PartialSync"/>
- <reference ref="Blaze.OpenCorrespondingBuildFile"/>
- </group>
-
- <group id="Blaze.EditorTabPopupMenu">
<add-to-group anchor="after" group-id="EditorTabPopupMenu" relative-to-action="CopyReference"/>
+ <add-to-group anchor="before" group-id="EditorPopupMenu" relative-to-action="$SearchWeb"/>
<separator/>
<reference ref="Blaze.PartialSync"/>
<reference ref="Blaze.OpenCorrespondingBuildFile"/>
@@ -153,6 +154,8 @@
serviceImplementation="com.google.idea.blaze.base.io.InputStreamProviderImpl"/>
<applicationService serviceInterface="com.google.idea.blaze.base.io.FileAttributeProvider"
serviceImplementation="com.google.idea.blaze.base.io.FileAttributeProvider"/>
+ <applicationService serviceInterface="com.google.idea.blaze.base.io.VirtualFileSystemProvider"
+ serviceImplementation="com.google.idea.blaze.base.io.VirtualFileSystemProviderImpl"/>
<applicationService serviceInterface="com.google.idea.blaze.base.buildmodifier.BuildFileModifier"
serviceImplementation="com.google.idea.blaze.base.lang.buildfile.actions.BuildFileModifierImpl"/>
<projectService serviceInterface="com.google.idea.blaze.base.buildmodifier.FileSystemModifier"
@@ -193,6 +196,7 @@
serviceImplementation="com.google.idea.blaze.base.wizard2.BazelWizardOptionProvider"/>
<projectService serviceInterface="com.google.idea.blaze.base.sync.workspace.WorkspacePathResolverProvider"
serviceImplementation="com.google.idea.blaze.base.sync.workspace.WorkspacePathResolverProviderImpl"/>
+ <projectService serviceImplementation="com.google.idea.blaze.base.sync.SyncCache"/>
<configurationType implementation="com.google.idea.blaze.base.run.BlazeCommandRunConfigurationType"/>
<runConfigurationProducer
implementation="com.google.idea.blaze.base.run.producers.AllInPackageBlazeConfigurationProducer"
@@ -200,6 +204,9 @@
<runConfigurationProducer
implementation="com.google.idea.blaze.base.run.producers.BlazeBuildFileRunConfigurationProducer"
order="first"/>
+ <runConfigurationProducer
+ implementation="com.google.idea.blaze.base.run.producers.BlazeFilterExistingRunConfigurationProducer"
+ order="first"/>
<stepsBeforeRunProvider implementation="com.google.idea.blaze.base.run.BlazeBeforeRunTaskProvider"/>
<applicationService serviceInterface="com.google.idea.blaze.base.help.BlazeHelpHandler"
serviceImplementation="com.google.idea.blaze.base.help.BlazeHelpHandlerImpl"/>
@@ -223,8 +230,9 @@
<extensions defaultExtensionNs="com.intellij">
<fileTypeFactory implementation="com.google.idea.blaze.base.lang.buildfile.language.BuildFileTypeFactory"/>
<annotator language="BUILD" implementationClass="com.google.idea.blaze.base.lang.buildfile.validation.HighlightingAnnotator"/>
- <!--<annotator language="BUILD" implementationClass="com.google.idea.blaze.base.lang.buildfile.validation.ErrorAnnotator"/>-->
+ <!--<annotator language="BUILD" implementationClass="com.google.idea.blaze.base.lang.buildfile.validation.LoadErrorAnnotator"/>-->
<annotator language="BUILD" implementationClass="com.google.idea.blaze.base.lang.buildfile.validation.GlobErrorAnnotator"/>
+ <annotator language="BUILD" implementationClass="com.google.idea.blaze.base.lang.buildfile.validation.BuiltInRuleAnnotator"/>
<colorSettingsPage implementation="com.google.idea.blaze.base.lang.buildfile.highlighting.BuildColorsPage"/>
<projectService serviceImplementation="com.google.idea.blaze.base.lang.buildfile.psi.util.BuildElementGenerator"/>
<projectService serviceImplementation="com.google.idea.blaze.base.lang.buildfile.references.BuildReferenceManager"/>
@@ -248,6 +256,7 @@
<editor.backspaceModeOverride language="BUILD" implementationClass="com.intellij.codeInsight.editorActions.SmartBackspaceDisabler"/>
<filetype.stubBuilder filetype="BUILD" implementationClass="com.google.idea.blaze.base.lang.buildfile.stubs.BuildFileStubBuilder"/>
<editorNotificationProvider implementation="com.google.idea.blaze.base.lang.AdditionalLanguagesHelper"/>
+ <usageTypeProvider implementation="com.google.idea.blaze.base.lang.buildfile.findusages.BuildUsageTypeProvider"/>
</extensions>
<extensions defaultExtensionNs="com.intellij.lang">
@@ -261,6 +270,7 @@
<findUsagesProvider language="BUILD" implementationClass="com.google.idea.blaze.base.lang.buildfile.findusages.BuildFindUsagesProvider"/>
<refactoringSupport language="BUILD" implementationClass="com.google.idea.blaze.base.lang.buildfile.refactor.BuildRefactoringSupportProvider"/>
<documentationProvider language="BUILD" implementationClass="com.google.idea.blaze.base.lang.buildfile.documentation.BuildDocumentationProvider"/>
+ <elementManipulator forClass="com.google.idea.blaze.base.lang.buildfile.psi.StringLiteral" implementationClass="com.google.idea.blaze.base.lang.buildfile.refactor.StringLiteralElementManipulator"/>
</extensions>
<extensionPoints>
@@ -304,7 +314,6 @@
<extensionPoint qualifiedName="com.google.idea.blaze.BuildFlagsProvider" interface="com.google.idea.blaze.base.command.BuildFlagsProvider"/>
<extensionPoint qualifiedName="com.google.idea.blaze.BuildSystemProvider" interface="com.google.idea.blaze.base.bazel.BuildSystemProvider"/>
<extensionPoint qualifiedName="com.google.idea.blaze.BuildifierBinaryProvider" interface="com.google.idea.blaze.base.buildmodifier.BuildifierBinaryProvider"/>
- <extensionPoint qualifiedName="com.google.idea.blaze.LoggingService" interface="com.google.idea.blaze.base.metrics.LoggingService"/>
<extensionPoint qualifiedName="com.google.idea.blaze.BlazeCommandRunConfigurationHandlerProvider" interface="com.google.idea.blaze.base.run.confighandler.BlazeCommandRunConfigurationHandlerProvider"/>
<extensionPoint qualifiedName="com.google.idea.blaze.BlazeUserSettingsContributor" interface="com.google.idea.blaze.base.settings.ui.BlazeUserSettingsContributor$Provider"/>
<extensionPoint qualifiedName="com.google.idea.blaze.BlazePsiDirectoryRootNodeNameModifier" interface="com.google.idea.blaze.base.treeview.BlazePsiDirectoryRootNodeNameModifier"/>
@@ -313,13 +322,16 @@
<extensionPoint qualifiedName="com.google.idea.blaze.ProjectDataDirectoryValidator" interface="com.google.idea.blaze.base.wizard2.ProjectDataDirectoryValidator"/>
<extensionPoint qualifiedName="com.google.idea.blaze.AspectStrategyProvider" interface="com.google.idea.blaze.base.sync.aspects.strategy.AspectStrategyProvider"/>
<extensionPoint qualifiedName="com.google.idea.blaze.DistributedExecutorSupport" interface="com.google.idea.blaze.base.run.DistributedExecutorSupport"/>
+ <extensionPoint qualifiedName="com.google.idea.blaze.FileStringParser" interface="com.google.idea.blaze.base.run.filter.FileResolver"/>
+ <extensionPoint qualifiedName="com.google.idea.blaze.BlazeTestXmlFinderStrategy" interface="com.google.idea.blaze.base.run.testlogs.BlazeTestXmlFinderStrategy"/>
+ <extensionPoint qualifiedName="com.google.idea.blaze.BlazeTestEventsHandler" interface="com.google.idea.blaze.base.run.smrunner.BlazeTestEventsHandler"/>
+ <extensionPoint qualifiedName="com.google.idea.blaze.AttributeSpecificStringLiteralReferenceProvider" interface="com.google.idea.blaze.base.lang.buildfile.references.AttributeSpecificStringLiteralReferenceProvider"/>
</extensionPoints>
<extensions defaultExtensionNs="com.google.idea.blaze">
+ <SyncListener implementation="com.google.idea.blaze.base.sync.SyncCache$ClearSyncCache"/>
<SyncListener implementation="com.google.idea.blaze.base.run.BlazeRunConfigurationSyncListener"/>
<SyncListener implementation="com.google.idea.blaze.base.sync.status.BlazeSyncStatusListener"/>
- <SyncListener implementation="com.google.idea.blaze.base.run.testmap.TestTargetFilterImpl$ClearTestMap"/>
- <SyncListener implementation="com.google.idea.blaze.base.targetmaps.SourceToTargetMapImpl$ClearSourceToTargetMap"/>
<SyncPlugin implementation="com.google.idea.blaze.base.lang.buildfile.sync.BuildLangSyncPlugin"/>
<BuildFlagsProvider implementation="com.google.idea.blaze.base.command.BuildFlagsProviderImpl"/>
<VcsHandler implementation="com.google.idea.blaze.base.vcs.git.GitBlazeVcsHandler"/>
@@ -331,6 +343,9 @@
<TestTargetHeuristic implementation="com.google.idea.blaze.base.run.TestSizeHeuristic" order="last" id="TestSizeHeuristic"/>
<RunConfigurationFactory implementation="com.google.idea.blaze.base.run.BlazeBuildTargetRunConfigurationFactory" order="last"/>
<AspectStrategyProvider implementation="com.google.idea.blaze.base.sync.aspects.strategy.AspectStrategyProviderBazel" order="last"/>
+ <FileStringParser implementation="com.google.idea.blaze.base.run.filter.StandardFileResolver" order="last"/>
+ <BlazeTestXmlFinderStrategy implementation="com.google.idea.blaze.base.run.testlogs.TargetPathTestXmlFinderStrategy"/>
+ <BlazeTestEventsHandler implementation="com.google.idea.blaze.base.run.smrunner.BlazeCompositeTestEventsHandler" order="last"/>
</extensions>
</idea-plugin>
diff --git a/base/src/com/google/idea/blaze/base/actions/BlazeBuildService.java b/base/src/com/google/idea/blaze/base/actions/BlazeBuildService.java
index 4da44f1..6b9e095 100644
--- a/base/src/com/google/idea/blaze/base/actions/BlazeBuildService.java
+++ b/base/src/com/google/idea/blaze/base/actions/BlazeBuildService.java
@@ -21,7 +21,6 @@
import com.google.idea.blaze.base.async.executor.BlazeExecutor;
import com.google.idea.blaze.base.experiments.ExperimentScope;
import com.google.idea.blaze.base.filecache.FileCaches;
-import com.google.idea.blaze.base.metrics.Action;
import com.google.idea.blaze.base.model.BlazeProjectData;
import com.google.idea.blaze.base.model.primitives.Label;
import com.google.idea.blaze.base.model.primitives.TargetExpression;
@@ -34,7 +33,6 @@
import com.google.idea.blaze.base.scope.scopes.BlazeConsoleScope;
import com.google.idea.blaze.base.scope.scopes.IdeaLogScope;
import com.google.idea.blaze.base.scope.scopes.IssuesScope;
-import com.google.idea.blaze.base.scope.scopes.LoggedTimingScope;
import com.google.idea.blaze.base.scope.scopes.NotificationScope;
import com.google.idea.blaze.base.scope.scopes.TimingScope;
import com.google.idea.blaze.base.settings.Blaze;
@@ -59,7 +57,6 @@
project,
Lists.newArrayList(targets),
ProjectViewManager.getInstance(project).getProjectViewSet(),
- new LoggedTimingScope(project, Action.MAKE_MODULE_TOTAL_TIME),
new NotificationScope(
project,
"Make",
@@ -80,7 +77,6 @@
project,
projectViewSet.listItems(TargetSection.KEY),
projectViewSet,
- new LoggedTimingScope(project, Action.MAKE_PROJECT_TOTAL_TIME),
new NotificationScope(
project,
"Make",
@@ -94,7 +90,6 @@
Project project,
List<TargetExpression> targets,
ProjectViewSet projectViewSet,
- LoggedTimingScope loggedTimingScope,
NotificationScope notificationScope) {
if (targets.isEmpty() || projectViewSet == null) {
return;
@@ -115,7 +110,6 @@
.push(new IssuesScope(project))
.push(new IdeaLogScope())
.push(new TimingScope("Make"))
- .push(loggedTimingScope)
.push(notificationScope);
WorkspaceRoot workspaceRoot = WorkspaceRoot.fromProject(project);
diff --git a/base/src/com/google/idea/blaze/base/async/FutureUtil.java b/base/src/com/google/idea/blaze/base/async/FutureUtil.java
index 07f3e4e..e3be151 100644
--- a/base/src/com/google/idea/blaze/base/async/FutureUtil.java
+++ b/base/src/com/google/idea/blaze/base/async/FutureUtil.java
@@ -52,7 +52,7 @@
/** Builder for the future */
public static class Builder<T> {
- private static final Logger LOG = Logger.getInstance(FutureUtil.class);
+ private static final Logger logger = Logger.getInstance(FutureUtil.class);
private final BlazeContext context;
private final ListenableFuture<T> future;
private String timingCategory;
@@ -95,7 +95,7 @@
Thread.currentThread().interrupt();
context.setCancelled();
} catch (ExecutionException e) {
- LOG.error(e);
+ logger.error(e);
if (errorMessage != null) {
IssueOutput.error(errorMessage).submit(childContext);
}
diff --git a/base/src/com/google/idea/blaze/base/async/process/ExternalTask.java b/base/src/com/google/idea/blaze/base/async/process/ExternalTask.java
index 86b7d50..67fe83b 100644
--- a/base/src/com/google/idea/blaze/base/async/process/ExternalTask.java
+++ b/base/src/com/google/idea/blaze/base/async/process/ExternalTask.java
@@ -41,7 +41,7 @@
/** Invokes an external process */
public class ExternalTask {
- private static final Logger LOG = Logger.getInstance(ExternalTask.class);
+ private static final Logger logger = Logger.getInstance(ExternalTask.class);
static final OutputStream NULL_STREAM = ByteStreams.nullOutputStream();
@@ -222,6 +222,8 @@
Thread shutdownHook = new Thread(process::destroy);
try {
Runtime.getRuntime().addShutdownHook(shutdownHook);
+ // These tasks are non-interactive, so close the stream connected to the process's input.
+ process.getOutputStream().close();
Thread stdoutThread = ProcessUtil.forwardAsync(process.getInputStream(), stdout);
Thread stderrThread = null;
if (!redirectErrorStream) {
@@ -248,7 +250,7 @@
}
}
} catch (IOException e) {
- LOG.warn(e);
+ logger.warn(e);
IssueOutput.error(e.getMessage()).submit(context);
}
} finally {
diff --git a/base/src/com/google/idea/blaze/base/async/process/ProcessUtil.java b/base/src/com/google/idea/blaze/base/async/process/ProcessUtil.java
index 01b02b7..733fca7 100644
--- a/base/src/com/google/idea/blaze/base/async/process/ProcessUtil.java
+++ b/base/src/com/google/idea/blaze/base/async/process/ProcessUtil.java
@@ -21,7 +21,7 @@
import java.io.OutputStream;
class ProcessUtil {
- private static final Logger LOG = Logger.getInstance(ProcessUtil.class);
+ private static final Logger logger = Logger.getInstance(ProcessUtil.class);
public static Thread forwardAsync(final InputStream input, final OutputStream output) {
Thread thread =
@@ -40,7 +40,7 @@
read = input.read(buffer);
}
} catch (IOException e) {
- LOG.warn("Error redirecting output", e);
+ logger.warn("Error redirecting output", e);
}
}
});
diff --git a/base/src/com/google/idea/blaze/base/bazel/BuildSystemProvider.java b/base/src/com/google/idea/blaze/base/bazel/BuildSystemProvider.java
index 6ce5aca..61c7257 100644
--- a/base/src/com/google/idea/blaze/base/bazel/BuildSystemProvider.java
+++ b/base/src/com/google/idea/blaze/base/bazel/BuildSystemProvider.java
@@ -21,7 +21,6 @@
import com.google.idea.blaze.base.model.BlazeVersionData;
import com.google.idea.blaze.base.model.primitives.WorkspaceRoot;
import com.google.idea.blaze.base.settings.Blaze.BuildSystem;
-import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.fileTypes.FileNameMatcher;
import com.intellij.openapi.vfs.VirtualFile;
@@ -65,10 +64,6 @@
return provider.getWorkspaceRootProvider();
}
- static BuildSystemProvider getInstance() {
- return ServiceManager.getService(BuildSystemProvider.class);
- }
-
/**
* Returns the default build system for this application. This should only be called in situations
* where it doesn't make sense to use the current project.<br>
diff --git a/base/src/com/google/idea/blaze/base/buildmap/OpenCorrespondingBuildFile.java b/base/src/com/google/idea/blaze/base/buildmap/OpenCorrespondingBuildFile.java
index 592b7b4..712022f 100644
--- a/base/src/com/google/idea/blaze/base/buildmap/OpenCorrespondingBuildFile.java
+++ b/base/src/com/google/idea/blaze/base/buildmap/OpenCorrespondingBuildFile.java
@@ -17,8 +17,11 @@
import com.google.common.collect.Iterables;
import com.google.idea.blaze.base.actions.BlazeProjectAction;
-import com.google.idea.blaze.base.metrics.Action;
-import com.google.idea.blaze.base.metrics.LoggingService;
+import com.google.idea.blaze.base.lang.buildfile.references.BuildReferenceManager;
+import com.google.idea.blaze.base.model.BlazeProjectData;
+import com.google.idea.blaze.base.model.primitives.Label;
+import com.google.idea.blaze.base.sync.data.BlazeProjectDataManager;
+import com.google.idea.blaze.base.targetmaps.SourceToTargetMap;
import com.intellij.ide.actions.OpenFileAction;
import com.intellij.openapi.actionSystem.ActionPlaces;
import com.intellij.openapi.actionSystem.AnActionEvent;
@@ -27,6 +30,8 @@
import com.intellij.openapi.actionSystem.Presentation;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.NavigatablePsiElement;
+import com.intellij.psi.PsiElement;
import java.io.File;
import java.util.Collection;
import javax.annotation.Nullable;
@@ -36,16 +41,29 @@
@Override
protected void actionPerformedInBlazeProject(Project project, AnActionEvent e) {
VirtualFile virtualFile = e.getData(CommonDataKeys.VIRTUAL_FILE);
+ if (virtualFile == null) {
+ return;
+ }
+ navigateToTargetOrFile(project, virtualFile);
+ }
+
+ /** Returns true if a target or BUILD file could be found and navigated to. */
+ private static void navigateToTargetOrFile(Project project, VirtualFile virtualFile) {
+ // first, look for a specific target which includes this source file
+ PsiElement target = findBuildTarget(project, new File(virtualFile.getPath()));
+ if (target instanceof NavigatablePsiElement) {
+ ((NavigatablePsiElement) target).navigate(true);
+ return;
+ }
File file = getBuildFile(project, virtualFile);
if (file == null) {
return;
}
OpenFileAction.openFile(file.getPath(), project);
- LoggingService.reportEvent(project, Action.OPEN_CORRESPONDING_BUILD_FILE);
}
@Nullable
- private File getBuildFile(Project project, @Nullable VirtualFile virtualFile) {
+ private static File getBuildFile(Project project, @Nullable VirtualFile virtualFile) {
if (virtualFile == null) {
return null;
}
@@ -54,6 +72,25 @@
return Iterables.getFirst(fileInfoList, null);
}
+ @Nullable
+ private static PsiElement findBuildTarget(Project project, File file) {
+ BlazeProjectData blazeProjectData =
+ BlazeProjectDataManager.getInstance(project).getBlazeProjectData();
+ if (blazeProjectData == null) {
+ return null;
+ }
+ Label label =
+ SourceToTargetMap.getInstance(project)
+ .getTargetsToBuildForSourceFile(file)
+ .stream()
+ .findFirst()
+ .orElse(null);
+ if (label == null) {
+ return null;
+ }
+ return BuildReferenceManager.getInstance(project).resolveLabel(label);
+ }
+
@Override
protected void updateForBlazeProject(Project project, AnActionEvent e) {
Presentation presentation = e.getPresentation();
diff --git a/base/src/com/google/idea/blaze/base/command/info/BlazeInfo.java b/base/src/com/google/idea/blaze/base/command/info/BlazeInfo.java
index 6099fc2..1d329aa 100644
--- a/base/src/com/google/idea/blaze/base/command/info/BlazeInfo.java
+++ b/base/src/com/google/idea/blaze/base/command/info/BlazeInfo.java
@@ -31,6 +31,7 @@
public static final String BUILD_LANGUAGE = "build-language";
public static final String OUTPUT_BASE_KEY = "output_base";
public static final String MASTER_LOG = "master-log";
+ public static final String COMMAND_LOG = "command_log";
public static final String RELEASE = "release";
public static String blazeBinKey(BuildSystem buildSystem) {
@@ -55,6 +56,17 @@
}
}
+ public static String blazeTestlogsKey(BuildSystem buildSystem) {
+ switch (buildSystem) {
+ case Blaze:
+ return "blaze-testlogs";
+ case Bazel:
+ return "bazel-testlogs";
+ default:
+ throw new IllegalArgumentException("Unrecognized build system: " + buildSystem);
+ }
+ }
+
public static BlazeInfo getInstance() {
return ServiceManager.getService(BlazeInfo.class);
}
diff --git a/base/src/com/google/idea/blaze/base/command/info/BlazeInfoImpl.java b/base/src/com/google/idea/blaze/base/command/info/BlazeInfoImpl.java
index cf82fdd..4b9fe6c 100644
--- a/base/src/com/google/idea/blaze/base/command/info/BlazeInfoImpl.java
+++ b/base/src/com/google/idea/blaze/base/command/info/BlazeInfoImpl.java
@@ -30,7 +30,7 @@
import javax.annotation.Nullable;
class BlazeInfoImpl extends BlazeInfo {
- private static final Logger LOG = Logger.getInstance(BlazeInfoImpl.class);
+ private static final Logger logger = Logger.getInstance(BlazeInfoImpl.class);
@Override
public ListenableFuture<String> runBlazeInfo(
@@ -110,7 +110,7 @@
for (String blazeInfoLine : blazeInfoLines) {
// Just split on the first ":".
String[] keyValue = blazeInfoLine.split(":", 2);
- LOG.assertTrue(keyValue.length == 2, blazeInfoLine);
+ logger.assertTrue(keyValue.length == 2, blazeInfoLine);
String key = keyValue[0].trim();
String value = keyValue[1].trim();
blazeInfoMapBuilder.put(key, value);
diff --git a/base/src/com/google/idea/blaze/base/console/ColoredConsoleStream.java b/base/src/com/google/idea/blaze/base/console/ColoredConsoleStream.java
new file mode 100644
index 0000000..1ee034e
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/console/ColoredConsoleStream.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2017 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.console;
+
+import com.intellij.execution.process.AnsiEscapeDecoder;
+import com.intellij.execution.process.AnsiEscapeDecoder.ColoredTextAcceptor;
+import com.intellij.execution.process.ProcessOutputTypes;
+import com.intellij.execution.ui.ConsoleViewContentType;
+import com.intellij.openapi.util.Key;
+
+/** ConsoleStream that decodes color codes before forwarding data to the next console stream. */
+public class ColoredConsoleStream implements ColoredTextAcceptor, ConsoleStream {
+
+ private final ConsoleStream consoleStream;
+ private final AnsiEscapeDecoder ansiEscapeDecoder = new AnsiEscapeDecoder();
+
+ public ColoredConsoleStream(ConsoleStream consoleStream) {
+ this.consoleStream = consoleStream;
+ }
+
+ @Override
+ public void print(String text, ConsoleViewContentType contentType) {
+ Key<?> key =
+ contentType == ConsoleViewContentType.ERROR_OUTPUT
+ ? ProcessOutputTypes.STDERR
+ : ProcessOutputTypes.STDOUT;
+ ansiEscapeDecoder.escapeText(text, key, this);
+ }
+
+ @Override
+ public void coloredTextAvailable(String escapedText, @SuppressWarnings("rawtypes") Key key) {
+ ConsoleViewContentType contentType = ConsoleViewContentType.getConsoleViewType(key);
+ consoleStream.print(escapedText, contentType);
+ }
+}
diff --git a/base/src/com/google/idea/blaze/base/run/smrunner/BlazeTestEventsHandlerProvider.java b/base/src/com/google/idea/blaze/base/console/ConsoleStream.java
similarity index 62%
rename from base/src/com/google/idea/blaze/base/run/smrunner/BlazeTestEventsHandlerProvider.java
rename to base/src/com/google/idea/blaze/base/console/ConsoleStream.java
index c02a0cb..15dab3c 100644
--- a/base/src/com/google/idea/blaze/base/run/smrunner/BlazeTestEventsHandlerProvider.java
+++ b/base/src/com/google/idea/blaze/base/console/ConsoleStream.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 The Bazel Authors. All rights reserved.
+ * Copyright 2017 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.
@@ -13,10 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.google.idea.blaze.base.run.smrunner;
+package com.google.idea.blaze.base.console;
-/** Provides a {@link BlazeTestEventsHandler}. */
-public interface BlazeTestEventsHandlerProvider {
+import com.intellij.execution.ui.ConsoleViewContentType;
- BlazeTestEventsHandler getHandler();
+/** Stream that outputs strings with a {@link ConsoleViewContentType}. */
+public interface ConsoleStream {
+
+ void print(String text, ConsoleViewContentType contentType);
}
diff --git a/base/src/com/google/idea/blaze/base/filecache/FileDiffer.java b/base/src/com/google/idea/blaze/base/filecache/FileDiffer.java
index a76fa8d..0142f06 100644
--- a/base/src/com/google/idea/blaze/base/filecache/FileDiffer.java
+++ b/base/src/com/google/idea/blaze/base/filecache/FileDiffer.java
@@ -27,7 +27,7 @@
/** Provides a diffing service for a collection of files. */
public final class FileDiffer {
- private static Logger LOG = Logger.getInstance(FileDiffer.class);
+ private static Logger logger = Logger.getInstance(FileDiffer.class);
private FileDiffer() {}
@@ -50,7 +50,7 @@
try {
return ModifiedTimeScanner.readTimestamps(files);
} catch (Exception e) {
- LOG.error(e);
+ logger.error(e);
return null;
}
}
diff --git a/base/src/com/google/idea/blaze/base/ide/NewBlazePackageAction.java b/base/src/com/google/idea/blaze/base/ide/NewBlazePackageAction.java
index 2d0f519..19c2b10 100644
--- a/base/src/com/google/idea/blaze/base/ide/NewBlazePackageAction.java
+++ b/base/src/com/google/idea/blaze/base/ide/NewBlazePackageAction.java
@@ -21,7 +21,6 @@
import com.google.idea.blaze.base.actions.BlazeProjectAction;
import com.google.idea.blaze.base.buildmodifier.BuildFileModifier;
import com.google.idea.blaze.base.buildmodifier.FileSystemModifier;
-import com.google.idea.blaze.base.metrics.Action;
import com.google.idea.blaze.base.model.primitives.Kind;
import com.google.idea.blaze.base.model.primitives.Label;
import com.google.idea.blaze.base.model.primitives.WorkspacePath;
@@ -33,9 +32,9 @@
import com.google.idea.blaze.base.scope.ScopedOperation;
import com.google.idea.blaze.base.scope.output.PrintOutput;
import com.google.idea.blaze.base.scope.output.StatusOutput;
-import com.google.idea.blaze.base.scope.scopes.LoggedTimingScope;
import com.google.idea.blaze.base.settings.Blaze;
import com.google.idea.blaze.base.sync.projectview.ImportRoots;
+import com.google.idea.blaze.base.util.WorkspacePathUtil;
import com.intellij.history.LocalHistory;
import com.intellij.history.LocalHistoryAction;
import com.intellij.ide.IdeView;
@@ -46,7 +45,6 @@
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.project.Project;
-import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDirectory;
@@ -60,7 +58,7 @@
import org.jetbrains.annotations.NotNull;
class NewBlazePackageAction extends BlazeProjectAction implements DumbAware {
- private static final Logger LOG = Logger.getInstance(NewBlazePackageAction.class);
+ private static final Logger logger = Logger.getInstance(NewBlazePackageAction.class);
private static final String BUILD_FILE_NAME = "BUILD";
@@ -75,8 +73,6 @@
new ScopedOperation() {
@Override
public void execute(@NotNull final BlazeContext context) {
- context.push(new LoggedTimingScope(project, Action.CREATE_BLAZE_PACKAGE));
-
if (view == null || project == null) {
return;
}
@@ -96,8 +92,8 @@
final Label newRule = newBlazePackageDialog.getNewRule();
final Kind newRuleKind = newBlazePackageDialog.getNewRuleKind();
// If we returned OK, we should have a non null result
- LOG.assertTrue(newRule != null);
- LOG.assertTrue(newRuleKind != null);
+ logger.assertTrue(newRule != null);
+ logger.assertTrue(newRuleKind != null);
context.output(
new StatusOutput(
@@ -113,7 +109,7 @@
WorkspaceRoot.fromProject(project).fileForPath(newRule.blazePackage());
VirtualFile virtualFile = VfsUtil.findFileByIoFile(newDirectory, true);
// We just created this file, it should exist
- LOG.assertTrue(virtualFile != null);
+ logger.assertTrue(virtualFile != null);
PsiFile psiFile = PsiManager.getInstance(project).findFile(virtualFile);
view.selectElement(psiFile);
}
@@ -217,13 +213,7 @@
return false;
}
WorkspacePath workspacePath = workspaceRoot.workspacePathFor(virtualFile);
- return importRoots
- .rootDirectories()
- .stream()
- .anyMatch(
- importRoot ->
- FileUtil.isAncestor(
- importRoot.relativePath(), workspacePath.relativePath(), false));
+ return WorkspacePathUtil.isUnderAnyWorkspacePath(importRoots.rootDirectories(), workspacePath);
}
@Nullable
diff --git a/base/src/com/google/idea/blaze/base/ide/NewBlazePackageDialog.java b/base/src/com/google/idea/blaze/base/ide/NewBlazePackageDialog.java
index f51ccd9..1f5d7c5 100644
--- a/base/src/com/google/idea/blaze/base/ide/NewBlazePackageDialog.java
+++ b/base/src/com/google/idea/blaze/base/ide/NewBlazePackageDialog.java
@@ -45,7 +45,7 @@
import org.jetbrains.annotations.Nullable;
class NewBlazePackageDialog extends DialogWrapper {
- private static final Logger LOG = Logger.getInstance(NewBlazePackageDialog.class);
+ private static final Logger logger = Logger.getInstance(NewBlazePackageDialog.class);
@NotNull private final Project project;
@NotNull private final PsiDirectory parentDirectory;
@@ -105,7 +105,7 @@
@Override
protected void doOKAction() {
WorkspaceRoot workspaceRoot = WorkspaceRoot.fromProject(project);
- LOG.assertTrue(parentDirectory.getVirtualFile().isInLocalFileSystem());
+ logger.assertTrue(parentDirectory.getVirtualFile().isInLocalFileSystem());
File parentDirectoryFile = new File(parentDirectory.getVirtualFile().getPath());
String newPackageName = packageNameField.getText();
File newPackageDirectory = new File(parentDirectoryFile, newPackageName);
diff --git a/base/src/com/google/idea/blaze/base/ide/NewBlazeRuleAction.java b/base/src/com/google/idea/blaze/base/ide/NewBlazeRuleAction.java
index 422dada..1e072af 100644
--- a/base/src/com/google/idea/blaze/base/ide/NewBlazeRuleAction.java
+++ b/base/src/com/google/idea/blaze/base/ide/NewBlazeRuleAction.java
@@ -18,12 +18,10 @@
import com.google.idea.blaze.base.actions.BlazeProjectAction;
import com.google.idea.blaze.base.experiments.ExperimentScope;
-import com.google.idea.blaze.base.metrics.Action;
import com.google.idea.blaze.base.scope.Scope;
import com.google.idea.blaze.base.scope.scopes.BlazeConsoleScope;
import com.google.idea.blaze.base.scope.scopes.IdeaLogScope;
import com.google.idea.blaze.base.scope.scopes.IssuesScope;
-import com.google.idea.blaze.base.scope.scopes.LoggedTimingScope;
import com.google.idea.blaze.base.settings.Blaze;
import com.intellij.openapi.actionSystem.ActionPlaces;
import com.intellij.openapi.actionSystem.AnActionEvent;
@@ -54,8 +52,7 @@
.push(new ExperimentScope())
.push(new BlazeConsoleScope.Builder(project).build())
.push(new IssuesScope(project))
- .push(new IdeaLogScope())
- .push(new LoggedTimingScope(project, Action.CREATE_BLAZE_RULE));
+ .push(new IdeaLogScope());
NewBlazeRuleDialog newBlazeRuleDialog =
new NewBlazeRuleDialog(context, project, virtualFile);
newBlazeRuleDialog.show();
diff --git a/base/src/com/google/idea/blaze/base/ide/NewBlazeRuleDialog.java b/base/src/com/google/idea/blaze/base/ide/NewBlazeRuleDialog.java
index ef114da..d77e25c 100644
--- a/base/src/com/google/idea/blaze/base/ide/NewBlazeRuleDialog.java
+++ b/base/src/com/google/idea/blaze/base/ide/NewBlazeRuleDialog.java
@@ -37,7 +37,7 @@
import javax.swing.JPanel;
class NewBlazeRuleDialog extends DialogWrapper {
- private static final Logger LOG = Logger.getInstance(NewBlazeRuleDialog.class);
+ private static final Logger logger = Logger.getInstance(NewBlazeRuleDialog.class);
private static final int UI_INDENT = 0;
private static final int TEXT_BOX_WIDTH = 40;
diff --git a/base/src/com/google/idea/blaze/base/ideinfo/IntellijPluginDeployInfo.java b/base/src/com/google/idea/blaze/base/ideinfo/IntellijPluginDeployInfo.java
new file mode 100644
index 0000000..426fa6b
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/ideinfo/IntellijPluginDeployInfo.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2017 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.ideinfo;
+
+import com.google.common.collect.ImmutableList;
+import java.io.Serializable;
+import javax.annotation.concurrent.Immutable;
+
+/** A special rule representing the files that need to be deployed for an IntelliJ plugin */
+@Immutable
+public class IntellijPluginDeployInfo implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ /** A single file for deployment */
+ @Immutable
+ public static class IntellijPluginDeployFile implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ /** The source file to deploy. */
+ public final ArtifactLocation src;
+ /** A plugins-directory relative location to deploy to. */
+ public final String deployLocation;
+
+ public IntellijPluginDeployFile(ArtifactLocation src, String deployLocation) {
+ this.src = src;
+ this.deployLocation = deployLocation;
+ }
+ }
+
+ public final ImmutableList<IntellijPluginDeployFile> deployFiles;
+
+ public IntellijPluginDeployInfo(ImmutableList<IntellijPluginDeployFile> deployFiles) {
+ this.deployFiles = deployFiles;
+ }
+}
diff --git a/base/src/com/google/idea/blaze/base/ideinfo/JavaIdeInfo.java b/base/src/com/google/idea/blaze/base/ideinfo/JavaIdeInfo.java
index bf9e527..174db51 100644
--- a/base/src/com/google/idea/blaze/base/ideinfo/JavaIdeInfo.java
+++ b/base/src/com/google/idea/blaze/base/ideinfo/JavaIdeInfo.java
@@ -46,17 +46,22 @@
/** File containing dependencies. */
@Nullable public final ArtifactLocation jdepsFile;
+ /** main_class attribute value for java_binary targets */
+ @Nullable public String javaBinaryMainClass;
+
public JavaIdeInfo(
Collection<LibraryArtifact> jars,
Collection<LibraryArtifact> generatedJars,
@Nullable LibraryArtifact filteredGenJar,
@Nullable ArtifactLocation packageManifest,
- @Nullable ArtifactLocation jdepsFile) {
+ @Nullable ArtifactLocation jdepsFile,
+ @Nullable String javaBinaryMainClass) {
this.jars = jars;
this.generatedJars = generatedJars;
this.packageManifest = packageManifest;
this.jdepsFile = jdepsFile;
this.filteredGenJar = filteredGenJar;
+ this.javaBinaryMainClass = javaBinaryMainClass;
}
public static Builder builder() {
@@ -67,7 +72,8 @@
public static class Builder {
ImmutableList.Builder<LibraryArtifact> jars = ImmutableList.builder();
ImmutableList.Builder<LibraryArtifact> generatedJars = ImmutableList.builder();
- LibraryArtifact filteredGenJar;
+ @Nullable LibraryArtifact filteredGenJar;
+ @Nullable String mainClass;
public Builder addJar(LibraryArtifact.Builder jar) {
jars.add(jar.build());
@@ -84,8 +90,14 @@
return this;
}
+ public Builder setMainClass(@Nullable String mainClass) {
+ this.mainClass = mainClass;
+ return this;
+ }
+
public JavaIdeInfo build() {
- return new JavaIdeInfo(jars.build(), generatedJars.build(), filteredGenJar, null, null);
+ return new JavaIdeInfo(
+ jars.build(), generatedJars.build(), filteredGenJar, null, null, mainClass);
}
}
}
diff --git a/base/src/com/google/idea/blaze/base/ideinfo/TargetIdeInfo.java b/base/src/com/google/idea/blaze/base/ideinfo/TargetIdeInfo.java
index 6ea3685..34f2bc2 100644
--- a/base/src/com/google/idea/blaze/base/ideinfo/TargetIdeInfo.java
+++ b/base/src/com/google/idea/blaze/base/ideinfo/TargetIdeInfo.java
@@ -27,7 +27,7 @@
/** Simple implementation of TargetIdeInfo. */
public final class TargetIdeInfo implements Serializable {
- private static final long serialVersionUID = 14L;
+ private static final long serialVersionUID = 15L;
public final TargetKey key;
public final Kind kind;
@@ -43,6 +43,7 @@
@Nullable public final TestIdeInfo testIdeInfo;
@Nullable public final ProtoLibraryLegacyInfo protoLibraryLegacyInfo;
@Nullable public final JavaToolchainIdeInfo javaToolchainIdeInfo;
+ @Nullable public final IntellijPluginDeployInfo intellijPluginDeployInfo;
public TargetIdeInfo(
TargetKey key,
@@ -58,7 +59,8 @@
@Nullable PyIdeInfo pyIdeInfo,
@Nullable TestIdeInfo testIdeInfo,
@Nullable ProtoLibraryLegacyInfo protoLibraryLegacyInfo,
- @Nullable JavaToolchainIdeInfo javaToolchainIdeInfo) {
+ @Nullable JavaToolchainIdeInfo javaToolchainIdeInfo,
+ @Nullable IntellijPluginDeployInfo intellijPluginDeployInfo) {
this.key = key;
this.kind = kind;
this.buildFile = buildFile;
@@ -73,6 +75,7 @@
this.testIdeInfo = testIdeInfo;
this.protoLibraryLegacyInfo = protoLibraryLegacyInfo;
this.javaToolchainIdeInfo = javaToolchainIdeInfo;
+ this.intellijPluginDeployInfo = intellijPluginDeployInfo;
}
@Override
@@ -243,7 +246,8 @@
pyIdeInfo,
testIdeInfo,
protoLibraryLegacyInfo,
- javaToolchainIdeInfo);
+ javaToolchainIdeInfo,
+ null);
}
}
}
diff --git a/base/src/com/google/idea/blaze/base/io/FileAttributeProvider.java b/base/src/com/google/idea/blaze/base/io/FileAttributeProvider.java
index aeb0f24..64c83fb 100644
--- a/base/src/com/google/idea/blaze/base/io/FileAttributeProvider.java
+++ b/base/src/com/google/idea/blaze/base/io/FileAttributeProvider.java
@@ -17,6 +17,7 @@
import com.intellij.openapi.components.ServiceManager;
import java.io.File;
+import javax.annotation.Nullable;
/** Simple file system checks (existence, isDirectory) */
public class FileAttributeProvider {
@@ -45,6 +46,7 @@
return file.length();
}
+ @Nullable
public File[] listFiles(File file) {
return file.listFiles();
}
diff --git a/base/src/com/google/idea/blaze/base/io/VirtualFileSystemProvider.java b/base/src/com/google/idea/blaze/base/io/VirtualFileSystemProvider.java
new file mode 100644
index 0000000..182204f
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/io/VirtualFileSystemProvider.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2017 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.io;
+
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import java.io.File;
+import javax.annotation.Nullable;
+
+/** Provides indirection for virtual file systems. */
+public interface VirtualFileSystemProvider {
+
+ static VirtualFileSystemProvider getInstance() {
+ return ServiceManager.getService(VirtualFileSystemProvider.class);
+ }
+
+ LocalFileSystem getSystem();
+
+ /**
+ * Like {@link com.intellij.openapi.vfs.VfsUtil#findFileByIoFile} with refresh set to true.
+ *
+ * <p>Note: there are restrictions on the calling context. See comments for
+ * refreshAndFindFileByIoFile.
+ */
+ @Nullable
+ static VirtualFile findFileByIoFileRefreshIfNeeded(File file) {
+ LocalFileSystem fileSystem = getInstance().getSystem();
+ VirtualFile virtualFile = fileSystem.findFileByIoFile(file);
+ if (virtualFile == null || !virtualFile.isValid()) {
+ virtualFile = fileSystem.refreshAndFindFileByIoFile(file);
+ }
+ return virtualFile;
+ }
+}
diff --git a/base/src/com/google/idea/blaze/base/io/VirtualFileSystemProviderImpl.java b/base/src/com/google/idea/blaze/base/io/VirtualFileSystemProviderImpl.java
new file mode 100644
index 0000000..86a3495
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/io/VirtualFileSystemProviderImpl.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2017 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.io;
+
+import com.intellij.openapi.vfs.LocalFileSystem;
+
+/** Default implementation of {@link VirtualFileSystemProvider}. */
+public class VirtualFileSystemProviderImpl implements VirtualFileSystemProvider {
+
+ @Override
+ public LocalFileSystem getSystem() {
+ return LocalFileSystem.getInstance();
+ }
+}
diff --git a/base/src/com/google/idea/blaze/base/lang/buildfile/actions/BuildFileModifierImpl.java b/base/src/com/google/idea/blaze/base/lang/buildfile/actions/BuildFileModifierImpl.java
index a4fe33a..dd9178c 100644
--- a/base/src/com/google/idea/blaze/base/lang/buildfile/actions/BuildFileModifierImpl.java
+++ b/base/src/com/google/idea/blaze/base/lang/buildfile/actions/BuildFileModifierImpl.java
@@ -37,7 +37,7 @@
/** Implementation of BuildFileModifier. Modifies the PSI tree directly. */
public class BuildFileModifierImpl implements BuildFileModifier {
- private static final Logger LOG = Logger.getInstance(BuildFileModifierImpl.class);
+ private static final Logger logger = Logger.getInstance(BuildFileModifierImpl.class);
@Override
public boolean addRule(Project project, BlazeContext context, Label newRule, Kind ruleKind) {
@@ -53,7 +53,7 @@
LocalFileSystem.getInstance().refreshIoFiles(ImmutableList.of(file));
BuildFile buildFile = manager.resolveBlazePackage(newRule.blazePackage());
if (buildFile == null) {
- LOG.error("No BUILD file found at location: " + newRule.blazePackage());
+ logger.error("No BUILD file found at location: " + newRule.blazePackage());
return false;
}
buildFile.add(createRule(project, ruleKind, newRule.targetName().toString()));
diff --git a/base/src/com/google/idea/blaze/base/lang/buildfile/findusages/BuildUsageTypeProvider.java b/base/src/com/google/idea/blaze/base/lang/buildfile/findusages/BuildUsageTypeProvider.java
new file mode 100644
index 0000000..6f86a21
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/lang/buildfile/findusages/BuildUsageTypeProvider.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2017 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.lang.buildfile.findusages;
+
+import com.google.idea.blaze.base.lang.buildfile.psi.BuildElement;
+import com.google.idea.blaze.base.lang.buildfile.psi.GlobExpression;
+import com.google.idea.blaze.base.lang.buildfile.psi.LoadStatement;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.util.PsiTreeUtil;
+import com.intellij.usages.UsageTarget;
+import com.intellij.usages.impl.rules.UsageType;
+import com.intellij.usages.impl.rules.UsageTypeProviderEx;
+import javax.annotation.Nullable;
+import org.jetbrains.annotations.NotNull;
+
+/** Used to provide user-visible string when grouping 'find usages' results by type. */
+public class BuildUsageTypeProvider implements UsageTypeProviderEx {
+ private static final UsageType IN_LOAD = new UsageType("Usage in load statement");
+ private static final UsageType IN_GLOB = new UsageType("Usage in BUILD glob");
+ private static final UsageType GENERIC = new UsageType("Usage in BUILD/Skylark file");
+
+ @Override
+ @Nullable
+ public UsageType getUsageType(PsiElement element) {
+ return getUsageType(element, UsageTarget.EMPTY_ARRAY);
+ }
+
+ @Override
+ @Nullable
+ public UsageType getUsageType(PsiElement element, @NotNull UsageTarget[] targets) {
+ if (!(element instanceof BuildElement)) {
+ return null;
+ }
+ if (PsiTreeUtil.getParentOfType(element, LoadStatement.class) != null) {
+ return IN_LOAD;
+ }
+ if (PsiTreeUtil.getParentOfType(element, GlobExpression.class, false) != null) {
+ return IN_GLOB;
+ }
+ return GENERIC;
+ }
+}
diff --git a/base/src/com/google/idea/blaze/base/lang/buildfile/globbing/UnixGlob.java b/base/src/com/google/idea/blaze/base/lang/buildfile/globbing/UnixGlob.java
index 321908b..b7a44c4 100644
--- a/base/src/com/google/idea/blaze/base/lang/buildfile/globbing/UnixGlob.java
+++ b/base/src/com/google/idea/blaze/base/lang/buildfile/globbing/UnixGlob.java
@@ -33,9 +33,7 @@
import com.google.idea.blaze.base.lang.buildfile.validation.GlobPatternValidator;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.progress.ProgressManager;
-import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
-import com.intellij.openapi.vfs.VirtualFileSystem;
import com.intellij.openapi.vfs.ex.temp.TempFileSystem;
import java.io.File;
import java.io.IOException;
@@ -738,13 +736,6 @@
}
}
- private static VirtualFileSystem getFileSystem() {
- if (ApplicationManager.getApplication().isUnitTestMode()) {
- return TempFileSystem.getInstance();
- }
- return LocalFileSystem.getInstance();
- }
-
@Nullable
private File[] getChildren(File file) {
if (!ApplicationManager.getApplication().isUnitTestMode()) {
diff --git a/base/src/com/google/idea/blaze/base/lang/buildfile/language/semantics/BuiltInNamesProvider.java b/base/src/com/google/idea/blaze/base/lang/buildfile/language/semantics/BuiltInNamesProvider.java
index 7b90bc2..b2b063a 100644
--- a/base/src/com/google/idea/blaze/base/lang/buildfile/language/semantics/BuiltInNamesProvider.java
+++ b/base/src/com/google/idea/blaze/base/lang/buildfile/language/semantics/BuiltInNamesProvider.java
@@ -71,7 +71,7 @@
ImmutableSet.of(
"load",
"package",
- "pacakge_group",
+ "package_group",
"licenses",
"exports_files",
"glob",
diff --git a/base/src/com/google/idea/blaze/base/lang/buildfile/language/semantics/RuleDefinition.java b/base/src/com/google/idea/blaze/base/lang/buildfile/language/semantics/RuleDefinition.java
index aa85f8d..085fdf8 100644
--- a/base/src/com/google/idea/blaze/base/lang/buildfile/language/semantics/RuleDefinition.java
+++ b/base/src/com/google/idea/blaze/base/lang/buildfile/language/semantics/RuleDefinition.java
@@ -24,6 +24,8 @@
/** Simple implementation of RuleDefinition, from build.proto */
public class RuleDefinition implements Serializable {
+ private static final long serialVersionUID = 2L;
+
/**
* In previous versions of blaze/bazel, this wasn't included in the proto. All other documented
* attributes seem to be.
@@ -48,6 +50,7 @@
public final String name;
/** This map is not exhaustive; it only contains documented attributes. */
public final ImmutableMap<String, AttributeDefinition> attributes;
+ public final ImmutableMap<String, AttributeDefinition> mandatoryAttributes;
@Nullable public final String documentation;
@@ -58,6 +61,14 @@
this.name = name;
this.attributes = attributes;
this.documentation = documentation;
+
+ ImmutableMap.Builder<String, AttributeDefinition> builder = ImmutableMap.builder();
+ for (AttributeDefinition attr : attributes.values()) {
+ if (attr.mandatory) {
+ builder.put(attr.name, attr);
+ }
+ }
+ mandatoryAttributes = builder.build();
}
public ImmutableSet<String> getKnownAttributeNames() {
diff --git a/base/src/com/google/idea/blaze/base/lang/buildfile/parser/ExpressionParsing.java b/base/src/com/google/idea/blaze/base/lang/buildfile/parser/ExpressionParsing.java
index 11688ef..f87d429 100644
--- a/base/src/com/google/idea/blaze/base/lang/buildfile/parser/ExpressionParsing.java
+++ b/base/src/com/google/idea/blaze/base/lang/buildfile/parser/ExpressionParsing.java
@@ -86,23 +86,27 @@
}
public void parseExpression(boolean insideParens) {
- // handle lists without parens (e.g. 'a,b,c = 1')
- PsiBuilder.Marker marker = insideParens ? null : builder.mark();
+ PsiBuilder.Marker tupleMarker = builder.mark();
parseNonTupleExpression();
if (currentToken() == TokenKind.COMMA) {
- parseExpressionList();
- if (marker != null) {
- marker.done(BuildElementTypes.LIST_LITERAL);
- }
- } else if (marker != null) {
- marker.drop();
+ parseExpressionList(insideParens);
+ tupleMarker.done(BuildElementTypes.TUPLE_EXPRESSION);
+ } else {
+ tupleMarker.drop();
}
}
- // expr_list ::= ( ',' expr )* ','?
- private void parseExpressionList() {
+ /**
+ * Parses a comma-separated list of expressions. It assumes that the first expression was already
+ * parsed, so it starts with a comma. It is used to parse tuples and list elements.<br>
+ * expr_list ::= ( ',' expr )* ','?
+ */
+ private void parseExpressionList(boolean trailingColonAllowed) {
while (matches(TokenKind.COMMA)) {
if (atAnyOfTokens(EXPR_LIST_TERMINATOR_SET)) {
+ if (!trailingColonAllowed) {
+ builder.error("Trailing commas are allowed only in parenthesized tuples.");
+ }
break;
}
parseNonTupleExpression();
@@ -270,12 +274,12 @@
marker = builder.mark();
builder.advanceLexer();
if (matches(TokenKind.RPAREN)) {
- marker.done(BuildElementTypes.LIST_LITERAL);
+ marker.done(BuildElementTypes.TUPLE_EXPRESSION);
return;
}
parseExpression(true);
expect(TokenKind.RPAREN, true);
- marker.done(BuildElementTypes.LIST_LITERAL);
+ marker.done(BuildElementTypes.PARENTHESIZED_EXPRESSION);
return;
case MINUS:
marker = builder.mark();
@@ -405,7 +409,7 @@
marker.done(BuildElementTypes.LIST_COMPREHENSION_EXPR);
return;
case COMMA:
- parseExpressionList();
+ parseExpressionList(true);
if (!matches(TokenKind.RBRACKET)) {
builder.error("expected 'for' or ']'");
syncPast(LIST_TERMINATOR_SET);
@@ -475,7 +479,7 @@
expect(TokenKind.IN);
parseNonTupleExpression(0);
} else if (matches(TokenKind.IF)) {
- parseExpression(true);
+ parseExpression(false);
} else if (matches(closingBracket)) {
return;
} else {
diff --git a/base/src/com/google/idea/blaze/base/lang/buildfile/psi/BuildElementTypes.java b/base/src/com/google/idea/blaze/base/lang/buildfile/psi/BuildElementTypes.java
index 5f33a6d..7c4d142 100644
--- a/base/src/com/google/idea/blaze/base/lang/buildfile/psi/BuildElementTypes.java
+++ b/base/src/com/google/idea/blaze/base/lang/buildfile/psi/BuildElementTypes.java
@@ -79,6 +79,9 @@
BuildElementType LIST_COMPREHENSION_EXPR =
new BuildElementType("list_comp", ListComprehensionExpression.class);
BuildElementType LOADED_SYMBOL = new BuildElementType("loaded_symbol", LoadedSymbol.class);
+ BuildElementType PARENTHESIZED_EXPRESSION =
+ new BuildElementType("parens", ParenthesizedExpression.class);
+ BuildElementType TUPLE_EXPRESSION = new BuildElementType("tuple", TupleExpression.class);
TokenSet EXPRESSIONS =
TokenSet.create(
@@ -90,6 +93,8 @@
STRING_LITERAL,
INTEGER_LITERAL,
LIST_LITERAL,
+ PARENTHESIZED_EXPRESSION,
+ TUPLE_EXPRESSION,
REFERENCE_EXPRESSION,
TARGET_EXPRESSION,
LIST_COMPREHENSION_EXPR,
diff --git a/base/src/com/google/idea/blaze/base/lang/buildfile/psi/BuildElementVisitor.java b/base/src/com/google/idea/blaze/base/lang/buildfile/psi/BuildElementVisitor.java
index 68e9455..804a5dc 100644
--- a/base/src/com/google/idea/blaze/base/lang/buildfile/psi/BuildElementVisitor.java
+++ b/base/src/com/google/idea/blaze/base/lang/buildfile/psi/BuildElementVisitor.java
@@ -143,4 +143,12 @@
public void visitLoadedSymbol(LoadedSymbol node) {
visitElement(node);
}
+
+ public void visitParenthesizedExpression(ParenthesizedExpression node) {
+ visitElement(node);
+ }
+
+ public void visitTupleExpression(TupleExpression node) {
+ visitElement(node);
+ }
}
diff --git a/base/src/com/google/idea/blaze/base/lang/buildfile/psi/ListLiteral.java b/base/src/com/google/idea/blaze/base/lang/buildfile/psi/ListLiteral.java
index 65fe842..971c824 100644
--- a/base/src/com/google/idea/blaze/base/lang/buildfile/psi/ListLiteral.java
+++ b/base/src/com/google/idea/blaze/base/lang/buildfile/psi/ListLiteral.java
@@ -18,7 +18,7 @@
import com.google.common.collect.ImmutableList;
import com.intellij.lang.ASTNode;
-/** PSI element for list and tuple literals */
+/** PSI element for list literals */
public class ListLiteral extends BuildListType<Expression> implements LiteralExpression {
public ListLiteral(ASTNode astNode) {
@@ -51,6 +51,6 @@
@Override
public ImmutableList<Character> getEndChars() {
- return ImmutableList.of(')', ']');
+ return ImmutableList.of(']');
}
}
diff --git a/base/src/com/google/idea/blaze/base/lang/buildfile/psi/ParenthesizedExpression.java b/base/src/com/google/idea/blaze/base/lang/buildfile/psi/ParenthesizedExpression.java
new file mode 100644
index 0000000..0852db9
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/lang/buildfile/psi/ParenthesizedExpression.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2017 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.lang.buildfile.psi;
+
+import com.intellij.lang.ASTNode;
+import com.intellij.psi.util.PsiTreeUtil;
+import javax.annotation.Nullable;
+
+/** PSI element for a parenthesized expression. */
+public class ParenthesizedExpression extends BuildElementImpl implements Expression {
+
+ public ParenthesizedExpression(ASTNode astNode) {
+ super(astNode);
+ }
+
+ @Override
+ protected void acceptVisitor(BuildElementVisitor visitor) {
+ visitor.visitParenthesizedExpression(this);
+ }
+
+ @Nullable
+ public Expression getContainedExpression() {
+ Expression expr = this;
+ while (expr instanceof ParenthesizedExpression) {
+ expr = PsiTreeUtil.getChildOfType(this, Expression.class);
+ }
+ return expr;
+ }
+}
diff --git a/base/src/com/google/idea/blaze/base/lang/buildfile/psi/StringLiteral.java b/base/src/com/google/idea/blaze/base/lang/buildfile/psi/StringLiteral.java
index 009dda6..027107e 100644
--- a/base/src/com/google/idea/blaze/base/lang/buildfile/psi/StringLiteral.java
+++ b/base/src/com/google/idea/blaze/base/lang/buildfile/psi/StringLiteral.java
@@ -15,11 +15,15 @@
*/
package com.google.idea.blaze.base.lang.buildfile.psi;
+import com.google.idea.blaze.base.lang.buildfile.psi.Argument.Keyword;
+import com.google.idea.blaze.base.lang.buildfile.psi.util.PsiUtils;
+import com.google.idea.blaze.base.lang.buildfile.references.AttributeSpecificStringLiteralReferenceProvider;
import com.google.idea.blaze.base.lang.buildfile.references.LabelReference;
import com.google.idea.blaze.base.lang.buildfile.references.LoadedSymbolReference;
import com.google.idea.blaze.base.lang.buildfile.references.PackageReferenceFragment;
import com.google.idea.blaze.base.lang.buildfile.references.QuoteType;
import com.intellij.lang.ASTNode;
+import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiReference;
import javax.annotation.Nullable;
@@ -32,21 +36,30 @@
* (in which case escaped characters, raw strings, etc. are unlikely).
*/
public static String stripQuotes(String string) {
+ return textRangeInElement(string).substring(string);
+ }
+
+ /** The range of text characters, excluding leading and trailing quotes. */
+ public static TextRange textRangeInElement(String string) {
// TODO: Handle escaped characters, etc. here?
// (extract logic from BuildLexerBase.addStringLiteral)
if (string.startsWith("\"\"\"")) {
- return string.length() <= 3 ? "" : string.substring(3, endTrimIndex(string, '"', 3));
+ return string.length() <= 3
+ ? TextRange.EMPTY_RANGE
+ : TextRange.create(3, endTrimIndex(string, '"', 3));
}
if (string.startsWith("'''")) {
- return string.length() <= 3 ? "" : string.substring(3, endTrimIndex(string, '\'', 3));
+ return string.length() <= 3
+ ? TextRange.EMPTY_RANGE
+ : TextRange.create(3, endTrimIndex(string, '\'', 3));
}
if (string.startsWith("\"")) {
- return string.substring(1, endTrimIndex(string, '"', 1));
+ return TextRange.create(1, endTrimIndex(string, '"', 1));
}
if (string.startsWith("'")) {
- return string.substring(1, endTrimIndex(string, '\'', 1));
+ return TextRange.create(1, endTrimIndex(string, '\'', 1));
}
- return string;
+ return TextRange.allOf(string);
}
private static int endTrimIndex(String string, char quoteChar, int numberQuoteChars) {
@@ -104,6 +117,15 @@
*/
@Override
public PsiReference[] getReferences() {
+ // first look for attribute-specific references
+ String attributeName = getParentAttributeName();
+ if (attributeName != null) {
+ PsiReference[] refs =
+ AttributeSpecificStringLiteralReferenceProvider.findReferences(attributeName, this);
+ if (refs.length != 0) {
+ return refs;
+ }
+ }
PsiReference primaryReference = getReference();
if (primaryReference instanceof LabelReference) {
return new PsiReference[] {
@@ -134,6 +156,13 @@
return new LabelReference(this, true);
}
+ /** If this string is an attribute value within a BUILD rule, return the attribute type. */
+ @Nullable
+ private String getParentAttributeName() {
+ Keyword parentKeyword = PsiUtils.getParentOfType(this, Keyword.class);
+ return parentKeyword != null ? parentKeyword.getName() : null;
+ }
+
@Nullable
public LoadStatement getLoadStatementParent() {
PsiElement parent = getParent();
diff --git a/base/src/com/google/idea/blaze/base/lang/buildfile/psi/TupleExpression.java b/base/src/com/google/idea/blaze/base/lang/buildfile/psi/TupleExpression.java
new file mode 100644
index 0000000..762bb8a
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/lang/buildfile/psi/TupleExpression.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2017 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.lang.buildfile.psi;
+
+import com.google.common.collect.ImmutableList;
+import com.intellij.lang.ASTNode;
+
+/**
+ * PSI element for tuples inside a parenthesized expression. Also used for tuples without enclosing
+ * parentheses, not supported in Skylark (accompanied by an appropriate error annotation).
+ */
+public class TupleExpression extends BuildListType<Expression> implements Expression {
+
+ public TupleExpression(ASTNode astNode) {
+ super(astNode, Expression.class);
+ }
+
+ @Override
+ protected void acceptVisitor(BuildElementVisitor visitor) {
+ visitor.visitTupleExpression(this);
+ }
+
+ @Override
+ public String getPresentableText() {
+ return "tuple";
+ }
+
+ public Expression[] getChildExpressions() {
+ return findChildrenByClass(Expression.class);
+ }
+
+ @Override
+ public Expression[] getElements() {
+ return getChildExpressions();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return findChildByClass(Expression.class) != null;
+ }
+
+ @Override
+ public ImmutableList<Character> getEndChars() {
+ return ImmutableList.of(')');
+ }
+}
diff --git a/base/src/com/google/idea/blaze/base/lang/buildfile/psi/util/PsiUtils.java b/base/src/com/google/idea/blaze/base/lang/buildfile/psi/util/PsiUtils.java
index 229220b..30d8c5e 100644
--- a/base/src/com/google/idea/blaze/base/lang/buildfile/psi/util/PsiUtils.java
+++ b/base/src/com/google/idea/blaze/base/lang/buildfile/psi/util/PsiUtils.java
@@ -18,6 +18,7 @@
import com.google.common.collect.Lists;
import com.google.idea.blaze.base.lang.buildfile.psi.AssignmentStatement;
import com.google.idea.blaze.base.lang.buildfile.psi.Expression;
+import com.google.idea.blaze.base.lang.buildfile.psi.ParenthesizedExpression;
import com.google.idea.blaze.base.lang.buildfile.psi.ReferenceExpression;
import com.google.idea.blaze.base.lang.buildfile.psi.TargetExpression;
import com.intellij.lang.ASTNode;
@@ -154,18 +155,29 @@
/**
* For ReferenceExpressions, follows the chain of references until it hits a
- * non-ReferenceExpression. For other types, returns the input expression.
+ * non-ReferenceExpression.<br>
+ * Unwraps ParenthesizedExpression.<br>
+ * For other types, returns the input expression.
*/
public static PsiElement getReferencedTarget(Expression expr) {
PsiElement element = expr;
- while (element instanceof ReferenceExpression) {
- PsiElement referencedElement = ((ReferenceExpression) element).getReferencedElement();
- if (referencedElement == null) {
+ while (true) {
+ PsiElement unwrapped = unwrap(element);
+ if (unwrapped == null || unwrapped == element) {
return element;
}
- element = referencedElement;
+ element = unwrapped;
}
- return element;
+ }
+
+ private static PsiElement unwrap(PsiElement expr) {
+ if (expr instanceof ParenthesizedExpression) {
+ return ((ParenthesizedExpression) expr).getContainedExpression();
+ }
+ if (expr instanceof ReferenceExpression) {
+ return ((ReferenceExpression) expr).getReferencedElement();
+ }
+ return expr;
}
/**
diff --git a/base/src/com/google/idea/blaze/base/lang/buildfile/refactor/StringLiteralElementManipulator.java b/base/src/com/google/idea/blaze/base/lang/buildfile/refactor/StringLiteralElementManipulator.java
new file mode 100644
index 0000000..502d5dd
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/lang/buildfile/refactor/StringLiteralElementManipulator.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2017 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.lang.buildfile.refactor;
+
+import com.google.idea.blaze.base.lang.buildfile.psi.StringLiteral;
+import com.google.idea.blaze.base.lang.buildfile.psi.util.PsiUtils;
+import com.intellij.lang.ASTNode;
+import com.intellij.openapi.util.TextRange;
+import com.intellij.psi.AbstractElementManipulator;
+import com.intellij.util.IncorrectOperationException;
+import org.jetbrains.annotations.NotNull;
+
+/** Allows direct text substitution in {@link StringLiteral}'s by IntelliJ's refactoring tools. */
+public class StringLiteralElementManipulator extends AbstractElementManipulator<StringLiteral> {
+
+ @Override
+ public StringLiteral handleContentChange(
+ StringLiteral element, TextRange range, String newContent)
+ throws IncorrectOperationException {
+ ASTNode node = element.getNode();
+ node.replaceChild(
+ node.getFirstChildNode(), PsiUtils.createNewLabel(element.getProject(), newContent));
+ return element;
+ }
+
+ @NotNull
+ @Override
+ public TextRange getRangeInElement(@NotNull StringLiteral element) {
+ return StringLiteral.textRangeInElement(element.getText());
+ }
+}
diff --git a/base/src/com/google/idea/blaze/base/lang/buildfile/references/AttributeSpecificStringLiteralReferenceProvider.java b/base/src/com/google/idea/blaze/base/lang/buildfile/references/AttributeSpecificStringLiteralReferenceProvider.java
new file mode 100644
index 0000000..a34c12d
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/lang/buildfile/references/AttributeSpecificStringLiteralReferenceProvider.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2017 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.lang.buildfile.references;
+
+import com.google.idea.blaze.base.lang.buildfile.psi.StringLiteral;
+import com.intellij.openapi.extensions.ExtensionPointName;
+import com.intellij.psi.PsiReference;
+
+/** Non-default reference provider for {@link StringLiteral} values for a given attribute type. */
+public interface AttributeSpecificStringLiteralReferenceProvider {
+
+ ExtensionPointName<AttributeSpecificStringLiteralReferenceProvider> EP_NAME =
+ ExtensionPointName.create(
+ "com.google.idea.blaze.AttributeSpecificStringLiteralReferenceProvider");
+
+ /** Find a reference type specific to values of this attribute. */
+ static PsiReference[] findReferences(String attributeName, StringLiteral literal) {
+ for (AttributeSpecificStringLiteralReferenceProvider provider : EP_NAME.getExtensions()) {
+ PsiReference[] refs = provider.getReferences(attributeName, literal);
+ if (refs.length != 0) {
+ return refs;
+ }
+ }
+ return PsiReference.EMPTY_ARRAY;
+ }
+
+ /** Find references specific to this attribute. */
+ PsiReference[] getReferences(String attributeName, StringLiteral literal);
+}
diff --git a/base/src/com/google/idea/blaze/base/lang/buildfile/references/BuildReferenceManager.java b/base/src/com/google/idea/blaze/base/lang/buildfile/references/BuildReferenceManager.java
index 6635ade..9b72b1d 100644
--- a/base/src/com/google/idea/blaze/base/lang/buildfile/references/BuildReferenceManager.java
+++ b/base/src/com/google/idea/blaze/base/lang/buildfile/references/BuildReferenceManager.java
@@ -17,6 +17,7 @@
import com.google.common.collect.Lists;
import com.google.idea.blaze.base.io.FileAttributeProvider;
+import com.google.idea.blaze.base.io.VirtualFileSystemProvider;
import com.google.idea.blaze.base.lang.buildfile.completion.BuildLookupElement;
import com.google.idea.blaze.base.lang.buildfile.psi.BuildFile;
import com.google.idea.blaze.base.lang.buildfile.psi.FuncallExpression;
@@ -26,16 +27,12 @@
import com.google.idea.blaze.base.settings.Blaze;
import com.google.idea.blaze.base.sync.workspace.WorkspacePathResolver;
import com.google.idea.blaze.base.sync.workspace.WorkspacePathResolverProvider;
-import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
-import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
-import com.intellij.openapi.vfs.VirtualFileSystem;
-import com.intellij.openapi.vfs.ex.temp.TempFileSystem;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiFileSystemItem;
@@ -100,7 +97,8 @@
@Nullable
public PsiFileSystemItem resolveFile(File file) {
- VirtualFile vf = getFileSystem().findFileByPath(file.getPath());
+ VirtualFile vf =
+ VirtualFileSystemProvider.getInstance().getSystem().findFileByPath(file.getPath());
if (vf == null) {
return null;
}
@@ -152,7 +150,8 @@
if (file == null || !provider.isDirectory(file)) {
return BuildLookupElement.EMPTY_ARRAY;
}
- VirtualFile vf = getFileSystem().findFileByPath(file.getPath());
+ VirtualFile vf =
+ VirtualFileSystemProvider.getInstance().getSystem().findFileByPath(file.getPath());
if (vf == null || !vf.isDirectory()) {
return BuildLookupElement.EMPTY_ARRAY;
}
@@ -212,7 +211,10 @@
if (packageDirectory == null || !provider.isDirectory(packageDirectory)) {
return null;
}
- VirtualFile vf = getFileSystem().findFileByPath(packageDirectory.getPath());
+ VirtualFile vf =
+ VirtualFileSystemProvider.getInstance()
+ .getSystem()
+ .findFileByPath(packageDirectory.getPath());
if (vf == null) {
return null;
}
@@ -248,11 +250,4 @@
WorkspacePathResolver pathResolver = getWorkspacePathResolver();
return pathResolver != null ? pathResolver.getWorkspacePath(new File(absolutePath)) : null;
}
-
- private static VirtualFileSystem getFileSystem() {
- if (ApplicationManager.getApplication().isUnitTestMode()) {
- return TempFileSystem.getInstance();
- }
- return LocalFileSystem.getInstance();
- }
}
diff --git a/base/src/com/google/idea/blaze/base/lang/buildfile/references/GlobReference.java b/base/src/com/google/idea/blaze/base/lang/buildfile/references/GlobReference.java
index c880fb8..c3ddb46 100644
--- a/base/src/com/google/idea/blaze/base/lang/buildfile/references/GlobReference.java
+++ b/base/src/com/google/idea/blaze/base/lang/buildfile/references/GlobReference.java
@@ -41,7 +41,7 @@
/** References from a glob to a list of files contained in the same blaze package. */
public class GlobReference extends PsiPolyVariantCachingReference {
- private static final Logger LOG = Logger.getInstance(GlobReference.class);
+ private static final Logger logger = Logger.getInstance(GlobReference.class);
private final GlobExpression element;
@@ -203,7 +203,7 @@
try {
return range.substring(text);
} catch (StringIndexOutOfBoundsException e) {
- LOG.error(
+ logger.error(
"Wrong range in reference " + this + ": " + range + ". Reference text: '" + text + "'",
e);
return text;
diff --git a/base/src/com/google/idea/blaze/base/lang/buildfile/references/LabelUtils.java b/base/src/com/google/idea/blaze/base/lang/buildfile/references/LabelUtils.java
index 3ac8a5f..db58991 100644
--- a/base/src/com/google/idea/blaze/base/lang/buildfile/references/LabelUtils.java
+++ b/base/src/com/google/idea/blaze/base/lang/buildfile/references/LabelUtils.java
@@ -15,7 +15,9 @@
*/
package com.google.idea.blaze.base.lang.buildfile.references;
+import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Lists;
+import com.google.common.collect.Multimap;
import com.google.idea.blaze.base.lang.buildfile.psi.BuildFile;
import com.google.idea.blaze.base.lang.buildfile.search.BlazePackage;
import com.google.idea.blaze.base.model.primitives.Label;
@@ -23,6 +25,7 @@
import com.google.idea.blaze.base.model.primitives.WorkspacePath;
import com.intellij.codeInsight.completion.CompletionUtilCore;
import com.intellij.util.PathUtil;
+import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nullable;
@@ -52,6 +55,14 @@
return null;
}
WorkspacePath packagePath = blazePackage.buildFile.getPackageWorkspacePath();
+ return createLabelFromRuleName(packagePath, ruleName);
+ }
+
+ public static Label createLabelFromRuleName(
+ @Nullable WorkspacePath packagePath, @Nullable String ruleName) {
+ if (ruleName == null) {
+ return null;
+ }
TargetName name = TargetName.createIfValid(ruleName);
if (packagePath == null || name == null) {
return null;
@@ -160,6 +171,36 @@
}
/**
+ * Return a map from a base label string -> variants of the label string that share the common
+ * base.
+ */
+ public static Multimap<String, String> getAllValidLabelStringsPartitioned(
+ Label label, boolean includePackageLocalLabels) {
+ Multimap<String, String> stringToVariant = ArrayListMultimap.create();
+ String fullLabelString = label.toString();
+ List<String> fullVariants = new ArrayList<>();
+ fullVariants.add(fullLabelString);
+ String packagePath = label.blazePackage().relativePath();
+ String ruleName = label.targetName().toString();
+ if (!packagePath.isEmpty()) {
+ if (PathUtil.getFileName(packagePath).equals(ruleName)) {
+ String implicitRuleName = "//" + packagePath;
+ fullVariants.add(implicitRuleName);
+ fullLabelString = implicitRuleName;
+ }
+ }
+ stringToVariant.putAll(fullLabelString, fullVariants);
+ if (!includePackageLocalLabels) {
+ return stringToVariant;
+ }
+ List<String> localVariants = new ArrayList<>();
+ localVariants.add(":" + ruleName);
+ localVariants.add(ruleName);
+ stringToVariant.putAll(ruleName, localVariants);
+ return stringToVariant;
+ }
+
+ /**
* IntelliJ inserts an identifier string at the caret position during code completion.<br>
* We're only interested in the portion of the string before the caret, so trim the rest.
*/
diff --git a/base/src/com/google/idea/blaze/base/lang/buildfile/search/ExcludeBuildFilesScope.java b/base/src/com/google/idea/blaze/base/lang/buildfile/search/ExcludeBuildFilesScope.java
index 3fa294b..66fb03c 100644
--- a/base/src/com/google/idea/blaze/base/lang/buildfile/search/ExcludeBuildFilesScope.java
+++ b/base/src/com/google/idea/blaze/base/lang/buildfile/search/ExcludeBuildFilesScope.java
@@ -28,11 +28,12 @@
* <br>
* BUILD file / BUILD package references are handled by a separate reference searcher.
*
- * <p>This is a hack, but greatly improves efficiency. The reasoning behind this: - BUILD files have
- * very strict file reference patterns, and very narrow direct reference scopes (a package can't
- * directly reference files in another package). - IJ *constantly* performs global searches on
- * strings when manipulating files (e.g. searching for file uses for highlighting, rename, move
- * operations). This causes us to re-parse every BUILD file in the project, multiple times.
+ * <p>This is a hack, but greatly improves efficiency. The reasoning behind this:
+ * <li>BUILD files have very strict file reference patterns, and very narrow direct reference scopes
+ * (a package can't directly reference files in another package).
+ * <li>IJ *constantly* performs global searches on strings when manipulating files (e.g. searching
+ * for file uses for highlighting, rename, move operations). This causes us to re-parse every
+ * BUILD file in the project, multiple times.
*/
public class ExcludeBuildFilesScope extends UseScopeOptimizer {
diff --git a/base/src/com/google/idea/blaze/base/lang/buildfile/sync/BuildLangSyncPlugin.java b/base/src/com/google/idea/blaze/base/lang/buildfile/sync/BuildLangSyncPlugin.java
index 561cd1a..0279a2f 100644
--- a/base/src/com/google/idea/blaze/base/lang/buildfile/sync/BuildLangSyncPlugin.java
+++ b/base/src/com/google/idea/blaze/base/lang/buildfile/sync/BuildLangSyncPlugin.java
@@ -44,7 +44,7 @@
/** Updates the language specification during the blaze sync process */
public class BuildLangSyncPlugin extends BlazeSyncPlugin.Adapter {
- private static final Logger LOG = Logger.getInstance(BuildLangSyncPlugin.class);
+ private static final Logger logger = Logger.getInstance(BuildLangSyncPlugin.class);
@Override
public void updateSyncState(
@@ -115,11 +115,11 @@
return null;
} catch (ExecutionException | InvalidProtocolBufferException | NullPointerException e) {
if (!ApplicationManager.getApplication().isUnitTestMode()) {
- LOG.error(e);
+ logger.error(e);
}
return null;
} catch (Throwable e) {
- LOG.error(e);
+ logger.error(e);
return null;
}
}
diff --git a/base/src/com/google/idea/blaze/base/lang/buildfile/validation/BuildElementValidation.java b/base/src/com/google/idea/blaze/base/lang/buildfile/validation/BuildElementValidation.java
new file mode 100644
index 0000000..138908c
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/lang/buildfile/validation/BuildElementValidation.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2017 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.lang.buildfile.validation;
+
+import com.google.idea.blaze.base.lang.buildfile.psi.DictionaryLiteral;
+import com.google.idea.blaze.base.lang.buildfile.psi.FunctionStatement;
+import com.google.idea.blaze.base.lang.buildfile.psi.GlobExpression;
+import com.google.idea.blaze.base.lang.buildfile.psi.IntegerLiteral;
+import com.google.idea.blaze.base.lang.buildfile.psi.ListLiteral;
+import com.google.idea.blaze.base.lang.buildfile.psi.LiteralExpression;
+import com.google.idea.blaze.base.lang.buildfile.psi.LoadStatement;
+import com.google.idea.blaze.base.lang.buildfile.psi.StringLiteral;
+import com.google.repackaged.devtools.build.lib.query2.proto.proto2api.Build;
+import com.google.repackaged.devtools.build.lib.query2.proto.proto2api.Build.Attribute.Discriminator;
+import com.intellij.psi.PsiElement;
+import java.util.EnumSet;
+
+/**
+ * Provides simple validation of BUILD psi element types (e.g. is the type known to not resolve to a
+ * string).<br>
+ * We err on the side of avoiding spurious errors.
+ */
+class BuildElementValidation {
+
+ private static final EnumSet<Build.Attribute.Discriminator> LIST_TYPES =
+ EnumSet.of(
+ Discriminator.STRING_LIST,
+ Discriminator.LABEL_LIST,
+ Discriminator.OUTPUT_LIST,
+ Discriminator.FILESET_ENTRY_LIST,
+ Discriminator.INTEGER_LIST,
+ Discriminator.LICENSE);
+
+ private static final EnumSet<Build.Attribute.Discriminator> DICT_TYPES =
+ EnumSet.of(Discriminator.LABEL_LIST_DICT, Discriminator.STRING_LIST_DICT);
+
+ private static final EnumSet<Build.Attribute.Discriminator> STRING_TYPES =
+ EnumSet.of(
+ Discriminator.STRING,
+ Discriminator.LABEL,
+ Discriminator.OUTPUT,
+ Discriminator.BOOLEAN,
+ Discriminator.TRISTATE);
+
+ private static final EnumSet<Build.Attribute.Discriminator> INTEGER_TYPES =
+ EnumSet.of(Discriminator.INTEGER, Discriminator.BOOLEAN, Discriminator.TRISTATE);
+
+ /** Returns false iff we know with certainty that the element cannot resolve to the given type. */
+ public static boolean possiblyValidType(PsiElement element, Build.Attribute.Discriminator type) {
+ if (type == Discriminator.UNKNOWN) {
+ return true;
+ }
+ if (element instanceof ListLiteral || element instanceof GlobExpression) {
+ return LIST_TYPES.contains(type);
+ }
+ if (element instanceof StringLiteral) {
+ return STRING_TYPES.contains(type);
+ }
+ if (element instanceof DictionaryLiteral) {
+ return DICT_TYPES.contains(type);
+ }
+ if (element instanceof IntegerLiteral) {
+ return INTEGER_TYPES.contains(type);
+ }
+ return true;
+ }
+
+ /** Returns false iff we know with certainty that the element cannot resolve to a list literal. */
+ public static boolean possiblyValidListLiteral(PsiElement element) {
+ if (element instanceof ListLiteral || element instanceof GlobExpression) {
+ return true; // these evaluate directly to list literals
+ }
+ if (element instanceof LiteralExpression) {
+ return false; // all other literals cannot evaluate to a ListLiteral
+ }
+ if (element instanceof LoadStatement || element instanceof FunctionStatement) {
+ return false;
+ }
+ // everything else treated as possibly evaluating to a list
+ return true;
+ }
+
+ /**
+ * Returns false iff we know with certainty that the element cannot resolve to a string literal.
+ */
+ public static boolean possiblyValidStringLiteral(PsiElement element) {
+ if (element instanceof StringLiteral) {
+ return true;
+ }
+ if (element instanceof LiteralExpression) {
+ return false; // all other literals cannot evaluate to a StringLiteral
+ }
+ if (element instanceof LoadStatement
+ || element instanceof FunctionStatement
+ || element instanceof GlobExpression) {
+ return false;
+ }
+ // everything else treated as possibly evaluating to a string
+ return true;
+ }
+}
diff --git a/base/src/com/google/idea/blaze/base/lang/buildfile/validation/BuiltInRuleAnnotator.java b/base/src/com/google/idea/blaze/base/lang/buildfile/validation/BuiltInRuleAnnotator.java
new file mode 100644
index 0000000..48f8136
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/lang/buildfile/validation/BuiltInRuleAnnotator.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2017 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.lang.buildfile.validation;
+
+import com.google.common.base.Joiner;
+import com.google.idea.blaze.base.lang.buildfile.language.semantics.AttributeDefinition;
+import com.google.idea.blaze.base.lang.buildfile.language.semantics.BuildLanguageSpec;
+import com.google.idea.blaze.base.lang.buildfile.language.semantics.BuildLanguageSpecProvider;
+import com.google.idea.blaze.base.lang.buildfile.language.semantics.RuleDefinition;
+import com.google.idea.blaze.base.lang.buildfile.psi.Argument;
+import com.google.idea.blaze.base.lang.buildfile.psi.Expression;
+import com.google.idea.blaze.base.lang.buildfile.psi.FuncallExpression;
+import com.google.idea.blaze.base.lang.buildfile.psi.util.PsiUtils;
+import com.intellij.psi.PsiElement;
+import java.util.Set;
+import java.util.TreeSet;
+
+/** Validation of known rule types. */
+public class BuiltInRuleAnnotator extends BuildAnnotator {
+
+ @Override
+ public void visitFuncallExpression(FuncallExpression node) {
+ BuildLanguageSpec spec =
+ BuildLanguageSpecProvider.getInstance().getLanguageSpec(node.getProject());
+ if (spec == null) {
+ return;
+ }
+ String ruleName = node.getFunctionName();
+ RuleDefinition rule = spec.getRule(ruleName);
+ if (rule == null) {
+ return;
+ }
+ Set<String> missingAttributes = new TreeSet<>(rule.mandatoryAttributes.keySet());
+ for (Argument arg : node.getArguments()) {
+ String name = arg.getName();
+ if (name == null) {
+ continue;
+ }
+ AttributeDefinition attribute = rule.getAttribute(name);
+ if (attribute == null) {
+ markError(
+ arg, String.format("Unrecognized attribute '%s' for rule type '%s'", name, ruleName));
+ continue;
+ }
+ missingAttributes.remove(name);
+ Expression argValue = arg.getValue();
+ if (argValue == null) {
+ continue;
+ }
+ PsiElement rootElement = PsiUtils.getReferencedTargetValue(argValue);
+ if (!BuildElementValidation.possiblyValidType(rootElement, attribute.type)) {
+ markError(
+ arg,
+ String.format(
+ "Invalid value for attribute '%s'. Expected a value of type '%s'",
+ name, attribute.type));
+ }
+ }
+ if (!missingAttributes.isEmpty()) {
+ markError(
+ node,
+ String.format(
+ "Target missing required attribute(s): %s", Joiner.on(',').join(missingAttributes)));
+ }
+ }
+}
diff --git a/base/src/com/google/idea/blaze/base/lang/buildfile/validation/GlobErrorAnnotator.java b/base/src/com/google/idea/blaze/base/lang/buildfile/validation/GlobErrorAnnotator.java
index 9cf5e7b..7c2c1f3 100644
--- a/base/src/com/google/idea/blaze/base/lang/buildfile/validation/GlobErrorAnnotator.java
+++ b/base/src/com/google/idea/blaze/base/lang/buildfile/validation/GlobErrorAnnotator.java
@@ -17,11 +17,8 @@
import com.google.idea.blaze.base.lang.buildfile.psi.Argument;
import com.google.idea.blaze.base.lang.buildfile.psi.Expression;
-import com.google.idea.blaze.base.lang.buildfile.psi.FunctionStatement;
import com.google.idea.blaze.base.lang.buildfile.psi.GlobExpression;
import com.google.idea.blaze.base.lang.buildfile.psi.ListLiteral;
-import com.google.idea.blaze.base.lang.buildfile.psi.LiteralExpression;
-import com.google.idea.blaze.base.lang.buildfile.psi.LoadStatement;
import com.google.idea.blaze.base.lang.buildfile.psi.ReferenceExpression;
import com.google.idea.blaze.base.lang.buildfile.psi.StringLiteral;
import com.google.idea.blaze.base.lang.buildfile.psi.util.PsiUtils;
@@ -76,7 +73,8 @@
if (rootElement instanceof ListLiteral) {
return validatePatternList(keyword, ((ListLiteral) rootElement).getChildExpressions());
}
- if (rootElement instanceof ReferenceExpression || !possiblyValidListLiteral(rootElement)) {
+ if (rootElement instanceof ReferenceExpression
+ || !BuildElementValidation.possiblyValidListLiteral(rootElement)) {
markError(expr, "Glob parameter '" + keyword + "' must be a list of strings");
return false;
}
@@ -89,7 +87,8 @@
boolean possiblyHasString = false;
for (Expression expr : expressions) {
PsiElement rootElement = PsiUtils.getReferencedTargetValue(expr);
- if (rootElement instanceof ReferenceExpression || !possiblyValidStringLiteral(rootElement)) {
+ if (rootElement instanceof ReferenceExpression
+ || !BuildElementValidation.possiblyValidStringLiteral(rootElement)) {
markError(expr, "Glob parameter '" + keyword + "' must be a list of strings");
} else {
possiblyHasString = true;
@@ -107,38 +106,4 @@
markError(pattern, error);
}
}
-
- /** Returns false iff we know with certainty that the element cannot resolve to a list literal. */
- private static boolean possiblyValidListLiteral(PsiElement element) {
- if (element instanceof ListLiteral || element instanceof GlobExpression) {
- return true; // these evaluate directly to list literals
- }
- if (element instanceof LiteralExpression) {
- return false; // all other literals cannot evaluate to a ListLiteral
- }
- if (element instanceof LoadStatement || element instanceof FunctionStatement) {
- return false;
- }
- // everything else treated as possibly evaluating to a list
- return true;
- }
-
- /**
- * Returns false iff we know with certainty that the element cannot resolve to a string literal.
- */
- private static boolean possiblyValidStringLiteral(PsiElement element) {
- if (element instanceof StringLiteral) {
- return true;
- }
- if (element instanceof LiteralExpression) {
- return false; // all other literals cannot evaluate to a StringLiteral
- }
- if (element instanceof LoadStatement
- || element instanceof FunctionStatement
- || element instanceof GlobExpression) {
- return false;
- }
- // everything else treated as possibly evaluating to a string
- return true;
- }
}
diff --git a/base/src/com/google/idea/blaze/base/lang/buildfile/validation/ErrorAnnotator.java b/base/src/com/google/idea/blaze/base/lang/buildfile/validation/LoadErrorAnnotator.java
similarity index 96%
rename from base/src/com/google/idea/blaze/base/lang/buildfile/validation/ErrorAnnotator.java
rename to base/src/com/google/idea/blaze/base/lang/buildfile/validation/LoadErrorAnnotator.java
index 40e89b2..c8ea27a 100644
--- a/base/src/com/google/idea/blaze/base/lang/buildfile/validation/ErrorAnnotator.java
+++ b/base/src/com/google/idea/blaze/base/lang/buildfile/validation/LoadErrorAnnotator.java
@@ -30,14 +30,14 @@
import javax.annotation.Nullable;
/**
- * Additional error annotations, post parsing.
+ * Error annotations for load statements, post parsing.
*
* <p>This has been turned off because it's unusable. BuildFile is re-parsed *every* time it's
* touched, and is never cached. Until this is fixed, we can't run any annotators touching the file.
*
* <p>One option: try moving all expensive checks to 'visitFile', so they're not run in parallel
*/
-public class ErrorAnnotator extends BuildAnnotator {
+public class LoadErrorAnnotator extends BuildAnnotator {
@Override
public void visitLoadStatement(LoadStatement node) {
@@ -91,7 +91,7 @@
public void visitFuncallExpression(FuncallExpression node) {
FunctionStatement function = (FunctionStatement) node.getReferencedElement();
if (function == null) {
- // likely a built-in rule. We don't yet recognize these.
+ // likely a built-in rule.
return;
}
// check keyword args match function parameters
diff --git a/base/src/com/google/idea/blaze/base/lang/buildfile/views/BuildStructureViewElement.java b/base/src/com/google/idea/blaze/base/lang/buildfile/views/BuildStructureViewElement.java
index 1f71b8c..75768dd 100644
--- a/base/src/com/google/idea/blaze/base/lang/buildfile/views/BuildStructureViewElement.java
+++ b/base/src/com/google/idea/blaze/base/lang/buildfile/views/BuildStructureViewElement.java
@@ -18,7 +18,6 @@
import com.google.common.collect.ImmutableList;
import com.google.idea.blaze.base.lang.buildfile.psi.BuildElement;
import com.google.idea.blaze.base.lang.buildfile.psi.BuildFile;
-import com.google.idea.blaze.base.lang.buildfile.psi.ListLiteral;
import com.intellij.ide.structureView.StructureViewTreeElement;
import com.intellij.ide.structureView.impl.common.PsiTreeElementBase;
import java.util.Collection;
@@ -38,8 +37,6 @@
@NotNull
@Override
public Collection<StructureViewTreeElement> getChildrenBase() {
- if (element instanceof ListLiteral) {}
-
if (!(element instanceof BuildFile)) {
// TODO: show inner build rules in Skylark .bzl extensions
return ImmutableList.of();
diff --git a/base/src/com/google/idea/blaze/base/lang/projectview/references/ProjectViewLabelReference.java b/base/src/com/google/idea/blaze/base/lang/projectview/references/ProjectViewLabelReference.java
index d1748ec..f2f3634 100644
--- a/base/src/com/google/idea/blaze/base/lang/projectview/references/ProjectViewLabelReference.java
+++ b/base/src/com/google/idea/blaze/base/lang/projectview/references/ProjectViewLabelReference.java
@@ -15,6 +15,7 @@
*/
package com.google.idea.blaze.base.lang.projectview.references;
+import com.google.idea.blaze.base.io.VirtualFileSystemProvider;
import com.google.idea.blaze.base.lang.buildfile.completion.BuildLookupElement;
import com.google.idea.blaze.base.lang.buildfile.completion.LabelRuleLookupElement;
import com.google.idea.blaze.base.lang.buildfile.psi.BuildFile;
@@ -28,14 +29,10 @@
import com.google.idea.blaze.base.model.primitives.Label;
import com.google.idea.blaze.base.model.primitives.WorkspacePath;
import com.intellij.lang.ASTNode;
-import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
-import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
-import com.intellij.openapi.vfs.VirtualFileSystem;
-import com.intellij.openapi.vfs.ex.temp.TempFileSystem;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFileSystemItem;
import com.intellij.psi.PsiManager;
@@ -79,7 +76,8 @@
if (file == null) {
return null;
}
- VirtualFile vf = getFileSystem().findFileByPath(file.getPath());
+ VirtualFile vf =
+ VirtualFileSystemProvider.getInstance().getSystem().findFileByPath(file.getPath());
if (vf == null) {
return null;
}
@@ -170,11 +168,4 @@
private Project getProject() {
return myElement.getProject();
}
-
- private static VirtualFileSystem getFileSystem() {
- if (ApplicationManager.getApplication().isUnitTestMode()) {
- return TempFileSystem.getInstance();
- }
- return LocalFileSystem.getInstance();
- }
}
diff --git a/base/src/com/google/idea/blaze/base/metrics/Action.java b/base/src/com/google/idea/blaze/base/metrics/Action.java
deleted file mode 100644
index b7f1ccf..0000000
--- a/base/src/com/google/idea/blaze/base/metrics/Action.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright 2016 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.metrics;
-
-import org.jetbrains.annotations.NonNls;
-import org.jetbrains.annotations.NotNull;
-
-/**
- * An item that can be logged. All actions contain a name that is used as a primary key. The name
- * should be immutable forever to keep the logs sane.
- *
- * <p>The name used by each {@link Action} should be [a-zA-Z0-9]* to keep things robust since
- * various log back ends may have different rules about what may or may not be in a key.
- *
- * <p>Do not use any of the following retired values for enums: INDEX_TOTAL_TIME("index")
- * REBUILD_TOTAL_TIME("rtt") SYNC_SAVE_FILES("ssf") SYNC_COMPUTE_MODULE_DIFF("scmd")
- * RUN_TOTAL_TIME("ttrp") DEBUG_TOTAL_TIME("ttsbp") RUN_TOTAL_TIME_FOR_ANDROID_TEST("ttrpat")
- * DEBUG_TOTAL_TIME_FOR_ANDROID_TEST("ttsbpat") IMPORT_TOTAL_TIME("tip")
- * IDE_BUILD_INFO_RESPONSE("ibi") RULES_EXTRACTION("re") BLAZE_MODULES_CREATION("mvc")
- * INTELLIJ_MODULE_CREATION("imc") SYNC_RESET_PROJECT("srp")
- *
- * <p>
- */
-public enum Action {
- MAKE_PROJECT_TOTAL_TIME("mtt"),
- MAKE_MODULE_TOTAL_TIME("mmtt"),
-
- SYNC_TOTAL_TIME("stt"),
- SYNC_IMPORT_DATA_TIME("sidt"),
- BLAZE_BUILD_DURING_SYNC("bb"),
- BLAZE_BUILD("bld"),
-
- APK_BUILD_AND_INSTALL("apkbi"),
-
- BLAZE_COMMAND_USAGE("ttrpbc"),
-
- OPEN_IN_CODESEARCH("oics"),
- COPY_DEPOT_PATH("cg3p"),
- OPEN_CORRESPONDING_BUILD_FILE("ocbf"),
-
- CREATE_BLAZE_RULE("cbr"),
- CREATE_BLAZE_PACKAGE("cbp"),
-
- SYNC_SDK("ssdk"),
-
- C_RESOLVE_FILE("crf"),
- BLAZE_CLION_TEST_RUN("ctr"),
- BLAZE_CLION_TEST_DEBUG("ctd"),
-
- PYTHON_ACTIVE("pysync");
-
- @NotNull @NonNls private final String name;
-
- Action(@NotNull String name) {
- this.name = name;
- }
-
- @NotNull
- public String getName() {
- return name;
- }
-}
diff --git a/base/src/com/google/idea/blaze/base/metrics/LoggingService.java b/base/src/com/google/idea/blaze/base/metrics/LoggingService.java
deleted file mode 100644
index 47a03df..0000000
--- a/base/src/com/google/idea/blaze/base/metrics/LoggingService.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright 2016 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.metrics;
-
-import com.intellij.openapi.extensions.ExtensionPointName;
-import com.intellij.openapi.project.Project;
-import javax.annotation.Nullable;
-
-/**
- * Logging service that handles logging timing, hit, and other events to an external sink for later
- * analysis.
- */
-public interface LoggingService {
-
- ExtensionPointName<LoggingService> EP_NAME =
- ExtensionPointName.create("com.google.idea.blaze.LoggingService");
-
- /**
- * Report a value for an event to the available logging services.
- *
- * @param variable The variable to report to. Once a value is selected for a logical measurement,
- * the variable's name should never change, even if the colloquial name for the variable
- * changes.
- */
- static void reportEvent(Project project, Action variable) {
- reportEvent(project, variable, 0);
- }
-
- /**
- * Report a value for an event to the available logging services.
- *
- * @param variable The variable to report to. Once a value is selected for a logical measurement,
- * the variable's name should never change, even if the colloquial name for the variable
- * changes.
- * @param value should be >= 0, set the value to 0 if the value is meaningless
- */
- static void reportEvent(Project project, Action variable, long value) {
- for (LoggingService service : EP_NAME.getExtensions()) {
- service.doReportEvent(project, variable, value);
- }
- }
-
- /**
- * Report a value for an event to the logging service
- *
- * @param variable The variable to report to. Once a value is selected for a logical measurement,
- * the variable's name should never change, even if the colloquial name for the variable
- * changes.
- * @param value should be >= 0, set the value to 0 if the value is meaningless
- */
- void doReportEvent(@Nullable Project project, Action variable, long value);
-}
diff --git a/base/src/com/google/idea/blaze/base/model/BlazeLibrary.java b/base/src/com/google/idea/blaze/base/model/BlazeLibrary.java
index d0cbe97..4676411 100644
--- a/base/src/com/google/idea/blaze/base/model/BlazeLibrary.java
+++ b/base/src/com/google/idea/blaze/base/model/BlazeLibrary.java
@@ -16,17 +16,14 @@
package com.google.idea.blaze.base.model;
import com.google.common.base.Objects;
+import com.google.idea.blaze.base.io.VirtualFileSystemProvider;
import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoder;
-import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.libraries.Library;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.io.FileUtilRt;
-import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.StandardFileSystems;
import com.intellij.openapi.vfs.VirtualFileManager;
-import com.intellij.openapi.vfs.VirtualFileSystem;
-import com.intellij.openapi.vfs.ex.temp.TempFileSystem;
import com.intellij.util.io.URLUtil;
import java.io.File;
import java.io.Serializable;
@@ -77,7 +74,9 @@
FileUtilRt.extensionEquals(name, "jar") || FileUtilRt.extensionEquals(name, "zip");
// .jar files require an URL with "jar" protocol.
String protocol =
- isJarFile ? StandardFileSystems.JAR_PROTOCOL : defaultFileSystem().getProtocol();
+ isJarFile
+ ? StandardFileSystems.JAR_PROTOCOL
+ : VirtualFileSystemProvider.getInstance().getSystem().getProtocol();
String filePath = FileUtil.toSystemIndependentName(path.getPath());
String url = VirtualFileManager.constructUrl(protocol, filePath);
if (isJarFile) {
@@ -85,11 +84,4 @@
}
return url;
}
-
- private static VirtualFileSystem defaultFileSystem() {
- if (ApplicationManager.getApplication().isUnitTestMode()) {
- return TempFileSystem.getInstance();
- }
- return LocalFileSystem.getInstance();
- }
}
diff --git a/base/src/com/google/idea/blaze/base/model/primitives/ExecutionRootPath.java b/base/src/com/google/idea/blaze/base/model/primitives/ExecutionRootPath.java
index 1a7f550..4c8d3f0 100644
--- a/base/src/com/google/idea/blaze/base/model/primitives/ExecutionRootPath.java
+++ b/base/src/com/google/idea/blaze/base/model/primitives/ExecutionRootPath.java
@@ -42,6 +42,10 @@
return path;
}
+ public boolean isAbsolute() {
+ return path.isAbsolute();
+ }
+
public File getFileRootedAt(File absoluteRoot) {
if (path.isAbsolute()) {
return path;
diff --git a/base/src/com/google/idea/blaze/base/model/primitives/Kind.java b/base/src/com/google/idea/blaze/base/model/primitives/Kind.java
index 206a73e..eba71f9 100644
--- a/base/src/com/google/idea/blaze/base/model/primitives/Kind.java
+++ b/base/src/com/google/idea/blaze/base/model/primitives/Kind.java
@@ -56,6 +56,7 @@
GO_LIBRARY("go_library", LanguageClass.GO),
GO_APPENGINE_LIBRARY("go_appengine_library", LanguageClass.GO),
GO_WRAP_CC("go_wrap_cc", LanguageClass.GO),
+ INTELLIJ_PLUGIN_DEBUG_TARGET("intellij_plugin_debug_target", LanguageClass.JAVA),
;
static final ImmutableMap<String, Kind> STRING_TO_KIND = makeStringToKindMap();
diff --git a/base/src/com/google/idea/blaze/base/model/primitives/Label.java b/base/src/com/google/idea/blaze/base/model/primitives/Label.java
index c5f31ab..c06f6a2 100644
--- a/base/src/com/google/idea/blaze/base/model/primitives/Label.java
+++ b/base/src/com/google/idea/blaze/base/model/primitives/Label.java
@@ -26,7 +26,7 @@
/** Wrapper around a string for a blaze label (//package:rule). */
@Immutable
public final class Label extends TargetExpression {
- private static final Logger LOG = Logger.getInstance(Label.class);
+ private static final Logger logger = Logger.getInstance(Label.class);
public static final long serialVersionUID = 2L;
@@ -102,7 +102,7 @@
String labelStr = toString();
int startIndex = labelStr.indexOf("//") + "//".length();
int colonIndex = labelStr.lastIndexOf(':');
- LOG.assertTrue(colonIndex >= 0);
+ logger.assertTrue(colonIndex >= 0);
return new WorkspacePath(labelStr.substring(startIndex, colonIndex));
}
diff --git a/base/src/com/google/idea/blaze/base/projectview/ProjectViewEdit.java b/base/src/com/google/idea/blaze/base/projectview/ProjectViewEdit.java
index 7087681..4740038 100644
--- a/base/src/com/google/idea/blaze/base/projectview/ProjectViewEdit.java
+++ b/base/src/com/google/idea/blaze/base/projectview/ProjectViewEdit.java
@@ -32,7 +32,7 @@
/** Represents a modification to one or more project view files. */
public class ProjectViewEdit {
- private static final Logger LOG = Logger.getInstance(ProjectViewEdit.class);
+ private static final Logger logger = Logger.getInstance(ProjectViewEdit.class);
private final Project project;
private final List<Modification> modifications;
@@ -100,7 +100,7 @@
ProjectViewStorageManager.getInstance()
.writeProjectView(projectViewText, modification.projectViewFile);
} catch (IOException e) {
- LOG.error(e);
+ logger.error(e);
Messages.showErrorDialog(
project,
"Could not write updated project view. Is the file write protected?",
diff --git a/base/src/com/google/idea/blaze/base/projectview/ProjectViewManagerImpl.java b/base/src/com/google/idea/blaze/base/projectview/ProjectViewManagerImpl.java
index 1e48489..bab57dc 100644
--- a/base/src/com/google/idea/blaze/base/projectview/ProjectViewManagerImpl.java
+++ b/base/src/com/google/idea/blaze/base/projectview/ProjectViewManagerImpl.java
@@ -35,7 +35,7 @@
/** Stores mutable per-project user settings. */
final class ProjectViewManagerImpl extends ProjectViewManager {
- private static final Logger LOG = Logger.getInstance(ProjectViewManagerImpl.class);
+ private static final Logger logger = Logger.getInstance(ProjectViewManagerImpl.class);
private static final String CACHE_FILE_NAME = "project.view.dat";
private final Project project;
@@ -64,7 +64,7 @@
classLoaders.add(Thread.currentThread().getContextClassLoader());
loadedProjectViewSet = (ProjectViewSet) SerializationUtil.loadFromDisk(file, classLoaders);
} catch (IOException e) {
- LOG.info(e);
+ logger.info(e);
}
this.projectViewSet = loadedProjectViewSet;
this.projectViewSetLoaded = true;
@@ -90,7 +90,7 @@
try {
SerializationUtil.saveToDisk(file, projectViewSet);
} catch (IOException e) {
- LOG.error(e);
+ logger.error(e);
}
this.projectViewSet = projectViewSet;
}
diff --git a/base/src/com/google/idea/blaze/base/projectview/ProjectViewStorageManagerImpl.java b/base/src/com/google/idea/blaze/base/projectview/ProjectViewStorageManagerImpl.java
index de784d7..729b1d8 100644
--- a/base/src/com/google/idea/blaze/base/projectview/ProjectViewStorageManagerImpl.java
+++ b/base/src/com/google/idea/blaze/base/projectview/ProjectViewStorageManagerImpl.java
@@ -28,7 +28,7 @@
/** Project view storage implementation. */
final class ProjectViewStorageManagerImpl extends ProjectViewStorageManager {
- private static final Logger LOG = Logger.getInstance(ProjectViewManagerImpl.class);
+ private static final Logger logger = Logger.getInstance(ProjectViewManagerImpl.class);
@Nullable
@Override
diff --git a/base/src/com/google/idea/blaze/base/projectview/ProjectViewVerifier.java b/base/src/com/google/idea/blaze/base/projectview/ProjectViewVerifier.java
index 0953d07..9b4b465 100644
--- a/base/src/com/google/idea/blaze/base/projectview/ProjectViewVerifier.java
+++ b/base/src/com/google/idea/blaze/base/projectview/ProjectViewVerifier.java
@@ -15,13 +15,17 @@
*/
package com.google.idea.blaze.base.projectview;
+import static java.util.stream.Collectors.toList;
+
import com.google.common.collect.Lists;
import com.google.idea.blaze.base.io.FileAttributeProvider;
import com.google.idea.blaze.base.model.primitives.WorkspacePath;
+import com.google.idea.blaze.base.projectview.ProjectViewSet.ProjectViewFile;
import com.google.idea.blaze.base.projectview.section.ListSection;
+import com.google.idea.blaze.base.projectview.section.SectionParser;
import com.google.idea.blaze.base.projectview.section.sections.DirectoryEntry;
import com.google.idea.blaze.base.projectview.section.sections.DirectorySection;
-import com.google.idea.blaze.base.projectview.section.sections.ExcludedSourceSection;
+import com.google.idea.blaze.base.projectview.section.sections.Sections;
import com.google.idea.blaze.base.scope.BlazeContext;
import com.google.idea.blaze.base.scope.output.IssueOutput;
import com.google.idea.blaze.base.sync.BlazeSyncPlugin;
@@ -56,22 +60,47 @@
return false;
}
}
- if (!projectViewSet.listItems(ExcludedSourceSection.KEY).isEmpty()) {
- IssueOutput.warn("excluded_sources is deprecated and has no effect.")
- .inFile(projectViewSet.getTopLevelProjectViewFile().projectViewFile)
- .submit(context);
- }
+ warnAboutDeprecatedSections(context, projectViewSet);
if (!verifyIncludedPackagesExistOnDisk(context, workspacePathResolver, projectViewSet)) {
return false;
}
return true;
}
+ private static void warnAboutDeprecatedSections(
+ BlazeContext context, ProjectViewSet projectViewSet) {
+ List<SectionParser> deprecatedParsers =
+ Sections.getParsers().stream().filter(SectionParser::isDeprecated).collect(toList());
+ for (SectionParser sectionParser : deprecatedParsers) {
+ for (ProjectViewFile projectViewFile : projectViewSet.getProjectViewFiles()) {
+ ProjectView projectView = projectViewFile.projectView;
+ if (projectView
+ .getSections()
+ .stream()
+ .anyMatch(section -> section.isSectionType(sectionParser.getSectionKey()))) {
+ String deprecationMessage = sectionParser.getDeprecationMessage();
+ if (deprecationMessage == null) {
+ deprecationMessage = String.format("%s is deprecated", sectionParser.getName());
+ }
+ IssueOutput.warn(deprecationMessage)
+ .inFile(projectViewFile.projectViewFile)
+ .submit(context);
+ }
+ }
+ }
+ }
+
private static boolean verifyIncludedPackagesAreNotExcluded(
BlazeContext context, ProjectViewSet projectViewSet) {
boolean ok = true;
- List<WorkspacePath> includedDirectories = getIncludedDirectories(projectViewSet);
+ List<WorkspacePath> includedDirectories =
+ projectViewSet
+ .listItems(DirectorySection.KEY)
+ .stream()
+ .filter(entry -> entry.included)
+ .map(entry -> entry.directory)
+ .collect(toList());
for (WorkspacePath includedDirectory : includedDirectories) {
for (ProjectViewSet.ProjectViewFile projectViewFile : projectViewSet.getProjectViewFiles()) {
@@ -103,16 +132,6 @@
return ok;
}
- private static List<WorkspacePath> getIncludedDirectories(ProjectViewSet projectViewSet) {
- List<WorkspacePath> includedDirectories = Lists.newArrayList();
- for (DirectoryEntry entry : projectViewSet.listItems(DirectorySection.KEY)) {
- if (entry.included) {
- includedDirectories.add(entry.directory);
- }
- }
- return includedDirectories;
- }
-
private static boolean verifyIncludedPackagesExistOnDisk(
BlazeContext context,
WorkspacePathResolver workspacePathResolver,
diff --git a/base/src/com/google/idea/blaze/base/projectview/section/SectionParser.java b/base/src/com/google/idea/blaze/base/projectview/section/SectionParser.java
index ddfa57e..1e0f043 100644
--- a/base/src/com/google/idea/blaze/base/projectview/section/SectionParser.java
+++ b/base/src/com/google/idea/blaze/base/projectview/section/SectionParser.java
@@ -47,6 +47,11 @@
return false;
}
+ @Nullable
+ public String getDeprecationMessage() {
+ return null;
+ }
+
/** Allows the section to add a default value. Used during the wizard. */
public ProjectView addProjectViewDefaultValue(ProjectView projectView) {
return projectView;
diff --git a/base/src/com/google/idea/blaze/base/projectview/section/sections/ExcludedSourceSection.java b/base/src/com/google/idea/blaze/base/projectview/section/sections/ExcludedSourceSection.java
index de6081f..8fceb2a 100644
--- a/base/src/com/google/idea/blaze/base/projectview/section/sections/ExcludedSourceSection.java
+++ b/base/src/com/google/idea/blaze/base/projectview/section/sections/ExcludedSourceSection.java
@@ -20,6 +20,7 @@
import com.google.idea.blaze.base.projectview.section.ListSection;
import com.google.idea.blaze.base.projectview.section.SectionKey;
import com.google.idea.blaze.base.projectview.section.SectionParser;
+import javax.annotation.Nullable;
/** Section for excluding source files. */
@Deprecated
@@ -31,5 +32,11 @@
public boolean isDeprecated() {
return true;
}
+
+ @Nullable
+ @Override
+ public String getDeprecationMessage() {
+ return "excluded_sources is deprecated and has no effect.";
+ }
};
}
diff --git a/base/src/com/google/idea/blaze/base/projectview/section/sections/MetricsProjectSection.java b/base/src/com/google/idea/blaze/base/projectview/section/sections/MetricsProjectSection.java
deleted file mode 100644
index 3a0d6c8..0000000
--- a/base/src/com/google/idea/blaze/base/projectview/section/sections/MetricsProjectSection.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 2016 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.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.ScalarSectionParser;
-import com.google.idea.blaze.base.projectview.section.SectionKey;
-import com.google.idea.blaze.base.projectview.section.SectionParser;
-import com.intellij.openapi.util.text.StringUtil;
-import javax.annotation.Nullable;
-
-/** Sets the metrics project to allow monitoring of individual projects */
-public class MetricsProjectSection {
- public static final SectionKey<String, ScalarSection<String>> KEY =
- SectionKey.of("metrics_project");
- public static final SectionParser PARSER = new MetricsProjectSectionParser();
-
- private static class MetricsProjectSectionParser extends ScalarSectionParser<String> {
- public MetricsProjectSectionParser() {
- super(KEY, ':');
- }
-
- @Nullable
- @Override
- protected String parseItem(ProjectViewParser parser, ParseContext parseContext, String rest) {
- return StringUtil.unquoteString(rest);
- }
-
- @Override
- protected void printItem(StringBuilder sb, String value) {
- sb.append(value);
- }
-
- @Override
- public ItemType getItemType() {
- return ItemType.Other;
- }
- }
-}
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 ad0c35e..4d071a3 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
@@ -36,7 +36,6 @@
ImportTargetOutputSection.PARSER,
ExcludeTargetSection.PARSER,
ExcludedSourceSection.PARSER,
- MetricsProjectSection.PARSER,
RunConfigurationsSection.PARSER);
public static List<SectionParser> getParsers() {
diff --git a/base/src/com/google/idea/blaze/base/run/BlazeCommandRunConfiguration.java b/base/src/com/google/idea/blaze/base/run/BlazeCommandRunConfiguration.java
index 07fd7e3..9448702 100644
--- a/base/src/com/google/idea/blaze/base/run/BlazeCommandRunConfiguration.java
+++ b/base/src/com/google/idea/blaze/base/run/BlazeCommandRunConfiguration.java
@@ -38,6 +38,7 @@
import com.intellij.execution.configurations.LocatableConfigurationBase;
import com.intellij.execution.configurations.ModuleRunProfile;
import com.intellij.execution.configurations.RunConfiguration;
+import com.intellij.execution.configurations.RunConfigurationWithSuppressedDefaultDebugAction;
import com.intellij.execution.configurations.RunProfileState;
import com.intellij.execution.configurations.RuntimeConfigurationError;
import com.intellij.execution.configurations.RuntimeConfigurationException;
@@ -66,9 +67,12 @@
/** A run configuration which executes Blaze commands. */
public class BlazeCommandRunConfiguration extends LocatableConfigurationBase
- implements BlazeRunConfiguration, RunnerIconProvider, ModuleRunProfile {
+ implements BlazeRunConfiguration,
+ RunnerIconProvider,
+ ModuleRunProfile,
+ RunConfigurationWithSuppressedDefaultDebugAction {
- private static final Logger LOG = Logger.getInstance(BlazeCommandRunConfiguration.class);
+ private static final Logger logger = Logger.getInstance(BlazeCommandRunConfiguration.class);
private static final String HANDLER_ATTR = "handler-id";
private static final String TARGET_TAG = "blaze-target";
@@ -103,7 +107,7 @@
try {
handler.getState().readExternal(elementState);
} catch (InvalidDataException e) {
- LOG.error(e);
+ logger.error(e);
}
}
@@ -164,14 +168,14 @@
try {
handler.getState().writeExternal(elementState);
} catch (WriteExternalException e) {
- LOG.error(e);
+ logger.error(e);
}
handlerProvider = newProvider;
handler = newProvider.createHandler(this);
try {
handler.getState().readExternal(elementState);
} catch (InvalidDataException e) {
- LOG.error(e);
+ logger.error(e);
}
}
@@ -321,7 +325,7 @@
try {
configuration.handler.getState().readExternal(configuration.elementState);
} catch (InvalidDataException e) {
- LOG.error(e);
+ logger.error(e);
}
return configuration;
@@ -425,7 +429,7 @@
try {
handler.getState().readExternal(config.elementState);
} catch (InvalidDataException e) {
- LOG.error(e);
+ logger.error(e);
}
handlerStateEditor = handler.getState().getEditor(config.getProject());
@@ -459,7 +463,7 @@
try {
handler.getState().writeExternal(elementState);
} catch (WriteExternalException e) {
- LOG.error(e);
+ logger.error(e);
}
config.keepInSync = keepInSyncCheckBox.isVisible() ? keepInSyncCheckBox.isSelected() : null;
@@ -469,7 +473,7 @@
try {
config.handler.getState().readExternal(config.elementState);
} catch (InvalidDataException e) {
- LOG.error(e);
+ logger.error(e);
}
// finally, update the handler
diff --git a/base/src/com/google/idea/blaze/base/run/DistributedExecutorSupport.java b/base/src/com/google/idea/blaze/base/run/DistributedExecutorSupport.java
index 851eab6..c6991ca 100644
--- a/base/src/com/google/idea/blaze/base/run/DistributedExecutorSupport.java
+++ b/base/src/com/google/idea/blaze/base/run/DistributedExecutorSupport.java
@@ -44,21 +44,18 @@
}
/** Returns the blaze/bazel flags required to specify whether to run on a distributed executor. */
- static List<String> getBlazeFlags(Project project, @Nullable Boolean runDistributed) {
- if (runDistributed == null) {
- return ImmutableList.of();
- }
+ static List<String> getBlazeFlags(Project project, boolean runDistributed) {
DistributedExecutorSupport executorInfo = getAvailableExecutor(Blaze.getBuildSystem(project));
if (executorInfo == null) {
return ImmutableList.of();
}
- return ImmutableList.of(executorInfo.getBlazeFlag(runDistributed));
+ return executorInfo.getBlazeFlags(runDistributed);
}
String executorName();
boolean isAvailable(BuildSystem buildSystem);
- /** Get blaze/bazel flag specifying whether to run on this distributed executor */
- String getBlazeFlag(boolean runDistributed);
+ /** Get blaze/bazel flags specifying whether to run on this distributed executor */
+ ImmutableList<String> getBlazeFlags(boolean runDistributed);
}
diff --git a/base/src/com/google/idea/blaze/base/run/confighandler/BlazeCommandGenericRunConfigurationRunner.java b/base/src/com/google/idea/blaze/base/run/confighandler/BlazeCommandGenericRunConfigurationRunner.java
index d2c6512..7c508e2 100644
--- a/base/src/com/google/idea/blaze/base/run/confighandler/BlazeCommandGenericRunConfigurationRunner.java
+++ b/base/src/com/google/idea/blaze/base/run/confighandler/BlazeCommandGenericRunConfigurationRunner.java
@@ -21,25 +21,24 @@
import com.google.idea.blaze.base.command.BlazeCommandName;
import com.google.idea.blaze.base.command.BlazeFlags;
import com.google.idea.blaze.base.issueparser.IssueOutputLineProcessor;
-import com.google.idea.blaze.base.metrics.Action;
import com.google.idea.blaze.base.model.primitives.WorkspaceRoot;
import com.google.idea.blaze.base.projectview.ProjectViewManager;
import com.google.idea.blaze.base.projectview.ProjectViewSet;
import com.google.idea.blaze.base.run.BlazeCommandRunConfiguration;
import com.google.idea.blaze.base.run.DistributedExecutorSupport;
+import com.google.idea.blaze.base.run.filter.BlazeTargetFilter;
import com.google.idea.blaze.base.run.processhandler.LineProcessingProcessAdapter;
import com.google.idea.blaze.base.run.processhandler.ScopedBlazeProcessHandler;
import com.google.idea.blaze.base.run.smrunner.BlazeTestEventsHandler;
-import com.google.idea.blaze.base.run.smrunner.BlazeTestEventsHandlerProvider;
import com.google.idea.blaze.base.run.smrunner.SmRunnerUtils;
import com.google.idea.blaze.base.run.state.BlazeCommandRunConfigurationCommonState;
import com.google.idea.blaze.base.scope.BlazeContext;
import com.google.idea.blaze.base.scope.scopes.IdeaLogScope;
import com.google.idea.blaze.base.scope.scopes.IssuesScope;
-import com.google.idea.blaze.base.scope.scopes.LoggedTimingScope;
import com.google.idea.blaze.base.settings.Blaze;
import com.google.idea.blaze.base.settings.BlazeImportSettings;
import com.google.idea.blaze.base.settings.BlazeImportSettingsManager;
+import com.google.idea.common.experiments.BoolExperiment;
import com.intellij.execution.DefaultExecutionResult;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.ExecutionResult;
@@ -48,6 +47,7 @@
import com.intellij.execution.configurations.RunProfile;
import com.intellij.execution.configurations.RunProfileState;
import com.intellij.execution.configurations.WrappingRunConfiguration;
+import com.intellij.execution.filters.Filter;
import com.intellij.execution.filters.TextConsoleBuilderImpl;
import com.intellij.execution.process.ProcessHandler;
import com.intellij.execution.process.ProcessListener;
@@ -55,7 +55,7 @@
import com.intellij.execution.runners.ProgramRunner;
import com.intellij.execution.ui.ConsoleView;
import com.intellij.openapi.project.Project;
-import javax.annotation.Nullable;
+import java.util.Collection;
import org.jetbrains.annotations.NotNull;
/**
@@ -65,9 +65,12 @@
public final class BlazeCommandGenericRunConfigurationRunner
implements BlazeCommandRunConfigurationRunner {
+ private static final BoolExperiment smRunnerUiEnabled =
+ new BoolExperiment("use.smrunner.ui.general", true);
+
@Override
public RunProfileState getRunProfileState(Executor executor, ExecutionEnvironment environment) {
- return new BlazeCommandRunProfileState(environment, null);
+ return new BlazeCommandRunProfileState(environment, ImmutableList.of());
}
@Override
@@ -80,16 +83,19 @@
public static class BlazeCommandRunProfileState extends CommandLineState {
private final BlazeCommandRunConfiguration configuration;
private final BlazeCommandRunConfigurationCommonState handlerState;
- @Nullable private final BlazeTestEventsHandlerProvider testEventsHandlerProvider;
+ private final ImmutableList<Filter> consoleFilters;
public BlazeCommandRunProfileState(
- ExecutionEnvironment environment,
- @Nullable BlazeTestEventsHandlerProvider testEventsHandlerProvider) {
+ ExecutionEnvironment environment, Collection<Filter> consoleFilters) {
super(environment);
this.configuration = getConfiguration(environment);
this.handlerState =
(BlazeCommandRunConfigurationCommonState) configuration.getHandler().getState();
- this.testEventsHandlerProvider = testEventsHandlerProvider;
+ this.consoleFilters =
+ ImmutableList.<Filter>builder()
+ .addAll(consoleFilters)
+ .add(new BlazeTargetFilter(environment.getProject()))
+ .build();
}
private static BlazeCommandRunConfiguration getConfiguration(ExecutionEnvironment environment) {
@@ -120,11 +126,11 @@
ImmutableList<String> testHandlerFlags = ImmutableList.of();
BlazeTestEventsHandler testEventsHandler =
- canUseTestUi() && testEventsHandlerProvider != null
- ? testEventsHandlerProvider.getHandler()
+ canUseTestUi()
+ ? BlazeTestEventsHandler.getHandlerForTarget(project, configuration.getTarget())
: null;
if (testEventsHandler != null) {
- testHandlerFlags = testEventsHandler.getBlazeFlags();
+ testHandlerFlags = BlazeTestEventsHandler.getBlazeFlags(project);
setConsoleBuilder(
new TextConsoleBuilderImpl(project) {
@Override
@@ -134,6 +140,7 @@
}
});
}
+ addConsoleFilters(consoleFilters.toArray(new Filter[0]));
BlazeCommand blazeCommand = getBlazeCommand(project, testHandlerFlags);
@@ -146,7 +153,6 @@
@Override
public void onBlazeContextStart(BlazeContext context) {
context
- .push(new LoggedTimingScope(project, Action.BLAZE_COMMAND_USAGE))
.push(new IssuesScope(project))
.push(new IdeaLogScope());
}
@@ -165,23 +171,29 @@
ProjectViewSet projectViewSet = ProjectViewManager.getInstance(project).getProjectViewSet();
assert projectViewSet != null;
- return BlazeCommand.builder(Blaze.getBuildSystem(project), handlerState.getCommand())
- .setBlazeBinary(handlerState.getBlazeBinary())
- .addTargets(configuration.getTarget())
- .addBlazeFlags(BlazeFlags.buildFlags(project, projectViewSet))
- .addBlazeFlags(testHandlerFlags)
- .addBlazeFlags(handlerState.getBlazeFlags())
- .addBlazeFlags(
- DistributedExecutorSupport.getBlazeFlags(
- project, handlerState.getRunOnDistributedExecutor()))
- .addExeFlags(handlerState.getExeFlags())
- .build();
+ BlazeCommand.Builder command =
+ BlazeCommand.builder(Blaze.getBuildSystem(project), handlerState.getCommand())
+ .setBlazeBinary(handlerState.getBlazeBinary())
+ .addTargets(configuration.getTarget())
+ .addBlazeFlags(BlazeFlags.buildFlags(project, projectViewSet))
+ .addBlazeFlags(testHandlerFlags)
+ .addBlazeFlags(handlerState.getBlazeFlags())
+ .addExeFlags(handlerState.getExeFlags());
+
+ boolean runDistributed = handlerState.getRunOnDistributedExecutor();
+ command.addBlazeFlags(
+ DistributedExecutorSupport.getBlazeFlags(
+ project, handlerState.getRunOnDistributedExecutor()));
+ if (!runDistributed) {
+ command.addBlazeFlags(BlazeFlags.TEST_OUTPUT_STREAMED);
+ }
+ return command.build();
}
private boolean canUseTestUi() {
- return testEventsHandlerProvider != null
+ return smRunnerUiEnabled.getValue()
&& BlazeCommandName.TEST.equals(handlerState.getCommand())
- && !Boolean.TRUE.equals(handlerState.getRunOnDistributedExecutor());
+ && !handlerState.getRunOnDistributedExecutor();
}
}
}
diff --git a/base/src/com/google/idea/blaze/base/run/filter/BlazeTargetFilter.java b/base/src/com/google/idea/blaze/base/run/filter/BlazeTargetFilter.java
new file mode 100644
index 0000000..1cf0e0c
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/run/filter/BlazeTargetFilter.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2016 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.run.filter;
+
+import com.google.idea.blaze.base.lang.buildfile.references.BuildReferenceManager;
+import com.google.idea.blaze.base.lang.buildfile.references.LabelUtils;
+import com.google.idea.blaze.base.model.primitives.Label;
+import com.intellij.execution.filters.Filter;
+import com.intellij.execution.filters.HyperlinkInfo;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.NavigatablePsiElement;
+import com.intellij.psi.PsiElement;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import org.jetbrains.annotations.Nullable;
+
+/** Parse blaze targets in streamed output. */
+public class BlazeTargetFilter implements Filter {
+
+ private static final Pattern TARGET_PATTERN = Pattern.compile("//([^\\s:]*):(\\S*)");
+
+ private final Project project;
+
+ public BlazeTargetFilter(Project project) {
+ this.project = project;
+ }
+
+ @Nullable
+ @Override
+ public Result applyFilter(String line, int entireLength) {
+ Matcher matcher = TARGET_PATTERN.matcher(line);
+ if (!matcher.find()) {
+ return null;
+ }
+ String labelString = matcher.group();
+ Label label = LabelUtils.createLabelFromString(null, labelString);
+ if (label == null) {
+ return null;
+ }
+ PsiElement psi = BuildReferenceManager.getInstance(project).resolveLabel(label);
+ if (!(psi instanceof NavigatablePsiElement)) {
+ return null;
+ }
+ HyperlinkInfo link = project -> ((NavigatablePsiElement) psi).navigate(true);
+ int offset = entireLength - line.length();
+ return new Result(matcher.start() + offset, matcher.end() + offset, link);
+ }
+}
diff --git a/base/src/com/google/idea/blaze/base/run/filter/FileResolver.java b/base/src/com/google/idea/blaze/base/run/filter/FileResolver.java
new file mode 100644
index 0000000..5ada31d
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/run/filter/FileResolver.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2016 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.run.filter;
+
+import com.intellij.openapi.extensions.ExtensionPointName;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import javax.annotation.Nullable;
+
+/** Parses file strings in blaze/bazel output. */
+public interface FileResolver {
+
+ ExtensionPointName<FileResolver> EP_NAME =
+ ExtensionPointName.create("com.google.idea.blaze.FileStringParser");
+
+ /**
+ * Iterates through all available {@link FileResolver}s, returning the first successful result.
+ */
+ static VirtualFile resolve(Project project, String fileString) {
+ for (FileResolver parser : EP_NAME.getExtensions()) {
+ VirtualFile result = parser.resolveToFile(project, fileString);
+ if (result != null) {
+ return result;
+ }
+ }
+ return null;
+ }
+
+ @Nullable
+ VirtualFile resolveToFile(Project project, String fileString);
+}
diff --git a/base/src/com/google/idea/blaze/base/run/filter/StandardFileResolver.java b/base/src/com/google/idea/blaze/base/run/filter/StandardFileResolver.java
new file mode 100644
index 0000000..15a3d09
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/run/filter/StandardFileResolver.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2016 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.run.filter;
+
+import com.google.idea.blaze.base.io.VirtualFileSystemProvider;
+import com.google.idea.blaze.base.model.BlazeProjectData;
+import com.google.idea.blaze.base.sync.data.BlazeProjectDataManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import java.io.File;
+import javax.annotation.Nullable;
+
+/** Parses absolute and workspace-relative paths. */
+public class StandardFileResolver implements FileResolver {
+
+ @Nullable
+ @Override
+ public VirtualFile resolveToFile(Project project, String fileString) {
+ File file = new File(fileString);
+ if (file.isAbsolute()) {
+ return VirtualFileSystemProvider.getInstance().getSystem().findFileByPath(file.getPath());
+ }
+ BlazeProjectData projectData =
+ BlazeProjectDataManager.getInstance(project).getBlazeProjectData();
+ if (projectData == null) {
+ return null;
+ }
+ file = projectData.workspacePathResolver.resolveToFile(fileString);
+ return VirtualFileSystemProvider.getInstance().getSystem().findFileByPath(file.getPath());
+ }
+}
diff --git a/base/src/com/google/idea/blaze/base/run/producers/BlazeBuildFileRunConfigurationProducer.java b/base/src/com/google/idea/blaze/base/run/producers/BlazeBuildFileRunConfigurationProducer.java
index c8d0ba3..60faff7 100644
--- a/base/src/com/google/idea/blaze/base/run/producers/BlazeBuildFileRunConfigurationProducer.java
+++ b/base/src/com/google/idea/blaze/base/run/producers/BlazeBuildFileRunConfigurationProducer.java
@@ -40,7 +40,7 @@
public class BlazeBuildFileRunConfigurationProducer
extends BlazeRunConfigurationProducer<BlazeCommandRunConfiguration> {
- private static final Logger LOG =
+ private static final Logger logger =
Logger.getInstance(BlazeBuildFileRunConfigurationProducer.class);
private static class BuildTarget {
@@ -162,7 +162,7 @@
public static void setupConfiguration(RunConfiguration configuration, Label label) {
BuildTarget target = buildTargetFromLabel(configuration.getProject(), label);
if (target == null || !(configuration instanceof BlazeCommandRunConfiguration)) {
- LOG.error("Configuration not handled by BUILD file config producer: " + configuration);
+ logger.error("Configuration not handled by BUILD file config producer: " + configuration);
return;
}
setupBuildFileConfiguration((BlazeCommandRunConfiguration) configuration, target);
diff --git a/base/src/com/google/idea/blaze/base/run/producers/BlazeFilterExistingRunConfigurationProducer.java b/base/src/com/google/idea/blaze/base/run/producers/BlazeFilterExistingRunConfigurationProducer.java
new file mode 100644
index 0000000..feae47b
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/run/producers/BlazeFilterExistingRunConfigurationProducer.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2017 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.run.producers;
+
+import com.google.idea.blaze.base.command.BlazeCommandName;
+import com.google.idea.blaze.base.command.BlazeFlags;
+import com.google.idea.blaze.base.model.primitives.TargetExpression;
+import com.google.idea.blaze.base.run.BlazeCommandRunConfiguration;
+import com.google.idea.blaze.base.run.BlazeCommandRunConfigurationType;
+import com.google.idea.blaze.base.run.smrunner.BlazeTestEventsHandler;
+import com.google.idea.blaze.base.run.smrunner.SmRunnerUtils;
+import com.google.idea.blaze.base.run.state.BlazeCommandRunConfigurationCommonState;
+import com.intellij.execution.Location;
+import com.intellij.execution.actions.ConfigurationContext;
+import com.intellij.execution.configurations.RunConfiguration;
+import com.intellij.openapi.util.Ref;
+import com.intellij.psi.PsiElement;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import javax.annotation.Nullable;
+
+/**
+ * Handles the specific case where the user creates a run configuration by selecting test suites /
+ * classes / methods from the test UI tree.
+ *
+ * <p>In this special case we already know the blaze target string, and only need to apply a filter
+ * to the existing configuration. Delegates language-specific filter calculation to {@link
+ * BlazeTestEventsHandler}.
+ */
+public class BlazeFilterExistingRunConfigurationProducer
+ extends BlazeRunConfigurationProducer<BlazeCommandRunConfiguration> {
+
+ public BlazeFilterExistingRunConfigurationProducer() {
+ super(BlazeCommandRunConfigurationType.getInstance());
+ }
+
+ @Override
+ protected boolean doSetupConfigFromContext(
+ BlazeCommandRunConfiguration configuration,
+ ConfigurationContext context,
+ Ref<PsiElement> sourceElement) {
+ String testFilter = getTestFilter(context);
+ if (testFilter == null) {
+ return false;
+ }
+ BlazeCommandRunConfigurationCommonState handlerState =
+ configuration.getHandlerStateIfType(BlazeCommandRunConfigurationCommonState.class);
+ if (handlerState == null || !BlazeCommandName.TEST.equals(handlerState.getCommand())) {
+ return false;
+ }
+ // replace old test filter flag if present
+ List<String> flags = new ArrayList<>(handlerState.getBlazeFlags());
+ flags.removeIf((flag) -> flag.startsWith(BlazeFlags.TEST_FILTER));
+ flags.add(testFilter);
+ handlerState.setBlazeFlags(flags);
+ configuration.setName(configuration.getName() + " (filtered)");
+ configuration.setNameChangedByUser(true);
+ return true;
+ }
+
+ @Override
+ protected boolean doIsConfigFromContext(
+ BlazeCommandRunConfiguration configuration, ConfigurationContext context) {
+ String testFilter = getTestFilter(context);
+ if (testFilter == null) {
+ return false;
+ }
+ BlazeCommandRunConfigurationCommonState handlerState =
+ configuration.getHandlerStateIfType(BlazeCommandRunConfigurationCommonState.class);
+
+ return handlerState != null
+ && Objects.equals(handlerState.getCommand(), BlazeCommandName.TEST)
+ && Objects.equals(testFilter, handlerState.getTestFilterFlag());
+ }
+
+ @Nullable
+ private static String getTestFilter(ConfigurationContext context) {
+ RunConfiguration base = context.getOriginalConfiguration(null);
+ if (!(base instanceof BlazeCommandRunConfiguration)) {
+ return null;
+ }
+ TargetExpression target = ((BlazeCommandRunConfiguration) base).getTarget();
+ if (target == null) {
+ return null;
+ }
+ BlazeTestEventsHandler testEventsHandler =
+ BlazeTestEventsHandler.getHandlerForTarget(context.getProject(), target);
+ if (testEventsHandler == null) {
+ return null;
+ }
+ List<Location<?>> selectedElements = SmRunnerUtils.getSelectedSmRunnerTreeElements(context);
+ if (selectedElements.isEmpty()) {
+ return null;
+ }
+ return testEventsHandler.getTestFilter(context.getProject(), selectedElements);
+ }
+}
diff --git a/base/src/com/google/idea/blaze/base/run/smrunner/BlazeCompositeTestEventsHandler.java b/base/src/com/google/idea/blaze/base/run/smrunner/BlazeCompositeTestEventsHandler.java
new file mode 100644
index 0000000..5d86bdf
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/run/smrunner/BlazeCompositeTestEventsHandler.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2017 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.run.smrunner;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import com.google.idea.blaze.base.model.primitives.Kind;
+import com.intellij.execution.Location;
+import com.intellij.execution.testframework.actions.AbstractRerunFailedTestsAction;
+import com.intellij.execution.testframework.sm.runner.SMTestLocator;
+import com.intellij.execution.ui.ConsoleView;
+import com.intellij.openapi.project.Project;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import javax.annotation.Nullable;
+
+/** Combines multiple language-specific handlers (e.g. to handle test_suite targets). */
+public class BlazeCompositeTestEventsHandler extends BlazeTestEventsHandler {
+
+ private static ImmutableMap<Kind, BlazeTestEventsHandler> collectHandlers() {
+ Map<Kind, BlazeTestEventsHandler> map = new HashMap<>();
+ for (BlazeTestEventsHandler handler : BlazeTestEventsHandler.EP_NAME.getExtensions()) {
+ if (handler instanceof BlazeCompositeTestEventsHandler) {
+ continue;
+ }
+ for (Kind kind : handler.handledKinds()) {
+ // earlier handlers get priority.
+ map.putIfAbsent(kind, handler);
+ }
+ }
+ return Maps.immutableEnumMap(map);
+ }
+
+ private static ImmutableMap<Kind, BlazeTestEventsHandler> handlers;
+
+ private static ImmutableMap<Kind, BlazeTestEventsHandler> getHandlers() {
+ if (handlers == null) {
+ handlers = collectHandlers();
+ }
+ return handlers;
+ }
+
+ @Override
+ public boolean handlesTargetKind(@Nullable Kind kind) {
+ // composite handler specifically exists to handle test-suites and multi-target blaze
+ // invocations, so must handle targets without a kind.
+ return kind == null || kind == Kind.TEST_SUITE || handledKinds().contains(kind);
+ }
+
+ @Override
+ protected EnumSet<Kind> handledKinds() {
+ ImmutableSet<Kind> handledKinds = getHandlers().keySet();
+ return !handledKinds.isEmpty() ? EnumSet.copyOf(handledKinds) : EnumSet.noneOf(Kind.class);
+ }
+
+ @Override
+ public SMTestLocator getTestLocator() {
+ return new CompositeSMTestLocator(
+ ImmutableList.copyOf(
+ getHandlers()
+ .values()
+ .stream()
+ .map(BlazeTestEventsHandler::getTestLocator)
+ .collect(Collectors.toList())));
+ }
+
+ @Nullable
+ @Override
+ public String getTestFilter(Project project, List<Location<?>> testLocations) {
+ // We make no attempt to support re-running a subset of tests for test_suites or target patterns
+ return null;
+ }
+
+ @Nullable
+ @Override
+ public AbstractRerunFailedTestsAction createRerunFailedTestsAction(ConsoleView consoleView) {
+ return null;
+ }
+
+ /** Converts the testsuite name in the blaze test XML to a user-friendly format */
+ @Override
+ public String suiteDisplayName(@Nullable Kind kind, String rawName) {
+ BlazeTestEventsHandler handler = kind != null ? getHandlers().get(kind) : null;
+ return handler != null
+ ? handler.suiteDisplayName(kind, rawName)
+ : super.suiteDisplayName(kind, rawName);
+ }
+
+ /** Converts the testcase name in the blaze test XML to a user-friendly format */
+ @Override
+ public String testDisplayName(@Nullable Kind kind, String rawName) {
+ BlazeTestEventsHandler handler = kind != null ? getHandlers().get(kind) : null;
+ return handler != null
+ ? handler.testDisplayName(kind, rawName)
+ : super.testDisplayName(kind, rawName);
+ }
+
+ @Override
+ public String suiteLocationUrl(@Nullable Kind kind, String name) {
+ BlazeTestEventsHandler handler = kind != null ? getHandlers().get(kind) : null;
+ return handler != null
+ ? handler.suiteLocationUrl(kind, name)
+ : super.suiteLocationUrl(kind, name);
+ }
+
+ @Override
+ public String testLocationUrl(
+ @Nullable Kind kind, String parentSuite, String name, @Nullable String className) {
+ BlazeTestEventsHandler handler = getHandlers().get(kind);
+ return handler != null
+ ? handler.testLocationUrl(kind, parentSuite, name, className)
+ : super.testLocationUrl(kind, parentSuite, name, className);
+ }
+}
diff --git a/base/src/com/google/idea/blaze/base/run/smrunner/BlazeRerunFailedTestsAction.java b/base/src/com/google/idea/blaze/base/run/smrunner/BlazeRerunFailedTestsAction.java
index bc90989..2522bab 100644
--- a/base/src/com/google/idea/blaze/base/run/smrunner/BlazeRerunFailedTestsAction.java
+++ b/base/src/com/google/idea/blaze/base/run/smrunner/BlazeRerunFailedTestsAction.java
@@ -21,14 +21,20 @@
import com.google.idea.blaze.base.run.state.BlazeCommandRunConfigurationCommonState;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.Executor;
+import com.intellij.execution.Location;
import com.intellij.execution.configurations.RunProfileState;
import com.intellij.execution.runners.ExecutionEnvironment;
+import com.intellij.execution.testframework.AbstractTestProxy;
import com.intellij.execution.testframework.TestFrameworkRunningModel;
import com.intellij.execution.testframework.actions.AbstractRerunFailedTestsAction;
import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.ComponentContainer;
+import com.intellij.psi.search.GlobalSearchScope;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
import javax.annotation.Nullable;
/** Re-run failed tests. */
@@ -77,12 +83,27 @@
if (handlerState == null || !BlazeCommandName.TEST.equals(handlerState.getCommand())) {
return null;
}
- String testFilter = eventsHandler.getTestFilter(getProject(), getFailedTests(getProject()));
+ Project project = getProject();
+ List<Location<?>> locations =
+ getFailedTests(project)
+ .stream()
+ .map((test) -> toLocation(project, test))
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
+ String testFilter = eventsHandler.getTestFilter(getProject(), locations);
+ if (testFilter == null) {
+ return null;
+ }
List<String> blazeFlags = setTestFilter(handlerState.getBlazeFlags(), testFilter);
handlerState.setBlazeFlags(blazeFlags);
return configuration.getState(executor, environment);
}
+ @Nullable
+ private Location<?> toLocation(Project project, AbstractTestProxy test) {
+ return test.getLocation(project, GlobalSearchScope.allScope(project));
+ }
+
/** Replaces existing test_filter flag, or appends if none exists. */
private List<String> setTestFilter(List<String> flags, String testFilter) {
List<String> copy = new ArrayList<>(flags);
diff --git a/base/src/com/google/idea/blaze/base/run/smrunner/BlazeTestConsoleProperties.java b/base/src/com/google/idea/blaze/base/run/smrunner/BlazeTestConsoleProperties.java
index 6582c23..f4c95b9 100644
--- a/base/src/com/google/idea/blaze/base/run/smrunner/BlazeTestConsoleProperties.java
+++ b/base/src/com/google/idea/blaze/base/run/smrunner/BlazeTestConsoleProperties.java
@@ -34,7 +34,7 @@
public BlazeTestConsoleProperties(
RunConfiguration runConfiguration, Executor executor, BlazeTestEventsHandler eventsHandler) {
- super(runConfiguration, eventsHandler.frameworkName, executor);
+ super(runConfiguration, SmRunnerUtils.BLAZE_FRAMEWORK, executor);
this.eventsHandler = eventsHandler;
}
diff --git a/base/src/com/google/idea/blaze/base/run/smrunner/BlazeTestEventsHandler.java b/base/src/com/google/idea/blaze/base/run/smrunner/BlazeTestEventsHandler.java
index 87e1b7f..2f3286f 100644
--- a/base/src/com/google/idea/blaze/base/run/smrunner/BlazeTestEventsHandler.java
+++ b/base/src/com/google/idea/blaze/base/run/smrunner/BlazeTestEventsHandler.java
@@ -17,42 +17,75 @@
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
-import com.intellij.execution.testframework.AbstractTestProxy;
+import com.google.idea.blaze.base.ideinfo.TargetIdeInfo;
+import com.google.idea.blaze.base.model.primitives.Kind;
+import com.google.idea.blaze.base.model.primitives.Label;
+import com.google.idea.blaze.base.model.primitives.TargetExpression;
+import com.google.idea.blaze.base.run.smrunner.BlazeXmlSchema.TestSuite;
+import com.google.idea.blaze.base.run.targetfinder.TargetFinder;
+import com.google.idea.blaze.base.settings.Blaze;
+import com.google.idea.blaze.base.settings.Blaze.BuildSystem;
+import com.intellij.execution.Location;
import com.intellij.execution.testframework.actions.AbstractRerunFailedTestsAction;
import com.intellij.execution.testframework.sm.runner.SMTestLocator;
import com.intellij.execution.ui.ConsoleView;
+import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.project.Project;
import com.intellij.util.io.URLUtil;
-import java.io.File;
-import java.io.IOException;
-import java.nio.file.Files;
+import java.util.EnumSet;
import java.util.List;
import javax.annotation.Nullable;
/** Language-specific handling of SM runner test protocol */
public abstract class BlazeTestEventsHandler {
- public final String frameworkName;
- public final File testOutputXml;
-
- protected BlazeTestEventsHandler(String frameworkName) {
- this.frameworkName = frameworkName;
- this.testOutputXml = generateTempTestXmlFile();
- }
+ static final ExtensionPointName<BlazeTestEventsHandler> EP_NAME =
+ ExtensionPointName.create("com.google.idea.blaze.BlazeTestEventsHandler");
/**
* Blaze/Bazel flags required for test UI.<br>
- * Forces local test execution, without sharding, and sets the output test xml path.
+ * Forces local test execution, without sharding.
*/
- public ImmutableList<String> getBlazeFlags() {
- return ImmutableList.of(
- "--test_env=XML_OUTPUT_FILE=" + testOutputXml,
- "--test_sharding_strategy=disabled",
- "--runs_per_test=1",
- "--flaky_test_attempts=1",
- "--test_strategy=local");
+ public static ImmutableList<String> getBlazeFlags(Project project) {
+ ImmutableList.Builder<String> flags =
+ ImmutableList.<String>builder()
+ .add(
+ "--test_sharding_strategy=disabled",
+ "--runs_per_test=1",
+ "--flaky_test_attempts=1");
+ if (Blaze.getBuildSystem(project) == BuildSystem.Blaze) {
+ flags.add("--test_strategy=local");
+ }
+ return flags.build();
}
+ @Nullable
+ public static BlazeTestEventsHandler getHandlerForTarget(
+ Project project, TargetExpression target) {
+ Kind kind = getKindForTarget(project, target);
+ for (BlazeTestEventsHandler handler : EP_NAME.getExtensions()) {
+ if (handler.handlesTargetKind(kind)) {
+ return handler;
+ }
+ }
+ return null;
+ }
+
+ @Nullable
+ private static Kind getKindForTarget(Project project, TargetExpression target) {
+ if (!(target instanceof Label)) {
+ return null;
+ }
+ TargetIdeInfo targetInfo = TargetFinder.getInstance().targetForLabel(project, (Label) target);
+ return targetInfo != null ? targetInfo.kind : null;
+ }
+
+ public boolean handlesTargetKind(@Nullable Kind kind) {
+ return handledKinds().contains(kind);
+ }
+
+ protected abstract EnumSet<Kind> handledKinds();
+
public abstract SMTestLocator getTestLocator();
/**
@@ -61,7 +94,7 @@
* @return null if no filter can be constructed for these tests.
*/
@Nullable
- public abstract String getTestFilter(Project project, List<AbstractTestProxy> failedTests);
+ public abstract String getTestFilter(Project project, List<Location<?>> testLocations);
@Nullable
public AbstractRerunFailedTestsAction createRerunFailedTestsAction(ConsoleView consoleView) {
@@ -69,20 +102,21 @@
}
/** Converts the testsuite name in the blaze test XML to a user-friendly format */
- public String suiteDisplayName(String rawName) {
+ public String suiteDisplayName(@Nullable Kind kind, String rawName) {
return rawName;
}
/** Converts the testcase name in the blaze test XML to a user-friendly format */
- public String testDisplayName(String rawName) {
+ public String testDisplayName(@Nullable Kind kind, String rawName) {
return rawName;
}
- public String suiteLocationUrl(String name) {
+ public String suiteLocationUrl(@Nullable Kind kind, String name) {
return SmRunnerUtils.GENERIC_SUITE_PROTOCOL + URLUtil.SCHEME_SEPARATOR + name;
}
- public String testLocationUrl(String name, @Nullable String className) {
+ public String testLocationUrl(
+ @Nullable Kind kind, String parentSuite, String name, @Nullable String className) {
String base = SmRunnerUtils.GENERIC_TEST_PROTOCOL + URLUtil.SCHEME_SEPARATOR + name;
if (Strings.isNullOrEmpty(className)) {
return base;
@@ -90,14 +124,9 @@
return base + SmRunnerUtils.TEST_NAME_PARTS_SPLITTER + className;
}
- private static File generateTempTestXmlFile() {
- try {
- File file = Files.createTempFile("blazeTest", ".xml").toFile();
- file.deleteOnExit();
- return file;
-
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
+ /** Whether to skip logging a {@link TestSuite}. */
+ public boolean ignoreSuite(TestSuite suite) {
+ // by default only include innermost 'testsuite' elements
+ return suite.testSuites.isEmpty();
}
}
diff --git a/base/src/com/google/idea/blaze/base/run/smrunner/BlazeXmlSchema.java b/base/src/com/google/idea/blaze/base/run/smrunner/BlazeXmlSchema.java
index 8f6d1f0..4126bc8 100644
--- a/base/src/com/google/idea/blaze/base/run/smrunner/BlazeXmlSchema.java
+++ b/base/src/com/google/idea/blaze/base/run/smrunner/BlazeXmlSchema.java
@@ -32,7 +32,7 @@
static {
try {
- CONTEXT = JAXBContext.newInstance(TestSuite.class);
+ CONTEXT = JAXBContext.newInstance(TestSuite.class, TestSuites.class);
} catch (JAXBException e) {
throw new RuntimeException(e);
}
@@ -40,15 +40,33 @@
static TestSuite parse(InputStream input) {
try {
- return (TestSuite) CONTEXT.createUnmarshaller().unmarshal(input);
+ Object parsed = CONTEXT.createUnmarshaller().unmarshal(input);
+ return parsed instanceof TestSuites
+ ? ((TestSuites) parsed).convertToTestSuite()
+ : (TestSuite) parsed;
+
} catch (JAXBException e) {
throw new RuntimeException("Failed to parse test XML", e);
}
}
+ // optional wrapping XML element. Some test runners don't include it.
@XmlRootElement(name = "testsuites")
- static class TestSuite {
- @XmlAttribute String name;
+ static class TestSuites {
+ @XmlElement(name = "testsuite")
+ List<TestSuite> testSuites = Lists.newArrayList();
+
+ TestSuite convertToTestSuite() {
+ TestSuite suite = new TestSuite();
+ suite.testSuites.addAll(testSuites);
+ return suite;
+ }
+ }
+
+ /** XML output by blaze test runners. */
+ @XmlRootElement(name = "testsuite")
+ public static class TestSuite {
+ public @XmlAttribute String name;
@XmlAttribute String classname;
@XmlAttribute int tests;
@XmlAttribute int failures;
@@ -70,7 +88,7 @@
ErrorOrFailureOrSkipped failure;
@XmlElement(name = "testsuite")
- List<TestSuite> testSuites = Lists.newArrayList();
+ public List<TestSuite> testSuites = Lists.newArrayList();
@XmlElement(name = "testdecorator")
List<TestSuite> testDecorators = Lists.newArrayList();
diff --git a/base/src/com/google/idea/blaze/base/run/smrunner/BlazeXmlToTestEventsConverter.java b/base/src/com/google/idea/blaze/base/run/smrunner/BlazeXmlToTestEventsConverter.java
index 82dcae8..548557a 100644
--- a/base/src/com/google/idea/blaze/base/run/smrunner/BlazeXmlToTestEventsConverter.java
+++ b/base/src/com/google/idea/blaze/base/run/smrunner/BlazeXmlToTestEventsConverter.java
@@ -15,9 +15,15 @@
*/
package com.google.idea.blaze.base.run.smrunner;
+import com.google.idea.blaze.base.ideinfo.TargetIdeInfo;
+import com.google.idea.blaze.base.model.primitives.Kind;
+import com.google.idea.blaze.base.model.primitives.Label;
import com.google.idea.blaze.base.run.smrunner.BlazeXmlSchema.ErrorOrFailureOrSkipped;
import com.google.idea.blaze.base.run.smrunner.BlazeXmlSchema.TestCase;
import com.google.idea.blaze.base.run.smrunner.BlazeXmlSchema.TestSuite;
+import com.google.idea.blaze.base.run.targetfinder.TargetFinder;
+import com.google.idea.blaze.base.run.testlogs.BlazeTestXmlFinderStrategy;
+import com.google.idea.blaze.base.run.testlogs.CompletedTestTarget;
import com.google.idea.sdkcompat.smrunner.SmRunnerCompatUtils;
import com.intellij.execution.process.ProcessOutputTypes;
import com.intellij.execution.testframework.TestConsoleProperties;
@@ -29,6 +35,7 @@
import com.intellij.execution.testframework.sm.runner.events.TestStartedEvent;
import com.intellij.execution.testframework.sm.runner.events.TestSuiteFinishedEvent;
import com.intellij.execution.testframework.sm.runner.events.TestSuiteStartedEvent;
+import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
import java.io.FileInputStream;
import java.io.InputStream;
@@ -42,6 +49,11 @@
private static final ErrorOrFailureOrSkipped NO_ERROR = new ErrorOrFailureOrSkipped();
+ {
+ NO_ERROR.message = "No message"; // cannot be null
+ }
+
+ private final Project project;
private final BlazeTestEventsHandler eventsHandler;
public BlazeXmlToTestEventsConverter(
@@ -49,6 +61,7 @@
TestConsoleProperties testConsoleProperties,
BlazeTestEventsHandler eventsHandler) {
super(testFrameworkName, testConsoleProperties);
+ this.project = testConsoleProperties.getProject();
this.eventsHandler = eventsHandler;
}
@@ -72,41 +85,52 @@
public void flushBufferBeforeTerminating() {
super.flushBufferBeforeTerminating();
onStartTesting();
- try (InputStream input = new FileInputStream(eventsHandler.testOutputXml)) {
- parseXmlInput(getProcessor(), input);
- } catch (Exception e) {
- // ignore parsing errors -- most common cause is user cancellation, which we can't easily
- // recognize.
+ getProcessor().onTestsReporterAttached();
+
+ for (CompletedTestTarget testTarget : BlazeTestXmlFinderStrategy.locateTestXmlFiles(project)) {
+ try (InputStream input = new FileInputStream(testTarget.testResultXml)) {
+ parseXmlInput(getProcessor(), getKind(project, testTarget.label), input);
+ } catch (Exception e) {
+ // ignore parsing errors -- most common cause is user cancellation, which we can't easily
+ // recognize.
+ }
}
}
- private void parseXmlInput(GeneralTestEventsProcessor processor, InputStream input) {
- TestSuite testResult = BlazeXmlSchema.parse(input);
- processor.onTestsReporterAttached();
- processTestSuite(processor, testResult);
+ @Nullable
+ private static Kind getKind(Project project, Label label) {
+ TargetIdeInfo target = TargetFinder.getInstance().targetForLabel(project, label);
+ return target != null ? target.kind : null;
}
- private void processTestSuite(GeneralTestEventsProcessor processor, TestSuite suite) {
+ private void parseXmlInput(
+ GeneralTestEventsProcessor processor, @Nullable Kind kind, InputStream input) {
+ TestSuite testResult = BlazeXmlSchema.parse(input);
+ processTestSuite(processor, kind, testResult);
+ }
+
+ private void processTestSuite(
+ GeneralTestEventsProcessor processor, @Nullable Kind kind, TestSuite suite) {
if (!hasRunChild(suite)) {
return;
}
- // don't include the outermost 'testsuites' element.
- boolean logSuite = suite.testSuites.isEmpty();
+ // only include the innermost 'testsuite' element
+ boolean logSuite = !eventsHandler.ignoreSuite(suite);
if (suite.name != null && logSuite) {
TestSuiteStarted suiteStarted =
- new TestSuiteStarted(eventsHandler.suiteDisplayName(suite.name));
- String locationUrl = eventsHandler.suiteLocationUrl(suite.name);
+ new TestSuiteStarted(eventsHandler.suiteDisplayName(kind, suite.name));
+ String locationUrl = eventsHandler.suiteLocationUrl(kind, suite.name);
processor.onSuiteStarted(new TestSuiteStartedEvent(suiteStarted, locationUrl));
}
for (TestSuite child : suite.testSuites) {
- processTestSuite(processor, child);
+ processTestSuite(processor, kind, child);
}
for (TestSuite decorator : suite.testDecorators) {
- processTestSuite(processor, decorator);
+ processTestSuite(processor, kind, decorator);
}
for (TestCase test : suite.testCases) {
- processTestCase(processor, test);
+ processTestCase(processor, kind, suite, test);
}
if (suite.sysOut != null) {
@@ -118,7 +142,7 @@
if (suite.name != null && logSuite) {
processor.onSuiteFinished(
- new TestSuiteFinishedEvent(eventsHandler.suiteDisplayName(suite.name)));
+ new TestSuiteFinishedEvent(eventsHandler.suiteDisplayName(kind, suite.name)));
}
}
@@ -138,7 +162,7 @@
}
}
for (TestCase test : suite.testCases) {
- if ("run".equals(test.status)) {
+ if (wasRun(test) && !isIgnored(test)) {
return true;
}
}
@@ -149,6 +173,14 @@
return "interrupted".equalsIgnoreCase(test.result) || "cancelled".equalsIgnoreCase(test.result);
}
+ private static boolean wasRun(TestCase test) {
+ // 'status' is not always set. In cases where it's not, tests which aren't run have a 0 runtime.
+ if (test.status != null) {
+ return test.status.equals("run");
+ }
+ return parseTimeMillis(test.time) != 0;
+ }
+
private static boolean isIgnored(TestCase test) {
if (test.skipped != null) {
return true;
@@ -162,12 +194,14 @@
return test.failure != null || test.error != null;
}
- private void processTestCase(GeneralTestEventsProcessor processor, TestCase test) {
- if (test.name == null || "notrun".equals(test.status) || isCancelled(test)) {
+ private void processTestCase(
+ GeneralTestEventsProcessor processor, @Nullable Kind kind, TestSuite parent, TestCase test) {
+ if (test.name == null || !wasRun(test) || isCancelled(test)) {
return;
}
- String displayName = eventsHandler.testDisplayName(test.name);
- String locationUrl = eventsHandler.testLocationUrl(test.name, test.classname);
+ String displayName = eventsHandler.testDisplayName(kind, test.name);
+ String locationUrl =
+ eventsHandler.testLocationUrl(kind, parent.name, test.name, test.classname);
processor.onTestStarted(new TestStartedEvent(displayName, locationUrl));
if (test.sysOut != null) {
diff --git a/base/src/com/google/idea/blaze/base/run/smrunner/CompositeSMTestLocator.java b/base/src/com/google/idea/blaze/base/run/smrunner/CompositeSMTestLocator.java
new file mode 100644
index 0000000..22588d7
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/run/smrunner/CompositeSMTestLocator.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2017 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.run.smrunner;
+
+import com.google.common.collect.ImmutableList;
+import com.intellij.execution.Location;
+import com.intellij.execution.testframework.sm.runner.SMTestLocator;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.search.GlobalSearchScope;
+import java.util.List;
+
+/** Combines multiple language-specific {@link SMTestLocator}s. */
+public class CompositeSMTestLocator implements SMTestLocator {
+
+ private final ImmutableList<SMTestLocator> locators;
+
+ protected CompositeSMTestLocator(ImmutableList<SMTestLocator> locators) {
+ this.locators = locators;
+ }
+
+ @Override
+ public List<Location> getLocation(
+ String protocol, String path, Project project, GlobalSearchScope scope) {
+ for (SMTestLocator locator : locators) {
+ List<Location> result = locator.getLocation(protocol, path, project, scope);
+ if (!result.isEmpty()) {
+ return result;
+ }
+ }
+ return ImmutableList.of();
+ }
+}
diff --git a/base/src/com/google/idea/blaze/base/run/smrunner/SmRunnerUtils.java b/base/src/com/google/idea/blaze/base/run/smrunner/SmRunnerUtils.java
index 04715a7..b9e80ba 100644
--- a/base/src/com/google/idea/blaze/base/run/smrunner/SmRunnerUtils.java
+++ b/base/src/com/google/idea/blaze/base/run/smrunner/SmRunnerUtils.java
@@ -15,17 +15,29 @@
*/
package com.google.idea.blaze.base.run.smrunner;
+import com.google.common.collect.ImmutableList;
import com.intellij.execution.DefaultExecutionResult;
import com.intellij.execution.Executor;
+import com.intellij.execution.Location;
+import com.intellij.execution.actions.ConfigurationContext;
import com.intellij.execution.configurations.RunConfiguration;
import com.intellij.execution.testframework.TestConsoleProperties;
import com.intellij.execution.testframework.actions.AbstractRerunFailedTestsAction;
import com.intellij.execution.testframework.sm.SMTestRunnerConnectionUtil;
import com.intellij.execution.testframework.sm.runner.SMTRunnerConsoleProperties;
+import com.intellij.execution.testframework.sm.runner.SMTestProxy;
import com.intellij.execution.testframework.sm.runner.ui.SMTRunnerConsoleView;
+import com.intellij.execution.testframework.sm.runner.ui.SMTRunnerTestTreeView;
import com.intellij.execution.ui.ExecutionConsole;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Disposer;
+import com.intellij.psi.search.GlobalSearchScope;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+import javax.annotation.Nullable;
+import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
/** Utility methods for setting up the SM runner test UI. */
@@ -34,6 +46,7 @@
public static final String GENERIC_SUITE_PROTOCOL = "blaze:suite";
public static final String GENERIC_TEST_PROTOCOL = "blaze:test";
public static final String TEST_NAME_PARTS_SPLITTER = "::";
+ public static final String BLAZE_FRAMEWORK = "blaze-test";
public static SMTRunnerConsoleView getConsoleView(
Project project,
@@ -44,7 +57,7 @@
new BlazeTestConsoleProperties(configuration, executor, eventsHandler);
SMTRunnerConsoleView console =
(SMTRunnerConsoleView)
- SMTestRunnerConnectionUtil.createConsole(eventsHandler.frameworkName, properties);
+ SMTestRunnerConnectionUtil.createConsole(BLAZE_FRAMEWORK, properties);
Disposer.register(project, console);
console
.getResultsViewer()
@@ -73,4 +86,30 @@
}
return result;
}
+
+ public static List<Location<?>> getSelectedSmRunnerTreeElements(ConfigurationContext context) {
+ SMTRunnerTestTreeView treeView =
+ SMTRunnerTestTreeView.SM_TEST_RUNNER_VIEW.getData(context.getDataContext());
+ if (treeView == null) {
+ return ImmutableList.of();
+ }
+ TreePath[] paths = treeView.getSelectionPaths();
+ if (paths == null || paths.length == 0) {
+ return ImmutableList.of();
+ }
+ return Arrays.stream(paths)
+ .map((path) -> toLocation(context.getProject(), treeView, path))
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
+ }
+
+ @Nullable
+ private static Location<?> toLocation(
+ Project project, SMTRunnerTestTreeView treeView, TreePath path) {
+ if (treeView.isPathSelected(path.getParentPath())) {
+ return null;
+ }
+ SMTestProxy test = treeView.getSelectedTest(path);
+ return test != null ? test.getLocation(project, GlobalSearchScope.allScope(project)) : null;
+ }
}
diff --git a/base/src/com/google/idea/blaze/base/run/state/BlazeCommandRunConfigurationCommonState.java b/base/src/com/google/idea/blaze/base/run/state/BlazeCommandRunConfigurationCommonState.java
index 1991ae6..ef2ec8e 100644
--- a/base/src/com/google/idea/blaze/base/run/state/BlazeCommandRunConfigurationCommonState.java
+++ b/base/src/com/google/idea/blaze/base/run/state/BlazeCommandRunConfigurationCommonState.java
@@ -96,11 +96,11 @@
return null;
}
- public Boolean getRunOnDistributedExecutor() {
+ public boolean getRunOnDistributedExecutor() {
return runOnDistributedExecutor.runOnDistributedExecutor;
}
- public void setRunOnDistributedExecutor(Boolean runOnDistributedExecutor) {
+ public void setRunOnDistributedExecutor(boolean runOnDistributedExecutor) {
this.runOnDistributedExecutor.runOnDistributedExecutor = runOnDistributedExecutor;
}
diff --git a/base/src/com/google/idea/blaze/base/run/state/BlazeRunOnDistributedExecutorState.java b/base/src/com/google/idea/blaze/base/run/state/BlazeRunOnDistributedExecutorState.java
index b251d3f..556b3f3 100644
--- a/base/src/com/google/idea/blaze/base/run/state/BlazeRunOnDistributedExecutorState.java
+++ b/base/src/com/google/idea/blaze/base/run/state/BlazeRunOnDistributedExecutorState.java
@@ -29,8 +29,8 @@
import org.jdom.Element;
/**
- * Provides an option to run blaze/bazel on a distributed executor, if available, rather than
- * locally.
+ * Provides an option to run blaze/bazel on a distributed executor, if available. If unchecked, we
+ * fall back to whatever the default is.
*/
public class BlazeRunOnDistributedExecutorState implements RunConfigurationState {
@@ -39,10 +39,10 @@
@Nullable private final DistributedExecutorSupport executorInfo;
- public Boolean runOnDistributedExecutor = null;
+ public boolean runOnDistributedExecutor;
BlazeRunOnDistributedExecutorState(BuildSystem buildSystem) {
- this.executorInfo = DistributedExecutorSupport.getAvailableExecutor(buildSystem);
+ executorInfo = DistributedExecutorSupport.getAvailableExecutor(buildSystem);
}
@Override
@@ -55,7 +55,7 @@
@Override
public void writeExternal(Element element) throws WriteExternalException {
- if (executorInfo != null && runOnDistributedExecutor != null) {
+ if (executorInfo != null && runOnDistributedExecutor) {
element.setAttribute(
RUN_ON_DISTRIBUTED_EXECUTOR_ATTR, Boolean.toString(runOnDistributedExecutor));
} else {
@@ -71,12 +71,11 @@
/** Editor for {@link BlazeRunOnDistributedExecutorState} */
class RunOnExecutorStateEditor implements RunConfigurationStateEditor {
- private final JBCheckBox checkBox =
- new JBCheckBox("Run on " + (executorInfo != null ? executorInfo.executorName() : null));
+ private final String executorName =
+ executorInfo != null ? executorInfo.executorName() : "distributed executor";
+ private final JBCheckBox checkBox = new JBCheckBox("Run on " + executorName);
private final JLabel warning =
- new JLabel(
- "Warning: test UI integration is not available when running on distributed "
- + "executor");
+ new JLabel("Warning: test UI integration is not available when running on " + executorName);
private boolean componentVisible = executorInfo != null;
private boolean isTest = false;
@@ -90,9 +89,7 @@
@Override
public void resetEditorFrom(RunConfigurationState genericState) {
BlazeRunOnDistributedExecutorState state = (BlazeRunOnDistributedExecutorState) genericState;
- if (state.runOnDistributedExecutor != null) {
- checkBox.setSelected(state.runOnDistributedExecutor);
- }
+ checkBox.setSelected(state.runOnDistributedExecutor);
}
@Override
diff --git a/base/src/com/google/idea/blaze/base/run/testlogs/BlazeCommandLogParser.java b/base/src/com/google/idea/blaze/base/run/testlogs/BlazeCommandLogParser.java
new file mode 100644
index 0000000..9993c83
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/run/testlogs/BlazeCommandLogParser.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2017 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.run.testlogs;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableSet;
+import com.google.idea.blaze.base.model.primitives.Label;
+import com.intellij.openapi.diagnostic.Logger;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.Objects;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import javax.annotation.Nullable;
+
+/** Parses the list of test targets run from the bazel command log */
+public class BlazeCommandLogParser {
+
+ private static final Logger logger = Logger.getInstance(BlazeCommandLogParser.class);
+
+ private static final Pattern TEST_LOG = Pattern.compile("^(//[^\\s]*) .*? (PASSED|FAILED)");
+
+ /** Finds log location and target label for all tests listed in the master log. */
+ public static ImmutableSet<Label> parseTestTargets(File commandLog) {
+ try (Stream<String> stream = Files.lines(Paths.get(commandLog.getPath()))) {
+ return parseTestTargets(stream);
+ } catch (IOException e) {
+ logger.warn("Error parsing master log", e);
+ return ImmutableSet.of();
+ }
+ }
+
+ @VisibleForTesting
+ static ImmutableSet<Label> parseTestTargets(Stream<String> lines) {
+ return ImmutableSet.copyOf(
+ lines
+ .map(BlazeCommandLogParser::parseTestTarget)
+ .filter(Objects::nonNull)
+ .collect(Collectors.toSet()));
+ }
+
+ @Nullable
+ @VisibleForTesting
+ static Label parseTestTarget(String line) {
+ Matcher match = TEST_LOG.matcher(line);
+ if (!match.find()) {
+ return null;
+ }
+ return Label.createIfValid(match.group(1));
+ }
+}
diff --git a/base/src/com/google/idea/blaze/base/run/testlogs/BlazeTestLogParser.java b/base/src/com/google/idea/blaze/base/run/testlogs/BlazeTestLogParser.java
new file mode 100644
index 0000000..a262398
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/run/testlogs/BlazeTestLogParser.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2017 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.run.testlogs;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.intellij.openapi.diagnostic.Logger;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.Objects;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Stream;
+import javax.annotation.Nullable;
+
+/** Parses an individual test target's log file produced by blaze. */
+public class BlazeTestLogParser {
+
+ private static final Logger logger = Logger.getInstance(BlazeTestLogParser.class);
+
+ private static final Pattern XML_LOCATION = Pattern.compile("XML_OUTPUT_FILE=([^\\s]*)");
+
+ /** Finds log location and target label for all tests listed in the master log. */
+ @Nullable
+ public static File findTestXmlFile(File testLog) {
+ try (Stream<String> stream = Files.lines(Paths.get(testLog.getPath()))) {
+ return parseTestXmlFile(stream);
+ } catch (IOException e) {
+ logger.warn("Error parsing test log", e);
+ return null;
+ }
+ }
+
+ @Nullable
+ @VisibleForTesting
+ static File parseTestXmlFile(Stream<String> stream) {
+ return stream
+ .map(BlazeTestLogParser::parseXmlLocation)
+ .filter(Objects::nonNull)
+ .findFirst()
+ .orElse(null);
+ }
+
+ @Nullable
+ @VisibleForTesting
+ static File parseXmlLocation(String line) {
+ Matcher match = XML_LOCATION.matcher(line);
+ if (!match.find()) {
+ return null;
+ }
+ return new File(match.group(1));
+ }
+}
diff --git a/base/src/com/google/idea/blaze/base/run/testlogs/BlazeTestXmlFinderStrategy.java b/base/src/com/google/idea/blaze/base/run/testlogs/BlazeTestXmlFinderStrategy.java
new file mode 100644
index 0000000..174d512
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/run/testlogs/BlazeTestXmlFinderStrategy.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2017 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.run.testlogs;
+
+import com.google.common.collect.ImmutableList;
+import com.google.idea.blaze.base.settings.Blaze;
+import com.google.idea.blaze.base.settings.Blaze.BuildSystem;
+import com.intellij.openapi.extensions.ExtensionPointName;
+import com.intellij.openapi.project.Project;
+
+/** A strategy for locating output test XML files. */
+public interface BlazeTestXmlFinderStrategy {
+
+ ExtensionPointName<BlazeTestXmlFinderStrategy> EP_NAME =
+ ExtensionPointName.create("com.google.idea.blaze.BlazeTestXmlFinderStrategy");
+
+ /**
+ * Attempt to find all output test XML files associated with the given run configuration. Called
+ * after the 'blaze test' process completes.
+ */
+ static ImmutableList<CompletedTestTarget> locateTestXmlFiles(Project project) {
+ BuildSystem buildSystem = Blaze.getBuildSystem(project);
+ ImmutableList.Builder<CompletedTestTarget> output = ImmutableList.builder();
+ for (BlazeTestXmlFinderStrategy strategy : EP_NAME.getExtensions()) {
+ if (strategy.handlesBuildSystem(buildSystem)) {
+ output.addAll(strategy.findTestXmlFiles(project));
+ }
+ }
+ return output.build();
+ }
+
+ /**
+ * Attempt to find all output test XML files associated with the given run configuration using a
+ * particular strategy. Called after the 'blaze test' process completes.
+ */
+ ImmutableList<CompletedTestTarget> findTestXmlFiles(Project project);
+
+ boolean handlesBuildSystem(BuildSystem buildSystem);
+}
diff --git a/base/src/com/google/idea/blaze/base/run/testlogs/CompletedTestTarget.java b/base/src/com/google/idea/blaze/base/run/testlogs/CompletedTestTarget.java
new file mode 100644
index 0000000..bf0b33b
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/run/testlogs/CompletedTestTarget.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2017 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.run.testlogs;
+
+import com.google.idea.blaze.base.model.primitives.Label;
+import java.io.File;
+
+/** Information relating to a completed test target. */
+public class CompletedTestTarget {
+
+ public final File testResultXml;
+ public final Label label;
+
+ public CompletedTestTarget(File testResultXml, Label label) {
+ this.testResultXml = testResultXml;
+ this.label = label;
+ }
+}
diff --git a/base/src/com/google/idea/blaze/base/run/testlogs/TargetPathTestXmlFinderStrategy.java b/base/src/com/google/idea/blaze/base/run/testlogs/TargetPathTestXmlFinderStrategy.java
new file mode 100644
index 0000000..ce5ec40
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/run/testlogs/TargetPathTestXmlFinderStrategy.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2017 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.run.testlogs;
+
+import com.google.common.collect.ImmutableList;
+import com.google.idea.blaze.base.command.info.BlazeInfo;
+import com.google.idea.blaze.base.model.BlazeProjectData;
+import com.google.idea.blaze.base.model.primitives.Label;
+import com.google.idea.blaze.base.settings.Blaze;
+import com.google.idea.blaze.base.settings.Blaze.BuildSystem;
+import com.google.idea.blaze.base.sync.data.BlazeProjectDataManager;
+import com.intellij.openapi.project.Project;
+import java.io.File;
+import java.util.Objects;
+import java.util.stream.Collectors;
+import javax.annotation.Nullable;
+
+/**
+ * Attempts to parse the list of test targets from the command log, then searches the corresponding
+ * path in the bazel-testlogs output tree.
+ */
+public class TargetPathTestXmlFinderStrategy implements BlazeTestXmlFinderStrategy {
+
+ @Override
+ public boolean handlesBuildSystem(BuildSystem buildSystem) {
+ return buildSystem == BuildSystem.Bazel;
+ }
+
+ @Override
+ public ImmutableList<CompletedTestTarget> findTestXmlFiles(Project project) {
+ File testLogsDir = getTestLogsTree(project);
+ if (testLogsDir == null) {
+ return ImmutableList.of();
+ }
+ File commandLog = getCommandLog(project);
+ if (commandLog == null) {
+ return ImmutableList.of();
+ }
+ return ImmutableList.copyOf(
+ BlazeCommandLogParser.parseTestTargets(commandLog)
+ .stream()
+ .map((label) -> toKindAndTestXml(testLogsDir, label))
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList()));
+ }
+
+ @Nullable
+ private static CompletedTestTarget toKindAndTestXml(File testLogsDir, Label label) {
+ String labelPath = label.blazePackage() + File.separator + label.targetName();
+ File testXml = new File(testLogsDir, labelPath + File.separator + "test.xml");
+ return new CompletedTestTarget(testXml, label);
+ }
+
+ @Nullable
+ private static File getTestLogsTree(Project project) {
+ BlazeProjectData projectData =
+ BlazeProjectDataManager.getInstance(project).getBlazeProjectData();
+ if (projectData == null) {
+ return null;
+ }
+ String testLogsLocation =
+ projectData.blazeInfo.get(BlazeInfo.blazeTestlogsKey(Blaze.getBuildSystem(project)));
+ return testLogsLocation != null ? new File(testLogsLocation) : null;
+ }
+
+ @Nullable
+ private static File getCommandLog(Project project) {
+ BlazeProjectData projectData =
+ BlazeProjectDataManager.getInstance(project).getBlazeProjectData();
+ if (projectData == null) {
+ return null;
+ }
+ String commandLogLocation = projectData.blazeInfo.get(BlazeInfo.COMMAND_LOG);
+ return commandLogLocation != null ? new File(commandLogLocation) : null;
+ }
+}
diff --git a/base/src/com/google/idea/blaze/base/run/testmap/FilteredTargetMap.java b/base/src/com/google/idea/blaze/base/run/testmap/FilteredTargetMap.java
new file mode 100644
index 0000000..34080d4
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/run/testmap/FilteredTargetMap.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2017 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.run.testmap;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Queues;
+import com.google.common.collect.Sets;
+import com.google.idea.blaze.base.ideinfo.ArtifactLocation;
+import com.google.idea.blaze.base.ideinfo.TargetIdeInfo;
+import com.google.idea.blaze.base.ideinfo.TargetKey;
+import com.google.idea.blaze.base.ideinfo.TargetMap;
+import com.google.idea.blaze.base.model.BlazeProjectData;
+import com.google.idea.blaze.base.model.primitives.Label;
+import com.google.idea.blaze.base.sync.data.BlazeProjectDataManager;
+import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoder;
+import com.intellij.openapi.project.Project;
+import java.io.File;
+import java.util.Collection;
+import java.util.List;
+import java.util.Queue;
+import java.util.Set;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+/** Filters a {@link TargetMap} according to a given filter. */
+public class FilteredTargetMap {
+
+ private final Project project;
+ private final Multimap<File, TargetKey> rootsMap;
+ private final TargetMap targetMap;
+ private final Predicate<TargetIdeInfo> filter;
+
+ public FilteredTargetMap(
+ Project project,
+ ArtifactLocationDecoder artifactLocationDecoder,
+ TargetMap targetMap,
+ Predicate<TargetIdeInfo> filter) {
+ this.project = project;
+ this.rootsMap = createRootsMap(artifactLocationDecoder, targetMap.targets());
+ this.targetMap = targetMap;
+ this.filter = filter;
+ }
+
+ public Collection<TargetIdeInfo> targetsForSourceFile(File sourceFile) {
+ BlazeProjectData blazeProjectData =
+ BlazeProjectDataManager.getInstance(project).getBlazeProjectData();
+ if (blazeProjectData != null) {
+ return targetsForSourceFileImpl(blazeProjectData.reverseDependencies, sourceFile);
+ }
+ return ImmutableList.of();
+ }
+
+ @VisibleForTesting
+ Collection<Label> targetsForSourceFile(
+ ImmutableMultimap<TargetKey, TargetKey> rdepsMap, File sourceFile) {
+ return targetsForSourceFileImpl(rdepsMap, sourceFile)
+ .stream()
+ .filter(TargetIdeInfo::isPlainTarget)
+ .map(target -> target.key.label)
+ .collect(Collectors.toList());
+ }
+
+ private Collection<TargetIdeInfo> targetsForSourceFileImpl(
+ ImmutableMultimap<TargetKey, TargetKey> rdepsMap, File sourceFile) {
+ List<TargetIdeInfo> result = Lists.newArrayList();
+ Collection<TargetKey> roots = rootsMap.get(sourceFile);
+
+ Queue<TargetKey> todo = Queues.newArrayDeque();
+ for (TargetKey label : roots) {
+ todo.add(label);
+ }
+ Set<TargetKey> seen = Sets.newHashSet();
+ while (!todo.isEmpty()) {
+ TargetKey targetKey = todo.remove();
+ if (!seen.add(targetKey)) {
+ continue;
+ }
+
+ TargetIdeInfo target = targetMap.get(targetKey);
+ if (filter.test(target)) {
+ result.add(target);
+ }
+ for (TargetKey rdep : rdepsMap.get(targetKey)) {
+ todo.add(rdep);
+ }
+ }
+ return result;
+ }
+
+ private static Multimap<File, TargetKey> createRootsMap(
+ ArtifactLocationDecoder artifactLocationDecoder, Collection<TargetIdeInfo> targets) {
+ Multimap<File, TargetKey> result = ArrayListMultimap.create();
+ for (TargetIdeInfo target : targets) {
+ for (ArtifactLocation source : target.sources) {
+ result.put(artifactLocationDecoder.decode(source), target.key);
+ }
+ }
+ return result;
+ }
+}
diff --git a/base/src/com/google/idea/blaze/base/run/testmap/TestTargetFilterImpl.java b/base/src/com/google/idea/blaze/base/run/testmap/TestTargetFilterImpl.java
index 99d490f..3a74582 100644
--- a/base/src/com/google/idea/blaze/base/run/testmap/TestTargetFilterImpl.java
+++ b/base/src/com/google/idea/blaze/base/run/testmap/TestTargetFilterImpl.java
@@ -16,35 +16,17 @@
package com.google.idea.blaze.base.run.testmap;
import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMultimap;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Multimap;
-import com.google.common.collect.Queues;
-import com.google.common.collect.Sets;
-import com.google.idea.blaze.base.ideinfo.ArtifactLocation;
import com.google.idea.blaze.base.ideinfo.TargetIdeInfo;
-import com.google.idea.blaze.base.ideinfo.TargetKey;
import com.google.idea.blaze.base.ideinfo.TargetMap;
import com.google.idea.blaze.base.model.BlazeProjectData;
import com.google.idea.blaze.base.model.primitives.Kind;
-import com.google.idea.blaze.base.model.primitives.Label;
-import com.google.idea.blaze.base.projectview.ProjectViewSet;
import com.google.idea.blaze.base.run.TestTargetFinder;
-import com.google.idea.blaze.base.scope.BlazeContext;
-import com.google.idea.blaze.base.settings.BlazeImportSettings;
-import com.google.idea.blaze.base.sync.BlazeSyncParams.SyncMode;
-import com.google.idea.blaze.base.sync.SyncListener;
-import com.google.idea.blaze.base.sync.data.BlazeProjectDataManager;
+import com.google.idea.blaze.base.sync.SyncCache;
import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoder;
import com.intellij.openapi.project.Project;
import java.io.File;
import java.util.Collection;
-import java.util.List;
-import java.util.Queue;
-import java.util.Set;
-import java.util.stream.Collectors;
import javax.annotation.Nullable;
/**
@@ -55,89 +37,6 @@
public class TestTargetFilterImpl implements TestTargetFinder {
private final Project project;
- @Nullable private TestMap testMap;
-
- static class TestMap {
- private final Project project;
- private final Multimap<File, TargetKey> rootsMap;
- private final TargetMap targetMap;
-
- TestMap(Project project, ArtifactLocationDecoder artifactLocationDecoder, TargetMap targetMap) {
- this.project = project;
- this.rootsMap = createRootsMap(artifactLocationDecoder, targetMap.targets());
- this.targetMap = targetMap;
- }
-
- private Collection<TargetIdeInfo> testTargetsForSourceFile(File sourceFile) {
- BlazeProjectData blazeProjectData =
- BlazeProjectDataManager.getInstance(project).getBlazeProjectData();
- if (blazeProjectData != null) {
- return testTargetsForSourceFileImpl(blazeProjectData.reverseDependencies, sourceFile);
- }
- return ImmutableList.of();
- }
-
- @VisibleForTesting
- Collection<Label> testTargetsForSourceFile(
- ImmutableMultimap<TargetKey, TargetKey> rdepsMap, File sourceFile) {
- return testTargetsForSourceFileImpl(rdepsMap, sourceFile)
- .stream()
- .filter(TargetIdeInfo::isPlainTarget)
- .map(target -> target.key.label)
- .collect(Collectors.toList());
- }
-
- Collection<TargetIdeInfo> testTargetsForSourceFileImpl(
- ImmutableMultimap<TargetKey, TargetKey> rdepsMap, File sourceFile) {
- List<TargetIdeInfo> result = Lists.newArrayList();
- Collection<TargetKey> roots = rootsMap.get(sourceFile);
-
- Queue<TargetKey> todo = Queues.newArrayDeque();
- for (TargetKey label : roots) {
- todo.add(label);
- }
- Set<TargetKey> seen = Sets.newHashSet();
- while (!todo.isEmpty()) {
- TargetKey targetKey = todo.remove();
- if (!seen.add(targetKey)) {
- continue;
- }
-
- TargetIdeInfo target = targetMap.get(targetKey);
- if (isTestTarget(target)) {
- result.add(target);
- }
- for (TargetKey rdep : rdepsMap.get(targetKey)) {
- todo.add(rdep);
- }
- }
- return result;
- }
-
- static Multimap<File, TargetKey> createRootsMap(
- ArtifactLocationDecoder artifactLocationDecoder, Collection<TargetIdeInfo> targets) {
- Multimap<File, TargetKey> result = ArrayListMultimap.create();
- for (TargetIdeInfo target : targets) {
- for (ArtifactLocation source : target.sources) {
- result.put(artifactLocationDecoder.decode(source), target.key);
- }
- }
- return result;
- }
-
- private static boolean isTestTarget(@Nullable TargetIdeInfo target) {
- return target != null
- && target.kind != null
- && target.kind.isOneOf(
- Kind.ANDROID_ROBOLECTRIC_TEST,
- Kind.ANDROID_TEST,
- Kind.JAVA_TEST,
- Kind.GWT_TEST,
- Kind.CC_TEST,
- Kind.PY_TEST,
- Kind.GO_TEST);
- }
- }
public TestTargetFilterImpl(Project project) {
this.project = project;
@@ -145,47 +44,35 @@
@Override
public Collection<TargetIdeInfo> testTargetsForSourceFile(File sourceFile) {
- TestMap testMap = getTestMap();
+ FilteredTargetMap testMap =
+ SyncCache.getInstance(project)
+ .get(TestTargetFilterImpl.class, TestTargetFilterImpl::computeTestMap);
if (testMap == null) {
return ImmutableList.of();
}
- return testMap.testTargetsForSourceFile(sourceFile);
+ return testMap.targetsForSourceFile(sourceFile);
}
- private synchronized TestMap getTestMap() {
- if (testMap == null) {
- testMap = initTestMap();
- }
- return testMap;
+ private static FilteredTargetMap computeTestMap(Project project, BlazeProjectData projectData) {
+ return computeTestMap(project, projectData.artifactLocationDecoder, projectData.targetMap);
}
- @Nullable
- private TestMap initTestMap() {
- BlazeProjectData blazeProjectData =
- BlazeProjectDataManager.getInstance(project).getBlazeProjectData();
- if (blazeProjectData == null) {
- return null;
- }
- return new TestMap(
- project, blazeProjectData.artifactLocationDecoder, blazeProjectData.targetMap);
+ @VisibleForTesting
+ static FilteredTargetMap computeTestMap(
+ Project project, ArtifactLocationDecoder decoder, TargetMap targetMap) {
+ return new FilteredTargetMap(project, decoder, targetMap, TestTargetFilterImpl::isTestTarget);
}
- private synchronized void clearMapData() {
- this.testMap = null;
- }
-
- static class ClearTestMap extends SyncListener.Adapter {
- @Override
- public void onSyncComplete(
- Project project,
- BlazeContext context,
- BlazeImportSettings importSettings,
- ProjectViewSet projectViewSet,
- BlazeProjectData blazeProjectData,
- SyncMode syncMode,
- SyncResult syncResult) {
- TestTargetFinder testTargetFinder = TestTargetFinder.getInstance(project);
- ((TestTargetFilterImpl) testTargetFinder).clearMapData();
- }
+ private static boolean isTestTarget(@Nullable TargetIdeInfo target) {
+ return target != null
+ && target.kind != null
+ && target.kind.isOneOf(
+ Kind.ANDROID_ROBOLECTRIC_TEST,
+ Kind.ANDROID_TEST,
+ Kind.JAVA_TEST,
+ Kind.GWT_TEST,
+ Kind.CC_TEST,
+ Kind.PY_TEST,
+ Kind.GO_TEST);
}
}
diff --git a/base/src/com/google/idea/blaze/base/scope/Scope.java b/base/src/com/google/idea/blaze/base/scope/Scope.java
index bb0c98d..4c0f12a 100644
--- a/base/src/com/google/idea/blaze/base/scope/Scope.java
+++ b/base/src/com/google/idea/blaze/base/scope/Scope.java
@@ -21,7 +21,7 @@
/** Helper methods to run scoped functions and operations in a scoped context. */
public final class Scope {
- private static final Logger LOG = Logger.getInstance(Scope.class);
+ private static final Logger logger = Logger.getInstance(Scope.class);
/** Runs a scoped function in a new root scope. */
public static <T> T root(@NotNull ScopedFunction<T> scopedFunction) {
@@ -36,7 +36,7 @@
return scopedFunction.execute(context);
} catch (RuntimeException e) {
context.setHasError();
- LOG.error(e);
+ logger.error(e);
throw e;
} finally {
context.endScope();
@@ -56,7 +56,7 @@
scopedOperation.execute(context);
} catch (RuntimeException e) {
context.setHasError();
- LOG.error(e);
+ logger.error(e);
throw e;
} finally {
context.endScope();
diff --git a/base/src/com/google/idea/blaze/base/scope/scopes/BlazeConsoleScope.java b/base/src/com/google/idea/blaze/base/scope/scopes/BlazeConsoleScope.java
index 19205cf..1875f20 100644
--- a/base/src/com/google/idea/blaze/base/scope/scopes/BlazeConsoleScope.java
+++ b/base/src/com/google/idea/blaze/base/scope/scopes/BlazeConsoleScope.java
@@ -16,6 +16,8 @@
package com.google.idea.blaze.base.scope.scopes;
import com.google.idea.blaze.base.console.BlazeConsoleService;
+import com.google.idea.blaze.base.console.ColoredConsoleStream;
+import com.google.idea.blaze.base.console.ConsoleStream;
import com.google.idea.blaze.base.scope.BlazeContext;
import com.google.idea.blaze.base.scope.BlazeScope;
import com.google.idea.blaze.base.scope.OutputSink;
@@ -37,6 +39,7 @@
private Project project;
private ProgressIndicator progressIndicator;
private boolean suppressConsole = false;
+ private boolean escapeAnsiColorCodes = false;
public Builder(@NotNull Project project) {
this(project, null);
@@ -52,13 +55,17 @@
return this;
}
+ public Builder escapeAnsiColorcodes(boolean escapeAnsiColorCodes) {
+ this.escapeAnsiColorCodes = escapeAnsiColorCodes;
+ return this;
+ }
+
public BlazeConsoleScope build() {
- return new BlazeConsoleScope(project, progressIndicator, suppressConsole);
+ return new BlazeConsoleScope(
+ project, progressIndicator, suppressConsole, escapeAnsiColorCodes);
}
}
- @NotNull private final Project project;
-
@NotNull private final BlazeConsoleService blazeConsoleService;
@Nullable private final ProgressIndicator progressIndicator;
@@ -66,6 +73,8 @@
private final boolean showDialogOnChange;
private boolean activated;
+ private final ConsoleStream consoleStream;
+
private OutputSink<PrintOutput> printSink =
(output) -> {
@NotNull String text = output.getText();
@@ -89,20 +98,23 @@
private BlazeConsoleScope(
@NotNull Project project,
@Nullable ProgressIndicator progressIndicator,
- boolean suppressConsole) {
- this.project = project;
+ boolean suppressConsole,
+ boolean escapeAnsiColorCodes) {
this.blazeConsoleService = BlazeConsoleService.getInstance(project);
this.progressIndicator = progressIndicator;
this.showDialogOnChange = !suppressConsole;
+ ConsoleStream sinkConsoleStream = blazeConsoleService::print;
+ this.consoleStream =
+ escapeAnsiColorCodes ? new ColoredConsoleStream(sinkConsoleStream) : sinkConsoleStream;
}
private void print(String text, ConsoleViewContentType contentType) {
- blazeConsoleService.print(text + "\n", contentType);
+ consoleStream.print(text, contentType);
+ consoleStream.print("\n", contentType);
if (showDialogOnChange && !activated) {
activated = true;
- ApplicationManager.getApplication()
- .invokeLater(() -> blazeConsoleService.activateConsoleWindow());
+ ApplicationManager.getApplication().invokeLater(blazeConsoleService::activateConsoleWindow);
}
}
diff --git a/base/src/com/google/idea/blaze/base/scope/scopes/IdeaLogScope.java b/base/src/com/google/idea/blaze/base/scope/scopes/IdeaLogScope.java
index 4a1130c..dc86561 100644
--- a/base/src/com/google/idea/blaze/base/scope/scopes/IdeaLogScope.java
+++ b/base/src/com/google/idea/blaze/base/scope/scopes/IdeaLogScope.java
@@ -27,11 +27,11 @@
/** Scope that captures relevant output to the IntelliJ log file. */
public class IdeaLogScope implements BlazeScope {
- private static final Logger LOG = Logger.getInstance(IdeaLogScope.class);
+ private static final Logger logger = Logger.getInstance(IdeaLogScope.class);
private static final OutputSink<IssueOutput> issueSink =
(output) -> {
- LOG.warn(output.toString());
+ logger.warn(output.toString());
return OutputSink.Propagation.Continue;
};
@@ -41,10 +41,10 @@
case NORMAL:
break;
case LOGGED:
- LOG.info(output.getText());
+ logger.info(output.getText());
break;
case ERROR:
- LOG.warn(output.getText());
+ logger.warn(output.getText());
break;
}
return OutputSink.Propagation.Continue;
@@ -52,7 +52,7 @@
private static final OutputSink<StatusOutput> statusSink =
(output) -> {
- LOG.info(output.getStatus());
+ logger.info(output.getStatus());
return OutputSink.Propagation.Continue;
};
diff --git a/base/src/com/google/idea/blaze/base/scope/scopes/LoggedTimingScope.java b/base/src/com/google/idea/blaze/base/scope/scopes/LoggedTimingScope.java
deleted file mode 100644
index 3b34acd..0000000
--- a/base/src/com/google/idea/blaze/base/scope/scopes/LoggedTimingScope.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 2016 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.scope.scopes;
-
-import com.google.common.base.Stopwatch;
-import com.google.idea.blaze.base.metrics.Action;
-import com.google.idea.blaze.base.metrics.LoggingService;
-import com.google.idea.blaze.base.scope.BlazeContext;
-import com.google.idea.blaze.base.scope.BlazeScope;
-import com.intellij.openapi.project.Project;
-import java.util.concurrent.TimeUnit;
-
-/** Timing scope where the results are sent to a logging service */
-public class LoggedTimingScope implements BlazeScope {
- // It is not guaranteed that the threading model will be sane during the entirety of this scope,
- // so we use wall clock time and not ThreadMXBean where we could get user/system time.
-
- Project project;
- private final Action action;
- private Stopwatch timer;
-
- /** @param action The action we will be reporting a time for to the logging service */
- public LoggedTimingScope(Project project, Action action) {
- this.project = project;
- this.action = action;
- this.timer = Stopwatch.createUnstarted();
- }
-
- @Override
- public void onScopeBegin(BlazeContext context) {
- timer.start();
- }
-
- @Override
- public void onScopeEnd(BlazeContext context) {
- if (!context.isCancelled()) {
- long totalMS = timer.elapsed(TimeUnit.MILLISECONDS);
- LoggingService.reportEvent(project, action, totalMS);
- }
- }
-}
diff --git a/base/src/com/google/idea/blaze/base/settings/ui/EditProjectViewAction.java b/base/src/com/google/idea/blaze/base/settings/ui/OpenAllProjectViewsAction.java
similarity index 62%
rename from base/src/com/google/idea/blaze/base/settings/ui/EditProjectViewAction.java
rename to base/src/com/google/idea/blaze/base/settings/ui/OpenAllProjectViewsAction.java
index 926483b..d1e572e 100644
--- a/base/src/com/google/idea/blaze/base/settings/ui/EditProjectViewAction.java
+++ b/base/src/com/google/idea/blaze/base/settings/ui/OpenAllProjectViewsAction.java
@@ -19,15 +19,10 @@
import com.google.idea.blaze.base.projectview.ProjectViewManager;
import com.google.idea.blaze.base.projectview.ProjectViewSet;
import com.intellij.openapi.actionSystem.AnActionEvent;
-import com.intellij.openapi.fileEditor.FileEditorManager;
-import com.intellij.openapi.fileEditor.OpenFileDescriptor;
import com.intellij.openapi.project.Project;
-import com.intellij.openapi.vfs.VfsUtil;
-import com.intellij.openapi.vfs.VirtualFile;
-import java.io.File;
/** Opens all the user's project views. */
-public class EditProjectViewAction extends BlazeProjectAction {
+public class OpenAllProjectViewsAction extends BlazeProjectAction {
@Override
protected void actionPerformedInBlazeProject(Project project, AnActionEvent e) {
@@ -35,15 +30,8 @@
if (projectViewSet == null) {
return;
}
- for (ProjectViewSet.ProjectViewFile projectViewFile : projectViewSet.getProjectViewFiles()) {
- File file = projectViewFile.projectViewFile;
- if (file != null) {
- VirtualFile virtualFile = VfsUtil.findFileByIoFile(file, true);
- if (virtualFile != null) {
- OpenFileDescriptor descriptor = new OpenFileDescriptor(project, virtualFile);
- FileEditorManager.getInstance(project).openTextEditor(descriptor, true);
- }
- }
- }
+ projectViewSet
+ .getProjectViewFiles()
+ .forEach(f -> ProjectViewHelper.openProjectViewFile(project, f));
}
}
diff --git a/base/src/com/google/idea/blaze/base/settings/ui/EditProjectViewAction.java b/base/src/com/google/idea/blaze/base/settings/ui/OpenLocalProjectViewAction.java
similarity index 60%
copy from base/src/com/google/idea/blaze/base/settings/ui/EditProjectViewAction.java
copy to base/src/com/google/idea/blaze/base/settings/ui/OpenLocalProjectViewAction.java
index 926483b..0b40fb8 100644
--- a/base/src/com/google/idea/blaze/base/settings/ui/EditProjectViewAction.java
+++ b/base/src/com/google/idea/blaze/base/settings/ui/OpenLocalProjectViewAction.java
@@ -19,15 +19,10 @@
import com.google.idea.blaze.base.projectview.ProjectViewManager;
import com.google.idea.blaze.base.projectview.ProjectViewSet;
import com.intellij.openapi.actionSystem.AnActionEvent;
-import com.intellij.openapi.fileEditor.FileEditorManager;
-import com.intellij.openapi.fileEditor.OpenFileDescriptor;
import com.intellij.openapi.project.Project;
-import com.intellij.openapi.vfs.VfsUtil;
-import com.intellij.openapi.vfs.VirtualFile;
-import java.io.File;
-/** Opens all the user's project views. */
-public class EditProjectViewAction extends BlazeProjectAction {
+/** Opens the user's local project view file. */
+public class OpenLocalProjectViewAction extends BlazeProjectAction {
@Override
protected void actionPerformedInBlazeProject(Project project, AnActionEvent e) {
@@ -35,15 +30,6 @@
if (projectViewSet == null) {
return;
}
- for (ProjectViewSet.ProjectViewFile projectViewFile : projectViewSet.getProjectViewFiles()) {
- File file = projectViewFile.projectViewFile;
- if (file != null) {
- VirtualFile virtualFile = VfsUtil.findFileByIoFile(file, true);
- if (virtualFile != null) {
- OpenFileDescriptor descriptor = new OpenFileDescriptor(project, virtualFile);
- FileEditorManager.getInstance(project).openTextEditor(descriptor, true);
- }
- }
- }
+ ProjectViewHelper.openProjectViewFile(project, projectViewSet.getTopLevelProjectViewFile());
}
}
diff --git a/base/src/com/google/idea/blaze/base/settings/ui/ProjectViewHelper.java b/base/src/com/google/idea/blaze/base/settings/ui/ProjectViewHelper.java
new file mode 100644
index 0000000..7c95d50
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/settings/ui/ProjectViewHelper.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2016 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.settings.ui;
+
+import com.google.idea.blaze.base.projectview.ProjectViewSet.ProjectViewFile;
+import com.intellij.openapi.fileEditor.FileEditorManager;
+import com.intellij.openapi.fileEditor.OpenFileDescriptor;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import java.io.File;
+
+/** Helper methods for manipulating project views. */
+final class ProjectViewHelper {
+ private ProjectViewHelper() {}
+
+ static void openProjectViewFile(Project project, ProjectViewFile projectViewFile) {
+ File file = projectViewFile.projectViewFile;
+ if (file != null) {
+ VirtualFile virtualFile = VfsUtil.findFileByIoFile(file, true);
+ if (virtualFile != null) {
+ OpenFileDescriptor descriptor = new OpenFileDescriptor(project, virtualFile);
+ FileEditorManager.getInstance(project).openTextEditor(descriptor, true);
+ }
+ }
+ }
+}
diff --git a/base/src/com/google/idea/blaze/base/sync/BlazeSyncPlugin.java b/base/src/com/google/idea/blaze/base/sync/BlazeSyncPlugin.java
index aa429f3..6675f13 100644
--- a/base/src/com/google/idea/blaze/base/sync/BlazeSyncPlugin.java
+++ b/base/src/com/google/idea/blaze/base/sync/BlazeSyncPlugin.java
@@ -65,13 +65,6 @@
*/
ModifiableRootModel editModule(Module module);
- /**
- * Registers a module. This prevents garbage collection of the module upon commit.
- *
- * @return True if the module exists and was registered.
- */
- boolean registerModule(String moduleName);
-
/** Finds a module by name. This doesn't register the module. */
@Nullable
Module findModule(String moduleName);
@@ -115,6 +108,13 @@
SyncState.Builder syncStateBuilder,
@Nullable SyncState previousSyncState);
+ /**
+ * Refresh any VFS files which may have changed during sync, and aren't covered by file watchers.
+ *
+ * <p>Called prior to updateProjectSdk and updateProjectStructure, from inside a write action.
+ */
+ void refreshVirtualFileSystem(BlazeProjectData blazeProjectData);
+
/** Updates the sdk for the project. */
void updateProjectSdk(
Project project,
@@ -240,6 +240,9 @@
ModifiableRootModel workspaceModifiableModel) {}
@Override
+ public void refreshVirtualFileSystem(BlazeProjectData blazeProjectData) {}
+
+ @Override
public void updateInMemoryState(
Project project,
BlazeContext context,
diff --git a/base/src/com/google/idea/blaze/base/sync/BlazeSyncTask.java b/base/src/com/google/idea/blaze/base/sync/BlazeSyncTask.java
index db199e3..9b6849f 100644
--- a/base/src/com/google/idea/blaze/base/sync/BlazeSyncTask.java
+++ b/base/src/com/google/idea/blaze/base/sync/BlazeSyncTask.java
@@ -32,7 +32,7 @@
import com.google.idea.blaze.base.ideinfo.TargetKey;
import com.google.idea.blaze.base.ideinfo.TargetMap;
import com.google.idea.blaze.base.io.FileAttributeProvider;
-import com.google.idea.blaze.base.metrics.Action;
+import com.google.idea.blaze.base.io.VirtualFileSystemProvider;
import com.google.idea.blaze.base.model.BlazeLibrary;
import com.google.idea.blaze.base.model.BlazeProjectData;
import com.google.idea.blaze.base.model.BlazeVersionData;
@@ -54,7 +54,6 @@
import com.google.idea.blaze.base.scope.scopes.BlazeConsoleScope;
import com.google.idea.blaze.base.scope.scopes.IdeaLogScope;
import com.google.idea.blaze.base.scope.scopes.IssuesScope;
-import com.google.idea.blaze.base.scope.scopes.LoggedTimingScope;
import com.google.idea.blaze.base.scope.scopes.NotificationScope;
import com.google.idea.blaze.base.scope.scopes.PerformanceWarningScope;
import com.google.idea.blaze.base.scope.scopes.ProgressIndicatorScope;
@@ -100,10 +99,7 @@
import com.intellij.openapi.roots.ModifiableRootModel;
import com.intellij.openapi.roots.ex.ProjectRootManagerEx;
import com.intellij.openapi.util.io.FileUtil;
-import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFileManager;
-import com.intellij.openapi.vfs.VirtualFileSystem;
-import com.intellij.openapi.vfs.ex.temp.TempFileSystem;
import java.io.File;
import java.util.Collection;
import java.util.Collections;
@@ -115,14 +111,13 @@
/** Syncs the project with blaze. */
final class BlazeSyncTask implements Progressive {
- private static final Logger LOG = Logger.getInstance(BlazeSyncTask.class);
+ private static final Logger logger = Logger.getInstance(BlazeSyncTask.class);
private final Project project;
private final BlazeImportSettings importSettings;
private final WorkspaceRoot workspaceRoot;
private final BlazeSyncParams syncParams;
private final boolean showPerformanceWarnings;
- private long syncStartTime;
BlazeSyncTask(
Project project, BlazeImportSettings importSettings, final BlazeSyncParams syncParams) {
@@ -141,10 +136,7 @@
if (showPerformanceWarnings) {
context.push(new PerformanceWarningScope());
}
- context
- .push(new ProgressIndicatorScope(indicator))
- .push(new TimingScope("Sync"))
- .push(new LoggedTimingScope(project, Action.SYNC_TOTAL_TIME));
+ context.push(new ProgressIndicatorScope(indicator)).push(new TimingScope("Sync"));
if (!syncParams.backgroundSync) {
context
@@ -191,7 +183,7 @@
onSyncComplete(project, context, projectViewSet, blazeProjectData, syncMode, syncResult);
}
} catch (AssertionError | Exception e) {
- LOG.error(e);
+ logger.error(e);
IssueOutput.error("Internal error: " + e.getMessage()).submit(context);
} finally {
afterSync(project, context, syncMode, syncResult);
@@ -202,7 +194,7 @@
/** @return true if sync successfully completed */
private SyncResult doSyncProject(
BlazeContext context, SyncMode syncMode, @Nullable BlazeProjectData oldBlazeProjectData) {
- this.syncStartTime = System.currentTimeMillis();
+ long syncStartTime = System.currentTimeMillis();
if (!FileAttributeProvider.getInstance().exists(workspaceRoot.directory())) {
IssueOutput.error(String.format("Workspace '%s' doesn't exist.", workspaceRoot.directory()))
@@ -404,14 +396,11 @@
.onError("Prefetch failed")
.run();
+ refreshVirtualFileSystem(context, newBlazeProjectData);
+
boolean success =
updateProject(
- project,
- context,
- projectViewSet,
- blazeVersionData,
- oldBlazeProjectData,
- newBlazeProjectData);
+ context, projectViewSet, blazeVersionData, oldBlazeProjectData, newBlazeProjectData);
if (!success) {
return SyncResult.FAILURE;
}
@@ -437,6 +426,25 @@
return syncResult;
}
+ private static void refreshVirtualFileSystem(
+ BlazeContext context, BlazeProjectData blazeProjectData) {
+ Transactions.submitTransactionAndWait(
+ () ->
+ ApplicationManager.getApplication()
+ .runWriteAction(
+ (Runnable)
+ () ->
+ Scope.push(
+ context,
+ (childContext) -> {
+ childContext.push(new TimingScope("RefreshVirtualFileSystem"));
+ for (BlazeSyncPlugin syncPlugin :
+ BlazeSyncPlugin.EP_NAME.getExtensions()) {
+ syncPlugin.refreshVirtualFileSystem(blazeProjectData);
+ }
+ })));
+ }
+
static class WorkspacePathResolverAndProjectView {
final WorkspacePathResolver workspacePathResolver;
final ProjectViewSet projectViewSet;
@@ -465,7 +473,7 @@
context,
(childContext) -> {
childContext.push(new TimingScope("UpdateVcs"));
- return vcsSyncHandler.update(context, blazeRoots, executor);
+ return vcsSyncHandler.update(context, executor);
});
if (!ok) {
return null;
@@ -612,9 +620,7 @@
return Scope.push(
parentContext,
context -> {
- context
- .push(new LoggedTimingScope(project, Action.BLAZE_BUILD_DURING_SYNC))
- .push(new TimingScope(Blaze.buildSystemName(project) + "Build"));
+ context.push(new TimingScope(Blaze.buildSystemName(project) + "Build"));
context.output(new StatusOutput("Building IDE resolve files..."));
// We don't want IDE resolve errors to fail the whole sync
@@ -630,7 +636,6 @@
}
private boolean updateProject(
- Project project,
BlazeContext parentContext,
ProjectViewSet projectViewSet,
BlazeVersionData blazeVersionData,
@@ -639,42 +644,38 @@
return Scope.push(
parentContext,
context -> {
- context
- .push(new LoggedTimingScope(project, Action.SYNC_IMPORT_DATA_TIME))
- .push(new TimingScope("UpdateProjectStructure"));
+ context.push(new TimingScope("UpdateProjectStructure"));
context.output(new StatusOutput("Committing project structure..."));
try {
Transactions.submitTransactionAndWait(
- () -> {
- ApplicationManager.getApplication()
- .runWriteAction(
- (Runnable)
- () -> {
- ProjectRootManagerEx.getInstanceEx(this.project)
- .mergeRootsChangesDuring(
- () -> {
- updateProjectSdk(
- context,
- projectViewSet,
- blazeVersionData,
- newBlazeProjectData);
- updateProjectStructure(
- context,
- importSettings,
- projectViewSet,
- newBlazeProjectData,
- oldBlazeProjectData);
- });
- });
- });
- } catch (Throwable t) {
- IssueOutput.error("Internal error. Error: " + t).submit(context);
- LOG.error(t);
+ () ->
+ ApplicationManager.getApplication()
+ .runWriteAction(
+ (Runnable)
+ () ->
+ ProjectRootManagerEx.getInstanceEx(this.project)
+ .mergeRootsChangesDuring(
+ () -> {
+ updateProjectSdk(
+ context,
+ projectViewSet,
+ blazeVersionData,
+ newBlazeProjectData);
+ updateProjectStructure(
+ context,
+ importSettings,
+ projectViewSet,
+ newBlazeProjectData,
+ oldBlazeProjectData);
+ })));
+ } catch (Throwable e) {
+ IssueOutput.error("Internal error. Error: " + e).submit(context);
+ logger.error(e);
return false;
}
- BlazeProjectDataManagerImpl.getImpl(this.project)
+ BlazeProjectDataManagerImpl.getImpl(project)
.saveProject(importSettings, newBlazeProjectData);
return true;
});
@@ -720,12 +721,7 @@
ModifiableRootModel workspaceModifiableModel = moduleEditor.editModule(workspaceModule);
ContentEntryEditor.createContentEntries(
- project,
- context,
- workspaceRoot,
- projectViewSet,
- newBlazeProjectData,
- workspaceModifiableModel);
+ project, workspaceRoot, projectViewSet, newBlazeProjectData, workspaceModifiableModel);
List<BlazeLibrary> libraries = BlazeLibraryCollector.getLibraries(newBlazeProjectData);
LibraryEditor.updateProjectLibraries(project, context, newBlazeProjectData, libraries);
@@ -799,14 +795,8 @@
private static String pathToUrl(File path) {
String filePath = FileUtil.toSystemIndependentName(path.getPath());
- return VirtualFileManager.constructUrl(defaultFileSystem().getProtocol(), filePath);
- }
-
- private static VirtualFileSystem defaultFileSystem() {
- if (ApplicationManager.getApplication().isUnitTestMode()) {
- return TempFileSystem.getInstance();
- }
- return LocalFileSystem.getInstance();
+ return VirtualFileManager.constructUrl(
+ VirtualFileSystemProvider.getInstance().getSystem().getProtocol(), filePath);
}
private static void onSyncStart(Project project, BlazeContext context, SyncMode syncMode) {
diff --git a/base/src/com/google/idea/blaze/base/sync/BuildTargetFinder.java b/base/src/com/google/idea/blaze/base/sync/BuildTargetFinder.java
index 8525a20..8d8ca16 100644
--- a/base/src/com/google/idea/blaze/base/sync/BuildTargetFinder.java
+++ b/base/src/com/google/idea/blaze/base/sync/BuildTargetFinder.java
@@ -41,7 +41,7 @@
}
@Nullable
- public TargetExpression findTargetForFile(File file) {
+ public File findBuildFileForFile(File file) {
if (fileAttributeProvider.isFile(file)) {
file = file.getParentFile();
if (file == null) {
@@ -66,12 +66,21 @@
do {
File buildFile = buildSystemProvider.findBuildFileInDirectory(currentDirectory);
if (buildFile != null) {
- return TargetExpression.allFromPackageNonRecursive(
- workspaceRoot.workspacePathFor(currentDirectory));
+ return buildFile;
}
currentDirectory = currentDirectory.getParentFile();
} while (currentDirectory != null && FileUtil.isAncestor(root, currentDirectory, false));
return null;
}
+
+ @Nullable
+ public TargetExpression findTargetForFile(File file) {
+ File buildFile = findBuildFileForFile(file);
+ if (buildFile != null) {
+ return TargetExpression.allFromPackageNonRecursive(
+ workspaceRoot.workspacePathFor(buildFile.getParentFile()));
+ }
+ return null;
+ }
}
diff --git a/base/src/com/google/idea/blaze/base/sync/GenericSourceFolderProvider.java b/base/src/com/google/idea/blaze/base/sync/GenericSourceFolderProvider.java
index 1810e9c..a2d1d8c 100644
--- a/base/src/com/google/idea/blaze/base/sync/GenericSourceFolderProvider.java
+++ b/base/src/com/google/idea/blaze/base/sync/GenericSourceFolderProvider.java
@@ -16,9 +16,10 @@
package com.google.idea.blaze.base.sync;
import com.google.common.collect.ImmutableMap;
+import com.google.idea.blaze.base.util.UrlUtil;
import com.intellij.openapi.roots.ContentEntry;
import com.intellij.openapi.roots.SourceFolder;
-import com.intellij.openapi.vfs.VirtualFile;
+import java.io.File;
/** An implementation of {@link SourceFolderProvider} with no language-specific settings. */
public class GenericSourceFolderProvider implements SourceFolderProvider {
@@ -28,22 +29,14 @@
private GenericSourceFolderProvider() {}
@Override
- public ImmutableMap<VirtualFile, SourceFolder> initializeSourceFolders(
- ContentEntry contentEntry) {
- ImmutableMap.Builder<VirtualFile, SourceFolder> output = ImmutableMap.builder();
- VirtualFile file = contentEntry.getFile();
- if (file != null) {
- output.put(file, contentEntry.addSourceFolder(file, false));
- }
- return output.build();
+ public ImmutableMap<File, SourceFolder> initializeSourceFolders(ContentEntry contentEntry) {
+ String url = contentEntry.getUrl();
+ return ImmutableMap.of(UrlUtil.urlToFile(url), contentEntry.addSourceFolder(url, false));
}
@Override
public SourceFolder setSourceFolderForLocation(
- ContentEntry contentEntry,
- SourceFolder parentFolder,
- VirtualFile file,
- boolean isTestSource) {
- return contentEntry.addSourceFolder(file, isTestSource);
+ ContentEntry contentEntry, SourceFolder parentFolder, File file, boolean isTestSource) {
+ return contentEntry.addSourceFolder(UrlUtil.fileToIdeaUrl(file), isTestSource);
}
}
diff --git a/base/src/com/google/idea/blaze/base/sync/SourceFolderProvider.java b/base/src/com/google/idea/blaze/base/sync/SourceFolderProvider.java
index 6514a82..91f3630 100644
--- a/base/src/com/google/idea/blaze/base/sync/SourceFolderProvider.java
+++ b/base/src/com/google/idea/blaze/base/sync/SourceFolderProvider.java
@@ -19,7 +19,7 @@
import com.google.idea.blaze.base.model.BlazeProjectData;
import com.intellij.openapi.roots.ContentEntry;
import com.intellij.openapi.roots.SourceFolder;
-import com.intellij.openapi.vfs.VirtualFile;
+import java.io.File;
/** Provides source folders for each content entry during sync. */
public interface SourceFolderProvider {
@@ -42,11 +42,11 @@
* 'initial' because the 'is test' property (and potentially additional test source folders) are
* added later.
*/
- ImmutableMap<VirtualFile, SourceFolder> initializeSourceFolders(ContentEntry contentEntry);
+ ImmutableMap<File, SourceFolder> initializeSourceFolders(ContentEntry contentEntry);
/**
* Sets the source folder for the given file, incorporating the test information as appropriate.
*/
SourceFolder setSourceFolderForLocation(
- ContentEntry contentEntry, SourceFolder parentFolder, VirtualFile file, boolean isTestSource);
+ ContentEntry contentEntry, SourceFolder parentFolder, File file, boolean isTestSource);
}
diff --git a/base/src/com/google/idea/blaze/base/sync/SyncCache.java b/base/src/com/google/idea/blaze/base/sync/SyncCache.java
new file mode 100644
index 0000000..24cdd46
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/sync/SyncCache.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2016 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.sync;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.Maps;
+import com.google.idea.blaze.base.model.BlazeProjectData;
+import com.google.idea.blaze.base.projectview.ProjectViewSet;
+import com.google.idea.blaze.base.scope.BlazeContext;
+import com.google.idea.blaze.base.settings.BlazeImportSettings;
+import com.google.idea.blaze.base.sync.BlazeSyncParams.SyncMode;
+import com.google.idea.blaze.base.sync.data.BlazeProjectDataManager;
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.project.Project;
+import java.util.Map;
+import javax.annotation.Nullable;
+
+/** Computes a cache on the project data. */
+public class SyncCache {
+ /** Computes a value based on the sync project data. */
+ public interface SyncCacheComputable<T> {
+ T compute(Project project, BlazeProjectData projectData);
+ }
+
+ private final Project project;
+ private final Map<Object, Object> cache = Maps.newHashMap();
+
+ public SyncCache(Project project) {
+ this.project = project;
+ }
+
+ public static SyncCache getInstance(Project project) {
+ return ServiceManager.getService(project, SyncCache.class);
+ }
+
+ /** Computes a value derived from the sync project data and caches it until the next sync. */
+ @Nullable
+ @SuppressWarnings("unchecked")
+ public synchronized <T> T get(Object key, SyncCacheComputable<T> computable) {
+ T value = (T) cache.get(key);
+ if (value == null) {
+ BlazeProjectData blazeProjectData =
+ BlazeProjectDataManager.getInstance(project).getBlazeProjectData();
+ if (blazeProjectData != null) {
+ value = computable.compute(project, blazeProjectData);
+ cache.put(key, value);
+ }
+ }
+ return value;
+ }
+
+ @VisibleForTesting
+ public synchronized void clear() {
+ cache.clear();
+ }
+
+ static class ClearSyncCache extends SyncListener.Adapter {
+ @Override
+ public void onSyncComplete(
+ Project project,
+ BlazeContext context,
+ BlazeImportSettings importSettings,
+ ProjectViewSet projectViewSet,
+ BlazeProjectData blazeProjectData,
+ SyncMode syncMode,
+ SyncResult syncResult) {
+ SyncCache syncCache = getInstance(project);
+ syncCache.clear();
+ }
+ }
+}
diff --git a/base/src/com/google/idea/blaze/base/sync/aspects/BlazeIdeInterfaceAspectsImpl.java b/base/src/com/google/idea/blaze/base/sync/aspects/BlazeIdeInterfaceAspectsImpl.java
index 36e29af..12a09c5 100644
--- a/base/src/com/google/idea/blaze/base/sync/aspects/BlazeIdeInterfaceAspectsImpl.java
+++ b/base/src/com/google/idea/blaze/base/sync/aspects/BlazeIdeInterfaceAspectsImpl.java
@@ -35,7 +35,6 @@
import com.google.idea.blaze.base.ideinfo.TargetKey;
import com.google.idea.blaze.base.ideinfo.TargetMap;
import com.google.idea.blaze.base.issueparser.IssueOutputLineProcessor;
-import com.google.idea.blaze.base.metrics.Action;
import com.google.idea.blaze.base.model.BlazeVersionData;
import com.google.idea.blaze.base.model.SyncState;
import com.google.idea.blaze.base.model.primitives.TargetExpression;
@@ -48,7 +47,6 @@
import com.google.idea.blaze.base.scope.ScopedFunction;
import com.google.idea.blaze.base.scope.output.PerformanceWarning;
import com.google.idea.blaze.base.scope.output.PrintOutput;
-import com.google.idea.blaze.base.scope.scopes.LoggedTimingScope;
import com.google.idea.blaze.base.scope.scopes.TimingScope;
import com.google.idea.blaze.base.settings.Blaze;
import com.google.idea.blaze.base.settings.Blaze.BuildSystem;
@@ -75,7 +73,7 @@
/** Implementation of BlazeIdeInterface based on aspects. */
public class BlazeIdeInterfaceAspectsImpl implements BlazeIdeInterface {
- private static final Logger LOG = Logger.getInstance(BlazeIdeInterfaceAspectsImpl.class);
+ private static final Logger logger = Logger.getInstance(BlazeIdeInterfaceAspectsImpl.class);
static class State implements Serializable {
private static final long serialVersionUID = 14L;
@@ -219,7 +217,7 @@
new ExperimentalShowArtifactsLineProcessor(result, fileFilter),
new IssueOutputLineProcessor(project, context, workspaceRoot)))
.build()
- .run(new LoggedTimingScope(project, Action.BLAZE_BUILD));
+ .run();
BuildResult buildResult = BuildResult.fromExitCode(retVal);
return new IdeInfoResult(result, buildResult);
@@ -357,7 +355,7 @@
});
if (result.error != null) {
- LOG.error(result.error);
+ logger.error(result.error);
return null;
}
return result.result;
@@ -397,7 +395,7 @@
}
private static boolean hasIdeCompileOutputGroup(BlazeVersionData blazeVersionData) {
- return blazeVersionData.bazelIsAtLeastVersion(0, 4, 3);
+ return blazeVersionData.bazelIsAtLeastVersion(0, 4, 4);
}
private static BuildResult resolveIdeArtifacts(
@@ -433,7 +431,7 @@
LineProcessingOutputStream.of(
new IssueOutputLineProcessor(project, context, workspaceRoot)))
.build()
- .run(new LoggedTimingScope(project, Action.BLAZE_BUILD));
+ .run();
return BuildResult.fromExitCode(retVal);
}
diff --git a/base/src/com/google/idea/blaze/base/sync/aspects/IdeInfoFromProtobuf.java b/base/src/com/google/idea/blaze/base/sync/aspects/IdeInfoFromProtobuf.java
index c140587..8bf9585 100644
--- a/base/src/com/google/idea/blaze/base/sync/aspects/IdeInfoFromProtobuf.java
+++ b/base/src/com/google/idea/blaze/base/sync/aspects/IdeInfoFromProtobuf.java
@@ -27,6 +27,8 @@
import com.google.idea.blaze.base.ideinfo.CToolchainIdeInfo;
import com.google.idea.blaze.base.ideinfo.Dependency;
import com.google.idea.blaze.base.ideinfo.Dependency.DependencyType;
+import com.google.idea.blaze.base.ideinfo.IntellijPluginDeployInfo;
+import com.google.idea.blaze.base.ideinfo.IntellijPluginDeployInfo.IntellijPluginDeployFile;
import com.google.idea.blaze.base.ideinfo.JavaIdeInfo;
import com.google.idea.blaze.base.ideinfo.JavaToolchainIdeInfo;
import com.google.idea.blaze.base.ideinfo.LibraryArtifact;
@@ -123,6 +125,11 @@
if (message.hasJavaToolchainIdeInfo()) {
javaToolchainIdeInfo = makeJavaToolchainIdeInfo(message.getJavaToolchainIdeInfo());
}
+ IntellijPluginDeployInfo intellijPluginDeployInfo = null;
+ if (message.hasIntellijPluginDeployInfo()) {
+ intellijPluginDeployInfo =
+ makeIntellijPluginDeployInfo(message.getIntellijPluginDeployInfo());
+ }
return new TargetIdeInfo(
key,
@@ -138,7 +145,8 @@
pyIdeInfo,
testIdeInfo,
protoLibraryLegacyInfo,
- javaToolchainIdeInfo);
+ javaToolchainIdeInfo,
+ intellijPluginDeployInfo);
}
private static Collection<Dependency> makeDependencyListFromLabelList(
@@ -244,7 +252,8 @@
javaIdeInfo.hasPackageManifest()
? makeArtifactLocation(javaIdeInfo.getPackageManifest())
: null,
- javaIdeInfo.hasJdeps() ? makeArtifactLocation(javaIdeInfo.getJdeps()) : null);
+ javaIdeInfo.hasJdeps() ? makeArtifactLocation(javaIdeInfo.getJdeps()) : null,
+ Strings.emptyToNull(javaIdeInfo.getMainClass()));
}
private static AndroidIdeInfo makeAndroidIdeInfo(IntellijIdeInfo.AndroidIdeInfo androidIdeInfo) {
@@ -325,6 +334,21 @@
javaToolchainIdeInfo.getSourceVersion(), javaToolchainIdeInfo.getTargetVersion());
}
+ private static IntellijPluginDeployInfo makeIntellijPluginDeployInfo(
+ IntellijIdeInfo.IntellijPluginDeployInfo intellijPluginDeployInfo) {
+ return new IntellijPluginDeployInfo(
+ ImmutableList.copyOf(
+ intellijPluginDeployInfo
+ .getDeployFilesList()
+ .stream()
+ .map(
+ deployFile ->
+ new IntellijPluginDeployFile(
+ makeArtifactLocation(deployFile.getSrc()),
+ deployFile.getDeployLocation()))
+ .collect(toList())));
+ }
+
private static Collection<LibraryArtifact> makeLibraryArtifactList(
List<IntellijIdeInfo.LibraryArtifact> jarsList) {
ImmutableList.Builder<LibraryArtifact> builder = ImmutableList.builder();
diff --git a/base/src/com/google/idea/blaze/base/sync/aspects/strategy/AspectStrategyProviderBazel.java b/base/src/com/google/idea/blaze/base/sync/aspects/strategy/AspectStrategyProviderBazel.java
index f3b1029..180ae9e 100644
--- a/base/src/com/google/idea/blaze/base/sync/aspects/strategy/AspectStrategyProviderBazel.java
+++ b/base/src/com/google/idea/blaze/base/sync/aspects/strategy/AspectStrategyProviderBazel.java
@@ -21,12 +21,12 @@
class AspectStrategyProviderBazel implements AspectStrategyProvider {
private static final BoolExperiment useSkylarkAspect =
- new BoolExperiment("use.skylark.aspect.bazel", true);
+ new BoolExperiment("use.skylark.aspect.bazel.2", true);
@Override
public AspectStrategy getAspectStrategy(Project project, BlazeVersionData blazeVersionData) {
boolean canUseSkylark =
- useSkylarkAspect.getValue() && blazeVersionData.bazelIsAtLeastVersion(0, 4, 3);
+ useSkylarkAspect.getValue() && blazeVersionData.bazelIsAtLeastVersion(0, 4, 4);
return canUseSkylark ? new AspectStrategySkylark() : new AspectStrategyNative();
}
diff --git a/base/src/com/google/idea/blaze/base/sync/data/BlazeProjectDataManagerImpl.java b/base/src/com/google/idea/blaze/base/sync/data/BlazeProjectDataManagerImpl.java
index 754e60f..bd4494f 100644
--- a/base/src/com/google/idea/blaze/base/sync/data/BlazeProjectDataManagerImpl.java
+++ b/base/src/com/google/idea/blaze/base/sync/data/BlazeProjectDataManagerImpl.java
@@ -34,7 +34,8 @@
/** Stores a cache of blaze project data and issues any side effects when that data is updated. */
public class BlazeProjectDataManagerImpl implements BlazeProjectDataManager {
- private static final Logger LOG = Logger.getInstance(BlazeProjectDataManagerImpl.class.getName());
+ private static final Logger logger =
+ Logger.getInstance(BlazeProjectDataManagerImpl.class.getName());
private final Project project;
@@ -89,7 +90,7 @@
context.output(
new StatusOutput(
String.format("Stale %s project cache, sync will be needed", buildSystemName)));
- LOG.info(e);
+ logger.info(e);
}
this.blazeProjectData = blazeProjectData;
@@ -110,7 +111,7 @@
File file = getCacheFile(project, importSettings);
SerializationUtil.saveToDisk(file, blazeProjectData);
} catch (IOException e) {
- LOG.error(
+ logger.error(
"Could not save cache data file to disk. Please resync project. Error: "
+ e.getMessage());
}
diff --git a/base/src/com/google/idea/blaze/base/sync/libraries/LibraryEditor.java b/base/src/com/google/idea/blaze/base/sync/libraries/LibraryEditor.java
index 8452d19..44969c3 100644
--- a/base/src/com/google/idea/blaze/base/sync/libraries/LibraryEditor.java
+++ b/base/src/com/google/idea/blaze/base/sync/libraries/LibraryEditor.java
@@ -40,7 +40,7 @@
/** Edits IntelliJ libraries */
public class LibraryEditor {
- private static final Logger LOG = Logger.getInstance(LibraryEditor.class);
+ private static final Logger logger = Logger.getInstance(LibraryEditor.class);
public static void updateProjectLibraries(
Project project,
@@ -142,7 +142,7 @@
LibraryTable libraryTable = ProjectLibraryTable.getInstance(model.getProject());
Library library = libraryTable.getLibraryByName(libraryKey.getIntelliJLibraryName());
if (library == null) {
- LOG.error(
+ logger.error(
"Library missing: "
+ libraryKey.getIntelliJLibraryName()
+ ". Please resync project to resolve.");
diff --git a/base/src/com/google/idea/blaze/base/sync/projectstructure/ContentEntryEditor.java b/base/src/com/google/idea/blaze/base/sync/projectstructure/ContentEntryEditor.java
index d8e72f6..5b76a0c 100644
--- a/base/src/com/google/idea/blaze/base/sync/projectstructure/ContentEntryEditor.java
+++ b/base/src/com/google/idea/blaze/base/sync/projectstructure/ContentEntryEditor.java
@@ -19,40 +19,29 @@
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
+import com.google.idea.blaze.base.io.FileAttributeProvider;
import com.google.idea.blaze.base.model.BlazeProjectData;
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;
-import com.google.idea.blaze.base.scope.output.IssueOutput;
import com.google.idea.blaze.base.settings.Blaze;
import com.google.idea.blaze.base.sync.SourceFolderProvider;
import com.google.idea.blaze.base.sync.projectview.ImportRoots;
import com.google.idea.blaze.base.sync.projectview.SourceTestConfig;
-import com.intellij.openapi.application.ApplicationManager;
+import com.google.idea.blaze.base.util.UrlUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ContentEntry;
import com.intellij.openapi.roots.ModifiableRootModel;
import com.intellij.openapi.roots.SourceFolder;
-import com.intellij.openapi.util.io.FileUtil;
-import com.intellij.openapi.util.io.FileUtilRt;
-import com.intellij.openapi.vfs.LocalFileSystem;
-import com.intellij.openapi.vfs.VirtualFile;
-import com.intellij.openapi.vfs.VirtualFileManager;
-import com.intellij.openapi.vfs.VirtualFileSystem;
-import com.intellij.openapi.vfs.ex.temp.TempFileSystem;
-import com.intellij.util.io.URLUtil;
import java.io.File;
import java.util.Collection;
import java.util.List;
-import javax.annotation.Nullable;
/** Modifies content entries based on project data. */
public class ContentEntryEditor {
public static void createContentEntries(
Project project,
- BlazeContext context,
WorkspaceRoot workspaceRoot,
ProjectViewSet projectViewSet,
BlazeProjectData blazeProjectData,
@@ -72,27 +61,18 @@
List<ContentEntry> contentEntries = Lists.newArrayList();
for (WorkspacePath rootDirectory : rootDirectories) {
File root = workspaceRoot.fileForPath(rootDirectory);
- ContentEntry contentEntry = modifiableRootModel.addContentEntry(pathToUrl(root.getPath()));
+ ContentEntry contentEntry =
+ modifiableRootModel.addContentEntry(UrlUtil.pathToUrl(root.getPath()));
contentEntries.add(contentEntry);
for (WorkspacePath exclude : excludesByRootDirectory.get(rootDirectory)) {
File excludeFolder = workspaceRoot.fileForPath(exclude);
- contentEntry.addExcludeFolder(pathToIdeaUrl(excludeFolder));
+ contentEntry.addExcludeFolder(UrlUtil.fileToIdeaUrl(excludeFolder));
}
- ImmutableMap<VirtualFile, SourceFolder> sourceFolders =
+ ImmutableMap<File, SourceFolder> sourceFolders =
provider.initializeSourceFolders(contentEntry);
- VirtualFile rootFile = getVirtualFile(root);
- if (rootFile == null) {
- IssueOutput.warn(
- String.format(
- "Could not find directory %s. Your 'test_sources' project view "
- + "attribute will not have any effect. Please resync.",
- workspaceRoot))
- .submit(context);
- continue;
- }
- SourceFolder rootSource = sourceFolders.get(rootFile);
+ SourceFolder rootSource = sourceFolders.get(root);
walkFileSystem(
workspaceRoot,
testConfig,
@@ -101,7 +81,7 @@
provider,
sourceFolders,
rootSource,
- rootFile);
+ root);
}
}
@@ -111,10 +91,10 @@
Collection<WorkspacePath> excludedDirectories,
ContentEntry contentEntry,
SourceFolderProvider provider,
- ImmutableMap<VirtualFile, SourceFolder> sourceFolders,
+ ImmutableMap<File, SourceFolder> sourceFolders,
SourceFolder parent,
- VirtualFile file) {
- if (!file.isDirectory()) {
+ File file) {
+ if (!FileAttributeProvider.getInstance().isDirectory(file)) {
return;
}
WorkspacePath workspacePath;
@@ -128,7 +108,7 @@
return;
}
boolean isTest = testConfig.isTestSource(workspacePath.relativePath());
- SourceFolder current = sourceFolders.get(file);
+ SourceFolder current = sourceFolders.get(new File(file.getPath()));
SourceFolder currentOrParent = current != null ? current : parent;
if (isTest != currentOrParent.isTestSource()) {
currentOrParent =
@@ -137,7 +117,11 @@
contentEntry.removeSourceFolder(current);
}
}
- for (VirtualFile child : file.getChildren()) {
+ File[] children = FileAttributeProvider.getInstance().listFiles(file);
+ if (children == null) {
+ return;
+ }
+ for (File child : children) {
walkFileSystem(
workspaceRoot,
testConfig,
@@ -150,18 +134,6 @@
}
}
- @Nullable
- private static VirtualFile getVirtualFile(File file) {
- return getFileSystem().findFileByPath(file.getPath());
- }
-
- private static VirtualFileSystem getFileSystem() {
- if (ApplicationManager.getApplication().isUnitTestMode()) {
- return TempFileSystem.getInstance();
- }
- return LocalFileSystem.getInstance();
- }
-
private static Multimap<WorkspacePath, WorkspacePath> sortExcludesByRootDirectory(
Collection<WorkspacePath> rootDirectories, Collection<WorkspacePath> excludedDirectories) {
@@ -189,30 +161,4 @@
&& (relativePath.length() == rootDirectoryString.length()
|| (relativePath.charAt(rootDirectoryString.length()) == '/'));
}
-
- private static String pathToUrl(String filePath) {
- filePath = FileUtil.toSystemIndependentName(filePath);
- if (filePath.endsWith(".srcjar") || filePath.endsWith(".jar")) {
- return URLUtil.JAR_PROTOCOL + URLUtil.SCHEME_SEPARATOR + filePath + URLUtil.JAR_SEPARATOR;
- } else if (filePath.contains("src.jar!")) {
- return URLUtil.JAR_PROTOCOL + URLUtil.SCHEME_SEPARATOR + filePath;
- } else {
- return VirtualFileManager.constructUrl(defaultFileSystem().getProtocol(), filePath);
- }
- }
-
- private static VirtualFileSystem defaultFileSystem() {
- if (ApplicationManager.getApplication().isUnitTestMode()) {
- return TempFileSystem.getInstance();
- }
- return LocalFileSystem.getInstance();
- }
-
- private static String pathToIdeaUrl(File path) {
- return pathToUrl(toSystemIndependentName(path.getPath()));
- }
-
- private static String toSystemIndependentName(String aFileName) {
- return FileUtilRt.toSystemIndependentName(aFileName);
- }
}
diff --git a/base/src/com/google/idea/blaze/base/sync/projectstructure/ModuleEditorImpl.java b/base/src/com/google/idea/blaze/base/sync/projectstructure/ModuleEditorImpl.java
index 2f05f68..7a9fde2 100644
--- a/base/src/com/google/idea/blaze/base/sync/projectstructure/ModuleEditorImpl.java
+++ b/base/src/com/google/idea/blaze/base/sync/projectstructure/ModuleEditorImpl.java
@@ -17,7 +17,7 @@
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Lists;
-import com.google.common.collect.Sets;
+import com.google.common.collect.Maps;
import com.google.idea.blaze.base.io.FileAttributeProvider;
import com.google.idea.blaze.base.scope.BlazeContext;
import com.google.idea.blaze.base.scope.output.PrintOutput;
@@ -40,22 +40,20 @@
import com.intellij.openapi.vfs.VirtualFile;
import java.io.File;
import java.io.IOException;
-import java.util.Collection;
import java.util.List;
-import java.util.Set;
+import java.util.Map;
import javax.annotation.Nullable;
/** Module editor implementation. */
public class ModuleEditorImpl implements BlazeSyncPlugin.ModuleEditor {
- private static final Logger LOG = Logger.getInstance(ModuleEditorImpl.class.getName());
+ private static final Logger logger = Logger.getInstance(ModuleEditorImpl.class.getName());
private static final String EXTERNAL_SYSTEM_ID_KEY = "external.system.id";
private static final String EXTERNAL_SYSTEM_ID_VALUE = "Blaze";
private final Project project;
private final ModifiableModuleModel moduleModel;
private final File imlDirectory;
- private final Set<String> moduleNames = Sets.newHashSet();
- @VisibleForTesting public Collection<ModifiableRootModel> modifiableModels = Lists.newArrayList();
+ @VisibleForTesting public Map<String, ModifiableRootModel> modules = Maps.newHashMap();
public ModuleEditorImpl(Project project, BlazeImportSettings importSettings) {
this.project = project;
@@ -64,21 +62,12 @@
this.imlDirectory = getImlDirectory(importSettings);
if (!FileAttributeProvider.getInstance().exists(imlDirectory)) {
if (!imlDirectory.mkdirs()) {
- LOG.error("Could not make directory: " + imlDirectory.getPath());
+ logger.error("Could not make directory: " + imlDirectory.getPath());
}
}
}
@Override
- public boolean registerModule(String moduleName) {
- boolean hasModule = moduleModel.findModuleByName(moduleName) != null;
- if (hasModule) {
- moduleNames.add(moduleName);
- }
- return hasModule;
- }
-
- @Override
public Module createModule(String moduleName, ModuleType moduleType) {
Module module = moduleModel.findModuleByName(moduleName);
if (module == null) {
@@ -88,16 +77,10 @@
module.setOption(EXTERNAL_SYSTEM_ID_KEY, EXTERNAL_SYSTEM_ID_VALUE);
}
module.setOption(Module.ELEMENT_TYPE, moduleType.getId());
- moduleNames.add(moduleName);
- return module;
- }
- @Override
- public ModifiableRootModel editModule(Module module) {
ModifiableRootModel modifiableModel =
ModuleRootManager.getInstance(module).getModifiableModel();
- modifiableModels.add(modifiableModel);
-
+ modules.put(module.getName(), modifiableModel);
modifiableModel.clear();
modifiableModel.inheritSdk();
CompilerModuleExtension compilerSettings =
@@ -106,7 +89,12 @@
compilerSettings.inheritCompilerOutputPath(false);
}
- return modifiableModel;
+ return module;
+ }
+
+ @Override
+ public ModifiableRootModel editModule(Module module) {
+ return modules.get(module.getName());
}
@Override
@@ -118,7 +106,7 @@
public void commitWithGc(BlazeContext context) {
List<Module> orphanModules = Lists.newArrayList();
for (Module module : ModuleManager.getInstance(project).getModules()) {
- if (!moduleNames.contains(module.getName())) {
+ if (!modules.containsKey(module.getName())) {
orphanModules.add(module);
}
}
@@ -135,15 +123,14 @@
}
}
- context.output(
- PrintOutput.log(String.format("Workspace has %s modules", modifiableModels.size())));
+ context.output(PrintOutput.log(String.format("Workspace has %s modules", modules.size())));
commit();
}
@Override
public void commit() {
- ModifiableModelCommitter.multiCommit(modifiableModels, moduleModel);
+ ModifiableModelCommitter.multiCommit(modules.values(), moduleModel);
}
private File getImlDirectory(BlazeImportSettings importSettings) {
@@ -165,7 +152,7 @@
try {
imlVirtualFile.delete(this);
} catch (IOException e) {
- LOG.warn(
+ logger.warn(
String.format(
"Could not delete file: %s, will try to continue anyway.",
imlVirtualFile.getPath()),
diff --git a/base/src/com/google/idea/blaze/base/sync/projectview/ImportRoots.java b/base/src/com/google/idea/blaze/base/sync/projectview/ImportRoots.java
index 5a73ef4..c484df1 100644
--- a/base/src/com/google/idea/blaze/base/sync/projectview/ImportRoots.java
+++ b/base/src/com/google/idea/blaze/base/sync/projectview/ImportRoots.java
@@ -27,7 +27,7 @@
import com.google.idea.blaze.base.projectview.section.sections.DirectoryEntry;
import com.google.idea.blaze.base.projectview.section.sections.DirectorySection;
import com.google.idea.blaze.base.settings.Blaze.BuildSystem;
-import com.intellij.openapi.util.io.FileUtil;
+import com.google.idea.blaze.base.util.WorkspacePathUtil;
import java.util.Collection;
import java.util.Set;
@@ -66,30 +66,20 @@
}
public ImportRoots build() {
- ImmutableCollection<WorkspacePath> rootDirectories = rootDirectoriesBuilder.build();
-
// Remove any duplicates and any overlapping directories
- ImmutableSet.Builder<WorkspacePath> minimalRootDirectories = ImmutableSet.builder();
- for (WorkspacePath directory : rootDirectories) {
- boolean ok = true;
- for (WorkspacePath otherDirectory : rootDirectories) {
- if (directory == otherDirectory) {
- continue;
- }
- ok = ok && !isAncestor(otherDirectory.relativePath(), directory.relativePath());
- }
- if (ok) {
- minimalRootDirectories.add(directory);
- }
- }
+ ImmutableSet<WorkspacePath> minimalRootDirectories =
+ WorkspacePathUtil.calculateMinimalWorkspacePaths(rootDirectoriesBuilder.build());
// for bazel projects, if we're including the workspace root,
// we force-exclude the bazel artifact directories
// (e.g. bazel-bin, bazel-genfiles).
- if (buildSystem == BuildSystem.Bazel && hasWorkspaceRoot(rootDirectories)) {
+ if (buildSystem == BuildSystem.Bazel && hasWorkspaceRoot(minimalRootDirectories)) {
excludeBuildSystemArtifacts();
}
- return new ImportRoots(minimalRootDirectories.build(), excludeDirectoriesBuilder.build());
+ ImmutableSet<WorkspacePath> minimalExcludes =
+ WorkspacePathUtil.calculateMinimalWorkspacePaths(excludeDirectoriesBuilder.build());
+
+ return new ImportRoots(minimalRootDirectories, minimalExcludes);
}
private void excludeBuildSystemArtifacts() {
@@ -158,14 +148,4 @@
}
return false;
}
-
- /** Returns true if 'path' is a strict child of 'ancestorPath'. */
- private static boolean isAncestor(String ancestorPath, String path) {
- // FileUtil.isAncestor has a bug in its handling of equal,
- // empty paths (it ignores the 'strict' flag in this case).
- if (ancestorPath.equals(path)) {
- return false;
- }
- return FileUtil.isAncestor(ancestorPath, path, true);
- }
}
diff --git a/base/src/com/google/idea/blaze/base/sync/projectview/LanguageSupport.java b/base/src/com/google/idea/blaze/base/sync/projectview/LanguageSupport.java
index b01fbdc..da9ae7f 100644
--- a/base/src/com/google/idea/blaze/base/sync/projectview/LanguageSupport.java
+++ b/base/src/com/google/idea/blaze/base/sync/projectview/LanguageSupport.java
@@ -31,7 +31,7 @@
/** Reads the user's language preferences from the project view. */
public class LanguageSupport {
- private static final Logger LOG = Logger.getInstance(LanguageSupport.class);
+ private static final Logger logger = Logger.getInstance(LanguageSupport.class);
public static WorkspaceLanguageSettings createWorkspaceLanguageSettings(
BlazeContext context, ProjectViewSet projectViewSet) {
@@ -56,7 +56,7 @@
}
if (workspaceType == null) {
- LOG.error("Could not find workspace type."); // Should never happen
+ logger.error("Could not find workspace type."); // Should never happen
return null;
}
diff --git a/base/src/com/google/idea/blaze/base/sync/workspace/BlazeRoots.java b/base/src/com/google/idea/blaze/base/sync/workspace/BlazeRoots.java
index aa63765..c66d024 100644
--- a/base/src/com/google/idea/blaze/base/sync/workspace/BlazeRoots.java
+++ b/base/src/com/google/idea/blaze/base/sync/workspace/BlazeRoots.java
@@ -31,7 +31,7 @@
/** The data output by BlazeInfo. */
public class BlazeRoots implements Serializable {
public static final long serialVersionUID = 3L;
- private static final Logger LOG = Logger.getInstance(BlazeRoots.class);
+ private static final Logger logger = Logger.getInstance(BlazeRoots.class);
public static BlazeRoots build(
BuildSystem buildSystem,
@@ -60,8 +60,8 @@
ExecutionRootPath blazeGenfilesExecutionRootPath =
ExecutionRootPath.createAncestorRelativePath(executionRoot, new File(blazeGenfilesRoot));
File externalSourceRootFile = new File(externalSourceRoot.trim());
- LOG.assertTrue(blazeBinExecutionRootPath != null);
- LOG.assertTrue(blazeGenfilesExecutionRootPath != null);
+ logger.assertTrue(blazeBinExecutionRootPath != null);
+ logger.assertTrue(blazeGenfilesExecutionRootPath != null);
return new BlazeRoots(
executionRoot,
packagePaths,
diff --git a/base/src/com/google/idea/blaze/base/sync/workspace/ExecutionRootPathResolver.java b/base/src/com/google/idea/blaze/base/sync/workspace/ExecutionRootPathResolver.java
new file mode 100644
index 0000000..c6b7eef
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/sync/workspace/ExecutionRootPathResolver.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2016 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.sync.workspace;
+
+import com.google.common.collect.ImmutableList;
+import com.google.idea.blaze.base.model.primitives.ExecutionRootPath;
+import com.google.idea.blaze.base.model.primitives.WorkspacePath;
+import com.intellij.openapi.util.io.FileUtil;
+import java.io.File;
+import java.util.List;
+
+/**
+ * Converts execution-root-relative paths to absolute files with a minimum of file system calls
+ * (typically none).
+ *
+ * <p>Files which exist both underneath the execution root and within the workspace will be resolved
+ * to workspace paths.
+ */
+public class ExecutionRootPathResolver {
+
+ private final BlazeRoots blazeRoots;
+ private final WorkspacePathResolver workspacePathResolver;
+
+ public ExecutionRootPathResolver(
+ BlazeRoots blazeRoots, WorkspacePathResolver workspacePathResolver) {
+ this.blazeRoots = blazeRoots;
+ this.workspacePathResolver = workspacePathResolver;
+ }
+
+ /**
+ * This method should be used for directories. Returns all workspace files corresponding to the
+ * given execution-root-relative path. If the file does not exist inside the workspace (e.g. for
+ * blaze output files or external workspace files), returns the path rooted in the execution root.
+ */
+ public ImmutableList<File> resolveToIncludeDirectories(ExecutionRootPath path) {
+ if (path.isAbsolute()) {
+ return ImmutableList.of(path.getAbsoluteOrRelativeFile());
+ }
+ if (isInWorkspace(path)) {
+ WorkspacePath workspacePath = new WorkspacePath(path.getAbsoluteOrRelativeFile().getPath());
+ return workspacePathResolver.resolveToIncludeDirectories(workspacePath);
+ }
+ return ImmutableList.of(path.getFileRootedAt(blazeRoots.executionRoot));
+ }
+
+ private boolean isInWorkspace(ExecutionRootPath path) {
+ boolean inOutputDir =
+ ExecutionRootPath.isAncestor(blazeRoots.blazeBinExecutionRootPath, path, false)
+ || ExecutionRootPath.isAncestor(blazeRoots.blazeGenfilesExecutionRootPath, path, false)
+ || isExternalWorkspacePath(path);
+ return !inOutputDir;
+ }
+
+ private static boolean isExternalWorkspacePath(ExecutionRootPath path) {
+ List<String> pathComponents = FileUtil.splitPath(path.getAbsoluteOrRelativeFile().getPath());
+ return pathComponents.size() > 1 && "external".equals(pathComponents.get(0));
+ }
+}
diff --git a/base/src/com/google/idea/blaze/base/sync/workspace/WorkspacePathResolver.java b/base/src/com/google/idea/blaze/base/sync/workspace/WorkspacePathResolver.java
index f7f8df3..128ca91 100644
--- a/base/src/com/google/idea/blaze/base/sync/workspace/WorkspacePathResolver.java
+++ b/base/src/com/google/idea/blaze/base/sync/workspace/WorkspacePathResolver.java
@@ -16,7 +16,6 @@
package com.google.idea.blaze.base.sync.workspace;
import com.google.common.collect.ImmutableList;
-import com.google.idea.blaze.base.model.primitives.ExecutionRootPath;
import com.google.idea.blaze.base.model.primitives.WorkspacePath;
import java.io.File;
import java.io.Serializable;
@@ -40,9 +39,9 @@
/**
* This method should be used for directories. Returns all workspace files corresponding to the
- * given execution-root-relative path.
+ * given workspace path.
*/
- ImmutableList<File> resolveToIncludeDirectories(ExecutionRootPath executionRootPath);
+ ImmutableList<File> resolveToIncludeDirectories(WorkspacePath relativePath);
/** Finds the package root directory that a workspace relative path is in. */
File findPackageRoot(String relativePath);
diff --git a/base/src/com/google/idea/blaze/base/sync/workspace/WorkspacePathResolverImpl.java b/base/src/com/google/idea/blaze/base/sync/workspace/WorkspacePathResolverImpl.java
index 0dc1c69..ac131c1 100644
--- a/base/src/com/google/idea/blaze/base/sync/workspace/WorkspacePathResolverImpl.java
+++ b/base/src/com/google/idea/blaze/base/sync/workspace/WorkspacePathResolverImpl.java
@@ -17,7 +17,6 @@
import com.google.common.collect.ImmutableList;
import com.google.idea.blaze.base.io.FileAttributeProvider;
-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 java.io.File;
@@ -45,9 +44,8 @@
}
@Override
- public ImmutableList<File> resolveToIncludeDirectories(ExecutionRootPath executionRootPath) {
- File trackedLocation = executionRootPath.getFileRootedAt(workspaceRoot.directory());
- return ImmutableList.of(trackedLocation);
+ public ImmutableList<File> resolveToIncludeDirectories(WorkspacePath relativePath) {
+ return ImmutableList.of(workspaceRoot.fileForPath(relativePath));
}
@Override
diff --git a/base/src/com/google/idea/blaze/base/targetmaps/SourceToTargetMapImpl.java b/base/src/com/google/idea/blaze/base/targetmaps/SourceToTargetMapImpl.java
index 5cf22c6..4d4a28f 100644
--- a/base/src/com/google/idea/blaze/base/targetmaps/SourceToTargetMapImpl.java
+++ b/base/src/com/google/idea/blaze/base/targetmaps/SourceToTargetMapImpl.java
@@ -23,14 +23,9 @@
import com.google.idea.blaze.base.ideinfo.TargetKey;
import com.google.idea.blaze.base.model.BlazeProjectData;
import com.google.idea.blaze.base.model.primitives.Label;
-import com.google.idea.blaze.base.projectview.ProjectViewSet;
-import com.google.idea.blaze.base.scope.BlazeContext;
-import com.google.idea.blaze.base.settings.BlazeImportSettings;
-import com.google.idea.blaze.base.sync.BlazeSyncParams.SyncMode;
-import com.google.idea.blaze.base.sync.SyncListener;
+import com.google.idea.blaze.base.sync.SyncCache;
import com.google.idea.blaze.base.sync.data.BlazeProjectDataManager;
import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoder;
-import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.project.Project;
import java.io.File;
import java.util.Objects;
@@ -40,11 +35,6 @@
/** Maps source files to their respective targets */
public class SourceToTargetMapImpl implements SourceToTargetMap {
private final Project project;
- private ImmutableMultimap<File, TargetKey> sourceToTargetMap;
-
- public static SourceToTargetMapImpl getImpl(Project project) {
- return (SourceToTargetMapImpl) ServiceManager.getService(project, SourceToTargetMap.class);
- }
public SourceToTargetMapImpl(Project project) {
this.project = project;
@@ -80,23 +70,13 @@
@Nullable
private synchronized ImmutableMultimap<File, TargetKey> getSourceToTargetMap() {
- if (this.sourceToTargetMap == null) {
- this.sourceToTargetMap = initSourceToTargetMap();
- }
- return this.sourceToTargetMap;
- }
-
- private synchronized void clearSourceToTargetMap() {
- this.sourceToTargetMap = null;
+ return SyncCache.getInstance(project)
+ .get(SourceToTargetMapImpl.class, SourceToTargetMapImpl::computeSourceToTargetMap);
}
@Nullable
- private ImmutableMultimap<File, TargetKey> initSourceToTargetMap() {
- BlazeProjectData blazeProjectData =
- BlazeProjectDataManager.getInstance(project).getBlazeProjectData();
- if (blazeProjectData == null) {
- return null;
- }
+ private static ImmutableMultimap<File, TargetKey> computeSourceToTargetMap(
+ Project project, BlazeProjectData blazeProjectData) {
ArtifactLocationDecoder artifactLocationDecoder = blazeProjectData.artifactLocationDecoder;
ImmutableMultimap.Builder<File, TargetKey> sourceToTargetMap = ImmutableMultimap.builder();
for (TargetIdeInfo target : blazeProjectData.targetMap.targets()) {
@@ -107,18 +87,4 @@
}
return sourceToTargetMap.build();
}
-
- static class ClearSourceToTargetMap extends SyncListener.Adapter {
- @Override
- public void onSyncComplete(
- Project project,
- BlazeContext context,
- BlazeImportSettings importSettings,
- ProjectViewSet projectViewSet,
- BlazeProjectData blazeProjectData,
- SyncMode syncMode,
- SyncResult syncResult) {
- getImpl(project).clearSourceToTargetMap();
- }
- }
}
diff --git a/base/src/com/google/idea/blaze/base/util/SerializationUtil.java b/base/src/com/google/idea/blaze/base/util/SerializationUtil.java
index 51f46d4..f098673 100644
--- a/base/src/com/google/idea/blaze/base/util/SerializationUtil.java
+++ b/base/src/com/google/idea/blaze/base/util/SerializationUtil.java
@@ -31,7 +31,7 @@
/** Utils for serialization. */
public class SerializationUtil {
- private static final Logger LOG = Logger.getInstance(SerializationUtil.class.getName());
+ private static final Logger logger = Logger.getInstance(SerializationUtil.class.getName());
public static void saveToDisk(@NotNull File file, @NotNull Serializable serializable)
throws IOException {
diff --git a/base/src/com/google/idea/blaze/base/util/UrlUtil.java b/base/src/com/google/idea/blaze/base/util/UrlUtil.java
new file mode 100644
index 0000000..a2cc7b2
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/util/UrlUtil.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2017 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.util;
+
+import com.google.idea.blaze.base.io.VirtualFileSystemProvider;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.io.FileUtilRt;
+import com.intellij.openapi.vfs.VirtualFileManager;
+import com.intellij.util.io.URLUtil;
+import java.io.File;
+
+/** Utility methods for converting between URLs and file paths. */
+public class UrlUtil {
+
+ public static File urlToFile(String url) {
+ return new File(VirtualFileManager.extractPath(url));
+ }
+
+ public static String fileToIdeaUrl(File path) {
+ return pathToUrl(toSystemIndependentName(path.getPath()));
+ }
+
+ public static String pathToUrl(String filePath) {
+ filePath = FileUtil.toSystemIndependentName(filePath);
+ if (filePath.endsWith(".srcjar") || filePath.endsWith(".jar")) {
+ return URLUtil.JAR_PROTOCOL + URLUtil.SCHEME_SEPARATOR + filePath + URLUtil.JAR_SEPARATOR;
+ } else if (filePath.contains("src.jar!")) {
+ return URLUtil.JAR_PROTOCOL + URLUtil.SCHEME_SEPARATOR + filePath;
+ } else {
+ return VirtualFileManager.constructUrl(
+ VirtualFileSystemProvider.getInstance().getSystem().getProtocol(), filePath);
+ }
+ }
+
+ private static String toSystemIndependentName(String aFileName) {
+ return FileUtilRt.toSystemIndependentName(aFileName);
+ }
+}
diff --git a/base/src/com/google/idea/blaze/base/util/WorkspacePathUtil.java b/base/src/com/google/idea/blaze/base/util/WorkspacePathUtil.java
new file mode 100644
index 0000000..e77df9e
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/util/WorkspacePathUtil.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2016 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.util;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.idea.blaze.base.model.primitives.WorkspacePath;
+import com.intellij.openapi.util.io.FileUtil;
+import java.util.Collection;
+
+/** Removes any duplicates or overlapping directories */
+public class WorkspacePathUtil {
+
+ /** Returns whether the given workspace path is a child of any workspace path. */
+ public static boolean isUnderAnyWorkspacePath(
+ Collection<WorkspacePath> ancestors, WorkspacePath child) {
+ return ancestors
+ .stream()
+ .anyMatch(
+ importRoot ->
+ FileUtil.isAncestor(importRoot.relativePath(), child.relativePath(), false));
+ }
+
+ /** Removes any duplicates or overlapping directories */
+ public static ImmutableSet<WorkspacePath> calculateMinimalWorkspacePaths(
+ Collection<WorkspacePath> workspacePaths) {
+ ImmutableSet.Builder<WorkspacePath> minimalWorkspacePaths = ImmutableSet.builder();
+ for (WorkspacePath directory : workspacePaths) {
+ boolean ok = true;
+ for (WorkspacePath otherDirectory : workspacePaths) {
+ if (directory.equals(otherDirectory)) {
+ continue;
+ }
+ if (FileUtil.isAncestor(otherDirectory.relativePath(), directory.relativePath(), true)) {
+ ok = false;
+ break;
+ }
+ }
+ if (ok) {
+ minimalWorkspacePaths.add(directory);
+ }
+ }
+ return minimalWorkspacePaths.build();
+ }
+}
diff --git a/base/src/com/google/idea/blaze/base/vcs/BlazeVcsHandler.java b/base/src/com/google/idea/blaze/base/vcs/BlazeVcsHandler.java
index b05e69a..c5a855f 100644
--- a/base/src/com/google/idea/blaze/base/vcs/BlazeVcsHandler.java
+++ b/base/src/com/google/idea/blaze/base/vcs/BlazeVcsHandler.java
@@ -17,12 +17,12 @@
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
+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;
import com.google.idea.blaze.base.settings.Blaze;
import com.google.idea.blaze.base.settings.Blaze.BuildSystem;
-import com.google.idea.blaze.base.sync.workspace.BlazeRoots;
import com.google.idea.blaze.base.sync.workspace.WorkingSet;
import com.google.idea.blaze.base.sync.workspace.WorkspacePathResolver;
import com.intellij.openapi.extensions.ExtensionPointName;
@@ -59,6 +59,14 @@
WorkspaceRoot workspaceRoot,
ListeningExecutorService executor);
+ /** Returns the original file content of a file path from "upstream". */
+ ListenableFuture<String> getUpstreamContent(
+ Project project,
+ BlazeContext context,
+ WorkspaceRoot workspaceRoot,
+ WorkspacePath path,
+ ListeningExecutorService executor);
+
/** Optionally creates a sync handler to perform vcs-specific computation during sync. */
@Nullable
BlazeVcsSyncHandler createSyncHandler(Project project, WorkspaceRoot workspaceRoot);
@@ -76,7 +84,7 @@
*
* @return True for OK, false to abort the sync process.
*/
- boolean update(BlazeContext context, BlazeRoots blazeRoots, ListeningExecutorService executor);
+ boolean update(BlazeContext context, ListeningExecutorService executor);
/** Returns a custom workspace path resolver for this vcs. */
@Nullable
diff --git a/base/src/com/google/idea/blaze/base/vcs/FallbackBlazeVcsHandler.java b/base/src/com/google/idea/blaze/base/vcs/FallbackBlazeVcsHandler.java
index a975203..63cfcf4 100644
--- a/base/src/com/google/idea/blaze/base/vcs/FallbackBlazeVcsHandler.java
+++ b/base/src/com/google/idea/blaze/base/vcs/FallbackBlazeVcsHandler.java
@@ -18,6 +18,7 @@
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.idea.blaze.base.model.primitives.WorkspacePath;
import com.google.idea.blaze.base.model.primitives.WorkspaceRoot;
import com.google.idea.blaze.base.scope.BlazeContext;
import com.google.idea.blaze.base.settings.Blaze.BuildSystem;
@@ -50,6 +51,16 @@
return Futures.immediateFuture(null);
}
+ @Override
+ public ListenableFuture<String> getUpstreamContent(
+ Project project,
+ BlazeContext context,
+ WorkspaceRoot workspaceRoot,
+ WorkspacePath path,
+ ListeningExecutorService executor) {
+ return Futures.immediateFuture("");
+ }
+
@Nullable
@Override
public BlazeVcsSyncHandler createSyncHandler(Project project, WorkspaceRoot workspaceRoot) {
diff --git a/base/src/com/google/idea/blaze/base/vcs/git/GitBlazeVcsHandler.java b/base/src/com/google/idea/blaze/base/vcs/git/GitBlazeVcsHandler.java
index e1ffb78..4e58d0d 100644
--- a/base/src/com/google/idea/blaze/base/vcs/git/GitBlazeVcsHandler.java
+++ b/base/src/com/google/idea/blaze/base/vcs/git/GitBlazeVcsHandler.java
@@ -19,6 +19,7 @@
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.idea.blaze.base.async.process.ExternalTask;
import com.google.idea.blaze.base.io.FileAttributeProvider;
+import com.google.idea.blaze.base.model.primitives.WorkspacePath;
import com.google.idea.blaze.base.model.primitives.WorkspaceRoot;
import com.google.idea.blaze.base.scope.BlazeContext;
import com.google.idea.blaze.base.settings.Blaze.BuildSystem;
@@ -34,7 +35,7 @@
/** Vcs diff provider for git */
public class GitBlazeVcsHandler implements BlazeVcsHandler {
- private static final Logger LOG = Logger.getInstance(GitBlazeVcsHandler.class);
+ private static final Logger logger = Logger.getInstance(GitBlazeVcsHandler.class);
@Override
public String getVcsName() {
@@ -70,6 +71,36 @@
return null;
}
+ @Override
+ public ListenableFuture<String> getUpstreamContent(
+ Project project,
+ BlazeContext context,
+ WorkspaceRoot workspaceRoot,
+ WorkspacePath path,
+ ListeningExecutorService executor) {
+ return executor.submit(() -> getGitUpstreamContent(workspaceRoot, path));
+ }
+
+ private static String getGitUpstreamContent(WorkspaceRoot workspaceRoot, WorkspacePath path) {
+ String upstreamSha = getUpstreamSha(workspaceRoot, false);
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ ExternalTask.builder(workspaceRoot)
+ .args(
+ "git",
+ "show",
+ // Normally "For plain blobs, it shows the plain contents.", but let's add some
+ // options to be a bit more paranoid.
+ "--no-color",
+ "--no-expand-tabs",
+ "--no-notes",
+ "--no-textconv",
+ String.format("%s:./%s", upstreamSha, path.relativePath()))
+ .stdout(outputStream)
+ .build()
+ .run();
+ return outputStream.toString();
+ }
+
private static boolean isGitRepository(WorkspaceRoot workspaceRoot) {
// TODO: What if the git repo root is a parent directory of the workspace root?
// Just call 'git rev-parse --is-inside-work-tree' or similar instead?
@@ -102,7 +133,7 @@
.run();
if (retVal != 0) {
if (!suppressErrors) {
- LOG.error(stderr);
+ logger.error(stderr);
}
return null;
}
diff --git a/base/src/com/google/idea/blaze/base/vcs/git/GitWorkingSetProvider.java b/base/src/com/google/idea/blaze/base/vcs/git/GitWorkingSetProvider.java
index cfef8c0..465c6ac 100644
--- a/base/src/com/google/idea/blaze/base/vcs/git/GitWorkingSetProvider.java
+++ b/base/src/com/google/idea/blaze/base/vcs/git/GitWorkingSetProvider.java
@@ -33,7 +33,7 @@
/** Vcs diff provider for git. */
public class GitWorkingSetProvider {
- private static final Logger LOG = Logger.getInstance(GitWorkingSetProvider.class);
+ private static final Logger logger = Logger.getInstance(GitWorkingSetProvider.class);
/**
* Finds all changes between HEAD and the git commit specified by the provided SHA.<br>
@@ -58,7 +58,7 @@
.build()
.run();
if (retVal != 0) {
- LOG.error(stderr);
+ logger.error(stderr);
return null;
}
@@ -100,7 +100,7 @@
.build()
.run();
if (retVal != 0) {
- LOG.error(stderr);
+ logger.error(stderr);
return null;
}
return StringUtil.trimEnd(stdout.toString(), "\n");
diff --git a/base/src/com/google/idea/blaze/base/wizard2/BlazeNewProjectBuilder.java b/base/src/com/google/idea/blaze/base/wizard2/BlazeNewProjectBuilder.java
index 11b4fbb..16648e6 100644
--- a/base/src/com/google/idea/blaze/base/wizard2/BlazeNewProjectBuilder.java
+++ b/base/src/com/google/idea/blaze/base/wizard2/BlazeNewProjectBuilder.java
@@ -40,7 +40,7 @@
/** Contains the state to build a new project throughout the new project wizard process. */
public final class BlazeNewProjectBuilder {
- private static final Logger LOG = Logger.getInstance(BlazeNewProjectBuilder.class);
+ private static final Logger logger = Logger.getInstance(BlazeNewProjectBuilder.class);
// The import wizard should keep this many items around for fields that care about history
public static final int HISTORY_SIZE = 8;
@@ -198,7 +198,7 @@
}
try {
- LOG.assertTrue(projectViewFile != null);
+ logger.assertTrue(projectViewFile != null);
ProjectViewStorageManager.getInstance()
.writeProjectView(ProjectViewParser.projectViewToString(projectView), projectViewFile);
} catch (IOException e) {
diff --git a/base/src/com/google/idea/blaze/base/wizard2/BlazeSelectProjectViewOption.java b/base/src/com/google/idea/blaze/base/wizard2/BlazeSelectProjectViewOption.java
index 2f7a6b5..353ecb4 100644
--- a/base/src/com/google/idea/blaze/base/wizard2/BlazeSelectProjectViewOption.java
+++ b/base/src/com/google/idea/blaze/base/wizard2/BlazeSelectProjectViewOption.java
@@ -37,5 +37,10 @@
return false;
}
+ /** Returns the default project name */
+ default String getDefaultProjectName(String workspaceName) {
+ return workspaceName;
+ }
+
void commit();
}
diff --git a/base/src/com/google/idea/blaze/base/wizard2/GenerateFromBuildFileSelectProjectViewOption.java b/base/src/com/google/idea/blaze/base/wizard2/GenerateFromBuildFileSelectProjectViewOption.java
index b2edeff..341d023 100644
--- a/base/src/com/google/idea/blaze/base/wizard2/GenerateFromBuildFileSelectProjectViewOption.java
+++ b/base/src/com/google/idea/blaze/base/wizard2/GenerateFromBuildFileSelectProjectViewOption.java
@@ -33,6 +33,7 @@
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
import com.intellij.openapi.fileChooser.FileChooserDialog;
import com.intellij.openapi.fileChooser.FileChooserFactory;
+import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
@@ -119,6 +120,12 @@
}
@Override
+ public String getDefaultProjectName(String workspaceName) {
+ File buildFileParent = new File(getBuildFilePath()).getParentFile();
+ return buildFileParent != null ? buildFileParent.getName() : workspaceName;
+ }
+
+ @Override
public void commit() {
userSettings.put(LAST_WORKSPACE_PATH, getBuildFilePath());
buildFilePathField.addCurrentTextToHistory();
@@ -182,6 +189,7 @@
private void chooseWorkspacePath() {
BuildSystemProvider buildSystem =
BuildSystemProvider.getBuildSystemProvider(builder.getBuildSystem());
+ assert buildSystem != null;
FileChooserDescriptor descriptor =
new FileChooserDescriptor(true, false, false, false, false, false)
.withShowHiddenFiles(true) // Show root project view file
@@ -216,6 +224,14 @@
return;
}
VirtualFile file = files[0];
+
+ if (!FileUtil.isAncestor(fileBrowserRoot.getPath(), file.getPath(), true)) {
+ Messages.showErrorDialog(
+ String.format("You must choose a BUILD file under %s.", fileBrowserRoot.getPath()),
+ "Cannot Use BUILD File");
+ return;
+ }
+
String newWorkspacePath = FileUtil.getRelativePath(fileBrowserRoot, new File(file.getPath()));
buildFilePathField.setText(newWorkspacePath);
}
diff --git a/base/src/com/google/idea/blaze/base/wizard2/ImportFromWorkspaceProjectViewOption.java b/base/src/com/google/idea/blaze/base/wizard2/ImportFromWorkspaceProjectViewOption.java
index bdec2eb..fe43d1c 100644
--- a/base/src/com/google/idea/blaze/base/wizard2/ImportFromWorkspaceProjectViewOption.java
+++ b/base/src/com/google/idea/blaze/base/wizard2/ImportFromWorkspaceProjectViewOption.java
@@ -109,6 +109,16 @@
}
@Override
+ public String getDefaultProjectName(String workspaceName) {
+ File projectViewFile = new File(getProjectViewPath());
+ File projectViewDirectory = projectViewFile.getParentFile();
+ if (projectViewDirectory == null) {
+ return workspaceName;
+ }
+ return projectViewDirectory.getName();
+ }
+
+ @Override
public void commit() {
userSettings.put(LAST_WORKSPACE_PATH, getProjectViewPath());
projectViewPathField.addCurrentTextToHistory();
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 4f3be05..e936db3 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
@@ -79,7 +79,7 @@
private static final FileChooserDescriptor PROJECT_FOLDER_DESCRIPTOR =
new FileChooserDescriptor(false, true, false, false, false, false);
- private static final Logger LOG = Logger.getInstance(BlazeEditProjectViewControl.class);
+ private static final Logger logger = Logger.getInstance(BlazeEditProjectViewControl.class);
private static final BoolExperiment allowAddprojectViewDefaultValues =
new BoolExperiment("allow.add.project.view.default.values", true);
@@ -148,7 +148,8 @@
public void update(BlazeNewProjectBuilder builder) {
BlazeSelectWorkspaceOption workspaceOption = builder.getWorkspaceOption();
BlazeSelectProjectViewOption projectViewOption = builder.getProjectViewOption();
- String workspaceName = workspaceOption.getWorkspaceName();
+ String defaultProjectName =
+ projectViewOption.getDefaultProjectName(workspaceOption.getWorkspaceName());
WorkspaceRoot workspaceRoot = workspaceOption.getWorkspaceRoot();
WorkspacePath workspacePath = projectViewOption.getSharedProjectView();
String initialProjectViewText = projectViewOption.getInitialProjectViewText();
@@ -160,7 +161,7 @@
HashCode hashCode =
Hashing.md5()
.newHasher()
- .putUnencodedChars(workspaceName)
+ .putUnencodedChars(defaultProjectName)
.putUnencodedChars(workspaceRoot.toString())
.putUnencodedChars(workspacePath != null ? workspacePath.toString() : "")
.putUnencodedChars(initialProjectViewText != null ? initialProjectViewText : "")
@@ -171,7 +172,7 @@
if (!hashCode.equals(paramsHash)) {
this.paramsHash = hashCode;
init(
- workspaceName,
+ defaultProjectName,
workspaceRoot,
workspacePathResolver,
workspacePath,
@@ -198,7 +199,7 @@
}
private void init(
- String workspaceName,
+ String defaultProjectName,
WorkspaceRoot workspaceRoot,
WorkspacePathResolver workspacePathResolver,
@Nullable WorkspacePath sharedProjectView,
@@ -211,8 +212,8 @@
this.workspaceRoot = workspaceRoot;
this.workspacePathResolver = workspacePathResolver;
- projectNameField.setText(workspaceName);
- String defaultDataDir = getDefaultProjectDataDirectory(workspaceName);
+ projectNameField.setText(defaultProjectName);
+ String defaultDataDir = getDefaultProjectDataDirectory(defaultProjectName);
projectDataDirField.setText(defaultDataDir);
String projectViewText = "";
@@ -225,15 +226,15 @@
projectViewText =
ProjectViewStorageManager.getInstance().loadProjectView(sharedProjectViewFile);
if (projectViewText == null) {
- LOG.error("Could not load project view: " + sharedProjectViewFile);
+ logger.error("Could not load project view: " + sharedProjectViewFile);
projectViewText = "";
}
} catch (IOException e) {
- LOG.error(e);
+ logger.error(e);
}
} else {
projectViewText = initialProjectViewText;
- LOG.assertTrue(projectViewText != null);
+ logger.assertTrue(projectViewText != null);
}
projectViewUi.init(
diff --git a/base/src/com/google/idea/blaze/base/wizard2/ui/BlazeSelectOptionControl.java b/base/src/com/google/idea/blaze/base/wizard2/ui/BlazeSelectOptionControl.java
index a79644d..69a36e4 100644
--- a/base/src/com/google/idea/blaze/base/wizard2/ui/BlazeSelectOptionControl.java
+++ b/base/src/com/google/idea/blaze/base/wizard2/ui/BlazeSelectOptionControl.java
@@ -37,7 +37,7 @@
/** UI for selecting a client during the import process. */
public abstract class BlazeSelectOptionControl<T extends BlazeWizardOption> {
- private static final Logger LOG = Logger.getInstance(BlazeSelectOptionControl.class);
+ private static final Logger logger = Logger.getInstance(BlazeSelectOptionControl.class);
private final BlazeWizardUserSettings userSettings;
private final JPanel canvas;
@@ -56,7 +56,7 @@
BlazeSelectOptionControl(BlazeNewProjectBuilder builder, Collection<T> options) {
if (options == null) {
- LOG.error("No options on select screen '" + getTitle() + "'");
+ logger.error("No options on select screen '" + getTitle() + "'");
}
this.userSettings = builder.getUserSettings();
diff --git a/base/tests/integrationtests/com/google/idea/blaze/base/lang/buildfile/parser/BuildParserTest.java b/base/tests/integrationtests/com/google/idea/blaze/base/lang/buildfile/parser/BuildParserTest.java
index 4d1a295..3823ea8 100644
--- a/base/tests/integrationtests/com/google/idea/blaze/base/lang/buildfile/parser/BuildParserTest.java
+++ b/base/tests/integrationtests/com/google/idea/blaze/base/lang/buildfile/parser/BuildParserTest.java
@@ -63,7 +63,7 @@
@Test
public void testAssign() throws Exception {
- assertThat(parse("a, b = 5\n")).isEqualTo("assignment(list(reference, target), int)");
+ assertThat(parse("a, b = 5\n")).isEqualTo("assignment(tuple(reference, target), int)");
assertNoErrors();
}
@@ -244,24 +244,43 @@
@Test
public void testEmptyTuple() throws Exception {
- assertThat(parse("()")).isEqualTo("list");
- assertNoErrors();
- }
-
- @Test
- public void testTupleTrailingComma() throws Exception {
- assertThat(parse("(42,)")).isEqualTo("list(int)");
+ assertThat(parse("()")).isEqualTo("tuple");
assertNoErrors();
}
@Test
public void testSingleton() throws Exception {
- assertThat(parse("(42)")) // not a tuple!
- .isEqualTo("list(int)");
+ assertThat(parse("(42)")).isEqualTo("parens(int)");
assertNoErrors();
}
@Test
+ public void testTupleWithoutParens() throws Exception {
+ assertThat(parse("a,b = 1")).isEqualTo("assignment(tuple(reference, target), int)");
+ assertNoErrors();
+ }
+
+ @Test
+ public void testTupleWithParens() throws Exception {
+ assertThat(parse("(a,b) = 1"))
+ .isEqualTo("assignment(parens(tuple(reference, reference)), int)");
+ assertNoErrors();
+ }
+
+ @Test
+ public void testTupleTrailingComma() throws Exception {
+ assertThat(parse("(42,)")).isEqualTo("parens(tuple(int))");
+ assertNoErrors();
+ }
+
+ @Test
+ public void testTupleTrailingCommaWithoutParens() throws Exception {
+ // valid python, but not valid skylark
+ assertThat(parse("42,1,")).isEqualTo("tuple(int, int)");
+ assertContainsError("Trailing commas are allowed only in parenthesized tuples.");
+ }
+
+ @Test
public void testDictionaryLiterals() throws Exception {
assertThat(parse("{1:42}")).isEqualTo("dict(dict_entry(int, int))");
assertNoErrors();
@@ -370,21 +389,21 @@
@Test
public void testPrecedence2() {
assertThat(parse("('%sx' + 'foo') * 'bar'"))
- .isEqualTo("binary_op(list(binary_op(string, string)), string)");
+ .isEqualTo("binary_op(parens(binary_op(string, string)), string)");
assertNoErrors();
}
@Test
public void testPrecedence3() {
assertThat(parse("'%sx' % ('foo' + 'bar')"))
- .isEqualTo("binary_op(string, list(binary_op(string, string)))");
+ .isEqualTo("binary_op(string, parens(binary_op(string, string)))");
assertNoErrors();
}
@Test
public void testPrecedence4() throws Exception {
assertThat(parse("1 + - (2 - 3)"))
- .isEqualTo("binary_op(int, positional(list(binary_op(int, int))))");
+ .isEqualTo("binary_op(int, positional(parens(binary_op(int, int))))");
assertNoErrors();
}
diff --git a/base/tests/integrationtests/com/google/idea/blaze/base/lang/buildfile/validation/BuiltInRuleAnnotatorTest.java b/base/tests/integrationtests/com/google/idea/blaze/base/lang/buildfile/validation/BuiltInRuleAnnotatorTest.java
new file mode 100644
index 0000000..28eece6
--- /dev/null
+++ b/base/tests/integrationtests/com/google/idea/blaze/base/lang/buildfile/validation/BuiltInRuleAnnotatorTest.java
@@ -0,0 +1,298 @@
+/*
+ * Copyright 2017 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.lang.buildfile.validation;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.idea.blaze.base.lang.buildfile.BuildFileIntegrationTestCase;
+import com.google.idea.blaze.base.lang.buildfile.language.semantics.AttributeDefinition;
+import com.google.idea.blaze.base.lang.buildfile.language.semantics.BuildLanguageSpec;
+import com.google.idea.blaze.base.lang.buildfile.language.semantics.BuildLanguageSpecProvider;
+import com.google.idea.blaze.base.lang.buildfile.language.semantics.RuleDefinition;
+import com.google.idea.blaze.base.lang.buildfile.psi.BuildFile;
+import com.google.idea.blaze.base.lang.buildfile.psi.FuncallExpression;
+import com.google.idea.blaze.base.lang.buildfile.psi.util.PsiUtils;
+import com.google.idea.blaze.base.model.primitives.WorkspacePath;
+import com.google.repackaged.devtools.build.lib.query2.proto.proto2api.Build.Attribute.Discriminator;
+import com.intellij.codeInsight.daemon.impl.AnnotationHolderImpl;
+import com.intellij.lang.annotation.Annotation;
+import com.intellij.lang.annotation.AnnotationHolder;
+import com.intellij.lang.annotation.AnnotationSession;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.PsiFile;
+import java.util.List;
+import java.util.stream.Collectors;
+import javax.annotation.Nullable;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Integration tests for {@link BuiltInRuleAnnotator}. */
+@RunWith(JUnit4.class)
+public class BuiltInRuleAnnotatorTest extends BuildFileIntegrationTestCase {
+
+ private static final AttributeDefinition NAME_ATTRIBUTE =
+ new AttributeDefinition("name", Discriminator.STRING, true, null, null);
+
+ private static final AttributeDefinition SRCS_ATTRIBUTE =
+ new AttributeDefinition("srcs", Discriminator.LABEL_LIST, false, null, null);
+
+ private static final AttributeDefinition NEVERLINK_ATTRIBUTE =
+ new AttributeDefinition("neverlink", Discriminator.BOOLEAN, false, null, null);
+
+ private static final RuleDefinition JAVA_TEST =
+ new RuleDefinition(
+ "java_test",
+ ImmutableMap.of(
+ "name", NAME_ATTRIBUTE, "srcs", SRCS_ATTRIBUTE, "neverlink", NEVERLINK_ATTRIBUTE),
+ null);
+
+ private MockBuildLanguageSpecProvider specProvider;
+
+ @Before
+ public final void before() {
+ specProvider = new MockBuildLanguageSpecProvider();
+ registerApplicationService(BuildLanguageSpecProvider.class, specProvider);
+ }
+
+ @Test
+ public void testUnrecognizedRuleTypeIgnored() {
+ setRules("java_library", "java_binary");
+ BuildFile file =
+ createBuildFile(
+ new WorkspacePath("java/com/google/BUILD"), "java_import(name = 'import',)");
+
+ assertNoErrors(file);
+ }
+
+ @Test
+ public void testNoErrorsForValidStandardRule() {
+ specProvider.setRules(ImmutableMap.of(JAVA_TEST.name, JAVA_TEST));
+ BuildFile file =
+ createBuildFile(
+ new WorkspacePath("java/com/google/BUILD"),
+ "java_test(",
+ " name = 'import',",
+ " srcs = ['src'],",
+ " neverlink = 0,",
+ ")");
+ assertNoErrors(file);
+ }
+
+ @Test
+ public void testGlobTreatedAsList() {
+ specProvider.setRules(ImmutableMap.of(JAVA_TEST.name, JAVA_TEST));
+ BuildFile file =
+ createBuildFile(
+ new WorkspacePath("java/com/google/BUILD"),
+ "java_test(",
+ " name = 'import',",
+ " srcs = glob(['src']),",
+ " neverlink = 0,",
+ ")");
+ assertNoErrors(file);
+ }
+
+ @Test
+ public void testMissingMandatoryAttributeError() {
+ specProvider.setRules(ImmutableMap.of(JAVA_TEST.name, JAVA_TEST));
+ BuildFile file =
+ createBuildFile(
+ new WorkspacePath("java/com/google/BUILD"),
+ "java_test(",
+ " srcs = ['src'],",
+ " neverlink = 0,",
+ ")");
+ assertHasError(file, "Target missing required attribute(s): name");
+ }
+
+ @Test
+ public void testInvalidAttributeTypeError() {
+ specProvider.setRules(ImmutableMap.of(JAVA_TEST.name, JAVA_TEST));
+ BuildFile file =
+ createBuildFile(
+ new WorkspacePath("java/com/google/BUILD"),
+ "java_test(",
+ " name = 'import',",
+ " srcs = 'src',",
+ " neverlink = 0,",
+ ")");
+ assertHasError(
+ file,
+ String.format(
+ "Invalid value for attribute 'srcs'. Expected a value of type '%s'",
+ Discriminator.LABEL_LIST));
+ }
+
+ @Test
+ public void testInvalidAttributeTypeError2() {
+ specProvider.setRules(ImmutableMap.of(JAVA_TEST.name, JAVA_TEST));
+ BuildFile file =
+ createBuildFile(
+ new WorkspacePath("java/com/google/BUILD"),
+ "java_test(",
+ " name = ['import'],",
+ " srcs = ['src'],",
+ " neverlink = 0,",
+ ")");
+ assertHasError(
+ file,
+ String.format(
+ "Invalid value for attribute 'name'. Expected a value of type '%s'",
+ Discriminator.STRING));
+ }
+
+ @Test
+ public void testNoErrorForIfStatement() {
+ specProvider.setRules(ImmutableMap.of(JAVA_TEST.name, JAVA_TEST));
+ BuildFile file =
+ createBuildFile(
+ new WorkspacePath("java/com/google/BUILD"),
+ "java_test(",
+ " name = 'import',",
+ " srcs = ['src'],",
+ " neverlink = if test : a else b",
+ ")");
+ // we don't know what the if statement evaluates to
+ assertNoErrors(file);
+ }
+
+ @Test
+ public void testUnknownReferenceIgnored() {
+ specProvider.setRules(ImmutableMap.of(JAVA_TEST.name, JAVA_TEST));
+ BuildFile file =
+ createBuildFile(
+ new WorkspacePath("java/com/google/BUILD"),
+ "java_test(",
+ " name = 'import',",
+ " srcs = ['src'],",
+ " neverlink = ref,",
+ ")");
+ assertNoErrors(file);
+ }
+
+ @Test
+ public void testKnownIncorrectReference() {
+ specProvider.setRules(ImmutableMap.of(JAVA_TEST.name, JAVA_TEST));
+ BuildFile file =
+ createBuildFile(
+ new WorkspacePath("java/com/google/BUILD"),
+ "ref = []",
+ "java_test(",
+ " name = 'import',",
+ " srcs = ['src'],",
+ " neverlink = ref,",
+ ")");
+ assertHasError(
+ file,
+ String.format(
+ "Invalid value for attribute 'neverlink'. Expected a value of type '%s'",
+ Discriminator.BOOLEAN));
+ }
+
+ @Test
+ public void testValidValueInsideParenthesizedExpression() {
+ specProvider.setRules(ImmutableMap.of(JAVA_TEST.name, JAVA_TEST));
+ BuildFile file =
+ createBuildFile(
+ new WorkspacePath("java/com/google/BUILD"),
+ "java_test(",
+ " name = ('import' + '_suffix'),",
+ ")");
+ assertNoErrors(file);
+ }
+
+ @Test
+ public void testInvalidValueInsideParenthesizedExpression() {
+ specProvider.setRules(ImmutableMap.of(JAVA_TEST.name, JAVA_TEST));
+ BuildFile file =
+ createBuildFile(
+ new WorkspacePath("java/com/google/BUILD"), "java_test(", " name = (1),", ")");
+ assertHasError(
+ file,
+ String.format(
+ "Invalid value for attribute 'name'. Expected a value of type '%s'",
+ Discriminator.STRING));
+ }
+
+ @Test
+ public void testUnresolvedValueInsideParenthesizedExpression() {
+ specProvider.setRules(ImmutableMap.of(JAVA_TEST.name, JAVA_TEST));
+ BuildFile file =
+ createBuildFile(
+ new WorkspacePath("java/com/google/BUILD"), "java_test(", " name = (ref),", ")");
+ assertNoErrors(file);
+ }
+
+ private void setRules(String... ruleNames) {
+ ImmutableMap.Builder<String, RuleDefinition> rules = ImmutableMap.builder();
+ for (String name : ruleNames) {
+ rules.put(name, new RuleDefinition(name, ImmutableMap.of(), null));
+ }
+ specProvider.setRules(rules.build());
+ }
+
+ private static class MockBuildLanguageSpecProvider implements BuildLanguageSpecProvider {
+
+ BuildLanguageSpec languageSpec = new BuildLanguageSpec(ImmutableMap.of());
+
+ void setRules(ImmutableMap<String, RuleDefinition> rules) {
+ languageSpec = new BuildLanguageSpec(rules);
+ }
+
+ @Nullable
+ @Override
+ public BuildLanguageSpec getLanguageSpec(Project project) {
+ return languageSpec;
+ }
+ }
+
+ private void assertNoErrors(BuildFile file) {
+ assertThat(validateFile(file)).isEmpty();
+ }
+
+ private void assertHasError(BuildFile file, String error) {
+ assertHasError(validateFile(file), error);
+ }
+
+ private static void assertHasError(List<Annotation> annotations, String error) {
+ List<String> messages =
+ annotations.stream().map(Annotation::getMessage).collect(Collectors.toList());
+
+ assertThat(messages).contains(error);
+ }
+
+ private List<Annotation> validateFile(BuildFile file) {
+ BuiltInRuleAnnotator annotator = createAnnotator(file);
+ PsiUtils.findAllChildrenOfClassRecursive(file, FuncallExpression.class)
+ .forEach(annotator::visitFuncallExpression);
+ return annotationHolder;
+ }
+
+ private BuiltInRuleAnnotator createAnnotator(PsiFile file) {
+ annotationHolder = new AnnotationHolderImpl(new AnnotationSession(file));
+ return new BuiltInRuleAnnotator() {
+ @Override
+ protected AnnotationHolder getHolder() {
+ return annotationHolder;
+ }
+ };
+ }
+
+ private AnnotationHolderImpl annotationHolder = null;
+}
diff --git a/base/tests/integrationtests/com/google/idea/blaze/base/sync/ImportRootsTest.java b/base/tests/integrationtests/com/google/idea/blaze/base/sync/ImportRootsTest.java
index 139ca30..84bf00f 100644
--- a/base/tests/integrationtests/com/google/idea/blaze/base/sync/ImportRootsTest.java
+++ b/base/tests/integrationtests/com/google/idea/blaze/base/sync/ImportRootsTest.java
@@ -127,4 +127,15 @@
.build();
assertThat(importRoots.rootDirectories()).containsExactly(new WorkspacePath("."));
}
+
+ @Test
+ public void testOverlappingExcludesAreFiltered() {
+ ImportRoots importRoots =
+ ImportRoots.builder(workspaceRoot, BuildSystem.Blaze)
+ .add(new DirectoryEntry(new WorkspacePath("root"), false))
+ .add(new DirectoryEntry(new WorkspacePath("root"), false))
+ .add(new DirectoryEntry(new WorkspacePath("root/subdir"), false))
+ .build();
+ assertThat(importRoots.excludeDirectories()).containsExactly(new WorkspacePath("root"));
+ }
}
diff --git a/base/tests/unittests/com/google/idea/blaze/base/actions/BlazeBuildServiceTest.java b/base/tests/unittests/com/google/idea/blaze/base/actions/BlazeBuildServiceTest.java
index 6ca947d..cce3237 100644
--- a/base/tests/unittests/com/google/idea/blaze/base/actions/BlazeBuildServiceTest.java
+++ b/base/tests/unittests/com/google/idea/blaze/base/actions/BlazeBuildServiceTest.java
@@ -85,7 +85,7 @@
assertThat(service).isNotNull();
// Can't mock BlazeExecutor.submitTask.
- doNothing().when(service).buildTargetExpressions(any(), any(), any(), any(), any());
+ doNothing().when(service).buildTargetExpressions(any(), any(), any(), any());
}
@Test
@@ -94,7 +94,7 @@
ImmutableList.of(new Label("//foo:bar"), new Label("//foo:baz"));
List<TargetExpression> targets = Lists.newArrayList(labels);
service.buildFile(project, "Foo.java", labels);
- verify(service).buildTargetExpressions(eq(project), eq(targets), eq(viewSet), any(), any());
+ verify(service).buildTargetExpressions(eq(project), eq(targets), eq(viewSet), any());
}
@Test
@@ -104,7 +104,7 @@
Lists.newArrayList(
TargetExpression.fromString("//view/target:one"),
TargetExpression.fromString("//view/target:two"));
- verify(service).buildTargetExpressions(eq(project), eq(targets), eq(viewSet), any(), any());
+ verify(service).buildTargetExpressions(eq(project), eq(targets), eq(viewSet), any());
}
private static class MockProjectViewManager extends ProjectViewManager {
diff --git a/base/tests/unittests/com/google/idea/blaze/base/metrics/ActionTest.java b/base/tests/unittests/com/google/idea/blaze/base/metrics/ActionTest.java
deleted file mode 100644
index 2cbb7ce..0000000
--- a/base/tests/unittests/com/google/idea/blaze/base/metrics/ActionTest.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright 2016 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.metrics;
-
-import com.google.common.collect.Sets;
-import java.util.HashSet;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import org.junit.Assert;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-/** Test for metric actions. */
-@RunWith(JUnit4.class)
-public class ActionTest {
-
- @Test
- public void ensureAllActionEnumsHaveUniqueNames() {
- HashSet<String> names = Sets.newHashSet();
- for (Action action : Action.values()) {
- String name = action.getName();
- Assert.assertTrue(name + " is not unique", names.add(name));
- }
- }
-
- @Test
- public void ensureAllActionEnumNamesAreAlphanumeric() {
- Pattern pattern = Pattern.compile("[a-zA-Z0-9]*");
- for (Action action : Action.values()) {
- String name = action.getName();
- Matcher matcher = pattern.matcher(name);
- Assert.assertTrue(name + " is not valid", matcher.matches());
- }
- }
-}
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 ed1fd3a..7758293 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
@@ -38,7 +38,6 @@
import com.google.idea.blaze.base.projectview.section.sections.ExcludedSourceSection;
import com.google.idea.blaze.base.projectview.section.sections.ImportSection;
import com.google.idea.blaze.base.projectview.section.sections.ImportTargetOutputSection;
-import com.google.idea.blaze.base.projectview.section.sections.MetricsProjectSection;
import com.google.idea.blaze.base.projectview.section.sections.RunConfigurationsSection;
import com.google.idea.blaze.base.projectview.section.sections.Sections;
import com.google.idea.blaze.base.projectview.section.sections.TargetSection;
@@ -90,7 +89,6 @@
.add(ScalarSection.builder(WorkspaceTypeSection.KEY).set(WorkspaceType.JAVA))
.add(
ListSection.builder(AdditionalLanguagesSection.KEY).add(LanguageClass.JAVA))
- .add(ScalarSection.builder(MetricsProjectSection.KEY).set("my project"))
.add(TextBlockSection.of(TextBlock.newLine()))
.add(
ListSection.builder(RunConfigurationsSection.KEY)
diff --git a/base/tests/unittests/com/google/idea/blaze/base/run/smrunner/BlazeXmlSchemaTest.java b/base/tests/unittests/com/google/idea/blaze/base/run/smrunner/BlazeXmlSchemaTest.java
new file mode 100644
index 0000000..5c7dc35
--- /dev/null
+++ b/base/tests/unittests/com/google/idea/blaze/base/run/smrunner/BlazeXmlSchemaTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2017 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.run.smrunner;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
+import com.google.idea.blaze.base.run.smrunner.BlazeXmlSchema.TestSuite;
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Unit tests for {@link BlazeXmlSchema}. */
+@RunWith(JUnit4.class)
+public class BlazeXmlSchemaTest {
+
+ @Test
+ public void testNoTestSuitesOuterElement() {
+ List<String> lines =
+ ImmutableList.of(
+ " <testsuite name=\"foo/bar\" tests=\"1\" time=\"19.268\">",
+ " <testcase name=\"TestName\" result=\"completed\" status=\"run\" time=\"19.2\">",
+ " <system-out>PASS

</system-out>",
+ " </testcase>",
+ " </testsuite>");
+ InputStream stream =
+ new ByteArrayInputStream(Joiner.on('\n').join(lines).getBytes(StandardCharsets.UTF_8));
+ TestSuite parsed = BlazeXmlSchema.parse(stream);
+ assertThat(parsed).isNotNull();
+ }
+
+ @Test
+ public void testOuterTestSuitesElement() {
+ List<String> lines =
+ ImmutableList.of(
+ "<?xml version='1.0' encoding='UTF-8'?>",
+ "<testsuites>",
+ " <testsuite name='foo' hostname='localhost' tests='331' failures='0' id='0'>",
+ " <properties />",
+ " <system-out />",
+ " <system-err />",
+ " </testsuite>",
+ " <testsuite name='bar'>",
+ " <testcase name='bar_test_1' time='12.2' />",
+ " <system-out />",
+ " </testsuite>",
+ "</testsuites>");
+ InputStream stream =
+ new ByteArrayInputStream(Joiner.on('\n').join(lines).getBytes(StandardCharsets.UTF_8));
+ TestSuite parsed = BlazeXmlSchema.parse(stream);
+ assertThat(parsed).isNotNull();
+ }
+}
diff --git a/base/tests/unittests/com/google/idea/blaze/base/run/testlogs/BlazeCommandLogParserTest.java b/base/tests/unittests/com/google/idea/blaze/base/run/testlogs/BlazeCommandLogParserTest.java
new file mode 100644
index 0000000..e3e63ad
--- /dev/null
+++ b/base/tests/unittests/com/google/idea/blaze/base/run/testlogs/BlazeCommandLogParserTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2017 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.run.testlogs;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.collect.ImmutableList;
+import com.google.idea.blaze.base.model.primitives.Label;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Unit tests for {@link BlazeCommandLogParser}. */
+@RunWith(JUnit4.class)
+public class BlazeCommandLogParserTest {
+
+ @Test
+ public void testParseTestXmlLine() {
+ assertThat(BlazeCommandLogParser.parseTestTarget("//path/to:target PASSED in 5.3s"))
+ .isEqualTo(new Label("//path/to:target"));
+
+ assertThat(BlazeCommandLogParser.parseTestTarget("//path/to:target FAILED in 5.3s"))
+ .isEqualTo(new Label("//path/to:target"));
+
+ assertThat(BlazeCommandLogParser.parseTestTarget("//path/to:target (cached) PASSED in 5.3s"))
+ .isEqualTo(new Label("//path/to:target"));
+ }
+
+ @Test
+ public void testNonTestXmlLinesIgnored() {
+ assertThat(BlazeCommandLogParser.parseTestTarget("Executed 0 out of 1 test: 1 test passes."))
+ .isNull();
+ assertThat(BlazeCommandLogParser.parseTestTarget("INFO: Found 8 test targets...")).isNull();
+ assertThat(BlazeCommandLogParser.parseTestTarget("Target //golang:unit_tests up-to-date:"))
+ .isNull();
+ assertThat(BlazeCommandLogParser.parseTestTarget(" bazel-bin/golang/unit_tests.jar")).isNull();
+ }
+
+ @Test
+ public void testMultipleInputLines() {
+ List<String> lines =
+ ImmutableList.of(
+ "INFO: Found 3 test targets...",
+ "INFO: Elapsed time: 3.239s, Critical Path: 1.65s",
+ "//base:integration_tests (cached) PASSED in 27.9s",
+ "//base:unit_tests (cached) PASSED in 4.3s",
+ "//golang:unit_tests FAILED in 0.6s",
+ "Executed 1 out of 3 test: 2 test passes.");
+ assertThat(BlazeCommandLogParser.parseTestTargets(lines.stream()))
+ .containsExactly(
+ new Label("//base:integration_tests"),
+ new Label("//base:unit_tests"),
+ new Label("//golang:unit_tests"));
+ }
+}
diff --git a/base/tests/unittests/com/google/idea/blaze/base/run/testlogs/BlazeTestLogParserTest.java b/base/tests/unittests/com/google/idea/blaze/base/run/testlogs/BlazeTestLogParserTest.java
new file mode 100644
index 0000000..d490769
--- /dev/null
+++ b/base/tests/unittests/com/google/idea/blaze/base/run/testlogs/BlazeTestLogParserTest.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2017 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.run.testlogs;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.collect.ImmutableList;
+import java.io.File;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Unit tests for {@link BlazeTestLogParser}. */
+@RunWith(JUnit4.class)
+public class BlazeTestLogParserTest {
+
+ @Test
+ public void testParseTestXmlLine() {
+ assertThat(BlazeTestLogParser.parseXmlLocation(" XML_OUTPUT_FILE=/tmp/test.xml \\"))
+ .isEqualTo(new File("/tmp/test.xml"));
+ }
+
+ @Test
+ public void testNonTestXmlLinesIgnored() {
+ assertThat(BlazeTestLogParser.parseXmlLocation(" TEST_TMPDIR=/tmp/test \\")).isNull();
+ }
+
+ @Test
+ public void testMultipleInputLines() {
+ List<String> lines =
+ ImmutableList.of(
+ "Test command:",
+ "cd /build/work/runfiles/workspace && \\",
+ " env - \\",
+ " JAVA_RUNFILES=/build/work/runfiles \\",
+ " PWD=/build/work/runfiles/workspace \\",
+ " XML_OUTPUT_FILE=/tmp/dir/test.xml \\",
+ " USER=username \\");
+ assertThat(BlazeTestLogParser.parseTestXmlFile(lines.stream()))
+ .isEqualTo(new File("/tmp/dir/test.xml"));
+ }
+}
diff --git a/base/tests/unittests/com/google/idea/blaze/base/run/testmap/TestMapTest.java b/base/tests/unittests/com/google/idea/blaze/base/run/testmap/TestMapTest.java
index 9bea7c2..8a855ae 100644
--- a/base/tests/unittests/com/google/idea/blaze/base/run/testmap/TestMapTest.java
+++ b/base/tests/unittests/com/google/idea/blaze/base/run/testmap/TestMapTest.java
@@ -25,7 +25,6 @@
import com.google.idea.blaze.base.ideinfo.TargetMap;
import com.google.idea.blaze.base.ideinfo.TargetMapBuilder;
import com.google.idea.blaze.base.model.primitives.Label;
-import com.google.idea.blaze.base.run.testmap.TestTargetFilterImpl.TestMap;
import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoder;
import com.google.idea.blaze.base.targetmaps.ReverseDependencyMap;
import com.google.idea.common.experiments.ExperimentService;
@@ -65,10 +64,11 @@
.addSource(sourceRoot("test/Test.java")))
.build();
- TestMap testMap = new TestMap(project, artifactLocationDecoder, targetMap);
+ FilteredTargetMap testMap =
+ TestTargetFilterImpl.computeTestMap(project, artifactLocationDecoder, targetMap);
ImmutableMultimap<TargetKey, TargetKey> reverseDependencies =
ReverseDependencyMap.createRdepsMap(targetMap);
- assertThat(testMap.testTargetsForSourceFile(reverseDependencies, new File("/test/Test.java")))
+ assertThat(testMap.targetsForSourceFile(reverseDependencies, new File("/test/Test.java")))
.containsExactly(new Label("//test:test"));
}
@@ -90,10 +90,11 @@
.addSource(sourceRoot("test/Test.java")))
.build();
- TestMap testMap = new TestMap(project, artifactLocationDecoder, targetMap);
+ FilteredTargetMap testMap =
+ TestTargetFilterImpl.computeTestMap(project, artifactLocationDecoder, targetMap);
ImmutableMultimap<TargetKey, TargetKey> reverseDependencies =
ReverseDependencyMap.createRdepsMap(targetMap);
- assertThat(testMap.testTargetsForSourceFile(reverseDependencies, new File("/test/Test.java")))
+ assertThat(testMap.targetsForSourceFile(reverseDependencies, new File("/test/Test.java")))
.containsExactly(new Label("//test:test"));
}
@@ -121,10 +122,11 @@
.addSource(sourceRoot("test/Test.java")))
.build();
- TestMap testMap = new TestMap(project, artifactLocationDecoder, targetMap);
+ FilteredTargetMap testMap =
+ TestTargetFilterImpl.computeTestMap(project, artifactLocationDecoder, targetMap);
ImmutableMultimap<TargetKey, TargetKey> reverseDependencies =
ReverseDependencyMap.createRdepsMap(targetMap);
- assertThat(testMap.testTargetsForSourceFile(reverseDependencies, new File("/test/Test.java")))
+ assertThat(testMap.targetsForSourceFile(reverseDependencies, new File("/test/Test.java")))
.containsExactly(new Label("//test:test"), new Label("//test:test2"));
}
@@ -158,10 +160,11 @@
.addDependency("//test:lib"))
.build();
- TestMap testMap = new TestMap(project, artifactLocationDecoder, targetMap);
+ FilteredTargetMap testMap =
+ TestTargetFilterImpl.computeTestMap(project, artifactLocationDecoder, targetMap);
ImmutableMultimap<TargetKey, TargetKey> reverseDependencies =
ReverseDependencyMap.createRdepsMap(targetMap);
- assertThat(testMap.testTargetsForSourceFile(reverseDependencies, new File("/test/Test.java")))
+ assertThat(testMap.targetsForSourceFile(reverseDependencies, new File("/test/Test.java")))
.containsExactly(new Label("//test:test"), new Label("//test:test2"))
.inOrder();
}
@@ -196,10 +199,11 @@
.addSource(sourceRoot("test/Test.java")))
.build();
- TestMap testMap = new TestMap(project, artifactLocationDecoder, targetMap);
+ FilteredTargetMap testMap =
+ TestTargetFilterImpl.computeTestMap(project, artifactLocationDecoder, targetMap);
ImmutableMultimap<TargetKey, TargetKey> reverseDependencies =
ReverseDependencyMap.createRdepsMap(targetMap);
- assertThat(testMap.testTargetsForSourceFile(reverseDependencies, new File("/test/Test.java")))
+ assertThat(testMap.targetsForSourceFile(reverseDependencies, new File("/test/Test.java")))
.containsExactly(new Label("//test:test"), new Label("//test:test2"));
}
@@ -228,17 +232,15 @@
.addSource(sourceRoot("test/Test.java")))
.build();
- TestMap testMap = new TestMap(project, artifactLocationDecoder, targetMap);
+ FilteredTargetMap testMap =
+ TestTargetFilterImpl.computeTestMap(project, artifactLocationDecoder, targetMap);
ImmutableMultimap<TargetKey, TargetKey> reverseDependencies =
ReverseDependencyMap.createRdepsMap(targetMap);
- assertThat(testMap.testTargetsForSourceFile(reverseDependencies, new File("/test/Test.java")))
+ assertThat(testMap.targetsForSourceFile(reverseDependencies, new File("/test/Test.java")))
.containsExactly(new Label("//test:test"));
}
private ArtifactLocation sourceRoot(String relativePath) {
- return ArtifactLocation.builder()
- .setRelativePath(relativePath)
- .setIsSource(true)
- .build();
+ return ArtifactLocation.builder().setRelativePath(relativePath).setIsSource(true).build();
}
}
diff --git a/base/tests/unittests/com/google/idea/blaze/base/sync/workspace/ExecutionRootPathResolverTest.java b/base/tests/unittests/com/google/idea/blaze/base/sync/workspace/ExecutionRootPathResolverTest.java
new file mode 100644
index 0000000..f880353
--- /dev/null
+++ b/base/tests/unittests/com/google/idea/blaze/base/sync/workspace/ExecutionRootPathResolverTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2016 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.sync.workspace;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.collect.ImmutableList;
+import com.google.idea.blaze.base.BlazeTestCase;
+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 java.io.File;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Units tests for {@link ExecutionRootPathResolver}. */
+@RunWith(JUnit4.class)
+public class ExecutionRootPathResolverTest extends BlazeTestCase {
+
+ private static final WorkspaceRoot WORKSPACE_ROOT = new WorkspaceRoot(new File("/path/to/root"));
+ private static final String EXECUTION_ROOT = "/path/to/_blaze_user/1234bf129e/root";
+
+ private static final BlazeRoots BLAZE_ROOTS =
+ new BlazeRoots(
+ new File(EXECUTION_ROOT),
+ ImmutableList.of(WORKSPACE_ROOT.directory()),
+ new ExecutionRootPath("blaze-out/crosstool/bin"),
+ new ExecutionRootPath("blaze-out/crosstool/genfiles"),
+ null);
+
+ private final ExecutionRootPathResolver pathResolver =
+ new ExecutionRootPathResolver(
+ BLAZE_ROOTS, new WorkspacePathResolverImpl(WORKSPACE_ROOT, BLAZE_ROOTS));
+
+ @Test
+ public void testExternalWorkspacePathRelativeToExecRoot() {
+ ImmutableList<File> files =
+ pathResolver.resolveToIncludeDirectories(new ExecutionRootPath("external/guava/src"));
+ assertThat(files).containsExactly(new File(EXECUTION_ROOT, "external/guava/src"));
+ }
+
+ @Test
+ public void testGenfilesPathRelativeToExecRoot() {
+ ImmutableList<File> files =
+ pathResolver.resolveToIncludeDirectories(
+ new ExecutionRootPath("blaze-out/crosstool/genfiles/res/normal"));
+ assertThat(files)
+ .containsExactly(new File(EXECUTION_ROOT, "blaze-out/crosstool/genfiles/res/normal"));
+ }
+
+ @Test
+ public void testNonOutputPathsRelativeToWorkspaceRoot() {
+ ImmutableList<File> files =
+ pathResolver.resolveToIncludeDirectories(new ExecutionRootPath("tools/fast"));
+ assertThat(files).containsExactly(WORKSPACE_ROOT.fileForPath(new WorkspacePath("tools/fast")));
+ }
+}
diff --git a/base/tests/unittests/com/google/idea/blaze/base/sync/workspace/WorkspacePathResolverImplTest.java b/base/tests/unittests/com/google/idea/blaze/base/sync/workspace/WorkspacePathResolverImplTest.java
index ff130fb..282ea53 100644
--- a/base/tests/unittests/com/google/idea/blaze/base/sync/workspace/WorkspacePathResolverImplTest.java
+++ b/base/tests/unittests/com/google/idea/blaze/base/sync/workspace/WorkspacePathResolverImplTest.java
@@ -20,6 +20,7 @@
import com.google.common.collect.ImmutableList;
import com.google.idea.blaze.base.BlazeTestCase;
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 java.io.File;
import org.junit.Test;
@@ -45,7 +46,7 @@
WorkspacePathResolver workspacePathResolver =
new WorkspacePathResolverImpl(WORKSPACE_ROOT, BLAZE_CITC_ROOTS);
ImmutableList<File> files =
- workspacePathResolver.resolveToIncludeDirectories(new ExecutionRootPath("tools/fast"));
+ workspacePathResolver.resolveToIncludeDirectories(new WorkspacePath("tools/fast"));
assertThat(files).containsExactly(new File("/path/to/root/tools/fast"));
}
@@ -55,7 +56,7 @@
new WorkspacePathResolverImpl(WORKSPACE_ROOT, BLAZE_CITC_ROOTS);
ImmutableList<File> files =
workspacePathResolver.resolveToIncludeDirectories(
- new ExecutionRootPath("blaze-out/crosstool/bin/tools/fast"));
+ new WorkspacePath("blaze-out/crosstool/bin/tools/fast"));
assertThat(files).containsExactly(new File("/path/to/root/blaze-out/crosstool/bin/tools/fast"));
}
}
diff --git a/base/tests/utils/integration/com/google/idea/blaze/base/BlazeIntegrationTestCase.java b/base/tests/utils/integration/com/google/idea/blaze/base/BlazeIntegrationTestCase.java
index bd21294..8027d58 100644
--- a/base/tests/utils/integration/com/google/idea/blaze/base/BlazeIntegrationTestCase.java
+++ b/base/tests/utils/integration/com/google/idea/blaze/base/BlazeIntegrationTestCase.java
@@ -17,6 +17,7 @@
import com.google.idea.blaze.base.io.FileAttributeProvider;
import com.google.idea.blaze.base.io.InputStreamProvider;
+import com.google.idea.blaze.base.io.VirtualFileSystemProvider;
import com.google.idea.blaze.base.model.primitives.WorkspacePath;
import com.google.idea.blaze.base.model.primitives.WorkspaceRoot;
import com.google.idea.blaze.base.settings.Blaze.BuildSystem;
@@ -129,6 +130,8 @@
}
return vf.getInputStream();
});
+ registerApplicationService(
+ VirtualFileSystemProvider.class, new TestFileSystem.TempVirtualFileSystemProvider());
}
@After
diff --git a/base/tests/utils/integration/com/google/idea/blaze/base/TestFileSystem.java b/base/tests/utils/integration/com/google/idea/blaze/base/TestFileSystem.java
index 834eeab..cf15e9f 100644
--- a/base/tests/utils/integration/com/google/idea/blaze/base/TestFileSystem.java
+++ b/base/tests/utils/integration/com/google/idea/blaze/base/TestFileSystem.java
@@ -19,10 +19,12 @@
import com.google.common.base.Joiner;
import com.google.idea.blaze.base.io.FileAttributeProvider;
+import com.google.idea.blaze.base.io.VirtualFileSystemProvider;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.application.Result;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.ex.temp.TempFileSystem;
import com.intellij.psi.PsiDirectory;
@@ -32,6 +34,7 @@
import com.intellij.testFramework.fixtures.TempDirTestFixture;
import java.io.File;
import java.io.IOException;
+import java.util.Arrays;
/** Creates temp files for integration tests. */
public class TestFileSystem {
@@ -165,8 +168,32 @@
return vf != null && vf.exists() && !vf.isDirectory();
}
+ @Override
+ public File[] listFiles(File file) {
+ VirtualFile vf = getVirtualFile(file);
+ if (vf == null) {
+ return null;
+ }
+ VirtualFile[] children = vf.getChildren();
+ if (children == null) {
+ return null;
+ }
+ return Arrays.stream(vf.getChildren()).map((f) -> new File(f.getPath())).toArray(File[]::new);
+ }
+
private VirtualFile getVirtualFile(File file) {
return fileSystem.findFileByPath(file.getPath());
}
}
+
+ /** Redirects VirtualFileSystem operations to the TempFileSystem used for these tests. */
+ public static class TempVirtualFileSystemProvider implements VirtualFileSystemProvider {
+
+ final TempFileSystem fileSystem = TempFileSystem.getInstance();
+
+ @Override
+ public LocalFileSystem getSystem() {
+ return fileSystem;
+ }
+ }
}
diff --git a/base/tests/utils/integration/com/google/idea/blaze/base/sync/BlazeSyncIntegrationTestCase.java b/base/tests/utils/integration/com/google/idea/blaze/base/sync/BlazeSyncIntegrationTestCase.java
index a0ac661..53c6b53 100644
--- a/base/tests/utils/integration/com/google/idea/blaze/base/sync/BlazeSyncIntegrationTestCase.java
+++ b/base/tests/utils/integration/com/google/idea/blaze/base/sync/BlazeSyncIntegrationTestCase.java
@@ -109,7 +109,7 @@
public void commit() {
// don't commit module changes,
// and make sure they're properly disposed when the test is finished
- for (ModifiableRootModel model : modifiableModels) {
+ for (ModifiableRootModel model : modules.values()) {
Disposer.register(getTestRootDisposable(), model::dispose);
if (model.getModule().getName().equals(BlazeDataStorage.WORKSPACE_MODULE_NAME)) {
workspaceContentEntries = ImmutableList.copyOf(model.getContentEntries());
@@ -245,6 +245,16 @@
return Futures.immediateFuture(workingSet);
}
+ @Override
+ public ListenableFuture<String> getUpstreamContent(
+ Project project,
+ BlazeContext context,
+ WorkspaceRoot workspaceRoot,
+ WorkspacePath path,
+ ListeningExecutorService executor) {
+ return Futures.immediateFuture("");
+ }
+
@Nullable
@Override
public BlazeVcsSyncHandler createSyncHandler(Project project, WorkspaceRoot workspaceRoot) {
diff --git a/build_defs/build_defs.bzl b/build_defs/build_defs.bzl
index 3104e0f..b695241 100644
--- a/build_defs/build_defs.bzl
+++ b/build_defs/build_defs.bzl
@@ -1,6 +1,8 @@
"""Custom build macros for IntelliJ plugin handling.
"""
+load(":intellij_plugin_debug_target.bzl", "intellij_plugin_debug_target")
+
def merged_plugin_xml(name, srcs, **kwargs):
"""Merges N plugin.xml files together."""
merge_tool = "//build_defs:merge_xml"
@@ -176,8 +178,17 @@
tools = [api_version_txt_tool],
**kwargs)
-def intellij_plugin(name, deps, plugin_xml, meta_inf_files=[], **kwargs):
- """Creates an intellij plugin from the given deps and plugin.xml."""
+def intellij_plugin(name, deps, plugin_xml, meta_inf_files=[], jar_name=None, **kwargs):
+ """Creates an intellij plugin from the given deps and plugin.xml.
+
+ Args:
+ name: The name of the target
+ deps: Any java dependencies rolled up into the plugin jar.
+ plugin_xml: An xml file to be placed in META-INF/plugin.jar
+ meta_inf_files: Any further files to be placed in META-INF/plugin.jar
+ jar_name: The name of the final plugin jar, or <name>.jar if None
+ **kwargs: Any further arguments to be passed to the final target
+ """
zip_tool = "//third_party:zip"
binary_name = name + "_binary"
deploy_jar = binary_name + "_deploy.jar"
@@ -201,12 +212,14 @@
cmd.append("meta_inf_files='$(locations {meta_inf_file})'".format(meta_inf_file=meta_inf_file))
cmd.append("for f in $$meta_inf_files; do cp $$f META-INF/; done")
cmd.append("$(location {zip_tool}) -u $@ META-INF/* >/dev/null".format(zip_tool=zip_tool))
+ cmd.append("rm -rf META-INF")
+ jar_name = jar_name or (name + ".jar")
native.genrule(
name = name + "_genrule",
srcs = srcs,
tools = [zip_tool],
- outs = [name + ".jar"],
+ outs = [jar_name],
cmd = " ; ".join(cmd),
)
diff --git a/build_defs/intellij_plugin_debug_target.bzl b/build_defs/intellij_plugin_debug_target.bzl
new file mode 100644
index 0000000..484a6fe
--- /dev/null
+++ b/build_defs/intellij_plugin_debug_target.bzl
@@ -0,0 +1,107 @@
+"""IntelliJ plugin debug target rule used for debugging IntelliJ plugins.
+
+Creates a plugin target debuggable from IntelliJ. Any files in
+the 'deps' attribute are deployed to the plugin sandbox.
+
+Any files are stripped of their prefix and installed into
+<sandbox>/plugins. If you need structure, first put the files
+into a pkgfilegroup. The files will be installed relative to the
+'plugins' directory if present in the pkgfilegroup prefix.
+
+intellij_plugin_debug_targets can be nested.
+
+pkgfilegroup(
+ name = "foo_files",
+ srcs = [
+ ":my_plugin_jar",
+ ":my_additional_plugin_files",
+ ],
+ prefix = "plugins/foo/lib",
+)
+
+intellij_plugin_debug_target(
+ name = "my_debug_target",
+ deps = [
+ ":my_jar",
+ ],
+)
+
+"""
+
+def _trim_start(path, prefix):
+ return path[len(prefix):] if path.startswith(prefix) else path
+
+def _pkgfilegroup_deploy_file(ctx, f):
+ strip_prefix = ctx.rule.attr.strip_prefix
+ prefix = ctx.rule.attr.prefix
+ if strip_prefix == ".":
+ stripped_relative_path = f.basename
+ elif strip_prefix.startswith("/"):
+ stripped_relative_path = _trim_start(f.short_path, strip_prefix[1:])
+ else:
+ stripped_relative_path = _trim_start(f.short_path, PACKAGE_NAME)
+ stripped_relative_path = _trim_start(stripped_relative_path, strip_prefix)
+ stripped_relative_path = _trim_start(stripped_relative_path, "/")
+
+ # If there's a 'plugins' directory, make destination relative to that
+ plugini = prefix.find("plugins/")
+ plugins_prefix = prefix[plugini + len("plugins/"):] if plugini >= 0 else prefix
+
+ # If the install location is still absolute, fail
+ if plugins_prefix.startswith("/"):
+ fail("Cannot compute plugins-relative install directory for pkgfilegroup")
+
+ dest = plugins_prefix + "/" + stripped_relative_path if plugins_prefix else stripped_relative_path
+ return struct(
+ src = f,
+ deploy_location = dest,
+ )
+
+def _flat_deploy_file(f):
+ return struct(
+ src = f,
+ deploy_location = f.basename,
+ )
+
+def _intellij_plugin_debug_target_aspect_impl(target, ctx):
+ aspect_intellij_plugin_deploy_info = None
+
+ if ctx.rule.kind == "intellij_plugin_debug_target":
+ aspect_intellij_plugin_deploy_info = target.intellij_plugin_deploy_info
+ elif ctx.rule.kind == "pkgfilegroup":
+ aspect_intellij_plugin_deploy_info = struct(
+ deploy_files = [_pkgfilegroup_deploy_file(ctx, f) for f in target.files],
+ )
+ else:
+ aspect_intellij_plugin_deploy_info = struct(
+ deploy_files = [_flat_deploy_file(f) for f in target.files],
+ )
+
+ return struct(
+ files = target.files,
+ aspect_intellij_plugin_deploy_info = aspect_intellij_plugin_deploy_info,
+ )
+
+_intellij_plugin_debug_target_aspect = aspect(
+ implementation = _intellij_plugin_debug_target_aspect_impl,
+)
+
+def _intellij_plugin_debug_target_impl(ctx):
+ files = set()
+ deploy_files = []
+ for target in ctx.attr.deps:
+ files = files | target.files
+ deploy_files.extend(target.aspect_intellij_plugin_deploy_info.deploy_files)
+ return struct(
+ files = files,
+ intellij_plugin_deploy_info = struct(
+ deploy_files = deploy_files,
+ )
+ )
+
+intellij_plugin_debug_target = rule(
+ implementation = _intellij_plugin_debug_target_impl,
+ attrs = {
+ "deps": attr.label_list(aspects = [_intellij_plugin_debug_target_aspect]),
+ },
+)
diff --git a/clwb/src/com/google/idea/blaze/clwb/run/BlazeCidrLauncher.java b/clwb/src/com/google/idea/blaze/clwb/run/BlazeCidrLauncher.java
index 9195ab0..410cf57 100644
--- a/clwb/src/com/google/idea/blaze/clwb/run/BlazeCidrLauncher.java
+++ b/clwb/src/com/google/idea/blaze/clwb/run/BlazeCidrLauncher.java
@@ -21,8 +21,6 @@
import com.google.idea.blaze.base.command.BlazeCommandName;
import com.google.idea.blaze.base.command.BlazeFlags;
import com.google.idea.blaze.base.issueparser.IssueOutputLineProcessor;
-import com.google.idea.blaze.base.metrics.Action;
-import com.google.idea.blaze.base.metrics.LoggingService;
import com.google.idea.blaze.base.model.primitives.TargetExpression;
import com.google.idea.blaze.base.model.primitives.WorkspaceRoot;
import com.google.idea.blaze.base.projectview.ProjectViewManager;
@@ -33,7 +31,6 @@
import com.google.idea.blaze.base.run.state.BlazeCommandRunConfigurationCommonState;
import com.google.idea.blaze.base.scope.BlazeContext;
import com.google.idea.blaze.base.scope.scopes.IssuesScope;
-import com.google.idea.blaze.base.scope.scopes.LoggedTimingScope;
import com.google.idea.blaze.base.settings.Blaze;
import com.google.idea.blaze.base.settings.BlazeImportSettings;
import com.google.idea.blaze.base.settings.BlazeImportSettingsManager;
@@ -111,8 +108,6 @@
@Override
public void onBlazeContextStart(BlazeContext context) {
context
- .push(new LoggedTimingScope(project, Action.BLAZE_COMMAND_USAGE))
- .push(new LoggedTimingScope(project, Action.BLAZE_CLION_TEST_RUN))
.push(new IssuesScope(project));
}
@@ -153,7 +148,6 @@
CidrDebugProcess result =
new CidrLocalDebugProcess(parameters, session, state.getConsoleBuilder());
- LoggingService.reportEvent(project, Action.BLAZE_CLION_TEST_DEBUG);
return result;
}
diff --git a/clwb/src/com/google/idea/blaze/clwb/run/BlazeCidrRunConfigurationRunner.java b/clwb/src/com/google/idea/blaze/clwb/run/BlazeCidrRunConfigurationRunner.java
index 8c36f71..208cf11 100644
--- a/clwb/src/com/google/idea/blaze/clwb/run/BlazeCidrRunConfigurationRunner.java
+++ b/clwb/src/com/google/idea/blaze/clwb/run/BlazeCidrRunConfigurationRunner.java
@@ -26,7 +26,6 @@
import com.google.idea.blaze.base.command.BlazeFlags;
import com.google.idea.blaze.base.command.ExperimentalShowArtifactsLineProcessor;
import com.google.idea.blaze.base.issueparser.IssueOutputLineProcessor;
-import com.google.idea.blaze.base.metrics.Action;
import com.google.idea.blaze.base.model.primitives.WorkspaceRoot;
import com.google.idea.blaze.base.projectview.ProjectViewManager;
import com.google.idea.blaze.base.projectview.ProjectViewSet;
@@ -38,7 +37,6 @@
import com.google.idea.blaze.base.scope.output.StatusOutput;
import com.google.idea.blaze.base.scope.scopes.BlazeConsoleScope;
import com.google.idea.blaze.base.scope.scopes.IssuesScope;
-import com.google.idea.blaze.base.scope.scopes.LoggedTimingScope;
import com.google.idea.blaze.base.settings.Blaze;
import com.google.idea.blaze.base.util.SaveUtil;
import com.google.idea.common.experiments.BoolExperiment;
@@ -119,7 +117,6 @@
@Override
protected void execute(BlazeContext context) {
context
- .push(new LoggedTimingScope(project, Action.BLAZE_COMMAND_USAGE))
.push(new IssuesScope(project))
.push(new BlazeConsoleScope.Builder(project).build());
diff --git a/clwb/src/com/google/idea/blaze/clwb/run/producers/BlazeCidrTestConfigurationProducer.java b/clwb/src/com/google/idea/blaze/clwb/run/producers/BlazeCidrTestConfigurationProducer.java
index 2954d92..3589c9a 100644
--- a/clwb/src/com/google/idea/blaze/clwb/run/producers/BlazeCidrTestConfigurationProducer.java
+++ b/clwb/src/com/google/idea/blaze/clwb/run/producers/BlazeCidrTestConfigurationProducer.java
@@ -27,6 +27,7 @@
import com.google.idea.blaze.base.settings.Blaze;
import com.intellij.execution.Location;
import com.intellij.execution.actions.ConfigurationContext;
+import com.intellij.openapi.actionSystem.LangDataKeys;
import com.intellij.openapi.util.Couple;
import com.intellij.openapi.util.Ref;
import com.intellij.psi.PsiElement;
@@ -100,13 +101,28 @@
super(BlazeCommandRunConfigurationType.getInstance());
}
+ /** The single selected {@link PsiElement}. Returns null if multiple elements are selected. */
+ @Nullable
+ private static PsiElement selectedPsiElement(ConfigurationContext context) {
+ PsiElement[] psi = LangDataKeys.PSI_ELEMENT_ARRAY.getData(context.getDataContext());
+ if (psi != null && psi.length > 1) {
+ return null; // multiple elements selected.
+ }
+ Location<?> location = context.getLocation();
+ return location != null ? location.getPsiElement() : null;
+ }
+
@Override
protected boolean doSetupConfigFromContext(
BlazeCommandRunConfiguration configuration,
ConfigurationContext context,
Ref<PsiElement> sourceElement) {
- TestTarget testObject = findTestObject(context.getLocation());
+ PsiElement element = selectedPsiElement(context);
+ if (element == null) {
+ return false;
+ }
+ TestTarget testObject = findTestObject(element);
if (testObject == null) {
return false;
}
@@ -144,7 +160,11 @@
if (!Objects.equals(handlerState.getCommand(), BlazeCommandName.TEST)) {
return false;
}
- TestTarget testObject = findTestObject(context.getLocation());
+ PsiElement element = selectedPsiElement(context);
+ if (element == null) {
+ return false;
+ }
+ TestTarget testObject = findTestObject(element);
if (testObject == null) {
return false;
}
@@ -154,11 +174,9 @@
}
@Nullable
- private static TestTarget findTestObject(Location<?> location) {
+ private static TestTarget findTestObject(PsiElement element) {
// Copied from on CidrGoogleTestRunConfigurationProducer::findTestObject.
-
// Precedence order (decreasing): class/function, macro, file
- PsiElement element = location.getPsiElement();
PsiElement parent =
PsiTreeUtil.getNonStrictParentOfType(element, OCFunctionDefinition.class, OCStruct.class);
diff --git a/common/actionhelper/src/com/google/idea/common/actionhelper/ActionPresentationHelper.java b/common/actionhelper/src/com/google/idea/common/actionhelper/ActionPresentationHelper.java
index 96a7882..69c5e1e 100644
--- a/common/actionhelper/src/com/google/idea/common/actionhelper/ActionPresentationHelper.java
+++ b/common/actionhelper/src/com/google/idea/common/actionhelper/ActionPresentationHelper.java
@@ -169,7 +169,7 @@
String text = enabled && hasSubject ? subjectText : this.text;
if (text != null) {
- presentation.setText(text);
+ presentation.setText(text, false);
}
}
}
diff --git a/common/binaryhelper/src/com/google/idea/common/binaryhelper/HelperBinaryUtil.java b/common/binaryhelper/src/com/google/idea/common/binaryhelper/HelperBinaryUtil.java
index fb1be91..c5c92e7 100644
--- a/common/binaryhelper/src/com/google/idea/common/binaryhelper/HelperBinaryUtil.java
+++ b/common/binaryhelper/src/com/google/idea/common/binaryhelper/HelperBinaryUtil.java
@@ -30,7 +30,7 @@
/** Binaries provided to IntelliJ at runtime */
public final class HelperBinaryUtil {
- private static final Logger LOG = Logger.getInstance(HelperBinaryUtil.class);
+ private static final Logger logger = Logger.getInstance(HelperBinaryUtil.class);
private static File tempDirectory;
private static final Map<String, File> cachedFiles = new HashMap<>();
@@ -53,7 +53,7 @@
URL url = HelperBinaryUtil.class.getResource(binaryFilePath);
if (url == null) {
- LOG.error(String.format("Helper binary '%s' was not found", binaryFilePath));
+ logger.error(String.format("Helper binary '%s' was not found", binaryFilePath));
return null;
}
try (InputStream inputStream = URLUtil.openResourceStream(url)) {
@@ -63,7 +63,7 @@
cachedFiles.put(binaryName, file);
return file;
} catch (IOException e) {
- LOG.error(String.format("Error loading helper binary '%s'", binaryFilePath));
+ logger.error(String.format("Error loading helper binary '%s'", binaryFilePath));
return null;
}
}
diff --git a/cpp/BUILD b/cpp/BUILD
index d5361bf..c157efc 100644
--- a/cpp/BUILD
+++ b/cpp/BUILD
@@ -18,6 +18,7 @@
"//base",
"//common/experiments",
"//intellij_platform_sdk:plugin_api",
+ "//sdkcompat",
"@jsr305_annotations//jar",
],
)
diff --git a/cpp/src/META-INF/blaze-cpp.xml b/cpp/src/META-INF/blaze-cpp.xml
index 3c71cad..aa0fb37 100644
--- a/cpp/src/META-INF/blaze-cpp.xml
+++ b/cpp/src/META-INF/blaze-cpp.xml
@@ -20,6 +20,7 @@
<extensions defaultExtensionNs="com.google.idea.blaze">
<SyncPlugin implementation="com.google.idea.blaze.cpp.BlazeCSyncPlugin"/>
<PrefetchFileSource implementation="com.google.idea.blaze.cpp.CPrefetchFileSource"/>
+ <SyncListener implementation="com.google.idea.blaze.cpp.BlazeCppSymbolRebuildSyncListener"/>
</extensions>
<extensions defaultExtensionNs="cidr.lang">
diff --git a/cpp/src/com/google/idea/blaze/cpp/BlazeCSyncPlugin.java b/cpp/src/com/google/idea/blaze/cpp/BlazeCSyncPlugin.java
index 1c64fbc..4168385 100644
--- a/cpp/src/com/google/idea/blaze/cpp/BlazeCSyncPlugin.java
+++ b/cpp/src/com/google/idea/blaze/cpp/BlazeCSyncPlugin.java
@@ -16,6 +16,7 @@
package com.google.idea.blaze.cpp;
import com.google.common.collect.ImmutableSet;
+import com.google.idea.blaze.base.io.VirtualFileSystemProvider;
import com.google.idea.blaze.base.model.BlazeProjectData;
import com.google.idea.blaze.base.model.primitives.LanguageClass;
import com.google.idea.blaze.base.model.primitives.WorkspaceRoot;
@@ -25,13 +26,20 @@
import com.google.idea.blaze.base.scope.Scope;
import com.google.idea.blaze.base.scope.scopes.TimingScope;
import com.google.idea.blaze.base.sync.BlazeSyncPlugin;
+import com.google.idea.common.experiments.BoolExperiment;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VirtualFile;
import com.jetbrains.cidr.lang.workspace.OCWorkspace;
import com.jetbrains.cidr.lang.workspace.OCWorkspaceManager;
import java.util.Set;
final class BlazeCSyncPlugin extends BlazeSyncPlugin.Adapter {
+
+ private static final BoolExperiment refreshExecRoot =
+ new BoolExperiment("refresh.exec.root.cpp", true);
+
@Override
public Set<LanguageClass> getSupportedLanguagesInWorkspace(WorkspaceType workspaceType) {
if (workspaceType == WorkspaceType.C) {
@@ -64,4 +72,26 @@
}
});
}
+
+ @Override
+ public void refreshVirtualFileSystem(BlazeProjectData blazeProjectData) {
+ if (!refreshExecRoot.getValue()) {
+ return;
+ }
+ refreshExecRoot(blazeProjectData);
+ }
+
+ private static void refreshExecRoot(BlazeProjectData blazeProjectData) {
+ // recursive refresh of the blaze execution root. This is required because:
+ // <li>Our blaze aspect can't tell us exactly which genfiles are required to resolve the project
+ // <li>Cidr caches the directory contents as part of symbol building, so we need to do this work
+ // up front.
+ VirtualFile execRoot =
+ VirtualFileSystemProvider.getInstance()
+ .getSystem()
+ .refreshAndFindFileByIoFile(blazeProjectData.blazeRoots.executionRoot);
+ if (execRoot != null) {
+ VfsUtil.markDirtyAndRefresh(false, true, true, execRoot);
+ }
+ }
}
diff --git a/cpp/src/com/google/idea/blaze/cpp/BlazeCWorkspace.java b/cpp/src/com/google/idea/blaze/cpp/BlazeCWorkspace.java
index 1b4937b..abcb294 100644
--- a/cpp/src/com/google/idea/blaze/cpp/BlazeCWorkspace.java
+++ b/cpp/src/com/google/idea/blaze/cpp/BlazeCWorkspace.java
@@ -20,15 +20,10 @@
import com.google.idea.blaze.base.model.BlazeProjectData;
import com.google.idea.blaze.base.scope.BlazeContext;
import com.google.idea.blaze.base.settings.Blaze;
-import com.google.idea.common.experiments.BoolExperiment;
-import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
-import com.intellij.openapi.vfs.LocalFileSystem;
-import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
-import com.intellij.openapi.vfs.ex.temp.TempFileSystem;
import com.jetbrains.cidr.lang.symbols.OCSymbol;
import com.jetbrains.cidr.lang.workspace.OCResolveConfiguration;
import com.jetbrains.cidr.lang.workspace.OCWorkspace;
@@ -39,10 +34,7 @@
/** Main entry point for C/CPP configuration data. */
public final class BlazeCWorkspace implements OCWorkspace {
- private static final Logger LOG = Logger.getInstance(BlazeCWorkspace.class);
-
- private static final BoolExperiment refreshExecRoot =
- new BoolExperiment("refresh.exec.root.cpp", true);
+ private static final Logger logger = Logger.getInstance(BlazeCWorkspace.class);
@Nullable private final Project project;
@Nullable private final OCWorkspaceModificationTrackers modTrackers;
@@ -65,47 +57,17 @@
}
public void update(BlazeContext context, BlazeProjectData blazeProjectData) {
- LOG.assertTrue(project != null);
- LOG.assertTrue(modTrackers != null);
- LOG.assertTrue(configurationResolver != null);
+ logger.assertTrue(project != null);
+ logger.assertTrue(modTrackers != null);
+ logger.assertTrue(configurationResolver != null);
long start = System.currentTimeMillis();
- if (refreshExecRoot.getValue()) {
- refreshExecRoot(blazeProjectData);
- }
-
// Non-incremental update to our c configurations.
configurationResolver.update(context, blazeProjectData);
long end = System.currentTimeMillis();
- LOG.info(String.format("Blaze OCWorkspace update took: %d ms", (end - start)));
-
- ApplicationManager.getApplication()
- .runWriteAction(
- () -> {
- if (project.isDisposed()) {
- return;
- }
- // TODO(salguarnieri) Avoid bumping all of these trackers; figure out what has changed
- modTrackers.getProjectFilesListTracker().incModificationCount();
- modTrackers.getSourceFilesListTracker().incModificationCount();
- modTrackers.getBuildConfigurationChangesTracker().incModificationCount();
- modTrackers.getBuildSettingsChangesTracker().incModificationCount();
- });
- }
-
- private static void refreshExecRoot(BlazeProjectData blazeProjectData) {
- // recursive refresh of the blaze execution root. This is required because:
- // <li>Our blaze aspect can't tell us exactly which genfiles are required to resolve the project
- // <li>Cidr caches the directory contents as part of symbol building, so we need to do this work
- // up front.
- VirtualFile execRoot =
- getFileSystem().findFileByIoFile(blazeProjectData.blazeRoots.executionRoot);
- if (execRoot != null) {
- ApplicationManager.getApplication()
- .runWriteAction(() -> VfsUtil.markDirtyAndRefresh(false, true, true, execRoot));
- }
+ logger.info(String.format("Blaze OCWorkspace update took: %d ms", (end - start)));
}
@Override
@@ -143,7 +105,7 @@
@Override
public OCWorkspaceModificationTrackers getModificationTrackers() {
- LOG.assertTrue(modTrackers != null);
+ logger.assertTrue(modTrackers != null);
return modTrackers;
}
@@ -163,11 +125,4 @@
OCResolveConfiguration config = configurationResolver.getConfigurationForFile(sourceFile);
return config == null ? ImmutableList.of() : ImmutableList.of(config);
}
-
- private static LocalFileSystem getFileSystem() {
- if (ApplicationManager.getApplication().isUnitTestMode()) {
- return TempFileSystem.getInstance();
- }
- return LocalFileSystem.getInstance();
- }
}
diff --git a/cpp/src/com/google/idea/blaze/cpp/BlazeConfigurationResolver.java b/cpp/src/com/google/idea/blaze/cpp/BlazeConfigurationResolver.java
index 7ba79d8..5e6e8b9 100644
--- a/cpp/src/com/google/idea/blaze/cpp/BlazeConfigurationResolver.java
+++ b/cpp/src/com/google/idea/blaze/cpp/BlazeConfigurationResolver.java
@@ -39,6 +39,7 @@
import com.google.idea.blaze.base.scope.ScopedFunction;
import com.google.idea.blaze.base.scope.output.IssueOutput;
import com.google.idea.blaze.base.scope.scopes.TimingScope;
+import com.google.idea.blaze.base.sync.workspace.ExecutionRootPathResolver;
import com.google.idea.blaze.base.sync.workspace.WorkspacePathResolver;
import com.google.idea.blaze.base.targetmaps.SourceToTargetMap;
import com.intellij.openapi.diagnostic.Logger;
@@ -108,6 +109,8 @@
private static ImmutableMap<File, VirtualFile> doCollectHeaderRoots(
BlazeContext context, BlazeProjectData projectData, Set<ExecutionRootPath> rootPaths) {
+ ExecutionRootPathResolver pathResolver =
+ new ExecutionRootPathResolver(projectData.blazeRoots, projectData.workspacePathResolver);
ConcurrentMap<File, VirtualFile> rootsMap = Maps.newConcurrentMap();
List<ListenableFuture<Void>> futures = Lists.newArrayListWithCapacity(rootPaths.size());
for (ExecutionRootPath path : rootPaths) {
@@ -115,7 +118,7 @@
submit(
() -> {
ImmutableList<File> possibleDirectories =
- projectData.workspacePathResolver.resolveToIncludeDirectories(path);
+ pathResolver.resolveToIncludeDirectories(path);
for (File file : possibleDirectories) {
VirtualFile vf = getVirtualFile(file);
if (vf != null) {
@@ -249,6 +252,8 @@
BlazeResolveConfiguration config =
BlazeResolveConfiguration.createConfigurationForTarget(
project,
+ new ExecutionRootPathResolver(
+ blazeProjectData.blazeRoots, blazeProjectData.workspacePathResolver),
blazeProjectData.workspacePathResolver,
headerRoots,
blazeProjectData.targetMap.get(targetKey),
diff --git a/cpp/src/com/google/idea/blaze/cpp/BlazeCppSymbolRebuildSyncListener.java b/cpp/src/com/google/idea/blaze/cpp/BlazeCppSymbolRebuildSyncListener.java
new file mode 100644
index 0000000..07b1743
--- /dev/null
+++ b/cpp/src/com/google/idea/blaze/cpp/BlazeCppSymbolRebuildSyncListener.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2016 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.cpp;
+
+import com.google.idea.blaze.base.model.BlazeProjectData;
+import com.google.idea.blaze.base.projectview.ProjectViewSet;
+import com.google.idea.blaze.base.scope.BlazeContext;
+import com.google.idea.blaze.base.settings.BlazeImportSettings;
+import com.google.idea.blaze.base.sync.BlazeSyncParams.SyncMode;
+import com.google.idea.blaze.base.sync.SyncListener;
+import com.google.idea.sdkcompat.transactions.Transactions;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.project.Project;
+import com.jetbrains.cidr.lang.workspace.OCWorkspace;
+import com.jetbrains.cidr.lang.workspace.OCWorkspaceManager;
+import com.jetbrains.cidr.lang.workspace.OCWorkspaceModificationTrackers;
+
+/** Runs after sync, triggering a rebuild of the symbol tables. */
+public class BlazeCppSymbolRebuildSyncListener extends SyncListener.Adapter {
+
+ @Override
+ public void onSyncComplete(
+ Project project,
+ BlazeContext context,
+ BlazeImportSettings importSettings,
+ ProjectViewSet projectViewSet,
+ BlazeProjectData blazeProjectData,
+ SyncMode syncMode,
+ SyncResult syncResult) {
+
+ OCWorkspace workspace = OCWorkspaceManager.getWorkspace(project);
+ if (!(workspace instanceof BlazeCWorkspace)) {
+ return;
+ }
+ rebuildSymbolTables((BlazeCWorkspace) workspace);
+ }
+
+ private static void rebuildSymbolTables(BlazeCWorkspace workspace) {
+ OCWorkspaceModificationTrackers modTrackers = workspace.getModificationTrackers();
+ Transactions.submitTransactionAndWait(
+ () ->
+ ApplicationManager.getApplication()
+ .runWriteAction(
+ () -> {
+ modTrackers.getProjectFilesListTracker().incModificationCount();
+ modTrackers.getSourceFilesListTracker().incModificationCount();
+ modTrackers.getBuildConfigurationChangesTracker().incModificationCount();
+ modTrackers.getBuildSettingsChangesTracker().incModificationCount();
+ }));
+ }
+}
diff --git a/cpp/src/com/google/idea/blaze/cpp/BlazeCustomHeaderProvider.java b/cpp/src/com/google/idea/blaze/cpp/BlazeCustomHeaderProvider.java
index 9130e7e..9492320 100644
--- a/cpp/src/com/google/idea/blaze/cpp/BlazeCustomHeaderProvider.java
+++ b/cpp/src/com/google/idea/blaze/cpp/BlazeCustomHeaderProvider.java
@@ -15,11 +15,9 @@
*/
package com.google.idea.blaze.cpp;
-import com.intellij.openapi.application.ApplicationManager;
+import com.google.idea.blaze.base.io.VirtualFileSystemProvider;
import com.intellij.openapi.project.Project;
-import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
-import com.intellij.openapi.vfs.ex.temp.TempFileSystem;
import com.jetbrains.cidr.lang.CustomHeaderProvider;
import com.jetbrains.cidr.lang.workspace.OCResolveConfiguration;
import com.jetbrains.cidr.lang.workspace.OCResolveRootAndConfiguration;
@@ -58,7 +56,7 @@
((BlazeResolveConfiguration) configuration)
.getWorkspacePathResolver()
.resolveToFile(includeString);
- return getFileSystem().findFileByIoFile(file);
+ return VirtualFileSystemProvider.getInstance().getSystem().findFileByIoFile(file);
}
@Nullable
@@ -73,11 +71,4 @@
String serializationPath, Project project, VirtualFile currentFile) {
return null;
}
-
- private static LocalFileSystem getFileSystem() {
- if (ApplicationManager.getApplication().isUnitTestMode()) {
- return TempFileSystem.getInstance();
- }
- return LocalFileSystem.getInstance();
- }
}
diff --git a/cpp/src/com/google/idea/blaze/cpp/BlazeResolveConfigurationTemporaryBase.java b/cpp/src/com/google/idea/blaze/cpp/BlazeResolveConfigurationTemporaryBase.java
index f32f97d..2b04ff2 100644
--- a/cpp/src/com/google/idea/blaze/cpp/BlazeResolveConfigurationTemporaryBase.java
+++ b/cpp/src/com/google/idea/blaze/cpp/BlazeResolveConfigurationTemporaryBase.java
@@ -31,6 +31,7 @@
import com.google.idea.blaze.base.scope.BlazeContext;
import com.google.idea.blaze.base.scope.Scope;
import com.google.idea.blaze.base.scope.scopes.TimingScope;
+import com.google.idea.blaze.base.sync.workspace.ExecutionRootPathResolver;
import com.google.idea.blaze.base.sync.workspace.WorkspacePathResolver;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
@@ -67,6 +68,7 @@
public static final Logger LOG = Logger.getInstance(BlazeResolveConfiguration.class);
+ private final ExecutionRootPathResolver executionRootPathResolver;
private final WorkspacePathResolver workspacePathResolver;
/* project, label are protected instead of private just so v145 can access */
@@ -84,6 +86,7 @@
@Nullable
public static BlazeResolveConfiguration createConfigurationForTarget(
Project project,
+ ExecutionRootPathResolver executionRootPathResolver,
WorkspacePathResolver workspacePathResolver,
ImmutableMap<File, VirtualFile> headerRoots,
TargetIdeInfo target,
@@ -119,6 +122,7 @@
return new BlazeResolveConfiguration(
project,
+ executionRootPathResolver,
workspacePathResolver,
headerRoots,
target.key,
@@ -175,6 +179,7 @@
public BlazeResolveConfigurationTemporaryBase(
Project project,
+ ExecutionRootPathResolver executionRootPathResolver,
WorkspacePathResolver workspacePathResolver,
ImmutableMap<File, VirtualFile> headerRoots,
TargetKey targetKey,
@@ -189,6 +194,7 @@
File cppCompilerExecutable,
ImmutableList<String> cCompilerFlags,
ImmutableList<String> cppCompilerFlags) {
+ this.executionRootPathResolver = executionRootPathResolver;
this.workspacePathResolver = workspacePathResolver;
this.project = project;
this.targetKey = targetKey;
@@ -320,7 +326,7 @@
boolean isUserHeader) {
for (ExecutionRootPath executionRootPath : paths) {
ImmutableList<File> possibleDirectories =
- workspacePathResolver.resolveToIncludeDirectories(executionRootPath);
+ executionRootPathResolver.resolveToIncludeDirectories(executionRootPath);
for (File f : possibleDirectories) {
VirtualFile vf = virtualFileCache.get(f);
if (vf != null) {
diff --git a/cpp/src/com/google/idea/blaze/cpp/versioned/v145/BlazeResolveConfiguration.java b/cpp/src/com/google/idea/blaze/cpp/versioned/v145/BlazeResolveConfiguration.java
index 0d99ab5..b2d63a0 100644
--- a/cpp/src/com/google/idea/blaze/cpp/versioned/v145/BlazeResolveConfiguration.java
+++ b/cpp/src/com/google/idea/blaze/cpp/versioned/v145/BlazeResolveConfiguration.java
@@ -20,6 +20,7 @@
import com.google.common.collect.ImmutableMap;
import com.google.idea.blaze.base.ideinfo.TargetKey;
import com.google.idea.blaze.base.model.primitives.ExecutionRootPath;
+import com.google.idea.blaze.base.sync.workspace.ExecutionRootPathResolver;
import com.google.idea.blaze.base.sync.workspace.WorkspacePathResolver;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
@@ -36,6 +37,7 @@
public BlazeResolveConfiguration(
Project project,
+ ExecutionRootPathResolver executionRootPathResolver,
WorkspacePathResolver workspacePathResolver,
ImmutableMap<File, VirtualFile> headerRoots,
TargetKey targetKey,
@@ -52,6 +54,7 @@
ImmutableList<String> cppCompilerFlags) {
super(
project,
+ executionRootPathResolver,
workspacePathResolver,
headerRoots,
targetKey,
diff --git a/cpp/src/com/google/idea/blaze/cpp/versioned/v162/BlazeResolveConfiguration.java b/cpp/src/com/google/idea/blaze/cpp/versioned/v162/BlazeResolveConfiguration.java
index 71ba414..959936b 100644
--- a/cpp/src/com/google/idea/blaze/cpp/versioned/v162/BlazeResolveConfiguration.java
+++ b/cpp/src/com/google/idea/blaze/cpp/versioned/v162/BlazeResolveConfiguration.java
@@ -20,6 +20,7 @@
import com.google.common.collect.ImmutableMap;
import com.google.idea.blaze.base.ideinfo.TargetKey;
import com.google.idea.blaze.base.model.primitives.ExecutionRootPath;
+import com.google.idea.blaze.base.sync.workspace.ExecutionRootPathResolver;
import com.google.idea.blaze.base.sync.workspace.WorkspacePathResolver;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
@@ -29,6 +30,7 @@
public BlazeResolveConfiguration(
Project project,
+ ExecutionRootPathResolver executionRootPathResolver,
WorkspacePathResolver workspacePathResolver,
ImmutableMap<File, VirtualFile> headerRoots,
TargetKey targetKey,
@@ -45,6 +47,7 @@
ImmutableList<String> cppCompilerFlags) {
super(
project,
+ executionRootPathResolver,
workspacePathResolver,
headerRoots,
targetKey,
diff --git a/intellij_platform_sdk/build_defs.bzl b/intellij_platform_sdk/build_defs.bzl
index e7c5c2d..6fa3efb 100644
--- a/intellij_platform_sdk/build_defs.bzl
+++ b/intellij_platform_sdk/build_defs.bzl
@@ -4,7 +4,7 @@
INDIRECT_IJ_PRODUCTS = {
"intellij-latest": "intellij-2016.3.1",
"android-studio-latest": "android-studio-145.1617.8",
- "android-studio-beta": "android-studio-2.3.0.3",
+ "android-studio-beta": "android-studio-2.3.0.4",
"clion-latest": "clion-162.1967.7",
}
diff --git a/java/src/META-INF/blaze-java.xml b/java/src/META-INF/blaze-java.xml
index 5466c27..e46fa6c 100644
--- a/java/src/META-INF/blaze-java.xml
+++ b/java/src/META-INF/blaze-java.xml
@@ -36,7 +36,7 @@
</action>
<group id="Blaze.Java.ProjectViewPopupMenu">
- <add-to-group group-id="Blaze.ProjectViewPopupMenu"/>
+ <add-to-group group-id="Blaze.PerFileContextMenu"/>
<reference id="Blaze.ExcludeLibraryAction"/>
<reference id="Blaze.AttachSourceJarAction"/>
<reference id="Blaze.AddLibraryTargetDirectoryToProjectView"/>
@@ -70,6 +70,8 @@
<FileCache implementation="com.google.idea.blaze.java.libraries.JarCache$FileCacheAdapter"/>
<PrefetchFileSource implementation="com.google.idea.blaze.java.sync.JavaPrefetchFileSource"/>
<SyncListener implementation="com.google.idea.blaze.java.syncstatus.SyncStatusHelper$UpdateSyncStatusMap"/>
+ <BlazeTestEventsHandler implementation="com.google.idea.blaze.java.run.BlazeJavaTestEventsHandler"/>
+ <AttributeSpecificStringLiteralReferenceProvider implementation="com.google.idea.blaze.java.lang.build.references.JavaClassQualifiedNameReference"/>
</extensions>
<extensions defaultExtensionNs="com.intellij">
@@ -104,6 +106,7 @@
<attachSourcesProvider implementation="com.google.idea.blaze.java.libraries.BlazeAttachSourceProvider"/>
<applicationService serviceImplementation="com.google.idea.blaze.java.settings.BlazeJavaUserSettings"/>
<projectService serviceImplementation="com.google.idea.blaze.java.syncstatus.SyncStatusHelper"/>
+ <psi.referenceContributor language="BUILD" implementation="com.google.idea.blaze.java.lang.build.references.JavaClassReferenceContributor"/>
</extensions>
<project-components>
diff --git a/java/src/com/google/idea/blaze/java/lang/build/references/JavaClassQualifiedNameReference.java b/java/src/com/google/idea/blaze/java/lang/build/references/JavaClassQualifiedNameReference.java
new file mode 100644
index 0000000..408247c
--- /dev/null
+++ b/java/src/com/google/idea/blaze/java/lang/build/references/JavaClassQualifiedNameReference.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2017 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.java.lang.build.references;
+
+import static com.intellij.patterns.PlatformPatterns.psiElement;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.idea.blaze.base.lang.buildfile.language.BuildFileLanguage;
+import com.google.idea.blaze.base.lang.buildfile.psi.Argument.Keyword;
+import com.google.idea.blaze.base.lang.buildfile.psi.StringLiteral;
+import com.google.idea.blaze.base.lang.buildfile.references.AttributeSpecificStringLiteralReferenceProvider;
+import com.intellij.patterns.ElementPattern;
+import com.intellij.patterns.PatternCondition;
+import com.intellij.patterns.PatternConditionPlus;
+import com.intellij.patterns.StandardPatterns;
+import com.intellij.psi.PsiReference;
+import com.intellij.psi.impl.PsiElementBase;
+import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistry;
+import com.intellij.util.PairProcessor;
+import com.intellij.util.ProcessingContext;
+
+/** Handles attribute values which should be fully-qualified java class names. */
+public class JavaClassQualifiedNameReference
+ implements AttributeSpecificStringLiteralReferenceProvider {
+
+ private static final ImmutableSet<String> JAVA_CLASS_STRING_TYPES =
+ ImmutableSet.of("main_class", "test_class");
+
+ public static final ElementPattern<StringLiteral> PATTERN =
+ psiElement(StringLiteral.class)
+ .withLanguage(BuildFileLanguage.INSTANCE)
+ .inside(
+ psiElement(Keyword.class)
+ .with(nameCondition(StandardPatterns.string().oneOf(JAVA_CLASS_STRING_TYPES))));
+
+ private static PatternCondition<PsiElementBase> nameCondition(final ElementPattern pattern) {
+ return new PatternConditionPlus<PsiElementBase, String>("_withPsiName", pattern) {
+ @Override
+ public boolean processValues(
+ PsiElementBase t,
+ ProcessingContext context,
+ PairProcessor<String, ProcessingContext> processor) {
+ return processor.process(t.getName(), context);
+ }
+ };
+ }
+
+ @Override
+ public PsiReference[] getReferences(String attributeName, StringLiteral literal) {
+ if (!JAVA_CLASS_STRING_TYPES.contains(attributeName)) {
+ return PsiReference.EMPTY_ARRAY;
+ }
+ return ReferenceProvidersRegistry.getReferencesFromProviders(literal);
+ }
+}
diff --git a/java/src/com/google/idea/blaze/java/lang/build/references/JavaClassReferenceContributor.java b/java/src/com/google/idea/blaze/java/lang/build/references/JavaClassReferenceContributor.java
new file mode 100644
index 0000000..7fefd48
--- /dev/null
+++ b/java/src/com/google/idea/blaze/java/lang/build/references/JavaClassReferenceContributor.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2017 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.java.lang.build.references;
+
+import com.intellij.psi.PsiReferenceContributor;
+import com.intellij.psi.PsiReferenceRegistrar;
+import com.intellij.psi.impl.source.resolve.reference.impl.providers.JavaClassReferenceProvider;
+import com.intellij.psi.util.ClassKind;
+
+/** Provides BUILD references to java classes from fully-qualified class names. */
+public class JavaClassReferenceContributor extends PsiReferenceContributor {
+
+ @Override
+ public void registerReferenceProviders(PsiReferenceRegistrar registrar) {
+ JavaClassReferenceProvider provider = new JavaClassReferenceProvider();
+ provider.setOption(JavaClassReferenceProvider.CLASS_KIND, ClassKind.CLASS);
+ registrar.registerReferenceProvider(
+ JavaClassQualifiedNameReference.PATTERN, new JavaClassReferenceProvider());
+ }
+}
diff --git a/java/src/com/google/idea/blaze/java/libraries/AddLibraryTargetDirectoryToProjectViewAction.java b/java/src/com/google/idea/blaze/java/libraries/AddLibraryTargetDirectoryToProjectViewAction.java
index a954942..ec05bb7 100644
--- a/java/src/com/google/idea/blaze/java/libraries/AddLibraryTargetDirectoryToProjectViewAction.java
+++ b/java/src/com/google/idea/blaze/java/libraries/AddLibraryTargetDirectoryToProjectViewAction.java
@@ -15,6 +15,8 @@
*/
package com.google.idea.blaze.java.libraries;
+import static java.util.stream.Collectors.toList;
+
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
@@ -34,13 +36,13 @@
import com.google.idea.blaze.base.sync.BlazeSyncManager;
import com.google.idea.blaze.base.sync.BlazeSyncParams;
import com.google.idea.blaze.base.sync.data.BlazeProjectDataManager;
+import com.google.idea.blaze.base.util.WorkspacePathUtil;
import com.google.idea.blaze.java.sync.model.BlazeJarLibrary;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.Presentation;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.libraries.Library;
import com.intellij.openapi.ui.Messages;
-import com.intellij.openapi.util.io.FileUtil;
import java.io.File;
import java.util.List;
import java.util.Set;
@@ -107,13 +109,14 @@
return null;
}
boolean exists =
- projectViewSet
- .listItems(DirectorySection.KEY)
- .stream()
- .anyMatch(
- entry ->
- FileUtil.isAncestor(
- entry.directory.relativePath(), workspacePath.relativePath(), false));
+ WorkspacePathUtil.isUnderAnyWorkspacePath(
+ projectViewSet
+ .listItems(DirectorySection.KEY)
+ .stream()
+ .filter(entry -> entry.included)
+ .map(entry -> entry.directory)
+ .collect(toList()),
+ workspacePath);
if (exists) {
return null;
}
diff --git a/java/src/com/google/idea/blaze/java/libraries/JarCache.java b/java/src/com/google/idea/blaze/java/libraries/JarCache.java
index 544e7ad..8617b8c 100644
--- a/java/src/com/google/idea/blaze/java/libraries/JarCache.java
+++ b/java/src/com/google/idea/blaze/java/libraries/JarCache.java
@@ -59,7 +59,7 @@
/** Local cache of the jars referenced by the project. */
public class JarCache {
- private static final Logger LOG = Logger.getInstance(JarCache.class);
+ private static final Logger logger = Logger.getInstance(JarCache.class);
private final Project project;
private final BlazeImportSettings importSettings;
@@ -144,7 +144,7 @@
// Ensure the cache dir exists
if (!cacheDir.exists()) {
if (!cacheDir.mkdirs()) {
- LOG.error("Could not create jar cache directory");
+ logger.error("Could not create jar cache directory");
return;
}
}
@@ -199,7 +199,7 @@
StandardCopyOption.REPLACE_EXISTING,
StandardCopyOption.COPY_ATTRIBUTES);
} catch (IOException e) {
- LOG.warn(e);
+ logger.warn(e);
}
}));
}
@@ -213,7 +213,7 @@
try {
Files.deleteIfExists(Paths.get(cacheFile.getPath()));
} catch (IOException e) {
- LOG.warn(e);
+ logger.warn(e);
}
}));
}
@@ -223,9 +223,9 @@
Futures.allAsList(futures).get();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
- LOG.warn(e);
+ logger.warn(e);
} catch (ExecutionException e) {
- LOG.error(e);
+ logger.error(e);
}
if (context != null && updatedFiles.size() > 0) {
context.output(PrintOutput.log(String.format("Copied %d jars", updatedFiles.size())));
@@ -247,7 +247,7 @@
"Total Jar Cache size: %d kB (%d files)",
total / 1024, finalCacheFiles.length)));
} catch (Exception e) {
- LOG.warn("Could not determine cache size", e);
+ logger.warn("Could not determine cache size", e);
}
}
}
diff --git a/java/src/com/google/idea/blaze/java/projectview/ExcludedLibrarySection.java b/java/src/com/google/idea/blaze/java/projectview/ExcludedLibrarySection.java
index 908d48d..d2b0764 100644
--- a/java/src/com/google/idea/blaze/java/projectview/ExcludedLibrarySection.java
+++ b/java/src/com/google/idea/blaze/java/projectview/ExcludedLibrarySection.java
@@ -20,6 +20,7 @@
import com.google.idea.blaze.base.projectview.section.ListSection;
import com.google.idea.blaze.base.projectview.section.SectionKey;
import com.google.idea.blaze.base.projectview.section.SectionParser;
+import javax.annotation.Nullable;
/** Section for excluding libraries. */
@Deprecated
@@ -31,5 +32,11 @@
public boolean isDeprecated() {
return true;
}
+
+ @Nullable
+ @Override
+ public String getDeprecationMessage() {
+ return "excluded_libraries has been deprecated. Please use 'exclude_library' instead.";
+ }
};
}
diff --git a/java/src/com/google/idea/blaze/java/run/BlazeJavaRunProfileState.java b/java/src/com/google/idea/blaze/java/run/BlazeJavaRunProfileState.java
index 47b971b..6175856 100644
--- a/java/src/com/google/idea/blaze/java/run/BlazeJavaRunProfileState.java
+++ b/java/src/com/google/idea/blaze/java/run/BlazeJavaRunProfileState.java
@@ -22,21 +22,21 @@
import com.google.idea.blaze.base.command.BlazeCommandName;
import com.google.idea.blaze.base.command.BlazeFlags;
import com.google.idea.blaze.base.issueparser.IssueOutputLineProcessor;
-import com.google.idea.blaze.base.metrics.Action;
import com.google.idea.blaze.base.model.primitives.Kind;
import com.google.idea.blaze.base.model.primitives.WorkspaceRoot;
import com.google.idea.blaze.base.projectview.ProjectViewManager;
import com.google.idea.blaze.base.projectview.ProjectViewSet;
import com.google.idea.blaze.base.run.BlazeCommandRunConfiguration;
import com.google.idea.blaze.base.run.DistributedExecutorSupport;
+import com.google.idea.blaze.base.run.filter.BlazeTargetFilter;
import com.google.idea.blaze.base.run.processhandler.LineProcessingProcessAdapter;
import com.google.idea.blaze.base.run.processhandler.ScopedBlazeProcessHandler;
+import com.google.idea.blaze.base.run.smrunner.BlazeTestEventsHandler;
import com.google.idea.blaze.base.run.smrunner.SmRunnerUtils;
import com.google.idea.blaze.base.run.state.BlazeCommandRunConfigurationCommonState;
import com.google.idea.blaze.base.scope.BlazeContext;
import com.google.idea.blaze.base.scope.scopes.IdeaLogScope;
import com.google.idea.blaze.base.scope.scopes.IssuesScope;
-import com.google.idea.blaze.base.scope.scopes.LoggedTimingScope;
import com.google.idea.blaze.base.settings.Blaze;
import com.google.idea.blaze.base.settings.BlazeImportSettings;
import com.google.idea.blaze.base.settings.BlazeImportSettingsManager;
@@ -102,10 +102,16 @@
BlazeCommand blazeCommand;
if (useTestUi()) {
- BlazeJavaTestEventsHandler eventsHandler = new BlazeJavaTestEventsHandler();
+ BlazeTestEventsHandler eventsHandler =
+ BlazeTestEventsHandler.getHandlerForTarget(project, configuration.getTarget());
+ assert (eventsHandler != null);
blazeCommand =
getBlazeCommand(
- project, configuration, projectViewSet, eventsHandler.getBlazeFlags(), debug);
+ project,
+ configuration,
+ projectViewSet,
+ BlazeTestEventsHandler.getBlazeFlags(project),
+ debug);
setConsoleBuilder(
new TextConsoleBuilderImpl(project) {
@Override
@@ -118,6 +124,7 @@
blazeCommand =
getBlazeCommand(project, configuration, projectViewSet, ImmutableList.of(), debug);
}
+ addConsoleFilters(new BlazeTargetFilter(project));
WorkspaceRoot workspaceRoot = WorkspaceRoot.fromImportSettings(importSettings);
return new ScopedBlazeProcessHandler(
@@ -127,10 +134,7 @@
new ScopedBlazeProcessHandler.ScopedProcessHandlerDelegate() {
@Override
public void onBlazeContextStart(BlazeContext context) {
- context
- .push(new LoggedTimingScope(project, Action.BLAZE_COMMAND_USAGE))
- .push(new IssuesScope(project))
- .push(new IdeaLogScope());
+ context.push(new IssuesScope(project)).push(new IdeaLogScope());
}
@Override
@@ -158,7 +162,7 @@
configuration.getHandlerStateIfType(BlazeCommandRunConfigurationCommonState.class);
return state != null
&& BlazeCommandName.TEST.equals(state.getCommand())
- && !Boolean.TRUE.equals(state.getRunOnDistributedExecutor());
+ && !state.getRunOnDistributedExecutor();
}
@Override
@@ -204,9 +208,11 @@
command.addBlazeFlags(BlazeFlags.JAVA_TEST_DEBUG);
}
} else {
- command.addBlazeFlags(
- DistributedExecutorSupport.getBlazeFlags(
- project, handlerState.getRunOnDistributedExecutor()));
+ boolean runDistributed = handlerState.getRunOnDistributedExecutor();
+ command.addBlazeFlags(DistributedExecutorSupport.getBlazeFlags(project, runDistributed));
+ if (!runDistributed) {
+ command.addBlazeFlags(BlazeFlags.TEST_OUTPUT_STREAMED);
+ }
}
command.addExeFlags(handlerState.getExeFlags());
diff --git a/java/src/com/google/idea/blaze/java/run/BlazeJavaTestEventsHandler.java b/java/src/com/google/idea/blaze/java/run/BlazeJavaTestEventsHandler.java
index 9af9b64..4a1024f 100644
--- a/java/src/com/google/idea/blaze/java/run/BlazeJavaTestEventsHandler.java
+++ b/java/src/com/google/idea/blaze/java/run/BlazeJavaTestEventsHandler.java
@@ -16,10 +16,11 @@
package com.google.idea.blaze.java.run;
import com.google.idea.blaze.base.command.BlazeFlags;
+import com.google.idea.blaze.base.model.primitives.Kind;
import com.google.idea.blaze.base.run.smrunner.BlazeTestEventsHandler;
+import com.google.idea.blaze.base.run.smrunner.BlazeXmlSchema.TestSuite;
import com.google.idea.blaze.java.run.producers.BlazeJUnitTestFilterFlags;
import com.intellij.execution.Location;
-import com.intellij.execution.testframework.AbstractTestProxy;
import com.intellij.execution.testframework.JavaTestLocator;
import com.intellij.execution.testframework.sm.runner.SMTestLocator;
import com.intellij.openapi.project.Project;
@@ -27,17 +28,36 @@
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiMethod;
-import com.intellij.psi.search.GlobalSearchScope;
-import com.intellij.util.containers.MultiMap;
import com.intellij.util.io.URLUtil;
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import javax.annotation.Nullable;
/** Provides java-specific methods needed by the SM-runner test UI. */
public class BlazeJavaTestEventsHandler extends BlazeTestEventsHandler {
- public BlazeJavaTestEventsHandler() {
- super("Blaze Java Test");
+ @Override
+ protected EnumSet<Kind> handledKinds() {
+ return EnumSet.of(Kind.JAVA_TEST, Kind.ANDROID_ROBOLECTRIC_TEST, Kind.GWT_TEST);
+ }
+
+ /** Overridden to support parameterized tests, which use nested test_suite XML elements. */
+ @Override
+ public boolean ignoreSuite(TestSuite suite) {
+ if (suite.testSuites.isEmpty()) {
+ return false;
+ }
+ for (TestSuite child : suite.testSuites) {
+ // target/class names are fully-qualified; unqualified names denote parameterized methods
+ if (!child.name.contains(".")) {
+ return false;
+ }
+ }
+ return true;
}
@Override
@@ -46,20 +66,34 @@
}
@Override
- public String suiteLocationUrl(String name) {
+ public String suiteLocationUrl(@Nullable Kind kind, String name) {
return JavaTestLocator.SUITE_PROTOCOL + URLUtil.SCHEME_SEPARATOR + name;
}
@Override
- public String testLocationUrl(String name, @Nullable String classname) {
+ public String testLocationUrl(
+ @Nullable Kind kind, String parentSuite, String name, @Nullable String classname) {
if (classname == null) {
return null;
}
- return JavaTestLocator.TEST_PROTOCOL + URLUtil.SCHEME_SEPARATOR + classname + "." + name;
+ String classComponent = JavaTestLocator.TEST_PROTOCOL + URLUtil.SCHEME_SEPARATOR + classname;
+ String parameterComponent = extractParameterComponent(name);
+ if (parameterComponent != null) {
+ return classComponent + "." + parentSuite + parameterComponent;
+ }
+ return classComponent + "." + name;
+ }
+
+ @Nullable
+ private static String extractParameterComponent(String name) {
+ if (name.startsWith("[") && name.contains("]")) {
+ return name.substring(0, name.indexOf(']') + 1);
+ }
+ return null;
}
@Override
- public String suiteDisplayName(String rawName) {
+ public String suiteDisplayName(@Nullable Kind kind, String rawName) {
String name = StringUtil.trimEnd(rawName, '.');
int lastPointIx = name.lastIndexOf('.');
return lastPointIx != -1 ? name.substring(lastPointIx + 1, name.length()) : name;
@@ -67,28 +101,29 @@
@Nullable
@Override
- public String getTestFilter(Project project, List<AbstractTestProxy> failedTests) {
- GlobalSearchScope projectScope = GlobalSearchScope.allScope(project);
- MultiMap<PsiClass, PsiMethod> failedMethodsPerClass = new MultiMap<>();
- for (AbstractTestProxy test : failedTests) {
- appendTest(failedMethodsPerClass, test.getLocation(project, projectScope));
+ public String getTestFilter(Project project, List<Location<?>> testLocations) {
+ Map<PsiClass, Collection<Location<?>>> failedClassesAndMethods = new HashMap<>();
+ for (Location<?> location : testLocations) {
+ appendTest(failedClassesAndMethods, location);
}
- String filter = BlazeJUnitTestFilterFlags.testFilterForClassesAndMethods(failedMethodsPerClass);
+ String filter =
+ BlazeJUnitTestFilterFlags.testFilterForClassesAndMethods(failedClassesAndMethods);
return filter != null ? BlazeFlags.TEST_FILTER + "=" + filter : null;
}
- private static void appendTest(
- MultiMap<PsiClass, PsiMethod> testMap, @Nullable Location<?> testLocation) {
- if (testLocation == null) {
+ private static void appendTest(Map<PsiClass, Collection<Location<?>>> map, Location<?> location) {
+ PsiElement psi = location.getPsiElement();
+ if (psi instanceof PsiClass) {
+ map.computeIfAbsent((PsiClass) psi, k -> new HashSet<>());
return;
}
- PsiElement method = testLocation.getPsiElement();
- if (!(method instanceof PsiMethod)) {
+ if (!(psi instanceof PsiMethod)) {
return;
}
- PsiClass psiClass = ((PsiMethod) method).getContainingClass();
- if (psiClass != null) {
- testMap.putValue(psiClass, (PsiMethod) method);
+ PsiClass psiClass = ((PsiMethod) psi).getContainingClass();
+ if (psiClass == null) {
+ return;
}
+ map.computeIfAbsent(psiClass, k -> new HashSet<>()).add(location);
}
}
diff --git a/java/src/com/google/idea/blaze/java/run/producers/BlazeJUnitTestFilterFlags.java b/java/src/com/google/idea/blaze/java/run/producers/BlazeJUnitTestFilterFlags.java
index 8f2290f..4ee0508 100644
--- a/java/src/com/google/idea/blaze/java/run/producers/BlazeJUnitTestFilterFlags.java
+++ b/java/src/com/google/idea/blaze/java/run/producers/BlazeJUnitTestFilterFlags.java
@@ -19,13 +19,18 @@
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
+import com.intellij.codeInsight.AnnotationUtil;
+import com.intellij.execution.Location;
import com.intellij.execution.junit.JUnitUtil;
import com.intellij.execution.junit2.PsiMemberParameterizedLocation;
+import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiMethod;
-import com.intellij.util.containers.MultiMap;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
+import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
@@ -57,12 +62,34 @@
PsiClass psiClass, Collection<PsiMethod> methods) {
JUnitVersion version =
JUnitUtil.isJUnit4TestClass(psiClass) ? JUnitVersion.JUNIT_4 : JUnitVersion.JUNIT_3;
- return testFilterForClassAndMethods(psiClass, version, methods);
+ return testFilterForClassAndMethods(psiClass, version, extractMethodFilters(psiClass, methods));
+ }
+
+ /** Runs all parameterized versions of methods. */
+ private static List<String> extractMethodFilters(
+ PsiClass psiClass, Collection<PsiMethod> methods) {
+ // standard org.junit.runners.Parameterized class requires no per-test annotations
+ boolean parameterizedClass = isParameterized(psiClass);
+ return methods
+ .stream()
+ .map((method) -> methodFilter(method, parameterizedClass))
+ .sorted()
+ .collect(Collectors.toList());
+ }
+
+ private static boolean isParameterized(PsiClass testClass) {
+ return PsiMemberParameterizedLocation.getParameterizedLocation(testClass, null) != null;
+ }
+
+ private static String methodFilter(PsiMethod method, boolean parameterizedClass) {
+ boolean parameterized =
+ parameterizedClass || AnnotationUtil.findAnnotation(method, "Parameters") != null;
+ return parameterized ? method.getName() + "(\\[.+\\])?" : method.getName();
}
@Nullable
public static String testFilterForClassesAndMethods(
- MultiMap<PsiClass, PsiMethod> methodsPerClass) {
+ Map<PsiClass, Collection<Location<?>>> methodsPerClass) {
// Note: this could be incorrect if there are no JUnit4 classes in this sample, but some in the
// java_test target they're run from.
JUnitVersion version =
@@ -72,15 +99,40 @@
@Nullable
public static String testFilterForClassesAndMethods(
- MultiMap<PsiClass, PsiMethod> methodsPerClass, JUnitVersion version) {
- StringBuilder output = new StringBuilder();
- for (Entry<PsiClass, Collection<PsiMethod>> entry : methodsPerClass.entrySet()) {
- String filter = testFilterForClassAndMethods(entry.getKey(), version, entry.getValue());
+ Map<PsiClass, Collection<Location<?>>> methodsPerClass, JUnitVersion version) {
+ List<String> classFilters = new ArrayList<>();
+ for (Entry<PsiClass, Collection<Location<?>>> entry : methodsPerClass.entrySet()) {
+ String filter =
+ testFilterForClassAndMethods(
+ entry.getKey(), version, extractMethodFilters(entry.getValue()));
if (filter != null) {
- output.append(filter);
+ classFilters.add(filter);
}
}
- return Strings.emptyToNull(output.toString());
+ return version == JUnitVersion.JUNIT_4
+ ? String.join("|", classFilters)
+ : String.join(",", classFilters);
+ }
+
+ /** Only runs specified parameterized versions, where relevant. */
+ private static List<String> extractMethodFilters(Collection<Location<?>> methods) {
+ return methods
+ .stream()
+ .map(BlazeJUnitTestFilterFlags::testFilterForLocation)
+ .sorted()
+ .collect(Collectors.toList());
+ }
+
+ private static String testFilterForLocation(Location<?> location) {
+ PsiElement psi = location.getPsiElement();
+ assert (psi instanceof PsiMethod);
+ String methodName = ((PsiMethod) psi).getName();
+ if (location instanceof PsiMemberParameterizedLocation) {
+ return methodName
+ + StringUtil.escapeToRegexp(
+ ((PsiMemberParameterizedLocation) location).getParamSetName());
+ }
+ return methodName;
}
private static boolean hasJUnit4Test(Collection<PsiClass> classes) {
@@ -98,19 +150,12 @@
*/
@Nullable
private static String testFilterForClassAndMethods(
- PsiClass psiClass, JUnitVersion version, Collection<PsiMethod> methods) {
+ PsiClass psiClass, JUnitVersion version, List<String> methodFilters) {
String className = psiClass.getQualifiedName();
if (className == null) {
return null;
}
- // Sort so multiple configurations created with different selection orders are the same.
- List<String> methodNames =
- methods.stream().map(PsiMethod::getName).sorted().collect(Collectors.toList());
- return testFilterForClassAndMethods(className, methodNames, version, isParameterized(psiClass));
- }
-
- private static boolean isParameterized(PsiClass testClass) {
- return PsiMemberParameterizedLocation.getParameterizedLocation(testClass, null) != null;
+ return testFilterForClassAndMethods(className, version, methodFilters);
}
/**
@@ -119,10 +164,7 @@
*/
@VisibleForTesting
static String testFilterForClassAndMethods(
- String className,
- List<String> methodNames,
- JUnitVersion jUnitVersion,
- boolean parameterized) {
+ String className, JUnitVersion jUnitVersion, List<String> methodNames) {
StringBuilder output = new StringBuilder(className);
String methodNamePattern = concatenateMethodNames(methodNames, jUnitVersion);
if (Strings.isNullOrEmpty(methodNamePattern)) {
@@ -137,10 +179,6 @@
if (jUnitVersion == JUnitVersion.JUNIT_3) {
return output.toString();
}
- // parameterized tests include their parameters between brackets after the method name
- if (parameterized) {
- output.append("(\\[.+\\])?");
- }
output.append('$');
return output.toString();
}
diff --git a/java/src/com/google/idea/blaze/java/run/producers/BlazeJavaMainClassRunConfigurationProducer.java b/java/src/com/google/idea/blaze/java/run/producers/BlazeJavaMainClassRunConfigurationProducer.java
index 6e9be07..b90826a 100644
--- a/java/src/com/google/idea/blaze/java/run/producers/BlazeJavaMainClassRunConfigurationProducer.java
+++ b/java/src/com/google/idea/blaze/java/run/producers/BlazeJavaMainClassRunConfigurationProducer.java
@@ -15,20 +15,18 @@
*/
package com.google.idea.blaze.java.run.producers;
-import com.google.common.collect.ImmutableCollection;
-import com.google.common.collect.Sets;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
import com.google.idea.blaze.base.command.BlazeCommandName;
import com.google.idea.blaze.base.ideinfo.TargetIdeInfo;
-import com.google.idea.blaze.base.ideinfo.TargetKey;
import com.google.idea.blaze.base.model.BlazeProjectData;
import com.google.idea.blaze.base.model.primitives.Kind;
-import com.google.idea.blaze.base.model.primitives.Label;
import com.google.idea.blaze.base.run.BlazeCommandRunConfiguration;
import com.google.idea.blaze.base.run.BlazeCommandRunConfigurationType;
import com.google.idea.blaze.base.run.producers.BlazeRunConfigurationProducer;
import com.google.idea.blaze.base.run.state.BlazeCommandRunConfigurationCommonState;
-import com.google.idea.blaze.base.sync.data.BlazeProjectDataManager;
-import com.google.idea.blaze.base.targetmaps.SourceToTargetMap;
+import com.google.idea.blaze.base.run.testmap.FilteredTargetMap;
+import com.google.idea.blaze.base.sync.SyncCache;
import com.google.idea.blaze.java.run.RunUtil;
import com.intellij.execution.JavaExecutionUtil;
import com.intellij.execution.Location;
@@ -41,16 +39,16 @@
import com.intellij.psi.PsiMethod;
import com.intellij.psi.util.PsiMethodUtil;
import java.io.File;
-import java.util.ArrayDeque;
+import java.util.Collection;
import java.util.Objects;
-import java.util.Queue;
-import java.util.Set;
import org.jetbrains.annotations.Nullable;
/** Creates run configurations for Java main classes sourced by java_binary targets. */
public class BlazeJavaMainClassRunConfigurationProducer
extends BlazeRunConfigurationProducer<BlazeCommandRunConfiguration> {
+ private static final String JAVA_BINARY_MAP_KEY = "BlazeJavaBinaryMap";
+
public BlazeJavaMainClassRunConfigurationProducer() {
super(BlazeCommandRunConfigurationType.getInstance());
}
@@ -73,11 +71,11 @@
sourceElement.set(mainMethod);
}
- Label label = getTargetLabel(context.getProject(), mainClass);
- if (label == null) {
+ TargetIdeInfo target = getTarget(context.getProject(), mainClass);
+ if (target == null) {
return false;
}
- configuration.setTarget(label);
+ configuration.setTarget(target.key.label);
BlazeCommandRunConfigurationCommonState handlerState =
configuration.getHandlerStateIfType(BlazeCommandRunConfigurationCommonState.class);
if (handlerState == null) {
@@ -103,11 +101,11 @@
if (mainClass == null) {
return false;
}
- Label label = getTargetLabel(context.getProject(), mainClass);
- if (label == null) {
+ TargetIdeInfo target = getTarget(context.getProject(), mainClass);
+ if (target == null) {
return false;
}
- return Objects.equals(configuration.getTarget(), label);
+ return Objects.equals(configuration.getTarget(), target.key.label);
}
@Nullable
@@ -128,35 +126,60 @@
}
@Nullable
- private static Label getTargetLabel(Project project, PsiClass mainClass) {
+ private static TargetIdeInfo getTarget(Project project, PsiClass mainClass) {
File mainClassFile = RunUtil.getFileForClass(mainClass);
- ImmutableCollection<TargetKey> targetKeys =
- SourceToTargetMap.getInstance(project).getRulesForSourceFile(mainClassFile);
- BlazeProjectData blazeProjectData =
- BlazeProjectDataManager.getInstance(project).getBlazeProjectData();
- if (blazeProjectData == null) {
+ if (mainClassFile == null) {
return null;
}
- // Find the first java_binary, BFS
- Queue<TargetKey> todo = new ArrayDeque<>();
- todo.addAll(targetKeys);
- Set<TargetKey> seen = Sets.newHashSet();
- while (!todo.isEmpty()) {
- TargetKey targetKey = todo.remove();
- if (!seen.add(targetKey)) {
- continue;
- }
- TargetIdeInfo target = blazeProjectData.targetMap.get(targetKey);
- if (target == null) {
- continue;
- }
- if (target.kind == Kind.JAVA_BINARY && target.isPlainTarget()) {
- // Best-effort guess: the main_class attribute isn't exposed, but assume
- // mainClass is the main_class because it is sourced by the java_binary.
- return target.key.label;
- }
- todo.addAll(blazeProjectData.reverseDependencies.get(targetKey));
+ Collection<TargetIdeInfo> javaBinaryTargets = findJavaBinaryTargets(project, mainClassFile);
+
+ String qualifiedName = mainClass.getQualifiedName();
+ String className = mainClass.getName();
+ if (qualifiedName == null || className == null) {
+ // out of date psi element; just take the first match
+ return Iterables.getFirst(javaBinaryTargets, null);
}
- return null;
+
+ // first look for a matching main_class
+ TargetIdeInfo match =
+ javaBinaryTargets
+ .stream()
+ .filter(
+ target ->
+ target.javaIdeInfo != null
+ && qualifiedName.equals(target.javaIdeInfo.javaBinaryMainClass))
+ .findFirst()
+ .orElse(null);
+ if (match != null) {
+ return match;
+ }
+
+ match =
+ javaBinaryTargets
+ .stream()
+ .filter(target -> className.equals(target.key.label.targetName().toString()))
+ .findFirst()
+ .orElse(null);
+ if (match != null) {
+ return match;
+ }
+ return Iterables.getFirst(javaBinaryTargets, null);
+ }
+
+ /** Returns all java_binary targets reachable from the given source file. */
+ private static Collection<TargetIdeInfo> findJavaBinaryTargets(
+ Project project, File mainClassFile) {
+ FilteredTargetMap map =
+ SyncCache.getInstance(project)
+ .get(JAVA_BINARY_MAP_KEY, BlazeJavaMainClassRunConfigurationProducer::computeTargetMap);
+ return map != null ? map.targetsForSourceFile(mainClassFile) : ImmutableList.of();
+ }
+
+ private static FilteredTargetMap computeTargetMap(Project project, BlazeProjectData projectData) {
+ return new FilteredTargetMap(
+ project,
+ projectData.artifactLocationDecoder,
+ projectData.targetMap,
+ (targetIdeInfo) -> targetIdeInfo.kind == Kind.JAVA_BINARY && targetIdeInfo.isPlainTarget());
}
}
diff --git a/java/src/com/google/idea/blaze/java/run/producers/BlazeJavaTestClassConfigurationProducer.java b/java/src/com/google/idea/blaze/java/run/producers/BlazeJavaTestClassConfigurationProducer.java
index 95bc4ee..7d9a20f 100644
--- a/java/src/com/google/idea/blaze/java/run/producers/BlazeJavaTestClassConfigurationProducer.java
+++ b/java/src/com/google/idea/blaze/java/run/producers/BlazeJavaTestClassConfigurationProducer.java
@@ -15,7 +15,6 @@
*/
package com.google.idea.blaze.java.run.producers;
-import com.google.common.collect.ImmutableList;
import com.google.idea.blaze.base.command.BlazeCommandName;
import com.google.idea.blaze.base.command.BlazeFlags;
import com.google.idea.blaze.base.ideinfo.TargetIdeInfo;
@@ -24,6 +23,7 @@
import com.google.idea.blaze.base.run.BlazeCommandRunConfigurationType;
import com.google.idea.blaze.base.run.BlazeConfigurationNameBuilder;
import com.google.idea.blaze.base.run.producers.BlazeRunConfigurationProducer;
+import com.google.idea.blaze.base.run.smrunner.SmRunnerUtils;
import com.google.idea.blaze.base.run.state.BlazeCommandRunConfigurationCommonState;
import com.google.idea.blaze.java.run.RunUtil;
import com.intellij.execution.JavaExecutionUtil;
@@ -35,6 +35,8 @@
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiModifier;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Objects;
import org.jetbrains.annotations.NotNull;
@@ -59,6 +61,10 @@
return false;
}
+ if (!SmRunnerUtils.getSelectedSmRunnerTreeElements(context).isEmpty()) {
+ // handled by a different producer
+ return false;
+ }
if (JUnitConfigurationUtil.isMultipleElementsSelected(context)) {
return false;
}
@@ -84,23 +90,22 @@
if (handlerState == null) {
return false;
}
+ String testFilter = BlazeJUnitTestFilterFlags.testFilterForClass(testClass);
+ if (testFilter == null) {
+ return false;
+ }
handlerState.setCommand(BlazeCommandName.TEST);
- ImmutableList.Builder<String> flags = ImmutableList.builder();
-
- String testFilter = BlazeJUnitTestFilterFlags.testFilterForClass(testClass);
- if (testFilter != null) {
- flags.add(BlazeFlags.TEST_FILTER + "=" + testFilter);
- }
- flags.add(BlazeFlags.TEST_OUTPUT_STREAMED);
- flags.addAll(handlerState.getBlazeFlags());
-
- handlerState.setBlazeFlags(flags.build());
+ // remove old test filter flag if present
+ List<String> flags = new ArrayList<>(handlerState.getBlazeFlags());
+ flags.removeIf((flag) -> flag.startsWith(BlazeFlags.TEST_FILTER));
+ flags.add(BlazeFlags.TEST_FILTER + "=" + testFilter);
+ handlerState.setBlazeFlags(flags);
BlazeConfigurationNameBuilder nameBuilder = new BlazeConfigurationNameBuilder(configuration);
nameBuilder.setTargetString(testClass.getName());
configuration.setName(nameBuilder.build());
-
+ configuration.setNameChangedByUser(true); // don't revert to generated name
return true;
}
@@ -115,6 +120,10 @@
return false;
}
+ if (!SmRunnerUtils.getSelectedSmRunnerTreeElements(context).isEmpty()) {
+ // handled by a different producer
+ return false;
+ }
if (JUnitConfigurationUtil.isMultipleElementsSelected(context)) {
return false;
}
@@ -143,6 +152,9 @@
return false;
}
String filter = BlazeJUnitTestFilterFlags.testFilterForClass(testClass);
- return Objects.equals(filter, handlerState.getTestFilterFlag());
+ if (filter == null) {
+ return false;
+ }
+ return Objects.equals(BlazeFlags.TEST_FILTER + "=" + filter, handlerState.getTestFilterFlag());
}
}
diff --git a/java/src/com/google/idea/blaze/java/run/producers/BlazeJavaTestMethodConfigurationProducer.java b/java/src/com/google/idea/blaze/java/run/producers/BlazeJavaTestMethodConfigurationProducer.java
index 1a7e149..e0453d1 100644
--- a/java/src/com/google/idea/blaze/java/run/producers/BlazeJavaTestMethodConfigurationProducer.java
+++ b/java/src/com/google/idea/blaze/java/run/producers/BlazeJavaTestMethodConfigurationProducer.java
@@ -15,7 +15,6 @@
*/
package com.google.idea.blaze.java.run.producers;
-import com.google.common.collect.ImmutableList;
import com.google.idea.blaze.base.command.BlazeCommandName;
import com.google.idea.blaze.base.command.BlazeFlags;
import com.google.idea.blaze.base.ideinfo.TargetIdeInfo;
@@ -24,6 +23,7 @@
import com.google.idea.blaze.base.run.BlazeCommandRunConfigurationType;
import com.google.idea.blaze.base.run.BlazeConfigurationNameBuilder;
import com.google.idea.blaze.base.run.producers.BlazeRunConfigurationProducer;
+import com.google.idea.blaze.base.run.smrunner.SmRunnerUtils;
import com.google.idea.blaze.base.run.state.BlazeCommandRunConfigurationCommonState;
import com.google.idea.blaze.java.run.RunUtil;
import com.intellij.execution.actions.ConfigurationContext;
@@ -31,10 +31,11 @@
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiMethod;
+import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
-import org.jetbrains.annotations.NotNull;
+import javax.annotation.Nullable;
/** Producer for run configurations related to Java test methods in Blaze. */
public class BlazeJavaTestMethodConfigurationProducer
@@ -64,9 +65,9 @@
@Override
protected boolean doSetupConfigFromContext(
- @NotNull BlazeCommandRunConfiguration configuration,
- @NotNull ConfigurationContext context,
- @NotNull Ref<PsiElement> sourceElement) {
+ BlazeCommandRunConfiguration configuration,
+ ConfigurationContext context,
+ Ref<PsiElement> sourceElement) {
SelectedMethodInfo methodInfo = getSelectedMethodInfo(context);
if (methodInfo == null) {
@@ -93,12 +94,11 @@
}
handlerState.setCommand(BlazeCommandName.TEST);
- ImmutableList.Builder<String> flags = ImmutableList.builder();
+ // remove old test filter flag if present
+ List<String> flags = new ArrayList<>(handlerState.getBlazeFlags());
+ flags.removeIf((flag) -> flag.startsWith(BlazeFlags.TEST_FILTER));
flags.add(methodInfo.testFilterFlag);
- flags.add(BlazeFlags.TEST_OUTPUT_STREAMED);
- flags.addAll(handlerState.getBlazeFlags());
-
- handlerState.setBlazeFlags(flags.build());
+ handlerState.setBlazeFlags(flags);
BlazeConfigurationNameBuilder nameBuilder = new BlazeConfigurationNameBuilder(configuration);
nameBuilder.setTargetString(
@@ -106,13 +106,13 @@
"%s.%s",
methodInfo.containingClass.getName(), String.join(",", methodInfo.methodNames)));
configuration.setName(nameBuilder.build());
-
+ configuration.setNameChangedByUser(true); // don't revert to generated name
return true;
}
@Override
protected boolean doIsConfigFromContext(
- @NotNull BlazeCommandRunConfiguration configuration, @NotNull ConfigurationContext context) {
+ BlazeCommandRunConfiguration configuration, ConfigurationContext context) {
BlazeCommandRunConfigurationCommonState handlerState =
configuration.getHandlerStateIfType(BlazeCommandRunConfigurationCommonState.class);
if (handlerState == null) {
@@ -131,7 +131,12 @@
return flags.contains(methodInfo.testFilterFlag);
}
+ @Nullable
private static SelectedMethodInfo getSelectedMethodInfo(ConfigurationContext context) {
+ if (!SmRunnerUtils.getSelectedSmRunnerTreeElements(context).isEmpty()) {
+ // handled by a different producer
+ return null;
+ }
final List<PsiMethod> selectedMethods = TestMethodSelectionUtil.getSelectedMethods(context);
if (selectedMethods == null) {
return null;
diff --git a/java/src/com/google/idea/blaze/java/run/producers/NonBlazeProducerSuppressor.java b/java/src/com/google/idea/blaze/java/run/producers/NonBlazeProducerSuppressor.java
index 7c4f4ca..faf3545 100644
--- a/java/src/com/google/idea/blaze/java/run/producers/NonBlazeProducerSuppressor.java
+++ b/java/src/com/google/idea/blaze/java/run/producers/NonBlazeProducerSuppressor.java
@@ -19,9 +19,15 @@
import com.google.idea.blaze.base.settings.Blaze;
import com.intellij.execution.RunConfigurationProducerService;
import com.intellij.execution.actions.RunConfigurationProducer;
+import com.intellij.ide.plugins.IdeaPluginDescriptor;
+import com.intellij.ide.plugins.PluginManager;
import com.intellij.openapi.components.AbstractProjectComponent;
+import com.intellij.openapi.extensions.PluginId;
import com.intellij.openapi.project.Project;
import java.util.Collection;
+import java.util.Objects;
+import java.util.stream.Collectors;
+import javax.annotation.Nullable;
/** Suppresses certain non-Blaze configuration producers in Blaze projects. */
public class NonBlazeProducerSuppressor extends AbstractProjectComponent {
@@ -33,7 +39,42 @@
com.intellij.execution.junit.AllInDirectoryConfigurationProducer.class,
com.intellij.execution.junit.AllInPackageConfigurationProducer.class,
com.intellij.execution.junit.TestClassConfigurationProducer.class,
- com.intellij.execution.junit.TestMethodConfigurationProducer.class);
+ com.intellij.execution.junit.TestMethodConfigurationProducer.class,
+ com.intellij.execution.junit.PatternConfigurationProducer.class);
+
+ private static final ImmutableList<String> KOTLIN_JUNIT_PRODUCERS =
+ ImmutableList.of(
+ "org.jetbrains.kotlin.idea.run.KotlinJUnitRunConfigurationProducer",
+ "org.jetbrains.kotlin.idea.run.KotlinPatternConfigurationProducer");
+
+ private static Collection<Class<? extends RunConfigurationProducer<?>>> getKotlinProducers() {
+ // rather than compiling against the Kotlin plugin, and including a switch in the our
+ // plugin.xml, just get the classes manually via the plugin class loader.
+ IdeaPluginDescriptor plugin = PluginManager.getPlugin(PluginId.getId("org.jetbrains.kotlin"));
+ if (plugin == null || !plugin.isEnabled()) {
+ return ImmutableList.of();
+ }
+ ClassLoader loader = plugin.getPluginClassLoader();
+ return KOTLIN_JUNIT_PRODUCERS
+ .stream()
+ .map((qualifiedName) -> loadClass(loader, qualifiedName))
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
+ }
+
+ @Nullable
+ private static Class<RunConfigurationProducer<?>> loadClass(
+ ClassLoader loader, String qualifiedName) {
+ try {
+ Class<?> clazz = loader.loadClass(qualifiedName);
+ if (RunConfigurationProducer.class.isAssignableFrom(clazz)) {
+ return (Class<RunConfigurationProducer<?>>) clazz;
+ }
+ return null;
+ } catch (ClassNotFoundException ignored) {
+ return null;
+ }
+ }
public NonBlazeProducerSuppressor(Project project) {
super(project);
@@ -49,6 +90,10 @@
private static void suppressProducers(Project project) {
RunConfigurationProducerService producerService =
RunConfigurationProducerService.getInstance(project);
- PRODUCERS_TO_SUPPRESS.forEach(producerService::addIgnoredProducer);
+ ImmutableList.<Class<? extends RunConfigurationProducer<?>>>builder()
+ .addAll(PRODUCERS_TO_SUPPRESS)
+ .addAll(getKotlinProducers())
+ .build()
+ .forEach(producerService::addIgnoredProducer);
}
}
diff --git a/java/src/com/google/idea/blaze/java/sync/jdeps/JdepsFileReader.java b/java/src/com/google/idea/blaze/java/sync/jdeps/JdepsFileReader.java
index ed9bf6a..550c00c 100644
--- a/java/src/com/google/idea/blaze/java/sync/jdeps/JdepsFileReader.java
+++ b/java/src/com/google/idea/blaze/java/sync/jdeps/JdepsFileReader.java
@@ -51,7 +51,7 @@
/** Reads jdeps from the ide info result. */
public class JdepsFileReader {
- private static final Logger LOG = Logger.getInstance(JdepsFileReader.class);
+ private static final Logger logger = Logger.getInstance(JdepsFileReader.class);
static class JdepsState implements Serializable {
private static final long serialVersionUID = 4L;
@@ -173,7 +173,7 @@
return new Result(updatedFile, targetKey, dependencyStringList);
}
} catch (FileNotFoundException e) {
- LOG.info("Could not open jdeps file: " + updatedFile);
+ logger.info("Could not open jdeps file: " + updatedFile);
}
return null;
}));
@@ -191,7 +191,7 @@
"Loaded %d jdeps files, total size %dkB",
updatedFiles.size(), totalSizeLoaded.get() / 1024)));
} catch (InterruptedException | ExecutionException e) {
- LOG.error(e);
+ logger.error(e);
return null;
}
return state;
diff --git a/java/src/com/google/idea/blaze/java/sync/projectstructure/JavaSourceFolderProvider.java b/java/src/com/google/idea/blaze/java/sync/projectstructure/JavaSourceFolderProvider.java
index abe5804..3f7b512 100644
--- a/java/src/com/google/idea/blaze/java/sync/projectstructure/JavaSourceFolderProvider.java
+++ b/java/src/com/google/idea/blaze/java/sync/projectstructure/JavaSourceFolderProvider.java
@@ -19,21 +19,14 @@
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableMap;
import com.google.idea.blaze.base.sync.SourceFolderProvider;
+import com.google.idea.blaze.base.util.UrlUtil;
import com.google.idea.blaze.java.sync.model.BlazeContentEntry;
import com.google.idea.blaze.java.sync.model.BlazeJavaSyncData;
import com.google.idea.blaze.java.sync.model.BlazeSourceDirectory;
-import com.intellij.openapi.application.ApplicationManager;
-import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.roots.ContentEntry;
import com.intellij.openapi.roots.SourceFolder;
import com.intellij.openapi.util.io.FileUtil;
-import com.intellij.openapi.vfs.LocalFileSystem;
-import com.intellij.openapi.vfs.VfsUtilCore;
-import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;
-import com.intellij.openapi.vfs.VirtualFileSystem;
-import com.intellij.openapi.vfs.ex.temp.TempFileSystem;
-import com.intellij.util.io.URLUtil;
import java.io.File;
import javax.annotation.Nullable;
import org.jetbrains.jps.model.JpsElement;
@@ -43,7 +36,6 @@
/** Edits source folders in IntelliJ content entries */
public class JavaSourceFolderProvider implements SourceFolderProvider {
- private static final Logger logger = Logger.getInstance(JavaSourceFolderProvider.class);
private final ImmutableMap<File, BlazeContentEntry> blazeContentEntries;
@@ -64,20 +56,14 @@
}
@Override
- public ImmutableMap<VirtualFile, SourceFolder> initializeSourceFolders(
- ContentEntry contentEntry) {
- ImmutableMap.Builder<VirtualFile, SourceFolder> output = ImmutableMap.builder();
- VirtualFile virtualFile = contentEntry.getFile();
- if (virtualFile == null) {
- return output.build();
- }
-
- File contentRoot = new File(virtualFile.getPath());
- BlazeContentEntry javaContentEntry = blazeContentEntries.get(contentRoot);
+ public ImmutableMap<File, SourceFolder> initializeSourceFolders(ContentEntry contentEntry) {
+ ImmutableMap.Builder<File, SourceFolder> output = ImmutableMap.builder();
+ BlazeContentEntry javaContentEntry =
+ blazeContentEntries.get(UrlUtil.urlToFile(contentEntry.getUrl()));
if (javaContentEntry != null) {
for (BlazeSourceDirectory sourceDirectory : javaContentEntry.sources) {
SourceFolder sourceFolder = addSourceFolderToContentEntry(contentEntry, sourceDirectory);
- output.put(sourceFolder.getFile(), sourceFolder);
+ output.put(UrlUtil.urlToFile(sourceFolder.getUrl()), sourceFolder);
}
}
return output.build();
@@ -85,17 +71,15 @@
@Override
public SourceFolder setSourceFolderForLocation(
- ContentEntry contentEntry,
- SourceFolder parentFolder,
- VirtualFile file,
- boolean isTestSource) {
+ ContentEntry contentEntry, SourceFolder parentFolder, File file, boolean isTestSource) {
SourceFolder sourceFolder;
if (isResource(parentFolder)) {
JavaResourceRootType resourceRootType =
isTestSource ? JavaResourceRootType.TEST_RESOURCE : JavaResourceRootType.RESOURCE;
- sourceFolder = contentEntry.addSourceFolder(pathToUrl(file.getPath()), resourceRootType);
+ sourceFolder =
+ contentEntry.addSourceFolder(UrlUtil.pathToUrl(file.getPath()), resourceRootType);
} else {
- sourceFolder = contentEntry.addSourceFolder(pathToUrl(file.getPath()), isTestSource);
+ sourceFolder = contentEntry.addSourceFolder(UrlUtil.pathToUrl(file.getPath()), isTestSource);
}
sourceFolder.setPackagePrefix(derivePackagePrefix(file, parentFolder));
JpsModuleSourceRoot sourceRoot = sourceFolder.getJpsElement();
@@ -106,14 +90,17 @@
return sourceFolder;
}
- private static String derivePackagePrefix(VirtualFile file, SourceFolder parentFolder) {
+ private static String derivePackagePrefix(File file, SourceFolder parentFolder) {
String parentPackagePrefix = parentFolder.getPackagePrefix();
- logger.assertTrue(parentFolder.getFile() != null);
- String relativePath = VfsUtilCore.getRelativePath(file, parentFolder.getFile(), '.');
+ String parentPath = VirtualFileManager.extractPath(parentFolder.getUrl());
+ String relativePath =
+ FileUtil.toCanonicalPath(
+ FileUtil.getRelativePath(parentPath, file.getPath(), File.separatorChar));
if (Strings.isNullOrEmpty(relativePath)) {
return parentPackagePrefix;
}
- return parentPackagePrefix + "." + relativePath;
+
+ return parentPackagePrefix + "." + relativePath.replaceAll(File.separator, ".");
}
@VisibleForTesting
@@ -137,9 +124,9 @@
if (sourceDirectory.getIsResource()) {
sourceFolder =
contentEntry.addSourceFolder(
- pathToUrl(sourceDir.getPath()), JavaResourceRootType.RESOURCE);
+ UrlUtil.pathToUrl(sourceDir.getPath()), JavaResourceRootType.RESOURCE);
} else {
- sourceFolder = contentEntry.addSourceFolder(pathToUrl(sourceDir.getPath()), false);
+ sourceFolder = contentEntry.addSourceFolder(UrlUtil.pathToUrl(sourceDir.getPath()), false);
}
JpsModuleSourceRoot sourceRoot = sourceFolder.getJpsElement();
JpsElement properties = sourceRoot.getProperties();
@@ -155,22 +142,4 @@
}
return sourceFolder;
}
-
- private static String pathToUrl(String filePath) {
- filePath = FileUtil.toSystemIndependentName(filePath);
- if (filePath.endsWith(".srcjar") || filePath.endsWith(".jar")) {
- return URLUtil.JAR_PROTOCOL + URLUtil.SCHEME_SEPARATOR + filePath + URLUtil.JAR_SEPARATOR;
- } else if (filePath.contains("src.jar!")) {
- return URLUtil.JAR_PROTOCOL + URLUtil.SCHEME_SEPARATOR + filePath;
- } else {
- return VirtualFileManager.constructUrl(defaultFileSystem().getProtocol(), filePath);
- }
- }
-
- private static VirtualFileSystem defaultFileSystem() {
- if (ApplicationManager.getApplication().isUnitTestMode()) {
- return TempFileSystem.getInstance();
- }
- return LocalFileSystem.getInstance();
- }
}
diff --git a/java/src/com/google/idea/blaze/java/sync/source/FilePathJavaPackageReader.java b/java/src/com/google/idea/blaze/java/sync/source/FilePathJavaPackageReader.java
index 0c1265a..2879324 100644
--- a/java/src/com/google/idea/blaze/java/sync/source/FilePathJavaPackageReader.java
+++ b/java/src/com/google/idea/blaze/java/sync/source/FilePathJavaPackageReader.java
@@ -15,10 +15,12 @@
*/
package com.google.idea.blaze.java.sync.source;
+import com.google.common.base.Strings;
import com.google.idea.blaze.base.model.primitives.WorkspacePath;
import com.google.idea.blaze.base.scope.BlazeContext;
import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoder;
import com.google.idea.blaze.base.util.PackagePrefixCalculator;
+import java.io.File;
/** Gets the package from a java file by its file path alone (i.e. without opening the file). */
public final class FilePathJavaPackageReader extends JavaPackageReader {
@@ -27,11 +29,8 @@
BlazeContext context,
ArtifactLocationDecoder artifactLocationDecoder,
SourceArtifact sourceArtifact) {
- String directory = sourceArtifact.artifactLocation.getRelativePath();
- int i = directory.lastIndexOf('/');
- if (i >= 0) {
- directory = directory.substring(0, i);
- }
- return PackagePrefixCalculator.packagePrefixOf(new WorkspacePath(directory));
+ String parentPath = new File(sourceArtifact.artifactLocation.relativePath).getParent();
+ return PackagePrefixCalculator.packagePrefixOf(
+ new WorkspacePath(Strings.nullToEmpty(parentPath)));
}
}
diff --git a/java/src/com/google/idea/blaze/java/sync/source/JavaSourcePackageReader.java b/java/src/com/google/idea/blaze/java/sync/source/JavaSourcePackageReader.java
index f67f76c..64e1cb5 100644
--- a/java/src/com/google/idea/blaze/java/sync/source/JavaSourcePackageReader.java
+++ b/java/src/com/google/idea/blaze/java/sync/source/JavaSourcePackageReader.java
@@ -40,7 +40,7 @@
return ServiceManager.getService(JavaSourcePackageReader.class);
}
- private static final Logger LOG = Logger.getInstance(SourceDirectoryCalculator.class);
+ private static final Logger logger = Logger.getInstance(SourceDirectoryCalculator.class);
private static final Pattern JAVA_PACKAGE_PATTERN =
Pattern.compile("^\\s*package\\s+([\\w\\.]+);");
@@ -76,7 +76,7 @@
.submit(context);
return null;
} catch (IOException e) {
- LOG.error(e);
+ logger.error(e);
return null;
}
}
diff --git a/java/src/com/google/idea/blaze/java/sync/source/PackageManifestReader.java b/java/src/com/google/idea/blaze/java/sync/source/PackageManifestReader.java
index 6338e14..52fced0 100644
--- a/java/src/com/google/idea/blaze/java/sync/source/PackageManifestReader.java
+++ b/java/src/com/google/idea/blaze/java/sync/source/PackageManifestReader.java
@@ -44,7 +44,7 @@
/** Reads package manifests. */
public class PackageManifestReader {
- private static final Logger LOG = Logger.getInstance(SourceDirectoryCalculator.class);
+ private static final Logger logger = Logger.getInstance(SourceDirectoryCalculator.class);
public static PackageManifestReader getInstance() {
return ServiceManager.getService(PackageManifestReader.class);
@@ -105,7 +105,7 @@
try {
Futures.allAsList(futures).get();
} catch (ExecutionException | InterruptedException e) {
- LOG.error(e);
+ logger.error(e);
throw new IllegalStateException("Could not read sources");
}
return manifestMap;
@@ -131,7 +131,7 @@
}
return outputMap;
} catch (IOException e) {
- LOG.error(e);
+ logger.error(e);
return outputMap;
}
}
diff --git a/java/src/com/google/idea/blaze/java/sync/source/SourceDirectoryCalculator.java b/java/src/com/google/idea/blaze/java/sync/source/SourceDirectoryCalculator.java
index 133625e..e1e4203 100644
--- a/java/src/com/google/idea/blaze/java/sync/source/SourceDirectoryCalculator.java
+++ b/java/src/com/google/idea/blaze/java/sync/source/SourceDirectoryCalculator.java
@@ -63,7 +63,7 @@
*/
public final class SourceDirectoryCalculator {
- private static final Logger LOG = Logger.getInstance(SourceDirectoryCalculator.class);
+ private static final Logger logger = Logger.getInstance(SourceDirectoryCalculator.class);
private static final Splitter PACKAGE_SPLITTER = Splitter.on('.');
private static final Splitter PATH_SPLITTER = Splitter.on('/');
@@ -250,7 +250,7 @@
}
}
} catch (ExecutionException | InterruptedException e) {
- LOG.error(e);
+ logger.error(e);
throw new IllegalStateException("Could not read sources");
}
@@ -464,9 +464,8 @@
.submit(context);
return null;
}
- return new SourceRoot(
- new WorkspacePath(new File(sourceArtifact.artifactLocation.getRelativePath()).getParent()),
- declaredPackage);
+ String parentPath = new File(sourceArtifact.artifactLocation.relativePath).getParent();
+ return new SourceRoot(new WorkspacePath(Strings.nullToEmpty(parentPath)), declaredPackage);
}
static class SourceRoot {
diff --git a/java/tests/integrationtests/com/google/idea/blaze/java/lang/build/completion/JavaClassQualifiedNameCompletionTest.java b/java/tests/integrationtests/com/google/idea/blaze/java/lang/build/completion/JavaClassQualifiedNameCompletionTest.java
new file mode 100644
index 0000000..ceb0589
--- /dev/null
+++ b/java/tests/integrationtests/com/google/idea/blaze/java/lang/build/completion/JavaClassQualifiedNameCompletionTest.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2017 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.java.lang.build.completion;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.idea.blaze.base.lang.buildfile.BuildFileIntegrationTestCase;
+import com.google.idea.blaze.base.lang.buildfile.psi.BuildFile;
+import com.google.idea.blaze.base.model.primitives.WorkspacePath;
+import com.intellij.codeInsight.completion.CompletionType;
+import com.intellij.codeInsight.lookup.LookupElement;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.testFramework.fixtures.CompletionAutoPopupTester;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for code completion of funcall arguments. */
+@RunWith(JUnit4.class)
+public class JavaClassQualifiedNameCompletionTest extends BuildFileIntegrationTestCase {
+
+ private CompletionAutoPopupTester completionTester;
+
+ @Before
+ public final void before() {
+ completionTester = new CompletionAutoPopupTester(testFixture);
+ }
+
+ /** Completion UI testing can't be run on the EDT. */
+ @Override
+ protected boolean runTestsOnEdt() {
+ return false;
+ }
+
+ @Test
+ public void testCompleteClassName() {
+ completionTester.runWithAutoPopupEnabled(
+ () -> {
+ workspace.createPsiFile(
+ new WorkspacePath("java/com/google/bin/Main.java"),
+ "package com.google.bin;",
+ "public class Main {",
+ " public void main() {}",
+ "}");
+ BuildFile file =
+ createBuildFile(
+ new WorkspacePath("java/com/google/BUILD"),
+ "java_binary(",
+ " name = 'binary',",
+ " main_class = 'com.google.bin.M',",
+ ")");
+
+ Editor editor = editorTest.openFileInEditor(file.getVirtualFile());
+ editorTest.setCaretPosition(editor, 2, " main_class = 'com.google.bin.M".length());
+
+ testFixture.complete(CompletionType.CLASS_NAME);
+ assertFileContents(
+ file,
+ "java_binary(",
+ " name = 'binary',",
+ " main_class = 'com.google.bin.Main',",
+ ")");
+ });
+ }
+
+ @Test
+ public void testNoCompletionForOtherAttributes() {
+ completionTester.runWithAutoPopupEnabled(
+ () -> {
+ workspace.createPsiFile(
+ new WorkspacePath("java/com/google/bin/Main.java"),
+ "package com.google.bin;",
+ "public class Main {",
+ " public void main() {}",
+ "}");
+ BuildFile file =
+ createBuildFile(
+ new WorkspacePath("java/com/google/BUILD"),
+ "java_binary(",
+ " name = 'binary',",
+ " main_clazz = 'com.google.bin.M',",
+ ")");
+
+ Editor editor = editorTest.openFileInEditor(file.getVirtualFile());
+ editorTest.setCaretPosition(editor, 2, " main_clazz = 'com.google.bin.M".length());
+
+ LookupElement[] completionItems = testFixture.complete(CompletionType.CLASS_NAME);
+ assertThat(completionItems).isEmpty();
+
+ assertFileContents(
+ file,
+ "java_binary(",
+ " name = 'binary',",
+ " main_clazz = 'com.google.bin.M',",
+ ")");
+ });
+ }
+}
diff --git a/java/tests/integrationtests/com/google/idea/blaze/java/lang/build/references/JavaClassQualifiedNameReferenceTest.java b/java/tests/integrationtests/com/google/idea/blaze/java/lang/build/references/JavaClassQualifiedNameReferenceTest.java
new file mode 100644
index 0000000..7565fcc
--- /dev/null
+++ b/java/tests/integrationtests/com/google/idea/blaze/java/lang/build/references/JavaClassQualifiedNameReferenceTest.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2017 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.java.lang.build.references;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.idea.blaze.base.lang.buildfile.BuildFileIntegrationTestCase;
+import com.google.idea.blaze.base.lang.buildfile.psi.ArgumentList;
+import com.google.idea.blaze.base.lang.buildfile.psi.BuildFile;
+import com.google.idea.blaze.base.lang.buildfile.psi.FuncallExpression;
+import com.google.idea.blaze.base.model.primitives.WorkspacePath;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiClassOwner;
+import com.intellij.psi.PsiFile;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Integration tests for {@link JavaClassQualifiedNameReference}. */
+@RunWith(JUnit4.class)
+public class JavaClassQualifiedNameReferenceTest extends BuildFileIntegrationTestCase {
+
+ @Test
+ public void testReferencesJavaClass() {
+ PsiFile javaFile =
+ workspace.createPsiFile(
+ new WorkspacePath("java/com/google/bin/Main.java"),
+ "package com.google.bin;",
+ "public class Main {",
+ " public void main() {}",
+ "}");
+ PsiClass javaClass = ((PsiClassOwner) javaFile).getClasses()[0];
+
+ BuildFile file =
+ createBuildFile(
+ new WorkspacePath("java/com/google/BUILD"),
+ "java_binary(",
+ " name = 'binary',",
+ " main_class = 'com.google.bin.Main',",
+ ")");
+
+ ArgumentList args = file.firstChildOfClass(FuncallExpression.class).getArgList();
+ assertThat(args.getKeywordArgument("main_class").getValue().getReferencedElement())
+ .isEqualTo(javaClass);
+ }
+}
diff --git a/java/tests/integrationtests/com/google/idea/blaze/java/run/BlazeJavaTestEventsHandlerTest.java b/java/tests/integrationtests/com/google/idea/blaze/java/run/BlazeJavaTestEventsHandlerTest.java
new file mode 100644
index 0000000..ddde4ee
--- /dev/null
+++ b/java/tests/integrationtests/com/google/idea/blaze/java/run/BlazeJavaTestEventsHandlerTest.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2017 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.java.run;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.collect.Iterables;
+import com.google.idea.blaze.base.BlazeIntegrationTestCase;
+import com.google.idea.blaze.base.model.primitives.WorkspacePath;
+import com.intellij.execution.Location;
+import com.intellij.openapi.vfs.VirtualFileManager;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiClassOwner;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.search.GlobalSearchScope;
+import javax.annotation.Nullable;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Integration tests for {@link BlazeJavaTestEventsHandler}. */
+@RunWith(JUnit4.class)
+public class BlazeJavaTestEventsHandlerTest extends BlazeIntegrationTestCase {
+
+ private final BlazeJavaTestEventsHandler handler = new BlazeJavaTestEventsHandler();
+
+ @Test
+ public void testSuiteLocationResolves() {
+ PsiFile javaFile =
+ workspace.createPsiFile(
+ new WorkspacePath("java/com/google/lib/JavaClass.java"),
+ "package com.google.lib;",
+ "public class JavaClass {}");
+ PsiClass javaClass = ((PsiClassOwner) javaFile).getClasses()[0];
+ assertThat(javaClass).isNotNull();
+
+ String url = handler.suiteLocationUrl(null, "com.google.lib.JavaClass");
+ Location<?> location = getLocation(url);
+ assertThat(location.getPsiElement()).isEqualTo(javaClass);
+ }
+
+ @Test
+ public void testMethodLocationResolves() {
+ PsiFile javaFile =
+ workspace.createPsiFile(
+ new WorkspacePath("java/com/google/lib/JavaClass.java"),
+ "package com.google.lib;",
+ "public class JavaClass {",
+ " public void testMethod() {}",
+ "}");
+ PsiClass javaClass = ((PsiClassOwner) javaFile).getClasses()[0];
+ PsiMethod method = javaClass.findMethodsByName("testMethod", false)[0];
+ assertThat(method).isNotNull();
+
+ String url = handler.testLocationUrl(null, null, "testMethod", "com.google.lib.JavaClass");
+ Location<?> location = getLocation(url);
+ assertThat(location.getPsiElement()).isEqualTo(method);
+ }
+
+ @Test
+ public void testParameterizedMethodLocationResolves() {
+ PsiFile javaFile =
+ workspace.createPsiFile(
+ new WorkspacePath("java/com/google/lib/JavaClass.java"),
+ "package com.google.lib;",
+ "public class JavaClass {",
+ " public void testMethod() {}",
+ "}");
+ PsiClass javaClass = ((PsiClassOwner) javaFile).getClasses()[0];
+ PsiMethod method = javaClass.findMethodsByName("testMethod", false)[0];
+ assertThat(method).isNotNull();
+
+ String url =
+ handler.testLocationUrl(
+ null, "testMethod", "[0] true (testMethod)", "com.google.lib.JavaClass");
+ Location<?> location = getLocation(url);
+ assertThat(location.getPsiElement()).isEqualTo(method);
+ }
+
+ @Nullable
+ private Location<?> getLocation(String url) {
+ String protocol = VirtualFileManager.extractProtocol(url);
+ String path = VirtualFileManager.extractPath(url);
+ if (protocol == null) {
+ return null;
+ }
+ return Iterables.getFirst(
+ handler
+ .getTestLocator()
+ .getLocation(protocol, path, getProject(), GlobalSearchScope.allScope(getProject())),
+ null);
+ }
+}
diff --git a/java/tests/integrationtests/com/google/idea/blaze/java/run/producers/BlazeJUnitTestFilterFlagsIntegrationTest.java b/java/tests/integrationtests/com/google/idea/blaze/java/run/producers/BlazeJUnitTestFilterFlagsIntegrationTest.java
new file mode 100644
index 0000000..14159ac
--- /dev/null
+++ b/java/tests/integrationtests/com/google/idea/blaze/java/run/producers/BlazeJUnitTestFilterFlagsIntegrationTest.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2017 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.java.run.producers;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.idea.blaze.base.BlazeIntegrationTestCase;
+import com.google.idea.blaze.base.model.primitives.WorkspacePath;
+import com.intellij.execution.Location;
+import com.intellij.execution.PsiLocation;
+import com.intellij.execution.junit2.PsiMemberParameterizedLocation;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiClassOwner;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.PsiMethod;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Integration tests for {@link BlazeJUnitTestFilterFlags}. The functionality that relies on
+ * PsiElements, so can't go in the unit tests.
+ */
+@RunWith(JUnit4.class)
+public class BlazeJUnitTestFilterFlagsIntegrationTest extends BlazeIntegrationTestCase {
+
+ @Before
+ public final void doSetup() {
+ // required for IntelliJ to recognize annotations, JUnit version, etc.
+ workspace.createPsiFile(
+ new WorkspacePath("org/junit/runner/RunWith.java"),
+ "package org.junit.runner;"
+ + "public @interface RunWith {"
+ + " Class<? extends Runner> value();"
+ + "}");
+ workspace.createPsiFile(
+ new WorkspacePath("org/junit/Test"), "package org.junit;", "public @interface Test {}");
+ workspace.createPsiFile(
+ new WorkspacePath("org/junit/runners/JUnit4"),
+ "package org.junit.runners;",
+ "public class JUnit4 {}");
+ }
+
+ @Test
+ public void testParameterizedMethods() {
+ PsiFile javaFile =
+ workspace.createPsiFile(
+ new WorkspacePath("java/com/google/lib/JavaClass.java"),
+ "package com.google.lib;",
+ "import org.junit.Test;",
+ "import org.junit.runner.RunWith;",
+ "import org.junit.runners.JUnit4;",
+ "@RunWith(JUnit4.class)",
+ "public class JavaClass {",
+ " @Test",
+ " public void testMethod1() {}",
+ " @Test",
+ " public void testMethod2() {}",
+ "}");
+ PsiClass javaClass = ((PsiClassOwner) javaFile).getClasses()[0];
+
+ PsiMethod method1 = javaClass.findMethodsByName("testMethod1", false)[0];
+ Location<?> location1 =
+ new PsiMemberParameterizedLocation(getProject(), method1, javaClass, "[param]");
+
+ PsiMethod method2 = javaClass.findMethodsByName("testMethod2", false)[0];
+ Location<?> location2 =
+ new PsiMemberParameterizedLocation(getProject(), method2, javaClass, "[3]");
+
+ assertThat(
+ BlazeJUnitTestFilterFlags.testFilterForClassesAndMethods(
+ ImmutableMap.of(javaClass, ImmutableList.of(location1, location2))))
+ .isEqualTo("com.google.lib.JavaClass#(testMethod1\\[param\\]|testMethod2\\[3\\])$");
+ }
+
+ @Test
+ public void testMultipleClassesWithParameterizedMethods() {
+ PsiFile javaFile1 =
+ workspace.createPsiFile(
+ new WorkspacePath("java/com/google/lib/JavaClass1.java"),
+ "package com.google.lib;",
+ "import org.junit.Test;",
+ "import org.junit.runner.RunWith;",
+ "import org.junit.runners.JUnit4;",
+ "@RunWith(JUnit4.class)",
+ "public class JavaClass1 {",
+ " @Test",
+ " public void testMethod1() {}",
+ " @Test",
+ " public void testMethod2() {}",
+ "}");
+ PsiFile javaFile2 =
+ workspace.createPsiFile(
+ new WorkspacePath("java/com/google/lib/JavaClass2.java"),
+ "package com.google.lib;",
+ "import org.junit.Test;",
+ "import org.junit.runner.RunWith;",
+ "import org.junit.runners.JUnit4;",
+ "@RunWith(JUnit4.class)",
+ "public class JavaClass2 {",
+ " @Test",
+ " public void testMethod() {}",
+ "}");
+ PsiClass javaClass1 = ((PsiClassOwner) javaFile1).getClasses()[0];
+
+ PsiMethod class1Method1 = javaClass1.findMethodsByName("testMethod1", false)[0];
+ Location<?> class1Location1 =
+ new PsiMemberParameterizedLocation(getProject(), class1Method1, javaClass1, "[param]");
+
+ PsiMethod class1Method2 = javaClass1.findMethodsByName("testMethod2", false)[0];
+ Location<?> class1Location2 =
+ new PsiMemberParameterizedLocation(getProject(), class1Method2, javaClass1, "[3]");
+
+ PsiClass javaClass2 = ((PsiClassOwner) javaFile2).getClasses()[0];
+ PsiMethod class2Method = javaClass2.findMethodsByName("testMethod", false)[0];
+
+ assertThat(
+ BlazeJUnitTestFilterFlags.testFilterForClassesAndMethods(
+ ImmutableMap.of(
+ javaClass1,
+ ImmutableList.of(class1Location1, class1Location2),
+ javaClass2,
+ ImmutableList.of(new PsiLocation<>(class2Method)))))
+ .isEqualTo(
+ Joiner.on('|')
+ .join(
+ "com.google.lib.JavaClass1#(testMethod1\\[param\\]|testMethod2\\[3\\])$",
+ "com.google.lib.JavaClass2#testMethod$"));
+ }
+}
diff --git a/java/tests/integrationtests/com/google/idea/blaze/java/run/producers/BlazeJavaMainClassConfigurationProducerTest.java b/java/tests/integrationtests/com/google/idea/blaze/java/run/producers/BlazeJavaMainClassConfigurationProducerTest.java
new file mode 100644
index 0000000..6135b2f
--- /dev/null
+++ b/java/tests/integrationtests/com/google/idea/blaze/java/run/producers/BlazeJavaMainClassConfigurationProducerTest.java
@@ -0,0 +1,264 @@
+/*
+ * Copyright 2017 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.java.run.producers;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.collect.ImmutableList;
+import com.google.idea.blaze.base.BlazeIntegrationTestCase;
+import com.google.idea.blaze.base.EditorTestHelper;
+import com.google.idea.blaze.base.ideinfo.ArtifactLocation;
+import com.google.idea.blaze.base.ideinfo.JavaIdeInfo;
+import com.google.idea.blaze.base.ideinfo.TargetIdeInfo;
+import com.google.idea.blaze.base.ideinfo.TargetMap;
+import com.google.idea.blaze.base.ideinfo.TargetMapBuilder;
+import com.google.idea.blaze.base.model.MockBlazeProjectDataBuilder;
+import com.google.idea.blaze.base.model.MockBlazeProjectDataManager;
+import com.google.idea.blaze.base.model.primitives.ExecutionRootPath;
+import com.google.idea.blaze.base.model.primitives.TargetExpression;
+import com.google.idea.blaze.base.model.primitives.WorkspacePath;
+import com.google.idea.blaze.base.run.BlazeRunConfiguration;
+import com.google.idea.blaze.base.sync.SyncCache;
+import com.google.idea.blaze.base.sync.data.BlazeProjectDataManager;
+import com.google.idea.blaze.base.sync.workspace.BlazeRoots;
+import com.intellij.execution.Location;
+import com.intellij.execution.PsiLocation;
+import com.intellij.execution.RunnerAndConfigurationSettings;
+import com.intellij.execution.actions.ConfigurationContext;
+import com.intellij.execution.configurations.RunConfiguration;
+import com.intellij.openapi.actionSystem.CommonDataKeys;
+import com.intellij.openapi.actionSystem.LangDataKeys;
+import com.intellij.openapi.module.ModuleUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.PsiFile;
+import com.intellij.testFramework.MapDataContext;
+import java.io.File;
+import org.jetbrains.annotations.Nullable;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Integration tests for {@link BlazeJavaMainClassConfigurationProducer}. */
+@RunWith(JUnit4.class)
+public class BlazeJavaMainClassConfigurationProducerTest extends BlazeIntegrationTestCase {
+
+ private EditorTestHelper editorTest;
+
+ @Before
+ public final void doSetup() {
+ BlazeProjectDataManager mockProjectDataManager =
+ new MockBlazeProjectDataManager(getMockBlazeProjectDataBuilder().build());
+ registerProjectService(BlazeProjectDataManager.class, mockProjectDataManager);
+ editorTest = new EditorTestHelper(getProject(), testFixture);
+ }
+
+ @After
+ public final void doTearDown() {
+ SyncCache.getInstance(getProject()).clear();
+ }
+
+ @Test
+ public void testUniqueJavaBinaryChosen() {
+ setTargets(
+ TargetMapBuilder.builder()
+ .addTarget(
+ TargetIdeInfo.builder()
+ .setKind("java_binary")
+ .setLabel("//com/google/binary:UnrelatedName")
+ .addSource(sourceRoot("com/google/binary/MainClass.java"))
+ .build())
+ .build());
+
+ PsiFile javaClass =
+ workspace.createPsiFile(
+ WorkspacePath.createIfValid("com/google/binary/MainClass.java"),
+ "package com.google.binary;",
+ "import java.lang.String;",
+ "public class MainClass {",
+ " public static void main(String[] args) {}",
+ "}");
+
+ RunConfiguration config = createConfigurationFromLocation(javaClass);
+
+ assertThat(config).isInstanceOf(BlazeRunConfiguration.class);
+ BlazeRunConfiguration blazeConfig = (BlazeRunConfiguration) config;
+ assertThat(blazeConfig.getTarget())
+ .isEqualTo(TargetExpression.fromString("//com/google/binary:UnrelatedName"));
+ }
+
+ @Test
+ public void testNoJavaBinaryChosenIfNotInRDeps() {
+ setTargets(
+ TargetMapBuilder.builder()
+ .addTarget(
+ TargetIdeInfo.builder()
+ .setKind("java_binary")
+ .setLabel("//com/google/binary:MainClass")
+ .addSource(sourceRoot("com/google/binary/OtherClass.java"))
+ .build())
+ .build());
+
+ PsiFile javaClass =
+ workspace.createPsiFile(
+ WorkspacePath.createIfValid("com/google/binary/MainClass.java"),
+ "package com.google.binary;",
+ "import java.lang.String;",
+ "public class MainClass {",
+ " public static void main(String[] args) {}",
+ "}");
+
+ assertThat(createConfigurationFromLocation(javaClass))
+ .isNotInstanceOf(BlazeRunConfiguration.class);
+ }
+
+ @Test
+ public void testNoResultForClassWithoutMainMethod() {
+ setTargets(
+ TargetMapBuilder.builder()
+ .addTarget(
+ TargetIdeInfo.builder()
+ .setKind("java_binary")
+ .setLabel("//com/google/binary:MainClass")
+ .addSource(sourceRoot("com/google/binary/MainClass.java"))
+ .setJavaInfo(JavaIdeInfo.builder().setMainClass("com.google.binary.MainClass"))
+ .build())
+ .build());
+
+ PsiFile javaClass =
+ workspace.createPsiFile(
+ WorkspacePath.createIfValid("com/google/binary/MainClass.java"),
+ "package com.google.binary;",
+ "public class MainClass {}");
+
+ assertThat(createConfigurationFromLocation(javaClass)).isNull();
+ }
+
+ @Test
+ public void testJavaBinaryWithMatchingNameChosen() {
+ setTargets(
+ TargetMapBuilder.builder()
+ .addTarget(
+ TargetIdeInfo.builder()
+ .setKind("java_binary")
+ .setLabel("//com/google/binary:UnrelatedName")
+ .addSource(sourceRoot("com/google/binary/MainClass.java"))
+ .build())
+ .addTarget(
+ TargetIdeInfo.builder()
+ .setKind("java_binary")
+ .setLabel("//com/google/binary:MainClass")
+ .addSource(sourceRoot("com/google/binary/MainClass.java"))
+ .build())
+ .build());
+
+ PsiFile javaClass =
+ workspace.createPsiFile(
+ WorkspacePath.createIfValid("com/google/binary/MainClass.java"),
+ "package com.google.binary;",
+ "import java.lang.String;",
+ "public class MainClass {",
+ " public static void main(String[] args) {}",
+ "}");
+
+ RunConfiguration config = createConfigurationFromLocation(javaClass);
+ assertThat(config).isInstanceOf(BlazeRunConfiguration.class);
+ BlazeRunConfiguration blazeConfig = (BlazeRunConfiguration) config;
+ assertThat(blazeConfig.getTarget())
+ .isEqualTo(TargetExpression.fromString("//com/google/binary:MainClass"));
+ }
+
+ @Test
+ public void testJavaBinaryWithMatchingMainClassChosen() {
+ setTargets(
+ TargetMapBuilder.builder()
+ .addTarget(
+ TargetIdeInfo.builder()
+ .setKind("java_binary")
+ .setLabel("//com/google/binary:UnrelatedName")
+ .addSource(sourceRoot("com/google/binary/MainClass.java"))
+ .build())
+ .addTarget(
+ TargetIdeInfo.builder()
+ .setKind("java_binary")
+ .setLabel("//com/google/binary:OtherName")
+ .setJavaInfo(JavaIdeInfo.builder().setMainClass("com.google.binary.MainClass"))
+ .addSource(sourceRoot("com/google/binary/MainClass.java"))
+ .build())
+ .build());
+
+ PsiFile javaClass =
+ workspace.createPsiFile(
+ WorkspacePath.createIfValid("com/google/binary/MainClass.java"),
+ "package com.google.binary;",
+ "import java.lang.String;",
+ "public class MainClass {",
+ " public static void main(String[] args) {}",
+ "}");
+
+ RunConfiguration config = createConfigurationFromLocation(javaClass);
+
+ assertThat(config).isInstanceOf(BlazeRunConfiguration.class);
+ BlazeRunConfiguration blazeConfig = (BlazeRunConfiguration) config;
+ assertThat(blazeConfig.getTarget())
+ .isEqualTo(TargetExpression.fromString("//com/google/binary:OtherName"));
+ }
+
+ @Nullable
+ private RunConfiguration createConfigurationFromLocation(PsiFile psiFile) {
+ // a nauseating hack to force IntelliJ to recognize 'main' methods...
+ workspace.createPsiFile(
+ WorkspacePath.createIfValid("java/lang/String.java"),
+ "package java.lang;",
+ "public class String {}");
+ editorTest.openFileInEditor(psiFile);
+
+ final MapDataContext dataContext = new MapDataContext();
+
+ dataContext.put(CommonDataKeys.PROJECT, getProject());
+ dataContext.put(LangDataKeys.MODULE, ModuleUtil.findModuleForPsiElement(psiFile));
+ dataContext.put(Location.DATA_KEY, PsiLocation.fromPsiElement(psiFile));
+ RunnerAndConfigurationSettings settings =
+ ConfigurationContext.getFromContext(dataContext).getConfiguration();
+ return settings != null ? settings.getConfiguration() : null;
+ }
+
+ private MockBlazeProjectDataBuilder getMockBlazeProjectDataBuilder() {
+ String executionRootPath = "usr/local/_blaze_";
+ VirtualFile vf = fileSystem.createDirectory(executionRootPath);
+ BlazeRoots fakeRoots =
+ new BlazeRoots(
+ new File(vf.getPath()),
+ ImmutableList.of(workspaceRoot.directory()),
+ new ExecutionRootPath("out/crosstool/bin"),
+ new ExecutionRootPath("out/crosstool/gen"),
+ null);
+ return MockBlazeProjectDataBuilder.builder(workspaceRoot).setBlazeRoots(fakeRoots);
+ }
+
+ private void setTargets(TargetMap targets) {
+ BlazeProjectDataManager mockProjectDataManager =
+ new MockBlazeProjectDataManager(
+ getMockBlazeProjectDataBuilder().setTargetMap(targets).build());
+ registerProjectService(BlazeProjectDataManager.class, mockProjectDataManager);
+ SyncCache.getInstance(getProject()).clear();
+ }
+
+ private static ArtifactLocation sourceRoot(String relativePath) {
+ return ArtifactLocation.builder().setRelativePath(relativePath).setIsSource(true).build();
+ }
+}
diff --git a/java/tests/integrationtests/com/google/idea/blaze/java/sync/projectstructure/JavaSourceFolderProviderTest.java b/java/tests/integrationtests/com/google/idea/blaze/java/sync/projectstructure/JavaSourceFolderProviderTest.java
index 73e67a3..bd15bde 100644
--- a/java/tests/integrationtests/com/google/idea/blaze/java/sync/projectstructure/JavaSourceFolderProviderTest.java
+++ b/java/tests/integrationtests/com/google/idea/blaze/java/sync/projectstructure/JavaSourceFolderProviderTest.java
@@ -31,6 +31,7 @@
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.roots.SourceFolder;
import com.intellij.openapi.vfs.VirtualFile;
+import java.io.File;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -77,21 +78,21 @@
VirtualFile gen = workspace.createDirectory(new WorkspacePath("java/apps/gen"));
VirtualFile res = workspace.createDirectory(new WorkspacePath("java/apps/resources"));
- ImmutableMap<VirtualFile, SourceFolder> sourceFolders =
+ ImmutableMap<File, SourceFolder> sourceFolders =
provider.initializeSourceFolders(getContentEntry(root));
assertThat(sourceFolders).hasSize(3);
- SourceFolder rootSource = sourceFolders.get(root);
+ SourceFolder rootSource = sourceFolders.get(new File(root.getPath()));
assertThat(rootSource.getPackagePrefix()).isEqualTo("apps");
assertThat(JavaSourceFolderProvider.isGenerated(rootSource)).isFalse();
assertThat(JavaSourceFolderProvider.isResource(rootSource)).isFalse();
- SourceFolder genSource = sourceFolders.get(gen);
+ SourceFolder genSource = sourceFolders.get(new File(gen.getPath()));
assertThat(genSource.getPackagePrefix()).isEqualTo("apps.gen");
assertThat(JavaSourceFolderProvider.isGenerated(genSource)).isTrue();
assertThat(JavaSourceFolderProvider.isResource(genSource)).isFalse();
- SourceFolder resSource = sourceFolders.get(res);
+ SourceFolder resSource = sourceFolders.get(new File(res.getPath()));
assertThat(JavaSourceFolderProvider.isGenerated(resSource)).isFalse();
assertThat(JavaSourceFolderProvider.isResource(resSource)).isTrue();
@@ -99,7 +100,8 @@
sourceFolders = provider.initializeSourceFolders(getContentEntry(testRoot));
assertThat(sourceFolders).hasSize(1);
- assertThat(sourceFolders.get(testRoot).getPackagePrefix()).isEqualTo("apps.example");
+ assertThat(sourceFolders.get(new File(testRoot.getPath())).getPackagePrefix())
+ .isEqualTo("apps.example");
}
@Test
@@ -123,16 +125,19 @@
VirtualFile root = workspace.createDirectory(new WorkspacePath("java/apps"));
ContentEntry contentEntry = getContentEntry(root);
- ImmutableMap<VirtualFile, SourceFolder> sourceFolders =
- provider.initializeSourceFolders(contentEntry);
+ ImmutableMap<File, SourceFolder> sourceFolders = provider.initializeSourceFolders(contentEntry);
assertThat(sourceFolders).hasSize(1);
- VirtualFile testRoot = workspace.createDirectory(new WorkspacePath("java/apps/tests"));
+ VirtualFile testRoot = workspace.createDirectory(new WorkspacePath("java/apps/tests/model"));
SourceFolder testSourceChild =
- provider.setSourceFolderForLocation(contentEntry, sourceFolders.get(root), testRoot, true);
+ provider.setSourceFolderForLocation(
+ contentEntry,
+ sourceFolders.get(new File(root.getPath())),
+ new File(testRoot.getPath()),
+ true);
assertThat(testSourceChild.isTestSource()).isTrue();
- assertThat(testSourceChild.getPackagePrefix()).isEqualTo("apps.tests");
+ assertThat(testSourceChild.getPackagePrefix()).isEqualTo("apps.tests.model");
}
private ContentEntry getContentEntry(VirtualFile root) {
diff --git a/java/tests/unittests/com/google/idea/blaze/java/run/BlazeJavaRunProfileStateTest.java b/java/tests/unittests/com/google/idea/blaze/java/run/BlazeJavaRunProfileStateTest.java
index 10e43eb..622105e 100644
--- a/java/tests/unittests/com/google/idea/blaze/java/run/BlazeJavaRunProfileStateTest.java
+++ b/java/tests/unittests/com/google/idea/blaze/java/run/BlazeJavaRunProfileStateTest.java
@@ -102,6 +102,7 @@
BlazeFlags.getToolTagFlag(),
"--flag1",
"--flag2",
+ "--test_output=streamed",
"--",
"//label:rule"));
}
diff --git a/java/tests/unittests/com/google/idea/blaze/java/run/producers/BlazeJUnitTestFilterFlagsTest.java b/java/tests/unittests/com/google/idea/blaze/java/run/producers/BlazeJUnitTestFilterFlagsTest.java
index d5d2cde..2f5917c 100644
--- a/java/tests/unittests/com/google/idea/blaze/java/run/producers/BlazeJUnitTestFilterFlagsTest.java
+++ b/java/tests/unittests/com/google/idea/blaze/java/run/producers/BlazeJUnitTestFilterFlagsTest.java
@@ -42,7 +42,7 @@
public void testSingleJUnit4ClassFilter() {
assertThat(
BlazeJUnitTestFilterFlags.testFilterForClassAndMethods(
- "com.google.idea.ClassName", ImmutableList.of(), JUnitVersion.JUNIT_4, false))
+ "com.google.idea.ClassName", JUnitVersion.JUNIT_4, ImmutableList.of()))
.isEqualTo("com.google.idea.ClassName#");
}
@@ -50,7 +50,7 @@
public void testSingleJUnit3ClassFilter() {
assertThat(
BlazeJUnitTestFilterFlags.testFilterForClassAndMethods(
- "com.google.idea.ClassName", ImmutableList.of(), JUnitVersion.JUNIT_3, false))
+ "com.google.idea.ClassName", JUnitVersion.JUNIT_3, ImmutableList.of()))
.isEqualTo("com.google.idea.ClassName");
}
@@ -58,7 +58,7 @@
public void testParameterizedIgnoredForSingleClass() {
assertThat(
BlazeJUnitTestFilterFlags.testFilterForClassAndMethods(
- "com.google.idea.ClassName", ImmutableList.of(), JUnitVersion.JUNIT_4, true))
+ "com.google.idea.ClassName", JUnitVersion.JUNIT_4, ImmutableList.of()))
.isEqualTo("com.google.idea.ClassName#");
}
@@ -66,10 +66,7 @@
public void testJUnit4ClassAndSingleMethod() {
assertThat(
BlazeJUnitTestFilterFlags.testFilterForClassAndMethods(
- "com.google.idea.ClassName",
- ImmutableList.of("testMethod1"),
- JUnitVersion.JUNIT_4,
- false))
+ "com.google.idea.ClassName", JUnitVersion.JUNIT_4, ImmutableList.of("testMethod1")))
.isEqualTo("com.google.idea.ClassName#testMethod1$");
}
@@ -77,10 +74,7 @@
public void testJUnit3ClassAndSingleMethod() {
assertThat(
BlazeJUnitTestFilterFlags.testFilterForClassAndMethods(
- "com.google.idea.ClassName",
- ImmutableList.of("testMethod1"),
- JUnitVersion.JUNIT_3,
- false))
+ "com.google.idea.ClassName", JUnitVersion.JUNIT_3, ImmutableList.of("testMethod1")))
.isEqualTo("com.google.idea.ClassName#testMethod1");
}
@@ -89,9 +83,8 @@
assertThat(
BlazeJUnitTestFilterFlags.testFilterForClassAndMethods(
"com.google.idea.ClassName",
- ImmutableList.of("testMethod1", "testMethod2"),
JUnitVersion.JUNIT_4,
- false))
+ ImmutableList.of("testMethod1", "testMethod2")))
.isEqualTo("com.google.idea.ClassName#(testMethod1|testMethod2)$");
}
@@ -100,10 +93,9 @@
assertThat(
BlazeJUnitTestFilterFlags.testFilterForClassAndMethods(
"com.google.idea.ClassName",
- ImmutableList.of("testMethod1", "testMethod2"),
JUnitVersion.JUNIT_4,
- true))
- .isEqualTo("com.google.idea.ClassName#(testMethod1|testMethod2)(\\[.+\\])?$");
+ ImmutableList.of("testMethod1(\\[.+\\])?", "testMethod2(\\[.+\\])?")))
+ .isEqualTo("com.google.idea.ClassName#(testMethod1(\\[.+\\])?|testMethod2(\\[.+\\])?)$");
}
@Test
@@ -111,20 +103,8 @@
assertThat(
BlazeJUnitTestFilterFlags.testFilterForClassAndMethods(
"com.google.idea.ClassName",
- ImmutableList.of("testMethod1", "testMethod2"),
JUnitVersion.JUNIT_3,
- false))
- .isEqualTo("com.google.idea.ClassName#testMethod1,testMethod2");
- }
-
- @Test
- public void testParameterizedIgnoredForJUnit3() {
- assertThat(
- BlazeJUnitTestFilterFlags.testFilterForClassAndMethods(
- "com.google.idea.ClassName",
- ImmutableList.of("testMethod1", "testMethod2"),
- JUnitVersion.JUNIT_3,
- true))
+ ImmutableList.of("testMethod1", "testMethod2")))
.isEqualTo("com.google.idea.ClassName#testMethod1,testMethod2");
}
}
diff --git a/java/tests/unittests/com/google/idea/blaze/java/sync/source/SourceDirectoryCalculatorTest.java b/java/tests/unittests/com/google/idea/blaze/java/sync/source/SourceDirectoryCalculatorTest.java
index cd5b051..25fac98 100644
--- a/java/tests/unittests/com/google/idea/blaze/java/sync/source/SourceDirectoryCalculatorTest.java
+++ b/java/tests/unittests/com/google/idea/blaze/java/sync/source/SourceDirectoryCalculatorTest.java
@@ -159,6 +159,33 @@
}
@Test
+ public void testHandlesSourceAtProjectRoot() throws Exception {
+ mockInputStreamProvider.addFile("/root/Bla.java", "package com.google;\n public class Bla {}");
+ List<SourceArtifact> sourceArtifacts =
+ ImmutableList.of(
+ SourceArtifact.builder(TargetKey.forPlainTarget(LABEL))
+ .setArtifactLocation(
+ ArtifactLocation.builder().setRelativePath("Bla.java").setIsSource(true))
+ .build());
+ ImmutableList<BlazeContentEntry> result =
+ sourceDirectoryCalculator.calculateContentEntries(
+ project,
+ context,
+ workspaceRoot,
+ decoder,
+ ImmutableList.of(new WorkspacePath("")),
+ sourceArtifacts,
+ NO_MANIFESTS);
+ assertThat(result)
+ .containsExactly(
+ BlazeContentEntry.builder("/root")
+ .addSource(
+ BlazeSourceDirectory.builder("/root").setPackagePrefix("com.google").build())
+ .build());
+ issues.assertNoIssues();
+ }
+
+ @Test
public void testSourcesToSourceDirectories_testReturnsTest() throws Exception {
mockInputStreamProvider.addFile(
"/root/java/com/google/Bla.java", "package com.google;\n public class Bla {}");
diff --git a/plugin_dev/src/com/google/idea/blaze/plugin/IntellijPluginRule.java b/plugin_dev/src/com/google/idea/blaze/plugin/IntellijPluginRule.java
index c669c8f..3947b69 100644
--- a/plugin_dev/src/com/google/idea/blaze/plugin/IntellijPluginRule.java
+++ b/plugin_dev/src/com/google/idea/blaze/plugin/IntellijPluginRule.java
@@ -25,7 +25,13 @@
public static final String TARGET_TAG_IJ_PLUGIN_BUNDLE = "intellij-plugin-bundle";
public static boolean isPluginTarget(TargetIdeInfo target) {
- return isPluginBundle(target) || isSinglePluginTarget(target);
+ return isIntellijPluginDebugTarget(target)
+ || isPluginBundle(target)
+ || isSinglePluginTarget(target);
+ }
+
+ public static boolean isIntellijPluginDebugTarget(TargetIdeInfo target) {
+ return target.intellijPluginDeployInfo != null;
}
public static boolean isPluginBundle(TargetIdeInfo target) {
@@ -36,5 +42,4 @@
public static boolean isSinglePluginTarget(TargetIdeInfo target) {
return target.kindIsOneOf(Kind.JAVA_IMPORT) && target.tags.contains(TARGET_TAG_IJ_PLUGIN);
}
-
}
diff --git a/plugin_dev/src/com/google/idea/blaze/plugin/run/BlazeIntellijPluginConfiguration.java b/plugin_dev/src/com/google/idea/blaze/plugin/run/BlazeIntellijPluginConfiguration.java
index a68f89b..528cac4 100644
--- a/plugin_dev/src/com/google/idea/blaze/plugin/run/BlazeIntellijPluginConfiguration.java
+++ b/plugin_dev/src/com/google/idea/blaze/plugin/run/BlazeIntellijPluginConfiguration.java
@@ -18,18 +18,12 @@
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Joiner;
import com.google.common.base.Strings;
-import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Ordering;
import com.google.idea.blaze.base.command.BlazeCommand;
import com.google.idea.blaze.base.command.BlazeCommandName;
import com.google.idea.blaze.base.command.BlazeFlags;
-import com.google.idea.blaze.base.ideinfo.Dependency;
-import com.google.idea.blaze.base.ideinfo.Dependency.DependencyType;
-import com.google.idea.blaze.base.ideinfo.JavaIdeInfo;
-import com.google.idea.blaze.base.ideinfo.LibraryArtifact;
import com.google.idea.blaze.base.ideinfo.TargetIdeInfo;
-import com.google.idea.blaze.base.model.BlazeProjectData;
import com.google.idea.blaze.base.model.primitives.Label;
import com.google.idea.blaze.base.model.primitives.TargetExpression;
import com.google.idea.blaze.base.projectview.ProjectViewSet;
@@ -39,8 +33,6 @@
import com.google.idea.blaze.base.run.state.RunConfigurationStateEditor;
import com.google.idea.blaze.base.run.targetfinder.TargetFinder;
import com.google.idea.blaze.base.settings.Blaze;
-import com.google.idea.blaze.base.sync.data.BlazeProjectDataManager;
-import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoder;
import com.google.idea.blaze.base.ui.UiUtil;
import com.google.idea.blaze.plugin.IntellijPluginRule;
import com.intellij.execution.ExecutionException;
@@ -59,8 +51,6 @@
import com.intellij.execution.process.ProcessAdapter;
import com.intellij.execution.process.ProcessEvent;
import com.intellij.execution.runners.ExecutionEnvironment;
-import com.intellij.ide.plugins.IdeaPluginDescriptor;
-import com.intellij.ide.plugins.PluginManagerCore;
import com.intellij.openapi.application.JetBrainsProtocolHandler;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.options.ConfigurationException;
@@ -73,7 +63,6 @@
import com.intellij.openapi.roots.ui.configuration.projectRoot.ProjectSdksModel;
import com.intellij.openapi.ui.ComboBox;
import com.intellij.openapi.ui.LabeledComponent;
-import com.intellij.openapi.util.BuildNumber;
import com.intellij.openapi.util.InvalidDataException;
import com.intellij.openapi.util.WriteExternalException;
import com.intellij.ui.ListCellRendererWrapper;
@@ -83,11 +72,8 @@
import java.awt.BorderLayout;
import java.io.File;
import java.io.IOException;
-import java.nio.file.Files;
import java.nio.file.Paths;
-import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.List;
import javax.annotation.Nullable;
import javax.swing.DefaultComboBoxModel;
@@ -181,56 +167,6 @@
return result;
}
- private ImmutableList<File> findPluginJars() throws ExecutionException {
- BlazeProjectData blazeProjectData =
- BlazeProjectDataManager.getInstance(getProject()).getBlazeProjectData();
- if (blazeProjectData == null) {
- throw new ExecutionException("Not synced yet, please sync project");
- }
- TargetIdeInfo target = TargetFinder.getInstance().targetForLabel(getProject(), getTarget());
- if (target == null) {
- throw new ExecutionException(
- buildSystem + " target '" + getTarget() + "' not imported during sync");
- }
- return IntellijPluginRule.isPluginBundle(target)
- ? findBundledJars(blazeProjectData.artifactLocationDecoder, target)
- : ImmutableList.of(findPluginJar(blazeProjectData.artifactLocationDecoder, target));
- }
-
- private ImmutableList<File> findBundledJars(
- ArtifactLocationDecoder artifactLocationDecoder, TargetIdeInfo target)
- throws ExecutionException {
- ImmutableList.Builder<File> jars = ImmutableList.builder();
- for (Dependency dep : target.dependencies) {
- if (dep.dependencyType == DependencyType.COMPILE_TIME && dep.targetKey.isPlainTarget()) {
- TargetIdeInfo depTarget =
- TargetFinder.getInstance().targetForLabel(getProject(), dep.targetKey.label);
- if (depTarget != null && IntellijPluginRule.isSinglePluginTarget(depTarget)) {
- jars.add(findPluginJar(artifactLocationDecoder, depTarget));
- }
- }
- }
- return jars.build();
- }
-
- private File findPluginJar(ArtifactLocationDecoder artifactLocationDecoder, TargetIdeInfo target)
- throws ExecutionException {
- JavaIdeInfo javaIdeInfo = target.javaIdeInfo;
- if (!IntellijPluginRule.isSinglePluginTarget(target) || javaIdeInfo == null) {
- throw new ExecutionException(
- buildSystem + " target '" + target + "' is not a valid intellij_plugin target");
- }
- Collection<LibraryArtifact> jars = javaIdeInfo.jars;
- if (javaIdeInfo.jars.size() > 1) {
- throw new ExecutionException("Invalid IntelliJ plugin target: it has multiple output jars");
- }
- LibraryArtifact artifact = jars.isEmpty() ? null : jars.iterator().next();
- if (artifact == null || artifact.classJar == null) {
- throw new ExecutionException("No output plugin jar found for '" + target + "'");
- }
- return artifactLocationDecoder.decode(artifact.classJar);
- }
-
/**
* Plugin jar has been previously created via blaze build. This method: - copies jar to sandbox
* environment - cracks open jar and finds plugin.xml (with ID, etc., needed for JVM args) - sets
@@ -254,15 +190,11 @@
} catch (IOException e) {
throw new ExecutionException("No sandbox specified for IntelliJ Platform Plugin SDK");
}
- final String canonicalSandbox = sandboxHome;
- final ImmutableList<File> pluginJars = findPluginJars();
- for (File file : pluginJars) {
- if (!file.exists()) {
- throw new ExecutionException(
- String.format(
- "Plugin jar '%s' not found. Did the %s build fail?", file.getName(), buildSystem));
- }
- }
+ String buildNumber = IdeaJdkHelper.getBuildNumber(ideaJdk);
+ final BlazeIntellijPluginDeployer deployer =
+ new BlazeIntellijPluginDeployer(getProject(), sandboxHome, buildNumber);
+ deployer.addTarget(getTarget());
+
// copy license from running instance of idea
IdeaJdkHelper.copyIDEALicense(sandboxHome);
@@ -270,11 +202,7 @@
new JavaCommandLineState(env) {
@Override
protected JavaParameters createJavaParameters() throws ExecutionException {
- String buildNumber = IdeaJdkHelper.getBuildNumber(ideaJdk);
- List<String> pluginIds = Lists.newArrayList();
- for (File jar : pluginJars) {
- pluginIds.add(copyPluginJarToSandbox(jar, buildNumber, canonicalSandbox));
- }
+ List<String> pluginIds = deployer.deploy();
final JavaParameters params = new JavaParameters();
@@ -304,9 +232,7 @@
new ProcessAdapter() {
@Override
public void processTerminated(ProcessEvent event) {
- for (File jar : pluginJars) {
- pluginDestination(jar, canonicalSandbox).delete();
- }
+ deployer.deleteDeployment();
}
});
return handler;
@@ -315,31 +241,6 @@
return state;
}
- private static File pluginDestination(File jar, String sandboxPath) {
- return new File(sandboxPath, "plugins/" + jar.getName());
- }
-
- /** Copies the plugin jar to the sandbox, and returns the plugin ID. */
- private static String copyPluginJarToSandbox(File jar, String buildNumber, String sandboxPath)
- throws ExecutionException {
- IdeaPluginDescriptor pluginDescriptor = PluginManagerCore.loadDescriptor(jar, "plugin.xml");
- if (PluginManagerCore.isIncompatible(pluginDescriptor, BuildNumber.fromString(buildNumber))) {
- throw new ExecutionException(
- String.format(
- "Plugin SDK version '%s' is incompatible with this plugin "
- + "(since: '%s', until: '%s')",
- buildNumber, pluginDescriptor.getSinceBuild(), pluginDescriptor.getUntilBuild()));
- }
- File pluginJarDestination = pluginDestination(jar, sandboxPath);
- try {
- pluginJarDestination.getParentFile().mkdirs();
- Files.copy(jar.toPath(), pluginJarDestination.toPath(), StandardCopyOption.REPLACE_EXISTING);
- } catch (IOException e) {
- throw new ExecutionException("Error copying plugin jar to sandbox", e);
- }
- return pluginDescriptor.getPluginId().getIdString();
- }
-
private static void fillParameterList(ParametersList list, @Nullable String value) {
if (value == null) {
return;
diff --git a/plugin_dev/src/com/google/idea/blaze/plugin/run/BlazeIntellijPluginDeployer.java b/plugin_dev/src/com/google/idea/blaze/plugin/run/BlazeIntellijPluginDeployer.java
new file mode 100644
index 0000000..c06caf2
--- /dev/null
+++ b/plugin_dev/src/com/google/idea/blaze/plugin/run/BlazeIntellijPluginDeployer.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright 2016 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.plugin.run;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.idea.blaze.base.ideinfo.Dependency;
+import com.google.idea.blaze.base.ideinfo.Dependency.DependencyType;
+import com.google.idea.blaze.base.ideinfo.IntellijPluginDeployInfo;
+import com.google.idea.blaze.base.ideinfo.IntellijPluginDeployInfo.IntellijPluginDeployFile;
+import com.google.idea.blaze.base.ideinfo.JavaIdeInfo;
+import com.google.idea.blaze.base.ideinfo.LibraryArtifact;
+import com.google.idea.blaze.base.ideinfo.TargetIdeInfo;
+import com.google.idea.blaze.base.ideinfo.TargetKey;
+import com.google.idea.blaze.base.ideinfo.TargetMap;
+import com.google.idea.blaze.base.model.BlazeProjectData;
+import com.google.idea.blaze.base.model.primitives.Label;
+import com.google.idea.blaze.base.sync.data.BlazeProjectDataManager;
+import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoder;
+import com.google.idea.blaze.plugin.IntellijPluginRule;
+import com.intellij.execution.ExecutionException;
+import com.intellij.ide.plugins.IdeaPluginDescriptor;
+import com.intellij.ide.plugins.PluginManagerCore;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.BuildNumber;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.StandardCopyOption;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import org.jetbrains.annotations.Nullable;
+
+/** Handles finding files to deploy and copying these into the sandbox. */
+class BlazeIntellijPluginDeployer {
+ private final String sandboxHome;
+ private final String buildNumber;
+ private final TargetMap targetMap;
+ private final ArtifactLocationDecoder artifactLocationDecoder;
+ private Map<File, File> filesToDeploy = Maps.newHashMap();
+
+ BlazeIntellijPluginDeployer(Project project, String sandboxHome, String buildNumber)
+ throws ExecutionException {
+ BlazeProjectData blazeProjectData =
+ BlazeProjectDataManager.getInstance(project).getBlazeProjectData();
+ if (blazeProjectData == null) {
+ throw new ExecutionException("Not synced yet, please sync project");
+ }
+ this.sandboxHome = sandboxHome;
+ this.buildNumber = buildNumber;
+ this.targetMap = blazeProjectData.targetMap;
+ this.artifactLocationDecoder = blazeProjectData.artifactLocationDecoder;
+ }
+
+ /** Adds an intellij plugin target to deploy */
+ void addTarget(Label label) throws ExecutionException {
+ ImmutableList<IntellijPluginDeployInfo> deployInfos = findDeployInfo(label);
+ ImmutableMap<File, File> filesToDeploy = getFilesToDeploy(deployInfos);
+ this.filesToDeploy.putAll(filesToDeploy);
+ }
+
+ List<String> deploy() throws ExecutionException {
+ for (File file : filesToDeploy.keySet()) {
+ if (!file.exists()) {
+ throw new ExecutionException(
+ String.format("Plugin file '%s' not found. Did the build fail?", file.getName()));
+ }
+ }
+ List<String> pluginIds = readPluginIds(filesToDeploy.keySet());
+ for (Map.Entry<File, File> entry : filesToDeploy.entrySet()) {
+ copyFileToSandbox(entry.getKey(), entry.getValue());
+ }
+ return pluginIds;
+ }
+
+ void deleteDeployment() {
+ for (File file : filesToDeploy.values()) {
+ if (file.exists()) {
+ file.delete();
+ }
+ }
+ }
+
+ private ImmutableList<IntellijPluginDeployInfo> findDeployInfo(Label label)
+ throws ExecutionException {
+ TargetIdeInfo target = targetMap.get(TargetKey.forPlainTarget(label));
+ if (target == null) {
+ throw new ExecutionException("Target '" + label + "' not imported during sync");
+ }
+ if (IntellijPluginRule.isIntellijPluginDebugTarget(target)) {
+ assert target.intellijPluginDeployInfo != null;
+ return ImmutableList.of(target.intellijPluginDeployInfo);
+ } else if (IntellijPluginRule.isSinglePluginTarget(target)) {
+ return ImmutableList.of(deployInfoForIntellijPlugin(target));
+ } else if (IntellijPluginRule.isPluginBundle(target)) {
+ return deployInfoForLegacyBundle(target);
+ }
+ throw new ExecutionException("Target is not a supported intellij plugin type.");
+ }
+
+ private ImmutableList<IntellijPluginDeployInfo> deployInfoForLegacyBundle(TargetIdeInfo target)
+ throws ExecutionException {
+ ImmutableList.Builder<IntellijPluginDeployInfo> deployInfoBuilder = ImmutableList.builder();
+ for (Dependency dep : target.dependencies) {
+ if (dep.dependencyType == DependencyType.COMPILE_TIME && dep.targetKey.isPlainTarget()) {
+ TargetIdeInfo depTarget = targetMap.get(dep.targetKey);
+ if (depTarget != null && IntellijPluginRule.isSinglePluginTarget(depTarget)) {
+ deployInfoBuilder.add(deployInfoForIntellijPlugin(depTarget));
+ }
+ }
+ }
+ return deployInfoBuilder.build();
+ }
+
+ private static IntellijPluginDeployInfo deployInfoForIntellijPlugin(TargetIdeInfo target)
+ throws ExecutionException {
+ JavaIdeInfo javaIdeInfo = target.javaIdeInfo;
+ if (!IntellijPluginRule.isSinglePluginTarget(target) || javaIdeInfo == null) {
+ throw new ExecutionException("Target '" + target + "' is not a valid intellij_plugin target");
+ }
+ Collection<LibraryArtifact> jars = javaIdeInfo.jars;
+ if (javaIdeInfo.jars.size() > 1) {
+ throw new ExecutionException("Invalid IntelliJ plugin target: it has multiple output jars");
+ }
+ LibraryArtifact artifact = jars.isEmpty() ? null : jars.iterator().next();
+ if (artifact == null || artifact.classJar == null) {
+ throw new ExecutionException("No output plugin jar found for '" + target + "'");
+ }
+ IntellijPluginDeployFile deployFile =
+ new IntellijPluginDeployFile(
+ artifact.classJar, new File(artifact.classJar.relativePath).getName());
+ return new IntellijPluginDeployInfo(ImmutableList.of(deployFile));
+ }
+
+ private ImmutableMap<File, File> getFilesToDeploy(
+ Collection<IntellijPluginDeployInfo> deployInfos) {
+ ImmutableMap.Builder<File, File> result = ImmutableMap.builder();
+ for (IntellijPluginDeployInfo deployInfo : deployInfos) {
+ for (IntellijPluginDeployFile deployFile : deployInfo.deployFiles) {
+ File src = artifactLocationDecoder.decode(deployFile.src);
+ File dest = new File(sandboxPluginDirectory(sandboxHome), deployFile.deployLocation);
+ result.put(src, dest);
+ }
+ }
+ return result.build();
+ }
+
+ private static File sandboxPluginDirectory(String sandboxHome) {
+ return new File(sandboxHome, "plugins");
+ }
+
+ private List<String> readPluginIds(Collection<File> files) throws ExecutionException {
+ List<String> pluginIds = Lists.newArrayList();
+ for (File file : files) {
+ if (file.getName().endsWith(".jar")) {
+ String pluginId = readPluginIdFromJar(buildNumber, file);
+ if (pluginId != null) {
+ pluginIds.add(pluginId);
+ }
+ }
+ }
+ return pluginIds;
+ }
+
+ @Nullable
+ private static String readPluginIdFromJar(String buildNumber, File jar)
+ throws ExecutionException {
+ IdeaPluginDescriptor pluginDescriptor = PluginManagerCore.loadDescriptor(jar, "plugin.xml");
+ if (pluginDescriptor == null) {
+ return null;
+ }
+ if (PluginManagerCore.isIncompatible(pluginDescriptor, BuildNumber.fromString(buildNumber))) {
+ throw new ExecutionException(
+ String.format(
+ "Plugin SDK version '%s' is incompatible with this plugin "
+ + "(since: '%s', until: '%s')",
+ buildNumber, pluginDescriptor.getSinceBuild(), pluginDescriptor.getUntilBuild()));
+ }
+ return pluginDescriptor.getPluginId().getIdString();
+ }
+
+ private static void copyFileToSandbox(File src, File dest) throws ExecutionException {
+ try {
+ dest.getParentFile().mkdirs();
+ Files.copy(src.toPath(), dest.toPath(), StandardCopyOption.REPLACE_EXISTING);
+ } catch (IOException e) {
+ throw new ExecutionException("Error copying plugin file to sandbox", e);
+ }
+ }
+}
diff --git a/plugin_dev/src/com/google/idea/blaze/plugin/run/BuildPluginBeforeRunTaskProvider.java b/plugin_dev/src/com/google/idea/blaze/plugin/run/BuildPluginBeforeRunTaskProvider.java
index de99b7a..2a9d071 100644
--- a/plugin_dev/src/com/google/idea/blaze/plugin/run/BuildPluginBeforeRunTaskProvider.java
+++ b/plugin_dev/src/com/google/idea/blaze/plugin/run/BuildPluginBeforeRunTaskProvider.java
@@ -24,7 +24,6 @@
import com.google.idea.blaze.base.experiments.ExperimentScope;
import com.google.idea.blaze.base.filecache.FileCaches;
import com.google.idea.blaze.base.issueparser.IssueOutputLineProcessor;
-import com.google.idea.blaze.base.metrics.Action;
import com.google.idea.blaze.base.model.primitives.WorkspaceRoot;
import com.google.idea.blaze.base.projectview.ProjectViewManager;
import com.google.idea.blaze.base.projectview.ProjectViewSet;
@@ -35,7 +34,6 @@
import com.google.idea.blaze.base.scope.scopes.BlazeConsoleScope;
import com.google.idea.blaze.base.scope.scopes.IdeaLogScope;
import com.google.idea.blaze.base.scope.scopes.IssuesScope;
-import com.google.idea.blaze.base.scope.scopes.LoggedTimingScope;
import com.google.idea.blaze.base.settings.Blaze;
import com.google.idea.blaze.base.settings.BlazeUserSettings;
import com.google.idea.blaze.base.util.SaveUtil;
@@ -177,7 +175,7 @@
LineProcessingOutputStream.of(
new IssueOutputLineProcessor(project, context, workspaceRoot)))
.build()
- .run(new LoggedTimingScope(project, Action.BLAZE_BUILD));
+ .run();
if (retVal != 0) {
context.setHasError();
}
diff --git a/proto_deps/proto_deps.jar b/proto_deps/proto_deps.jar
index 6f79519..ac441be 100755
--- a/proto_deps/proto_deps.jar
+++ b/proto_deps/proto_deps.jar
Binary files differ
diff --git a/version.bzl b/version.bzl
index 1bcca22..85514a2 100644
--- a/version.bzl
+++ b/version.bzl
@@ -1,3 +1,3 @@
"""Version of the blaze plugin."""
-VERSION = "2017.01.09.1"
+VERSION = "2017.01.30.4"