Import of bazel plugin using copybara
PiperOrigin-RevId: 145021219
diff --git a/BUILD b/BUILD
index f87b1dc..99282bc 100644
--- a/BUILD
+++ b/BUILD
@@ -10,6 +10,7 @@
tests = [
"//base:integration_tests",
"//base:unit_tests",
+ "//golang:unit_tests",
"//ijwb:integration_tests",
"//ijwb:unit_tests",
"//java:integration_tests",
diff --git a/WORKSPACE b/WORKSPACE
index edb340b..bad62f5 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -2,6 +2,14 @@
# Long-lived download links available at: https://www.jetbrains.com/intellij-repository/releases
+# The plugin api for IntelliJ 2016.3.1. This is required to build IJwB,
+# and run integration tests.
+new_http_archive(
+ name = "intellij_ce_2016_3_1",
+ build_file = "intellij_platform_sdk/BUILD.idea",
+ url = "https://www.jetbrains.com/intellij-repository/releases/com/jetbrains/intellij/idea/ideaIC/2016.3.1/ideaIC-2016.3.1.zip",
+)
+
# The plugin api for IntelliJ 2016.2.4. This is required to build IJwB,
# and run integration tests.
new_http_archive(
@@ -18,6 +26,30 @@
url = "https://download.jetbrains.com/cpp/CLion-2016.2.2.tar.gz",
)
+# The plugin api for CLion 2016.3.2. This is required to build CLwB,
+# and run integration tests.
+new_http_archive(
+ name = "clion_2016_3_2",
+ build_file = "intellij_platform_sdk/BUILD.clion",
+ url = "https://download.jetbrains.com/cpp/CLion-2016.3.2.tar.gz",
+)
+
+# The plugin api for Android Studio 2.3 Beta 1. This is required to build ASwB,
+# and run integration tests.
+new_http_archive(
+ name = "android_studio_2_3_0_3",
+ build_file = "intellij_platform_sdk/BUILD.android_studio",
+ url = "https://dl.google.com/dl/android/studio/ide-zips/2.3.0.3/android-studio-ide-162.3573574-linux.zip",
+)
+
+# The plugin api for Android Studio 2.3 Beta 2. This is required to build ASwB,
+# and run integration tests.
+new_http_archive(
+ name = "android_studio_2_3_0_4",
+ build_file = "intellij_platform_sdk/BUILD.android_studio",
+ url = "https://dl.google.com/dl/android/studio/ide-zips/2.3.0.4/android-studio-ide-162.3616766-linux.zip",
+)
+
# The plugin api for Android Studio 2.2 stable. This is required to build ASwB,
# and run integration tests.
new_http_archive(
diff --git a/aswb/2.2/src/com/google/idea/blaze/android/compatibility/Compatibility.java b/aswb/2.2/src/com/google/idea/blaze/android/compatibility/Compatibility.java
new file mode 100644
index 0000000..d65d9f2
--- /dev/null
+++ b/aswb/2.2/src/com/google/idea/blaze/android/compatibility/Compatibility.java
@@ -0,0 +1,138 @@
+/*
+ * 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.android.compatibility;
+
+import com.android.sdklib.AndroidVersion;
+import com.android.tools.idea.run.ConsolePrinter;
+import com.android.tools.idea.run.editor.AndroidDebugger;
+import com.android.tools.idea.run.editor.AndroidDebuggerState;
+import com.android.tools.idea.run.tasks.DebugConnectorTask;
+import com.android.tools.idea.run.util.LaunchStatus;
+import com.intellij.execution.Executor;
+import com.intellij.execution.configurations.ConfigurationFactory;
+import com.intellij.execution.configurations.RunConfiguration;
+import com.intellij.execution.runners.ExecutionEnvironment;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.projectRoots.Sdk;
+import java.io.File;
+import java.util.List;
+import java.util.Set;
+import org.jetbrains.android.facet.AndroidFacet;
+import org.jetbrains.android.sdk.AndroidSdkAdditionalData;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.jps.android.model.impl.JpsAndroidModuleProperties;
+
+/** Compatibility facades for Android Studio 2.2. */
+public class Compatibility {
+ private Compatibility() {}
+ /**
+ * Facade for {@link org.jetbrains.android.sdk.AndroidSdkUtils} and {@link
+ * com.android.tools.idea.sdk.AndroidSdks#getInstance()}.
+ */
+ public static class AndroidSdkUtils {
+ private AndroidSdkUtils() {}
+
+ public static Sdk findSuitableAndroidSdk(String targetHash) {
+ return org.jetbrains.android.sdk.AndroidSdkUtils.findSuitableAndroidSdk(targetHash);
+ }
+
+ public static List<Sdk> getAllAndroidSdks() {
+ return org.jetbrains.android.sdk.AndroidSdkUtils.getAllAndroidSdks();
+ }
+
+ public static AndroidSdkAdditionalData getAndroidSdkAdditionalData(Sdk sdk) {
+ return org.jetbrains.android.sdk.AndroidSdkUtils.getAndroidSdkAdditionalData(sdk);
+ }
+ }
+
+ /**
+ * Facade for {@link com.android.tools.idea.sdk.IdeSdks} and {@link
+ * com.android.tools.idea.sdk.IdeSdks#getInstance()}.
+ */
+ public static class IdeSdks {
+ private IdeSdks() {}
+
+ public static File getAndroidSdkPath() {
+ return com.android.tools.idea.sdk.IdeSdks.getAndroidSdkPath();
+ }
+
+ public static List<Sdk> createAndroidSdkPerAndroidTarget(File androidSdkPath) {
+ return com.android.tools.idea.sdk.IdeSdks.createAndroidSdkPerAndroidTarget(androidSdkPath);
+ }
+ }
+
+ /**
+ * Facade for {@link com.android.tools.idea.run.testing.AndroidTestListener} and {@link
+ * com.android.tools.idea.testartifacts.instrumented.AndroidTestListener}
+ */
+ public static class AndroidTestListener
+ extends com.android.tools.idea.run.testing.AndroidTestListener {
+ public AndroidTestListener(LaunchStatus launchStatus, ConsolePrinter consolePrinter) {
+ super(launchStatus, consolePrinter);
+ }
+ }
+
+ /**
+ * Facade for {@link com.android.tools.idea.run.testing.AndroidTestConsoleProperties} and {@link
+ * com.android.tools.idea.testartifacts.instrumented.AndroidTestConsoleProperties}
+ */
+ public static class AndroidTestConsoleProperties
+ extends com.android.tools.idea.run.testing.AndroidTestConsoleProperties {
+ public AndroidTestConsoleProperties(RunConfiguration runConfiguration, Executor executor) {
+ super(runConfiguration, executor);
+ }
+ }
+
+ /**
+ * Facade for {@link com.android.tools.idea.run.testing.AndroidTestRunConfiguration} and {@link
+ * com.android.tools.idea.testartifacts.instrumented.AndroidTestRunConfiguration}
+ */
+ public static class AndroidTestRunConfiguration
+ extends com.android.tools.idea.run.testing.AndroidTestRunConfiguration {
+ public AndroidTestRunConfiguration(Project project, ConfigurationFactory configurationFactory) {
+ super(project, configurationFactory);
+ }
+ }
+
+ /** Facade for {@link com.android.tools.idea.run.tasks.ConnectDebuggerTask}. */
+ public abstract static class ConnectDebuggerTask
+ extends com.android.tools.idea.run.tasks.ConnectDebuggerTask {
+ protected ConnectDebuggerTask(
+ Set<String> applicationIds,
+ AndroidDebugger<?> debugger,
+ Project project,
+ boolean monitorRemoteProcess) {
+ super(applicationIds, debugger, project);
+ }
+ }
+
+ public static <S extends AndroidDebuggerState> DebugConnectorTask getConnectDebuggerTask(
+ AndroidDebugger<S> androidDebugger,
+ ExecutionEnvironment env,
+ @Nullable AndroidVersion version,
+ Set<String> applicationIds,
+ AndroidFacet facet,
+ S state,
+ String runConfigTypeId,
+ boolean monitorRemoteProcess) {
+ return androidDebugger.getConnectDebuggerTask(
+ env, version, applicationIds, facet, state, runConfigTypeId);
+ }
+
+ public static void setFacetStateIsLibraryProject(JpsAndroidModuleProperties facetState) {
+ facetState.LIBRARY_PROJECT = true;
+ }
+}
diff --git a/aswb/src/com/google/idea/blaze/android/sync/model/idea/NullClassJarProvider.java b/aswb/2.2/src/com/google/idea/blaze/android/sync/model/idea/BlazeClassJarProvider.java
similarity index 78%
rename from aswb/src/com/google/idea/blaze/android/sync/model/idea/NullClassJarProvider.java
rename to aswb/2.2/src/com/google/idea/blaze/android/sync/model/idea/BlazeClassJarProvider.java
index 037f4fa..20c2c22 100644
--- a/aswb/src/com/google/idea/blaze/android/sync/model/idea/NullClassJarProvider.java
+++ b/aswb/2.2/src/com/google/idea/blaze/android/sync/model/idea/BlazeClassJarProvider.java
@@ -18,22 +18,23 @@
import com.android.tools.idea.model.ClassJarProvider;
import com.google.common.collect.ImmutableList;
import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import java.util.List;
-import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/** Returns no class jars. Used to disable the layout editor loading jars. */
-public class NullClassJarProvider extends ClassJarProvider {
+public class BlazeClassJarProvider extends ClassJarProvider {
+ public BlazeClassJarProvider(Project project) {}
+
@Nullable
@Override
- public VirtualFile findModuleClassFile(@NotNull String className, @NotNull Module module) {
+ public VirtualFile findModuleClassFile(String className, Module module) {
return null;
}
- @NotNull
@Override
- public List<VirtualFile> getModuleExternalLibraries(@NotNull Module module) {
+ public List<VirtualFile> getModuleExternalLibraries(Module module) {
return ImmutableList.of();
}
}
diff --git a/aswb/tests/utils/integration/com/google/idea/blaze/android/AndroidTestCleanupHelper.java b/aswb/2.2/tests/utils/integration/com/google/idea/blaze/android/AndroidTestCleanupHelper.java
similarity index 100%
rename from aswb/tests/utils/integration/com/google/idea/blaze/android/AndroidTestCleanupHelper.java
rename to aswb/2.2/tests/utils/integration/com/google/idea/blaze/android/AndroidTestCleanupHelper.java
diff --git a/aswb/tests/utils/integration/com/google/idea/blaze/android/AndroidTestSetupRule.java b/aswb/2.2/tests/utils/integration/com/google/idea/blaze/android/AndroidTestSetupRule.java
similarity index 100%
rename from aswb/tests/utils/integration/com/google/idea/blaze/android/AndroidTestSetupRule.java
rename to aswb/2.2/tests/utils/integration/com/google/idea/blaze/android/AndroidTestSetupRule.java
diff --git a/aswb/tests/utils/integration/com/google/idea/blaze/android/BlazeAndroidIntegrationTestCase.java b/aswb/2.2/tests/utils/integration/com/google/idea/blaze/android/BlazeAndroidIntegrationTestCase.java
similarity index 100%
rename from aswb/tests/utils/integration/com/google/idea/blaze/android/BlazeAndroidIntegrationTestCase.java
rename to aswb/2.2/tests/utils/integration/com/google/idea/blaze/android/BlazeAndroidIntegrationTestCase.java
diff --git a/aswb/2.3/src/META-INF/aswb_beta.xml b/aswb/2.3/src/META-INF/aswb_beta.xml
new file mode 100644
index 0000000..8df2945
--- /dev/null
+++ b/aswb/2.3/src/META-INF/aswb_beta.xml
@@ -0,0 +1,35 @@
+<!--
+ ~ 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.
+ -->
+<idea-plugin>
+ <depends>com.google.gct.test.recorder</depends>
+
+ <extensions defaultExtensionNs="com.google.idea.blaze">
+ <BlazeUserSettingsContributor implementation="com.google.idea.blaze.android.settings.BlazeAndroidUserSettingsContributor$BlazeAndroidUserSettingsProvider"/>
+ </extensions>
+
+ <extensions defaultExtensionNs="com.android.project">
+ <buildSystemService implementation="com.google.idea.blaze.android.project.BlazeBuildSystemService"/>
+ <featureEnableService implementation="com.google.idea.blaze.android.project.BlazeFeatureEnableService"/>
+ </extensions>
+
+ <extensions defaultExtensionNs="com.android.rendering">
+ <renderErrorContributor implementation="com.google.idea.blaze.android.rendering.BlazeRenderErrorContributor$BlazeProvider"/>
+ </extensions>
+
+ <extensions defaultExtensionNs="com.google.gct.testrecorder.run">
+ <testRecorderRunConfigurationProxyProvider implementation="com.google.idea.blaze.android.run.testrecorder.TestRecorderBlazeCommandRunConfigurationProxyProvider" />
+ </extensions>
+</idea-plugin>
diff --git a/aswb/2.3/src/com/google/idea/blaze/android/compatibility/Compatibility.java b/aswb/2.3/src/com/google/idea/blaze/android/compatibility/Compatibility.java
new file mode 100644
index 0000000..e614959
--- /dev/null
+++ b/aswb/2.3/src/com/google/idea/blaze/android/compatibility/Compatibility.java
@@ -0,0 +1,141 @@
+/*
+ * 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.android.compatibility;
+
+import com.android.sdklib.AndroidVersion;
+import com.android.tools.idea.run.ConsolePrinter;
+import com.android.tools.idea.run.editor.AndroidDebugger;
+import com.android.tools.idea.run.editor.AndroidDebuggerState;
+import com.android.tools.idea.run.tasks.DebugConnectorTask;
+import com.android.tools.idea.run.util.LaunchStatus;
+import com.intellij.execution.Executor;
+import com.intellij.execution.configurations.ConfigurationFactory;
+import com.intellij.execution.configurations.RunConfiguration;
+import com.intellij.execution.runners.ExecutionEnvironment;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.projectRoots.Sdk;
+import java.io.File;
+import java.util.List;
+import java.util.Set;
+import org.jetbrains.android.facet.AndroidFacet;
+import org.jetbrains.android.sdk.AndroidSdkAdditionalData;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.jps.android.model.impl.JpsAndroidModuleProperties;
+
+/** Compatibility facades for Android Studio 2.3. */
+public class Compatibility {
+ private Compatibility() {}
+
+ /**
+ * Facade for {@link org.jetbrains.android.sdk.AndroidSdkUtils} and {@link
+ * com.android.tools.idea.sdk.AndroidSdks#getInstance()}.
+ */
+ public static class AndroidSdkUtils {
+ private AndroidSdkUtils() {}
+
+ public static Sdk findSuitableAndroidSdk(String targetHash) {
+ return com.android.tools.idea.sdk.AndroidSdks.getInstance()
+ .findSuitableAndroidSdk(targetHash);
+ }
+
+ public static List<Sdk> getAllAndroidSdks() {
+ return com.android.tools.idea.sdk.AndroidSdks.getInstance().getAllAndroidSdks();
+ }
+
+ public static AndroidSdkAdditionalData getAndroidSdkAdditionalData(Sdk sdk) {
+ return com.android.tools.idea.sdk.AndroidSdks.getInstance().getAndroidSdkAdditionalData(sdk);
+ }
+ }
+
+ /**
+ * Facade for {@link com.android.tools.idea.sdk.IdeSdks} and {@link
+ * com.android.tools.idea.sdk.IdeSdks#getInstance()}.
+ */
+ public static class IdeSdks {
+ private IdeSdks() {}
+
+ public static File getAndroidSdkPath() {
+ return com.android.tools.idea.sdk.IdeSdks.getInstance().getAndroidSdkPath();
+ }
+
+ public static List<Sdk> createAndroidSdkPerAndroidTarget(File androidSdkPath) {
+ return com.android.tools.idea.sdk.IdeSdks.getInstance()
+ .createAndroidSdkPerAndroidTarget(androidSdkPath);
+ }
+ }
+
+ /**
+ * Facade for {@link com.android.tools.idea.run.testing.AndroidTestListener} and {@link
+ * com.android.tools.idea.testartifacts.instrumented.AndroidTestListener}
+ */
+ public static class AndroidTestListener
+ extends com.android.tools.idea.testartifacts.instrumented.AndroidTestListener {
+ public AndroidTestListener(LaunchStatus launchStatus, ConsolePrinter consolePrinter) {
+ super(launchStatus, consolePrinter);
+ }
+ }
+
+ /**
+ * Facade for {@link com.android.tools.idea.run.testing.AndroidTestConsoleProperties} and {@link
+ * com.android.tools.idea.testartifacts.instrumented.AndroidTestConsoleProperties}
+ */
+ public static class AndroidTestConsoleProperties
+ extends com.android.tools.idea.testartifacts.instrumented.AndroidTestConsoleProperties {
+ public AndroidTestConsoleProperties(RunConfiguration runConfiguration, Executor executor) {
+ super(runConfiguration, executor);
+ }
+ }
+
+ /**
+ * Facade for {@link com.android.tools.idea.run.testing.AndroidTestRunConfiguration} and {@link
+ * com.android.tools.idea.testartifacts.instrumented.AndroidTestRunConfiguration}
+ */
+ public static class AndroidTestRunConfiguration
+ extends com.android.tools.idea.testartifacts.instrumented.AndroidTestRunConfiguration {
+ public AndroidTestRunConfiguration(Project project, ConfigurationFactory configurationFactory) {
+ super(project, configurationFactory);
+ }
+ }
+
+ /** Facade for {@link com.android.tools.idea.run.tasks.ConnectDebuggerTask}. */
+ public abstract static class ConnectDebuggerTask
+ extends com.android.tools.idea.run.tasks.ConnectDebuggerTask {
+ protected ConnectDebuggerTask(
+ Set<String> applicationIds,
+ AndroidDebugger<?> debugger,
+ Project project,
+ boolean monitorRemoteProcess) {
+ super(applicationIds, debugger, project, monitorRemoteProcess);
+ }
+ }
+
+ public static <S extends AndroidDebuggerState> DebugConnectorTask getConnectDebuggerTask(
+ AndroidDebugger<S> androidDebugger,
+ ExecutionEnvironment env,
+ @Nullable AndroidVersion version,
+ Set<String> applicationIds,
+ AndroidFacet facet,
+ S state,
+ String runConfigTypeId,
+ boolean monitorRemoteProcess) {
+ return androidDebugger.getConnectDebuggerTask(
+ env, version, applicationIds, facet, state, runConfigTypeId, monitorRemoteProcess);
+ }
+
+ public static void setFacetStateIsLibraryProject(JpsAndroidModuleProperties facetState) {
+ facetState.PROJECT_TYPE = com.android.builder.model.AndroidProject.PROJECT_TYPE_LIBRARY;
+ }
+}
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
new file mode 100644
index 0000000..36923d7
--- /dev/null
+++ b/aswb/2.3/src/com/google/idea/blaze/android/project/BlazeBuildSystemService.java
@@ -0,0 +1,99 @@
+/*
+ * 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.android.project;
+
+import com.android.tools.idea.project.BuildSystemService;
+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.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;
+
+/** Blaze implementation of {@link BuildSystemService} for build system specific operations. */
+public class BlazeBuildSystemService extends BuildSystemService {
+ @Override
+ public boolean isApplicable(Project project) {
+ return Blaze.isBlazeProject(project);
+ }
+
+ @Override
+ public void buildProject(Project project) {
+ BlazeBuildService.getInstance().buildProject(project);
+ }
+
+ @Override
+ public void syncProject(Project project) {
+ BlazeSyncManager.getInstance(project).incrementalProjectSync();
+ }
+
+ @Override
+ public void addDependency(Module module, String artifact) {
+ Project project = module.getProject();
+ BlazeProjectData blazeProjectData =
+ BlazeProjectDataManager.getInstance(project).getBlazeProjectData();
+ if (blazeProjectData == null) {
+ return;
+ }
+ AndroidResourceModuleRegistry registry = AndroidResourceModuleRegistry.getInstance(project);
+ TargetIdeInfo targetIdeInfo = blazeProjectData.targetMap.get(registry.getTargetKey(module));
+ if (targetIdeInfo == null || targetIdeInfo.buildFile == null) {
+ return;
+ }
+
+ // TODO: automagically edit deps instead of just opening the BUILD file?
+ // Need to translate Gradle coordinates into blaze targets.
+ // Will probably need to hardcode for each dependency.
+ FileEditorManager fileEditorManager = FileEditorManager.getInstance(project);
+ PsiElement buildTargetPsi =
+ BuildReferenceManager.getInstance(project).resolveLabel(targetIdeInfo.key.label);
+ if (buildTargetPsi != null) {
+ // If we can find a PSI for the target,
+ // then we can jump straight to the target in the build file.
+ fileEditorManager.openTextEditor(
+ new OpenFileDescriptor(
+ project,
+ buildTargetPsi.getContainingFile().getVirtualFile(),
+ buildTargetPsi.getTextOffset()),
+ true);
+ } else {
+ // If not, just the build file is good enough.
+ File buildIoFile = blazeProjectData.artifactLocationDecoder.decode(targetIdeInfo.buildFile);
+ VirtualFile buildVirtualFile = findFileByIoFile(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/project/BlazeFeatureEnableService.java b/aswb/2.3/src/com/google/idea/blaze/android/project/BlazeFeatureEnableService.java
new file mode 100644
index 0000000..e59f375
--- /dev/null
+++ b/aswb/2.3/src/com/google/idea/blaze/android/project/BlazeFeatureEnableService.java
@@ -0,0 +1,46 @@
+/*
+ * 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.android.project;
+
+import com.android.tools.idea.project.FeatureEnableService;
+import com.google.idea.blaze.android.settings.BlazeAndroidUserSettings;
+import com.google.idea.blaze.base.settings.Blaze;
+import com.google.idea.blaze.base.sync.data.BlazeProjectDataManager;
+import com.google.idea.common.experiments.BoolExperiment;
+import com.intellij.openapi.project.Project;
+
+/** Enable features supported by the blaze integration. */
+public class BlazeFeatureEnableService extends FeatureEnableService {
+ private static final BoolExperiment ENABLE_LAYOUT_EDITOR =
+ new BoolExperiment("enable.layout.editor", true);
+
+ @Override
+ protected boolean isApplicable(Project project) {
+ return Blaze.isBlazeProject(project);
+ }
+
+ @Override
+ public boolean isLayoutEditorEnabled(Project project) {
+ return isLayoutEditorExperimentEnabled()
+ && BlazeAndroidUserSettings.getInstance().getUseLayoutEditor()
+ // Can't render if we don't have the data ready.
+ && BlazeProjectDataManager.getInstance(project).getBlazeProjectData() != null;
+ }
+
+ public static boolean isLayoutEditorExperimentEnabled() {
+ return ENABLE_LAYOUT_EDITOR.getValue();
+ }
+}
diff --git a/aswb/2.3/src/com/google/idea/blaze/android/rendering/BlazeRenderErrorContributor.java b/aswb/2.3/src/com/google/idea/blaze/android/rendering/BlazeRenderErrorContributor.java
new file mode 100644
index 0000000..b4c8498
--- /dev/null
+++ b/aswb/2.3/src/com/google/idea/blaze/android/rendering/BlazeRenderErrorContributor.java
@@ -0,0 +1,338 @@
+/*
+ * 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.android.rendering;
+
+import static com.android.SdkConstants.ANDROID_MANIFEST_XML;
+
+import com.android.tools.idea.rendering.HtmlLinkManager;
+import com.android.tools.idea.rendering.RenderErrorContributor;
+import com.android.tools.idea.rendering.RenderLogger;
+import com.android.tools.idea.rendering.RenderResult;
+import com.android.tools.idea.rendering.errors.ui.RenderErrorModel;
+import com.android.utils.HtmlBuilder;
+import com.google.common.collect.ImmutableCollection;
+import com.google.common.collect.Maps;
+import com.google.common.collect.SortedSetMultimap;
+import com.google.common.collect.TreeMultimap;
+import com.google.idea.blaze.android.sync.model.AndroidResourceModule;
+import com.google.idea.blaze.android.sync.model.AndroidResourceModuleRegistry;
+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.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.data.BlazeProjectDataManager;
+import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoder;
+import com.google.idea.blaze.base.targetmaps.SourceToTargetMap;
+import com.google.idea.blaze.base.targetmaps.TransitiveDependencyMap;
+import com.intellij.lang.annotation.HighlightSeverity;
+import com.intellij.openapi.actionSystem.DataContext;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.IndexNotReadyException;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.VfsUtilCore;
+import com.intellij.psi.JavaPsiFacade;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.search.GlobalSearchScope;
+import java.io.File;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import org.jetbrains.annotations.Nullable;
+
+/** Contribute blaze specific render errors. */
+public class BlazeRenderErrorContributor extends RenderErrorContributor {
+ private RenderLogger logger;
+ private Module module;
+ private Project project;
+
+ public BlazeRenderErrorContributor(RenderResult result, @Nullable DataContext dataContext) {
+ super(result, dataContext);
+ logger = result.getLogger();
+ module = result.getModule();
+ project = module.getProject();
+ }
+
+ @Override
+ public Collection<RenderErrorModel.Issue> reportIssues() {
+ BlazeProjectData blazeProjectData =
+ BlazeProjectDataManager.getInstance(project).getBlazeProjectData();
+
+ if (blazeProjectData == null || !logger.hasErrors()) {
+ return getIssues();
+ }
+
+ TargetMap targetMap = blazeProjectData.targetMap;
+ ArtifactLocationDecoder decoder = blazeProjectData.artifactLocationDecoder;
+ AndroidResourceModule resourceModule =
+ AndroidResourceModuleRegistry.getInstance(project).get(module);
+ if (resourceModule == null) {
+ return getIssues();
+ }
+
+ TargetIdeInfo target = targetMap.get(resourceModule.targetKey);
+ if (target == null) {
+ return getIssues();
+ }
+
+ reportGeneratedResources(resourceModule, targetMap, decoder);
+ reportNonStandardAndroidManifestName(target, decoder);
+ reportResourceTargetShouldDependOnClassTarget(target, targetMap, decoder);
+ return getIssues();
+ }
+
+ /**
+ * We can't find generated resources. If a layout uses them, the layout won't render correctly.
+ */
+ private void reportGeneratedResources(
+ AndroidResourceModule resourceModule, TargetMap targetMap, ArtifactLocationDecoder decoder) {
+ Map<String, Throwable> brokenClasses = logger.getBrokenClasses();
+ if (brokenClasses == null || brokenClasses.isEmpty()) {
+ return;
+ }
+
+ // Sorted entries for deterministic error message.
+ SortedMap<ArtifactLocation, TargetIdeInfo> generatedResources =
+ Maps.newTreeMap(getGeneratedResources(targetMap.get(resourceModule.targetKey)));
+
+ for (TargetKey dependency : resourceModule.transitiveResourceDependencies) {
+ generatedResources.putAll(getGeneratedResources(targetMap.get(dependency)));
+ }
+
+ if (generatedResources.isEmpty()) {
+ return;
+ }
+
+ HtmlBuilder builder = new HtmlBuilder();
+ builder.add("Generated resources will not be discovered by the IDE:");
+ builder.beginList();
+ for (Map.Entry<ArtifactLocation, TargetIdeInfo> entry : generatedResources.entrySet()) {
+ ArtifactLocation resource = entry.getKey();
+ TargetIdeInfo target = entry.getValue();
+ builder.listItem().add(resource.getRelativePath()).add(" from ");
+ addTargetLink(builder, target, decoder);
+ }
+ builder
+ .endList()
+ .add("Please avoid using generated resources, ")
+ .addLink("then ", "sync the project", " ", getLinkManager().createSyncProjectUrl())
+ .addLink("and ", "refresh the layout", ".", getLinkManager().createRefreshRenderUrl());
+ addIssue()
+ .setSeverity(HighlightSeverity.ERROR, HIGH_PRIORITY + 1) // Reported above broken classes
+ .setSummary("Generated resources")
+ .setHtmlContent(builder)
+ .build();
+ }
+
+ private static SortedMap<ArtifactLocation, TargetIdeInfo> getGeneratedResources(
+ TargetIdeInfo target) {
+ if (target == null || target.androidIdeInfo == null) {
+ return Collections.emptySortedMap();
+ }
+ SortedMap<ArtifactLocation, TargetIdeInfo> generatedResources = Maps.newTreeMap();
+ generatedResources.putAll(
+ target
+ .androidIdeInfo
+ .resources
+ .stream()
+ .filter(ArtifactLocation::isGenerated)
+ .collect(Collectors.toMap(Function.identity(), resource -> target)));
+ return generatedResources;
+ }
+
+ /**
+ * When the Android manifest isn't AndroidManifest.xml, resolving resource IDs would fail. This
+ * doesn't seem to be an issue if the manifest belongs to one of the target's dependencies.
+ */
+ private void reportNonStandardAndroidManifestName(
+ TargetIdeInfo target, ArtifactLocationDecoder decoder) {
+ if (target.androidIdeInfo == null || target.androidIdeInfo.manifest == null) {
+ return;
+ }
+
+ Map<String, Throwable> brokenClasses = logger.getBrokenClasses();
+ if (brokenClasses == null || brokenClasses.isEmpty()) {
+ return;
+ }
+
+ File manifest = decoder.decode(target.androidIdeInfo.manifest);
+ if (manifest.getName().equals(ANDROID_MANIFEST_XML)) {
+ return;
+ }
+
+ HtmlBuilder builder = new HtmlBuilder();
+ addTargetLink(builder, target, decoder)
+ .add(" uses a non-standard name for the Android manifest: ");
+ String linkToManifest = HtmlLinkManager.createFilePositionUrl(manifest, -1, 0);
+ if (linkToManifest != null) {
+ builder.addLink(manifest.getName(), linkToManifest);
+ } else {
+ builder.newline().add(manifest.getPath());
+ }
+ // TODO: add a link to automatically rename the file and refactor all references.
+ builder
+ .newline()
+ .add("Please rename it to ")
+ .add(ANDROID_MANIFEST_XML)
+ .addLink(", then ", "sync the project", "", getLinkManager().createSyncProjectUrl())
+ .addLink(" and ", "refresh the layout", ".", getLinkManager().createRefreshRenderUrl());
+ addIssue()
+ .setSeverity(HighlightSeverity.ERROR, HIGH_PRIORITY + 1) // Reported above broken classes.
+ .setSummary("Non-standard manifest name")
+ .setHtmlContent(builder)
+ .build();
+ }
+
+ /**
+ * Blaze doesn't resolve class dependencies from resources until building the final
+ * android_binary, so we could end up with resources that ultimately build correctly, but fail to
+ * find their class dependencies during rendering in the layout editor.
+ */
+ private void reportResourceTargetShouldDependOnClassTarget(
+ TargetIdeInfo target, TargetMap targetMap, ArtifactLocationDecoder decoder) {
+ Collection<String> missingClasses = logger.getMissingClasses();
+ if (missingClasses == null || missingClasses.isEmpty()) {
+ return;
+ }
+
+ // Sorted entries for deterministic error message.
+ SortedSetMultimap<String, TargetKey> missingClassToTargetMap = TreeMultimap.create();
+
+ SourceToTargetMap sourceToTargetMap = SourceToTargetMap.getInstance(project);
+ ImmutableCollection transitiveDependencies =
+ TransitiveDependencyMap.getInstance(project).getTransitiveDependencies(target.key);
+
+ for (String missingClass : missingClasses) {
+ File sourceFile = getSourceFileForClass(missingClass);
+ if (sourceFile == null) {
+ continue;
+ }
+ ImmutableCollection<TargetKey> sourceTargets =
+ sourceToTargetMap.getRulesForSourceFile(sourceFile);
+ if (sourceTargets
+ .stream()
+ .noneMatch(
+ sourceTarget ->
+ sourceTarget.equals(target.key)
+ || transitiveDependencies.contains(sourceTarget))) {
+ missingClassToTargetMap.putAll(missingClass, sourceTargets);
+ }
+ }
+
+ if (missingClassToTargetMap.isEmpty()) {
+ return;
+ }
+
+ HtmlBuilder builder = new HtmlBuilder();
+ addTargetLink(builder, target, decoder)
+ .add(" contains resource files that reference these classes:")
+ .beginList();
+ for (String missingClass : missingClassToTargetMap.keySet()) {
+ builder
+ .listItem()
+ .addLink(missingClass, getLinkManager().createOpenClassUrl(missingClass))
+ .add(" from ");
+ for (TargetKey targetKey : missingClassToTargetMap.get(missingClass)) {
+ addTargetLink(builder, targetMap.get(targetKey), decoder).add(" ");
+ }
+ }
+ builder.endList().add("Please fix your dependencies so that ");
+ addTargetLink(builder, target, decoder)
+ .add(" correctly depends on these classes, ")
+ .addLink("then ", "sync the project", " ", getLinkManager().createSyncProjectUrl())
+ .addLink("and ", "refresh the layout", ".", getLinkManager().createRefreshRenderUrl())
+ .newline()
+ .newline()
+ .addBold(
+ "NOTE: blaze can still build with the incorrect dependencies "
+ + "due to the way it handles resources, "
+ + "but the layout editor needs them to be correct.");
+
+ addIssue()
+ .setSeverity(HighlightSeverity.ERROR, HIGH_PRIORITY + 1) // Reported above missing classes.
+ .setSummary("Missing class dependencies")
+ .setHtmlContent(builder)
+ .build();
+ }
+
+ private File getSourceFileForClass(String className) {
+ return ApplicationManager.getApplication()
+ .runReadAction(
+ (Computable<File>)
+ () -> {
+ try {
+ PsiClass psiClass =
+ JavaPsiFacade.getInstance(project)
+ .findClass(className, GlobalSearchScope.projectScope(project));
+ if (psiClass == null) {
+ return null;
+ }
+ return VfsUtilCore.virtualToIoFile(
+ psiClass.getContainingFile().getVirtualFile());
+ } catch (IndexNotReadyException ignored) {
+ // We're in dumb mode. Abort! Abort!
+ return null;
+ }
+ });
+ }
+
+ private HtmlBuilder addTargetLink(
+ HtmlBuilder builder, TargetIdeInfo target, ArtifactLocationDecoder decoder) {
+ File buildFile = decoder.decode(target.buildFile);
+ int line =
+ ApplicationManager.getApplication()
+ .runReadAction(
+ (Computable<Integer>)
+ () -> {
+ PsiElement buildTargetPsi =
+ BuildReferenceManager.getInstance(project).resolveLabel(target.key.label);
+ if (buildTargetPsi == null) {
+ return -1;
+ }
+ return StringUtil.offsetToLineNumber(
+ buildTargetPsi.getContainingFile().getText(),
+ buildTargetPsi.getTextOffset());
+ });
+ String url = HtmlLinkManager.createFilePositionUrl(buildFile, line, 0);
+ if (url != null) {
+ return builder.addLink(target.toString(), url);
+ }
+ return builder.add(target.toString());
+ }
+
+ /** Extension to provide {@link BlazeRenderErrorContributor}. */
+ public static class BlazeProvider extends Provider {
+ @Override
+ public boolean isApplicable(Project project) {
+ return Blaze.isBlazeProject(project);
+ }
+
+ @Override
+ public RenderErrorContributor getContributor(
+ RenderResult result, @Nullable DataContext dataContext) {
+ return new BlazeRenderErrorContributor(result, dataContext);
+ }
+ }
+}
diff --git a/aswb/2.3/src/com/google/idea/blaze/android/run/testrecorder/TestRecorderBlazeCommandRunConfiguration.java b/aswb/2.3/src/com/google/idea/blaze/android/run/testrecorder/TestRecorderBlazeCommandRunConfiguration.java
new file mode 100644
index 0000000..2f6c62e
--- /dev/null
+++ b/aswb/2.3/src/com/google/idea/blaze/android/run/testrecorder/TestRecorderBlazeCommandRunConfiguration.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.android.run.testrecorder;
+
+import com.google.idea.blaze.base.run.BlazeCommandRunConfiguration;
+import com.intellij.openapi.diagnostic.Logger;
+import org.jdom.Element;
+
+/** Blaze integration with test recorder. */
+public class TestRecorderBlazeCommandRunConfiguration extends BlazeCommandRunConfiguration {
+ private static final Logger LOGGER =
+ Logger.getInstance(TestRecorderBlazeCommandRunConfiguration.class);
+
+ public TestRecorderBlazeCommandRunConfiguration(BlazeCommandRunConfiguration baseConfiguration) {
+ super(
+ baseConfiguration.getProject(),
+ baseConfiguration.getFactory(),
+ "TestRecorder" + baseConfiguration.getName());
+ Element element = new Element("toClone");
+ try {
+ baseConfiguration.writeExternal(element);
+ this.readExternal(element);
+ } catch (Exception e) {
+ LOGGER.error(e);
+ }
+ }
+}
diff --git a/aswb/2.3/src/com/google/idea/blaze/android/run/testrecorder/TestRecorderBlazeCommandRunConfigurationProxy.java b/aswb/2.3/src/com/google/idea/blaze/android/run/testrecorder/TestRecorderBlazeCommandRunConfigurationProxy.java
new file mode 100644
index 0000000..b91a720
--- /dev/null
+++ b/aswb/2.3/src/com/google/idea/blaze/android/run/testrecorder/TestRecorderBlazeCommandRunConfigurationProxy.java
@@ -0,0 +1,83 @@
+/*
+ * 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.android.run.testrecorder;
+
+import com.android.annotations.Nullable;
+import com.android.ddmlib.IDevice;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.gct.testrecorder.run.TestRecorderRunConfigurationProxy;
+import com.google.idea.blaze.android.run.binary.BlazeAndroidBinaryRunConfigurationHandler;
+import com.google.idea.blaze.android.run.binary.BlazeAndroidBinaryRunConfigurationState;
+import com.google.idea.blaze.android.run.runner.BlazeAndroidRunConfigurationRunner;
+import com.google.idea.blaze.base.run.BlazeCommandRunConfiguration;
+import com.intellij.execution.configurations.LocatableConfigurationBase;
+import com.intellij.execution.runners.ExecutionEnvironment;
+import com.intellij.openapi.module.Module;
+import java.util.List;
+
+/** {@link TestRecorderRunConfigurationProxy} for blaze. */
+public class TestRecorderBlazeCommandRunConfigurationProxy
+ implements TestRecorderRunConfigurationProxy {
+
+ private final BlazeCommandRunConfiguration myBaseConfiguration;
+ private final BlazeAndroidBinaryRunConfigurationHandler myBaseConfigurationHandler;
+
+ public TestRecorderBlazeCommandRunConfigurationProxy(
+ BlazeCommandRunConfiguration baseConfiguration) {
+ myBaseConfiguration = baseConfiguration;
+ myBaseConfigurationHandler =
+ (BlazeAndroidBinaryRunConfigurationHandler) baseConfiguration.getHandler();
+ }
+
+ @Override
+ public LocatableConfigurationBase getTestRecorderRunConfiguration() {
+ return new TestRecorderBlazeCommandRunConfiguration(myBaseConfiguration);
+ }
+
+ @Override
+ public Module getModule() {
+ return myBaseConfigurationHandler.getModule();
+ }
+
+ @Override
+ public boolean isLaunchActivitySupported() {
+ String mode = myBaseConfigurationHandler.getState().getMode();
+
+ // Supported launch activities are Default and Specified.
+ return BlazeAndroidBinaryRunConfigurationState.LAUNCH_DEFAULT_ACTIVITY.equals(mode)
+ || BlazeAndroidBinaryRunConfigurationState.LAUNCH_SPECIFIC_ACTIVITY.equals(mode);
+ }
+
+ @Override
+ public String getLaunchActivityClass() {
+ BlazeAndroidBinaryRunConfigurationState state = myBaseConfigurationHandler.getState();
+
+ if (BlazeAndroidBinaryRunConfigurationState.LAUNCH_SPECIFIC_ACTIVITY.equals(state.getMode())) {
+ return state.getActivityClass();
+ }
+
+ return "";
+ }
+
+ @Nullable
+ @Override
+ public List<ListenableFuture<IDevice>> getDeviceFutures(ExecutionEnvironment environment) {
+ return environment
+ .getCopyableUserData(BlazeAndroidRunConfigurationRunner.DEVICE_SESSION_KEY)
+ .deviceFutures
+ .get();
+ }
+}
diff --git a/aswb/2.3/src/com/google/idea/blaze/android/run/testrecorder/TestRecorderBlazeCommandRunConfigurationProxyProvider.java b/aswb/2.3/src/com/google/idea/blaze/android/run/testrecorder/TestRecorderBlazeCommandRunConfigurationProxyProvider.java
new file mode 100644
index 0000000..8a2c206
--- /dev/null
+++ b/aswb/2.3/src/com/google/idea/blaze/android/run/testrecorder/TestRecorderBlazeCommandRunConfigurationProxyProvider.java
@@ -0,0 +1,41 @@
+/*
+ * 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.android.run.testrecorder;
+
+import com.android.annotations.Nullable;
+import com.google.gct.testrecorder.run.TestRecorderRunConfigurationProxy;
+import com.google.gct.testrecorder.run.TestRecorderRunConfigurationProxyProvider;
+import com.google.idea.blaze.android.run.binary.BlazeAndroidBinaryRunConfigurationHandler;
+import com.google.idea.blaze.base.run.BlazeCommandRunConfiguration;
+import com.intellij.execution.configurations.RunConfiguration;
+
+/** Provides {@link TestRecorderBlazeCommandRunConfigurationProxy}. */
+public class TestRecorderBlazeCommandRunConfigurationProxyProvider
+ implements TestRecorderRunConfigurationProxyProvider {
+
+ @Nullable
+ @Override
+ public TestRecorderRunConfigurationProxy getProxy(@Nullable RunConfiguration runConfiguration) {
+ if (runConfiguration instanceof BlazeCommandRunConfiguration
+ && ((BlazeCommandRunConfiguration) runConfiguration).getHandler()
+ instanceof BlazeAndroidBinaryRunConfigurationHandler) {
+ return new TestRecorderBlazeCommandRunConfigurationProxy(
+ (BlazeCommandRunConfiguration) runConfiguration);
+ }
+
+ return null;
+ }
+}
diff --git a/aswb/2.3/src/com/google/idea/blaze/android/settings/BlazeAndroidUserSettingsContributor.java b/aswb/2.3/src/com/google/idea/blaze/android/settings/BlazeAndroidUserSettingsContributor.java
new file mode 100644
index 0000000..3ff141b
--- /dev/null
+++ b/aswb/2.3/src/com/google/idea/blaze/android/settings/BlazeAndroidUserSettingsContributor.java
@@ -0,0 +1,96 @@
+/*
+ * 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.android.settings;
+
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
+import com.google.idea.blaze.android.project.BlazeFeatureEnableService;
+import com.google.idea.blaze.base.settings.ui.BlazeUserSettingsContributor;
+import com.intellij.uiDesigner.core.GridConstraints;
+import javax.swing.JCheckBox;
+import javax.swing.JComponent;
+import javax.swing.JPanel;
+
+/** Contributes Android-specific settings. */
+public class BlazeAndroidUserSettingsContributor implements BlazeUserSettingsContributor {
+ private final ImmutableList<JComponent> components;
+ private JCheckBox useLayoutEditor;
+
+ BlazeAndroidUserSettingsContributor() {
+ useLayoutEditor = new JCheckBox();
+ useLayoutEditor.setSelected(false);
+ useLayoutEditor.setText("Use the layout editor for layout XML files. May freeze IDE.");
+
+ ImmutableList.Builder<JComponent> builder = ImmutableList.builder();
+ if (BlazeFeatureEnableService.isLayoutEditorExperimentEnabled()) {
+ builder.add(useLayoutEditor);
+ }
+ components = builder.build();
+ }
+
+ @Override
+ public void apply() {
+ BlazeAndroidUserSettings settings = BlazeAndroidUserSettings.getInstance();
+ settings.setUseLayoutEditor(useLayoutEditor.isSelected());
+ }
+
+ @Override
+ public void reset() {
+ BlazeAndroidUserSettings settings = BlazeAndroidUserSettings.getInstance();
+ useLayoutEditor.setSelected(settings.getUseLayoutEditor());
+ }
+
+ @Override
+ public boolean isModified() {
+ BlazeAndroidUserSettings settings = BlazeAndroidUserSettings.getInstance();
+ return !Objects.equal(useLayoutEditor.isSelected(), settings.getUseLayoutEditor());
+ }
+
+ @Override
+ public int getRowCount() {
+ return components.size();
+ }
+
+ @Override
+ public int addComponents(JPanel panel, int rowi) {
+ for (JComponent contributedComponent : components) {
+ panel.add(
+ contributedComponent,
+ new GridConstraints(
+ rowi++,
+ 0,
+ 1,
+ 2,
+ GridConstraints.ANCHOR_NORTHWEST,
+ GridConstraints.FILL_NONE,
+ GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
+ GridConstraints.SIZEPOLICY_FIXED,
+ null,
+ null,
+ null,
+ 0,
+ false));
+ }
+ return rowi;
+ }
+
+ static class BlazeAndroidUserSettingsProvider implements Provider {
+ @Override
+ public BlazeUserSettingsContributor getContributor() {
+ return new BlazeAndroidUserSettingsContributor();
+ }
+ }
+}
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
new file mode 100644
index 0000000..15c2add
--- /dev/null
+++ b/aswb/2.3/src/com/google/idea/blaze/android/sync/model/idea/BlazeClassJarProvider.java
@@ -0,0 +1,224 @@
+/*
+ * 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.android.sync.model.idea;
+
+import com.android.SdkConstants;
+import com.android.tools.idea.model.ClassJarProvider;
+import com.android.tools.idea.res.AppResourceRepository;
+import com.android.tools.idea.res.ResourceClassRegistry;
+import com.google.common.collect.Lists;
+import com.google.idea.blaze.android.sync.model.AndroidResourceModuleRegistry;
+import com.google.idea.blaze.base.ideinfo.AndroidIdeInfo;
+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.sync.data.BlazeProjectDataManager;
+import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoder;
+import com.google.idea.blaze.base.targetmaps.TransitiveDependencyMap;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.ModalityState;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.JarFileSystem;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.ex.temp.TempFileSystem;
+import com.intellij.util.containers.OrderedSet;
+import java.io.File;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+import org.jetbrains.annotations.Nullable;
+
+/** Collects class jars from the user's build. */
+public class BlazeClassJarProvider extends ClassJarProvider {
+
+ private final Project project;
+ private AtomicBoolean pendingModuleJarsRefresh;
+ private AtomicBoolean pendingDependencyJarsRefresh;
+
+ public BlazeClassJarProvider(final Project project) {
+ this.project = project;
+ this.pendingModuleJarsRefresh = new AtomicBoolean(false);
+ this.pendingDependencyJarsRefresh = new AtomicBoolean(false);
+ }
+
+ @Override
+ @Nullable
+ public VirtualFile findModuleClassFile(String className, Module module) {
+ BlazeProjectData blazeProjectData =
+ BlazeProjectDataManager.getInstance(project).getBlazeProjectData();
+ if (blazeProjectData == null) {
+ return null;
+ }
+
+ ArtifactLocationDecoder decoder = blazeProjectData.artifactLocationDecoder;
+ AndroidResourceModuleRegistry registry = AndroidResourceModuleRegistry.getInstance(project);
+ TargetIdeInfo target = blazeProjectData.targetMap.get(registry.getTargetKey(module));
+
+ if (target == null || target.javaIdeInfo == null) {
+ return null;
+ }
+
+ // As a potential optimization, we could choose an arbitrary android_binary target
+ // that depends on the library to provide a single complete resource jar,
+ // instead of having to rely on dynamic class generation.
+ // TODO: benchmark to see if optimization is worthwhile.
+
+ String classNamePath = className.replace('.', File.separatorChar) + SdkConstants.DOT_CLASS;
+
+ List<File> missingClassJars = Lists.newArrayList();
+ for (LibraryArtifact jar : target.javaIdeInfo.jars) {
+ if (jar.classJar == null) {
+ continue;
+ }
+ File classJarFile = decoder.decode(jar.classJar);
+ VirtualFile classJarVF = findFileByIoFile(classJarFile);
+ if (classJarVF == null) {
+ if (classJarFile.exists()) {
+ missingClassJars.add(classJarFile);
+ }
+ continue;
+ }
+ VirtualFile classFile = findClassInJar(classJarVF, classNamePath);
+ if (classFile != null) {
+ return classFile;
+ }
+ }
+
+ maybeRefreshJars(missingClassJars, pendingModuleJarsRefresh);
+ return null;
+ }
+
+ @Nullable
+ private static VirtualFile findClassInJar(final VirtualFile classJar, String classNamePath) {
+ VirtualFile jarRoot = getJarRootForLocalFile(classJar);
+ if (jarRoot == null) {
+ return null;
+ }
+ return jarRoot.findFileByRelativePath(classNamePath);
+ }
+
+ @Override
+ public List<VirtualFile> getModuleExternalLibraries(Module module) {
+ OrderedSet<VirtualFile> results = new OrderedSet<>();
+ BlazeProjectData blazeProjectData =
+ BlazeProjectDataManager.getInstance(project).getBlazeProjectData();
+
+ if (blazeProjectData == null) {
+ return results;
+ }
+
+ TargetMap targetMap = blazeProjectData.targetMap;
+ ArtifactLocationDecoder decoder = blazeProjectData.artifactLocationDecoder;
+
+ AndroidResourceModuleRegistry registry = AndroidResourceModuleRegistry.getInstance(project);
+ TargetIdeInfo target = targetMap.get(registry.getTargetKey(module));
+
+ if (target == null) {
+ return results;
+ }
+
+ AppResourceRepository repository = AppResourceRepository.getAppResources(module, true);
+
+ List<File> missingClassJars = Lists.newArrayList();
+ for (TargetKey dependencyTargetKey :
+ TransitiveDependencyMap.getInstance(project).getTransitiveDependencies(target.key)) {
+ TargetIdeInfo dependencyTarget = targetMap.get(dependencyTargetKey);
+ if (dependencyTarget == null) {
+ continue;
+ }
+ JavaIdeInfo javaIdeInfo = dependencyTarget.javaIdeInfo;
+ AndroidIdeInfo androidIdeInfo = dependencyTarget.androidIdeInfo;
+
+ // Add all non-resource jars to be searched.
+ // Multiple resource jars will have ID conflicts unless generated dynamically.
+ if (javaIdeInfo != null) {
+ for (LibraryArtifact jar : javaIdeInfo.jars) {
+ if (androidIdeInfo != null && jar.equals(androidIdeInfo.resourceJar)) {
+ // No resource jars.
+ continue;
+ }
+ // Some of these could be empty class jars from resource only android_library targets.
+ // A potential optimization could be to filter out jars like these,
+ // so we don't waste time fetching and searching them.
+ // TODO: benchmark to see if optimization is worthwhile.
+ if (jar.classJar != null) {
+ File classJarFile = decoder.decode(jar.classJar);
+ VirtualFile classJar = findFileByIoFile(classJarFile);
+ if (classJar != null) {
+ results.add(classJar);
+ } else if (classJarFile.exists()) {
+ missingClassJars.add(classJarFile);
+ }
+ }
+ }
+ }
+
+ // Tell ResourceClassRegistry which repository contains our resources and the java packages of
+ // the resources that we're interested in.
+ // When the class loader tries to load a custom view, and the view references resource
+ // classes, layoutlib will ask the class loader for these resource classes.
+ // If these resource classes are in a separate jar from the target (i.e., in a dependency),
+ // then offering their jars will lead to a conflict in the resource IDs.
+ // So instead, the resource class generator will produce dummy resource classes with
+ // non-conflicting IDs to satisfy the class loader.
+ // The resource repository remembers the dynamic IDs that it handed out and when the layoutlib
+ // calls to ask about the name and content of a given resource ID, the repository can just
+ // answer what it has already stored.
+ if (androidIdeInfo != null && repository != null) {
+ ResourceClassRegistry.get(module.getProject())
+ .addLibrary(repository, androidIdeInfo.resourceJavaPackage);
+ }
+ }
+
+ maybeRefreshJars(missingClassJars, pendingDependencyJarsRefresh);
+ 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
+ // still tries to refresh the IO files synchronously. A global async refresh can't find new
+ // files in the ObjFS since we're not watching it.
+ // We need to do our own asynchronous refresh, and guard it with a flag to prevent the event
+ // queue from overflowing.
+ if (!missingJars.isEmpty() && !pendingRefresh.getAndSet(true)) {
+ ApplicationManager.getApplication()
+ .invokeLater(
+ () -> {
+ LocalFileSystem.getInstance().refreshIoFiles(missingJars);
+ pendingRefresh.set(false);
+ },
+ ModalityState.NON_MODAL);
+ }
+ }
+
+ private static VirtualFile getJarRootForLocalFile(VirtualFile file) {
+ return ApplicationManager.getApplication().isUnitTestMode()
+ ? TempFileSystem.getInstance().findFileByPath(file.getPath() + JarFileSystem.JAR_SEPARATOR)
+ : JarFileSystem.getInstance().getJarRootForLocalFile(file);
+ }
+}
diff --git a/aswb/2.3/tests/integrationtests/com/google/idea/blaze/android/sync/model/idea/BlazeClassJarProviderIntegrationTest.java b/aswb/2.3/tests/integrationtests/com/google/idea/blaze/android/sync/model/idea/BlazeClassJarProviderIntegrationTest.java
new file mode 100644
index 0000000..05f1ccf
--- /dev/null
+++ b/aswb/2.3/tests/integrationtests/com/google/idea/blaze/android/sync/model/idea/BlazeClassJarProviderIntegrationTest.java
@@ -0,0 +1,501 @@
+/*
+ * 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.android.sync.model.idea;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.mock;
+
+import com.android.tools.idea.model.ClassJarProvider;
+import com.android.tools.idea.res.AppResourceRepository;
+import com.android.tools.idea.res.ResourceClassRegistry;
+import com.google.idea.blaze.android.sync.model.AndroidResourceModule;
+import com.google.idea.blaze.android.sync.model.AndroidResourceModuleRegistry;
+import com.google.idea.blaze.base.BlazeIntegrationTestCase;
+import com.google.idea.blaze.base.ideinfo.AndroidIdeInfo;
+import com.google.idea.blaze.base.ideinfo.ArtifactLocation;
+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.ideinfo.TargetMapBuilder;
+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.sync.workspace.ArtifactLocationDecoder;
+import com.intellij.facet.FacetManager;
+import com.intellij.facet.ModifiableFacetModel;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.vfs.VirtualFile;
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import org.jetbrains.android.facet.AndroidFacet;
+import org.jetbrains.android.facet.AndroidFacetConfiguration;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Integration test for {@link BlazeClassJarProvider}. */
+@RunWith(JUnit4.class)
+public class BlazeClassJarProviderIntegrationTest extends BlazeIntegrationTestCase {
+ private static final String BLAZE_BIN = "blaze-out/crosstool/bin";
+
+ private Module module;
+ private ClassJarProvider classJarProvider;
+
+ @Before
+ public void doSetup() {
+ module = testFixture.getModule();
+
+ ArtifactLocationDecoder decoder =
+ (location) -> new File("/src", location.getExecutionRootRelativePath());
+
+ mockBlazeProjectDataManager(
+ new BlazeProjectData(
+ 0L, buildTargetMap(), null, null, null, null, decoder, null, null, null));
+ classJarProvider = new BlazeClassJarProvider(getProject());
+ }
+
+ @Test
+ public void testFindModuleClassFile() {
+ createClassesInJars();
+
+ // Make sure we can find classes in the main resource module.
+ assertThat(classJarProvider.findModuleClassFile("com.google.example.main.MainActivity", module))
+ .isEqualTo(
+ fileSystem.findFile(
+ BLAZE_BIN
+ + "/com/google/example/main.jar!"
+ + "/com/google/example/main/MainActivity.class"));
+ assertThat(classJarProvider.findModuleClassFile("com.google.example.main.R", module))
+ .isEqualTo(
+ fileSystem.findFile(
+ BLAZE_BIN
+ + "/com/google/example/main_resources.jar!"
+ + "/com/google/example/main/R.class"));
+ assertThat(classJarProvider.findModuleClassFile("com.google.example.main.R$string", module))
+ .isEqualTo(
+ fileSystem.findFile(
+ BLAZE_BIN
+ + "/com/google/example/main_resources.jar!"
+ + "/com/google/example/main/R$string.class"));
+
+ // And not classes that are missing.
+ assertThat(classJarProvider.findModuleClassFile("com.google.example.main.MissingClass", module))
+ .isNull();
+ assertThat(classJarProvider.findModuleClassFile("com.google.example.main.R$missing", module))
+ .isNull();
+
+ // And not classes in other libraries.
+ assertThat(classJarProvider.findModuleClassFile("com.google.example.java.CustomView", module))
+ .isNull();
+ assertThat(classJarProvider.findModuleClassFile("com.google.example.android_res.R", module))
+ .isNull();
+ assertThat(
+ classJarProvider.findModuleClassFile("com.google.example.android_res.R$style", module))
+ .isNull();
+ assertThat(
+ classJarProvider.findModuleClassFile(
+ "com.google.unrelated.android_res.R$layout", module))
+ .isNull();
+ }
+
+ @Test
+ public void testMissingMainJar() {
+ createClassesInJars();
+
+ ApplicationManager.getApplication()
+ .runWriteAction(
+ () -> {
+ try {
+ // Let's pretend that this hasn't been built yet.
+ fileSystem.findFile(BLAZE_BIN + "/com/google/example/main.jar").delete(this);
+ } catch (IOException ignored) {
+ // ignored
+ }
+ });
+ // This hasn't been built yet, and shouldn't be found.
+ assertThat(classJarProvider.findModuleClassFile("com.google.example.main.MainActivity", module))
+ .isNull();
+ // But these should still be found.
+ assertThat(classJarProvider.findModuleClassFile("com.google.example.main.R", module))
+ .isEqualTo(
+ fileSystem.findFile(
+ BLAZE_BIN
+ + "/com/google/example/main_resources.jar!"
+ + "/com/google/example/main/R.class"));
+ assertThat(classJarProvider.findModuleClassFile("com.google.example.main.R$string", module))
+ .isEqualTo(
+ fileSystem.findFile(
+ BLAZE_BIN
+ + "/com/google/example/main_resources.jar!"
+ + "/com/google/example/main/R$string.class"));
+ }
+
+ @Test
+ public void testGetModuleExternalLibraries() {
+ // Need AndroidFact for AppResourceRepository.
+ ApplicationManager.getApplication()
+ .runWriteAction(
+ () -> {
+ ModifiableFacetModel model = FacetManager.getInstance(module).createModifiableModel();
+ model.addFacet(new MockAndroidFacet(module));
+ model.commit();
+ });
+
+ List<VirtualFile> externalLibraries = classJarProvider.getModuleExternalLibraries(module);
+ assertThat(externalLibraries)
+ .containsExactly(
+ fileSystem.findFile(BLAZE_BIN + "/com/google/example/android_lib.jar"),
+ fileSystem.findFile(BLAZE_BIN + "/com/google/example/android_res.jar"),
+ fileSystem.findFile(BLAZE_BIN + "/com/google/example/android_res2.jar"),
+ fileSystem.findFile(BLAZE_BIN + "/com/google/example/transitive/android_res.jar"),
+ fileSystem.findFile(BLAZE_BIN + "/com/google/example/java.jar"),
+ fileSystem.findFile(BLAZE_BIN + "/com/google/example/transitive/java.jar"),
+ fileSystem.findFile(BLAZE_BIN + "/com/google/example/shared/java.jar"),
+ fileSystem.findFile(BLAZE_BIN + "/com/google/example/shared2/java.jar"),
+ fileSystem.findFile("com/google/example/import.jar"),
+ fileSystem.findFile("com/google/example/transitive/import.jar"),
+ fileSystem.findFile("com/google/example/transitive/import2.jar"));
+
+ // Make sure we can generate dynamic classes from all resource packages in dependencies.
+ ResourceClassRegistry registry = ResourceClassRegistry.get(getProject());
+ AppResourceRepository repository = AppResourceRepository.getAppResources(module, false);
+ assertThat(repository).isNotNull();
+ assertThat(registry.findClassDefinition("com.google.example.android_res.R", repository))
+ .isNotNull();
+ assertThat(registry.findClassDefinition("com.google.example.android_res.R$string", repository))
+ .isNotNull();
+ assertThat(registry.findClassDefinition("com.google.example.android_res2.R", repository))
+ .isNotNull();
+ assertThat(registry.findClassDefinition("com.google.example.android_res2.R$layout", repository))
+ .isNotNull();
+ assertThat(
+ registry.findClassDefinition("com.google.example.transitive.android_res.R", repository))
+ .isNotNull();
+ assertThat(
+ registry.findClassDefinition(
+ "com.google.example.transitive.android_res.R$style", repository))
+ .isNotNull();
+
+ // And nothing else.
+ assertThat(registry.findClassDefinition("com.google.example.main.MainActivity", repository))
+ .isNull();
+ assertThat(registry.findClassDefinition("com.google.example.android_res.Bogus", repository))
+ .isNull();
+ assertThat(registry.findClassDefinition("com.google.example.main.R", repository)).isNull();
+ assertThat(registry.findClassDefinition("com.google.example.main.R$string", repository))
+ .isNull();
+ assertThat(registry.findClassDefinition("com.google.example.java.CustomView", repository))
+ .isNull();
+ assertThat(registry.findClassDefinition("com.google.unrelated.android_res.R", repository))
+ .isNull();
+ assertThat(
+ registry.findClassDefinition("com.google.unrelated.android_res.R$layout", repository))
+ .isNull();
+ }
+
+ @Test
+ public void testMissingExternalJars() {
+ ApplicationManager.getApplication()
+ .runWriteAction(
+ () -> {
+ try {
+ // Let's pretend that these haven't been built yet.
+ fileSystem.findFile(BLAZE_BIN + "/com/google/example/java.jar").delete(this);
+ fileSystem
+ .findFile(BLAZE_BIN + "/com/google/example/android_res2.jar")
+ .delete(this);
+ fileSystem
+ .findFile(BLAZE_BIN + "/com/google/example/shared2/java.jar")
+ .delete(this);
+ } catch (IOException ignored) {
+ // ignored
+ }
+ });
+ List<VirtualFile> externalLibraries = classJarProvider.getModuleExternalLibraries(module);
+ assertThat(externalLibraries)
+ .containsExactly(
+ fileSystem.findFile(BLAZE_BIN + "/com/google/example/android_lib.jar"),
+ fileSystem.findFile(BLAZE_BIN + "/com/google/example/android_res.jar"),
+ // This should be missing.
+ // fileSystem.findFile(BLAZE_BIN + "/com/google/example/android_res2.jar"),
+ fileSystem.findFile(BLAZE_BIN + "/com/google/example/transitive/android_res.jar"),
+ // This should be missing.
+ // fileSystem.findFile(BLAZE_BIN + "/com/google/example/java.jar"),
+ fileSystem.findFile(BLAZE_BIN + "/com/google/example/transitive/java.jar"),
+ fileSystem.findFile(BLAZE_BIN + "/com/google/example/shared/java.jar"),
+ // This should be missing.
+ // fileSystem.findFile(BLAZE_BIN + "/com/google/example/shared2/java.jar"),
+ fileSystem.findFile("com/google/example/import.jar"),
+ fileSystem.findFile("com/google/example/transitive/import.jar"),
+ fileSystem.findFile("com/google/example/transitive/import2.jar"));
+ }
+
+ private void createClassesInJars() {
+ fileSystem.createFile(
+ BLAZE_BIN
+ + "/com/google/example/main.jar!"
+ + "/com/google/example/main/MainActivity.class");
+ fileSystem.createFile(
+ BLAZE_BIN + "/com/google/example/main_resources.jar!" + "/com/google/example/main/R.class");
+ fileSystem.createFile(
+ BLAZE_BIN
+ + "/com/google/example/main_resources.jar!"
+ + "/com/google/example/main/R$string.class");
+ fileSystem.createFile(
+ BLAZE_BIN + "/com/google/example/java.jar!" + "/com/google/example/java/CustomView.class");
+ fileSystem.createFile(
+ BLAZE_BIN
+ + "/com/google/example/android_res_resources.jar!"
+ + "/com/google/example/android_res/R.class");
+ fileSystem.createFile(
+ BLAZE_BIN
+ + "/com/google/example/android_res_resources.jar!"
+ + "/com/google/example/android_res/R$style.class");
+ fileSystem.createFile(
+ BLAZE_BIN
+ + "/com/google/unrelated/android_res_resources.jar!"
+ + "/com/google/unrelated/android_res/R$layout.class");
+ }
+
+ private TargetMap buildTargetMap() {
+ Label mainResourceLibrary = new Label("//com/google/example:main");
+ Label androidLibraryDependency = new Label("//com/google/example:android_lib");
+ Label androidResourceDependency = new Label("//com/google/example:android_res");
+ Label androidResourceDependency2 = new Label("//com/google/example:android_res2");
+ Label transitiveResourceDependency = new Label("//com/google/example/transitive:android_res");
+ Label javaDependency = new Label("//com/google/example:java");
+ Label transitiveJavaDependency = new Label("//com/google/example/transitive:java");
+ Label sharedJavaDependency = new Label("//com/google/example/shared:java");
+ Label sharedJavaDependency2 = new Label("//com/google/example/shared2:java");
+ Label importDependency = new Label("//com/google/example:import");
+ Label transitiveImportDependency = new Label("//com/google/example/transitive:import");
+ Label unrelatedJava = new Label("//com/google/unrelated:java");
+ Label unrelatedAndroidLibrary = new Label("//com/google/unrelated:android_lib");
+ Label unrelatedAndroidResource = new Label("//com/google/unrelated:android_res");
+
+ AndroidResourceModuleRegistry registry = new AndroidResourceModuleRegistry();
+ registry.put(
+ module,
+ AndroidResourceModule.builder(TargetKey.forPlainTarget(mainResourceLibrary)).build());
+ // Not using these, but they should be in the registry.
+ registry.put(
+ mock(Module.class),
+ AndroidResourceModule.builder(TargetKey.forPlainTarget(androidResourceDependency)).build());
+ registry.put(
+ mock(Module.class),
+ AndroidResourceModule.builder(TargetKey.forPlainTarget(androidResourceDependency2))
+ .build());
+ registry.put(
+ mock(Module.class),
+ AndroidResourceModule.builder(TargetKey.forPlainTarget(transitiveResourceDependency))
+ .build());
+ registry.put(
+ mock(Module.class),
+ AndroidResourceModule.builder(TargetKey.forPlainTarget(unrelatedAndroidResource)).build());
+ registerProjectService(AndroidResourceModuleRegistry.class, registry);
+
+ return TargetMapBuilder.builder()
+ .addTarget(
+ TargetIdeInfo.builder()
+ .setLabel(mainResourceLibrary)
+ .setKind(Kind.ANDROID_LIBRARY)
+ .setJavaInfo(
+ javaInfoWithJars(
+ "com/google/example/main.jar", "com/google/example/main_resources.jar"))
+ .setAndroidInfo(
+ androidInfoWithResourceAndJar(
+ "com.google.example.main",
+ "com/google/example/main/res",
+ "com/google/example/main_resources.jar"))
+ .addDependency(androidLibraryDependency)
+ .addDependency(androidResourceDependency)
+ .addDependency(androidResourceDependency2)
+ .addDependency(javaDependency)
+ .addDependency(importDependency))
+ .addTarget(
+ TargetIdeInfo.builder()
+ .setLabel(androidLibraryDependency)
+ .setKind(Kind.ANDROID_LIBRARY)
+ .setJavaInfo(javaInfoWithJars("com/google/example/android_lib.jar"))
+ .addDependency(transitiveResourceDependency))
+ .addTarget(
+ TargetIdeInfo.builder()
+ .setLabel(androidResourceDependency)
+ .setKind(Kind.ANDROID_LIBRARY)
+ .setJavaInfo(
+ javaInfoWithJars(
+ "com/google/example/android_res.jar",
+ "com/google/example/android_res_resources.jar"))
+ .setAndroidInfo(
+ androidInfoWithResourceAndJar(
+ "com.google.example.android_res",
+ "com/google/example/android_res/res",
+ "com/google/example/android_res_resources.jar")))
+ .addTarget(
+ TargetIdeInfo.builder()
+ .setLabel(androidResourceDependency2)
+ .setKind(Kind.ANDROID_LIBRARY)
+ .setJavaInfo(
+ javaInfoWithJars(
+ "com/google/example/android_res2.jar",
+ "com/google/example/android_res2_resources.jar"))
+ .setAndroidInfo(
+ androidInfoWithResourceAndJar(
+ "com.google.example.android_res2",
+ "com/google/example/android_res2/res",
+ "com/google/example/android_res2_resources.jar")))
+ .addTarget(
+ TargetIdeInfo.builder()
+ .setLabel(transitiveResourceDependency)
+ .setKind(Kind.ANDROID_LIBRARY)
+ .setJavaInfo(
+ javaInfoWithJars(
+ "com/google/example/transitive/android_res.jar",
+ "com/google/example/transitive/android_res_resources.jar"))
+ .setAndroidInfo(
+ androidInfoWithResourceAndJar(
+ "com.google.example.transitive.android_res",
+ "com/google/example/transitive/android_res/res",
+ "com/google/example/transitive/android_res_resources.jar")))
+ .addTarget(
+ TargetIdeInfo.builder()
+ .setLabel(javaDependency)
+ .setKind(Kind.JAVA_LIBRARY)
+ .setJavaInfo(javaInfoWithJars("com/google/example/java.jar"))
+ .addDependency(transitiveJavaDependency)
+ .addDependency(sharedJavaDependency)
+ .addDependency(sharedJavaDependency2)
+ .addDependency(transitiveImportDependency))
+ .addTarget(
+ TargetIdeInfo.builder()
+ .setLabel(transitiveJavaDependency)
+ .setKind(Kind.JAVA_LIBRARY)
+ .setJavaInfo(javaInfoWithJars("com/google/example/transitive/java.jar"))
+ .addDependency(sharedJavaDependency)
+ .addDependency(sharedJavaDependency2))
+ .addTarget(
+ TargetIdeInfo.builder()
+ .setLabel(sharedJavaDependency)
+ .setKind(Kind.JAVA_LIBRARY)
+ .setJavaInfo(javaInfoWithJars("com/google/example/shared/java.jar"))
+ .addDependency(sharedJavaDependency2))
+ .addTarget(
+ TargetIdeInfo.builder()
+ .setLabel(sharedJavaDependency2)
+ .setKind(Kind.JAVA_LIBRARY)
+ .setJavaInfo(javaInfoWithJars("com/google/example/shared2/java.jar")))
+ .addTarget(
+ TargetIdeInfo.builder()
+ .setLabel(importDependency)
+ .setKind(Kind.JAVA_IMPORT)
+ .setJavaInfo(javaInfoWithCheckedInJars("com/google/example/import.jar")))
+ .addTarget(
+ TargetIdeInfo.builder()
+ .setLabel(transitiveImportDependency)
+ .setKind(Kind.JAVA_IMPORT)
+ .setJavaInfo(
+ javaInfoWithCheckedInJars(
+ "com/google/example/transitive/import.jar",
+ "com/google/example/transitive/import2.jar")))
+ .addTarget(
+ TargetIdeInfo.builder()
+ .setLabel(unrelatedJava)
+ .setKind(Kind.JAVA_LIBRARY)
+ .setJavaInfo(javaInfoWithJars("com/google/unrelated/java.jar")))
+ .addTarget(
+ TargetIdeInfo.builder()
+ .setLabel(unrelatedAndroidLibrary)
+ .setKind(Kind.ANDROID_LIBRARY)
+ .setJavaInfo(javaInfoWithJars("com/google/unrelated/android_lib.jar")))
+ .addTarget(
+ TargetIdeInfo.builder()
+ .setLabel(unrelatedAndroidResource)
+ .setKind(Kind.ANDROID_LIBRARY)
+ .setJavaInfo(
+ javaInfoWithJars(
+ "com/google/unrelated/android_res.jar",
+ "com/google/unrelated/android_res_resources.jar"))
+ .setAndroidInfo(
+ androidInfoWithResourceAndJar(
+ "com.google.unrelated.android_res",
+ "com/google/unrelated/android_res/res",
+ "com/google/unrelated/android_res_resources.jar")))
+ .build();
+ }
+
+ private JavaIdeInfo.Builder javaInfoWithJars(String... relativeJarPaths) {
+ JavaIdeInfo.Builder builder = JavaIdeInfo.builder();
+ for (String relativeJarPath : relativeJarPaths) {
+ ArtifactLocation jar =
+ ArtifactLocation.builder()
+ .setRootExecutionPathFragment(BLAZE_BIN)
+ .setRelativePath(relativeJarPath)
+ .setIsSource(false)
+ .build();
+ builder.addJar(LibraryArtifact.builder().setClassJar(jar));
+ fileSystem.createFile(jar.getExecutionRootRelativePath());
+ }
+ return builder;
+ }
+
+ private JavaIdeInfo.Builder javaInfoWithCheckedInJars(String... relativeJarPaths) {
+ JavaIdeInfo.Builder builder = JavaIdeInfo.builder();
+ for (String relativeJarPath : relativeJarPaths) {
+ ArtifactLocation jar =
+ ArtifactLocation.builder().setRelativePath(relativeJarPath).setIsSource(true).build();
+ builder.addJar(LibraryArtifact.builder().setClassJar(jar));
+ fileSystem.createFile(jar.getExecutionRootRelativePath());
+ }
+ return builder;
+ }
+
+ private static AndroidIdeInfo.Builder androidInfoWithResourceAndJar(
+ String javaPackage, String relativeResourcePath, String relativeJarPath) {
+ return AndroidIdeInfo.builder()
+ .setGenerateResourceClass(true)
+ .setResourceJavaPackage(javaPackage)
+ .addResource(
+ ArtifactLocation.builder()
+ .setRelativePath(relativeResourcePath)
+ .setIsSource(true)
+ .build())
+ .setResourceJar(
+ LibraryArtifact.builder()
+ .setClassJar(
+ // No need to createFile for this one since it should also be in the Java info.
+ ArtifactLocation.builder()
+ .setRootExecutionPathFragment(BLAZE_BIN)
+ .setRelativePath(relativeJarPath)
+ .setIsSource(false)
+ .build()));
+ }
+
+ private static class MockAndroidFacet extends AndroidFacet {
+ public MockAndroidFacet(Module module) {
+ super(module, AndroidFacet.NAME, new AndroidFacetConfiguration());
+ }
+
+ @Override
+ public void initFacet() {
+ // We don't need this, but it causes trouble when it tries looking for project templates.
+ }
+ }
+}
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
new file mode 100755
index 0000000..72541b3
--- /dev/null
+++ b/aswb/2.3/tests/unittests/com/google/idea/blaze/android/project/BlazeBuildSystemServiceTest.java
@@ -0,0 +1,263 @@
+/*
+ * 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.android.project;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import com.android.tools.idea.project.BuildSystemService;
+import com.google.common.collect.Maps;
+import com.google.idea.blaze.android.sync.model.AndroidResourceModule;
+import com.google.idea.blaze.android.sync.model.AndroidResourceModuleRegistry;
+import com.google.idea.blaze.base.BlazeTestCase;
+import com.google.idea.blaze.base.actions.BlazeBuildService;
+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.ideinfo.TargetMapBuilder;
+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.projectview.ProjectViewManager;
+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.BlazeImportSettings;
+import com.google.idea.blaze.base.settings.BlazeImportSettingsManager;
+import com.google.idea.blaze.base.sync.BlazeSyncManager;
+import com.google.idea.blaze.base.sync.BlazeSyncPlugin.ModuleEditor;
+import com.google.idea.blaze.base.sync.data.BlazeProjectDataManager;
+import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoder;
+import com.google.idea.blaze.base.sync.workspace.WorkspacePathResolver;
+import com.intellij.mock.MockModule;
+import com.intellij.mock.MockVirtualFile;
+import com.intellij.openapi.editor.LazyRangeMarkerFactory;
+import com.intellij.openapi.editor.impl.LazyRangeMarkerFactoryImpl;
+import com.intellij.openapi.extensions.ExtensionPoint;
+import com.intellij.openapi.extensions.ExtensionPointName;
+import com.intellij.openapi.fileEditor.FileEditorManager;
+import com.intellij.openapi.fileEditor.OpenFileDescriptor;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.ex.temp.TempFileSystem;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import java.io.File;
+import java.util.Map;
+import javax.annotation.Nullable;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.ArgumentCaptor;
+
+/** Test cases for {@link BlazeBuildSystemService}. */
+@RunWith(JUnit4.class)
+public class BlazeBuildSystemServiceTest extends BlazeTestCase {
+ Module module;
+ BuildSystemService service;
+
+ @Override
+ protected void initTest(Container applicationServices, Container projectServices) {
+ module = new MockModule(project, () -> {});
+
+ mockBlazeImportSettings(projectServices); // For Blaze.isBlazeProject.
+ createMocksForBuildProject(applicationServices);
+ createMocksForSyncProject(projectServices);
+ createMocksForAddDependency(applicationServices, projectServices);
+
+ ExtensionPoint<BuildSystemService> extensionPoint =
+ registerExtensionPoint(
+ ExtensionPointName.create("com.android.project.buildSystemService"),
+ BuildSystemService.class);
+ extensionPoint.registerExtension(new BlazeBuildSystemService());
+
+ service = BuildSystemService.getInstance(project);
+ }
+
+ @Test
+ public void testIsBlazeBuildSystemService() {
+ assertThat(service).isInstanceOf(BlazeBuildSystemService.class);
+ }
+
+ @Test
+ public void testBuildProject() {
+ service.buildProject(project);
+ verify(BlazeBuildService.getInstance()).buildProject(project);
+ verifyNoMoreInteractions(BlazeBuildService.getInstance());
+ }
+
+ @Test
+ public void testSyncProject() {
+ service.syncProject(project);
+ verify(BlazeSyncManager.getInstance(project)).incrementalProjectSync();
+ verifyNoMoreInteractions(BlazeSyncManager.getInstance(project));
+ }
+
+ @Test
+ public void testAddDependencyWithBuildTargetPsi() throws Exception {
+ PsiElement buildTargetPsi = mock(PsiElement.class);
+ PsiFile psiFile = mock(PsiFile.class);
+
+ BuildReferenceManager buildReferenceManager = BuildReferenceManager.getInstance(project);
+ when(buildReferenceManager.resolveLabel(new Label("//foo:bar"))).thenReturn(buildTargetPsi);
+ when(buildTargetPsi.getContainingFile()).thenReturn(psiFile);
+ when(buildTargetPsi.getTextOffset()).thenReturn(1337);
+
+ VirtualFile buildFile = TempFileSystem.getInstance().findFileByPath("/foo/BUILD");
+ assertThat(buildFile).isNotNull();
+ when(psiFile.getVirtualFile()).thenReturn(buildFile);
+
+ String dependency = "com.android.foo:bar"; // Doesn't matter.
+
+ service.addDependency(module, dependency);
+
+ ArgumentCaptor<OpenFileDescriptor> descriptorCaptor =
+ ArgumentCaptor.forClass(OpenFileDescriptor.class);
+ verify(FileEditorManager.getInstance(project))
+ .openTextEditor(descriptorCaptor.capture(), eq(true));
+ OpenFileDescriptor descriptor = descriptorCaptor.getValue();
+ assertThat(descriptor.getProject()).isEqualTo(project);
+ assertThat(descriptor.getFile()).isEqualTo(buildFile);
+ assertThat(descriptor.getOffset()).isEqualTo(1337);
+ verifyNoMoreInteractions(FileEditorManager.getInstance(project));
+ }
+
+ @Test
+ public void testAddDependencyWithoutBuildTargetPsi() throws Exception {
+ // Can't find PSI for the target.
+ when(BuildReferenceManager.getInstance(project).resolveLabel(new Label("//foo:bar")))
+ .thenReturn(null);
+
+ VirtualFile buildFile = TempFileSystem.getInstance().findFileByPath("/foo/BUILD");
+ assertThat(buildFile).isNotNull();
+
+ String dependency = "com.android.foo:bar"; // Doesn't matter.
+
+ service.addDependency(module, dependency);
+
+ verify(FileEditorManager.getInstance(project)).openFile(buildFile, true);
+ verifyNoMoreInteractions(FileEditorManager.getInstance(project));
+ }
+
+ private void mockBlazeImportSettings(Container projectServices) {
+ BlazeImportSettingsManager importSettingsManager = new BlazeImportSettingsManager(project);
+ importSettingsManager.setImportSettings(
+ new BlazeImportSettings("", "", "", "", "", Blaze.BuildSystem.Blaze));
+ projectServices.register(BlazeImportSettingsManager.class, importSettingsManager);
+ }
+
+ private static void createMocksForBuildProject(Container applicationServices) {
+ applicationServices.register(BlazeBuildService.class, mock(BlazeBuildService.class));
+ }
+
+ private static void createMocksForSyncProject(Container projectServices) {
+ projectServices.register(ProjectViewManager.class, new MockProjectViewManager());
+ projectServices.register(BlazeSyncManager.class, mock(BlazeSyncManager.class));
+ }
+
+ private void createMocksForAddDependency(
+ Container applicationServices, Container projectServices) {
+ projectServices.register(BlazeProjectDataManager.class, new MockProjectDataManager());
+ projectServices.register(FileEditorManager.class, mock(FileEditorManager.class));
+ projectServices.register(BuildReferenceManager.class, mock(BuildReferenceManager.class));
+ projectServices.register(LazyRangeMarkerFactory.class, mock(LazyRangeMarkerFactoryImpl.class));
+
+ applicationServices.register(TempFileSystem.class, new MockFileSystem("/foo/BUILD"));
+
+ AndroidResourceModuleRegistry moduleRegistry = new AndroidResourceModuleRegistry();
+ moduleRegistry.put(
+ module,
+ AndroidResourceModule.builder(TargetKey.forPlainTarget(new Label("//foo:bar"))).build());
+ projectServices.register(AndroidResourceModuleRegistry.class, moduleRegistry);
+ }
+
+ private static class MockProjectViewManager extends ProjectViewManager {
+ private ProjectViewSet viewSet;
+
+ public MockProjectViewManager() {
+ this.viewSet = ProjectViewSet.builder().build();
+ }
+
+ @Nullable
+ @Override
+ public ProjectViewSet getProjectViewSet() {
+ return viewSet;
+ }
+
+ @Nullable
+ @Override
+ public ProjectViewSet reloadProjectView(
+ BlazeContext context, WorkspacePathResolver workspacePathResolver) {
+ return viewSet;
+ }
+ }
+
+ private static class MockProjectDataManager implements BlazeProjectDataManager {
+ private BlazeProjectData projectData;
+
+ public MockProjectDataManager() {
+ TargetMap targetMap =
+ TargetMapBuilder.builder()
+ .addTarget(
+ TargetIdeInfo.builder()
+ .setLabel(new Label("//foo:bar"))
+ .setBuildFile(ArtifactLocation.builder().setRelativePath("foo/BUILD").build())
+ .build())
+ .build();
+ ArtifactLocationDecoder decoder = (location) -> new File("/", location.getRelativePath());
+
+ projectData =
+ new BlazeProjectData(0L, targetMap, null, null, null, null, decoder, null, null, null);
+ }
+
+ @Nullable
+ @Override
+ public BlazeProjectData getBlazeProjectData() {
+ return projectData;
+ }
+
+ @Override
+ public ModuleEditor editModules() {
+ return null;
+ }
+ }
+
+ private static class MockFileSystem extends TempFileSystem {
+ private Map<String, VirtualFile> files;
+
+ public MockFileSystem(String... paths) {
+ files = Maps.newHashMap();
+ for (String path : paths) {
+ files.put(path, new MockVirtualFile(path));
+ }
+ }
+
+ @Override
+ public VirtualFile findFileByPath(String path) {
+ return files.get(path);
+ }
+
+ @Override
+ public VirtualFile findFileByIoFile(File file) {
+ return findFileByPath(file.getPath());
+ }
+ }
+}
diff --git a/aswb/2.3/tests/unittests/com/google/idea/blaze/android/project/BlazeFeatureEnabledServiceTest.java b/aswb/2.3/tests/unittests/com/google/idea/blaze/android/project/BlazeFeatureEnabledServiceTest.java
new file mode 100644
index 0000000..e4fe775
--- /dev/null
+++ b/aswb/2.3/tests/unittests/com/google/idea/blaze/android/project/BlazeFeatureEnabledServiceTest.java
@@ -0,0 +1,158 @@
+/*
+ * 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.android.project;
+
+import static com.google.common.truth.Truth.THROW_ASSERTION_ERROR;
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.mock;
+
+import com.android.tools.idea.project.FeatureEnableService;
+import com.google.common.collect.ImmutableList;
+import com.google.idea.blaze.android.settings.BlazeAndroidUserSettings;
+import com.google.idea.blaze.base.BlazeTestCase;
+import com.google.idea.blaze.base.model.BlazeProjectData;
+import com.google.idea.blaze.base.settings.Blaze.BuildSystem;
+import com.google.idea.blaze.base.settings.BlazeImportSettings;
+import com.google.idea.blaze.base.settings.BlazeImportSettingsManager;
+import com.google.idea.blaze.base.settings.BlazeImportSettingsManagerLegacy;
+import com.google.idea.blaze.base.sync.BlazeSyncPlugin.ModuleEditor;
+import com.google.idea.blaze.base.sync.data.BlazeProjectDataManager;
+import com.google.idea.common.experiments.ExperimentService;
+import com.intellij.openapi.extensions.ExtensionPoint;
+import com.intellij.openapi.extensions.ExtensionPointName;
+import javax.annotation.Nullable;
+import org.jetbrains.annotations.NotNull;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Test cases for {@link BlazeFeatureEnableService}. */
+@RunWith(JUnit4.class)
+public class BlazeFeatureEnabledServiceTest extends BlazeTestCase {
+ private MockExperimentService experimentService;
+ private BlazeAndroidUserSettings userSettings;
+ private MockBlazeProjectDataManager projectDataManager;
+ private FeatureEnableService featureEnableService;
+
+ @Override
+ protected void initTest(
+ @NotNull Container applicationServices, @NotNull Container projectServices) {
+ super.initTest(applicationServices, projectServices);
+ experimentService = new MockExperimentService();
+ applicationServices.register(ExperimentService.class, experimentService);
+
+ userSettings = new BlazeAndroidUserSettings();
+ applicationServices.register(BlazeAndroidUserSettings.class, userSettings);
+
+ projectDataManager = new MockBlazeProjectDataManager();
+ projectServices.register(BlazeProjectDataManager.class, projectDataManager);
+
+ BlazeImportSettingsManager importSettingsManager = new BlazeImportSettingsManager(project);
+ importSettingsManager.setImportSettings(
+ new BlazeImportSettings(null, null, null, null, null, BuildSystem.Blaze));
+ projectServices.register(BlazeImportSettingsManager.class, importSettingsManager);
+ projectServices.register(
+ BlazeImportSettingsManagerLegacy.class, new BlazeImportSettingsManagerLegacy(project));
+
+ ExtensionPoint<FeatureEnableService> extensionPoint =
+ registerExtensionPoint(
+ ExtensionPointName.create("com.android.project.featureEnableService"),
+ FeatureEnableService.class);
+ extensionPoint.registerExtension(new BlazeFeatureEnableService());
+
+ featureEnableService = FeatureEnableService.getInstance(project);
+ }
+
+ @Test
+ public void testIsBlazeFeatureEnableService() {
+ assertThat(featureEnableService).isInstanceOf(BlazeFeatureEnableService.class);
+ }
+
+ @Test
+ public void testIsNotBlazeFeatureEnableService() {
+ BlazeImportSettingsManager.getInstance(project).loadState(null);
+ assertThat(FeatureEnableService.getInstance(project))
+ .isNotInstanceOf(BlazeFeatureEnableService.class);
+ }
+
+ @Test
+ public void testGetLayoutEditorEnabled() {
+ for (boolean experimentEnabled : ImmutableList.of(true, false)) {
+ for (boolean settingEnabled : ImmutableList.of(true, false)) {
+ for (boolean projectDataReady : ImmutableList.of(true, false)) {
+ userSettings.setUseLayoutEditor(settingEnabled);
+ experimentService.setEnableLayoutEditor(experimentEnabled);
+ projectDataManager.setBlazeProjectData(
+ projectDataReady ? mock(BlazeProjectData.class) : null);
+ assertThat(featureEnableService.isLayoutEditorEnabled(project))
+ .isEqualTo(settingEnabled && experimentEnabled && projectDataReady);
+ }
+ }
+ }
+ }
+
+ private static class MockBlazeProjectDataManager implements BlazeProjectDataManager {
+ private BlazeProjectData blazeProjectData;
+
+ public void setBlazeProjectData(BlazeProjectData blazeProjectData) {
+ this.blazeProjectData = blazeProjectData;
+ }
+
+ @Nullable
+ @Override
+ public BlazeProjectData getBlazeProjectData() {
+ return blazeProjectData;
+ }
+
+ @Override
+ public ModuleEditor editModules() {
+ return null;
+ }
+ }
+
+ private static class MockExperimentService implements ExperimentService {
+ private boolean enableLayoutEditor;
+
+ public void setEnableLayoutEditor(boolean enableLayoutEditor) {
+ this.enableLayoutEditor = enableLayoutEditor;
+ }
+
+ @Override
+ public boolean getExperiment(String key, boolean defaultValue) {
+ assertThat(key).isEqualTo("enable.layout.editor");
+ return enableLayoutEditor;
+ }
+
+ @Nullable
+ @Override
+ public String getExperimentString(String key, @Nullable String defaultValue) {
+ THROW_ASSERTION_ERROR.fail("Should not be called.");
+ return null;
+ }
+
+ @Override
+ public int getExperimentInt(String key, int defaultValue) {
+ THROW_ASSERTION_ERROR.fail("Should not be called.");
+ return 0;
+ }
+
+ @Override
+ public void startExperimentScope() {}
+
+ @Override
+ public void endExperimentScope() {}
+ }
+}
diff --git a/aswb/2.3/tests/unittests/com/google/idea/blaze/android/rendering/BlazeRenderErrorContributorTest.java b/aswb/2.3/tests/unittests/com/google/idea/blaze/android/rendering/BlazeRenderErrorContributorTest.java
new file mode 100644
index 0000000..5bbeec6
--- /dev/null
+++ b/aswb/2.3/tests/unittests/com/google/idea/blaze/android/rendering/BlazeRenderErrorContributorTest.java
@@ -0,0 +1,768 @@
+/*
+ * 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.android.rendering;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.mock;
+
+import com.android.tools.idea.rendering.RenderErrorContributor;
+import com.android.tools.idea.rendering.RenderErrorModelFactory;
+import com.android.tools.idea.rendering.RenderResult;
+import com.android.tools.idea.rendering.errors.ui.RenderErrorModel;
+import com.google.common.collect.ImmutableCollection;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+import com.google.idea.blaze.android.sync.model.AndroidResourceModule;
+import com.google.idea.blaze.android.sync.model.AndroidResourceModuleRegistry;
+import com.google.idea.blaze.base.BlazeTestCase;
+import com.google.idea.blaze.base.ideinfo.AndroidIdeInfo;
+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.TargetKey;
+import com.google.idea.blaze.base.ideinfo.TargetMap;
+import com.google.idea.blaze.base.ideinfo.TargetMapBuilder;
+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.settings.Blaze.BuildSystem;
+import com.google.idea.blaze.base.settings.BlazeImportSettings;
+import com.google.idea.blaze.base.settings.BlazeImportSettingsManager;
+import com.google.idea.blaze.base.settings.BlazeImportSettingsManagerLegacy;
+import com.google.idea.blaze.base.sync.BlazeSyncPlugin.ModuleEditor;
+import com.google.idea.blaze.base.sync.data.BlazeProjectDataManager;
+import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoder;
+import com.google.idea.blaze.base.targetmaps.SourceToTargetMap;
+import com.google.idea.blaze.base.targetmaps.TransitiveDependencyMap;
+import com.intellij.mock.MockModule;
+import com.intellij.mock.MockPsiFile;
+import com.intellij.mock.MockPsiManager;
+import com.intellij.mock.MockVirtualFile;
+import com.intellij.openapi.extensions.ExtensionPoint;
+import com.intellij.openapi.extensions.ExtensionPointName;
+import com.intellij.openapi.fileTypes.FileTypeManager;
+import com.intellij.openapi.fileTypes.MockFileTypeManager;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleUtilCore;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VfsUtilCore;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.JavaPsiFacade;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.PsiManager;
+import com.intellij.psi.impl.JavaPsiFacadeImpl;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.psi.search.ProjectScopeBuilder;
+import com.intellij.psi.search.ProjectScopeBuilderImpl;
+import com.intellij.psi.util.PsiUtil.NullPsiClass;
+import java.io.File;
+import java.util.stream.Collectors;
+import javax.annotation.Nullable;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Unit tests for {@link BlazeRenderErrorContributor}. */
+@RunWith(JUnit4.class)
+public class BlazeRenderErrorContributorTest extends BlazeTestCase {
+ private static final String BLAZE_BIN = "blaze-out/crosstool/bin";
+ private static final String GENERATED_RESOURCES_ERROR = "Generated resources";
+ private static final String NON_STANDARD_MANIFEST_NAME_ERROR = "Non-standard manifest name";
+ private static final String MISSING_CLASS_DEPENDENCIES_ERROR = "Missing class dependencies";
+
+ private Module module;
+ private MockBlazeProjectDataManager projectDataManager;
+ private BlazeRenderErrorContributor.BlazeProvider provider;
+
+ @Override
+ protected void initTest(Container applicationServices, Container projectServices) {
+ super.initTest(applicationServices, projectServices);
+ applicationServices.register(FileTypeManager.class, new MockFileTypeManager());
+
+ projectServices.register(BuildReferenceManager.class, new MockBuildReferenceManager(project));
+ projectServices.register(TransitiveDependencyMap.class, new TransitiveDependencyMap(project));
+ projectServices.register(ProjectScopeBuilder.class, new ProjectScopeBuilderImpl(project));
+ projectServices.register(
+ AndroidResourceModuleRegistry.class, new AndroidResourceModuleRegistry());
+
+ BlazeImportSettingsManager importSettingsManager = new BlazeImportSettingsManager(project);
+ BlazeImportSettings settings = new BlazeImportSettings("", "", "", "", "", BuildSystem.Blaze);
+ importSettingsManager.setImportSettings(settings);
+ projectServices.register(BlazeImportSettingsManager.class, importSettingsManager);
+ projectServices.register(
+ BlazeImportSettingsManagerLegacy.class, new BlazeImportSettingsManagerLegacy(project));
+
+ createPsiClassesAndSourceToTargetMap(projectServices);
+
+ projectDataManager = new MockBlazeProjectDataManager();
+ projectServices.register(BlazeProjectDataManager.class, projectDataManager);
+
+ ExtensionPoint<RenderErrorContributor.Provider> extensionPoint =
+ registerExtensionPoint(
+ ExtensionPointName.create("com.android.rendering.renderErrorContributor"),
+ RenderErrorContributor.Provider.class);
+ extensionPoint.registerExtension(new RenderErrorContributor.Provider());
+ extensionPoint.registerExtension(new BlazeRenderErrorContributor.BlazeProvider());
+
+ module = new MockModule(project, () -> {});
+
+ // For the isApplicable tests.
+ provider = new BlazeRenderErrorContributor.BlazeProvider();
+ }
+
+ @Test
+ public void testProviderIsApplicable() {
+ assertThat(provider.isApplicable(project)).isTrue();
+ }
+
+ @Test
+ public void testProviderNotApplicableIfNotBlaze() {
+ BlazeImportSettingsManager.getInstance(project).loadState(null);
+ assertThat(provider.isApplicable(project)).isFalse();
+ }
+
+ @Test
+ public void testNoIssuesIfNoErrors() {
+ PsiFile file = new MockPsiFile(new MockPsiManager(project));
+ file.putUserData(ModuleUtilCore.KEY_MODULE, module);
+ RenderResult result = RenderResult.createBlank(file);
+ RenderErrorModel errorModel = RenderErrorModelFactory.createErrorModel(result, null);
+ assertThat(errorModel.getIssues()).isEmpty();
+ }
+
+ @Test
+ public void testNoBlazeIssuesIfNoRelatedErrors() {
+ RenderErrorModel errorModel = createRenderErrorModelWithBrokenClasses();
+ errorModel
+ .getIssues()
+ .forEach(
+ issue ->
+ assertThat(issue.getSummary())
+ .isNoneOf(
+ GENERATED_RESOURCES_ERROR,
+ NON_STANDARD_MANIFEST_NAME_ERROR,
+ MISSING_CLASS_DEPENDENCIES_ERROR));
+ }
+
+ @Test
+ public void testReportGeneratedResources() {
+ createTargetMapWithGeneratedResources();
+ RenderErrorModel errorModel = createRenderErrorModelWithBrokenClasses();
+
+ RenderErrorModel.Issue generatedResourcesIssue =
+ Iterables.getOnlyElement(
+ errorModel
+ .getIssues()
+ .stream()
+ .filter(issue -> issue.getSummary().equals(GENERATED_RESOURCES_ERROR))
+ .collect(Collectors.toList()));
+
+ assertThat(generatedResourcesIssue.getHtmlContent())
+ .isEqualTo(
+ "Generated resources will not be discovered by the IDE:"
+ + "<DL>"
+ + "<DD>-&NBSP;"
+ + "com/google/example/dependency/generated/res "
+ + "from <A HREF=\"file:///src/com/google/example/dependency/BUILD\">"
+ + "//com/google/example:generated</A>"
+ + "<DD>-&NBSP;"
+ + "com/google/example/main/generated/res "
+ + "from <A HREF=\"file:///src/com/google/example/main/BUILD\">"
+ + "//com/google/example:main</A>"
+ + "<DD>-&NBSP;"
+ + "com/google/example/transitive/generated/one/res "
+ + "from <A HREF=\"file:///src/com/google/example/transitive/BUILD\">"
+ + "//com/google/example/transitive:generated</A>"
+ + "<DD>-&NBSP;"
+ + "com/google/example/transitive/generated/two/res "
+ + "from <A HREF=\"file:///src/com/google/example/transitive/BUILD\">"
+ + "//com/google/example/transitive:generated</A>"
+ + "</DL>"
+ + "Please avoid using generated resources, then "
+ + "<A HREF=\"action:sync\">sync the project</A> and "
+ + "<A HREF=\"refreshRender\">refresh the layout</A>.");
+ }
+
+ @Test
+ public void testReportNonStandardAndroidManifestName() {
+ createTargetMapWithNonStandardAndroidManifestName();
+ RenderErrorModel errorModel = createRenderErrorModelWithBrokenClasses();
+
+ RenderErrorModel.Issue nonStandardManifestNameIssue =
+ Iterables.getOnlyElement(
+ errorModel
+ .getIssues()
+ .stream()
+ .filter(issue -> issue.getSummary().equals(NON_STANDARD_MANIFEST_NAME_ERROR))
+ .collect(Collectors.toList()));
+
+ assertThat(nonStandardManifestNameIssue.getHtmlContent())
+ .isEqualTo(
+ "<A HREF=\"file:///src/com/google/example/main/BUILD\">"
+ + "//com/google/example:main</A> "
+ + "uses a non-standard name for the Android manifest: "
+ + "<A HREF=\"file:///src/com/google/example/main/WeirdManifest.xml\">"
+ + "WeirdManifest.xml</A>"
+ + "<BR/>"
+ + "Please rename it to AndroidManifest.xml, then "
+ + "<A HREF=\"action:sync\">sync the project</A> and "
+ + "<A HREF=\"refreshRender\">refresh the layout</A>.");
+ }
+
+ @Test
+ public void testNoReportNonStandardAndroidManifestNameInDependency() {
+ createTargetMapWithNonStandardAndroidManifestNameInDependency();
+ RenderErrorModel errorModel = createRenderErrorModelWithBrokenClasses();
+
+ errorModel
+ .getIssues()
+ .forEach(
+ issue -> assertThat(issue.getSummary()).isNotEqualTo(NON_STANDARD_MANIFEST_NAME_ERROR));
+ }
+
+ @Test
+ public void testReportMissingClassDependencies() {
+ createTargetMapWithMissingClassDependency();
+ RenderErrorModel errorModel =
+ createRenderErrorModelWithMissingClasses(
+ "com.google.example.independent.LibraryView",
+ "com.google.example.independent.LibraryView2",
+ "com.google.example.independent.Library2View",
+ "com.google.example.dependent.LibraryView",
+ "com.google.example.ResourceView");
+
+ RenderErrorModel.Issue missingClassDependenciesIssue =
+ Iterables.getOnlyElement(
+ errorModel
+ .getIssues()
+ .stream()
+ .filter(issue -> issue.getSummary().equals(MISSING_CLASS_DEPENDENCIES_ERROR))
+ .collect(Collectors.toList()));
+
+ assertThat(missingClassDependenciesIssue.getHtmlContent())
+ .isEqualTo(
+ "<A HREF=\"file:///src/com/google/example/BUILD\">"
+ + "//com/google/example:resources</A> "
+ + "contains resource files that reference these classes:"
+ + "<DL>"
+ + "<DD>-&NBSP;"
+ + "<A HREF=\"openClass:com.google.example.independent.Library2View\">"
+ + "com.google.example.independent.Library2View</A> "
+ + "from <A HREF=\"file:///src/com/google/example/BUILD\">"
+ + "//com/google/example/independent:library2</A> "
+ + "<DD>-&NBSP;"
+ + "<A HREF=\"openClass:com.google.example.independent.LibraryView\">"
+ + "com.google.example.independent.LibraryView</A> "
+ + "from <A HREF=\"file:///src/com/google/example/BUILD\">"
+ + "//com/google/example/independent:library</A> "
+ + "<DD>-&NBSP;"
+ + "<A HREF=\"openClass:com.google.example.independent.LibraryView2\">"
+ + "com.google.example.independent.LibraryView2</A> "
+ + "from <A HREF=\"file:///src/com/google/example/BUILD\">"
+ + "//com/google/example/independent:library</A> "
+ + "</DL>"
+ + "Please fix your dependencies so that "
+ + "<A HREF=\"file:///src/com/google/example/BUILD\">"
+ + "//com/google/example:resources</A> "
+ + "correctly depends on these classes, then "
+ + "<A HREF=\"action:sync\">sync the project</A> and "
+ + "<A HREF=\"refreshRender\">refresh the layout</A>."
+ + "<BR/>"
+ + "<BR/>"
+ + "<B>NOTE: blaze can still build with the incorrect dependencies "
+ + "due to the way it handles resources, "
+ + "but the layout editor needs them to be correct.</B>");
+ }
+
+ @Test
+ public void testNoReportMissingClassDependenciesIfClassInSameTarget() {
+ createTargetMapWithMissingClassDependency();
+ RenderErrorModel errorModel =
+ createRenderErrorModelWithMissingClasses("com.google.example.ResourceView");
+
+ errorModel
+ .getIssues()
+ .forEach(
+ issue -> assertThat(issue.getSummary()).isNotEqualTo(MISSING_CLASS_DEPENDENCIES_ERROR));
+ }
+
+ @Test
+ public void testNoReportMissingClassDependenciesIfClassInDependency() {
+ createTargetMapWithMissingClassDependency();
+ RenderErrorModel errorModel =
+ createRenderErrorModelWithMissingClasses("com.google.example.dependent.LibraryView");
+
+ errorModel
+ .getIssues()
+ .forEach(
+ issue -> assertThat(issue.getSummary()).isNotEqualTo(MISSING_CLASS_DEPENDENCIES_ERROR));
+ }
+
+ private RenderErrorModel createRenderErrorModelWithBrokenClasses() {
+ PsiFile file = new MockPsiFile(new MockPsiManager(project));
+ file.putUserData(ModuleUtilCore.KEY_MODULE, module);
+ RenderResult result = RenderResult.createBlank(file);
+ result
+ .getLogger()
+ .addBrokenClass("com.google.example.CustomView", new Exception("resource not found"));
+ return RenderErrorModelFactory.createErrorModel(result, null);
+ }
+
+ private RenderErrorModel createRenderErrorModelWithMissingClasses(String... classNames) {
+ PsiFile file = new MockPsiFile(new MockPsiManager(project));
+ file.putUserData(ModuleUtilCore.KEY_MODULE, module);
+ RenderResult result = RenderResult.createBlank(file);
+ for (String className : classNames) {
+ result.getLogger().addMissingClass(className);
+ }
+ return RenderErrorModelFactory.createErrorModel(result, null);
+ }
+
+ private static ArtifactLocation artifact(String relativePath, boolean isSource) {
+ return ArtifactLocation.builder()
+ .setIsSource(isSource)
+ .setRootExecutionPathFragment(isSource ? "" : BLAZE_BIN)
+ .setRelativePath(relativePath)
+ .build();
+ }
+
+ private void createTargetMapWithGeneratedResources() {
+ Label mainResourcesTarget = new Label("//com/google/example:main");
+ Label dependencyGeneratedResourceTarget = new Label("//com/google/example:generated");
+ Label dependencySourceResourceTarget = new Label("//com/google/example:source");
+ Label transitiveGeneratedResourcesTarget =
+ new Label("//com/google/example/transitive:generated");
+ Label transitiveSourceResourceTarget = new Label("//com/google/example/transitive:source");
+ Label unrelatedGeneratedResourceTarget = new Label("//com/google/unrelated:generated");
+ Label unrelatedSourceResourceTarget = new Label("//com/google/unrelated:source");
+
+ ArtifactLocation mainGeneratedResource =
+ artifact("com/google/example/main/generated/res", false);
+ ArtifactLocation mainSourceResource = artifact("com/google/example/main/source/res", true);
+ ArtifactLocation dependencyGeneratedResource =
+ artifact("com/google/example/dependency/generated/res", false);
+ ArtifactLocation dependencySourceResource =
+ artifact("com/google/example/dependency/source/res", true);
+ ArtifactLocation transitiveGeneratedResourceOne =
+ artifact("com/google/example/transitive/generated/one/res", false);
+ ArtifactLocation transitiveGeneratedResourceTwo =
+ artifact("com/google/example/transitive/generated/two/res", false);
+ ArtifactLocation transitiveSourceResource =
+ artifact("com/google/example/transitive/source/res", true);
+ ArtifactLocation unrelatedGeneratedResource =
+ artifact("com/google/unrelated/generated/res", false);
+ ArtifactLocation unrelatedSourceResource = artifact("com/google/unrelated/source/res", true);
+
+ ArtifactLocation mainBuildFile = artifact("com/google/example/main/BUILD", true);
+ ArtifactLocation dependencyBuildFile = artifact("com/google/example/dependency/BUILD", true);
+ ArtifactLocation transitiveBuildFile = artifact("com/google/example/transitive/BUILD", true);
+ ArtifactLocation unrelatedBuildFile = artifact("com/google/unrelated/BUILD", true);
+
+ AndroidResourceModuleRegistry registry = AndroidResourceModuleRegistry.getInstance(project);
+ registry.put(
+ module,
+ AndroidResourceModule.builder(TargetKey.forPlainTarget(mainResourcesTarget))
+ // .addResource(mainGeneratedResource) // Dropped.
+ .addResource(mainSourceResource)
+ .addTransitiveResourceDependency(dependencyGeneratedResourceTarget)
+ .addTransitiveResource(dependencyGeneratedResource)
+ .addTransitiveResourceDependency(dependencySourceResourceTarget)
+ .addTransitiveResource(dependencySourceResource)
+ .addTransitiveResourceDependency(transitiveGeneratedResourcesTarget)
+ .addTransitiveResource(transitiveGeneratedResourceOne)
+ .addTransitiveResource(transitiveGeneratedResourceTwo)
+ .addTransitiveResourceDependency(transitiveSourceResourceTarget)
+ .addTransitiveResource(transitiveSourceResource)
+ .build());
+ // Not using these, but they should be in the registry.
+ registry.put(
+ mock(Module.class),
+ AndroidResourceModule.builder(TargetKey.forPlainTarget(dependencyGeneratedResourceTarget))
+ // .addResource(dependencyGeneratedResource) // Dropped.
+ .addTransitiveResourceDependency(transitiveSourceResourceTarget)
+ .addTransitiveResource(transitiveSourceResource)
+ .build());
+ registry.put(
+ mock(Module.class),
+ AndroidResourceModule.builder(TargetKey.forPlainTarget(dependencySourceResourceTarget))
+ .addResource(dependencySourceResource)
+ .addTransitiveResourceDependency(transitiveGeneratedResourcesTarget)
+ .addTransitiveResource(transitiveGeneratedResourceOne)
+ .addTransitiveResource(transitiveGeneratedResourceTwo)
+ .build());
+ registry.put(
+ mock(Module.class),
+ AndroidResourceModule.builder(TargetKey.forPlainTarget(transitiveGeneratedResourcesTarget))
+ // .addResource(transitiveGeneratedResourceOne) // Dropped.
+ // .addResource(transitiveGeneratedResourceTwo) // Dropped.
+ .build());
+ registry.put(
+ mock(Module.class),
+ AndroidResourceModule.builder(TargetKey.forPlainTarget(transitiveSourceResourceTarget))
+ .addResource(transitiveSourceResource)
+ .build());
+ registry.put(
+ mock(Module.class),
+ AndroidResourceModule.builder(TargetKey.forPlainTarget(unrelatedGeneratedResourceTarget))
+ // .addResource(unrelatedGeneratedResource) // Dropped.
+ .build());
+ registry.put(
+ mock(Module.class),
+ AndroidResourceModule.builder(TargetKey.forPlainTarget(unrelatedSourceResourceTarget))
+ .addResource(unrelatedSourceResource)
+ .build());
+
+ TargetMap targetMap =
+ TargetMapBuilder.builder()
+ .addTarget(
+ TargetIdeInfo.builder()
+ .setLabel(mainResourcesTarget)
+ .setBuildFile(mainBuildFile)
+ .setAndroidInfo(
+ AndroidIdeInfo.builder()
+ .setGenerateResourceClass(true)
+ .addResource(mainGeneratedResource)
+ .addResource(mainSourceResource))
+ .addDependency(dependencyGeneratedResourceTarget)
+ .addDependency(dependencySourceResourceTarget))
+ .addTarget(
+ TargetIdeInfo.builder()
+ .setLabel(dependencyGeneratedResourceTarget)
+ .setBuildFile(dependencyBuildFile)
+ .setAndroidInfo(
+ AndroidIdeInfo.builder()
+ .setGenerateResourceClass(true)
+ .addResource(dependencyGeneratedResource))
+ .addDependency(transitiveSourceResourceTarget))
+ .addTarget(
+ TargetIdeInfo.builder()
+ .setLabel(dependencySourceResourceTarget)
+ .setBuildFile(dependencyBuildFile)
+ .setAndroidInfo(
+ AndroidIdeInfo.builder()
+ .setGenerateResourceClass(true)
+ .addResource(dependencySourceResource))
+ .addDependency(transitiveGeneratedResourcesTarget))
+ .addTarget(
+ TargetIdeInfo.builder()
+ .setLabel(transitiveGeneratedResourcesTarget)
+ .setBuildFile(transitiveBuildFile)
+ .setAndroidInfo(
+ AndroidIdeInfo.builder()
+ .setGenerateResourceClass(true)
+ .addResource(transitiveGeneratedResourceOne)
+ .addResource(transitiveGeneratedResourceTwo)))
+ .addTarget(
+ TargetIdeInfo.builder()
+ .setLabel(transitiveSourceResourceTarget)
+ .setBuildFile(transitiveBuildFile)
+ .setAndroidInfo(
+ AndroidIdeInfo.builder()
+ .setGenerateResourceClass(true)
+ .addResource(transitiveSourceResource)))
+ .addTarget(
+ TargetIdeInfo.builder()
+ .setLabel(unrelatedGeneratedResourceTarget)
+ .setBuildFile(unrelatedBuildFile)
+ .setAndroidInfo(
+ AndroidIdeInfo.builder()
+ .setGenerateResourceClass(true)
+ .addResource(unrelatedGeneratedResource)))
+ .addTarget(
+ TargetIdeInfo.builder()
+ .setLabel(unrelatedSourceResourceTarget)
+ .setBuildFile(unrelatedBuildFile)
+ .setAndroidInfo(
+ AndroidIdeInfo.builder()
+ .setGenerateResourceClass(true)
+ .addResource(unrelatedSourceResource)))
+ .build();
+
+ projectDataManager.setTargetMap(targetMap);
+ }
+
+ private void createTargetMapWithNonStandardAndroidManifestName() {
+ Label mainResourceTarget = new Label("//com/google/example:main");
+
+ ArtifactLocation mainManifest = artifact("com/google/example/main/WeirdManifest.xml", true);
+ ArtifactLocation mainResource = artifact("com/google/example/main/res", true);
+ ArtifactLocation mainBuildFile = artifact("com/google/example/main/BUILD", true);
+
+ AndroidResourceModuleRegistry registry = AndroidResourceModuleRegistry.getInstance(project);
+ registry.put(
+ module,
+ AndroidResourceModule.builder(TargetKey.forPlainTarget(mainResourceTarget))
+ .addResource(mainResource)
+ .build());
+
+ TargetMap targetMap =
+ TargetMapBuilder.builder()
+ .addTarget(
+ TargetIdeInfo.builder()
+ .setLabel(mainResourceTarget)
+ .setBuildFile(mainBuildFile)
+ .setAndroidInfo(
+ AndroidIdeInfo.builder()
+ .setGenerateResourceClass(true)
+ .setManifestFile(mainManifest)
+ .addResource(mainResource)))
+ .build();
+
+ projectDataManager.setTargetMap(targetMap);
+ }
+
+ private void createTargetMapWithNonStandardAndroidManifestNameInDependency() {
+ Label mainResourceTarget = new Label("//com/google/example:main");
+ Label dependencyResourceTarget = new Label("//com/google/example:dependency");
+
+ ArtifactLocation mainManifest = artifact("com/google/example/main/AndroidManifest.xml", true);
+ ArtifactLocation mainResource = artifact("com/google/example/main/res", true);
+ ArtifactLocation mainBuildFile = artifact("com/google/example/main/BUILD", true);
+
+ ArtifactLocation dependencyManifest =
+ artifact("com/google/example/dependency/MyManifest.xml", true);
+ ArtifactLocation dependencyResource = artifact("com/google/example/dependency/res", true);
+ ArtifactLocation dependencyBuildFile = artifact("com/google/example/dependency/BUILD", true);
+
+ AndroidResourceModuleRegistry registry = AndroidResourceModuleRegistry.getInstance(project);
+ registry.put(
+ module,
+ AndroidResourceModule.builder(TargetKey.forPlainTarget(mainResourceTarget))
+ .addResource(mainResource)
+ .addTransitiveResourceDependency(dependencyResourceTarget)
+ .addTransitiveResource(dependencyResource)
+ .build());
+ registry.put(
+ mock(Module.class),
+ AndroidResourceModule.builder(TargetKey.forPlainTarget(dependencyResourceTarget))
+ .addResource(dependencyResource)
+ .build());
+
+ TargetMap targetMap =
+ TargetMapBuilder.builder()
+ .addTarget(
+ TargetIdeInfo.builder()
+ .setLabel(mainResourceTarget)
+ .setBuildFile(mainBuildFile)
+ .setAndroidInfo(
+ AndroidIdeInfo.builder()
+ .setGenerateResourceClass(true)
+ .setManifestFile(mainManifest)
+ .addResource(mainResource)))
+ .addTarget(
+ TargetIdeInfo.builder()
+ .setLabel(dependencyResourceTarget)
+ .setBuildFile(dependencyBuildFile)
+ .setAndroidInfo(
+ AndroidIdeInfo.builder()
+ .setGenerateResourceClass(true)
+ .setManifestFile(dependencyManifest)
+ .addResource(dependencyResource)))
+ .build();
+
+ projectDataManager.setTargetMap(targetMap);
+ }
+
+ private void createTargetMapWithMissingClassDependency() {
+ Label parentTarget = new Label("//com/google/example:app");
+ Label independentLibraryTarget = new Label("//com/google/example/independent:library");
+ Label independentLibrary2Target = new Label("//com/google/example/independent:library2");
+ Label dependentLibraryTarget = new Label("//com/google/example/dependent:library");
+ Label resourcesTarget = new Label("//com/google/example:resources");
+
+ ArtifactLocation manifest = artifact("com/google/example/AndroidManifest.xml", true);
+ ArtifactLocation resources = artifact("com/google/example/res", true);
+ ArtifactLocation buildFile = artifact("com/google/example/BUILD", true);
+
+ AndroidResourceModuleRegistry registry = AndroidResourceModuleRegistry.getInstance(project);
+ registry.put(
+ module,
+ AndroidResourceModule.builder(TargetKey.forPlainTarget(resourcesTarget))
+ .addResource(resources)
+ .build());
+
+ TargetMap targetMap =
+ TargetMapBuilder.builder()
+ .addTarget(
+ TargetIdeInfo.builder()
+ .setLabel(parentTarget)
+ .setBuildFile(buildFile)
+ .addDependency(independentLibraryTarget)
+ .addDependency(resourcesTarget))
+ .addTarget(
+ TargetIdeInfo.builder()
+ .setLabel(independentLibraryTarget)
+ .setBuildFile(buildFile)
+ .setJavaInfo(JavaIdeInfo.builder())
+ .addDependency(independentLibrary2Target))
+ .addTarget(
+ TargetIdeInfo.builder()
+ .setLabel(independentLibrary2Target)
+ .setBuildFile(buildFile)
+ .setJavaInfo(JavaIdeInfo.builder()))
+ .addTarget(
+ TargetIdeInfo.builder()
+ .setLabel(resourcesTarget)
+ .setBuildFile(buildFile)
+ .setAndroidInfo(
+ AndroidIdeInfo.builder()
+ .setGenerateResourceClass(true)
+ .setManifestFile(manifest)
+ .addResource(resources))
+ .addDependency(dependentLibraryTarget))
+ .addTarget(
+ TargetIdeInfo.builder()
+ .setLabel(dependentLibraryTarget)
+ .setBuildFile(buildFile)
+ .setJavaInfo(JavaIdeInfo.builder()))
+ .build();
+
+ projectDataManager.setTargetMap(targetMap);
+ }
+
+ private void createPsiClassesAndSourceToTargetMap(Container projectServices) {
+ PsiManager psiManager = new MockPsiManager(project);
+
+ VirtualFile independentLibraryView =
+ new MockVirtualFile("src/com/google/example/independent/LibraryView.java");
+ VirtualFile independentLibraryView2 =
+ new MockVirtualFile("src/com/google/example/independent/LibraryView2.java");
+ VirtualFile independentLibrary2View =
+ new MockVirtualFile("src/com/google/example/independent/Library2View.java");
+ VirtualFile dependentLibraryView =
+ new MockVirtualFile("src/com/google/example/dependent/LibraryView.java");
+ VirtualFile resourceView = new MockVirtualFile("src/com/google/example/ResourceView.java");
+
+ ImmutableMap<String, PsiClass> classes =
+ ImmutableMap.of(
+ "com.google.example.independent.LibraryView",
+ new MockPsiClass(psiManager, independentLibraryView),
+ "com.google.example.independent.LibraryView2",
+ new MockPsiClass(psiManager, independentLibraryView2),
+ "com.google.example.independent.Library2View",
+ new MockPsiClass(psiManager, independentLibrary2View),
+ "com.google.example.dependent.LibraryView",
+ new MockPsiClass(psiManager, dependentLibraryView),
+ "com.google.example.ResourceView",
+ new MockPsiClass(psiManager, resourceView));
+
+ ImmutableMap<File, TargetKey> sourceToTarget =
+ ImmutableMap.of(
+ VfsUtilCore.virtualToIoFile(independentLibraryView),
+ TargetKey.forPlainTarget(new Label("//com/google/example/independent:library")),
+ VfsUtilCore.virtualToIoFile(independentLibraryView2),
+ TargetKey.forPlainTarget(new Label("//com/google/example/independent:library")),
+ VfsUtilCore.virtualToIoFile(independentLibrary2View),
+ TargetKey.forPlainTarget(new Label("//com/google/example/independent:library2")),
+ VfsUtilCore.virtualToIoFile(dependentLibraryView),
+ TargetKey.forPlainTarget(new Label("//com/google/example/dependent:library")),
+ VfsUtilCore.virtualToIoFile(resourceView),
+ TargetKey.forPlainTarget(new Label("//com/google/example:resources")));
+
+ projectServices.register(
+ JavaPsiFacade.class, new MockJavaPsiFacade(project, psiManager, classes));
+ projectServices.register(SourceToTargetMap.class, new MockSourceToTargetMap(sourceToTarget));
+ }
+
+ private static class MockBlazeProjectDataManager implements BlazeProjectDataManager {
+ private BlazeProjectData blazeProjectData;
+
+ public void setTargetMap(TargetMap targetMap) {
+ ArtifactLocationDecoder decoder =
+ (location) -> new File("/src", location.getExecutionRootRelativePath());
+ this.blazeProjectData =
+ new BlazeProjectData(0L, targetMap, null, null, null, null, decoder, null, null, null);
+ }
+
+ @Nullable
+ @Override
+ public BlazeProjectData getBlazeProjectData() {
+ return blazeProjectData;
+ }
+
+ @Override
+ public ModuleEditor editModules() {
+ return null;
+ }
+ }
+
+ private static class MockBuildReferenceManager extends BuildReferenceManager {
+ public MockBuildReferenceManager(Project project) {
+ super(project);
+ }
+
+ @Nullable
+ @Override
+ public PsiElement resolveLabel(Label label) {
+ return null;
+ }
+ }
+
+ private static class MockPsiClass extends NullPsiClass {
+ private PsiFile psiFile;
+
+ public MockPsiClass(PsiManager psiManager, VirtualFile virtualFile) {
+ psiFile =
+ new MockPsiFile(psiManager) {
+ @Override
+ public VirtualFile getVirtualFile() {
+ return virtualFile;
+ }
+ };
+ }
+
+ @Override
+ public PsiFile getContainingFile() {
+ return psiFile;
+ }
+ }
+
+ private static class MockJavaPsiFacade extends JavaPsiFacadeImpl {
+ private ImmutableMap<String, PsiClass> classes;
+
+ public MockJavaPsiFacade(
+ Project project, PsiManager psiManager, ImmutableMap<String, PsiClass> classes) {
+ super(project, psiManager, null, null);
+ this.classes = classes;
+ }
+
+ @Override
+ public PsiClass findClass(String qualifiedName, GlobalSearchScope scope) {
+ return classes.get(qualifiedName);
+ }
+ }
+
+ private static class MockSourceToTargetMap implements SourceToTargetMap {
+ private ImmutableMap<File, TargetKey> sourceToTarget;
+
+ public MockSourceToTargetMap(ImmutableMap<File, TargetKey> sourceToTarget) {
+ this.sourceToTarget = sourceToTarget;
+ }
+
+ @Override
+ public ImmutableCollection<Label> getTargetsToBuildForSourceFile(File file) {
+ return ImmutableList.of();
+ }
+
+ @Override
+ public ImmutableCollection<TargetKey> getRulesForSourceFile(File file) {
+ return ImmutableList.of(sourceToTarget.get(file));
+ }
+ }
+}
diff --git a/aswb/2.3/tests/unittests/com/google/idea/blaze/android/run/testrecorder/BlazeConfigurationsTest.java b/aswb/2.3/tests/unittests/com/google/idea/blaze/android/run/testrecorder/BlazeConfigurationsTest.java
new file mode 100644
index 0000000..86c20cf
--- /dev/null
+++ b/aswb/2.3/tests/unittests/com/google/idea/blaze/android/run/testrecorder/BlazeConfigurationsTest.java
@@ -0,0 +1,292 @@
+/*
+ * 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.android.run.testrecorder;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.android.tools.idea.run.editor.AndroidDebugger;
+import com.android.tools.idea.run.editor.AndroidJavaDebugger;
+import com.android.tools.idea.run.editor.DeployTargetProvider;
+import com.android.tools.idea.run.editor.ShowChooserTargetProvider;
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.gct.testrecorder.run.TestRecorderRunConfigurationProxy;
+import com.google.gct.testrecorder.run.TestRecorderRunConfigurationProxyProvider;
+import com.google.gct.testrecorder.ui.TestRecorderAction;
+import com.google.idea.blaze.android.run.binary.BlazeAndroidBinaryRunConfigurationHandler;
+import com.google.idea.blaze.android.run.binary.BlazeAndroidBinaryRunConfigurationHandlerProvider;
+import com.google.idea.blaze.android.run.binary.BlazeAndroidBinaryRunConfigurationState;
+import com.google.idea.blaze.base.BlazeTestCase;
+import com.google.idea.blaze.base.bazel.BuildSystemProvider;
+import com.google.idea.blaze.base.bazel.WorkspaceRootProvider;
+import com.google.idea.blaze.base.ideinfo.TargetIdeInfo;
+import com.google.idea.blaze.base.lang.buildfile.language.semantics.RuleDefinition;
+import com.google.idea.blaze.base.model.BlazeVersionData;
+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.WorkspaceRoot;
+import com.google.idea.blaze.base.run.BlazeBeforeRunTaskProvider;
+import com.google.idea.blaze.base.run.BlazeCommandRunConfiguration;
+import com.google.idea.blaze.base.run.BlazeCommandRunConfigurationType;
+import com.google.idea.blaze.base.run.confighandler.BlazeCommandRunConfigurationHandler;
+import com.google.idea.blaze.base.run.confighandler.BlazeCommandRunConfigurationHandlerProvider;
+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.google.idea.blaze.base.settings.BlazeImportSettings;
+import com.google.idea.blaze.base.settings.BlazeImportSettingsManager;
+import com.intellij.execution.BeforeRunTaskProvider;
+import com.intellij.execution.RunManager;
+import com.intellij.execution.RunManagerEx;
+import com.intellij.execution.configurations.ConfigurationType;
+import com.intellij.execution.configurations.RunConfiguration;
+import com.intellij.execution.impl.RunManagerImpl;
+import com.intellij.ide.util.ProjectPropertiesComponentImpl;
+import com.intellij.mock.MockModule;
+import com.intellij.mock.MockProject;
+import com.intellij.openapi.extensions.ExtensionPoint;
+import com.intellij.openapi.extensions.ExtensionPointName;
+import com.intellij.openapi.extensions.Extensions;
+import com.intellij.openapi.extensions.impl.ExtensionPointImpl;
+import com.intellij.openapi.extensions.impl.ExtensionsAreaImpl;
+import com.intellij.openapi.fileTypes.FileNameMatcher;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+import java.io.File;
+import java.util.List;
+import javax.annotation.Nullable;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Test cases for {@link TestRecorderBlazeCommandRunConfiguration}. */
+@RunWith(JUnit4.class)
+public class BlazeConfigurationsTest extends BlazeTestCase {
+
+ @Override
+ protected void initTest(Container applicationServices, Container projectServices) {
+ super.initTest(applicationServices, projectServices);
+
+ mockBlazeImportSettings(projectServices);
+ applicationServices.register(TargetFinder.class, new MockTargetFinder());
+
+ ExtensionPoint<ConfigurationType> configurationTypeExtensionPoint =
+ registerExtensionPoint(ConfigurationType.CONFIGURATION_TYPE_EP, ConfigurationType.class);
+ configurationTypeExtensionPoint.registerExtension(new BlazeCommandRunConfigurationType());
+
+ ExtensionPoint<BlazeCommandRunConfigurationHandlerProvider> handlerProviderExtensionPoint =
+ registerExtensionPoint(
+ BlazeCommandRunConfigurationHandlerProvider.EP_NAME,
+ BlazeCommandRunConfigurationHandlerProvider.class);
+ handlerProviderExtensionPoint.registerExtension(
+ new MockBlazeAndroidBinaryRunConfigurationHandlerProvider());
+
+ ExtensionPoint<BuildSystemProvider> buildSystemProviderExtensionPoint =
+ registerExtensionPoint(BuildSystemProvider.EP_NAME, BuildSystemProvider.class);
+ buildSystemProviderExtensionPoint.registerExtension(new MockBuildSystemProvider());
+
+ ExtensionPoint<DeployTargetProvider> deployTargetProviderExtensionPoint =
+ registerExtensionPoint(
+ ExtensionPointName.create("com.android.run.deployTargetProvider"),
+ DeployTargetProvider.class);
+ deployTargetProviderExtensionPoint.registerExtension(new ShowChooserTargetProvider());
+
+ ExtensionPoint<AndroidDebugger> androidDebuggerExtensionPoint =
+ registerExtensionPoint(AndroidDebugger.EP_NAME, AndroidDebugger.class);
+ androidDebuggerExtensionPoint.registerExtension(new AndroidJavaDebugger());
+
+ ExtensionPoint<BeforeRunTaskProvider> beforeRunTaskProviderExtensionPoint =
+ registerExtensionPoint(
+ ExtensionPointName.create("com.intellij.stepsBeforeRunProvider"),
+ BeforeRunTaskProvider.class);
+ ((ExtensionsAreaImpl) Extensions.getArea(project))
+ .registerExtensionPoint((ExtensionPointImpl) beforeRunTaskProviderExtensionPoint);
+ beforeRunTaskProviderExtensionPoint.registerExtension(new BlazeBeforeRunTaskProvider());
+
+ ExtensionPoint<TestRecorderRunConfigurationProxyProvider>
+ testRecorderRunConfigurationProxyProviderExtensionPoint =
+ registerExtensionPoint(
+ ExtensionPointName.create(
+ "com.google.gct.testrecorder.run.testRecorderRunConfigurationProxyProvider"),
+ TestRecorderRunConfigurationProxyProvider.class);
+ testRecorderRunConfigurationProxyProviderExtensionPoint.registerExtension(
+ new TestRecorderBlazeCommandRunConfigurationProxyProvider());
+
+ ((MockProject) project)
+ .addComponent(
+ RunManager.class, new RunManagerImpl(project, new ProjectPropertiesComponentImpl()));
+ }
+
+ @Test
+ public void testSuitableRunConfigurations() {
+ addConfigurations();
+
+ List<RunConfiguration> allConfigurations =
+ RunManagerEx.getInstanceEx(project).getAllConfigurationsList();
+ assertThat(allConfigurations.size()).isEqualTo(2);
+
+ List<RunConfiguration> suitableConfigurations =
+ TestRecorderAction.getSuitableRunConfigurations(project);
+ assertThat(suitableConfigurations.size()).isEqualTo(1);
+ assertThat(suitableConfigurations.get(0).getName()).isEqualTo("AndroidBinaryConfiguration");
+ }
+
+ @Test
+ public void testLaunchActivityClass() {
+ BlazeCommandRunConfiguration blazeConfiguration =
+ BlazeCommandRunConfigurationType.getInstance()
+ .getFactory()
+ .createTemplateConfiguration(project);
+ blazeConfiguration.setTarget(new Label("//label:android_binary_rule"));
+ BlazeAndroidBinaryRunConfigurationState configurationState =
+ ((BlazeAndroidBinaryRunConfigurationHandler) blazeConfiguration.getHandler()).getState();
+ configurationState.setMode(BlazeAndroidBinaryRunConfigurationState.LAUNCH_SPECIFIC_ACTIVITY);
+ configurationState.setActivityClass("MyAppMainActivity");
+
+ TestRecorderRunConfigurationProxy proxy =
+ TestRecorderRunConfigurationProxy.getInstance(blazeConfiguration);
+ assertThat(proxy).isNotNull();
+ assertThat(proxy.getLaunchActivityClass()).isEqualTo("MyAppMainActivity");
+ }
+
+ private void mockBlazeImportSettings(Container projectServices) {
+ BlazeImportSettingsManager importSettingsManager = new BlazeImportSettingsManager(project);
+ importSettingsManager.setImportSettings(
+ new BlazeImportSettings("", "", "", "", "", Blaze.BuildSystem.Blaze));
+ projectServices.register(BlazeImportSettingsManager.class, importSettingsManager);
+ }
+
+ private void addConfigurations() {
+ RunManagerImpl runManager = (RunManagerImpl) RunManagerEx.getInstanceEx(project);
+ BlazeCommandRunConfigurationType.BlazeCommandRunConfigurationFactory configurationFactory =
+ BlazeCommandRunConfigurationType.getInstance().getFactory();
+
+ BlazeCommandRunConfiguration blazeAndroidBinaryConfiguration =
+ configurationFactory.createTemplateConfiguration(project);
+ blazeAndroidBinaryConfiguration.setName("AndroidBinaryConfiguration");
+ blazeAndroidBinaryConfiguration.setTarget(new Label("//label:android_binary_rule"));
+
+ BlazeCommandRunConfiguration blazeAndroidTestConfiguration =
+ configurationFactory.createTemplateConfiguration(project);
+ blazeAndroidTestConfiguration.setName("AndroidTestConfiguration");
+ blazeAndroidTestConfiguration.setTarget(new Label("//label:android_test_rule"));
+
+ runManager.addConfiguration(
+ runManager.createConfiguration(blazeAndroidBinaryConfiguration, configurationFactory),
+ true);
+ runManager.addConfiguration(
+ runManager.createConfiguration(blazeAndroidTestConfiguration, configurationFactory), true);
+ }
+
+ private class MockTargetFinder extends TargetFinder {
+ @Override
+ public List<TargetIdeInfo> findTargets(Project project, Predicate<TargetIdeInfo> predicate) {
+ return null;
+ }
+
+ @Override
+ public TargetIdeInfo targetForLabel(Project project, final Label label) {
+ TargetIdeInfo.Builder builder = TargetIdeInfo.builder().setLabel(label);
+ if (label.equals(new Label("//label:android_binary_rule"))) {
+ builder.setKind(Kind.ANDROID_BINARY);
+ } else if (label.equals(new Label("//label:android_test_rule"))) {
+ builder.setKind(Kind.ANDROID_TEST);
+ }
+ return builder.build();
+ }
+ }
+
+ private class MockBlazeAndroidBinaryRunConfigurationHandlerProvider
+ extends BlazeAndroidBinaryRunConfigurationHandlerProvider {
+ @Override
+ public boolean canHandleKind(Kind kind) {
+ return true;
+ }
+
+ @Override
+ public BlazeCommandRunConfigurationHandler createHandler(BlazeCommandRunConfiguration config) {
+ return new MockBlazeAndroidBinaryRunConfigurationHandler(config);
+ }
+ }
+
+ private class MockBlazeAndroidBinaryRunConfigurationHandler
+ extends BlazeAndroidBinaryRunConfigurationHandler {
+ private final MockModule mockModule;
+
+ MockBlazeAndroidBinaryRunConfigurationHandler(BlazeCommandRunConfiguration configuration) {
+ super(configuration);
+ mockModule = new MockModule(project, () -> {});
+ }
+
+ @Nullable
+ @Override
+ public Module getModule() {
+ Label label = getLabel();
+ if (label != null && label.equals(new Label("//label:android_binary_rule"))) {
+ return mockModule;
+ }
+
+ return null;
+ }
+ }
+
+ private class MockBuildSystemProvider implements BuildSystemProvider {
+ @Override
+ public Blaze.BuildSystem buildSystem() {
+ return Blaze.BuildSystem.Blaze;
+ }
+
+ @Override
+ public WorkspaceRootProvider getWorkspaceRootProvider() {
+ return null;
+ }
+
+ @Override
+ public ImmutableList<String> buildArtifactDirectories(WorkspaceRoot root) {
+ return null;
+ }
+
+ @Nullable
+ @Override
+ public String getRuleDocumentationUrl(RuleDefinition rule) {
+ return null;
+ }
+
+ @Override
+ public boolean isBuildFile(String fileName) {
+ return false;
+ }
+
+ @Nullable
+ @Override
+ public File findBuildFileInDirectory(File directory) {
+ return null;
+ }
+
+ @Override
+ public FileNameMatcher buildFileMatcher() {
+ return null;
+ }
+
+ @Override
+ public void populateBlazeVersionData(
+ BuildSystem buildSystem,
+ WorkspaceRoot workspaceRoot,
+ ImmutableMap<String, String> blazeInfo,
+ BlazeVersionData.Builder builder) {}
+ }
+}
diff --git a/aswb/src/com/google/idea/blaze/android/run/binary/instantrun/InstantRunExperiment.java b/aswb/2.3/tests/utils/integration/com/google/idea/blaze/android/BlazeAndroidIntegrationTestCase.java
similarity index 65%
rename from aswb/src/com/google/idea/blaze/android/run/binary/instantrun/InstantRunExperiment.java
rename to aswb/2.3/tests/utils/integration/com/google/idea/blaze/android/BlazeAndroidIntegrationTestCase.java
index 148b1d9..74459a0 100644
--- a/aswb/src/com/google/idea/blaze/android/run/binary/instantrun/InstantRunExperiment.java
+++ b/aswb/2.3/tests/utils/integration/com/google/idea/blaze/android/BlazeAndroidIntegrationTestCase.java
@@ -13,12 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.google.idea.blaze.android.run.binary.instantrun;
-import com.google.idea.common.experiments.BoolExperiment;
+package com.google.idea.blaze.android;
-/** Holds the instant run experiment */
-public class InstantRunExperiment {
- public static final BoolExperiment INSTANT_RUN_ENABLED =
- new BoolExperiment("instant.run.enabled", false);
-}
+import com.google.idea.blaze.base.BlazeIntegrationTestCase;
+
+/** Compatibility test class for Blaze Android integration tests. */
+public abstract class BlazeAndroidIntegrationTestCase extends BlazeIntegrationTestCase {}
diff --git a/aswb/BUILD b/aswb/BUILD
index f3cfd00..16fc99b 100644
--- a/aswb/BUILD
+++ b/aswb/BUILD
@@ -11,6 +11,7 @@
"stamped_plugin_xml",
)
load("//:version.bzl", "VERSION")
+load("//intellij_platform_sdk:build_defs.bzl", "select_for_plugin_api")
merged_plugin_xml(
name = "merged_plugin_xml_common",
@@ -19,7 +20,12 @@
"//base:plugin_xml",
"//cpp:plugin_xml",
"//java:plugin_xml",
- ],
+ ] + select_for_plugin_api({
+ # TODO(chaorenl): remove when 2.2 is obsolete
+ "android-studio-145.1617.8": [],
+ "android-studio-2.3.0.3": ["2.3/src/META-INF/aswb_beta.xml"],
+ "android-studio-2.3.0.4": ["2.3/src/META-INF/aswb_beta.xml"],
+ }),
visibility = [
"//visibility:public",
],
@@ -45,7 +51,12 @@
java_library(
name = "aswb_lib",
- srcs = glob(["src/**/*.java"]),
+ srcs = glob(["src/**/*.java"]) + select_for_plugin_api({
+ # TODO(chaorenl): remove when 2.2 is obsolete
+ "android-studio-145.1617.8": glob(["2.2/src/**/*.java"]),
+ "android-studio-2.3.0.3": glob(["2.3/src/**/*.java"]),
+ "android-studio-2.3.0.4": glob(["2.3/src/**/*.java"]),
+ }),
resources = glob(["resources/**/*"]),
visibility = [
"//visibility:public",
@@ -61,10 +72,15 @@
],
)
+# TODO(chaorenl): remove when 2.2 is obsolete
java_library(
name = "integration_test_utils",
testonly = 1,
- srcs = glob(["tests/utils/integration/**/*.java"]),
+ srcs = select_for_plugin_api({
+ "android-studio-145.1617.8": glob(["2.2/tests/utils/integration/**/*.java"]),
+ "android-studio-2.3.0.3": glob(["2.3/tests/utils/integration/**/*.java"]),
+ "android-studio-2.3.0.4": glob(["2.3/tests/utils/integration/**/*.java"]),
+ }),
deps = [
"//base",
"//base:integration_test_utils",
@@ -83,7 +99,12 @@
intellij_unit_test_suite(
name = "unit_tests",
- srcs = glob(["tests/unittests/**/*.java"]),
+ srcs = glob(["tests/unittests/**/*.java"]) + select_for_plugin_api({
+ # TODO(chaorenl): remove when 2.2 is obsolete
+ "android-studio-145.1617.8": [],
+ "android-studio-2.3.0.3": glob(["2.3/tests/unittests/**/*.java"]),
+ "android-studio-2.3.0.4": glob(["2.3/tests/unittests/**/*.java"]),
+ }),
test_package_root = "com.google.idea.blaze.android",
deps = [
":aswb_lib",
@@ -101,7 +122,12 @@
intellij_integration_test_suite(
name = "integration_tests",
- srcs = glob(["tests/integrationtests/**/*.java"]),
+ srcs = glob(["tests/integrationtests/**/*.java"]) + select_for_plugin_api({
+ # TODO(chaorenl): remove when 2.2 is obsolete
+ "android-studio-145.1617.8": [],
+ "android-studio-2.3.0.3": glob(["2.3/tests/integrationtests/**/*.java"]),
+ "android-studio-2.3.0.4": glob(["2.3/tests/integrationtests/**/*.java"]),
+ }),
platform_prefix = "AndroidStudio",
required_plugins = "com.google.idea.bazel.aswb",
test_package_root = "com.google.idea.blaze.android",
diff --git a/aswb/aswb.bazelproject b/aswb/aswb.bazelproject
index 34cbb52..c2f7d36 100644
--- a/aswb/aswb.bazelproject
+++ b/aswb/aswb.bazelproject
@@ -4,6 +4,8 @@
-plugin_dev
-clwb
-cpp/src/com/google/idea/blaze/cpp/versioned/v162
+ # TODO(chaorenl): remove when 2.2 is obsolete.
+ -aswb/2.3
targets:
//aswb:aswb_bazel
diff --git a/aswb/src/META-INF/aswb.xml b/aswb/src/META-INF/aswb.xml
index ca22494..48f701d 100644
--- a/aswb/src/META-INF/aswb.xml
+++ b/aswb/src/META-INF/aswb.xml
@@ -18,7 +18,8 @@
<depends>com.intellij.modules.androidstudio</depends>
<depends>org.jetbrains.android</depends>
- <depends>com.android.tools.idea.updater</depends>
+ <!-- TODO(chaorenl): remove when 2.2 is obsolete -->
+ <depends optional="true">com.android.tools.idea.updater</depends>
<extensions defaultExtensionNs="com.intellij">
<java.elementFinder implementation="com.google.idea.blaze.android.resources.AndroidResourceClassFinder"
@@ -31,14 +32,14 @@
<runConfigurationProducer
implementation="com.google.idea.blaze.android.run.test.BlazeAndroidTestMethodRunConfigurationProducer"
order="first"/>
- <configurationType implementation="com.google.idea.blaze.android.run.binary.BlazeAndroidBinaryRunConfigurationType"/>
- <configurationType implementation="com.google.idea.blaze.android.run.test.BlazeAndroidTestRunConfigurationType"/>
<programRunner implementation="com.google.idea.blaze.android.run.binary.BlazeAndroidBinaryProgramRunner" order="first"/>
<executor implementation="com.google.idea.blaze.android.run.binary.mobileinstall.IncrementalInstallRunExecutor" order="last"/>
<executor implementation="com.google.idea.blaze.android.run.binary.mobileinstall.IncrementalInstallDebugExecutor" order="last"/>
<applicationService serviceInterface="com.google.idea.blaze.base.plugin.BlazePluginId"
serviceImplementation="com.google.idea.blaze.android.plugin.AswbPlugin"/>
<projectService serviceImplementation="com.google.idea.blaze.android.manifest.ManifestParser"/>
+ <projectService serviceImplementation="com.google.idea.blaze.android.sync.model.AndroidResourceModuleRegistry"/>
+ <applicationService serviceImplementation="com.google.idea.blaze.android.settings.BlazeAndroidUserSettings"/>
</extensions>
<extensions defaultExtensionNs="org.jetbrains.android.actions">
@@ -88,4 +89,4 @@
</component>
</application-components>
-</idea-plugin>
\ No newline at end of file
+</idea-plugin>
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 056f692..0c14f79 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,7 @@
*/
package com.google.idea.blaze.android.projectview;
+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;
import com.google.idea.blaze.base.projectview.parser.ParseContext;
@@ -28,7 +29,6 @@
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.util.text.StringUtil;
import java.util.Collection;
-import org.jetbrains.android.sdk.AndroidSdkUtils;
import org.jetbrains.annotations.Nullable;
/** Allows manual override of the android sdk. */
diff --git a/aswb/src/com/google/idea/blaze/android/run/BlazeAndroidRunConfigurationCommonState.java b/aswb/src/com/google/idea/blaze/android/run/BlazeAndroidRunConfigurationCommonState.java
index 5bd0c77..6926023 100644
--- a/aswb/src/com/google/idea/blaze/android/run/BlazeAndroidRunConfigurationCommonState.java
+++ b/aswb/src/com/google/idea/blaze/android/run/BlazeAndroidRunConfigurationCommonState.java
@@ -215,5 +215,11 @@
}
return UiUtil.createBox(result);
}
+
+ @Override
+ public void setComponentEnabled(boolean enabled) {
+ userFlagsEditor.setComponentEnabled(enabled);
+ enableNativeDebuggingCheckBox.setEnabled(enabled);
+ }
}
}
diff --git a/aswb/src/com/google/idea/blaze/android/run/binary/BlazeAndroidBinaryNormalBuildRunContext.java b/aswb/src/com/google/idea/blaze/android/run/binary/BlazeAndroidBinaryNormalBuildRunContext.java
index b90cc35..5351213 100644
--- a/aswb/src/com/google/idea/blaze/android/run/binary/BlazeAndroidBinaryNormalBuildRunContext.java
+++ b/aswb/src/com/google/idea/blaze/android/run/binary/BlazeAndroidBinaryNormalBuildRunContext.java
@@ -33,6 +33,7 @@
import com.android.tools.idea.run.util.ProcessHandlerLaunchStatus;
import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.Futures;
+import com.google.idea.blaze.android.compatibility.Compatibility;
import com.google.idea.blaze.android.run.deployinfo.BlazeAndroidDeployInfo;
import com.google.idea.blaze.android.run.deployinfo.BlazeApkProvider;
import com.google.idea.blaze.android.run.runner.BlazeAndroidDeviceSelector;
@@ -167,14 +168,22 @@
@Nullable
@Override
+ @SuppressWarnings("unchecked")
public DebugConnectorTask getDebuggerTask(
AndroidDebugger androidDebugger,
AndroidDebuggerState androidDebuggerState,
- Set<String> packageIds)
+ Set<String> packageIds,
+ boolean monitorRemoteProcess)
throws ExecutionException {
- //noinspection unchecked
- return androidDebugger.getConnectDebuggerTask(
- env, null, packageIds, facet, androidDebuggerState, runConfiguration.getType().getId());
+ return Compatibility.getConnectDebuggerTask(
+ androidDebugger,
+ env,
+ null,
+ packageIds,
+ facet,
+ androidDebuggerState,
+ runConfiguration.getType().getId(),
+ monitorRemoteProcess);
}
@Nullable
diff --git a/aswb/src/com/google/idea/blaze/android/run/binary/BlazeAndroidBinaryRunConfigurationHandler.java b/aswb/src/com/google/idea/blaze/android/run/binary/BlazeAndroidBinaryRunConfigurationHandler.java
index a70da60..3b222f7 100644
--- a/aswb/src/com/google/idea/blaze/android/run/binary/BlazeAndroidBinaryRunConfigurationHandler.java
+++ b/aswb/src/com/google/idea/blaze/android/run/binary/BlazeAndroidBinaryRunConfigurationHandler.java
@@ -15,16 +15,13 @@
*/
package com.google.idea.blaze.android.run.binary;
-import com.android.sdklib.AndroidVersion;
-import com.android.tools.idea.fd.InstantRunManager;
-import com.android.tools.idea.run.AndroidSessionInfo;
+import com.android.annotations.VisibleForTesting;
import com.android.tools.idea.run.ValidationError;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.idea.blaze.android.run.BlazeAndroidRunConfigurationCommonState;
import com.google.idea.blaze.android.run.BlazeAndroidRunConfigurationHandler;
import com.google.idea.blaze.android.run.BlazeAndroidRunConfigurationValidationUtil;
-import com.google.idea.blaze.android.run.binary.instantrun.BlazeAndroidBinaryInstantRunContext;
import com.google.idea.blaze.android.run.binary.mobileinstall.BlazeAndroidBinaryMobileInstallRunContext;
import com.google.idea.blaze.android.run.runner.BlazeAndroidRunConfigurationRunner;
import com.google.idea.blaze.android.run.runner.BlazeAndroidRunContext;
@@ -42,15 +39,12 @@
import com.intellij.execution.Executor;
import com.intellij.execution.configurations.RunConfiguration;
import com.intellij.execution.configurations.RuntimeConfigurationException;
-import com.intellij.execution.executors.DefaultRunExecutor;
import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
-import icons.AndroidIcons;
import java.util.List;
import javax.swing.Icon;
import org.jetbrains.android.facet.AndroidFacet;
-import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
@@ -63,7 +57,8 @@
private final BlazeCommandRunConfiguration configuration;
private final BlazeAndroidBinaryRunConfigurationState configState;
- BlazeAndroidBinaryRunConfigurationHandler(BlazeCommandRunConfiguration configuration) {
+ @VisibleForTesting
+ protected BlazeAndroidBinaryRunConfigurationHandler(BlazeCommandRunConfiguration configuration) {
this.configuration = configuration;
configState =
new BlazeAndroidBinaryRunConfigurationState(
@@ -91,7 +86,7 @@
}
@Nullable
- private Module getModule() {
+ public Module getModule() {
Label target = getLabel();
return target != null
? BlazeAndroidProjectStructureSyncer.ensureRunConfigurationModule(
@@ -125,10 +120,7 @@
AndroidFacet facet,
ExecutionEnvironment env,
ImmutableList<String> buildFlags) {
- if (configState.instantRun()) {
- return new BlazeAndroidBinaryInstantRunContext(
- project, facet, configuration, env, configState, getLabel(), buildFlags);
- } else if (configState.mobileInstall()) {
+ if (configState.mobileInstall()) {
return new BlazeAndroidBinaryMobileInstallRunContext(
project, facet, configuration, env, configState, getLabel(), buildFlags);
} else {
@@ -187,27 +179,7 @@
@Override
@Nullable
- public Icon getExecutorIcon(@NotNull RunConfiguration configuration, @NotNull Executor executor) {
- if (!configState.instantRun()) {
- return null;
- }
-
- AndroidSessionInfo info =
- AndroidSessionInfo.findOldSession(
- this.configuration.getProject(), null, this.configuration.getUniqueID());
- if (info == null || !info.isInstantRun() || !info.getExecutorId().equals(executor.getId())) {
- return null;
- }
-
- // Make sure instant run is supported on the relevant device, if found.
- AndroidVersion androidVersion =
- InstantRunManager.getMinDeviceApiLevel(info.getProcessHandler());
- if (!InstantRunManager.isInstantRunCapableDeviceVersion(androidVersion)) {
- return null;
- }
-
- return executor instanceof DefaultRunExecutor
- ? AndroidIcons.RunIcons.Replay
- : AndroidIcons.RunIcons.DebugReattach;
+ public Icon getExecutorIcon(RunConfiguration configuration, Executor executor) {
+ return null;
}
}
diff --git a/aswb/src/com/google/idea/blaze/android/run/binary/BlazeAndroidBinaryRunConfigurationState.java b/aswb/src/com/google/idea/blaze/android/run/binary/BlazeAndroidBinaryRunConfigurationState.java
index 6557f8b..cb368d4 100644
--- a/aswb/src/com/google/idea/blaze/android/run/binary/BlazeAndroidBinaryRunConfigurationState.java
+++ b/aswb/src/com/google/idea/blaze/android/run/binary/BlazeAndroidBinaryRunConfigurationState.java
@@ -42,12 +42,10 @@
private static final String MOBILE_INSTALL_ATTR = "blaze-mobile-install";
private static final String USE_SPLIT_APKS_IF_POSSIBLE = "use-split-apks-if-possible";
- private static final String INSTANT_RUN_ATTR = "instant-run";
private static final String WORK_PROFILE_ATTR = "use-work-profile-if-present";
private static final String USER_ID_ATTR = "user-id";
private boolean mobileInstall = false;
private boolean useSplitApksIfPossible = false;
- private boolean instantRun = false;
private boolean useWorkProfileIfPresent = false;
private Integer userId;
@@ -85,14 +83,6 @@
this.useSplitApksIfPossible = useSplitApksIfPossible;
}
- boolean instantRun() {
- return instantRun;
- }
-
- void setInstantRun(boolean instantRun) {
- this.instantRun = instantRun;
- }
-
public boolean useWorkProfileIfPresent() {
return useWorkProfileIfPresent;
}
@@ -156,7 +146,6 @@
setMobileInstall(Boolean.parseBoolean(element.getAttributeValue(MOBILE_INSTALL_ATTR)));
setUseSplitApksIfPossible(
Boolean.parseBoolean(element.getAttributeValue(USE_SPLIT_APKS_IF_POSSIBLE)));
- setInstantRun(Boolean.parseBoolean(element.getAttributeValue(INSTANT_RUN_ATTR)));
setUseWorkProfileIfPresent(Boolean.parseBoolean(element.getAttributeValue(WORK_PROFILE_ATTR)));
String userIdString = element.getAttributeValue(USER_ID_ATTR);
@@ -196,7 +185,6 @@
element.setAttribute(MODE, mode);
element.setAttribute(MOBILE_INSTALL_ATTR, Boolean.toString(mobileInstall));
element.setAttribute(USE_SPLIT_APKS_IF_POSSIBLE, Boolean.toString(useSplitApksIfPossible));
- element.setAttribute(INSTANT_RUN_ATTR, Boolean.toString(instantRun));
element.setAttribute(WORK_PROFILE_ATTR, Boolean.toString(useWorkProfileIfPresent));
if (userId != null) {
diff --git a/aswb/src/com/google/idea/blaze/android/run/binary/BlazeAndroidBinaryRunConfigurationStateEditor.java b/aswb/src/com/google/idea/blaze/android/run/binary/BlazeAndroidBinaryRunConfigurationStateEditor.java
index c7fbff6..8ce6279 100644
--- a/aswb/src/com/google/idea/blaze/android/run/binary/BlazeAndroidBinaryRunConfigurationStateEditor.java
+++ b/aswb/src/com/google/idea/blaze/android/run/binary/BlazeAndroidBinaryRunConfigurationStateEditor.java
@@ -16,7 +16,6 @@
package com.google.idea.blaze.android.run.binary;
import com.android.tools.idea.run.activity.ActivityLocatorUtils;
-import com.google.idea.blaze.android.run.binary.instantrun.InstantRunExperiment;
import com.google.idea.blaze.base.run.state.RunConfigurationState;
import com.google.idea.blaze.base.run.state.RunConfigurationStateEditor;
import com.google.idea.blaze.base.ui.IntegerTextField;
@@ -75,11 +74,12 @@
private JRadioButton launchCustomButton;
private JCheckBox mobileInstallCheckBox;
private JCheckBox splitApksCheckBox;
- private JCheckBox instantRunCheckBox;
private JCheckBox useWorkProfileIfPresentCheckBox;
private JLabel userIdLabel;
private IntegerTextField userIdField;
+ private boolean componentEnabled = true;
+
BlazeAndroidBinaryRunConfigurationStateEditor(
RunConfigurationStateEditor commonStateEditor, Project project) {
this.commonStateEditor = commonStateEditor;
@@ -126,34 +126,15 @@
}
}
});
- ActionListener listener = e -> activityField.setEnabled(launchCustomButton.isSelected());
+ ActionListener listener = e -> updateEnabledState();
launchCustomButton.addActionListener(listener);
launchDefaultButton.addActionListener(listener);
launchNothingButton.addActionListener(listener);
- instantRunCheckBox.setVisible(InstantRunExperiment.INSTANT_RUN_ENABLED.getValue());
-
- /* Only one of mobile-install and instant run can be selected at any one time */
- mobileInstallCheckBox.addActionListener(
- e -> {
- if (mobileInstallCheckBox.isSelected()) {
- instantRunCheckBox.setSelected(false);
- }
- });
- instantRunCheckBox.addActionListener(
- e -> {
- if (instantRunCheckBox.isSelected()) {
- mobileInstallCheckBox.setSelected(false);
- }
- });
-
mobileInstallCheckBox.addActionListener(
e -> splitApksCheckBox.setVisible(mobileInstallCheckBox.isSelected()));
- useWorkProfileIfPresentCheckBox.addActionListener(
- e -> {
- setUserIdEnabled(!useWorkProfileIfPresentCheckBox.isSelected());
- });
+ useWorkProfileIfPresentCheckBox.addActionListener(e -> updateEnabledState());
}
@Override
@@ -170,24 +151,18 @@
} else {
launchNothingButton.setSelected(true);
}
- activityField.setEnabled(launchSpecificActivity);
if (launchSpecificActivity) {
activityField.getChildComponent().setText(state.getActivityClass());
}
mobileInstallCheckBox.setSelected(state.mobileInstall());
splitApksCheckBox.setSelected(state.useSplitApksIfPossible());
- instantRunCheckBox.setSelected(state.instantRun());
useWorkProfileIfPresentCheckBox.setSelected(state.useWorkProfileIfPresent());
userIdField.setValue(state.getUserId());
- setUserIdEnabled(!state.useWorkProfileIfPresent());
splitApksCheckBox.setVisible(state.mobileInstall());
- }
- private void setUserIdEnabled(boolean enabled) {
- userIdLabel.setEnabled(enabled);
- userIdField.setEnabled(enabled);
+ updateEnabledState();
}
@Override
@@ -207,7 +182,6 @@
}
state.setMobileInstall(mobileInstallCheckBox.isSelected());
state.setUseSplitApksIfPossible(splitApksCheckBox.isSelected());
- state.setInstantRun(instantRunCheckBox.isSelected());
state.setUseWorkProfileIfPresent(useWorkProfileIfPresentCheckBox.isSelected());
}
@@ -216,6 +190,26 @@
return UiUtil.createBox(commonStateEditor.createComponent(), panel);
}
+ private void updateEnabledState() {
+ boolean useWorkProfile = useWorkProfileIfPresentCheckBox.isSelected();
+ userIdLabel.setEnabled(componentEnabled && !useWorkProfile);
+ userIdField.setEnabled(componentEnabled && !useWorkProfile);
+ commonStateEditor.setComponentEnabled(componentEnabled);
+ activityField.setEnabled(componentEnabled && launchCustomButton.isSelected());
+ launchNothingButton.setEnabled(componentEnabled);
+ launchDefaultButton.setEnabled(componentEnabled);
+ launchCustomButton.setEnabled(componentEnabled);
+ mobileInstallCheckBox.setEnabled(componentEnabled);
+ splitApksCheckBox.setEnabled(componentEnabled);
+ useWorkProfileIfPresentCheckBox.setEnabled(componentEnabled);
+ }
+
+ @Override
+ public void setComponentEnabled(boolean enabled) {
+ componentEnabled = enabled;
+ updateEnabledState();
+ }
+
private void createUIComponents(Project project) {
final EditorTextField editorTextField =
new LanguageTextField(PlainTextLanguage.INSTANCE, project, "") {
@@ -465,24 +459,6 @@
null,
0,
false));
- instantRunCheckBox = new JCheckBox();
- instantRunCheckBox.setText(" Use InstantRun");
- panel.add(
- instantRunCheckBox,
- new GridConstraints(
- 2,
- 0,
- 1,
- 2,
- GridConstraints.ANCHOR_WEST,
- GridConstraints.FILL_NONE,
- GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
- GridConstraints.SIZEPOLICY_FIXED,
- null,
- null,
- null,
- 0,
- false));
ButtonGroup buttonGroup;
buttonGroup = new ButtonGroup();
buttonGroup.add(launchDefaultButton);
diff --git a/aswb/src/com/google/idea/blaze/android/run/binary/BlazeAndroidBinaryRunConfigurationType.java b/aswb/src/com/google/idea/blaze/android/run/binary/BlazeAndroidBinaryRunConfigurationType.java
deleted file mode 100644
index bfc5a02..0000000
--- a/aswb/src/com/google/idea/blaze/android/run/binary/BlazeAndroidBinaryRunConfigurationType.java
+++ /dev/null
@@ -1,125 +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.android.run.binary;
-
-import com.google.idea.blaze.base.run.BlazeCommandRunConfiguration;
-import com.google.idea.blaze.base.run.BlazeCommandRunConfigurationType;
-import com.google.idea.blaze.base.settings.Blaze;
-import com.intellij.execution.BeforeRunTask;
-import com.intellij.execution.configurations.ConfigurationFactory;
-import com.intellij.execution.configurations.ConfigurationType;
-import com.intellij.execution.configurations.ConfigurationTypeUtil;
-import com.intellij.execution.configurations.UnknownConfigurationType;
-import com.intellij.openapi.project.Project;
-import com.intellij.openapi.util.Key;
-import icons.AndroidIcons;
-import javax.swing.Icon;
-import org.jetbrains.annotations.NotNull;
-
-/**
- * A type for Android application run configurations adapted specifically to run android_binary
- * targets.
- *
- * @deprecated See {@link com.google.idea.blaze.base.run.BlazeCommandRunConfigurationType}. Retained
- * in 1.9 for legacy purposes, to allow existing BlazeAndroidBinaryRunConfigurations to be
- * updated to BlazeCommandRunConfigurations. Intended to be removed in 2.1.
- */
-// Hack: extend UnknownConfigurationType to completely hide it in the Run/Debug Configurations UI.
-@Deprecated
-public class BlazeAndroidBinaryRunConfigurationType extends UnknownConfigurationType {
- private final BlazeAndroidBinaryRunConfigurationFactory factory =
- new BlazeAndroidBinaryRunConfigurationFactory(this);
-
- static class BlazeAndroidBinaryRunConfigurationFactory extends ConfigurationFactory {
-
- protected BlazeAndroidBinaryRunConfigurationFactory(@NotNull ConfigurationType type) {
- super(type);
- }
-
- @Override
- public String getName() {
- // Used to look up this ConfigurationFactory.
- // Preserve value so legacy configurations can be loaded.
- return Blaze.defaultBuildSystemName() + " Android Binary";
- }
-
- @Override
- @NotNull
- public BlazeCommandRunConfiguration createTemplateConfiguration(@NotNull Project project) {
- // Create a BlazeCommandRunConfiguration instead, to update legacy configurations.
- return BlazeCommandRunConfigurationType.getInstance()
- .getFactory()
- .createTemplateConfiguration(project);
- }
-
- @Override
- public boolean canConfigurationBeSingleton() {
- return false;
- }
-
- @Override
- public boolean isApplicable(@NotNull Project project) {
- return false;
- }
-
- @Override
- public void configureBeforeRunTaskDefaults(
- Key<? extends BeforeRunTask> providerID, BeforeRunTask task) {
- // Removed BlazeAndroidBeforeRunTaskProvider; this method won't be called anymore anyhow.
- }
-
- @Override
- public boolean isConfigurationSingletonByDefault() {
- return false;
- }
- }
-
- @NotNull
- public static BlazeAndroidBinaryRunConfigurationType getInstance() {
- return ConfigurationTypeUtil.findConfigurationType(
- BlazeAndroidBinaryRunConfigurationType.class);
- }
-
- @Override
- @NotNull
- public String getDisplayName() {
- return "Legacy " + Blaze.defaultBuildSystemName() + " Android Binary";
- }
-
- @Override
- public String getConfigurationTypeDescription() {
- return "Launch/debug configuration for android_binary rules. "
- + "Use Blaze Command instead; this legacy configuration type is being removed.";
- }
-
- @Override
- public Icon getIcon() {
- return AndroidIcons.Android;
- }
-
- @Override
- @NotNull
- public String getId() {
- // Used to look up this ConfigurationType.
- // Preserve value so legacy configurations can be loaded.
- return "BlazeAndroidBinaryRunConfigurationType";
- }
-
- @Override
- public BlazeAndroidBinaryRunConfigurationFactory[] getConfigurationFactories() {
- return new BlazeAndroidBinaryRunConfigurationFactory[] {factory};
- }
-}
diff --git a/aswb/src/com/google/idea/blaze/android/run/binary/BlazeDefaultActivityLocator.java b/aswb/src/com/google/idea/blaze/android/run/binary/BlazeDefaultActivityLocator.java
index d6fcd4a..50c2dfd 100644
--- a/aswb/src/com/google/idea/blaze/android/run/binary/BlazeDefaultActivityLocator.java
+++ b/aswb/src/com/google/idea/blaze/android/run/binary/BlazeDefaultActivityLocator.java
@@ -45,7 +45,13 @@
@NotNull
@Override
public String getQualifiedActivityName(@NotNull IDevice device) throws ActivityLocatorException {
- Manifest manifest = ManifestParser.getInstance(project).getManifest(mergedManifestFile);
+ // Run in a read action since otherwise, it might throw a read access exception.
+ Manifest manifest =
+ ApplicationManager.getApplication()
+ .runReadAction(
+ (Computable<Manifest>)
+ () -> ManifestParser.getInstance(project).getManifest(mergedManifestFile));
+
if (manifest == null) {
throw new ActivityLocatorException("Could not locate merged manifest");
}
diff --git a/aswb/src/com/google/idea/blaze/android/run/binary/instantrun/BlazeAndroidBinaryInstantRunContext.java b/aswb/src/com/google/idea/blaze/android/run/binary/instantrun/BlazeAndroidBinaryInstantRunContext.java
deleted file mode 100644
index b35d0b2..0000000
--- a/aswb/src/com/google/idea/blaze/android/run/binary/instantrun/BlazeAndroidBinaryInstantRunContext.java
+++ /dev/null
@@ -1,196 +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.android.run.binary.instantrun;
-
-import com.android.ddmlib.IDevice;
-import com.android.tools.idea.fd.InstantRunBuildAnalyzer;
-import com.android.tools.idea.fd.InstantRunUtils;
-import com.android.tools.idea.run.ApplicationIdProvider;
-import com.android.tools.idea.run.ConsolePrinter;
-import com.android.tools.idea.run.ConsoleProvider;
-import com.android.tools.idea.run.LaunchOptions;
-import com.android.tools.idea.run.activity.DefaultStartActivityFlagsProvider;
-import com.android.tools.idea.run.activity.StartActivityFlagsProvider;
-import com.android.tools.idea.run.editor.AndroidDebugger;
-import com.android.tools.idea.run.editor.AndroidDebuggerState;
-import com.android.tools.idea.run.tasks.DebugConnectorTask;
-import com.android.tools.idea.run.tasks.LaunchTask;
-import com.android.tools.idea.run.tasks.LaunchTasksProvider;
-import com.android.tools.idea.run.tasks.UpdateSessionTasksProvider;
-import com.android.tools.idea.run.util.ProcessHandlerLaunchStatus;
-import com.google.common.collect.ImmutableList;
-import com.google.common.util.concurrent.Futures;
-import com.google.idea.blaze.android.run.binary.BlazeAndroidBinaryApplicationLaunchTaskProvider;
-import com.google.idea.blaze.android.run.binary.BlazeAndroidBinaryConsoleProvider;
-import com.google.idea.blaze.android.run.binary.BlazeAndroidBinaryRunConfigurationState;
-import com.google.idea.blaze.android.run.binary.UserIdHelper;
-import com.google.idea.blaze.android.run.runner.BlazeAndroidDeviceSelector;
-import com.google.idea.blaze.android.run.runner.BlazeAndroidLaunchTasksProvider;
-import com.google.idea.blaze.android.run.runner.BlazeAndroidRunConfigurationDebuggerManager;
-import com.google.idea.blaze.android.run.runner.BlazeAndroidRunContext;
-import com.google.idea.blaze.android.run.runner.BlazeApkBuildStep;
-import com.google.idea.blaze.base.model.primitives.Label;
-import com.intellij.execution.ExecutionException;
-import com.intellij.execution.configurations.RunConfiguration;
-import com.intellij.execution.runners.ExecutionEnvironment;
-import com.intellij.openapi.project.Project;
-import java.util.Set;
-import javax.annotation.Nullable;
-import org.jetbrains.android.facet.AndroidFacet;
-import org.jetbrains.annotations.NotNull;
-
-/** Run context for InstantRun. */
-public class BlazeAndroidBinaryInstantRunContext implements BlazeAndroidRunContext {
-
- private final Project project;
- private final AndroidFacet facet;
- private final RunConfiguration runConfiguration;
- private final ExecutionEnvironment env;
- private final BlazeAndroidBinaryRunConfigurationState configState;
-
- private final BlazeAndroidBinaryConsoleProvider consoleProvider;
- private final BlazeApkBuildStepInstantRun buildStep;
-
- public BlazeAndroidBinaryInstantRunContext(
- Project project,
- AndroidFacet facet,
- RunConfiguration runConfiguration,
- ExecutionEnvironment env,
- BlazeAndroidBinaryRunConfigurationState configState,
- Label label,
- ImmutableList<String> buildFlags) {
- this.project = project;
- this.facet = facet;
- this.runConfiguration = runConfiguration;
- this.env = env;
- this.configState = configState;
- this.consoleProvider = new BlazeAndroidBinaryConsoleProvider(project);
- this.buildStep = new BlazeApkBuildStepInstantRun(project, env, label, buildFlags);
- }
-
- @Override
- public BlazeAndroidDeviceSelector getDeviceSelector() {
- return new BlazeInstantRunDeviceSelector();
- }
-
- @Override
- public void augmentEnvironment(ExecutionEnvironment env) {
- InstantRunUtils.setInstantRunEnabled(env, true);
- }
-
- @Override
- public void augmentLaunchOptions(@NotNull LaunchOptions.Builder options) {
- options.setDeploy(true).setOpenLogcatAutomatically(true);
- }
-
- @NotNull
- @Override
- public ConsoleProvider getConsoleProvider() {
- return consoleProvider;
- }
-
- @Override
- public ApplicationIdProvider getApplicationIdProvider() throws ExecutionException {
- return Futures.get(buildStep.getApplicationIdProvider(), ExecutionException.class);
- }
-
- @Override
- public BlazeApkBuildStep getBuildStep() {
- return buildStep;
- }
-
- @Override
- public LaunchTasksProvider getLaunchTasksProvider(
- LaunchOptions.Builder launchOptionsBuilder,
- boolean isDebug,
- BlazeAndroidRunConfigurationDebuggerManager debuggerManager)
- throws ExecutionException {
- InstantRunBuildAnalyzer analyzer =
- Futures.get(buildStep.getInstantRunBuildAnalyzer(), ExecutionException.class);
-
- if (analyzer.canReuseProcessHandler()) {
- return new UpdateSessionTasksProvider(analyzer);
- }
- return new BlazeAndroidLaunchTasksProvider(
- project,
- this,
- getApplicationIdProvider(),
- launchOptionsBuilder,
- isDebug,
- true,
- debuggerManager);
- }
-
- @Override
- public ImmutableList<LaunchTask> getDeployTasks(IDevice device, LaunchOptions launchOptions)
- throws ExecutionException {
- InstantRunBuildAnalyzer analyzer =
- Futures.get(buildStep.getInstantRunBuildAnalyzer(), ExecutionException.class);
- return ImmutableList.<LaunchTask>builder()
- .addAll(analyzer.getDeployTasks(launchOptions))
- .add(analyzer.getNotificationTask())
- .build();
- }
-
- @Nullable
- @Override
- public LaunchTask getApplicationLaunchTask(
- LaunchOptions launchOptions,
- @Nullable Integer userId,
- AndroidDebugger androidDebugger,
- AndroidDebuggerState androidDebuggerState,
- ProcessHandlerLaunchStatus processHandlerLaunchStatus)
- throws ExecutionException {
- BlazeApkBuildStepInstantRun.BuildResult buildResult =
- Futures.get(buildStep.getBuildResult(), ExecutionException.class);
-
- final StartActivityFlagsProvider startActivityFlagsProvider =
- new DefaultStartActivityFlagsProvider(
- androidDebugger,
- androidDebuggerState,
- project,
- launchOptions.isDebug(),
- UserIdHelper.getFlagsFromUserId(userId));
-
- ApplicationIdProvider applicationIdProvider = getApplicationIdProvider();
- return BlazeAndroidBinaryApplicationLaunchTaskProvider.getApplicationLaunchTask(
- project,
- applicationIdProvider,
- buildResult.mergedManifestFile,
- configState,
- startActivityFlagsProvider,
- processHandlerLaunchStatus);
- }
-
- @Nullable
- @Override
- public DebugConnectorTask getDebuggerTask(
- AndroidDebugger androidDebugger,
- AndroidDebuggerState androidDebuggerState,
- Set<String> packageIds)
- throws ExecutionException {
- //noinspection unchecked
- return androidDebugger.getConnectDebuggerTask(
- env, null, packageIds, facet, androidDebuggerState, runConfiguration.getType().getId());
- }
-
- @Nullable
- @Override
- public Integer getUserId(IDevice device, ConsolePrinter consolePrinter)
- throws ExecutionException {
- return UserIdHelper.getUserIdFromConfigurationState(device, consolePrinter, configState);
- }
-}
diff --git a/aswb/src/com/google/idea/blaze/android/run/binary/instantrun/BlazeApkBuildStepInstantRun.java b/aswb/src/com/google/idea/blaze/android/run/binary/instantrun/BlazeApkBuildStepInstantRun.java
deleted file mode 100644
index 2830533..0000000
--- a/aswb/src/com/google/idea/blaze/android/run/binary/instantrun/BlazeApkBuildStepInstantRun.java
+++ /dev/null
@@ -1,384 +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.android.run.binary.instantrun;
-
-import com.android.ddmlib.IDevice;
-import com.android.tools.idea.fd.InstantRunBuildAnalyzer;
-import com.android.tools.idea.fd.InstantRunBuilder;
-import com.android.tools.idea.fd.InstantRunContext;
-import com.android.tools.idea.fd.InstantRunUtils;
-import com.android.tools.idea.fd.RunAsValidityService;
-import com.android.tools.idea.gradle.run.MakeBeforeRunTaskProvider;
-import com.android.tools.idea.run.AndroidDevice;
-import com.android.tools.idea.run.AndroidRunConfigContext;
-import com.android.tools.idea.run.AndroidSessionInfo;
-import com.android.tools.idea.run.ApkProvisionException;
-import com.android.tools.idea.run.ApplicationIdProvider;
-import com.android.tools.idea.run.DeviceFutures;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
-import com.google.common.util.concurrent.ListenableFuture;
-import com.google.common.util.concurrent.SettableFuture;
-import com.google.idea.blaze.android.manifest.ManifestParser;
-import com.google.idea.blaze.android.run.runner.BlazeAndroidDeviceSelector;
-import com.google.idea.blaze.android.run.runner.BlazeApkBuildStep;
-import com.google.idea.blaze.base.async.executor.BlazeExecutor;
-import com.google.idea.blaze.base.async.process.ExternalTask;
-import com.google.idea.blaze.base.async.process.LineProcessingOutputStream;
-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.command.ExperimentalShowArtifactsLineProcessor;
-import com.google.idea.blaze.base.command.info.BlazeInfo;
-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.google.repackaged.devtools.build.lib.rules.android.apkmanifest.ApkManifestOuterClass;
-import com.intellij.execution.Executor;
-import com.intellij.execution.process.ProcessHandler;
-import com.intellij.execution.runners.ExecutionEnvironment;
-import com.intellij.openapi.diagnostic.Logger;
-import com.intellij.openapi.project.Project;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.PrintWriter;
-import java.lang.reflect.InvocationTargetException;
-import java.util.List;
-import java.util.concurrent.CancellationException;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import javax.annotation.Nullable;
-import org.jetbrains.annotations.NotNull;
-
-/** Builds the APK using normal blaze build. */
-class BlazeApkBuildStepInstantRun implements BlazeApkBuildStep {
- private static final Logger LOG = Logger.getInstance(BlazeApkBuildStepInstantRun.class);
-
- private final Project project;
- private final Executor executor;
- private final ExecutionEnvironment env;
- private final Label label;
- private final ImmutableList<String> buildFlags;
- private final File instantRunArtifactDirectory;
- private final File instantRunGradleBuildFile;
- private final File instantRunBuildInfoFile;
- private final File instantRunGradlePropertiesFile;
-
- public static class BuildResult {
- public final File executionRoot;
- public final File mergedManifestFile;
- public final File apkManifestProtoFile;
- public final ApkManifestOuterClass.ApkManifest apkManifestProto;
-
- public BuildResult(
- File executionRoot,
- File mergedManifestFile,
- File apkManifestProtoFile,
- ApkManifestOuterClass.ApkManifest apkManifestProto) {
- this.executionRoot = executionRoot;
- this.mergedManifestFile = mergedManifestFile;
- this.apkManifestProtoFile = apkManifestProtoFile;
- this.apkManifestProto = apkManifestProto;
- }
- }
-
- private final SettableFuture<BuildResult> buildResultFuture = SettableFuture.create();
- private final SettableFuture<ApplicationIdProvider> applicationIdProviderFuture =
- SettableFuture.create();
- private final SettableFuture<InstantRunContext> instantRunContextFuture = SettableFuture.create();
- private final SettableFuture<InstantRunBuildAnalyzer> instantRunBuildAnalyzerFuture =
- SettableFuture.create();
-
- public BlazeApkBuildStepInstantRun(
- Project project, ExecutionEnvironment env, Label label, ImmutableList<String> buildFlags) {
- this.project = project;
- this.executor = env.getExecutor();
- this.env = env;
- this.label = label;
- this.buildFlags = buildFlags;
- this.instantRunArtifactDirectory =
- BlazeInstantRunGradleIntegration.getInstantRunArtifactDirectory(project, label);
- this.instantRunBuildInfoFile =
- new File(instantRunArtifactDirectory, "build/reload-dex/debug/build-info.xml");
- this.instantRunGradleBuildFile = new File(instantRunArtifactDirectory, "build.gradle");
- this.instantRunGradlePropertiesFile =
- new File(instantRunArtifactDirectory, "gradle.properties");
- }
-
- @Override
- public boolean build(
- BlazeContext context, BlazeAndroidDeviceSelector.DeviceSession deviceSession) {
- if (!instantRunArtifactDirectory.exists() && !instantRunArtifactDirectory.mkdirs()) {
- IssueOutput.error(
- "Could not create instant run artifact directory: " + instantRunArtifactDirectory)
- .submit(context);
- return false;
- }
-
- BuildResult buildResult = buildApkManifest(context);
- if (buildResult == null) {
- return false;
- }
-
- String gradleUrl = BlazeInstantRunGradleIntegration.getGradleUrl(context);
- if (gradleUrl == null) {
- return false;
- }
-
- ApplicationIdProvider applicationIdProvider =
- new BlazeInstantRunApplicationIdProvider(project, buildResult);
- applicationIdProviderFuture.set(applicationIdProvider);
-
- // Write build.gradle
- try (PrintWriter printWriter = new PrintWriter(instantRunGradleBuildFile)) {
- printWriter.print(
- BlazeInstantRunGradleIntegration.getGradleBuildInfoString(
- gradleUrl, buildResult.executionRoot, buildResult.apkManifestProtoFile));
- } catch (IOException e) {
- IssueOutput.error("Could not write build.gradle file: " + e).submit(context);
- return false;
- }
-
- // Write gradle.properties
- try (PrintWriter printWriter = new PrintWriter(instantRunGradlePropertiesFile)) {
- printWriter.print(BlazeInstantRunGradleIntegration.getGradlePropertiesString());
- } catch (IOException e) {
- IssueOutput.error("Could not write build.gradle file: " + e).submit(context);
- return false;
- }
-
- String applicationId = null;
- try {
- applicationId = applicationIdProvider.getPackageName();
- } catch (ApkProvisionException e) {
- return false;
- }
-
- return invokeGradleIrTasks(context, deviceSession, buildResult, applicationId);
- }
-
- private BuildResult buildApkManifest(BlazeContext context) {
- final ScopedTask buildTask =
- new ScopedTask(context) {
- @Override
- protected void execute(@NotNull BlazeContext context) {
- WorkspaceRoot workspaceRoot = WorkspaceRoot.fromProject(project);
- String executionRoot = getExecutionRoot(context, workspaceRoot);
- if (executionRoot == null) {
- IssueOutput.error("Could not get execution root").submit(context);
- return;
- }
-
- BlazeCommand.Builder command =
- BlazeCommand.builder(Blaze.getBuildSystem(project), BlazeCommandName.BUILD);
-
- command
- .addTargets(label)
- .addBlazeFlags(buildFlags)
- .addBlazeFlags("--output_groups=apk_manifest")
- .addBlazeFlags(BlazeFlags.EXPERIMENTAL_SHOW_ARTIFACTS);
-
- List<File> apkManifestFiles = Lists.newArrayList();
-
- SaveUtil.saveAllFiles();
- int retVal =
- ExternalTask.builder(workspaceRoot)
- .addBlazeCommand(command.build())
- .context(context)
- .stderr(
- LineProcessingOutputStream.of(
- new ExperimentalShowArtifactsLineProcessor(
- apkManifestFiles, fileName -> fileName.endsWith("apk_manifest")),
- new IssueOutputLineProcessor(project, context, workspaceRoot)))
- .build()
- .run(new LoggedTimingScope(project, Action.BLAZE_BUILD));
- FileCaches.refresh(project);
-
- if (retVal != 0) {
- context.setHasError();
- return;
- }
-
- File apkManifestFile = Iterables.getOnlyElement(apkManifestFiles, null);
- if (apkManifestFile == null) {
- IssueOutput.error("Could not find APK manifest file").submit(context);
- return;
- }
-
- ApkManifestOuterClass.ApkManifest apkManifestProto;
- try (InputStream inputStream = new FileInputStream(apkManifestFile)) {
- apkManifestProto = ApkManifestOuterClass.ApkManifest.parseFrom(inputStream);
- } catch (IOException e) {
- LOG.error(e);
- IssueOutput.error("Error parsing apk proto").submit(context);
- return;
- }
-
- // Refresh the manifest
- File mergedManifestFile =
- new File(executionRoot, apkManifestProto.getAndroidManifest().getExecRootPath());
- ManifestParser.getInstance(project)
- .refreshManifests(ImmutableList.of(mergedManifestFile));
-
- BuildResult buildResult =
- new BuildResult(
- new File(executionRoot), mergedManifestFile, apkManifestFile, apkManifestProto);
- buildResultFuture.set(buildResult);
- }
- };
-
- BlazeExecutor.submitTask(
- project,
- String.format("Executing %s apk build", Blaze.buildSystemName(project)),
- buildTask);
-
- try {
- BuildResult buildResult = buildResultFuture.get();
- if (!context.shouldContinue()) {
- return null;
- }
- return buildResult;
- } catch (InterruptedException | ExecutionException e) {
- context.setHasError();
- } catch (CancellationException e) {
- context.setCancelled();
- }
- return null;
- }
-
- private boolean invokeGradleIrTasks(
- BlazeContext context,
- BlazeAndroidDeviceSelector.DeviceSession deviceSession,
- BuildResult buildResult,
- String applicationId) {
- InstantRunContext instantRunContext =
- new BlazeInstantRunContext(
- project, buildResult.apkManifestProto, applicationId, instantRunBuildInfoFile);
- instantRunContextFuture.set(instantRunContext);
- ProcessHandler previousSessionProcessHandler =
- deviceSession.sessionInfo != null ? deviceSession.sessionInfo.getProcessHandler() : null;
- DeviceFutures deviceFutures = deviceSession.deviceFutures;
- assert deviceFutures != null;
- List<AndroidDevice> targetDevices = deviceFutures.getDevices();
- AndroidDevice androidDevice = targetDevices.get(0);
- IDevice device = getLaunchedDevice(androidDevice);
-
- AndroidRunConfigContext runConfigContext = new AndroidRunConfigContext();
- runConfigContext.setTargetDevices(deviceFutures);
-
- AndroidSessionInfo info = deviceSession.sessionInfo;
- runConfigContext.setSameExecutorAsPreviousSession(
- info != null && executor.getId().equals(info.getExecutorId()));
- runConfigContext.setCleanRerun(InstantRunUtils.isCleanReRun(env));
-
- InstantRunBuilder instantRunBuilder =
- new InstantRunBuilder(
- device,
- instantRunContext,
- runConfigContext,
- new BlazeInstantRunTasksProvider(),
- RunAsValidityService.getInstance());
-
- try {
- List<String> cmdLineArgs = Lists.newArrayList();
- cmdLineArgs.addAll(MakeBeforeRunTaskProvider.getDeviceSpecificArguments(targetDevices));
- BlazeInstantRunGradleTaskRunner taskRunner =
- new BlazeInstantRunGradleTaskRunner(project, context, instantRunGradleBuildFile);
- boolean success = instantRunBuilder.build(taskRunner, cmdLineArgs);
- LOG.info("Gradle invocation complete, success = " + success);
- if (!success) {
- return false;
- }
- } catch (InvocationTargetException e) {
- LOG.info("Unexpected error while launching gradle before run tasks", e);
- return false;
- } catch (InterruptedException e) {
- LOG.info("Interrupted while launching gradle before run tasks");
- Thread.currentThread().interrupt();
- return false;
- }
-
- InstantRunBuildAnalyzer analyzer =
- new InstantRunBuildAnalyzer(project, instantRunContext, previousSessionProcessHandler);
- instantRunBuildAnalyzerFuture.set(analyzer);
- return true;
- }
-
- ListenableFuture<BuildResult> getBuildResult() {
- return buildResultFuture;
- }
-
- ListenableFuture<ApplicationIdProvider> getApplicationIdProvider() {
- return applicationIdProviderFuture;
- }
-
- ListenableFuture<InstantRunContext> getInstantRunContext() {
- return instantRunContextFuture;
- }
-
- ListenableFuture<InstantRunBuildAnalyzer> getInstantRunBuildAnalyzer() {
- return instantRunBuildAnalyzerFuture;
- }
-
- private String getExecutionRoot(BlazeContext context, WorkspaceRoot workspaceRoot) {
- ListenableFuture<String> execRootFuture =
- BlazeInfo.getInstance()
- .runBlazeInfo(
- context,
- Blaze.getBuildSystem(project),
- workspaceRoot,
- buildFlags,
- BlazeInfo.EXECUTION_ROOT_KEY);
- try {
- return execRootFuture.get();
- } catch (InterruptedException e) {
- context.setCancelled();
- } catch (ExecutionException e) {
- LOG.error(e);
- context.setHasError();
- }
- return null;
- }
-
- @Nullable
- private static IDevice getLaunchedDevice(@NotNull AndroidDevice device) {
- if (!device.getLaunchedDevice().isDone()) {
- // If we don't have access to the device (this happens if the AVD is still launching)
- return null;
- }
-
- try {
- return device.getLaunchedDevice().get(1, TimeUnit.MILLISECONDS);
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- return null;
- } catch (ExecutionException | TimeoutException e) {
- return null;
- }
- }
-}
diff --git a/aswb/src/com/google/idea/blaze/android/run/binary/instantrun/BlazeInstantRunApplicationIdProvider.java b/aswb/src/com/google/idea/blaze/android/run/binary/instantrun/BlazeInstantRunApplicationIdProvider.java
deleted file mode 100644
index ab1461e..0000000
--- a/aswb/src/com/google/idea/blaze/android/run/binary/instantrun/BlazeInstantRunApplicationIdProvider.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.android.run.binary.instantrun;
-
-import com.android.tools.idea.run.ApkProvisionException;
-import com.android.tools.idea.run.ApplicationIdProvider;
-import com.google.idea.blaze.android.manifest.ManifestParser;
-import com.intellij.openapi.application.ApplicationManager;
-import com.intellij.openapi.project.Project;
-import com.intellij.openapi.util.Computable;
-import java.io.File;
-import org.jetbrains.android.dom.manifest.Manifest;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-/** Application id provider for blaze instant run. */
-public class BlazeInstantRunApplicationIdProvider implements ApplicationIdProvider {
- private final Project project;
- private final BlazeApkBuildStepInstantRun.BuildResult buildResult;
-
- public BlazeInstantRunApplicationIdProvider(
- Project project, BlazeApkBuildStepInstantRun.BuildResult buildResult) {
- this.project = project;
- this.buildResult = buildResult;
- }
-
- @NotNull
- @Override
- public String getPackageName() throws ApkProvisionException {
- File manifestFile =
- new File(
- buildResult.executionRoot,
- buildResult.apkManifestProto.getAndroidManifest().getExecRootPath());
- Manifest manifest = ManifestParser.getInstance(project).getManifest(manifestFile);
- if (manifest == null) {
- throw new ApkProvisionException("Could not find merged manifest: " + manifestFile);
- }
- String applicationId =
- ApplicationManager.getApplication()
- .runReadAction((Computable<String>) () -> manifest.getPackage().getValue());
- if (applicationId == null) {
- throw new ApkProvisionException("No application id in merged manifest: " + manifestFile);
- }
- return applicationId;
- }
-
- @Nullable
- @Override
- public String getTestPackageName() throws ApkProvisionException {
- return null;
- }
-}
diff --git a/aswb/src/com/google/idea/blaze/android/run/binary/instantrun/BlazeInstantRunContext.java b/aswb/src/com/google/idea/blaze/android/run/binary/instantrun/BlazeInstantRunContext.java
deleted file mode 100644
index 9ea7c03..0000000
--- a/aswb/src/com/google/idea/blaze/android/run/binary/instantrun/BlazeInstantRunContext.java
+++ /dev/null
@@ -1,105 +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.android.run.binary.instantrun;
-
-import com.android.tools.fd.client.InstantRunBuildInfo;
-import com.android.tools.idea.fd.BuildSelection;
-import com.android.tools.idea.fd.FileChangeListener;
-import com.android.tools.idea.fd.InstantRunContext;
-import com.google.common.hash.HashCode;
-import com.google.repackaged.devtools.build.lib.rules.android.apkmanifest.ApkManifestOuterClass;
-import com.intellij.openapi.diagnostic.Logger;
-import com.intellij.openapi.project.Project;
-import java.io.File;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.Paths;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-/** Blaze implementation of instant run context. */
-public class BlazeInstantRunContext implements InstantRunContext {
- private static final Logger LOG = Logger.getInstance(BlazeInstantRunContext.class);
- private final Project project;
- private final ApkManifestOuterClass.ApkManifest apkManifest;
- private final String applicationId;
- private final File instantRunBuildInfoFile;
- private BuildSelection buildSelection;
-
- BlazeInstantRunContext(
- Project project,
- ApkManifestOuterClass.ApkManifest apkManifest,
- String applicationId,
- File instantRunBuildInfoFile) {
- this.project = project;
- this.apkManifest = apkManifest;
- this.applicationId = applicationId;
- this.instantRunBuildInfoFile = instantRunBuildInfoFile;
- }
-
- @NotNull
- @Override
- public String getApplicationId() {
- return applicationId;
- }
-
- @NotNull
- @Override
- public HashCode getManifestResourcesHash() {
- // TODO b/28373160
- return HashCode.fromInt(0);
- }
-
- @Override
- public boolean usesMultipleProcesses() {
- // TODO(tomlu) -- does this make sense in blaze? We can of course just parse the manifest.
- return false;
- }
-
- @Nullable
- @Override
- public FileChangeListener.Changes getFileChangesAndReset() {
- return null;
- }
-
- @Nullable
- @Override
- public InstantRunBuildInfo getInstantRunBuildInfo() {
- if (instantRunBuildInfoFile.exists()) {
- try {
- String xml =
- new String(
- Files.readAllBytes(Paths.get(instantRunBuildInfoFile.getPath())),
- StandardCharsets.UTF_8);
- return InstantRunBuildInfo.get(xml);
- } catch (IOException e) {
- LOG.error(e);
- }
- }
- return null;
- }
-
- @Override
- public void setBuildSelection(@NotNull BuildSelection buildSelection) {
- this.buildSelection = buildSelection;
- }
-
- @Override
- public BuildSelection getBuildSelection() {
- return buildSelection;
- }
-}
diff --git a/aswb/src/com/google/idea/blaze/android/run/binary/instantrun/BlazeInstantRunDeviceSelector.java b/aswb/src/com/google/idea/blaze/android/run/binary/instantrun/BlazeInstantRunDeviceSelector.java
deleted file mode 100644
index 8c26fc4..0000000
--- a/aswb/src/com/google/idea/blaze/android/run/binary/instantrun/BlazeInstantRunDeviceSelector.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * 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.binary.instantrun;
-
-import com.android.ddmlib.IDevice;
-import com.android.tools.idea.fd.InstantRunManager;
-import com.android.tools.idea.fd.InstantRunUtils;
-import com.android.tools.idea.run.AndroidSessionInfo;
-import com.android.tools.idea.run.DeviceFutures;
-import com.google.idea.blaze.android.run.runner.BlazeAndroidDeviceSelector;
-import com.google.idea.blaze.android.run.runner.BlazeAndroidRunConfigurationDeployTargetManager;
-import com.intellij.execution.ExecutionException;
-import com.intellij.execution.Executor;
-import com.intellij.execution.runners.ExecutionEnvironment;
-import com.intellij.openapi.project.Project;
-import java.util.List;
-import javax.annotation.Nullable;
-import org.jetbrains.android.facet.AndroidFacet;
-
-/** Tries to reuse devices from a previous session. */
-public class BlazeInstantRunDeviceSelector implements BlazeAndroidDeviceSelector {
- NormalDeviceSelector normalDeviceSelector = new NormalDeviceSelector();
-
- @Override
- public DeviceSession getDevice(
- Project project,
- AndroidFacet facet,
- BlazeAndroidRunConfigurationDeployTargetManager deployTargetManager,
- Executor executor,
- ExecutionEnvironment env,
- AndroidSessionInfo info,
- boolean debug,
- int runConfigId)
- throws ExecutionException {
- DeviceFutures deviceFutures = null;
- if (info != null) {
- // if there is an existing previous session,
- // then see if we can detect devices to fast deploy to
- deviceFutures = getFastDeployDevices(executor, info);
-
- if (InstantRunUtils.isReRun(env)) {
- info.getProcessHandler().destroyProcess();
- info = null;
- }
- }
-
- if (deviceFutures != null) {
- return new DeviceSession(null, deviceFutures, info);
- }
-
- // Fall back to normal device selection
- return normalDeviceSelector.getDevice(
- project, facet, deployTargetManager, executor, env, info, debug, runConfigId);
- }
-
- @Nullable
- private static DeviceFutures getFastDeployDevices(Executor executor, AndroidSessionInfo info) {
- if (!info.getExecutorId().equals(executor.getId())) {
- String msg =
- String.format(
- "Cannot Instant Run since old executor (%1$s) doesn't match current executor (%2$s)",
- info.getExecutorId(), executor.getId());
- InstantRunManager.LOG.info(msg);
- return null;
- }
-
- List<IDevice> devices = info.getDevices();
- if (devices == null || devices.isEmpty()) {
- InstantRunManager.LOG.info(
- "Cannot Instant Run since we could not locate "
- + "the devices from the existing launch session");
- return null;
- }
-
- if (devices.size() > 1) {
- InstantRunManager.LOG.info(
- "Last run was on > 1 device, not reusing devices and prompting again");
- return null;
- }
-
- return DeviceFutures.forDevices(devices);
- }
-}
diff --git a/aswb/src/com/google/idea/blaze/android/run/binary/instantrun/BlazeInstantRunGradleIntegration.java b/aswb/src/com/google/idea/blaze/android/run/binary/instantrun/BlazeInstantRunGradleIntegration.java
deleted file mode 100644
index a9873ca..0000000
--- a/aswb/src/com/google/idea/blaze/android/run/binary/instantrun/BlazeInstantRunGradleIntegration.java
+++ /dev/null
@@ -1,126 +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.android.run.binary.instantrun;
-
-import com.android.SdkConstants;
-import com.google.common.base.Joiner;
-import com.google.common.hash.Hashing;
-import com.google.idea.blaze.base.async.process.ExternalTask;
-import com.google.idea.blaze.base.async.process.LineProcessingOutputStream;
-import com.google.idea.blaze.base.async.process.PrintOutputLineProcessor;
-import com.google.idea.blaze.base.model.primitives.Label;
-import com.google.idea.blaze.base.scope.BlazeContext;
-import com.google.idea.blaze.base.scope.output.IssueOutput;
-import com.google.idea.blaze.base.scope.output.StatusOutput;
-import com.google.idea.blaze.base.settings.BlazeImportSettings;
-import com.google.idea.blaze.base.settings.BlazeImportSettingsManager;
-import com.google.idea.blaze.base.sync.data.BlazeDataStorage;
-import com.google.idea.common.experiments.DeveloperFlag;
-import com.google.idea.common.experiments.StringExperiment;
-import com.intellij.openapi.application.PathManager;
-import com.intellij.openapi.project.Project;
-import java.io.File;
-import javax.annotation.Nullable;
-
-/** Defines where instant run storage and artifacts go. */
-class BlazeInstantRunGradleIntegration {
- private static final String INSTANT_RUN_SUBDIRECTORY = "instantrun";
-
- private static final StringExperiment LOCAL_GRADLE_VERSION =
- new StringExperiment("use.local.gradle.version");
- private static final DeveloperFlag REBUILD_LOCAL_GRADLE =
- new DeveloperFlag("rebuild.local.gradle");
-
- /** Gets a unique directory for a given target that can be used for the build process. */
- static File getInstantRunArtifactDirectory(Project project, Label target) {
- BlazeImportSettings importSettings =
- BlazeImportSettingsManager.getInstance(project).getImportSettings();
- assert importSettings != null;
- File dataSubDirectory = BlazeDataStorage.getProjectDataDir(importSettings);
- File instantRunDirectory = new File(dataSubDirectory, INSTANT_RUN_SUBDIRECTORY);
- String targetHash = Hashing.md5().hashUnencodedChars(target.toString()).toString();
- return new File(instantRunDirectory, targetHash);
- }
-
- @Nullable
- static String getGradleUrl(BlazeContext context) {
- String localGradleVersion = LOCAL_GRADLE_VERSION.getValue();
- boolean isDevMode = localGradleVersion != null;
-
- if (isDevMode) {
- String toolsIdeaPath = PathManager.getHomePath();
- File toolsDir = new File(toolsIdeaPath).getParentFile();
- File repoDir = toolsDir.getParentFile();
- File localGradleDirectory =
- new File(
- new File(repoDir, "out/repo/com/android/tools/build/builder"), localGradleVersion);
- if (REBUILD_LOCAL_GRADLE.getValue() || !localGradleDirectory.exists()) {
- // Build gradle
- context.output(new StatusOutput("Building local Gradle..."));
- int retVal =
- ExternalTask.builder(toolsDir)
- .args("./gradlew", ":init", ":publishLocal")
- .stdout(LineProcessingOutputStream.of(new PrintOutputLineProcessor(context)))
- .build()
- .run();
-
- if (retVal != 0) {
- IssueOutput.error("Gradle build failed.").submit(context);
- return null;
- }
- }
- return new File(repoDir, "out/repo").getPath();
- }
-
- // Not supported yet
- IssueOutput.error(
- "You must specify 'use.local.gradle.version' experiment, "
- + "non-local gradle not supported yet.")
- .submit(context);
- return null;
- }
-
- static String getGradlePropertiesString() {
- return Joiner.on('\n')
- .join("org.gradle.daemon=true", "org.gradle.jvmargs=-XX:MaxPermSize=1024m -Xmx4096m");
- }
-
- static String getGradleBuildInfoString(
- String gradleUrl, File executionRoot, File apkManifestFile) {
- String template =
- Joiner.on('\n')
- .join(
- "buildscript {",
- " repositories {",
- " jcenter()",
- " maven { url '%s' }",
- " }",
- " dependencies {",
- " classpath 'com.android.tools.build:gradle:%s'",
- " }",
- "}",
- "apply plugin: 'com.android.external.build'",
- "externalBuild {",
- " executionRoot = '%s'",
- " buildManifestPath = '%s'",
- "}");
- String gradleVersion = LOCAL_GRADLE_VERSION.getValue();
- gradleVersion = gradleVersion != null ? gradleVersion : SdkConstants.GRADLE_LATEST_VERSION;
-
- return String.format(
- template, gradleUrl, gradleVersion, executionRoot.getPath(), apkManifestFile.getPath());
- }
-}
diff --git a/aswb/src/com/google/idea/blaze/android/run/binary/instantrun/BlazeInstantRunGradleTaskRunner.java b/aswb/src/com/google/idea/blaze/android/run/binary/instantrun/BlazeInstantRunGradleTaskRunner.java
deleted file mode 100644
index bdeab5a..0000000
--- a/aswb/src/com/google/idea/blaze/android/run/binary/instantrun/BlazeInstantRunGradleTaskRunner.java
+++ /dev/null
@@ -1,130 +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.android.run.binary.instantrun;
-
-import static com.android.tools.idea.gradle.util.GradleUtil.GRADLE_SYSTEM_ID;
-import static com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskType.EXECUTE_TASK;
-
-import com.android.builder.model.AndroidProject;
-import com.android.tools.idea.gradle.invoker.GradleInvocationResult;
-import com.android.tools.idea.gradle.invoker.GradleInvoker;
-import com.android.tools.idea.gradle.run.GradleTaskRunner;
-import com.android.tools.idea.gradle.util.AndroidGradleSettings;
-import com.android.tools.idea.gradle.util.BuildMode;
-import com.google.common.base.Strings;
-import com.google.common.collect.ImmutableList;
-import com.google.idea.blaze.base.scope.BlazeContext;
-import com.google.idea.blaze.base.scope.output.PrintOutput;
-import com.intellij.openapi.application.ApplicationManager;
-import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskId;
-import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskNotificationListenerAdapter;
-import com.intellij.openapi.project.Project;
-import com.intellij.util.concurrency.Semaphore;
-import java.io.File;
-import java.lang.reflect.InvocationTargetException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicBoolean;
-import javax.swing.SwingUtilities;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-class BlazeInstantRunGradleTaskRunner implements GradleTaskRunner {
- private final Project project;
- private final BlazeContext context;
- private final File instantRunGradleBuildFile;
-
- public BlazeInstantRunGradleTaskRunner(
- Project project, BlazeContext context, File instantRunGradleBuildFile) {
- this.project = project;
- this.context = context;
- this.instantRunGradleBuildFile = instantRunGradleBuildFile;
- }
-
- @Override
- public boolean run(
- @NotNull List<String> tasks,
- @Nullable BuildMode buildMode,
- @NotNull List<String> commandLineArguments)
- throws InvocationTargetException, InterruptedException {
- assert !ApplicationManager.getApplication().isDispatchThread();
-
- final GradleInvoker gradleInvoker = GradleInvoker.getInstance(project);
-
- final AtomicBoolean success = new AtomicBoolean();
- final Semaphore done = new Semaphore();
- done.down();
-
- final GradleInvoker.AfterGradleInvocationTask afterTask =
- new GradleInvoker.AfterGradleInvocationTask() {
- @Override
- public void execute(@NotNull GradleInvocationResult result) {
- success.set(result.isBuildSuccessful());
- gradleInvoker.removeAfterGradleInvocationTask(this);
- done.up();
- }
- };
-
- ExternalSystemTaskId taskId =
- ExternalSystemTaskId.create(GRADLE_SYSTEM_ID, EXECUTE_TASK, project);
- List<String> jvmArguments = ImmutableList.of();
-
- // https://code.google.com/p/android/issues/detail?id=213040 -
- // make split apks only available if an env var is set
- List<String> args = new ArrayList<>(commandLineArguments);
- if (!Boolean.valueOf(System.getenv(GradleTaskRunner.USE_SPLIT_APK))) {
- // force multi dex when the env var is not set to true
- args.add(
- AndroidGradleSettings.createProjectProperty(
- AndroidProject.PROPERTY_SIGNING_COLDSWAP_MODE, "MULTIDEX"));
- }
-
- // To ensure that the "Run Configuration" waits for the Gradle tasks to be executed,
- // we use SwingUtilities.invokeAndWait. I tried
- // using Application.invokeAndWait but it never worked.
- // IDEA also uses SwingUtilities in this scenario (see CompileStepBeforeRun.)
- SwingUtilities.invokeAndWait(
- () -> {
- gradleInvoker.addAfterGradleInvocationTask(afterTask);
- gradleInvoker.executeTasks(
- tasks,
- jvmArguments,
- args,
- taskId,
- new GradleNotificationListener(),
- instantRunGradleBuildFile,
- false,
- true);
- });
-
- done.waitFor();
- return success.get();
- }
-
- class GradleNotificationListener extends ExternalSystemTaskNotificationListenerAdapter {
- @Override
- public void onTaskOutput(
- @NotNull ExternalSystemTaskId id, @NotNull String text, boolean stdOut) {
- super.onTaskOutput(id, text, stdOut);
- String toPrint = text.trim();
- if (!Strings.isNullOrEmpty(toPrint)) {
- context.output(
- new PrintOutput(
- toPrint, stdOut ? PrintOutput.OutputType.NORMAL : PrintOutput.OutputType.ERROR));
- }
- }
- }
-}
diff --git a/aswb/src/com/google/idea/blaze/android/run/binary/instantrun/BlazeInstantRunTasksProvider.java b/aswb/src/com/google/idea/blaze/android/run/binary/instantrun/BlazeInstantRunTasksProvider.java
deleted file mode 100644
index 52a82f7..0000000
--- a/aswb/src/com/google/idea/blaze/android/run/binary/instantrun/BlazeInstantRunTasksProvider.java
+++ /dev/null
@@ -1,36 +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.android.run.binary.instantrun;
-
-import com.android.tools.idea.fd.InstantRunTasksProvider;
-import com.google.common.collect.ImmutableList;
-import java.util.List;
-import org.jetbrains.annotations.NotNull;
-
-/** Returns blaze-specific instant run tasks. */
-public class BlazeInstantRunTasksProvider implements InstantRunTasksProvider {
- @NotNull
- @Override
- public List<String> getCleanAndGenerateSourcesTasks() {
- return ImmutableList.of();
- }
-
- @NotNull
- @Override
- public List<String> getFullBuildTasks() {
- return ImmutableList.of("process");
- }
-}
diff --git a/aswb/src/com/google/idea/blaze/android/run/binary/mobileinstall/BlazeAndroidBinaryMobileInstallRunContext.java b/aswb/src/com/google/idea/blaze/android/run/binary/mobileinstall/BlazeAndroidBinaryMobileInstallRunContext.java
index a25eba9..2ea7ebc 100644
--- a/aswb/src/com/google/idea/blaze/android/run/binary/mobileinstall/BlazeAndroidBinaryMobileInstallRunContext.java
+++ b/aswb/src/com/google/idea/blaze/android/run/binary/mobileinstall/BlazeAndroidBinaryMobileInstallRunContext.java
@@ -30,6 +30,7 @@
import com.android.tools.idea.run.util.ProcessHandlerLaunchStatus;
import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.Futures;
+import com.google.idea.blaze.android.compatibility.Compatibility;
import com.google.idea.blaze.android.run.binary.BlazeAndroidBinaryApplicationIdProvider;
import com.google.idea.blaze.android.run.binary.BlazeAndroidBinaryApplicationLaunchTaskProvider;
import com.google.idea.blaze.android.run.binary.BlazeAndroidBinaryConsoleProvider;
@@ -159,14 +160,22 @@
@Nullable
@Override
+ @SuppressWarnings("unchecked")
public DebugConnectorTask getDebuggerTask(
AndroidDebugger androidDebugger,
AndroidDebuggerState androidDebuggerState,
- Set<String> packageIds)
+ Set<String> packageIds,
+ boolean monitorRemoteProcess)
throws ExecutionException {
- //noinspection unchecked
- return androidDebugger.getConnectDebuggerTask(
- env, null, packageIds, facet, androidDebuggerState, runConfiguration.getType().getId());
+ return Compatibility.getConnectDebuggerTask(
+ androidDebugger,
+ env,
+ null,
+ packageIds,
+ facet,
+ androidDebuggerState,
+ runConfiguration.getType().getId(),
+ monitorRemoteProcess);
}
@Nullable
diff --git a/aswb/src/com/google/idea/blaze/android/run/binary/mobileinstall/BlazeApkBuildStepMobileInstall.java b/aswb/src/com/google/idea/blaze/android/run/binary/mobileinstall/BlazeApkBuildStepMobileInstall.java
index 24d49b6..1256ff4 100644
--- a/aswb/src/com/google/idea/blaze/android/run/binary/mobileinstall/BlazeApkBuildStepMobileInstall.java
+++ b/aswb/src/com/google/idea/blaze/android/run/binary/mobileinstall/BlazeApkBuildStepMobileInstall.java
@@ -23,6 +23,7 @@
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture;
import com.google.common.util.concurrent.UncheckedExecutionException;
+import com.google.idea.blaze.android.compatibility.Compatibility.AndroidSdkUtils;
import com.google.idea.blaze.android.run.deployinfo.BlazeAndroidDeployInfo;
import com.google.idea.blaze.android.run.deployinfo.BlazeApkDeployInfoProtoHelper;
import com.google.idea.blaze.android.run.runner.BlazeAndroidDeviceSelector;
@@ -56,8 +57,6 @@
import java.nio.file.Paths;
import java.util.concurrent.CancellationException;
import javax.annotation.Nullable;
-import org.jetbrains.android.sdk.AndroidSdkUtils;
-import org.jetbrains.annotations.NotNull;
/** Builds and installs the APK using mobile-install. */
public class BlazeApkBuildStepMobileInstall implements BlazeApkBuildStep {
@@ -89,7 +88,7 @@
final ScopedTask buildTask =
new ScopedTask(context) {
@Override
- protected void execute(@NotNull BlazeContext context) {
+ protected void execute(BlazeContext context) {
boolean incrementalInstall = env.getExecutor() instanceof IncrementalInstallExecutor;
DeviceFutures deviceFutures = deviceSession.deviceFutures;
@@ -203,8 +202,7 @@
}
@Nullable
- private static IDevice resolveDevice(
- @NotNull BlazeContext context, @NotNull DeviceFutures deviceFutures) {
+ private static IDevice resolveDevice(BlazeContext context, DeviceFutures deviceFutures) {
if (deviceFutures.get().size() != 1) {
IssueOutput.error("Only one device can be used with mobile-install.").submit(context);
return null;
diff --git a/aswb/src/com/google/idea/blaze/android/run/deployinfo/BlazeAndroidDeployInfo.java b/aswb/src/com/google/idea/blaze/android/run/deployinfo/BlazeAndroidDeployInfo.java
index e5f5ad3..afb7909 100644
--- a/aswb/src/com/google/idea/blaze/android/run/deployinfo/BlazeAndroidDeployInfo.java
+++ b/aswb/src/com/google/idea/blaze/android/run/deployinfo/BlazeAndroidDeployInfo.java
@@ -18,7 +18,9 @@
import com.google.common.collect.Lists;
import com.google.idea.blaze.android.manifest.ManifestParser;
import com.google.repackaged.devtools.build.lib.rules.android.deployinfo.AndroidDeployInfoOuterClass;
+import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Computable;
import java.io.File;
import java.util.List;
import java.util.Objects;
@@ -49,7 +51,11 @@
@Nullable
public Manifest getMergedManifest() {
File manifestFile = getMergedManifestFile();
- return ManifestParser.getInstance(project).getManifest(manifestFile);
+ // Run in a read action since otherwise, it might throw a read access exception.
+ return ApplicationManager.getApplication()
+ .runReadAction(
+ (Computable<Manifest>)
+ () -> ManifestParser.getInstance(project).getManifest(manifestFile));
}
public List<File> getAdditionalMergedManifestFiles() {
diff --git a/aswb/src/com/google/idea/blaze/android/run/runner/BlazeAndroidLaunchTasksProvider.java b/aswb/src/com/google/idea/blaze/android/run/runner/BlazeAndroidLaunchTasksProvider.java
index 7354b25..dacc20b 100644
--- a/aswb/src/com/google/idea/blaze/android/run/runner/BlazeAndroidLaunchTasksProvider.java
+++ b/aswb/src/com/google/idea/blaze/android/run/runner/BlazeAndroidLaunchTasksProvider.java
@@ -169,7 +169,8 @@
}
try {
- return runContext.getDebuggerTask(androidDebugger, androidDebuggerState, packageIds);
+ return runContext.getDebuggerTask(
+ androidDebugger, androidDebuggerState, packageIds, monitorRemoteProcess());
} catch (ExecutionException e) {
launchStatus.terminateLaunch(e.getMessage());
return null;
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 76e3bfc..6d8feb4 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
@@ -36,6 +36,7 @@
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;
import com.google.idea.blaze.base.scope.output.IssueOutput;
import com.google.idea.blaze.base.scope.scopes.BlazeConsoleScope;
@@ -79,7 +80,7 @@
private static final Key<BlazeAndroidRunContext> RUN_CONTEXT_KEY =
Key.create("blaze.run.context");
- private static final Key<BlazeAndroidDeviceSelector.DeviceSession> DEVICE_SESSION_KEY =
+ public static final Key<BlazeAndroidDeviceSelector.DeviceSession> DEVICE_SESSION_KEY =
Key.create("blaze.device.session");
private final Module module;
@@ -251,7 +252,17 @@
@Nullable
@Override
- public ExecutionResult execute(Executor executor, @NotNull ProgramRunner runner)
+ public ExecutionResult execute(Executor executor, ProgramRunner runner)
+ throws ExecutionException {
+ DefaultExecutionResult result = executeInner(executor, runner);
+ if (result == null) {
+ return null;
+ }
+ return SmRunnerUtils.attachRerunFailedTestsAction(result);
+ }
+
+ @Nullable
+ private DefaultExecutionResult executeInner(Executor executor, @NotNull ProgramRunner<?> runner)
throws ExecutionException {
ProcessHandler processHandler;
ConsoleView console;
diff --git a/aswb/src/com/google/idea/blaze/android/run/runner/BlazeAndroidRunContext.java b/aswb/src/com/google/idea/blaze/android/run/runner/BlazeAndroidRunContext.java
index 1778a63..a0653a1 100644
--- a/aswb/src/com/google/idea/blaze/android/run/runner/BlazeAndroidRunContext.java
+++ b/aswb/src/com/google/idea/blaze/android/run/runner/BlazeAndroidRunContext.java
@@ -72,7 +72,8 @@
DebugConnectorTask getDebuggerTask(
AndroidDebugger androidDebugger,
AndroidDebuggerState androidDebuggerState,
- Set<String> packageIds)
+ Set<String> packageIds,
+ boolean monitorRemoteProcess)
throws ExecutionException;
@Nullable
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 56ff338..d91419a 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
@@ -16,10 +16,13 @@
package com.google.idea.blaze.android.run.test;
import com.android.tools.idea.run.ConsoleProvider;
-import com.android.tools.idea.run.testing.AndroidTestConsoleProperties;
+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.SmRunnerUtils;
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.filters.TextConsoleBuilderFactory;
import com.intellij.execution.process.ProcessHandler;
import com.intellij.execution.testframework.sm.SMTestRunnerConnectionUtil;
@@ -27,37 +30,50 @@
import com.intellij.openapi.Disposable;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Disposer;
-import org.jetbrains.annotations.NotNull;
+import javax.annotation.Nullable;
/** Console provider for android_test */
class AndroidTestConsoleProvider implements ConsoleProvider {
private final Project project;
private final RunConfiguration runConfiguration;
private final BlazeAndroidTestRunConfigurationState configState;
+ @Nullable private final BlazeAndroidTestEventsHandler testEventsHandler;
AndroidTestConsoleProvider(
Project project,
RunConfiguration runConfiguration,
- BlazeAndroidTestRunConfigurationState configState) {
+ BlazeAndroidTestRunConfigurationState configState,
+ @Nullable BlazeAndroidTestEventsHandler testEventsHandler) {
this.project = project;
this.runConfiguration = runConfiguration;
this.configState = configState;
+ this.testEventsHandler = testEventsHandler;
}
- @NotNull
@Override
- public ConsoleView createAndAttach(
- @NotNull Disposable parent, @NotNull ProcessHandler handler, @NotNull Executor executor)
+ public ConsoleView createAndAttach(Disposable parent, ProcessHandler handler, Executor executor)
throws ExecutionException {
if (!configState.isRunThroughBlaze()) {
return getStockConsoleProvider().createAndAttach(parent, handler, executor);
}
- ConsoleView console =
- TextConsoleBuilderFactory.getInstance().createBuilder(project).getConsole();
+ ConsoleView console = createBlazeTestConsole(executor);
console.attachToProcess(handler);
return console;
}
+ private ConsoleView createBlazeTestConsole(Executor executor) {
+ if (testEventsHandler == null || isDebugging(executor)) {
+ // SM runner console not yet supported when debugging, because we're calling this once per
+ // test case (see ConnectBlazeTestDebuggerTask::setUpForReattachingDebugger)
+ return TextConsoleBuilderFactory.getInstance().createBuilder(project).getConsole();
+ }
+ return SmRunnerUtils.getConsoleView(project, runConfiguration, executor, testEventsHandler);
+ }
+
+ private static boolean isDebugging(Executor executor) {
+ return executor instanceof DefaultDebugExecutor;
+ }
+
private ConsoleProvider getStockConsoleProvider() {
return (parent, handler, executor) -> {
AndroidTestConsoleProperties properties =
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 64e8239..8d08ec0 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
@@ -15,8 +15,8 @@
*/
package com.google.idea.blaze.android.run.test;
-import com.android.tools.idea.run.testing.AndroidTestRunConfiguration;
import com.google.common.base.Strings;
+import com.google.idea.blaze.android.compatibility.Compatibility.AndroidTestRunConfiguration;
import com.google.idea.blaze.base.ideinfo.TargetIdeInfo;
import com.google.idea.blaze.base.model.primitives.Kind;
import com.google.idea.blaze.base.run.BlazeCommandRunConfiguration;
diff --git a/aswb/src/com/google/idea/blaze/android/run/test/BlazeAndroidTestFilter.java b/aswb/src/com/google/idea/blaze/android/run/test/BlazeAndroidTestFilter.java
index c6f4a3b..e5f2b1d 100644
--- a/aswb/src/com/google/idea/blaze/android/run/test/BlazeAndroidTestFilter.java
+++ b/aswb/src/com/google/idea/blaze/android/run/test/BlazeAndroidTestFilter.java
@@ -15,8 +15,8 @@
*/
package com.google.idea.blaze.android.run.test;
-import com.android.tools.idea.run.testing.AndroidTestRunConfiguration;
import com.google.common.collect.ImmutableList;
+import com.google.idea.blaze.android.compatibility.Compatibility.AndroidTestRunConfiguration;
import com.google.idea.blaze.base.command.BlazeFlags;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
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 0c42ea9..e37f07f 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
@@ -15,8 +15,8 @@
*/
package com.google.idea.blaze.android.run.test;
-import com.android.tools.idea.run.testing.AndroidTestRunConfiguration;
import com.google.common.base.Strings;
+import com.google.idea.blaze.android.compatibility.Compatibility.AndroidTestRunConfiguration;
import com.google.idea.blaze.base.ideinfo.TargetIdeInfo;
import com.google.idea.blaze.base.model.primitives.Kind;
import com.google.idea.blaze.base.run.BlazeCommandRunConfiguration;
diff --git a/aswb/src/com/google/idea/blaze/android/run/test/BlazeAndroidTestRunConfigurationStateEditor.java b/aswb/src/com/google/idea/blaze/android/run/test/BlazeAndroidTestRunConfigurationStateEditor.java
index eb8293a..5df4512 100644
--- a/aswb/src/com/google/idea/blaze/android/run/test/BlazeAndroidTestRunConfigurationStateEditor.java
+++ b/aswb/src/com/google/idea/blaze/android/run/test/BlazeAndroidTestRunConfigurationStateEditor.java
@@ -16,10 +16,10 @@
package com.google.idea.blaze.android.run.test;
-import static com.android.tools.idea.run.testing.AndroidTestRunConfiguration.TEST_ALL_IN_MODULE;
-import static com.android.tools.idea.run.testing.AndroidTestRunConfiguration.TEST_ALL_IN_PACKAGE;
-import static com.android.tools.idea.run.testing.AndroidTestRunConfiguration.TEST_CLASS;
-import static com.android.tools.idea.run.testing.AndroidTestRunConfiguration.TEST_METHOD;
+import static com.google.idea.blaze.android.compatibility.Compatibility.AndroidTestRunConfiguration.TEST_ALL_IN_MODULE;
+import static com.google.idea.blaze.android.compatibility.Compatibility.AndroidTestRunConfiguration.TEST_ALL_IN_PACKAGE;
+import static com.google.idea.blaze.android.compatibility.Compatibility.AndroidTestRunConfiguration.TEST_CLASS;
+import static com.google.idea.blaze.android.compatibility.Compatibility.AndroidTestRunConfiguration.TEST_METHOD;
import com.google.idea.blaze.base.run.state.RunConfigurationState;
import com.google.idea.blaze.base.run.state.RunConfigurationStateEditor;
@@ -64,6 +64,8 @@
private JCheckBox runThroughBlazeTestCheckBox;
private final JRadioButton[] testingType2RadioButton = new JRadioButton[4];
+ private boolean componentEnabled = true;
+
BlazeAndroidTestRunConfigurationStateEditor(
RunConfigurationStateEditor commonStateEditor, Project project) {
this.commonStateEditor = commonStateEditor;
@@ -478,4 +480,29 @@
public JComponent createComponent() {
return UiUtil.createBox(commonStateEditor.createComponent(), panel);
}
+
+ @Override
+ public void setComponentEnabled(boolean enabled) {
+ componentEnabled = enabled;
+ updateEnabledState();
+ }
+
+ private void updateEnabledState() {
+ commonStateEditor.setComponentEnabled(componentEnabled);
+ allInPackageButton.setEnabled(componentEnabled);
+ classButton.setEnabled(componentEnabled);
+ testMethodButton.setEnabled(componentEnabled);
+ allInTargetButton.setEnabled(componentEnabled);
+ packageComponent.setEnabled(componentEnabled);
+ classComponent.setEnabled(componentEnabled);
+ methodComponent.setEnabled(componentEnabled);
+ runnerComponent.setEnabled(componentEnabled);
+ labelTest.setEnabled(componentEnabled);
+ runThroughBlazeTestCheckBox.setEnabled(componentEnabled);
+ for (JComponent button : testingType2RadioButton) {
+ if (button != null) {
+ button.setEnabled(componentEnabled);
+ }
+ }
+ }
}
diff --git a/aswb/src/com/google/idea/blaze/android/run/test/BlazeAndroidTestRunConfigurationType.java b/aswb/src/com/google/idea/blaze/android/run/test/BlazeAndroidTestRunConfigurationType.java
deleted file mode 100644
index 2104d75..0000000
--- a/aswb/src/com/google/idea/blaze/android/run/test/BlazeAndroidTestRunConfigurationType.java
+++ /dev/null
@@ -1,132 +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.android.run.test;
-
-import com.google.idea.blaze.base.run.BlazeCommandRunConfiguration;
-import com.google.idea.blaze.base.run.BlazeCommandRunConfigurationType;
-import com.google.idea.blaze.base.settings.Blaze;
-import com.intellij.execution.BeforeRunTask;
-import com.intellij.execution.configurations.ConfigurationFactory;
-import com.intellij.execution.configurations.ConfigurationType;
-import com.intellij.execution.configurations.ConfigurationTypeUtil;
-import com.intellij.execution.configurations.UnknownConfigurationType;
-import com.intellij.icons.AllIcons;
-import com.intellij.openapi.project.Project;
-import com.intellij.openapi.util.Key;
-import com.intellij.ui.LayeredIcon;
-import icons.AndroidIcons;
-import javax.swing.Icon;
-import org.jetbrains.annotations.NotNull;
-
-/**
- * A type for Android test run configurations adapted specifically to run android_test targets.
- *
- * @deprecated See {@link com.google.idea.blaze.base.run.BlazeCommandRunConfigurationType}. Retained
- * in 1.9 for legacy purposes, to allow existing BlazeAndroidTestRunConfigurations to be updated
- * to BlazeCommandRunConfigurations. Intended to be removed in 2.1.
- */
-// Hack: extend UnknownConfigurationType to completely hide it in the Run/Debug Configurations UI.
-@Deprecated
-public class BlazeAndroidTestRunConfigurationType extends UnknownConfigurationType {
- private static final Icon ANDROID_TEST_ICON;
-
- static {
- LayeredIcon icon = new LayeredIcon(2);
- icon.setIcon(AndroidIcons.Android, 0);
- icon.setIcon(AllIcons.Nodes.JunitTestMark, 1);
- ANDROID_TEST_ICON = icon;
- }
-
- private final BlazeAndroidTestRunConfigurationFactory factory =
- new BlazeAndroidTestRunConfigurationFactory(this);
-
- static class BlazeAndroidTestRunConfigurationFactory extends ConfigurationFactory {
-
- protected BlazeAndroidTestRunConfigurationFactory(@NotNull ConfigurationType type) {
- super(type);
- }
-
- @Override
- public String getName() {
- // Used to look up this ConfigurationFactory.
- // Preserve value so legacy configurations can be loaded.
- return Blaze.defaultBuildSystemName() + " Android Test";
- }
-
- @Override
- @NotNull
- public BlazeCommandRunConfiguration createTemplateConfiguration(@NotNull Project project) {
- // Create a BlazeCommandRunConfiguration instead, to update legacy configurations.
- return BlazeCommandRunConfigurationType.getInstance()
- .getFactory()
- .createTemplateConfiguration(project);
- }
-
- @Override
- public boolean canConfigurationBeSingleton() {
- return false;
- }
-
- @Override
- public boolean isApplicable(@NotNull Project project) {
- return Blaze.isBlazeProject(project);
- }
-
- @Override
- public void configureBeforeRunTaskDefaults(
- Key<? extends BeforeRunTask> providerID, BeforeRunTask task) {
- // Removed BlazeAndroidBeforeRunTaskProvider; this method won't be called anymore anyhow.
- }
-
- @Override
- public boolean isConfigurationSingletonByDefault() {
- return true;
- }
- }
-
- public static BlazeAndroidTestRunConfigurationType getInstance() {
- return ConfigurationTypeUtil.findConfigurationType(BlazeAndroidTestRunConfigurationType.class);
- }
-
- @Override
- public String getDisplayName() {
- return "Legacy " + Blaze.defaultBuildSystemName() + " Android Test";
- }
-
- @Override
- public String getConfigurationTypeDescription() {
- return "Launch/debug configuration for android_test rules "
- + "Use Blaze Command instead; this legacy configuration type is being removed.";
- }
-
- @Override
- public Icon getIcon() {
- return ANDROID_TEST_ICON;
- }
-
- @Override
- @NotNull
- public String getId() {
- // Used to look up this ConfigurationType.
- // Preserve value so legacy configurations can be loaded.
- return "BlazeAndroidTestRunConfigurationType";
- }
-
- @Override
- public BlazeAndroidTestRunConfigurationFactory[] getConfigurationFactories() {
- return new BlazeAndroidTestRunConfigurationFactory[] {factory};
- }
-}
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 ee1013b..cf418e9 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
@@ -33,6 +33,7 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.Futures;
+import com.google.idea.blaze.android.compatibility.Compatibility;
import com.google.idea.blaze.android.run.deployinfo.BlazeAndroidDeployInfo;
import com.google.idea.blaze.android.run.deployinfo.BlazeApkProvider;
import com.google.idea.blaze.android.run.runner.BlazeAndroidDeviceSelector;
@@ -41,9 +42,13 @@
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.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;
import java.util.Collection;
@@ -55,6 +60,10 @@
/** Run context for android_test. */
class BlazeAndroidTestRunContext implements BlazeAndroidRunContext {
+
+ static final BoolExperiment smRunnerUiEnabled =
+ new BoolExperiment("use.smrunner.ui.android", true);
+
private final Project project;
private final AndroidFacet facet;
private final RunConfiguration runConfiguration;
@@ -82,12 +91,28 @@
this.env = env;
this.label = label;
this.configState = configState;
- this.buildFlags = buildFlags;
- this.consoleProvider = new AndroidTestConsoleProvider(project, runConfiguration, configState);
this.buildStep = new BlazeApkBuildStepNormalBuild(project, label, buildFlags);
this.applicationIdProvider =
new BlazeAndroidTestApplicationIdProvider(project, buildStep.getDeployInfo());
this.apkProvider = new BlazeApkProvider(project, buildStep.getDeployInfo());
+
+ BlazeAndroidTestEventsHandler testEventsHandler = null;
+ if (smRunnerUiEnabled.getValue() && !isDebugging(env.getExecutor())) {
+ testEventsHandler = new BlazeAndroidTestEventsHandler();
+ this.buildFlags =
+ ImmutableList.<String>builder()
+ .addAll(testEventsHandler.getBlazeFlags())
+ .addAll(buildFlags)
+ .build();
+ } else {
+ this.buildFlags = buildFlags;
+ }
+ this.consoleProvider =
+ new AndroidTestConsoleProvider(project, runConfiguration, configState, testEventsHandler);
+ }
+
+ private static boolean isDebugging(Executor executor) {
+ return executor instanceof DefaultDebugExecutor;
}
@Override
@@ -192,18 +217,26 @@
}
@Override
+ @SuppressWarnings("unchecked")
public DebugConnectorTask getDebuggerTask(
AndroidDebugger androidDebugger,
AndroidDebuggerState androidDebuggerState,
- @NotNull Set<String> packageIds)
+ @NotNull Set<String> packageIds,
+ boolean monitorRemoteProcess)
throws ExecutionException {
if (configState.isRunThroughBlaze()) {
return new ConnectBlazeTestDebuggerTask(
env.getProject(), androidDebugger, packageIds, applicationIdProvider, this);
}
- //noinspection unchecked
- return androidDebugger.getConnectDebuggerTask(
- env, null, packageIds, facet, androidDebuggerState, runConfiguration.getType().getId());
+ return Compatibility.getConnectDebuggerTask(
+ androidDebugger,
+ env,
+ null,
+ packageIds,
+ facet,
+ androidDebuggerState,
+ runConfiguration.getType().getId(),
+ monitorRemoteProcess);
}
void onLaunchTaskComplete() {
diff --git a/aswb/src/com/google/idea/blaze/android/run/test/ConnectBlazeTestDebuggerTask.java b/aswb/src/com/google/idea/blaze/android/run/test/ConnectBlazeTestDebuggerTask.java
index 7153880..8e2328e 100644
--- a/aswb/src/com/google/idea/blaze/android/run/test/ConnectBlazeTestDebuggerTask.java
+++ b/aswb/src/com/google/idea/blaze/android/run/test/ConnectBlazeTestDebuggerTask.java
@@ -28,9 +28,9 @@
import com.android.tools.idea.run.LaunchInfo;
import com.android.tools.idea.run.ProcessHandlerConsolePrinter;
import com.android.tools.idea.run.editor.AndroidDebugger;
-import com.android.tools.idea.run.tasks.ConnectDebuggerTask;
import com.android.tools.idea.run.tasks.ConnectJavaDebuggerTask;
import com.android.tools.idea.run.util.ProcessHandlerLaunchStatus;
+import com.google.idea.blaze.android.compatibility.Compatibility.ConnectDebuggerTask;
import com.intellij.debugger.engine.RemoteDebugProcessHandler;
import com.intellij.debugger.ui.DebuggerPanelsManager;
import com.intellij.execution.ExecutionException;
@@ -62,7 +62,7 @@
Set<String> applicationIds,
ApplicationIdProvider applicationIdProvider,
BlazeAndroidTestRunContext runContext) {
- super(applicationIds, debugger, project);
+ super(applicationIds, debugger, project, true);
this.project = project;
this.applicationIdProvider = applicationIdProvider;
this.runContext = runContext;
diff --git a/aswb/src/com/google/idea/blaze/android/run/test/StockAndroidTestLaunchTask.java b/aswb/src/com/google/idea/blaze/android/run/test/StockAndroidTestLaunchTask.java
index cf3664b..2430f3c 100644
--- a/aswb/src/com/google/idea/blaze/android/run/test/StockAndroidTestLaunchTask.java
+++ b/aswb/src/com/google/idea/blaze/android/run/test/StockAndroidTestLaunchTask.java
@@ -21,9 +21,9 @@
import com.android.tools.idea.run.ApplicationIdProvider;
import com.android.tools.idea.run.ConsolePrinter;
import com.android.tools.idea.run.tasks.LaunchTask;
-import com.android.tools.idea.run.testing.AndroidTestListener;
import com.android.tools.idea.run.util.LaunchStatus;
import com.google.common.collect.ImmutableList;
+import com.google.idea.blaze.android.compatibility.Compatibility.AndroidTestListener;
import com.google.idea.blaze.android.run.deployinfo.BlazeAndroidDeployInfo;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
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
new file mode 100644
index 0000000..f37359a
--- /dev/null
+++ b/aswb/src/com/google/idea/blaze/android/run/test/smrunner/BlazeAndroidTestEventsHandler.java
@@ -0,0 +1,110 @@
+/*
+ * 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.android.run.test.smrunner;
+
+import com.google.idea.blaze.base.command.BlazeFlags;
+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.List;
+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
+ public SMTestLocator getTestLocator() {
+ return BlazeAndroidTestLocator.INSTANCE;
+ }
+
+ @Override
+ public String suiteLocationUrl(String name) {
+ return SmRunnerUtils.GENERIC_SUITE_PROTOCOL + URLUtil.SCHEME_SEPARATOR + name;
+ }
+
+ @Override
+ public String testLocationUrl(String name, @Nullable String className) {
+ // ignore initial value of className -- it's the test runner class.
+ name = StringUtil.trimTrailing(name, '-');
+ if (!name.contains("-")) {
+ return SmRunnerUtils.GENERIC_SUITE_PROTOCOL + URLUtil.SCHEME_SEPARATOR + name;
+ }
+ int ix = name.lastIndexOf('-');
+ className = name.substring(0, ix);
+ String methodName = name.substring(ix + 1);
+ return SmRunnerUtils.GENERIC_SUITE_PROTOCOL
+ + URLUtil.SCHEME_SEPARATOR
+ + className
+ + SmRunnerUtils.TEST_NAME_PARTS_SPLITTER
+ + methodName;
+ }
+
+ @Override
+ public String testDisplayName(String rawName) {
+ String name = StringUtil.trimTrailing(rawName, '-');
+ if (name.contains("-")) {
+ int ix = name.lastIndexOf('-');
+ return name.substring(ix + 1);
+ }
+ return name;
+ }
+
+ @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));
+ }
+ // the android test runner always runs with JUnit4
+ String filter =
+ BlazeJUnitTestFilterFlags.testFilterForClassesAndMethods(
+ failedMethodsPerClass, JUnitVersion.JUNIT_4);
+ return filter != null ? BlazeFlags.TEST_FILTER + "=" + filter : null;
+ }
+
+ private void appendTest(
+ MultiMap<PsiClass, PsiMethod> testMap, @Nullable Location<?> testLocation) {
+ if (testLocation == null) {
+ return;
+ }
+ PsiElement method = testLocation.getPsiElement();
+ if (!(method instanceof PsiMethod)) {
+ return;
+ }
+ PsiClass psiClass = ((PsiMethod) method).getContainingClass();
+ if (psiClass != null) {
+ testMap.putValue(psiClass, (PsiMethod) method);
+ }
+ }
+}
diff --git a/aswb/src/com/google/idea/blaze/android/run/test/smrunner/BlazeAndroidTestLocator.java b/aswb/src/com/google/idea/blaze/android/run/test/smrunner/BlazeAndroidTestLocator.java
new file mode 100644
index 0000000..c03f759
--- /dev/null
+++ b/aswb/src/com/google/idea/blaze/android/run/test/smrunner/BlazeAndroidTestLocator.java
@@ -0,0 +1,96 @@
+/*
+ * 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.android.run.test.smrunner;
+
+import com.google.common.collect.ImmutableList;
+import com.google.idea.blaze.base.run.smrunner.SmRunnerUtils;
+import com.intellij.execution.Location;
+import com.intellij.execution.PsiLocation;
+import com.intellij.execution.junit.JUnitUtil;
+import com.intellij.execution.testframework.sm.runner.SMTestLocator;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.JavaPsiFacade;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.psi.search.PsiShortNamesCache;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/** Locate android test classes / methods for test UI navigation. */
+public class BlazeAndroidTestLocator implements SMTestLocator {
+
+ public static final BlazeAndroidTestLocator INSTANCE = new BlazeAndroidTestLocator();
+
+ private BlazeAndroidTestLocator() {}
+
+ @Override
+ public List<Location> getLocation(
+ String protocol, String path, Project project, GlobalSearchScope scope) {
+ if (!protocol.equals(SmRunnerUtils.GENERIC_SUITE_PROTOCOL)) {
+ return ImmutableList.of();
+ }
+ String[] split = path.split(SmRunnerUtils.TEST_NAME_PARTS_SPLITTER);
+ List<PsiClass> classes = findClasses(project, scope, split[0]);
+ if (classes.isEmpty()) {
+ return ImmutableList.of();
+ }
+ if (split.length <= 1) {
+ return classes.stream().map(PsiLocation::new).collect(Collectors.toList());
+ }
+
+ String methodName = split[1];
+ List<Location> results = new ArrayList<>();
+ for (PsiClass psiClass : classes) {
+ PsiMethod method = findTestMethod(psiClass, methodName);
+ if (method != null) {
+ results.add(new PsiLocation<>(method));
+ }
+ }
+ return results.isEmpty() ? ImmutableList.of(new PsiLocation<>(classes.get(0))) : results;
+ }
+
+ private static List<PsiClass> findClasses(
+ Project project, GlobalSearchScope scope, String className) {
+ PsiClass psiClass = JavaPsiFacade.getInstance(project).findClass(className, scope);
+ if (psiClass != null) {
+ return ImmutableList.of(psiClass);
+ }
+ // handle unqualified class names
+ return Arrays.stream(PsiShortNamesCache.getInstance(project).getClassesByName(className, scope))
+ .filter(JUnitUtil::isTestClass)
+ .collect(Collectors.toList());
+ }
+
+ private static PsiMethod findTestMethod(PsiClass psiClass, String methodName) {
+ final PsiMethod[] methods = psiClass.findMethodsByName(methodName, true);
+
+ if (methods.length == 0) {
+ return null;
+ }
+ if (methods.length == 1) {
+ return methods[0];
+ }
+ for (PsiMethod method : methods) {
+ if (method.getParameterList().getParametersCount() == 0) {
+ return method;
+ }
+ }
+ return methods[0];
+ }
+}
diff --git a/aswb/src/com/google/idea/blaze/android/settings/BlazeAndroidUserSettings.java b/aswb/src/com/google/idea/blaze/android/settings/BlazeAndroidUserSettings.java
new file mode 100644
index 0000000..6d9487b
--- /dev/null
+++ b/aswb/src/com/google/idea/blaze/android/settings/BlazeAndroidUserSettings.java
@@ -0,0 +1,54 @@
+/*
+ * 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.android.settings;
+
+import com.intellij.openapi.components.PersistentStateComponent;
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.components.State;
+import com.intellij.openapi.components.Storage;
+import com.intellij.util.xmlb.XmlSerializerUtil;
+import org.jetbrains.annotations.NotNull;
+
+/** Android-specific user settings. */
+@State(name = "BlazeAndroidUserSettings", storages = @Storage("blaze.android.user.settings.xml"))
+public class BlazeAndroidUserSettings
+ implements PersistentStateComponent<BlazeAndroidUserSettings> {
+
+ private boolean useLayoutEditor = false;
+
+ public static BlazeAndroidUserSettings getInstance() {
+ return ServiceManager.getService(BlazeAndroidUserSettings.class);
+ }
+
+ @Override
+ @NotNull
+ public BlazeAndroidUserSettings getState() {
+ return this;
+ }
+
+ @Override
+ public void loadState(BlazeAndroidUserSettings state) {
+ XmlSerializerUtil.copyBean(state, this);
+ }
+
+ public boolean getUseLayoutEditor() {
+ return useLayoutEditor;
+ }
+
+ public void setUseLayoutEditor(boolean useLayoutEditor) {
+ this.useLayoutEditor = useLayoutEditor;
+ }
+}
diff --git a/aswb/src/com/google/idea/blaze/android/sync/BazelAndroidJdkProvider.java b/aswb/src/com/google/idea/blaze/android/sync/BazelAndroidJdkProvider.java
index f45b368..d603ff3 100644
--- a/aswb/src/com/google/idea/blaze/android/sync/BazelAndroidJdkProvider.java
+++ b/aswb/src/com/google/idea/blaze/android/sync/BazelAndroidJdkProvider.java
@@ -15,9 +15,8 @@
*/
package com.google.idea.blaze.android.sync;
-import com.google.idea.blaze.base.settings.Blaze;
+import com.google.idea.blaze.base.model.BlazeVersionData;
import com.google.idea.blaze.base.settings.Blaze.BuildSystem;
-import com.intellij.openapi.project.Project;
import com.intellij.pom.java.LanguageLevel;
import javax.annotation.Nullable;
@@ -26,8 +25,11 @@
@Nullable
@Override
- public LanguageLevel getLanguageLevel(Project project) {
- BuildSystem buildSystem = Blaze.getBuildSystem(project);
- return buildSystem == BuildSystem.Bazel ? LanguageLevel.JDK_1_7 : null;
+ public LanguageLevel getLanguageLevel(
+ BuildSystem buildSystem, BlazeVersionData blazeVersionData) {
+ if (buildSystem != BuildSystem.Bazel) {
+ return null;
+ }
+ return LanguageLevel.JDK_1_7;
}
}
diff --git a/aswb/src/com/google/idea/blaze/android/sync/BlazeAndroidSyncListener.java b/aswb/src/com/google/idea/blaze/android/sync/BlazeAndroidSyncListener.java
index 815c4c9..253b4db 100644
--- a/aswb/src/com/google/idea/blaze/android/sync/BlazeAndroidSyncListener.java
+++ b/aswb/src/com/google/idea/blaze/android/sync/BlazeAndroidSyncListener.java
@@ -27,7 +27,7 @@
@Override
public void afterSync(
Project project, BlazeContext context, SyncMode syncMode, SyncResult syncResult) {
- if (syncResult == SyncResult.SUCCESS || syncResult == SyncResult.PARTIAL_SUCCESS) {
+ if (syncResult.successful()) {
DumbService dumbService = DumbService.getInstance(project);
dumbService.queueTask(new ResourceFolderRegistry.PopulateCachesTask(project));
}
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 72b352d..74d41e7 100644
--- a/aswb/src/com/google/idea/blaze/android/sync/BlazeAndroidSyncPlugin.java
+++ b/aswb/src/com/google/idea/blaze/android/sync/BlazeAndroidSyncPlugin.java
@@ -15,9 +15,10 @@
*/
package com.google.idea.blaze.android.sync;
-import com.android.tools.idea.sdk.IdeSdks;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
+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.AndroidSdkPlatformSection;
import com.google.idea.blaze.android.projectview.GeneratedAndroidResourcesSection;
@@ -29,6 +30,7 @@
import com.google.idea.blaze.android.sync.sdk.AndroidSdkFromProjectView;
import com.google.idea.blaze.base.ideinfo.TargetMap;
import com.google.idea.blaze.base.model.BlazeProjectData;
+import com.google.idea.blaze.base.model.BlazeVersionData;
import com.google.idea.blaze.base.model.SyncState;
import com.google.idea.blaze.base.model.primitives.LanguageClass;
import com.google.idea.blaze.base.model.primitives.WorkspaceRoot;
@@ -40,6 +42,7 @@
import com.google.idea.blaze.base.scope.output.IssueOutput;
import com.google.idea.blaze.base.scope.output.StatusOutput;
import com.google.idea.blaze.base.scope.scopes.TimingScope;
+import com.google.idea.blaze.base.settings.Blaze;
import com.google.idea.blaze.base.sync.BlazeSyncPlugin;
import com.google.idea.blaze.base.sync.SourceFolderProvider;
import com.google.idea.blaze.base.sync.libraries.LibrarySource;
@@ -69,7 +72,6 @@
import java.util.Set;
import javax.annotation.Nullable;
import org.jetbrains.android.facet.AndroidFacet;
-import org.jetbrains.android.sdk.AndroidSdkUtils;
/** ASwB sync plugin. */
public class BlazeAndroidSyncPlugin extends BlazeSyncPlugin.Adapter {
@@ -160,6 +162,7 @@
Project project,
BlazeContext context,
ProjectViewSet projectViewSet,
+ BlazeVersionData blazeVersionData,
BlazeProjectData blazeProjectData) {
if (!isAndroidWorkspace(blazeProjectData.workspaceLanguageSettings)) {
return;
@@ -180,7 +183,9 @@
return;
}
- LanguageLevel defaultLanguageLevel = BuildSystemAndroidJdkProvider.languageLevel(project);
+ LanguageLevel defaultLanguageLevel =
+ BuildSystemAndroidJdkProvider.languageLevel(
+ Blaze.getBuildSystem(project), blazeVersionData);
LanguageLevel javaLanguageLevel =
JavaLanguageLevelSection.getLanguageLevel(projectViewSet, defaultLanguageLevel);
setProjectSdkAndLanguageLevel(project, sdk, javaLanguageLevel);
@@ -200,7 +205,6 @@
BlazeAndroidProjectStructureSyncer.updateProjectStructure(
project,
context,
- workspaceRoot,
projectViewSet,
blazeProjectData,
moduleEditor,
@@ -209,6 +213,23 @@
isAndroidWorkspace(blazeProjectData.workspaceLanguageSettings));
}
+ @Override
+ public void updateInMemoryState(
+ Project project,
+ BlazeContext context,
+ WorkspaceRoot workspaceRoot,
+ ProjectViewSet projectViewSet,
+ BlazeProjectData blazeProjectData,
+ Module workspaceModule) {
+ BlazeAndroidProjectStructureSyncer.updateInMemoryState(
+ project,
+ workspaceRoot,
+ projectViewSet,
+ blazeProjectData,
+ workspaceModule,
+ isAndroidWorkspace(blazeProjectData.workspaceLanguageSettings));
+ }
+
@Nullable
@Override
public SourceFolderProvider getSourceFolderProvider(BlazeProjectData projectData) {
diff --git a/aswb/src/com/google/idea/blaze/android/sync/BuildSystemAndroidJdkProvider.java b/aswb/src/com/google/idea/blaze/android/sync/BuildSystemAndroidJdkProvider.java
index d9b53bf..94c4c9a 100644
--- a/aswb/src/com/google/idea/blaze/android/sync/BuildSystemAndroidJdkProvider.java
+++ b/aswb/src/com/google/idea/blaze/android/sync/BuildSystemAndroidJdkProvider.java
@@ -15,8 +15,9 @@
*/
package com.google.idea.blaze.android.sync;
+import com.google.idea.blaze.base.model.BlazeVersionData;
+import com.google.idea.blaze.base.settings.Blaze.BuildSystem;
import com.intellij.openapi.extensions.ExtensionPointName;
-import com.intellij.openapi.project.Project;
import com.intellij.pom.java.LanguageLevel;
import javax.annotation.Nullable;
@@ -28,9 +29,9 @@
LanguageLevel DEFAULT_LANGUAGE_LEVEL = LanguageLevel.JDK_1_7;
- static LanguageLevel languageLevel(Project project) {
+ static LanguageLevel languageLevel(BuildSystem buildSystem, BlazeVersionData blazeVersionData) {
for (BuildSystemAndroidJdkProvider provider : EP_NAME.getExtensions()) {
- LanguageLevel level = provider.getLanguageLevel(project);
+ LanguageLevel level = provider.getLanguageLevel(buildSystem, blazeVersionData);
if (level != null) {
return level;
}
@@ -43,5 +44,5 @@
* unable to determine it.
*/
@Nullable
- LanguageLevel getLanguageLevel(Project project);
+ LanguageLevel getLanguageLevel(BuildSystem buildSystem, BlazeVersionData blazeVersionData);
}
diff --git a/aswb/src/com/google/idea/blaze/android/sync/model/AndroidResourceModuleRegistry.java b/aswb/src/com/google/idea/blaze/android/sync/model/AndroidResourceModuleRegistry.java
new file mode 100644
index 0000000..a1f6bf8
--- /dev/null
+++ b/aswb/src/com/google/idea/blaze/android/sync/model/AndroidResourceModuleRegistry.java
@@ -0,0 +1,55 @@
+/*
+ * 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.android.sync.model;
+
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+import com.google.idea.blaze.base.ideinfo.TargetKey;
+import com.google.idea.blaze.base.model.primitives.Label;
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+
+/** Keeps track of which resource modules correspond to which resource target. */
+public class AndroidResourceModuleRegistry {
+ private final BiMap<Module, AndroidResourceModule> moduleMap = HashBiMap.create();
+
+ public static AndroidResourceModuleRegistry getInstance(Project project) {
+ return ServiceManager.getService(project, AndroidResourceModuleRegistry.class);
+ }
+
+ public Label getLabel(Module module) {
+ TargetKey targetKey = getTargetKey(module);
+ return targetKey == null ? null : targetKey.label;
+ }
+
+ public TargetKey getTargetKey(Module module) {
+ AndroidResourceModule resourceModule = get(module);
+ return resourceModule == null ? null : resourceModule.targetKey;
+ }
+
+ public AndroidResourceModule get(Module module) {
+ return moduleMap.get(module);
+ }
+
+ public void put(Module module, AndroidResourceModule resourceModule) {
+ moduleMap.put(module, resourceModule);
+ }
+
+ public void clear() {
+ moduleMap.clear();
+ }
+}
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 7a13e1e..8e19be7 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
@@ -22,16 +22,16 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
import com.google.idea.blaze.android.manifest.ManifestParser;
-import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Computable;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import java.io.File;
import java.util.List;
import java.util.Set;
import org.jetbrains.android.dom.manifest.Manifest;
-import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
@@ -39,10 +39,7 @@
* user-selected build variant.
*/
public class BlazeAndroidModel implements AndroidModel {
- private static final Logger LOG = Logger.getInstance(BlazeAndroidModel.class);
-
private Project project;
- private Module module;
private final File rootDirPath;
private final SourceProvider sourceProvider;
private final List<SourceProvider> sourceProviders; // Singleton list of sourceProvider
@@ -60,7 +57,6 @@
String resourceJavaPackage,
int androidSdkApiLevel) {
this.project = project;
- this.module = module;
this.rootDirPath = rootDirPath;
this.sourceProvider = sourceProvider;
this.sourceProviders = ImmutableList.of(sourceProvider);
@@ -69,45 +65,43 @@
this.androidSdkApiLevel = androidSdkApiLevel;
}
- @NotNull
@Override
public SourceProvider getDefaultSourceProvider() {
return sourceProvider;
}
- @NotNull
@Override
public List<SourceProvider> getActiveSourceProviders() {
return sourceProviders;
}
- @NotNull
@Override
public List<SourceProvider> getTestSourceProviders() {
return sourceProviders;
}
- @NotNull
@Override
public List<SourceProvider> getAllSourceProviders() {
return sourceProviders;
}
@Override
- @NotNull
public String getApplicationId() {
- String result = null;
- Manifest manifest = ManifestParser.getInstance(project).getManifest(moduleManifest);
- if (manifest != null) {
- result = manifest.getPackage().getValue();
- }
- if (result == null) {
- result = resourceJavaPackage;
- }
- return result;
+ // Run in a read action since otherwise, it might throw a read access exception.
+ return ApplicationManager.getApplication()
+ .runReadAction(
+ (Computable<String>)
+ () -> {
+ Manifest manifest =
+ ManifestParser.getInstance(project).getManifest(moduleManifest);
+ if (manifest == null) {
+ return resourceJavaPackage;
+ }
+ String packageName = manifest.getPackage().getValue();
+ return packageName == null ? resourceJavaPackage : packageName;
+ });
}
- @NotNull
@Override
public Set<String> getAllApplicationIds() {
Set<String> applicationIds = Sets.newHashSet();
@@ -149,18 +143,16 @@
return null;
}
- @NotNull
@Override
public File getRootDirPath() {
return rootDirPath;
}
@Override
- public boolean isGenerated(@NotNull VirtualFile file) {
+ public boolean isGenerated(VirtualFile file) {
return false;
}
- @NotNull
@Override
public VirtualFile getRootDir() {
File rootDirPath = getRootDirPath();
@@ -175,14 +167,13 @@
}
@Override
- @NotNull
public ClassJarProvider getClassJarProvider() {
- return new NullClassJarProvider();
+ return new BlazeClassJarProvider(project);
}
@Override
@Nullable
- public Long getLastBuildTimestamp(@NotNull Project project) {
+ public Long getLastBuildTimestamp(Project project) {
// TODO(jvoung): Coordinate with blaze build actions to be able determine last build time.
return null;
}
diff --git a/aswb/src/com/google/idea/blaze/android/sync/model/idea/BlazeClassJarProvider.java b/aswb/src/com/google/idea/blaze/android/sync/model/idea/BlazeClassJarProvider.java
deleted file mode 100644
index dbd24d6..0000000
--- a/aswb/src/com/google/idea/blaze/android/sync/model/idea/BlazeClassJarProvider.java
+++ /dev/null
@@ -1,112 +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.android.sync.model.idea;
-
-import com.android.SdkConstants;
-import com.android.tools.idea.model.ClassJarProvider;
-import com.google.idea.blaze.base.ideinfo.ArtifactLocation;
-import com.google.idea.blaze.base.ideinfo.LibraryArtifact;
-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;
-import com.google.idea.blaze.java.sync.model.BlazeJarLibrary;
-import com.google.idea.blaze.java.sync.model.BlazeJavaSyncData;
-import com.intellij.openapi.module.Module;
-import com.intellij.openapi.project.Project;
-import com.intellij.openapi.vfs.JarFileSystem;
-import com.intellij.openapi.vfs.LocalFileSystem;
-import com.intellij.openapi.vfs.VirtualFile;
-import com.intellij.util.containers.OrderedSet;
-import java.io.File;
-import java.util.List;
-import org.jetbrains.annotations.Nullable;
-
-/** Collects class jars from the user's build. */
-public class BlazeClassJarProvider extends ClassJarProvider {
-
- private final Project project;
-
- public BlazeClassJarProvider(final Project project) {
- this.project = project;
- }
-
- @Override
- @Nullable
- public VirtualFile findModuleClassFile(String className, Module module) {
- BlazeProjectData blazeProjectData =
- BlazeProjectDataManager.getInstance(project).getBlazeProjectData();
- if (blazeProjectData == null) {
- return null;
- }
- LocalFileSystem localVfs = LocalFileSystem.getInstance();
- String classNamePath = className.replace('.', File.separatorChar) + SdkConstants.DOT_CLASS;
- BlazeJavaSyncData syncData = blazeProjectData.syncState.get(BlazeJavaSyncData.class);
- if (syncData == null) {
- return null;
- }
- ArtifactLocationDecoder artifactLocationDecoder = blazeProjectData.artifactLocationDecoder;
- for (File classJar : artifactLocationDecoder.decodeAll(syncData.importResult.buildOutputJars)) {
- VirtualFile classJarVF = localVfs.findFileByIoFile(classJar);
- if (classJarVF == null) {
- continue;
- }
- VirtualFile classFile = findClassInJar(classJarVF, classNamePath);
- if (classFile != null) {
- return classFile;
- }
- }
- return null;
- }
-
- @Nullable
- private static VirtualFile findClassInJar(final VirtualFile classJar, String classNamePath) {
- VirtualFile jarRoot = JarFileSystem.getInstance().getJarRootForLocalFile(classJar);
- if (jarRoot == null) {
- return null;
- }
- return jarRoot.findFileByRelativePath(classNamePath);
- }
-
- @Override
- public List<VirtualFile> getModuleExternalLibraries(Module module) {
- OrderedSet<VirtualFile> results = new OrderedSet<VirtualFile>();
- BlazeProjectData blazeProjectData =
- BlazeProjectDataManager.getInstance(project).getBlazeProjectData();
- if (blazeProjectData == null) {
- return results;
- }
- BlazeJavaSyncData syncData = blazeProjectData.syncState.get(BlazeJavaSyncData.class);
- if (syncData == null) {
- return null;
- }
- ArtifactLocationDecoder artifactLocationDecoder = blazeProjectData.artifactLocationDecoder;
- LocalFileSystem localVfs = LocalFileSystem.getInstance();
- for (BlazeJarLibrary blazeLibrary : syncData.importResult.libraries.values()) {
- LibraryArtifact libraryArtifact = blazeLibrary.libraryArtifact;
- ArtifactLocation classJar = libraryArtifact.classJar;
- if (classJar == null) {
- continue;
- }
- VirtualFile libVF = localVfs.findFileByIoFile(artifactLocationDecoder.decode(classJar));
- if (libVF == null) {
- continue;
- }
- results.add(libVF);
- }
-
- return results;
- }
-}
diff --git a/aswb/src/com/google/idea/blaze/android/sync/projectstructure/AndroidFacetModuleCustomizer.java b/aswb/src/com/google/idea/blaze/android/sync/projectstructure/AndroidFacetModuleCustomizer.java
index db6443a..541be52 100755
--- a/aswb/src/com/google/idea/blaze/android/sync/projectstructure/AndroidFacetModuleCustomizer.java
+++ b/aswb/src/com/google/idea/blaze/android/sync/projectstructure/AndroidFacetModuleCustomizer.java
@@ -16,6 +16,7 @@
package com.google.idea.blaze.android.sync.projectstructure;
import com.android.builder.model.AndroidProject;
+import com.google.idea.blaze.android.compatibility.Compatibility;
import com.intellij.facet.FacetManager;
import com.intellij.facet.ModifiableFacetModel;
import com.intellij.openapi.module.Module;
@@ -55,7 +56,7 @@
private static void configureFacet(AndroidFacet facet) {
JpsAndroidModuleProperties facetState = facet.getProperties();
facetState.ALLOW_USER_CONFIGURATION = false;
- facetState.LIBRARY_PROJECT = true;
+ Compatibility.setFacetStateIsLibraryProject(facetState);
facetState.MANIFEST_FILE_RELATIVE_PATH = "";
facetState.RES_FOLDER_RELATIVE_PATH = "";
facetState.ASSETS_FOLDER_RELATIVE_PATH = "";
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 20d5462..cd5ff8f 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
@@ -15,14 +15,18 @@
*/
package com.google.idea.blaze.android.sync.projectstructure;
+import static java.util.stream.Collectors.toSet;
+
import com.android.builder.model.SourceProvider;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.idea.blaze.android.projectview.GeneratedAndroidResourcesSection;
import com.google.idea.blaze.android.resources.LightResourceClassService;
import com.google.idea.blaze.android.run.BlazeAndroidRunConfigurationHandler;
import com.google.idea.blaze.android.sync.model.AndroidResourceModule;
+import com.google.idea.blaze.android.sync.model.AndroidResourceModuleRegistry;
import com.google.idea.blaze.android.sync.model.AndroidSdkPlatform;
import com.google.idea.blaze.android.sync.model.BlazeAndroidSyncData;
import com.google.idea.blaze.android.sync.model.idea.BlazeAndroidModel;
@@ -54,6 +58,7 @@
import com.intellij.openapi.roots.ModifiableRootModel;
import java.io.File;
import java.util.Collection;
+import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nullable;
@@ -65,149 +70,141 @@
public static void updateProjectStructure(
Project project,
BlazeContext context,
- WorkspaceRoot workspaceRoot,
ProjectViewSet projectViewSet,
BlazeProjectData blazeProjectData,
BlazeSyncPlugin.ModuleEditor moduleEditor,
Module workspaceModule,
ModifiableRootModel workspaceModifiableModel,
boolean isAndroidWorkspace) {
- LightResourceClassService.Builder rClassBuilder =
- new LightResourceClassService.Builder(project);
-
- if (isAndroidWorkspace) {
- BlazeAndroidSyncData syncData = blazeProjectData.syncState.get(BlazeAndroidSyncData.class);
- if (syncData == null) {
- return;
- }
-
- AndroidSdkPlatform androidSdkPlatform = syncData.androidSdkPlatform;
- if (androidSdkPlatform != null) {
- int totalOrderEntries = 0;
-
- // Create the workspace module
- updateWorkspaceModule(project, workspaceRoot, workspaceModule, androidSdkPlatform);
-
- // Create android resource modules
- // Because we're setting up dependencies, the modules have to exist before we configure them
- Map<TargetKey, AndroidResourceModule> targetToAndroidResourceModule = Maps.newHashMap();
- for (AndroidResourceModule androidResourceModule :
- syncData.importResult.androidResourceModules) {
- targetToAndroidResourceModule.put(androidResourceModule.targetKey, androidResourceModule);
- String moduleName = moduleNameForAndroidModule(androidResourceModule.targetKey);
- moduleEditor.createModule(moduleName, StdModuleTypes.JAVA);
- }
-
- // Configure android resource modules
- for (AndroidResourceModule androidResourceModule : targetToAndroidResourceModule.values()) {
- TargetIdeInfo target = blazeProjectData.targetMap.get(androidResourceModule.targetKey);
- AndroidIdeInfo androidIdeInfo = target.androidIdeInfo;
- assert androidIdeInfo != null;
-
- String moduleName = moduleNameForAndroidModule(target.key);
- Module module = moduleEditor.findModule(moduleName);
- assert module != null;
- ModifiableRootModel modifiableRootModel = moduleEditor.editModule(module);
-
- updateAndroidTargetModule(
- project,
- workspaceRoot,
- blazeProjectData.artifactLocationDecoder,
- androidSdkPlatform,
- target,
- module,
- modifiableRootModel,
- androidResourceModule);
-
- for (TargetKey resourceDependency :
- androidResourceModule.transitiveResourceDependencies) {
- if (!targetToAndroidResourceModule.containsKey(resourceDependency)) {
- continue;
- }
- String dependencyModuleName = moduleNameForAndroidModule(resourceDependency);
- Module dependency = moduleEditor.findModule(dependencyModuleName);
- if (dependency == null) {
- continue;
- }
- modifiableRootModel.addModuleOrderEntry(dependency);
- ++totalOrderEntries;
- }
- rClassBuilder.addRClass(androidIdeInfo.resourceJavaPackage, module);
- // Add a dependency from the workspace to the resource module
- workspaceModifiableModel.addModuleOrderEntry(module);
- }
-
- // Collect potential android run configuration targets
- Set<Label> runConfigurationModuleTargets = Sets.newHashSet();
-
- // Get all explicitly mentioned targets
- // Doing this now will cut down on root changes later
- for (TargetExpression targetExpression : projectViewSet.listItems(TargetSection.KEY)) {
- if (!(targetExpression instanceof Label)) {
- continue;
- }
- Label label = (Label) targetExpression;
- runConfigurationModuleTargets.add(label);
- }
- // Get any pre-existing targets
- for (RunConfiguration runConfiguration :
- RunManager.getInstance(project).getAllConfigurationsList()) {
- BlazeAndroidRunConfigurationHandler handler =
- BlazeAndroidRunConfigurationHandler.getHandlerFrom(runConfiguration);
- if (handler == null) {
- continue;
- }
- runConfigurationModuleTargets.add(handler.getLabel());
- }
-
- int totalRunConfigurationModules = 0;
- for (Label label : runConfigurationModuleTargets) {
- TargetKey targetKey = TargetKey.forPlainTarget(label);
- // If it's a resource module, it will already have been created
- if (targetToAndroidResourceModule.containsKey(targetKey)) {
- continue;
- }
- // Ensure the label is a supported android rule that exists
- TargetIdeInfo target = blazeProjectData.targetMap.get(targetKey);
- if (target == null) {
- continue;
- }
- if (!target.kindIsOneOf(Kind.ANDROID_BINARY, Kind.ANDROID_TEST)) {
- continue;
- }
-
- String moduleName = moduleNameForAndroidModule(targetKey);
- Module module = moduleEditor.createModule(moduleName, StdModuleTypes.JAVA);
- ModifiableRootModel modifiableRootModel = moduleEditor.editModule(module);
- updateAndroidTargetModule(
- project,
- workspaceRoot,
- blazeProjectData.artifactLocationDecoder,
- androidSdkPlatform,
- target,
- module,
- modifiableRootModel,
- null);
- ++totalRunConfigurationModules;
- }
-
- int whitelistedGenResources =
- projectViewSet.listItems(GeneratedAndroidResourcesSection.KEY).size();
- context.output(
- PrintOutput.log(
- String.format(
- "Android resource module count: %d, run config modules: %d, order entries: %d, "
- + "generated resources: %d",
- syncData.importResult.androidResourceModules.size(),
- totalRunConfigurationModules,
- totalOrderEntries,
- whitelistedGenResources)));
- }
- } else {
+ if (!isAndroidWorkspace) {
AndroidFacetModuleCustomizer.removeAndroidFacet(workspaceModule);
+ return;
}
- LightResourceClassService.getInstance(project).installRClasses(rClassBuilder);
+ BlazeAndroidSyncData syncData = blazeProjectData.syncState.get(BlazeAndroidSyncData.class);
+ if (syncData == null) {
+ return;
+ }
+ AndroidSdkPlatform androidSdkPlatform = syncData.androidSdkPlatform;
+ if (androidSdkPlatform == null) {
+ return;
+ }
+
+ // Configure workspace module as an android module
+ AndroidFacetModuleCustomizer.createAndroidFacet(workspaceModule);
+
+ // Create android resource modules
+ // Because we're setting up dependencies, the modules have to exist before we configure them
+ Map<TargetKey, AndroidResourceModule> targetToAndroidResourceModule = Maps.newHashMap();
+ for (AndroidResourceModule androidResourceModule :
+ syncData.importResult.androidResourceModules) {
+ targetToAndroidResourceModule.put(androidResourceModule.targetKey, androidResourceModule);
+ String moduleName = moduleNameForAndroidModule(androidResourceModule.targetKey);
+ Module module = moduleEditor.createModule(moduleName, StdModuleTypes.JAVA);
+ AndroidFacetModuleCustomizer.createAndroidFacet(module);
+ }
+
+ // Configure android resource modules
+ int totalOrderEntries = 0;
+ for (AndroidResourceModule androidResourceModule : targetToAndroidResourceModule.values()) {
+ TargetIdeInfo target = blazeProjectData.targetMap.get(androidResourceModule.targetKey);
+ AndroidIdeInfo androidIdeInfo = target.androidIdeInfo;
+ assert androidIdeInfo != null;
+
+ String moduleName = moduleNameForAndroidModule(target.key);
+ Module module = moduleEditor.findModule(moduleName);
+ assert module != null;
+ ModifiableRootModel modifiableRootModel = moduleEditor.editModule(module);
+
+ Collection<File> resources =
+ blazeProjectData.artifactLocationDecoder.decodeAll(androidResourceModule.resources);
+ ResourceModuleContentRootCustomizer.setupContentRoots(modifiableRootModel, resources);
+
+ for (TargetKey resourceDependency : androidResourceModule.transitiveResourceDependencies) {
+ if (!targetToAndroidResourceModule.containsKey(resourceDependency)) {
+ continue;
+ }
+ String dependencyModuleName = moduleNameForAndroidModule(resourceDependency);
+ Module dependency = moduleEditor.findModule(dependencyModuleName);
+ if (dependency == null) {
+ continue;
+ }
+ modifiableRootModel.addModuleOrderEntry(dependency);
+ ++totalOrderEntries;
+ }
+ // Add a dependency from the workspace to the resource module
+ workspaceModifiableModel.addModuleOrderEntry(module);
+ }
+
+ List<TargetIdeInfo> runConfigurationTargets =
+ getRunConfigurationTargets(
+ project, projectViewSet, blazeProjectData, targetToAndroidResourceModule.keySet());
+ for (TargetIdeInfo target : runConfigurationTargets) {
+ TargetKey targetKey = target.key;
+ String moduleName = moduleNameForAndroidModule(targetKey);
+ Module module = moduleEditor.createModule(moduleName, StdModuleTypes.JAVA);
+ AndroidFacetModuleCustomizer.createAndroidFacet(module);
+ }
+
+ int whitelistedGenResources =
+ projectViewSet.listItems(GeneratedAndroidResourcesSection.KEY).size();
+ context.output(
+ PrintOutput.log(
+ String.format(
+ "Android resource module count: %d, run config modules: %d, order entries: %d, "
+ + "generated resources: %d",
+ syncData.importResult.androidResourceModules.size(),
+ runConfigurationTargets.size(),
+ totalOrderEntries,
+ whitelistedGenResources)));
+ }
+
+ // Collect potential android run configuration targets
+ private static List<TargetIdeInfo> getRunConfigurationTargets(
+ Project project,
+ ProjectViewSet projectViewSet,
+ BlazeProjectData blazeProjectData,
+ Set<TargetKey> androidResourceModules) {
+ List<TargetIdeInfo> result = Lists.newArrayList();
+ Set<Label> runConfigurationModuleTargets = Sets.newHashSet();
+
+ // Get all explicitly mentioned targets
+ // Doing this now will cut down on root changes later
+ for (TargetExpression targetExpression : projectViewSet.listItems(TargetSection.KEY)) {
+ if (!(targetExpression instanceof Label)) {
+ continue;
+ }
+ Label label = (Label) targetExpression;
+ runConfigurationModuleTargets.add(label);
+ }
+ // Get any pre-existing targets
+ for (RunConfiguration runConfiguration :
+ RunManager.getInstance(project).getAllConfigurationsList()) {
+ BlazeAndroidRunConfigurationHandler handler =
+ BlazeAndroidRunConfigurationHandler.getHandlerFrom(runConfiguration);
+ if (handler == null) {
+ continue;
+ }
+ runConfigurationModuleTargets.add(handler.getLabel());
+ }
+
+ for (Label label : runConfigurationModuleTargets) {
+ TargetKey targetKey = TargetKey.forPlainTarget(label);
+ // If it's a resource module, it will already have been created
+ if (androidResourceModules.contains(targetKey)) {
+ continue;
+ }
+ // Ensure the label is a supported android rule that exists
+ TargetIdeInfo target = blazeProjectData.targetMap.get(targetKey);
+ if (target == null) {
+ continue;
+ }
+ if (!target.kindIsOneOf(Kind.ANDROID_BINARY, Kind.ANDROID_TEST)) {
+ continue;
+ }
+ result.add(target);
+ }
+ return result;
}
/** Ensures a suitable module exists for the given android target. */
@@ -220,7 +217,6 @@
return module;
}
- WorkspaceRoot workspaceRoot = WorkspaceRoot.fromProject(project);
BlazeProjectData blazeProjectData =
BlazeProjectDataManager.getInstance(project).getBlazeProjectData();
if (blazeProjectData == null) {
@@ -246,20 +242,10 @@
BlazeSyncPlugin.ModuleEditor moduleEditor =
BlazeProjectDataManager.getInstance(project).editModules();
Module newModule = moduleEditor.createModule(moduleName, StdModuleTypes.JAVA);
- ModifiableRootModel modifiableRootModel = moduleEditor.editModule(newModule);
-
ApplicationManager.getApplication()
.runWriteAction(
() -> {
- updateAndroidTargetModule(
- project,
- workspaceRoot,
- blazeProjectData.artifactLocationDecoder,
- androidSdkPlatform,
- target,
- newModule,
- modifiableRootModel,
- null);
+ AndroidFacetModuleCustomizer.createAndroidFacet(newModule);
moduleEditor.commit();
});
return newModule;
@@ -273,8 +259,122 @@
.replace(':', '.');
}
+ public static void updateInMemoryState(
+ Project project,
+ WorkspaceRoot workspaceRoot,
+ ProjectViewSet projectViewSet,
+ BlazeProjectData blazeProjectData,
+ Module workspaceModule,
+ boolean isAndroidWorkspace) {
+ LightResourceClassService.Builder rClassBuilder =
+ new LightResourceClassService.Builder(project);
+ AndroidResourceModuleRegistry registry = AndroidResourceModuleRegistry.getInstance(project);
+ registry.clear();
+ if (isAndroidWorkspace) {
+ updateInMemoryState(
+ project,
+ workspaceRoot,
+ projectViewSet,
+ blazeProjectData,
+ workspaceModule,
+ registry,
+ rClassBuilder);
+ }
+ LightResourceClassService.getInstance(project).installRClasses(rClassBuilder);
+ }
+
+ private static void updateInMemoryState(
+ Project project,
+ WorkspaceRoot workspaceRoot,
+ ProjectViewSet projectViewSet,
+ BlazeProjectData blazeProjectData,
+ Module workspaceModule,
+ AndroidResourceModuleRegistry registry,
+ LightResourceClassService.Builder rClassBuilder) {
+ BlazeAndroidSyncData syncData = blazeProjectData.syncState.get(BlazeAndroidSyncData.class);
+ if (syncData == null) {
+ return;
+ }
+ AndroidSdkPlatform androidSdkPlatform = syncData.androidSdkPlatform;
+ if (androidSdkPlatform == null) {
+ return;
+ }
+
+ updateWorkspaceModuleFacetInMemoryState(
+ project, workspaceRoot, workspaceModule, androidSdkPlatform);
+
+ ArtifactLocationDecoder artifactLocationDecoder = blazeProjectData.artifactLocationDecoder;
+ ModuleManager moduleManager = ModuleManager.getInstance(project);
+ for (AndroidResourceModule androidResourceModule :
+ syncData.importResult.androidResourceModules) {
+ TargetIdeInfo target = blazeProjectData.targetMap.get(androidResourceModule.targetKey);
+ String moduleName = moduleNameForAndroidModule(target.key);
+ Module module = moduleManager.findModuleByName(moduleName);
+ registry.put(module, androidResourceModule);
+
+ AndroidIdeInfo androidIdeInfo = target.androidIdeInfo;
+ assert androidIdeInfo != null;
+
+ updateModuleFacetInMemoryState(
+ project,
+ androidSdkPlatform,
+ module,
+ moduleDirectoryForAndroidTarget(workspaceRoot, target),
+ manifestFileForAndroidTarget(
+ artifactLocationDecoder,
+ androidIdeInfo,
+ moduleDirectoryForAndroidTarget(workspaceRoot, target)),
+ androidIdeInfo.resourceJavaPackage,
+ artifactLocationDecoder.decodeAll(androidResourceModule.transitiveResources));
+ rClassBuilder.addRClass(androidIdeInfo.resourceJavaPackage, module);
+ }
+
+ Set<TargetKey> androidResourceModules =
+ syncData
+ .importResult
+ .androidResourceModules
+ .stream()
+ .map(androidResourceModule -> androidResourceModule.targetKey)
+ .collect(toSet());
+ List<TargetIdeInfo> runConfigurationTargets =
+ getRunConfigurationTargets(
+ project, projectViewSet, blazeProjectData, androidResourceModules);
+ for (TargetIdeInfo target : runConfigurationTargets) {
+ String moduleName = moduleNameForAndroidModule(target.key);
+ Module module = moduleManager.findModuleByName(moduleName);
+ AndroidIdeInfo androidIdeInfo = target.androidIdeInfo;
+ assert androidIdeInfo != null;
+ updateModuleFacetInMemoryState(
+ project,
+ androidSdkPlatform,
+ module,
+ moduleDirectoryForAndroidTarget(workspaceRoot, target),
+ manifestFileForAndroidTarget(
+ artifactLocationDecoder,
+ androidIdeInfo,
+ moduleDirectoryForAndroidTarget(workspaceRoot, target)),
+ androidIdeInfo.resourceJavaPackage,
+ ImmutableList.of());
+ }
+ }
+
+ private static File moduleDirectoryForAndroidTarget(
+ WorkspaceRoot workspaceRoot, TargetIdeInfo target) {
+ return workspaceRoot.fileForPath(target.key.label.blazePackage());
+ }
+
+ private static File manifestFileForAndroidTarget(
+ ArtifactLocationDecoder artifactLocationDecoder,
+ AndroidIdeInfo androidIdeInfo,
+ File moduleDirectory) {
+ ArtifactLocation manifestArtifactLocation = androidIdeInfo.manifest;
+ return manifestArtifactLocation != null
+ ? artifactLocationDecoder.decode(manifestArtifactLocation)
+ : new File(moduleDirectory, "AndroidManifest.xml");
+ }
+
/** Updates the shared workspace module with android info. */
- private static void updateWorkspaceModule(
+ private static void updateWorkspaceModuleFacetInMemoryState(
Project project,
WorkspaceRoot workspaceRoot,
Module workspaceModule,
@@ -283,8 +383,7 @@
File manifest = new File(workspaceRoot.directory(), "AndroidManifest.xml");
String resourceJavaPackage = ":workspace";
ImmutableList<File> transitiveResources = ImmutableList.of();
-
- createAndroidModel(
+ updateModuleFacetInMemoryState(
project,
androidSdkPlatform,
workspaceModule,
@@ -294,49 +393,7 @@
transitiveResources);
}
- /** Updates a module from an android rule. */
- private static void updateAndroidTargetModule(
- Project project,
- WorkspaceRoot workspaceRoot,
- ArtifactLocationDecoder artifactLocationDecoder,
- AndroidSdkPlatform androidSdkPlatform,
- TargetIdeInfo target,
- Module module,
- ModifiableRootModel modifiableRootModel,
- @Nullable AndroidResourceModule androidResourceModule) {
-
- Collection<File> resources =
- androidResourceModule != null
- ? artifactLocationDecoder.decodeAll(androidResourceModule.resources)
- : ImmutableList.of();
- Collection<File> transitiveResources =
- androidResourceModule != null
- ? artifactLocationDecoder.decodeAll(androidResourceModule.transitiveResources)
- : ImmutableList.of();
-
- AndroidIdeInfo androidIdeInfo = target.androidIdeInfo;
- assert androidIdeInfo != null;
-
- File moduleDirectory = workspaceRoot.fileForPath(target.key.label.blazePackage());
- ArtifactLocation manifestArtifactLocation = androidIdeInfo.manifest;
- File manifest =
- manifestArtifactLocation != null
- ? artifactLocationDecoder.decode(manifestArtifactLocation)
- : new File(moduleDirectory, "AndroidManifest.xml");
- String resourceJavaPackage = androidIdeInfo.resourceJavaPackage;
- ResourceModuleContentRootCustomizer.setupContentRoots(modifiableRootModel, resources);
-
- createAndroidModel(
- project,
- androidSdkPlatform,
- module,
- moduleDirectory,
- manifest,
- resourceJavaPackage,
- transitiveResources);
- }
-
- private static void createAndroidModel(
+ private static void updateModuleFacetInMemoryState(
Project project,
AndroidSdkPlatform androidSdkPlatform,
Module module,
@@ -344,7 +401,6 @@
File manifest,
String resourceJavaPackage,
Collection<File> transitiveResources) {
- AndroidFacetModuleCustomizer.createAndroidFacet(module);
SourceProvider sourceProvider =
new SourceProviderImpl(module.getName(), manifest, transitiveResources);
BlazeAndroidModel 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 05b52e4..e4ae1b5 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,6 +17,7 @@
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
+import com.google.idea.blaze.android.compatibility.Compatibility.AndroidSdkUtils;
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;
@@ -29,7 +30,6 @@
import javax.annotation.Nullable;
import org.jetbrains.android.sdk.AndroidPlatform;
import org.jetbrains.android.sdk.AndroidSdkAdditionalData;
-import org.jetbrains.android.sdk.AndroidSdkUtils;
/** Calculates AndroidSdkPlatform. */
public class AndroidSdkFromProjectView {
diff --git a/aswb/src/com/google/idea/blaze/android/sync/sdk/SdkUtil.java b/aswb/src/com/google/idea/blaze/android/sync/sdk/SdkUtil.java
index 933679d..fcbe1f9 100644
--- a/aswb/src/com/google/idea/blaze/android/sync/sdk/SdkUtil.java
+++ b/aswb/src/com/google/idea/blaze/android/sync/sdk/SdkUtil.java
@@ -16,6 +16,7 @@
package com.google.idea.blaze.android.sync.sdk;
import com.android.tools.idea.updater.configure.SdkUpdaterConfigurableProvider;
+import com.google.idea.blaze.android.compatibility.Compatibility.AndroidSdkUtils;
import com.google.idea.blaze.android.sync.model.AndroidSdkPlatform;
import com.google.idea.blaze.android.sync.model.BlazeAndroidSyncData;
import com.google.idea.blaze.base.model.BlazeProjectData;
@@ -26,7 +27,6 @@
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.Sdk;
import org.jetbrains.android.sdk.AndroidPlatform;
-import org.jetbrains.android.sdk.AndroidSdkUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
diff --git a/aswb/tests/integrationtests/com/google/idea/blaze/android/run/BlazeAndroidRunConfigurationCommonStateMigrationTest.java b/aswb/tests/integrationtests/com/google/idea/blaze/android/run/BlazeAndroidRunConfigurationCommonStateMigrationTest.java
index 0a222a7..7eca0c6 100644
--- a/aswb/tests/integrationtests/com/google/idea/blaze/android/run/BlazeAndroidRunConfigurationCommonStateMigrationTest.java
+++ b/aswb/tests/integrationtests/com/google/idea/blaze/android/run/BlazeAndroidRunConfigurationCommonStateMigrationTest.java
@@ -90,58 +90,6 @@
}
@Test
- public void readAndWriteShouldMigrate() throws Exception {
- String oldXml =
- "<?xml version=\"1.0\"?>"
- + "<configuration blaze-native-debug=\"true\">"
- + " <blaze-user-flag>--flag1</blaze-user-flag>"
- + " <blaze-user-flag>--flag2</blaze-user-flag>"
- + " <option name=\"USE_LAST_SELECTED_DEVICE\" value=\"true\" />"
- + " <option name=\"PREFERRED_AVD\" value=\"some avd\" />"
- + DEBUGGER_STATE_AUTO_RAW_XML
- + DEBUGGER_STATE_NATIVE_RAW_XML
- + DEBUGGER_STATE_JAVA_RAW_XML
- + DEBUGGER_STATE_HYBRID_RAW_XML
- + DEBUGGER_STATE_BLAZE_AUTO_RAW_XML
- + "</configuration>";
- Element oldElement = saxBuilder.build(new StringReader(oldXml)).getRootElement();
-
- state.readExternal(oldElement);
- Element migratedElement = new Element("configuration");
- state.writeExternal(migratedElement);
-
- assertThat(migratedElement.getChildren()).hasSize(4);
- assertThat(migratedElement.getAttribute("blaze-native-debug").getValue()).isEqualTo("true");
-
- List<Element> flagElements = migratedElement.getChildren("blaze-user-flag");
- assertThat(flagElements).hasSize(2);
- assertThat(flagElements.get(0).getText()).isEqualTo("--flag1");
- assertThat(flagElements.get(1).getText()).isEqualTo("--flag2");
-
- Element deployTargetStatesElement = migratedElement.getChild("android-deploy-target-states");
- assertThat(xmlOutputter.outputString(deployTargetStatesElement))
- .isEqualTo(formatRawXml(DEPLOY_TARGET_STATES_RAW_XML));
-
- Element debuggerStatesElement = migratedElement.getChild("android-debugger-states");
- assertThat(debuggerStatesElement.getChildren()).hasSize(5);
- Element debuggerStateElement = debuggerStatesElement.getChild("Auto");
- assertThat(xmlOutputter.outputString(debuggerStateElement))
- .isEqualTo(formatRawXml(DEBUGGER_STATE_AUTO_RAW_XML));
- debuggerStateElement = debuggerStatesElement.getChild("Native");
- assertThat(xmlOutputter.outputString(debuggerStateElement))
- .isEqualTo(formatRawXml(DEBUGGER_STATE_NATIVE_RAW_XML));
- debuggerStateElement = debuggerStatesElement.getChild("Java");
- assertThat(xmlOutputter.outputString(debuggerStateElement))
- .isEqualTo(formatRawXml(DEBUGGER_STATE_JAVA_RAW_XML));
- debuggerStateElement = debuggerStatesElement.getChild("Hybrid");
- assertThat(xmlOutputter.outputString(debuggerStateElement))
- .isEqualTo(formatRawXml(DEBUGGER_STATE_HYBRID_RAW_XML));
- debuggerStateElement = debuggerStatesElement.getChild("BlazeAuto");
- assertThat(xmlOutputter.outputString(debuggerStateElement))
- .isEqualTo(formatRawXml(DEBUGGER_STATE_BLAZE_AUTO_RAW_XML));
- }
-
- @Test
public void readAndWriteShouldRemoveExtraElements() throws Exception {
String oldXml =
"<?xml version=\"1.0\"?>"
diff --git a/aswb/tests/integrationtests/com/google/idea/blaze/android/run/binary/BlazeAndroidBinaryRunConfigurationStateTest.java b/aswb/tests/integrationtests/com/google/idea/blaze/android/run/binary/BlazeAndroidBinaryRunConfigurationStateTest.java
index 76cd2e5..05de9a5 100644
--- a/aswb/tests/integrationtests/com/google/idea/blaze/android/run/binary/BlazeAndroidBinaryRunConfigurationStateTest.java
+++ b/aswb/tests/integrationtests/com/google/idea/blaze/android/run/binary/BlazeAndroidBinaryRunConfigurationStateTest.java
@@ -63,7 +63,6 @@
state.setMode(BlazeAndroidBinaryRunConfigurationState.LAUNCH_SPECIFIC_ACTIVITY);
state.setMobileInstall(true);
state.setUseSplitApksIfPossible(false);
- state.setInstantRun(true);
state.setUseWorkProfileIfPresent(true);
state.setUserId(2);
state.setDeepLink("http://deeplink");
@@ -83,7 +82,6 @@
.isEqualTo(BlazeAndroidBinaryRunConfigurationState.LAUNCH_SPECIFIC_ACTIVITY);
assertThat(readState.mobileInstall()).isTrue();
assertThat(readState.useSplitApksIfPossible()).isFalse();
- assertThat(readState.instantRun()).isTrue();
assertThat(readState.useWorkProfileIfPresent()).isTrue();
assertThat(readState.getUserId()).isEqualTo(2);
assertThat(readState.getDeepLink()).isEqualTo("http://deeplink");
@@ -107,7 +105,6 @@
assertThat(readState.getMode()).isEqualTo(state.getMode());
assertThat(readState.mobileInstall()).isEqualTo(state.mobileInstall());
assertThat(readState.useSplitApksIfPossible()).isEqualTo(state.useSplitApksIfPossible());
- assertThat(readState.instantRun()).isEqualTo(state.instantRun());
assertThat(readState.useWorkProfileIfPresent()).isEqualTo(state.useWorkProfileIfPresent());
assertThat(readState.getUserId()).isEqualTo(state.getUserId());
assertThat(readState.getDeepLink()).isEqualTo(state.getDeepLink());
@@ -125,7 +122,6 @@
state.setMode(BlazeAndroidBinaryRunConfigurationState.LAUNCH_SPECIFIC_ACTIVITY);
state.setMobileInstall(true);
state.setUseSplitApksIfPossible(false);
- state.setInstantRun(true);
state.setUseWorkProfileIfPresent(true);
state.setUserId(2);
state.setDeepLink("http://deeplink");
@@ -151,7 +147,6 @@
state.setMode(BlazeAndroidBinaryRunConfigurationState.LAUNCH_SPECIFIC_ACTIVITY);
state.setMobileInstall(true);
state.setUseSplitApksIfPossible(false);
- state.setInstantRun(true);
state.setUseWorkProfileIfPresent(true);
state.setUserId(2);
// We don't test DeepLink because it is not exposed in the editor.
@@ -171,7 +166,6 @@
assertThat(readState.getMode()).isEqualTo(state.getMode());
assertThat(readState.mobileInstall()).isEqualTo(state.mobileInstall());
assertThat(readState.useSplitApksIfPossible()).isEqualTo(state.useSplitApksIfPossible());
- assertThat(readState.instantRun()).isEqualTo(state.instantRun());
assertThat(readState.useWorkProfileIfPresent()).isEqualTo(state.useWorkProfileIfPresent());
assertThat(readState.getUserId()).isEqualTo(state.getUserId());
// We don't test DeepLink because it is not exposed in the editor.
@@ -197,7 +191,6 @@
assertThat(readState.getMode()).isEqualTo(state.getMode());
assertThat(readState.mobileInstall()).isEqualTo(state.mobileInstall());
assertThat(readState.useSplitApksIfPossible()).isEqualTo(state.useSplitApksIfPossible());
- assertThat(readState.instantRun()).isEqualTo(state.instantRun());
assertThat(readState.useWorkProfileIfPresent()).isEqualTo(state.useWorkProfileIfPresent());
assertThat(readState.getUserId()).isEqualTo(state.getUserId());
// We don't test DeepLink because it is not exposed in the editor.
diff --git a/aswb/tests/unittests/com/google/idea/blaze/android/sync/importer/model/idea/AndroidResourceModuleRegistryTest.java b/aswb/tests/unittests/com/google/idea/blaze/android/sync/importer/model/idea/AndroidResourceModuleRegistryTest.java
new file mode 100644
index 0000000..9a8e66c
--- /dev/null
+++ b/aswb/tests/unittests/com/google/idea/blaze/android/sync/importer/model/idea/AndroidResourceModuleRegistryTest.java
@@ -0,0 +1,117 @@
+/*
+ * 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.android.sync.importer.model.idea;
+
+import static com.google.common.truth.Truth.THROW_ASSERTION_ERROR;
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.mock;
+
+import com.google.idea.blaze.android.sync.model.AndroidResourceModule;
+import com.google.idea.blaze.android.sync.model.AndroidResourceModuleRegistry;
+import com.google.idea.blaze.base.BlazeTestCase;
+import com.google.idea.blaze.base.ideinfo.TargetKey;
+import com.google.idea.blaze.base.model.primitives.Label;
+import com.intellij.openapi.module.Module;
+import org.jetbrains.annotations.NotNull;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Unit tests for {@link AndroidResourceModuleRegistry}. */
+@RunWith(JUnit4.class)
+public class AndroidResourceModuleRegistryTest extends BlazeTestCase {
+ private AndroidResourceModuleRegistry registry;
+
+ @Override
+ protected void initTest(
+ @NotNull Container applicationServices, @NotNull Container projectServices) {
+ super.initTest(applicationServices, projectServices);
+ projectServices.register(
+ AndroidResourceModuleRegistry.class, new AndroidResourceModuleRegistry());
+
+ registry = AndroidResourceModuleRegistry.getInstance(getProject());
+ }
+
+ @Test
+ public void testPutAndGet() {
+ Module moduleOne = mock(Module.class);
+ Module moduleTwo = mock(Module.class);
+ Module moduleThree = mock(Module.class);
+ AndroidResourceModule resourceModuleOne =
+ AndroidResourceModule.builder(TargetKey.forPlainTarget(new Label("//foo/bar:one"))).build();
+ AndroidResourceModule resourceModuleTwo =
+ AndroidResourceModule.builder(TargetKey.forPlainTarget(new Label("//foo/bar:two"))).build();
+ AndroidResourceModule resourceModuleThree =
+ AndroidResourceModule.builder(TargetKey.forPlainTarget(new Label("//foo/bar:three")))
+ .build();
+ registry.put(moduleOne, resourceModuleOne);
+ registry.put(moduleTwo, resourceModuleTwo);
+ registry.put(moduleThree, resourceModuleThree);
+
+ assertThat(registry.get(moduleOne)).isEqualTo(resourceModuleOne);
+ assertThat(registry.get(moduleTwo)).isEqualTo(resourceModuleTwo);
+ assertThat(registry.get(moduleThree)).isEqualTo(resourceModuleThree);
+ assertThat(registry.getTargetKey(moduleOne)).isEqualTo(resourceModuleOne.targetKey);
+ assertThat(registry.getTargetKey(moduleTwo)).isEqualTo(resourceModuleTwo.targetKey);
+ assertThat(registry.getTargetKey(moduleThree)).isEqualTo(resourceModuleThree.targetKey);
+ assertThat(registry.getLabel(moduleOne)).isEqualTo(resourceModuleOne.targetKey.label);
+ assertThat(registry.getLabel(moduleTwo)).isEqualTo(resourceModuleTwo.targetKey.label);
+ assertThat(registry.getLabel(moduleThree)).isEqualTo(resourceModuleThree.targetKey.label);
+ }
+
+ @Test
+ public void testPutSameKeyDifferentValues() {
+ Module module = mock(Module.class);
+ AndroidResourceModule resourceModuleOne =
+ AndroidResourceModule.builder(TargetKey.forPlainTarget(new Label("//foo/bar:one"))).build();
+ AndroidResourceModule resourceModuleTwo =
+ AndroidResourceModule.builder(TargetKey.forPlainTarget(new Label("//foo/bar:two"))).build();
+ registry.put(module, resourceModuleOne);
+ registry.put(module, resourceModuleTwo);
+ assertThat(registry.get(module)).isEqualTo(resourceModuleTwo);
+ }
+
+ @Test
+ public void testPutDifferentKeysSameValue() {
+ Module moduleOne = mock(Module.class);
+ Module moduleTwo = mock(Module.class);
+ AndroidResourceModule resourceModule =
+ AndroidResourceModule.builder(TargetKey.forPlainTarget(new Label("//foo/bar:one"))).build();
+ registry.put(moduleOne, resourceModule);
+ try {
+ registry.put(moduleTwo, resourceModule);
+ THROW_ASSERTION_ERROR.fail("Expected IllegalArgumentException");
+ } catch (IllegalArgumentException ignored) {
+ // ignored
+ }
+ assertThat(registry.get(moduleOne)).isEqualTo(resourceModule);
+ assertThat(registry.get(moduleTwo)).isNull();
+ }
+
+ @Test
+ public void testGetNull() {
+ assertThat(registry.get(null)).isNull();
+ assertThat(registry.getTargetKey(null)).isNull();
+ assertThat(registry.getLabel(null)).isNull();
+ }
+
+ @Test
+ public void testGetWithoutPut() {
+ assertThat(registry.get(mock(Module.class))).isNull();
+ assertThat(registry.getTargetKey(mock(Module.class))).isNull();
+ assertThat(registry.getLabel(mock(Module.class))).isNull();
+ }
+}
diff --git a/base/BUILD b/base/BUILD
index 6902fd5..850f1dd 100644
--- a/base/BUILD
+++ b/base/BUILD
@@ -6,11 +6,13 @@
resources = glob(["resources/**/*"]),
visibility = ["//visibility:public"],
deps = [
+ "//common/actionhelper",
"//common/binaryhelper",
"//common/experiments",
"//common/formatter",
"//intellij_platform_sdk:plugin_api",
"//proto_deps",
+ "//sdkcompat",
"@jsr305_annotations//jar",
],
)
@@ -29,6 +31,7 @@
deps = [
"//base",
"//intellij_platform_sdk:plugin_api_for_tests",
+ "//sdkcompat",
"@jsr305_annotations//jar",
"@junit//jar",
],
diff --git a/base/src/META-INF/blaze-base.xml b/base/src/META-INF/blaze-base.xml
index 1e12599..34d33f4 100644
--- a/base/src/META-INF/blaze-base.xml
+++ b/base/src/META-INF/blaze-base.xml
@@ -15,46 +15,86 @@
-->
<idea-plugin>
<actions>
- <action id="MakeBlazeProject" class="com.google.idea.blaze.base.actions.BlazeMakeProjectAction" use-shortcut-of="CompileDirty" icon="AllIcons.Actions.Compile">
+ <action id="MakeBlazeProject"
+ class="com.google.idea.blaze.base.actions.BlazeMakeProjectAction"
+ text="Compile Project"
+ use-shortcut-of="CompileDirty"
+ icon="AllIcons.Actions.Compile">
</action>
- <action id="MakeBlazeModule" class="com.google.idea.blaze.base.actions.BlazeCompileFileAction">
+ <action id="MakeBlazeModule"
+ class="com.google.idea.blaze.base.actions.BlazeCompileFileAction"
+ text="Compile File">
</action>
- <action id="Blaze.IncrementalSyncProject" class="com.google.idea.blaze.base.sync.actions.IncrementalSyncProjectAction" icon="BlazeIcons.Blaze">
+ <action id="Blaze.IncrementalSyncProject"
+ class="com.google.idea.blaze.base.sync.actions.IncrementalSyncProjectAction"
+ text="Sync Project with BUILD Files"
+ icon="BlazeIcons.Blaze">
</action>
- <action id="Blaze.FullSyncProject" class="com.google.idea.blaze.base.sync.actions.FullSyncProjectAction" icon="BlazeIcons.BlazeSlow">
+ <action id="Blaze.FullSyncProject"
+ class="com.google.idea.blaze.base.sync.actions.FullSyncProjectAction"
+ text="Non-Incrementally Sync Project with BUILD Files"
+ icon="BlazeIcons.BlazeSlow">
</action>
- <action id="Blaze.SyncWorkingSet" class="com.google.idea.blaze.base.sync.actions.SyncWorkingSetAction" icon="BlazeIcons.Blaze" text="Sync Working Set">
+ <action id="Blaze.SyncWorkingSet"
+ class="com.google.idea.blaze.base.sync.actions.SyncWorkingSetAction"
+ text="Sync Working Set"
+ icon="BlazeIcons.Blaze">
</action>
- <action id="Blaze.ExpandSyncToWorkingSet" class="com.google.idea.blaze.base.sync.actions.ExpandSyncToWorkingSetAction" text="Expand Sync to Working Set">
+ <action id="Blaze.ExpandSyncToWorkingSet"
+ class="com.google.idea.blaze.base.sync.actions.ExpandSyncToWorkingSetAction"
+ text="Expand Sync to Working Set">
</action>
- <action id="Blaze.ShowPerformanceWarnings" class="com.google.idea.blaze.base.sync.actions.ShowPerformanceWarningsToggleAction" text="Show Performance Warnings">
+ <action id="Blaze.ShowPerformanceWarnings"
+ class="com.google.idea.blaze.base.sync.actions.ShowPerformanceWarningsToggleAction"
+ text="Show Performance Warnings">
</action>
- <action id="Blaze.EditProjectView" class="com.google.idea.blaze.base.settings.ui.EditProjectViewAction" text="Edit Project View..." icon="BlazeIcons.Blaze">
+ <action id="Blaze.EditProjectView"
+ class="com.google.idea.blaze.base.settings.ui.EditProjectViewAction"
+ text="Open Project View Files">
</action>
-
<action class="com.google.idea.blaze.base.buildmap.OpenCorrespondingBuildFile"
- id="Blaze.OpenCorrespondingBuildFile"
- icon="BlazeIcons.Blaze"
- text="Open Corresponding BUILD File">
+ id="Blaze.OpenCorrespondingBuildFile"
+ text="Open Corresponding BUILD File">
</action>
<action class="com.google.idea.blaze.base.sync.actions.PartialSyncAction"
- id="Blaze.PartialSync"
- icon="BlazeIcons.Blaze">
+ id="Blaze.PartialSync"
+ text="Partially Sync File"
+ icon="BlazeIcons.Blaze">
</action>
+ <action id="Blaze.ExportRunConfigurations"
+ class="com.google.idea.blaze.base.run.exporter.ExportRunConfigurationAction"
+ text="Export Run Configurations"
+ icon="AllIcons.Actions.Export">
+ </action>
+ <action id="Blaze.NewPackageAction"
+ class="com.google.idea.blaze.base.ide.NewBlazePackageAction"
+ text="New Package"/>
+ <action id="Blaze.NewRuleAction"
+ class="com.google.idea.blaze.base.ide.NewBlazeRuleAction"
+ text="New Rule"
+ popup="true"/>
<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="MakeBlazeProject"/>
- <reference id="MakeBlazeModule"/>
- <separator/>
<reference id="Blaze.EditProjectView"/>
+ <group id ="Blaze.SyncMenuGroup" text="Sync" popup="true">
+ <reference id="Blaze.IncrementalSyncProject"/>
+ <reference id="Blaze.FullSyncProject"/>
+ <reference id="Blaze.SyncWorkingSet"/>
+ <reference id="Blaze.PartialSync"/>
+ <reference id="Blaze.ExpandSyncToWorkingSet"/>
+ <reference id="Blaze.ShowPerformanceWarnings"/>
+ </group>
+ <group id="Blaze.BuildMenuGroup" text="Build" popup="true">
+ <reference id="MakeBlazeProject"/>
+ <reference id="MakeBlazeModule"/>
+ </group>
+ <!--Add popup groups anchored after this bookmark-->
+ <group id="Blaze.MenuGroupsBookmark"/>
<separator/>
- <reference id="Blaze.IncrementalSyncProject"/>
- <reference id="Blaze.FullSyncProject"/>
- <reference id="Blaze.SyncWorkingSet"/>
- <reference id="Blaze.PartialSync"/>
- <reference id="Blaze.ExpandSyncToWorkingSet"/>
- <reference id="Blaze.ShowPerformanceWarnings"/>
+ <reference id="Blaze.ExportRunConfigurations"/>
+ <!--Add single menu items anchored after this bookmark-->
+ <group id="Blaze.MenuFooter"/>
</group>
<group id="Blaze.MainToolBarActionGroup">
@@ -65,8 +105,8 @@
<group id="Blaze.NewActions" text="Edit Blaze structure" description="Create new Blaze packages, rules, etc.">
<add-to-group group-id="NewGroup" anchor="first"/>
- <action id="Blaze.NewPackageAction" class="com.google.idea.blaze.base.ide.NewBlazePackageAction" popup="true"/>
- <action id="Blaze.NewRuleAction" class="com.google.idea.blaze.base.ide.NewBlazeRuleAction" popup="true"/>
+ <reference id="Blaze.NewPackageAction"/>
+ <reference id="Blaze.NewRuleAction"/>
<separator/>
</group>
@@ -137,6 +177,8 @@
<projectService serviceImplementation="com.google.idea.blaze.base.buildmap.FileToBuildMap"/>
<projectService serviceInterface="com.google.idea.blaze.base.targetmaps.SourceToTargetMap"
serviceImplementation="com.google.idea.blaze.base.targetmaps.SourceToTargetMapImpl"/>
+ <projectService serviceInterface="com.google.idea.blaze.base.targetmaps.TransitiveDependencyMap"
+ serviceImplementation="com.google.idea.blaze.base.targetmaps.TransitiveDependencyMap"/>
<projectService serviceImplementation="com.google.idea.blaze.base.settings.BlazeImportSettingsManager"/>
<projectService serviceImplementation="com.google.idea.blaze.base.settings.BlazeImportSettingsManagerLegacy"/>
<applicationService serviceImplementation="com.google.idea.blaze.base.settings.BlazeUserSettings"/>
@@ -147,6 +189,8 @@
<applicationService serviceInterface="com.google.idea.blaze.base.prefetch.PrefetchService"
serviceImplementation="com.google.idea.blaze.base.prefetch.PrefetchServiceImpl"/>
<applicationService serviceImplementation="com.google.idea.blaze.base.wizard2.BlazeWizardUserSettingsStorage"/>
+ <applicationService serviceInterface="com.google.idea.blaze.base.wizard2.BlazeWizardOptionProvider"
+ 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"/>
<configurationType implementation="com.google.idea.blaze.base.run.BlazeCommandRunConfigurationType"/>
@@ -159,6 +203,7 @@
<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"/>
+ <applicationService serviceImplementation="com.google.idea.blaze.base.actions.BlazeBuildService"/>
<additionalTextAttributes scheme="Default" file="base/resources/colorSchemes/BuildDefault.xml"/>
<typedHandler implementation="com.google.idea.blaze.base.lang.buildfile.completion.BuildCompletionAutoPopupHandler"/>
@@ -254,8 +299,6 @@
<extensionPoint qualifiedName="com.google.idea.blaze.PsiFileProvider" interface="com.google.idea.blaze.base.lang.buildfile.search.PsiFileProvider"/>
<extensionPoint qualifiedName="com.google.idea.blaze.VcsHandler"
interface="com.google.idea.blaze.base.vcs.BlazeVcsHandler"/>
- <extensionPoint qualifiedName="com.google.idea.blaze.BlazeWizardOptionProvider"
- interface="com.google.idea.blaze.base.wizard2.BlazeWizardOptionProvider"/>
<extensionPoint qualifiedName="com.google.idea.blaze.DefaultSdkProvider"
interface="com.google.idea.blaze.base.sync.sdk.DefaultSdkProvider"/>
<extensionPoint qualifiedName="com.google.idea.blaze.BuildFlagsProvider" interface="com.google.idea.blaze.base.command.BuildFlagsProvider"/>
@@ -269,6 +312,7 @@
<extensionPoint qualifiedName="com.google.idea.blaze.TestTargetHeuristic" interface="com.google.idea.blaze.base.run.TestTargetHeuristic"/>
<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"/>
</extensionPoints>
<extensions defaultExtensionNs="com.google.idea.blaze">
@@ -276,9 +320,7 @@
<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"/>
- <SyncListener implementation="com.google.idea.blaze.base.lang.buildfile.language.semantics.BuildLanguageSpecProviderImpl"/>
<SyncPlugin implementation="com.google.idea.blaze.base.lang.buildfile.sync.BuildLangSyncPlugin"/>
- <BlazeWizardOptionProvider implementation="com.google.idea.blaze.base.wizard2.BazelWizardOptionProvider"/>
<BuildFlagsProvider implementation="com.google.idea.blaze.base.command.BuildFlagsProviderImpl"/>
<VcsHandler implementation="com.google.idea.blaze.base.vcs.git.GitBlazeVcsHandler"/>
<VcsHandler implementation="com.google.idea.blaze.base.vcs.FallbackBlazeVcsHandler" order="last" id="fallback"/>
diff --git a/base/src/com/google/idea/blaze/base/actions/BlazeAction.java b/base/src/com/google/idea/blaze/base/actions/BlazeAction.java
deleted file mode 100644
index ed31396..0000000
--- a/base/src/com/google/idea/blaze/base/actions/BlazeAction.java
+++ /dev/null
@@ -1,59 +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.actions;
-
-import com.google.idea.blaze.base.settings.Blaze;
-import com.intellij.openapi.actionSystem.AnAction;
-import com.intellij.openapi.actionSystem.AnActionEvent;
-import com.intellij.openapi.project.Project;
-import javax.swing.Icon;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-/** Base class action that hides for non-blaze projects. */
-public abstract class BlazeAction extends AnAction {
- protected BlazeAction() {}
-
- protected BlazeAction(Icon icon) {
- super(icon);
- }
-
- protected BlazeAction(@Nullable String text) {
- super(text);
- }
-
- protected BlazeAction(@Nullable String text, @Nullable String description, @Nullable Icon icon) {
- super(text, description, icon);
- }
-
- @Override
- public final void update(AnActionEvent e) {
- if (!isBlazeProject(e)) {
- e.getPresentation().setEnabledAndVisible(false);
- return;
- }
-
- e.getPresentation().setEnabledAndVisible(true);
- doUpdate(e);
- }
-
- protected void doUpdate(@NotNull AnActionEvent e) {}
-
- private static boolean isBlazeProject(@NotNull AnActionEvent e) {
- Project project = e.getProject();
- return project != null && Blaze.isBlazeProject(project);
- }
-}
diff --git a/base/src/com/google/idea/blaze/base/actions/BlazeBuildService.java b/base/src/com/google/idea/blaze/base/actions/BlazeBuildService.java
new file mode 100644
index 0000000..4da44f1
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/actions/BlazeBuildService.java
@@ -0,0 +1,141 @@
+/*
+ * 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.actions;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableCollection;
+import com.google.common.collect.Lists;
+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;
+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.projectview.section.sections.TargetSection;
+import com.google.idea.blaze.base.scope.BlazeContext;
+import com.google.idea.blaze.base.scope.ScopedTask;
+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;
+import com.google.idea.blaze.base.sync.aspects.BlazeIdeInterface;
+import com.google.idea.blaze.base.sync.data.BlazeProjectDataManager;
+import com.google.idea.blaze.base.util.SaveUtil;
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.project.Project;
+import java.util.List;
+
+/** Utility to build various collections of targets. */
+public class BlazeBuildService {
+ public static BlazeBuildService getInstance() {
+ return ServiceManager.getService(BlazeBuildService.class);
+ }
+
+ public void buildFile(Project project, String fileName, ImmutableCollection<Label> targets) {
+ if (project == null || !Blaze.isBlazeProject(project) || fileName == null) {
+ return;
+ }
+ buildTargetExpressions(
+ project,
+ Lists.newArrayList(targets),
+ ProjectViewManager.getInstance(project).getProjectViewSet(),
+ new LoggedTimingScope(project, Action.MAKE_MODULE_TOTAL_TIME),
+ new NotificationScope(
+ project,
+ "Make",
+ "Make " + fileName,
+ "Make " + fileName + " completed successfully",
+ "Make " + fileName + " failed"));
+ }
+
+ public void buildProject(Project project) {
+ if (project == null || !Blaze.isBlazeProject(project)) {
+ return;
+ }
+ ProjectViewSet projectViewSet = ProjectViewManager.getInstance(project).getProjectViewSet();
+ if (projectViewSet == null) {
+ return;
+ }
+ buildTargetExpressions(
+ project,
+ projectViewSet.listItems(TargetSection.KEY),
+ projectViewSet,
+ new LoggedTimingScope(project, Action.MAKE_PROJECT_TOTAL_TIME),
+ new NotificationScope(
+ project,
+ "Make",
+ "Make project",
+ "Make project completed successfully",
+ "Make project failed"));
+ }
+
+ @VisibleForTesting
+ void buildTargetExpressions(
+ Project project,
+ List<TargetExpression> targets,
+ ProjectViewSet projectViewSet,
+ LoggedTimingScope loggedTimingScope,
+ NotificationScope notificationScope) {
+ if (targets.isEmpty() || projectViewSet == null) {
+ return;
+ }
+ BlazeProjectData blazeProjectData =
+ BlazeProjectDataManager.getInstance(project).getBlazeProjectData();
+ if (blazeProjectData == null) {
+ return;
+ }
+ BlazeExecutor.submitTask(
+ project,
+ new ScopedTask() {
+ @Override
+ public void execute(BlazeContext context) {
+ context
+ .push(new ExperimentScope())
+ .push(new BlazeConsoleScope.Builder(project).build())
+ .push(new IssuesScope(project))
+ .push(new IdeaLogScope())
+ .push(new TimingScope("Make"))
+ .push(loggedTimingScope)
+ .push(notificationScope);
+
+ WorkspaceRoot workspaceRoot = WorkspaceRoot.fromProject(project);
+ BlazeIdeInterface blazeIdeInterface = BlazeIdeInterface.getInstance();
+
+ SaveUtil.saveAllFiles();
+ BlazeIdeInterface.BuildResult buildResult =
+ blazeIdeInterface.compileIdeArtifacts(
+ project,
+ context,
+ workspaceRoot,
+ projectViewSet,
+ blazeProjectData.blazeVersionData,
+ targets);
+ FileCaches.refresh(project);
+
+ if (buildResult != BlazeIdeInterface.BuildResult.SUCCESS) {
+ context.setHasError();
+ }
+ }
+ });
+ }
+}
diff --git a/base/src/com/google/idea/blaze/base/actions/BlazeCompileFileAction.java b/base/src/com/google/idea/blaze/base/actions/BlazeCompileFileAction.java
index 00ad470..130de1b 100644
--- a/base/src/com/google/idea/blaze/base/actions/BlazeCompileFileAction.java
+++ b/base/src/com/google/idea/blaze/base/actions/BlazeCompileFileAction.java
@@ -17,65 +17,29 @@
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Lists;
-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;
-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.scope.BlazeContext;
-import com.google.idea.blaze.base.scope.ScopedTask;
-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.sync.aspects.BlazeIdeInterface;
-import com.google.idea.blaze.base.sync.aspects.BlazeIdeInterface.BuildResult;
-import com.google.idea.blaze.base.sync.data.BlazeProjectDataManager;
import com.google.idea.blaze.base.targetmaps.SourceToTargetMap;
-import com.google.idea.blaze.base.util.SaveUtil;
+import com.google.idea.common.actionhelper.ActionPresentationHelper;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
-import com.intellij.openapi.actionSystem.Presentation;
-import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import java.io.File;
-import java.util.List;
-import org.jetbrains.annotations.NotNull;
-class BlazeCompileFileAction extends BlazeAction {
- private static final Logger LOG = Logger.getInstance(BlazeCompileFileAction.class);
+class BlazeCompileFileAction extends BlazeProjectAction {
- public BlazeCompileFileAction() {
- super("Compile file");
+ @Override
+ protected void updateForBlazeProject(Project project, AnActionEvent e) {
+ ActionPresentationHelper.of(e)
+ .disableIf(getTargets(e).isEmpty())
+ .setTextWithSubject("Compile File", "Compile %s", e.getData(CommonDataKeys.VIRTUAL_FILE))
+ .disableWithoutSubject()
+ .commit();
}
@Override
- protected void doUpdate(@NotNull AnActionEvent e) {
- // IntelliJ uses different logic for 1 vs many module selection. When many modules are selected
- // modules with more than 1 content root are ignored
- // (ProjectViewImpl#moduleBySingleContentRoot).
- if (getTargets(e).isEmpty()) {
- Presentation presentation = e.getPresentation();
- presentation.setEnabled(false);
- }
- }
-
- @Override
- public void actionPerformed(AnActionEvent e) {
- Project project = e.getProject();
- if (project != null) {
- ImmutableCollection<Label> targets = getTargets(e);
- buildSourceFile(project, targets);
- }
+ protected void actionPerformedInBlazeProject(Project project, AnActionEvent e) {
+ BlazeBuildService.getInstance().buildFile(project, getFileName(e), getTargets(e));
}
private ImmutableCollection<Label> getTargets(AnActionEvent e) {
@@ -88,53 +52,8 @@
return ImmutableList.of();
}
- private static void buildSourceFile(
- @NotNull Project project, @NotNull ImmutableCollection<Label> targets) {
- BlazeProjectData blazeProjectData =
- BlazeProjectDataManager.getInstance(project).getBlazeProjectData();
- if (blazeProjectData == null || targets.isEmpty()) {
- return;
- }
- final ProjectViewSet projectViewSet =
- ProjectViewManager.getInstance(project).getProjectViewSet();
- if (projectViewSet == null) {
- return;
- }
- BlazeExecutor.submitTask(
- project,
- new ScopedTask() {
- @Override
- public void execute(@NotNull BlazeContext context) {
- context
- .push(new ExperimentScope())
- .push(new BlazeConsoleScope.Builder(project).build())
- .push(new IssuesScope(project))
- .push(new IdeaLogScope())
- .push(new TimingScope("Make"))
- .push(new LoggedTimingScope(project, Action.MAKE_MODULE_TOTAL_TIME))
- .push(
- new NotificationScope(
- project,
- "Make",
- "Make module",
- "Make module completed successfully",
- "Make module failed"));
-
- WorkspaceRoot workspaceRoot = WorkspaceRoot.fromProject(project);
-
- BlazeIdeInterface blazeIdeInterface = BlazeIdeInterface.getInstance();
- List<TargetExpression> targetExpressions = Lists.newArrayList(targets);
-
- SaveUtil.saveAllFiles();
- BuildResult buildResult =
- blazeIdeInterface.resolveIdeArtifacts(
- project, context, workspaceRoot, projectViewSet, targetExpressions);
- FileCaches.refresh(project);
-
- if (buildResult != BuildResult.SUCCESS) {
- context.setHasError();
- }
- }
- });
+ private static String getFileName(AnActionEvent e) {
+ VirtualFile virtualFile = e.getData(CommonDataKeys.VIRTUAL_FILE);
+ return virtualFile == null ? null : virtualFile.getName();
}
}
diff --git a/base/src/com/google/idea/blaze/base/actions/BlazeMakeProjectAction.java b/base/src/com/google/idea/blaze/base/actions/BlazeMakeProjectAction.java
index 64964c5..a124759 100644
--- a/base/src/com/google/idea/blaze/base/actions/BlazeMakeProjectAction.java
+++ b/base/src/com/google/idea/blaze/base/actions/BlazeMakeProjectAction.java
@@ -15,102 +15,13 @@
*/
package com.google.idea.blaze.base.actions;
-import com.google.common.collect.Lists;
-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.TargetExpression;
-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.projectview.section.sections.TargetSection;
-import com.google.idea.blaze.base.scope.BlazeContext;
-import com.google.idea.blaze.base.scope.ScopedTask;
-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;
-import com.google.idea.blaze.base.sync.aspects.BlazeIdeInterface;
-import com.google.idea.blaze.base.sync.aspects.BlazeIdeInterface.BuildResult;
-import com.google.idea.blaze.base.sync.data.BlazeProjectDataManager;
-import com.google.idea.blaze.base.util.SaveUtil;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.project.Project;
-import java.util.List;
-import org.jetbrains.annotations.NotNull;
-class BlazeMakeProjectAction extends BlazeAction {
-
- public BlazeMakeProjectAction() {
- super("Make Project");
- }
+class BlazeMakeProjectAction extends BlazeProjectAction {
@Override
- public final void actionPerformed(AnActionEvent e) {
- Project project = e.getProject();
- if (project != null && Blaze.isBlazeProject(project)) {
- buildBlazeProject(project);
- }
- }
-
- protected void buildBlazeProject(@NotNull final Project project) {
-
- BlazeExecutor.submitTask(
- project,
- new ScopedTask() {
- @Override
- public void execute(@NotNull BlazeContext context) {
- context
- .push(new ExperimentScope())
- .push(new BlazeConsoleScope.Builder(project).build())
- .push(new IssuesScope(project))
- .push(new IdeaLogScope())
- .push(new TimingScope("Make"))
- .push(new LoggedTimingScope(project, Action.MAKE_PROJECT_TOTAL_TIME))
- .push(
- new NotificationScope(
- project,
- "Make",
- "Make project",
- "Make project completed successfully",
- "Make project failed"));
-
- BlazeProjectData blazeProjectData =
- BlazeProjectDataManager.getInstance(project).getBlazeProjectData();
- if (blazeProjectData == null) {
- return;
- }
- SaveUtil.saveAllFiles();
-
- ProjectViewSet projectViewSet =
- ProjectViewManager.getInstance(project)
- .reloadProjectView(context, blazeProjectData.workspacePathResolver);
- if (projectViewSet == null) {
- return;
- }
-
- WorkspaceRoot workspaceRoot = WorkspaceRoot.fromProject(project);
-
- List<TargetExpression> targets = Lists.newArrayList();
- targets.addAll(projectViewSet.listItems(TargetSection.KEY));
-
- BlazeIdeInterface blazeIdeInterface = BlazeIdeInterface.getInstance();
-
- BuildResult buildResult =
- blazeIdeInterface.resolveIdeArtifacts(
- project, context, workspaceRoot, projectViewSet, targets);
- FileCaches.refresh(project);
-
- if (buildResult != BuildResult.SUCCESS) {
- context.setHasError();
- ;
- }
- }
- });
+ protected void actionPerformedInBlazeProject(Project project, AnActionEvent e) {
+ BlazeBuildService.getInstance().buildProject(project);
}
}
diff --git a/base/src/com/google/idea/blaze/base/actions/BlazeProjectAction.java b/base/src/com/google/idea/blaze/base/actions/BlazeProjectAction.java
new file mode 100644
index 0000000..fdf81be
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/actions/BlazeProjectAction.java
@@ -0,0 +1,86 @@
+/*
+ * 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.actions;
+
+import com.google.idea.blaze.base.settings.Blaze;
+import com.google.idea.blaze.base.settings.Blaze.BuildSystem;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.project.Project;
+import javax.annotation.Nullable;
+import javax.swing.Icon;
+
+/** Base class action that hides for non-blaze projects. */
+public abstract class BlazeProjectAction extends AnAction {
+ protected BlazeProjectAction() {}
+
+ protected BlazeProjectAction(Icon icon) {
+ super(icon);
+ }
+
+ protected BlazeProjectAction(@Nullable String text) {
+ super(text);
+ }
+
+ protected BlazeProjectAction(
+ @Nullable String text, @Nullable String description, @Nullable Icon icon) {
+ super(text, description, icon);
+ }
+
+ @Override
+ public final void update(AnActionEvent e) {
+ Project project = e.getProject();
+ if (project == null || !Blaze.isBlazeProject(project)) {
+ e.getPresentation().setEnabledAndVisible(false);
+ return;
+ }
+
+ e.getPresentation().setEnabledAndVisible(true);
+
+ if (!compatibleBuildSystem(project)) {
+ e.getPresentation().setEnabled(false);
+ return;
+ }
+
+ updateForBlazeProject(project, e);
+ }
+
+ @Override
+ public final void actionPerformed(AnActionEvent anActionEvent) {
+ Project project = anActionEvent.getProject();
+ if (project == null) {
+ return;
+ }
+ actionPerformedInBlazeProject(project, anActionEvent);
+ }
+
+ protected void updateForBlazeProject(Project project, AnActionEvent e) {}
+
+ protected abstract void actionPerformedInBlazeProject(Project project, AnActionEvent e);
+
+ private boolean compatibleBuildSystem(Project project) {
+ BuildSystem requiredBuildSystem = requiredBuildSystem();
+ if (requiredBuildSystem == null) {
+ return true;
+ }
+ return Blaze.getBuildSystem(project) == requiredBuildSystem;
+ }
+
+ @Nullable
+ protected BuildSystem requiredBuildSystem() {
+ return null;
+ }
+}
diff --git a/base/src/com/google/idea/blaze/base/actions/BlazeToggleAction.java b/base/src/com/google/idea/blaze/base/actions/BlazeProjectToggleAction.java
similarity index 73%
rename from base/src/com/google/idea/blaze/base/actions/BlazeToggleAction.java
rename to base/src/com/google/idea/blaze/base/actions/BlazeProjectToggleAction.java
index 0413761..270f067 100644
--- a/base/src/com/google/idea/blaze/base/actions/BlazeToggleAction.java
+++ b/base/src/com/google/idea/blaze/base/actions/BlazeProjectToggleAction.java
@@ -19,39 +19,34 @@
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.ToggleAction;
import com.intellij.openapi.project.Project;
+import javax.annotation.Nullable;
import javax.swing.Icon;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
/** Base class toggle action that hides for non-blaze projects. */
-public abstract class BlazeToggleAction extends ToggleAction {
- protected BlazeToggleAction() {}
+public abstract class BlazeProjectToggleAction extends ToggleAction {
+ protected BlazeProjectToggleAction() {}
- protected BlazeToggleAction(@Nullable String text) {
+ protected BlazeProjectToggleAction(@Nullable String text) {
super(text);
}
- protected BlazeToggleAction(
+ protected BlazeProjectToggleAction(
@Nullable String text, @Nullable String description, @Nullable Icon icon) {
super(text, description, icon);
}
@Override
public final void update(AnActionEvent e) {
- if (!isBlazeProject(e)) {
+ Project project = e.getProject();
+ if (project == null || !Blaze.isBlazeProject(project)) {
e.getPresentation().setEnabledAndVisible(false);
return;
}
e.getPresentation().setEnabledAndVisible(true);
super.update(e);
- doUpdate(e);
+ updateForBlazeProject(project, e);
}
- protected void doUpdate(@NotNull AnActionEvent e) {}
-
- private static boolean isBlazeProject(@NotNull AnActionEvent e) {
- Project project = e.getProject();
- return project != null && Blaze.isBlazeProject(project);
- }
+ protected void updateForBlazeProject(Project project, AnActionEvent e) {}
}
diff --git a/base/src/com/google/idea/blaze/base/async/AsyncUtil.java b/base/src/com/google/idea/blaze/base/async/AsyncUtil.java
deleted file mode 100644
index 153f995..0000000
--- a/base/src/com/google/idea/blaze/base/async/AsyncUtil.java
+++ /dev/null
@@ -1,58 +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.async;
-
-import com.intellij.openapi.application.ApplicationManager;
-import com.intellij.util.ui.UIUtil;
-import org.jetbrains.annotations.NotNull;
-
-/** Async utilities. */
-public class AsyncUtil {
- public static void executeProjectChangeAction(@NotNull final Runnable task) throws Throwable {
- final ValueHolder<Throwable> error = new ValueHolder<Throwable>();
-
- executeOnEdt(
- new Runnable() {
- @Override
- public void run() {
- ApplicationManager.getApplication()
- .runWriteAction(
- new Runnable() {
- @Override
- public void run() {
- try {
- task.run();
- } catch (Throwable t) {
- error.value = t;
- }
- }
- });
- }
- });
-
- if (error.value != null) {
- throw error.value;
- }
- }
-
- private static void executeOnEdt(@NotNull Runnable task) {
- if (ApplicationManager.getApplication().isDispatchThread()) {
- task.run();
- } else {
- UIUtil.invokeAndWaitIfNeeded(task);
- }
- }
-}
diff --git a/base/src/com/google/idea/blaze/base/bazel/BazelBuildSystemProvider.java b/base/src/com/google/idea/blaze/base/bazel/BazelBuildSystemProvider.java
index d207ff2..b3d4598 100644
--- a/base/src/com/google/idea/blaze/base/bazel/BazelBuildSystemProvider.java
+++ b/base/src/com/google/idea/blaze/base/bazel/BazelBuildSystemProvider.java
@@ -16,8 +16,10 @@
package com.google.idea.blaze.base.bazel;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
import com.google.idea.blaze.base.io.FileAttributeProvider;
import com.google.idea.blaze.base.lang.buildfile.language.semantics.RuleDefinition;
+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.fileTypes.ExactFileNameMatcher;
@@ -27,6 +29,7 @@
/** Provides the bazel build system name string. */
public class BazelBuildSystemProvider implements BuildSystemProvider {
+
@Override
public BuildSystem buildSystem() {
return BuildSystem.Bazel;
@@ -72,4 +75,16 @@
public FileNameMatcher buildFileMatcher() {
return new ExactFileNameMatcher("BUILD");
}
+
+ @Override
+ public void populateBlazeVersionData(
+ BuildSystem buildSystem,
+ WorkspaceRoot workspaceRoot,
+ ImmutableMap<String, String> blazeInfo,
+ BlazeVersionData.Builder builder) {
+ if (buildSystem != BuildSystem.Bazel) {
+ return;
+ }
+ builder.setBazelVersion(BazelVersion.parseVersion(blazeInfo));
+ }
}
diff --git a/base/src/com/google/idea/blaze/base/bazel/BazelVersion.java b/base/src/com/google/idea/blaze/base/bazel/BazelVersion.java
new file mode 100644
index 0000000..44582cb
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/bazel/BazelVersion.java
@@ -0,0 +1,109 @@
+/*
+ * 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.bazel;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Objects;
+import com.google.common.collect.ComparisonChain;
+import com.google.idea.blaze.base.command.info.BlazeInfo;
+import com.intellij.openapi.util.text.StringUtil;
+import java.io.Serializable;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import javax.annotation.Nullable;
+
+/** Bazel version */
+public class BazelVersion implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ public static final BazelVersion UNKNOWN = new BazelVersion(0, 0, 0);
+ private static final Pattern PATTERN = Pattern.compile("([[0-9]\\.]+)");
+
+ public final int major;
+ public final int minor;
+ public final int bugfix;
+
+ BazelVersion(int major, int minor, int bugfix) {
+ this.bugfix = bugfix;
+ this.minor = minor;
+ this.major = major;
+ }
+
+ @VisibleForTesting
+ static BazelVersion parseVersion(@Nullable String string) {
+ if (string == null) {
+ return UNKNOWN;
+ }
+ Matcher matcher = PATTERN.matcher(string);
+ if (!matcher.find()) {
+ return UNKNOWN;
+ }
+ try {
+ BazelVersion version = parseVersion(matcher.group(1).split("\\."));
+ if (version == null) {
+ return UNKNOWN;
+ }
+ return version;
+ } catch (Exception e) {
+ return UNKNOWN;
+ }
+ }
+
+ @Nullable
+ private static BazelVersion parseVersion(String[] numbers) {
+ if (numbers.length < 1) {
+ return null;
+ }
+ int major = StringUtil.parseInt(numbers[0], -1);
+ if (major < 0) {
+ return null;
+ }
+ int minor = numbers.length > 1 ? StringUtil.parseInt(numbers[1], 0) : 0;
+ int bugfix = numbers.length > 2 ? StringUtil.parseInt(numbers[2], 0) : 0;
+ return new BazelVersion(major, minor, bugfix);
+ }
+
+ public static BazelVersion parseVersion(Map<String, String> blazeInfo) {
+ return parseVersion(blazeInfo.get(BlazeInfo.RELEASE));
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ BazelVersion that = (BazelVersion) o;
+ return major == that.major && minor == that.minor && bugfix == that.bugfix;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(major, minor, bugfix);
+ }
+
+ public boolean isAtLeast(int major, int minor, int bugfix) {
+ return ComparisonChain.start()
+ .compare(this.major, major)
+ .compare(this.minor, minor)
+ .compare(this.bugfix, bugfix)
+ .result()
+ >= 0;
+ }
+}
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 4b47716..6ce5aca 100644
--- a/base/src/com/google/idea/blaze/base/bazel/BuildSystemProvider.java
+++ b/base/src/com/google/idea/blaze/base/bazel/BuildSystemProvider.java
@@ -16,7 +16,9 @@
package com.google.idea.blaze.base.bazel;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
import com.google.idea.blaze.base.lang.buildfile.language.semantics.RuleDefinition;
+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;
@@ -105,4 +107,11 @@
}
FileNameMatcher buildFileMatcher();
+
+ /** Populates the passed builder with version data. */
+ void populateBlazeVersionData(
+ BuildSystem buildSystem,
+ WorkspaceRoot workspaceRoot,
+ ImmutableMap<String, String> blazeInfo,
+ BlazeVersionData.Builder builder);
}
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 2754da2..592b7b4 100644
--- a/base/src/com/google/idea/blaze/base/buildmap/OpenCorrespondingBuildFile.java
+++ b/base/src/com/google/idea/blaze/base/buildmap/OpenCorrespondingBuildFile.java
@@ -16,7 +16,7 @@
package com.google.idea.blaze.base.buildmap;
import com.google.common.collect.Iterables;
-import com.google.idea.blaze.base.actions.BlazeAction;
+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.intellij.ide.actions.OpenFileAction;
@@ -29,16 +29,12 @@
import com.intellij.openapi.vfs.VirtualFile;
import java.io.File;
import java.util.Collection;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
+import javax.annotation.Nullable;
-class OpenCorrespondingBuildFile extends BlazeAction {
+class OpenCorrespondingBuildFile extends BlazeProjectAction {
+
@Override
- public void actionPerformed(AnActionEvent e) {
- Project project = e.getProject();
- if (project == null) {
- return;
- }
+ protected void actionPerformedInBlazeProject(Project project, AnActionEvent e) {
VirtualFile virtualFile = e.getData(CommonDataKeys.VIRTUAL_FILE);
File file = getBuildFile(project, virtualFile);
if (file == null) {
@@ -49,10 +45,7 @@
}
@Nullable
- private File getBuildFile(@Nullable Project project, @Nullable VirtualFile virtualFile) {
- if (project == null) {
- return null;
- }
+ private File getBuildFile(Project project, @Nullable VirtualFile virtualFile) {
if (virtualFile == null) {
return null;
}
@@ -62,12 +55,11 @@
}
@Override
- protected void doUpdate(@NotNull AnActionEvent e) {
+ protected void updateForBlazeProject(Project project, AnActionEvent e) {
Presentation presentation = e.getPresentation();
DataContext dataContext = e.getDataContext();
VirtualFile virtualFile = CommonDataKeys.VIRTUAL_FILE.getData(dataContext);
- Project project = CommonDataKeys.PROJECT.getData(dataContext);
- boolean visible = (project != null && virtualFile != null);
+ boolean visible = virtualFile != null;
boolean enabled = getBuildFile(project, virtualFile) != null;
presentation.setVisible(visible || ActionPlaces.isMainMenuOrActionSearch(e.getPlace()));
presentation.setEnabled(enabled);
diff --git a/base/src/com/google/idea/blaze/base/command/BlazeFlags.java b/base/src/com/google/idea/blaze/base/command/BlazeFlags.java
index 2338f24..1f24f62 100644
--- a/base/src/com/google/idea/blaze/base/command/BlazeFlags.java
+++ b/base/src/com/google/idea/blaze/base/command/BlazeFlags.java
@@ -41,13 +41,6 @@
public static final String TEST_OUTPUT_STREAMED = "--test_output=streamed";
// Filters the unit tests that are run (used with regexp for Java/Robolectric tests).
public static final String TEST_FILTER = "--test_filter";
- // Skips checking for output file modifications (reduced statting -> faster).
- public static final String NO_CHECK_OUTPUTS = "--noexperimental_check_output_files";
- // Ignores implicit dependencies (e.g. java_library rules depending implicitly on
- // "//transconsole/tools:aggregate_messages" in order to support translations).
- public static final String NO_IMPLICIT_DEPS = "--noimplicit_deps";
- // Ignores host dependencies.
- public static final String NO_HOST_DEPS = "--nohost_deps";
// When used with mobile-install, deploys the an app incrementally.
public static final String INCREMENTAL = "--incremental";
// When used with mobile-install, deploys the an app incrementally
diff --git a/base/src/com/google/idea/blaze/base/command/BuildFlagsProviderImpl.java b/base/src/com/google/idea/blaze/base/command/BuildFlagsProviderImpl.java
index 290f001..2e40662 100644
--- a/base/src/com/google/idea/blaze/base/command/BuildFlagsProviderImpl.java
+++ b/base/src/com/google/idea/blaze/base/command/BuildFlagsProviderImpl.java
@@ -15,7 +15,6 @@
*/
package com.google.idea.blaze.base.command;
-import static com.google.idea.blaze.base.command.BlazeFlags.NO_CHECK_OUTPUTS;
import static com.google.idea.blaze.base.command.BlazeFlags.VERSION_WINDOW_FOR_DIRTY_NODE_GC;
import com.google.idea.blaze.base.projectview.ProjectViewSet;
@@ -28,8 +27,6 @@
private static final BoolExperiment experimentUseVersionWindowForDirtyNodeGc =
new BoolExperiment("ide_build_info.use_version_window_for_dirty_node_gc", false);
- private static final BoolExperiment experimentNoExperimentalCheckOutputFiles =
- new BoolExperiment("build.noexperimental_check_output_files", false);
@Override
public void addBuildFlags(
@@ -37,9 +34,6 @@
if (experimentUseVersionWindowForDirtyNodeGc.getValue()) {
flags.add(VERSION_WINDOW_FOR_DIRTY_NODE_GC);
}
- if (experimentNoExperimentalCheckOutputFiles.getValue()) {
- flags.add(NO_CHECK_OUTPUTS);
- }
flags.add("--curses=no");
flags.add("--color=no");
}
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 5346e8c..6099fc2 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 RELEASE = "release";
public static String blazeBinKey(BuildSystem buildSystem) {
switch (buildSystem) {
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 3780bf1..2d0f519 100644
--- a/base/src/com/google/idea/blaze/base/ide/NewBlazePackageAction.java
+++ b/base/src/com/google/idea/blaze/base/ide/NewBlazePackageAction.java
@@ -18,7 +18,7 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
-import com.google.idea.blaze.base.actions.BlazeAction;
+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;
@@ -41,7 +41,6 @@
import com.intellij.ide.IdeView;
import com.intellij.ide.util.DirectoryChooserUtil;
import com.intellij.openapi.actionSystem.AnActionEvent;
-import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.actionSystem.LangDataKeys;
import com.intellij.openapi.actionSystem.Presentation;
import com.intellij.openapi.diagnostic.Logger;
@@ -60,7 +59,7 @@
import javax.annotation.Nullable;
import org.jetbrains.annotations.NotNull;
-class NewBlazePackageAction extends BlazeAction implements DumbAware {
+class NewBlazePackageAction extends BlazeProjectAction implements DumbAware {
private static final Logger LOG = Logger.getInstance(NewBlazePackageAction.class);
private static final String BUILD_FILE_NAME = "BUILD";
@@ -70,9 +69,8 @@
}
@Override
- public void actionPerformed(AnActionEvent event) {
+ protected void actionPerformedInBlazeProject(Project project, AnActionEvent event) {
final IdeView view = event.getData(LangDataKeys.IDE_VIEW);
- final Project project = event.getData(CommonDataKeys.PROJECT);
Scope.root(
new ScopedOperation() {
@Override
@@ -160,10 +158,10 @@
}
@Override
- protected void doUpdate(@NotNull AnActionEvent event) {
+ protected void updateForBlazeProject(Project project, @NotNull AnActionEvent event) {
Presentation presentation = event.getPresentation();
if (isEnabled(event)) {
- String text = String.format("New %s Package", Blaze.buildSystemName(event.getProject()));
+ String text = String.format("New %s Package", Blaze.buildSystemName(project));
presentation.setEnabledAndVisible(true);
presentation.setText(text);
presentation.setDescription(text);
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 d67dccf..422dada 100644
--- a/base/src/com/google/idea/blaze/base/ide/NewBlazeRuleAction.java
+++ b/base/src/com/google/idea/blaze/base/ide/NewBlazeRuleAction.java
@@ -16,7 +16,7 @@
package com.google.idea.blaze.base.ide;
-import com.google.idea.blaze.base.actions.BlazeAction;
+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;
@@ -34,20 +34,15 @@
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import javax.annotation.Nullable;
-import org.jetbrains.annotations.NotNull;
-class NewBlazeRuleAction extends BlazeAction implements DumbAware {
+class NewBlazeRuleAction extends BlazeProjectAction implements DumbAware {
public NewBlazeRuleAction() {
super();
}
@Override
- public void actionPerformed(AnActionEvent event) {
- final Project project = event.getData(CommonDataKeys.PROJECT);
- if (project == null) {
- return;
- }
+ protected void actionPerformedInBlazeProject(Project project, AnActionEvent event) {
final VirtualFile virtualFile = event.getData(CommonDataKeys.VIRTUAL_FILE);
if (virtualFile == null) {
return;
@@ -68,15 +63,12 @@
}
@Override
- protected void doUpdate(@NotNull AnActionEvent event) {
+ protected void updateForBlazeProject(Project project, AnActionEvent event) {
Presentation presentation = event.getPresentation();
DataContext dataContext = event.getDataContext();
VirtualFile file = CommonDataKeys.VIRTUAL_FILE.getData(dataContext);
- Project project = CommonDataKeys.PROJECT.getData(dataContext);
boolean enabled =
- (project != null
- && file != null
- && Blaze.getBuildSystemProvider(project).isBuildFile(file.getName()));
+ file != null && Blaze.getBuildSystemProvider(project).isBuildFile(file.getName());
presentation.setVisible(enabled || ActionPlaces.isMainMenuOrActionSearch(event.getPlace()));
presentation.setEnabled(enabled);
presentation.setText(getText(project));
diff --git a/base/src/com/google/idea/blaze/base/ideinfo/TargetKey.java b/base/src/com/google/idea/blaze/base/ideinfo/TargetKey.java
index 0a347c6..d1e99a1 100644
--- a/base/src/com/google/idea/blaze/base/ideinfo/TargetKey.java
+++ b/base/src/com/google/idea/blaze/base/ideinfo/TargetKey.java
@@ -15,36 +15,42 @@
*/
package com.google.idea.blaze.base.ideinfo;
+import com.google.common.base.Joiner;
import com.google.common.base.Objects;
import com.google.common.collect.ComparisonChain;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Ordering;
import com.google.idea.blaze.base.model.primitives.Label;
import java.io.Serializable;
-import javax.annotation.Nullable;
+import java.util.List;
/** A key that uniquely idenfifies a target in the target map */
public class TargetKey implements Serializable, Comparable<TargetKey> {
- private static final long serialVersionUID = 2L;
+ private static final long serialVersionUID = 3L;
public final Label label;
- @Nullable private final String aspectId;
+ private final ImmutableList<String> aspectIds;
- private TargetKey(Label label, @Nullable String aspectId) {
+ private TargetKey(Label label, ImmutableList<String> aspectIds) {
this.label = label;
- this.aspectId = aspectId;
+ this.aspectIds = aspectIds;
}
/** Returns a key identifying a plain target */
public static TargetKey forPlainTarget(Label label) {
- return new TargetKey(label, null);
+ return new TargetKey(label, ImmutableList.of());
}
/** Returns a key identifying a general target */
- public static TargetKey forGeneralTarget(Label label, @Nullable String aspectId) {
- return new TargetKey(label, aspectId);
+ public static TargetKey forGeneralTarget(Label label, List<String> aspectIds) {
+ if (aspectIds.isEmpty()) {
+ return forPlainTarget(label);
+ }
+ return new TargetKey(label, ImmutableList.copyOf(aspectIds));
}
public boolean isPlainTarget() {
- return aspectId == null;
+ return aspectIds.isEmpty();
}
@Override
@@ -56,24 +62,27 @@
return false;
}
TargetKey key = (TargetKey) o;
- return Objects.equal(label, key.label) && Objects.equal(aspectId, key.aspectId);
+ return Objects.equal(label, key.label) && Objects.equal(aspectIds, key.aspectIds);
}
@Override
public int hashCode() {
- return Objects.hashCode(label, aspectId);
+ return Objects.hashCode(label, aspectIds);
}
@Override
public String toString() {
- if (aspectId == null) {
+ if (aspectIds.isEmpty()) {
return label.toString();
}
- return label.toString() + "#" + aspectId;
+ return label.toString() + "#" + Joiner.on('#').join(aspectIds);
}
@Override
public int compareTo(TargetKey o) {
- return ComparisonChain.start().compare(label, o.label).compare(aspectId, o.aspectId).result();
+ return ComparisonChain.start()
+ .compare(label, o.label)
+ .compare(aspectIds, o.aspectIds, Ordering.natural().lexicographical())
+ .result();
}
}
diff --git a/base/src/com/google/idea/blaze/base/lang/buildfile/language/semantics/BuildLanguageSpecProviderImpl.java b/base/src/com/google/idea/blaze/base/lang/buildfile/language/semantics/BuildLanguageSpecProviderImpl.java
index c57a138..f443957 100644
--- a/base/src/com/google/idea/blaze/base/lang/buildfile/language/semantics/BuildLanguageSpecProviderImpl.java
+++ b/base/src/com/google/idea/blaze/base/lang/buildfile/language/semantics/BuildLanguageSpecProviderImpl.java
@@ -15,41 +15,25 @@
*/
package com.google.idea.blaze.base.lang.buildfile.language.semantics;
-import com.google.common.collect.Maps;
import com.google.idea.blaze.base.lang.buildfile.sync.LanguageSpecResult;
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.blaze.base.sync.data.BlazeProjectDataManager;
import com.intellij.openapi.project.Project;
-import java.util.Map;
/** Calls 'blaze info build-language', to retrieve the language spec. */
-public class BuildLanguageSpecProviderImpl extends SyncListener.Adapter
- implements BuildLanguageSpecProvider {
-
- private static final Map<Project, LanguageSpecResult> calculatedSpecs = Maps.newHashMap();
+public class BuildLanguageSpecProviderImpl implements BuildLanguageSpecProvider {
@Override
public BuildLanguageSpec getLanguageSpec(Project project) {
- LanguageSpecResult result = calculatedSpecs.get(project);
- return result != null ? result.spec : null;
- }
-
- @Override
- public void onSyncComplete(
- Project project,
- BlazeContext context,
- BlazeImportSettings importSettings,
- ProjectViewSet projectViewSet,
- BlazeProjectData blazeProjectData,
- SyncMode syncMode,
- SyncResult syncResult) {
- LanguageSpecResult spec = blazeProjectData.syncState.get(LanguageSpecResult.class);
- if (spec != null) {
- calculatedSpecs.put(project, spec);
+ BlazeProjectData blazeProjectData =
+ BlazeProjectDataManager.getInstance(project).getBlazeProjectData();
+ if (blazeProjectData == null) {
+ return null;
}
+ LanguageSpecResult spec = blazeProjectData.syncState.get(LanguageSpecResult.class);
+ if (spec == null) {
+ return null;
+ }
+ return spec.spec;
}
}
diff --git a/base/src/com/google/idea/blaze/base/model/BlazeProjectData.java b/base/src/com/google/idea/blaze/base/model/BlazeProjectData.java
index 84f6936..4a401c8 100644
--- a/base/src/com/google/idea/blaze/base/model/BlazeProjectData.java
+++ b/base/src/com/google/idea/blaze/base/model/BlazeProjectData.java
@@ -22,51 +22,46 @@
import com.google.idea.blaze.base.sync.projectview.WorkspaceLanguageSettings;
import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoder;
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 java.io.Serializable;
-import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
/** The top-level object serialized to cache. */
@Immutable
public class BlazeProjectData implements Serializable {
- private static final long serialVersionUID = 25L;
+ private static final long serialVersionUID = 27L;
public final long syncTime;
public final TargetMap targetMap;
public final ImmutableMap<String, String> blazeInfo;
public final BlazeRoots blazeRoots;
- @Nullable public final WorkingSet workingSet;
+ public final BlazeVersionData blazeVersionData;
public final WorkspacePathResolver workspacePathResolver;
public final ArtifactLocationDecoder artifactLocationDecoder;
public final WorkspaceLanguageSettings workspaceLanguageSettings;
public final SyncState syncState;
public final ImmutableMultimap<TargetKey, TargetKey> reverseDependencies;
- @Nullable public final String vcsName;
public BlazeProjectData(
long syncTime,
TargetMap targetMap,
ImmutableMap<String, String> blazeInfo,
BlazeRoots blazeRoots,
- @Nullable WorkingSet workingSet,
+ BlazeVersionData blazeVersionData,
WorkspacePathResolver workspacePathResolver,
ArtifactLocationDecoder artifactLocationDecoder,
- WorkspaceLanguageSettings workspaceLangaugeSettings,
+ WorkspaceLanguageSettings workspaceLanguageSettings,
SyncState syncState,
- ImmutableMultimap<TargetKey, TargetKey> reverseDependencies,
- String vcsName) {
+ ImmutableMultimap<TargetKey, TargetKey> reverseDependencies) {
this.syncTime = syncTime;
this.targetMap = targetMap;
this.blazeInfo = blazeInfo;
this.blazeRoots = blazeRoots;
- this.workingSet = workingSet;
+ this.blazeVersionData = blazeVersionData;
this.workspacePathResolver = workspacePathResolver;
this.artifactLocationDecoder = artifactLocationDecoder;
- this.workspaceLanguageSettings = workspaceLangaugeSettings;
+ this.workspaceLanguageSettings = workspaceLanguageSettings;
this.syncState = syncState;
this.reverseDependencies = reverseDependencies;
- this.vcsName = vcsName;
}
}
diff --git a/base/src/com/google/idea/blaze/base/model/BlazeVersionData.java b/base/src/com/google/idea/blaze/base/model/BlazeVersionData.java
new file mode 100644
index 0000000..1cbdbff
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/model/BlazeVersionData.java
@@ -0,0 +1,98 @@
+/*
+ * 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.model;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableMap;
+import com.google.idea.blaze.base.bazel.BazelVersion;
+import com.google.idea.blaze.base.bazel.BuildSystemProvider;
+import com.google.idea.blaze.base.model.primitives.WorkspaceRoot;
+import com.google.idea.blaze.base.settings.Blaze.BuildSystem;
+import java.io.Serializable;
+import javax.annotation.Nullable;
+
+/**
+ * Version data about the user's blaze/bazel and other info needed for switching behaviour
+ * dynamically.
+ */
+public class BlazeVersionData implements Serializable {
+ private static final long serialVersionUID = 2L;
+
+ @Nullable private final Long blazeCl;
+ @Nullable private final Long clientCl;
+ @Nullable private final BazelVersion bazelVersion;
+
+ @VisibleForTesting
+ public BlazeVersionData() {
+ this(null, null, null);
+ }
+
+ private BlazeVersionData(
+ @Nullable Long blazeCl, @Nullable Long clientCl, @Nullable BazelVersion bazelVersion) {
+ this.blazeCl = blazeCl;
+ this.clientCl = clientCl;
+ this.bazelVersion = bazelVersion;
+ }
+
+ public boolean blazeContainsCl(long cl) {
+ return blazeCl != null && blazeCl >= cl;
+ }
+
+ public boolean blazeClientIsAtLeastCl(long cl) {
+ return clientCl != null && clientCl >= cl;
+ }
+
+ public boolean bazelIsAtLeastVersion(int major, int minor, int bugfix) {
+ return bazelVersion != null && bazelVersion.isAtLeast(major, minor, bugfix);
+ }
+
+ public static BlazeVersionData build(
+ BuildSystem buildSystem,
+ WorkspaceRoot workspaceRoot,
+ ImmutableMap<String, String> blazeInfo) {
+ Builder builder = new Builder();
+ for (BuildSystemProvider provider : BuildSystemProvider.EP_NAME.getExtensions()) {
+ provider.populateBlazeVersionData(buildSystem, workspaceRoot, blazeInfo, builder);
+ }
+ return builder.build();
+ }
+
+ /** Builder class for constructing the blaze version data */
+ public static class Builder {
+ public Long blazeCl;
+ public Long clientCl;
+ public BazelVersion bazelVersion;
+
+ public Builder setBlazeCl(Long blazeCl) {
+ this.blazeCl = blazeCl;
+ return this;
+ }
+
+ public Builder setClientCl(Long clientCl) {
+ this.clientCl = clientCl;
+ return this;
+ }
+
+ public Builder setBazelVersion(BazelVersion bazelVersion) {
+ this.bazelVersion = bazelVersion;
+ return this;
+ }
+
+ public BlazeVersionData build() {
+ return new BlazeVersionData(blazeCl, clientCl, bazelVersion);
+ }
+ }
+}
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 88d5199..206a73e 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
@@ -49,6 +49,13 @@
PY_TEST("py_test", LanguageClass.PYTHON),
PY_APPENGINE_BINARY("py_appengine_binary", LanguageClass.PYTHON),
PY_WRAP_CC("py_wrap_cc", LanguageClass.PYTHON),
+ GO_TEST("go_test", LanguageClass.GO),
+ GO_APPENGINE_TEST("go_appengine_test", LanguageClass.GO),
+ GO_BINARY("go_binary", LanguageClass.GO),
+ GO_APPENGINE_BINARY("go_appengine_binary", LanguageClass.GO),
+ GO_LIBRARY("go_library", LanguageClass.GO),
+ GO_APPENGINE_LIBRARY("go_appengine_library", LanguageClass.GO),
+ GO_WRAP_CC("go_wrap_cc", LanguageClass.GO),
;
static final ImmutableMap<String, Kind> STRING_TO_KIND = makeStringToKindMap();
diff --git a/base/src/com/google/idea/blaze/base/model/primitives/LanguageClass.java b/base/src/com/google/idea/blaze/base/model/primitives/LanguageClass.java
index 9addd48..cf030a2 100644
--- a/base/src/com/google/idea/blaze/base/model/primitives/LanguageClass.java
+++ b/base/src/com/google/idea/blaze/base/model/primitives/LanguageClass.java
@@ -28,6 +28,7 @@
JAVASCRIPT("javascript", ImmutableSet.of("js", "applejs")),
TYPESCRIPT("typescript", ImmutableSet.of("ts", "ats")),
DART("dart", ImmutableSet.of("dart")),
+ GO("go", ImmutableSet.of("go")),
PYTHON("python", ImmutableSet.of("py", "pyw"));
private static final ImmutableMap<String, LanguageClass> RECOGNIZED_EXTENSIONS =
diff --git a/base/src/com/google/idea/blaze/base/model/primitives/WorkspaceType.java b/base/src/com/google/idea/blaze/base/model/primitives/WorkspaceType.java
index 290118b..c88ad5a 100644
--- a/base/src/com/google/idea/blaze/base/model/primitives/WorkspaceType.java
+++ b/base/src/com/google/idea/blaze/base/model/primitives/WorkspaceType.java
@@ -15,6 +15,8 @@
*/
package com.google.idea.blaze.base.model.primitives;
+import com.google.common.collect.ImmutableSet;
+
/**
* Workspace types.
*
@@ -27,22 +29,23 @@
JAVA("java", LanguageClass.JAVA),
PYTHON("python", LanguageClass.PYTHON),
JAVASCRIPT("javascript", LanguageClass.JAVASCRIPT),
+ GO("go", LanguageClass.GO),
INTELLIJ_PLUGIN("intellij_plugin", LanguageClass.JAVA);
private final String name;
// the languages active by default for this WorkspaceType
- private final LanguageClass[] languages;
+ private final ImmutableSet<LanguageClass> languages;
WorkspaceType(String name, LanguageClass... languages) {
this.name = name;
- this.languages = languages;
+ this.languages = ImmutableSet.copyOf(languages);
}
public String getName() {
return name;
}
- public LanguageClass[] getLanguages() {
+ public ImmutableSet<LanguageClass> getLanguages() {
return languages;
}
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 553900d..0953d07 100644
--- a/base/src/com/google/idea/blaze/base/projectview/ProjectViewVerifier.java
+++ b/base/src/com/google/idea/blaze/base/projectview/ProjectViewVerifier.java
@@ -18,7 +18,6 @@
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.model.primitives.WorkspaceRoot;
import com.google.idea.blaze.base.projectview.section.ListSection;
import com.google.idea.blaze.base.projectview.section.sections.DirectoryEntry;
import com.google.idea.blaze.base.projectview.section.sections.DirectorySection;
@@ -27,7 +26,9 @@
import com.google.idea.blaze.base.scope.output.IssueOutput;
import com.google.idea.blaze.base.sync.BlazeSyncPlugin;
import com.google.idea.blaze.base.sync.projectview.WorkspaceLanguageSettings;
+import com.google.idea.blaze.base.sync.workspace.WorkspacePathResolver;
import com.intellij.openapi.util.io.FileUtil;
+import java.io.File;
import java.util.List;
/** Verifies project views. */
@@ -44,16 +45,7 @@
/** Verifies the project view. Any errors are output to the context as issues. */
public static boolean verifyProjectView(
BlazeContext context,
- WorkspaceRoot workspaceRoot,
- ProjectViewSet projectViewSet,
- WorkspaceLanguageSettings workspaceLanguageSettings) {
- return verifyProjectViewNoDisk(context, projectViewSet, workspaceLanguageSettings)
- && verifyIncludedPackagesExistOnDisk(context, workspaceRoot, projectViewSet);
- }
-
- /** Verifies the project view, without hitting disk. */
- public static boolean verifyProjectViewNoDisk(
- BlazeContext context,
+ WorkspacePathResolver workspacePathResolver,
ProjectViewSet projectViewSet,
WorkspaceLanguageSettings workspaceLanguageSettings) {
if (!verifyIncludedPackagesAreNotExcluded(context, projectViewSet)) {
@@ -69,6 +61,9 @@
.inFile(projectViewSet.getTopLevelProjectViewFile().projectViewFile)
.submit(context);
}
+ if (!verifyIncludedPackagesExistOnDisk(context, workspacePathResolver, projectViewSet)) {
+ return false;
+ }
return true;
}
@@ -119,7 +114,9 @@
}
private static boolean verifyIncludedPackagesExistOnDisk(
- BlazeContext context, WorkspaceRoot workspaceRoot, ProjectViewSet projectViewSet) {
+ BlazeContext context,
+ WorkspacePathResolver workspacePathResolver,
+ ProjectViewSet projectViewSet) {
boolean ok = true;
FileAttributeProvider fileAttributeProvider = FileAttributeProvider.getInstance();
@@ -135,12 +132,19 @@
continue;
}
WorkspacePath workspacePath = entry.directory;
- if (!fileAttributeProvider.exists(workspaceRoot.fileForPath(workspacePath))) {
+ File file = workspacePathResolver.resolveToFile(workspacePath);
+ if (!fileAttributeProvider.exists(file)) {
IssueOutput.error(
String.format(
- "Directory '%s' specified in import roots not found "
- + "under workspace root '%s'",
- workspacePath, workspaceRoot))
+ "Directory '%s' specified in project view not found.", workspacePath))
+ .inFile(projectViewFile.projectViewFile)
+ .withData(new MissingDirectoryIssueData(workspacePath))
+ .submit(context);
+ ok = false;
+ } else if (!fileAttributeProvider.isDirectory(file)) {
+ IssueOutput.error(
+ String.format(
+ "Directory '%s' specified in project view is a file.", workspacePath))
.inFile(projectViewFile.projectViewFile)
.withData(new MissingDirectoryIssueData(workspacePath))
.submit(context);
diff --git a/base/src/com/google/idea/blaze/base/projectview/section/sections/RunConfigurationsSection.java b/base/src/com/google/idea/blaze/base/projectview/section/sections/RunConfigurationsSection.java
new file mode 100644
index 0000000..9ffda84
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/projectview/section/sections/RunConfigurationsSection.java
@@ -0,0 +1,63 @@
+/*
+ * 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.common.collect.Lists;
+import com.google.idea.blaze.base.model.primitives.WorkspacePath;
+import com.google.idea.blaze.base.projectview.parser.ParseContext;
+import com.google.idea.blaze.base.projectview.parser.ProjectViewParser;
+import com.google.idea.blaze.base.projectview.section.ListSection;
+import com.google.idea.blaze.base.projectview.section.ListSectionParser;
+import com.google.idea.blaze.base.projectview.section.SectionKey;
+import com.google.idea.blaze.base.projectview.section.SectionParser;
+import com.google.idea.blaze.base.ui.BlazeValidationError;
+import java.util.List;
+import javax.annotation.Nullable;
+
+/** Allows users to import run configurations from XML files in their workspace. */
+public class RunConfigurationsSection {
+ public static final SectionKey<WorkspacePath, ListSection<WorkspacePath>> KEY =
+ SectionKey.of("import_run_configurations");
+ public static final SectionParser PARSER = new RunConfigurationsSectionParser();
+
+ private static class RunConfigurationsSectionParser extends ListSectionParser<WorkspacePath> {
+ private RunConfigurationsSectionParser() {
+ super(KEY);
+ }
+
+ @Nullable
+ @Override
+ protected WorkspacePath parseItem(ProjectViewParser parser, ParseContext parseContext) {
+ String text = parseContext.current().text;
+ List<BlazeValidationError> errors = Lists.newArrayList();
+ if (!WorkspacePath.validate(text, errors)) {
+ parseContext.addErrors(errors);
+ return null;
+ }
+ return new WorkspacePath(text);
+ }
+
+ @Override
+ protected void printItem(WorkspacePath item, StringBuilder sb) {
+ sb.append(item);
+ }
+
+ @Override
+ public ItemType getItemType() {
+ return ItemType.FileSystemItem;
+ }
+ }
+}
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 93b2fd9..ad0c35e 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,8 @@
ImportTargetOutputSection.PARSER,
ExcludeTargetSection.PARSER,
ExcludedSourceSection.PARSER,
- MetricsProjectSection.PARSER);
+ MetricsProjectSection.PARSER,
+ RunConfigurationsSection.PARSER);
public static List<SectionParser> getParsers() {
List<SectionParser> parsers = Lists.newArrayList(PARSERS);
diff --git a/base/src/com/google/idea/blaze/base/run/BlazeBeforeRunTaskProvider.java b/base/src/com/google/idea/blaze/base/run/BlazeBeforeRunTaskProvider.java
index 7eb51e3..9b5e0e1 100644
--- a/base/src/com/google/idea/blaze/base/run/BlazeBeforeRunTaskProvider.java
+++ b/base/src/com/google/idea/blaze/base/run/BlazeBeforeRunTaskProvider.java
@@ -20,6 +20,7 @@
import com.intellij.execution.BeforeRunTask;
import com.intellij.execution.BeforeRunTaskProvider;
import com.intellij.execution.configurations.RunConfiguration;
+import com.intellij.execution.configurations.WrappingRunConfiguration;
import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.util.Key;
@@ -91,6 +92,9 @@
@Override
public boolean canExecuteTask(RunConfiguration configuration, Task task) {
+ if (configuration instanceof WrappingRunConfiguration) {
+ configuration = ((WrappingRunConfiguration) configuration).getPeer();
+ }
return configuration instanceof BlazeCommandRunConfiguration;
}
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 d6200ee..07fd7e3 100644
--- a/base/src/com/google/idea/blaze/base/run/BlazeCommandRunConfiguration.java
+++ b/base/src/com/google/idea/blaze/base/run/BlazeCommandRunConfiguration.java
@@ -36,18 +36,21 @@
import com.intellij.execution.RunnerIconProvider;
import com.intellij.execution.configurations.ConfigurationFactory;
import com.intellij.execution.configurations.LocatableConfigurationBase;
+import com.intellij.execution.configurations.ModuleRunProfile;
import com.intellij.execution.configurations.RunConfiguration;
import com.intellij.execution.configurations.RunProfileState;
import com.intellij.execution.configurations.RuntimeConfigurationError;
import com.intellij.execution.configurations.RuntimeConfigurationException;
import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.module.Module;
import com.intellij.openapi.options.SettingsEditor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.InvalidDataException;
import com.intellij.openapi.util.WriteExternalException;
import com.intellij.ui.TextFieldWithAutoCompletion;
import com.intellij.ui.TextFieldWithAutoCompletion.StringsCompletionProvider;
+import com.intellij.ui.components.JBCheckBox;
import com.intellij.ui.components.JBLabel;
import com.intellij.util.ui.UIUtil;
import java.util.Collection;
@@ -63,13 +66,15 @@
/** A run configuration which executes Blaze commands. */
public class BlazeCommandRunConfiguration extends LocatableConfigurationBase
- implements BlazeRunConfiguration, RunnerIconProvider {
+ implements BlazeRunConfiguration, RunnerIconProvider, ModuleRunProfile {
private static final Logger LOG = Logger.getInstance(BlazeCommandRunConfiguration.class);
private static final String HANDLER_ATTR = "handler-id";
private static final String TARGET_TAG = "blaze-target";
private static final String KIND_ATTR = "kind";
+ private static final String KEEP_IN_SYNC_TAG = "keep-in-sync";
+
/**
* This tag is actually written by {@link com.intellij.execution.impl.RunManagerImpl}; it
* represents the before-run tasks of the configuration. We need to know about it to avoid writing
@@ -83,6 +88,10 @@
@Nullable private TargetExpression target;
// Null if the target is null, not a Label, or not a known rule.
@Nullable private Kind targetKind;
+
+ // for keeping imported configurations in sync with their source XML
+ @Nullable private Boolean keepInSync = null;
+
private BlazeCommandRunConfigurationHandlerProvider handlerProvider;
private BlazeCommandRunConfigurationHandler handler;
@@ -118,6 +127,17 @@
}
@Override
+ public void setKeepInSync(@Nullable Boolean keepInSync) {
+ this.keepInSync = keepInSync;
+ }
+
+ @Override
+ @Nullable
+ public Boolean getKeepInSync() {
+ return keepInSync;
+ }
+
+ @Override
@Nullable
public TargetExpression getTarget() {
return target;
@@ -125,6 +145,10 @@
public void setTarget(@Nullable TargetExpression target) {
this.target = target;
+ updateHandler();
+ }
+
+ private void updateHandler() {
targetKind = getKindForTarget();
BlazeCommandRunConfigurationHandlerProvider handlerProvider =
@@ -208,6 +232,9 @@
super.readExternal(element);
element = element.clone();
+ String keepInSyncString = element.getAttributeValue(KEEP_IN_SYNC_TAG);
+ keepInSync = keepInSyncString != null ? Boolean.parseBoolean(keepInSyncString) : null;
+
// Target is persisted as a tag to permit multiple targets in the future.
Element targetElement = element.getChild(TARGET_TAG);
if (targetElement != null && !Strings.isNullOrEmpty(targetElement.getTextTrim())) {
@@ -234,6 +261,7 @@
element.removeAttribute(KIND_ATTR);
element.removeAttribute(HANDLER_ATTR);
element.removeChildren(TARGET_TAG);
+ element.removeAttribute(KEEP_IN_SYNC_TAG);
// remove legacy attribute, if present
element.removeAttribute(TARGET_TAG);
@@ -253,7 +281,11 @@
}
element.addContent(targetElement);
}
+ if (keepInSync != null) {
+ element.setAttribute(KEEP_IN_SYNC_TAG, Boolean.toString(keepInSync));
+ }
element.setAttribute(HANDLER_ATTR, handlerProvider.getId());
+
handler.getState().writeExternal(elementState);
// copy our internal state to the provided Element, skipping items already present
@@ -283,6 +315,7 @@
configuration.elementState = elementState.clone();
configuration.target = target;
configuration.targetKind = targetKind;
+ configuration.keepInSync = keepInSync;
configuration.handlerProvider = handlerProvider;
configuration.handler = handlerProvider.createHandler(this);
try {
@@ -298,6 +331,11 @@
@Nullable
public RunProfileState getState(Executor executor, ExecutionEnvironment environment)
throws ExecutionException {
+ if (target != null) {
+ // We need to update the handler manually because it might otherwise be out of date (e.g.
+ // because the target map has changed since the last update).
+ updateHandler();
+ }
BlazeCommandRunConfigurationRunner runner = handler.createRunner(executor, environment);
if (runner != null) {
environment.putCopyableUserData(BlazeCommandRunConfigurationRunner.RUNNER_KEY, runner);
@@ -323,6 +361,11 @@
return new BlazeCommandRunConfigurationSettingsEditor(this);
}
+ @Override
+ public Module[] getModules() {
+ return new Module[0];
+ }
+
static class BlazeCommandRunConfigurationSettingsEditor
extends SettingsEditor<BlazeCommandRunConfiguration> {
@@ -332,7 +375,9 @@
private JComponent handlerStateComponent;
private Element elementState;
+ private final Box editorWithoutSyncCheckBox;
private final Box editor;
+ private final JBCheckBox keepInSyncCheckBox;
private final JBLabel targetExpressionLabel;
private final TextFieldWithAutoCompletion<String> targetField;
@@ -343,16 +388,35 @@
project, new TargetCompletionProvider(project), true, null);
elementState = config.elementState.clone();
targetExpressionLabel = new JBLabel(UIUtil.ComponentStyle.LARGE);
- editor = UiUtil.createBox(targetExpressionLabel, targetField);
- updateTargetExpressionLabel(config);
+ keepInSyncCheckBox = new JBCheckBox("Keep in sync with source XML");
+ editorWithoutSyncCheckBox = UiUtil.createBox(targetExpressionLabel, targetField);
+ editor = UiUtil.createBox(editorWithoutSyncCheckBox, keepInSyncCheckBox);
+ updateEditor(config);
updateHandlerEditor(config);
+ keepInSyncCheckBox.addItemListener(e -> updateEnabledStatus());
}
- private void updateTargetExpressionLabel(BlazeCommandRunConfiguration config) {
+ private void updateEditor(BlazeCommandRunConfiguration config) {
targetExpressionLabel.setText(
String.format(
"Target expression (%s handled by %s):",
config.getTargetKindName(), config.handler.getHandlerName()));
+ keepInSyncCheckBox.setVisible(config.keepInSync != null);
+ if (config.keepInSync != null) {
+ keepInSyncCheckBox.setSelected(config.keepInSync);
+ }
+ updateEnabledStatus();
+ }
+
+ private void updateEnabledStatus() {
+ setEnabled(!keepInSyncCheckBox.isVisible() || !keepInSyncCheckBox.isSelected());
+ }
+
+ private void setEnabled(boolean enabled) {
+ if (handlerStateEditor != null) {
+ handlerStateEditor.setComponentEnabled(enabled);
+ }
+ targetField.setEnabled(enabled);
}
private void updateHandlerEditor(BlazeCommandRunConfiguration config) {
@@ -366,10 +430,10 @@
handlerStateEditor = handler.getState().getEditor(config.getProject());
if (handlerStateComponent != null) {
- editor.remove(handlerStateComponent);
+ editorWithoutSyncCheckBox.remove(handlerStateComponent);
}
handlerStateComponent = handlerStateEditor.createComponent();
- editor.add(handlerStateComponent);
+ editorWithoutSyncCheckBox.add(handlerStateComponent);
}
@Override
@@ -380,7 +444,7 @@
@Override
protected void resetEditorFrom(BlazeCommandRunConfiguration config) {
elementState = config.elementState.clone();
- updateTargetExpressionLabel(config);
+ updateEditor(config);
if (config.handlerProvider != handlerProvider) {
updateHandlerEditor(config);
}
@@ -397,6 +461,7 @@
} catch (WriteExternalException e) {
LOG.error(e);
}
+ config.keepInSync = keepInSyncCheckBox.isVisible() ? keepInSyncCheckBox.isSelected() : null;
// now set the config's state, based on the editor's (possibly out of date) handler
config.updateHandlerIfDifferentProvider(handlerProvider);
@@ -411,7 +476,7 @@
String targetString = targetField.getText();
config.setTarget(
Strings.isNullOrEmpty(targetString) ? null : TargetExpression.fromString(targetString));
- updateTargetExpressionLabel(config);
+ updateEditor(config);
if (config.handlerProvider != handlerProvider) {
updateHandlerEditor(config);
handlerStateEditor.resetEditorFrom(config.handler.getState());
diff --git a/base/src/com/google/idea/blaze/base/run/BlazeRunConfiguration.java b/base/src/com/google/idea/blaze/base/run/BlazeRunConfiguration.java
index f3409af..5a4daed 100644
--- a/base/src/com/google/idea/blaze/base/run/BlazeRunConfiguration.java
+++ b/base/src/com/google/idea/blaze/base/run/BlazeRunConfiguration.java
@@ -22,4 +22,9 @@
public interface BlazeRunConfiguration {
@Nullable
TargetExpression getTarget();
+
+ /** Keep in sync with source XML */
+ void setKeepInSync(@Nullable Boolean keepInSync);
+
+ Boolean getKeepInSync();
}
diff --git a/base/src/com/google/idea/blaze/base/run/BlazeRunConfigurationSyncListener.java b/base/src/com/google/idea/blaze/base/run/BlazeRunConfigurationSyncListener.java
index 74a3812..a1e0549 100644
--- a/base/src/com/google/idea/blaze/base/run/BlazeRunConfigurationSyncListener.java
+++ b/base/src/com/google/idea/blaze/base/run/BlazeRunConfigurationSyncListener.java
@@ -20,20 +20,28 @@
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;
+import com.google.idea.blaze.base.projectview.section.sections.RunConfigurationsSection;
import com.google.idea.blaze.base.projectview.section.sections.TargetSection;
+import com.google.idea.blaze.base.run.exporter.RunConfigurationSerializer;
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.workspace.WorkspacePathResolver;
+import com.google.idea.sdkcompat.transactions.Transactions;
import com.intellij.execution.RunManager;
import com.intellij.execution.RunnerAndConfigurationSettings;
import com.intellij.execution.configurations.RunConfiguration;
import com.intellij.openapi.project.Project;
-import com.intellij.util.ui.UIUtil;
+import java.io.File;
import java.util.List;
import java.util.Set;
+import java.util.stream.Collectors;
-/** Creates run configurations for project view targets, where appropriate. */
+/**
+ * Imports run configurations specified in the project view, and creates run configurations for
+ * project view targets, where appropriate.
+ */
public class BlazeRunConfigurationSyncListener extends SyncListener.Adapter {
@Override
@@ -45,23 +53,40 @@
BlazeProjectData blazeProjectData,
SyncMode syncMode,
SyncResult syncResult) {
+ if (syncMode == SyncMode.STARTUP) {
+ return;
+ }
- UIUtil.invokeAndWaitIfNeeded(
- (Runnable)
- () -> {
- Set<Label> labelsWithConfigs = labelsWithConfigs(project);
- Set<TargetExpression> targetExpressions =
- Sets.newHashSet(projectViewSet.listItems(TargetSection.KEY));
- // We only auto-generate configurations for rules listed in the project view.
- for (TargetExpression target : targetExpressions) {
- if (!(target instanceof Label) || labelsWithConfigs.contains(target)) {
- continue;
- }
- Label label = (Label) target;
- labelsWithConfigs.add(label);
- maybeAddRunConfiguration(project, blazeProjectData, label);
- }
- });
+ Set<File> xmlFiles =
+ getImportedRunConfigurations(projectViewSet, blazeProjectData.workspacePathResolver);
+ Transactions.submitTransactionAndWait(
+ () -> {
+ // First, import from specified XML files. Then auto-generate from targets.
+ xmlFiles.forEach(
+ (file) -> RunConfigurationSerializer.loadFromXmlIgnoreExisting(project, file));
+
+ Set<Label> labelsWithConfigs = labelsWithConfigs(project);
+ Set<TargetExpression> targetExpressions =
+ Sets.newHashSet(projectViewSet.listItems(TargetSection.KEY));
+ // We only auto-generate configurations for rules listed in the project view.
+ for (TargetExpression target : targetExpressions) {
+ if (!(target instanceof Label) || labelsWithConfigs.contains(target)) {
+ continue;
+ }
+ Label label = (Label) target;
+ labelsWithConfigs.add(label);
+ maybeAddRunConfiguration(project, blazeProjectData, label);
+ }
+ });
+ }
+
+ private static Set<File> getImportedRunConfigurations(
+ ProjectViewSet projectViewSet, WorkspacePathResolver pathResolver) {
+ return projectViewSet
+ .listItems(RunConfigurationsSection.KEY)
+ .stream()
+ .map(pathResolver::resolveToFile)
+ .collect(Collectors.toSet());
}
/** Collects a set of all the Blaze labels that have an associated run configuration. */
diff --git a/base/src/com/google/idea/blaze/base/run/DistributedExecutorSupport.java b/base/src/com/google/idea/blaze/base/run/DistributedExecutorSupport.java
new file mode 100644
index 0000000..851eab6
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/run/DistributedExecutorSupport.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.base.run;
+
+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;
+import java.util.List;
+import javax.annotation.Nullable;
+
+/** Information about any distributed executor available to the build system. */
+public interface DistributedExecutorSupport {
+
+ ExtensionPointName<DistributedExecutorSupport> EP_NAME =
+ ExtensionPointName.create("com.google.idea.blaze.DistributedExecutorSupport");
+
+ /**
+ * Returns the name of an available distributed executor, if one exists for the given build
+ * system.
+ */
+ @Nullable
+ static DistributedExecutorSupport getAvailableExecutor(BuildSystem buildSystem) {
+ for (DistributedExecutorSupport executor : EP_NAME.getExtensions()) {
+ if (executor.isAvailable(buildSystem)) {
+ return executor;
+ }
+ }
+ return null;
+ }
+
+ /** 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();
+ }
+ DistributedExecutorSupport executorInfo = getAvailableExecutor(Blaze.getBuildSystem(project));
+ if (executorInfo == null) {
+ return ImmutableList.of();
+ }
+ return ImmutableList.of(executorInfo.getBlazeFlag(runDistributed));
+ }
+
+ String executorName();
+
+ boolean isAvailable(BuildSystem buildSystem);
+
+ /** Get blaze/bazel flag specifying whether to run on this distributed executor */
+ String getBlazeFlag(boolean runDistributed);
+}
diff --git a/base/src/com/google/idea/blaze/base/run/confighandler/BlazeCommandGenericRunConfigurationHandler.java b/base/src/com/google/idea/blaze/base/run/confighandler/BlazeCommandGenericRunConfigurationHandler.java
index 0080b3f..da3d641 100644
--- a/base/src/com/google/idea/blaze/base/run/confighandler/BlazeCommandGenericRunConfigurationHandler.java
+++ b/base/src/com/google/idea/blaze/base/run/confighandler/BlazeCommandGenericRunConfigurationHandler.java
@@ -20,6 +20,7 @@
import com.google.idea.blaze.base.run.BlazeConfigurationNameBuilder;
import com.google.idea.blaze.base.run.state.BlazeCommandRunConfigurationCommonState;
import com.google.idea.blaze.base.settings.Blaze;
+import com.google.idea.blaze.base.settings.Blaze.BuildSystem;
import com.intellij.execution.Executor;
import com.intellij.execution.configurations.RunConfiguration;
import com.intellij.execution.configurations.RuntimeConfigurationException;
@@ -38,8 +39,9 @@
private final BlazeCommandRunConfigurationCommonState state;
public BlazeCommandGenericRunConfigurationHandler(BlazeCommandRunConfiguration configuration) {
- this.buildSystemName = Blaze.buildSystemName(configuration.getProject());
- this.state = new BlazeCommandRunConfigurationCommonState(buildSystemName);
+ BuildSystem buildSystem = Blaze.getBuildSystem(configuration.getProject());
+ this.buildSystemName = buildSystem.getName();
+ this.state = new BlazeCommandRunConfigurationCommonState(buildSystem);
}
@Override
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 79a5147..d2c6512 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
@@ -18,6 +18,7 @@
import com.google.common.collect.ImmutableList;
import com.google.idea.blaze.base.async.process.LineProcessingOutputStream;
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.issueparser.IssueOutputLineProcessor;
import com.google.idea.blaze.base.metrics.Action;
@@ -25,8 +26,12 @@
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.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;
@@ -35,15 +40,22 @@
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.intellij.execution.DefaultExecutionResult;
import com.intellij.execution.ExecutionException;
+import com.intellij.execution.ExecutionResult;
import com.intellij.execution.Executor;
import com.intellij.execution.configurations.CommandLineState;
import com.intellij.execution.configurations.RunProfile;
import com.intellij.execution.configurations.RunProfileState;
+import com.intellij.execution.configurations.WrappingRunConfiguration;
+import com.intellij.execution.filters.TextConsoleBuilderImpl;
import com.intellij.execution.process.ProcessHandler;
import com.intellij.execution.process.ProcessListener;
import com.intellij.execution.runners.ExecutionEnvironment;
+import com.intellij.execution.runners.ProgramRunner;
+import com.intellij.execution.ui.ConsoleView;
import com.intellij.openapi.project.Project;
+import javax.annotation.Nullable;
import org.jetbrains.annotations.NotNull;
/**
@@ -55,7 +67,7 @@
@Override
public RunProfileState getRunProfileState(Executor executor, ExecutionEnvironment environment) {
- return new BlazeCommandRunProfileState(environment);
+ return new BlazeCommandRunProfileState(environment, null);
}
@Override
@@ -68,13 +80,31 @@
public static class BlazeCommandRunProfileState extends CommandLineState {
private final BlazeCommandRunConfiguration configuration;
private final BlazeCommandRunConfigurationCommonState handlerState;
+ @Nullable private final BlazeTestEventsHandlerProvider testEventsHandlerProvider;
- public BlazeCommandRunProfileState(ExecutionEnvironment environment) {
+ public BlazeCommandRunProfileState(
+ ExecutionEnvironment environment,
+ @Nullable BlazeTestEventsHandlerProvider testEventsHandlerProvider) {
super(environment);
- RunProfile runProfile = environment.getRunProfile();
- configuration = (BlazeCommandRunConfiguration) runProfile;
- handlerState =
+ this.configuration = getConfiguration(environment);
+ this.handlerState =
(BlazeCommandRunConfigurationCommonState) configuration.getHandler().getState();
+ this.testEventsHandlerProvider = testEventsHandlerProvider;
+ }
+
+ private static BlazeCommandRunConfiguration getConfiguration(ExecutionEnvironment environment) {
+ RunProfile runProfile = environment.getRunProfile();
+ if (runProfile instanceof WrappingRunConfiguration) {
+ runProfile = ((WrappingRunConfiguration) runProfile).getPeer();
+ }
+ return (BlazeCommandRunConfiguration) runProfile;
+ }
+
+ @Override
+ public ExecutionResult execute(Executor executor, ProgramRunner runner)
+ throws ExecutionException {
+ DefaultExecutionResult result = (DefaultExecutionResult) super.execute(executor, runner);
+ return SmRunnerUtils.attachRerunFailedTestsAction(result);
}
@Override
@@ -88,14 +118,24 @@
ProjectViewSet projectViewSet = ProjectViewManager.getInstance(project).getProjectViewSet();
assert projectViewSet != null;
- BlazeCommand blazeCommand =
- BlazeCommand.builder(Blaze.getBuildSystem(project), handlerState.getCommand())
- .setBlazeBinary(handlerState.getBlazeBinary())
- .addTargets(configuration.getTarget())
- .addBlazeFlags(BlazeFlags.buildFlags(project, projectViewSet))
- .addBlazeFlags(handlerState.getBlazeFlags())
- .addExeFlags(handlerState.getExeFlags())
- .build();
+ ImmutableList<String> testHandlerFlags = ImmutableList.of();
+ BlazeTestEventsHandler testEventsHandler =
+ canUseTestUi() && testEventsHandlerProvider != null
+ ? testEventsHandlerProvider.getHandler()
+ : null;
+ if (testEventsHandler != null) {
+ testHandlerFlags = testEventsHandler.getBlazeFlags();
+ setConsoleBuilder(
+ new TextConsoleBuilderImpl(project) {
+ @Override
+ protected ConsoleView createConsole() {
+ return SmRunnerUtils.getConsoleView(
+ project, configuration, getEnvironment().getExecutor(), testEventsHandler);
+ }
+ });
+ }
+
+ BlazeCommand blazeCommand = getBlazeCommand(project, testHandlerFlags);
WorkspaceRoot workspaceRoot = WorkspaceRoot.fromImportSettings(importSettings);
return new ScopedBlazeProcessHandler(
@@ -120,5 +160,28 @@
}
});
}
+
+ private BlazeCommand getBlazeCommand(Project project, ImmutableList<String> testHandlerFlags) {
+ 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();
+ }
+
+ private boolean canUseTestUi() {
+ return testEventsHandlerProvider != null
+ && BlazeCommandName.TEST.equals(handlerState.getCommand())
+ && !Boolean.TRUE.equals(handlerState.getRunOnDistributedExecutor());
+ }
}
}
diff --git a/base/src/com/google/idea/blaze/base/run/exporter/ExportRunConfigurationAction.java b/base/src/com/google/idea/blaze/base/run/exporter/ExportRunConfigurationAction.java
new file mode 100644
index 0000000..bc97225
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/run/exporter/ExportRunConfigurationAction.java
@@ -0,0 +1,46 @@
+/*
+ * 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.exporter;
+
+import com.google.idea.blaze.base.actions.BlazeProjectAction;
+import com.google.idea.blaze.base.run.BlazeRunConfiguration;
+import com.intellij.execution.RunManager;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.project.DumbAware;
+import com.intellij.openapi.project.Project;
+
+/**
+ * Export selected run configurations to file, so they can be checked in and shared between users.
+ */
+public class ExportRunConfigurationAction extends BlazeProjectAction implements DumbAware {
+
+ @Override
+ protected void updateForBlazeProject(Project project, AnActionEvent e) {
+ boolean hasBlazeConfigs =
+ RunManager.getInstance(project)
+ .getAllConfigurationsList()
+ .stream()
+ .anyMatch((config) -> config instanceof BlazeRunConfiguration);
+ if (!hasBlazeConfigs) {
+ e.getPresentation().setEnabled(false);
+ }
+ }
+
+ @Override
+ protected void actionPerformedInBlazeProject(Project project, AnActionEvent e) {
+ new ExportRunConfigurationDialog(project).show();
+ }
+}
diff --git a/base/src/com/google/idea/blaze/base/run/exporter/ExportRunConfigurationDialog.java b/base/src/com/google/idea/blaze/base/run/exporter/ExportRunConfigurationDialog.java
new file mode 100644
index 0000000..c4802c1
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/run/exporter/ExportRunConfigurationDialog.java
@@ -0,0 +1,270 @@
+/*
+ * 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.exporter;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
+import com.google.idea.blaze.base.io.FileAttributeProvider;
+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.projectview.ProjectViewSet.ProjectViewFile;
+import com.google.idea.blaze.base.run.BlazeRunConfiguration;
+import com.google.idea.blaze.base.settings.Blaze;
+import com.intellij.execution.RunManager;
+import com.intellij.execution.configurations.RunConfiguration;
+import com.intellij.icons.AllIcons;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.Presentation;
+import com.intellij.openapi.fileChooser.FileChooserDescriptor;
+import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
+import com.intellij.openapi.fileChooser.FileChooserDialog;
+import com.intellij.openapi.fileChooser.FileChooserFactory;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.openapi.ui.ValidationInfo;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.ui.AnActionButton;
+import com.intellij.ui.BooleanTableCellEditor;
+import com.intellij.ui.BooleanTableCellRenderer;
+import com.intellij.ui.ColoredTableCellRenderer;
+import com.intellij.ui.FieldPanel;
+import com.intellij.ui.GuiUtils;
+import com.intellij.ui.IdeBorderFactory;
+import com.intellij.ui.ToolbarDecorator;
+import com.intellij.ui.table.JBTable;
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.event.ActionListener;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+import javax.annotation.Nullable;
+import javax.swing.DefaultCellEditor;
+import javax.swing.JComponent;
+import javax.swing.JPanel;
+import javax.swing.JTable;
+import javax.swing.table.TableColumn;
+import org.jdom.output.Format;
+import org.jdom.output.XMLOutputter;
+
+/** UI for exporting blaze run configurations. */
+public class ExportRunConfigurationDialog extends DialogWrapper {
+
+ private final ImmutableList<RunConfiguration> blazeConfigurations;
+ private final ExportRunConfigurationTableModel tableModel;
+ private final JBTable table;
+ private final FieldPanel outputDirectoryPanel;
+
+ ExportRunConfigurationDialog(Project project) {
+ super(project, true);
+ blazeConfigurations =
+ ImmutableList.copyOf(
+ RunManager.getInstance(project)
+ .getAllConfigurationsList()
+ .stream()
+ .filter((config) -> config instanceof BlazeRunConfiguration)
+ .collect(Collectors.toList()));
+ tableModel = new ExportRunConfigurationTableModel(blazeConfigurations);
+ table = new JBTable(tableModel);
+
+ TableColumn booleanColumn = table.getColumnModel().getColumn(0);
+ booleanColumn.setCellRenderer(new BooleanTableCellRenderer());
+ booleanColumn.setCellEditor(new BooleanTableCellEditor());
+ int width = table.getFontMetrics(table.getFont()).stringWidth(table.getColumnName(0)) + 10;
+ booleanColumn.setPreferredWidth(width);
+ booleanColumn.setMinWidth(width);
+ booleanColumn.setMaxWidth(width);
+
+ table
+ .getColumnModel()
+ .getColumn(2)
+ .setCellEditor(new DefaultCellEditor(GuiUtils.createUndoableTextField()));
+
+ TableColumn nameColumn = table.getColumnModel().getColumn(1);
+ nameColumn.setCellRenderer(
+ new ColoredTableCellRenderer() {
+ @Override
+ protected void customizeCellRenderer(
+ JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int col) {
+ RunConfiguration config = blazeConfigurations.get(row);
+ setIcon(config.getType().getIcon());
+ append(config.getName());
+ }
+ });
+
+ table.setPreferredSize(new Dimension(700, 700));
+ table.setShowColumns(true);
+
+ final ActionListener browseAction = e -> chooseDirectory();
+ outputDirectoryPanel =
+ new FieldPanel("Export configurations to directory:", null, browseAction, null);
+ File defaultExportDirectory = defaultExportDirectory(project);
+ if (defaultExportDirectory != null) {
+ outputDirectoryPanel.setText(defaultExportDirectory.getPath());
+ }
+
+ String buildSystem = Blaze.buildSystemName(project);
+ setTitle(String.format("Export %s Run Configurations", buildSystem));
+ init();
+ }
+
+ /** Try to find a checked-in project view file. Otherwise, fall back to the workspace root. */
+ @Nullable
+ private static File defaultExportDirectory(Project project) {
+ WorkspaceRoot workspaceRoot = WorkspaceRoot.fromProjectSafe(project);
+ if (workspaceRoot == null) {
+ return null;
+ }
+ ProjectViewSet projectViewSet = ProjectViewManager.getInstance(project).getProjectViewSet();
+ if (projectViewSet != null) {
+ for (ProjectViewFile projectViewFile : projectViewSet.getProjectViewFiles()) {
+ File file = projectViewFile.projectViewFile;
+ if (file != null && FileUtil.isAncestor(workspaceRoot.directory(), file, false)) {
+ return file.getParentFile();
+ }
+ }
+ }
+ return workspaceRoot.directory();
+ }
+
+ private String getOutputDirectoryPath() {
+ return Strings.nullToEmpty(outputDirectoryPanel.getText()).trim();
+ }
+
+ private void chooseDirectory() {
+ FileChooserDescriptor descriptor =
+ FileChooserDescriptorFactory.createSingleFolderDescriptor()
+ .withTitle("Export Directory Location")
+ .withDescription("Choose directory to export run configurations to")
+ .withHideIgnored(false);
+ FileChooserDialog chooser =
+ FileChooserFactory.getInstance().createFileChooser(descriptor, null, null);
+
+ final VirtualFile[] files;
+ File existingLocation = new File(getOutputDirectoryPath());
+ if (existingLocation.exists()) {
+ VirtualFile toSelect =
+ LocalFileSystem.getInstance().refreshAndFindFileByPath(existingLocation.getPath());
+ files = chooser.choose(null, toSelect);
+ } else {
+ files = chooser.choose(null);
+ }
+ if (files.length == 0) {
+ return;
+ }
+ VirtualFile file = files[0];
+ outputDirectoryPanel.setText(file.getPath());
+ }
+
+ @Nullable
+ @Override
+ protected ValidationInfo doValidate() {
+ String outputDir = getOutputDirectoryPath();
+ if (outputDir.isEmpty()) {
+ return new ValidationInfo("Choose an output directory");
+ }
+ if (!FileAttributeProvider.getInstance().exists(new File(outputDir))) {
+ return new ValidationInfo("Invalid output directory");
+ }
+ Set<String> names = new HashSet<>();
+ for (int i = 0; i < blazeConfigurations.size(); i++) {
+ if (!tableModel.enabled[i]) {
+ continue;
+ }
+ if (!names.add(tableModel.paths[i])) {
+ return new ValidationInfo("Duplicate output file name '" + tableModel.paths[i] + "'");
+ }
+ }
+ return null;
+ }
+
+ @Override
+ protected void doOKAction() {
+ File outputDir = new File(getOutputDirectoryPath());
+ List<File> outputFiles = new ArrayList<>();
+ for (int i = 0; i < blazeConfigurations.size(); i++) {
+ if (!tableModel.enabled[i]) {
+ continue;
+ }
+ File outputFile = new File(outputDir, tableModel.paths[i]);
+ writeConfiguration(blazeConfigurations.get(i), outputFile);
+ outputFiles.add(outputFile);
+ }
+ LocalFileSystem.getInstance().refreshIoFiles(outputFiles);
+ super.doOKAction();
+ }
+
+ private static void writeConfiguration(RunConfiguration configuration, File outputFile) {
+ try (FileOutputStream writer = new FileOutputStream(outputFile, false)) {
+ XMLOutputter xmlOutputter = new XMLOutputter(Format.getCompactFormat());
+ xmlOutputter.output(RunConfigurationSerializer.writeToXml(configuration), writer);
+ } catch (IOException e) {
+ throw new RuntimeException("Error exporting run configuration to file: " + outputFile);
+ }
+ }
+
+ @Override
+ protected JComponent createNorthPanel() {
+ return outputDirectoryPanel;
+ }
+
+ @Override
+ protected JComponent createCenterPanel() {
+ JPanel panel = new JPanel(new BorderLayout());
+ panel.setBorder(IdeBorderFactory.createTitledBorder("Run Configurations", false));
+ panel.add(
+ ToolbarDecorator.createDecorator(table).addExtraAction(new SelectAllButton()).createPanel(),
+ BorderLayout.CENTER);
+ return panel;
+ }
+
+ private class SelectAllButton extends AnActionButton {
+
+ boolean allSelected = false;
+
+ private SelectAllButton() {
+ super("Select All", AllIcons.Actions.Selectall);
+ }
+
+ @Override
+ public synchronized void actionPerformed(AnActionEvent anActionEvent) {
+ boolean newState = !allSelected;
+ for (int i = 0; i < tableModel.enabled.length; i++) {
+ table.setValueAt(newState, i, 0);
+ }
+ allSelected = newState;
+ Presentation presentation = anActionEvent.getPresentation();
+ if (allSelected) {
+ presentation.setText("Deselect All");
+ presentation.setIcon(AllIcons.Actions.Unselectall);
+ } else {
+ presentation.setText("Select All");
+ presentation.setIcon(AllIcons.Actions.Selectall);
+ }
+ tableModel.fireTableDataChanged();
+ table.revalidate();
+ table.repaint();
+ }
+ }
+}
diff --git a/base/src/com/google/idea/blaze/base/run/exporter/ExportRunConfigurationTableModel.java b/base/src/com/google/idea/blaze/base/run/exporter/ExportRunConfigurationTableModel.java
new file mode 100644
index 0000000..cc00d52
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/run/exporter/ExportRunConfigurationTableModel.java
@@ -0,0 +1,107 @@
+/*
+ * 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.exporter;
+
+import com.google.common.collect.ImmutableList;
+import com.intellij.execution.configurations.RunConfiguration;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.util.text.UniqueNameGenerator;
+import java.util.List;
+import javax.swing.table.AbstractTableModel;
+
+/** Table model used by the 'export run configurations' UI. */
+class ExportRunConfigurationTableModel extends AbstractTableModel {
+
+ private static final ImmutableList<String> COLUMN_NAMES =
+ ImmutableList.of("Export", "Name", "Output filename");
+ private static final ImmutableList<Class<?>> COLUMN_CLASSES =
+ ImmutableList.of(Boolean.class, String.class, String.class);
+
+ final Boolean[] enabled;
+ final String[] names;
+ final String[] paths;
+
+ ExportRunConfigurationTableModel(List<RunConfiguration> configurations) {
+ enabled = new Boolean[configurations.size()];
+ names = new String[configurations.size()];
+ paths = new String[configurations.size()];
+
+ UniqueNameGenerator nameGenerator = new UniqueNameGenerator();
+ for (int i = 0; i < configurations.size(); i++) {
+ RunConfiguration config = configurations.get(i);
+ enabled[i] = false;
+ names[i] = config.getName();
+ paths[i] =
+ nameGenerator.generateUniqueName(FileUtil.sanitizeFileName(config.getName()), "", ".xml");
+ }
+ }
+
+ @Override
+ public int getColumnCount() {
+ return 3;
+ }
+
+ @Override
+ public Class<?> getColumnClass(int columnIndex) {
+ return COLUMN_CLASSES.get(columnIndex);
+ }
+
+ @Override
+ public String getColumnName(int column) {
+ return COLUMN_NAMES.get(column);
+ }
+
+ @Override
+ public int getRowCount() {
+ return enabled.length;
+ }
+
+ @Override
+ public boolean isCellEditable(int rowIndex, int columnIndex) {
+ return columnIndex != 1;
+ }
+
+ @Override
+ public Object getValueAt(int rowIndex, int columnIndex) {
+ switch (columnIndex) {
+ case 0:
+ return enabled[rowIndex];
+ case 1:
+ return names[rowIndex];
+ case 2:
+ return paths[rowIndex];
+ default:
+ throw new RuntimeException("Invalid column index: " + columnIndex);
+ }
+ }
+
+ @Override
+ public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
+ switch (columnIndex) {
+ case 0:
+ enabled[rowIndex] = (Boolean) aValue;
+ return;
+ case 1:
+ names[rowIndex] = (String) aValue;
+ return;
+ case 2:
+ paths[rowIndex] = (String) aValue;
+ return;
+ default:
+ throw new RuntimeException("Invalid column index: " + columnIndex);
+ }
+ }
+}
diff --git a/base/src/com/google/idea/blaze/base/run/exporter/RunConfigurationSerializer.java b/base/src/com/google/idea/blaze/base/run/exporter/RunConfigurationSerializer.java
new file mode 100644
index 0000000..aed9db4
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/run/exporter/RunConfigurationSerializer.java
@@ -0,0 +1,125 @@
+/*
+ * 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.exporter;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.idea.blaze.base.run.BlazeRunConfiguration;
+import com.intellij.execution.RunnerAndConfigurationSettings;
+import com.intellij.execution.configurations.RunConfiguration;
+import com.intellij.execution.impl.RunManagerImpl;
+import com.intellij.execution.impl.RunnerAndConfigurationSettingsImpl;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.InvalidDataException;
+import com.intellij.openapi.util.JDOMUtil;
+import com.intellij.openapi.util.WriteExternalException;
+import java.io.File;
+import java.io.IOException;
+import javax.annotation.Nullable;
+import org.jdom.Element;
+import org.jdom.JDOMException;
+
+/** Utility methods for converting run configuration to/from XML. */
+public class RunConfigurationSerializer {
+
+ private static final Logger logger = Logger.getInstance(RunConfigurationSerializer.class);
+
+ public static Element writeToXml(RunConfiguration configuration) {
+ RunnerAndConfigurationSettings settings =
+ RunManagerImpl.getInstanceImpl(configuration.getProject()).getSettings(configuration);
+ Element element = new Element("configuration");
+ try {
+ ((RunnerAndConfigurationSettingsImpl) settings).writeExternal(element);
+ } catch (WriteExternalException e) {
+ logger.warn("Error serializing run configuration to XML", e);
+ }
+ return element;
+ }
+
+ /**
+ * Parses a RunConfiguration from the given XML file, and adds it to the project, if there's not
+ * already a run configuration with the same name and type,
+ */
+ public static void loadFromXmlIgnoreExisting(Project project, File xmlFile) {
+ try {
+ loadFromXmlElementIgnoreExisting(project, JDOMUtil.load(xmlFile));
+ } catch (InvalidDataException | JDOMException | IOException e) {
+ logger.warn("Error parsing run configuration from XML", e);
+ }
+ }
+
+ /**
+ * Parses a RunConfiguration from the given XML element, and adds it to the project, if there's
+ * not already a run configuration with the same name and type,
+ */
+ @VisibleForTesting
+ static void loadFromXmlElementIgnoreExisting(Project project, Element element)
+ throws InvalidDataException {
+ if (!shouldLoadConfiguration(project, element)) {
+ return;
+ }
+ RunnerAndConfigurationSettings settings =
+ RunManagerImpl.getInstanceImpl(project).loadConfiguration(element, false);
+ RunConfiguration config = settings != null ? settings.getConfiguration() : null;
+ if (config instanceof BlazeRunConfiguration) {
+ ((BlazeRunConfiguration) config).setKeepInSync(true);
+ }
+ }
+
+ /**
+ * Deserializes the configuration represented by the given XML element, then searches for an
+ * existing run configuration in the project with the same name and type.
+ */
+ @Nullable
+ @VisibleForTesting
+ static RunnerAndConfigurationSettings findExisting(Project project, Element element)
+ throws InvalidDataException {
+ RunManagerImpl manager = RunManagerImpl.getInstanceImpl(project);
+ RunnerAndConfigurationSettingsImpl settings = new RunnerAndConfigurationSettingsImpl(manager);
+ settings.readExternal(element);
+ RunConfiguration config = settings.getConfiguration();
+ if (config == null) {
+ return null;
+ }
+ return manager.findConfigurationByTypeAndName(config.getType().getId(), config.getName());
+ }
+
+ /**
+ * Returns true if there's either no matching configuration altready in the project, or the
+ * matching configuration is marked as 'keep in sync'.
+ */
+ @VisibleForTesting
+ static boolean shouldLoadConfiguration(Project project, Element element)
+ throws InvalidDataException {
+ RunnerAndConfigurationSettings existing = findExisting(project, element);
+ if (existing == null) {
+ return true;
+ }
+ RunConfiguration config = existing.getConfiguration();
+ if (!(config instanceof BlazeRunConfiguration)) {
+ return false;
+ }
+ BlazeRunConfiguration blazeConfig = (BlazeRunConfiguration) config;
+ Boolean keepInSync = blazeConfig.getKeepInSync();
+ if (keepInSync == null) {
+ // if the matching configuration was never previously imported, don't overwrite, but activate
+ // the UI option to keep it in sync.
+ blazeConfig.setKeepInSync(false);
+ return false;
+ }
+ return keepInSync;
+ }
+}
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
new file mode 100644
index 0000000..bc90989
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/run/smrunner/BlazeRerunFailedTestsAction.java
@@ -0,0 +1,100 @@
+/*
+ * 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.smrunner;
+
+import com.google.idea.blaze.base.command.BlazeCommandName;
+import com.google.idea.blaze.base.command.BlazeFlags;
+import com.google.idea.blaze.base.run.BlazeCommandRunConfiguration;
+import com.google.idea.blaze.base.run.state.BlazeCommandRunConfigurationCommonState;
+import com.intellij.execution.ExecutionException;
+import com.intellij.execution.Executor;
+import com.intellij.execution.configurations.RunProfileState;
+import com.intellij.execution.runners.ExecutionEnvironment;
+import com.intellij.execution.testframework.TestFrameworkRunningModel;
+import com.intellij.execution.testframework.actions.AbstractRerunFailedTestsAction;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.ui.ComponentContainer;
+import java.util.ArrayList;
+import java.util.List;
+import javax.annotation.Nullable;
+
+/** Re-run failed tests. */
+public class BlazeRerunFailedTestsAction extends AbstractRerunFailedTestsAction {
+
+ private final BlazeTestEventsHandler eventsHandler;
+
+ public BlazeRerunFailedTestsAction(
+ BlazeTestEventsHandler eventsHandler, ComponentContainer componentContainer) {
+ super(componentContainer);
+ this.eventsHandler = eventsHandler;
+ }
+
+ @Override
+ @Nullable
+ protected MyRunProfile getRunProfile(ExecutionEnvironment environment) {
+ final TestFrameworkRunningModel model = getModel();
+ if (model == null) {
+ return null;
+ }
+ BlazeCommandRunConfiguration config =
+ (BlazeCommandRunConfiguration) model.getProperties().getConfiguration();
+ return new BlazeRerunTestRunProfile(config.clone());
+ }
+
+ class BlazeRerunTestRunProfile extends MyRunProfile {
+
+ private final BlazeCommandRunConfiguration configuration;
+
+ BlazeRerunTestRunProfile(BlazeCommandRunConfiguration configuration) {
+ super(configuration);
+ this.configuration = configuration;
+ }
+
+ @Override
+ public Module[] getModules() {
+ return Module.EMPTY_ARRAY;
+ }
+
+ @Nullable
+ @Override
+ public RunProfileState getState(Executor executor, ExecutionEnvironment environment)
+ throws ExecutionException {
+ BlazeCommandRunConfigurationCommonState handlerState =
+ configuration.getHandlerStateIfType(BlazeCommandRunConfigurationCommonState.class);
+ if (handlerState == null || !BlazeCommandName.TEST.equals(handlerState.getCommand())) {
+ return null;
+ }
+ String testFilter = eventsHandler.getTestFilter(getProject(), getFailedTests(getProject()));
+ List<String> blazeFlags = setTestFilter(handlerState.getBlazeFlags(), testFilter);
+ handlerState.setBlazeFlags(blazeFlags);
+ return configuration.getState(executor, environment);
+ }
+
+ /** 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);
+ for (int i = 0; i < copy.size(); i++) {
+ String flag = copy.get(i);
+ if (flag.startsWith(BlazeFlags.TEST_FILTER)) {
+ copy.set(i, testFilter);
+ return copy;
+ }
+ }
+ copy.add(testFilter);
+ return copy;
+ }
+ }
+}
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
new file mode 100644
index 0000000..6582c23
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/run/smrunner/BlazeTestConsoleProperties.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.run.smrunner;
+
+import com.intellij.execution.Executor;
+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.SMCustomMessagesParsing;
+import com.intellij.execution.testframework.sm.runner.OutputToGeneralTestEventsConverter;
+import com.intellij.execution.testframework.sm.runner.SMTRunnerConsoleProperties;
+import com.intellij.execution.testframework.sm.runner.SMTestLocator;
+import com.intellij.execution.ui.ConsoleView;
+import javax.annotation.Nullable;
+
+/** Integrates blaze test results with the SM-runner test UI. */
+public class BlazeTestConsoleProperties extends SMTRunnerConsoleProperties
+ implements SMCustomMessagesParsing {
+
+ private final BlazeTestEventsHandler eventsHandler;
+
+ public BlazeTestConsoleProperties(
+ RunConfiguration runConfiguration, Executor executor, BlazeTestEventsHandler eventsHandler) {
+ super(runConfiguration, eventsHandler.frameworkName, executor);
+ this.eventsHandler = eventsHandler;
+ }
+
+ @Override
+ public OutputToGeneralTestEventsConverter createTestEventsConverter(
+ String framework, TestConsoleProperties consoleProperties) {
+ return new BlazeXmlToTestEventsConverter(framework, consoleProperties, eventsHandler);
+ }
+
+ @Override
+ public SMTestLocator getTestLocator() {
+ return eventsHandler.getTestLocator();
+ }
+
+ @Nullable
+ @Override
+ public AbstractRerunFailedTestsAction createRerunFailedTestsAction(ConsoleView consoleView) {
+ return eventsHandler.createRerunFailedTestsAction(consoleView);
+ }
+}
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
new file mode 100644
index 0000000..87e1b7f
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/run/smrunner/BlazeTestEventsHandler.java
@@ -0,0 +1,103 @@
+/*
+ * 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.smrunner;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
+import com.intellij.execution.testframework.AbstractTestProxy;
+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 com.intellij.util.io.URLUtil;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+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();
+ }
+
+ /**
+ * Blaze/Bazel flags required for test UI.<br>
+ * Forces local test execution, without sharding, and sets the output test xml path.
+ */
+ 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 abstract SMTestLocator getTestLocator();
+
+ /**
+ * The --test_filter flag passed to blaze to rerun the given tests.
+ *
+ * @return null if no filter can be constructed for these tests.
+ */
+ @Nullable
+ public abstract String getTestFilter(Project project, List<AbstractTestProxy> failedTests);
+
+ @Nullable
+ public AbstractRerunFailedTestsAction createRerunFailedTestsAction(ConsoleView consoleView) {
+ return new BlazeRerunFailedTestsAction(this, consoleView);
+ }
+
+ /** Converts the testsuite name in the blaze test XML to a user-friendly format */
+ public String suiteDisplayName(String rawName) {
+ return rawName;
+ }
+
+ /** Converts the testcase name in the blaze test XML to a user-friendly format */
+ public String testDisplayName(String rawName) {
+ return rawName;
+ }
+
+ public String suiteLocationUrl(String name) {
+ return SmRunnerUtils.GENERIC_SUITE_PROTOCOL + URLUtil.SCHEME_SEPARATOR + name;
+ }
+
+ public String testLocationUrl(String name, @Nullable String className) {
+ String base = SmRunnerUtils.GENERIC_TEST_PROTOCOL + URLUtil.SCHEME_SEPARATOR + name;
+ if (Strings.isNullOrEmpty(className)) {
+ return base;
+ }
+ 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);
+ }
+ }
+}
diff --git a/aswb/src/com/google/idea/blaze/android/run/binary/instantrun/InstantRunExperiment.java b/base/src/com/google/idea/blaze/base/run/smrunner/BlazeTestEventsHandlerProvider.java
similarity index 61%
copy from aswb/src/com/google/idea/blaze/android/run/binary/instantrun/InstantRunExperiment.java
copy to base/src/com/google/idea/blaze/base/run/smrunner/BlazeTestEventsHandlerProvider.java
index 148b1d9..c02a0cb 100644
--- a/aswb/src/com/google/idea/blaze/android/run/binary/instantrun/InstantRunExperiment.java
+++ b/base/src/com/google/idea/blaze/base/run/smrunner/BlazeTestEventsHandlerProvider.java
@@ -5,7 +5,7 @@
* 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
+ * 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,
@@ -13,12 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.google.idea.blaze.android.run.binary.instantrun;
+package com.google.idea.blaze.base.run.smrunner;
-import com.google.idea.common.experiments.BoolExperiment;
+/** Provides a {@link BlazeTestEventsHandler}. */
+public interface BlazeTestEventsHandlerProvider {
-/** Holds the instant run experiment */
-public class InstantRunExperiment {
- public static final BoolExperiment INSTANT_RUN_ENABLED =
- new BoolExperiment("instant.run.enabled", false);
+ BlazeTestEventsHandler getHandler();
}
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
new file mode 100644
index 0000000..8f6d1f0
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/run/smrunner/BlazeXmlSchema.java
@@ -0,0 +1,110 @@
+/*
+ * 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.smrunner;
+
+import com.google.common.collect.Lists;
+import java.io.InputStream;
+import java.util.List;
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlValue;
+
+/** Used to parse the test.xml generated by the blaze/bazel testing framework. */
+public class BlazeXmlSchema {
+
+ private static final JAXBContext CONTEXT;
+
+ static {
+ try {
+ CONTEXT = JAXBContext.newInstance(TestSuite.class);
+ } catch (JAXBException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ static TestSuite parse(InputStream input) {
+ try {
+ return (TestSuite) CONTEXT.createUnmarshaller().unmarshal(input);
+ } catch (JAXBException e) {
+ throw new RuntimeException("Failed to parse test XML", e);
+ }
+ }
+
+ @XmlRootElement(name = "testsuites")
+ static class TestSuite {
+ @XmlAttribute String name;
+ @XmlAttribute String classname;
+ @XmlAttribute int tests;
+ @XmlAttribute int failures;
+ @XmlAttribute int errors;
+ @XmlAttribute int skipped;
+ @XmlAttribute int disabled;
+ @XmlAttribute double time;
+
+ @XmlAttribute(name = "system-out")
+ String sysOut;
+
+ @XmlAttribute(name = "system-err")
+ String sysErr;
+
+ @XmlElement(name = "error", type = ErrorOrFailureOrSkipped.class)
+ ErrorOrFailureOrSkipped error;
+
+ @XmlElement(name = "failure", type = ErrorOrFailureOrSkipped.class)
+ ErrorOrFailureOrSkipped failure;
+
+ @XmlElement(name = "testsuite")
+ List<TestSuite> testSuites = Lists.newArrayList();
+
+ @XmlElement(name = "testdecorator")
+ List<TestSuite> testDecorators = Lists.newArrayList();
+
+ @XmlElement(name = "testcase")
+ List<TestCase> testCases = Lists.newArrayList();
+ }
+
+ static class TestCase {
+ @XmlAttribute String name;
+ @XmlAttribute String classname;
+ @XmlAttribute String status;
+ @XmlAttribute String result;
+ @XmlAttribute String time;
+
+ @XmlAttribute(name = "system-out")
+ String sysOut;
+
+ @XmlAttribute(name = "system-err")
+ String sysErr;
+
+ @XmlElement(name = "error", type = ErrorOrFailureOrSkipped.class)
+ ErrorOrFailureOrSkipped error;
+
+ @XmlElement(name = "failure", type = ErrorOrFailureOrSkipped.class)
+ ErrorOrFailureOrSkipped failure;
+
+ @XmlElement(name = "skipped", type = ErrorOrFailureOrSkipped.class)
+ ErrorOrFailureOrSkipped skipped;
+ }
+
+ static class ErrorOrFailureOrSkipped {
+ @XmlValue String content;
+ @XmlAttribute String message;
+ @XmlAttribute String type;
+ }
+}
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
new file mode 100644
index 0000000..82dcae8
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/run/smrunner/BlazeXmlToTestEventsConverter.java
@@ -0,0 +1,207 @@
+/*
+ * 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.smrunner;
+
+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.sdkcompat.smrunner.SmRunnerCompatUtils;
+import com.intellij.execution.process.ProcessOutputTypes;
+import com.intellij.execution.testframework.TestConsoleProperties;
+import com.intellij.execution.testframework.sm.runner.GeneralTestEventsProcessor;
+import com.intellij.execution.testframework.sm.runner.OutputToGeneralTestEventsConverter;
+import com.intellij.execution.testframework.sm.runner.events.TestFinishedEvent;
+import com.intellij.execution.testframework.sm.runner.events.TestIgnoredEvent;
+import com.intellij.execution.testframework.sm.runner.events.TestOutputEvent;
+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.util.Key;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.text.ParseException;
+import javax.annotation.Nullable;
+import jetbrains.buildServer.messages.serviceMessages.ServiceMessageVisitor;
+import jetbrains.buildServer.messages.serviceMessages.TestSuiteStarted;
+
+/** Converts blaze test runner xml logs to smRunner events. */
+public class BlazeXmlToTestEventsConverter extends OutputToGeneralTestEventsConverter {
+
+ private static final ErrorOrFailureOrSkipped NO_ERROR = new ErrorOrFailureOrSkipped();
+
+ private final BlazeTestEventsHandler eventsHandler;
+
+ public BlazeXmlToTestEventsConverter(
+ String testFrameworkName,
+ TestConsoleProperties testConsoleProperties,
+ BlazeTestEventsHandler eventsHandler) {
+ super(testFrameworkName, testConsoleProperties);
+ this.eventsHandler = eventsHandler;
+ }
+
+ @Override
+ protected boolean processServiceMessages(
+ String s, Key key, ServiceMessageVisitor serviceMessageVisitor) throws ParseException {
+ return super.processServiceMessages(s, key, serviceMessageVisitor);
+ }
+
+ @Override
+ public void process(String text, Key outputType) {
+ super.process(text, outputType);
+ }
+
+ @Override
+ public void dispose() {
+ super.dispose();
+ }
+
+ @Override
+ 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.
+ }
+ }
+
+ private void parseXmlInput(GeneralTestEventsProcessor processor, InputStream input) {
+ TestSuite testResult = BlazeXmlSchema.parse(input);
+ processor.onTestsReporterAttached();
+ processTestSuite(processor, testResult);
+ }
+
+ private void processTestSuite(GeneralTestEventsProcessor processor, TestSuite suite) {
+ if (!hasRunChild(suite)) {
+ return;
+ }
+ // don't include the outermost 'testsuites' element.
+ boolean logSuite = suite.testSuites.isEmpty();
+ if (suite.name != null && logSuite) {
+ TestSuiteStarted suiteStarted =
+ new TestSuiteStarted(eventsHandler.suiteDisplayName(suite.name));
+ String locationUrl = eventsHandler.suiteLocationUrl(suite.name);
+ processor.onSuiteStarted(new TestSuiteStartedEvent(suiteStarted, locationUrl));
+ }
+
+ for (TestSuite child : suite.testSuites) {
+ processTestSuite(processor, child);
+ }
+ for (TestSuite decorator : suite.testDecorators) {
+ processTestSuite(processor, decorator);
+ }
+ for (TestCase test : suite.testCases) {
+ processTestCase(processor, test);
+ }
+
+ if (suite.sysOut != null) {
+ processor.onUncapturedOutput(suite.sysOut, ProcessOutputTypes.STDOUT);
+ }
+ if (suite.sysErr != null) {
+ processor.onUncapturedOutput(suite.sysErr, ProcessOutputTypes.STDERR);
+ }
+
+ if (suite.name != null && logSuite) {
+ processor.onSuiteFinished(
+ new TestSuiteFinishedEvent(eventsHandler.suiteDisplayName(suite.name)));
+ }
+ }
+
+ /**
+ * Does the test suite have at least one child which wasn't skipped? <br>
+ * This prevents spurious warnings from entirely filtered test classes.
+ */
+ private static boolean hasRunChild(TestSuite suite) {
+ for (TestSuite child : suite.testSuites) {
+ if (hasRunChild(child)) {
+ return true;
+ }
+ }
+ for (TestSuite child : suite.testDecorators) {
+ if (hasRunChild(child)) {
+ return true;
+ }
+ }
+ for (TestCase test : suite.testCases) {
+ if ("run".equals(test.status)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static boolean isCancelled(TestCase test) {
+ return "interrupted".equalsIgnoreCase(test.result) || "cancelled".equalsIgnoreCase(test.result);
+ }
+
+ private static boolean isIgnored(TestCase test) {
+ if (test.skipped != null) {
+ return true;
+ }
+ return "suppressed".equalsIgnoreCase(test.result)
+ || "skipped".equalsIgnoreCase(test.result)
+ || "filtered".equalsIgnoreCase(test.result);
+ }
+
+ private static boolean isFailed(TestCase test) {
+ return test.failure != null || test.error != null;
+ }
+
+ private void processTestCase(GeneralTestEventsProcessor processor, TestCase test) {
+ if (test.name == null || "notrun".equals(test.status) || isCancelled(test)) {
+ return;
+ }
+ String displayName = eventsHandler.testDisplayName(test.name);
+ String locationUrl = eventsHandler.testLocationUrl(test.name, test.classname);
+ processor.onTestStarted(new TestStartedEvent(displayName, locationUrl));
+
+ if (test.sysOut != null) {
+ processor.onTestOutput(new TestOutputEvent(displayName, test.sysOut, true));
+ }
+ if (test.sysErr != null) {
+ processor.onTestOutput(new TestOutputEvent(displayName, test.sysErr, true));
+ }
+
+ if (isIgnored(test)) {
+ ErrorOrFailureOrSkipped err = test.skipped != null ? test.skipped : NO_ERROR;
+ processor.onTestIgnored(new TestIgnoredEvent(displayName, err.message, err.content));
+ } else if (isFailed(test)) {
+ ErrorOrFailureOrSkipped err =
+ test.failure != null ? test.failure : test.error != null ? test.error : NO_ERROR;
+ processor.onTestFailure(
+ SmRunnerCompatUtils.getTestFailedEvent(
+ displayName, err.message, err.content, parseTimeMillis(test.time)));
+ }
+ processor.onTestFinished(new TestFinishedEvent(displayName, parseTimeMillis(test.time)));
+ }
+
+ private static long parseTimeMillis(@Nullable String time) {
+ if (time == null) {
+ return -1;
+ }
+ // if the number contains a decimal point, it's a value in seconds. Otherwise in milliseconds.
+ try {
+ if (time.contains(".")) {
+ return Math.round(Float.parseFloat(time) * 1000);
+ }
+ return Long.parseLong(time);
+ } catch (NumberFormatException e) {
+ return -1;
+ }
+ }
+}
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
new file mode 100644
index 0000000..04715a7
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/run/smrunner/SmRunnerUtils.java
@@ -0,0 +1,76 @@
+/*
+ * 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.smrunner;
+
+import com.intellij.execution.DefaultExecutionResult;
+import com.intellij.execution.Executor;
+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.ui.SMTRunnerConsoleView;
+import com.intellij.execution.ui.ExecutionConsole;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Disposer;
+import javax.swing.tree.TreeSelectionModel;
+
+/** Utility methods for setting up the SM runner test UI. */
+public class SmRunnerUtils {
+
+ 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 SMTRunnerConsoleView getConsoleView(
+ Project project,
+ RunConfiguration configuration,
+ Executor executor,
+ BlazeTestEventsHandler eventsHandler) {
+ SMTRunnerConsoleProperties properties =
+ new BlazeTestConsoleProperties(configuration, executor, eventsHandler);
+ SMTRunnerConsoleView console =
+ (SMTRunnerConsoleView)
+ SMTestRunnerConnectionUtil.createConsole(eventsHandler.frameworkName, properties);
+ Disposer.register(project, console);
+ console
+ .getResultsViewer()
+ .getTreeView()
+ .getSelectionModel()
+ .setSelectionMode(TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
+ return console;
+ }
+
+ public static DefaultExecutionResult attachRerunFailedTestsAction(DefaultExecutionResult result) {
+ ExecutionConsole console = result.getExecutionConsole();
+ if (!(console instanceof SMTRunnerConsoleView)) {
+ return result;
+ }
+ SMTRunnerConsoleView smConsole = (SMTRunnerConsoleView) console;
+ TestConsoleProperties consoleProperties = smConsole.getProperties();
+ if (!(consoleProperties instanceof BlazeTestConsoleProperties)) {
+ return result;
+ }
+ BlazeTestConsoleProperties properties = (BlazeTestConsoleProperties) consoleProperties;
+ AbstractRerunFailedTestsAction action = properties.createRerunFailedTestsAction(smConsole);
+ if (action != null) {
+ action.init(properties);
+ action.setModelProvider(smConsole::getResultsViewer);
+ result.setRestartActions(action);
+ }
+ return result;
+ }
+}
diff --git a/base/src/com/google/idea/blaze/base/run/state/BlazeBinaryState.java b/base/src/com/google/idea/blaze/base/run/state/BlazeBinaryState.java
index 2ddeffc..da96aad 100644
--- a/base/src/com/google/idea/blaze/base/run/state/BlazeBinaryState.java
+++ b/base/src/com/google/idea/blaze/base/run/state/BlazeBinaryState.java
@@ -72,6 +72,11 @@
}
@Override
+ public void setComponentEnabled(boolean enabled) {
+ blazeBinaryField.setEnabled(enabled);
+ }
+
+ @Override
public void resetEditorFrom(RunConfigurationState genericState) {
BlazeBinaryState state = (BlazeBinaryState) genericState;
blazeBinaryField.setText(Strings.nullToEmpty(state.getBlazeBinary()));
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 ab44e47..1991ae6 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
@@ -17,8 +17,11 @@
import com.google.idea.blaze.base.command.BlazeCommandName;
import com.google.idea.blaze.base.command.BlazeFlags;
+import com.google.idea.blaze.base.run.state.BlazeRunOnDistributedExecutorState.RunOnExecutorStateEditor;
+import com.google.idea.blaze.base.settings.Blaze.BuildSystem;
import com.intellij.execution.configurations.RuntimeConfigurationError;
import com.intellij.execution.configurations.RuntimeConfigurationException;
+import com.intellij.openapi.project.Project;
import java.io.File;
import java.util.List;
import javax.annotation.Nullable;
@@ -35,13 +38,15 @@
private final RunConfigurationFlagsState blazeFlags;
private final RunConfigurationFlagsState exeFlags;
private final BlazeBinaryState blazeBinary;
+ private final BlazeRunOnDistributedExecutorState runOnDistributedExecutor;
- public BlazeCommandRunConfigurationCommonState(String buildSystemName) {
+ public BlazeCommandRunConfigurationCommonState(BuildSystem buildSystem) {
command = new BlazeCommandState();
- blazeFlags = new RunConfigurationFlagsState(USER_BLAZE_FLAG_TAG, buildSystemName + " flags:");
+ blazeFlags = new RunConfigurationFlagsState(USER_BLAZE_FLAG_TAG, buildSystem + " flags:");
exeFlags = new RunConfigurationFlagsState(USER_EXE_FLAG_TAG, "Executable flags:");
blazeBinary = new BlazeBinaryState();
- addStates(command, blazeFlags, exeFlags, blazeBinary);
+ runOnDistributedExecutor = new BlazeRunOnDistributedExecutorState(buildSystem);
+ addStates(command, blazeFlags, exeFlags, blazeBinary, runOnDistributedExecutor);
}
@Nullable
@@ -91,6 +96,14 @@
return null;
}
+ public Boolean getRunOnDistributedExecutor() {
+ return runOnDistributedExecutor.runOnDistributedExecutor;
+ }
+
+ public void setRunOnDistributedExecutor(Boolean runOnDistributedExecutor) {
+ this.runOnDistributedExecutor.runOnDistributedExecutor = runOnDistributedExecutor;
+ }
+
public void validate(String buildSystemName) throws RuntimeConfigurationException {
if (getCommand() == null) {
throw new RuntimeConfigurationError("You must specify a command.");
@@ -100,4 +113,32 @@
throw new RuntimeConfigurationError(buildSystemName + " binary does not exist");
}
}
+
+ @Override
+ public RunConfigurationStateEditor getEditor(Project project) {
+ return new RunConfigurationCompositeStateEditor(project, getStates()) {
+
+ @Nullable
+ private final RunOnExecutorStateEditor runOnExecutorEditor =
+ (RunOnExecutorStateEditor)
+ editors
+ .stream()
+ .filter(editor -> editor instanceof RunOnExecutorStateEditor)
+ .findFirst()
+ .orElse(null);
+
+ @Override
+ public void applyEditorTo(RunConfigurationState genericState) {
+ BlazeCommandRunConfigurationCommonState state =
+ (BlazeCommandRunConfigurationCommonState) genericState;
+ super.applyEditorTo(genericState);
+
+ // this editor needs to update based on state provided by other children.
+ if (runOnExecutorEditor != null) {
+ boolean isTest = BlazeCommandName.TEST.equals(state.getCommand());
+ runOnExecutorEditor.updateVisibility(isTest);
+ }
+ }
+ };
+ }
}
diff --git a/base/src/com/google/idea/blaze/base/run/state/BlazeCommandState.java b/base/src/com/google/idea/blaze/base/run/state/BlazeCommandState.java
index 491acb7..221fe10 100644
--- a/base/src/com/google/idea/blaze/base/run/state/BlazeCommandState.java
+++ b/base/src/com/google/idea/blaze/base/run/state/BlazeCommandState.java
@@ -79,6 +79,11 @@
}
@Override
+ public void setComponentEnabled(boolean enabled) {
+ commandCombo.setEnabled(enabled);
+ }
+
+ @Override
public void resetEditorFrom(RunConfigurationState genericState) {
BlazeCommandState state = (BlazeCommandState) genericState;
commandCombo.setSelectedItem(state.getCommand());
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
new file mode 100644
index 0000000..b251d3f
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/run/state/BlazeRunOnDistributedExecutorState.java
@@ -0,0 +1,127 @@
+/*
+ * 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.state;
+
+import com.google.idea.blaze.base.run.DistributedExecutorSupport;
+import com.google.idea.blaze.base.settings.Blaze.BuildSystem;
+import com.google.idea.blaze.base.ui.UiUtil;
+import com.intellij.icons.AllIcons;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.InvalidDataException;
+import com.intellij.openapi.util.WriteExternalException;
+import com.intellij.ui.components.JBCheckBox;
+import javax.annotation.Nullable;
+import javax.swing.JComponent;
+import javax.swing.JLabel;
+import org.jdom.Element;
+
+/**
+ * Provides an option to run blaze/bazel on a distributed executor, if available, rather than
+ * locally.
+ */
+public class BlazeRunOnDistributedExecutorState implements RunConfigurationState {
+
+ private static final String RUN_ON_DISTRIBUTED_EXECUTOR_ATTR =
+ "blaze-run-on-distributed-executor";
+
+ @Nullable private final DistributedExecutorSupport executorInfo;
+
+ public Boolean runOnDistributedExecutor = null;
+
+ BlazeRunOnDistributedExecutorState(BuildSystem buildSystem) {
+ this.executorInfo = DistributedExecutorSupport.getAvailableExecutor(buildSystem);
+ }
+
+ @Override
+ public void readExternal(Element element) throws InvalidDataException {
+ String string = element.getAttributeValue(RUN_ON_DISTRIBUTED_EXECUTOR_ATTR);
+ if (string != null) {
+ runOnDistributedExecutor = Boolean.parseBoolean(string);
+ }
+ }
+
+ @Override
+ public void writeExternal(Element element) throws WriteExternalException {
+ if (executorInfo != null && runOnDistributedExecutor != null) {
+ element.setAttribute(
+ RUN_ON_DISTRIBUTED_EXECUTOR_ATTR, Boolean.toString(runOnDistributedExecutor));
+ } else {
+ element.removeAttribute(RUN_ON_DISTRIBUTED_EXECUTOR_ATTR);
+ }
+ }
+
+ @Override
+ public RunOnExecutorStateEditor getEditor(Project project) {
+ return new RunOnExecutorStateEditor();
+ }
+
+ /** Editor for {@link BlazeRunOnDistributedExecutorState} */
+ class RunOnExecutorStateEditor implements RunConfigurationStateEditor {
+
+ private final JBCheckBox checkBox =
+ new JBCheckBox("Run on " + (executorInfo != null ? executorInfo.executorName() : null));
+ private final JLabel warning =
+ new JLabel(
+ "Warning: test UI integration is not available when running on distributed "
+ + "executor");
+
+ private boolean componentVisible = executorInfo != null;
+ private boolean isTest = false;
+
+ private RunOnExecutorStateEditor() {
+ warning.setIcon(AllIcons.RunConfigurations.ConfigurationWarning);
+ checkBox.addItemListener(e -> setVisibility());
+ setVisibility();
+ }
+
+ @Override
+ public void resetEditorFrom(RunConfigurationState genericState) {
+ BlazeRunOnDistributedExecutorState state = (BlazeRunOnDistributedExecutorState) genericState;
+ if (state.runOnDistributedExecutor != null) {
+ checkBox.setSelected(state.runOnDistributedExecutor);
+ }
+ }
+
+ @Override
+ public void applyEditorTo(RunConfigurationState genericState) {
+ BlazeRunOnDistributedExecutorState state = (BlazeRunOnDistributedExecutorState) genericState;
+ if (checkBox.isVisible()) {
+ state.runOnDistributedExecutor = checkBox.isSelected();
+ }
+ }
+
+ @Override
+ public JComponent createComponent() {
+ return UiUtil.createBox(checkBox, warning);
+ }
+
+ @Override
+ public void setComponentEnabled(boolean enabled) {
+ checkBox.setEnabled(enabled);
+ }
+
+ void updateVisibility(boolean isTest) {
+ this.componentVisible = executorInfo != null;
+ this.isTest = isTest;
+ setVisibility();
+ }
+
+ private void setVisibility() {
+ warning.setVisible(componentVisible && isTest && checkBox.isSelected());
+ checkBox.setVisible(componentVisible);
+ }
+ }
+}
diff --git a/base/src/com/google/idea/blaze/base/run/state/RunConfigurationCompositeState.java b/base/src/com/google/idea/blaze/base/run/state/RunConfigurationCompositeState.java
index 077e02d..a317232 100644
--- a/base/src/com/google/idea/blaze/base/run/state/RunConfigurationCompositeState.java
+++ b/base/src/com/google/idea/blaze/base/run/state/RunConfigurationCompositeState.java
@@ -15,6 +15,7 @@
*/
package com.google.idea.blaze.base.run.state;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.idea.blaze.base.ui.UiUtil;
import com.intellij.openapi.project.Project;
@@ -30,10 +31,6 @@
public class RunConfigurationCompositeState implements RunConfigurationState {
private final List<RunConfigurationState> states;
- public RunConfigurationCompositeState(List<RunConfigurationState> states) {
- this.states = states;
- }
-
protected RunConfigurationCompositeState() {
this.states = Lists.newArrayList();
}
@@ -42,6 +39,10 @@
Collections.addAll(this.states, states);
}
+ protected ImmutableList<RunConfigurationState> getStates() {
+ return ImmutableList.copyOf(states);
+ }
+
@Override
public final void readExternal(Element element) throws InvalidDataException {
for (RunConfigurationState state : states) {
@@ -60,11 +61,11 @@
/** @return A {@link RunConfigurationStateEditor} for this state. */
@Override
- public final RunConfigurationStateEditor getEditor(Project project) {
+ public RunConfigurationStateEditor getEditor(Project project) {
return new RunConfigurationCompositeStateEditor(project, states);
}
- private static class RunConfigurationCompositeStateEditor implements RunConfigurationStateEditor {
+ static class RunConfigurationCompositeStateEditor implements RunConfigurationStateEditor {
List<RunConfigurationStateEditor> editors;
public RunConfigurationCompositeStateEditor(
@@ -96,5 +97,10 @@
.map(RunConfigurationStateEditor::createComponent)
.collect(Collectors.toList()));
}
+
+ @Override
+ public void setComponentEnabled(boolean enabled) {
+ editors.forEach(editor -> editor.setComponentEnabled(enabled));
+ }
}
}
diff --git a/base/src/com/google/idea/blaze/base/run/state/RunConfigurationFlagsState.java b/base/src/com/google/idea/blaze/base/run/state/RunConfigurationFlagsState.java
index c0edaa4..87d306c 100644
--- a/base/src/com/google/idea/blaze/base/run/state/RunConfigurationFlagsState.java
+++ b/base/src/com/google/idea/blaze/base/run/state/RunConfigurationFlagsState.java
@@ -19,6 +19,7 @@
import com.google.common.collect.ImmutableList;
import com.google.idea.blaze.base.ui.UiUtil;
import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.execution.ParametersListUtil;
import java.util.List;
import javax.swing.JComponent;
@@ -85,27 +86,38 @@
this.fieldLabel = fieldLabel;
}
+ /** Identical to {@link ParametersListUtil#join}, except args are newline-delimited. */
private static String makeFlagString(List<String> flags) {
- StringBuilder flagString = new StringBuilder();
+ StringBuilder builder = new StringBuilder();
for (String flag : flags) {
- if (flagString.length() > 0) {
- flagString.append('\n');
+ if (builder.length() > 0) {
+ builder.append('\n');
}
- if (flag.isEmpty() || flag.contains(" ") || flag.contains("|")) {
- flagString.append('"');
- flagString.append(flag);
- flagString.append('"');
- } else {
- flagString.append(flag);
- }
+ builder.append(encode(flag));
}
- return flagString.toString();
+ return builder.toString();
+ }
+
+ private static String encode(String flag) {
+ StringBuilder builder = new StringBuilder();
+ builder.append(flag);
+ StringUtil.escapeQuotes(builder);
+ if (builder.length() == 0
+ || StringUtil.indexOf(builder, ' ') >= 0
+ || StringUtil.indexOf(builder, '|') >= 0) {
+ StringUtil.quote(builder);
+ }
+ return builder.toString();
+ }
+
+ @Override
+ public void setComponentEnabled(boolean enabled) {
+ flagsField.setEnabled(enabled);
}
@Override
public void resetEditorFrom(RunConfigurationState genericState) {
RunConfigurationFlagsState state = (RunConfigurationFlagsState) genericState;
- // Normally we could just use ParametersListUtils.join, but that will only space-delimit args.
flagsField.setText(makeFlagString(state.getFlags()));
}
diff --git a/base/src/com/google/idea/blaze/base/run/state/RunConfigurationStateEditor.java b/base/src/com/google/idea/blaze/base/run/state/RunConfigurationStateEditor.java
index b04020e..3a905fc 100644
--- a/base/src/com/google/idea/blaze/base/run/state/RunConfigurationStateEditor.java
+++ b/base/src/com/google/idea/blaze/base/run/state/RunConfigurationStateEditor.java
@@ -28,4 +28,6 @@
/** @return A component to display for the editor. */
JComponent createComponent();
+
+ void setComponentEnabled(boolean enabled);
}
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 0b336f4..99d490f 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
@@ -134,7 +134,8 @@
Kind.JAVA_TEST,
Kind.GWT_TEST,
Kind.CC_TEST,
- Kind.PY_TEST);
+ Kind.PY_TEST,
+ Kind.GO_TEST);
}
}
diff --git a/base/src/com/google/idea/blaze/base/settings/ui/EditProjectViewAction.java b/base/src/com/google/idea/blaze/base/settings/ui/EditProjectViewAction.java
index b2e86be..926483b 100644
--- a/base/src/com/google/idea/blaze/base/settings/ui/EditProjectViewAction.java
+++ b/base/src/com/google/idea/blaze/base/settings/ui/EditProjectViewAction.java
@@ -15,7 +15,7 @@
*/
package com.google.idea.blaze.base.settings.ui;
-import com.google.idea.blaze.base.actions.BlazeAction;
+import com.google.idea.blaze.base.actions.BlazeProjectAction;
import com.google.idea.blaze.base.projectview.ProjectViewManager;
import com.google.idea.blaze.base.projectview.ProjectViewSet;
import com.intellij.openapi.actionSystem.AnActionEvent;
@@ -27,14 +27,10 @@
import java.io.File;
/** Opens all the user's project views. */
-public class EditProjectViewAction extends BlazeAction {
+public class EditProjectViewAction extends BlazeProjectAction {
@Override
- public void actionPerformed(AnActionEvent e) {
- Project project = e.getProject();
- if (project == null) {
- return;
- }
+ protected void actionPerformedInBlazeProject(Project project, AnActionEvent e) {
ProjectViewSet projectViewSet = ProjectViewManager.getInstance(project).getProjectViewSet();
if (projectViewSet == null) {
return;
diff --git a/base/src/com/google/idea/blaze/base/sync/BlazeSyncManager.java b/base/src/com/google/idea/blaze/base/sync/BlazeSyncManager.java
index db3cba2..c1c36b7 100644
--- a/base/src/com/google/idea/blaze/base/sync/BlazeSyncManager.java
+++ b/base/src/com/google/idea/blaze/base/sync/BlazeSyncManager.java
@@ -16,29 +16,31 @@
package com.google.idea.blaze.base.sync;
import com.google.idea.blaze.base.async.executor.BlazeExecutor;
+import com.google.idea.blaze.base.model.primitives.TargetExpression;
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.blaze.base.settings.BlazeUserSettings;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.startup.StartupManager;
-import org.jetbrains.annotations.NotNull;
+import java.util.List;
/** Manages syncing and its listeners. */
public class BlazeSyncManager {
- @NotNull private final Project project;
+ private final Project project;
- public BlazeSyncManager(@NotNull Project project) {
+ public BlazeSyncManager(Project project) {
this.project = project;
}
- public static BlazeSyncManager getInstance(@NotNull Project project) {
+ public static BlazeSyncManager getInstance(Project project) {
return ServiceManager.getService(project, BlazeSyncManager.class);
}
/** Requests a project sync with Blaze. */
- public void requestProjectSync(@NotNull final BlazeSyncParams syncParams) {
+ public void requestProjectSync(final BlazeSyncParams syncParams) {
StartupManager.getInstance(project)
.runWhenProjectIsInitialized(
new Runnable() {
@@ -59,4 +61,38 @@
}
});
}
+
+ public void fullProjectSync() {
+ BlazeSyncParams syncParams =
+ new BlazeSyncParams.Builder("Full Sync", BlazeSyncParams.SyncMode.FULL)
+ .addProjectViewTargets(true)
+ .addWorkingSet(BlazeUserSettings.getInstance().getExpandSyncToWorkingSet())
+ .build();
+ requestProjectSync(syncParams);
+ }
+
+ public void incrementalProjectSync() {
+ BlazeSyncParams syncParams =
+ new BlazeSyncParams.Builder("Sync", BlazeSyncParams.SyncMode.INCREMENTAL)
+ .addProjectViewTargets(true)
+ .addWorkingSet(BlazeUserSettings.getInstance().getExpandSyncToWorkingSet())
+ .build();
+ requestProjectSync(syncParams);
+ }
+
+ public void partialSync(List<TargetExpression> targetExpressions) {
+ BlazeSyncParams syncParams =
+ new BlazeSyncParams.Builder("Partial Sync", BlazeSyncParams.SyncMode.PARTIAL)
+ .addTargetExpressions(targetExpressions)
+ .build();
+ requestProjectSync(syncParams);
+ }
+
+ public void workingSetSync() {
+ BlazeSyncParams syncParams =
+ new BlazeSyncParams.Builder("Sync Working Set", BlazeSyncParams.SyncMode.PARTIAL)
+ .addWorkingSet(true)
+ .build();
+ requestProjectSync(syncParams);
+ }
}
diff --git a/base/src/com/google/idea/blaze/base/sync/BlazeSyncParams.java b/base/src/com/google/idea/blaze/base/sync/BlazeSyncParams.java
index 2ee84c7..22fba9c 100644
--- a/base/src/com/google/idea/blaze/base/sync/BlazeSyncParams.java
+++ b/base/src/com/google/idea/blaze/base/sync/BlazeSyncParams.java
@@ -27,7 +27,7 @@
/** The kind of sync. */
public enum SyncMode {
/** Happens on startup, restores in-memory state */
- RESTORE_EPHEMERAL_STATE,
+ STARTUP,
/** Partial / working set sync */
PARTIAL,
/** This is the standard incremental sync */
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 0b55343..aa429f3 100644
--- a/base/src/com/google/idea/blaze/base/sync/BlazeSyncPlugin.java
+++ b/base/src/com/google/idea/blaze/base/sync/BlazeSyncPlugin.java
@@ -19,6 +19,7 @@
import com.google.common.collect.ImmutableSet;
import com.google.idea.blaze.base.ideinfo.TargetMap;
import com.google.idea.blaze.base.model.BlazeProjectData;
+import com.google.idea.blaze.base.model.BlazeVersionData;
import com.google.idea.blaze.base.model.SyncState;
import com.google.idea.blaze.base.model.primitives.LanguageClass;
import com.google.idea.blaze.base.model.primitives.WorkspaceRoot;
@@ -119,6 +120,7 @@
Project project,
BlazeContext context,
ProjectViewSet projectViewSet,
+ BlazeVersionData blazeVersionData,
BlazeProjectData blazeProjectData);
@Nullable
@@ -136,6 +138,19 @@
Module workspaceModule,
ModifiableRootModel workspaceModifiableModel);
+ /**
+ * Updates in-memory state that isn't serialized by IntelliJ.
+ *
+ * <p>Called on sync and on startup, after updateProjectStructure. May not do any write actions.
+ */
+ void updateInMemoryState(
+ Project project,
+ BlazeContext context,
+ WorkspaceRoot workspaceRoot,
+ ProjectViewSet projectViewSet,
+ BlazeProjectData blazeProjectData,
+ Module workspaceModule);
+
/** Validates the project. */
boolean validate(Project project, BlazeContext context, BlazeProjectData blazeProjectData);
@@ -203,6 +218,7 @@
Project project,
BlazeContext context,
ProjectViewSet projectViewSet,
+ BlazeVersionData blazeVersionData,
BlazeProjectData blazeProjectData) {}
@Nullable
@@ -224,6 +240,15 @@
ModifiableRootModel workspaceModifiableModel) {}
@Override
+ public void updateInMemoryState(
+ Project project,
+ BlazeContext context,
+ WorkspaceRoot workspaceRoot,
+ ProjectViewSet projectViewSet,
+ BlazeProjectData blazeProjectData,
+ Module workspaceModule) {}
+
+ @Override
public boolean validate(
Project project, BlazeContext context, BlazeProjectData blazeProjectData) {
return true;
diff --git a/base/src/com/google/idea/blaze/base/sync/BlazeSyncStartupActivity.java b/base/src/com/google/idea/blaze/base/sync/BlazeSyncStartupActivity.java
index 5b6a8ee..0fe7dac 100644
--- a/base/src/com/google/idea/blaze/base/sync/BlazeSyncStartupActivity.java
+++ b/base/src/com/google/idea/blaze/base/sync/BlazeSyncStartupActivity.java
@@ -43,8 +43,7 @@
.addWorkingSet(BlazeUserSettings.getInstance().getExpandSyncToWorkingSet())
.build();
}
- return new BlazeSyncParams.Builder(
- "Sync Project", BlazeSyncParams.SyncMode.RESTORE_EPHEMERAL_STATE)
+ return new BlazeSyncParams.Builder("Sync Project", BlazeSyncParams.SyncMode.STARTUP)
.addProjectViewTargets(true)
.addWorkingSet(BlazeUserSettings.getInstance().getExpandSyncToWorkingSet())
.build();
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 3d4e3ec..1fb59c9 100644
--- a/base/src/com/google/idea/blaze/base/sync/BlazeSyncTask.java
+++ b/base/src/com/google/idea/blaze/base/sync/BlazeSyncTask.java
@@ -24,7 +24,6 @@
import com.google.common.collect.Sets;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
-import com.google.idea.blaze.base.async.AsyncUtil;
import com.google.idea.blaze.base.async.FutureUtil;
import com.google.idea.blaze.base.async.executor.BlazeExecutor;
import com.google.idea.blaze.base.command.info.BlazeInfo;
@@ -32,10 +31,13 @@
import com.google.idea.blaze.base.filecache.FileCaches;
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.model.BlazeLibrary;
import com.google.idea.blaze.base.model.BlazeProjectData;
+import com.google.idea.blaze.base.model.BlazeVersionData;
import com.google.idea.blaze.base.model.SyncState;
+import com.google.idea.blaze.base.model.SyncState.Builder;
import com.google.idea.blaze.base.model.primitives.TargetExpression;
import com.google.idea.blaze.base.model.primitives.WorkspacePath;
import com.google.idea.blaze.base.model.primitives.WorkspaceRoot;
@@ -66,6 +68,7 @@
import com.google.idea.blaze.base.sync.aspects.BlazeIdeInterface;
import com.google.idea.blaze.base.sync.aspects.BlazeIdeInterface.BuildResult;
import com.google.idea.blaze.base.sync.data.BlazeDataStorage;
+import com.google.idea.blaze.base.sync.data.BlazeProjectDataManager;
import com.google.idea.blaze.base.sync.data.BlazeProjectDataManagerImpl;
import com.google.idea.blaze.base.sync.libraries.BlazeLibraryCollector;
import com.google.idea.blaze.base.sync.libraries.LibraryEditor;
@@ -84,9 +87,11 @@
import com.google.idea.blaze.base.targetmaps.ReverseDependencyMap;
import com.google.idea.blaze.base.util.SaveUtil;
import com.google.idea.blaze.base.vcs.BlazeVcsHandler;
+import com.google.idea.sdkcompat.transactions.Transactions;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.module.ModuleType;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.Progressive;
@@ -173,7 +178,18 @@
}
onSyncStart(project, context, syncMode);
- syncResult = doSyncProject(context, syncMode, oldBlazeProjectData);
+ if (syncMode != SyncMode.STARTUP) {
+ syncResult = doSyncProject(context, syncMode, oldBlazeProjectData);
+ } else {
+ syncResult = SyncResult.SUCCESS;
+ }
+ if (syncResult.successful()) {
+ ProjectViewSet projectViewSet = ProjectViewManager.getInstance(project).getProjectViewSet();
+ BlazeProjectData blazeProjectData =
+ BlazeProjectDataManager.getInstance(project).getBlazeProjectData();
+ updateInMemoryState(project, context, projectViewSet, blazeProjectData);
+ onSyncComplete(project, context, projectViewSet, blazeProjectData, syncMode, syncResult);
+ }
} catch (AssertionError | Exception e) {
LOG.error(e);
IssueOutput.error("Internal error: " + e.getMessage()).submit(context);
@@ -188,13 +204,13 @@
BlazeContext context, SyncMode syncMode, @Nullable BlazeProjectData oldBlazeProjectData) {
this.syncStartTime = System.currentTimeMillis();
- BlazeVcsHandler vcsHandler = null;
- for (BlazeVcsHandler candidate : BlazeVcsHandler.EP_NAME.getExtensions()) {
- if (candidate.handlesProject(importSettings.getBuildSystem(), workspaceRoot)) {
- vcsHandler = candidate;
- break;
- }
+ if (!FileAttributeProvider.getInstance().exists(workspaceRoot.directory())) {
+ IssueOutput.error(String.format("Workspace '%s' doesn't exist.", workspaceRoot.directory()))
+ .submit(context);
+ return SyncResult.FAILURE;
}
+
+ BlazeVcsHandler vcsHandler = BlazeVcsHandler.vcsHandlerForProject(project);
if (vcsHandler == null) {
IssueOutput.error("Could not find a VCS handler").submit(context);
return SyncResult.FAILURE;
@@ -222,6 +238,8 @@
}
BlazeRoots blazeRoots =
BlazeRoots.build(importSettings.getBuildSystem(), workspaceRoot, blazeInfo);
+ BlazeVersionData blazeVersionData =
+ BlazeVersionData.build(importSettings.getBuildSystem(), workspaceRoot, blazeInfo);
WorkspacePathResolverAndProjectView workspacePathResolverAndProjectView =
computeWorkspacePathResolverAndProjectView(context, blazeRoots, vcsHandler, executor);
@@ -245,7 +263,7 @@
}
if (!ProjectViewVerifier.verifyProjectView(
- context, workspaceRoot, projectViewSet, workspaceLanguageSettings)) {
+ context, workspacePathResolver, projectViewSet, workspaceLanguageSettings)) {
return SyncResult.FAILURE;
}
@@ -269,120 +287,112 @@
printWorkingSet(context, workingSet);
}
- BuildResult ideInfoResult = BuildResult.SUCCESS;
- BuildResult ideResolveResult = BuildResult.SUCCESS;
- if (syncMode != SyncMode.RESTORE_EPHEMERAL_STATE || oldBlazeProjectData == null) {
- SyncState.Builder syncStateBuilder = new SyncState.Builder();
- SyncState previousSyncState =
- oldBlazeProjectData != null ? oldBlazeProjectData.syncState : null;
+ SyncState.Builder syncStateBuilder = new SyncState.Builder();
+ SyncState previousSyncState =
+ oldBlazeProjectData != null ? oldBlazeProjectData.syncState : null;
- List<TargetExpression> targets = Lists.newArrayList();
- if (syncParams.addProjectViewTargets || oldBlazeProjectData == null) {
- Collection<TargetExpression> projectViewTargets =
- projectViewSet.listItems(TargetSection.KEY);
- if (!projectViewTargets.isEmpty()) {
- targets.addAll(projectViewTargets);
- printTargets(context, "project view", projectViewTargets);
- }
+ List<TargetExpression> targets = Lists.newArrayList();
+ if (syncParams.addProjectViewTargets || oldBlazeProjectData == null) {
+ Collection<TargetExpression> projectViewTargets = projectViewSet.listItems(TargetSection.KEY);
+ if (!projectViewTargets.isEmpty()) {
+ targets.addAll(projectViewTargets);
+ printTargets(context, "project view", projectViewTargets);
}
- if (syncParams.addWorkingSet && workingSet != null) {
- Collection<? extends TargetExpression> workingSetTargets =
- getWorkingSetTargets(projectViewSet, workingSet);
- if (!workingSetTargets.isEmpty()) {
- targets.addAll(workingSetTargets);
- printTargets(context, "working set", workingSetTargets);
- }
- }
- if (!syncParams.targetExpressions.isEmpty()) {
- targets.addAll(syncParams.targetExpressions);
- printTargets(context, syncParams.title, syncParams.targetExpressions);
- }
-
- boolean mergeWithOldState = !syncParams.addProjectViewTargets;
- BlazeIdeInterface.IdeResult ideQueryResult =
- getIdeQueryResult(
- project,
- context,
- projectViewSet,
- targets,
- workspaceLanguageSettings,
- artifactLocationDecoder,
- syncStateBuilder,
- previousSyncState,
- mergeWithOldState);
- if (context.isCancelled()) {
- return SyncResult.CANCELLED;
- }
- if (ideQueryResult.targetMap == null
- || ideQueryResult.buildResult == BuildResult.FATAL_ERROR) {
- context.setHasError();
- return SyncResult.FAILURE;
- }
-
- TargetMap targetMap = ideQueryResult.targetMap;
- ideInfoResult = ideQueryResult.buildResult;
-
- ListenableFuture<ImmutableMultimap<TargetKey, TargetKey>> reverseDependenciesFuture =
- BlazeExecutor.getInstance().submit(() -> ReverseDependencyMap.createRdepsMap(targetMap));
-
- ideResolveResult =
- resolveIdeArtifacts(project, context, workspaceRoot, projectViewSet, targets);
- if (ideResolveResult == BuildResult.FATAL_ERROR) {
- context.setHasError();
- return SyncResult.FAILURE;
- }
- if (context.isCancelled()) {
- return SyncResult.CANCELLED;
- }
-
- Scope.push(
- context,
- (childContext) -> {
- childContext.push(new TimingScope("UpdateSyncState"));
- for (BlazeSyncPlugin syncPlugin : BlazeSyncPlugin.EP_NAME.getExtensions()) {
- syncPlugin.updateSyncState(
- project,
- childContext,
- workspaceRoot,
- projectViewSet,
- workspaceLanguageSettings,
- blazeRoots,
- workingSet,
- workspacePathResolver,
- artifactLocationDecoder,
- targetMap,
- syncStateBuilder,
- previousSyncState);
- }
- });
-
- ImmutableMultimap<TargetKey, TargetKey> reverseDependencies =
- FutureUtil.waitForFuture(context, reverseDependenciesFuture)
- .timed("ReverseDependencies")
- .onError("Failed to compute reverse dependency map")
- .run()
- .result();
- if (reverseDependencies == null) {
- return SyncResult.FAILURE;
- }
-
- newBlazeProjectData =
- new BlazeProjectData(
- syncStartTime,
- targetMap,
- blazeInfo,
- blazeRoots,
- workingSet,
- workspacePathResolver,
- artifactLocationDecoder,
- workspaceLanguageSettings,
- syncStateBuilder.build(),
- reverseDependencies,
- vcsHandler.getVcsName());
- } else {
- // Restore project based on old blaze project data
- newBlazeProjectData = oldBlazeProjectData;
}
+ if (syncParams.addWorkingSet && workingSet != null) {
+ Collection<? extends TargetExpression> workingSetTargets =
+ getWorkingSetTargets(projectViewSet, workingSet);
+ if (!workingSetTargets.isEmpty()) {
+ targets.addAll(workingSetTargets);
+ printTargets(context, "working set", workingSetTargets);
+ }
+ }
+ if (!syncParams.targetExpressions.isEmpty()) {
+ targets.addAll(syncParams.targetExpressions);
+ printTargets(context, syncParams.title, syncParams.targetExpressions);
+ }
+
+ boolean mergeWithOldState = !syncParams.addProjectViewTargets;
+ BlazeIdeInterface.IdeResult ideQueryResult =
+ getIdeQueryResult(
+ project,
+ context,
+ projectViewSet,
+ blazeVersionData,
+ targets,
+ workspaceLanguageSettings,
+ artifactLocationDecoder,
+ syncStateBuilder,
+ previousSyncState,
+ mergeWithOldState);
+ if (context.isCancelled()) {
+ return SyncResult.CANCELLED;
+ }
+ if (ideQueryResult.targetMap == null || ideQueryResult.buildResult == BuildResult.FATAL_ERROR) {
+ context.setHasError();
+ return SyncResult.FAILURE;
+ }
+
+ TargetMap targetMap = ideQueryResult.targetMap;
+ BuildResult ideInfoResult = ideQueryResult.buildResult;
+
+ ListenableFuture<ImmutableMultimap<TargetKey, TargetKey>> reverseDependenciesFuture =
+ BlazeExecutor.getInstance().submit(() -> ReverseDependencyMap.createRdepsMap(targetMap));
+
+ BuildResult ideResolveResult =
+ resolveIdeArtifacts(
+ project, context, workspaceRoot, projectViewSet, blazeVersionData, targets);
+ if (ideResolveResult == BuildResult.FATAL_ERROR) {
+ context.setHasError();
+ return SyncResult.FAILURE;
+ }
+ if (context.isCancelled()) {
+ return SyncResult.CANCELLED;
+ }
+
+ Scope.push(
+ context,
+ (childContext) -> {
+ childContext.push(new TimingScope("UpdateSyncState"));
+ for (BlazeSyncPlugin syncPlugin : BlazeSyncPlugin.EP_NAME.getExtensions()) {
+ syncPlugin.updateSyncState(
+ project,
+ childContext,
+ workspaceRoot,
+ projectViewSet,
+ workspaceLanguageSettings,
+ blazeRoots,
+ workingSet,
+ workspacePathResolver,
+ artifactLocationDecoder,
+ targetMap,
+ syncStateBuilder,
+ previousSyncState);
+ }
+ });
+
+ ImmutableMultimap<TargetKey, TargetKey> reverseDependencies =
+ FutureUtil.waitForFuture(context, reverseDependenciesFuture)
+ .timed("ReverseDependencies")
+ .onError("Failed to compute reverse dependency map")
+ .run()
+ .result();
+ if (reverseDependencies == null) {
+ return SyncResult.FAILURE;
+ }
+
+ newBlazeProjectData =
+ new BlazeProjectData(
+ syncStartTime,
+ targetMap,
+ blazeInfo,
+ blazeRoots,
+ blazeVersionData,
+ workspacePathResolver,
+ artifactLocationDecoder,
+ workspaceLanguageSettings,
+ syncStateBuilder.build(),
+ reverseDependencies);
FileCaches.onSync(project, context, projectViewSet, newBlazeProjectData, syncMode);
ListenableFuture<?> prefetch =
@@ -394,7 +404,13 @@
.run();
boolean success =
- updateProject(project, context, projectViewSet, oldBlazeProjectData, newBlazeProjectData);
+ updateProject(
+ project,
+ context,
+ projectViewSet,
+ blazeVersionData,
+ oldBlazeProjectData,
+ newBlazeProjectData);
if (!success) {
return SyncResult.FAILURE;
}
@@ -417,7 +433,6 @@
syncResult = SyncResult.PARTIAL_SUCCESS;
}
- onSyncComplete(project, context, projectViewSet, newBlazeProjectData, syncMode, syncResult);
return syncResult;
}
@@ -555,10 +570,11 @@
Project project,
BlazeContext parentContext,
ProjectViewSet projectViewSet,
+ BlazeVersionData blazeVersionData,
List<TargetExpression> targets,
WorkspaceLanguageSettings workspaceLanguageSettings,
ArtifactLocationDecoder artifactLocationDecoder,
- SyncState.Builder syncStateBuilder,
+ Builder syncStateBuilder,
@Nullable SyncState previousSyncState,
boolean mergeWithOldState) {
@@ -575,6 +591,7 @@
context,
workspaceRoot,
projectViewSet,
+ blazeVersionData,
targets,
workspaceLanguageSettings,
artifactLocationDecoder,
@@ -589,6 +606,7 @@
BlazeContext parentContext,
WorkspaceRoot workspaceRoot,
ProjectViewSet projectViewSet,
+ BlazeVersionData blazeVersionData,
List<TargetExpression> targetExpressions) {
return Scope.push(
parentContext,
@@ -606,7 +624,7 @@
}
BlazeIdeInterface blazeIdeInterface = BlazeIdeInterface.getInstance();
return blazeIdeInterface.resolveIdeArtifacts(
- project, context, workspaceRoot, projectViewSet, targetExpressions);
+ project, context, workspaceRoot, projectViewSet, blazeVersionData, targetExpressions);
});
}
@@ -614,6 +632,7 @@
Project project,
BlazeContext parentContext,
ProjectViewSet projectViewSet,
+ BlazeVersionData blazeVersionData,
@Nullable BlazeProjectData oldBlazeProjectData,
BlazeProjectData newBlazeProjectData) {
return Scope.push(
@@ -625,19 +644,29 @@
context.output(new StatusOutput("Committing project structure..."));
try {
- AsyncUtil.executeProjectChangeAction(
- () ->
- ProjectRootManagerEx.getInstanceEx(this.project)
- .mergeRootsChangesDuring(
- () -> {
- updateProjectSdk(context, projectViewSet, newBlazeProjectData);
- updateProjectStructure(
- context,
- importSettings,
- projectViewSet,
- newBlazeProjectData,
- oldBlazeProjectData);
- }));
+ 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);
@@ -651,9 +680,13 @@
}
private void updateProjectSdk(
- BlazeContext context, ProjectViewSet projectViewSet, BlazeProjectData newBlazeProjectData) {
+ BlazeContext context,
+ ProjectViewSet projectViewSet,
+ BlazeVersionData blazeVersionData,
+ BlazeProjectData newBlazeProjectData) {
for (BlazeSyncPlugin syncPlugin : BlazeSyncPlugin.EP_NAME.getExtensions()) {
- syncPlugin.updateProjectSdk(project, context, projectViewSet, newBlazeProjectData);
+ syncPlugin.updateProjectSdk(
+ project, context, projectViewSet, blazeVersionData, newBlazeProjectData);
}
}
@@ -716,6 +749,36 @@
moduleEditor.commitWithGc(context);
}
+ private void updateInMemoryState(
+ Project project,
+ BlazeContext parentContext,
+ ProjectViewSet projectViewSet,
+ BlazeProjectData blazeProjectData) {
+ Scope.push(
+ parentContext,
+ context -> {
+ context.push(new TimingScope("UpdateInMemoryState"));
+ context.output(new StatusOutput("Updating in-memory state..."));
+ ApplicationManager.getApplication()
+ .runReadAction(
+ () -> {
+ Module workspaceModule =
+ ModuleManager.getInstance(project)
+ .findModuleByName(BlazeDataStorage.WORKSPACE_MODULE_NAME);
+ for (BlazeSyncPlugin blazeSyncPlugin :
+ BlazeSyncPlugin.EP_NAME.getExtensions()) {
+ blazeSyncPlugin.updateInMemoryState(
+ project,
+ context,
+ workspaceRoot,
+ projectViewSet,
+ blazeProjectData,
+ workspaceModule);
+ }
+ });
+ });
+ }
+
/**
* Creates a module that includes the user's data directory.
*
diff --git a/base/src/com/google/idea/blaze/base/sync/SyncListener.java b/base/src/com/google/idea/blaze/base/sync/SyncListener.java
index a3512cb..7a0cf03 100644
--- a/base/src/com/google/idea/blaze/base/sync/SyncListener.java
+++ b/base/src/com/google/idea/blaze/base/sync/SyncListener.java
@@ -31,13 +31,23 @@
/** Result of the sync operation */
enum SyncResult {
/** Full success */
- SUCCESS,
+ SUCCESS(true),
/** The user has errors in their BUILD files or compilation errors */
- PARTIAL_SUCCESS,
+ PARTIAL_SUCCESS(true),
/** The user cancelled */
- CANCELLED,
+ CANCELLED(false),
/** Failure -- sync could not complete */
- FAILURE,
+ FAILURE(false);
+
+ private final boolean success;
+
+ SyncResult(boolean success) {
+ this.success = success;
+ }
+
+ public boolean successful() {
+ return success;
+ }
}
/** Called after open documents have been saved, prior to starting the blaze sync. */
diff --git a/base/src/com/google/idea/blaze/base/sync/actions/ExpandSyncToWorkingSetAction.java b/base/src/com/google/idea/blaze/base/sync/actions/ExpandSyncToWorkingSetAction.java
index 0509cca..54f2ca1 100644
--- a/base/src/com/google/idea/blaze/base/sync/actions/ExpandSyncToWorkingSetAction.java
+++ b/base/src/com/google/idea/blaze/base/sync/actions/ExpandSyncToWorkingSetAction.java
@@ -15,12 +15,12 @@
*/
package com.google.idea.blaze.base.sync.actions;
-import com.google.idea.blaze.base.actions.BlazeToggleAction;
+import com.google.idea.blaze.base.actions.BlazeProjectToggleAction;
import com.google.idea.blaze.base.settings.BlazeUserSettings;
import com.intellij.openapi.actionSystem.AnActionEvent;
/** Manages a tick box of whether to expand the sync targets to the working set. */
-public class ExpandSyncToWorkingSetAction extends BlazeToggleAction {
+public class ExpandSyncToWorkingSetAction extends BlazeProjectToggleAction {
@Override
public boolean isSelected(AnActionEvent e) {
return BlazeUserSettings.getInstance().getExpandSyncToWorkingSet();
diff --git a/base/src/com/google/idea/blaze/base/sync/actions/FullSyncProjectAction.java b/base/src/com/google/idea/blaze/base/sync/actions/FullSyncProjectAction.java
index 2c1f625..dfe752e 100644
--- a/base/src/com/google/idea/blaze/base/sync/actions/FullSyncProjectAction.java
+++ b/base/src/com/google/idea/blaze/base/sync/actions/FullSyncProjectAction.java
@@ -15,38 +15,29 @@
*/
package com.google.idea.blaze.base.sync.actions;
-import com.google.idea.blaze.base.actions.BlazeAction;
-import com.google.idea.blaze.base.settings.BlazeUserSettings;
+import com.google.idea.blaze.base.actions.BlazeProjectAction;
import com.google.idea.blaze.base.sync.BlazeSyncManager;
-import com.google.idea.blaze.base.sync.BlazeSyncParams;
-import com.google.idea.blaze.base.sync.BlazeSyncParams.SyncMode;
+import com.google.idea.blaze.base.sync.status.BlazeSyncStatus;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.Presentation;
import com.intellij.openapi.project.Project;
-/** Re-imports (syncs) an Android-Blaze project, without showing the "Import Project" wizard. */
-public class FullSyncProjectAction extends BlazeAction {
+/** Performs a full sync. This is like an incremental sync with some additional invalidation. */
+public class FullSyncProjectAction extends BlazeProjectAction {
- public FullSyncProjectAction() {
- super("Non-Incrementally Sync Project with BUILD Files");
+ @Override
+ protected void actionPerformedInBlazeProject(Project project, AnActionEvent e) {
+ BlazeSyncManager.getInstance(project).fullProjectSync();
+ updateStatus(project, e);
}
@Override
- public void actionPerformed(final AnActionEvent e) {
- Project project = e.getProject();
- if (project != null) {
- Presentation presentation = e.getPresentation();
- presentation.setEnabled(false);
- try {
- BlazeSyncParams syncParams =
- new BlazeSyncParams.Builder("Full Sync", SyncMode.FULL)
- .addProjectViewTargets(true)
- .addWorkingSet(BlazeUserSettings.getInstance().getExpandSyncToWorkingSet())
- .build();
- BlazeSyncManager.getInstance(project).requestProjectSync(syncParams);
- } finally {
- presentation.setEnabled(true);
- }
- }
+ protected void updateForBlazeProject(Project project, AnActionEvent e) {
+ updateStatus(project, e);
+ }
+
+ private static void updateStatus(Project project, AnActionEvent e) {
+ Presentation presentation = e.getPresentation();
+ presentation.setEnabled(!BlazeSyncStatus.getInstance(project).syncInProgress());
}
}
diff --git a/base/src/com/google/idea/blaze/base/sync/actions/IncrementalSyncProjectAction.java b/base/src/com/google/idea/blaze/base/sync/actions/IncrementalSyncProjectAction.java
index 134acfb..f772b67 100644
--- a/base/src/com/google/idea/blaze/base/sync/actions/IncrementalSyncProjectAction.java
+++ b/base/src/com/google/idea/blaze/base/sync/actions/IncrementalSyncProjectAction.java
@@ -15,14 +15,11 @@
*/
package com.google.idea.blaze.base.sync.actions;
-import com.google.idea.blaze.base.actions.BlazeAction;
+import com.google.idea.blaze.base.actions.BlazeProjectAction;
import com.google.idea.blaze.base.settings.Blaze;
import com.google.idea.blaze.base.settings.BlazeUserSettings;
import com.google.idea.blaze.base.sync.BlazeSyncManager;
-import com.google.idea.blaze.base.sync.BlazeSyncParams;
-import com.google.idea.blaze.base.sync.BlazeSyncParams.SyncMode;
import com.google.idea.blaze.base.sync.status.BlazeSyncStatus;
-import com.google.idea.blaze.base.sync.status.BlazeSyncStatusImpl;
import com.intellij.notification.Notification;
import com.intellij.notification.NotificationDisplayType;
import com.intellij.notification.NotificationGroup;
@@ -34,45 +31,27 @@
import icons.BlazeIcons;
import javax.swing.Icon;
-/** Re-imports (syncs) an Android-Blaze project, without showing the "Import Project" wizard. */
-public class IncrementalSyncProjectAction extends BlazeAction {
+/** Syncs the project with BUILD files. */
+public class IncrementalSyncProjectAction extends BlazeProjectAction {
- public IncrementalSyncProjectAction() {
- super("Sync Project with BUILD Files");
+ @Override
+ protected void actionPerformedInBlazeProject(Project project, AnActionEvent e) {
+ BlazeSyncManager.getInstance(project).incrementalProjectSync();
+ updateIcon(e);
}
@Override
- public void actionPerformed(final AnActionEvent e) {
- Project project = e.getProject();
- if (project != null) {
- BlazeSyncManager.getInstance(project)
- .requestProjectSync(
- new BlazeSyncParams.Builder("Sync", SyncMode.INCREMENTAL)
- .addProjectViewTargets(true)
- .addWorkingSet(BlazeUserSettings.getInstance().getExpandSyncToWorkingSet())
- .build());
- updateIcon(e);
- }
- }
-
- @Override
- protected void doUpdate(AnActionEvent e) {
- super.doUpdate(e);
+ protected void updateForBlazeProject(Project project, AnActionEvent e) {
updateIcon(e);
}
private static void updateIcon(AnActionEvent e) {
Project project = e.getProject();
Presentation presentation = e.getPresentation();
- if (project == null) {
- presentation.setIcon(BlazeIcons.Blaze);
- presentation.setEnabled(true);
- return;
- }
- BlazeSyncStatusImpl statusHelper = BlazeSyncStatusImpl.getImpl(project);
+ BlazeSyncStatus statusHelper = BlazeSyncStatus.getInstance(project);
BlazeSyncStatus.SyncStatus status = statusHelper.getStatus();
presentation.setIcon(getIcon(status));
- presentation.setEnabled(!statusHelper.syncInProgress.get());
+ presentation.setEnabled(!statusHelper.syncInProgress());
if (status == BlazeSyncStatus.SyncStatus.DIRTY
&& !BlazeUserSettings.getInstance().getSyncStatusPopupShown()) {
diff --git a/base/src/com/google/idea/blaze/base/sync/actions/PartialSyncAction.java b/base/src/com/google/idea/blaze/base/sync/actions/PartialSyncAction.java
index e761f11..60e8484 100644
--- a/base/src/com/google/idea/blaze/base/sync/actions/PartialSyncAction.java
+++ b/base/src/com/google/idea/blaze/base/sync/actions/PartialSyncAction.java
@@ -15,8 +15,9 @@
*/
package com.google.idea.blaze.base.sync.actions;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
-import com.google.idea.blaze.base.actions.BlazeAction;
+import com.google.idea.blaze.base.actions.BlazeProjectAction;
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;
@@ -24,13 +25,13 @@
import com.google.idea.blaze.base.settings.Blaze;
import com.google.idea.blaze.base.settings.Blaze.BuildSystem;
import com.google.idea.blaze.base.sync.BlazeSyncManager;
-import com.google.idea.blaze.base.sync.BlazeSyncParams;
import com.google.idea.blaze.base.sync.BuildTargetFinder;
import com.google.idea.blaze.base.sync.projectview.ImportRoots;
+import com.google.idea.blaze.base.sync.status.BlazeSyncStatus;
import com.google.idea.blaze.base.targetmaps.SourceToTargetMap;
+import com.google.idea.common.actionhelper.ActionPresentationHelper;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
-import com.intellij.openapi.actionSystem.Presentation;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import java.io.File;
@@ -38,57 +39,43 @@
import javax.annotation.Nullable;
/** Allows a partial sync of the project depending on what's been selected. */
-public class PartialSyncAction extends BlazeAction {
+public class PartialSyncAction extends BlazeProjectAction {
+
@Override
- public void actionPerformed(AnActionEvent e) {
- Project project = e.getProject();
- if (project != null) {
- List<TargetExpression> targetExpressions = Lists.newArrayList();
- getTargets(e, targetExpressions);
-
- BlazeSyncParams syncParams =
- new BlazeSyncParams.Builder("Partial Sync", BlazeSyncParams.SyncMode.PARTIAL)
- .addTargetExpressions(targetExpressions)
- .build();
-
- BlazeSyncManager.getInstance(project).requestProjectSync(syncParams);
- }
+ protected void actionPerformedInBlazeProject(Project project, AnActionEvent e) {
+ VirtualFile virtualFile = getSelectedFile(e);
+ List<TargetExpression> targets = getTargets(project, virtualFile);
+ BlazeSyncManager.getInstance(project).partialSync(targets);
}
@Override
- protected void doUpdate(AnActionEvent e) {
- super.doUpdate(e);
- List<TargetExpression> targets = Lists.newArrayList();
- String objectName = getTargets(e, targets);
-
- boolean enabled = objectName != null && !targets.isEmpty();
- Presentation presentation = e.getPresentation();
- presentation.setEnabled(enabled);
-
- if (enabled) {
- presentation.setText(
- String.format("Partially Sync %s with %s", objectName, buildSystemName(e.getProject())));
- } else {
- presentation.setText(String.format("Partial %s Sync", buildSystemName(e.getProject())));
- }
+ protected void updateForBlazeProject(Project project, AnActionEvent e) {
+ VirtualFile virtualFile = getSelectedFile(e);
+ List<TargetExpression> targets = getTargets(project, virtualFile);
+ ActionPresentationHelper.of(e)
+ .disableIf(BlazeSyncStatus.getInstance(project).syncInProgress())
+ .disableIf(targets.isEmpty())
+ .setTextWithSubject("Partially Sync File", "Partially Sync %s", virtualFile)
+ .disableWithoutSubject()
+ .commit();
}
- private static String buildSystemName(@Nullable Project project) {
- return Blaze.buildSystemName(project);
- }
-
- @Nullable
- private String getTargets(AnActionEvent e, List<TargetExpression> targets) {
- Project project = e.getProject();
+ private VirtualFile getSelectedFile(AnActionEvent e) {
VirtualFile virtualFile = e.getData(CommonDataKeys.VIRTUAL_FILE);
- if (project == null || virtualFile == null || !virtualFile.isInLocalFileSystem()) {
+ if (virtualFile == null || !virtualFile.isInLocalFileSystem()) {
return null;
}
+ return virtualFile;
+ }
+ private static List<TargetExpression> getTargets(
+ Project project, @Nullable VirtualFile virtualFile) {
+ if (virtualFile == null) {
+ return ImmutableList.of();
+ }
+ List<TargetExpression> targets = Lists.newArrayList();
WorkspaceRoot workspaceRoot = WorkspaceRoot.fromProject(project);
SourceToTargetMap.getInstance(project);
-
- String objectName = virtualFile.isDirectory() ? "Package" : "File";
if (!virtualFile.isDirectory()) {
targets.addAll(
SourceToTargetMap.getInstance(project)
@@ -109,7 +96,6 @@
}
}
}
-
- return objectName;
+ return targets;
}
}
diff --git a/base/src/com/google/idea/blaze/base/sync/actions/ShowPerformanceWarningsToggleAction.java b/base/src/com/google/idea/blaze/base/sync/actions/ShowPerformanceWarningsToggleAction.java
index d7c6e96..9cb2b46 100644
--- a/base/src/com/google/idea/blaze/base/sync/actions/ShowPerformanceWarningsToggleAction.java
+++ b/base/src/com/google/idea/blaze/base/sync/actions/ShowPerformanceWarningsToggleAction.java
@@ -15,12 +15,12 @@
*/
package com.google.idea.blaze.base.sync.actions;
-import com.google.idea.blaze.base.actions.BlazeToggleAction;
+import com.google.idea.blaze.base.actions.BlazeProjectToggleAction;
import com.google.idea.blaze.base.settings.BlazeUserSettings;
import com.intellij.openapi.actionSystem.AnActionEvent;
/** Manages a tick box of whether to show performance warnings. */
-public class ShowPerformanceWarningsToggleAction extends BlazeToggleAction {
+public class ShowPerformanceWarningsToggleAction extends BlazeProjectToggleAction {
@Override
public boolean isSelected(AnActionEvent e) {
return BlazeUserSettings.getInstance().getShowPerformanceWarnings();
diff --git a/base/src/com/google/idea/blaze/base/sync/actions/SyncWorkingSetAction.java b/base/src/com/google/idea/blaze/base/sync/actions/SyncWorkingSetAction.java
index 950f473..f72294b 100644
--- a/base/src/com/google/idea/blaze/base/sync/actions/SyncWorkingSetAction.java
+++ b/base/src/com/google/idea/blaze/base/sync/actions/SyncWorkingSetAction.java
@@ -15,25 +15,29 @@
*/
package com.google.idea.blaze.base.sync.actions;
-import com.google.idea.blaze.base.actions.BlazeAction;
+import com.google.idea.blaze.base.actions.BlazeProjectAction;
import com.google.idea.blaze.base.sync.BlazeSyncManager;
-import com.google.idea.blaze.base.sync.BlazeSyncParams;
-import com.google.idea.blaze.base.sync.BlazeSyncParams.SyncMode;
+import com.google.idea.blaze.base.sync.status.BlazeSyncStatus;
import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.Presentation;
import com.intellij.openapi.project.Project;
-/** Allows a partial sync of the project depending on what's been selected. */
-public class SyncWorkingSetAction extends BlazeAction {
- @Override
- public void actionPerformed(AnActionEvent e) {
- Project project = e.getProject();
- if (project != null) {
- BlazeSyncParams syncParams =
- new BlazeSyncParams.Builder("Sync Working Set", SyncMode.PARTIAL)
- .addWorkingSet(true)
- .build();
+/** Allows a partial sync of the working set. */
+public class SyncWorkingSetAction extends BlazeProjectAction {
- BlazeSyncManager.getInstance(project).requestProjectSync(syncParams);
- }
+ @Override
+ protected void actionPerformedInBlazeProject(Project project, AnActionEvent e) {
+ BlazeSyncManager.getInstance(project).workingSetSync();
+ updateStatus(project, e);
+ }
+
+ @Override
+ protected void updateForBlazeProject(Project project, AnActionEvent e) {
+ updateStatus(project, e);
+ }
+
+ private static void updateStatus(Project project, AnActionEvent e) {
+ Presentation presentation = e.getPresentation();
+ presentation.setEnabled(!BlazeSyncStatus.getInstance(project).syncInProgress());
}
}
diff --git a/base/src/com/google/idea/blaze/base/sync/aspects/BlazeIdeInterface.java b/base/src/com/google/idea/blaze/base/sync/aspects/BlazeIdeInterface.java
index 4051083..c355d5d 100644
--- a/base/src/com/google/idea/blaze/base/sync/aspects/BlazeIdeInterface.java
+++ b/base/src/com/google/idea/blaze/base/sync/aspects/BlazeIdeInterface.java
@@ -16,6 +16,7 @@
package com.google.idea.blaze.base.sync.aspects;
import com.google.idea.blaze.base.ideinfo.TargetMap;
+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;
import com.google.idea.blaze.base.model.primitives.WorkspaceRoot;
@@ -73,6 +74,7 @@
BlazeContext context,
WorkspaceRoot workspaceRoot,
ProjectViewSet projectViewSet,
+ BlazeVersionData blazeVersionData,
List<TargetExpression> targets,
WorkspaceLanguageSettings workspaceLanguageSettings,
ArtifactLocationDecoder artifactLocationDecoder,
@@ -90,5 +92,19 @@
BlazeContext context,
WorkspaceRoot workspaceRoot,
ProjectViewSet projectViewSet,
+ BlazeVersionData blazeVersionData,
+ List<TargetExpression> targets);
+
+ /**
+ * Attempts to compile the requested ide artifacts.
+ *
+ * <p>Amounts to a build of the ide-compile output group.
+ */
+ BuildResult compileIdeArtifacts(
+ Project project,
+ BlazeContext context,
+ WorkspaceRoot workspaceRoot,
+ ProjectViewSet projectViewSet,
+ BlazeVersionData blazeVersionData,
List<TargetExpression> targets);
}
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 f44f200..36e29af 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
@@ -36,6 +36,7 @@
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;
import com.google.idea.blaze.base.model.primitives.WorkspaceRoot;
@@ -91,6 +92,7 @@
BlazeContext context,
WorkspaceRoot workspaceRoot,
ProjectViewSet projectViewSet,
+ BlazeVersionData blazeVersionData,
List<TargetExpression> targets,
WorkspaceLanguageSettings workspaceLanguageSettings,
ArtifactLocationDecoder artifactLocationDecoder,
@@ -106,7 +108,7 @@
}
// If the aspect strategy has changed, redo everything from scratch
- final AspectStrategy aspectStrategy = getAspectStrategy(project);
+ final AspectStrategy aspectStrategy = getAspectStrategy(project, blazeVersionData);
if (prevState != null
&& !Objects.equal(prevState.aspectStrategyName, aspectStrategy.getName())) {
prevState = null;
@@ -375,8 +377,38 @@
BlazeContext context,
WorkspaceRoot workspaceRoot,
ProjectViewSet projectViewSet,
+ BlazeVersionData blazeVersionData,
List<TargetExpression> targets) {
- AspectStrategy aspectStrategy = getAspectStrategy(project);
+ return resolveIdeArtifacts(
+ project, context, workspaceRoot, projectViewSet, blazeVersionData, targets, false);
+ }
+
+ @Override
+ public BuildResult compileIdeArtifacts(
+ Project project,
+ BlazeContext context,
+ WorkspaceRoot workspaceRoot,
+ ProjectViewSet projectViewSet,
+ BlazeVersionData blazeVersionData,
+ List<TargetExpression> targets) {
+ boolean ideCompile = hasIdeCompileOutputGroup(blazeVersionData);
+ return resolveIdeArtifacts(
+ project, context, workspaceRoot, projectViewSet, blazeVersionData, targets, ideCompile);
+ }
+
+ private static boolean hasIdeCompileOutputGroup(BlazeVersionData blazeVersionData) {
+ return blazeVersionData.bazelIsAtLeastVersion(0, 4, 3);
+ }
+
+ private static BuildResult resolveIdeArtifacts(
+ Project project,
+ BlazeContext context,
+ WorkspaceRoot workspaceRoot,
+ ProjectViewSet projectViewSet,
+ BlazeVersionData blazeVersionData,
+ List<TargetExpression> targets,
+ boolean useIdeCompileOutputGroup) {
+ AspectStrategy aspectStrategy = getAspectStrategy(project, blazeVersionData);
BlazeCommand.Builder blazeCommandBuilder =
BlazeCommand.builder(Blaze.getBuildSystem(project), BlazeCommandName.BUILD)
@@ -385,7 +417,11 @@
.addBlazeFlags(BlazeFlags.KEEP_GOING)
.addBlazeFlags(BlazeFlags.buildFlags(project, projectViewSet));
- aspectStrategy.modifyIdeResolveCommand(blazeCommandBuilder);
+ if (useIdeCompileOutputGroup) {
+ aspectStrategy.modifyIdeCompileCommand(blazeCommandBuilder);
+ } else {
+ aspectStrategy.modifyIdeResolveCommand(blazeCommandBuilder);
+ }
BlazeCommand blazeCommand = blazeCommandBuilder.build();
@@ -402,9 +438,10 @@
return BuildResult.fromExitCode(retVal);
}
- private static AspectStrategy getAspectStrategy(Project project) {
+ private static AspectStrategy getAspectStrategy(
+ Project project, BlazeVersionData blazeVersionData) {
for (AspectStrategyProvider provider : AspectStrategyProvider.EP_NAME.getExtensions()) {
- AspectStrategy aspectStrategy = provider.getAspectStrategy(project);
+ AspectStrategy aspectStrategy = provider.getAspectStrategy(project, blazeVersionData);
if (aspectStrategy != null) {
return aspectStrategy;
}
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 f3f0557..c140587 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
@@ -150,8 +150,7 @@
}
private static TargetKey makeTargetKey(IntellijIdeInfo.TargetKey key) {
- return TargetKey.forGeneralTarget(
- new Label(key.getLabel()), Strings.emptyToNull(key.getAspectId()));
+ return TargetKey.forGeneralTarget(new Label(key.getLabel()), key.getAspectIdsList());
}
private static Dependency makeDependency(IntellijIdeInfo.Dependency dep) {
diff --git a/base/src/com/google/idea/blaze/base/sync/aspects/strategy/AspectStrategy.java b/base/src/com/google/idea/blaze/base/sync/aspects/strategy/AspectStrategy.java
index 598d8b6..d63630e 100644
--- a/base/src/com/google/idea/blaze/base/sync/aspects/strategy/AspectStrategy.java
+++ b/base/src/com/google/idea/blaze/base/sync/aspects/strategy/AspectStrategy.java
@@ -28,6 +28,8 @@
void modifyIdeResolveCommand(BlazeCommand.Builder blazeCommandBuilder);
+ void modifyIdeCompileCommand(BlazeCommand.Builder blazeCommandBuilder);
+
String getAspectOutputFileExtension();
IntellijIdeInfo.TargetIdeInfo readAspectFile(InputStream inputStream) throws IOException;
diff --git a/base/src/com/google/idea/blaze/base/sync/aspects/strategy/AspectStrategyNative.java b/base/src/com/google/idea/blaze/base/sync/aspects/strategy/AspectStrategyNative.java
index df88c4f..63787db 100644
--- a/base/src/com/google/idea/blaze/base/sync/aspects/strategy/AspectStrategyNative.java
+++ b/base/src/com/google/idea/blaze/base/sync/aspects/strategy/AspectStrategyNative.java
@@ -22,6 +22,7 @@
/** Aspect strategy for native. */
public class AspectStrategyNative implements AspectStrategy {
+
@Override
public String getName() {
return "NativeAspect";
@@ -42,6 +43,13 @@
}
@Override
+ public void modifyIdeCompileCommand(BlazeCommand.Builder blazeCommandBuilder) {
+ blazeCommandBuilder
+ .addBlazeFlags("--aspects=AndroidStudioInfoAspect")
+ .addBlazeFlags("--output_groups=ide-compile");
+ }
+
+ @Override
public String getAspectOutputFileExtension() {
return ".aswb-build";
}
diff --git a/base/src/com/google/idea/blaze/base/sync/aspects/strategy/AspectStrategyProvider.java b/base/src/com/google/idea/blaze/base/sync/aspects/strategy/AspectStrategyProvider.java
index 0fdd2b5..13c7586 100644
--- a/base/src/com/google/idea/blaze/base/sync/aspects/strategy/AspectStrategyProvider.java
+++ b/base/src/com/google/idea/blaze/base/sync/aspects/strategy/AspectStrategyProvider.java
@@ -15,6 +15,7 @@
*/
package com.google.idea.blaze.base.sync.aspects.strategy;
+import com.google.idea.blaze.base.model.BlazeVersionData;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.project.Project;
import javax.annotation.Nullable;
@@ -25,5 +26,5 @@
ExtensionPointName.create("com.google.idea.blaze.AspectStrategyProvider");
@Nullable
- AspectStrategy getAspectStrategy(Project project);
+ AspectStrategy getAspectStrategy(Project project, BlazeVersionData blazeVersionData);
}
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 9042523..f3b1029 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
@@ -15,14 +15,19 @@
*/
package com.google.idea.blaze.base.sync.aspects.strategy;
+import com.google.idea.blaze.base.model.BlazeVersionData;
import com.google.idea.common.experiments.BoolExperiment;
import com.intellij.openapi.project.Project;
class AspectStrategyProviderBazel implements AspectStrategyProvider {
- BoolExperiment useSkylarkAspect = new BoolExperiment("use.skylark.aspect.bazel", false);
+ private static final BoolExperiment useSkylarkAspect =
+ new BoolExperiment("use.skylark.aspect.bazel", true);
@Override
- public AspectStrategy getAspectStrategy(Project project) {
- return useSkylarkAspect.getValue() ? new AspectStrategySkylark() : new AspectStrategyNative();
+ public AspectStrategy getAspectStrategy(Project project, BlazeVersionData blazeVersionData) {
+ boolean canUseSkylark =
+ useSkylarkAspect.getValue() && blazeVersionData.bazelIsAtLeastVersion(0, 4, 3);
+
+ return canUseSkylark ? new AspectStrategySkylark() : new AspectStrategyNative();
}
}
diff --git a/base/src/com/google/idea/blaze/base/sync/aspects/strategy/AspectStrategySkylark.java b/base/src/com/google/idea/blaze/base/sync/aspects/strategy/AspectStrategySkylark.java
index 0139f87..aa3eadc 100644
--- a/base/src/com/google/idea/blaze/base/sync/aspects/strategy/AspectStrategySkylark.java
+++ b/base/src/com/google/idea/blaze/base/sync/aspects/strategy/AspectStrategySkylark.java
@@ -18,6 +18,7 @@
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.idea.blaze.base.command.BlazeCommand;
+import com.google.idea.blaze.base.command.BlazeCommand.Builder;
import com.google.repackaged.devtools.intellij.ideinfo.IntellijIdeInfo;
import com.google.repackaged.protobuf.TextFormat;
import java.io.IOException;
@@ -33,7 +34,7 @@
}
protected String getAspectFlag() {
- return "--aspects=@bazel_tools://tools/ide/intellij_info.bzl%intellij_info_aspect";
+ return "--aspects=@bazel_tools//tools/ide:intellij_info.bzl%intellij_info_aspect";
}
@Override
@@ -51,6 +52,13 @@
}
@Override
+ public void modifyIdeCompileCommand(Builder blazeCommandBuilder) {
+ blazeCommandBuilder
+ .addBlazeFlags(getAspectFlag())
+ .addBlazeFlags("--output_groups=intellij-compile");
+ }
+
+ @Override
public String getAspectOutputFileExtension() {
return ".intellij-info.txt";
}
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 871274e..d8e72f6 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
@@ -24,6 +24,7 @@
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;
@@ -82,6 +83,15 @@
ImmutableMap<VirtualFile, 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);
walkFileSystem(
workspaceRoot,
@@ -121,11 +131,11 @@
SourceFolder current = sourceFolders.get(file);
SourceFolder currentOrParent = current != null ? current : parent;
if (isTest != currentOrParent.isTestSource()) {
+ currentOrParent =
+ provider.setSourceFolderForLocation(contentEntry, currentOrParent, file, isTest);
if (current != null) {
contentEntry.removeSourceFolder(current);
}
- currentOrParent =
- provider.setSourceFolderForLocation(contentEntry, currentOrParent, file, isTest);
}
for (VirtualFile child : file.getChildren()) {
walkFileSystem(
diff --git a/base/src/com/google/idea/blaze/base/sync/status/BlazeSyncStatus.java b/base/src/com/google/idea/blaze/base/sync/status/BlazeSyncStatus.java
index aa98c7e..a1e3f7c 100644
--- a/base/src/com/google/idea/blaze/base/sync/status/BlazeSyncStatus.java
+++ b/base/src/com/google/idea/blaze/base/sync/status/BlazeSyncStatus.java
@@ -30,10 +30,13 @@
SyncStatus getStatus();
+
static BlazeSyncStatus getInstance(Project project) {
return ServiceManager.getService(project, BlazeSyncStatus.class);
}
+ boolean syncInProgress();
+
void setDirty();
void queueAutomaticSyncIfDirty();
diff --git a/base/src/com/google/idea/blaze/base/sync/status/BlazeSyncStatusImpl.java b/base/src/com/google/idea/blaze/base/sync/status/BlazeSyncStatusImpl.java
index 396f27d..ee60cd7 100644
--- a/base/src/com/google/idea/blaze/base/sync/status/BlazeSyncStatusImpl.java
+++ b/base/src/com/google/idea/blaze/base/sync/status/BlazeSyncStatusImpl.java
@@ -23,7 +23,6 @@
import com.google.idea.blaze.base.sync.BlazeSyncParams.SyncMode;
import com.google.idea.blaze.base.sync.SyncListener.SyncResult;
import com.intellij.openapi.diagnostic.Logger;
-import com.intellij.openapi.editor.Document;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.fileEditor.FileEditorManagerAdapter;
@@ -54,7 +53,7 @@
private final Project project;
- public final AtomicBoolean syncInProgress = new AtomicBoolean(false);
+ private final AtomicBoolean syncInProgress = new AtomicBoolean(false);
private final AtomicBoolean syncPending = new AtomicBoolean(false);
/** has a BUILD file changed since the last sync started */
@@ -86,6 +85,11 @@
return dirty ? SyncStatus.DIRTY : SyncStatus.CLEAN;
}
+ @Override
+ public boolean syncInProgress() {
+ return syncInProgress.get();
+ }
+
public void syncStarted() {
syncPending.set(false);
syncInProgress.set(true);
@@ -189,10 +193,9 @@
private void processEvent(@Nullable VirtualFile file) {
if (isSyncSensitiveFile(file)) {
- FileDocumentManager manager = FileDocumentManager.getInstance();
- Document doc = manager.getCachedDocument(file);
- if (doc != null) {
- manager.saveDocument(doc);
+ FileDocumentManager fileDocumentManager = FileDocumentManager.getInstance();
+ if (fileDocumentManager.isFileModified(file)) {
+ setDirty();
}
}
}
diff --git a/base/src/com/google/idea/blaze/base/targetmaps/TransitiveDependencyMap.java b/base/src/com/google/idea/blaze/base/targetmaps/TransitiveDependencyMap.java
new file mode 100644
index 0000000..f973c8d
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/targetmaps/TransitiveDependencyMap.java
@@ -0,0 +1,79 @@
+/*
+ * 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.targetmaps;
+
+import com.google.common.collect.ImmutableCollection;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Queues;
+import com.google.common.collect.Sets;
+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.sync.data.BlazeProjectDataManager;
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.project.Project;
+import java.util.List;
+import java.util.Queue;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/** Handy class to find all transitive dependencies of a given target */
+public class TransitiveDependencyMap {
+ private final Project project;
+
+ public static TransitiveDependencyMap getInstance(Project project) {
+ return ServiceManager.getService(project, TransitiveDependencyMap.class);
+ }
+
+ public TransitiveDependencyMap(Project project) {
+ this.project = project;
+ }
+
+ public ImmutableCollection<TargetKey> getTransitiveDependencies(TargetKey targetKey) {
+ BlazeProjectData blazeProjectData =
+ BlazeProjectDataManager.getInstance(project).getBlazeProjectData();
+ if (blazeProjectData == null) {
+ return ImmutableSet.of();
+ }
+ // TODO: see if we need caching.
+ return getTransitiveDependencies(targetKey, blazeProjectData.targetMap);
+ }
+
+ private static ImmutableCollection<TargetKey> getTransitiveDependencies(
+ TargetKey targetKey, TargetMap targetMap) {
+ Queue<TargetKey> targetsToVisit = Queues.newArrayDeque();
+ Set<TargetKey> transitiveDependencies = Sets.newHashSet();
+ targetsToVisit.add(targetKey);
+ while (!targetsToVisit.isEmpty()) {
+ TargetIdeInfo currentTarget = targetMap.get(targetsToVisit.remove());
+ if (currentTarget == null) {
+ continue;
+ }
+ List<TargetKey> newDependencies =
+ currentTarget
+ .dependencies
+ .stream()
+ .map(d -> TargetKey.forPlainTarget(d.targetKey.label))
+ // Get rid of the ones we've already seen.
+ .filter(r -> !transitiveDependencies.contains(r))
+ .collect(Collectors.toList());
+ targetsToVisit.addAll(newDependencies);
+ transitiveDependencies.addAll(newDependencies);
+ }
+ return ImmutableSet.copyOf(transitiveDependencies);
+ }
+}
diff --git a/base/src/com/google/idea/blaze/base/util/SaveUtil.java b/base/src/com/google/idea/blaze/base/util/SaveUtil.java
index 6454a7a..949f201 100644
--- a/base/src/com/google/idea/blaze/base/util/SaveUtil.java
+++ b/base/src/com/google/idea/blaze/base/util/SaveUtil.java
@@ -15,18 +15,13 @@
*/
package com.google.idea.blaze.base.util;
+import com.google.idea.sdkcompat.transactions.Transactions;
import com.intellij.openapi.fileEditor.FileDocumentManager;
-import com.intellij.util.ui.UIUtil;
/** Utility for saving all files. */
public class SaveUtil {
public static void saveAllFiles() {
- UIUtil.invokeAndWaitIfNeeded(
- new Runnable() {
- @Override
- public void run() {
- FileDocumentManager.getInstance().saveAllDocuments();
- }
- });
+ Transactions.submitTransactionAndWait(
+ () -> FileDocumentManager.getInstance().saveAllDocuments());
}
}
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 16e6909..b05e69a 100644
--- a/base/src/com/google/idea/blaze/base/vcs/BlazeVcsHandler.java
+++ b/base/src/com/google/idea/blaze/base/vcs/BlazeVcsHandler.java
@@ -20,6 +20,7 @@
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;
@@ -33,6 +34,18 @@
ExtensionPointName<BlazeVcsHandler> EP_NAME =
ExtensionPointName.create("com.google.idea.blaze.VcsHandler");
+ @Nullable
+ static BlazeVcsHandler vcsHandlerForProject(Project project) {
+ BuildSystem buildSystem = Blaze.getBuildSystem(project);
+ WorkspaceRoot workspaceRoot = WorkspaceRoot.fromProject(project);
+ for (BlazeVcsHandler candidate : BlazeVcsHandler.EP_NAME.getExtensions()) {
+ if (candidate.handlesProject(buildSystem, workspaceRoot)) {
+ return candidate;
+ }
+ }
+ return null;
+ }
+
/** Returns the name of this VCS, eg. "git" or "hg" */
String getVcsName();
diff --git a/base/src/com/google/idea/blaze/base/wizard2/BlazeWizardOptionProvider.java b/base/src/com/google/idea/blaze/base/wizard2/BlazeWizardOptionProvider.java
index 20a7e4d..1739755 100644
--- a/base/src/com/google/idea/blaze/base/wizard2/BlazeWizardOptionProvider.java
+++ b/base/src/com/google/idea/blaze/base/wizard2/BlazeWizardOptionProvider.java
@@ -15,16 +15,17 @@
*/
package com.google.idea.blaze.base.wizard2;
-import com.intellij.openapi.extensions.ExtensionPointName;
+import com.intellij.openapi.components.ServiceManager;
import java.util.Collection;
/** Provides options during the import process. */
public interface BlazeWizardOptionProvider {
- ExtensionPointName<BlazeWizardOptionProvider> EP_NAME =
- ExtensionPointName.create("com.google.idea.blaze.BlazeWizardOptionProvider");
-
Collection<BlazeSelectWorkspaceOption> getSelectWorkspaceOptions(BlazeNewProjectBuilder builder);
Collection<BlazeSelectProjectViewOption> getSelectProjectViewOptions(
BlazeNewProjectBuilder builder);
+
+ static BlazeWizardOptionProvider getInstance() {
+ return ServiceManager.getService(BlazeWizardOptionProvider.class);
+ }
}
diff --git a/base/src/com/google/idea/blaze/base/wizard2/CopyExternalProjectViewOption.java b/base/src/com/google/idea/blaze/base/wizard2/CopyExternalProjectViewOption.java
index 62db2c5..ae00949 100644
--- a/base/src/com/google/idea/blaze/base/wizard2/CopyExternalProjectViewOption.java
+++ b/base/src/com/google/idea/blaze/base/wizard2/CopyExternalProjectViewOption.java
@@ -35,14 +35,15 @@
import javax.swing.JComponent;
import javax.swing.JLabel;
-class CopyExternalProjectViewOption implements BlazeSelectProjectViewOption {
+/** Copies an external project view from anywhere on the user's file system */
+public class CopyExternalProjectViewOption implements BlazeSelectProjectViewOption {
private static final String LAST_WORKSPACE_PATH = "copy-external.last-project-view-path";
final BlazeWizardUserSettings userSettings;
final JComponent component;
final TextFieldWithStoredHistory projectViewPathField;
- CopyExternalProjectViewOption(BlazeNewProjectBuilder builder) {
+ public CopyExternalProjectViewOption(BlazeNewProjectBuilder builder) {
this.userSettings = builder.getUserSettings();
this.projectViewPathField = new TextFieldWithStoredHistory(LAST_WORKSPACE_PATH);
diff --git a/base/src/com/google/idea/blaze/base/wizard2/CreateFromScratchProjectViewOption.java b/base/src/com/google/idea/blaze/base/wizard2/CreateFromScratchProjectViewOption.java
index 366e424..3e8ebf7 100644
--- a/base/src/com/google/idea/blaze/base/wizard2/CreateFromScratchProjectViewOption.java
+++ b/base/src/com/google/idea/blaze/base/wizard2/CreateFromScratchProjectViewOption.java
@@ -19,7 +19,8 @@
import javax.annotation.Nullable;
import javax.swing.JComponent;
-class CreateFromScratchProjectViewOption implements BlazeSelectProjectViewOption {
+/** Creates an empty project view */
+public class CreateFromScratchProjectViewOption implements BlazeSelectProjectViewOption {
@Override
public String getOptionName() {
return "create-from-scratch";
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 e2e41d9..b2edeff 100644
--- a/base/src/com/google/idea/blaze/base/wizard2/GenerateFromBuildFileSelectProjectViewOption.java
+++ b/base/src/com/google/idea/blaze/base/wizard2/GenerateFromBuildFileSelectProjectViewOption.java
@@ -45,7 +45,8 @@
import javax.swing.JComponent;
import javax.swing.JLabel;
-class GenerateFromBuildFileSelectProjectViewOption implements BlazeSelectProjectViewOption {
+/** Generates a project view given a BUILD file */
+public class GenerateFromBuildFileSelectProjectViewOption implements BlazeSelectProjectViewOption {
private static final String LAST_WORKSPACE_PATH = "generate-from-build-file.last-workspace-path";
private final BlazeNewProjectBuilder builder;
private final BlazeWizardUserSettings userSettings;
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 c2d47e1..bdec2eb 100644
--- a/base/src/com/google/idea/blaze/base/wizard2/ImportFromWorkspaceProjectViewOption.java
+++ b/base/src/com/google/idea/blaze/base/wizard2/ImportFromWorkspaceProjectViewOption.java
@@ -39,7 +39,8 @@
import javax.swing.JComponent;
import javax.swing.JLabel;
-class ImportFromWorkspaceProjectViewOption implements BlazeSelectProjectViewOption {
+/** Imports a project view from source control */
+public class ImportFromWorkspaceProjectViewOption implements BlazeSelectProjectViewOption {
private static final String LAST_WORKSPACE_PATH = "import-from-workspace.last-workspace-path";
final BlazeNewProjectBuilder builder;
@@ -47,7 +48,7 @@
final JComponent component;
final TextFieldWithStoredHistory projectViewPathField;
- ImportFromWorkspaceProjectViewOption(BlazeNewProjectBuilder builder) {
+ public ImportFromWorkspaceProjectViewOption(BlazeNewProjectBuilder builder) {
this.builder = builder;
this.userSettings = builder.getUserSettings();
diff --git a/base/src/com/google/idea/blaze/base/wizard2/UseExistingBazelWorkspaceOption.java b/base/src/com/google/idea/blaze/base/wizard2/UseExistingBazelWorkspaceOption.java
index 2b8c058..51af079 100644
--- a/base/src/com/google/idea/blaze/base/wizard2/UseExistingBazelWorkspaceOption.java
+++ b/base/src/com/google/idea/blaze/base/wizard2/UseExistingBazelWorkspaceOption.java
@@ -36,12 +36,13 @@
import javax.swing.JComponent;
import javax.swing.JLabel;
-class UseExistingBazelWorkspaceOption implements BlazeSelectWorkspaceOption {
+/** Allows importing an existing bazel workspace */
+public class UseExistingBazelWorkspaceOption implements BlazeSelectWorkspaceOption {
private final JComponent component;
private final TextFieldWithHistory directoryField;
- UseExistingBazelWorkspaceOption(BlazeNewProjectBuilder builder) {
+ public UseExistingBazelWorkspaceOption(BlazeNewProjectBuilder builder) {
this.directoryField = new TextFieldWithHistory();
this.directoryField.setHistory(builder.getWorkspaceHistory(BuildSystem.Bazel));
this.directoryField.setHistorySize(BlazeNewProjectBuilder.HISTORY_SIZE);
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 0307b5a..4f3be05 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
@@ -92,6 +92,7 @@
private JTextField projectNameField;
private HashCode paramsHash;
private WorkspaceRoot workspaceRoot;
+ private WorkspacePathResolver workspacePathResolver;
public BlazeEditProjectViewControl(BlazeNewProjectBuilder builder, Disposable parentDisposable) {
this.projectViewUi = new ProjectViewUi(parentDisposable);
@@ -209,6 +210,7 @@
}
this.workspaceRoot = workspaceRoot;
+ this.workspacePathResolver = workspacePathResolver;
projectNameField.setText(workspaceName);
String defaultDataDir = getDefaultProjectDataDirectory(workspaceName);
projectDataDirField.setText(defaultDataDir);
@@ -328,7 +330,8 @@
return BlazeValidationResult.failure(projectViewParseError);
}
- ProjectViewValidator projectViewValidator = new ProjectViewValidator(projectViewSet);
+ ProjectViewValidator projectViewValidator =
+ new ProjectViewValidator(workspacePathResolver, projectViewSet);
ProgressManager.getInstance()
.runProcessWithProgressSynchronously(
projectViewValidator, "Validating Project", false, null);
@@ -347,12 +350,15 @@
}
private static class ProjectViewValidator implements Runnable {
+ private final WorkspacePathResolver workspacePathResolver;
private final ProjectViewSet projectViewSet;
private boolean success;
List<IssueOutput> errors = Lists.newArrayList();
- ProjectViewValidator(ProjectViewSet projectViewSet) {
+ ProjectViewValidator(
+ WorkspacePathResolver workspacePathResolver, ProjectViewSet projectViewSet) {
+ this.workspacePathResolver = workspacePathResolver;
this.projectViewSet = projectViewSet;
}
@@ -379,8 +385,8 @@
if (workspaceLanguageSettings == null) {
return false;
}
- return ProjectViewVerifier.verifyProjectViewNoDisk(
- context, projectViewSet, workspaceLanguageSettings);
+ return ProjectViewVerifier.verifyProjectView(
+ context, workspacePathResolver, projectViewSet, workspaceLanguageSettings);
}
}
diff --git a/base/src/com/google/idea/blaze/base/wizard2/ui/BlazeSelectProjectViewControl.java b/base/src/com/google/idea/blaze/base/wizard2/ui/BlazeSelectProjectViewControl.java
index a2b6467..4dc04cd 100644
--- a/base/src/com/google/idea/blaze/base/wizard2/ui/BlazeSelectProjectViewControl.java
+++ b/base/src/com/google/idea/blaze/base/wizard2/ui/BlazeSelectProjectViewControl.java
@@ -15,7 +15,6 @@
*/
package com.google.idea.blaze.base.wizard2.ui;
-import com.google.common.collect.Lists;
import com.google.idea.blaze.base.projectview.ProjectViewStorageManager;
import com.google.idea.blaze.base.ui.BlazeValidationResult;
import com.google.idea.blaze.base.wizard2.BlazeNewProjectBuilder;
@@ -30,11 +29,8 @@
private BlazeSelectOptionControl<BlazeSelectProjectViewOption> selectOptionControl;
public BlazeSelectProjectViewControl(BlazeNewProjectBuilder builder) {
- Collection<BlazeSelectProjectViewOption> options = Lists.newArrayList();
- for (BlazeWizardOptionProvider optionProvider :
- BlazeWizardOptionProvider.EP_NAME.getExtensions()) {
- options.addAll(optionProvider.getSelectProjectViewOptions(builder));
- }
+ Collection<BlazeSelectProjectViewOption> options =
+ BlazeWizardOptionProvider.getInstance().getSelectProjectViewOptions(builder);
this.selectOptionControl =
new BlazeSelectOptionControl<BlazeSelectProjectViewOption>(builder, options) {
diff --git a/base/src/com/google/idea/blaze/base/wizard2/ui/BlazeSelectWorkspaceControl.java b/base/src/com/google/idea/blaze/base/wizard2/ui/BlazeSelectWorkspaceControl.java
index f0c6b45..e24b446 100644
--- a/base/src/com/google/idea/blaze/base/wizard2/ui/BlazeSelectWorkspaceControl.java
+++ b/base/src/com/google/idea/blaze/base/wizard2/ui/BlazeSelectWorkspaceControl.java
@@ -15,7 +15,6 @@
*/
package com.google.idea.blaze.base.wizard2.ui;
-import com.google.common.collect.Lists;
import com.google.idea.blaze.base.ui.BlazeValidationResult;
import com.google.idea.blaze.base.wizard2.BlazeNewProjectBuilder;
import com.google.idea.blaze.base.wizard2.BlazeSelectWorkspaceOption;
@@ -28,11 +27,8 @@
BlazeSelectOptionControl<BlazeSelectWorkspaceOption> selectOptionControl;
public BlazeSelectWorkspaceControl(BlazeNewProjectBuilder builder) {
- Collection<BlazeSelectWorkspaceOption> options = Lists.newArrayList();
- for (BlazeWizardOptionProvider optionProvider :
- BlazeWizardOptionProvider.EP_NAME.getExtensions()) {
- options.addAll(optionProvider.getSelectWorkspaceOptions(builder));
- }
+ Collection<BlazeSelectWorkspaceOption> options =
+ BlazeWizardOptionProvider.getInstance().getSelectWorkspaceOptions(builder);
this.selectOptionControl =
new BlazeSelectOptionControl<BlazeSelectWorkspaceOption>(builder, options) {
diff --git a/base/tests/integrationtests/com/google/idea/blaze/base/lang/projectview/ProjectViewIntegrationTestCase.java b/base/tests/integrationtests/com/google/idea/blaze/base/lang/projectview/ProjectViewIntegrationTestCase.java
index 361b0bb..960660e 100644
--- a/base/tests/integrationtests/com/google/idea/blaze/base/lang/projectview/ProjectViewIntegrationTestCase.java
+++ b/base/tests/integrationtests/com/google/idea/blaze/base/lang/projectview/ProjectViewIntegrationTestCase.java
@@ -21,11 +21,12 @@
import com.google.idea.blaze.base.EditorTestHelper;
import com.google.idea.blaze.base.ideinfo.TargetMap;
import com.google.idea.blaze.base.model.BlazeProjectData;
+import com.google.idea.blaze.base.model.BlazeVersionData;
+import com.google.idea.blaze.base.model.SyncState;
import com.google.idea.blaze.base.model.primitives.ExecutionRootPath;
import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoder;
import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoderImpl;
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.google.idea.blaze.base.sync.workspace.WorkspacePathResolverImpl;
import org.junit.Before;
@@ -57,12 +58,11 @@
new TargetMap(ImmutableMap.of()),
ImmutableMap.of(),
fakeRoots,
- new WorkingSet(ImmutableList.of(), ImmutableList.of(), ImmutableList.of()),
+ new BlazeVersionData(),
workspacePathResolver,
artifactLocationDecoder,
null,
- null,
- null,
+ new SyncState.Builder().build(),
null);
}
}
diff --git a/base/tests/integrationtests/com/google/idea/blaze/base/run/BlazeCommandRunConfigurationGenericHandlerIntegrationTest.java b/base/tests/integrationtests/com/google/idea/blaze/base/run/BlazeCommandRunConfigurationGenericHandlerIntegrationTest.java
index c4f5242..eb8d69f 100644
--- a/base/tests/integrationtests/com/google/idea/blaze/base/run/BlazeCommandRunConfigurationGenericHandlerIntegrationTest.java
+++ b/base/tests/integrationtests/com/google/idea/blaze/base/run/BlazeCommandRunConfigurationGenericHandlerIntegrationTest.java
@@ -23,6 +23,7 @@
import com.google.idea.blaze.base.command.BlazeCommandName;
import com.google.idea.blaze.base.ideinfo.TargetMap;
import com.google.idea.blaze.base.model.BlazeProjectData;
+import com.google.idea.blaze.base.model.BlazeVersionData;
import com.google.idea.blaze.base.model.primitives.ExecutionRootPath;
import com.google.idea.blaze.base.model.primitives.TargetExpression;
import com.google.idea.blaze.base.run.BlazeCommandRunConfiguration.BlazeCommandRunConfigurationSettingsEditor;
@@ -31,7 +32,6 @@
import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoder;
import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoderImpl;
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.google.idea.blaze.base.sync.workspace.WorkspacePathResolverImpl;
import com.intellij.openapi.options.ConfigurationException;
@@ -79,12 +79,11 @@
new TargetMap(ImmutableMap.of()),
ImmutableMap.of(),
fakeRoots,
- new WorkingSet(ImmutableList.of(), ImmutableList.of(), ImmutableList.of()),
+ new BlazeVersionData(),
workspacePathResolver,
artifactLocationDecoder,
null,
null,
- null,
null);
}
diff --git a/base/tests/integrationtests/com/google/idea/blaze/base/run/BlazeCommandRunConfigurationRunManagerImplTest.java b/base/tests/integrationtests/com/google/idea/blaze/base/run/BlazeCommandRunConfigurationRunManagerImplTest.java
index 6395e65..237fa7f 100644
--- a/base/tests/integrationtests/com/google/idea/blaze/base/run/BlazeCommandRunConfigurationRunManagerImplTest.java
+++ b/base/tests/integrationtests/com/google/idea/blaze/base/run/BlazeCommandRunConfigurationRunManagerImplTest.java
@@ -23,13 +23,13 @@
import com.google.idea.blaze.base.BlazeIntegrationTestCase;
import com.google.idea.blaze.base.ideinfo.TargetMap;
import com.google.idea.blaze.base.model.BlazeProjectData;
+import com.google.idea.blaze.base.model.BlazeVersionData;
import com.google.idea.blaze.base.model.primitives.ExecutionRootPath;
import com.google.idea.blaze.base.model.primitives.Label;
import com.google.idea.blaze.base.run.BlazeCommandRunConfiguration.BlazeCommandRunConfigurationSettingsEditor;
import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoder;
import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoderImpl;
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.google.idea.blaze.base.sync.workspace.WorkspacePathResolverImpl;
import com.intellij.execution.RunnerAndConfigurationSettings;
@@ -89,12 +89,11 @@
new TargetMap(ImmutableMap.of()),
ImmutableMap.of(),
fakeRoots,
- new WorkingSet(ImmutableList.of(), ImmutableList.of(), ImmutableList.of()),
+ new BlazeVersionData(),
workspacePathResolver,
artifactLocationDecoder,
null,
null,
- null,
null);
}
diff --git a/base/tests/integrationtests/com/google/idea/blaze/base/run/BlazeCommandRunConfigurationSettingsEditorTest.java b/base/tests/integrationtests/com/google/idea/blaze/base/run/BlazeCommandRunConfigurationSettingsEditorTest.java
index 336930a..b9a2b48 100644
--- a/base/tests/integrationtests/com/google/idea/blaze/base/run/BlazeCommandRunConfigurationSettingsEditorTest.java
+++ b/base/tests/integrationtests/com/google/idea/blaze/base/run/BlazeCommandRunConfigurationSettingsEditorTest.java
@@ -22,13 +22,13 @@
import com.google.idea.blaze.base.BlazeIntegrationTestCase;
import com.google.idea.blaze.base.ideinfo.TargetMap;
import com.google.idea.blaze.base.model.BlazeProjectData;
+import com.google.idea.blaze.base.model.BlazeVersionData;
import com.google.idea.blaze.base.model.primitives.ExecutionRootPath;
import com.google.idea.blaze.base.model.primitives.Label;
import com.google.idea.blaze.base.run.BlazeCommandRunConfiguration.BlazeCommandRunConfigurationSettingsEditor;
import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoder;
import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoderImpl;
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.google.idea.blaze.base.sync.workspace.WorkspacePathResolverImpl;
import com.intellij.openapi.options.ConfigurationException;
@@ -70,12 +70,11 @@
new TargetMap(ImmutableMap.of()),
ImmutableMap.of(),
fakeRoots,
- new WorkingSet(ImmutableList.of(), ImmutableList.of(), ImmutableList.of()),
+ new BlazeVersionData(),
workspacePathResolver,
artifactLocationDecoder,
null,
null,
- null,
null);
}
diff --git a/base/tests/integrationtests/com/google/idea/blaze/base/run/exporter/RunConfigurationSerializerTest.java b/base/tests/integrationtests/com/google/idea/blaze/base/run/exporter/RunConfigurationSerializerTest.java
new file mode 100644
index 0000000..fd5896b
--- /dev/null
+++ b/base/tests/integrationtests/com/google/idea/blaze/base/run/exporter/RunConfigurationSerializerTest.java
@@ -0,0 +1,157 @@
+/*
+ * 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.exporter;
+
+import static com.google.common.truth.Truth.assertThat;
+
+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.ideinfo.TargetMap;
+import com.google.idea.blaze.base.model.BlazeProjectData;
+import com.google.idea.blaze.base.model.BlazeVersionData;
+import com.google.idea.blaze.base.model.primitives.ExecutionRootPath;
+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.sync.workspace.ArtifactLocationDecoder;
+import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoderImpl;
+import com.google.idea.blaze.base.sync.workspace.BlazeRoots;
+import com.google.idea.blaze.base.sync.workspace.WorkspacePathResolver;
+import com.google.idea.blaze.base.sync.workspace.WorkspacePathResolverImpl;
+import com.intellij.execution.RunnerAndConfigurationSettings;
+import com.intellij.execution.configurations.ConfigurationType;
+import com.intellij.execution.configurations.RunConfiguration;
+import com.intellij.execution.impl.RunManagerImpl;
+import com.intellij.openapi.util.InvalidDataException;
+import org.jdom.Element;
+import org.jdom.output.Format;
+import org.jdom.output.XMLOutputter;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Test that {@link RunConfigurationSerializer} serializes/deserializes run configurations
+ * correctly.
+ */
+@RunWith(JUnit4.class)
+public class RunConfigurationSerializerTest extends BlazeIntegrationTestCase {
+
+ private RunManagerImpl runManager;
+ private BlazeCommandRunConfigurationType type;
+ private BlazeCommandRunConfiguration configuration;
+
+ @Before
+ public final void doSetup() {
+ runManager = RunManagerImpl.getInstanceImpl(getProject());
+ // Without BlazeProjectData, the configuration editor is always disabled.
+ mockBlazeProjectDataManager(getMockBlazeProjectData());
+ type = BlazeCommandRunConfigurationType.getInstance();
+
+ RunnerAndConfigurationSettings runnerAndConfigurationSettings =
+ runManager.createConfiguration("Blaze Configuration", type.getFactory());
+ runManager.addConfiguration(runnerAndConfigurationSettings, false);
+ configuration =
+ (BlazeCommandRunConfiguration) runnerAndConfigurationSettings.getConfiguration();
+ }
+
+ private BlazeProjectData getMockBlazeProjectData() {
+ BlazeRoots fakeRoots =
+ new BlazeRoots(
+ null,
+ ImmutableList.of(workspaceRoot.directory()),
+ new ExecutionRootPath("out/crosstool/bin"),
+ new ExecutionRootPath("out/crosstool/gen"),
+ null);
+ WorkspacePathResolver workspacePathResolver =
+ new WorkspacePathResolverImpl(workspaceRoot, fakeRoots);
+ ArtifactLocationDecoder artifactLocationDecoder =
+ new ArtifactLocationDecoderImpl(fakeRoots, workspacePathResolver);
+ return new BlazeProjectData(
+ 0,
+ new TargetMap(ImmutableMap.of()),
+ ImmutableMap.of(),
+ fakeRoots,
+ new BlazeVersionData(),
+ workspacePathResolver,
+ artifactLocationDecoder,
+ null,
+ null,
+ null);
+ }
+
+ @After
+ public final void doTeardown() {
+ clearRunManager();
+ }
+
+ private void clearRunManager() {
+ runManager.clearAll();
+ // We don't need to do this at setup, because it is handled by RunManagerImpl's constructor.
+ // However, clearAll() clears the configuration types, so we need to reinitialize them.
+ runManager.initializeConfigurationTypes(
+ ConfigurationType.CONFIGURATION_TYPE_EP.getExtensions());
+ }
+
+ @Test
+ public void testRunConfigurationUnalteredBySerializationRoundTrip() throws InvalidDataException {
+ configuration.setTarget(new Label("//package:rule"));
+ configuration.setKeepInSync(true);
+
+ final Element initialElement = runManager.getState();
+
+ Element element = RunConfigurationSerializer.writeToXml(configuration);
+ assertThat(RunConfigurationSerializer.findExisting(getProject(), element)).isNotNull();
+
+ clearRunManager(); // remove configuration from project
+ RunConfigurationSerializer.loadFromXmlElementIgnoreExisting(getProject(), element);
+
+ final Element newElement = runManager.getState();
+ final XMLOutputter xmlOutputter = new XMLOutputter(Format.getCompactFormat());
+ assertThat(xmlOutputter.outputString(newElement))
+ .isEqualTo(xmlOutputter.outputString(initialElement));
+ }
+
+ @Test
+ public void testSetKeepInSyncWhenImporting() throws InvalidDataException {
+ configuration.setTarget(new Label("//package:rule"));
+ configuration.setKeepInSync(false);
+
+ Element element = RunConfigurationSerializer.writeToXml(configuration);
+ assertThat(RunConfigurationSerializer.findExisting(getProject(), element)).isNotNull();
+
+ clearRunManager(); // remove configuration from project
+ RunConfigurationSerializer.loadFromXmlElementIgnoreExisting(getProject(), element);
+
+ RunConfiguration config = runManager.getAllConfigurations()[0];
+ assertThat(config).isInstanceOf(BlazeCommandRunConfiguration.class);
+ assertThat(((BlazeCommandRunConfiguration) config).getKeepInSync()).isTrue();
+ }
+
+ @Test
+ public void testKeepInSyncRespectedWhenImporting() throws InvalidDataException {
+ Element element = RunConfigurationSerializer.writeToXml(configuration);
+
+ configuration.setKeepInSync(false);
+ assertThat(RunConfigurationSerializer.shouldLoadConfiguration(getProject(), element)).isFalse();
+
+ configuration.setKeepInSync(true);
+ assertThat(RunConfigurationSerializer.shouldLoadConfiguration(getProject(), element)).isTrue();
+ }
+}
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
new file mode 100644
index 0000000..e86bd3f
--- /dev/null
+++ b/base/tests/unittests/com/google/idea/blaze/base/actions/BlazeBuildServiceTest.java
@@ -0,0 +1,145 @@
+/*
+ * 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.actions;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import com.google.common.collect.ImmutableCollection;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import com.google.idea.blaze.base.BlazeTestCase;
+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.ProjectView;
+import com.google.idea.blaze.base.projectview.ProjectViewManager;
+import com.google.idea.blaze.base.projectview.ProjectViewSet;
+import com.google.idea.blaze.base.projectview.section.ListSection;
+import com.google.idea.blaze.base.projectview.section.sections.TargetSection;
+import com.google.idea.blaze.base.scope.BlazeContext;
+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.blaze.base.sync.BlazeSyncPlugin.ModuleEditor;
+import com.google.idea.blaze.base.sync.data.BlazeProjectDataManager;
+import com.google.idea.blaze.base.sync.workspace.WorkspacePathResolver;
+import java.io.File;
+import java.util.List;
+import javax.annotation.Nullable;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Test cases for {@link BlazeBuildService}. */
+@RunWith(JUnit4.class)
+public class BlazeBuildServiceTest extends BlazeTestCase {
+ BlazeBuildService service;
+ ProjectViewSet viewSet;
+
+ @Override
+ protected void initTest(Container applicationServices, Container projectServices) {
+ BlazeImportSettingsManager importSettingsManager = new BlazeImportSettingsManager(project);
+ importSettingsManager.setImportSettings(
+ new BlazeImportSettings("", "", "", "", "", Blaze.BuildSystem.Blaze));
+ projectServices.register(BlazeImportSettingsManager.class, importSettingsManager);
+
+ ProjectView view =
+ ProjectView.builder()
+ .add(
+ ListSection.builder(TargetSection.KEY)
+ .add(TargetExpression.fromString("//view/target:one"))
+ .add(TargetExpression.fromString("//view/target:two")))
+ .build();
+ viewSet = ProjectViewSet.builder().add(new File("view/target/.blazeproject"), view).build();
+ ProjectViewManager viewManager = new MockProjectViewManager(viewSet);
+ projectServices.register(ProjectViewManager.class, viewManager);
+
+ projectServices.register(BlazeProjectDataManager.class, new MockProjectDataManager());
+
+ applicationServices.register(BlazeBuildService.class, spy(new BlazeBuildService()));
+
+ service = BlazeBuildService.getInstance();
+ assertThat(service).isNotNull();
+
+ // Can't mock BlazeExecutor.submitTask.
+ doNothing().when(service).buildTargetExpressions(any(), any(), any(), any(), any());
+ }
+
+ @Test
+ public void testBuildFile() {
+ ImmutableCollection<Label> labels =
+ 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());
+ }
+
+ @Test
+ public void testBuildProject() {
+ service.buildProject(project);
+ List<TargetExpression> targets =
+ Lists.newArrayList(
+ TargetExpression.fromString("//view/target:one"),
+ TargetExpression.fromString("//view/target:two"));
+ verify(service).buildTargetExpressions(eq(project), eq(targets), eq(viewSet), any(), any());
+ }
+
+ private static class MockProjectViewManager extends ProjectViewManager {
+ private final ProjectViewSet viewSet;
+
+ public MockProjectViewManager(ProjectViewSet viewSet) {
+ this.viewSet = viewSet;
+ }
+
+ @Nullable
+ @Override
+ public ProjectViewSet getProjectViewSet() {
+ return viewSet;
+ }
+
+ @Nullable
+ @Override
+ public ProjectViewSet reloadProjectView(
+ BlazeContext context, WorkspacePathResolver workspacePathResolver) {
+ return viewSet;
+ }
+ }
+
+ private static class MockProjectDataManager implements BlazeProjectDataManager {
+ private final BlazeProjectData projectData;
+
+ public MockProjectDataManager() {
+ this.projectData =
+ new BlazeProjectData(0L, null, null, null, null, null, null, null, null, null);
+ }
+
+ @Nullable
+ @Override
+ public BlazeProjectData getBlazeProjectData() {
+ return projectData;
+ }
+
+ @Override
+ public ModuleEditor editModules() {
+ return null;
+ }
+ }
+}
diff --git a/base/tests/unittests/com/google/idea/blaze/base/bazel/BazelVersionTest.java b/base/tests/unittests/com/google/idea/blaze/base/bazel/BazelVersionTest.java
new file mode 100644
index 0000000..5860e40
--- /dev/null
+++ b/base/tests/unittests/com/google/idea/blaze/base/bazel/BazelVersionTest.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.bazel;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Unit tests for {@link BazelVersion}. */
+@RunWith(JUnit4.class)
+public class BazelVersionTest {
+
+ @Test
+ public void testParseOldVersionFormat() {
+ BazelVersion version = BazelVersion.parseVersion("release 0.4.1");
+ assertThat(version).isNotNull();
+ assertThat(version.major).isEqualTo(0);
+ assertThat(version.minor).isEqualTo(4);
+ assertThat(version.bugfix).isEqualTo(1);
+ }
+
+ @Test
+ public void testParseVersionFormatDistributionPackage() {
+ BazelVersion version = BazelVersion.parseVersion("release 0.4.3- (@non-git)");
+ assertThat(version).isNotNull();
+ assertThat(version.major).isEqualTo(0);
+ assertThat(version.minor).isEqualTo(4);
+ assertThat(version.bugfix).isEqualTo(3);
+ }
+
+ @Test
+ public void testParseVersionFormatManualBuild() {
+ BazelVersion version = BazelVersion.parseVersion("release 0.4.3- (@c9139896");
+ assertThat(version).isNotNull();
+ assertThat(version.major).isEqualTo(0);
+ assertThat(version.minor).isEqualTo(4);
+ assertThat(version.bugfix).isEqualTo(3);
+ }
+
+ @Test
+ public void testParseVersionFormatManualOld() {
+ BazelVersion version = BazelVersion.parseVersion("development version");
+ assertThat(version).isEqualTo(BazelVersion.UNKNOWN);
+ }
+
+ @Test
+ public void testIsAtLeast() {
+ BazelVersion version = BazelVersion.parseVersion("release 0.4.1");
+ assertThat(version).isNotNull();
+ assertThat(version.isAtLeast(0, 3, 2)).isTrue();
+ assertThat(version.isAtLeast(0, 4, 0)).isTrue();
+ assertThat(version.isAtLeast(0, 4, 1)).isTrue();
+ assertThat(version.isAtLeast(0, 4, 2)).isFalse();
+ assertThat(version.isAtLeast(0, 5, 0)).isFalse();
+ }
+}
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 40ea19f..ed1fd3a 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
@@ -39,6 +39,7 @@
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;
import com.google.idea.blaze.base.projectview.section.sections.TestSourceSection;
@@ -91,6 +92,9 @@
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)
+ .add(new WorkspacePath("test")))
.build())
.build();
diff --git a/base/tests/unittests/com/google/idea/blaze/base/projectview/ProjectViewVerifierTest.java b/base/tests/unittests/com/google/idea/blaze/base/projectview/ProjectViewVerifierTest.java
index 4d27c8e..fe15957 100644
--- a/base/tests/unittests/com/google/idea/blaze/base/projectview/ProjectViewVerifierTest.java
+++ b/base/tests/unittests/com/google/idea/blaze/base/projectview/ProjectViewVerifierTest.java
@@ -33,6 +33,8 @@
import com.google.idea.blaze.base.sync.BlazeSyncPlugin;
import com.google.idea.blaze.base.sync.projectview.ImportRoots;
import com.google.idea.blaze.base.sync.projectview.WorkspaceLanguageSettings;
+import com.google.idea.blaze.base.sync.workspace.WorkspacePathResolver;
+import com.google.idea.blaze.base.sync.workspace.WorkspacePathResolverImpl;
import java.io.File;
import java.util.Set;
import org.jetbrains.annotations.NotNull;
@@ -46,6 +48,8 @@
private static final String FAKE_ROOT = "/root";
private WorkspaceRoot workspaceRoot = new WorkspaceRoot(new File(FAKE_ROOT));
+ private WorkspacePathResolver workspacePathResolver =
+ new WorkspacePathResolverImpl(workspaceRoot);
private MockFileAttributeProvider fileAttributeProvider;
private ErrorCollector errorCollector = new ErrorCollector();
private BlazeContext context;
@@ -82,7 +86,7 @@
.build();
fileAttributeProvider.addProjectView(projectViewSet);
ProjectViewVerifier.verifyProjectView(
- context, workspaceRoot, projectViewSet, workspaceLanguageSettings);
+ context, workspacePathResolver, projectViewSet, workspaceLanguageSettings);
errorCollector.assertNoIssues();
}
@@ -104,7 +108,7 @@
.build();
fileAttributeProvider.addProjectView(projectViewSet);
ProjectViewVerifier.verifyProjectView(
- context, workspaceRoot, projectViewSet, workspaceLanguageSettings);
+ context, workspacePathResolver, projectViewSet, workspaceLanguageSettings);
errorCollector.assertIssues(
"java/com/google/android/apps/example is included, "
+ "but that contradicts java/com/google/android/apps/example which was excluded");
@@ -128,7 +132,7 @@
.build();
fileAttributeProvider.addProjectView(projectViewSet);
ProjectViewVerifier.verifyProjectView(
- context, workspaceRoot, projectViewSet, workspaceLanguageSettings);
+ context, workspacePathResolver, projectViewSet, workspaceLanguageSettings);
errorCollector.assertIssues(
"java/com/google/android/apps/example is included, "
+ "but that contradicts java/com/google/android/apps which was excluded");
@@ -153,7 +157,7 @@
.build();
fileAttributeProvider.addProjectView(projectViewSet);
ProjectViewVerifier.verifyProjectView(
- context, workspaceRoot, projectViewSet, workspaceLanguageSettings);
+ context, workspacePathResolver, projectViewSet, workspaceLanguageSettings);
errorCollector.assertNoIssues();
}
@@ -171,11 +175,33 @@
.build())
.build();
ProjectViewVerifier.verifyProjectView(
- context, workspaceRoot, projectViewSet, workspaceLanguageSettings);
+ context, workspacePathResolver, projectViewSet, workspaceLanguageSettings);
errorCollector.assertIssues(
String.format(
- "Directory '%s' specified in import roots not found under workspace root '%s'",
- "java/com/google/android/apps/example", "/root"));
+ "Directory '%s' specified in project view not found.",
+ "java/com/google/android/apps/example"));
+ }
+
+ @Test
+ public void testImportRootIsFileResultsInIssue() {
+ ProjectViewSet projectViewSet =
+ ProjectViewSet.builder()
+ .add(
+ ProjectView.builder()
+ .add(
+ ListSection.builder(DirectorySection.KEY)
+ .add(
+ DirectoryEntry.include(
+ new WorkspacePath("java/com/google/android/apps/example"))))
+ .build())
+ .build();
+ fileAttributeProvider.addFile(new WorkspacePath("java/com/google/android/apps/example"));
+ ProjectViewVerifier.verifyProjectView(
+ context, workspacePathResolver, projectViewSet, workspaceLanguageSettings);
+ errorCollector.assertIssues(
+ String.format(
+ "Directory '%s' specified in project view is a file.",
+ "java/com/google/android/apps/example"));
}
static class MockFileAttributeProvider extends FileAttributeProvider {
@@ -228,5 +254,10 @@
public boolean exists(File file) {
return files.contains(file);
}
+
+ @Override
+ public boolean isDirectory(File file) {
+ return directories.contains(file);
+ }
}
}
diff --git a/base/tests/unittests/com/google/idea/blaze/base/run/state/BlazeCommandRunConfigurationCommonStateTest.java b/base/tests/unittests/com/google/idea/blaze/base/run/state/BlazeCommandRunConfigurationCommonStateTest.java
index c8eaeec..5598b49 100644
--- a/base/tests/unittests/com/google/idea/blaze/base/run/state/BlazeCommandRunConfigurationCommonStateTest.java
+++ b/base/tests/unittests/com/google/idea/blaze/base/run/state/BlazeCommandRunConfigurationCommonStateTest.java
@@ -21,6 +21,7 @@
import com.google.common.collect.Lists;
import com.google.idea.blaze.base.BlazeTestCase;
import com.google.idea.blaze.base.command.BlazeCommandName;
+import com.google.idea.blaze.base.run.DistributedExecutorSupport;
import com.google.idea.blaze.base.settings.Blaze;
import com.google.idea.blaze.base.settings.BlazeImportSettings;
import com.google.idea.blaze.base.settings.BlazeImportSettingsManager;
@@ -28,7 +29,6 @@
import org.jdom.Element;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;
-import org.jetbrains.annotations.NotNull;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -43,8 +43,7 @@
private BlazeCommandRunConfigurationCommonState state;
@Override
- protected void initTest(
- @NotNull Container applicationServices, @NotNull Container projectServices) {
+ protected void initTest(Container applicationServices, Container projectServices) {
super.initTest(applicationServices, projectServices);
applicationServices.register(UISettings.class, new UISettings());
@@ -52,7 +51,9 @@
BlazeImportSettingsManager.class, new BlazeImportSettingsManager(project));
BlazeImportSettingsManager.getInstance(getProject()).setImportSettings(DUMMY_IMPORT_SETTINGS);
- state = new BlazeCommandRunConfigurationCommonState(Blaze.buildSystemName(project));
+ registerExtensionPoint(DistributedExecutorSupport.EP_NAME, DistributedExecutorSupport.class);
+
+ state = new BlazeCommandRunConfigurationCommonState(Blaze.getBuildSystem(project));
}
@Test
@@ -65,7 +66,7 @@
Element element = new Element("test");
state.writeExternal(element);
BlazeCommandRunConfigurationCommonState readState =
- new BlazeCommandRunConfigurationCommonState(Blaze.buildSystemName(project));
+ new BlazeCommandRunConfigurationCommonState(Blaze.getBuildSystem(project));
readState.readExternal(element);
assertThat(readState.getCommand()).isEqualTo(COMMAND);
@@ -79,7 +80,7 @@
Element element = new Element("test");
state.writeExternal(element);
BlazeCommandRunConfigurationCommonState readState =
- new BlazeCommandRunConfigurationCommonState(Blaze.buildSystemName(project));
+ new BlazeCommandRunConfigurationCommonState(Blaze.getBuildSystem(project));
readState.readExternal(element);
assertThat(readState.getCommand()).isEqualTo(state.getCommand());
@@ -96,7 +97,7 @@
Element element = new Element("test");
state.writeExternal(element);
BlazeCommandRunConfigurationCommonState readState =
- new BlazeCommandRunConfigurationCommonState(Blaze.buildSystemName(project));
+ new BlazeCommandRunConfigurationCommonState(Blaze.getBuildSystem(project));
readState.readExternal(element);
assertThat(readState.getBlazeFlags()).containsExactly("hi", "I'm", "Josh").inOrder();
@@ -132,7 +133,7 @@
editor.resetEditorFrom(state);
BlazeCommandRunConfigurationCommonState readState =
- new BlazeCommandRunConfigurationCommonState(Blaze.buildSystemName(project));
+ new BlazeCommandRunConfigurationCommonState(Blaze.getBuildSystem(project));
editor.applyEditorTo(readState);
assertThat(readState.getCommand()).isEqualTo(state.getCommand());
@@ -147,7 +148,7 @@
editor.resetEditorFrom(state);
BlazeCommandRunConfigurationCommonState readState =
- new BlazeCommandRunConfigurationCommonState(Blaze.buildSystemName(project));
+ new BlazeCommandRunConfigurationCommonState(Blaze.getBuildSystem(project));
editor.applyEditorTo(readState);
assertThat(readState.getCommand()).isEqualTo(state.getCommand());
diff --git a/base/tests/unittests/com/google/idea/blaze/base/run/state/RunConfigurationFlagStateTest.java b/base/tests/unittests/com/google/idea/blaze/base/run/state/RunConfigurationFlagStateTest.java
new file mode 100644
index 0000000..730ac3b
--- /dev/null
+++ b/base/tests/unittests/com/google/idea/blaze/base/run/state/RunConfigurationFlagStateTest.java
@@ -0,0 +1,75 @@
+/*
+ * 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.state;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Unit tests for {@link RunConfigurationFlagsState}. */
+@RunWith(JUnit4.class)
+public class RunConfigurationFlagStateTest {
+
+ @Test
+ public void testEscapedQuotesRetainedAfterReserialization() {
+ // previously, we were removing escape chars and quotes during ParametersListUtil.parse, then
+ // not putting them back when converting back to a string.
+ ImmutableList<String> flags = ImmutableList.of("--flag=\\\"Hello_world!\\\"", "--flag2");
+ RunConfigurationFlagsState state = new RunConfigurationFlagsState("tag", "field");
+ state.setFlags(flags);
+
+ RunConfigurationStateEditor editor = state.getEditor(null);
+ editor.resetEditorFrom(state);
+ editor.applyEditorTo(state);
+
+ assertThat(state.getFlags()).isEqualTo(flags);
+ }
+
+ @Test
+ public void testQuotesRetainedAfterReserialization() {
+ ImmutableList<String> flags = ImmutableList.of("\"--flag=test\"");
+ RunConfigurationFlagsState state = new RunConfigurationFlagsState("tag", "field");
+ state.setFlags(flags);
+
+ RunConfigurationStateEditor editor = state.getEditor(null);
+ editor.resetEditorFrom(state);
+ editor.applyEditorTo(state);
+
+ assertThat(state.getFlags()).isEqualTo(flags);
+ }
+
+ @Test
+ public void testNormalFlagsAreNotMangled() {
+ ImmutableList<String> flags =
+ ImmutableList.of(
+ "--test_sharding_strategy=disabled",
+ "--test_strategy=local",
+ "--experimental_show_artifacts",
+ "--test_filter=com.google.idea.blaze.base.run.state.RunConfigurationFlagStateTest#",
+ "--define=ij_product=intellij-latest");
+ RunConfigurationFlagsState state = new RunConfigurationFlagsState("tag", "field");
+ state.setFlags(flags);
+
+ RunConfigurationStateEditor editor = state.getEditor(null);
+ editor.resetEditorFrom(state);
+ editor.applyEditorTo(state);
+
+ assertThat(state.getFlags()).isEqualTo(flags);
+ }
+}
diff --git a/base/tests/unittests/com/google/idea/blaze/base/sync/BlazeSyncManagerTest.java b/base/tests/unittests/com/google/idea/blaze/base/sync/BlazeSyncManagerTest.java
new file mode 100644
index 0000000..c8cf5ba
--- /dev/null
+++ b/base/tests/unittests/com/google/idea/blaze/base/sync/BlazeSyncManagerTest.java
@@ -0,0 +1,115 @@
+/*
+ * 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 static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import com.google.common.collect.ImmutableList;
+import com.google.idea.blaze.base.BlazeTestCase;
+import com.google.idea.blaze.base.model.primitives.TargetExpression;
+import com.google.idea.blaze.base.settings.BlazeUserSettings;
+import com.google.idea.blaze.base.sync.BlazeSyncParams.SyncMode;
+import java.io.IOException;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
+
+/** Test cases for {@link BlazeSyncManager}. */
+@RunWith(JUnit4.class)
+public class BlazeSyncManagerTest extends BlazeTestCase {
+ @Spy BlazeSyncManager manager = new BlazeSyncManager(project);
+ @Captor ArgumentCaptor<BlazeSyncParams> paramsCaptor;
+
+ @Override
+ protected void initTest(Container applicationServices, Container projectServices) {
+ super.initTest(applicationServices, projectServices);
+ MockitoAnnotations.initMocks(this);
+ applicationServices.register(BlazeUserSettings.class, mock(BlazeUserSettings.class));
+ doNothing().when(manager).requestProjectSync(any());
+ projectServices.register(BlazeSyncManager.class, manager);
+ assertThat(BlazeSyncManager.getInstance(project)).isSameAs(manager);
+ }
+
+ @Test
+ public void testFullProjectSync() throws IOException {
+ manager.fullProjectSync();
+ verify(manager).requestProjectSync(paramsCaptor.capture());
+ BlazeSyncParams params = paramsCaptor.getValue();
+ assertThat(params).isNotNull();
+ assertThat(params.title).isEqualTo("Full Sync");
+ assertThat(params.syncMode).isEqualTo(SyncMode.FULL);
+ assertThat(params.backgroundSync).isFalse();
+ assertThat(params.addProjectViewTargets).isTrue();
+ assertThat(params.addWorkingSet)
+ .isEqualTo(BlazeUserSettings.getInstance().getExpandSyncToWorkingSet());
+ assertThat(params.targetExpressions).isEmpty();
+ }
+
+ @Test
+ public void testIncrementalProjectSync() throws IOException {
+ manager.incrementalProjectSync();
+ verify(manager).requestProjectSync(paramsCaptor.capture());
+ BlazeSyncParams params = paramsCaptor.getValue();
+ assertThat(params).isNotNull();
+ assertThat(params.title).isEqualTo("Sync");
+ assertThat(params.syncMode).isEqualTo(SyncMode.INCREMENTAL);
+ assertThat(params.backgroundSync).isFalse();
+ assertThat(params.addProjectViewTargets).isTrue();
+ assertThat(params.addWorkingSet)
+ .isEqualTo(BlazeUserSettings.getInstance().getExpandSyncToWorkingSet());
+ assertThat(params.targetExpressions).isEmpty();
+ }
+
+ @Test
+ public void testPartialSync() {
+ List<TargetExpression> targets =
+ ImmutableList.of(
+ TargetExpression.fromString("//foo:bar"), TargetExpression.fromString("//foo:baz"));
+ manager.partialSync(targets);
+ verify(manager).requestProjectSync(paramsCaptor.capture());
+ BlazeSyncParams params = paramsCaptor.getValue();
+ assertThat(params).isNotNull();
+ assertThat(params.title).isEqualTo("Partial Sync");
+ assertThat(params.syncMode).isEqualTo(SyncMode.PARTIAL);
+ assertThat(params.backgroundSync).isFalse();
+ assertThat(params.addProjectViewTargets).isFalse();
+ assertThat(params.addWorkingSet).isFalse();
+ assertThat(params.targetExpressions).isEqualTo(targets);
+ }
+
+ @Test
+ public void testWorkingSetSync() throws IOException {
+ manager.workingSetSync();
+ verify(manager).requestProjectSync(paramsCaptor.capture());
+ BlazeSyncParams params = paramsCaptor.getValue();
+ assertThat(params).isNotNull();
+ assertThat(params.title).isEqualTo("Sync Working Set");
+ assertThat(params.syncMode).isEqualTo(SyncMode.PARTIAL);
+ assertThat(params.backgroundSync).isFalse();
+ assertThat(params.addProjectViewTargets).isFalse();
+ assertThat(params.addWorkingSet).isTrue();
+ assertThat(params.targetExpressions).isEmpty();
+ }
+}
diff --git a/base/tests/unittests/com/google/idea/blaze/base/targetmaps/TransitiveDependencyMapTest.java b/base/tests/unittests/com/google/idea/blaze/base/targetmaps/TransitiveDependencyMapTest.java
new file mode 100644
index 0000000..3037ff1
--- /dev/null
+++ b/base/tests/unittests/com/google/idea/blaze/base/targetmaps/TransitiveDependencyMapTest.java
@@ -0,0 +1,167 @@
+/*
+ * 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.targetmaps;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.idea.blaze.base.BlazeTestCase;
+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.ideinfo.TargetMapBuilder;
+import com.google.idea.blaze.base.model.BlazeProjectData;
+import com.google.idea.blaze.base.model.primitives.Label;
+import com.google.idea.blaze.base.sync.BlazeSyncPlugin.ModuleEditor;
+import com.google.idea.blaze.base.sync.data.BlazeProjectDataManager;
+import javax.annotation.Nullable;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Unit tests for {@link TransitiveDependencyMap}. */
+@RunWith(JUnit4.class)
+public class TransitiveDependencyMapTest extends BlazeTestCase {
+ private TransitiveDependencyMap transitiveDependencyMap;
+
+ @Override
+ protected void initTest(Container applicationServices, Container projectServices) {
+ super.initTest(applicationServices, projectServices);
+ projectServices.register(
+ BlazeProjectDataManager.class,
+ new MockBlazeProjectDataManager(
+ new BlazeProjectData(
+ 0L, buildTargetMap(), null, null, null, null, null, null, null, null)));
+ projectServices.register(TransitiveDependencyMap.class, new TransitiveDependencyMap(project));
+ transitiveDependencyMap = TransitiveDependencyMap.getInstance(project);
+ }
+
+ @Test
+ public void testGetSimpleDependency() {
+ TargetKey simpleA = TargetKey.forPlainTarget(new Label("//com/google/example/simple:a"));
+ TargetKey simpleB = TargetKey.forPlainTarget(new Label("//com/google/example/simple:b"));
+
+ assertThat(transitiveDependencyMap.getTransitiveDependencies(simpleA)).containsExactly(simpleB);
+ assertThat(transitiveDependencyMap.getTransitiveDependencies(simpleB)).isEmpty();
+ }
+
+ @Test
+ public void testGetChainDependencies() {
+ TargetKey chainA = TargetKey.forPlainTarget(new Label("//com/google/example/chain:a"));
+ TargetKey chainB = TargetKey.forPlainTarget(new Label("//com/google/example/chain:b"));
+ TargetKey chainC = TargetKey.forPlainTarget(new Label("//com/google/example/chain:c"));
+ TargetKey chainD = TargetKey.forPlainTarget(new Label("//com/google/example/chain:d"));
+
+ assertThat(transitiveDependencyMap.getTransitiveDependencies(chainA))
+ .containsExactly(chainB, chainC, chainD);
+ assertThat(transitiveDependencyMap.getTransitiveDependencies(chainB))
+ .containsExactly(chainC, chainD);
+ assertThat(transitiveDependencyMap.getTransitiveDependencies(chainC)).containsExactly(chainD);
+ assertThat(transitiveDependencyMap.getTransitiveDependencies(chainD)).isEmpty();
+ }
+
+ @Test
+ public void testGetDiamondDependencies() {
+ TargetKey diamondA = TargetKey.forPlainTarget(new Label("//com/google/example/diamond:a"));
+ TargetKey diamondB = TargetKey.forPlainTarget(new Label("//com/google/example/diamond:b"));
+ TargetKey diamondBB = TargetKey.forPlainTarget(new Label("//com/google/example/diamond:bb"));
+ TargetKey diamondBBB = TargetKey.forPlainTarget(new Label("//com/google/example/diamond:bbb"));
+ TargetKey diamondC = TargetKey.forPlainTarget(new Label("//com/google/example/diamond:c"));
+ TargetKey diamondCC = TargetKey.forPlainTarget(new Label("//com/google/example/diamond:cc"));
+ TargetKey diamondCCC = TargetKey.forPlainTarget(new Label("//com/google/example/diamond:ccc"));
+
+ assertThat(transitiveDependencyMap.getTransitiveDependencies(diamondA))
+ .containsExactly(diamondB, diamondBB, diamondBBB, diamondC, diamondCC, diamondCCC);
+ assertThat(transitiveDependencyMap.getTransitiveDependencies(diamondB))
+ .containsExactly(diamondC);
+ assertThat(transitiveDependencyMap.getTransitiveDependencies(diamondBB))
+ .containsExactly(diamondC, diamondCC);
+ assertThat(transitiveDependencyMap.getTransitiveDependencies(diamondBBB))
+ .containsExactly(diamondC, diamondCC, diamondCCC);
+ assertThat(transitiveDependencyMap.getTransitiveDependencies(diamondC)).isEmpty();
+ assertThat(transitiveDependencyMap.getTransitiveDependencies(diamondCC)).isEmpty();
+ assertThat(transitiveDependencyMap.getTransitiveDependencies(diamondCCC)).isEmpty();
+ }
+
+ @Test
+ public void testGetDependencyForNonExistentTarget() {
+ TargetKey bogus = TargetKey.forPlainTarget(new Label("//com/google/fake:target"));
+ assertThat(transitiveDependencyMap.getTransitiveDependencies(bogus)).isEmpty();
+ }
+
+ private static TargetMap buildTargetMap() {
+ Label simpleA = new Label("//com/google/example/simple:a");
+ Label simpleB = new Label("//com/google/example/simple:b");
+ Label chainA = new Label("//com/google/example/chain:a");
+ Label chainB = new Label("//com/google/example/chain:b");
+ Label chainC = new Label("//com/google/example/chain:c");
+ Label chainD = new Label("//com/google/example/chain:d");
+ Label diamondA = new Label("//com/google/example/diamond:a");
+ Label diamondB = new Label("//com/google/example/diamond:b");
+ Label diamondBB = new Label("//com/google/example/diamond:bb");
+ Label diamondBBB = new Label("//com/google/example/diamond:bbb");
+ Label diamondC = new Label("//com/google/example/diamond:c");
+ Label diamondCC = new Label("//com/google/example/diamond:cc");
+ Label diamondCCC = new Label("//com/google/example/diamond:ccc");
+ return TargetMapBuilder.builder()
+ .addTarget(TargetIdeInfo.builder().setLabel(simpleA).addDependency(simpleB))
+ .addTarget(TargetIdeInfo.builder().setLabel(simpleB))
+ .addTarget(TargetIdeInfo.builder().setLabel(chainA).addDependency(chainB))
+ .addTarget(TargetIdeInfo.builder().setLabel(chainB).addDependency(chainC))
+ .addTarget(TargetIdeInfo.builder().setLabel(chainC).addDependency(chainD))
+ .addTarget(TargetIdeInfo.builder().setLabel(chainD))
+ .addTarget(
+ TargetIdeInfo.builder()
+ .setLabel(diamondA)
+ .addDependency(diamondB)
+ .addDependency(diamondBB)
+ .addDependency(diamondBBB))
+ .addTarget(TargetIdeInfo.builder().setLabel(diamondB).addDependency(diamondC))
+ .addTarget(
+ TargetIdeInfo.builder()
+ .setLabel(diamondBB)
+ .addDependency(diamondC)
+ .addDependency(diamondCC))
+ .addTarget(
+ TargetIdeInfo.builder()
+ .setLabel(diamondBBB)
+ .addDependency(diamondC)
+ .addDependency(diamondCC)
+ .addDependency(diamondCCC))
+ .addTarget(TargetIdeInfo.builder().setLabel(diamondC))
+ .addTarget(TargetIdeInfo.builder().setLabel(diamondCC))
+ .addTarget(TargetIdeInfo.builder().setLabel(diamondCCC))
+ .build();
+ }
+
+ private static class MockBlazeProjectDataManager implements BlazeProjectDataManager {
+ private final BlazeProjectData blazeProjectData;
+
+ public MockBlazeProjectDataManager(BlazeProjectData blazeProjectData) {
+ this.blazeProjectData = blazeProjectData;
+ }
+
+ @Nullable
+ @Override
+ public BlazeProjectData getBlazeProjectData() {
+ return blazeProjectData;
+ }
+
+ @Override
+ public ModuleEditor editModules() {
+ return null;
+ }
+ }
+}
diff --git a/base/tests/unittests/com/google/idea/blaze/base/vcs/git/GitStatusLineProcessorTest.java b/base/tests/unittests/com/google/idea/blaze/base/vcs/git/GitStatusLineProcessorTest.java
index 06cbe96..36fbdc0 100644
--- a/base/tests/unittests/com/google/idea/blaze/base/vcs/git/GitStatusLineProcessorTest.java
+++ b/base/tests/unittests/com/google/idea/blaze/base/vcs/git/GitStatusLineProcessorTest.java
@@ -18,9 +18,11 @@
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.WorkspacePath;
import com.google.idea.blaze.base.model.primitives.WorkspaceRoot;
import java.io.File;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -28,6 +30,7 @@
/** Tests for {@link GitStatusLineProcessor} */
@RunWith(JUnit4.class)
public class GitStatusLineProcessorTest {
+ @Rule public BlazeTestCase.IgnoreOnWindowsRule rule = new BlazeTestCase.IgnoreOnWindowsRule();
@Test
public void testGitStatusParser() {
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 633c9df..81d3e75 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
@@ -34,6 +34,7 @@
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.ProjectJdkTable;
+import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.testFramework.EdtTestUtil;
import com.intellij.testFramework.IdeaTestUtil;
@@ -44,6 +45,7 @@
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
import com.intellij.testFramework.fixtures.TestFixtureBuilder;
import com.intellij.testFramework.fixtures.impl.LightTempDirTestFixtureImpl;
+import com.intellij.util.ThrowableRunnable;
import java.io.File;
import java.io.FileNotFoundException;
import javax.annotation.Nullable;
@@ -51,10 +53,32 @@
import org.junit.Before;
import org.junit.Rule;
import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
/** Base test class for blaze integration tests. {@link UsefulTestCase} */
public abstract class BlazeIntegrationTestCase {
+ /** Test rule that ensures tests do not run on Windows (see http://b.android.com/222904) */
+ public static class IgnoreOnWindowsRule implements TestRule {
+ @Override
+ public Statement apply(Statement base, Description description) {
+ if (SystemInfo.isWindows) {
+ return new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ System.out.println(
+ "Test \""
+ + description.getDisplayName()
+ + "\" does not run on Windows (see http://b.android.com/222904)");
+ }
+ };
+ }
+ return base;
+ }
+ }
+
+ @Rule public final IgnoreOnWindowsRule rule = new IgnoreOnWindowsRule();
@Rule public final IntellijTestSetupRule setupRule = new IntellijTestSetupRule();
@Rule public final TestRule testRunWrapper = runTestsOnEdt() ? new EdtRule() : null;
@@ -74,20 +98,20 @@
testFixture.setUp();
fileSystem = new TestFileSystem(getProject(), testFixture.getTempDirFixture());
- Runnable writeAction =
- () ->
- ApplicationManager.getApplication()
- .runWriteAction(
- () -> {
- ProjectJdkTable.getInstance().addJdk(IdeaTestUtil.getMockJdk18());
- VirtualFile workspaceRootVirtualFile =
- fileSystem.createDirectory("workspace");
- workspaceRoot =
- new WorkspaceRoot(new File(workspaceRootVirtualFile.getPath()));
- projectDataDirectory = fileSystem.createDirectory("project-data-dir");
- workspace = new WorkspaceFileSystem(workspaceRoot, fileSystem);
- });
- EdtTestUtil.runInEdtAndWait(writeAction);
+ EdtTestUtil.runInEdtAndWait(
+ (ThrowableRunnable<Throwable>)
+ () ->
+ ApplicationManager.getApplication()
+ .runWriteAction(
+ () -> {
+ ProjectJdkTable.getInstance().addJdk(IdeaTestUtil.getMockJdk18());
+ VirtualFile workspaceRootVirtualFile =
+ fileSystem.createDirectory("workspace");
+ workspaceRoot =
+ new WorkspaceRoot(new File(workspaceRootVirtualFile.getPath()));
+ projectDataDirectory = fileSystem.createDirectory("project-data-dir");
+ workspace = new WorkspaceFileSystem(workspaceRoot, fileSystem);
+ }));
BlazeImportSettingsManager.getInstance(getProject())
.setImportSettings(
diff --git a/base/tests/utils/integration/com/google/idea/blaze/base/EditorTestHelper.java b/base/tests/utils/integration/com/google/idea/blaze/base/EditorTestHelper.java
index e7a2d4e..b4dede2 100644
--- a/base/tests/utils/integration/com/google/idea/blaze/base/EditorTestHelper.java
+++ b/base/tests/utils/integration/com/google/idea/blaze/base/EditorTestHelper.java
@@ -36,6 +36,7 @@
import com.intellij.testFramework.EditorTestUtil.CaretInfo;
import com.intellij.testFramework.EdtTestUtil;
import com.intellij.testFramework.fixtures.CodeInsightTestFixture;
+import com.intellij.util.ThrowableRunnable;
import java.util.Arrays;
import javax.annotation.Nullable;
@@ -54,7 +55,8 @@
}
public Editor openFileInEditor(VirtualFile file) {
- EdtTestUtil.runInEdtAndWait((Runnable) () -> testFixture.openFileInEditor(file));
+ EdtTestUtil.runInEdtAndWait(
+ (ThrowableRunnable<Throwable>) () -> testFixture.openFileInEditor(file));
return testFixture.getEditor();
}
@@ -125,7 +127,7 @@
public void setCaretPosition(Editor editor, int lineNumber, int columnNumber) {
final CaretInfo info = new CaretInfo(new LogicalPosition(lineNumber, columnNumber), null);
EdtTestUtil.runInEdtAndWait(
- (Runnable)
+ (ThrowableRunnable<Throwable>)
() ->
EditorTestUtil.setCaretsAndSelection(
editor, new CaretAndSelectionState(ImmutableList.of(info), null)));
diff --git a/base/tests/utils/integration/com/google/idea/blaze/base/lang/buildfile/BuildFileIntegrationTestCase.java b/base/tests/utils/integration/com/google/idea/blaze/base/lang/buildfile/BuildFileIntegrationTestCase.java
index efa81a8..78d1ec3 100644
--- a/base/tests/utils/integration/com/google/idea/blaze/base/lang/buildfile/BuildFileIntegrationTestCase.java
+++ b/base/tests/utils/integration/com/google/idea/blaze/base/lang/buildfile/BuildFileIntegrationTestCase.java
@@ -25,12 +25,13 @@
import com.google.idea.blaze.base.ideinfo.TargetMap;
import com.google.idea.blaze.base.lang.buildfile.psi.BuildFile;
import com.google.idea.blaze.base.model.BlazeProjectData;
+import com.google.idea.blaze.base.model.BlazeVersionData;
+import com.google.idea.blaze.base.model.SyncState;
import com.google.idea.blaze.base.model.primitives.ExecutionRootPath;
import com.google.idea.blaze.base.model.primitives.WorkspacePath;
import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoder;
import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoderImpl;
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.google.idea.blaze.base.sync.workspace.WorkspacePathResolverImpl;
import com.intellij.openapi.vfs.VirtualFile;
@@ -83,12 +84,11 @@
new TargetMap(ImmutableMap.of()),
ImmutableMap.of(),
fakeRoots,
- new WorkingSet(ImmutableList.of(), ImmutableList.of(), ImmutableList.of()),
+ new BlazeVersionData(),
workspacePathResolver,
artifactLocationDecoder,
null,
- null,
- null,
+ new SyncState.Builder().build(),
null);
}
}
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 b197413..a0ac661 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
@@ -29,6 +29,7 @@
import com.google.idea.blaze.base.command.info.BlazeInfo;
import com.google.idea.blaze.base.ideinfo.ArtifactLocation;
import com.google.idea.blaze.base.ideinfo.TargetMap;
+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;
import com.google.idea.blaze.base.model.primitives.WorkspacePath;
@@ -53,6 +54,7 @@
import com.google.idea.blaze.base.sync.workspace.WorkspacePathResolver;
import com.google.idea.blaze.base.sync.workspace.WorkspacePathResolverImpl;
import com.google.idea.blaze.base.vcs.BlazeVcsHandler;
+import com.intellij.ide.IdeEventQueue;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ContentEntry;
import com.intellij.openapi.roots.ModifiableRootModel;
@@ -60,6 +62,8 @@
import com.intellij.openapi.vfs.VirtualFile;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
import javax.annotation.Nullable;
import org.junit.Before;
@@ -182,7 +186,20 @@
project,
BlazeImportSettingsManager.getInstance(project).getImportSettings(),
syncParams);
- syncTask.syncProject(context);
+
+ // We need to run sync off EDT to keep IntelliJ's transaction system happy
+ // Because the sync task itself wants to run occasional EDT tasks, we'll have
+ // to keep flushing the event queue.
+ Future<?> future =
+ Executors.newSingleThreadExecutor().submit(() -> syncTask.syncProject(context));
+ while (!future.isDone()) {
+ IdeEventQueue.getInstance().flushQueue();
+ try {
+ Thread.sleep(50);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
}
private static class MockProjectViewManager extends ProjectViewManager {
@@ -282,6 +299,7 @@
BlazeContext context,
WorkspaceRoot workspaceRoot,
ProjectViewSet projectViewSet,
+ BlazeVersionData blazeVersionData,
List<TargetExpression> targets,
WorkspaceLanguageSettings workspaceLanguageSettings,
ArtifactLocationDecoder artifactLocationDecoder,
@@ -297,6 +315,18 @@
BlazeContext context,
WorkspaceRoot workspaceRoot,
ProjectViewSet projectViewSet,
+ BlazeVersionData blazeVersionData,
+ List<TargetExpression> targets) {
+ return BuildResult.SUCCESS;
+ }
+
+ @Override
+ public BuildResult compileIdeArtifacts(
+ Project project,
+ BlazeContext context,
+ WorkspaceRoot workspaceRoot,
+ ProjectViewSet projectViewSet,
+ BlazeVersionData blazeVersionData,
List<TargetExpression> targets) {
return BuildResult.SUCCESS;
}
diff --git a/base/tests/utils/unit/com/google/idea/blaze/base/BlazeTestCase.java b/base/tests/utils/unit/com/google/idea/blaze/base/BlazeTestCase.java
index 428b180..874fd28 100644
--- a/base/tests/utils/unit/com/google/idea/blaze/base/BlazeTestCase.java
+++ b/base/tests/utils/unit/com/google/idea/blaze/base/BlazeTestCase.java
@@ -19,17 +19,22 @@
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.extensions.DefaultPluginDescriptor;
-import com.intellij.openapi.extensions.ExtensionPoint;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.extensions.Extensions;
+import com.intellij.openapi.extensions.PluginDescriptor;
import com.intellij.openapi.extensions.PluginId;
import com.intellij.openapi.extensions.impl.ExtensionPointImpl;
import com.intellij.openapi.extensions.impl.ExtensionsAreaImpl;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.util.SystemInfo;
import org.jetbrains.annotations.NotNull;
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
import org.picocontainer.MutablePicoContainer;
/**
@@ -40,6 +45,27 @@
* <p>Provides a mock application and a mock project.
*/
public class BlazeTestCase {
+ /** Test rule that ensures tests do not run on Windows (see http://b.android.com/222904) */
+ public static class IgnoreOnWindowsRule implements TestRule {
+ @NotNull
+ @Override
+ public Statement apply(Statement base, Description description) {
+ if (SystemInfo.isWindows) {
+ return new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ System.out.println(
+ "Test \""
+ + description.getDisplayName()
+ + "\" does not run on Windows (see http://b.android.com/222904)");
+ }
+ };
+ }
+ return base;
+ }
+ }
+
+ @Rule public IgnoreOnWindowsRule rule = new IgnoreOnWindowsRule();
protected Project project;
private ExtensionsAreaImpl extensionsArea;
@@ -94,16 +120,9 @@
protected <T> ExtensionPointImpl<T> registerExtensionPoint(
@NotNull ExtensionPointName<T> name, @NotNull Class<T> type) {
- ExtensionPointImpl<T> extensionPoint =
- new ExtensionPointImpl<T>(
- name.getName(),
- type.getName(),
- ExtensionPoint.Kind.INTERFACE,
- extensionsArea,
- null,
- new Extensions.SimpleLogProvider(),
- new DefaultPluginDescriptor(PluginId.getId(type.getName()), type.getClassLoader()));
- extensionsArea.registerExtensionPoint(extensionPoint);
- return extensionPoint;
+ PluginDescriptor pluginDescriptor =
+ new DefaultPluginDescriptor(PluginId.getId(type.getName()), type.getClassLoader());
+ extensionsArea.registerExtensionPoint(name.getName(), type.getName(), pluginDescriptor);
+ return extensionsArea.getExtensionPoint(name.getName());
}
}
diff --git a/base/tests/utils/unit/com/google/idea/blaze/base/run/MockBlazeCommandRunConfigurationHandlerProvider.java b/base/tests/utils/unit/com/google/idea/blaze/base/run/MockBlazeCommandRunConfigurationHandlerProvider.java
index 4f30237..c4b6762 100644
--- a/base/tests/utils/unit/com/google/idea/blaze/base/run/MockBlazeCommandRunConfigurationHandlerProvider.java
+++ b/base/tests/utils/unit/com/google/idea/blaze/base/run/MockBlazeCommandRunConfigurationHandlerProvider.java
@@ -81,6 +81,9 @@
public JComponent createComponent() {
return null;
}
+
+ @Override
+ public void setComponentEnabled(boolean enabled) {}
};
}
}
diff --git a/build_defs/build_defs.bzl b/build_defs/build_defs.bzl
index f836204..3104e0f 100644
--- a/build_defs/build_defs.bzl
+++ b/build_defs/build_defs.bzl
@@ -191,22 +191,16 @@
"chmod +w $@",
"mkdir -p META-INF",
"cp $(location {plugin_xml}) META-INF/plugin.xml".format(plugin_xml=plugin_xml),
- "$(location {zip_tool}) -u $@ META-INF/plugin.xml >/dev/null".format(zip_tool=zip_tool),
]
- srcs = [
+ srcs = meta_inf_files + [
plugin_xml,
deploy_jar,
]
for meta_inf_file in meta_inf_files:
- cmd.append("cp $(location {meta_inf_file}) META-INF/$$(basename $(location {meta_inf_file}))".format(
- meta_inf_file=meta_inf_file,
- ))
- cmd.append("$(location {zip_tool}) -u $@ META-INF/$$(basename $(location {meta_inf_file})) >/dev/null".format(
- zip_tool=zip_tool,
- meta_inf_file=meta_inf_file,
- ))
- srcs.append(meta_inf_file)
+ 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))
native.genrule(
name = name + "_genrule",
@@ -236,28 +230,38 @@
tags = ["intellij-plugin-bundle"],
)
-def repackage_jar(name, src_rule, out,
- rules = [
- "com.google.common.** com.google.repackaged.common.@1",
- "com.google.gson.** com.google.repackaged.gson.@1",
- "com.google.protobuf.** com.google.repackaged.protobuf.@1",
- "com.google.thirdparty.** com.google.repackaged.thirdparty.@1",
- ], **kwargs):
+def repackaged_jar(name, deps, rules, launcher=None, **kwargs):
"""Repackages classes in a jar, to avoid collisions in the classpath.
Args:
name: the name of this target
- src_rule: a java_binary label with the create_executable attribute set to 0
- out: the output jarfile
+ deps: The dependencies repackage
rules: the rules to apply in the repackaging
- Only repackage some of com.google.** from proto_deps.jar.
- We do not repackage:
+ Do not repackage:
- com.google.net.** because that has JNI files which use
FindClass(JNIEnv *, const char *) with hard-coded native string
literals that jarjar doesn't rewrite.
- com.google.errorprone packages (rewriting will throw off blaze build).
+ launcher: The launcher arg to pass to java_binary
**kwargs: Any additional arguments to pass to the final target.
"""
+ java_binary_name = name + "_deploy_jar"
+ out = name + ".jar"
+ native.java_binary(
+ name = java_binary_name,
+ create_executable = 0,
+ stamp = 0,
+ launcher = launcher,
+ runtime_deps = deps,
+ )
+ _repackage_jar(name, java_binary_name, out, rules, **kwargs)
+
+def repackage_jar(name, src_rule, out, rules, **kwargs):
+ print("repackage_jar is deprecated. Please switch to repackaged_jar.")
+ _repackage_jar(name, src_rule, out, rules, **kwargs)
+
+def _repackage_jar(name, src_rule, out, rules, **kwargs):
+ """Repackages classes in a jar, to avoid collisions in the classpath."""
repackage_tool = "@jarjar//jar"
deploy_jar = "{src_rule}_deploy.jar".format(src_rule=src_rule)
script_lines = []
diff --git a/clwb/src/com/google/idea/blaze/clwb/run/BlazeCidrRunConfigurationHandler.java b/clwb/src/com/google/idea/blaze/clwb/run/BlazeCidrRunConfigurationHandler.java
index 29aea18..c5ded30 100644
--- a/clwb/src/com/google/idea/blaze/clwb/run/BlazeCidrRunConfigurationHandler.java
+++ b/clwb/src/com/google/idea/blaze/clwb/run/BlazeCidrRunConfigurationHandler.java
@@ -23,6 +23,7 @@
import com.google.idea.blaze.base.run.confighandler.BlazeCommandRunConfigurationRunner;
import com.google.idea.blaze.base.run.state.BlazeCommandRunConfigurationCommonState;
import com.google.idea.blaze.base.settings.Blaze;
+import com.google.idea.blaze.base.settings.Blaze.BuildSystem;
import com.intellij.execution.Executor;
import com.intellij.execution.RunnerAndConfigurationSettings;
import com.intellij.execution.configurations.RunConfiguration;
@@ -38,8 +39,9 @@
private final BlazeCommandRunConfigurationCommonState state;
public BlazeCidrRunConfigurationHandler(BlazeCommandRunConfiguration configuration) {
- this.buildSystemName = Blaze.buildSystemName(configuration.getProject());
- this.state = new BlazeCommandRunConfigurationCommonState(buildSystemName);
+ BuildSystem buildSystem = Blaze.getBuildSystem(configuration.getProject());
+ this.buildSystemName = buildSystem.getName();
+ this.state = new BlazeCommandRunConfigurationCommonState(buildSystem);
}
@Override
diff --git a/common/actionhelper/BUILD b/common/actionhelper/BUILD
new file mode 100644
index 0000000..0da6dd5
--- /dev/null
+++ b/common/actionhelper/BUILD
@@ -0,0 +1,11 @@
+licenses(["notice"]) # Apache 2.0
+
+java_library(
+ name = "actionhelper",
+ srcs = glob(["src/**/*.java"]),
+ visibility = ["//visibility:public"],
+ deps = [
+ "//intellij_platform_sdk:plugin_api",
+ "@jsr305_annotations//jar",
+ ],
+)
diff --git a/common/actionhelper/src/com/google/idea/common/actionhelper/ActionPresentationHelper.java b/common/actionhelper/src/com/google/idea/common/actionhelper/ActionPresentationHelper.java
new file mode 100644
index 0000000..96a7882
--- /dev/null
+++ b/common/actionhelper/src/com/google/idea/common/actionhelper/ActionPresentationHelper.java
@@ -0,0 +1,175 @@
+/*
+ * 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.common.actionhelper;
+
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.Presentation;
+import com.intellij.openapi.vfs.VirtualFile;
+import java.util.List;
+import javax.annotation.Nullable;
+
+/** Helps setting the presentation enabled/visible/text states. */
+public class ActionPresentationHelper {
+
+ private final Presentation presentation;
+ private boolean enabled = true;
+ private boolean visible = true;
+ private boolean disableWithoutSubject;
+ private boolean hasSubject;
+ private String text;
+ private String subjectText;
+
+ /** Converts a subject to a string */
+ @FunctionalInterface
+ public interface SubjectToString<T> {
+ String subjectToString(T subject);
+ }
+
+ private ActionPresentationHelper(Presentation presentation) {
+ this.presentation = presentation;
+ }
+
+ public static ActionPresentationHelper of(AnActionEvent e) {
+ return new ActionPresentationHelper(e.getPresentation());
+ }
+
+ /** Disables the action if the condition is true. */
+ public ActionPresentationHelper disableIf(boolean disableCondition) {
+ this.enabled = this.enabled && !disableCondition;
+ return this;
+ }
+
+ /** Hides the action if the condition is true. */
+ public ActionPresentationHelper hideIf(boolean hideCondition) {
+ this.visible = this.visible && !hideCondition;
+ return this;
+ }
+
+ /** Disables the action if no subject has been provided. */
+ public ActionPresentationHelper disableWithoutSubject() {
+ this.disableWithoutSubject = true;
+ return this;
+ }
+
+ /** Sets the text of the presentation. */
+ public ActionPresentationHelper setText(String text) {
+ this.text = text;
+ return this;
+ }
+
+ /**
+ * Sets the text depending on the subject.
+ *
+ * @param noSubjectText Text to set if there is no subject, or if the action is disabled.
+ * @param subjectText Text to set if there is a subject. If %s exists in the subject text,
+ * String.format is used with the quoted file name.
+ * @param file The subject. May be null.
+ */
+ public ActionPresentationHelper setTextWithSubject(
+ String noSubjectText, String subjectText, @Nullable VirtualFile file) {
+ return setTextWithSubject(
+ noSubjectText, subjectText, file, ActionPresentationHelper::quoteFileName);
+ }
+
+ /**
+ * Sets the text depending on the subject.
+ *
+ * @param noSubjectText Text to set if there is no subject, or if the action is disabled.
+ * @param subjectText Text to set if there is a subject. If %s exists in the subject text,
+ * String.format is used with the subject passed through subjectToString
+ * @param subject The subject. May be null.
+ * @param subjectToString Method used to convert the subject to a string.
+ */
+ public <T> ActionPresentationHelper setTextWithSubject(
+ String noSubjectText,
+ String subjectText,
+ @Nullable T subject,
+ SubjectToString<T> subjectToString) {
+ this.text = noSubjectText;
+ if (subject != null) {
+ this.subjectText =
+ subjectText.contains("%s")
+ ? String.format(subjectText, subjectToString.subjectToString(subject))
+ : subjectText;
+ this.hasSubject = true;
+ }
+ return this;
+ }
+
+ /**
+ * Sets the text depending on the subjects.
+ *
+ * @param noSubjectText Text to set if there is no subject, or if the action is disabled.
+ * @param singleSubjectText Text to set if there is a single subject. If %s exists in the subject
+ * text, String.format is used with the quoted single file name.
+ * @param multipleSubjectText Text to use if there are multiple subjects.
+ */
+ public ActionPresentationHelper setTextWithSubjects(
+ String noSubjectText,
+ String singleSubjectText,
+ String multipleSubjectText,
+ List<VirtualFile> files) {
+ return setTextWithSubjects(
+ noSubjectText,
+ singleSubjectText,
+ multipleSubjectText,
+ files,
+ ActionPresentationHelper::quoteFileName);
+ }
+
+ /**
+ * Sets the text depending on the subjects.
+ *
+ * @param noSubjectText Text to set if there is no subject, or if the action is disabled.
+ * @param singleSubjectText Text to set if there is a single subject. If %s exists in the subject
+ * text, String.format is used with the subject passed through subjectToString.
+ * @param multipleSubjectText Text to use if there are multiple subjects.
+ */
+ public <T> ActionPresentationHelper setTextWithSubjects(
+ String noSubjectText,
+ String singleSubjectText,
+ String multipleSubjectText,
+ List<T> subjects,
+ SubjectToString<T> subjectToString) {
+ if (subjects.size() > 1) {
+ this.text = noSubjectText;
+ this.subjectText = multipleSubjectText;
+ this.hasSubject = true;
+ return this;
+ } else {
+ T subject = !subjects.isEmpty() ? subjects.get(0) : null;
+ return setTextWithSubject(noSubjectText, singleSubjectText, subject, subjectToString);
+ }
+ }
+
+ private static String quoteFileName(VirtualFile file) {
+ return "\"" + file.getName() + "\"";
+ }
+
+ public void commit() {
+ boolean enabled = this.enabled;
+ if (disableWithoutSubject) {
+ enabled = enabled && hasSubject;
+ }
+ presentation.setEnabled(enabled);
+ presentation.setVisible(visible);
+
+ String text = enabled && hasSubject ? subjectText : this.text;
+ if (text != null) {
+ presentation.setText(text);
+ }
+ }
+}
diff --git a/common/formatter/BUILD b/common/formatter/BUILD
index 839a469..1e45774 100644
--- a/common/formatter/BUILD
+++ b/common/formatter/BUILD
@@ -6,6 +6,7 @@
visibility = ["//visibility:public"],
deps = [
"//intellij_platform_sdk:plugin_api",
+ "//sdkcompat",
"@jsr305_annotations//jar",
],
)
diff --git a/common/formatter/src/com/google/idea/common/formatter/DelegatingCodeStyleManager.java b/common/formatter/src/com/google/idea/common/formatter/DelegatingCodeStyleManager.java
index aa2460f..354fea4 100644
--- a/common/formatter/src/com/google/idea/common/formatter/DelegatingCodeStyleManager.java
+++ b/common/formatter/src/com/google/idea/common/formatter/DelegatingCodeStyleManager.java
@@ -15,6 +15,7 @@
*/
package com.google.idea.common.formatter;
+import com.google.idea.sdkcompat.codestyle.CodeStyleManagerSdkCompatAdapter;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.fileTypes.FileType;
@@ -31,7 +32,7 @@
import javax.annotation.Nullable;
/** A delegating {@link CodeStyleManager}. */
-public abstract class DelegatingCodeStyleManager extends CodeStyleManager {
+public abstract class DelegatingCodeStyleManager extends CodeStyleManagerSdkCompatAdapter {
private final CodeStyleManager delegate;
diff --git a/cpp/src/com/google/idea/blaze/cpp/BlazeCSyncPlugin.java b/cpp/src/com/google/idea/blaze/cpp/BlazeCSyncPlugin.java
index c066fc4..1c64fbc 100644
--- a/cpp/src/com/google/idea/blaze/cpp/BlazeCSyncPlugin.java
+++ b/cpp/src/com/google/idea/blaze/cpp/BlazeCSyncPlugin.java
@@ -27,11 +27,9 @@
import com.google.idea.blaze.base.sync.BlazeSyncPlugin;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
-import com.intellij.openapi.roots.ModifiableRootModel;
import com.jetbrains.cidr.lang.workspace.OCWorkspace;
import com.jetbrains.cidr.lang.workspace.OCWorkspaceManager;
import java.util.Set;
-import javax.annotation.Nullable;
final class BlazeCSyncPlugin extends BlazeSyncPlugin.Adapter {
@Override
@@ -43,16 +41,13 @@
}
@Override
- public void updateProjectStructure(
+ public void updateInMemoryState(
Project project,
BlazeContext context,
WorkspaceRoot workspaceRoot,
ProjectViewSet projectViewSet,
BlazeProjectData blazeProjectData,
- @Nullable BlazeProjectData oldBlazeProjectData,
- ModuleEditor moduleEditor,
- Module workspaceModule,
- ModifiableRootModel workspaceModifiableModel) {
+ Module workspaceModule) {
if (!blazeProjectData.workspaceLanguageSettings.isLanguageActive(LanguageClass.C)) {
return;
}
diff --git a/golang/BUILD b/golang/BUILD
new file mode 100644
index 0000000..16a486c
--- /dev/null
+++ b/golang/BUILD
@@ -0,0 +1,38 @@
+licenses(["notice"]) # Apache 2.0
+
+java_library(
+ name = "golang",
+ srcs = glob(["src/**/*.java"]),
+ visibility = ["//visibility:public"],
+ deps = [
+ "//base",
+ "//intellij_platform_sdk:plugin_api",
+ "//sdkcompat",
+ "@jsr305_annotations//jar",
+ ],
+)
+
+filegroup(
+ name = "plugin_xml",
+ srcs = ["src/META-INF/golang.xml"],
+ visibility = ["//visibility:public"],
+)
+
+load(
+ "//testing:test_defs.bzl",
+ "intellij_unit_test_suite",
+)
+
+intellij_unit_test_suite(
+ name = "unit_tests",
+ srcs = glob(["tests/unittests/**/*.java"]),
+ test_package_root = "com.google.idea.blaze.golang",
+ deps = [
+ ":golang",
+ "//base",
+ "//base:unit_test_utils",
+ "//intellij_platform_sdk:plugin_api_for_tests",
+ "@jsr305_annotations//jar",
+ "@junit//jar",
+ ],
+)
diff --git a/golang/src/META-INF/golang.xml b/golang/src/META-INF/golang.xml
new file mode 100644
index 0000000..32948ee
--- /dev/null
+++ b/golang/src/META-INF/golang.xml
@@ -0,0 +1,7 @@
+<idea-plugin>
+
+ <extensions defaultExtensionNs="com.google.idea.blaze">
+ <SyncPlugin implementation="com.google.idea.blaze.golang.sync.BlazeGoSyncPlugin"/>
+ </extensions>
+
+</idea-plugin>
diff --git a/golang/src/com/google/idea/blaze/golang/sdk/GoSdkUtil.java b/golang/src/com/google/idea/blaze/golang/sdk/GoSdkUtil.java
new file mode 100644
index 0000000..ba42571
--- /dev/null
+++ b/golang/src/com/google/idea/blaze/golang/sdk/GoSdkUtil.java
@@ -0,0 +1,59 @@
+/*
+ * 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.golang.sdk;
+
+import com.intellij.execution.configurations.PathEnvironmentVariableUtil;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import java.io.File;
+import java.io.IOException;
+import javax.annotation.Nullable;
+
+/**
+ * Go-lang SDK utility methods.
+ *
+ * <p>TODO: Remove this, and reference go-lang plugin source code directly.
+ */
+public class GoSdkUtil {
+
+ @Nullable
+ public static VirtualFile suggestSdkDirectory() {
+ String fromEnv = suggestSdkDirectoryPathFromEnv();
+ if (fromEnv != null) {
+ return LocalFileSystem.getInstance().findFileByPath(fromEnv);
+ }
+ return LocalFileSystem.getInstance().findFileByPath("/usr/local/go");
+ }
+
+ @Nullable
+ private static String suggestSdkDirectoryPathFromEnv() {
+ File fileFromPath = PathEnvironmentVariableUtil.findInPath("go");
+ if (fileFromPath != null) {
+ File canonicalFile;
+ try {
+ canonicalFile = fileFromPath.getCanonicalFile();
+ String path = canonicalFile.getPath();
+ if (path.endsWith("bin/go")) {
+ return StringUtil.trimEnd(path, "bin/go");
+ }
+ } catch (IOException e) {
+ // if it can't be found, just silently return null
+ }
+ }
+ return null;
+ }
+}
diff --git a/golang/src/com/google/idea/blaze/golang/sync/BlazeGoLibrarySource.java b/golang/src/com/google/idea/blaze/golang/sync/BlazeGoLibrarySource.java
new file mode 100644
index 0000000..693c5f7
--- /dev/null
+++ b/golang/src/com/google/idea/blaze/golang/sync/BlazeGoLibrarySource.java
@@ -0,0 +1,38 @@
+/*
+ * 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.golang.sync;
+
+import com.google.idea.blaze.base.sync.libraries.LibrarySource;
+import com.intellij.openapi.roots.libraries.Library;
+import java.util.function.Predicate;
+
+/** Prevents garbage collection of Go libraries */
+class BlazeGoLibrarySource extends LibrarySource.Adapter {
+
+ static final BlazeGoLibrarySource INSTANCE = new BlazeGoLibrarySource();
+
+ private BlazeGoLibrarySource() {}
+
+ @Override
+ public Predicate<Library> getGcRetentionFilter() {
+ return BlazeGoLibrarySource::isGoLibrary;
+ }
+
+ static boolean isGoLibrary(Library library) {
+ String name = library.getName();
+ return name != null && name.startsWith(BlazeGoSyncPlugin.GO_LIBRARY_PREFIX);
+ }
+}
diff --git a/golang/src/com/google/idea/blaze/golang/sync/BlazeGoSyncPlugin.java b/golang/src/com/google/idea/blaze/golang/sync/BlazeGoSyncPlugin.java
new file mode 100644
index 0000000..7f2a5c7
--- /dev/null
+++ b/golang/src/com/google/idea/blaze/golang/sync/BlazeGoSyncPlugin.java
@@ -0,0 +1,243 @@
+/*
+ * 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.golang.sync;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import com.google.idea.blaze.base.model.BlazeProjectData;
+import com.google.idea.blaze.base.model.BlazeVersionData;
+import com.google.idea.blaze.base.model.primitives.LanguageClass;
+import com.google.idea.blaze.base.model.primitives.WorkspaceRoot;
+import com.google.idea.blaze.base.model.primitives.WorkspaceType;
+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.sync.BlazeSyncPlugin;
+import com.google.idea.blaze.base.sync.GenericSourceFolderProvider;
+import com.google.idea.blaze.base.sync.SourceFolderProvider;
+import com.google.idea.blaze.base.sync.data.BlazeDataStorage;
+import com.google.idea.blaze.base.sync.libraries.LibrarySource;
+import com.google.idea.blaze.base.sync.projectview.WorkspaceLanguageSettings;
+import com.google.idea.blaze.golang.sdk.GoSdkUtil;
+import com.google.idea.sdkcompat.transactions.Transactions;
+import com.intellij.ide.plugins.IdeaPluginDescriptor;
+import com.intellij.ide.plugins.PluginManager;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.extensions.PluginId;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleType;
+import com.intellij.openapi.module.ModuleTypeManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.projectRoots.ProjectJdkTable;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.projectRoots.SdkType;
+import com.intellij.openapi.projectRoots.SdkTypeId;
+import com.intellij.openapi.projectRoots.impl.SdkConfigurationUtil;
+import com.intellij.openapi.roots.ModifiableRootModel;
+import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar;
+import com.intellij.openapi.updateSettings.impl.pluginsAdvertisement.PluginsAdvertiser;
+import com.intellij.openapi.util.EmptyRunnable;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.pom.NavigatableAdapter;
+import java.util.List;
+import java.util.Set;
+import javax.annotation.Nullable;
+
+/** Supports golang. */
+public class BlazeGoSyncPlugin extends BlazeSyncPlugin.Adapter {
+
+ static final String GO_LIBRARY_PREFIX = "GOPATH";
+ private static final String GO_MODULE_TYPE_ID = "GO_MODULE";
+ private static final String GO_PLUGIN_ID = "ro.redeul.google.go";
+ private static final String GO_SDK_TYPE_ID = "Go SDK";
+
+ @Nullable
+ @Override
+ public ModuleType<?> getWorkspaceModuleType(WorkspaceType workspaceType) {
+ if (workspaceType == WorkspaceType.GO) {
+ return ModuleTypeManager.getInstance().findByID(GO_MODULE_TYPE_ID);
+ }
+ return null;
+ }
+
+ @Override
+ public ImmutableList<WorkspaceType> getSupportedWorkspaceTypes() {
+ return ImmutableList.of(WorkspaceType.GO);
+ }
+
+ @Override
+ public Set<LanguageClass> getSupportedLanguagesInWorkspace(WorkspaceType workspaceType) {
+ if (workspaceType == WorkspaceType.GO) {
+ return ImmutableSet.of(LanguageClass.GO);
+ }
+ return ImmutableSet.of();
+ }
+
+ @Nullable
+ @Override
+ public WorkspaceType getDefaultWorkspaceType() {
+ return WorkspaceType.GO;
+ }
+
+ @Nullable
+ @Override
+ public SourceFolderProvider getSourceFolderProvider(BlazeProjectData projectData) {
+ if (!projectData.workspaceLanguageSettings.isWorkspaceType(WorkspaceType.GO)) {
+ return null;
+ }
+ return GenericSourceFolderProvider.INSTANCE;
+ }
+
+ @Override
+ public void updateProjectStructure(
+ Project project,
+ BlazeContext context,
+ WorkspaceRoot workspaceRoot,
+ ProjectViewSet projectViewSet,
+ BlazeProjectData blazeProjectData,
+ @Nullable BlazeProjectData oldBlazeProjectData,
+ ModuleEditor moduleEditor,
+ Module workspaceModule,
+ ModifiableRootModel workspaceModifiableModel) {
+ if (!blazeProjectData.workspaceLanguageSettings.isLanguageActive(LanguageClass.GO)) {
+ return;
+ }
+ for (Library lib : getGoLibraries(project)) {
+ if (workspaceModifiableModel.findLibraryOrderEntry(lib) == null) {
+ workspaceModifiableModel.addLibraryEntry(lib);
+ }
+ }
+ }
+
+ private static List<Library> getGoLibraries(Project project) {
+ List<Library> libraries = Lists.newArrayList();
+ LibraryTablesRegistrar registrar = LibraryTablesRegistrar.getInstance();
+ for (Library lib : registrar.getLibraryTable().getLibraries()) {
+ if (BlazeGoLibrarySource.isGoLibrary(lib)) {
+ libraries.add(lib);
+ }
+ }
+
+ String moduleLibraryName =
+ String.format("%s <%s>", GO_LIBRARY_PREFIX, BlazeDataStorage.WORKSPACE_MODULE_NAME);
+ Library goModuleLibrary =
+ registrar.getLibraryTable(project).getLibraryByName(moduleLibraryName);
+ if (goModuleLibrary != null) {
+ libraries.add(goModuleLibrary);
+ }
+ return libraries;
+ }
+
+ /**
+ * By default the Go plugin will create duplicate copies of project libraries, one for each
+ * module. We only care about library associated with the workspace module.
+ */
+ static boolean isGoLibraryForModule(Library library, String moduleName) {
+ String name = library.getName();
+ return name != null && name.equals("GOPATH <" + moduleName + ">");
+ }
+
+ @Nullable
+ @Override
+ public LibrarySource getLibrarySource(BlazeProjectData blazeProjectData) {
+ if (!blazeProjectData.workspaceLanguageSettings.isLanguageActive(LanguageClass.GO)) {
+ return null;
+ }
+ return BlazeGoLibrarySource.INSTANCE;
+ }
+
+ @Override
+ public boolean validateProjectView(
+ BlazeContext context,
+ ProjectViewSet projectViewSet,
+ WorkspaceLanguageSettings workspaceLanguageSettings) {
+ if (!workspaceLanguageSettings.isLanguageActive(LanguageClass.GO)) {
+ return true;
+ }
+ if (!isPluginEnabled()) {
+ IssueOutput.error("Go plugin needed for Go language support.")
+ .navigatable(
+ new NavigatableAdapter() {
+ @Override
+ public void navigate(boolean requestFocus) {
+ if (isPluginInstalled()) {
+ PluginManager.enablePlugin(GO_PLUGIN_ID);
+ } else {
+ PluginsAdvertiser.installAndEnablePlugins(
+ ImmutableSet.of(GO_PLUGIN_ID), EmptyRunnable.INSTANCE);
+ }
+ }
+ })
+ .submit(context);
+ return false;
+ }
+ return true;
+ }
+
+ private static boolean isPluginInstalled() {
+ return PluginManager.isPluginInstalled(PluginId.getId(GO_PLUGIN_ID));
+ }
+
+ private static boolean isPluginEnabled() {
+ IdeaPluginDescriptor plugin = PluginManager.getPlugin(PluginId.getId(GO_PLUGIN_ID));
+ return plugin != null && plugin.isEnabled();
+ }
+
+ @Override
+ public void updateProjectSdk(
+ Project project,
+ BlazeContext context,
+ ProjectViewSet projectViewSet,
+ BlazeVersionData blazeVersionData,
+ BlazeProjectData blazeProjectData) {
+ if (!blazeProjectData.workspaceLanguageSettings.isWorkspaceType(WorkspaceType.GO)) {
+ return;
+ }
+ Sdk currentSdk = ProjectRootManager.getInstance(project).getProjectSdk();
+ if (currentSdk != null && currentSdk.getSdkType().getName().equals(GO_SDK_TYPE_ID)) {
+ return;
+ }
+ Sdk sdk = getOrCreateGoSdk();
+ if (sdk != null) {
+ setProjectSdk(project, sdk);
+ }
+ }
+
+ @Nullable
+ private static Sdk getOrCreateGoSdk() {
+ ProjectJdkTable sdkTable = ProjectJdkTable.getInstance();
+ SdkTypeId type = sdkTable.getSdkTypeByName(GO_SDK_TYPE_ID);
+ List<Sdk> sdk = sdkTable.getSdksOfType(type);
+ if (!sdk.isEmpty()) {
+ return sdk.get(0);
+ }
+ VirtualFile defaultSdk = GoSdkUtil.suggestSdkDirectory();
+ if (defaultSdk != null) {
+ return SdkConfigurationUtil.createAndAddSDK(defaultSdk.getPath(), (SdkType) type);
+ }
+ return null;
+ }
+
+ private static void setProjectSdk(Project project, Sdk sdk) {
+ Transactions.submitTransactionAndWait(
+ () ->
+ ApplicationManager.getApplication()
+ .runWriteAction(() -> ProjectRootManager.getInstance(project).setProjectSdk(sdk)));
+ }
+}
diff --git a/golang/tests/unittests/com/google/idea/blaze/golang/sync/BlazeGoSyncPluginTest.java b/golang/tests/unittests/com/google/idea/blaze/golang/sync/BlazeGoSyncPluginTest.java
new file mode 100644
index 0000000..e563a27
--- /dev/null
+++ b/golang/tests/unittests/com/google/idea/blaze/golang/sync/BlazeGoSyncPluginTest.java
@@ -0,0 +1,116 @@
+/*
+ * 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.golang.sync;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.idea.blaze.base.BlazeTestCase;
+import com.google.idea.blaze.base.model.primitives.LanguageClass;
+import com.google.idea.blaze.base.model.primitives.WorkspaceType;
+import com.google.idea.blaze.base.projectview.ProjectView;
+import com.google.idea.blaze.base.projectview.ProjectViewSet;
+import com.google.idea.blaze.base.projectview.section.ListSection;
+import com.google.idea.blaze.base.projectview.section.ScalarSection;
+import com.google.idea.blaze.base.projectview.section.sections.AdditionalLanguagesSection;
+import com.google.idea.blaze.base.projectview.section.sections.WorkspaceTypeSection;
+import com.google.idea.blaze.base.scope.BlazeContext;
+import com.google.idea.blaze.base.scope.ErrorCollector;
+import com.google.idea.blaze.base.scope.output.IssueOutput;
+import com.google.idea.blaze.base.sync.BlazeSyncPlugin;
+import com.google.idea.blaze.base.sync.projectview.LanguageSupport;
+import com.google.idea.blaze.base.sync.projectview.WorkspaceLanguageSettings;
+import com.intellij.openapi.extensions.impl.ExtensionPointImpl;
+import java.util.Set;
+import javax.annotation.Nullable;
+import org.jetbrains.annotations.NotNull;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Unit tests for {@link BlazeGoSyncPlugin} */
+@RunWith(JUnit4.class)
+public class BlazeGoSyncPluginTest extends BlazeTestCase {
+
+ private final ErrorCollector errorCollector = new ErrorCollector();
+ private BlazeContext context;
+ private ExtensionPointImpl<BlazeSyncPlugin> syncPluginEp;
+
+ @Override
+ protected void initTest(
+ @NotNull Container applicationServices, @NotNull Container projectServices) {
+ super.initTest(applicationServices, projectServices);
+
+ syncPluginEp = registerExtensionPoint(BlazeSyncPlugin.EP_NAME, BlazeSyncPlugin.class);
+ syncPluginEp.registerExtension(new BlazeGoSyncPlugin());
+ context = new BlazeContext();
+ context.addOutputSink(IssueOutput.class, errorCollector);
+ }
+
+ @Test
+ public void testGoWorkspaceTypeSupported() {
+ ProjectViewSet projectViewSet =
+ ProjectViewSet.builder()
+ .add(
+ ProjectView.builder()
+ .add(ScalarSection.builder(WorkspaceTypeSection.KEY).set(WorkspaceType.GO))
+ .build())
+ .build();
+ WorkspaceLanguageSettings workspaceLanguageSettings =
+ LanguageSupport.createWorkspaceLanguageSettings(context, projectViewSet);
+ errorCollector.assertNoIssues();
+ assertThat(workspaceLanguageSettings)
+ .isEqualTo(
+ new WorkspaceLanguageSettings(
+ WorkspaceType.GO, ImmutableSet.of(LanguageClass.GENERIC, LanguageClass.GO)));
+ }
+
+ @Test
+ public void testGoNotAValidAdditionalLanguage() {
+ // add a java sync plugin so we have another workspace type available
+ syncPluginEp.registerExtension(
+ new BlazeSyncPlugin.Adapter() {
+ @Override
+ public ImmutableList<WorkspaceType> getSupportedWorkspaceTypes() {
+ return ImmutableList.of(WorkspaceType.JAVA);
+ }
+
+ @Override
+ public Set<LanguageClass> getSupportedLanguagesInWorkspace(WorkspaceType workspaceType) {
+ return ImmutableSet.of(LanguageClass.JAVA);
+ }
+
+ @Nullable
+ @Override
+ public WorkspaceType getDefaultWorkspaceType() {
+ return WorkspaceType.JAVA;
+ }
+ });
+
+ ProjectViewSet projectViewSet =
+ ProjectViewSet.builder()
+ .add(
+ ProjectView.builder()
+ .add(ScalarSection.builder(WorkspaceTypeSection.KEY).set(WorkspaceType.JAVA))
+ .add(ListSection.builder(AdditionalLanguagesSection.KEY).add(LanguageClass.GO))
+ .build())
+ .build();
+ LanguageSupport.createWorkspaceLanguageSettings(context, projectViewSet);
+ errorCollector.assertIssueContaining(
+ "Language 'go' is not supported for this plugin with workspace type: 'java'");
+ }
+}
diff --git a/ijwb/BUILD b/ijwb/BUILD
index 0ec5aad..55b2b72 100644
--- a/ijwb/BUILD
+++ b/ijwb/BUILD
@@ -17,6 +17,7 @@
srcs = [
"src/META-INF/ijwb.xml",
"//base:plugin_xml",
+ "//golang:plugin_xml",
"//java:plugin_xml",
"//plugin_dev:plugin_xml",
],
@@ -50,6 +51,7 @@
],
deps = [
"//base",
+ "//golang",
"//intellij_platform_sdk:plugin_api",
"//java",
"@jsr305_annotations//jar",
diff --git a/ijwb/ijwb.bazelproject b/ijwb/ijwb.bazelproject
index e8fc755..6bf0977 100644
--- a/ijwb/ijwb.bazelproject
+++ b/ijwb/ijwb.bazelproject
@@ -11,6 +11,9 @@
workspace_type: intellij_plugin
+build_flags:
+ --define=ij_product=intellij-latest
+
test_sources:
*/tests/unittests*
*/tests/integrationtests*
diff --git a/intellij_platform_sdk/BUILD b/intellij_platform_sdk/BUILD
index 2f68436..9edcbbf 100644
--- a/intellij_platform_sdk/BUILD
+++ b/intellij_platform_sdk/BUILD
@@ -62,6 +62,14 @@
},
)
+# Android Studio 2.3.0.4
+config_setting(
+ name = "android-studio-2.3.0.4",
+ values = {
+ "define": "ij_product=android-studio-2.3.0.4",
+ },
+)
+
config_setting(
name = "clion-latest",
values = {
@@ -69,6 +77,14 @@
},
)
+# CLion 2016.3.2
+config_setting(
+ name = "clion-2016.3.2",
+ values = {
+ "define": "ij_product=clion-2016.3.2",
+ },
+)
+
# CLion 2016.2.2
config_setting(
name = "clion-162.1967.7",
@@ -77,16 +93,13 @@
},
)
-# CLion 16 (2016.2.1)
-config_setting(
- name = "clion-162.1628.20",
- values = {
- "define": "ij_product=clion-162.1628.20",
- },
+load(
+ ":build_defs.bzl",
+ "select_for_ide",
+ "select_for_plugin_api",
+ "select_from_plugin_api_directory",
)
-load(":build_defs.bzl", "select_from_plugin_api_directory", "select_for_ide")
-
# The purpose of this rule is to hide the versioning
# complexity from users of this api.
# There will be additional versions added in the future
@@ -96,6 +109,7 @@
android_studio = [
":sdk",
":android_plugin",
+ ":test_recorder",
],
clion = [":sdk"],
intellij = [":sdk"],
@@ -128,7 +142,7 @@
java_library(
name = "plugin_api_for_grammar_kit",
visibility = ["//third_party/java/jetbrains/grammar_kit:__pkg__"],
- exports = ["//intellij_platform_sdk/IC_162_2032_8:sdk"],
+ exports = ["//intellij_platform_sdk/intellij_ce_2016_3_1:sdk"],
)
# Used to support IntelliJ plugin development in our plugin
@@ -172,7 +186,25 @@
android_studio = [":bundled_plugins"],
clion = [":bundled_plugins"],
intellij = [":bundled_plugins"],
- ),
+ ) + [":missing_test_classes"],
+)
+
+# Certain plugin API releases don't contain test classes, so any testServiceImplemention
+# in an upstream plugin will cause an error when the plugin is loaded for integration tests.
+# Here we have dummy versions of these missing classes.
+java_library(
+ name = "missing_test_classes",
+ srcs = select_for_plugin_api({
+ "android-studio-2.3.0.3": [
+ "missing/tests/com/jetbrains/cidr/modulemap/resolve/MockModuleMapManagerImpl.java",
+ ],
+ "android-studio-2.3.0.4": [
+ "missing/tests/com/jetbrains/cidr/modulemap/resolve/MockModuleMapManagerImpl.java",
+ ],
+ "default": [],
+ }) + ["missing/src/dummy/pkg/DummyClassToAvoidAnEmptyJavaLibrary.java"],
+ tags = ["intellij-missing-test-classes"],
+ deps = [":plugin_api"],
)
filegroup(
@@ -189,6 +221,7 @@
srcs = select_for_ide(
android_studio = ["android_studio_application_info_name.txt"],
clion = ["clion_application_info_name.txt"],
+ default = ["intellij_application_info_name.txt"],
intellij = ["intellij_application_info_name.txt"],
),
)
diff --git a/intellij_platform_sdk/BUILD.clion b/intellij_platform_sdk/BUILD.clion
index e395d09..c83e150 100644
--- a/intellij_platform_sdk/BUILD.clion
+++ b/intellij_platform_sdk/BUILD.clion
@@ -26,4 +26,4 @@
filegroup(
name = "application_info_jar",
srcs = glob(["clion-*/lib/clion.jar"]),
-)
+)
\ No newline at end of file
diff --git a/intellij_platform_sdk/build_defs.bzl b/intellij_platform_sdk/build_defs.bzl
index 57dbe76..e7c5c2d 100644
--- a/intellij_platform_sdk/build_defs.bzl
+++ b/intellij_platform_sdk/build_defs.bzl
@@ -2,7 +2,7 @@
# The current indirect ij_product mapping (eg. "intellij-latest")
INDIRECT_IJ_PRODUCTS = {
- "intellij-latest": "intellij-162.2032.8",
+ "intellij-latest": "intellij-2016.3.1",
"android-studio-latest": "android-studio-145.1617.8",
"android-studio-beta": "android-studio-2.3.0.3",
"clion-latest": "clion-162.1967.7",
@@ -25,14 +25,18 @@
ide="android-studio",
directory="android_studio_2_3_0_3",
),
- "clion-162.1628.20": struct(
- ide="clion",
- directory="CL_162_1628_20",
+ "android-studio-2.3.0.4": struct(
+ ide="android-studio",
+ directory="android_studio_2_3_0_4",
),
"clion-162.1967.7": struct(
ide="clion",
directory="CL_162_1967_7",
),
+ "clion-2016.3.2": struct(
+ ide="clion",
+ directory="clion_2016_3_2",
+ ),
}
# BUILD_VARS for each IDE corresponding to indirect ij_products, eg. "intellij-latest"
@@ -50,11 +54,8 @@
You may only include direct ij_products here,
not indirects (eg. intellij-latest).
Returns:
- A select statement on all plugin_apis. Unless you include a "default"
- clause any other matched plugin_api will return "None".
-
- A build without an ij_product is considered equivalent to building with
- "intellij-latest".
+ A select statement on all plugin_apis. Unless you include a "default",
+ a non-matched plugin_api will result in an error.
Example:
java_library(
@@ -65,6 +66,9 @@
}),
)
"""
+ if not params:
+ fail("Empty select_for_plugin_api")
+
for indirect_ij_product in INDIRECT_IJ_PRODUCTS:
if indirect_ij_product in params:
error_message = "".join([
@@ -72,43 +76,27 @@
"Instead, select on an exact ij_product."])
fail(error_message)
- # To make the select work with "intellij-latest" and friends,
- # we find if the user is currently selecting on what intellij-latest
- # is resolving to, and copy that. Example:
+ expanded_params = dict(**params)
+
+ # Expand all indirect plugin_apis to point to their
+ # corresponding direct plugin_api.
#
- # {"intellij-2016.3.1": "stuff"} ->
- # {"intellij-2016.3.1": "stuff", "intellij-latest": "stuff"}
- params = dict(**params)
+ # {"intellij-2016.3.1": "foo"} ->
+ # {"intellij-2016.3.1": "foo", "intellij-latest": "foo"}
for indirect_ij_product, resolved_plugin_api in INDIRECT_IJ_PRODUCTS.items():
if resolved_plugin_api in params:
- params[indirect_ij_product] = params[resolved_plugin_api]
+ expanded_params[indirect_ij_product] = params[resolved_plugin_api]
- if "default" not in params:
- # If "intellij-latest" is supported, we set "default" to that
- # This supports building with an empty command line. Example:
- #
- # {"intellij-2016.3.1": "stuff", "intellij-latest": "stuff"} ->
- # {"intellij-2016.3.1": "stuff", "intellij-latest": "stuff", "default": "stuff"}
- if "intellij-latest" in params:
- params["default"] = params["intellij-latest"]
-
- # Add the other indirect ij_products returning None rather than default
- for ij_product in INDIRECT_IJ_PRODUCTS:
- if ij_product not in params:
- params[ij_product] = None
- for ij_product in DIRECT_IJ_PRODUCTS:
- if ij_product not in params:
- params[ij_product] = None
-
- # Map to the actual targets
+ # Map the shorthand ij_products to full config_setting targets.
# This makes it more convenient so the user doesn't have to
# fully specify the path to the plugin_apis
select_params = dict()
- for ij_product, value in params.items():
+ for ij_product, value in expanded_params.items():
if ij_product == "default":
select_params["//conditions:default"] = value
else:
select_params["//intellij_platform_sdk:" + ij_product] = value
+
return select(select_params)
def select_for_ide(intellij=None, android_studio=None, clion=None, default=None):
@@ -134,7 +122,6 @@
intellij = intellij or default
android_studio = android_studio or default
clion = clion or default
- default = default or intellij
ide_to_value = {
"intellij" : intellij,
@@ -166,4 +153,8 @@
params = dict()
for ij_product, value in DIRECT_IJ_PRODUCTS.items():
params[ij_product] = [_plugin_api_directory(value) + item for item in ide_to_value[value.ide]]
+
+ # No ij_product == intellij-latest
+ params["default"] = params[INDIRECT_IJ_PRODUCTS["intellij-latest"]]
+
return select_for_plugin_api(params)
diff --git a/java/BUILD b/java/BUILD
index 80799c3..ac9f772 100644
--- a/java/BUILD
+++ b/java/BUILD
@@ -6,10 +6,12 @@
visibility = ["//visibility:public"],
deps = [
"//base",
+ "//common/actionhelper",
"//common/experiments",
"//intellij_platform_sdk:junit",
"//intellij_platform_sdk:plugin_api",
"//proto_deps",
+ "//sdkcompat",
"@jsr305_annotations//jar",
],
)
diff --git a/java/src/META-INF/blaze-java.xml b/java/src/META-INF/blaze-java.xml
index ae7ca4d..5466c27 100644
--- a/java/src/META-INF/blaze-java.xml
+++ b/java/src/META-INF/blaze-java.xml
@@ -19,31 +19,34 @@
<actions>
<action class="com.google.idea.blaze.java.libraries.ExcludeLibraryAction"
- id="Blaze.ExcludeLibraryAction"
- icon="BlazeIcons.Blaze"
- text="Exclude Library and Resync">
- <add-to-group group-id="Blaze.ProjectViewPopupMenu"/>
+ id="Blaze.ExcludeLibraryAction"
+ text="Exclude Library and Resync">
</action>
<action class="com.google.idea.blaze.java.libraries.AttachSourceJarAction"
- id="Blaze.AttachSourceJarAction"
- icon="BlazeIcons.Blaze"
- text="Attach Source Jar">
- <add-to-group group-id="Blaze.ProjectViewPopupMenu"/>
+ id="Blaze.AttachSourceJarAction"
+ text="Attach Source Jar">
</action>
<action class="com.google.idea.blaze.java.libraries.AddLibraryTargetDirectoryToProjectViewAction"
- id="Blaze.AddLibraryTargetDirectoryToProjectView"
- icon="BlazeIcons.Blaze"
- text="Add Library Target Directory To Project View">
- <add-to-group group-id="Blaze.ProjectViewPopupMenu"/>
+ id="Blaze.AddLibraryTargetDirectoryToProjectView"
+ text="Add Library Target Directory to Project View">
+ </action>
+ <action class="com.google.idea.blaze.java.libraries.DetachAllSourceJarsAction"
+ id="Blaze.DetachAllSourceJars"
+ text="Detach All Blaze Source Jars">
</action>
- <group>
- <action class="com.google.idea.blaze.java.libraries.DetachAllSourceJarsAction"
- id="Blaze.DetachAllSourceJars"
- text="Detach All Blaze Source Jars">
- </action>
- <separator/>
- <add-to-group group-id="Blaze.MainMenuActionGroup" relative-to-action="Blaze.EditProjectView" anchor="before"/>
+ <group id="Blaze.Java.ProjectViewPopupMenu">
+ <add-to-group group-id="Blaze.ProjectViewPopupMenu"/>
+ <reference id="Blaze.ExcludeLibraryAction"/>
+ <reference id="Blaze.AttachSourceJarAction"/>
+ <reference id="Blaze.AddLibraryTargetDirectoryToProjectView"/>
+ </group>
+
+ <group id="Blaze.JavaMenuGroup.Outer">
+ <add-to-group group-id="Blaze.MainMenuActionGroup" relative-to-action="Blaze.MenuFooter" anchor="after"/>
+ <group id="Blaze.JavaMenuGroup" text="Java">
+ <reference id="Blaze.DetachAllSourceJars"/>
+ </group>
</group>
<!-- IntelliJ specific actions -->
@@ -92,6 +95,9 @@
<projectService serviceImplementation="com.google.idea.blaze.java.libraries.SourceJarManager"/>
<refactoring.safeDeleteProcessor id="build_file_safe_delete" order="before javaProcessor"
implementation="com.google.idea.blaze.java.lang.build.BuildFileSafeDeleteProcessor"/>
+ <!--duplicated here in case the Kotlin plugin is present, as it also tries to replace javaProcessor-->
+ <refactoring.safeDeleteProcessor id="build_file_safe_delete_copy" order="before kotlinProcessor"
+ implementation="com.google.idea.blaze.java.lang.build.BuildFileSafeDeleteProcessor"/>
<projectService serviceImplementation="com.google.idea.blaze.java.libraries.JarCache"/>
<attachSourcesProvider implementation="com.google.idea.blaze.java.libraries.AddLibraryTargetDirectoryToProjectViewAttachSourcesProvider"/>
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 935fded..a954942 100644
--- a/java/src/com/google/idea/blaze/java/libraries/AddLibraryTargetDirectoryToProjectViewAction.java
+++ b/java/src/com/google/idea/blaze/java/libraries/AddLibraryTargetDirectoryToProjectViewAction.java
@@ -18,7 +18,7 @@
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
-import com.google.idea.blaze.base.actions.BlazeAction;
+import com.google.idea.blaze.base.actions.BlazeProjectAction;
import com.google.idea.blaze.base.ideinfo.TargetIdeInfo;
import com.google.idea.blaze.base.ideinfo.TargetKey;
import com.google.idea.blaze.base.model.BlazeProjectData;
@@ -44,14 +44,12 @@
import java.io.File;
import java.util.List;
import java.util.Set;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
+import javax.annotation.Nullable;
-class AddLibraryTargetDirectoryToProjectViewAction extends BlazeAction {
+class AddLibraryTargetDirectoryToProjectViewAction extends BlazeProjectAction {
+
@Override
- public void actionPerformed(AnActionEvent e) {
- Project project = e.getProject();
- assert project != null;
+ protected void actionPerformedInBlazeProject(Project project, AnActionEvent e) {
Library library = LibraryActionHelper.findLibraryForAction(e);
if (library != null) {
addDirectoriesToProjectView(project, ImmutableList.of(library));
@@ -59,18 +57,15 @@
}
@Override
- protected void doUpdate(@NotNull AnActionEvent e) {
+ protected void updateForBlazeProject(Project project, AnActionEvent e) {
Presentation presentation = e.getPresentation();
boolean visible = false;
boolean enabled = false;
- Project project = e.getProject();
- if (project != null) {
- Library library = LibraryActionHelper.findLibraryForAction(e);
- if (library != null) {
- visible = true;
- if (getDirectoryToAddForLibrary(project, library) != null) {
- enabled = true;
- }
+ Library library = LibraryActionHelper.findLibraryForAction(e);
+ if (library != null) {
+ visible = true;
+ if (getDirectoryToAddForLibrary(project, library) != null) {
+ enabled = true;
}
}
presentation.setVisible(visible);
diff --git a/java/src/com/google/idea/blaze/java/libraries/AttachSourceJarAction.java b/java/src/com/google/idea/blaze/java/libraries/AttachSourceJarAction.java
index 7e18d28..9961f6f 100644
--- a/java/src/com/google/idea/blaze/java/libraries/AttachSourceJarAction.java
+++ b/java/src/com/google/idea/blaze/java/libraries/AttachSourceJarAction.java
@@ -15,7 +15,7 @@
*/
package com.google.idea.blaze.java.libraries;
-import com.google.idea.blaze.base.actions.BlazeAction;
+import com.google.idea.blaze.base.actions.BlazeProjectAction;
import com.google.idea.blaze.base.ideinfo.LibraryArtifact;
import com.google.idea.blaze.base.model.BlazeProjectData;
import com.google.idea.blaze.base.sync.data.BlazeProjectDataManager;
@@ -30,13 +30,11 @@
import com.intellij.openapi.roots.libraries.Library;
import com.intellij.openapi.roots.libraries.LibraryTable;
import com.intellij.openapi.ui.Messages;
-import org.jetbrains.annotations.NotNull;
-class AttachSourceJarAction extends BlazeAction {
+class AttachSourceJarAction extends BlazeProjectAction {
+
@Override
- public void actionPerformed(AnActionEvent e) {
- Project project = e.getProject();
- assert project != null;
+ protected void actionPerformedInBlazeProject(Project project, AnActionEvent e) {
BlazeProjectData blazeProjectData =
BlazeProjectDataManager.getInstance(project).getBlazeProjectData();
if (blazeProjectData == null) {
@@ -77,28 +75,25 @@
}
@Override
- protected void doUpdate(@NotNull AnActionEvent e) {
+ protected void updateForBlazeProject(Project project, AnActionEvent e) {
Presentation presentation = e.getPresentation();
String text = "Attach Source Jar";
boolean visible = false;
boolean enabled = false;
- Project project = e.getProject();
- if (project != null) {
- BlazeProjectData blazeProjectData =
- BlazeProjectDataManager.getInstance(project).getBlazeProjectData();
- if (blazeProjectData != null) {
- Library library = LibraryActionHelper.findLibraryForAction(e);
- if (library != null) {
- visible = true;
+ BlazeProjectData blazeProjectData =
+ BlazeProjectDataManager.getInstance(project).getBlazeProjectData();
+ if (blazeProjectData != null) {
+ Library library = LibraryActionHelper.findLibraryForAction(e);
+ if (library != null) {
+ visible = true;
- BlazeJarLibrary blazeLibrary =
- LibraryActionHelper.findLibraryFromIntellijLibrary(
- e.getProject(), blazeProjectData, library);
- if (blazeLibrary != null && blazeLibrary.libraryArtifact.sourceJar != null) {
- enabled = true;
- if (SourceJarManager.getInstance(project).hasSourceJarAttached(blazeLibrary.key)) {
- text = "Detach Source Jar";
- }
+ BlazeJarLibrary blazeLibrary =
+ LibraryActionHelper.findLibraryFromIntellijLibrary(
+ e.getProject(), blazeProjectData, library);
+ if (blazeLibrary != null && blazeLibrary.libraryArtifact.sourceJar != null) {
+ enabled = true;
+ if (SourceJarManager.getInstance(project).hasSourceJarAttached(blazeLibrary.key)) {
+ text = "Detach Source Jar";
}
}
}
diff --git a/java/src/com/google/idea/blaze/java/libraries/BlazeAttachSourceProvider.java b/java/src/com/google/idea/blaze/java/libraries/BlazeAttachSourceProvider.java
index 05759ba..b5305b3 100644
--- a/java/src/com/google/idea/blaze/java/libraries/BlazeAttachSourceProvider.java
+++ b/java/src/com/google/idea/blaze/java/libraries/BlazeAttachSourceProvider.java
@@ -26,6 +26,7 @@
import com.google.idea.blaze.base.sync.libraries.LibraryEditor;
import com.google.idea.blaze.java.settings.BlazeJavaUserSettings;
import com.google.idea.blaze.java.sync.model.BlazeJarLibrary;
+import com.google.idea.sdkcompat.transactions.Transactions;
import com.intellij.codeInsight.AttachSourcesProvider;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.Project;
@@ -35,7 +36,6 @@
import com.intellij.openapi.roots.libraries.LibraryTable;
import com.intellij.openapi.util.ActionCallback;
import com.intellij.psi.PsiFile;
-import com.intellij.util.ui.UIUtil;
import java.util.Collection;
import java.util.List;
import org.jetbrains.annotations.NotNull;
@@ -85,7 +85,8 @@
* corresponding user setting is active.
*/
if (BlazeJavaUserSettings.getInstance().getAttachSourcesOnDemand()) {
- UIUtil.invokeLaterIfNeeded(
+ Transactions.submitTransaction(
+ project,
() -> {
attachSources(project, blazeProjectData, librariesToAttachSourceTo);
});
diff --git a/java/src/com/google/idea/blaze/java/libraries/DetachAllSourceJarsAction.java b/java/src/com/google/idea/blaze/java/libraries/DetachAllSourceJarsAction.java
index 10f70a3..37188f6 100644
--- a/java/src/com/google/idea/blaze/java/libraries/DetachAllSourceJarsAction.java
+++ b/java/src/com/google/idea/blaze/java/libraries/DetachAllSourceJarsAction.java
@@ -16,7 +16,7 @@
package com.google.idea.blaze.java.libraries;
import com.google.common.collect.Lists;
-import com.google.idea.blaze.base.actions.BlazeAction;
+import com.google.idea.blaze.base.actions.BlazeProjectAction;
import com.google.idea.blaze.base.model.BlazeProjectData;
import com.google.idea.blaze.base.model.LibraryKey;
import com.google.idea.blaze.base.sync.data.BlazeProjectDataManager;
@@ -30,12 +30,10 @@
import com.intellij.openapi.roots.libraries.LibraryTable;
import java.util.List;
-class DetachAllSourceJarsAction extends BlazeAction {
- @Override
- public void actionPerformed(AnActionEvent e) {
- Project project = e.getProject();
- assert project != null;
+class DetachAllSourceJarsAction extends BlazeProjectAction {
+ @Override
+ protected void actionPerformedInBlazeProject(Project project, AnActionEvent e) {
BlazeProjectData blazeProjectData =
BlazeProjectDataManager.getInstance(project).getBlazeProjectData();
if (blazeProjectData == null) {
diff --git a/java/src/com/google/idea/blaze/java/libraries/ExcludeLibraryAction.java b/java/src/com/google/idea/blaze/java/libraries/ExcludeLibraryAction.java
index 53d4ae8..48ab1d2 100644
--- a/java/src/com/google/idea/blaze/java/libraries/ExcludeLibraryAction.java
+++ b/java/src/com/google/idea/blaze/java/libraries/ExcludeLibraryAction.java
@@ -15,7 +15,7 @@
*/
package com.google.idea.blaze.java.libraries;
-import com.google.idea.blaze.base.actions.BlazeAction;
+import com.google.idea.blaze.base.actions.BlazeProjectAction;
import com.google.idea.blaze.base.ideinfo.LibraryArtifact;
import com.google.idea.blaze.base.model.BlazeProjectData;
import com.google.idea.blaze.base.projectview.ProjectViewEdit;
@@ -33,13 +33,11 @@
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.libraries.Library;
import com.intellij.openapi.ui.Messages;
-import org.jetbrains.annotations.NotNull;
-class ExcludeLibraryAction extends BlazeAction {
+class ExcludeLibraryAction extends BlazeProjectAction {
+
@Override
- public void actionPerformed(AnActionEvent e) {
- Project project = e.getProject();
- assert project != null;
+ protected void actionPerformedInBlazeProject(Project project, AnActionEvent e) {
BlazeProjectData blazeProjectData =
BlazeProjectDataManager.getInstance(project).getBlazeProjectData();
if (blazeProjectData == null) {
@@ -88,7 +86,7 @@
}
@Override
- protected void doUpdate(@NotNull AnActionEvent e) {
+ protected void updateForBlazeProject(Project project, AnActionEvent e) {
Presentation presentation = e.getPresentation();
boolean enabled = LibraryActionHelper.findLibraryForAction(e) != null;
presentation.setVisible(enabled);
diff --git a/java/src/com/google/idea/blaze/java/run/BlazeJavaDebuggerRunner.java b/java/src/com/google/idea/blaze/java/run/BlazeJavaDebuggerRunner.java
index 001f26e..33a6540 100644
--- a/java/src/com/google/idea/blaze/java/run/BlazeJavaDebuggerRunner.java
+++ b/java/src/com/google/idea/blaze/java/run/BlazeJavaDebuggerRunner.java
@@ -17,7 +17,7 @@
import com.google.idea.blaze.base.model.primitives.Kind;
import com.google.idea.blaze.base.run.BlazeCommandRunConfiguration;
-import com.intellij.debugger.impl.GenericDebuggerRunner;
+import com.google.idea.sdkcompat.debugger.GenericDebuggerRunnerSdkCompatAdapter;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.configurations.JavaParameters;
import com.intellij.execution.configurations.RemoteConnection;
@@ -27,19 +27,21 @@
import com.intellij.execution.executors.DefaultDebugExecutor;
import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.execution.ui.RunContentDescriptor;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
+import javax.annotation.Nullable;
/** A runner that adapts the GenericDebuggerRunner to work with Blaze run configurations. */
-public class BlazeJavaDebuggerRunner extends GenericDebuggerRunner {
+public class BlazeJavaDebuggerRunner extends GenericDebuggerRunnerSdkCompatAdapter {
+
+ // wait 10 minutes for the blaze build to complete before connecting
+ private static final long POLL_TIMEOUT_MILLIS = 10 * 60 * 1000;
+
@Override
- @NotNull
public String getRunnerId() {
return "Blaze-Debug";
}
@Override
- public boolean canRun(@NotNull final String executorId, @NotNull final RunProfile profile) {
+ public boolean canRun(final String executorId, final RunProfile profile) {
if (executorId.equals(DefaultDebugExecutor.EXECUTOR_ID)
&& profile instanceof BlazeCommandRunConfiguration) {
BlazeCommandRunConfiguration configuration = (BlazeCommandRunConfiguration) profile;
@@ -61,13 +63,12 @@
@Override
@Nullable
public RunContentDescriptor createContentDescriptor(
- @NotNull RunProfileState state, @NotNull ExecutionEnvironment environment)
- throws ExecutionException {
+ RunProfileState state, ExecutionEnvironment environment) throws ExecutionException {
if (!(state instanceof BlazeJavaRunProfileState)) {
return null;
}
BlazeJavaRunProfileState blazeState = (BlazeJavaRunProfileState) state;
RemoteConnection connection = blazeState.getRemoteConnection();
- return attachVirtualMachine(state, environment, connection, true /* pollConnection */);
+ return attachVirtualMachine(state, environment, connection, POLL_TIMEOUT_MILLIS);
}
}
diff --git a/java/src/com/google/idea/blaze/java/run/BlazeJavaRunConfigurationHandler.java b/java/src/com/google/idea/blaze/java/run/BlazeJavaRunConfigurationHandler.java
index fd26ffd..479b964 100644
--- a/java/src/com/google/idea/blaze/java/run/BlazeJavaRunConfigurationHandler.java
+++ b/java/src/com/google/idea/blaze/java/run/BlazeJavaRunConfigurationHandler.java
@@ -22,6 +22,7 @@
import com.google.idea.blaze.base.run.confighandler.BlazeCommandRunConfigurationRunner;
import com.google.idea.blaze.base.run.state.BlazeCommandRunConfigurationCommonState;
import com.google.idea.blaze.base.settings.Blaze;
+import com.google.idea.blaze.base.settings.Blaze.BuildSystem;
import com.intellij.execution.Executor;
import com.intellij.execution.configurations.RunConfiguration;
import com.intellij.execution.configurations.RunProfileState;
@@ -38,8 +39,9 @@
private final BlazeCommandRunConfigurationCommonState state;
public BlazeJavaRunConfigurationHandler(BlazeCommandRunConfiguration configuration) {
- this.buildSystemName = Blaze.buildSystemName(configuration.getProject());
- this.state = new BlazeCommandRunConfigurationCommonState(buildSystemName);
+ BuildSystem buildSystem = Blaze.getBuildSystem(configuration.getProject());
+ this.buildSystemName = buildSystem.getName();
+ this.state = new BlazeCommandRunConfigurationCommonState(buildSystem);
}
@Override
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 a712e65..47b971b 100644
--- a/java/src/com/google/idea/blaze/java/run/BlazeJavaRunProfileState.java
+++ b/java/src/com/google/idea/blaze/java/run/BlazeJavaRunProfileState.java
@@ -28,8 +28,10 @@
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.processhandler.LineProcessingProcessAdapter;
import com.google.idea.blaze.base.run.processhandler.ScopedBlazeProcessHandler;
+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;
@@ -38,14 +40,22 @@
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;
+import com.intellij.execution.Executor;
import com.intellij.execution.configurations.CommandLineState;
import com.intellij.execution.configurations.RemoteConnection;
import com.intellij.execution.configurations.RemoteState;
import com.intellij.execution.configurations.RunProfile;
+import com.intellij.execution.configurations.WrappingRunConfiguration;
+import com.intellij.execution.filters.TextConsoleBuilderImpl;
import com.intellij.execution.process.ProcessHandler;
import com.intellij.execution.process.ProcessListener;
import com.intellij.execution.runners.ExecutionEnvironment;
+import com.intellij.execution.runners.ProgramRunner;
+import com.intellij.execution.ui.ConsoleView;
import com.intellij.openapi.project.Project;
/**
@@ -54,6 +64,10 @@
* when using a debug executor.
*/
final class BlazeJavaRunProfileState extends CommandLineState implements RemoteState {
+
+ private static final BoolExperiment smRunnerUiEnabled =
+ new BoolExperiment("use.smrunner.ui.java", true);
+
// Blaze seems to always use this port for --java_debug.
// TODO(joshgiles): Look at manually identifying and setting port.
private static final int DEBUG_PORT = 5005;
@@ -64,12 +78,18 @@
public BlazeJavaRunProfileState(ExecutionEnvironment environment, boolean debug) {
super(environment);
- RunProfile runProfile = environment.getRunProfile();
- assert runProfile instanceof BlazeCommandRunConfiguration;
- configuration = (BlazeCommandRunConfiguration) runProfile;
+ this.configuration = getConfiguration(environment);
this.debug = debug;
}
+ private static BlazeCommandRunConfiguration getConfiguration(ExecutionEnvironment environment) {
+ RunProfile runProfile = environment.getRunProfile();
+ if (runProfile instanceof WrappingRunConfiguration) {
+ runProfile = ((WrappingRunConfiguration) runProfile).getPeer();
+ }
+ return (BlazeCommandRunConfiguration) runProfile;
+ }
+
@Override
protected ProcessHandler startProcess() throws ExecutionException {
Project project = configuration.getProject();
@@ -80,7 +100,25 @@
ProjectViewSet projectViewSet = ProjectViewManager.getInstance(project).getProjectViewSet();
assert projectViewSet != null;
- BlazeCommand blazeCommand = getBlazeCommand(project, configuration, projectViewSet, debug);
+ BlazeCommand blazeCommand;
+ if (useTestUi()) {
+ BlazeJavaTestEventsHandler eventsHandler = new BlazeJavaTestEventsHandler();
+ blazeCommand =
+ getBlazeCommand(
+ project, configuration, projectViewSet, eventsHandler.getBlazeFlags(), debug);
+ setConsoleBuilder(
+ new TextConsoleBuilderImpl(project) {
+ @Override
+ protected ConsoleView createConsole() {
+ return SmRunnerUtils.getConsoleView(
+ project, configuration, getEnvironment().getExecutor(), eventsHandler);
+ }
+ });
+ } else {
+ blazeCommand =
+ getBlazeCommand(project, configuration, projectViewSet, ImmutableList.of(), debug);
+ }
+
WorkspaceRoot workspaceRoot = WorkspaceRoot.fromImportSettings(importSettings);
return new ScopedBlazeProcessHandler(
project,
@@ -106,6 +144,24 @@
}
@Override
+ public ExecutionResult execute(Executor executor, ProgramRunner runner)
+ throws ExecutionException {
+ DefaultExecutionResult result = (DefaultExecutionResult) super.execute(executor, runner);
+ return SmRunnerUtils.attachRerunFailedTestsAction(result);
+ }
+
+ private boolean useTestUi() {
+ if (!smRunnerUiEnabled.getValue()) {
+ return false;
+ }
+ BlazeCommandRunConfigurationCommonState state =
+ configuration.getHandlerStateIfType(BlazeCommandRunConfigurationCommonState.class);
+ return state != null
+ && BlazeCommandName.TEST.equals(state.getCommand())
+ && !Boolean.TRUE.equals(state.getRunOnDistributedExecutor());
+ }
+
+ @Override
public RemoteConnection getRemoteConnection() {
if (!debug) {
return null;
@@ -122,6 +178,7 @@
Project project,
BlazeCommandRunConfiguration configuration,
ProjectViewSet projectViewSet,
+ ImmutableList<String> extraBlazeFlags,
boolean debug) {
BlazeCommandRunConfigurationCommonState handlerState =
@@ -135,6 +192,7 @@
.setBlazeBinary(handlerState.getBlazeBinary())
.addTargets(configuration.getTarget())
.addBlazeFlags(BlazeFlags.buildFlags(project, projectViewSet))
+ .addBlazeFlags(extraBlazeFlags)
.addBlazeFlags(handlerState.getBlazeFlags());
if (debug) {
@@ -145,6 +203,10 @@
} else {
command.addBlazeFlags(BlazeFlags.JAVA_TEST_DEBUG);
}
+ } else {
+ command.addBlazeFlags(
+ DistributedExecutorSupport.getBlazeFlags(
+ project, handlerState.getRunOnDistributedExecutor()));
}
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
new file mode 100644
index 0000000..9af9b64
--- /dev/null
+++ b/java/src/com/google/idea/blaze/java/run/BlazeJavaTestEventsHandler.java
@@ -0,0 +1,94 @@
+/*
+ * 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.java.run;
+
+import com.google.idea.blaze.base.command.BlazeFlags;
+import com.google.idea.blaze.base.run.smrunner.BlazeTestEventsHandler;
+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;
+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.List;
+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
+ public SMTestLocator getTestLocator() {
+ return JavaTestLocator.INSTANCE;
+ }
+
+ @Override
+ public String suiteLocationUrl(String name) {
+ return JavaTestLocator.SUITE_PROTOCOL + URLUtil.SCHEME_SEPARATOR + name;
+ }
+
+ @Override
+ public String testLocationUrl(String name, @Nullable String classname) {
+ if (classname == null) {
+ return null;
+ }
+ return JavaTestLocator.TEST_PROTOCOL + URLUtil.SCHEME_SEPARATOR + classname + "." + name;
+ }
+
+ @Override
+ public String suiteDisplayName(String rawName) {
+ String name = StringUtil.trimEnd(rawName, '.');
+ int lastPointIx = name.lastIndexOf('.');
+ return lastPointIx != -1 ? name.substring(lastPointIx + 1, name.length()) : name;
+ }
+
+ @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));
+ }
+ String filter = BlazeJUnitTestFilterFlags.testFilterForClassesAndMethods(failedMethodsPerClass);
+ return filter != null ? BlazeFlags.TEST_FILTER + "=" + filter : null;
+ }
+
+ private static void appendTest(
+ MultiMap<PsiClass, PsiMethod> testMap, @Nullable Location<?> testLocation) {
+ if (testLocation == null) {
+ return;
+ }
+ PsiElement method = testLocation.getPsiElement();
+ if (!(method instanceof PsiMethod)) {
+ return;
+ }
+ PsiClass psiClass = ((PsiMethod) method).getContainingClass();
+ if (psiClass != null) {
+ testMap.putValue(psiClass, (PsiMethod) method);
+ }
+ }
+}
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 2c6a6aa..8f2290f 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
@@ -16,76 +16,147 @@
package com.google.idea.blaze.java.run.producers;
+import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
-import com.google.idea.blaze.base.command.BlazeFlags;
-import com.google.idea.common.experiments.BoolExperiment;
+import com.google.common.collect.ImmutableList;
+import com.intellij.execution.junit.JUnitUtil;
+import com.intellij.execution.junit2.PsiMemberParameterizedLocation;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiMethod;
+import com.intellij.util.containers.MultiMap;
import java.util.Collection;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.stream.Collectors;
import javax.annotation.Nullable;
/** Utilities for building test filter flags for JUnit tests. */
public final class BlazeJUnitTestFilterFlags {
- private static final BoolExperiment enableParameterizedSupport =
- new BoolExperiment("enable.parameterized.test.support", true);
-
/** A version of JUnit to generate test filter flags for. */
public enum JUnitVersion {
JUNIT_3,
JUNIT_4
}
- public static String testFilterFlagForClass(String className, JUnitVersion jUnitVersion) {
- return testFilterFlagForClassAndMethod(
- className, null, jUnitVersion, /* parameterized doesn't matter for a class */ false);
+ /**
+ * Builds the JUnit test filter corresponding to the given class.<br>
+ * Returns null if no class name can be found.
+ */
+ @Nullable
+ public static String testFilterForClass(PsiClass psiClass) {
+ return testFilterForClassAndMethods(psiClass, ImmutableList.of());
}
- public static String testFilterFlagForClassAndMethod(
+ /**
+ * Builds the JUnit test filter corresponding to the given class and methods.<br>
+ * Returns null if no class name can be found.
+ */
+ @Nullable
+ public static String testFilterForClassAndMethods(
+ PsiClass psiClass, Collection<PsiMethod> methods) {
+ JUnitVersion version =
+ JUnitUtil.isJUnit4TestClass(psiClass) ? JUnitVersion.JUNIT_4 : JUnitVersion.JUNIT_3;
+ return testFilterForClassAndMethods(psiClass, version, methods);
+ }
+
+ @Nullable
+ public static String testFilterForClassesAndMethods(
+ MultiMap<PsiClass, PsiMethod> 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 =
+ hasJUnit4Test(methodsPerClass.keySet()) ? JUnitVersion.JUNIT_4 : JUnitVersion.JUNIT_3;
+ return testFilterForClassesAndMethods(methodsPerClass, version);
+ }
+
+ @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());
+ if (filter != null) {
+ output.append(filter);
+ }
+ }
+ return Strings.emptyToNull(output.toString());
+ }
+
+ private static boolean hasJUnit4Test(Collection<PsiClass> classes) {
+ for (PsiClass psiClass : classes) {
+ if (JUnitUtil.isJUnit4TestClass(psiClass)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Builds the JUnit test filter corresponding to the given class and methods.<br>
+ * Returns null if no class name can be found.
+ */
+ @Nullable
+ private static String testFilterForClassAndMethods(
+ PsiClass psiClass, JUnitVersion version, Collection<PsiMethod> methods) {
+ 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;
+ }
+
+ /**
+ * Builds the blaze test_filter flag for JUnit tests. Excludes the "--test_filter" component of
+ * the flag, so that multiple test classes can be combined.
+ */
+ @VisibleForTesting
+ static String testFilterForClassAndMethods(
String className,
- @Nullable String methodName,
+ List<String> methodNames,
JUnitVersion jUnitVersion,
boolean parameterized) {
- StringBuilder output = new StringBuilder(BlazeFlags.TEST_FILTER);
- output.append('=');
- output.append(className);
-
- if (!Strings.isNullOrEmpty(methodName)) {
- output.append('#');
- output.append(methodName);
- // JUnit 4 test filters are regexes, and must be terminated to avoid matching
- // unintended classes/methods. JUnit 3 test filters do not need or support this syntax.
+ StringBuilder output = new StringBuilder(className);
+ String methodNamePattern = concatenateMethodNames(methodNames, jUnitVersion);
+ if (Strings.isNullOrEmpty(methodNamePattern)) {
if (jUnitVersion == JUnitVersion.JUNIT_4) {
- // parameterized tests include their parameters between brackets after the method name
- if (parameterized && enableParameterizedSupport.getValue()) {
- output.append("(\\[.+\\])?");
- }
- output.append("$");
+ output.append('#');
}
- } else if (jUnitVersion == JUnitVersion.JUNIT_4) {
- output.append('#');
+ return output.toString();
}
-
+ output.append('#').append(methodNamePattern);
+ // JUnit 4 test filters are regexes, and must be terminated to avoid matching
+ // unintended classes/methods. JUnit 3 test filters do not need or support this syntax.
+ 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();
}
- public static String testFilterFlagForClassAndMethods(
- String className,
- Collection<String> methodNames,
- JUnitVersion jUnitVersion,
- boolean parameterized) {
- if (methodNames.size() == 0) {
- return testFilterFlagForClass(className, jUnitVersion);
- } else if (methodNames.size() == 1) {
- return testFilterFlagForClassAndMethod(
- className, methodNames.iterator().next(), jUnitVersion, parameterized);
+ @Nullable
+ private static String concatenateMethodNames(
+ List<String> methodNames, JUnitVersion jUnitVersion) {
+ if (methodNames.isEmpty()) {
+ return null;
}
- String methodNamePattern;
- if (jUnitVersion == JUnitVersion.JUNIT_4) {
- methodNamePattern = String.format("(%s)", String.join("|", methodNames));
- } else {
- methodNamePattern = String.join(",", methodNames);
+ if (methodNames.size() == 1) {
+ return methodNames.get(0);
}
- return testFilterFlagForClassAndMethod(
- className, methodNamePattern, jUnitVersion, parameterized);
+ return jUnitVersion == JUnitVersion.JUNIT_4
+ ? String.format("(%s)", String.join("|", methodNames))
+ : String.join(",", methodNames);
}
private BlazeJUnitTestFilterFlags() {}
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 3eb8ab3..95bc4ee 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
@@ -26,7 +26,6 @@
import com.google.idea.blaze.base.run.producers.BlazeRunConfigurationProducer;
import com.google.idea.blaze.base.run.state.BlazeCommandRunConfigurationCommonState;
import com.google.idea.blaze.java.run.RunUtil;
-import com.google.idea.blaze.java.run.producers.BlazeJUnitTestFilterFlags.JUnitVersion;
import com.intellij.execution.JavaExecutionUtil;
import com.intellij.execution.Location;
import com.intellij.execution.actions.ConfigurationContext;
@@ -36,7 +35,6 @@
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiModifier;
-import java.util.List;
import java.util.Objects;
import org.jetbrains.annotations.NotNull;
@@ -90,13 +88,10 @@
ImmutableList.Builder<String> flags = ImmutableList.builder();
- final String qualifiedName = testClass.getQualifiedName();
- if (qualifiedName != null) {
- final JUnitVersion jUnitVersion =
- JUnitUtil.isJUnit4TestClass(testClass) ? JUnitVersion.JUNIT_4 : JUnitVersion.JUNIT_3;
- flags.add(BlazeJUnitTestFilterFlags.testFilterFlagForClass(qualifiedName, jUnitVersion));
+ String testFilter = BlazeJUnitTestFilterFlags.testFilterForClass(testClass);
+ if (testFilter != null) {
+ flags.add(BlazeFlags.TEST_FILTER + "=" + testFilter);
}
-
flags.add(BlazeFlags.TEST_OUTPUT_STREAMED);
flags.addAll(handlerState.getBlazeFlags());
@@ -147,12 +142,7 @@
if (!Objects.equals(handlerState.getCommand(), BlazeCommandName.TEST)) {
return false;
}
- List<String> flags = handlerState.getBlazeFlags();
-
- final JUnitVersion jUnitVersion =
- JUnitUtil.isJUnit4TestClass(testClass) ? JUnitVersion.JUNIT_4 : JUnitVersion.JUNIT_3;
- return flags.contains(
- BlazeJUnitTestFilterFlags.testFilterFlagForClass(
- testClass.getQualifiedName(), jUnitVersion));
+ String filter = BlazeJUnitTestFilterFlags.testFilterForClass(testClass);
+ return Objects.equals(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 2fc899f..1a7e149 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
@@ -26,18 +26,14 @@
import com.google.idea.blaze.base.run.producers.BlazeRunConfigurationProducer;
import com.google.idea.blaze.base.run.state.BlazeCommandRunConfigurationCommonState;
import com.google.idea.blaze.java.run.RunUtil;
-import com.google.idea.blaze.java.run.producers.BlazeJUnitTestFilterFlags.JUnitVersion;
import com.intellij.execution.actions.ConfigurationContext;
-import com.intellij.execution.junit.JUnitUtil;
-import com.intellij.execution.junit2.PsiMemberParameterizedLocation;
import com.intellij.openapi.util.Ref;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiMethod;
-import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
import java.util.Objects;
+import java.util.stream.Collectors;
import org.jetbrains.annotations.NotNull;
/** Producer for run configurations related to Java test methods in Blaze. */
@@ -152,29 +148,15 @@
return null;
}
}
-
- final List<String> methodNames = new ArrayList<>();
- for (PsiMethod method : selectedMethods) {
- methodNames.add(method.getName());
- }
- // Sort so multiple configurations created with different selection orders are the same.
- Collections.sort(methodNames);
-
- final String qualifiedName = containingClass.getQualifiedName();
- if (qualifiedName == null) {
+ String testFilter =
+ BlazeJUnitTestFilterFlags.testFilterForClassAndMethods(containingClass, selectedMethods);
+ if (testFilter == null) {
return null;
}
- final JUnitVersion jUnitVersion =
- JUnitUtil.isJUnit4TestClass(containingClass) ? JUnitVersion.JUNIT_4 : JUnitVersion.JUNIT_3;
- boolean parameterized = isParameterized(containingClass);
- final String testFilterFlag =
- BlazeJUnitTestFilterFlags.testFilterFlagForClassAndMethods(
- qualifiedName, methodNames, jUnitVersion, parameterized);
-
+ // Sort so multiple configurations created with different selection orders are the same.
+ List<String> methodNames =
+ selectedMethods.stream().map(PsiMethod::getName).sorted().collect(Collectors.toList());
+ final String testFilterFlag = BlazeFlags.TEST_FILTER + "=" + testFilter;
return new SelectedMethodInfo(firstMethod, containingClass, methodNames, testFilterFlag);
}
-
- private static boolean isParameterized(PsiClass testClass) {
- return PsiMemberParameterizedLocation.getParameterizedLocation(testClass, null) != null;
- }
}
diff --git a/java/src/com/google/idea/blaze/java/sync/BlazeJavaSyncPlugin.java b/java/src/com/google/idea/blaze/java/sync/BlazeJavaSyncPlugin.java
index 65151a6..b971b94 100644
--- a/java/src/com/google/idea/blaze/java/sync/BlazeJavaSyncPlugin.java
+++ b/java/src/com/google/idea/blaze/java/sync/BlazeJavaSyncPlugin.java
@@ -22,6 +22,7 @@
import com.google.idea.blaze.base.ideinfo.TargetMap;
import com.google.idea.blaze.base.model.BlazeLibrary;
import com.google.idea.blaze.base.model.BlazeProjectData;
+import com.google.idea.blaze.base.model.BlazeVersionData;
import com.google.idea.blaze.base.model.SyncState;
import com.google.idea.blaze.base.model.primitives.LanguageClass;
import com.google.idea.blaze.base.model.primitives.WorkspaceRoot;
@@ -56,6 +57,7 @@
import com.google.idea.blaze.java.sync.projectstructure.JavaSourceFolderProvider;
import com.google.idea.blaze.java.sync.projectstructure.Jdks;
import com.google.idea.blaze.java.sync.workingset.JavaWorkingSet;
+import com.google.idea.sdkcompat.transactions.Transactions;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.module.ModuleType;
import com.intellij.openapi.module.StdModuleTypes;
@@ -64,7 +66,6 @@
import com.intellij.openapi.roots.LanguageLevelProjectExtension;
import com.intellij.openapi.roots.ex.ProjectRootManagerEx;
import com.intellij.pom.java.LanguageLevel;
-import com.intellij.util.ui.UIUtil;
import java.util.Collection;
import java.util.Set;
import javax.annotation.Nullable;
@@ -170,6 +171,7 @@
Project project,
BlazeContext context,
ProjectViewSet projectViewSet,
+ BlazeVersionData blazeVersionData,
BlazeProjectData blazeProjectData) {
if (!blazeProjectData.workspaceLanguageSettings.isWorkspaceType(WorkspaceType.JAVA)) {
return;
@@ -212,19 +214,18 @@
private static void setProjectSdkAndLanguageLevel(
final Project project, final Sdk sdk, final LanguageLevel javaLanguageLevel) {
- UIUtil.invokeAndWaitIfNeeded(
- (Runnable)
- () ->
- ApplicationManager.getApplication()
- .runWriteAction(
- () -> {
- ProjectRootManagerEx rootManager =
- ProjectRootManagerEx.getInstanceEx(project);
- rootManager.setProjectSdk(sdk);
- LanguageLevelProjectExtension ext =
- LanguageLevelProjectExtension.getInstance(project);
- ext.setLanguageLevel(javaLanguageLevel);
- }));
+ Transactions.submitTransactionAndWait(
+ () ->
+ ApplicationManager.getApplication()
+ .runWriteAction(
+ () -> {
+ ProjectRootManagerEx rootManager =
+ ProjectRootManagerEx.getInstanceEx(project);
+ rootManager.setProjectSdk(sdk);
+ LanguageLevelProjectExtension ext =
+ LanguageLevelProjectExtension.getInstance(project);
+ ext.setLanguageLevel(javaLanguageLevel);
+ }));
}
@Override
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 5855655..abe5804 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
@@ -23,6 +23,7 @@
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;
@@ -42,6 +43,7 @@
/** 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;
@@ -106,6 +108,7 @@
private static String derivePackagePrefix(VirtualFile file, SourceFolder parentFolder) {
String parentPackagePrefix = parentFolder.getPackagePrefix();
+ logger.assertTrue(parentFolder.getFile() != null);
String relativePath = VfsUtilCore.getRelativePath(file, parentFolder.getFile(), '.');
if (Strings.isNullOrEmpty(relativePath)) {
return parentPackagePrefix;
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 3f3aede..10e43eb 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
@@ -29,6 +29,7 @@
import com.google.idea.blaze.base.projectview.ProjectViewSet;
import com.google.idea.blaze.base.run.BlazeCommandRunConfiguration;
import com.google.idea.blaze.base.run.BlazeCommandRunConfigurationType;
+import com.google.idea.blaze.base.run.DistributedExecutorSupport;
import com.google.idea.blaze.base.run.confighandler.BlazeCommandGenericRunConfigurationHandlerProvider;
import com.google.idea.blaze.base.run.confighandler.BlazeCommandRunConfigurationHandlerProvider;
import com.google.idea.blaze.base.run.state.BlazeCommandRunConfigurationCommonState;
@@ -68,6 +69,7 @@
applicationServices.register(TargetFinder.class, new MockTargetFinder());
applicationServices.register(BlazeUserSettings.class, new BlazeUserSettings());
registerExtensionPoint(BuildFlagsProvider.EP_NAME, BuildFlagsProvider.class);
+ registerExtensionPoint(DistributedExecutorSupport.EP_NAME, DistributedExecutorSupport.class);
ExtensionPointImpl<BlazeCommandRunConfigurationHandlerProvider> handlerProviderEp =
registerExtensionPoint(
BlazeCommandRunConfigurationHandlerProvider.EP_NAME,
@@ -87,7 +89,11 @@
handlerState.setBlazeFlags(ImmutableList.of("--flag1", "--flag2"));
assertThat(
BlazeJavaRunProfileState.getBlazeCommand(
- project, configuration, ProjectViewSet.builder().build(), false /* debug */)
+ project,
+ configuration,
+ ProjectViewSet.builder().build(),
+ ImmutableList.of(),
+ false /* debug */)
.toList())
.isEqualTo(
ImmutableList.of(
@@ -108,7 +114,11 @@
handlerState.setCommand(BlazeCommandName.fromString("command"));
assertThat(
BlazeJavaRunProfileState.getBlazeCommand(
- project, configuration, ProjectViewSet.builder().build(), true /* debug */)
+ project,
+ configuration,
+ ProjectViewSet.builder().build(),
+ ImmutableList.of(),
+ true /* debug */)
.toList())
.isEqualTo(
ImmutableList.of(
@@ -128,7 +138,11 @@
handlerState.setCommand(BlazeCommandName.fromString("command"));
assertThat(
BlazeJavaRunProfileState.getBlazeCommand(
- project, configuration, ProjectViewSet.builder().build(), true /* debug */)
+ project,
+ configuration,
+ ProjectViewSet.builder().build(),
+ ImmutableList.of(),
+ true /* debug */)
.toList())
.isEqualTo(
ImmutableList.of(
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
new file mode 100644
index 0000000..d5d2cde
--- /dev/null
+++ b/java/tests/unittests/com/google/idea/blaze/java/run/producers/BlazeJUnitTestFilterFlagsTest.java
@@ -0,0 +1,130 @@
+/*
+ * 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.java.run.producers;
+
+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.java.run.producers.BlazeJUnitTestFilterFlags.JUnitVersion;
+import com.google.idea.common.experiments.ExperimentService;
+import com.google.idea.common.experiments.MockExperimentService;
+import org.jetbrains.annotations.NotNull;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Unit tests for {@link BlazeJUnitTestFilterFlags}. */
+@RunWith(JUnit4.class)
+public class BlazeJUnitTestFilterFlagsTest extends BlazeTestCase {
+
+ @Override
+ protected void initTest(
+ @NotNull Container applicationServices, @NotNull Container projectServices) {
+ ExperimentService experimentService = new MockExperimentService();
+ applicationServices.register(ExperimentService.class, experimentService);
+ }
+
+ @Test
+ public void testSingleJUnit4ClassFilter() {
+ assertThat(
+ BlazeJUnitTestFilterFlags.testFilterForClassAndMethods(
+ "com.google.idea.ClassName", ImmutableList.of(), JUnitVersion.JUNIT_4, false))
+ .isEqualTo("com.google.idea.ClassName#");
+ }
+
+ @Test
+ public void testSingleJUnit3ClassFilter() {
+ assertThat(
+ BlazeJUnitTestFilterFlags.testFilterForClassAndMethods(
+ "com.google.idea.ClassName", ImmutableList.of(), JUnitVersion.JUNIT_3, false))
+ .isEqualTo("com.google.idea.ClassName");
+ }
+
+ @Test
+ public void testParameterizedIgnoredForSingleClass() {
+ assertThat(
+ BlazeJUnitTestFilterFlags.testFilterForClassAndMethods(
+ "com.google.idea.ClassName", ImmutableList.of(), JUnitVersion.JUNIT_4, true))
+ .isEqualTo("com.google.idea.ClassName#");
+ }
+
+ @Test
+ public void testJUnit4ClassAndSingleMethod() {
+ assertThat(
+ BlazeJUnitTestFilterFlags.testFilterForClassAndMethods(
+ "com.google.idea.ClassName",
+ ImmutableList.of("testMethod1"),
+ JUnitVersion.JUNIT_4,
+ false))
+ .isEqualTo("com.google.idea.ClassName#testMethod1$");
+ }
+
+ @Test
+ public void testJUnit3ClassAndSingleMethod() {
+ assertThat(
+ BlazeJUnitTestFilterFlags.testFilterForClassAndMethods(
+ "com.google.idea.ClassName",
+ ImmutableList.of("testMethod1"),
+ JUnitVersion.JUNIT_3,
+ false))
+ .isEqualTo("com.google.idea.ClassName#testMethod1");
+ }
+
+ @Test
+ public void testJUnit4ClassAndMultipleMethods() {
+ assertThat(
+ BlazeJUnitTestFilterFlags.testFilterForClassAndMethods(
+ "com.google.idea.ClassName",
+ ImmutableList.of("testMethod1", "testMethod2"),
+ JUnitVersion.JUNIT_4,
+ false))
+ .isEqualTo("com.google.idea.ClassName#(testMethod1|testMethod2)$");
+ }
+
+ @Test
+ public void testJUnit4ParametrizedClassAndMultipleMethods() {
+ assertThat(
+ BlazeJUnitTestFilterFlags.testFilterForClassAndMethods(
+ "com.google.idea.ClassName",
+ ImmutableList.of("testMethod1", "testMethod2"),
+ JUnitVersion.JUNIT_4,
+ true))
+ .isEqualTo("com.google.idea.ClassName#(testMethod1|testMethod2)(\\[.+\\])?$");
+ }
+
+ @Test
+ public void testJUnit3ClassAndMultipleMethods() {
+ 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))
+ .isEqualTo("com.google.idea.ClassName#testMethod1,testMethod2");
+ }
+}
diff --git a/plugin_dev/BUILD b/plugin_dev/BUILD
index 48eb552..1c56953 100644
--- a/plugin_dev/BUILD
+++ b/plugin_dev/BUILD
@@ -9,6 +9,7 @@
"//intellij_platform_sdk:devkit",
"//intellij_platform_sdk:plugin_api",
"//java",
+ "//sdkcompat",
"@jsr305_annotations//jar",
],
)
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 00cbccb..a68f89b 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
@@ -35,6 +35,8 @@
import com.google.idea.blaze.base.projectview.ProjectViewSet;
import com.google.idea.blaze.base.run.BlazeConfigurationNameBuilder;
import com.google.idea.blaze.base.run.BlazeRunConfiguration;
+import com.google.idea.blaze.base.run.state.RunConfigurationFlagsState;
+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;
@@ -47,6 +49,7 @@
import com.intellij.execution.configurations.JavaCommandLineState;
import com.intellij.execution.configurations.JavaParameters;
import com.intellij.execution.configurations.LocatableConfigurationBase;
+import com.intellij.execution.configurations.LogFileOptions;
import com.intellij.execution.configurations.ModuleRunConfiguration;
import com.intellij.execution.configurations.ParametersList;
import com.intellij.execution.configurations.RunProfileState;
@@ -75,13 +78,15 @@
import com.intellij.openapi.util.WriteExternalException;
import com.intellij.ui.ListCellRendererWrapper;
import com.intellij.ui.RawCommandLineEditor;
+import com.intellij.ui.components.JBCheckBox;
import com.intellij.util.PlatformUtils;
-import com.intellij.util.execution.ParametersListUtil;
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;
@@ -89,7 +94,6 @@
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JList;
-import javax.swing.JTextArea;
import org.jdom.Element;
/**
@@ -105,16 +109,20 @@
private static final String SDK_ATTR = "blaze-plugin-sdk";
private static final String VM_PARAMS_ATTR = "blaze-vm-params";
private static final String PROGRAM_PARAMS_ATTR = "blaze-program-params";
+ private static final String KEEP_IN_SYNC_TAG = "keep-in-sync";
private final String buildSystem;
@Nullable private Label target;
- private ImmutableList<String> blazeFlags = ImmutableList.of();
- private ImmutableList<String> exeFlags = ImmutableList.of();
+ private final RunConfigurationFlagsState blazeFlags;
+ private final RunConfigurationFlagsState exeFlags;
@Nullable private Sdk pluginSdk;
@Nullable String vmParameters;
@Nullable private String programParameters;
+ // for keeping imported configurations in sync with their source XML
+ @Nullable private Boolean keepInSync = null;
+
public BlazeIntellijPluginConfiguration(
Project project,
ConfigurationFactory factory,
@@ -129,6 +137,19 @@
if (initialTarget != null) {
target = initialTarget.key.label;
}
+ blazeFlags = new RunConfigurationFlagsState(USER_BLAZE_FLAG_TAG, buildSystem + " flags:");
+ exeFlags = new RunConfigurationFlagsState(USER_EXE_FLAG_TAG, "Executable flags:");
+ }
+
+ @Override
+ public void setKeepInSync(@Nullable Boolean keepInSync) {
+ this.keepInSync = keepInSync;
+ }
+
+ @Override
+ @Nullable
+ public Boolean getKeepInSync() {
+ return keepInSync;
}
@Override
@@ -147,6 +168,19 @@
}
}
+ @Override
+ public ArrayList<LogFileOptions> getAllLogFiles() {
+ ArrayList<LogFileOptions> result = new ArrayList<>();
+ if (pluginSdk == null) {
+ return result;
+ }
+ String sandboxHome = IdeaJdkHelper.getSandboxHome(pluginSdk);
+ String logFile = Paths.get(sandboxHome, "system", "log", "idea.log").toString();
+ LogFileOptions logFileOptions = new LogFileOptions("idea.log", logFile, true, true, true);
+ result.add(logFileOptions);
+ return result;
+ }
+
private ImmutableList<File> findPluginJars() throws ExecutionException {
BlazeProjectData blazeProjectData =
BlazeProjectDataManager.getInstance(getProject()).getBlazeProjectData();
@@ -353,8 +387,8 @@
} else {
target = null;
}
- blazeFlags = loadUserFlags(element, USER_BLAZE_FLAG_TAG);
- exeFlags = loadUserFlags(element, USER_EXE_FLAG_TAG);
+ blazeFlags.readExternal(element);
+ exeFlags.readExternal(element);
String sdkName = element.getAttributeValue(SDK_ATTR);
if (!Strings.isNullOrEmpty(sdkName)) {
@@ -362,25 +396,9 @@
}
vmParameters = Strings.emptyToNull(element.getAttributeValue(VM_PARAMS_ATTR));
programParameters = Strings.emptyToNull(element.getAttributeValue(PROGRAM_PARAMS_ATTR));
- }
- private static ImmutableList<String> loadUserFlags(Element root, String tag) {
- ImmutableList.Builder<String> flagsBuilder = ImmutableList.builder();
- for (Element e : root.getChildren(tag)) {
- String flag = e.getTextTrim();
- if (flag != null && !flag.isEmpty()) {
- flagsBuilder.add(flag);
- }
- }
- return flagsBuilder.build();
- }
-
- private static void saveUserFlags(Element root, List<String> flags, String tag) {
- for (String flag : flags) {
- Element child = new Element(tag);
- child.setText(flag);
- root.addContent(child);
- }
+ String keepInSyncString = element.getAttributeValue(KEEP_IN_SYNC_TAG);
+ keepInSync = keepInSyncString != null ? Boolean.parseBoolean(keepInSyncString) : null;
}
@Override
@@ -392,8 +410,8 @@
targetElement.setText(target.toString());
element.addContent(targetElement);
}
- saveUserFlags(element, blazeFlags, USER_BLAZE_FLAG_TAG);
- saveUserFlags(element, exeFlags, USER_EXE_FLAG_TAG);
+ blazeFlags.writeExternal(element);
+ exeFlags.writeExternal(element);
if (pluginSdk != null) {
element.setAttribute(SDK_ATTR, pluginSdk.getName());
}
@@ -403,6 +421,9 @@
if (programParameters != null) {
element.setAttribute(PROGRAM_PARAMS_ATTR, programParameters);
}
+ if (keepInSync != null) {
+ element.setAttribute(KEEP_IN_SYNC_TAG, Boolean.toString(keepInSync));
+ }
}
@Override
@@ -410,11 +431,12 @@
final BlazeIntellijPluginConfiguration configuration =
(BlazeIntellijPluginConfiguration) super.clone();
configuration.target = target;
- configuration.blazeFlags = blazeFlags;
- configuration.exeFlags = exeFlags;
+ configuration.blazeFlags.setFlags(blazeFlags.getFlags());
+ configuration.exeFlags.setFlags(exeFlags.getFlags());
configuration.pluginSdk = pluginSdk;
configuration.vmParameters = vmParameters;
configuration.programParameters = programParameters;
+ configuration.keepInSync = keepInSync;
return configuration;
}
@@ -423,8 +445,8 @@
BlazeCommand.builder(Blaze.getBuildSystem(getProject()), BlazeCommandName.BUILD)
.addTargets(getTarget())
.addBlazeFlags(BlazeFlags.buildFlags(project, projectViewSet))
- .addBlazeFlags(blazeFlags)
- .addExeFlags(exeFlags);
+ .addBlazeFlags(blazeFlags.getFlags())
+ .addExeFlags(exeFlags.getFlags());
return command.build();
}
@@ -436,7 +458,8 @@
for (TargetIdeInfo target : javaTargets) {
javaLabels.add(target.key.label);
}
- return new BlazeIntellijPluginConfigurationSettingsEditor(buildSystem, javaLabels);
+ return new BlazeIntellijPluginConfigurationSettingsEditor(
+ javaLabels, blazeFlags.getEditor(getProject()), exeFlags.getEditor(getProject()));
}
@Override
@@ -456,21 +479,23 @@
@VisibleForTesting
static class BlazeIntellijPluginConfigurationSettingsEditor
extends SettingsEditor<BlazeIntellijPluginConfiguration> {
- private final String buildSystemName;
- private final ComboBox targetCombo;
- private final JTextArea blazeFlagsField = new JTextArea(5, 0);
- private final JTextArea exeFlagsField = new JTextArea(5, 0);
+ private final ComboBox<Label> targetCombo;
+ private final RunConfigurationStateEditor blazeFlagsEditor;
+ private final RunConfigurationStateEditor exeFlagsEditor;
private final JdkComboBox sdkCombo;
private final LabeledComponent<RawCommandLineEditor> vmParameters = new LabeledComponent<>();
private final LabeledComponent<RawCommandLineEditor> programParameters =
new LabeledComponent<>();
+ private final JBCheckBox keepInSyncCheckBox;
public BlazeIntellijPluginConfigurationSettingsEditor(
- String buildSystemName, List<Label> javaLabels) {
- this.buildSystemName = buildSystemName;
+ List<Label> javaLabels,
+ RunConfigurationStateEditor blazeFlagsEditor,
+ RunConfigurationStateEditor exeFlagsEditor) {
targetCombo =
- new ComboBox(
- new DefaultComboBoxModel(Ordering.usingToString().sortedCopy(javaLabels).toArray()));
+ new ComboBox<>(
+ new DefaultComboBoxModel<>(
+ Ordering.usingToString().sortedCopy(javaLabels).toArray(new Label[0])));
targetCombo.setRenderer(
new ListCellRendererWrapper<Label>() {
@Override
@@ -479,18 +504,35 @@
setText(value == null ? null : value.toString());
}
});
-
+ this.blazeFlagsEditor = blazeFlagsEditor;
+ this.exeFlagsEditor = exeFlagsEditor;
ProjectSdksModel sdksModel = new ProjectSdksModel();
sdksModel.reset(null);
sdkCombo = new JdkComboBox(sdksModel, IdeaJdkHelper::isIdeaJdkType);
+
+ keepInSyncCheckBox = new JBCheckBox("Keep in sync with source XML");
+ keepInSyncCheckBox.addItemListener(e -> updateEnabledStatus());
+ }
+
+ private void updateEnabledStatus() {
+ setEnabled(!keepInSyncCheckBox.isVisible() || !keepInSyncCheckBox.isSelected());
+ }
+
+ private void setEnabled(boolean enabled) {
+ targetCombo.setEnabled(enabled);
+ sdkCombo.setEnabled(enabled);
+ vmParameters.getComponent().setEnabled(enabled);
+ programParameters.getComponent().setEnabled(enabled);
+ blazeFlagsEditor.setComponentEnabled(enabled);
+ exeFlagsEditor.setComponentEnabled(enabled);
}
@VisibleForTesting
@Override
public void resetEditorFrom(BlazeIntellijPluginConfiguration s) {
targetCombo.setSelectedItem(s.getTarget());
- blazeFlagsField.setText(ParametersListUtil.join(s.blazeFlags));
- exeFlagsField.setText(ParametersListUtil.join(s.exeFlags));
+ blazeFlagsEditor.resetEditorFrom(s.blazeFlags);
+ exeFlagsEditor.resetEditorFrom(s.exeFlags);
if (s.pluginSdk != null) {
sdkCombo.setSelectedJdk(s.pluginSdk);
} else {
@@ -502,6 +544,10 @@
if (s.programParameters != null) {
programParameters.getComponent().setText(s.programParameters);
}
+ keepInSyncCheckBox.setVisible(s.keepInSync != null);
+ if (s.keepInSync != null) {
+ keepInSyncCheckBox.setSelected(s.keepInSync);
+ }
}
@VisibleForTesting
@@ -512,15 +558,12 @@
} catch (ClassCastException e) {
throw new ConfigurationException("Invalid label specified.");
}
- s.blazeFlags =
- ImmutableList.copyOf(
- ParametersListUtil.parse(Strings.nullToEmpty(blazeFlagsField.getText())));
- s.exeFlags =
- ImmutableList.copyOf(
- ParametersListUtil.parse(Strings.nullToEmpty(exeFlagsField.getText())));
+ blazeFlagsEditor.applyEditorTo(s.blazeFlags);
+ exeFlagsEditor.applyEditorTo(s.exeFlags);
s.pluginSdk = sdkCombo.getSelectedJdk();
s.vmParameters = vmParameters.getComponent().getText();
s.programParameters = programParameters.getComponent().getText();
+ s.keepInSync = keepInSyncCheckBox.isVisible() ? keepInSyncCheckBox.isSelected() : null;
}
@Override
@@ -544,10 +587,9 @@
vmParameters.getComponent(),
programParameters.getLabel(),
programParameters.getComponent(),
- new JLabel(buildSystemName + " flags:"),
- blazeFlagsField,
- new JLabel("Executable flags:"),
- exeFlagsField);
+ blazeFlagsEditor.createComponent(),
+ exeFlagsEditor.createComponent(),
+ keepInSyncCheckBox);
}
}
}
diff --git a/plugin_dev/src/com/google/idea/blaze/plugin/sync/IntellijPluginSyncPlugin.java b/plugin_dev/src/com/google/idea/blaze/plugin/sync/IntellijPluginSyncPlugin.java
index 34b06df..e2eeb8e 100644
--- a/plugin_dev/src/com/google/idea/blaze/plugin/sync/IntellijPluginSyncPlugin.java
+++ b/plugin_dev/src/com/google/idea/blaze/plugin/sync/IntellijPluginSyncPlugin.java
@@ -18,6 +18,7 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.idea.blaze.base.model.BlazeProjectData;
+import com.google.idea.blaze.base.model.BlazeVersionData;
import com.google.idea.blaze.base.model.primitives.LanguageClass;
import com.google.idea.blaze.base.model.primitives.WorkspaceType;
import com.google.idea.blaze.base.projectview.ProjectViewSet;
@@ -27,13 +28,13 @@
import com.google.idea.blaze.java.sync.JavaLanguageLevelHelper;
import com.google.idea.blaze.java.sync.model.BlazeJavaSyncData;
import com.google.idea.blaze.java.sync.projectstructure.JavaSourceFolderProvider;
+import com.google.idea.sdkcompat.transactions.Transactions;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.module.ModuleType;
import com.intellij.openapi.module.StdModuleTypes;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.LanguageLevelProjectExtension;
import com.intellij.pom.java.LanguageLevel;
-import com.intellij.util.ui.UIUtil;
import java.util.Set;
import javax.annotation.Nullable;
@@ -79,6 +80,7 @@
Project project,
BlazeContext context,
ProjectViewSet projectViewSet,
+ BlazeVersionData blazeVersionData,
BlazeProjectData blazeProjectData) {
if (!blazeProjectData.workspaceLanguageSettings.isWorkspaceType(
WorkspaceType.INTELLIJ_PLUGIN)) {
@@ -90,15 +92,14 @@
projectViewSet, blazeProjectData, LanguageLevel.JDK_1_7);
// Leave the SDK, but set the language level
- UIUtil.invokeAndWaitIfNeeded(
- (Runnable)
- () ->
- ApplicationManager.getApplication()
- .runWriteAction(
- () -> {
- LanguageLevelProjectExtension ext =
- LanguageLevelProjectExtension.getInstance(project);
- ext.setLanguageLevel(javaLanguageLevel);
- }));
+ Transactions.submitTransactionAndWait(
+ () ->
+ ApplicationManager.getApplication()
+ .runWriteAction(
+ () -> {
+ LanguageLevelProjectExtension ext =
+ LanguageLevelProjectExtension.getInstance(project);
+ ext.setLanguageLevel(javaLanguageLevel);
+ }));
}
}
diff --git a/proto_deps/proto_deps.jar b/proto_deps/proto_deps.jar
index 1a140af..6f79519 100755
--- a/proto_deps/proto_deps.jar
+++ b/proto_deps/proto_deps.jar
Binary files differ
diff --git a/sdkcompat/BUILD b/sdkcompat/BUILD
new file mode 100644
index 0000000..ce19ce5
--- /dev/null
+++ b/sdkcompat/BUILD
@@ -0,0 +1,25 @@
+# Description: Indirections for SDK changes to the underlying platform library.
+
+licenses(["notice"]) # Apache 2.0
+
+load("//intellij_platform_sdk:build_defs.bzl", "select_for_plugin_api")
+
+java_library(
+ name = "sdkcompat",
+ srcs = select_for_plugin_api({
+ "android-studio-145.1617.8": glob(["v145/**"]),
+ "android-studio-2.3.0.3": glob(["v162/**"]),
+ "android-studio-2.3.0.4": glob(["v162/**"]),
+ "intellij-2016.3.1": glob(["v163/**"]),
+ "intellij-162.2032.8": glob(["v162/**"]),
+ "clion-162.1967.7": glob(
+ ["v162/**"],
+ exclude = ["v162/com/google/idea/sdkcompat/debugger/**"],
+ ),
+ }),
+ visibility = ["//visibility:public"],
+ deps = [
+ "//intellij_platform_sdk:plugin_api",
+ "@jsr305_annotations//jar",
+ ],
+)
diff --git a/sdkcompat/v145/com/google/idea/sdkcompat/codestyle/CodeStyleManagerSdkCompatAdapter.java b/sdkcompat/v145/com/google/idea/sdkcompat/codestyle/CodeStyleManagerSdkCompatAdapter.java
new file mode 100644
index 0000000..6051cc9
--- /dev/null
+++ b/sdkcompat/v145/com/google/idea/sdkcompat/codestyle/CodeStyleManagerSdkCompatAdapter.java
@@ -0,0 +1,6 @@
+package com.google.idea.sdkcompat.codestyle;
+
+import com.intellij.psi.codeStyle.CodeStyleManager;
+
+/** Adapter to extend two bridge different IntelliJ SDK versions. */
+public abstract class CodeStyleManagerSdkCompatAdapter extends CodeStyleManager {}
diff --git a/sdkcompat/v145/com/google/idea/sdkcompat/debugger/GenericDebuggerRunnerSdkCompatAdapter.java b/sdkcompat/v145/com/google/idea/sdkcompat/debugger/GenericDebuggerRunnerSdkCompatAdapter.java
new file mode 100644
index 0000000..6dfc995
--- /dev/null
+++ b/sdkcompat/v145/com/google/idea/sdkcompat/debugger/GenericDebuggerRunnerSdkCompatAdapter.java
@@ -0,0 +1,25 @@
+package com.google.idea.sdkcompat.debugger;
+
+import com.intellij.debugger.impl.GenericDebuggerRunner;
+import com.intellij.execution.ExecutionException;
+import com.intellij.execution.configurations.RemoteConnection;
+import com.intellij.execution.configurations.RunProfileState;
+import com.intellij.execution.runners.ExecutionEnvironment;
+import com.intellij.execution.ui.RunContentDescriptor;
+import javax.annotation.Nullable;
+
+/** SDK compatibility for {@link GenericDebuggerRunner}. */
+public class GenericDebuggerRunnerSdkCompatAdapter extends GenericDebuggerRunner {
+
+ @Nullable
+ protected RunContentDescriptor attachVirtualMachine(
+ RunProfileState state,
+ ExecutionEnvironment env,
+ RemoteConnection connection,
+ long pollTimeout)
+ throws ExecutionException {
+ // no timeout available until 2016.2 onwards
+ return super.attachVirtualMachine(
+ state, env, connection, pollTimeout != 0 /* pollConnection */);
+ }
+}
diff --git a/sdkcompat/v145/com/google/idea/sdkcompat/smrunner/SmRunnerCompatUtils.java b/sdkcompat/v145/com/google/idea/sdkcompat/smrunner/SmRunnerCompatUtils.java
new file mode 100644
index 0000000..c4bd9a7
--- /dev/null
+++ b/sdkcompat/v145/com/google/idea/sdkcompat/smrunner/SmRunnerCompatUtils.java
@@ -0,0 +1,13 @@
+package com.google.idea.sdkcompat.smrunner;
+
+import com.intellij.execution.testframework.sm.runner.events.TestFailedEvent;
+import javax.annotation.Nullable;
+
+/** Handles SM-runner methods which have changed between our supported versions. */
+public class SmRunnerCompatUtils {
+
+ public static TestFailedEvent getTestFailedEvent(
+ String name, @Nullable String message, @Nullable String content, long duration) {
+ return new TestFailedEvent(name, -1, message, content, true, null, null, null, duration);
+ }
+}
diff --git a/sdkcompat/v145/com/google/idea/sdkcompat/transactions/Transactions.java b/sdkcompat/v145/com/google/idea/sdkcompat/transactions/Transactions.java
new file mode 100644
index 0000000..94ab905
--- /dev/null
+++ b/sdkcompat/v145/com/google/idea/sdkcompat/transactions/Transactions.java
@@ -0,0 +1,16 @@
+package com.google.idea.sdkcompat.transactions;
+
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.ModalityState;
+
+/** Created by tomlu on 12/22/16. */
+public class Transactions {
+ public static void submitTransactionAndWait(Runnable runnable) {
+ ApplicationManager.getApplication().invokeAndWait(runnable, ModalityState.any());
+ }
+
+ public static void submitTransaction(Disposable disposable, Runnable runnable) {
+ ApplicationManager.getApplication().invokeLater(runnable);
+ }
+}
diff --git a/sdkcompat/v145/com/google/idea/sdkcompat/vcs/ChangeListManagerSdkCompatAdapter.java b/sdkcompat/v145/com/google/idea/sdkcompat/vcs/ChangeListManagerSdkCompatAdapter.java
new file mode 100644
index 0000000..df055ea
--- /dev/null
+++ b/sdkcompat/v145/com/google/idea/sdkcompat/vcs/ChangeListManagerSdkCompatAdapter.java
@@ -0,0 +1,12 @@
+package com.google.idea.sdkcompat.vcs;
+
+import com.intellij.openapi.vcs.changes.ChangeListManager;
+import com.intellij.util.continuation.ContinuationPause;
+
+/** SDK adapter for change list interface. */
+public abstract class ChangeListManagerSdkCompatAdapter extends ChangeListManager {
+ @Override
+ public void freeze(ContinuationPause context, String reason) {
+ throw new UnsupportedOperationException("ChangeListManager#freeze()");
+ }
+}
diff --git a/sdkcompat/v162/com/google/idea/sdkcompat/codestyle/CodeStyleManagerSdkCompatAdapter.java b/sdkcompat/v162/com/google/idea/sdkcompat/codestyle/CodeStyleManagerSdkCompatAdapter.java
new file mode 100644
index 0000000..6051cc9
--- /dev/null
+++ b/sdkcompat/v162/com/google/idea/sdkcompat/codestyle/CodeStyleManagerSdkCompatAdapter.java
@@ -0,0 +1,6 @@
+package com.google.idea.sdkcompat.codestyle;
+
+import com.intellij.psi.codeStyle.CodeStyleManager;
+
+/** Adapter to extend two bridge different IntelliJ SDK versions. */
+public abstract class CodeStyleManagerSdkCompatAdapter extends CodeStyleManager {}
diff --git a/sdkcompat/v162/com/google/idea/sdkcompat/debugger/GenericDebuggerRunnerSdkCompatAdapter.java b/sdkcompat/v162/com/google/idea/sdkcompat/debugger/GenericDebuggerRunnerSdkCompatAdapter.java
new file mode 100644
index 0000000..ed822b3
--- /dev/null
+++ b/sdkcompat/v162/com/google/idea/sdkcompat/debugger/GenericDebuggerRunnerSdkCompatAdapter.java
@@ -0,0 +1,6 @@
+package com.google.idea.sdkcompat.debugger;
+
+import com.intellij.debugger.impl.GenericDebuggerRunner;
+
+/** SDK compatibility for {@link GenericDebuggerRunner}. */
+public class GenericDebuggerRunnerSdkCompatAdapter extends GenericDebuggerRunner {}
diff --git a/sdkcompat/v162/com/google/idea/sdkcompat/smrunner/SmRunnerCompatUtils.java b/sdkcompat/v162/com/google/idea/sdkcompat/smrunner/SmRunnerCompatUtils.java
new file mode 100644
index 0000000..1e9b474
--- /dev/null
+++ b/sdkcompat/v162/com/google/idea/sdkcompat/smrunner/SmRunnerCompatUtils.java
@@ -0,0 +1,13 @@
+package com.google.idea.sdkcompat.smrunner;
+
+import com.intellij.execution.testframework.sm.runner.events.TestFailedEvent;
+import javax.annotation.Nullable;
+
+/** Handles SM-runner methods which have changed between our supported versions. */
+public class SmRunnerCompatUtils {
+
+ public static TestFailedEvent getTestFailedEvent(
+ String name, @Nullable String message, @Nullable String content, long duration) {
+ return new TestFailedEvent(name, null, message, content, true, null, null, null, duration);
+ }
+}
diff --git a/sdkcompat/v162/com/google/idea/sdkcompat/transactions/Transactions.java b/sdkcompat/v162/com/google/idea/sdkcompat/transactions/Transactions.java
new file mode 100644
index 0000000..94ab905
--- /dev/null
+++ b/sdkcompat/v162/com/google/idea/sdkcompat/transactions/Transactions.java
@@ -0,0 +1,16 @@
+package com.google.idea.sdkcompat.transactions;
+
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.ModalityState;
+
+/** Created by tomlu on 12/22/16. */
+public class Transactions {
+ public static void submitTransactionAndWait(Runnable runnable) {
+ ApplicationManager.getApplication().invokeAndWait(runnable, ModalityState.any());
+ }
+
+ public static void submitTransaction(Disposable disposable, Runnable runnable) {
+ ApplicationManager.getApplication().invokeLater(runnable);
+ }
+}
diff --git a/sdkcompat/v162/com/google/idea/sdkcompat/vcs/ChangeListManagerSdkCompatAdapter.java b/sdkcompat/v162/com/google/idea/sdkcompat/vcs/ChangeListManagerSdkCompatAdapter.java
new file mode 100644
index 0000000..df055ea
--- /dev/null
+++ b/sdkcompat/v162/com/google/idea/sdkcompat/vcs/ChangeListManagerSdkCompatAdapter.java
@@ -0,0 +1,12 @@
+package com.google.idea.sdkcompat.vcs;
+
+import com.intellij.openapi.vcs.changes.ChangeListManager;
+import com.intellij.util.continuation.ContinuationPause;
+
+/** SDK adapter for change list interface. */
+public abstract class ChangeListManagerSdkCompatAdapter extends ChangeListManager {
+ @Override
+ public void freeze(ContinuationPause context, String reason) {
+ throw new UnsupportedOperationException("ChangeListManager#freeze()");
+ }
+}
diff --git a/sdkcompat/v163/com/google/idea/sdkcompat/codestyle/CodeStyleManagerSdkCompatAdapter.java b/sdkcompat/v163/com/google/idea/sdkcompat/codestyle/CodeStyleManagerSdkCompatAdapter.java
new file mode 100644
index 0000000..33d3ad4
--- /dev/null
+++ b/sdkcompat/v163/com/google/idea/sdkcompat/codestyle/CodeStyleManagerSdkCompatAdapter.java
@@ -0,0 +1,22 @@
+package com.google.idea.sdkcompat.codestyle;
+
+import com.intellij.openapi.util.TextRange;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.codeStyle.ChangedRangesInfo;
+import com.intellij.psi.codeStyle.CodeStyleManager;
+import com.intellij.util.IncorrectOperationException;
+import java.util.ArrayList;
+import java.util.List;
+import org.jetbrains.annotations.NotNull;
+
+/** Adapter to extend two bridge different IntelliJ SDK versions. */
+public abstract class CodeStyleManagerSdkCompatAdapter extends CodeStyleManager {
+ @Override
+ public void reformatTextWithContext(@NotNull PsiFile file, @NotNull ChangedRangesInfo info)
+ throws IncorrectOperationException {
+ List<TextRange> ranges = new ArrayList<>();
+ ranges.addAll(info.insertedRanges);
+ ranges.addAll(info.allChangedRanges);
+ this.reformatTextWithContext(file, ranges);
+ }
+}
diff --git a/sdkcompat/v163/com/google/idea/sdkcompat/debugger/GenericDebuggerRunnerSdkCompatAdapter.java b/sdkcompat/v163/com/google/idea/sdkcompat/debugger/GenericDebuggerRunnerSdkCompatAdapter.java
new file mode 100644
index 0000000..ed822b3
--- /dev/null
+++ b/sdkcompat/v163/com/google/idea/sdkcompat/debugger/GenericDebuggerRunnerSdkCompatAdapter.java
@@ -0,0 +1,6 @@
+package com.google.idea.sdkcompat.debugger;
+
+import com.intellij.debugger.impl.GenericDebuggerRunner;
+
+/** SDK compatibility for {@link GenericDebuggerRunner}. */
+public class GenericDebuggerRunnerSdkCompatAdapter extends GenericDebuggerRunner {}
diff --git a/sdkcompat/v163/com/google/idea/sdkcompat/smrunner/SmRunnerCompatUtils.java b/sdkcompat/v163/com/google/idea/sdkcompat/smrunner/SmRunnerCompatUtils.java
new file mode 100644
index 0000000..1e9b474
--- /dev/null
+++ b/sdkcompat/v163/com/google/idea/sdkcompat/smrunner/SmRunnerCompatUtils.java
@@ -0,0 +1,13 @@
+package com.google.idea.sdkcompat.smrunner;
+
+import com.intellij.execution.testframework.sm.runner.events.TestFailedEvent;
+import javax.annotation.Nullable;
+
+/** Handles SM-runner methods which have changed between our supported versions. */
+public class SmRunnerCompatUtils {
+
+ public static TestFailedEvent getTestFailedEvent(
+ String name, @Nullable String message, @Nullable String content, long duration) {
+ return new TestFailedEvent(name, null, message, content, true, null, null, null, duration);
+ }
+}
diff --git a/sdkcompat/v163/com/google/idea/sdkcompat/transactions/Transactions.java b/sdkcompat/v163/com/google/idea/sdkcompat/transactions/Transactions.java
new file mode 100644
index 0000000..8862aa8
--- /dev/null
+++ b/sdkcompat/v163/com/google/idea/sdkcompat/transactions/Transactions.java
@@ -0,0 +1,15 @@
+package com.google.idea.sdkcompat.transactions;
+
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.application.TransactionGuard;
+
+/** SDK adapter to use transaction guards. */
+public class Transactions {
+ public static void submitTransactionAndWait(Runnable runnable) {
+ TransactionGuard.getInstance().submitTransactionAndWait(runnable);
+ }
+
+ public static void submitTransaction(Disposable disposable, Runnable runnable) {
+ TransactionGuard.submitTransaction(disposable, runnable);
+ }
+}
diff --git a/sdkcompat/v163/com/google/idea/sdkcompat/vcs/ChangeListManagerSdkCompatAdapter.java b/sdkcompat/v163/com/google/idea/sdkcompat/vcs/ChangeListManagerSdkCompatAdapter.java
new file mode 100644
index 0000000..1e525c1
--- /dev/null
+++ b/sdkcompat/v163/com/google/idea/sdkcompat/vcs/ChangeListManagerSdkCompatAdapter.java
@@ -0,0 +1,6 @@
+package com.google.idea.sdkcompat.vcs;
+
+import com.intellij.openapi.vcs.changes.ChangeListManager;
+
+/** SDK adapter to for changelist interface. */
+public abstract class ChangeListManagerSdkCompatAdapter extends ChangeListManager {}
diff --git a/version.bzl b/version.bzl
index 0f144e5..1bcca22 100644
--- a/version.bzl
+++ b/version.bzl
@@ -1,3 +1,3 @@
"""Version of the blaze plugin."""
-VERSION = "2016.12.05.6"
+VERSION = "2017.01.09.1"