Merge pull request #129 from brendandouglas/master
Import of bazel plugin using copybara
diff --git a/BUILD b/BUILD
index 5e26650..1d898ce 100644
--- a/BUILD
+++ b/BUILD
@@ -30,6 +30,14 @@
],
)
+# UE-specific IJwB tests
+test_suite(
+ name = "ijwb_ue_tests",
+ tests = [
+ "//golang:integration_tests",
+ ],
+)
+
# ASwB tests, run with an Android Studio plugin SDK
test_suite(
name = "aswb_tests",
@@ -38,6 +46,8 @@
"//aswb:unit_tests",
"//base:integration_tests",
"//base:unit_tests",
+ "//cpp:integration_tests",
+ "//cpp:unit_tests",
"//java:integration_tests",
"//java:unit_tests",
],
diff --git a/CHANGELOG b/CHANGELOG
index 32034b6..15ceaa6 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,53 @@
+v2017.08.28
+===========
+* CLion test output: make URLs and bazel targets clickable
+* Retrieve Javadocs for unattached sources
+* CLion: incremental sync retains more caches. Prefill caches on project reload.
+
+v2017.08.14
+===========
+* Fix spurious 'unused' warnings for AutoFactory-annotated classes
+* Python: Test UI support for paramaterized python tests
+* Python: Linkify stack traces in Bazel Console view
+* Test UI: fix timeouts not being marked as failures
+* Go: migrate Go code to the latest JetBrains plugin
+
+v2017.08.01
+===========
+* Add a user setting to suppress the Bazel console during sync
+* Add support for IntelliJ 2017.1.5
+* ASwB: fix generated resources not resolving
+* Fix unresolved references when targets are built with multiple Bazel configurations
+* Python: fix 'argument list too long' errors when debugging
+
+v2017.07.17
+===========
+* Explicitly deprioritize older android/gwt-specific versions of libraries during sync
+* Improve test finder heuristics when creating run configurations
+
+v2017.07.05
+===========
+* CLwB: Show "unsynced" diagnostic file status for C++ files
+
+v2017.06.19
+===========
+* Improve performance when indexing proto_library targets in the working set.
+* Fix incorrectly reusing existing, but different, run configurations.
+* Order BUILD file structure view by target name, not rule type.
+
+v2017.06.05
+===========
+* Add Scala support to IntelliJ.
+* Add 'sync_flags' .blazeproject section, for flags only applied during sync.
+* Android Studio: NDK plugins are now optional.
+* CLion: Improve performance by prefetching required genfiles during sync.
+
+v2017.05.22
+===========
+* TypeScript: Support multiple ts_config rules in .bazelproject
+* Android Studio: Index javac jar for javax.lang classes
+* Show failed test targets in test result UI
+
v2017.05.08
===========
* Add Python support to CLion
diff --git a/README.md b/README.md
index 439b85e..1489497 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# An IntelliJ plugin for [Bazel](http://bazel.build) projects
-This is an early-access version of our Blaze plugins for IntelliJ,
+This is an early-access version of our Bazel plugins for IntelliJ,
Android Studio, and CLion.
This code drop is for educational purposes only and is currently
@@ -13,8 +13,7 @@
## Installation
You can find our plugin in the Jetbrains plugin repository by going to
-`Settings -> Browse Repositories`, and searching for `IntelliJ with Bazel`,
-`Android Studio with Bazel`, or `CLion with Bazel`.
+`Settings -> Browse Repositories`, and searching for `Bazel`.
## Usage
diff --git a/WORKSPACE b/WORKSPACE
index 9a4eaff..7d1168f 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -2,44 +2,40 @@
# Long-lived download links available at: https://www.jetbrains.com/intellij-repository/releases
-# The plugin api for IntelliJ 2017.1.1. This is required to build IJwB,
+# The plugin api for IntelliJ 2017.2. This is required to build IJwB,
# and run integration tests.
new_http_archive(
- name = "intellij_ce_2017_1_1",
+ name = "intellij_ce_2017_2_2",
build_file = "intellij_platform_sdk/BUILD.idea",
- url = "https://www.jetbrains.com/intellij-repository/releases/com/jetbrains/intellij/idea/ideaIC/2017.1.1/ideaIC-2017.1.1.zip",
+ url = "https://www.jetbrains.com/intellij-repository/releases/com/jetbrains/intellij/idea/ideaIC/2017.2.2/ideaIC-2017.2.2.zip",
+ sha256 = "4e14343ecaab00a3a02f2e51995a46cdbea90e20d336f806a1ec40892bbd7e53",
)
-# The plugin api for IntelliJ 2016.3.1. This is required to build IJwB,
+# The plugin api for IntelliJ 2017.1. This is required to build IJwB,
# and run integration tests.
new_http_archive(
- name = "intellij_ce_2016_3_1",
+ name = "intellij_ce_2017_1_5",
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",
+ url = "https://www.jetbrains.com/intellij-repository/releases/com/jetbrains/intellij/idea/ideaIC/2017.1.5/ideaIC-2017.1.5.zip",
+ sha256 = "d11beb116f500ecbf75b0a1098dfaad696bc8a15edceae163f53bc511ab79445"
)
-# The plugin api for IntelliJ 2016.2.4. This is required to build IJwB,
-# and run integration tests.
+# The plugin api for IntelliJ UE 2017.2. This is required to run UE-specific
+# integration tests.
new_http_archive(
- name = "IC_162_2032_8",
+ name = "intellij_ue_2017_2_2",
build_file = "intellij_platform_sdk/BUILD.idea",
- url = "https://www.jetbrains.com/intellij-repository/releases/com/jetbrains/intellij/idea/ideaIC/2016.2.4/ideaIC-2016.2.4.zip",
+ url = "https://www.jetbrains.com/intellij-repository/releases/com/jetbrains/intellij/idea/ideaIU/2017.2.2/ideaIU-2017.2.2.zip",
+ sha256 = "b5a07d41b255799b2cdf2aa8b1c154aa35bf88deac4ae3718b2bae257b68d47d",
)
-# The plugin api for CLion 2016.2.2. This is required to build CLwB,
-# and run integration tests.
+# The plugin api for IntelliJ UE 2017.1. This is required to run UE-specific
+# integration tests.
new_http_archive(
- name = "CL_162_1967_7",
- build_file = "intellij_platform_sdk/BUILD.clion",
- 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",
+ name = "intellij_ue_2017_1_5",
+ build_file = "intellij_platform_sdk/BUILD.idea",
+ url = "https://www.jetbrains.com/intellij-repository/releases/com/jetbrains/intellij/idea/ideaIU/2017.1.5/ideaIU-2017.1.5.zip",
+ sha256 = "6d887f79ca7853923060499cac7778b30fc82414f112deb8245d59179892bbad"
)
# The plugin api for CLion 2017.1.1. This is required to build CLwB,
@@ -48,6 +44,16 @@
name = "clion_2017_1_1",
build_file = "intellij_platform_sdk/BUILD.clion",
url = "https://download.jetbrains.com/cpp/CLion-2017.1.1.tar.gz",
+ sha256 = "9abd6bd38801ae6cf29db2cd133c700e8da11841093de872312fe33ed51309ae",
+)
+
+# The plugin api for CLion 2017.2.0. This is required to build CLwB,
+# and run integration tests.
+new_http_archive(
+ name = "clion_2017_2_1",
+ build_file = "intellij_platform_sdk/BUILD.clion",
+ url = "https://download.jetbrains.com/cpp/CLion-2017.2.1.tar.gz",
+ sha256 = "acd3d09a37a3fa922a85a48635d1b230d559ea68917e2e7895caf16460d50c13",
)
# The plugin api for Android Studio 2.3.1. This is required to build ASwB,
@@ -56,18 +62,16 @@
name = "android_studio_2_3_1_0",
build_file = "intellij_platform_sdk/BUILD.android_studio",
url = "https://dl.google.com/dl/android/studio/ide-zips/2.3.1.0/android-studio-ide-162.3871768-linux.zip",
+ sha256 = "36520f21678f80298b5df5fe5956db17a5984576f895fdcaa36ab0dbfb408433",
)
-# Python plugin for IntelliJ CE 2016.3. Required at compile-time for python-specific features.
+# The plugin api for Android Studio 3.0 Beta 1. This is required to build ASwB,
+# and run integration tests.
new_http_archive(
- name = "python_2016_3",
- build_file_content = "\n".join([
- "java_import(",
- " name = 'python',",
- " jars = ['python/lib/python.jar'],",
- " visibility = ['//visibility:public'],",
- ")"]),
- url = "https://plugins.jetbrains.com/files/7322/32326/python-community-163.298.zip",
+ name = "android_studio_3_0_0_9",
+ build_file = "intellij_platform_sdk/BUILD.android_studio",
+ url = "https://dl.google.com/dl/android/studio/ide-zips/3.0.0.9/android-studio-ide-171.4243858-linux.zip",
+ sha256 = "422cc6c85ee62186bdb81facd970c0058575dc45ba39f6fd8b326b430269f28c",
)
# Python plugin for IntelliJ CE 2017.1. Required at compile-time for python-specific features.
@@ -79,7 +83,66 @@
" jars = ['python-ce/lib/python-ce.jar'],",
" visibility = ['//visibility:public'],",
")"]),
- url = "https://plugins.jetbrains.com/files/7322/33704/python-ce-2017.1.171.3780.116.zip",
+ url = "https://download.plugins.jetbrains.com/7322/35756/python-ce-2017.1.171.4694.26.zip",
+ sha256 = "640d116ff01bcc2f87c75d20da642f17fcd86ed69929e4e0247ffc0df8aa780f",
+)
+
+# Python plugin for IntelliJ CE 2017.2. Required at compile-time for python-specific features.
+new_http_archive(
+ name = "python_2017_2",
+ build_file_content = "\n".join([
+ "java_import(",
+ " name = 'python',",
+ " jars = ['python-ce/lib/python-ce.jar'],",
+ " visibility = ['//visibility:public'],",
+ ")"]),
+ url = "https://download.plugins.jetbrains.com/7322/37356/python-ce-2017.2.172.3544.31.zip",
+ sha256 = "c7ee48c0bafb29f4a18eaac804b113c4dcdfeaaae174d9003c9ad96e44df6fe0",
+)
+
+# Go plugin for IntelliJ UE and CLion 2017.2.1. Required at compile-time for Bazel integration.
+new_http_archive(
+ name = "go_2017_2",
+ build_file_content = "\n".join([
+ "java_import(",
+ " name = 'go',",
+ " jars = glob(['intellij-go/lib/*.jar']),",
+ " visibility = ['//visibility:public'],",
+ ")"]),
+ url = "https://download.plugins.jetbrains.com/9568/37740/intellij-go-172.3757.46.zip",
+ sha256 = "3e5eb5415a05e6c30e79c263135c2937cc05e310e553889bd69eefa819705f9c",
+)
+
+# Go plugin for IntelliJ UE and CLion 2017.1.5. Required at compile-time for Bazel integration.
+new_http_archive(
+ name = "go_2017_1",
+ build_file_content = "\n".join([
+ "java_import(",
+ " name = 'go',",
+ " jars = glob(['intellij-go/lib/*.jar']),",
+ " visibility = ['//visibility:public'],",
+ ")"]),
+ url = "https://download.plugins.jetbrains.com/9568/36389/intellij-go-171.4694.61.zip",
+ sha256 = "16bf70045360c1c2a056c9ae540626dffa3680b8283cb89febaff3a9499b7101",
+)
+
+# Scala plugin for IntelliJ CE 2017.2 EAP. Required at compile-time for scala-specific features.
+new_http_archive(
+ name = "scala_2017_2",
+ build_file_content = "\n".join([
+ "java_import(",
+ " name = 'scala',",
+ " jars = [",
+ " 'Scala/lib/scala-plugin.jar',",
+ " 'Scala/lib/compiler-settings.jar',",
+ " 'Scala/lib/scala-library.jar',",
+ " 'Scala/lib/scalameta120.jar',",
+ " 'Scala/lib/scalatest-finders-patched.jar',",
+ " ],",
+ " visibility = ['//visibility:public'],",
+ ")"]),
+ url = "https://download.plugins.jetbrains.com/1347/35283/scala-intellij-bin-2017.2.2.zip",
+ sha256 = "1f0eef98da44dbc3f4f22b399a9175897aca448fd80405eca77fd61bd5fb7219",
)
# Scala plugin for IntelliJ CE 2017.1. Required at compile-time for scala-specific features.
@@ -87,17 +150,18 @@
name = "scala_2017_1",
build_file_content = "\n".join([
"java_import(",
- " name = 'scala-library',",
- " jars = ['Scala/lib/scala-library.jar'],",
- ")",
- "",
- "java_import(",
" name = 'scala',",
- " jars = ['Scala/lib/scala-plugin.jar'],",
- " runtime_deps = [':scala-library'],",
+ " jars = [",
+ " 'Scala/lib/scala-plugin.jar',",
+ " 'Scala/lib/compiler-settings.jar',",
+ " 'Scala/lib/scala-library.jar',",
+ " 'Scala/lib/scalameta120.jar',",
+ " 'Scala/lib/scalatest-finders-patched.jar',",
+ " ],",
" visibility = ['//visibility:public'],",
")"]),
url = "https://plugins.jetbrains.com/files/1347/33637/scala-intellij-bin-2017.1.15.zip",
+ sha256 = "b58670a3b52584effc6dd3d014e77fe80e2795b5e5e58716548ecc1452eca6cf",
)
# LICENSE: Common Public License 1.0
@@ -141,3 +205,17 @@
artifact = "com.googlecode.jarjar:jarjar:1.3",
sha1 = "b81c2719c63fa8e6f3eca5b11b8e9b5ad79463db",
)
+
+# LICENSE: The Apache Software License, Version 2.0
+maven_jar(
+ name = "auto_value",
+ artifact = "com.google.auto.value:auto-value:1.3",
+ sha1 = "4961194f62915eb45e21940537d60ac53912c57d",
+)
+
+# LICENSE: The Apache Software License, Version 2.0
+maven_jar(
+ name = "error_prone_annotations",
+ artifact = "com.google.errorprone:error_prone_annotations:2.0.15",
+ sha1 = "822652ed7196d119b35d2e22eb9cd4ffda11e640",
+)
diff --git a/aspect/BUILD b/aspect/BUILD
new file mode 100644
index 0000000..7f19914
--- /dev/null
+++ b/aspect/BUILD
@@ -0,0 +1,29 @@
+#
+# Description: Bazel aspect bundled with the Bazel IntelliJ plugin.
+#
+
+licenses(["notice"]) # Apache 2.0
+
+# the aspect files that will be bundled with the final plugin zip
+filegroup(
+ name = "aspect_files",
+ srcs = [
+ "WORKSPACE",
+ "intellij_info.bzl",
+ "intellij_info_impl.bzl",
+ ":BUILD.bazel",
+ "//aspect/tools:JarFilter_deploy.jar",
+ "//aspect/tools:PackageParser_deploy.jar",
+ ],
+ visibility = ["//visibility:public"],
+)
+
+# BUILD file bundled with the aspect must not override the BUILD file
+# used for development. So we name it BUILD.aspect, and rename prior
+# to bundling with the plugin.
+genrule(
+ name = "rename_files",
+ srcs = ["BUILD.aspect"],
+ outs = ["BUILD.bazel"],
+ cmd = "cp $< $@",
+)
diff --git a/aspect/BUILD.aspect b/aspect/BUILD.aspect
new file mode 100644
index 0000000..c109e12
--- /dev/null
+++ b/aspect/BUILD.aspect
@@ -0,0 +1,30 @@
+#
+# Description:
+# The final form of the BUILD file accessed at runtime as an external WORKSPACE.
+#
+
+licenses(["notice"]) # Apache 2.0
+
+java_binary(
+ name = "JarFilter_bin",
+ main_class = "com.google.idea.blaze.aspect.JarFilter",
+ runtime_deps = [":jar_filter_lib"],
+ visibility = ["//visibility:public"],
+)
+
+java_import(
+ name = "jar_filter_lib",
+ jars = ["JarFilter_deploy.jar"],
+)
+
+java_binary(
+ name = "PackageParser_bin",
+ main_class = "com.google.idea.blaze.aspect.PackageParser",
+ runtime_deps = [":package_parser_lib"],
+ visibility = ["//visibility:public"],
+)
+
+java_import(
+ name = "package_parser_lib",
+ jars = ["PackageParser_deploy.jar"],
+)
\ No newline at end of file
diff --git a/aspect/WORKSPACE b/aspect/WORKSPACE
new file mode 100644
index 0000000..e11b403
--- /dev/null
+++ b/aspect/WORKSPACE
@@ -0,0 +1 @@
+workspace(name = "intellij_aspect")
\ No newline at end of file
diff --git a/aspect/intellij_info.bzl b/aspect/intellij_info.bzl
new file mode 100644
index 0000000..3224efe
--- /dev/null
+++ b/aspect/intellij_info.bzl
@@ -0,0 +1,20 @@
+"""Bazel-specific intellij aspect."""
+
+load(
+ "//:intellij_info_impl.bzl",
+ "make_intellij_info_aspect",
+ "intellij_info_aspect_impl",
+)
+
+def tool_label(tool_name):
+ """Returns a label that points to a tool target in the bundled aspect workspace."""
+ return Label("//:" + tool_name + "_bin")
+
+semantics = struct(
+ tool_label = tool_label,
+)
+
+def _aspect_impl(target, ctx):
+ return intellij_info_aspect_impl(target, ctx, semantics)
+
+intellij_info_aspect = make_intellij_info_aspect(_aspect_impl, semantics)
diff --git a/aspect/intellij_info_impl.bzl b/aspect/intellij_info_impl.bzl
new file mode 100644
index 0000000..c5968e9
--- /dev/null
+++ b/aspect/intellij_info_impl.bzl
@@ -0,0 +1,753 @@
+"""Implementation of IntelliJ-specific information collecting aspect."""
+
+# Compile-time dependency attributes, grouped by type.
+DEPS = [
+ "_cc_toolchain", # From cc rules
+ "_stl", # From cc rules
+ "malloc", # From cc_binary rules
+ "_java_toolchain", # From java rules
+ "deps",
+ "exports",
+ "_robolectric", # From android_robolectric_test
+ "_android_sdk", # from android rules
+ "aidl_lib", # from android_sdk
+ "_scala_toolchain", # From scala rules
+]
+
+# Run-time dependency attributes, grouped by type.
+RUNTIME_DEPS = [
+ "runtime_deps",
+]
+
+PREREQUISITE_DEPS = []
+
+# Dependency type enum
+COMPILE_TIME = 0
+RUNTIME = 1
+
+##### Helpers
+
+def struct_omit_none(**kwargs):
+ """A replacement for standard `struct` function that omits the fields with None value."""
+ d = {name: kwargs[name] for name in kwargs if kwargs[name] != None}
+ return struct(**d)
+
+def artifact_location(f):
+ """Creates an ArtifactLocation proto from a File."""
+ if f == None:
+ return None
+
+ return to_artifact_location(
+ f.path,
+ f.root.path if not f.is_source else "",
+ f.is_source,
+ is_external_artifact(f.owner),
+ )
+
+def to_artifact_location(exec_path, root_exec_path_fragment, is_source, is_external):
+ """Derives workspace path from other path fragments, and creates an ArtifactLocation proto."""
+ # Bazel 0.4.4 has directory structure:
+ # exec_path = (root_fragment)? + (external/repo_name)? + relative_path
+ # Bazel 0.4.5 has planned directory structure:
+ # exec_path = (../repo_name)? + (root_fragment)? + relative_path
+ # Handle both cases by trying to strip the external workspace prefix before and after removing
+ # root_exec_path_fragment.
+ relative_path = strip_external_workspace_prefix(exec_path)
+ relative_path = strip_root_exec_path_fragment(relative_path, root_exec_path_fragment)
+ # Remove this line when Bazel 0.4.4 and earlier no longer need to be supported.
+ relative_path = strip_external_workspace_prefix(relative_path)
+
+ root_exec_path_fragment = exec_path[:-(len("/" + relative_path))]
+
+ return struct_omit_none(
+ relative_path = relative_path,
+ is_source = is_source,
+ is_external = is_external,
+ root_execution_path_fragment = root_exec_path_fragment,
+ is_new_external_version = True,
+ )
+
+def strip_root_exec_path_fragment(path, root_fragment):
+ if root_fragment and path.startswith(root_fragment + "/"):
+ return path[len(root_fragment + "/"):]
+ return path
+
+def strip_external_workspace_prefix(path):
+ """Either 'external/workspace_name/' or '../workspace_name/'."""
+ # Label.EXTERNAL_PATH_PREFIX is due to change from 'external' to '..' in Bazel 0.4.5.
+ # This code is for forwards and backwards compatibility.
+ # Remove the 'external/' check when Bazel 0.4.4 and earlier no longer need to be supported.
+ if path.startswith("../") or path.startswith("external/"):
+ return "/".join(path.split("/")[2:])
+ return path
+
+def is_external_artifact(label):
+ """Determines whether a label corresponds to an external artifact."""
+ # Label.EXTERNAL_PATH_PREFIX is due to change from 'external' to '..' in Bazel 0.4.5.
+ # This code is for forwards and backwards compatibility.
+ # Remove the 'external' check when Bazel 0.4.4 and earlier no longer need to be supported.
+ return label.workspace_root.startswith("external") or label.workspace_root.startswith("..")
+
+def source_directory_tuple(resource_file):
+ """Creates a tuple of (exec_path, root_exec_path_fragment, is_source, is_external)."""
+ relative_path = str(android_common.resource_source_directory(resource_file))
+ root_exec_path_fragment = resource_file.root.path if not resource_file.is_source else None
+ return (
+ relative_path if resource_file.is_source else root_exec_path_fragment + "/" + relative_path,
+ root_exec_path_fragment,
+ resource_file.is_source,
+ is_external_artifact(resource_file.owner)
+ )
+
+def all_unique_source_directories(resources):
+ """Builds a list of unique ArtifactLocation protos."""
+ # Sets can contain tuples, but cannot contain structs.
+ # Use set of tuples to unquify source directories.
+ source_directory_tuples = depset([source_directory_tuple(f) for f in resources])
+ return [to_artifact_location(
+ exec_path,
+ root_path_fragment,
+ is_source,
+ is_external)
+ for (exec_path, root_path_fragment, is_source, is_external) in source_directory_tuples]
+
+def build_file_artifact_location(ctx):
+ """Creates an ArtifactLocation proto representing a location of a given BUILD file."""
+ return to_artifact_location(
+ ctx.build_file_path,
+ ctx.build_file_path,
+ True,
+ is_external_artifact(ctx.label)
+ )
+
+def get_source_jar(output):
+ if hasattr(output, "source_jar"):
+ return output.source_jar
+ return None
+
+def library_artifact(java_output):
+ """Creates a LibraryArtifact representing a given java_output."""
+ if java_output == None or java_output.class_jar == None:
+ return None
+ return struct_omit_none(
+ jar = artifact_location(java_output.class_jar),
+ interface_jar = artifact_location(java_output.ijar),
+ source_jar = artifact_location(get_source_jar(java_output)),
+ )
+
+def annotation_processing_jars(annotation_processing):
+ """Creates a LibraryArtifact representing Java annotation processing jars."""
+ return struct_omit_none(
+ jar = artifact_location(annotation_processing.class_jar),
+ source_jar = artifact_location(annotation_processing.source_jar),
+ )
+
+def jars_from_output(output):
+ """Collect jars for intellij-resolve-files from Java output."""
+ if output == None:
+ return []
+ return [jar
+ for jar in [output.class_jar, output.ijar, get_source_jar(output)]
+ if jar != None and not jar.is_source]
+
+# TODO(salguarnieri) Remove once skylark provides the path safe string from a PathFragment.
+def replace_empty_path_with_dot(path):
+ return path or "."
+
+def sources_from_target(ctx):
+ """Get the list of sources from a target as artifact locations."""
+ return artifacts_from_target_list_attr(ctx, "srcs")
+
+def artifacts_from_target_list_attr(ctx, attr_name):
+ """Converts a list of targets to a list of artifact locations."""
+ return [artifact_location(f)
+ for target in getattr(ctx.rule.attr, attr_name, [])
+ for f in target.files]
+
+def _collect_target_from_attr(rule_attrs, attr_name, result):
+ """Collects the targets from the given attr into the result."""
+ if not hasattr(rule_attrs, attr_name):
+ return
+ attr_value = getattr(rule_attrs, attr_name)
+ type_name = type(attr_value)
+ if type_name == "Target":
+ result.append(attr_value)
+ elif type_name == "list":
+ result.extend(attr_value)
+
+def collect_targets_from_attrs(rule_attrs, attrs):
+ """Returns a list of targets from the given attributes."""
+ result = []
+ for attr_name in attrs:
+ _collect_target_from_attr(rule_attrs, attr_name, result)
+ return [target for target in result if is_valid_aspect_target(target)]
+
+def targets_to_labels(targets):
+ """Returns a set of label strings for the given targets."""
+ return depset([str(target.label) for target in targets])
+
+def list_omit_none(value):
+ """Returns a list of the value, or the empty list if None."""
+ return [value] if value else []
+
+def is_valid_aspect_target(target):
+ """Returns whether the target has had the aspect run on it."""
+ return hasattr(target, "intellij_info")
+
+def get_aspect_ids(ctx, target):
+ """Returns the all aspect ids, filtering out self."""
+ aspect_ids = None
+ if hasattr(ctx, "aspect_ids"):
+ aspect_ids = ctx.aspect_ids
+ elif hasattr(target, "aspect_ids"):
+ aspect_ids = target.aspect_ids
+ else:
+ return None
+ return [aspect_id for aspect_id in aspect_ids if "intellij_info_aspect" not in aspect_id]
+
+def make_target_key(label, aspect_ids):
+ """Returns a TargetKey proto struct from a target."""
+ return struct_omit_none(
+ label = str(label),
+ aspect_ids = tuple(aspect_ids) if aspect_ids else None
+ )
+
+def make_dep(dep, dependency_type):
+ """Returns a Dependency proto struct."""
+ return struct(
+ target = dep.intellij_info.target_key,
+ dependency_type = dependency_type,
+ )
+
+def make_deps(deps, dependency_type):
+ """Returns a list of Dependency proto structs."""
+ return [make_dep(dep, dependency_type) for dep in deps]
+
+def make_dep_from_label(label, dependency_type):
+ """Returns a Dependency proto struct from a label."""
+ return struct(
+ target = struct(label = str(label)),
+ dependency_type = dependency_type,
+ )
+
+def update_set_in_dict(input_dict, key, other_set):
+ """Updates depset in dict, merging it with another depset."""
+ input_dict[key] = input_dict.get(key, depset()) | other_set
+
+##### Builders for individual parts of the aspect output
+
+def collect_py_info(target, ctx, ide_info, ide_info_file, output_groups):
+ """Updates Python-specific output groups, returns false if not a Python target."""
+ if not hasattr(target, "py"):
+ return False
+
+ ide_info["py_ide_info"] = struct_omit_none(
+ sources = sources_from_target(ctx),
+ )
+ transitive_sources = target.py.transitive_sources
+ # TODO(brendandouglas): target to python files only
+ compile_files = target.output_group("files_to_compile_INTERNAL_")
+
+ update_set_in_dict(output_groups, "intellij-info-py", depset([ide_info_file]))
+ update_set_in_dict(output_groups, "intellij-compile-py", compile_files)
+ update_set_in_dict(output_groups, "intellij-resolve-py", transitive_sources)
+
+ # Add to legacy output groups for backwards compatibility
+ update_set_in_dict(output_groups, "intellij-compile", compile_files)
+ update_set_in_dict(output_groups, "intellij-resolve", transitive_sources)
+ return True
+
+def _collect_generated_proto_go_sources(target):
+ """Returns a depset of proto go source files generated by this target."""
+ if not hasattr(target, "proto_go_api_info"):
+ return None
+ files = getattr(target.proto_go_api_info, "files_to_build", [])
+ return depset([f for f in files if f.basename.endswith(".pb.go")])
+
+def collect_go_info(target, ide_info, ide_info_file, output_groups):
+ """Updates Go-specific output groups, returns false if not a recognized Go target."""
+ # currently there's no Go Skylark API, with the only exception being proto_library targets
+ if not hasattr(target, "proto_go_api_info"):
+ return False
+
+ generated_sources = _collect_generated_proto_go_sources(target)
+
+ ide_info["go_ide_info"] = struct_omit_none(
+ generated_sources = [artifact_location(f) for f in generated_sources],
+ )
+
+ update_set_in_dict(output_groups, "intellij-info-go", depset([ide_info_file]))
+ update_set_in_dict(output_groups, "intellij-compile-go", generated_sources)
+ update_set_in_dict(output_groups, "intellij-resolve-go", generated_sources)
+ return True
+
+def collect_cpp_info(target, ctx, ide_info, ide_info_file, output_groups):
+ """Updates C++-specific output groups, returns false if not a C++ target."""
+ if not hasattr(target, "cc"):
+ return False
+
+ sources = artifacts_from_target_list_attr(ctx, "srcs")
+ headers = artifacts_from_target_list_attr(ctx, "hdrs")
+ textual_headers = artifacts_from_target_list_attr(ctx, "textual_hdrs")
+
+ target_includes = []
+ if hasattr(ctx.rule.attr, "includes"):
+ target_includes = ctx.rule.attr.includes
+ target_defines = []
+ if hasattr(ctx.rule.attr, "defines"):
+ target_defines = ctx.rule.attr.defines
+ target_copts = []
+ if hasattr(ctx.rule.attr, "copts"):
+ target_copts = ctx.rule.attr.copts
+
+ cc_provider = target.cc
+
+ c_info = struct_omit_none(
+ source = sources,
+ header = headers,
+ textual_header = textual_headers,
+ target_include = target_includes,
+ target_define = target_defines,
+ target_copt = target_copts,
+ transitive_include_directory = cc_provider.include_directories,
+ transitive_quote_include_directory = cc_provider.quote_include_directories,
+ transitive_define = cc_provider.defines,
+ transitive_system_include_directory = cc_provider.system_include_directories,
+ )
+ ide_info["c_ide_info"] = c_info
+ resolve_files = cc_provider.transitive_headers
+ # TODO(brendandouglas): target to cpp files only
+ compile_files = target.output_group("files_to_compile_INTERNAL_")
+
+ update_set_in_dict(output_groups, "intellij-info-cpp", depset([ide_info_file]))
+ update_set_in_dict(output_groups, "intellij-compile-cpp", compile_files)
+ update_set_in_dict(output_groups, "intellij-resolve-cpp", resolve_files)
+
+ # Add to legacy output groups for backwards compatibility
+ update_set_in_dict(output_groups, "intellij-compile", compile_files)
+ update_set_in_dict(output_groups, "intellij-resolve", resolve_files)
+ return True
+
+def collect_c_toolchain_info(ctx, ide_info, ide_info_file, output_groups):
+ """Updates cc_toolchain-relevant output groups, returns false if not a cc_toolchain target."""
+ if ctx.rule.kind != "cc_toolchain":
+ return False
+
+ # This should exist because we requested it in our aspect definition.
+ cc_fragment = ctx.fragments.cpp
+
+ c_toolchain_info = struct_omit_none(
+ target_name = cc_fragment.target_gnu_system_name,
+ base_compiler_option = cc_fragment.compiler_options(ctx.features),
+ c_option = cc_fragment.c_options,
+ cpp_option = cc_fragment.cxx_options(ctx.features),
+ link_option = cc_fragment.link_options,
+ unfiltered_compiler_option = cc_fragment.unfiltered_compiler_options(ctx.features),
+ preprocessor_executable = replace_empty_path_with_dot(
+ str(cc_fragment.preprocessor_executable)),
+ cpp_executable = str(cc_fragment.compiler_executable),
+ built_in_include_directory = [str(d)
+ for d in cc_fragment.built_in_include_directories],
+ )
+ ide_info["c_toolchain_ide_info"] = c_toolchain_info
+ update_set_in_dict(output_groups, "intellij-info-cpp", depset([ide_info_file]))
+ return True
+
+def get_java_provider(target):
+ if hasattr(target, "proto_java"):
+ return target.proto_java
+ if hasattr(target, "java"):
+ return target.java
+ if hasattr(target, "scala"):
+ return target.scala
+ return None
+
+def get_java_jars(outputs):
+ """Handle both Java (java.outputs.jars list) and Scala (single scala.outputs) targets."""
+ if hasattr(outputs, "jars"):
+ return outputs.jars
+ if hasattr(outputs, "class_jar"):
+ return [outputs]
+ return []
+
+def collect_java_info(target, ctx, semantics, ide_info, ide_info_file, output_groups):
+ """Updates Java-specific output groups, returns false if not a Java target."""
+ java = get_java_provider(target)
+ if not java:
+ return False
+
+ java_semantics = semantics.java if hasattr(semantics, "java") else None
+ if java_semantics and java_semantics.skip_target(target, ctx):
+ return False
+
+ ide_info_files = depset()
+ sources = sources_from_target(ctx)
+ java_jars = get_java_jars(java.outputs)
+ jars = [library_artifact(output) for output in java_jars]
+ class_jars = [output.class_jar for output in java_jars if output and output.class_jar]
+ output_jars = [jar for output in java_jars for jar in jars_from_output(output)]
+ resolve_files = depset(output_jars)
+ compile_files = depset(class_jars)
+
+ gen_jars = []
+ if (hasattr(java, "annotation_processing") and
+ java.annotation_processing and
+ java.annotation_processing.enabled):
+ gen_jars = [annotation_processing_jars(java.annotation_processing)]
+ resolve_files = resolve_files | depset([
+ jar for jar in [java.annotation_processing.class_jar,
+ java.annotation_processing.source_jar]
+ if jar != None and not jar.is_source])
+ compile_files = compile_files | depset([
+ jar for jar in [java.annotation_processing.class_jar]
+ if jar != None and not jar.is_source])
+
+ jdeps = None
+ if hasattr(java.outputs, "jdeps"):
+ jdeps = artifact_location(java.outputs.jdeps)
+
+ java_sources, gen_java_sources, srcjars = divide_java_sources(ctx)
+
+ if java_semantics:
+ srcjars = java_semantics.filter_source_jars(target, ctx, srcjars)
+
+ package_manifest = None
+ if java_sources:
+ package_manifest = build_java_package_manifest(ctx, target, java_sources, ".java-manifest")
+ ide_info_files = ide_info_files | depset([package_manifest])
+
+ filtered_gen_jar = None
+ if java_sources and (gen_java_sources or srcjars):
+ filtered_gen_jar, filtered_gen_resolve_files = build_filtered_gen_jar(
+ ctx,
+ target,
+ java,
+ gen_java_sources,
+ srcjars
+ )
+ resolve_files = resolve_files | filtered_gen_resolve_files
+
+ java_info = struct_omit_none(
+ sources = sources,
+ jars = jars,
+ jdeps = jdeps,
+ generated_jars = gen_jars,
+ package_manifest = artifact_location(package_manifest),
+ filtered_gen_jar = filtered_gen_jar,
+ main_class = ctx.rule.attr.main_class if hasattr(ctx.rule.attr, "main_class") else None,
+ )
+
+ ide_info["java_ide_info"] = java_info
+ ide_info_files += depset([ide_info_file])
+ update_set_in_dict(output_groups, "intellij-info-java", ide_info_files)
+ update_set_in_dict(output_groups, "intellij-compile-java", compile_files)
+ update_set_in_dict(output_groups, "intellij-resolve-java", resolve_files)
+
+ # Add to legacy output groups for backwards compatibility
+ update_set_in_dict(output_groups, "intellij-info-text", ide_info_files)
+ update_set_in_dict(output_groups, "intellij-compile", compile_files)
+ update_set_in_dict(output_groups, "intellij-resolve", resolve_files)
+ return True
+
+def _package_manifest_file_argument(f):
+ artifact = artifact_location(f)
+ is_external = "1" if is_external_artifact(f.owner) else "0"
+ return artifact.root_execution_path_fragment + "," + artifact.relative_path + "," + is_external
+
+def build_java_package_manifest(ctx, target, source_files, suffix):
+ """Builds the java package manifest for the given source files."""
+ output = ctx.new_file(target.label.name + suffix)
+
+ args = []
+ args += ["--output_manifest", output.path]
+ args += ["--sources"]
+ args += [":".join([_package_manifest_file_argument(f) for f in source_files])]
+ argfile = ctx.new_file(ctx.configuration.bin_dir,
+ target.label.name + suffix + ".params")
+ ctx.file_action(output=argfile, content="\n".join(args))
+
+ ctx.action(
+ inputs = source_files + [argfile],
+ outputs = [output],
+ executable = ctx.executable._package_parser,
+ arguments = ["@" + argfile.path],
+ mnemonic = "JavaPackageManifest",
+ progress_message = "Parsing java package strings for " + str(target.label),
+ )
+ return output
+
+def build_filtered_gen_jar(ctx, target, java, gen_java_sources, srcjars):
+ """Filters the passed jar to contain only classes from the given manifest."""
+ jar_artifacts = []
+ source_jar_artifacts = []
+ for jar in java.outputs.jars:
+ if jar.ijar:
+ jar_artifacts.append(jar.ijar)
+ elif jar.class_jar:
+ jar_artifacts.append(jar.class_jar)
+ if jar.source_jar:
+ source_jar_artifacts.append(jar.source_jar)
+
+ filtered_jar = ctx.new_file(target.label.name + "-filtered-gen.jar")
+ filtered_source_jar = ctx.new_file(target.label.name + "-filtered-gen-src.jar")
+ args = []
+ for jar in jar_artifacts:
+ args += ["--filter_jar", jar.path]
+ for jar in source_jar_artifacts:
+ args += ["--filter_source_jar", jar.path]
+ args += ["--filtered_jar", filtered_jar.path]
+ args += ["--filtered_source_jar", filtered_source_jar.path]
+ if gen_java_sources:
+ for java_file in gen_java_sources:
+ args += ["--keep_java_file", java_file.path]
+ if srcjars:
+ for source_jar in srcjars:
+ args += ["--keep_source_jar", source_jar.path]
+ ctx.action(
+ inputs = jar_artifacts + source_jar_artifacts + gen_java_sources + srcjars,
+ outputs = [filtered_jar, filtered_source_jar],
+ executable = ctx.executable._jar_filter,
+ arguments = args,
+ mnemonic = "JarFilter",
+ progress_message = "Filtering generated code for " + str(target.label),
+ )
+ output_jar = struct(
+ jar=artifact_location(filtered_jar),
+ source_jar=artifact_location(filtered_source_jar),
+ )
+ intellij_resolve_files = depset([filtered_jar, filtered_source_jar])
+ return output_jar, intellij_resolve_files
+
+def divide_java_sources(ctx):
+ """Divide sources into plain java, generated java, and srcjars."""
+
+ java_sources = []
+ gen_java_sources = []
+ srcjars = []
+ if hasattr(ctx.rule.attr, "srcs"):
+ srcs = ctx.rule.attr.srcs
+ for src in srcs:
+ for f in src.files:
+ if f.basename.endswith(".java"):
+ if f.is_source:
+ java_sources.append(f)
+ else:
+ gen_java_sources.append(f)
+ elif f.basename.endswith(".srcjar"):
+ srcjars.append(f)
+
+ return java_sources, gen_java_sources, srcjars
+
+def collect_android_info(target, ctx, semantics, ide_info, ide_info_file, output_groups):
+ """Updates Android-specific output groups, returns false if not a Android target."""
+ if not hasattr(target, "android"):
+ return False
+
+ android_semantics = semantics.android if hasattr(semantics, "android") else None
+ extra_ide_info = android_semantics.extra_ide_info(target, ctx) if android_semantics else {}
+
+ android = target.android
+ android_info = struct_omit_none(
+ java_package = android.java_package,
+ idl_import_root = android.idl.import_root if hasattr(android.idl, "import_root") else None,
+ manifest = artifact_location(android.manifest),
+ apk = artifact_location(android.apk),
+ dependency_apk = [artifact_location(apk) for apk in android.apks_under_test],
+ has_idl_sources = android.idl.output != None,
+ idl_jar = library_artifact(android.idl.output),
+ generate_resource_class = android.defines_resources,
+ resources = all_unique_source_directories(android.resources),
+ resource_jar = library_artifact(android.resource_jar),
+ **extra_ide_info
+ )
+ resolve_files = depset(jars_from_output(android.idl.output))
+
+ if android.manifest and not android.manifest.is_source:
+ resolve_files = resolve_files | depset([android.manifest])
+
+ ide_info["android_ide_info"] = android_info
+ update_set_in_dict(output_groups, "intellij-info-android", depset([ide_info_file]))
+ update_set_in_dict(output_groups, "intellij-resolve-android", resolve_files)
+
+ # Add to legacy output groups for backwards compatibility
+ update_set_in_dict(output_groups, "intellij-resolve", resolve_files)
+ return True
+
+def collect_android_sdk_info(ctx, ide_info, ide_info_file, output_groups):
+ """Updates android_sdk-relevant groups, returns false if not an android_sdk target."""
+ if ctx.rule.kind != "android_sdk":
+ return False
+ android_jar_file = list(ctx.rule.attr.android_jar.files)[0]
+ ide_info["android_sdk_ide_info"] = struct(
+ android_jar = artifact_location(android_jar_file),
+ )
+ update_set_in_dict(output_groups, "intellij-info-android", depset([ide_info_file]))
+ return True
+
+def build_test_info(ctx):
+ """Build TestInfo."""
+ if not is_test_rule(ctx):
+ return None
+ return struct_omit_none(
+ size = ctx.rule.attr.size,
+ )
+
+def is_test_rule(ctx):
+ kind_string = ctx.rule.kind
+ return kind_string.endswith("_test")
+
+def collect_java_toolchain_info(target, ide_info, ide_info_file, output_groups):
+ """Updates java_toolchain-relevant output groups, returns false if not a java_toolchain target."""
+ if not hasattr(target, "java_toolchain"):
+ return False
+ toolchain_info = target.java_toolchain
+ javac_jar_file = toolchain_info.javac_jar if hasattr(toolchain_info, "javac_jar") else None
+ ide_info["java_toolchain_ide_info"] = struct_omit_none(
+ source_version = toolchain_info.source_version,
+ target_version = toolchain_info.target_version,
+ javac_jar = artifact_location(javac_jar_file),
+ )
+ update_set_in_dict(output_groups, "intellij-info-java", depset([ide_info_file]))
+ return True
+
+##### Main aspect function
+
+def intellij_info_aspect_impl(target, ctx, semantics):
+ """Aspect implementation function."""
+ tags = ctx.rule.attr.tags
+ if "no-ide" in tags:
+ return struct()
+
+ rule_attrs = ctx.rule.attr
+
+ # Collect direct dependencies
+ direct_dep_targets = collect_targets_from_attrs(
+ rule_attrs, semantics_extra_deps(DEPS, semantics, "extra_deps"))
+ direct_deps = make_deps(direct_dep_targets, COMPILE_TIME)
+
+ # Add exports from direct dependencies
+ exported_deps_from_deps = []
+ for dep in direct_dep_targets:
+ exported_deps_from_deps = exported_deps_from_deps + dep.intellij_info.export_deps
+
+ # Combine into all compile time deps
+ compiletime_deps = direct_deps + exported_deps_from_deps
+
+ # Propagate my own exports
+ export_deps = []
+ if hasattr(target, "java"):
+ transitive_exports = target.java.transitive_exports
+ export_deps = [make_dep_from_label(label, COMPILE_TIME) for label in transitive_exports]
+ # Empty android libraries export all their dependencies.
+ if ctx.rule.kind == "android_library":
+ if not hasattr(rule_attrs, "srcs") or not ctx.rule.attr.srcs:
+ export_deps = export_deps + compiletime_deps
+ export_deps = list(depset(export_deps))
+
+ # runtime_deps
+ runtime_dep_targets = collect_targets_from_attrs(
+ rule_attrs, semantics_extra_deps(RUNTIME_DEPS, semantics, "extra_runtime_deps"))
+ runtime_deps = make_deps(runtime_dep_targets, RUNTIME)
+ all_deps = list(depset(compiletime_deps + runtime_deps))
+
+ # extra prerequisites
+ extra_prerequisite_targets = collect_targets_from_attrs(
+ rule_attrs, semantics_extra_deps(PREREQUISITE_DEPS, semantics, "extra_prerequisites"))
+
+ # Roll up output files from my prerequisites
+ prerequisites = direct_dep_targets + runtime_dep_targets + extra_prerequisite_targets
+ output_groups = dict()
+ for dep in prerequisites:
+ for k, v in dep.intellij_info.output_groups.items():
+ update_set_in_dict(output_groups, k, v)
+
+ # Initialize the ide info dict, and corresponding output file
+ # This will be passed to each language-specific handler to fill in as required
+ file_name = target.label.name
+ aspect_ids = get_aspect_ids(ctx, target)
+ if aspect_ids:
+ aspect_hash = hash(".".join(aspect_ids))
+ file_name = file_name + "-" + str(aspect_hash)
+ file_name = file_name + ".intellij-info.txt"
+ ide_info_file = ctx.new_file(file_name)
+
+ target_key = make_target_key(target.label, aspect_ids)
+ ide_info = dict(
+ key = target_key,
+ kind_string = ctx.rule.kind,
+ deps = list(all_deps),
+ build_file_artifact_location = build_file_artifact_location(ctx),
+ tags = tags,
+ features = ctx.features,
+ )
+ # Collect test info
+ ide_info["test_info"] = build_test_info(ctx)
+
+ handled = False
+ handled = collect_py_info(target, ctx, ide_info, ide_info_file, output_groups) or handled
+ handled = collect_cpp_info(target, ctx, ide_info, ide_info_file, output_groups) or handled
+ handled = collect_c_toolchain_info(ctx, ide_info, ide_info_file, output_groups) or handled
+ handled = collect_go_info(target, ide_info, ide_info_file, output_groups) or handled
+ handled = collect_java_info(target, ctx, semantics, ide_info, ide_info_file, output_groups) or handled
+ handled = collect_java_toolchain_info(target, ide_info, ide_info_file, output_groups) or handled
+ handled = collect_android_info(target, ctx, semantics, ide_info, ide_info_file, output_groups) or handled
+ handled = collect_android_sdk_info(ctx, ide_info, ide_info_file, output_groups) or handled
+
+ # Any extra ide info
+ if hasattr(semantics, "extra_ide_info"):
+ handled = semantics.extra_ide_info(target, ctx, ide_info, ide_info_file, output_groups) or handled
+
+ # Add to generic output group if it's not handled by a language-specific handler
+ if not handled:
+ update_set_in_dict(output_groups, "intellij-info-generic", depset([ide_info_file]))
+
+ # Add to legacy output group for backwards compatibility
+ update_set_in_dict(output_groups, "intellij-info-text", depset([ide_info_file]))
+
+ # Output the ide information file.
+ info = struct_omit_none(**ide_info)
+ ctx.file_action(ide_info_file, info.to_proto())
+
+ # Return providers.
+ return struct_omit_none(
+ output_groups = output_groups,
+ intellij_info = struct(
+ target_key = target_key,
+ output_groups = output_groups,
+ export_deps = export_deps,
+ ),
+ )
+
+def semantics_extra_deps(base, semantics, name):
+ if not hasattr(semantics, name):
+ return base
+ extra_deps = getattr(semantics, name)
+ return base + extra_deps
+
+def make_intellij_info_aspect(aspect_impl, semantics):
+ """Creates the aspect given the semantics."""
+ tool_label = semantics.tool_label
+ deps = semantics_extra_deps(DEPS, semantics, "extra_deps")
+ runtime_deps = semantics_extra_deps(RUNTIME_DEPS, semantics, "extra_runtime_deps")
+ prerequisite_deps = semantics_extra_deps(PREREQUISITE_DEPS, semantics, "extra_prerequisites")
+
+ attr_aspects = deps + runtime_deps + prerequisite_deps
+
+ return aspect(
+ attrs = {
+ "_package_parser": attr.label(
+ default = tool_label("PackageParser"),
+ cfg = "host",
+ executable = True,
+ allow_files = True),
+ "_jar_filter": attr.label(
+ default = tool_label("JarFilter"),
+ cfg = "host",
+ executable = True,
+ allow_files = True),
+ },
+ attr_aspects = attr_aspects,
+ fragments = ["cpp"],
+ implementation = aspect_impl,
+ required_aspect_providers = ["proto_java"],
+ )
diff --git a/aspect/tools/BUILD b/aspect/tools/BUILD
new file mode 100644
index 0000000..fe42712
--- /dev/null
+++ b/aspect/tools/BUILD
@@ -0,0 +1,84 @@
+#
+# Description:
+# Tools needed by the bazel plugin's aspect.
+#
+
+package(default_visibility = ["//aspect:__pkg__"])
+
+licenses(["notice"]) # Apache 2.0
+
+# To prevent versioning conflicts when developing internally, we always use the same
+# guava version bundled with the IntelliJ plugin API.
+java_library(
+ name = "guava",
+ exports = ["//intellij_platform_sdk:guava"],
+)
+
+java_library(
+ name = "lib",
+ srcs = glob(["src/**/*.java"]),
+ deps = [
+ ":guava",
+ "//proto:proto_deps",
+ "@jsr305_annotations//jar",
+ ],
+)
+
+java_binary(
+ name = "JarFilter",
+ main_class = "com.google.idea.blaze.aspect.JarFilter",
+ visibility = ["//visibility:public"],
+ runtime_deps = [":lib"],
+)
+
+java_binary(
+ name = "PackageParser",
+ main_class = "com.google.idea.blaze.aspect.PackageParser",
+ visibility = ["//visibility:public"],
+ runtime_deps = [":lib"],
+)
+
+java_library(
+ name = "test_lib",
+ testonly = 1,
+ exports = [
+ ":guava",
+ ":lib",
+ "//intellij_platform_sdk:truth",
+ "//proto:proto_deps",
+ "@jsr305_annotations//jar",
+ "@junit//jar",
+ ],
+)
+
+java_test(
+ name = "JarFilterTest",
+ size = "medium",
+ srcs = ["tests/unittests/com/google/idea/blaze/aspect/JarFilterTest.java"],
+ test_class = "com.google.idea.blaze.aspect.JarFilterTest",
+ deps = [":test_lib"],
+)
+
+java_test(
+ name = "PackageParserTest",
+ size = "small",
+ srcs = ["tests/unittests/com/google/idea/blaze/aspect/PackageParserTest.java"],
+ test_class = "com.google.idea.blaze.aspect.PackageParserTest",
+ deps = [":test_lib"],
+)
+
+java_test(
+ name = "OptionParserTest",
+ size = "small",
+ srcs = ["tests/unittests/com/google/idea/blaze/aspect/OptionParserTest.java"],
+ test_class = "com.google.idea.blaze.aspect.OptionParserTest",
+ deps = [":test_lib"],
+)
+
+java_test(
+ name = "ArtifactLocationParserTest",
+ size = "small",
+ srcs = ["tests/unittests/com/google/idea/blaze/aspect/ArtifactLocationParserTest.java"],
+ test_class = "com.google.idea.blaze.aspect.ArtifactLocationParserTest",
+ deps = [":test_lib"],
+)
diff --git a/aspect/tools/src/com/google/idea/blaze/aspect/ArtifactLocationParser.java b/aspect/tools/src/com/google/idea/blaze/aspect/ArtifactLocationParser.java
new file mode 100644
index 0000000..6a5088c
--- /dev/null
+++ b/aspect/tools/src/com/google/idea/blaze/aspect/ArtifactLocationParser.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2017 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.aspect;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableList;
+import com.google.repackaged.devtools.intellij.ideinfo.IntellijIdeInfo.ArtifactLocation;
+import java.nio.file.FileSystems;
+import java.nio.file.Path;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+/** Parses an {@link ArtifactLocation} from a comma-separate list of string-encoded fields. */
+@VisibleForTesting
+public final class ArtifactLocationParser {
+ private ArtifactLocationParser() {}
+
+ private static final Splitter SPLITTER = Splitter.on(',');
+
+ @VisibleForTesting
+ static final String INVALID_FORMAT =
+ "Expected format rootExecutionPathFragment,relPath,isExternal";
+
+ private static Path getPath(String pathString) {
+ return FileSystems.getDefault().getPath(pathString);
+ }
+
+ /** Parse a colon-separated list of string-encoded {@link ArtifactLocation}s. */
+ static List<ArtifactLocation> parseList(String input) {
+ ImmutableList.Builder<ArtifactLocation> builder = ImmutableList.builder();
+ for (String piece : input.split(":")) {
+ if (!piece.isEmpty()) {
+ builder.add(parse(piece));
+ }
+ }
+ return builder.build();
+ }
+
+ /** Parse an {@link ArtifactLocation} from a comma-separate list of string-encoded fields. */
+ static ArtifactLocation parse(String input) {
+ Iterator<String> values = SPLITTER.split(input).iterator();
+ try {
+ Path rootExecutionPathFragment = getPath(values.next());
+ Path relPath = getPath(values.next());
+ boolean isExternal = values.next().equals("1");
+ if (values.hasNext()) {
+ throw new IllegalArgumentException(INVALID_FORMAT);
+ }
+
+ boolean isSource = rootExecutionPathFragment.toString().isEmpty();
+ return ArtifactLocation.newBuilder()
+ .setRootExecutionPathFragment(rootExecutionPathFragment.toString())
+ .setRelativePath(relPath.toString())
+ .setIsSource(isSource)
+ .setIsExternal(isExternal)
+ .build();
+
+ } catch (NoSuchElementException e) {
+ throw new IllegalArgumentException(INVALID_FORMAT);
+ }
+ }
+}
diff --git a/aspect/tools/src/com/google/idea/blaze/aspect/JarFilter.java b/aspect/tools/src/com/google/idea/blaze/aspect/JarFilter.java
new file mode 100644
index 0000000..53cb58d
--- /dev/null
+++ b/aspect/tools/src/com/google/idea/blaze/aspect/JarFilter.java
@@ -0,0 +1,299 @@
+/*
+ * Copyright 2017 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.aspect;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import com.google.common.io.Files;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.FileSystems;
+import java.nio.file.Path;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executors;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipOutputStream;
+import javax.annotation.Nullable;
+
+/** Filters a jar, keeping only the classes that are indicated. */
+public final class JarFilter {
+
+ /** The options for a {@link JarFilter} action. */
+ @VisibleForTesting
+ static final class JarFilterOptions {
+ List<Path> filterJars;
+ List<Path> filterSourceJars;
+ List<Path> keepJavaFiles;
+ List<Path> keepSourceJars;
+ Path filteredJar;
+ Path filteredSourceJar;
+ }
+
+ @VisibleForTesting
+ static JarFilterOptions parseArgs(String[] args) {
+ args = parseParamFileIfUsed(args);
+ JarFilterOptions options = new JarFilterOptions();
+ options.filterJars = OptionParser.parseMultiOption(args, "filter_jar", PATH_PARSER);
+ options.filterSourceJars =
+ OptionParser.parseMultiOption(args, "filter_source_jar", PATH_PARSER);
+ options.keepJavaFiles = OptionParser.parseMultiOption(args, "keep_java_file", PATH_PARSER);
+ options.keepSourceJars = OptionParser.parseMultiOption(args, "keep_source_jar", PATH_PARSER);
+ options.filteredJar = OptionParser.parseSingleOption(args, "filtered_jar", PATH_PARSER);
+ options.filteredSourceJar =
+ OptionParser.parseSingleOption(args, "filtered_source_jar", PATH_PARSER);
+ return options;
+ }
+
+ private static final Function<String, Path> PATH_PARSER =
+ string -> FileSystems.getDefault().getPath(string);
+
+ private static final Logger logger = Logger.getLogger(JarFilter.class.getName());
+
+ private static final Pattern JAVA_PACKAGE_PATTERN =
+ Pattern.compile("^\\s*package\\s+([\\w\\.]+);");
+
+ public static void main(String[] args) throws Exception {
+ JarFilterOptions options = parseArgs(args);
+ try {
+ main(options);
+ } catch (Throwable e) {
+ logger.log(Level.SEVERE, "Error filtering jars", e);
+ System.exit(1);
+ }
+ System.exit(0);
+ }
+
+ @VisibleForTesting
+ static void main(JarFilterOptions options) throws Exception {
+ Preconditions.checkNotNull(options.filteredJar);
+
+ if (options.filterJars == null) {
+ options.filterJars = ImmutableList.of();
+ }
+ if (options.filterSourceJars == null) {
+ options.filterSourceJars = ImmutableList.of();
+ }
+
+ final List<String> archiveFileNamePrefixes = Lists.newArrayList();
+ if (options.keepJavaFiles != null) {
+ archiveFileNamePrefixes.addAll(parseJavaFiles(options.keepJavaFiles));
+ }
+ if (options.keepSourceJars != null) {
+ archiveFileNamePrefixes.addAll(parseSrcJars(options.keepSourceJars));
+ }
+
+ filterJars(
+ options.filterJars,
+ options.filteredJar,
+ string -> shouldKeepClass(archiveFileNamePrefixes, string));
+ if (options.filteredSourceJar != null) {
+ filterJars(
+ options.filterSourceJars,
+ options.filteredSourceJar,
+ string -> shouldKeepJavaFile(archiveFileNamePrefixes, string));
+ }
+ }
+
+ private static String[] parseParamFileIfUsed(String[] args) {
+ if (args.length != 1 || !args[0].startsWith("@")) {
+ return args;
+ }
+ File paramFile = new File(args[0].substring(1));
+ try {
+ return Files.readLines(paramFile, StandardCharsets.UTF_8).toArray(new String[0]);
+ } catch (IOException e) {
+ throw new RuntimeException("Error parsing param file: " + args[0], e);
+ }
+ }
+
+ /** Finds the expected jar archive file name prefixes for the java files. */
+ private static List<String> parseJavaFiles(List<Path> javaFiles) throws IOException {
+ ListeningExecutorService executorService =
+ MoreExecutors.listeningDecorator(
+ Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()));
+
+ List<ListenableFuture<String>> futures = Lists.newArrayList();
+ for (final Path javaFile : javaFiles) {
+ futures.add(
+ executorService.submit(
+ () -> {
+ String packageString = getDeclaredPackageOfJavaFile(javaFile);
+ return packageString != null
+ ? getArchiveFileNamePrefix(javaFile.toString(), packageString)
+ : null;
+ }));
+ }
+ try {
+ List<String> archiveFileNamePrefixes = Futures.allAsList(futures).get();
+ List<String> result = Lists.newArrayList();
+ for (String archiveFileNamePrefix : archiveFileNamePrefixes) {
+ if (archiveFileNamePrefix != null) {
+ result.add(archiveFileNamePrefix);
+ }
+ }
+ return result;
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new IOException(e);
+ } catch (ExecutionException e) {
+ throw new IOException(e);
+ }
+ }
+
+ private static List<String> parseSrcJars(List<Path> srcJars) throws IOException {
+ List<String> result = Lists.newArrayList();
+ for (Path srcJar : srcJars) {
+ try (ZipFile sourceZipFile = new ZipFile(srcJar.toFile())) {
+ Enumeration<? extends ZipEntry> entries = sourceZipFile.entries();
+ while (entries.hasMoreElements()) {
+ ZipEntry entry = entries.nextElement();
+ if (!entry.getName().endsWith(".java")) {
+ continue;
+ }
+ try (BufferedReader reader =
+ new BufferedReader(
+ new InputStreamReader(sourceZipFile.getInputStream(entry), UTF_8))) {
+ String packageString = parseDeclaredPackage(reader);
+ if (packageString != null) {
+ String archiveFileNamePrefix =
+ getArchiveFileNamePrefix(entry.getName(), packageString);
+ result.add(archiveFileNamePrefix);
+ }
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+ @Nullable
+ private static String getDeclaredPackageOfJavaFile(Path javaFile) {
+ try (BufferedReader reader =
+ java.nio.file.Files.newBufferedReader(javaFile, StandardCharsets.UTF_8)) {
+ return parseDeclaredPackage(reader);
+
+ } catch (IOException e) {
+ logger.log(Level.WARNING, "Error parsing package string from java source: " + javaFile, e);
+ return null;
+ }
+ }
+
+ @Nullable
+ private static String parseDeclaredPackage(BufferedReader reader) throws IOException {
+ String line;
+ while ((line = reader.readLine()) != null) {
+ Matcher packageMatch = JAVA_PACKAGE_PATTERN.matcher(line);
+ if (packageMatch.find()) {
+ return packageMatch.group(1);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Computes the expected archive file name prefix of a java class.
+ *
+ * <p>Eg.: file java/com/google/foo/Foo.java, package com.google.foo -> com/google/foo/Foo
+ */
+ private static String getArchiveFileNamePrefix(String javaFile, String packageString) {
+ int lastSlashIndex = javaFile.lastIndexOf('/');
+ // On Windows, the separator could be '\\'
+ if (lastSlashIndex == -1) {
+ lastSlashIndex = javaFile.lastIndexOf('\\');
+ }
+ String fileName = lastSlashIndex != -1 ? javaFile.substring(lastSlashIndex + 1) : javaFile;
+ String className = fileName.substring(0, fileName.length() - ".java".length());
+ return packageString.replace('.', '/') + '/' + className;
+ }
+
+ /** Filters a list of jars, keeping anything matching the passed predicate. */
+ private static void filterJars(List<Path> jars, Path output, Predicate<String> shouldKeep)
+ throws IOException {
+ final int bufferSize = 8 * 1024;
+ byte[] buffer = new byte[bufferSize];
+
+ try (ZipOutputStream outputStream =
+ new ZipOutputStream(new FileOutputStream(output.toFile()))) {
+ for (Path jar : jars) {
+ try (ZipFile sourceZipFile = new ZipFile(jar.toFile())) {
+ Enumeration<? extends ZipEntry> entries = sourceZipFile.entries();
+ while (entries.hasMoreElements()) {
+ ZipEntry entry = entries.nextElement();
+ if (!shouldKeep.test(entry.getName())) {
+ continue;
+ }
+
+ ZipEntry newEntry = new ZipEntry(entry.getName());
+ outputStream.putNextEntry(newEntry);
+ try (InputStream inputStream = sourceZipFile.getInputStream(entry)) {
+ int len;
+ while ((len = inputStream.read(buffer)) != -1) {
+ outputStream.write(buffer, 0, len);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @VisibleForTesting
+ static boolean shouldKeepClass(List<String> archiveFileNamePrefixes, String name) {
+ if (!name.endsWith(".class")) {
+ return false;
+ }
+ for (String archiveFileNamePrefix : archiveFileNamePrefixes) {
+ if (name.startsWith(archiveFileNamePrefix)
+ && name.length() > archiveFileNamePrefix.length()) {
+ char c = name.charAt(archiveFileNamePrefix.length());
+ if (c == '.' || c == '$') {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private static boolean shouldKeepJavaFile(List<String> archiveFileNamePrefixes, String name) {
+ if (!name.endsWith(".java")) {
+ return false;
+ }
+ String nameWithoutJava = name.substring(0, name.length() - ".java".length());
+ return archiveFileNamePrefixes.contains(nameWithoutJava);
+ }
+}
diff --git a/aspect/tools/src/com/google/idea/blaze/aspect/OptionParser.java b/aspect/tools/src/com/google/idea/blaze/aspect/OptionParser.java
new file mode 100644
index 0000000..9e9bf4c
--- /dev/null
+++ b/aspect/tools/src/com/google/idea/blaze/aspect/OptionParser.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2017 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.aspect;
+
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Function;
+import javax.annotation.Nullable;
+
+/**
+ * A very light-weight command-line argument parser.
+ *
+ * <p>Expects args array of the form ["--arg_name1", "value1", "--arg_name2", "value2", ...]
+ */
+public final class OptionParser {
+ private OptionParser() {}
+
+ /**
+ * Searches for '--[name]' in the args list, and parses the following argument using the supplied
+ * parser. Returns null if not such argument found.
+ *
+ * @throws IllegalArgumentException if '--[name]' appears more than once, as this indicates a
+ * programming error on the client side.
+ */
+ @Nullable
+ static <T> T parseSingleOption(String[] args, String name, Function<String, T> parser) {
+ String argName = "--" + name;
+ for (int i = 0; i < args.length; i++) {
+ if (!args[i].equals(argName)) {
+ continue;
+ }
+ if (i == args.length - 1) {
+ throw new IllegalArgumentException("Expected value after " + args[i]);
+ }
+ for (int j = i + 2; j < args.length; j++) {
+ if (args[j].equals(argName)) {
+ throw new IllegalArgumentException("Expected " + args[i] + " to appear at most once");
+ }
+ }
+ return parser.apply(args[i + 1]);
+ }
+ return null;
+ }
+
+ /**
+ * Parse all '--[name]' flags in `args`, return a list of all their parsed values.
+ *
+ * <p>When args[i] = `--[name]`, then args[i+1] is parsed for the value, using `parser`.
+ *
+ * <p>Otherwise args[i] is ignored.
+ *
+ * <p>Returns an empty list if no occurrences of the flag are found.
+ */
+ static <T> List<T> parseMultiOption(String[] args, String name, Function<String, T> parser) {
+ List<T> result = null;
+ String argName = "--" + name;
+ for (int i = 0; i < args.length; i++) {
+ if (!args[i].equals(argName)) {
+ continue;
+ }
+ if (i == args.length - 1) {
+ throw new IllegalArgumentException("Expected value after " + args[i]);
+ }
+ T parsed = parser.apply(args[i + 1]);
+ if (parsed != null) {
+ if (result == null) {
+ result = new ArrayList<>();
+ }
+ result.add(parsed);
+ }
+ i++;
+ }
+ return result == null ? ImmutableList.of() : result;
+ }
+}
diff --git a/aspect/tools/src/com/google/idea/blaze/aspect/PackageParser.java b/aspect/tools/src/com/google/idea/blaze/aspect/PackageParser.java
new file mode 100644
index 0000000..a3a44c1
--- /dev/null
+++ b/aspect/tools/src/com/google/idea/blaze/aspect/PackageParser.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2017 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.aspect;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Maps;
+import com.google.common.io.Files;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+import com.google.repackaged.devtools.intellij.ideinfo.IntellijIdeInfo.ArtifactLocation;
+import com.google.repackaged.devtools.intellij.ideinfo.IntellijIdeInfo.JavaSourcePackage;
+import com.google.repackaged.devtools.intellij.ideinfo.IntellijIdeInfo.PackageManifest;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.FileSystems;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.Executors;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import javax.annotation.Nullable;
+
+/** Parses the package string from each of the source .java files. */
+public class PackageParser {
+
+ /** The options for a {@link PackageParser} action. */
+ @VisibleForTesting
+ static final class PackageParserOptions {
+ List<ArtifactLocation> sources;
+ Path outputManifest;
+ }
+
+ @VisibleForTesting
+ static PackageParserOptions parseArgs(String[] args) {
+ args = parseParamFileIfUsed(args);
+ PackageParserOptions options = new PackageParserOptions();
+ options.sources =
+ OptionParser.parseSingleOption(args, "sources", ArtifactLocationParser::parseList);
+ options.outputManifest =
+ OptionParser.parseSingleOption(
+ args, "output_manifest", string -> FileSystems.getDefault().getPath(string));
+ return options;
+ }
+
+ private static final Logger logger = Logger.getLogger(PackageParser.class.getName());
+
+ private static final Pattern PACKAGE_PATTERN = Pattern.compile("^\\s*package\\s+([\\w\\.]+)");
+
+ public static void main(String[] args) throws Exception {
+ PackageParserOptions options = parseArgs(args);
+ Preconditions.checkNotNull(options.outputManifest);
+
+ try {
+ PackageParser parser = new PackageParser(PackageParserIoProvider.INSTANCE);
+ Map<ArtifactLocation, String> outputMap = parser.parsePackageStrings(options.sources);
+ parser.writeManifest(outputMap, options.outputManifest);
+ } catch (Throwable e) {
+ logger.log(Level.SEVERE, "Error parsing package strings", e);
+ System.exit(1);
+ }
+ System.exit(0);
+ }
+
+ private static Path getExecutionPath(ArtifactLocation location) {
+ return Paths.get(location.getRootExecutionPathFragment(), location.getRelativePath());
+ }
+
+ private static String[] parseParamFileIfUsed(String[] args) {
+ if (args.length != 1 || !args[0].startsWith("@")) {
+ return args;
+ }
+ File paramFile = new File(args[0].substring(1));
+ try {
+ return Files.readLines(paramFile, StandardCharsets.UTF_8).toArray(new String[0]);
+ } catch (IOException e) {
+ throw new RuntimeException("Error parsing param file: " + args[0], e);
+ }
+ }
+
+ private final PackageParserIoProvider ioProvider;
+
+ @VisibleForTesting
+ PackageParser(PackageParserIoProvider ioProvider) {
+ this.ioProvider = ioProvider;
+ }
+
+ @VisibleForTesting
+ void writeManifest(Map<ArtifactLocation, String> sourceToPackageMap, Path outputFile)
+ throws IOException {
+ PackageManifest.Builder builder = PackageManifest.newBuilder();
+ for (Entry<ArtifactLocation, String> entry : sourceToPackageMap.entrySet()) {
+ JavaSourcePackage.Builder srcBuilder =
+ JavaSourcePackage.newBuilder()
+ .setPackageString(entry.getValue())
+ .setArtifactLocation(entry.getKey());
+ builder.addSources(srcBuilder.build());
+ }
+
+ try {
+ ioProvider.writeProto(builder.build(), outputFile);
+ } catch (IOException e) {
+ logger.log(Level.SEVERE, "Error writing package manifest", e);
+ throw e;
+ }
+ }
+
+ @VisibleForTesting
+ Map<ArtifactLocation, String> parsePackageStrings(List<ArtifactLocation> sources)
+ throws Exception {
+
+ ListeningExecutorService executorService =
+ MoreExecutors.listeningDecorator(
+ Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()));
+
+ Map<ArtifactLocation, ListenableFuture<String>> futures = Maps.newHashMap();
+ for (final ArtifactLocation source : sources) {
+ futures.put(source, executorService.submit(() -> getDeclaredPackageOfJavaFile(source)));
+ }
+ Map<ArtifactLocation, String> map = Maps.newHashMap();
+ for (Entry<ArtifactLocation, ListenableFuture<String>> entry : futures.entrySet()) {
+ String value = entry.getValue().get();
+ if (value != null) {
+ map.put(entry.getKey(), value);
+ }
+ }
+ return map;
+ }
+
+ @Nullable
+ private String getDeclaredPackageOfJavaFile(ArtifactLocation source) {
+ try (BufferedReader reader = ioProvider.getReader(getExecutionPath(source))) {
+ return parseDeclaredPackage(reader);
+
+ } catch (IOException e) {
+ logger.log(Level.WARNING, "Error parsing package string from java source: " + source, e);
+ return null;
+ }
+ }
+
+ @Nullable
+ private static String parseDeclaredPackage(BufferedReader reader) throws IOException {
+ String line;
+ while ((line = reader.readLine()) != null) {
+ Matcher packageMatch = PACKAGE_PATTERN.matcher(line);
+ if (packageMatch.find()) {
+ return packageMatch.group(1);
+ }
+ }
+ return null;
+ }
+}
diff --git a/aspect/tools/src/com/google/idea/blaze/aspect/PackageParserIoProvider.java b/aspect/tools/src/com/google/idea/blaze/aspect/PackageParserIoProvider.java
new file mode 100644
index 0000000..3d965c2
--- /dev/null
+++ b/aspect/tools/src/com/google/idea/blaze/aspect/PackageParserIoProvider.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2017 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.aspect;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.repackaged.protobuf.MessageLite;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+/** Provides a BufferedReader for the source java files, and a writer for the output proto */
+@VisibleForTesting
+public class PackageParserIoProvider {
+
+ static final PackageParserIoProvider INSTANCE = new PackageParserIoProvider();
+
+ void writeProto(MessageLite message, Path file) throws IOException {
+ try (OutputStream out = Files.newOutputStream(file)) {
+ message.writeTo(out);
+ }
+ }
+
+ BufferedReader getReader(Path file) throws IOException {
+ return Files.newBufferedReader(file, StandardCharsets.UTF_8);
+ }
+}
diff --git a/aspect/tools/tests/unittests/com/google/idea/blaze/aspect/ArtifactLocationParserTest.java b/aspect/tools/tests/unittests/com/google/idea/blaze/aspect/ArtifactLocationParserTest.java
new file mode 100644
index 0000000..9985f48
--- /dev/null
+++ b/aspect/tools/tests/unittests/com/google/idea/blaze/aspect/ArtifactLocationParserTest.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2017 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.aspect;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.fail;
+
+import com.google.common.base.Joiner;
+import com.google.repackaged.devtools.intellij.ideinfo.IntellijIdeInfo.ArtifactLocation;
+import java.nio.file.Paths;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests {@link ArtifactLocationParser}. */
+@RunWith(JUnit4.class)
+public class ArtifactLocationParserTest {
+
+ @Test
+ public void testConverterSourceArtifact() {
+ ArtifactLocation parsed =
+ ArtifactLocationParser.parse(Joiner.on(',').join("", "test.java", "0"));
+ assertThat(parsed)
+ .isEqualTo(
+ ArtifactLocation.newBuilder()
+ .setRelativePath(Paths.get("test.java").toString())
+ .setIsSource(true)
+ .setIsExternal(false)
+ .build());
+ }
+
+ @Test
+ public void testConverterDerivedArtifact() {
+ ArtifactLocation parsed =
+ ArtifactLocationParser.parse(Joiner.on(',').join("bin", "java/com/test.java", "0"));
+ assertThat(parsed)
+ .isEqualTo(
+ ArtifactLocation.newBuilder()
+ .setRootExecutionPathFragment(Paths.get("bin").toString())
+ .setRelativePath(Paths.get("java/com/test.java").toString())
+ .setIsSource(false)
+ .setIsExternal(false)
+ .build());
+ }
+
+ @Test
+ public void testConverterExternal() {
+ ArtifactLocation externalArtifact =
+ ArtifactLocationParser.parse(Joiner.on(',').join("", "test.java", "1"));
+ assertThat(externalArtifact)
+ .isEqualTo(
+ ArtifactLocation.newBuilder()
+ .setRelativePath(Paths.get("test.java").toString())
+ .setIsSource(true)
+ .setIsExternal(true)
+ .build());
+ ArtifactLocation nonExternalArtifact =
+ ArtifactLocationParser.parse(Joiner.on(',').join("", "test.java", "0"));
+ assertThat(nonExternalArtifact)
+ .isEqualTo(
+ ArtifactLocation.newBuilder()
+ .setRelativePath(Paths.get("test.java").toString())
+ .setIsSource(true)
+ .setIsExternal(false)
+ .build());
+ }
+
+ @Test
+ public void testInvalidFormatFails() {
+ assertFails("/root", ArtifactLocationParser.INVALID_FORMAT);
+ assertFails("/root,exec,rel,extra", ArtifactLocationParser.INVALID_FORMAT);
+ }
+
+ @Test
+ public void testParsingArtifactLocationList() {
+ String input =
+ Joiner.on(':')
+ .join(
+ Joiner.on(',').join("", "test.java", "0"),
+ Joiner.on(',').join("bin", "java/com/test.java", "0"),
+ Joiner.on(',').join("", "test.java", "1"));
+
+ assertThat(ArtifactLocationParser.parseList(input))
+ .containsExactly(
+ ArtifactLocation.newBuilder()
+ .setRelativePath(Paths.get("test.java").toString())
+ .setIsSource(true)
+ .build(),
+ ArtifactLocation.newBuilder()
+ .setRootExecutionPathFragment(Paths.get("bin").toString())
+ .setRelativePath(Paths.get("java/com/test.java").toString())
+ .setIsSource(false)
+ .build(),
+ ArtifactLocation.newBuilder()
+ .setRelativePath(Paths.get("test.java").toString())
+ .setIsSource(true)
+ .setIsExternal(true)
+ .build());
+ }
+
+ private void assertFails(String input, String expectedError) {
+ try {
+ ArtifactLocationParser.parse(input);
+ fail();
+ } catch (IllegalArgumentException e) {
+ assertThat(e).hasMessage(expectedError);
+ }
+ }
+}
diff --git a/aspect/tools/tests/unittests/com/google/idea/blaze/aspect/JarFilterTest.java b/aspect/tools/tests/unittests/com/google/idea/blaze/aspect/JarFilterTest.java
new file mode 100644
index 0000000..8153676
--- /dev/null
+++ b/aspect/tools/tests/unittests/com/google/idea/blaze/aspect/JarFilterTest.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2017 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.aspect;
+
+import static com.google.common.truth.Truth.assertThat;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import com.google.common.io.Files;
+import com.google.idea.blaze.aspect.JarFilter.JarFilterOptions;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipOutputStream;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Unit tests for {@link JarFilter} */
+@RunWith(JUnit4.class)
+public class JarFilterTest {
+
+ @Rule public TemporaryFolder folder = new TemporaryFolder();
+
+ @Test
+ public void testFilterMethod() throws Exception {
+ List<String> prefixes =
+ ImmutableList.of("com/google/foo/Foo", "com/google/bar/Bar", "com/google/baz/Baz");
+ assertThat(JarFilter.shouldKeepClass(prefixes, "com/google/foo/Foo.class")).isTrue();
+ assertThat(JarFilter.shouldKeepClass(prefixes, "com/google/foo/Foo$Inner.class")).isTrue();
+ assertThat(JarFilter.shouldKeepClass(prefixes, "com/google/bar/Bar.class")).isTrue();
+ assertThat(JarFilter.shouldKeepClass(prefixes, "com/google/foo/Foo/NotFoo.class")).isFalse();
+ assertThat(JarFilter.shouldKeepClass(prefixes, "wrong/com/google/foo/Foo.class")).isFalse();
+ }
+
+ @Test
+ public void fullIntegrationTest() throws Exception {
+ File fooJava = folder.newFile("Foo.java");
+ Files.write("package com.google.foo; class Foo { class Inner {} }".getBytes(UTF_8), fooJava);
+
+ File barJava = folder.newFile("Bar.java");
+ Files.write("package com.google.foo.bar; class Bar {}".getBytes(UTF_8), barJava);
+
+ File srcJar = folder.newFile("gen.srcjar");
+ try (ZipOutputStream zo = new ZipOutputStream(new FileOutputStream(srcJar))) {
+ zo.putNextEntry(new ZipEntry("com/google/foo/gen/Gen.java"));
+ zo.write("package gen; class Gen {}".getBytes(UTF_8));
+ zo.closeEntry();
+ zo.putNextEntry(new ZipEntry("com/google/foo/gen/Gen2.java"));
+ zo.write("package gen; class Gen2 {}".getBytes(UTF_8));
+ zo.closeEntry();
+ }
+
+ File src3Jar = folder.newFile("gen3.srcjar");
+ try (ZipOutputStream zo = new ZipOutputStream(new FileOutputStream(src3Jar))) {
+ zo.putNextEntry(new ZipEntry("com/google/foo/gen/Gen3.java"));
+ zo.write("package gen; class Gen3 {}".getBytes(UTF_8));
+ zo.closeEntry();
+ }
+
+ File filterJar = folder.newFile("foo.jar");
+ try (ZipOutputStream zo = new ZipOutputStream(new FileOutputStream(filterJar))) {
+ zo.putNextEntry(new ZipEntry("com/google/foo/Foo.class"));
+ zo.closeEntry();
+ zo.putNextEntry(new ZipEntry("com/google/foo/Foo$Inner.class"));
+ zo.closeEntry();
+ zo.putNextEntry(new ZipEntry("com/google/foo/bar/Bar.class"));
+ zo.closeEntry();
+ zo.putNextEntry(new ZipEntry("gen/Gen.class"));
+ zo.closeEntry();
+ zo.putNextEntry(new ZipEntry("gen/Gen2.class"));
+ zo.closeEntry();
+ zo.putNextEntry(new ZipEntry("gen/Gen3.class"));
+ zo.closeEntry();
+ zo.putNextEntry(new ZipEntry("com/google/foo/Foo2.class"));
+ zo.closeEntry();
+ }
+ File filterSrcJar = folder.newFile("foo-src.jar");
+ try (ZipOutputStream zo = new ZipOutputStream(new FileOutputStream(filterSrcJar))) {
+ zo.putNextEntry(new ZipEntry("com/google/foo/Foo.java"));
+ zo.closeEntry();
+ zo.putNextEntry(new ZipEntry("com/google/foo/bar/Bar.java"));
+ zo.closeEntry();
+ zo.putNextEntry(new ZipEntry("gen/Gen.java"));
+ zo.closeEntry();
+ zo.putNextEntry(new ZipEntry("gen/Gen2.java"));
+ zo.closeEntry();
+ zo.putNextEntry(new ZipEntry("gen/Gen3.java"));
+ zo.closeEntry();
+ zo.putNextEntry(new ZipEntry("com/google/foo/Foo2.java"));
+ zo.closeEntry();
+ zo.putNextEntry(new ZipEntry("com/google/foo/bar/Bar2.java"));
+ zo.closeEntry();
+ }
+
+ File filteredJar = folder.newFile("foo-filtered-gen.jar");
+ File filteredSourceJar = folder.newFile("foo-filtered-gen-src.jar");
+
+ String[] args =
+ new String[] {
+ "--keep_java_file",
+ fooJava.getPath(),
+ "--keep_java_file",
+ barJava.getPath(),
+ "--keep_source_jar",
+ srcJar.getPath(),
+ "--keep_source_jar",
+ src3Jar.getPath(),
+ "--filter_jar",
+ filterJar.getPath(),
+ "--filter_source_jar",
+ filterSrcJar.getPath(),
+ "--filtered_jar",
+ filteredJar.getPath(),
+ "--filtered_source_jar",
+ filteredSourceJar.getPath()
+ };
+ JarFilterOptions options = JarFilter.parseArgs(args);
+ JarFilter.main(options);
+
+ List<String> filteredJarNames = Lists.newArrayList();
+ try (ZipFile zipFile = new ZipFile(filteredJar)) {
+ Enumeration<? extends ZipEntry> entries = zipFile.entries();
+ while (entries.hasMoreElements()) {
+ ZipEntry zipEntry = entries.nextElement();
+ filteredJarNames.add(zipEntry.getName());
+ }
+ }
+
+ List<String> filteredSourceJarNames = Lists.newArrayList();
+ try (ZipFile zipFile = new ZipFile(filteredSourceJar)) {
+ Enumeration<? extends ZipEntry> entries = zipFile.entries();
+ while (entries.hasMoreElements()) {
+ ZipEntry zipEntry = entries.nextElement();
+ filteredSourceJarNames.add(zipEntry.getName());
+ }
+ }
+
+ assertThat(filteredJarNames)
+ .containsExactly(
+ "com/google/foo/Foo.class",
+ "com/google/foo/Foo$Inner.class",
+ "com/google/foo/bar/Bar.class",
+ "gen/Gen.class",
+ "gen/Gen2.class",
+ "gen/Gen3.class");
+
+ assertThat(filteredSourceJarNames)
+ .containsExactly(
+ "com/google/foo/Foo.java",
+ "com/google/foo/bar/Bar.java",
+ "gen/Gen.java",
+ "gen/Gen2.java",
+ "gen/Gen3.java");
+ }
+}
diff --git a/aspect/tools/tests/unittests/com/google/idea/blaze/aspect/OptionParserTest.java b/aspect/tools/tests/unittests/com/google/idea/blaze/aspect/OptionParserTest.java
new file mode 100644
index 0000000..c506e7a
--- /dev/null
+++ b/aspect/tools/tests/unittests/com/google/idea/blaze/aspect/OptionParserTest.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2017 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.aspect;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.fail;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Unit tests for {@link OptionParser}. */
+@RunWith(JUnit4.class)
+public class OptionParserTest {
+ @Test
+ public void testParseSingleOption() throws Exception {
+ // Test parsing of a flag that doesn't occur.
+ assertThat(
+ OptionParser.parseSingleOption(
+ new String[] {"--foo", "1", "--bar", "2"}, "unknown", String::toString))
+ .isNull();
+ // Test parsing of an integer flag.
+ assertThat(
+ OptionParser.<Integer>parseSingleOption(
+ new String[] {"--foo", "1", "--bar", "2"}, "bar", Integer::parseInt))
+ .isEqualTo(2);
+ // Test parsing of a flag that's missing its value: if there's a subsequent entry and it can be
+ // parsed, it'll be taken as the flag's value.
+ assertThat(
+ OptionParser.parseSingleOption(
+ new String[] {"--foo", "--bar", "--baz"}, "foo", String::toString))
+ .isEqualTo("--bar");
+ // Test parsing of a flag that's missing its value: if there's no subsequent entry, an exception
+ // is thrown.
+ try {
+ OptionParser.parseSingleOption(new String[] {"--foo", "1", "--bar"}, "bar", String::toString);
+ fail("Expected failure");
+ } catch (IllegalArgumentException e) {
+ assertThat(e).hasMessage("Expected value after --bar");
+ }
+ // Test that a single-value flag should not appear multiple times.
+ try {
+ OptionParser.parseSingleOption(
+ new String[] {"--foo", "1", "--foo", "2"}, "foo", String::toString);
+ fail("Expected failure");
+ } catch (IllegalArgumentException e) {
+ assertThat(e).hasMessage("Expected --foo to appear at most once");
+ }
+ }
+
+ @Test
+ public void testParseMultiOption() throws Exception {
+ // Test parsing of a flag that doesn't occur.
+ assertThat(
+ OptionParser.parseMultiOption(
+ new String[] {"--foo", "1", "--bar", "2"}, "unknown", String::toString))
+ .isEmpty();
+ // Test parsing of an integer multi-flag. Ensure that the flag name is matched wholly, and
+ // doesn't match other flags that it's a prefix of.
+ assertThat(
+ OptionParser.parseMultiOption(
+ new String[] {"--foo", "1", "--foooo", "2", "--foo", "3"},
+ "foo",
+ Integer::parseInt))
+ .containsExactly(1, 3)
+ .inOrder();
+ // Test that the --name=value style of flags is not supported (for sake of simplicity).
+ assertThat(
+ OptionParser.parseMultiOption(
+ new String[] {"--foo", "1", "--foo=2", "--foo", "3"}, "foo", Integer::parseInt))
+ .containsExactly(1, 3)
+ .inOrder();
+ // Test parsing a multi-flag where one occurrence cannot consume a value.
+ try {
+ OptionParser.parseMultiOption(
+ new String[] {"--foo", "1", "--bar", "2", "--foo"}, "foo", String::toString);
+ fail("Expected failure");
+ } catch (IllegalArgumentException e) {
+ assertThat(e).hasMessage("Expected value after --foo");
+ }
+ }
+}
diff --git a/aspect/tools/tests/unittests/com/google/idea/blaze/aspect/PackageParserTest.java b/aspect/tools/tests/unittests/com/google/idea/blaze/aspect/PackageParserTest.java
new file mode 100644
index 0000000..61a6892
--- /dev/null
+++ b/aspect/tools/tests/unittests/com/google/idea/blaze/aspect/PackageParserTest.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright 2017 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.aspect;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.fail;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.repackaged.devtools.intellij.ideinfo.IntellijIdeInfo.ArtifactLocation;
+import com.google.repackaged.protobuf.MessageLite;
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.StringWriter;
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.InvalidPathException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.Map;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Unit tests for {@link PackageParser} */
+@RunWith(JUnit4.class)
+public class PackageParserTest {
+
+ private static class MockPackageParserIoProvider extends PackageParserIoProvider {
+ private final Map<Path, InputStream> sources = Maps.newHashMap();
+ private final List<ArtifactLocation> sourceLocations = Lists.newArrayList();
+ private StringWriter writer = new StringWriter();
+
+ public MockPackageParserIoProvider addSource(ArtifactLocation source, String javaSrc) {
+ try {
+ Path path = Paths.get(source.getRootExecutionPathFragment(), source.getRelativePath());
+ sources.put(path, new ByteArrayInputStream(javaSrc.getBytes("UTF-8")));
+ sourceLocations.add(source);
+
+ } catch (UnsupportedEncodingException | InvalidPathException e) {
+ fail(e.getMessage());
+ }
+ return this;
+ }
+
+ public List<ArtifactLocation> getSourceLocations() {
+ return Lists.newArrayList(sourceLocations);
+ }
+
+ @Override
+ public BufferedReader getReader(Path file) throws IOException {
+ InputStream input = sources.get(file);
+ return new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8));
+ }
+
+ @Override
+ public void writeProto(MessageLite message, Path file) throws IOException {
+ writer.write(message.toString());
+ }
+ }
+
+ private static final ArtifactLocation DUMMY_SOURCE_ARTIFACT =
+ ArtifactLocation.newBuilder()
+ .setRelativePath("java/com/google/Foo.java")
+ .setIsSource(true)
+ .build();
+
+ private static final ArtifactLocation DUMMY_DERIVED_ARTIFACT =
+ ArtifactLocation.newBuilder()
+ .setRootExecutionPathFragment("bin")
+ .setRelativePath("java/com/google/Bla.java")
+ .setIsSource(false)
+ .build();
+
+ private static final ArtifactLocation DUMMY_SCALA_SOURCE_ARTIFACT =
+ ArtifactLocation.newBuilder()
+ .setRelativePath("scala/com/google/Foo.scala")
+ .setIsSource(true)
+ .build();
+
+ private static final ArtifactLocation DUMMY_SCALA_DERIVED_ARTIFACT =
+ ArtifactLocation.newBuilder()
+ .setRootExecutionPathFragment("bin")
+ .setRelativePath("scala/com/google/Bla.scala")
+ .setIsSource(false)
+ .build();
+
+ private MockPackageParserIoProvider mockIoProvider;
+ private PackageParser parser;
+
+ @Before
+ public void setUp() {
+ mockIoProvider = new MockPackageParserIoProvider();
+ parser = new PackageParser(mockIoProvider);
+ }
+
+ private Map<ArtifactLocation, String> parsePackageStrings() throws Exception {
+ List<ArtifactLocation> sources = mockIoProvider.getSourceLocations();
+ return parser.parsePackageStrings(sources);
+ }
+
+ @Test
+ public void testParseCommandLineArguments() throws Exception {
+ String[] args =
+ new String[] {
+ "--output_manifest",
+ "/tmp/out.manifest",
+ "--sources",
+ Joiner.on(':').join(",java/com/google/Foo.java,0", "bin/out,java/com/google/Bla.java,0")
+ };
+ PackageParser.PackageParserOptions options = PackageParser.parseArgs(args);
+ assertThat(options.outputManifest.toString())
+ .isEqualTo(Paths.get("/tmp/out.manifest").toString());
+ assertThat(options.sources).hasSize(2);
+ assertThat(options.sources.get(0))
+ .isEqualTo(
+ ArtifactLocation.newBuilder()
+ .setRelativePath(Paths.get("java/com/google/Foo.java").toString())
+ .setIsSource(true)
+ .build());
+ assertThat(options.sources.get(1))
+ .isEqualTo(
+ ArtifactLocation.newBuilder()
+ .setRootExecutionPathFragment(Paths.get("bin/out").toString())
+ .setRelativePath(Paths.get("java/com/google/Bla.java").toString())
+ .setIsSource(false)
+ .build());
+ }
+
+ @Test
+ public void testReadNoSources() throws Exception {
+ Map<ArtifactLocation, String> map = parsePackageStrings();
+ assertThat(map).isEmpty();
+ }
+
+ @Test
+ public void testSingleRead() throws Exception {
+ mockIoProvider.addSource(DUMMY_SOURCE_ARTIFACT, "package com.google;\n public class Bla {}\"");
+ Map<ArtifactLocation, String> map = parsePackageStrings();
+ assertThat(map).hasSize(1);
+ assertThat(map).containsEntry(DUMMY_SOURCE_ARTIFACT, "com.google");
+ }
+
+ @Test
+ public void testMultiRead() throws Exception {
+ mockIoProvider
+ .addSource(DUMMY_SOURCE_ARTIFACT, "package com.test;\n public class Foo {}\"")
+ .addSource(DUMMY_DERIVED_ARTIFACT, "package com.other;\n public class Bla {}\"");
+ Map<ArtifactLocation, String> map = parsePackageStrings();
+ assertThat(map).hasSize(2);
+ assertThat(map).containsEntry(DUMMY_SOURCE_ARTIFACT, "com.test");
+ assertThat(map).containsEntry(DUMMY_DERIVED_ARTIFACT, "com.other");
+ }
+
+ @Test
+ public void testReadSomeInvalid() throws Exception {
+ mockIoProvider
+ .addSource(DUMMY_SOURCE_ARTIFACT, "package %com.test;\n public class Foo {}\"")
+ .addSource(DUMMY_DERIVED_ARTIFACT, "package com.other;\n public class Bla {}\"");
+ Map<ArtifactLocation, String> map = parsePackageStrings();
+ assertThat(map).hasSize(1);
+ assertThat(map).containsEntry(DUMMY_DERIVED_ARTIFACT, "com.other");
+ }
+
+ @Test
+ public void testReadAllInvalid() throws Exception {
+ mockIoProvider
+ .addSource(DUMMY_SOURCE_ARTIFACT, "#package com.test;\n public class Foo {}\"")
+ .addSource(DUMMY_DERIVED_ARTIFACT, "package %com.other\n public class Bla {}\"");
+ Map<ArtifactLocation, String> map = parsePackageStrings();
+ assertThat(map).isEmpty();
+ }
+
+ @Test
+ public void testReadScala() throws Exception {
+ mockIoProvider
+ .addSource(DUMMY_SCALA_SOURCE_ARTIFACT, "package com.test\n class Foo {}\"")
+ .addSource(DUMMY_SCALA_DERIVED_ARTIFACT, "package com.other {}\n object Bla {}\"");
+ Map<ArtifactLocation, String> map = parsePackageStrings();
+ assertThat(map).containsEntry(DUMMY_SCALA_SOURCE_ARTIFACT, "com.test");
+ assertThat(map).containsEntry(DUMMY_SCALA_DERIVED_ARTIFACT, "com.other");
+ }
+
+ @Test
+ public void testWriteEmptyMap() throws Exception {
+ parser.writeManifest(Maps.newHashMap(), Paths.get("/java/com/google/test.manifest"));
+ assertThat(mockIoProvider.writer.toString()).isEmpty();
+ }
+
+ @Test
+ public void testWriteMap() throws Exception {
+ Map<ArtifactLocation, String> map =
+ ImmutableMap.of(DUMMY_SOURCE_ARTIFACT, "com.google", DUMMY_DERIVED_ARTIFACT, "com.other");
+ parser.writeManifest(map, Paths.get("/java/com/google/test.manifest"));
+
+ String writtenString = mockIoProvider.writer.toString();
+ assertThat(writtenString)
+ .contains(String.format("relative_path: \"%s\"", DUMMY_SOURCE_ARTIFACT.getRelativePath()));
+ assertThat(writtenString).contains("package_string: \"com.google\"");
+
+ assertThat(writtenString)
+ .contains(
+ String.format(
+ "root_execution_path_fragment: \"%s\"",
+ DUMMY_DERIVED_ARTIFACT.getRootExecutionPathFragment()));
+ assertThat(writtenString)
+ .contains(String.format("relative_path: \"%s\"", DUMMY_DERIVED_ARTIFACT.getRelativePath()));
+ assertThat(writtenString).contains("package_string: \"com.other\"");
+ }
+
+ @Test
+ public void testHandlesOldFormat() throws Exception {
+ String[] args =
+ new String[] {
+ "--output_manifest",
+ "/tmp/out.manifest",
+ "--sources",
+ Joiner.on(':')
+ .join(
+ ",java/com/google/Foo.java,/usr/local/google/code",
+ "bin,java/com/google/Bla.java,/usr/local/_tmp/code/bin")
+ };
+ PackageParser.PackageParserOptions options = PackageParser.parseArgs(args);
+ assertThat(options.outputManifest.toString())
+ .isEqualTo(Paths.get("/tmp/out.manifest").toString());
+ assertThat(options.sources).hasSize(2);
+ assertThat(options.sources.get(0))
+ .isEqualTo(
+ ArtifactLocation.newBuilder()
+ .setRelativePath(Paths.get("java/com/google/Foo.java").toString())
+ .setIsSource(true)
+ .build());
+ assertThat(options.sources.get(1))
+ .isEqualTo(
+ ArtifactLocation.newBuilder()
+ .setRootExecutionPathFragment(Paths.get("bin").toString())
+ .setRelativePath(Paths.get("java/com/google/Bla.java").toString())
+ .setIsSource(false)
+ .build());
+ }
+}
diff --git a/aswb/2.3/src/com/android/tools/idea/npw/project/AndroidProjectPaths.java b/aswb/2.3/src/com/android/tools/idea/npw/project/AndroidProjectPaths.java
new file mode 100644
index 0000000..8124a41
--- /dev/null
+++ b/aswb/2.3/src/com/android/tools/idea/npw/project/AndroidProjectPaths.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2017 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.tools.idea.npw.project;
+
+import java.io.File;
+import javax.annotation.Nullable;
+
+/** Fake {@link AndroidProjectPaths} for 2.3. */
+public interface AndroidProjectPaths {
+ File getModuleRoot();
+
+ File getSrcDirectory(@Nullable String packageName);
+
+ File getTestDirectory(@Nullable String packageName);
+
+ File getResDirectory();
+
+ File getAidlDirectory(@Nullable String packageName);
+
+ File getManifestDirectory();
+}
diff --git a/aswb/2.3/src/com/android/tools/idea/res/IdeResourceNameValidator.java b/aswb/2.3/src/com/android/tools/idea/res/IdeResourceNameValidator.java
new file mode 100644
index 0000000..117da24
--- /dev/null
+++ b/aswb/2.3/src/com/android/tools/idea/res/IdeResourceNameValidator.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2017 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.tools.idea.res;
+
+import com.android.resources.ResourceFolderType;
+
+/** Fake {@link IdeResourceNameValidator} for 2.3, renamed from {@link ResourceNameValidator}. */
+public class IdeResourceNameValidator {
+ public final ResourceNameValidator delegate;
+
+ public IdeResourceNameValidator(ResourceNameValidator delegate) {
+ this.delegate = delegate;
+ }
+
+ public static IdeResourceNameValidator forFilename(
+ ResourceFolderType type, String implicitExtension) {
+ return new IdeResourceNameValidator(ResourceNameValidator.create(false, type));
+ }
+
+ public String getErrorText(String inputString) {
+ return delegate.getErrorText(inputString);
+ }
+}
diff --git a/aswb/2.3/src/com/google/idea/sdkcompat/android/model/AndroidModelAdapter.java b/aswb/2.3/src/com/google/idea/sdkcompat/android/model/AndroidModelAdapter.java
new file mode 100644
index 0000000..b1fa492
--- /dev/null
+++ b/aswb/2.3/src/com/google/idea/sdkcompat/android/model/AndroidModelAdapter.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2017 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.sdkcompat.android.model;
+
+import com.android.tools.idea.model.AndroidModel;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import javax.annotation.Nullable;
+
+/** Compatibility adapter for {@link AndroidModel}. */
+public abstract class AndroidModelAdapter implements AndroidModel {
+ @Nullable
+ @Override
+ public Long getLastBuildTimestamp(Project project) {
+ return null;
+ }
+
+ public abstract boolean isClassFileOutOfDate(Module module, String fqcn, VirtualFile classFile);
+}
diff --git a/aswb/2.3/src/com/google/idea/sdkcompat/android/project/AndroidProjectInfoAdapter.java b/aswb/2.3/src/com/google/idea/sdkcompat/android/project/AndroidProjectInfoAdapter.java
new file mode 100644
index 0000000..c91afbe
--- /dev/null
+++ b/aswb/2.3/src/com/google/idea/sdkcompat/android/project/AndroidProjectInfoAdapter.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2017 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.sdkcompat.android.project;
+
+import com.android.tools.idea.gradle.util.Projects;
+import com.android.tools.idea.project.AndroidProjectInfo;
+import com.intellij.openapi.project.Project;
+
+/** Compatibility adapter for {@link AndroidProjectInfo}. */
+public class AndroidProjectInfoAdapter {
+ public static boolean requiredAndroidModelMissing(Project project) {
+ return Projects.requiredAndroidModelMissing(project);
+ }
+}
diff --git a/aswb/2.3/src/com/google/idea/sdkcompat/android/project/BuildSystemServiceAdapter.java b/aswb/2.3/src/com/google/idea/sdkcompat/android/project/BuildSystemServiceAdapter.java
new file mode 100644
index 0000000..8af0cec
--- /dev/null
+++ b/aswb/2.3/src/com/google/idea/sdkcompat/android/project/BuildSystemServiceAdapter.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2017 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.sdkcompat.android.project;
+
+import com.android.tools.idea.npw.project.AndroidSourceSet;
+import com.android.tools.idea.project.BuildSystemService;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import java.util.List;
+import javax.annotation.Nullable;
+import org.jetbrains.android.facet.AndroidFacet;
+
+/** Compatibility adapter for {@link BuildSystemService}. */
+public abstract class BuildSystemServiceAdapter extends BuildSystemService {
+ public abstract String mergeBuildFiles(
+ String dependencies,
+ String destinationContents,
+ Project project,
+ @Nullable String supportLibVersionFilter);
+
+ public abstract List<AndroidSourceSet> getSourceSets(
+ AndroidFacet facet, @Nullable VirtualFile targetDirectory);
+}
diff --git a/aswb/2.3/src/com/google/idea/sdkcompat/android/res/AppResourceRepositoryAdapter.java b/aswb/2.3/src/com/google/idea/sdkcompat/android/res/AppResourceRepositoryAdapter.java
new file mode 100644
index 0000000..b58d477
--- /dev/null
+++ b/aswb/2.3/src/com/google/idea/sdkcompat/android/res/AppResourceRepositoryAdapter.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2017 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.sdkcompat.android.res;
+
+import com.android.tools.idea.res.AppResourceRepository;
+import com.intellij.openapi.module.Module;
+
+/** Compatibility adapter for {@link AppResourceRepository}. */
+public class AppResourceRepositoryAdapter {
+ public static AppResourceRepository getOrCreateInstance(Module module) {
+ return AppResourceRepository.getAppResources(module, true);
+ }
+
+ public static AppResourceRepository findExistingInstance(Module module) {
+ return AppResourceRepository.getAppResources(module, false);
+ }
+}
diff --git a/aswb/2.3/src/com/google/idea/sdkcompat/android/resources/actions/CreateXmlResourceDialogAdapter.java b/aswb/2.3/src/com/google/idea/sdkcompat/android/resources/actions/CreateXmlResourceDialogAdapter.java
new file mode 100644
index 0000000..e7361b0
--- /dev/null
+++ b/aswb/2.3/src/com/google/idea/sdkcompat/android/resources/actions/CreateXmlResourceDialogAdapter.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2017 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.sdkcompat.android.resources.actions;
+
+import com.android.resources.ResourceType;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.ValidationInfo;
+import com.intellij.openapi.vfs.VirtualFile;
+import java.util.List;
+import javax.annotation.Nullable;
+import org.jetbrains.android.actions.CreateXmlResourceDialog;
+
+/** Compatibility adapter for {@link CreateXmlResourceDialog}. */
+public class CreateXmlResourceDialogAdapter {
+ public static ValidationInfo checkIfResourceAlreadyExists(
+ Project project,
+ VirtualFile resourceDir,
+ String resourceName,
+ @Nullable String resourceValue,
+ ResourceType resourceType,
+ List<String> dirNames,
+ String fileName) {
+ return CreateXmlResourceDialog.checkIfResourceAlreadyExists(
+ project, resourceDir, resourceName, resourceType, dirNames, fileName);
+ }
+}
diff --git a/aswb/2.3/src/com/google/idea/sdkcompat/android/resources/actions/CreateXmlResourcePanelAdapter.java b/aswb/2.3/src/com/google/idea/sdkcompat/android/resources/actions/CreateXmlResourcePanelAdapter.java
new file mode 100644
index 0000000..620cf18
--- /dev/null
+++ b/aswb/2.3/src/com/google/idea/sdkcompat/android/resources/actions/CreateXmlResourcePanelAdapter.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2017 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.sdkcompat.android.resources.actions;
+
+import com.android.tools.idea.res.IdeResourceNameValidator;
+import com.android.tools.idea.res.ResourceNameValidator;
+import org.jetbrains.android.actions.CreateXmlResourcePanel;
+
+/** Compatibility adapter for {@link CreateXmlResourcePanel}. */
+public abstract class CreateXmlResourcePanelAdapter implements CreateXmlResourcePanel {
+ protected abstract IdeResourceNameValidator getResourceNameValidatorCompat();
+
+ @Override
+ public ResourceNameValidator getResourceNameValidator() {
+ return getResourceNameValidatorCompat().delegate;
+ }
+}
diff --git a/aswb/2.3/src/com/google/idea/sdkcompat/android/resources/actions/NewResourceCreationHandlerAdapter.java b/aswb/2.3/src/com/google/idea/sdkcompat/android/resources/actions/NewResourceCreationHandlerAdapter.java
new file mode 100644
index 0000000..c7d8f3d
--- /dev/null
+++ b/aswb/2.3/src/com/google/idea/sdkcompat/android/resources/actions/NewResourceCreationHandlerAdapter.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2017 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.sdkcompat.android.resources.actions;
+
+import com.android.resources.ResourceFolderType;
+import com.android.resources.ResourceType;
+import com.android.tools.idea.res.IdeResourceNameValidator;
+import com.android.tools.idea.res.ResourceNameValidator;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.vfs.VirtualFile;
+import java.util.function.Function;
+import javax.annotation.Nullable;
+import org.jetbrains.android.actions.CreateXmlResourcePanel;
+import org.jetbrains.android.actions.NewResourceCreationHandler;
+
+/** Compatibility adapter for {@link NewResourceCreationHandler}. */
+public abstract class NewResourceCreationHandlerAdapter implements NewResourceCreationHandler {
+ protected abstract CreateXmlResourcePanel createNewResourceValuePanelCompat(
+ Module module,
+ ResourceType resourceType,
+ ResourceFolderType folderType,
+ @Nullable String resourceName,
+ @Nullable String resourceValue,
+ boolean chooseName,
+ boolean chooseValue,
+ boolean chooseFilename,
+ @Nullable VirtualFile defaultFile,
+ @Nullable VirtualFile contextFile,
+ Function<Module, IdeResourceNameValidator> nameValidatorFactory);
+
+ @Override
+ public CreateXmlResourcePanel createNewResourceValuePanel(
+ Module module,
+ ResourceType resourceType,
+ ResourceFolderType folderType,
+ @Nullable String resourceName,
+ @Nullable String resourceValue,
+ boolean chooseName,
+ boolean chooseValue,
+ boolean chooseFilename,
+ @Nullable VirtualFile defaultFile,
+ @Nullable VirtualFile contextFile,
+ Function<Module, ResourceNameValidator> nameValidatorFactory) {
+ return createNewResourceValuePanelCompat(
+ module,
+ resourceType,
+ folderType,
+ resourceName,
+ resourceValue,
+ chooseName,
+ chooseValue,
+ chooseFilename,
+ defaultFile,
+ contextFile,
+ m -> new IdeResourceNameValidator(nameValidatorFactory.apply(m)));
+ }
+}
diff --git a/aswb/2.3/src/com/google/idea/sdkcompat/android/sync/BlazeNdkDependencySyncPluginCompat.java b/aswb/2.3/src/com/google/idea/sdkcompat/android/sync/BlazeNdkDependencySyncPluginCompat.java
new file mode 100644
index 0000000..471b696
--- /dev/null
+++ b/aswb/2.3/src/com/google/idea/sdkcompat/android/sync/BlazeNdkDependencySyncPluginCompat.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2017 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.sdkcompat.android.sync;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.idea.blaze.android.sync.BlazeNdkDependencySyncPlugin;
+
+/** Compatibility indirection for {@link BlazeNdkDependencySyncPlugin}. */
+public class BlazeNdkDependencySyncPluginCompat {
+ public static final ImmutableMap<String, String> REQUIRED_PLUGINS =
+ ImmutableMap.of(
+ "Android NDK Support",
+ "com.android.tools.ndk",
+ "NDK WorkspaceManager Support",
+ "com.android.tools.ndk.workspace");
+}
diff --git a/aswb/3.0/src/META-INF/ndk-workspace-contents.xml b/aswb/3.0/src/META-INF/ndk-workspace-contents.xml
new file mode 100644
index 0000000..da35648
--- /dev/null
+++ b/aswb/3.0/src/META-INF/ndk-workspace-contents.xml
@@ -0,0 +1,20 @@
+<!--
+ ~ Copyright 2017 The Bazel Authors. All rights reserved.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<idea-plugin>
+ <extensions defaultExtensionNs="com.android.tools.ndk">
+ <workspaceProvider implementation="com.google.idea.blaze.android.ndk.workspace.BlazeNdkWorkspaceProvider"/>
+ </extensions>
+</idea-plugin>
\ No newline at end of file
diff --git a/aswb/3.0/src/com/google/idea/blaze/android/ndk/workspace/BlazeNdkWorkspaceProvider.java b/aswb/3.0/src/com/google/idea/blaze/android/ndk/workspace/BlazeNdkWorkspaceProvider.java
new file mode 100644
index 0000000..2a3353e
--- /dev/null
+++ b/aswb/3.0/src/com/google/idea/blaze/android/ndk/workspace/BlazeNdkWorkspaceProvider.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2017 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.android.ndk.workspace;
+
+import com.android.tools.ndk.workspace.NdkWorkspaceProvider;
+import com.google.idea.blaze.base.settings.Blaze;
+import com.google.idea.blaze.cpp.BlazeCWorkspace;
+import com.intellij.openapi.project.Project;
+import com.jetbrains.cidr.lang.workspace.OCWorkspace;
+import javax.annotation.Nullable;
+
+/** Extension to provide an NDK workspace. */
+public class BlazeNdkWorkspaceProvider extends NdkWorkspaceProvider {
+ @Nullable
+ @Override
+ public OCWorkspace findNdkWorkspace(Project project) {
+ if (Blaze.isBlazeProject(project)) {
+ return BlazeCWorkspace.getInstance(project);
+ }
+ return null;
+ }
+}
diff --git a/aswb/3.0/src/com/google/idea/sdkcompat/android/model/AndroidModelAdapter.java b/aswb/3.0/src/com/google/idea/sdkcompat/android/model/AndroidModelAdapter.java
new file mode 100644
index 0000000..c992e5a
--- /dev/null
+++ b/aswb/3.0/src/com/google/idea/sdkcompat/android/model/AndroidModelAdapter.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2017 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.sdkcompat.android.model;
+
+import com.android.tools.idea.model.AndroidModel;
+
+/** Compatibility adapter for {@link AndroidModel}. */
+public abstract class AndroidModelAdapter implements AndroidModel {}
diff --git a/aswb/3.0/src/com/google/idea/sdkcompat/android/project/AndroidProjectInfoAdapter.java b/aswb/3.0/src/com/google/idea/sdkcompat/android/project/AndroidProjectInfoAdapter.java
new file mode 100644
index 0000000..8ef7963
--- /dev/null
+++ b/aswb/3.0/src/com/google/idea/sdkcompat/android/project/AndroidProjectInfoAdapter.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2017 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.sdkcompat.android.project;
+
+import com.android.tools.idea.project.AndroidProjectInfo;
+import com.intellij.openapi.project.Project;
+
+/** Compatibility adapter for {@link AndroidProjectInfo}. */
+public class AndroidProjectInfoAdapter {
+ public static boolean requiredAndroidModelMissing(Project project) {
+ return AndroidProjectInfo.getInstance(project).requiredAndroidModelMissing();
+ }
+}
diff --git a/aswb/3.0/src/com/google/idea/sdkcompat/android/project/BuildSystemServiceAdapter.java b/aswb/3.0/src/com/google/idea/sdkcompat/android/project/BuildSystemServiceAdapter.java
new file mode 100644
index 0000000..e6201dd
--- /dev/null
+++ b/aswb/3.0/src/com/google/idea/sdkcompat/android/project/BuildSystemServiceAdapter.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2017 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.sdkcompat.android.project;
+
+import com.android.tools.idea.project.BuildSystemService;
+
+/** Compatibility adapter for {@link BuildSystemService}. */
+public abstract class BuildSystemServiceAdapter extends BuildSystemService {}
diff --git a/aswb/3.0/src/com/google/idea/sdkcompat/android/res/AppResourceRepositoryAdapter.java b/aswb/3.0/src/com/google/idea/sdkcompat/android/res/AppResourceRepositoryAdapter.java
new file mode 100644
index 0000000..890d132
--- /dev/null
+++ b/aswb/3.0/src/com/google/idea/sdkcompat/android/res/AppResourceRepositoryAdapter.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2017 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.sdkcompat.android.res;
+
+import com.android.tools.idea.res.AppResourceRepository;
+import com.intellij.openapi.module.Module;
+
+/** Compatibility adapter for {@link AppResourceRepository}. */
+public class AppResourceRepositoryAdapter {
+ public static AppResourceRepository getOrCreateInstance(Module module) {
+ return AppResourceRepository.getOrCreateInstance(module);
+ }
+
+ public static AppResourceRepository findExistingInstance(Module module) {
+ return AppResourceRepository.findExistingInstance(module);
+ }
+}
diff --git a/aswb/3.0/src/com/google/idea/sdkcompat/android/resources/actions/CreateXmlResourceDialogAdapter.java b/aswb/3.0/src/com/google/idea/sdkcompat/android/resources/actions/CreateXmlResourceDialogAdapter.java
new file mode 100644
index 0000000..9a424e1
--- /dev/null
+++ b/aswb/3.0/src/com/google/idea/sdkcompat/android/resources/actions/CreateXmlResourceDialogAdapter.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2017 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.sdkcompat.android.resources.actions;
+
+import com.android.resources.ResourceType;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.ValidationInfo;
+import com.intellij.openapi.vfs.VirtualFile;
+import java.util.List;
+import javax.annotation.Nullable;
+import org.jetbrains.android.actions.CreateXmlResourceDialog;
+
+/** Compatibility adapter for {@link CreateXmlResourceDialog}. */
+public class CreateXmlResourceDialogAdapter {
+ public static ValidationInfo checkIfResourceAlreadyExists(
+ Project project,
+ VirtualFile resourceDir,
+ String resourceName,
+ @Nullable String resourceValue,
+ ResourceType resourceType,
+ List<String> dirNames,
+ String fileName) {
+ return CreateXmlResourceDialog.checkIfResourceAlreadyExists(
+ project, resourceDir, resourceName, resourceValue, resourceType, dirNames, fileName);
+ }
+}
diff --git a/aswb/3.0/src/com/google/idea/sdkcompat/android/resources/actions/CreateXmlResourcePanelAdapter.java b/aswb/3.0/src/com/google/idea/sdkcompat/android/resources/actions/CreateXmlResourcePanelAdapter.java
new file mode 100644
index 0000000..15cf7b5
--- /dev/null
+++ b/aswb/3.0/src/com/google/idea/sdkcompat/android/resources/actions/CreateXmlResourcePanelAdapter.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2017 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.sdkcompat.android.resources.actions;
+
+import com.android.tools.idea.res.IdeResourceNameValidator;
+import org.jetbrains.android.actions.CreateXmlResourcePanel;
+
+/** Compatibility adapter for {@link CreateXmlResourcePanel}. */
+public abstract class CreateXmlResourcePanelAdapter implements CreateXmlResourcePanel {
+
+ protected abstract IdeResourceNameValidator getResourceNameValidatorCompat();
+
+ @Override
+ public IdeResourceNameValidator getResourceNameValidator() {
+ return getResourceNameValidatorCompat();
+ }
+}
diff --git a/aswb/3.0/src/com/google/idea/sdkcompat/android/resources/actions/NewResourceCreationHandlerAdapter.java b/aswb/3.0/src/com/google/idea/sdkcompat/android/resources/actions/NewResourceCreationHandlerAdapter.java
new file mode 100644
index 0000000..5c3f51f
--- /dev/null
+++ b/aswb/3.0/src/com/google/idea/sdkcompat/android/resources/actions/NewResourceCreationHandlerAdapter.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2017 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.sdkcompat.android.resources.actions;
+
+import com.android.resources.ResourceFolderType;
+import com.android.resources.ResourceType;
+import com.android.tools.idea.res.IdeResourceNameValidator;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.vfs.VirtualFile;
+import java.util.function.Function;
+import javax.annotation.Nullable;
+import org.jetbrains.android.actions.CreateXmlResourcePanel;
+import org.jetbrains.android.actions.NewResourceCreationHandler;
+
+/** Compatibility adapter for {@link NewResourceCreationHandler}. */
+public abstract class NewResourceCreationHandlerAdapter implements NewResourceCreationHandler {
+ public abstract CreateXmlResourcePanel createNewResourceValuePanelCompat(
+ Module module,
+ ResourceType resourceType,
+ ResourceFolderType folderType,
+ @Nullable String resourceName,
+ @Nullable String resourceValue,
+ boolean chooseName,
+ boolean chooseValue,
+ boolean chooseFilename,
+ @Nullable VirtualFile defaultFile,
+ @Nullable VirtualFile contextFile,
+ Function<Module, IdeResourceNameValidator> nameValidatorFactory);
+
+ @Override
+ public CreateXmlResourcePanel createNewResourceValuePanel(
+ Module module,
+ ResourceType resourceType,
+ ResourceFolderType folderType,
+ @Nullable String resourceName,
+ @Nullable String resourceValue,
+ boolean chooseName,
+ boolean chooseValue,
+ boolean chooseFilename,
+ @Nullable VirtualFile defaultFile,
+ @Nullable VirtualFile contextFile,
+ Function<Module, IdeResourceNameValidator> nameValidatorFactory) {
+ return createNewResourceValuePanelCompat(
+ module,
+ resourceType,
+ folderType,
+ resourceName,
+ resourceValue,
+ chooseName,
+ chooseValue,
+ chooseFilename,
+ defaultFile,
+ contextFile,
+ nameValidatorFactory);
+ }
+}
diff --git a/aswb/3.0/src/com/google/idea/sdkcompat/android/sync/BlazeNdkDependencySyncPluginCompat.java b/aswb/3.0/src/com/google/idea/sdkcompat/android/sync/BlazeNdkDependencySyncPluginCompat.java
new file mode 100644
index 0000000..3faaaf1
--- /dev/null
+++ b/aswb/3.0/src/com/google/idea/sdkcompat/android/sync/BlazeNdkDependencySyncPluginCompat.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2017 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.sdkcompat.android.sync;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.idea.blaze.android.sync.BlazeNdkDependencySyncPlugin;
+
+/** Compatibility indirection for {@link BlazeNdkDependencySyncPlugin}. */
+public class BlazeNdkDependencySyncPluginCompat {
+ public static final ImmutableMap<String, String> REQUIRED_PLUGINS =
+ ImmutableMap.of("Android NDK Support", "com.android.tools.ndk");
+}
diff --git a/aswb/3.0/tests/unittests/com/google/idea/blaze/android/npw/project/BlazeAndroidProjectPathsTest.java b/aswb/3.0/tests/unittests/com/google/idea/blaze/android/npw/project/BlazeAndroidProjectPathsTest.java
new file mode 100644
index 0000000..ef64612
--- /dev/null
+++ b/aswb/3.0/tests/unittests/com/google/idea/blaze/android/npw/project/BlazeAndroidProjectPathsTest.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright 2017 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.android.npw.project;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import com.android.builder.model.SourceProvider;
+import com.android.tools.idea.npw.project.AndroidProjectPaths;
+import com.android.tools.idea.npw.project.AndroidSourceSet;
+import com.android.tools.idea.project.BuildSystemService;
+import com.google.common.collect.ImmutableList;
+import com.google.idea.blaze.android.project.BlazeBuildSystemService;
+import com.google.idea.blaze.android.sync.model.idea.SourceProviderImpl;
+import com.google.idea.blaze.base.BlazeTestCase;
+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.facet.FacetType;
+import com.intellij.facet.FacetTypeRegistry;
+import com.intellij.facet.impl.FacetTypeRegistryImpl;
+import com.intellij.mock.MockModule;
+import com.intellij.mock.MockVirtualFile;
+import com.intellij.openapi.extensions.ExtensionPointName;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ModuleRootManager;
+import com.intellij.openapi.vfs.VfsUtilCore;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.JavaDirectoryService;
+import com.intellij.psi.PsiDirectory;
+import com.intellij.psi.PsiManager;
+import com.intellij.psi.PsiPackage;
+import com.intellij.psi.impl.file.PsiPackageImpl;
+import java.io.File;
+import java.util.List;
+import org.jetbrains.android.facet.AndroidFacet;
+import org.jetbrains.android.facet.AndroidFacetConfiguration;
+import org.jetbrains.android.facet.AndroidFacetType;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Test cases for {@link BlazeAndroidProjectPaths}. */
+@RunWith(JUnit4.class)
+public class BlazeAndroidProjectPathsTest extends BlazeTestCase {
+ private VirtualFile root = new MockVirtualFile(true, "root");
+ private VirtualFile resource = new MockVirtualFile(true, "root/resource");
+ private VirtualFile target = new MockVirtualFile(true, "root/library/com/google/target");
+
+ @Override
+ protected void initTest(Container applicationServices, Container projectServices) {
+ mockFacetRegistry(applicationServices);
+ mockBlazeImportSettings(projectServices);
+ mockPsiPackage(applicationServices, projectServices);
+
+ registerExtensionPoint(
+ ExtensionPointName.create("com.android.project.buildSystemService"),
+ BuildSystemService.class)
+ .registerExtension(new BlazeBuildSystemService());
+ }
+
+ /**
+ * If we have a resource module and a target directory, then we can get the res dir from the
+ * module, and use the target directory for everything else.
+ */
+ @Test
+ public void getResourceSourceSetsWithTargetDirectory() {
+ AndroidFacet facet = mockResourceFacet();
+ File resourceFile = VfsUtilCore.virtualToIoFile(resource);
+ File targetFile = VfsUtilCore.virtualToIoFile(target);
+ List<AndroidSourceSet> sourceSets = AndroidSourceSet.getSourceSets(facet, target);
+ assertThat(sourceSets).hasSize(1);
+ AndroidSourceSet sourceSet = sourceSets.get(0);
+ AndroidProjectPaths paths = sourceSet.getPaths();
+ assertThat(sourceSet.getName()).isEqualTo("com.google.target");
+ assertThat(paths.getModuleRoot()).isEqualTo(resourceFile);
+ assertThat(paths.getSrcDirectory(null)).isEqualTo(targetFile);
+ assertThat(paths.getTestDirectory(null)).isEqualTo(targetFile);
+ assertThat(paths.getResDirectory()).isEqualTo(new File(resourceFile, "res"));
+ assertThat(paths.getAidlDirectory(null)).isEqualTo(targetFile);
+ assertThat(paths.getManifestDirectory()).isEqualTo(targetFile);
+ }
+
+ /**
+ * If we have a target directory but no resource module, we'll assume the res dir is just
+ * target/res.
+ */
+ @Test
+ public void getWorkspaceSourceSetsWithTargetDirectory() {
+ AndroidFacet facet = mockWorkspaceFacet();
+ File rootFile = VfsUtilCore.virtualToIoFile(root);
+ File targetFile = VfsUtilCore.virtualToIoFile(target);
+ List<AndroidSourceSet> sourceSets = AndroidSourceSet.getSourceSets(facet, target);
+ assertThat(sourceSets).hasSize(1);
+ AndroidSourceSet sourceSet = sourceSets.get(0);
+ AndroidProjectPaths paths = sourceSet.getPaths();
+ assertThat(sourceSet.getName()).isEqualTo("com.google.target");
+ assertThat(paths.getModuleRoot()).isEqualTo(rootFile);
+ assertThat(paths.getSrcDirectory(null)).isEqualTo(targetFile);
+ assertThat(paths.getTestDirectory(null)).isEqualTo(targetFile);
+ assertThat(paths.getResDirectory()).isEqualTo(new File(targetFile, "res"));
+ assertThat(paths.getAidlDirectory(null)).isEqualTo(targetFile);
+ assertThat(paths.getManifestDirectory()).isEqualTo(targetFile);
+ }
+
+ /**
+ * If no target directory is given, but we have a resource module, we can still figure out some
+ * paths.
+ */
+ @Test
+ public void getResourceSourceSetsWithNoTargetDirectory() {
+ AndroidFacet facet = mockResourceFacet();
+ File rootFile = VfsUtilCore.virtualToIoFile(root);
+ File resourceFile = VfsUtilCore.virtualToIoFile(resource);
+ List<AndroidSourceSet> sourceSets = AndroidSourceSet.getSourceSets(facet, null);
+ assertThat(sourceSets).hasSize(1);
+ AndroidSourceSet sourceSet = sourceSets.get(0);
+ AndroidProjectPaths paths = sourceSet.getPaths();
+ assertThat(sourceSet.getName()).isEqualTo("com.google.resource");
+ assertThat(paths.getModuleRoot()).isEqualTo(resourceFile);
+ assertThat(paths.getSrcDirectory(null)).isEqualTo(resourceFile);
+ assertThat(paths.getTestDirectory(null)).isEqualTo(resourceFile);
+ assertThat(paths.getResDirectory()).isEqualTo(new File(resourceFile, "res"));
+ assertThat(paths.getAidlDirectory(null)).isEqualTo(resourceFile);
+ assertThat(paths.getManifestDirectory()).isEqualTo(resourceFile);
+ }
+
+ /**
+ * If no target directory is given, and we have the workspace module, we'll just use the module
+ * root.
+ */
+ @Test
+ public void getWorkspaceSourceSetsWithNoTargetDirectory() {
+ AndroidFacet facet = mockWorkspaceFacet();
+ File rootFile = VfsUtilCore.virtualToIoFile(root);
+ List<AndroidSourceSet> sourceSets = AndroidSourceSet.getSourceSets(facet, null);
+ assertThat(sourceSets).hasSize(1);
+ AndroidSourceSet sourceSet = sourceSets.get(0);
+ AndroidProjectPaths paths = sourceSet.getPaths();
+ assertThat(sourceSet.getName()).isEqualTo(".workspace");
+ assertThat(paths.getModuleRoot()).isEqualTo(rootFile);
+ assertThat(paths.getSrcDirectory(null)).isEqualTo(rootFile);
+ assertThat(paths.getTestDirectory(null)).isEqualTo(rootFile);
+ assertThat(paths.getResDirectory()).isEqualTo(new File(rootFile, "res"));
+ assertThat(paths.getAidlDirectory(null)).isEqualTo(rootFile);
+ assertThat(paths.getManifestDirectory()).isEqualTo(rootFile);
+ }
+
+ private void mockBlazeImportSettings(Container projectServices) {
+ BlazeImportSettingsManager importSettingsManager = new BlazeImportSettingsManager();
+ importSettingsManager.setImportSettings(
+ new BlazeImportSettings("", "", "", "", Blaze.BuildSystem.Blaze));
+ projectServices.register(BlazeImportSettingsManager.class, importSettingsManager);
+ }
+
+ private void mockPsiPackage(Container applicationServices, Container projectServices) {
+ projectServices.register(PsiManager.class, mock(PsiManager.class));
+ applicationServices.register(JavaDirectoryService.class, mock(JavaDirectoryService.class));
+ PsiManager manager = PsiManager.getInstance(project);
+ PsiDirectory targetPsiDirectory = mock(PsiDirectory.class);
+ PsiPackage targetPsiPackage = new PsiPackageImpl(manager, "com.google.target");
+ when(PsiManager.getInstance(project).findDirectory(target)).thenReturn(targetPsiDirectory);
+ when(JavaDirectoryService.getInstance().getPackage(targetPsiDirectory))
+ .thenReturn(targetPsiPackage);
+ }
+
+ private void mockFacetRegistry(Container applicationServices) {
+ applicationServices.register(FacetTypeRegistry.class, new FacetTypeRegistryImpl());
+ registerExtensionPoint(FacetType.EP_NAME, FacetType.class)
+ .registerExtension(new AndroidFacetType());
+ }
+
+ private AndroidFacet mockWorkspaceFacet() {
+ String name = ".workspace";
+ File rootFile = VfsUtilCore.virtualToIoFile(root);
+ SourceProvider sourceProvider =
+ new SourceProviderImpl(name, new File(rootFile, "AndroidManifest.xml"), ImmutableList.of());
+ return new MockAndroidFacet(project, name, root, sourceProvider);
+ }
+
+ private AndroidFacet mockResourceFacet() {
+ String name = "com.google.resource";
+ File resourceFile = VfsUtilCore.virtualToIoFile(resource);
+ SourceProvider sourceProvider =
+ new SourceProviderImpl(
+ name,
+ new File(resourceFile, "AndroidManifest.xml"),
+ ImmutableList.of(new File(resourceFile, "res")));
+ return new MockAndroidFacet(project, name, resource, sourceProvider);
+ }
+
+ private static class MockAndroidFacet extends AndroidFacet {
+ private SourceProvider sourceProvider;
+
+ public MockAndroidFacet(
+ Project project, String name, VirtualFile root, SourceProvider sourceProvider) {
+ super(new MockModule(project, () -> {}), AndroidFacet.NAME, new AndroidFacetConfiguration());
+ MockModule module = (MockModule) getModule();
+ module.setName(name);
+ ModuleRootManager rootManager = mock(ModuleRootManager.class);
+ when(rootManager.getContentRoots()).thenReturn(new VirtualFile[] {root});
+ module.addComponent(ModuleRootManager.class, rootManager);
+ this.sourceProvider = sourceProvider;
+ }
+
+ @Override
+ public SourceProvider getMainSourceProvider() {
+ return sourceProvider;
+ }
+ }
+}
diff --git a/aswb/3.0/tests/unittests/com/google/idea/blaze/android/sync/model/idea/BlazeAndroidModelTest.java b/aswb/3.0/tests/unittests/com/google/idea/blaze/android/sync/model/idea/BlazeAndroidModelTest.java
new file mode 100644
index 0000000..4432472
--- /dev/null
+++ b/aswb/3.0/tests/unittests/com/google/idea/blaze/android/sync/model/idea/BlazeAndroidModelTest.java
@@ -0,0 +1,213 @@
+/*
+ * 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 static org.mockito.Mockito.when;
+
+import com.android.builder.model.SourceProvider;
+import com.google.common.collect.ImmutableCollection;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.idea.blaze.base.BlazeTestCase;
+import com.google.idea.blaze.base.actions.BlazeBuildService;
+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.BuildSystem;
+import com.google.idea.blaze.base.settings.BlazeImportSettings;
+import com.google.idea.blaze.base.settings.BlazeImportSettingsManager;
+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.workspace.WorkspacePathResolver;
+import com.google.idea.sdkcompat.android.model.AndroidModelAdapter;
+import com.intellij.mock.MockFileDocumentManagerImpl;
+import com.intellij.mock.MockModule;
+import com.intellij.mock.MockPsiManager;
+import com.intellij.mock.MockVirtualFile;
+import com.intellij.openapi.fileEditor.FileDocumentManager;
+import com.intellij.openapi.fileTypes.FileTypeManager;
+import com.intellij.openapi.fileTypes.MockFileTypeManager;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.JarFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.VirtualFileManager;
+import com.intellij.openapi.vfs.VirtualFileSystem;
+import com.intellij.psi.JavaPsiFacade;
+import com.intellij.psi.PsiClass;
+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 javax.annotation.Nullable;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Test cases for {@link BlazeAndroidModel}. */
+@RunWith(JUnit4.class)
+public class BlazeAndroidModelTest extends BlazeTestCase {
+ private Module module;
+ private AndroidModelAdapter model;
+ private MockJavaPsiFacade facade;
+
+ @Override
+ protected void initTest(Container applicationServices, Container projectServices) {
+ applicationServices.register(FileTypeManager.class, new MockFileTypeManager());
+ applicationServices.register(
+ FileDocumentManager.class, new MockFileDocumentManagerImpl(null, null));
+ applicationServices.register(VirtualFileManager.class, mock(VirtualFileManager.class));
+ applicationServices.register(BlazeBuildService.class, new BlazeBuildService());
+ projectServices.register(ProjectScopeBuilder.class, new ProjectScopeBuilderImpl(project));
+ projectServices.register(ProjectViewManager.class, new MockProjectViewManager());
+ projectServices.register(
+ BlazeProjectDataManager.class, new BlazeProjectDataManagerImpl(project));
+
+ BlazeImportSettingsManager manager = new BlazeImportSettingsManager();
+ manager.setImportSettings(new BlazeImportSettings("", "", "", "", BuildSystem.Blaze));
+ projectServices.register(BlazeImportSettingsManager.class, manager);
+
+ facade =
+ new MockJavaPsiFacade(
+ project,
+ new MockPsiManager(project),
+ ImmutableList.of("com.google.example.Modified", "com.google.example.NotModified"));
+
+ projectServices.register(JavaPsiFacade.class, facade);
+ module = new MockModule(() -> {});
+ model = new BlazeAndroidModel(project, module, null, mock(SourceProvider.class), null, "", 0);
+ }
+
+ @Test
+ public void testIsClassFileOutOfDate() {
+ VirtualFile modifiedJarFile =
+ new MockJarVirtualFile(
+ "/build/com/google/example/libmodified.jar",
+ facade.getTimestamp("com.google.example.Modified") - 100);
+ VirtualFile notModifiedJarFile =
+ new MockJarVirtualFile(
+ "/build/com/google/example/libnotmodified.jar",
+ facade.getTimestamp("com.google.example.NotModified") + 100);
+ VirtualFile modifiedClassFile =
+ new MockClassVirtualFile(
+ "/build/com/google/example/libmodified.jar!/com/google/example/Modified.class",
+ modifiedJarFile);
+ VirtualFile notModifiedClassFile =
+ new MockClassVirtualFile(
+ "/build/com/google/example/libnotmodified.jar!/com/google/example/NotModified.class",
+ notModifiedJarFile);
+ assertThat(model.isClassFileOutOfDate(module, "com.google.example.Modified", modifiedClassFile))
+ .isTrue();
+ assertThat(
+ model.isClassFileOutOfDate(
+ module, "com.google.example.NotModified", notModifiedClassFile))
+ .isFalse();
+
+ BlazeBuildService.getInstance().buildProject(project);
+ assertThat(model.isClassFileOutOfDate(module, "com.google.example.Modified", modifiedClassFile))
+ .isFalse();
+ assertThat(
+ model.isClassFileOutOfDate(
+ module, "com.google.example.NotModified", notModifiedClassFile))
+ .isFalse();
+ }
+
+ private static class MockJavaPsiFacade extends JavaPsiFacadeImpl {
+ private ImmutableMap<String, PsiClass> classes;
+ private ImmutableMap<String, Long> timestamps;
+
+ MockJavaPsiFacade(
+ Project project, PsiManager psiManager, ImmutableCollection<String> classNames) {
+ super(project, psiManager, null, null);
+ ImmutableMap.Builder<String, PsiClass> classesBuilder = ImmutableMap.builder();
+ ImmutableMap.Builder<String, Long> timestampsBuilder = ImmutableMap.builder();
+ for (String className : classNames) {
+ VirtualFile virtualFile =
+ new MockVirtualFile("/src/" + className.replace('.', '/') + ".java");
+ PsiFile psiFile = mock(PsiFile.class);
+ when(psiFile.getVirtualFile()).thenReturn(virtualFile);
+ PsiClass psiClass = mock(PsiClass.class);
+ when(psiClass.getContainingFile()).thenReturn(psiFile);
+ classesBuilder.put(className, psiClass);
+ timestampsBuilder.put(className, virtualFile.getTimeStamp());
+ }
+ classes = classesBuilder.build();
+ timestamps = timestampsBuilder.build();
+ }
+
+ @Nullable
+ @Override
+ public PsiClass findClass(String qualifiedName, GlobalSearchScope scope) {
+ if (scope.equals(GlobalSearchScope.projectScope(getProject()))) {
+ return classes.get(qualifiedName);
+ }
+ return null;
+ }
+
+ long getTimestamp(String qualifiedName) {
+ return timestamps.get(qualifiedName);
+ }
+ }
+
+ private static class MockClassVirtualFile extends MockVirtualFile {
+ private static JarFileSystem fileSystem = mock(JarFileSystem.class);
+
+ MockClassVirtualFile(String name, VirtualFile jar) {
+ super(name);
+ when(fileSystem.getVirtualFileForJar(this)).thenReturn(jar);
+ }
+
+ @Override
+ public VirtualFileSystem getFileSystem() {
+ return fileSystem;
+ }
+ }
+
+ private static class MockJarVirtualFile extends MockVirtualFile {
+ private long timestamp;
+
+ MockJarVirtualFile(String name, long timestamp) {
+ super(name);
+ this.timestamp = timestamp;
+ }
+
+ @Override
+ public long getTimeStamp() {
+ return timestamp;
+ }
+ }
+
+ private static class MockProjectViewManager extends ProjectViewManager {
+ private ProjectViewSet set = new ProjectViewSet(ImmutableList.of());
+
+ @Nullable
+ @Override
+ public ProjectViewSet getProjectViewSet() {
+ return set;
+ }
+
+ @Nullable
+ @Override
+ public ProjectViewSet reloadProjectView(
+ BlazeContext context, WorkspacePathResolver workspacePathResolver) {
+ return null;
+ }
+ }
+}
diff --git a/aswb/BUILD b/aswb/BUILD
index 7772ebc..93784b5 100644
--- a/aswb/BUILD
+++ b/aswb/BUILD
@@ -8,20 +8,58 @@
"//build_defs:build_defs.bzl",
"intellij_plugin",
"merged_plugin_xml",
+ "optional_plugin_xml",
+ "plugin_deploy_zip",
+ "repackaged_files",
"stamped_plugin_xml",
)
+load(
+ "//build_defs:intellij_plugin_debug_target.bzl",
+ "intellij_plugin_debug_target",
+)
load("//:version.bzl", "VERSION")
+load("//intellij_platform_sdk:build_defs.bzl", "select_for_plugin_api")
+load(
+ "//testing:test_defs.bzl",
+ "intellij_integration_test_suite",
+ "intellij_unit_test_suite",
+)
merged_plugin_xml(
name = "merged_plugin_xml_common",
srcs = [
"src/META-INF/aswb.xml",
"//base:plugin_xml",
- "//cpp:plugin_xml",
"//java:plugin_xml",
],
)
+optional_plugin_xml(
+ name = "optional_cpp_xml",
+ module = "com.intellij.modules.cidr.lang",
+ plugin_xml = "//cpp:plugin_xml",
+)
+
+optional_plugin_xml(
+ name = "optional_ndk_xml",
+ module = "com.android.tools.ndk",
+ plugin_xml = ":merged_ndk_contents_xml",
+)
+
+merged_plugin_xml(
+ name = "merged_ndk_contents_xml",
+ srcs = ["src/META-INF/ndk-contents.xml"] + select_for_plugin_api({
+ "android-studio-2.3.1.0": [],
+ "android-studio-3.0.0.9": ["3.0/src/META-INF/ndk-workspace-contents.xml"],
+ }),
+)
+
+OPTIONAL_PLUGIN_XMLS = [
+ "//java:optional_xml",
+ ":optional_cpp_xml",
+ ":optional_ndk_xml",
+]
+
merged_plugin_xml(
name = "merged_plugin_xml",
srcs = [
@@ -35,7 +73,7 @@
changelog_file = "//:changelog",
include_product_code_in_stamp = True,
plugin_id = "com.google.idea.bazel.aswb",
- plugin_name = "Android Studio with Bazel",
+ plugin_name = "Bazel",
plugin_xml = ":merged_plugin_xml",
stamp_since_build = True,
version = VERSION,
@@ -43,17 +81,19 @@
java_library(
name = "aswb_lib",
- srcs = glob(["src/**/*.java"]),
+ srcs = glob(["src/**/*.java"]) + select_for_plugin_api({
+ "android-studio-2.3.1.0": glob(["2.3/src/**/*.java"]),
+ "android-studio-3.0.0.9": glob(["3.0/src/**/*.java"]),
+ }),
resources = glob(["resources/**/*"]),
- runtime_deps = [
- "//cpp",
- ],
deps = [
"//base",
"//common/experiments",
+ "//cpp",
"//intellij_platform_sdk:plugin_api",
"//java",
"//proto:proto_deps",
+ "//sdkcompat",
"@jsr305_annotations//jar",
],
)
@@ -68,15 +108,12 @@
],
)
-load(
- "//testing:test_defs.bzl",
- "intellij_integration_test_suite",
- "intellij_unit_test_suite",
-)
-
intellij_unit_test_suite(
name = "unit_tests",
- srcs = glob(["tests/unittests/**/*.java"]),
+ srcs = glob(["tests/unittests/**/*.java"]) + select_for_plugin_api({
+ "android-studio-2.3.1.0": [],
+ "android-studio-3.0.0.9": glob(["3.0/tests/unittests/**/*.java"]),
+ }),
test_package_root = "com.google.idea.blaze.android",
deps = [
":aswb_lib",
@@ -92,9 +129,20 @@
],
)
-intellij_integration_test_suite(
+test_suite(
name = "integration_tests",
- srcs = glob(["tests/integrationtests/**/*.java"]),
+ tests = [
+ ":NdkDependenciesTest",
+ ":normal_integration_tests",
+ ],
+)
+
+intellij_integration_test_suite(
+ name = "normal_integration_tests",
+ srcs = glob(
+ ["tests/integrationtests/**/*.java"],
+ exclude = ["tests/integrationtests/com/google/idea/blaze/android/plugin/NdkDependenciesTest.java"],
+ ),
platform_prefix = "AndroidStudio",
required_plugins = "com.google.idea.bazel.aswb",
test_package_root = "com.google.idea.blaze.android",
@@ -113,15 +161,69 @@
"//intellij_platform_sdk:plugin_api_for_tests",
"//java",
"//proto:proto_deps",
+ "//sdkcompat",
"@jsr305_annotations//jar",
"@junit//jar",
],
)
+intellij_integration_test_suite(
+ name = "NdkDependenciesTest",
+ srcs = ["tests/integrationtests/com/google/idea/blaze/android/plugin/NdkDependenciesTest.java"],
+ platform_prefix = "AndroidStudio",
+ required_plugins = "com.google.idea.bazel.aswb",
+ test_package_root = "com.google.idea.blaze.android",
+ runtime_deps = [
+ ":aswb_bazel",
+ "//cpp",
+ "//java",
+ ],
+ deps = [
+ ":aswb_lib",
+ ":integration_test_utils",
+ "//base",
+ "//base:integration_test_utils",
+ "//base:unit_test_utils",
+ "//intellij_platform_sdk:plugin_api_for_tests",
+ "//proto:proto_deps",
+ "@junit//jar",
+ ],
+)
+
intellij_plugin(
name = "aswb_bazel",
+ optional_plugin_xmls = OPTIONAL_PLUGIN_XMLS,
plugin_xml = ":stamped_plugin_xml",
deps = [
":aswb_lib",
],
)
+
+repackaged_files(
+ name = "plugin_jar",
+ srcs = [":aswb_bazel"],
+ prefix = "aswb/lib",
+)
+
+repackaged_files(
+ name = "aspect_directory",
+ srcs = ["//aspect:aspect_files"],
+ prefix = "aswb/aspect",
+)
+
+intellij_plugin_debug_target(
+ name = "aswb_bazel_dev",
+ deps = [
+ ":aspect_directory",
+ ":plugin_jar",
+ ],
+)
+
+plugin_deploy_zip(
+ name = "aswb_bazel_zip",
+ srcs = [
+ ":aspect_directory",
+ ":plugin_jar",
+ ],
+ zip_filename = "aswb_bazel.zip",
+)
diff --git a/aswb/src/META-INF/aswb.xml b/aswb/src/META-INF/aswb.xml
index 4bf7584..1faf854 100644
--- a/aswb/src/META-INF/aswb.xml
+++ b/aswb/src/META-INF/aswb.xml
@@ -51,14 +51,14 @@
<extensionPoints>
<extensionPoint qualifiedName="com.google.idea.blaze.BuildSystemAndroidJdkProvider" interface="com.google.idea.blaze.android.sync.BuildSystemAndroidJdkProvider"/>
+ <extensionPoint qualifiedName="com.google.idea.blaze.AndroidBinaryLaunchMethodsProvider" interface="com.google.idea.blaze.android.run.binary.BlazeAndroidBinaryLaunchMethodsProvider"/>
+ <extensionPoint qualifiedName="com.google.idea.blaze.AndroidTestLaunchMethodsProvider" interface="com.google.idea.blaze.android.run.test.BlazeAndroidTestLaunchMethodsProvider"/>
</extensionPoints>
<extensions defaultExtensionNs="com.google.idea.blaze">
<SyncPlugin implementation="com.google.idea.blaze.android.sync.BlazeAndroidSyncPlugin"/>
<SyncListener implementation="com.google.idea.blaze.android.sync.BlazeAndroidSyncListener"/>
- <SyncListener implementation="com.google.idea.blaze.android.cppimpl.BlazeNdkSupportEnabler"/>
<SyncListener implementation="com.google.idea.blaze.android.manifest.ManifestParser$ClearManifestParser"/>
- <RunConfigurationFactory implementation="com.google.idea.blaze.android.run.BlazeAndroidRunConfigurationFactory"/>
<JavaSyncAugmenter implementation="com.google.idea.blaze.android.sync.BlazeAndroidJavaSyncAugmenter"/>
<PrefetchFileSource implementation="com.google.idea.blaze.android.sync.AndroidPrefetchFileSource"/>
<BlazeCommandRunConfigurationHandlerProvider implementation="com.google.idea.blaze.android.run.binary.BlazeAndroidBinaryRunConfigurationHandlerProvider"/>
@@ -66,27 +66,14 @@
<BuildSystemAndroidJdkProvider implementation="com.google.idea.blaze.android.sync.BazelAndroidJdkProvider"/>
<BlazeTestEventsHandler implementation="com.google.idea.blaze.android.run.test.smrunner.BlazeAndroidTestEventsHandler"/>
<ProjectViewDefaultValueProvider implementation="com.google.idea.blaze.android.projectview.AndroidSdkPlatformSection$AndroidSdkPlatformProjectViewDefaultValueProvider"/>
+ <AndroidBinaryLaunchMethodsProvider implementation="com.google.idea.blaze.android.run.binary.BlazeAndroidBinaryLaunchMethodsProviderImpl"/>
+ <AndroidTestLaunchMethodsProvider implementation="com.google.idea.blaze.android.run.test.BlazeAndroidTestLaunchMethodsProviderImpl"/>
</extensions>
<extensions defaultExtensionNs="com.android.ide">
<sdkEventListener implementation="com.google.idea.blaze.android.sdk.AndroidSdkListener"/>
</extensions>
- <!-- BEGIN NDK SUPPORT -->
- <extensions defaultExtensionNs="com.intellij">
- <applicationService serviceInterface="com.google.idea.blaze.android.cppapi.BlazeNativeDebuggerIdProvider"
- serviceImplementation="com.google.idea.blaze.android.cppimpl.debug.BlazeNativeAndroidDebuggerIdProviderImpl"/>
- </extensions>
-
- <extensions defaultExtensionNs="cidr.debugger">
- <languageSupportFactory implementation="com.google.idea.blaze.android.cppimpl.debug.BlazeAndroidNativeDebuggerLanguageSupportFactory"/>
- </extensions>
-
- <extensions defaultExtensionNs="com.android.run">
- <androidDebugger implementation="com.google.idea.blaze.android.cppimpl.debug.BlazeAutoAndroidDebugger"/>
- </extensions>
- <!-- END NDK SUPPORT -->
-
<extensions defaultExtensionNs="com.google.idea.blaze">
<BlazeUserSettingsContributor implementation="com.google.idea.blaze.android.settings.BlazeAndroidUserSettingsContributor$BlazeAndroidUserSettingsProvider"/>
</extensions>
diff --git a/aswb/src/META-INF/ndk-contents.xml b/aswb/src/META-INF/ndk-contents.xml
new file mode 100644
index 0000000..d08d022
--- /dev/null
+++ b/aswb/src/META-INF/ndk-contents.xml
@@ -0,0 +1,36 @@
+<!--
+ ~ Copyright 2017 The Bazel Authors. All rights reserved.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<idea-plugin>
+ <depends>com.android.tools.ndk</depends>
+
+ <extensions defaultExtensionNs="com.google.idea.blaze">
+ <SyncListener implementation="com.google.idea.blaze.android.cppimpl.BlazeNdkSupportEnabler"/>
+ </extensions>
+
+ <extensions defaultExtensionNs="com.intellij">
+ <applicationService serviceInterface="com.google.idea.blaze.android.cppapi.BlazeNativeDebuggerIdProvider"
+ serviceImplementation="com.google.idea.blaze.android.cppimpl.debug.BlazeNativeAndroidDebuggerIdProviderImpl"/>
+ </extensions>
+
+ <extensions defaultExtensionNs="cidr.debugger">
+ <languageSupportFactory implementation="com.google.idea.blaze.android.cppimpl.debug.BlazeAndroidNativeDebuggerLanguageSupportFactory"/>
+ </extensions>
+
+ <extensions defaultExtensionNs="com.android.run">
+ <androidDebugger implementation="com.google.idea.blaze.android.cppimpl.debug.BlazeAutoAndroidDebugger"/>
+ </extensions>
+
+</idea-plugin>
\ No newline at end of file
diff --git a/aswb/src/com/google/idea/blaze/android/cppimpl/BlazeNdkSupportEnabler.java b/aswb/src/com/google/idea/blaze/android/cppimpl/BlazeNdkSupportEnabler.java
index 75e1a12..f806286 100644
--- a/aswb/src/com/google/idea/blaze/android/cppimpl/BlazeNdkSupportEnabler.java
+++ b/aswb/src/com/google/idea/blaze/android/cppimpl/BlazeNdkSupportEnabler.java
@@ -25,10 +25,12 @@
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.cpp.OCWorkspaceProvider;
+import com.google.idea.sdkcompat.cidr.OCWorkspaceModificationTrackersCompatUtils;
import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.TransactionGuard;
import com.intellij.openapi.project.Project;
import com.jetbrains.cidr.lang.workspace.OCWorkspace;
-import com.jetbrains.cidr.lang.workspace.OCWorkspaceManager;
final class BlazeNdkSupportEnabler extends SyncListener.Adapter {
@@ -56,29 +58,36 @@
* @param enabled if true, turn on C support in the IDE. If false, turn off C support in the IDE.
*/
private static void enableCSupportInIde(Project project, boolean enabled) {
- OCWorkspace workspace = OCWorkspaceManager.getWorkspace(project);
+ OCWorkspace workspace = OCWorkspaceProvider.getWorkspace(project);
+ if (workspace == null) {
+ // NDK workspace manager plugin isn't enabled.
+ return;
+ }
Boolean isCurrentlyEnabled = !LANGUAGE_SUPPORT_DISABLED.get(project, false);
if (isCurrentlyEnabled != enabled) {
NdkHelper.disableCppLanguageSupport(project, !enabled);
- rebuildSymbols(project, workspace);
+ rebuildSymbols(project);
}
}
- private static void rebuildSymbols(Project project, OCWorkspace workspace) {
- ApplicationManager.getApplication()
- .runReadAction(
- () -> {
- if (project.isDisposed()) {
- return;
- }
- // Notifying BuildSettingsChangeTracker in unitTestMode will leads to a dead lock.
- // See b/23087433 for more information.
- if (!ApplicationManager.getApplication().isUnitTestMode()) {
- workspace
- .getModificationTrackers()
- .getBuildSettingsChangesTracker()
- .incModificationCount();
- }
- });
+ private static void rebuildSymbols(Project project) {
+ TransactionGuard.getInstance()
+ .submitTransactionLater(
+ project,
+ () ->
+ ApplicationManager.getApplication().runReadAction(() -> doRebuildSymbols(project)));
+ }
+
+ private static void doRebuildSymbols(Project project) {
+ if (project.isDisposed()) {
+ return;
+ }
+ // Notifying BuildSettingsChangeTracker in unitTestMode will leads to a dead lock.
+ // See b/23087433 for more information.
+ if (!ApplicationManager.getApplication().isUnitTestMode()) {
+ OCWorkspaceModificationTrackersCompatUtils.getTrackers(project)
+ .getBuildSettingsChangesTracker()
+ .incModificationCount();
+ }
}
}
diff --git a/aswb/src/com/google/idea/blaze/android/npw/project/BlazeAndroidProjectPaths.java b/aswb/src/com/google/idea/blaze/android/npw/project/BlazeAndroidProjectPaths.java
new file mode 100644
index 0000000..6f797eb
--- /dev/null
+++ b/aswb/src/com/google/idea/blaze/android/npw/project/BlazeAndroidProjectPaths.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2017 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.android.npw.project;
+
+import com.android.SdkConstants;
+import com.android.builder.model.SourceProvider;
+import com.android.tools.idea.npw.project.AndroidProjectPaths;
+import com.android.tools.idea.npw.project.AndroidSourceSet;
+import com.google.common.collect.Iterables;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ModuleRootManager;
+import com.intellij.openapi.vfs.VfsUtilCore;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.JavaDirectoryService;
+import com.intellij.psi.PsiDirectory;
+import com.intellij.psi.PsiManager;
+import com.intellij.psi.PsiPackage;
+import java.io.File;
+import java.util.Collections;
+import java.util.List;
+import javax.annotation.Nullable;
+import org.jetbrains.android.facet.AndroidFacet;
+
+/**
+ * Project paths for a Blaze Android project.
+ *
+ * <p>We mostly just take whatever directory the user specified and put the new component there.
+ * Unlike Gradle, Blaze has no strict requirements regarding the structure of an Android project,
+ * but there are some common conventions:
+ *
+ * <pre>
+ * google3/
+ * |-java/com/google/foo/bar/... (module root)
+ * | |-BUILD
+ * | |-AndroidManifest.xml (manifest directory)
+ * | |-Baz.java (source directory of com.google.foo.bar.Baz)
+ * | |-Baz.aidl (aidl directory, option 1)
+ * | |-aidl/
+ * | | `-com/google/foo/bar/Baz.aidl (aidl directory, option 2)
+ * | `-res/... (res directory, one of the few things required by the build system)
+ * `-javatest/com/google/foo/bar/...
+ * |-BUILD
+ * `-BazTest.java (test directory of com.google.foo.bar.BazTest)
+ * </pre>
+ *
+ * However, this is also possible (package name unrelated to directory structure):
+ *
+ * <pre>
+ * google3/experimental/users/foo/my/own/project/
+ * |-Baz.java (com.google.foo.bar.Baz)
+ * `-BazTest.java (com.google.foo.bar.BazTest)
+ * </pre>
+ *
+ * So is this (versioned paths that aren't reflected by the package name):
+ *
+ * <pre>
+ * google3/third_party/com/google/foo/bar/
+ * |-v1/Baz.java (com.google.foo.bar.Baz)
+ * `-v2/Baz.java (com.google.foo.bar.Baz)
+ * </pre>
+ */
+public class BlazeAndroidProjectPaths implements AndroidProjectPaths {
+ @Nullable private File moduleRoot;
+ @Nullable private File srcDirectory;
+ @Nullable private File resDirectory;
+
+ @Nullable
+ @Override
+ public File getModuleRoot() {
+ return moduleRoot;
+ }
+
+ @Nullable
+ @Override
+ public File getSrcDirectory(@Nullable String packageName) {
+ return srcDirectory;
+ }
+
+ @Nullable
+ @Override
+ public File getTestDirectory(@Nullable String packageName) {
+ return srcDirectory;
+ }
+
+ @Nullable
+ @Override
+ public File getResDirectory() {
+ return resDirectory;
+ }
+
+ @Nullable
+ @Override
+ public File getAidlDirectory(@Nullable String packageName) {
+ return srcDirectory;
+ }
+
+ @Nullable
+ @Override
+ public File getManifestDirectory() {
+ return srcDirectory;
+ }
+
+ /**
+ * The new component wizard uses {@link AndroidSourceSet#getName()} for the default package name
+ * of the new component. If we can figure it out from the target directory here, then we can pass
+ * it to the new component wizard.
+ */
+ private static String getPackageName(Project project, VirtualFile targetDirectory) {
+ PsiDirectory psiDirectory = PsiManager.getInstance(project).findDirectory(targetDirectory);
+ if (psiDirectory == null) {
+ return null;
+ }
+ PsiPackage psiPackage = JavaDirectoryService.getInstance().getPackage(psiDirectory);
+ if (psiPackage == null) {
+ return null;
+ }
+ return psiPackage.getQualifiedName();
+ }
+
+ public static List<AndroidSourceSet> getSourceSets(
+ AndroidFacet androidFacet, @Nullable VirtualFile targetDirectory) {
+ Module module = androidFacet.getModule();
+ BlazeAndroidProjectPaths paths = new BlazeAndroidProjectPaths();
+ VirtualFile[] roots = ModuleRootManager.getInstance(module).getContentRoots();
+ if (roots.length > 0) {
+ paths.moduleRoot = VfsUtilCore.virtualToIoFile(roots[0]);
+ }
+
+ // We have a res dir if this happens to be a resource module.
+ SourceProvider sourceProvider = androidFacet.getMainSourceProvider();
+ paths.resDirectory = Iterables.getFirst(sourceProvider.getResDirectories(), null);
+
+ // If this happens to be a resource package,
+ // the module name (resource package) would be more descriptive than the facet name (Android).
+ // Otherwise, .workspace is still better than (Android).
+ String name = androidFacet.getModule().getName();
+ if (targetDirectory != null) {
+ String packageName = getPackageName(module.getProject(), targetDirectory);
+ if (packageName != null) {
+ name = packageName;
+ }
+ paths.srcDirectory = VfsUtilCore.virtualToIoFile(targetDirectory);
+ } else {
+ // People usually put the manifest file with their sources.
+ paths.srcDirectory = sourceProvider.getManifestFile().getParentFile();
+ }
+ if (paths.resDirectory == null) {
+ paths.resDirectory = new File(paths.srcDirectory, SdkConstants.FD_RES);
+ }
+ return Collections.singletonList(new AndroidSourceSet(name, paths));
+ }
+}
diff --git a/aswb/src/com/google/idea/blaze/android/project/BlazeBuildSystemService.java b/aswb/src/com/google/idea/blaze/android/project/BlazeBuildSystemService.java
index ac68a65..ce5ff5c 100644
--- a/aswb/src/com/google/idea/blaze/android/project/BlazeBuildSystemService.java
+++ b/aswb/src/com/google/idea/blaze/android/project/BlazeBuildSystemService.java
@@ -15,7 +15,8 @@
*/
package com.google.idea.blaze.android.project;
-import com.android.tools.idea.project.BuildSystemService;
+import com.android.tools.idea.npw.project.AndroidSourceSet;
+import com.google.idea.blaze.android.npw.project.BlazeAndroidProjectPaths;
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;
@@ -25,6 +26,7 @@
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.google.idea.sdkcompat.android.project.BuildSystemServiceAdapter;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
import com.intellij.openapi.module.Module;
@@ -32,9 +34,12 @@
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import java.io.File;
+import java.util.List;
+import javax.annotation.Nullable;
+import org.jetbrains.android.facet.AndroidFacet;
/** Blaze implementation of {@link BuildSystemService} for build system specific operations. */
-public class BlazeBuildSystemService extends BuildSystemService {
+public class BlazeBuildSystemService extends BuildSystemServiceAdapter {
@Override
public boolean isApplicable(Project project) {
return Blaze.isBlazeProject(project);
@@ -89,4 +94,20 @@
}
}
}
+
+ @Override
+ public String mergeBuildFiles(
+ String dependencies,
+ String destinationContents,
+ Project project,
+ @Nullable String supportLibVersionFilter) {
+ // TODO: check if necessary to implement.
+ return null;
+ }
+
+ @Override
+ public List<AndroidSourceSet> getSourceSets(
+ AndroidFacet facet, @Nullable VirtualFile targetDirectory) {
+ return BlazeAndroidProjectPaths.getSourceSets(facet, targetDirectory);
+ }
}
diff --git a/aswb/src/com/google/idea/blaze/android/resources/actions/BlazeCreateResourceFileDialog.java b/aswb/src/com/google/idea/blaze/android/resources/actions/BlazeCreateResourceFileDialog.java
index 6c132b0..dc57eab 100644
--- a/aswb/src/com/google/idea/blaze/android/resources/actions/BlazeCreateResourceFileDialog.java
+++ b/aswb/src/com/google/idea/blaze/android/resources/actions/BlazeCreateResourceFileDialog.java
@@ -15,10 +15,11 @@
*/
package com.google.idea.blaze.android.resources.actions;
+import com.android.SdkConstants;
import com.android.ide.common.resources.configuration.FolderConfiguration;
import com.android.resources.ResourceConstants;
import com.android.resources.ResourceFolderType;
-import com.android.tools.idea.res.ResourceNameValidator;
+import com.android.tools.idea.res.IdeResourceNameValidator;
import com.google.common.annotations.VisibleForTesting;
import com.intellij.CommonBundle;
import com.intellij.ide.actions.TemplateKindCombo;
@@ -221,7 +222,8 @@
if (typeName != null) {
ResourceFolderType type = ResourceFolderType.getFolderType(typeName);
if (type != null) {
- ResourceNameValidator validator = ResourceNameValidator.create(true, type);
+ IdeResourceNameValidator validator =
+ IdeResourceNameValidator.forFilename(type, SdkConstants.DOT_XML);
return validator.getErrorText(fileName);
}
}
diff --git a/aswb/src/com/google/idea/blaze/android/resources/actions/BlazeCreateXmlResourcePanel.java b/aswb/src/com/google/idea/blaze/android/resources/actions/BlazeCreateXmlResourcePanel.java
index cf4a6d8..2290928 100644
--- a/aswb/src/com/google/idea/blaze/android/resources/actions/BlazeCreateXmlResourcePanel.java
+++ b/aswb/src/com/google/idea/blaze/android/resources/actions/BlazeCreateXmlResourcePanel.java
@@ -17,7 +17,9 @@
import com.android.resources.ResourceFolderType;
import com.android.resources.ResourceType;
-import com.android.tools.idea.res.ResourceNameValidator;
+import com.android.tools.idea.res.IdeResourceNameValidator;
+import com.google.idea.sdkcompat.android.resources.actions.CreateXmlResourceDialogAdapter;
+import com.google.idea.sdkcompat.android.resources.actions.CreateXmlResourcePanelAdapter;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
import com.intellij.openapi.module.Module;
@@ -40,24 +42,22 @@
import java.util.List;
import java.util.Set;
import java.util.function.Function;
+import javax.annotation.Nullable;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JTextField;
import org.jetbrains.android.actions.CreateXmlResourceDialog;
-import org.jetbrains.android.actions.CreateXmlResourcePanel;
import org.jetbrains.android.actions.CreateXmlResourceSubdirPanel;
import org.jetbrains.android.actions.CreateXmlResourceSubdirPanel.Parent;
import org.jetbrains.android.util.AndroidResourceUtil;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
/**
* Embeddable UI for selecting how to create a new resource value (which XML file and directories to
* place it).
*/
-public class BlazeCreateXmlResourcePanel implements CreateXmlResourcePanel, Parent {
+public class BlazeCreateXmlResourcePanel extends CreateXmlResourcePanelAdapter implements Parent {
private JPanel myPanel;
private JTextField myNameField;
@@ -76,14 +76,14 @@
private JBLabel myDirectoriesLabel;
private CreateXmlResourceSubdirPanel mySubdirPanel;
- private ResourceNameValidator myResourceNameValidator;
+ private final IdeResourceNameValidator myResourceNameValidator;
@Nullable private VirtualFile myContextFile;
@Nullable private VirtualFile myResDirectory;
public BlazeCreateXmlResourcePanel(
- @NotNull Module module,
- @NotNull ResourceType resourceType,
- @NotNull ResourceFolderType folderType,
+ Module module,
+ ResourceType resourceType,
+ ResourceFolderType folderType,
@Nullable String resourceName,
@Nullable String resourceValue,
boolean chooseName,
@@ -91,7 +91,7 @@
boolean chooseFilename,
@Nullable VirtualFile defaultFile,
@Nullable VirtualFile contextFile,
- @NotNull Function<Module, ResourceNameValidator> nameValidatorFactory) {
+ Function<Module, IdeResourceNameValidator> nameValidatorFactory) {
setupUi();
setChangeNameVisible(false);
setChangeValueVisible(false);
@@ -178,7 +178,7 @@
}
@Override
- public void resetFromFile(@NotNull VirtualFile file, @NotNull Project project) {
+ public void resetFromFile(VirtualFile file, Project project) {
final VirtualFile parent = file.getParent();
if (parent == null) {
return;
@@ -247,16 +247,21 @@
return new ValidationInfo("choose directories", myDirectoriesPanel);
}
- return CreateXmlResourceDialog.checkIfResourceAlreadyExists(
- myModule.getProject(), resourceDir, resourceName, myResourceType, directoryNames, fileName);
+ return CreateXmlResourceDialogAdapter.checkIfResourceAlreadyExists(
+ myModule.getProject(),
+ resourceDir,
+ resourceName,
+ null,
+ myResourceType,
+ directoryNames,
+ fileName);
}
@Override
- public ResourceNameValidator getResourceNameValidator() {
+ public IdeResourceNameValidator getResourceNameValidatorCompat() {
return myResourceNameValidator;
}
- @NotNull
@Override
public Module getModule() {
return myModule;
diff --git a/aswb/src/com/google/idea/blaze/android/resources/actions/BlazeNewResourceCreationHandler.java b/aswb/src/com/google/idea/blaze/android/resources/actions/BlazeNewResourceCreationHandler.java
index 6bf7deb..c2b67aa 100644
--- a/aswb/src/com/google/idea/blaze/android/resources/actions/BlazeNewResourceCreationHandler.java
+++ b/aswb/src/com/google/idea/blaze/android/resources/actions/BlazeNewResourceCreationHandler.java
@@ -18,8 +18,9 @@
import com.android.ide.common.resources.configuration.FolderConfiguration;
import com.android.resources.ResourceFolderType;
import com.android.resources.ResourceType;
-import com.android.tools.idea.res.ResourceNameValidator;
+import com.android.tools.idea.res.IdeResourceNameValidator;
import com.google.idea.blaze.base.settings.Blaze;
+import com.google.idea.sdkcompat.android.resources.actions.NewResourceCreationHandlerAdapter;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
@@ -27,41 +28,37 @@
import com.intellij.psi.PsiDirectory;
import java.util.Collection;
import java.util.function.Function;
+import javax.annotation.Nullable;
import org.jetbrains.android.actions.CreateResourceDirectoryDialogBase;
import org.jetbrains.android.actions.CreateResourceFileDialogBase;
import org.jetbrains.android.actions.CreateTypedResourceFileAction;
import org.jetbrains.android.actions.CreateXmlResourcePanel;
-import org.jetbrains.android.actions.NewResourceCreationHandler;
import org.jetbrains.android.facet.AndroidFacet;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
/** Decides which create resource dialogs to use for Blaze projects. */
-public class BlazeNewResourceCreationHandler implements NewResourceCreationHandler {
+public class BlazeNewResourceCreationHandler extends NewResourceCreationHandlerAdapter {
@Override
- public boolean isApplicable(@NotNull Project project) {
+ public boolean isApplicable(Project project) {
return Blaze.isBlazeProject(project);
}
- @NotNull
@Override
public CreateResourceDirectoryDialogBase createNewResourceDirectoryDialog(
- @NotNull Project project,
+ Project project,
@Nullable Module module,
@Nullable ResourceFolderType resType,
@Nullable PsiDirectory resDirectory,
@Nullable DataContext dataContext,
- @NotNull CreateResourceDirectoryDialogBase.ValidatorFactory validatorFactory) {
+ CreateResourceDirectoryDialogBase.ValidatorFactory validatorFactory) {
return new BlazeCreateResourceDirectoryDialog(
project, module, resType, resDirectory, dataContext, validatorFactory);
}
- @NotNull
@Override
public CreateResourceFileDialogBase createNewResourceFileDialog(
- @NotNull AndroidFacet facet,
- @NotNull Collection<CreateTypedResourceFileAction> actions,
+ AndroidFacet facet,
+ Collection<CreateTypedResourceFileAction> actions,
@Nullable ResourceFolderType folderType,
@Nullable String filename,
@Nullable String rootElement,
@@ -70,7 +67,7 @@
boolean chooseModule,
@Nullable PsiDirectory resDirectory,
@Nullable DataContext dataContext,
- @NotNull CreateResourceFileDialogBase.ValidatorFactory validatorFactory) {
+ CreateResourceFileDialogBase.ValidatorFactory validatorFactory) {
return new BlazeCreateResourceFileDialog(
facet,
actions,
@@ -86,10 +83,10 @@
}
@Override
- public CreateXmlResourcePanel createNewResourceValuePanel(
- @NotNull Module module,
- @NotNull ResourceType resourceType,
- @NotNull ResourceFolderType folderType,
+ public CreateXmlResourcePanel createNewResourceValuePanelCompat(
+ Module module,
+ ResourceType resourceType,
+ ResourceFolderType folderType,
@Nullable String resourceName,
@Nullable String resourceValue,
boolean chooseName,
@@ -97,7 +94,7 @@
boolean chooseFilename,
@Nullable VirtualFile defaultFile,
@Nullable VirtualFile contextFile,
- @NotNull Function<Module, ResourceNameValidator> nameValidatorFactory) {
+ Function<Module, IdeResourceNameValidator> nameValidatorFactory) {
return new BlazeCreateXmlResourcePanel(
module,
resourceType,
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 eaff918..8609d14 100644
--- a/aswb/src/com/google/idea/blaze/android/run/BlazeAndroidRunConfigurationCommonState.java
+++ b/aswb/src/com/google/idea/blaze/android/run/BlazeAndroidRunConfigurationCommonState.java
@@ -23,7 +23,9 @@
import com.google.idea.blaze.android.cppapi.NdkSupport;
import com.google.idea.blaze.android.run.runner.BlazeAndroidRunConfigurationDebuggerManager;
import com.google.idea.blaze.android.run.runner.BlazeAndroidRunConfigurationDeployTargetManager;
+import com.google.idea.blaze.base.command.BlazeCommandName;
import com.google.idea.blaze.base.command.BlazeFlags;
+import com.google.idea.blaze.base.command.BlazeInvocationContext;
import com.google.idea.blaze.base.projectview.ProjectViewSet;
import com.google.idea.blaze.base.run.state.RunConfigurationFlagsState;
import com.google.idea.blaze.base.run.state.RunConfigurationState;
@@ -44,7 +46,8 @@
public class BlazeAndroidRunConfigurationCommonState implements RunConfigurationState {
private static final String DEPLOY_TARGET_STATES_TAG = "android-deploy-target-states";
private static final String DEBUGGER_STATES_TAG = "android-debugger-states";
- private static final String USER_FLAG_TAG = "blaze-user-flag";
+ private static final String USER_BLAZE_FLAG_TAG = "blaze-user-flag";
+ private static final String USER_EXE_FLAG_TAG = "blaze-user-exe-flag";
private static final String NATIVE_DEBUG_ATTR = "blaze-native-debug";
// We need to split "-c dbg" into two flags because we pass flags
@@ -56,15 +59,18 @@
private final BlazeAndroidRunConfigurationDeployTargetManager deployTargetManager;
private final BlazeAndroidRunConfigurationDebuggerManager debuggerManager;
- private final RunConfigurationFlagsState userFlags;
+ private final RunConfigurationFlagsState blazeFlags;
+ private final RunConfigurationFlagsState exeFlags;
private boolean nativeDebuggingEnabled = false;
public BlazeAndroidRunConfigurationCommonState(String buildSystemName, boolean isAndroidTest) {
this.deployTargetManager = new BlazeAndroidRunConfigurationDeployTargetManager(isAndroidTest);
this.debuggerManager = new BlazeAndroidRunConfigurationDebuggerManager(this);
- this.userFlags =
+ this.blazeFlags =
+ new RunConfigurationFlagsState(USER_BLAZE_FLAG_TAG, buildSystemName + " flags:");
+ this.exeFlags =
new RunConfigurationFlagsState(
- USER_FLAG_TAG, String.format("Custom %s build flags:", buildSystemName));
+ USER_EXE_FLAG_TAG, "Executable flags (mobile-install only):");
}
public BlazeAndroidRunConfigurationDeployTargetManager getDeployTargetManager() {
@@ -76,7 +82,11 @@
}
public RunConfigurationFlagsState getBlazeFlagsState() {
- return userFlags;
+ return blazeFlags;
+ }
+
+ public RunConfigurationFlagsState getExeFlagsState() {
+ return exeFlags;
}
public boolean isNativeDebuggingEnabled() {
@@ -88,9 +98,11 @@
}
public ImmutableList<String> getExpandedBuildFlags(
- Project project, ProjectViewSet projectViewSet) {
+ Project project, ProjectViewSet projectViewSet, BlazeCommandName command) {
return ImmutableList.<String>builder()
- .addAll(BlazeFlags.buildFlags(project, projectViewSet))
+ .addAll(
+ BlazeFlags.blazeFlags(
+ project, projectViewSet, command, BlazeInvocationContext.RunConfiguration))
.addAll(getBlazeFlagsState().getExpandedFlags())
.addAll(getNativeDebuggerFlags())
.build();
@@ -117,7 +129,8 @@
@Override
public void readExternal(Element element) throws InvalidDataException {
- userFlags.readExternal(element);
+ blazeFlags.readExternal(element);
+ exeFlags.readExternal(element);
setNativeDebuggingEnabled(Boolean.parseBoolean(element.getAttributeValue(NATIVE_DEBUG_ATTR)));
Element deployTargetStatesElement = element.getChild(DEPLOY_TARGET_STATES_TAG);
@@ -133,7 +146,8 @@
@Override
public void writeExternal(Element element) throws WriteExternalException {
- userFlags.writeExternal(element);
+ blazeFlags.writeExternal(element);
+ exeFlags.writeExternal(element);
element.setAttribute(NATIVE_DEBUG_ATTR, Boolean.toString(nativeDebuggingEnabled));
element.removeChildren(DEPLOY_TARGET_STATES_TAG);
@@ -155,12 +169,14 @@
private static class BlazeAndroidRunConfigurationCommonStateEditor
implements RunConfigurationStateEditor {
- private final RunConfigurationStateEditor userFlagsEditor;
+ private final RunConfigurationStateEditor blazeFlagsEditor;
+ private final RunConfigurationStateEditor exeFlagsEditor;
private final JCheckBox enableNativeDebuggingCheckBox;
BlazeAndroidRunConfigurationCommonStateEditor(
BlazeAndroidRunConfigurationCommonState state, Project project) {
- userFlagsEditor = state.userFlags.getEditor(project);
+ blazeFlagsEditor = state.blazeFlags.getEditor(project);
+ exeFlagsEditor = state.exeFlags.getEditor(project);
enableNativeDebuggingCheckBox = new JCheckBox("Enable native debugging", false);
}
@@ -168,7 +184,8 @@
public void resetEditorFrom(RunConfigurationState genericState) {
BlazeAndroidRunConfigurationCommonState state =
(BlazeAndroidRunConfigurationCommonState) genericState;
- userFlagsEditor.resetEditorFrom(state.userFlags);
+ blazeFlagsEditor.resetEditorFrom(state.blazeFlags);
+ exeFlagsEditor.resetEditorFrom(state.exeFlags);
enableNativeDebuggingCheckBox.setSelected(state.isNativeDebuggingEnabled());
}
@@ -176,13 +193,15 @@
public void applyEditorTo(RunConfigurationState genericState) {
BlazeAndroidRunConfigurationCommonState state =
(BlazeAndroidRunConfigurationCommonState) genericState;
- userFlagsEditor.applyEditorTo(state.userFlags);
+ blazeFlagsEditor.applyEditorTo(state.blazeFlags);
+ exeFlagsEditor.applyEditorTo(state.exeFlags);
state.setNativeDebuggingEnabled(enableNativeDebuggingCheckBox.isSelected());
}
@Override
public JComponent createComponent() {
- List<Component> result = Lists.newArrayList(userFlagsEditor.createComponent());
+ List<Component> result =
+ Lists.newArrayList(blazeFlagsEditor.createComponent(), exeFlagsEditor.createComponent());
if (NdkSupport.NDK_SUPPORT.getValue()) {
result.add(enableNativeDebuggingCheckBox);
}
@@ -191,7 +210,8 @@
@Override
public void setComponentEnabled(boolean enabled) {
- userFlagsEditor.setComponentEnabled(enabled);
+ blazeFlagsEditor.setComponentEnabled(enabled);
+ exeFlagsEditor.setComponentEnabled(enabled);
enableNativeDebuggingCheckBox.setEnabled(enabled);
}
}
diff --git a/aswb/src/com/google/idea/blaze/android/run/BlazeAndroidRunConfigurationFactory.java b/aswb/src/com/google/idea/blaze/android/run/BlazeAndroidRunConfigurationFactory.java
deleted file mode 100644
index 30955bf..0000000
--- a/aswb/src/com/google/idea/blaze/android/run/BlazeAndroidRunConfigurationFactory.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright 2016 The Bazel Authors. All rights reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.google.idea.blaze.android.run;
-
-import com.google.idea.blaze.base.ideinfo.TargetIdeInfo;
-import com.google.idea.blaze.base.ideinfo.TargetKey;
-import com.google.idea.blaze.base.model.BlazeProjectData;
-import com.google.idea.blaze.base.model.primitives.Kind;
-import com.google.idea.blaze.base.model.primitives.Label;
-import com.google.idea.blaze.base.run.BlazeCommandRunConfiguration;
-import com.google.idea.blaze.base.run.BlazeCommandRunConfigurationType;
-import com.google.idea.blaze.base.run.BlazeRunConfigurationFactory;
-import com.intellij.execution.configurations.ConfigurationFactory;
-import com.intellij.execution.configurations.RunConfiguration;
-import com.intellij.openapi.project.Project;
-
-/** Creates run configurations for android_binary and android_test. */
-public class BlazeAndroidRunConfigurationFactory extends BlazeRunConfigurationFactory {
- @Override
- public boolean handlesTarget(Project project, BlazeProjectData blazeProjectData, Label label) {
- TargetIdeInfo target = blazeProjectData.targetMap.get(TargetKey.forPlainTarget(label));
- return target != null && target.kindIsOneOf(Kind.ANDROID_BINARY, Kind.ANDROID_TEST);
- }
-
- @Override
- protected ConfigurationFactory getConfigurationFactory() {
- return BlazeCommandRunConfigurationType.getInstance().getFactory();
- }
-
- @Override
- public void setupConfiguration(RunConfiguration configuration, Label target) {
- final BlazeCommandRunConfiguration blazeConfig = (BlazeCommandRunConfiguration) configuration;
- blazeConfig.setTarget(target);
- blazeConfig.setGeneratedName();
- }
-}
diff --git a/aswb/src/com/google/idea/blaze/android/run/BlazeAndroidRunConfigurationValidationUtil.java b/aswb/src/com/google/idea/blaze/android/run/BlazeAndroidRunConfigurationValidationUtil.java
index 992f414..85bd239 100644
--- a/aswb/src/com/google/idea/blaze/android/run/BlazeAndroidRunConfigurationValidationUtil.java
+++ b/aswb/src/com/google/idea/blaze/android/run/BlazeAndroidRunConfigurationValidationUtil.java
@@ -15,7 +15,6 @@
*/
package com.google.idea.blaze.android.run;
-import com.android.tools.idea.gradle.util.Projects;
import com.android.tools.idea.run.ValidationError;
import com.google.common.collect.Lists;
import com.google.common.collect.Ordering;
@@ -25,6 +24,7 @@
import com.google.idea.blaze.base.projectview.ProjectViewSet;
import com.google.idea.blaze.base.run.targetfinder.TargetFinder;
import com.google.idea.blaze.base.settings.Blaze;
+import com.google.idea.sdkcompat.android.project.AndroidProjectInfoAdapter;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.configurations.RuntimeConfigurationError;
import com.intellij.execution.configurations.RuntimeConfigurationException;
@@ -73,7 +73,7 @@
return errors;
}
final Project project = module.getProject();
- if (Projects.requiredAndroidModelMissing(project)) {
+ if (AndroidProjectInfoAdapter.requiredAndroidModelMissing(project)) {
errors.add(ValidationError.fatal(SYNC_FAILED_ERR_MSG));
}
return errors;
diff --git a/aswb/src/com/google/idea/blaze/android/run/binary/BlazeAndroidBinaryApplicationIdProvider.java b/aswb/src/com/google/idea/blaze/android/run/binary/BlazeAndroidBinaryApplicationIdProvider.java
index d89f58e..40f7789 100644
--- a/aswb/src/com/google/idea/blaze/android/run/binary/BlazeAndroidBinaryApplicationIdProvider.java
+++ b/aswb/src/com/google/idea/blaze/android/run/binary/BlazeAndroidBinaryApplicationIdProvider.java
@@ -17,31 +17,24 @@
import com.android.tools.idea.run.ApkProvisionException;
import com.android.tools.idea.run.ApplicationIdProvider;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
import com.google.idea.blaze.android.run.deployinfo.BlazeAndroidDeployInfo;
+import com.google.idea.blaze.android.run.runner.BlazeApkBuildStep;
import com.intellij.openapi.application.ApplicationManager;
-import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Computable;
+import javax.annotation.Nullable;
import org.jetbrains.android.dom.manifest.Manifest;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
/** Application id provider for android_binary. */
public class BlazeAndroidBinaryApplicationIdProvider implements ApplicationIdProvider {
- private final Project project;
- private final ListenableFuture<BlazeAndroidDeployInfo> deployInfoFuture;
+ private final BlazeApkBuildStep buildStep;
- public BlazeAndroidBinaryApplicationIdProvider(
- Project project, ListenableFuture<BlazeAndroidDeployInfo> deployInfoFuture) {
- this.project = project;
- this.deployInfoFuture = deployInfoFuture;
+ public BlazeAndroidBinaryApplicationIdProvider(BlazeApkBuildStep buildStep) {
+ this.buildStep = buildStep;
}
- @NotNull
@Override
public String getPackageName() throws ApkProvisionException {
- BlazeAndroidDeployInfo deployInfo = Futures.get(deployInfoFuture, ApkProvisionException.class);
+ BlazeAndroidDeployInfo deployInfo = buildStep.getDeployInfo();
Manifest manifest = deployInfo.getMergedManifest();
if (manifest == null) {
throw new ApkProvisionException(
diff --git a/aswb/src/com/google/idea/blaze/android/run/binary/BlazeAndroidBinaryLaunchMethodsProvider.java b/aswb/src/com/google/idea/blaze/android/run/binary/BlazeAndroidBinaryLaunchMethodsProvider.java
new file mode 100644
index 0000000..8aca02d
--- /dev/null
+++ b/aswb/src/com/google/idea/blaze/android/run/binary/BlazeAndroidBinaryLaunchMethodsProvider.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2017 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.android.run.binary;
+
+import com.intellij.openapi.extensions.ExtensionPointName;
+import com.intellij.openapi.project.Project;
+import java.util.Arrays;
+import java.util.List;
+
+/** Provides a list of supported launch methods for android binaries. */
+public interface BlazeAndroidBinaryLaunchMethodsProvider {
+ ExtensionPointName<BlazeAndroidBinaryLaunchMethodsProvider> EP_NAME =
+ ExtensionPointName.create("com.google.idea.blaze.AndroidBinaryLaunchMethodsProvider");
+
+ static AndroidBinaryLaunchMethodComboEntry[] getAllLaunchMethods(Project project) {
+ return Arrays.stream(EP_NAME.getExtensions())
+ .flatMap(extension -> extension.getLaunchMethods(project).stream())
+ .toArray(AndroidBinaryLaunchMethodComboEntry[]::new);
+ }
+
+ List<AndroidBinaryLaunchMethodComboEntry> getLaunchMethods(Project project);
+
+ /** All possible binary launch methods. */
+ enum AndroidBinaryLaunchMethod {
+ NON_BLAZE,
+ MOBILE_INSTALL,
+ MOBILE_INSTALL_V2,
+ }
+
+ /** Launch methods wrapped for display in a combo box. */
+ class AndroidBinaryLaunchMethodComboEntry {
+ final AndroidBinaryLaunchMethod launchMethod;
+ private final String description;
+
+ public AndroidBinaryLaunchMethodComboEntry(
+ AndroidBinaryLaunchMethod launchMethod, String description) {
+ this.launchMethod = launchMethod;
+ this.description = description;
+ }
+
+ @Override
+ public String toString() {
+ return description;
+ }
+ }
+}
diff --git a/aswb/src/com/google/idea/blaze/android/run/binary/BlazeAndroidBinaryLaunchMethodsProviderImpl.java b/aswb/src/com/google/idea/blaze/android/run/binary/BlazeAndroidBinaryLaunchMethodsProviderImpl.java
new file mode 100644
index 0000000..8d02764
--- /dev/null
+++ b/aswb/src/com/google/idea/blaze/android/run/binary/BlazeAndroidBinaryLaunchMethodsProviderImpl.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2017 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.android.run.binary;
+
+import com.google.common.collect.ImmutableList;
+import com.google.idea.blaze.base.settings.Blaze;
+import com.intellij.openapi.project.Project;
+import java.util.List;
+
+/** Provides a list of supported launch methods from bazel and blaze for android binaries. */
+public class BlazeAndroidBinaryLaunchMethodsProviderImpl
+ implements BlazeAndroidBinaryLaunchMethodsProvider {
+ @Override
+ public List<AndroidBinaryLaunchMethodComboEntry> getLaunchMethods(Project project) {
+ String blaze = Blaze.buildSystemName(project);
+ return ImmutableList.of(
+ new AndroidBinaryLaunchMethodComboEntry(
+ AndroidBinaryLaunchMethod.NON_BLAZE, String.format("Run without using %s", blaze)),
+ new AndroidBinaryLaunchMethodComboEntry(
+ AndroidBinaryLaunchMethod.MOBILE_INSTALL,
+ String.format("Run with %s mobile-install", blaze.toLowerCase())));
+ }
+}
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 cb6b23c..a1abc9b 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
@@ -32,7 +32,6 @@
import com.android.tools.idea.run.tasks.LaunchTasksProvider;
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.deployinfo.BlazeAndroidDeployInfo;
import com.google.idea.blaze.android.run.deployinfo.BlazeApkProvider;
import com.google.idea.blaze.android.run.runner.BlazeAndroidDeviceSelector;
@@ -50,7 +49,6 @@
import java.util.Set;
import javax.annotation.Nullable;
import org.jetbrains.android.facet.AndroidFacet;
-import org.jetbrains.annotations.NotNull;
/** Run context for android_binary. */
class BlazeAndroidBinaryNormalBuildRunContext implements BlazeAndroidRunContext {
@@ -65,24 +63,23 @@
private final BlazeApkProvider apkProvider;
private final ApplicationIdProvider applicationIdProvider;
- public BlazeAndroidBinaryNormalBuildRunContext(
+ BlazeAndroidBinaryNormalBuildRunContext(
Project project,
AndroidFacet facet,
RunConfiguration runConfiguration,
ExecutionEnvironment env,
BlazeAndroidBinaryRunConfigurationState configState,
Label label,
- ImmutableList<String> buildFlags) {
+ ImmutableList<String> blazeFlags) {
this.project = project;
this.facet = facet;
this.runConfiguration = runConfiguration;
this.env = env;
this.configState = configState;
this.consoleProvider = new BlazeAndroidBinaryConsoleProvider(project);
- this.buildStep = new BlazeApkBuildStepNormalBuild(project, label, buildFlags);
- this.apkProvider = new BlazeApkProvider(project, buildStep.getDeployInfo());
- this.applicationIdProvider =
- new BlazeAndroidBinaryApplicationIdProvider(project, buildStep.getDeployInfo());
+ this.buildStep = new BlazeApkBuildStepNormalBuild(project, label, blazeFlags);
+ this.apkProvider = new BlazeApkProvider(project, buildStep);
+ this.applicationIdProvider = new BlazeAndroidBinaryApplicationIdProvider(buildStep);
}
@Override
@@ -94,11 +91,10 @@
}
@Override
- public void augmentLaunchOptions(@NotNull LaunchOptions.Builder options) {
+ public void augmentLaunchOptions(LaunchOptions.Builder options) {
options.setDeploy(true).setOpenLogcatAutomatically(true);
}
- @NotNull
@Override
public ConsoleProvider getConsoleProvider() {
return consoleProvider;
@@ -153,8 +149,12 @@
launchOptions.isDebug(),
UserIdHelper.getFlagsFromUserId(userId));
- BlazeAndroidDeployInfo deployInfo =
- Futures.get(buildStep.getDeployInfo(), ExecutionException.class);
+ BlazeAndroidDeployInfo deployInfo;
+ try {
+ deployInfo = buildStep.getDeployInfo();
+ } catch (ApkProvisionException e) {
+ throw new ExecutionException(e);
+ }
return BlazeAndroidBinaryApplicationLaunchTaskProvider.getApplicationLaunchTask(
project,
diff --git a/aswb/src/com/google/idea/blaze/android/run/binary/BlazeAndroidBinaryProgramRunner.java b/aswb/src/com/google/idea/blaze/android/run/binary/BlazeAndroidBinaryProgramRunner.java
index 37619aa..c64bb10 100644
--- a/aswb/src/com/google/idea/blaze/android/run/binary/BlazeAndroidBinaryProgramRunner.java
+++ b/aswb/src/com/google/idea/blaze/android/run/binary/BlazeAndroidBinaryProgramRunner.java
@@ -18,6 +18,7 @@
import com.android.tools.idea.fd.InstantRunUtils;
import com.android.tools.idea.run.AndroidSessionInfo;
import com.google.idea.blaze.android.run.BlazeAndroidRunConfigurationHandler;
+import com.google.idea.blaze.android.run.binary.BlazeAndroidBinaryLaunchMethodsProvider.AndroidBinaryLaunchMethod;
import com.google.idea.blaze.android.run.binary.mobileinstall.IncrementalInstallDebugExecutor;
import com.google.idea.blaze.android.run.binary.mobileinstall.IncrementalInstallRunExecutor;
import com.intellij.execution.ExecutionException;
@@ -30,12 +31,11 @@
import com.intellij.execution.runners.DefaultProgramRunner;
import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.execution.ui.RunContentDescriptor;
-import org.jetbrains.annotations.NotNull;
/** Program runner for {@link BlazeAndroidRunConfiguration} */
public class BlazeAndroidBinaryProgramRunner extends DefaultProgramRunner {
@Override
- public boolean canRun(@NotNull String executorId, @NotNull RunProfile profile) {
+ public boolean canRun(String executorId, RunProfile profile) {
BlazeAndroidRunConfigurationHandler handler =
BlazeAndroidRunConfigurationHandler.getHandlerFrom(profile);
if (handler == null) {
@@ -51,15 +51,17 @@
if (!(handler instanceof BlazeAndroidBinaryRunConfigurationHandler)) {
return false;
}
- return ((BlazeAndroidBinaryRunConfigurationHandler) handler).getState().mobileInstall()
+ AndroidBinaryLaunchMethod launchMethod =
+ ((BlazeAndroidBinaryRunConfigurationHandler) handler).getState().getLaunchMethod();
+ return (AndroidBinaryLaunchMethod.MOBILE_INSTALL.equals(launchMethod)
+ || AndroidBinaryLaunchMethod.MOBILE_INSTALL_V2.equals(launchMethod))
&& (IncrementalInstallDebugExecutor.EXECUTOR_ID.equals(executorId)
|| IncrementalInstallRunExecutor.EXECUTOR_ID.equals(executorId));
}
@Override
protected RunContentDescriptor doExecute(
- @NotNull final RunProfileState state, @NotNull final ExecutionEnvironment env)
- throws ExecutionException {
+ final RunProfileState state, final ExecutionEnvironment env) throws ExecutionException {
RunContentDescriptor descriptor = super.doExecute(state, env);
if (descriptor != null) {
ProcessHandler processHandler = descriptor.getProcessHandler();
@@ -84,7 +86,6 @@
}
@Override
- @NotNull
public String getRunnerId() {
return "AndroidProgramRunner";
}
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 f57d51f..0a26f27 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
@@ -26,6 +26,7 @@
import com.google.idea.blaze.android.run.runner.BlazeAndroidRunConfigurationRunner;
import com.google.idea.blaze.android.run.runner.BlazeAndroidRunContext;
import com.google.idea.blaze.android.sync.projectstructure.BlazeAndroidProjectStructureSyncer;
+import com.google.idea.blaze.base.command.BlazeCommandName;
import com.google.idea.blaze.base.model.primitives.Kind;
import com.google.idea.blaze.base.model.primitives.Label;
import com.google.idea.blaze.base.model.primitives.TargetExpression;
@@ -104,9 +105,14 @@
ProjectViewSet projectViewSet = ProjectViewManager.getInstance(project).getProjectViewSet();
BlazeAndroidRunConfigurationValidationUtil.validateExecution(module, facet, projectViewSet);
- ImmutableList<String> buildFlags =
- configState.getCommonState().getExpandedBuildFlags(project, projectViewSet);
- BlazeAndroidRunContext runContext = createRunContext(project, facet, environment, buildFlags);
+ ImmutableList<String> blazeFlags =
+ configState
+ .getCommonState()
+ .getExpandedBuildFlags(project, projectViewSet, BlazeCommandName.RUN);
+ ImmutableList<String> exeFlags =
+ ImmutableList.copyOf(configState.getCommonState().getExeFlagsState().getExpandedFlags());
+ BlazeAndroidRunContext runContext =
+ createRunContext(project, facet, environment, blazeFlags, exeFlags);
return new BlazeAndroidRunConfigurationRunner(
module,
@@ -120,14 +126,18 @@
Project project,
AndroidFacet facet,
ExecutionEnvironment env,
- ImmutableList<String> buildFlags) {
- if (configState.mobileInstall()) {
- return new BlazeAndroidBinaryMobileInstallRunContext(
- project, facet, configuration, env, configState, getLabel(), buildFlags);
- } else {
- return new BlazeAndroidBinaryNormalBuildRunContext(
- project, facet, configuration, env, configState, getLabel(), buildFlags);
+ ImmutableList<String> blazeFlags,
+ ImmutableList<String> exeFlags) {
+ switch (configState.getLaunchMethod()) {
+ case MOBILE_INSTALL:
+ case MOBILE_INSTALL_V2:
+ return new BlazeAndroidBinaryMobileInstallRunContext(
+ project, facet, configuration, env, configState, getLabel(), blazeFlags, exeFlags);
+ case NON_BLAZE:
+ return new BlazeAndroidBinaryNormalBuildRunContext(
+ project, facet, configuration, env, configState, getLabel(), blazeFlags);
}
+ throw new AssertionError();
}
@Override
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 8bc2076..0dff73d 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
@@ -20,6 +20,7 @@
import com.google.common.base.Strings;
import com.google.common.collect.Maps;
import com.google.idea.blaze.android.run.BlazeAndroidRunConfigurationCommonState;
+import com.google.idea.blaze.android.run.binary.BlazeAndroidBinaryLaunchMethodsProvider.AndroidBinaryLaunchMethod;
import com.google.idea.blaze.base.run.state.RunConfigurationState;
import com.google.idea.blaze.base.run.state.RunConfigurationStateEditor;
import com.intellij.openapi.project.Project;
@@ -38,11 +39,15 @@
public static final String DO_NOTHING = "do_nothing";
public static final String LAUNCH_DEEP_LINK = "launch_deep_link";
- private static final String MOBILE_INSTALL_ATTR = "blaze-mobile-install";
+ private static final String LAUNCH_METHOD_ATTR = "launch-method";
+ @Deprecated private static final String MOBILE_INSTALL_ATTR = "blaze-mobile-install";
+ // Remove once v2 becomes default.
private static final String USE_SPLIT_APKS_IF_POSSIBLE = "use-split-apks-if-possible";
+
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 AndroidBinaryLaunchMethod launchMethod = AndroidBinaryLaunchMethod.NON_BLAZE;
private boolean useSplitApksIfPossible = false;
private boolean useWorkProfileIfPresent = false;
private Integer userId;
@@ -65,12 +70,12 @@
return commonState;
}
- boolean mobileInstall() {
- return mobileInstall;
+ public AndroidBinaryLaunchMethod getLaunchMethod() {
+ return launchMethod;
}
- void setMobileInstall(boolean mobileInstall) {
- this.mobileInstall = mobileInstall;
+ void setLaunchMethod(AndroidBinaryLaunchMethod launchMethod) {
+ this.launchMethod = launchMethod;
}
public boolean useSplitApksIfPossible() {
@@ -137,7 +142,16 @@
setActivityClass(Strings.nullToEmpty(element.getAttributeValue(ACTIVITY_CLASS)));
String modeValue = element.getAttributeValue(MODE);
setMode(Strings.isNullOrEmpty(modeValue) ? LAUNCH_DEFAULT_ACTIVITY : modeValue);
- setMobileInstall(Boolean.parseBoolean(element.getAttributeValue(MOBILE_INSTALL_ATTR)));
+ String launchMethodAttribute = element.getAttributeValue(LAUNCH_METHOD_ATTR);
+ if (launchMethodAttribute != null) {
+ launchMethod = AndroidBinaryLaunchMethod.valueOf(launchMethodAttribute);
+ } else {
+ if (Boolean.parseBoolean(element.getAttributeValue(MOBILE_INSTALL_ATTR))) {
+ launchMethod = AndroidBinaryLaunchMethod.MOBILE_INSTALL;
+ } else {
+ launchMethod = AndroidBinaryLaunchMethod.NON_BLAZE;
+ }
+ }
setUseSplitApksIfPossible(
Boolean.parseBoolean(element.getAttributeValue(USE_SPLIT_APKS_IF_POSSIBLE)));
setUseWorkProfileIfPresent(Boolean.parseBoolean(element.getAttributeValue(WORK_PROFILE_ATTR)));
@@ -177,7 +191,7 @@
element.setAttribute(DEEP_LINK, deepLink);
element.setAttribute(ACTIVITY_CLASS, activityClass);
element.setAttribute(MODE, mode);
- element.setAttribute(MOBILE_INSTALL_ATTR, Boolean.toString(mobileInstall));
+ element.setAttribute(LAUNCH_METHOD_ATTR, launchMethod.name());
element.setAttribute(USE_SPLIT_APKS_IF_POSSIBLE, Boolean.toString(useSplitApksIfPossible));
element.setAttribute(WORK_PROFILE_ATTR, Boolean.toString(useWorkProfileIfPresent));
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 8ce6279..65697d1 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,6 +16,8 @@
package com.google.idea.blaze.android.run.binary;
import com.android.tools.idea.run.activity.ActivityLocatorUtils;
+import com.google.idea.blaze.android.run.binary.BlazeAndroidBinaryLaunchMethodsProvider.AndroidBinaryLaunchMethod;
+import com.google.idea.blaze.android.run.binary.BlazeAndroidBinaryLaunchMethodsProvider.AndroidBinaryLaunchMethodComboEntry;
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;
@@ -49,6 +51,7 @@
import javax.swing.BorderFactory;
import javax.swing.ButtonGroup;
import javax.swing.JCheckBox;
+import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
@@ -72,7 +75,7 @@
private JRadioButton launchNothingButton;
private JRadioButton launchDefaultButton;
private JRadioButton launchCustomButton;
- private JCheckBox mobileInstallCheckBox;
+ private JComboBox<AndroidBinaryLaunchMethodComboEntry> launchMethodComboBox;
private JCheckBox splitApksCheckBox;
private JCheckBox useWorkProfileIfPresentCheckBox;
private JLabel userIdLabel;
@@ -131,8 +134,12 @@
launchDefaultButton.addActionListener(listener);
launchNothingButton.addActionListener(listener);
- mobileInstallCheckBox.addActionListener(
- e -> splitApksCheckBox.setVisible(mobileInstallCheckBox.isSelected()));
+ launchMethodComboBox.addActionListener(
+ e ->
+ splitApksCheckBox.setVisible(
+ ((AndroidBinaryLaunchMethodComboEntry) launchMethodComboBox.getSelectedItem())
+ .launchMethod.equals(AndroidBinaryLaunchMethod.MOBILE_INSTALL) // v1 only
+ ));
useWorkProfileIfPresentCheckBox.addActionListener(e -> updateEnabledState());
}
@@ -155,12 +162,18 @@
activityField.getChildComponent().setText(state.getActivityClass());
}
- mobileInstallCheckBox.setSelected(state.mobileInstall());
+ for (int i = 0; i < launchMethodComboBox.getItemCount(); ++i) {
+ if (launchMethodComboBox.getItemAt(i).launchMethod.equals(state.getLaunchMethod())) {
+ launchMethodComboBox.setSelectedIndex(i);
+ break;
+ }
+ }
splitApksCheckBox.setSelected(state.useSplitApksIfPossible());
useWorkProfileIfPresentCheckBox.setSelected(state.useWorkProfileIfPresent());
userIdField.setValue(state.getUserId());
- splitApksCheckBox.setVisible(state.mobileInstall());
+ splitApksCheckBox.setVisible(
+ state.getLaunchMethod().equals(AndroidBinaryLaunchMethod.MOBILE_INSTALL));
updateEnabledState();
}
@@ -180,7 +193,9 @@
} else {
state.setMode(BlazeAndroidBinaryRunConfigurationState.DO_NOTHING);
}
- state.setMobileInstall(mobileInstallCheckBox.isSelected());
+ state.setLaunchMethod(
+ ((AndroidBinaryLaunchMethodComboEntry) launchMethodComboBox.getSelectedItem())
+ .launchMethod);
state.setUseSplitApksIfPossible(splitApksCheckBox.isSelected());
state.setUseWorkProfileIfPresent(useWorkProfileIfPresentCheckBox.isSelected());
}
@@ -199,7 +214,7 @@
launchNothingButton.setEnabled(componentEnabled);
launchDefaultButton.setEnabled(componentEnabled);
launchCustomButton.setEnabled(componentEnabled);
- mobileInstallCheckBox.setEnabled(componentEnabled);
+ launchMethodComboBox.setEnabled(componentEnabled);
splitApksCheckBox.setEnabled(componentEnabled);
useWorkProfileIfPresentCheckBox.setEnabled(componentEnabled);
}
@@ -423,10 +438,10 @@
null,
0,
false));
- mobileInstallCheckBox = new JCheckBox();
- mobileInstallCheckBox.setText(" Use mobile-install");
+ launchMethodComboBox =
+ new JComboBox<>(BlazeAndroidBinaryLaunchMethodsProvider.getAllLaunchMethods(project));
panel.add(
- mobileInstallCheckBox,
+ launchMethodComboBox,
new GridConstraints(
0,
0,
@@ -442,7 +457,7 @@
0,
false));
splitApksCheckBox = new JCheckBox();
- splitApksCheckBox.setText(" Use --split_apks where possible");
+ splitApksCheckBox.setText("Use --split_apks where possible");
panel.add(
splitApksCheckBox,
new GridConstraints(
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 da33631..ff1ab76 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
@@ -16,6 +16,7 @@
package com.google.idea.blaze.android.run.binary.mobileinstall;
import com.android.ddmlib.IDevice;
+import com.android.tools.idea.run.ApkProvisionException;
import com.android.tools.idea.run.ApplicationIdProvider;
import com.android.tools.idea.run.ConsolePrinter;
import com.android.tools.idea.run.ConsoleProvider;
@@ -29,10 +30,10 @@
import com.android.tools.idea.run.tasks.LaunchTasksProvider;
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.BlazeAndroidBinaryApplicationIdProvider;
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.BlazeAndroidBinaryLaunchMethodsProvider.AndroidBinaryLaunchMethod;
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.deployinfo.BlazeAndroidDeployInfo;
@@ -49,7 +50,6 @@
import java.util.Set;
import javax.annotation.Nullable;
import org.jetbrains.android.facet.AndroidFacet;
-import org.jetbrains.annotations.NotNull;
/** Run context for android_binary. */
public class BlazeAndroidBinaryMobileInstallRunContext implements BlazeAndroidRunContext {
@@ -70,7 +70,8 @@
ExecutionEnvironment env,
BlazeAndroidBinaryRunConfigurationState configState,
Label label,
- ImmutableList<String> buildFlags) {
+ ImmutableList<String> blazeFlags,
+ ImmutableList<String> exeFlags) {
this.project = project;
this.facet = facet;
this.runConfiguration = runConfiguration;
@@ -79,9 +80,14 @@
this.consoleProvider = new BlazeAndroidBinaryConsoleProvider(project);
this.buildStep =
new BlazeApkBuildStepMobileInstall(
- project, env, label, buildFlags, configState.useSplitApksIfPossible());
- this.applicationIdProvider =
- new BlazeAndroidBinaryApplicationIdProvider(project, buildStep.getDeployInfo());
+ project,
+ env,
+ label,
+ blazeFlags,
+ exeFlags,
+ configState.useSplitApksIfPossible(),
+ configState.getLaunchMethod().equals(AndroidBinaryLaunchMethod.MOBILE_INSTALL_V2));
+ this.applicationIdProvider = new BlazeAndroidBinaryApplicationIdProvider(buildStep);
}
@Override
@@ -93,11 +99,10 @@
public void augmentEnvironment(ExecutionEnvironment env) {}
@Override
- public void augmentLaunchOptions(@NotNull LaunchOptions.Builder options) {
+ public void augmentLaunchOptions(LaunchOptions.Builder options) {
options.setDeploy(false).setOpenLogcatAutomatically(true);
}
- @NotNull
@Override
public ConsoleProvider getConsoleProvider() {
return consoleProvider;
@@ -144,9 +149,12 @@
project,
launchOptions.isDebug(),
UserIdHelper.getFlagsFromUserId(userId));
-
- BlazeAndroidDeployInfo deployInfo =
- Futures.get(buildStep.getDeployInfo(), ExecutionException.class);
+ BlazeAndroidDeployInfo deployInfo;
+ try {
+ deployInfo = buildStep.getDeployInfo();
+ } catch (ApkProvisionException e) {
+ throw new ExecutionException(e);
+ }
return BlazeAndroidBinaryApplicationLaunchTaskProvider.getApplicationLaunchTask(
project,
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 582afea..6bec217 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
@@ -16,12 +16,12 @@
package com.google.idea.blaze.android.run.binary.mobileinstall;
import com.android.ddmlib.IDevice;
+import com.android.tools.idea.run.ApkProvisionException;
import com.android.tools.idea.run.DeviceFutures;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.util.concurrent.Futures;
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.run.deployinfo.BlazeAndroidDeployInfo;
import com.google.idea.blaze.android.run.deployinfo.BlazeApkDeployInfoProtoHelper;
@@ -59,21 +59,27 @@
private final Project project;
private final ExecutionEnvironment env;
private final Label label;
- private final ImmutableList<String> buildFlags;
+ private final ImmutableList<String> blazeFlags;
+ private final ImmutableList<String> exeFlags;
private final boolean useSplitApksIfPossible;
- private final SettableFuture<BlazeAndroidDeployInfo> deployInfoFuture = SettableFuture.create();
+ private final boolean mobileInstallV2;
+ private BlazeAndroidDeployInfo deployInfo = null;
public BlazeApkBuildStepMobileInstall(
Project project,
ExecutionEnvironment env,
Label label,
- ImmutableList<String> buildFlags,
- boolean useSplitApksIfPossible) {
+ ImmutableList<String> blazeFlags,
+ ImmutableList<String> exeFlags,
+ boolean useSplitApksIfPossible,
+ boolean mobileInstallV2) {
this.project = project;
this.env = env;
this.label = label;
- this.buildFlags = buildFlags;
+ this.blazeFlags = blazeFlags;
+ this.exeFlags = exeFlags;
this.useSplitApksIfPossible = useSplitApksIfPossible;
+ this.mobileInstallV2 = mobileInstallV2;
}
@Override
@@ -95,31 +101,50 @@
BlazeCommand.builder(
Blaze.getBuildSystemProvider(project).getBinaryPath(),
BlazeCommandName.MOBILE_INSTALL);
- command.addBlazeFlags(BlazeFlags.adbSerialFlags(device.getSerialNumber()));
+
+ if (mobileInstallV2) {
+ // Will become no-op once v2 is default.
+ command.addBlazeFlags("--mode=skylark");
+ }
+
+ if (mobileInstallV2) {
+ command.addExeFlags(BlazeFlags.DEVICE, device.getSerialNumber());
+ } else {
+ command.addBlazeFlags(
+ BlazeFlags.ADB_ARG + "-s ", BlazeFlags.ADB_ARG + device.getSerialNumber());
+ }
if (USE_SDK_ADB.getValue()) {
File adb = AndroidSdkUtils.getAdb(project);
if (adb != null) {
- command.addBlazeFlags(ImmutableList.of("--adb", adb.toString()));
+ if (mobileInstallV2) {
+ command.addExeFlags(BlazeFlags.ADB_PATH, adb.toString());
+ } else {
+ command.addBlazeFlags(BlazeFlags.ADB, adb.toString());
+ }
}
}
- // split-apks only supported for API level 23 and above
- if (useSplitApksIfPossible && device.getVersion().getApiLevel() >= 23) {
- command.addBlazeFlags(BlazeFlags.SPLIT_APKS);
- } else if (incrementalInstall) {
- command.addBlazeFlags(BlazeFlags.INCREMENTAL);
+ // These flags are obsolete in V2.
+ if (!mobileInstallV2) {
+ // split-apks only supported for API level 23 and above
+ if (useSplitApksIfPossible && device.getVersion().getApiLevel() >= 23) {
+ command.addBlazeFlags(BlazeFlags.SPLIT_APKS);
+ } else if (incrementalInstall) {
+ command.addBlazeFlags(BlazeFlags.INCREMENTAL);
+ }
}
WorkspaceRoot workspaceRoot = WorkspaceRoot.fromProject(project);
BlazeApkDeployInfoProtoHelper deployInfoHelper =
- new BlazeApkDeployInfoProtoHelper(project, buildFlags);
+ new BlazeApkDeployInfoProtoHelper(project, blazeFlags);
BuildResultHelper buildResultHelper = deployInfoHelper.getBuildResultHelper();
command
.addTargets(label)
- .addBlazeFlags(buildFlags)
- .addBlazeFlags(buildResultHelper.getBuildFlags());
+ .addBlazeFlags(blazeFlags)
+ .addBlazeFlags(buildResultHelper.getBuildFlags())
+ .addExeFlags(exeFlags);
SaveUtil.saveAllFiles();
int retVal =
@@ -138,12 +163,10 @@
return;
}
- BlazeAndroidDeployInfo deployInfo = deployInfoHelper.readDeployInfo(context);
+ deployInfo = deployInfoHelper.readDeployInfo(context);
if (deployInfo == null) {
IssueOutput.error("Could not read apk deploy info from build").submit(context);
- return;
}
- deployInfoFuture.set(deployInfo);
}
};
@@ -163,8 +186,12 @@
return context.shouldContinue();
}
- public ListenableFuture<BlazeAndroidDeployInfo> getDeployInfo() {
- return deployInfoFuture;
+ @Override
+ public BlazeAndroidDeployInfo getDeployInfo() throws ApkProvisionException {
+ if (deployInfo != null) {
+ return deployInfo;
+ }
+ throw new ApkProvisionException("Failed to read APK deploy info");
}
@Nullable
diff --git a/aswb/src/com/google/idea/blaze/android/run/deployinfo/BlazeApkDeployInfoProtoHelper.java b/aswb/src/com/google/idea/blaze/android/run/deployinfo/BlazeApkDeployInfoProtoHelper.java
index 98e2e87..bed1ef9 100644
--- a/aswb/src/com/google/idea/blaze/android/run/deployinfo/BlazeApkDeployInfoProtoHelper.java
+++ b/aswb/src/com/google/idea/blaze/android/run/deployinfo/BlazeApkDeployInfoProtoHelper.java
@@ -22,9 +22,11 @@
import com.google.idea.blaze.base.command.buildresult.BuildResultHelper;
import com.google.idea.blaze.base.command.info.BlazeInfo;
import com.google.idea.blaze.base.command.info.BlazeInfoRunner;
+import com.google.idea.blaze.base.model.BlazeProjectData;
import com.google.idea.blaze.base.model.primitives.WorkspaceRoot;
import com.google.idea.blaze.base.scope.BlazeContext;
import com.google.idea.blaze.base.settings.Blaze;
+import com.google.idea.blaze.base.sync.data.BlazeProjectDataManager;
import com.google.repackaged.devtools.build.lib.rules.android.deployinfo.AndroidDeployInfoOuterClass;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
@@ -49,8 +51,12 @@
this.project = project;
this.buildFlags = buildFlags;
this.workspaceRoot = WorkspaceRoot.fromProject(project);
+
+ BlazeProjectData projectData =
+ BlazeProjectDataManager.getInstance(project).getBlazeProjectData();
this.buildResultHelper =
- BuildResultHelper.forFiles(fileName -> fileName.endsWith(".deployinfo.pb"));
+ BuildResultHelper.forFiles(
+ projectData.blazeVersionData, fileName -> fileName.endsWith(".deployinfo.pb"));
}
public BuildResultHelper getBuildResultHelper() {
diff --git a/aswb/src/com/google/idea/blaze/android/run/deployinfo/BlazeApkProvider.java b/aswb/src/com/google/idea/blaze/android/run/deployinfo/BlazeApkProvider.java
index bdcc5d6..3f1f644 100644
--- a/aswb/src/com/google/idea/blaze/android/run/deployinfo/BlazeApkProvider.java
+++ b/aswb/src/com/google/idea/blaze/android/run/deployinfo/BlazeApkProvider.java
@@ -21,30 +21,26 @@
import com.android.tools.idea.run.ApkProvisionException;
import com.android.tools.idea.run.ValidationError;
import com.google.common.collect.ImmutableList;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
import com.google.idea.blaze.android.run.runner.AaptUtil;
+import com.google.idea.blaze.android.run.runner.BlazeApkBuildStep;
import com.intellij.openapi.project.Project;
import java.io.File;
import java.util.Collection;
import java.util.List;
-import org.jetbrains.annotations.NotNull;
/** Apk provider from deploy info proto */
public class BlazeApkProvider implements ApkProvider {
private final Project project;
- private final ListenableFuture<BlazeAndroidDeployInfo> deployInfoFuture;
+ private final BlazeApkBuildStep buildStep;
- public BlazeApkProvider(
- Project project, ListenableFuture<BlazeAndroidDeployInfo> deployInfoFuture) {
+ public BlazeApkProvider(Project project, BlazeApkBuildStep buildStep) {
this.project = project;
- this.deployInfoFuture = deployInfoFuture;
+ this.buildStep = buildStep;
}
- @NotNull
@Override
- public Collection<ApkInfo> getApks(@NotNull IDevice device) throws ApkProvisionException {
- BlazeAndroidDeployInfo deployInfo = Futures.get(deployInfoFuture, ApkProvisionException.class);
+ public Collection<ApkInfo> getApks(IDevice device) throws ApkProvisionException {
+ BlazeAndroidDeployInfo deployInfo = buildStep.getDeployInfo();
ImmutableList.Builder<ApkInfo> apkInfos = ImmutableList.builder();
for (File apk : deployInfo.getApksToDeploy()) {
apkInfos.add(new ApkInfo(apk, manifestPackageForApk(apk)));
@@ -52,8 +48,7 @@
return apkInfos.build();
}
- @NotNull
- private String manifestPackageForApk(@NotNull final File apk) throws ApkProvisionException {
+ private String manifestPackageForApk(final File apk) throws ApkProvisionException {
try {
return AaptUtil.getApkManifestPackage(project, apk);
} catch (AaptUtil.AaptUtilException e) {
@@ -66,7 +61,6 @@
}
}
- @NotNull
@Override
public List<ValidationError> validate() {
return ImmutableList.of();
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 bf56acb..66fe3f0 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
@@ -42,6 +42,7 @@
import com.google.idea.blaze.base.scope.scopes.IdeaLogScope;
import com.google.idea.blaze.base.scope.scopes.IssuesScope;
import com.google.idea.blaze.base.settings.BlazeUserSettings;
+import com.google.idea.blaze.base.settings.BlazeUserSettings.BlazeConsolePopupBehavior;
import com.intellij.execution.DefaultExecutionResult;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.ExecutionResult;
@@ -190,7 +191,10 @@
@Override
public boolean executeBeforeRunTask(ExecutionEnvironment env) {
final Project project = env.getProject();
- boolean suppressConsole = BlazeUserSettings.getInstance().getSuppressConsoleForRunAction();
+ BlazeConsolePopupBehavior consolePopupBehavior =
+ BlazeUserSettings.getInstance().getSuppressConsoleForRunAction()
+ ? BlazeConsolePopupBehavior.NEVER
+ : BlazeConsolePopupBehavior.ALWAYS;
return Scope.root(
context -> {
context
@@ -198,7 +202,7 @@
.push(new ExperimentScope())
.push(
new BlazeConsoleScope.Builder(project)
- .setSuppressConsole(suppressConsole)
+ .setPopupBehavior(consolePopupBehavior)
.build())
.push(new IdeaLogScope());
diff --git a/aswb/src/com/google/idea/blaze/android/run/runner/BlazeApkBuildStep.java b/aswb/src/com/google/idea/blaze/android/run/runner/BlazeApkBuildStep.java
index b437f7f..74a0f24 100644
--- a/aswb/src/com/google/idea/blaze/android/run/runner/BlazeApkBuildStep.java
+++ b/aswb/src/com/google/idea/blaze/android/run/runner/BlazeApkBuildStep.java
@@ -15,14 +15,18 @@
*/
package com.google.idea.blaze.android.run.runner;
+import com.android.tools.idea.run.ApkProvisionException;
+import com.google.idea.blaze.android.run.deployinfo.BlazeAndroidDeployInfo;
import com.google.idea.blaze.base.scope.BlazeContext;
/** Builds the APK. */
public interface BlazeApkBuildStep {
/**
- * Builds an optionally installs the APK.
+ * Builds and optionally installs the APK.
*
* @return True to continue the launch.
*/
boolean build(BlazeContext context, BlazeAndroidDeviceSelector.DeviceSession deviceSession);
+
+ BlazeAndroidDeployInfo getDeployInfo() throws ApkProvisionException;
}
diff --git a/aswb/src/com/google/idea/blaze/android/run/runner/BlazeApkBuildStepNormalBuild.java b/aswb/src/com/google/idea/blaze/android/run/runner/BlazeApkBuildStepNormalBuild.java
index 58daf4f..11c7f03 100644
--- a/aswb/src/com/google/idea/blaze/android/run/runner/BlazeApkBuildStepNormalBuild.java
+++ b/aswb/src/com/google/idea/blaze/android/run/runner/BlazeApkBuildStepNormalBuild.java
@@ -15,10 +15,10 @@
*/
package com.google.idea.blaze.android.run.runner;
+import com.android.tools.idea.run.ApkProvisionException;
import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
-import com.google.common.util.concurrent.SettableFuture;
import com.google.idea.blaze.android.run.deployinfo.BlazeAndroidDeployInfo;
import com.google.idea.blaze.android.run.deployinfo.BlazeApkDeployInfoProtoHelper;
import com.google.idea.blaze.base.async.executor.BlazeExecutor;
@@ -38,14 +38,13 @@
import com.intellij.execution.ExecutionException;
import com.intellij.openapi.project.Project;
import java.util.concurrent.CancellationException;
-import org.jetbrains.annotations.NotNull;
/** Builds the APK using normal blaze build. */
public class BlazeApkBuildStepNormalBuild implements BlazeApkBuildStep {
private final Project project;
private final Label label;
private final ImmutableList<String> buildFlags;
- private final SettableFuture<BlazeAndroidDeployInfo> deployInfoFuture = SettableFuture.create();
+ private BlazeAndroidDeployInfo deployInfo = null;
public BlazeApkBuildStepNormalBuild(
Project project, Label label, ImmutableList<String> buildFlags) {
@@ -60,7 +59,7 @@
final ScopedTask buildTask =
new ScopedTask(context) {
@Override
- protected void execute(@NotNull BlazeContext context) {
+ protected void execute(BlazeContext context) {
BlazeCommand.Builder command =
BlazeCommand.builder(
Blaze.getBuildSystemProvider(project).getBinaryPath(), BlazeCommandName.BUILD);
@@ -92,12 +91,10 @@
context.setHasError();
return;
}
- BlazeAndroidDeployInfo deployInfo = deployInfoHelper.readDeployInfo(context);
+ deployInfo = deployInfoHelper.readDeployInfo(context);
if (deployInfo == null) {
IssueOutput.error("Could not read apk deploy info from build").submit(context);
- return;
}
- deployInfoFuture.set(deployInfo);
}
};
@@ -117,7 +114,11 @@
return context.shouldContinue();
}
- public ListenableFuture<BlazeAndroidDeployInfo> getDeployInfo() {
- return deployInfoFuture;
+ @Override
+ public BlazeAndroidDeployInfo getDeployInfo() throws ApkProvisionException {
+ if (deployInfo != null) {
+ return deployInfo;
+ }
+ throw new ApkProvisionException("Failed to read APK deploy info");
}
}
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 92311c4..4915256 100644
--- a/aswb/src/com/google/idea/blaze/android/run/test/AndroidTestConsoleProvider.java
+++ b/aswb/src/com/google/idea/blaze/android/run/test/AndroidTestConsoleProvider.java
@@ -17,11 +17,11 @@
import com.android.tools.idea.run.ConsoleProvider;
import com.android.tools.idea.testartifacts.instrumented.AndroidTestConsoleProperties;
-import com.google.idea.blaze.base.run.smrunner.BlazeTestEventsHandler;
+import com.google.idea.blaze.base.run.BlazeCommandRunConfiguration;
+import com.google.idea.blaze.base.run.smrunner.BlazeTestUiSession;
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;
@@ -35,39 +35,43 @@
/** Console provider for android_test */
class AndroidTestConsoleProvider implements ConsoleProvider {
private final Project project;
- private final RunConfiguration runConfiguration;
+ private final BlazeCommandRunConfiguration runConfiguration;
private final BlazeAndroidTestRunConfigurationState configState;
- @Nullable private final BlazeTestEventsHandler testEventsHandler;
+ @Nullable private final BlazeTestUiSession testUiSession;
AndroidTestConsoleProvider(
Project project,
- RunConfiguration runConfiguration,
+ BlazeCommandRunConfiguration runConfiguration,
BlazeAndroidTestRunConfigurationState configState,
- @Nullable BlazeTestEventsHandler testEventsHandler) {
+ @Nullable BlazeTestUiSession testUiSession) {
this.project = project;
this.runConfiguration = runConfiguration;
this.configState = configState;
- this.testEventsHandler = testEventsHandler;
+ this.testUiSession = testUiSession;
}
@Override
public ConsoleView createAndAttach(Disposable parent, ProcessHandler handler, Executor executor)
throws ExecutionException {
- if (!configState.isRunThroughBlaze()) {
- return getStockConsoleProvider().createAndAttach(parent, handler, executor);
+ switch (configState.getLaunchMethod()) {
+ case BLAZE_TEST:
+ ConsoleView console = createBlazeTestConsole(executor);
+ console.attachToProcess(handler);
+ return console;
+ case NON_BLAZE:
+ case MOBILE_INSTALL:
+ return getStockConsoleProvider().createAndAttach(parent, handler, executor);
}
- ConsoleView console = createBlazeTestConsole(executor);
- console.attachToProcess(handler);
- return console;
+ throw new AssertionError();
}
private ConsoleView createBlazeTestConsole(Executor executor) {
- if (testEventsHandler == null || isDebugging(executor)) {
+ if (testUiSession == 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);
+ return SmRunnerUtils.getConsoleView(project, runConfiguration, executor, testUiSession);
}
private static boolean isDebugging(Executor executor) {
diff --git a/aswb/src/com/google/idea/blaze/android/run/test/BlazeAndroidTestApplicationIdProvider.java b/aswb/src/com/google/idea/blaze/android/run/test/BlazeAndroidTestApplicationIdProvider.java
index 71f37fd..9ad030c 100644
--- a/aswb/src/com/google/idea/blaze/android/run/test/BlazeAndroidTestApplicationIdProvider.java
+++ b/aswb/src/com/google/idea/blaze/android/run/test/BlazeAndroidTestApplicationIdProvider.java
@@ -18,31 +18,24 @@
import com.android.tools.idea.run.ApkProvisionException;
import com.android.tools.idea.run.ApplicationIdProvider;
import com.google.common.collect.Iterables;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
import com.google.idea.blaze.android.run.deployinfo.BlazeAndroidDeployInfo;
+import com.google.idea.blaze.android.run.runner.BlazeApkBuildStep;
import com.intellij.openapi.application.ApplicationManager;
-import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Computable;
+import javax.annotation.Nullable;
import org.jetbrains.android.dom.manifest.Manifest;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
/** Application id provider for android_binary. */
public class BlazeAndroidTestApplicationIdProvider implements ApplicationIdProvider {
- private final Project project;
- private final ListenableFuture<BlazeAndroidDeployInfo> deployInfoFuture;
+ private final BlazeApkBuildStep buildStep;
- public BlazeAndroidTestApplicationIdProvider(
- Project project, ListenableFuture<BlazeAndroidDeployInfo> deployInfoFuture) {
- this.project = project;
- this.deployInfoFuture = deployInfoFuture;
+ BlazeAndroidTestApplicationIdProvider(BlazeApkBuildStep buildStep) {
+ this.buildStep = buildStep;
}
- @NotNull
@Override
public String getPackageName() throws ApkProvisionException {
- BlazeAndroidDeployInfo deployInfo = Futures.get(deployInfoFuture, ApkProvisionException.class);
+ BlazeAndroidDeployInfo deployInfo = buildStep.getDeployInfo();
Manifest manifest = Iterables.getFirst(deployInfo.getAdditionalMergedManifests(), null);
if (manifest == null) {
// The application may not have a separate package,
@@ -61,7 +54,7 @@
@Nullable
@Override
public String getTestPackageName() throws ApkProvisionException {
- BlazeAndroidDeployInfo deployInfo = Futures.get(deployInfoFuture, ApkProvisionException.class);
+ BlazeAndroidDeployInfo deployInfo = buildStep.getDeployInfo();
Manifest manifest = deployInfo.getMergedManifest();
if (manifest == null) {
throw new ApkProvisionException(
diff --git a/aswb/src/com/google/idea/blaze/android/run/test/BlazeAndroidTestLaunchMethodsProvider.java b/aswb/src/com/google/idea/blaze/android/run/test/BlazeAndroidTestLaunchMethodsProvider.java
new file mode 100644
index 0000000..5258c85
--- /dev/null
+++ b/aswb/src/com/google/idea/blaze/android/run/test/BlazeAndroidTestLaunchMethodsProvider.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2017 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.android.run.test;
+
+import com.intellij.openapi.extensions.ExtensionPointName;
+import com.intellij.openapi.project.Project;
+import java.util.Arrays;
+import java.util.List;
+
+/** Provides a list of supported launch methods for android tests. */
+public interface BlazeAndroidTestLaunchMethodsProvider {
+ ExtensionPointName<BlazeAndroidTestLaunchMethodsProvider> EP_NAME =
+ ExtensionPointName.create("com.google.idea.blaze.AndroidTestLaunchMethodsProvider");
+
+ static AndroidTestLaunchMethodComboEntry[] getAllLaunchMethods(Project project) {
+ return Arrays.stream(EP_NAME.getExtensions())
+ .flatMap(extension -> extension.getLaunchMethods(project).stream())
+ .toArray(AndroidTestLaunchMethodComboEntry[]::new);
+ }
+
+ List<AndroidTestLaunchMethodComboEntry> getLaunchMethods(Project project);
+
+ /** All possible test launch methods. */
+ enum AndroidTestLaunchMethod {
+ NON_BLAZE,
+ BLAZE_TEST,
+ MOBILE_INSTALL,
+ }
+
+ /** Launch methods wrapped for display in a combo box. */
+ class AndroidTestLaunchMethodComboEntry {
+ final AndroidTestLaunchMethod launchMethod;
+ private final String description;
+
+ public AndroidTestLaunchMethodComboEntry(
+ AndroidTestLaunchMethod launchMethod, String description) {
+ this.launchMethod = launchMethod;
+ this.description = description;
+ }
+
+ @Override
+ public String toString() {
+ return description;
+ }
+ }
+}
diff --git a/aswb/src/com/google/idea/blaze/android/run/test/BlazeAndroidTestLaunchMethodsProviderImpl.java b/aswb/src/com/google/idea/blaze/android/run/test/BlazeAndroidTestLaunchMethodsProviderImpl.java
new file mode 100644
index 0000000..f60327b
--- /dev/null
+++ b/aswb/src/com/google/idea/blaze/android/run/test/BlazeAndroidTestLaunchMethodsProviderImpl.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2017 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.android.run.test;
+
+import com.google.common.collect.ImmutableList;
+import com.google.idea.blaze.base.settings.Blaze;
+import com.intellij.openapi.project.Project;
+import java.util.List;
+
+/** Provides a list of supported launch methods from bazel and blaze for android tests. */
+public class BlazeAndroidTestLaunchMethodsProviderImpl
+ implements BlazeAndroidTestLaunchMethodsProvider {
+ @Override
+ public List<AndroidTestLaunchMethodComboEntry> getLaunchMethods(Project project) {
+ String blaze = Blaze.buildSystemName(project);
+ return ImmutableList.of(
+ new AndroidTestLaunchMethodComboEntry(
+ AndroidTestLaunchMethod.NON_BLAZE, String.format("Run without using %s", blaze)),
+ new AndroidTestLaunchMethodComboEntry(
+ AndroidTestLaunchMethod.BLAZE_TEST,
+ String.format("Run with %s test", blaze.toLowerCase())));
+ }
+}
diff --git a/aswb/src/com/google/idea/blaze/android/run/test/BlazeAndroidTestRunConfigurationHandler.java b/aswb/src/com/google/idea/blaze/android/run/test/BlazeAndroidTestRunConfigurationHandler.java
index 4b94b3c..adece56 100644
--- a/aswb/src/com/google/idea/blaze/android/run/test/BlazeAndroidTestRunConfigurationHandler.java
+++ b/aswb/src/com/google/idea/blaze/android/run/test/BlazeAndroidTestRunConfigurationHandler.java
@@ -24,6 +24,7 @@
import com.google.idea.blaze.android.run.runner.BlazeAndroidRunConfigurationRunner;
import com.google.idea.blaze.android.run.runner.BlazeAndroidRunContext;
import com.google.idea.blaze.android.sync.projectstructure.BlazeAndroidProjectStructureSyncer;
+import com.google.idea.blaze.base.command.BlazeCommandName;
import com.google.idea.blaze.base.model.primitives.Kind;
import com.google.idea.blaze.base.model.primitives.Label;
import com.google.idea.blaze.base.model.primitives.TargetExpression;
@@ -102,9 +103,14 @@
ProjectViewSet projectViewSet = ProjectViewManager.getInstance(project).getProjectViewSet();
BlazeAndroidRunConfigurationValidationUtil.validateExecution(module, facet, projectViewSet);
- ImmutableList<String> buildFlags =
- configState.getCommonState().getExpandedBuildFlags(project, projectViewSet);
- BlazeAndroidRunContext runContext = createRunContext(project, facet, environment, buildFlags);
+ ImmutableList<String> blazeFlags =
+ configState
+ .getCommonState()
+ .getExpandedBuildFlags(project, projectViewSet, BlazeCommandName.TEST);
+ ImmutableList<String> exeFlags =
+ ImmutableList.copyOf(configState.getCommonState().getExeFlagsState().getExpandedFlags());
+ BlazeAndroidRunContext runContext =
+ createRunContext(project, facet, environment, blazeFlags, exeFlags);
return new BlazeAndroidRunConfigurationRunner(
module,
@@ -118,9 +124,10 @@
Project project,
AndroidFacet facet,
ExecutionEnvironment env,
- ImmutableList<String> buildFlags) {
+ ImmutableList<String> blazeFlags,
+ ImmutableList<String> exeFlags) {
return new BlazeAndroidTestRunContext(
- project, facet, configuration, env, configState, getLabel(), buildFlags);
+ project, facet, configuration, env, configState, getLabel(), blazeFlags, exeFlags);
}
@Override
diff --git a/aswb/src/com/google/idea/blaze/android/run/test/BlazeAndroidTestRunConfigurationState.java b/aswb/src/com/google/idea/blaze/android/run/test/BlazeAndroidTestRunConfigurationState.java
index 074b17b..e67388e 100644
--- a/aswb/src/com/google/idea/blaze/android/run/test/BlazeAndroidTestRunConfigurationState.java
+++ b/aswb/src/com/google/idea/blaze/android/run/test/BlazeAndroidTestRunConfigurationState.java
@@ -19,6 +19,7 @@
import com.google.common.base.Strings;
import com.google.common.collect.Maps;
import com.google.idea.blaze.android.run.BlazeAndroidRunConfigurationCommonState;
+import com.google.idea.blaze.android.run.test.BlazeAndroidTestLaunchMethodsProvider.AndroidTestLaunchMethod;
import com.google.idea.blaze.base.run.state.RunConfigurationState;
import com.google.idea.blaze.base.run.state.RunConfigurationStateEditor;
import com.intellij.openapi.project.Project;
@@ -34,7 +35,8 @@
/** State specific for the android test configuration. */
final class BlazeAndroidTestRunConfigurationState implements RunConfigurationState {
- private static final String RUN_THROUGH_BLAZE_ATTR = "blaze-run-through-blaze";
+ private static final String LAUNCH_METHOD_ATTR = "launch-method";
+ @Deprecated private static final String RUN_THROUGH_BLAZE_ATTR = "blaze-run-through-blaze";
public static final int TEST_ALL_IN_MODULE = 0;
public static final int TEST_ALL_IN_PACKAGE = 1;
@@ -60,8 +62,7 @@
private String packageName = "";
private String extraOptions = "";
- // Whether to delegate to 'blaze test'.
- private boolean runThroughBlaze;
+ private AndroidTestLaunchMethod launchMethod = AndroidTestLaunchMethod.NON_BLAZE;
private final BlazeAndroidRunConfigurationCommonState commonState;
@@ -74,12 +75,12 @@
}
@Contract(pure = true)
- boolean isRunThroughBlaze() {
- return runThroughBlaze;
+ AndroidTestLaunchMethod getLaunchMethod() {
+ return launchMethod;
}
- void setRunThroughBlaze(boolean runThroughBlaze) {
- this.runThroughBlaze = runThroughBlaze;
+ void setLaunchMethod(AndroidTestLaunchMethod launchMethod) {
+ this.launchMethod = launchMethod;
}
public int getTestingType() {
@@ -152,7 +153,17 @@
className = Strings.nullToEmpty(element.getAttributeValue(CLASS_NAME));
packageName = Strings.nullToEmpty(element.getAttributeValue(PACKAGE_NAME));
extraOptions = Strings.nullToEmpty(element.getAttributeValue(EXTRA_OPTIONS));
- runThroughBlaze = Boolean.parseBoolean(element.getAttributeValue(RUN_THROUGH_BLAZE_ATTR));
+
+ String launchMethodAttribute = element.getAttributeValue(LAUNCH_METHOD_ATTR);
+ if (launchMethodAttribute != null) {
+ launchMethod = AndroidTestLaunchMethod.valueOf(launchMethodAttribute);
+ } else {
+ if (Boolean.parseBoolean(element.getAttributeValue(RUN_THROUGH_BLAZE_ATTR))) {
+ launchMethod = AndroidTestLaunchMethod.BLAZE_TEST;
+ } else {
+ launchMethod = AndroidTestLaunchMethod.NON_BLAZE;
+ }
+ }
for (Map.Entry<String, String> entry : getLegacyValues(element).entrySet()) {
String value = entry.getValue();
@@ -187,7 +198,7 @@
public void writeExternal(Element element) throws WriteExternalException {
commonState.writeExternal(element);
- element.setAttribute(RUN_THROUGH_BLAZE_ATTR, Boolean.toString(runThroughBlaze));
+ element.setAttribute(LAUNCH_METHOD_ATTR, launchMethod.name());
element.setAttribute(TESTING_TYPE, Integer.toString(testingType));
element.setAttribute(INSTRUMENTATION_RUNNER_CLASS, instrumentationRunnerClass);
element.setAttribute(METHOD_NAME, methodName);
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 5e5e6c8..0bf5c63 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
@@ -21,9 +21,9 @@
import static com.android.tools.idea.testartifacts.instrumented.AndroidTestRunConfiguration.TEST_CLASS;
import static com.android.tools.idea.testartifacts.instrumented.AndroidTestRunConfiguration.TEST_METHOD;
+import com.google.idea.blaze.android.run.test.BlazeAndroidTestLaunchMethodsProvider.AndroidTestLaunchMethodComboEntry;
import com.google.idea.blaze.base.run.state.RunConfigurationState;
import com.google.idea.blaze.base.run.state.RunConfigurationStateEditor;
-import com.google.idea.blaze.base.settings.Blaze;
import com.google.idea.blaze.base.ui.UiUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.LabeledComponent;
@@ -38,7 +38,7 @@
import java.util.ResourceBundle;
import javax.swing.AbstractButton;
import javax.swing.ButtonGroup;
-import javax.swing.JCheckBox;
+import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
@@ -61,7 +61,7 @@
private JPanel panel;
private LabeledComponent<EditorTextField> runnerComponent;
private JBLabel labelTest;
- private JCheckBox runThroughBlazeTestCheckBox;
+ private JComboBox<AndroidTestLaunchMethodComboEntry> launchMethodComboBox;
private final JRadioButton[] testingType2RadioButton = new JRadioButton[4];
private boolean componentEnabled = true;
@@ -351,13 +351,10 @@
null,
0,
false));
- runThroughBlazeTestCheckBox = new JCheckBox();
- runThroughBlazeTestCheckBox.setText(
- String.format("Run through '%s test'", Blaze.buildSystemName(project).toLowerCase()));
- runThroughBlazeTestCheckBox.setToolTipText(
- String.format("Slower, but more truthful to %s", Blaze.buildSystemName(project)));
+ launchMethodComboBox =
+ new JComboBox<>(BlazeAndroidTestLaunchMethodsProvider.getAllLaunchMethods(project));
panel.add(
- runThroughBlazeTestCheckBox,
+ launchMethodComboBox,
new GridConstraints(
0,
0,
@@ -452,7 +449,8 @@
(BlazeAndroidTestRunConfigurationState) genericState;
commonStateEditor.applyEditorTo(state.getCommonState());
- state.setRunThroughBlaze(runThroughBlazeTestCheckBox.isSelected());
+ state.setLaunchMethod(
+ ((AndroidTestLaunchMethodComboEntry) launchMethodComboBox.getSelectedItem()).launchMethod);
state.setTestingType(getTestingType());
state.setClassName(classComponent.getComponent().getText());
@@ -467,8 +465,12 @@
(BlazeAndroidTestRunConfigurationState) genericState;
commonStateEditor.resetEditorFrom(state.getCommonState());
- runThroughBlazeTestCheckBox.setSelected(state.isRunThroughBlaze());
-
+ for (int i = 0; i < launchMethodComboBox.getItemCount(); ++i) {
+ if (launchMethodComboBox.getItemAt(i).launchMethod.equals(state.getLaunchMethod())) {
+ launchMethodComboBox.setSelectedIndex(i);
+ break;
+ }
+ }
updateButtonsAndLabelComponents(state.getTestingType());
packageComponent.getComponent().setText(state.getPackageName());
classComponent.getComponent().setText(state.getClassName());
@@ -498,7 +500,7 @@
methodComponent.setEnabled(componentEnabled);
runnerComponent.setEnabled(componentEnabled);
labelTest.setEnabled(componentEnabled);
- runThroughBlazeTestCheckBox.setEnabled(componentEnabled);
+ launchMethodComboBox.setEnabled(componentEnabled);
for (JComponent button : testingType2RadioButton) {
if (button != null) {
button.setEnabled(componentEnabled);
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 9962acf..b8d5966 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
@@ -32,7 +32,7 @@
import com.android.tools.idea.run.util.ProcessHandlerLaunchStatus;
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.run.binary.mobileinstall.BlazeApkBuildStepMobileInstall;
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 +41,12 @@
import com.google.idea.blaze.android.run.runner.BlazeAndroidRunContext;
import com.google.idea.blaze.android.run.runner.BlazeApkBuildStep;
import com.google.idea.blaze.android.run.runner.BlazeApkBuildStepNormalBuild;
+import com.google.idea.blaze.android.run.test.BlazeAndroidTestLaunchMethodsProvider.AndroidTestLaunchMethod;
import com.google.idea.blaze.base.model.primitives.Label;
import com.google.idea.blaze.base.run.BlazeCommandRunConfiguration;
-import com.google.idea.blaze.base.run.smrunner.BlazeTestEventsHandler;
+import com.google.idea.blaze.base.run.smrunner.BlazeTestUiSession;
+import com.google.idea.blaze.base.run.smrunner.TestUiSessionProvider;
+import com.google.idea.blaze.base.settings.Blaze;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.Executor;
import com.intellij.execution.executors.DefaultDebugExecutor;
@@ -54,7 +57,6 @@
import java.util.Set;
import javax.annotation.Nullable;
import org.jetbrains.android.facet.AndroidFacet;
-import org.jetbrains.annotations.NotNull;
/** Run context for android_test. */
class BlazeAndroidTestRunContext implements BlazeAndroidRunContext {
@@ -65,47 +67,55 @@
private final ExecutionEnvironment env;
private final BlazeAndroidTestRunConfigurationState configState;
private final Label label;
- private final ImmutableList<String> buildFlags;
+ private final ImmutableList<String> blazeFlags;
private final List<Runnable> launchTaskCompleteListeners = Lists.newArrayList();
private final ConsoleProvider consoleProvider;
- private final BlazeApkBuildStepNormalBuild buildStep;
+ private final BlazeApkBuildStep buildStep;
private final ApplicationIdProvider applicationIdProvider;
private final ApkProvider apkProvider;
- public BlazeAndroidTestRunContext(
+ BlazeAndroidTestRunContext(
Project project,
AndroidFacet facet,
BlazeCommandRunConfiguration runConfiguration,
ExecutionEnvironment env,
BlazeAndroidTestRunConfigurationState configState,
Label label,
- ImmutableList<String> buildFlags) {
+ ImmutableList<String> blazeFlags,
+ ImmutableList<String> exeFlags) {
this.project = project;
this.facet = facet;
this.runConfiguration = runConfiguration;
this.env = env;
this.label = label;
this.configState = configState;
- this.buildStep = new BlazeApkBuildStepNormalBuild(project, label, buildFlags);
- this.applicationIdProvider =
- new BlazeAndroidTestApplicationIdProvider(project, buildStep.getDeployInfo());
- this.apkProvider = new BlazeApkProvider(project, buildStep.getDeployInfo());
+ this.buildStep =
+ configState.getLaunchMethod().equals(AndroidTestLaunchMethod.MOBILE_INSTALL)
+ ? new BlazeApkBuildStepMobileInstall(
+ project, env, label, blazeFlags, exeFlags, false, true)
+ : new BlazeApkBuildStepNormalBuild(project, label, blazeFlags);
+ this.applicationIdProvider = new BlazeAndroidTestApplicationIdProvider(buildStep);
+ this.apkProvider = new BlazeApkProvider(project, buildStep);
- BlazeTestEventsHandler testEventsHandler = null;
- if (!isDebugging(env.getExecutor())) {
- testEventsHandler =
- BlazeTestEventsHandler.getHandlerForTarget(project, runConfiguration.getTarget());
- assert (testEventsHandler != null);
- this.buildFlags =
+ BlazeTestUiSession testUiSession =
+ canUseTestUi(env.getExecutor())
+ ? TestUiSessionProvider.createForTarget(project, runConfiguration.getTarget())
+ : null;
+ if (testUiSession != null) {
+ this.blazeFlags =
ImmutableList.<String>builder()
- .addAll(BlazeTestEventsHandler.getBlazeFlags(project))
- .addAll(buildFlags)
+ .addAll(testUiSession.getBlazeFlags())
+ .addAll(blazeFlags)
.build();
} else {
- this.buildFlags = buildFlags;
+ this.blazeFlags = blazeFlags;
}
this.consoleProvider =
- new AndroidTestConsoleProvider(project, runConfiguration, configState, testEventsHandler);
+ new AndroidTestConsoleProvider(project, runConfiguration, configState, testUiSession);
+ }
+
+ private static boolean canUseTestUi(Executor executor) {
+ return !isDebugging(executor);
}
private static boolean isDebugging(Executor executor) {
@@ -122,7 +132,7 @@
@Override
public void augmentLaunchOptions(LaunchOptions.Builder options) {
- options.setDeploy(!configState.isRunThroughBlaze());
+ options.setDeploy(!configState.getLaunchMethod().equals(AndroidTestLaunchMethod.BLAZE_TEST));
}
@Override
@@ -160,25 +170,35 @@
@Override
public ImmutableList<LaunchTask> getDeployTasks(IDevice device, LaunchOptions launchOptions)
throws ExecutionException {
- if (!configState.isRunThroughBlaze()) {
- BlazeAndroidDeployInfo deployInfo =
- Futures.get(buildStep.getDeployInfo(), ExecutionException.class);
- if (!deployInfo.getDataToDeploy().isEmpty()) {
- throw new ExecutionException(
- "This test target has data dependencies (defined in the 'data' attribute).\n"
- + "These can only be installed if the configuration is run through 'blaze test'.\n"
- + "Check the \"Run through 'blaze test'\" checkbox on your "
- + "run configuration and try again.");
- }
+ switch (configState.getLaunchMethod()) {
+ case NON_BLAZE:
+ BlazeAndroidDeployInfo deployInfo;
+ try {
+ deployInfo = buildStep.getDeployInfo();
+ } catch (ApkProvisionException e) {
+ throw new ExecutionException(e);
+ }
+ if (!deployInfo.getDataToDeploy().isEmpty()) {
+ throw new ExecutionException(
+ String.format(
+ "This test target has data dependencies (defined in the 'data' attribute).\n"
+ + "These can only be installed if the configuration is run through blaze.\n"
+ + "Choose \"Run with %1$s test\" on your run configuration and try again.",
+ Blaze.getBuildSystem(project).getLowerCaseName()));
+ }
+ // fall through
+ case BLAZE_TEST:
+ Collection<ApkInfo> apks;
+ try {
+ apks = apkProvider.getApks(device);
+ } catch (ApkProvisionException e) {
+ throw new ExecutionException(e);
+ }
+ return ImmutableList.of(new DeployApkTask(project, launchOptions, apks));
+ case MOBILE_INSTALL:
+ return ImmutableList.of();
}
-
- Collection<ApkInfo> apks;
- try {
- apks = apkProvider.getApks(device);
- } catch (ApkProvisionException e) {
- throw new ExecutionException(e);
- }
- return ImmutableList.of(new DeployApkTask(project, launchOptions, apks));
+ throw new AssertionError();
}
@Nullable
@@ -190,27 +210,35 @@
AndroidDebuggerState androidDebuggerState,
ProcessHandlerLaunchStatus processHandlerLaunchStatus)
throws ExecutionException {
- if (configState.isRunThroughBlaze()) {
- return new BlazeAndroidTestLaunchTask(
- project,
- label,
- buildFlags,
- new BlazeAndroidTestFilter(
- configState.getTestingType(),
- configState.getClassName(),
- configState.getMethodName(),
- configState.getPackageName()),
- this,
- launchOptions.isDebug());
+ switch (configState.getLaunchMethod()) {
+ case BLAZE_TEST:
+ return new BlazeAndroidTestLaunchTask(
+ project,
+ label,
+ blazeFlags,
+ new BlazeAndroidTestFilter(
+ configState.getTestingType(),
+ configState.getClassName(),
+ configState.getMethodName(),
+ configState.getPackageName()),
+ this,
+ launchOptions.isDebug());
+ case NON_BLAZE:
+ case MOBILE_INSTALL:
+ BlazeAndroidDeployInfo deployInfo;
+ try {
+ deployInfo = buildStep.getDeployInfo();
+ } catch (ApkProvisionException e) {
+ throw new ExecutionException(e);
+ }
+ return StockAndroidTestLaunchTask.getStockTestLaunchTask(
+ configState,
+ applicationIdProvider,
+ launchOptions.isDebug(),
+ deployInfo,
+ processHandlerLaunchStatus);
}
- BlazeAndroidDeployInfo deployInfo =
- Futures.get(buildStep.getDeployInfo(), ExecutionException.class);
- return StockAndroidTestLaunchTask.getStockTestLaunchTask(
- configState,
- applicationIdProvider,
- launchOptions.isDebug(),
- deployInfo,
- processHandlerLaunchStatus);
+ throw new AssertionError();
}
@Override
@@ -218,21 +246,25 @@
public DebugConnectorTask getDebuggerTask(
AndroidDebugger androidDebugger,
AndroidDebuggerState androidDebuggerState,
- @NotNull Set<String> packageIds,
+ Set<String> packageIds,
boolean monitorRemoteProcess)
throws ExecutionException {
- if (configState.isRunThroughBlaze()) {
- return new ConnectBlazeTestDebuggerTask(
- env.getProject(), androidDebugger, packageIds, applicationIdProvider, this);
+ switch (configState.getLaunchMethod()) {
+ case BLAZE_TEST:
+ return new ConnectBlazeTestDebuggerTask(
+ env.getProject(), androidDebugger, packageIds, applicationIdProvider, this);
+ case NON_BLAZE:
+ case MOBILE_INSTALL:
+ return androidDebugger.getConnectDebuggerTask(
+ env,
+ null,
+ packageIds,
+ facet,
+ androidDebuggerState,
+ runConfiguration.getType().getId(),
+ monitorRemoteProcess);
}
- return androidDebugger.getConnectDebuggerTask(
- env,
- null,
- packageIds,
- facet,
- androidDebuggerState,
- runConfiguration.getType().getId(),
- monitorRemoteProcess);
+ throw new AssertionError();
}
void onLaunchTaskComplete() {
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 a10773e..01f2b86 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
@@ -25,6 +25,7 @@
import com.android.tools.idea.testartifacts.instrumented.AndroidTestListener;
import com.google.common.collect.ImmutableList;
import com.google.idea.blaze.android.run.deployinfo.BlazeAndroidDeployInfo;
+import com.google.idea.blaze.android.run.test.BlazeAndroidTestLaunchMethodsProvider.AndroidTestLaunchMethod;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Computable;
@@ -37,6 +38,10 @@
final class StockAndroidTestLaunchTask implements LaunchTask {
private static final Logger LOG = Logger.getInstance(StockAndroidTestLaunchTask.class);
+ private static final String TEST_FILE_ARG = "testFile";
+ private static final String TEST_FILE_LOCATION_FORMAT =
+ "/data/local/tmp/deployment/%s/test/all_tests.txt";
+
private final BlazeAndroidTestRunConfigurationState configState;
private final String instrumentationTestRunner;
private final String testApplicationId;
@@ -165,6 +170,10 @@
final RemoteAndroidTestRunner runner =
new RemoteAndroidTestRunner(testApplicationId, instrumentationTestRunner, device);
+ if (configState.getLaunchMethod().equals(AndroidTestLaunchMethod.MOBILE_INSTALL)) {
+ runner.addInstrumentationArg(
+ TEST_FILE_ARG, String.format(TEST_FILE_LOCATION_FORMAT, testApplicationId));
+ }
switch (configState.getTestingType()) {
case BlazeAndroidTestRunConfigurationState.TEST_ALL_IN_MODULE:
break;
@@ -177,6 +186,9 @@
case BlazeAndroidTestRunConfigurationState.TEST_METHOD:
runner.setMethodName(configState.getClassName(), configState.getMethodName());
break;
+ default:
+ LOG.error(String.format("Unrecognized testing type: %d", configState.getTestingType()));
+ return false;
}
runner.setDebug(waitForDebugger);
runner.setRunOptions(configState.getExtraOptions());
diff --git a/aswb/src/com/google/idea/blaze/android/run/test/smrunner/BlazeAndroidTestEventsHandler.java b/aswb/src/com/google/idea/blaze/android/run/test/smrunner/BlazeAndroidTestEventsHandler.java
index 947a070..1a5bc9c 100644
--- a/aswb/src/com/google/idea/blaze/android/run/test/smrunner/BlazeAndroidTestEventsHandler.java
+++ b/aswb/src/com/google/idea/blaze/android/run/test/smrunner/BlazeAndroidTestEventsHandler.java
@@ -30,7 +30,6 @@
import com.intellij.psi.PsiMethod;
import com.intellij.util.io.URLUtil;
import java.util.Collection;
-import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -38,11 +37,11 @@
import javax.annotation.Nullable;
/** Provides java-specific methods needed by the SM-runner test UI. */
-public class BlazeAndroidTestEventsHandler extends BlazeTestEventsHandler {
+public class BlazeAndroidTestEventsHandler implements BlazeTestEventsHandler {
@Override
- protected EnumSet<Kind> handledKinds() {
- return EnumSet.of(Kind.ANDROID_TEST);
+ public boolean handlesKind(@Nullable Kind kind) {
+ return kind == Kind.ANDROID_TEST;
}
@Override
diff --git a/aswb/src/com/google/idea/blaze/android/sync/AndroidPrefetchFileSource.java b/aswb/src/com/google/idea/blaze/android/sync/AndroidPrefetchFileSource.java
index 821f098..930b5e3 100644
--- a/aswb/src/com/google/idea/blaze/android/sync/AndroidPrefetchFileSource.java
+++ b/aswb/src/com/google/idea/blaze/android/sync/AndroidPrefetchFileSource.java
@@ -20,10 +20,10 @@
import com.google.idea.blaze.base.model.BlazeProjectData;
import com.google.idea.blaze.base.prefetch.PrefetchFileSource;
import com.google.idea.blaze.base.projectview.ProjectViewSet;
+import com.google.idea.blaze.base.sync.projectview.ImportRoots;
import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoder;
import com.intellij.openapi.project.Project;
import java.io.File;
-import java.util.Collection;
import java.util.Set;
/** Adds the resource directories outside our source roots to prefetch. */
@@ -32,8 +32,9 @@
public void addFilesToPrefetch(
Project project,
ProjectViewSet projectViewSet,
+ ImportRoots importRoots,
BlazeProjectData blazeProjectData,
- Collection<File> files) {
+ Set<File> files) {
BlazeAndroidSyncData syncData = blazeProjectData.syncState.get(BlazeAndroidSyncData.class);
if (syncData == null) {
return;
@@ -46,7 +47,7 @@
}
@Override
- public Set<String> prefetchSrcFileExtensions() {
+ public Set<String> prefetchFileExtensions() {
return ImmutableSet.of("xml");
}
}
diff --git a/aswb/src/com/google/idea/blaze/android/sync/BlazeAndroidLibrarySource.java b/aswb/src/com/google/idea/blaze/android/sync/BlazeAndroidLibrarySource.java
index 73bb993..03f8bf0 100644
--- a/aswb/src/com/google/idea/blaze/android/sync/BlazeAndroidLibrarySource.java
+++ b/aswb/src/com/google/idea/blaze/android/sync/BlazeAndroidLibrarySource.java
@@ -17,10 +17,12 @@
import com.google.common.collect.ImmutableList;
import com.google.idea.blaze.android.sync.model.BlazeAndroidSyncData;
+import com.google.idea.blaze.base.ideinfo.LibraryArtifact;
import com.google.idea.blaze.base.model.BlazeLibrary;
import com.google.idea.blaze.base.model.BlazeProjectData;
import com.google.idea.blaze.base.sync.libraries.LibrarySource;
-import java.util.Collection;
+import com.google.idea.blaze.java.sync.model.BlazeJarLibrary;
+import java.util.List;
class BlazeAndroidLibrarySource extends LibrarySource.Adapter {
private final BlazeProjectData blazeProjectData;
@@ -30,7 +32,7 @@
}
@Override
- public Collection<BlazeLibrary> getLibraries() {
+ public List<BlazeLibrary> getLibraries() {
BlazeAndroidSyncData syncData = blazeProjectData.syncState.get(BlazeAndroidSyncData.class);
if (syncData == null) {
return ImmutableList.of();
@@ -39,6 +41,10 @@
if (syncData.importResult.resourceLibrary != null) {
libraries.add(syncData.importResult.resourceLibrary);
}
+ if (syncData.importResult.javacJar != null) {
+ libraries.add(
+ new BlazeJarLibrary(new LibraryArtifact(null, syncData.importResult.javacJar, null)));
+ }
return libraries.build();
}
}
diff --git a/aswb/src/com/google/idea/blaze/android/sync/BlazeNdkDependencySyncPlugin.java b/aswb/src/com/google/idea/blaze/android/sync/BlazeNdkDependencySyncPlugin.java
new file mode 100644
index 0000000..0177a1e
--- /dev/null
+++ b/aswb/src/com/google/idea/blaze/android/sync/BlazeNdkDependencySyncPlugin.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2017 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.android.sync;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableList;
+import com.google.idea.blaze.base.model.BlazeProjectData;
+import com.google.idea.blaze.base.model.primitives.LanguageClass;
+import com.google.idea.blaze.base.plugin.PluginUtils;
+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.sdkcompat.android.sync.BlazeNdkDependencySyncPluginCompat;
+import com.intellij.openapi.project.Project;
+import java.util.stream.Collectors;
+
+/**
+ * Returns an error during sync (with quick-fix) if NDK support is requested, but the required
+ * plugin dependencies aren't enabled.
+ */
+public final class BlazeNdkDependencySyncPlugin extends BlazeSyncPlugin.Adapter {
+
+ private static class PluginNameAndId {
+ final String name;
+ final String id;
+
+ PluginNameAndId(String pluginName, String pluginId) {
+ this.name = pluginName;
+ this.id = pluginId;
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+ }
+
+ private static final ImmutableList<PluginNameAndId> REQUIRED_PLUGINS =
+ ImmutableList.copyOf(
+ BlazeNdkDependencySyncPluginCompat.REQUIRED_PLUGINS
+ .entrySet()
+ .stream()
+ .map(e -> new PluginNameAndId(e.getKey(), e.getValue()))
+ .collect(Collectors.toList()));
+
+ /** Returns the IDs of the plugins required for NDK support. */
+ @VisibleForTesting
+ public static ImmutableList<String> getPluginsRequiredForNdkSupport() {
+ return ImmutableList.copyOf(
+ REQUIRED_PLUGINS.stream().map(plugin -> plugin.id).collect(Collectors.toList()));
+ }
+
+ @Override
+ public boolean validate(
+ Project project, BlazeContext context, BlazeProjectData blazeProjectData) {
+ if (!blazeProjectData.workspaceLanguageSettings.isLanguageActive(LanguageClass.C)) {
+ return true;
+ }
+ boolean missingPlugin = false;
+ for (PluginNameAndId plugin : REQUIRED_PLUGINS) {
+ if (!PluginUtils.isPluginEnabled(plugin.id)) {
+ missingPlugin = true;
+ notifyMissingPlugin(context, plugin);
+ }
+ }
+ return !missingPlugin;
+ }
+
+ private static void notifyMissingPlugin(BlazeContext context, PluginNameAndId plugin) {
+ String msg =
+ String.format(
+ "Plugin '%s' required for NDK support isn't enabled.\n"
+ + "Click here to install/enable it, then restart the IDE",
+ plugin.name);
+ IssueOutput.error(msg)
+ .navigatable(PluginUtils.installOrEnablePluginNavigable(plugin.id))
+ .submit(context);
+ }
+}
diff --git a/aswb/src/com/google/idea/blaze/android/sync/importer/BlazeAndroidWorkspaceImporter.java b/aswb/src/com/google/idea/blaze/android/sync/importer/BlazeAndroidWorkspaceImporter.java
index 3cb5351..f9228c6 100644
--- a/aswb/src/com/google/idea/blaze/android/sync/importer/BlazeAndroidWorkspaceImporter.java
+++ b/aswb/src/com/google/idea/blaze/android/sync/importer/BlazeAndroidWorkspaceImporter.java
@@ -33,6 +33,7 @@
import com.google.idea.blaze.base.ideinfo.TargetIdeInfo;
import com.google.idea.blaze.base.ideinfo.TargetKey;
import com.google.idea.blaze.base.ideinfo.TargetMap;
+import com.google.idea.blaze.base.model.primitives.Kind;
import com.google.idea.blaze.base.model.primitives.LanguageClass;
import com.google.idea.blaze.base.model.primitives.WorkspaceRoot;
import com.google.idea.blaze.base.projectview.ProjectViewSet;
@@ -45,6 +46,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -88,7 +90,7 @@
targetMap
.targets()
.stream()
- .filter(target -> target.kind.getLanguageClass() == LanguageClass.ANDROID)
+ .filter(target -> target.kind.languageClass == LanguageClass.ANDROID)
.filter(target -> target.androidIdeInfo != null)
.filter(importFilter::isSourceTarget)
.filter(target -> !importFilter.excludeTarget(target))
@@ -114,7 +116,20 @@
buildAndroidResourceModules(workspaceBuilder);
BlazeResourceLibrary resourceLibrary = createResourceLibrary(androidResourceModules);
- return new BlazeAndroidImportResult(androidResourceModules, resourceLibrary);
+ return new BlazeAndroidImportResult(
+ androidResourceModules, resourceLibrary, getJavacJar(targetMap.targets()));
+ }
+
+ private static ArtifactLocation getJavacJar(Collection<TargetIdeInfo> targets) {
+ return targets
+ .stream()
+ .filter(target -> target.kind == Kind.JAVA_TOOLCHAIN)
+ .map(
+ target ->
+ target.javaToolchainIdeInfo != null ? target.javaToolchainIdeInfo.javacJar : null)
+ .filter(Objects::nonNull)
+ .findFirst()
+ .orElse(null);
}
private void addSourceTarget(
diff --git a/aswb/src/com/google/idea/blaze/android/sync/model/BlazeAndroidImportResult.java b/aswb/src/com/google/idea/blaze/android/sync/model/BlazeAndroidImportResult.java
index b4750a2..0ac6cbd 100644
--- a/aswb/src/com/google/idea/blaze/android/sync/model/BlazeAndroidImportResult.java
+++ b/aswb/src/com/google/idea/blaze/android/sync/model/BlazeAndroidImportResult.java
@@ -16,6 +16,7 @@
package com.google.idea.blaze.android.sync.model;
import com.google.common.collect.ImmutableCollection;
+import com.google.idea.blaze.base.ideinfo.ArtifactLocation;
import java.io.Serializable;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
@@ -23,15 +24,18 @@
/** The result of a blaze import operation. */
@Immutable
public class BlazeAndroidImportResult implements Serializable {
- private static final long serialVersionUID = 3L;
+ private static final long serialVersionUID = 4L;
public final ImmutableCollection<AndroidResourceModule> androidResourceModules;
@Nullable public final BlazeResourceLibrary resourceLibrary;
+ @Nullable public final ArtifactLocation javacJar;
public BlazeAndroidImportResult(
ImmutableCollection<AndroidResourceModule> androidResourceModules,
- @Nullable BlazeResourceLibrary resourceLibrary) {
+ @Nullable BlazeResourceLibrary resourceLibrary,
+ @Nullable ArtifactLocation javacJar) {
this.androidResourceModules = androidResourceModules;
this.resourceLibrary = resourceLibrary;
+ this.javacJar = javacJar;
}
}
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 b89365f..79cd572 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
@@ -17,28 +17,36 @@
import com.android.builder.model.SourceProvider;
import com.android.sdklib.AndroidVersion;
-import com.android.tools.idea.model.AndroidModel;
import com.android.tools.idea.model.ClassJarProvider;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
import com.google.idea.blaze.android.manifest.ManifestParser;
+import com.google.idea.blaze.base.actions.BlazeBuildService;
+import com.google.idea.sdkcompat.android.model.AndroidModelAdapter;
import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.vfs.JarFileSystem;
import com.intellij.openapi.vfs.LocalFileSystem;
+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.PsiFile;
+import com.intellij.psi.search.GlobalSearchScope;
import java.io.File;
import java.util.List;
import java.util.Set;
+import javax.annotation.Nullable;
import org.jetbrains.android.dom.manifest.Manifest;
-import org.jetbrains.annotations.Nullable;
/**
* Contains Android-Blaze related state necessary for configuring an IDEA project based on a
* user-selected build variant.
*/
-public class BlazeAndroidModel implements AndroidModel {
+public class BlazeAndroidModel extends AndroidModelAdapter {
private Project project;
private final File rootDirPath;
private final SourceProvider sourceProvider;
@@ -172,9 +180,58 @@
}
@Override
- @Nullable
- public Long getLastBuildTimestamp(Project project) {
- // TODO(jvoung): Coordinate with blaze build actions to be able determine last build time.
- return null;
+ public boolean isClassFileOutOfDate(Module module, String fqcn, VirtualFile classFile) {
+ VirtualFile sourceFile =
+ ApplicationManager.getApplication()
+ .runReadAction(
+ (Computable<VirtualFile>)
+ () -> {
+ PsiClass psiClass =
+ JavaPsiFacade.getInstance(project)
+ .findClass(fqcn, GlobalSearchScope.projectScope(project));
+ if (psiClass == null) {
+ return null;
+ }
+ PsiFile psiFile = psiClass.getContainingFile();
+ if (psiFile == null) {
+ return null;
+ }
+ return psiFile.getVirtualFile();
+ });
+ if (sourceFile == null) {
+ return false;
+ }
+
+ // Edited but not yet saved?
+ if (FileDocumentManager.getInstance().isFileModified(sourceFile)) {
+ return true;
+ }
+
+ long sourceTimeStamp = sourceFile.getTimeStamp();
+ long buildTimeStamp = classFile.getTimeStamp();
+
+ if (classFile.getFileSystem() instanceof JarFileSystem) {
+ JarFileSystem jarFileSystem = (JarFileSystem) classFile.getFileSystem();
+ VirtualFile jarFile = jarFileSystem.getVirtualFileForJar(classFile);
+ if (jarFile != null) {
+ if (jarFile.getFileSystem() instanceof LocalFileSystem) {
+ // The virtual file timestamp could be stale since we don't watch this file.
+ buildTimeStamp = VfsUtilCore.virtualToIoFile(jarFile).lastModified();
+ } else {
+ buildTimeStamp = jarFile.getTimeStamp();
+ }
+ }
+ }
+
+ if (sourceTimeStamp > buildTimeStamp) {
+ // It's possible that the source file's timestamp has been updated, but the content remains
+ // same. In this case, blaze will not try to rebuild the jar, we have to also check whether
+ // the user recently clicked the build button. So they can at least manually get rid of the
+ // error.
+ Long projectBuildTimeStamp = BlazeBuildService.getLastBuildTimeStamp(project);
+ return projectBuildTimeStamp == null || sourceTimeStamp > projectBuildTimeStamp;
+ }
+
+ return false;
}
}
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
index 9641ced..cbad879 100644
--- 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
@@ -32,6 +32,7 @@
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.google.idea.sdkcompat.android.res.AppResourceRepositoryAdapter;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.module.Module;
@@ -44,20 +45,21 @@
import java.io.File;
import java.util.Collection;
import java.util.List;
+import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
-import org.jetbrains.annotations.Nullable;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import javax.annotation.Nullable;
/** Collects class jars from the user's build. */
public class BlazeClassJarProvider extends ClassJarProvider {
private final Project project;
- private AtomicBoolean pendingModuleJarsRefresh;
- private AtomicBoolean pendingDependencyJarsRefresh;
+ private final AtomicBoolean pendingJarsRefresh;
public BlazeClassJarProvider(final Project project) {
this.project = project;
- this.pendingModuleJarsRefresh = new AtomicBoolean(false);
- this.pendingDependencyJarsRefresh = new AtomicBoolean(false);
+ this.pendingJarsRefresh = new AtomicBoolean(false);
}
@Override
@@ -69,6 +71,7 @@
return null;
}
+ TargetMap targetMap = blazeProjectData.targetMap;
ArtifactLocationDecoder decoder = blazeProjectData.artifactLocationDecoder;
AndroidResourceModuleRegistry registry = AndroidResourceModuleRegistry.getInstance(project);
TargetIdeInfo target = blazeProjectData.targetMap.get(registry.getTargetKey(module));
@@ -84,9 +87,19 @@
String classNamePath = className.replace('.', File.separatorChar) + SdkConstants.DOT_CLASS;
+ List<LibraryArtifact> jarsToSearch = Lists.newArrayList(target.javaIdeInfo.jars);
+ jarsToSearch.addAll(
+ TransitiveDependencyMap.getInstance(project)
+ .getTransitiveDependencies(target.key)
+ .stream()
+ .map(targetMap::get)
+ .filter(Objects::nonNull)
+ .flatMap(BlazeClassJarProvider::getNonResourceJars)
+ .collect(Collectors.toList()));
+
List<File> missingClassJars = Lists.newArrayList();
- for (LibraryArtifact jar : target.javaIdeInfo.jars) {
- if (jar.classJar == null) {
+ for (LibraryArtifact jar : jarsToSearch) {
+ if (jar.classJar == null || jar.classJar.isSource()) {
continue;
}
File classJarFile = decoder.decode(jar.classJar);
@@ -104,10 +117,21 @@
}
}
- maybeRefreshJars(missingClassJars, pendingModuleJarsRefresh);
+ maybeRefreshJars(missingClassJars, pendingJarsRefresh);
return null;
}
+ private static Stream<LibraryArtifact> getNonResourceJars(TargetIdeInfo target) {
+ if (target.javaIdeInfo == null) {
+ return null;
+ }
+ Stream<LibraryArtifact> jars = target.javaIdeInfo.jars.stream();
+ if (target.androidIdeInfo != null) {
+ jars = jars.filter(jar -> !jar.equals(target.androidIdeInfo.resourceJar));
+ }
+ return jars;
+ }
+
@Nullable
private static VirtualFile findClassInJar(final VirtualFile classJar, String classNamePath) {
VirtualFile jarRoot = getJarRootForLocalFile(classJar);
@@ -137,38 +161,26 @@
return results;
}
- AppResourceRepository repository = AppResourceRepository.getAppResources(module, true);
+ AppResourceRepository repository = AppResourceRepositoryAdapter.getOrCreateInstance(module);
- 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.
+ // Add all import jars as external libraries.
+ JavaIdeInfo javaIdeInfo = dependencyTarget.javaIdeInfo;
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);
+ if (jar.classJar != null && jar.classJar.isSource()) {
VirtualFile classJar =
- VirtualFileSystemProvider.getInstance().getSystem().findFileByIoFile(classJarFile);
+ VirtualFileSystemProvider.getInstance()
+ .getSystem()
+ .findFileByIoFile(decoder.decode(jar.classJar));
if (classJar != null) {
results.add(classJar);
- } else if (classJarFile.exists()) {
- missingClassJars.add(classJarFile);
}
}
}
@@ -185,13 +197,13 @@
// 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.
+ AndroidIdeInfo androidIdeInfo = dependencyTarget.androidIdeInfo;
if (androidIdeInfo != null && repository != null) {
ResourceClassRegistry.get(module.getProject())
.addLibrary(repository, androidIdeInfo.resourceJavaPackage);
}
}
- maybeRefreshJars(missingClassJars, pendingDependencyJarsRefresh);
return results;
}
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 c4eb537..17b2fb7 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
@@ -67,8 +67,8 @@
return null;
}
- String androidSdk = projectViewSet.getScalarValue(AndroidSdkPlatformSection.KEY);
- Integer androidMinSdk = projectViewSet.getScalarValue(AndroidMinSdkSection.KEY);
+ String androidSdk = projectViewSet.getScalarValue(AndroidSdkPlatformSection.KEY).orElse(null);
+ Integer androidMinSdk = projectViewSet.getScalarValue(AndroidMinSdkSection.KEY).orElse(null);
if (androidSdk == null) {
ProjectViewFile projectViewFile = projectViewSet.getTopLevelProjectViewFile();
diff --git a/aswb/tests/integrationtests/com/google/idea/blaze/android/plugin/NdkDependenciesTest.java b/aswb/tests/integrationtests/com/google/idea/blaze/android/plugin/NdkDependenciesTest.java
new file mode 100644
index 0000000..88e15b1
--- /dev/null
+++ b/aswb/tests/integrationtests/com/google/idea/blaze/android/plugin/NdkDependenciesTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2017 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.android.plugin;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.idea.blaze.android.settings.BlazeAndroidUserSettings;
+import com.google.idea.blaze.android.sync.BlazeNdkDependencySyncPlugin;
+import com.google.idea.blaze.base.BlazeIntegrationTestCase;
+import com.google.idea.blaze.base.plugin.PluginUtils;
+import com.google.idea.testing.DisablePluginsTestRule;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Test that ASwB successfully loads when the NDK plugins aren't enabled. Must be run as a
+ * stand-alone test, because it requires its own {@link Application}, and only one such object can
+ * be constructed per test suite.
+ */
+@RunWith(JUnit4.class)
+public class NdkDependenciesTest extends BlazeIntegrationTestCase {
+
+ @ClassRule
+ public static TestRule setDisabledPlugins =
+ new DisablePluginsTestRule(BlazeNdkDependencySyncPlugin.getPluginsRequiredForNdkSupport());
+
+ @Test
+ public void testNdkPluginsInstalled() {
+ assertThat(PluginUtils.isPluginInstalled("com.android.tools.ndk")).isTrue();
+ assertThat(PluginUtils.isPluginInstalled("com.google.idea.bazel.aswb")).isTrue();
+ }
+
+ @Test
+ public void testPluginLoadsWithoutNdkPlugins() {
+ assertThat(PluginUtils.isPluginEnabled("com.android.tools.ndk")).isFalse();
+ assertThat(PluginUtils.isPluginEnabled("com.google.idea.bazel.aswb")).isTrue();
+
+ // Plugins with classloader failures can be spuriously marked as enabled. Also test that
+ // ASwB plugin classes are actually loaded.
+ assertThat(BlazeAndroidUserSettings.getInstance()).isNotNull();
+ }
+}
diff --git a/aswb/tests/integrationtests/com/google/idea/blaze/android/prefetch/AswbPrefetchFileSourceTest.java b/aswb/tests/integrationtests/com/google/idea/blaze/android/prefetch/AswbPrefetchFileSourceTest.java
new file mode 100644
index 0000000..85fe799
--- /dev/null
+++ b/aswb/tests/integrationtests/com/google/idea/blaze/android/prefetch/AswbPrefetchFileSourceTest.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2017 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.android.prefetch;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.idea.blaze.base.BlazeIntegrationTestCase;
+import com.google.idea.blaze.base.prefetch.PrefetchFileSource;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Test for file extensions prefetched in ASwB. */
+@RunWith(JUnit4.class)
+public class AswbPrefetchFileSourceTest extends BlazeIntegrationTestCase {
+
+ @Test
+ public void testPrefetchedExtensions() {
+ assertThat(PrefetchFileSource.getAllPrefetchFileExtensions())
+ .containsExactly(
+ "java", "proto", "c", "cc", "cpp", "cxx", "c++", "C", "h", "hh", "hpp", "hxx", "inc",
+ "xml");
+ }
+}
diff --git a/aswb/tests/integrationtests/com/google/idea/blaze/android/prefetch/CPrefetchFileSourceTest.java b/aswb/tests/integrationtests/com/google/idea/blaze/android/prefetch/CPrefetchFileSourceTest.java
new file mode 100644
index 0000000..354f1e3
--- /dev/null
+++ b/aswb/tests/integrationtests/com/google/idea/blaze/android/prefetch/CPrefetchFileSourceTest.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2017 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.android.prefetch;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.base.Joiner;
+import com.google.idea.blaze.base.BlazeIntegrationTestCase;
+import com.google.idea.blaze.base.ideinfo.ArtifactLocation;
+import com.google.idea.blaze.base.ideinfo.CIdeInfo;
+import com.google.idea.blaze.base.ideinfo.TargetIdeInfo;
+import com.google.idea.blaze.base.ideinfo.TargetMapBuilder;
+import com.google.idea.blaze.base.model.BlazeProjectData;
+import com.google.idea.blaze.base.model.MockBlazeProjectDataBuilder;
+import com.google.idea.blaze.base.model.primitives.WorkspacePath;
+import com.google.idea.blaze.base.model.primitives.WorkspaceRoot;
+import com.google.idea.blaze.base.projectview.ProjectViewSet;
+import com.google.idea.blaze.base.projectview.parser.ProjectViewParser;
+import com.google.idea.blaze.base.scope.BlazeContext;
+import com.google.idea.blaze.base.settings.BlazeImportSettings;
+import com.google.idea.blaze.base.settings.BlazeImportSettingsManager;
+import com.google.idea.blaze.base.sync.projectview.ImportRoots;
+import com.google.idea.blaze.base.sync.projectview.LanguageSupport;
+import com.google.idea.blaze.base.sync.workspace.WorkspacePathResolverImpl;
+import com.google.idea.blaze.cpp.CPrefetchFileSource;
+import java.io.File;
+import java.util.HashSet;
+import java.util.Set;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Unit tests for {@link CPrefetchFileSource}. */
+@RunWith(JUnit4.class)
+public class CPrefetchFileSourceTest extends BlazeIntegrationTestCase {
+
+ @Test
+ public void testSourceFilesInProjectIgnored() {
+ ProjectViewSet projectViewSet =
+ parseProjectView(
+ "directories:",
+ " java/com/google",
+ "targets:",
+ " //java/com/google:lib",
+ "additional_languages:",
+ " c",
+ "android_sdk_platform: android-25");
+
+ BlazeProjectData projectData =
+ MockBlazeProjectDataBuilder.builder(workspaceRoot)
+ .setTargetMap(
+ TargetMapBuilder.builder()
+ .addTarget(
+ TargetIdeInfo.builder()
+ .setBuildFile(sourceRoot("java/com/google/BUILD"))
+ .setLabel("//java/com/google:lib")
+ .setKind("cc_library")
+ .addSource(sourceRoot("java/com/google/native.cc"))
+ .addSource(sourceRoot("java/com/google/native.h")))
+ .build())
+ .setWorkspaceLanguageSettings(
+ LanguageSupport.createWorkspaceLanguageSettings(projectViewSet))
+ .build();
+
+ Set<File> filesToPrefetch = new HashSet<>();
+ new CPrefetchFileSource()
+ .addFilesToPrefetch(
+ getProject(),
+ projectViewSet,
+ getImportRoots(projectViewSet),
+ projectData,
+ filesToPrefetch);
+
+ assertThat(filesToPrefetch).isEmpty();
+ }
+
+ @Test
+ public void testCppHeaderFilesOutsideProjectIncluded() {
+ ProjectViewSet projectViewSet =
+ parseProjectView(
+ "directories:",
+ " java/com/google",
+ "targets:",
+ " //java/com/google:lib",
+ "additional_languages:",
+ " c",
+ "android_sdk_platform: android-25");
+
+ BlazeProjectData projectData =
+ MockBlazeProjectDataBuilder.builder(workspaceRoot)
+ .setTargetMap(
+ TargetMapBuilder.builder()
+ .addTarget(
+ TargetIdeInfo.builder()
+ .setBuildFile(sourceRoot("third_party/library/BUILD"))
+ .setLabel("//third_party/library:dep")
+ .setKind("cc_library")
+ .setCInfo(
+ CIdeInfo.builder()
+ .addSource(sourceRoot("third_party/library/main.cc"))
+ .addHeader(sourceRoot("third_party/library/dep.h"))
+ .addHeader(sourceRoot("third_party/library/other.h"))
+ .addTextualHeader(sourceRoot("third_party/library/textual.h"))))
+ .build())
+ .setWorkspaceLanguageSettings(
+ LanguageSupport.createWorkspaceLanguageSettings(projectViewSet))
+ .build();
+
+ Set<File> filesToPrefetch = new HashSet<>();
+ new CPrefetchFileSource()
+ .addFilesToPrefetch(
+ getProject(),
+ projectViewSet,
+ getImportRoots(projectViewSet),
+ projectData,
+ filesToPrefetch);
+
+ assertThat(filesToPrefetch)
+ .containsExactly(
+ workspaceFile("third_party/library/dep.h"),
+ workspaceFile("third_party/library/other.h"),
+ workspaceFile("third_party/library/textual.h"));
+ }
+
+ @Test
+ public void testJavaSourceFilesIgnored() {
+ ProjectViewSet projectViewSet =
+ parseProjectView(
+ "directories:",
+ " java/com/google",
+ "targets:",
+ " //java/com/google:lib",
+ "additional_languages:",
+ " c",
+ "android_sdk_platform: android-25");
+
+ BlazeProjectData projectData =
+ MockBlazeProjectDataBuilder.builder(workspaceRoot)
+ .setTargetMap(
+ TargetMapBuilder.builder()
+ .addTarget(
+ TargetIdeInfo.builder()
+ .setBuildFile(sourceRoot("third_party/library/BUILD"))
+ .setLabel("//third_party/library:lib")
+ .setKind("java_library")
+ .addSource(sourceRoot("third_party/library/Library.java")))
+ .build())
+ .setWorkspaceLanguageSettings(
+ LanguageSupport.createWorkspaceLanguageSettings(projectViewSet))
+ .build();
+
+ Set<File> filesToPrefetch = new HashSet<>();
+ new CPrefetchFileSource()
+ .addFilesToPrefetch(
+ getProject(),
+ projectViewSet,
+ getImportRoots(projectViewSet),
+ projectData,
+ filesToPrefetch);
+
+ assertThat(filesToPrefetch).isEmpty();
+ }
+
+ private ProjectViewSet parseProjectView(String... contents) {
+ ProjectViewParser projectViewParser =
+ new ProjectViewParser(new BlazeContext(), new WorkspacePathResolverImpl(workspaceRoot));
+ projectViewParser.parseProjectView(Joiner.on("\n").join(contents));
+ return projectViewParser.getResult();
+ }
+
+ private static ArtifactLocation sourceRoot(String relativePath) {
+ return ArtifactLocation.builder().setRelativePath(relativePath).setIsSource(true).build();
+ }
+
+ private File workspaceFile(String workspacePath) {
+ return workspaceRoot.fileForPath(new WorkspacePath(workspacePath));
+ }
+
+ private ImportRoots getImportRoots(ProjectViewSet projectViewSet) {
+ BlazeImportSettings importSettings =
+ BlazeImportSettingsManager.getInstance(getProject()).getImportSettings();
+ WorkspaceRoot workspaceRoot = WorkspaceRoot.fromImportSettings(importSettings);
+ return ImportRoots.builder(workspaceRoot, importSettings.getBuildSystem())
+ .add(projectViewSet)
+ .build();
+ }
+}
diff --git a/aswb/tests/integrationtests/com/google/idea/blaze/android/run/BlazeAndroidRunConfigurationCommonStateTest.java b/aswb/tests/integrationtests/com/google/idea/blaze/android/run/BlazeAndroidRunConfigurationCommonStateTest.java
index 0d9cba6..0df7e0c 100644
--- a/aswb/tests/integrationtests/com/google/idea/blaze/android/run/BlazeAndroidRunConfigurationCommonStateTest.java
+++ b/aswb/tests/integrationtests/com/google/idea/blaze/android/run/BlazeAndroidRunConfigurationCommonStateTest.java
@@ -42,6 +42,7 @@
@Rule
public final AndroidIntegrationTestSetupRule androidSetupRule =
new AndroidIntegrationTestSetupRule();
+
private BlazeAndroidRunConfigurationCommonState state;
@Before
@@ -58,6 +59,7 @@
@Test
public void readAndWriteShouldMatch() throws InvalidDataException, WriteExternalException {
state.getBlazeFlagsState().setRawFlags(ImmutableList.of("--flag1", "--flag2"));
+ state.getExeFlagsState().setRawFlags(ImmutableList.of("--exe1", "--exe2"));
state.setNativeDebuggingEnabled(true);
Element element = new Element("test");
@@ -69,6 +71,9 @@
assertThat(readState.getBlazeFlagsState().getRawFlags())
.containsExactly("--flag1", "--flag2")
.inOrder();
+ assertThat(readState.getExeFlagsState().getRawFlags())
+ .containsExactly("--exe1", "--exe2")
+ .inOrder();
assertThat(readState.isNativeDebuggingEnabled()).isTrue();
}
@@ -82,6 +87,8 @@
assertThat(readState.getBlazeFlagsState().getRawFlags())
.isEqualTo(state.getBlazeFlagsState().getRawFlags());
+ assertThat(readState.getExeFlagsState().getRawFlags())
+ .isEqualTo(state.getExeFlagsState().getRawFlags());
assertThat(readState.isNativeDebuggingEnabled()).isEqualTo(state.isNativeDebuggingEnabled());
}
@@ -90,6 +97,9 @@
state
.getBlazeFlagsState()
.setRawFlags(ImmutableList.of("hi ", "", "I'm", " ", "\t", "Josh\r\n", "\n"));
+ state
+ .getExeFlagsState()
+ .setRawFlags(ImmutableList.of("one ", "", "two", " ", "\t", "three\r\n", "\n"));
Element element = new Element("test");
state.writeExternal(element);
@@ -100,6 +110,9 @@
assertThat(readState.getBlazeFlagsState().getRawFlags())
.containsExactly("hi", "I'm", "Josh")
.inOrder();
+ assertThat(readState.getExeFlagsState().getRawFlags())
+ .containsExactly("one", "two", "three")
+ .inOrder();
}
@Test
@@ -107,6 +120,7 @@
final XMLOutputter xmlOutputter = new XMLOutputter(Format.getCompactFormat());
state.getBlazeFlagsState().setRawFlags(ImmutableList.of("--flag1", "--flag2"));
+ state.getExeFlagsState().setRawFlags(ImmutableList.of("--exe1", "--exe2"));
state.setNativeDebuggingEnabled(true);
Element firstWrite = new Element("test");
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 e72c5de..51f8f49 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
@@ -22,6 +22,7 @@
import com.google.idea.blaze.android.AndroidIntegrationTestSetupRule;
import com.google.idea.blaze.android.cppapi.NdkSupport;
import com.google.idea.blaze.android.run.BlazeAndroidRunConfigurationCommonState;
+import com.google.idea.blaze.android.run.binary.BlazeAndroidBinaryLaunchMethodsProvider.AndroidBinaryLaunchMethod;
import com.google.idea.blaze.base.BlazeIntegrationTestCase;
import com.google.idea.blaze.base.run.state.RunConfigurationStateEditor;
import com.google.idea.common.experiments.ExperimentService;
@@ -45,6 +46,7 @@
@Rule
public final AndroidIntegrationTestSetupRule androidSetupRule =
new AndroidIntegrationTestSetupRule();
+
private BlazeAndroidBinaryRunConfigurationState state;
@Before
@@ -66,7 +68,7 @@
state.setActivityClass("com.example.TestActivity");
state.setMode(BlazeAndroidBinaryRunConfigurationState.LAUNCH_SPECIFIC_ACTIVITY);
- state.setMobileInstall(true);
+ state.setLaunchMethod(AndroidBinaryLaunchMethod.MOBILE_INSTALL);
state.setUseSplitApksIfPossible(false);
state.setUseWorkProfileIfPresent(true);
state.setUserId(2);
@@ -87,7 +89,7 @@
assertThat(readState.getActivityClass()).isEqualTo("com.example.TestActivity");
assertThat(readState.getMode())
.isEqualTo(BlazeAndroidBinaryRunConfigurationState.LAUNCH_SPECIFIC_ACTIVITY);
- assertThat(readState.mobileInstall()).isTrue();
+ assertThat(readState.getLaunchMethod()).isEqualTo(AndroidBinaryLaunchMethod.MOBILE_INSTALL);
assertThat(readState.useSplitApksIfPossible()).isFalse();
assertThat(readState.useWorkProfileIfPresent()).isTrue();
assertThat(readState.getUserId()).isEqualTo(2);
@@ -111,7 +113,7 @@
assertThat(readState.getActivityClass()).isEqualTo(state.getActivityClass());
assertThat(readState.getMode()).isEqualTo(state.getMode());
- assertThat(readState.mobileInstall()).isEqualTo(state.mobileInstall());
+ assertThat(readState.getLaunchMethod()).isEqualTo(state.getLaunchMethod());
assertThat(readState.useSplitApksIfPossible()).isEqualTo(state.useSplitApksIfPossible());
assertThat(readState.useWorkProfileIfPresent()).isEqualTo(state.useWorkProfileIfPresent());
assertThat(readState.getUserId()).isEqualTo(state.getUserId());
@@ -128,7 +130,7 @@
state.setActivityClass("com.example.TestActivity");
state.setMode(BlazeAndroidBinaryRunConfigurationState.LAUNCH_SPECIFIC_ACTIVITY);
- state.setMobileInstall(true);
+ state.setLaunchMethod(AndroidBinaryLaunchMethod.MOBILE_INSTALL);
state.setUseSplitApksIfPossible(false);
state.setUseWorkProfileIfPresent(true);
state.setUserId(2);
@@ -153,12 +155,12 @@
state.setActivityClass("com.example.TestActivity");
state.setMode(BlazeAndroidBinaryRunConfigurationState.LAUNCH_SPECIFIC_ACTIVITY);
- state.setMobileInstall(true);
+ state.setLaunchMethod(AndroidBinaryLaunchMethod.MOBILE_INSTALL);
state.setUseSplitApksIfPossible(false);
state.setUseWorkProfileIfPresent(true);
state.setUserId(2);
// We don't test DeepLink because it is not exposed in the editor.
- //state.setDeepLink("http://deeplink");
+ // state.setDeepLink("http://deeplink");
editor.resetEditorFrom(state);
BlazeAndroidBinaryRunConfigurationState readState =
@@ -173,12 +175,12 @@
assertThat(readState.getActivityClass()).isEqualTo(state.getActivityClass());
assertThat(readState.getMode()).isEqualTo(state.getMode());
- assertThat(readState.mobileInstall()).isEqualTo(state.mobileInstall());
+ assertThat(readState.getLaunchMethod()).isEqualTo(state.getLaunchMethod());
assertThat(readState.useSplitApksIfPossible()).isEqualTo(state.useSplitApksIfPossible());
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.
- //assertThat(readState.getDeepLink()).isEqualTo(state.getDeepLink());
+ // assertThat(readState.getDeepLink()).isEqualTo(state.getDeepLink());
}
@Test
@@ -199,11 +201,11 @@
assertThat(readState.getActivityClass()).isEqualTo(state.getActivityClass());
assertThat(readState.getMode()).isEqualTo(state.getMode());
- assertThat(readState.mobileInstall()).isEqualTo(state.mobileInstall());
+ assertThat(readState.getLaunchMethod()).isEqualTo(state.getLaunchMethod());
assertThat(readState.useSplitApksIfPossible()).isEqualTo(state.useSplitApksIfPossible());
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.
- //assertThat(readState.getDeepLink()).isEqualTo(state.getDeepLink());
+ // assertThat(readState.getDeepLink()).isEqualTo(state.getDeepLink());
}
}
diff --git a/aswb/tests/integrationtests/com/google/idea/blaze/android/run/test/BlazeAndroidTestRunConfigurationStateTest.java b/aswb/tests/integrationtests/com/google/idea/blaze/android/run/test/BlazeAndroidTestRunConfigurationStateTest.java
index 4944264..800ea7c 100644
--- a/aswb/tests/integrationtests/com/google/idea/blaze/android/run/test/BlazeAndroidTestRunConfigurationStateTest.java
+++ b/aswb/tests/integrationtests/com/google/idea/blaze/android/run/test/BlazeAndroidTestRunConfigurationStateTest.java
@@ -21,6 +21,7 @@
import com.google.idea.blaze.android.AndroidIntegrationTestSetupRule;
import com.google.idea.blaze.android.cppapi.NdkSupport;
import com.google.idea.blaze.android.run.BlazeAndroidRunConfigurationCommonState;
+import com.google.idea.blaze.android.run.test.BlazeAndroidTestLaunchMethodsProvider.AndroidTestLaunchMethod;
import com.google.idea.blaze.base.BlazeIntegrationTestCase;
import com.google.idea.blaze.base.run.state.RunConfigurationStateEditor;
import com.google.idea.common.experiments.ExperimentService;
@@ -44,6 +45,7 @@
@Rule
public final AndroidIntegrationTestSetupRule androidSetupRule =
new AndroidIntegrationTestSetupRule();
+
private BlazeAndroidTestRunConfigurationState state;
@Before
@@ -61,6 +63,7 @@
public void readAndWriteShouldMatch() throws InvalidDataException, WriteExternalException {
BlazeAndroidRunConfigurationCommonState commonState = state.getCommonState();
commonState.getBlazeFlagsState().setRawFlags(ImmutableList.of("--flag1", "--flag2"));
+ commonState.getExeFlagsState().setRawFlags(ImmutableList.of("--exe1", "--exe2"));
commonState.setNativeDebuggingEnabled(true);
state.setTestingType(BlazeAndroidTestRunConfigurationState.TEST_METHOD);
@@ -68,7 +71,7 @@
state.setMethodName("fooMethod");
state.setClassName("BarClass");
state.setPackageName("com.test.package.name");
- state.setRunThroughBlaze(true);
+ state.setLaunchMethod(AndroidTestLaunchMethod.BLAZE_TEST);
state.setExtraOptions("--option");
Element element = new Element("test");
@@ -81,6 +84,9 @@
assertThat(readCommonState.getBlazeFlagsState().getRawFlags())
.containsExactly("--flag1", "--flag2")
.inOrder();
+ assertThat(readCommonState.getExeFlagsState().getRawFlags())
+ .containsExactly("--exe1", "--exe2")
+ .inOrder();
assertThat(readCommonState.isNativeDebuggingEnabled()).isTrue();
assertThat(readState.getTestingType())
@@ -89,7 +95,7 @@
assertThat(readState.getMethodName()).isEqualTo("fooMethod");
assertThat(readState.getClassName()).isEqualTo("BarClass");
assertThat(readState.getPackageName()).isEqualTo("com.test.package.name");
- assertThat(readState.isRunThroughBlaze()).isTrue();
+ assertThat(readState.getLaunchMethod()).isEqualTo(AndroidTestLaunchMethod.BLAZE_TEST);
assertThat(readState.getExtraOptions()).isEqualTo("--option");
}
@@ -105,6 +111,8 @@
BlazeAndroidRunConfigurationCommonState readCommonState = readState.getCommonState();
assertThat(readCommonState.getBlazeFlagsState().getRawFlags())
.isEqualTo(commonState.getBlazeFlagsState().getRawFlags());
+ assertThat(readCommonState.getExeFlagsState().getRawFlags())
+ .isEqualTo(commonState.getExeFlagsState().getRawFlags());
assertThat(readCommonState.isNativeDebuggingEnabled())
.isEqualTo(commonState.isNativeDebuggingEnabled());
@@ -114,7 +122,7 @@
assertThat(readState.getMethodName()).isEqualTo(state.getMethodName());
assertThat(readState.getClassName()).isEqualTo(state.getClassName());
assertThat(readState.getPackageName()).isEqualTo(state.getPackageName());
- assertThat(readState.isRunThroughBlaze()).isEqualTo(state.isRunThroughBlaze());
+ assertThat(readState.getLaunchMethod()).isEqualTo(state.getLaunchMethod());
assertThat(readState.getExtraOptions()).isEqualTo(state.getExtraOptions());
}
@@ -131,7 +139,7 @@
state.setMethodName("fooMethod");
state.setClassName("BarClass");
state.setPackageName("com.test.package.name");
- state.setRunThroughBlaze(true);
+ state.setLaunchMethod(AndroidTestLaunchMethod.MOBILE_INSTALL);
state.setExtraOptions("--option");
Element firstWrite = new Element("test");
@@ -149,6 +157,7 @@
BlazeAndroidRunConfigurationCommonState commonState = state.getCommonState();
commonState.getBlazeFlagsState().setRawFlags(ImmutableList.of("--flag1", "--flag2"));
+ commonState.getExeFlagsState().setRawFlags(ImmutableList.of("--exe1", "--exe2"));
commonState.setNativeDebuggingEnabled(true);
state.setTestingType(BlazeAndroidTestRunConfigurationState.TEST_METHOD);
@@ -156,9 +165,9 @@
state.setMethodName("fooMethod");
state.setClassName("BarClass");
state.setPackageName("com.test.package.name");
- state.setRunThroughBlaze(true);
+ state.setLaunchMethod(AndroidTestLaunchMethod.BLAZE_TEST);
// We don't test ExtraOptions because it is not exposed in the editor.
- //state.setExtraOptions("--option");
+ // state.setExtraOptions("--option");
editor.resetEditorFrom(state);
BlazeAndroidTestRunConfigurationState readState =
@@ -168,6 +177,8 @@
BlazeAndroidRunConfigurationCommonState readCommonState = readState.getCommonState();
assertThat(readCommonState.getBlazeFlagsState().getRawFlags())
.isEqualTo(commonState.getBlazeFlagsState().getRawFlags());
+ assertThat(readCommonState.getExeFlagsState().getRawFlags())
+ .isEqualTo(commonState.getExeFlagsState().getRawFlags());
assertThat(readCommonState.isNativeDebuggingEnabled())
.isEqualTo(commonState.isNativeDebuggingEnabled());
@@ -177,9 +188,9 @@
assertThat(readState.getMethodName()).isEqualTo(state.getMethodName());
assertThat(readState.getClassName()).isEqualTo(state.getClassName());
assertThat(readState.getPackageName()).isEqualTo(state.getPackageName());
- assertThat(readState.isRunThroughBlaze()).isEqualTo(state.isRunThroughBlaze());
+ assertThat(readState.getLaunchMethod()).isEqualTo(state.getLaunchMethod());
// We don't test ExtraOptions because it is not exposed in the editor.
- //assertThat(readState.getExtraOptions()).isEqualTo(state.getExtraOptions());
+ // assertThat(readState.getExtraOptions()).isEqualTo(state.getExtraOptions());
}
@Test
@@ -195,6 +206,8 @@
BlazeAndroidRunConfigurationCommonState readCommonState = readState.getCommonState();
assertThat(readCommonState.getBlazeFlagsState().getRawFlags())
.isEqualTo(commonState.getBlazeFlagsState().getRawFlags());
+ assertThat(readCommonState.getExeFlagsState().getRawFlags())
+ .isEqualTo(commonState.getExeFlagsState().getRawFlags());
assertThat(readCommonState.isNativeDebuggingEnabled())
.isEqualTo(commonState.isNativeDebuggingEnabled());
@@ -204,8 +217,8 @@
assertThat(readState.getMethodName()).isEqualTo(state.getMethodName());
assertThat(readState.getClassName()).isEqualTo(state.getClassName());
assertThat(readState.getPackageName()).isEqualTo(state.getPackageName());
- assertThat(readState.isRunThroughBlaze()).isEqualTo(state.isRunThroughBlaze());
+ assertThat(readState.getLaunchMethod()).isEqualTo(state.getLaunchMethod());
// We don't test ExtraOptions because it is not exposed in the editor.
- //assertThat(readState.getExtraOptions()).isEqualTo(state.getExtraOptions());
+ // assertThat(readState.getExtraOptions()).isEqualTo(state.getExtraOptions());
}
}
diff --git a/aswb/tests/integrationtests/com/google/idea/blaze/android/sync/AndroidSyncTest.java b/aswb/tests/integrationtests/com/google/idea/blaze/android/sync/AndroidSyncTest.java
index a712648..f1f048a 100644
--- a/aswb/tests/integrationtests/com/google/idea/blaze/android/sync/AndroidSyncTest.java
+++ b/aswb/tests/integrationtests/com/google/idea/blaze/android/sync/AndroidSyncTest.java
@@ -42,7 +42,10 @@
import com.google.idea.blaze.base.sync.data.BlazeProjectDataManager;
import com.google.idea.blaze.base.sync.projectstructure.ModuleFinder;
import com.google.idea.blaze.cpp.BlazeCWorkspace;
+import com.google.idea.blaze.cpp.CompilerVersionChecker;
+import com.google.idea.blaze.cpp.MockCompilerVersionChecker;
import com.google.idea.blaze.java.sync.BlazeJavaSyncAugmenter;
+import com.google.idea.sdkcompat.cidr.CidrCompilerSwitchesAdapter;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.projectRoots.SdkTypeId;
@@ -74,6 +77,8 @@
public void setup() {
mockSdk("android-25", "Android 25 SDK");
registerProjectService(OCWorkspaceManager.class, new MockOCWorkspaceManager());
+ registerApplicationService(
+ CompilerVersionChecker.class, new MockCompilerVersionChecker("1234"));
}
private void mockSdk(String targetHash, String sdkName) {
@@ -288,7 +293,8 @@
.addTransitiveSystemIncludeDirectories(
ImmutableList.of(
new ExecutionRootPath("third_party/stl/gcc3"),
- new ExecutionRootPath("third_party/java/jdk/include"))))
+ new ExecutionRootPath("third_party/java/jdk/include")))
+ .addSource(sourceRoot("java/com/google/jni/native.cc")))
.addSource(sourceRoot("java/com/google/jni/native.cc"))
.addDependency("//android_ndk_linux/toolchains:aarch64"))
.addTarget(
@@ -307,7 +313,8 @@
.addTransitiveSystemIncludeDirectories(
ImmutableList.of(
new ExecutionRootPath("third_party/stl/gcc3"),
- new ExecutionRootPath("third_party/java/jdk/include"))))
+ new ExecutionRootPath("third_party/java/jdk/include")))
+ .addSource(sourceRoot("java/com/google/jni/native2.cc")))
.addSource(sourceRoot("java/com/google/jni/native2.cc"))
.addDependency("//java/com/google:native_lib")
.addDependency("//android_ndk_linux/toolchains:armv7a"))
@@ -374,7 +381,8 @@
assertThat(resolveConfigurations).hasSize(1);
OCCompilerSettings compilerSettings = resolveConfigurations.get(0).getCompilerSettings();
List<String> compilerSwitches =
- compilerSettings.getCompilerSwitches(OCLanguageKind.CPP, nativeCc).getCommandLineArgs();
+ CidrCompilerSwitchesAdapter.getCommandLineArgs(
+ compilerSettings.getCompilerSwitches(OCLanguageKind.CPP, nativeCc));
assertThat(compilerSwitches)
.contains("--sysroot=android_ndk_linux/platforms/android-21/arch-arm64");
@@ -383,7 +391,8 @@
assertThat(resolveConfigurations).hasSize(1);
compilerSettings = resolveConfigurations.get(0).getCompilerSettings();
compilerSwitches =
- compilerSettings.getCompilerSwitches(OCLanguageKind.CPP, nativeCc).getCommandLineArgs();
+ CidrCompilerSwitchesAdapter.getCommandLineArgs(
+ compilerSettings.getCompilerSwitches(OCLanguageKind.CPP, nativeCc));
assertThat(compilerSwitches)
.contains("--sysroot=android_ndk_linux/platforms/android-18/arch-arm");
}
diff --git a/aswb/tests/integrationtests/com/google/idea/blaze/android/sync/model/idea/BlazeClassJarProviderIntegrationTest.java b/aswb/tests/integrationtests/com/google/idea/blaze/android/sync/model/idea/BlazeClassJarProviderIntegrationTest.java
index e784f6a..f55ad08 100644
--- a/aswb/tests/integrationtests/com/google/idea/blaze/android/sync/model/idea/BlazeClassJarProviderIntegrationTest.java
+++ b/aswb/tests/integrationtests/com/google/idea/blaze/android/sync/model/idea/BlazeClassJarProviderIntegrationTest.java
@@ -39,6 +39,7 @@
import com.google.idea.blaze.base.model.primitives.Label;
import com.google.idea.blaze.base.sync.data.BlazeProjectDataManager;
import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoder;
+import com.google.idea.sdkcompat.android.res.AppResourceRepositoryAdapter;
import com.intellij.facet.FacetManager;
import com.intellij.facet.ModifiableFacetModel;
import com.intellij.openapi.application.ApplicationManager;
@@ -84,75 +85,165 @@
createClassesInJars();
// Make sure we can find classes in the main resource module.
- assertThat(classJarProvider.findModuleClassFile("com.google.example.main.MainActivity", module))
+ VirtualFile found =
+ classJarProvider.findModuleClassFile("com.google.example.main.MainActivity", module);
+ assertThat(found).isNotNull();
+ assertThat(found)
.isEqualTo(
fileSystem.findFile(
BLAZE_BIN
- + "/com/google/example/main.jar!"
+ + "/com/google/example/libmain.jar!"
+ "/com/google/example/main/MainActivity.class"));
- assertThat(classJarProvider.findModuleClassFile("com.google.example.main.R", module))
+ found = classJarProvider.findModuleClassFile("com.google.example.main.R", module);
+ assertThat(found).isNotNull();
+ assertThat(found)
.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))
+
+ found = classJarProvider.findModuleClassFile("com.google.example.main.R$string", module);
+ assertThat(found).isNotNull();
+ assertThat(found)
.isEqualTo(
fileSystem.findFile(
BLAZE_BIN
+ "/com/google/example/main_resources.jar!"
+ "/com/google/example/main/R$string.class"));
+ // And dependencies.
+ found = classJarProvider.findModuleClassFile("com.google.example.java.Java", module);
+ assertThat(found).isNotNull();
+ assertThat(found)
+ .isEqualTo(
+ fileSystem.findFile(
+ BLAZE_BIN
+ + "/com/google/example/libjava.jar!"
+ + "/com/google/example/java/Java.class"));
+ found = classJarProvider.findModuleClassFile("com.google.example.shared.Shared", module);
+ assertThat(found).isNotNull();
+ assertThat(found)
+ .isEqualTo(
+ fileSystem.findFile(
+ BLAZE_BIN
+ + "/com/google/example/libshared.jar!"
+ + "/com/google/example/shared/Shared.class"));
+ found = classJarProvider.findModuleClassFile("com.google.example.shared2.Shared2", module);
+ assertThat(found).isNotNull();
+ assertThat(found)
+ .isEqualTo(
+ fileSystem.findFile(
+ BLAZE_BIN
+ + "/com/google/example/libshared2.jar!"
+ + "/com/google/example/shared2/Shared2.class"));
+ found =
+ classJarProvider.findModuleClassFile("com.google.example.transitive.Transitive", module);
+ assertThat(found).isNotNull();
+ assertThat(found)
+ .isEqualTo(
+ fileSystem.findFile(
+ BLAZE_BIN
+ + "/com/google/example/libtransitive.jar!"
+ + "/com/google/example/transitive/Transitive.class"));
+
+ // But not resource classes in dependencies.
+ assertThat(classJarProvider.findModuleClassFile("com.google.example.resource.R", module))
+ .isNull();
+ assertThat(classJarProvider.findModuleClassFile("com.google.example.resource.R$style", module))
+ .isNull();
+ assertThat(classJarProvider.findModuleClassFile("com.google.example.resource.R$layout", module))
+ .isNull();
+ assertThat(classJarProvider.findModuleClassFile("com.google.example.resource2.R", module))
+ .isNull();
+
// 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))
+ // And not imported libraries.
+ assertThat(classJarProvider.findModuleClassFile("com.google.example.import.Import", module))
.isNull();
assertThat(
classJarProvider.findModuleClassFile(
- "com.google.unrelated.android_res.R$layout", module))
+ "com.google.example.transitive.import.TransitiveImport", module))
.isNull();
+
+ // And not unrelated libraries.
+ assertThat(classJarProvider.findModuleClassFile("com.google.unrelated.Java", module)).isNull();
+ assertThat(classJarProvider.findModuleClassFile("com.google.unrelated.Android", module))
+ .isNull();
+ assertThat(classJarProvider.findModuleClassFile("com.google.unrelated.Resource", module))
+ .isNull();
+ assertThat(classJarProvider.findModuleClassFile("com.google.unrelated.R", module)).isNull();
}
@Test
- public void testMissingMainJar() {
+ public void testMissingClassJars() {
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);
+ // Let's pretend that these haven't been built yet.
+ fileSystem.findFile(BLAZE_BIN + "/com/google/example/libmain.jar").delete(this);
+ fileSystem.findFile(BLAZE_BIN + "/com/google/example/libjava.jar").delete(this);
+ fileSystem.findFile(BLAZE_BIN + "/com/google/example/libshared.jar").delete(this);
+ fileSystem
+ .findFile(BLAZE_BIN + "/com/google/example/libtransitive.jar")
+ .delete(this);
} catch (IOException ignored) {
// ignored
}
});
- // This hasn't been built yet, and shouldn't be found.
+ // These hasn't been built yet, and shouldn't be found.
assertThat(classJarProvider.findModuleClassFile("com.google.example.main.MainActivity", module))
.isNull();
+ assertThat(classJarProvider.findModuleClassFile("com.google.example.java.Java", module))
+ .isNull();
+ assertThat(classJarProvider.findModuleClassFile("com.google.example.shared.Shared", module))
+ .isNull();
+ assertThat(
+ classJarProvider.findModuleClassFile(
+ "com.google.example.transitive.Transitive", module))
+ .isNull();
+
// But these should still be found.
- assertThat(classJarProvider.findModuleClassFile("com.google.example.main.R", module))
+ VirtualFile found = classJarProvider.findModuleClassFile("com.google.example.main.R", module);
+ assertThat(found).isNotNull();
+ assertThat(found)
.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))
+ found = classJarProvider.findModuleClassFile("com.google.example.main.R$string", module);
+ assertThat(found).isNotNull();
+ assertThat(found)
.isEqualTo(
fileSystem.findFile(
BLAZE_BIN
+ "/com/google/example/main_resources.jar!"
+ "/com/google/example/main/R$string.class"));
+ found = classJarProvider.findModuleClassFile("com.google.example.android.Android", module);
+ assertThat(found).isNotNull();
+ assertThat(found)
+ .isEqualTo(
+ fileSystem.findFile(
+ BLAZE_BIN
+ + "/com/google/example/libandroid.jar!"
+ + "/com/google/example/android/Android.class"));
+ found = classJarProvider.findModuleClassFile("com.google.example.shared2.Shared2", module);
+ assertThat(found).isNotNull();
+ assertThat(found)
+ .isEqualTo(
+ fileSystem.findFile(
+ BLAZE_BIN
+ + "/com/google/example/libshared2.jar!"
+ + "/com/google/example/shared2/Shared2.class"));
}
@Test
@@ -169,53 +260,40 @@
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"));
+ fileSystem.findFile("com/google/example/libimport.jar"),
+ fileSystem.findFile("com/google/example/transitive/libimport.jar"),
+ fileSystem.findFile("com/google/example/transitive/libimport2.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);
+ AppResourceRepository repository = AppResourceRepositoryAdapter.findExistingInstance(module);
assertThat(repository).isNotNull();
- assertThat(registry.findClassDefinition("com.google.example.android_res.R", repository))
+ assertThat(registry.findClassDefinition("com.google.example.resource.R", repository))
.isNotNull();
- assertThat(registry.findClassDefinition("com.google.example.android_res.R$string", repository))
+ assertThat(registry.findClassDefinition("com.google.example.resource.R$string", repository))
.isNotNull();
- assertThat(registry.findClassDefinition("com.google.example.android_res2.R", repository))
+ assertThat(registry.findClassDefinition("com.google.example.resource2.R", repository))
.isNotNull();
- assertThat(registry.findClassDefinition("com.google.example.android_res2.R$layout", repository))
+ assertThat(registry.findClassDefinition("com.google.example.resource2.R$layout", repository))
.isNotNull();
- assertThat(
- registry.findClassDefinition("com.google.example.transitive.android_res.R", repository))
+ assertThat(registry.findClassDefinition("com.google.example.transitive.resource.R", repository))
.isNotNull();
assertThat(
registry.findClassDefinition(
- "com.google.example.transitive.android_res.R$style", repository))
+ "com.google.example.transitive.resource.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))
+ assertThat(registry.findClassDefinition("com.google.example.resource.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();
+ assertThat(registry.findClassDefinition("com.google.example.java.Java", repository)).isNull();
+ assertThat(registry.findClassDefinition("com.google.unrelated.R", repository)).isNull();
+ assertThat(registry.findClassDefinition("com.google.unrelated.R$layout", repository)).isNull();
}
@Test
@@ -224,14 +302,9 @@
.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);
+ // Let's pretend that these were deleted.
+ fileSystem.findFile("com/google/example/libimport.jar").delete(this);
+ fileSystem.findFile("com/google/example/transitive/libimport.jar").delete(this);
} catch (IOException ignored) {
// ignored
}
@@ -239,26 +312,16 @@
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"));
+ // These should be missing.
+ // fileSystem.findFile("com/google/example/libimport.jar"),
+ // fileSystem.findFile("com/google/example/transitive/libimport.jar"),
+ fileSystem.findFile("com/google/example/transitive/libimport2.jar"));
}
private void createClassesInJars() {
fileSystem.createFile(
BLAZE_BIN
- + "/com/google/example/main.jar!"
+ + "/com/google/example/libmain.jar!"
+ "/com/google/example/main/MainActivity.class");
fileSystem.createFile(
BLAZE_BIN + "/com/google/example/main_resources.jar!" + "/com/google/example/main/R.class");
@@ -267,37 +330,81 @@
+ "/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");
+ BLAZE_BIN
+ + "/com/google/example/libandroid.jar!"
+ + "/com/google/example/android/Android.class");
fileSystem.createFile(
BLAZE_BIN
- + "/com/google/example/android_res_resources.jar!"
- + "/com/google/example/android_res/R.class");
+ + "/com/google/example/resource_resources.jar!"
+ + "/com/google/example/resource/R.class");
fileSystem.createFile(
BLAZE_BIN
- + "/com/google/example/android_res_resources.jar!"
- + "/com/google/example/android_res/R$style.class");
+ + "/com/google/example/resource_resources.jar!"
+ + "/com/google/example/resource/R$style.class");
fileSystem.createFile(
BLAZE_BIN
- + "/com/google/unrelated/android_res_resources.jar!"
- + "/com/google/unrelated/android_res/R$layout.class");
+ + "/com/google/unrelated/resource_resources.jar!"
+ + "/com/google/unrelated/resource/R$layout.class");
+ fileSystem.createFile(
+ BLAZE_BIN
+ + "/com/google/example/resource2_resources.jar!"
+ + "/com/google/example/resource2/R.class");
+ fileSystem.createFile(
+ BLAZE_BIN
+ + "/com/google/example/transitive/resource_resources.jar!"
+ + "/com/google/example/transitive/R.class");
+ fileSystem.createFile(
+ BLAZE_BIN + "/com/google/example/libjava.jar!" + "/com/google/example/java/Java.class");
+ fileSystem.createFile(
+ BLAZE_BIN
+ + "/com/google/example/libtransitive.jar!"
+ + "/com/google/example/transitive/Transitive.class");
+ fileSystem.createFile(
+ BLAZE_BIN
+ + "/com/google/example/libshared.jar!"
+ + "/com/google/example/shared/Shared.class");
+ fileSystem.createFile(
+ BLAZE_BIN
+ + "/com/google/example/libshared2.jar!"
+ + "/com/google/example/shared2/Shared2.class");
+ fileSystem.createFile(
+ "com/google/example/libimport.jar!" + "/com/google/example/import/Import.class");
+ fileSystem.createFile(
+ "com/google/example/transitive/libimport.jar!"
+ + "/com/google/example/transitive/import/TransitiveImport.class");
+ fileSystem.createFile(
+ BLAZE_BIN
+ + "/com/google/example/unrelated/libjava.jar!"
+ + "/com/google/example/unrelated/Java.class");
+ fileSystem.createFile(
+ BLAZE_BIN
+ + "/com/google/example/unrelated/libandroid.jar!"
+ + "/com/google/example/unrelated/Android.class");
+ fileSystem.createFile(
+ BLAZE_BIN
+ + "/com/google/example/unrelated/libresource.jar!"
+ + "/com/google/example/unrelated/Resource.class");
+ fileSystem.createFile(
+ BLAZE_BIN
+ + "/com/google/example/unrelated/resource_resources.jar!"
+ + "/com/google/example/unrelated/R.class");
}
private TargetMap buildTargetMap() {
Label mainResourceLibrary = Label.create("//com/google/example:main");
- Label androidLibraryDependency = Label.create("//com/google/example:android_lib");
- Label androidResourceDependency = Label.create("//com/google/example:android_res");
- Label androidResourceDependency2 = Label.create("//com/google/example:android_res2");
- Label transitiveResourceDependency =
- Label.create("//com/google/example/transitive:android_res");
+ Label androidDependency = Label.create("//com/google/example:android");
+ Label resourceDependency = Label.create("//com/google/example:resource");
+ Label resourceDependency2 = Label.create("//com/google/example:resource2");
+ Label transitiveResourceDependency = Label.create("//com/google/example/transitive:resource");
Label javaDependency = Label.create("//com/google/example:java");
- Label transitiveJavaDependency = Label.create("//com/google/example/transitive:java");
- Label sharedJavaDependency = Label.create("//com/google/example/shared:java");
- Label sharedJavaDependency2 = Label.create("//com/google/example/shared2:java");
+ Label transitiveJavaDependency = Label.create("//com/google/example:transitive");
+ Label sharedJavaDependency = Label.create("//com/google/example:shared");
+ Label sharedJavaDependency2 = Label.create("//com/google/example:shared2");
Label importDependency = Label.create("//com/google/example:import");
Label transitiveImportDependency = Label.create("//com/google/example/transitive:import");
Label unrelatedJava = Label.create("//com/google/unrelated:java");
- Label unrelatedAndroidLibrary = Label.create("//com/google/unrelated:android_lib");
- Label unrelatedAndroidResource = Label.create("//com/google/unrelated:android_res");
+ Label unrelatedAndroid = Label.create("//com/google/unrelated:android");
+ Label unrelatedResource = Label.create("//com/google/unrelated:resource");
AndroidResourceModuleRegistry registry = new AndroidResourceModuleRegistry();
registry.put(
@@ -306,18 +413,17 @@
// Not using these, but they should be in the registry.
registry.put(
mock(Module.class),
- AndroidResourceModule.builder(TargetKey.forPlainTarget(androidResourceDependency)).build());
+ AndroidResourceModule.builder(TargetKey.forPlainTarget(resourceDependency)).build());
registry.put(
mock(Module.class),
- AndroidResourceModule.builder(TargetKey.forPlainTarget(androidResourceDependency2))
- .build());
+ AndroidResourceModule.builder(TargetKey.forPlainTarget(resourceDependency2)).build());
registry.put(
mock(Module.class),
AndroidResourceModule.builder(TargetKey.forPlainTarget(transitiveResourceDependency))
.build());
registry.put(
mock(Module.class),
- AndroidResourceModule.builder(TargetKey.forPlainTarget(unrelatedAndroidResource)).build());
+ AndroidResourceModule.builder(TargetKey.forPlainTarget(unrelatedResource)).build());
registerProjectService(AndroidResourceModuleRegistry.class, registry);
return TargetMapBuilder.builder()
@@ -327,67 +433,67 @@
.setKind(Kind.ANDROID_LIBRARY)
.setJavaInfo(
javaInfoWithJars(
- "com/google/example/main.jar", "com/google/example/main_resources.jar"))
+ "com/google/example/libmain.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(androidDependency)
+ .addDependency(resourceDependency)
+ .addDependency(resourceDependency2)
.addDependency(javaDependency)
.addDependency(importDependency))
.addTarget(
TargetIdeInfo.builder()
- .setLabel(androidLibraryDependency)
+ .setLabel(androidDependency)
.setKind(Kind.ANDROID_LIBRARY)
- .setJavaInfo(javaInfoWithJars("com/google/example/android_lib.jar"))
+ .setJavaInfo(javaInfoWithJars("com/google/example/libandroid.jar"))
.addDependency(transitiveResourceDependency))
.addTarget(
TargetIdeInfo.builder()
- .setLabel(androidResourceDependency)
+ .setLabel(resourceDependency)
.setKind(Kind.ANDROID_LIBRARY)
.setJavaInfo(
javaInfoWithJars(
- "com/google/example/android_res.jar",
- "com/google/example/android_res_resources.jar"))
+ "com/google/example/resource.jar",
+ "com/google/example/resource_resources.jar"))
.setAndroidInfo(
androidInfoWithResourceAndJar(
- "com.google.example.android_res",
- "com/google/example/android_res/res",
- "com/google/example/android_res_resources.jar")))
+ "com.google.example.resource",
+ "com/google/example/resource/res",
+ "com/google/example/resource_resources.jar")))
.addTarget(
TargetIdeInfo.builder()
- .setLabel(androidResourceDependency2)
+ .setLabel(resourceDependency2)
.setKind(Kind.ANDROID_LIBRARY)
.setJavaInfo(
javaInfoWithJars(
- "com/google/example/android_res2.jar",
- "com/google/example/android_res2_resources.jar"))
+ "com/google/example/resource2.jar",
+ "com/google/example/resource2_resources.jar"))
.setAndroidInfo(
androidInfoWithResourceAndJar(
- "com.google.example.android_res2",
- "com/google/example/android_res2/res",
- "com/google/example/android_res2_resources.jar")))
+ "com.google.example.resource2",
+ "com/google/example/resource2/res",
+ "com/google/example/resource2_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"))
+ "com/google/example/transitive/resource.jar",
+ "com/google/example/transitive/resource_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")))
+ "com.google.example.transitive.resource",
+ "com/google/example/transitive/resource/res",
+ "com/google/example/transitive/resource_resources.jar")))
.addTarget(
TargetIdeInfo.builder()
.setLabel(javaDependency)
.setKind(Kind.JAVA_LIBRARY)
- .setJavaInfo(javaInfoWithJars("com/google/example/java.jar"))
+ .setJavaInfo(javaInfoWithJars("com/google/example/libjava.jar"))
.addDependency(transitiveJavaDependency)
.addDependency(sharedJavaDependency)
.addDependency(sharedJavaDependency2)
@@ -396,56 +502,56 @@
TargetIdeInfo.builder()
.setLabel(transitiveJavaDependency)
.setKind(Kind.JAVA_LIBRARY)
- .setJavaInfo(javaInfoWithJars("com/google/example/transitive/java.jar"))
+ .setJavaInfo(javaInfoWithJars("com/google/example/libtransitive.jar"))
.addDependency(sharedJavaDependency)
.addDependency(sharedJavaDependency2))
.addTarget(
TargetIdeInfo.builder()
.setLabel(sharedJavaDependency)
.setKind(Kind.JAVA_LIBRARY)
- .setJavaInfo(javaInfoWithJars("com/google/example/shared/java.jar"))
+ .setJavaInfo(javaInfoWithJars("com/google/example/libshared.jar"))
.addDependency(sharedJavaDependency2))
.addTarget(
TargetIdeInfo.builder()
.setLabel(sharedJavaDependency2)
.setKind(Kind.JAVA_LIBRARY)
- .setJavaInfo(javaInfoWithJars("com/google/example/shared2/java.jar")))
+ .setJavaInfo(javaInfoWithJars("com/google/example/libshared2.jar")))
.addTarget(
TargetIdeInfo.builder()
.setLabel(importDependency)
.setKind(Kind.JAVA_IMPORT)
- .setJavaInfo(javaInfoWithCheckedInJars("com/google/example/import.jar")))
+ .setJavaInfo(javaInfoWithCheckedInJars("com/google/example/libimport.jar")))
.addTarget(
TargetIdeInfo.builder()
.setLabel(transitiveImportDependency)
.setKind(Kind.JAVA_IMPORT)
.setJavaInfo(
javaInfoWithCheckedInJars(
- "com/google/example/transitive/import.jar",
- "com/google/example/transitive/import2.jar")))
+ "com/google/example/transitive/libimport.jar",
+ "com/google/example/transitive/libimport2.jar")))
.addTarget(
TargetIdeInfo.builder()
.setLabel(unrelatedJava)
.setKind(Kind.JAVA_LIBRARY)
- .setJavaInfo(javaInfoWithJars("com/google/unrelated/java.jar")))
+ .setJavaInfo(javaInfoWithJars("com/google/unrelated/libjava.jar")))
.addTarget(
TargetIdeInfo.builder()
- .setLabel(unrelatedAndroidLibrary)
+ .setLabel(unrelatedAndroid)
.setKind(Kind.ANDROID_LIBRARY)
- .setJavaInfo(javaInfoWithJars("com/google/unrelated/android_lib.jar")))
+ .setJavaInfo(javaInfoWithJars("com/google/unrelated/libandroid.jar")))
.addTarget(
TargetIdeInfo.builder()
- .setLabel(unrelatedAndroidResource)
+ .setLabel(unrelatedResource)
.setKind(Kind.ANDROID_LIBRARY)
.setJavaInfo(
javaInfoWithJars(
- "com/google/unrelated/android_res.jar",
- "com/google/unrelated/android_res_resources.jar"))
+ "com/google/unrelated/libresource.jar",
+ "com/google/unrelated/resource_resources.jar"))
.setAndroidInfo(
androidInfoWithResourceAndJar(
- "com.google.unrelated.android_res",
- "com/google/unrelated/android_res/res",
- "com/google/unrelated/android_res_resources.jar")))
+ "com.google.unrelated.resource",
+ "com/google/unrelated/resource/res",
+ "com/google/unrelated/resource_resources.jar")))
.build();
}
diff --git a/aswb/tests/unittests/com/google/idea/blaze/android/rendering/BlazeRenderErrorContributorTest.java b/aswb/tests/unittests/com/google/idea/blaze/android/rendering/BlazeRenderErrorContributorTest.java
index 2b49d8c..3163f63 100644
--- a/aswb/tests/unittests/com/google/idea/blaze/android/rendering/BlazeRenderErrorContributorTest.java
+++ b/aswb/tests/unittests/com/google/idea/blaze/android/rendering/BlazeRenderErrorContributorTest.java
@@ -17,6 +17,7 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
import com.android.tools.idea.rendering.RenderErrorContributor;
import com.android.tools.idea.rendering.RenderErrorModelFactory;
@@ -59,6 +60,7 @@
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ProjectFileIndex;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.JavaPsiFacade;
@@ -70,7 +72,6 @@
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;
@@ -96,6 +97,7 @@
super.initTest(applicationServices, projectServices);
applicationServices.register(FileTypeManager.class, new MockFileTypeManager());
+ projectServices.register(ProjectFileIndex.class, mock(ProjectFileIndex.class));
projectServices.register(BuildReferenceManager.class, new MockBuildReferenceManager(project));
projectServices.register(TransitiveDependencyMap.class, new TransitiveDependencyMap(project));
projectServices.register(ProjectScopeBuilder.class, new ProjectScopeBuilderImpl(project));
@@ -651,15 +653,15 @@
ImmutableMap<String, PsiClass> classes =
ImmutableMap.of(
"com.google.example.independent.LibraryView",
- new MockPsiClass(psiManager, independentLibraryView),
+ mockPsiClass(independentLibraryView),
"com.google.example.independent.LibraryView2",
- new MockPsiClass(psiManager, independentLibraryView2),
+ mockPsiClass(independentLibraryView2),
"com.google.example.independent.Library2View",
- new MockPsiClass(psiManager, independentLibrary2View),
+ mockPsiClass(independentLibrary2View),
"com.google.example.dependent.LibraryView",
- new MockPsiClass(psiManager, dependentLibraryView),
+ mockPsiClass(dependentLibraryView),
"com.google.example.ResourceView",
- new MockPsiClass(psiManager, resourceView));
+ mockPsiClass(resourceView));
ImmutableMap<File, TargetKey> sourceToTarget =
ImmutableMap.of(
@@ -679,6 +681,14 @@
projectServices.register(SourceToTargetMap.class, new MockSourceToTargetMap(sourceToTarget));
}
+ private static PsiClass mockPsiClass(VirtualFile virtualFile) {
+ PsiFile psiFile = mock(PsiFile.class);
+ when(psiFile.getVirtualFile()).thenReturn(virtualFile);
+ PsiClass psiClass = mock(PsiClass.class);
+ when(psiClass.getContainingFile()).thenReturn(psiFile);
+ return psiClass;
+ }
+
private static class MockBlazeProjectDataManager implements BlazeProjectDataManager {
private BlazeProjectData blazeProjectData;
@@ -711,25 +721,6 @@
}
}
- 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;
diff --git a/aswb/tests/unittests/com/google/idea/blaze/android/run/testrecorder/BlazeConfigurationsTest.java b/aswb/tests/unittests/com/google/idea/blaze/android/run/testrecorder/BlazeConfigurationsTest.java
index b48fe02..c9114dd 100644
--- a/aswb/tests/unittests/com/google/idea/blaze/android/run/testrecorder/BlazeConfigurationsTest.java
+++ b/aswb/tests/unittests/com/google/idea/blaze/android/run/testrecorder/BlazeConfigurationsTest.java
@@ -21,7 +21,6 @@
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.gct.testrecorder.run.TestRecorderRunConfigurationProxy;
import com.google.gct.testrecorder.run.TestRecorderRunConfigurationProxyProvider;
@@ -68,6 +67,7 @@
import com.intellij.openapi.project.Project;
import java.io.File;
import java.util.List;
+import java.util.function.Predicate;
import javax.annotation.Nullable;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -278,6 +278,11 @@
}
@Override
+ public String getLanguageSupportDocumentationUrl(String relativeDocName) {
+ return null;
+ }
+
+ @Override
public boolean isBuildFile(String fileName) {
return false;
}
diff --git a/base/BUILD b/base/BUILD
index 7afe714..e51bb7f 100644
--- a/base/BUILD
+++ b/base/BUILD
@@ -3,16 +3,20 @@
java_library(
name = "base",
srcs = glob(["src/**/*.java"]),
+ javacopts = ["-Xep:FutureReturnValueIgnored:OFF"],
resources = glob(["resources/**/*"]),
visibility = ["//visibility:public"],
deps = [
"//common/actionhelper",
"//common/binaryhelper",
+ "//common/concurrency",
"//common/experiments",
"//common/formatter",
+ "//common/guava",
"//intellij_platform_sdk:plugin_api",
"//proto:proto_deps",
"//sdkcompat",
+ "//third_party/auto_value",
"@jsr305_annotations//jar",
],
)
@@ -90,6 +94,7 @@
":unit_test_utils",
"//common/experiments",
"//common/experiments:unit_test_utils",
+ "//common/guava",
"//intellij_platform_sdk:plugin_api_for_tests",
"//proto:proto_deps",
"@jsr305_annotations//jar",
diff --git a/base/src/META-INF/blaze-base.xml b/base/src/META-INF/blaze-base.xml
index f472193..5755e9e 100644
--- a/base/src/META-INF/blaze-base.xml
+++ b/base/src/META-INF/blaze-base.xml
@@ -175,8 +175,6 @@
serviceImplementation="com.google.idea.blaze.base.io.VirtualFileSystemProviderImpl"/>
<applicationService serviceInterface="com.google.idea.blaze.base.buildmodifier.BuildFileModifier"
serviceImplementation="com.google.idea.blaze.base.lang.buildfile.actions.BuildFileModifierImpl"/>
- <projectService serviceInterface="com.google.idea.blaze.base.buildmodifier.FileSystemModifier"
- serviceImplementation="com.google.idea.blaze.base.buildmodifier.FileSystemModifierImpl"/>
<applicationService serviceInterface="com.google.idea.blaze.base.run.targetfinder.TargetFinder"
serviceImplementation="com.google.idea.blaze.base.run.targetfinder.TargetFinderImpl"/>
<applicationService serviceInterface="com.google.idea.blaze.base.command.info.BlazeInfoRunner"
@@ -350,11 +348,14 @@
<extensionPoint qualifiedName="com.google.idea.blaze.AspectStrategyProvider" interface="com.google.idea.blaze.base.sync.aspects.strategy.AspectStrategyProvider"/>
<extensionPoint qualifiedName="com.google.idea.blaze.DistributedExecutorSupport" interface="com.google.idea.blaze.base.run.DistributedExecutorSupport"/>
<extensionPoint qualifiedName="com.google.idea.blaze.FileStringParser" interface="com.google.idea.blaze.base.run.filter.FileResolver"/>
- <extensionPoint qualifiedName="com.google.idea.blaze.BlazeTestXmlFinderStrategy" interface="com.google.idea.blaze.base.run.testlogs.BlazeTestResultFinderStrategy"/>
<extensionPoint qualifiedName="com.google.idea.blaze.BlazeTestEventsHandler" interface="com.google.idea.blaze.base.run.smrunner.BlazeTestEventsHandler"/>
<extensionPoint qualifiedName="com.google.idea.blaze.AttributeSpecificStringLiteralReferenceProvider" interface="com.google.idea.blaze.base.lang.buildfile.references.AttributeSpecificStringLiteralReferenceProvider"/>
<extensionPoint qualifiedName="com.google.idea.blaze.EventLogger" interface="com.google.idea.blaze.base.logging.EventLogger"/>
<extensionPoint qualifiedName="com.google.idea.blaze.ProjectViewDefaultValueProvider" interface="com.google.idea.blaze.base.projectview.section.ProjectViewDefaultValueProvider"/>
+ <extensionPoint qualifiedName="com.google.idea.blaze.TestUiSessionProvider" interface="com.google.idea.blaze.base.run.smrunner.TestUiSessionProvider"/>
+ <extensionPoint qualifiedName="com.google.idea.blaze.BlazeLibrarySorter" interface="com.google.idea.blaze.base.sync.libraries.BlazeLibrarySorter"/>
+ <extensionPoint qualifiedName="com.google.idea.blaze.BuildSystemVersionChecker" interface="com.google.idea.blaze.base.plugin.BuildSystemVersionChecker"/>
+ <extensionPoint qualifiedName="com.google.idea.blaze.BlazeIssueParserProvider" interface="com.google.idea.blaze.base.issueparser.BlazeIssueParserProvider"/>
</extensionPoints>
<extensions defaultExtensionNs="com.google.idea.blaze">
@@ -368,17 +369,19 @@
<BuildSystemProvider implementation="com.google.idea.blaze.base.bazel.BazelBuildSystemProvider" order="last"/>
<BuildifierBinaryProvider implementation="com.google.idea.blaze.base.buildmodifier.BazelBuildifierBinaryProvider"/>
<BlazeCommandRunConfigurationHandlerProvider implementation="com.google.idea.blaze.base.run.confighandler.BlazeCommandGenericRunConfigurationHandlerProvider" order="last"/>
- <TestTargetHeuristic implementation="com.google.idea.blaze.base.run.TargetNameHeuristic" order="first" id="TargetNameHeuristic"/>
- <TestTargetHeuristic implementation="com.google.idea.blaze.base.run.TestTargetSourcesHeuristic"/>
+ <TestTargetHeuristic implementation="com.google.idea.blaze.base.run.TargetNameHeuristic" id="TargetNameHeuristic"/>
+ <TestTargetHeuristic implementation="com.google.idea.blaze.base.run.TestTargetSourcesHeuristic" order="after TargetNameHeuristic"/>
<TestTargetHeuristic implementation="com.google.idea.blaze.base.run.TestSizeHeuristic" order="last" id="TestSizeHeuristic"/>
<RunConfigurationFactory implementation="com.google.idea.blaze.base.run.BlazeBuildTargetRunConfigurationFactory" order="last"/>
<AspectStrategyProvider implementation="com.google.idea.blaze.base.sync.aspects.strategy.AspectStrategyProviderBazel" order="last"/>
<FileStringParser implementation="com.google.idea.blaze.base.run.filter.StandardFileResolver" order="last"/>
- <BlazeTestXmlFinderStrategy implementation="com.google.idea.blaze.base.run.testlogs.TargetPathTestResultFinderStrategy"/>
- <BlazeTestEventsHandler implementation="com.google.idea.blaze.base.run.smrunner.BlazeCompositeTestEventsHandler" order="last"/>
+ <BlazeTestEventsHandler implementation="com.google.idea.blaze.base.run.smrunner.BlazeGenericTestEventsHandler" order="last"/>
<ProjectViewDefaultValueProvider implementation="com.google.idea.blaze.base.projectview.section.sections.DirectorySection$DirectoriesProjectViewDefaultValueProvider"/>
<ProjectViewDefaultValueProvider implementation="com.google.idea.blaze.base.projectview.section.sections.TargetSection$TargetsProjectViewDefaultValueProvider"/>
<ProjectViewDefaultValueProvider implementation="com.google.idea.blaze.base.projectview.section.sections.AdditionalLanguagesSection$AdditionalLanguagesDefaultValueProvider"/>
+ <PrefetchFileSource implementation="com.google.idea.blaze.base.prefetch.ProtoPrefetchFileSource"/>
+ <TestUiSessionProvider implementation="com.google.idea.blaze.base.run.smrunner.BazelTestUiSessionProvider"/>
+ <BuildSystemVersionChecker implementation="com.google.idea.blaze.base.plugin.BazelVersionChecker"/>
</extensions>
</idea-plugin>
diff --git a/base/src/com/google/idea/blaze/base/actions/BlazeBuildService.java b/base/src/com/google/idea/blaze/base/actions/BlazeBuildService.java
index 36a23e9..50abc6d 100644
--- a/base/src/com/google/idea/blaze/base/actions/BlazeBuildService.java
+++ b/base/src/com/google/idea/blaze/base/actions/BlazeBuildService.java
@@ -44,10 +44,14 @@
import com.google.idea.blaze.base.util.SaveUtil;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Key;
import java.util.List;
/** Utility to build various collections of targets. */
public class BlazeBuildService {
+ private static final Key<Long> PROJECT_LAST_BUILD_TIMESTAMP_KEY =
+ Key.create("blaze.project.last.build.timestamp");
+
public static BlazeBuildService getInstance() {
return ServiceManager.getService(BlazeBuildService.class);
}
@@ -86,6 +90,15 @@
"Make project",
"Make project completed successfully",
"Make project failed"));
+
+ // In case the user touched a file, but didn't change its content. The user will get a false
+ // positive for class file out of date. We need a way for the user to suppress the false
+ // message. Clicking the "build project" link should at least make the message go away.
+ project.putUserData(PROJECT_LAST_BUILD_TIMESTAMP_KEY, System.currentTimeMillis());
+ }
+
+ public static Long getLastBuildTimeStamp(Project project) {
+ return project.getUserData(PROJECT_LAST_BUILD_TIMESTAMP_KEY);
}
@VisibleForTesting
@@ -137,6 +150,7 @@
workspaceRoot,
projectViewSet,
blazeProjectData.blazeVersionData,
+ blazeProjectData.workspaceLanguageSettings,
shardedTargets.shardedTargets);
FileCaches.refresh(project);
diff --git a/base/src/com/google/idea/blaze/base/async/executor/BlazeExecutorImpl.java b/base/src/com/google/idea/blaze/base/async/executor/BlazeExecutorImpl.java
index 92b0223..b40667c 100644
--- a/base/src/com/google/idea/blaze/base/async/executor/BlazeExecutorImpl.java
+++ b/base/src/com/google/idea/blaze/base/async/executor/BlazeExecutorImpl.java
@@ -18,6 +18,7 @@
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
+import com.google.idea.common.concurrency.ConcurrencyUtil;
import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
@@ -25,7 +26,9 @@
public class BlazeExecutorImpl extends BlazeExecutor {
private final ListeningExecutorService executorService =
- MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(16));
+ MoreExecutors.listeningDecorator(
+ Executors.newFixedThreadPool(
+ 16, ConcurrencyUtil.namedDaemonThreadPoolFactory(BlazeExecutorImpl.class)));
@Override
public <T> ListenableFuture<T> submit(Callable<T> callable) {
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 85296be..4e01815 100644
--- a/base/src/com/google/idea/blaze/base/bazel/BazelBuildSystemProvider.java
+++ b/base/src/com/google/idea/blaze/base/bazel/BazelBuildSystemProvider.java
@@ -32,6 +32,8 @@
/** Provides the bazel build system name string. */
public class BazelBuildSystemProvider implements BuildSystemProvider {
+ private static final String BAZEL_DOC_SITE = "https://ij.bazel.build/docs";
+
@Override
public BuildSystem buildSystem() {
return BuildSystem.Bazel;
@@ -64,7 +66,12 @@
@Override
public String getProjectViewDocumentationUrl() {
- return "https://ij.bazel.build/docs/project-views.html";
+ return BAZEL_DOC_SITE + "/project-views.html";
+ }
+
+ @Override
+ public String getLanguageSupportDocumentationUrl(String relativeDocName) {
+ return String.format("%s/%s.html", BAZEL_DOC_SITE, relativeDocName);
}
@Override
diff --git a/base/src/com/google/idea/blaze/base/bazel/BazelVersion.java b/base/src/com/google/idea/blaze/base/bazel/BazelVersion.java
index 19be022..ec82ddb 100644
--- a/base/src/com/google/idea/blaze/base/bazel/BazelVersion.java
+++ b/base/src/com/google/idea/blaze/base/bazel/BazelVersion.java
@@ -29,14 +29,14 @@
public class BazelVersion implements Serializable {
private static final long serialVersionUID = 1L;
- public static final BazelVersion UNKNOWN = new BazelVersion(0, 0, 0);
+ static final BazelVersion DEVELOPMENT = new BazelVersion(999, 999, 999);
private static final Pattern PATTERN = Pattern.compile("([[0-9]\\.]+)");
- public final int major;
- public final int minor;
- public final int bugfix;
+ final int major;
+ final int minor;
+ final int bugfix;
- BazelVersion(int major, int minor, int bugfix) {
+ public BazelVersion(int major, int minor, int bugfix) {
this.bugfix = bugfix;
this.minor = minor;
this.major = major;
@@ -44,21 +44,22 @@
@VisibleForTesting
static BazelVersion parseVersion(@Nullable String string) {
+ // treat all unknown / development versions as the very latest version
if (string == null) {
- return UNKNOWN;
+ return DEVELOPMENT;
}
Matcher matcher = PATTERN.matcher(string);
if (!matcher.find()) {
- return UNKNOWN;
+ return DEVELOPMENT;
}
try {
BazelVersion version = parseVersion(matcher.group(1).split("\\."));
if (version == null) {
- return UNKNOWN;
+ return DEVELOPMENT;
}
return version;
} catch (Exception e) {
- return UNKNOWN;
+ return DEVELOPMENT;
}
}
@@ -76,11 +77,16 @@
return new BazelVersion(major, minor, bugfix);
}
- public static BazelVersion parseVersion(BlazeInfo blazeInfo) {
+ static BazelVersion parseVersion(BlazeInfo blazeInfo) {
return parseVersion(blazeInfo.get(BlazeInfo.RELEASE));
}
@Override
+ public String toString() {
+ return String.format("%s.%s.%s", major, minor, bugfix);
+ }
+
+ @Override
public boolean equals(Object o) {
if (this == o) {
return true;
@@ -97,6 +103,10 @@
return Objects.hashCode(major, minor, bugfix);
}
+ public boolean isAtLeast(BazelVersion version) {
+ return isAtLeast(version.major, version.minor, version.bugfix);
+ }
+
public boolean isAtLeast(int major, int minor, int bugfix) {
return ComparisonChain.start()
.compare(this.major, major)
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 22392d4..214bc96 100644
--- a/base/src/com/google/idea/blaze/base/bazel/BuildSystemProvider.java
+++ b/base/src/com/google/idea/blaze/base/bazel/BuildSystemProvider.java
@@ -94,6 +94,15 @@
@Nullable
String getProjectViewDocumentationUrl();
+ /**
+ * The URL providing documentation for language support, if one can be found.
+ *
+ * @param relativeDocName the path to the language doc, relative to the plugin documentation
+ * site's base URL, without the webpage's file extension.
+ */
+ @Nullable
+ String getLanguageSupportDocumentationUrl(String relativeDocName);
+
/** Check if the given filename is a valid BUILD file name. */
boolean isBuildFile(String fileName);
diff --git a/base/src/com/google/idea/blaze/base/buildmodifier/BuildFileFormatter.java b/base/src/com/google/idea/blaze/base/buildmodifier/BuildFileFormatter.java
index 5875466..52690ec 100644
--- a/base/src/com/google/idea/blaze/base/buildmodifier/BuildFileFormatter.java
+++ b/base/src/com/google/idea/blaze/base/buildmodifier/BuildFileFormatter.java
@@ -15,17 +15,27 @@
*/
package com.google.idea.blaze.base.buildmodifier;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.idea.blaze.base.async.executor.BlazeExecutor;
+import com.intellij.openapi.diagnostic.Logger;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
import javax.annotation.Nullable;
/** Formats BUILD files using 'buildifier' */
public class BuildFileFormatter {
+ private static final Logger logger = Logger.getInstance(BuildFileFormatter.class);
+ // 10 seconds ought to be enough for formatting a BUILD file...
+ private static final long TIMEOUT_SECS = 10;
+
@Nullable
private static File getBuildifierBinary() {
for (BuildifierBinaryProvider provider : BuildifierBinaryProvider.EP_NAME.getExtensions()) {
@@ -37,7 +47,29 @@
return null;
}
- static String formatText(String text) {
+ /**
+ * Format the BUILD file with a timeout. The tool may be fetched from a network filesystem, and we
+ * don't want to block the UI if there is a network issue.
+ *
+ * @return formatted text, or null if there is an error or timeout
+ */
+ @Nullable
+ static String formatTextWithTimeout(String text) {
+ BlazeExecutor executor = BlazeExecutor.getInstance();
+ ListenableFuture<String> result = executor.submit(() -> formatText(text));
+ try {
+ return result.get(TIMEOUT_SECS, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ return null;
+ } catch (ExecutionException | TimeoutException e) {
+ logger.warn(e);
+ return null;
+ }
+ }
+
+ @Nullable
+ private static String formatText(String text) {
try {
File buildifierBinary = getBuildifierBinary();
if (buildifierBinary == null) {
@@ -49,9 +81,9 @@
file.delete();
return formattedFile;
} catch (IOException e) {
- e.printStackTrace();
+ logger.warn(e);
}
- return text;
+ return null;
}
private static void formatFile(File file, String buildifierBinaryPath) throws IOException {
diff --git a/base/src/com/google/idea/blaze/base/buildmodifier/BuildifierDelegatingCodeStyleManager.java b/base/src/com/google/idea/blaze/base/buildmodifier/BuildifierDelegatingCodeStyleManager.java
index ca0827d..d00fcd1 100644
--- a/base/src/com/google/idea/blaze/base/buildmodifier/BuildifierDelegatingCodeStyleManager.java
+++ b/base/src/com/google/idea/blaze/base/buildmodifier/BuildifierDelegatingCodeStyleManager.java
@@ -101,7 +101,7 @@
String text, Collection<TextRange> ranges) {
ImmutableMap.Builder<TextRange, String> output = ImmutableMap.builder();
for (TextRange range : ranges) {
- String result = BuildFileFormatter.formatText(range.substring(text));
+ String result = BuildFileFormatter.formatTextWithTimeout(range.substring(text));
if (result == null) {
return ImmutableMap.of();
}
diff --git a/base/src/com/google/idea/blaze/base/buildmodifier/FileSaveHandler.java b/base/src/com/google/idea/blaze/base/buildmodifier/FileSaveHandler.java
index daeae84..0b2e14b 100644
--- a/base/src/com/google/idea/blaze/base/buildmodifier/FileSaveHandler.java
+++ b/base/src/com/google/idea/blaze/base/buildmodifier/FileSaveHandler.java
@@ -45,7 +45,10 @@
int lines = document.getLineCount();
if (lines > 0) {
String text = document.getText();
- String formattedText = BuildFileFormatter.formatText(text);
+ String formattedText = BuildFileFormatter.formatTextWithTimeout(text);
+ if (formattedText == null) {
+ return;
+ }
updateDocument(document, formattedText);
}
}
diff --git a/base/src/com/google/idea/blaze/base/buildmodifier/FileSystemModifier.java b/base/src/com/google/idea/blaze/base/buildmodifier/FileSystemModifier.java
deleted file mode 100644
index 96719c1..0000000
--- a/base/src/com/google/idea/blaze/base/buildmodifier/FileSystemModifier.java
+++ /dev/null
@@ -1,37 +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.buildmodifier;
-
-import com.google.idea.blaze.base.model.primitives.WorkspacePath;
-import com.intellij.openapi.components.ServiceManager;
-import com.intellij.openapi.project.Project;
-import java.io.File;
-import javax.annotation.Nullable;
-import org.jetbrains.annotations.NotNull;
-
-/** Modifies the file system. Interface so we can mock it in tests */
-public abstract class FileSystemModifier {
-
- public static FileSystemModifier getInstance(@NotNull Project project) {
- return ServiceManager.getService(project, FileSystemModifier.class);
- }
-
- @Nullable
- public abstract File makeWorkspacePathDirs(@NotNull WorkspacePath workspacePath);
-
- @Nullable
- public abstract File createFile(@NotNull WorkspacePath parentDir, @NotNull String name);
-}
diff --git a/base/src/com/google/idea/blaze/base/buildmodifier/FileSystemModifierImpl.java b/base/src/com/google/idea/blaze/base/buildmodifier/FileSystemModifierImpl.java
deleted file mode 100644
index 7e873ee..0000000
--- a/base/src/com/google/idea/blaze/base/buildmodifier/FileSystemModifierImpl.java
+++ /dev/null
@@ -1,57 +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.buildmodifier;
-
-import com.google.idea.blaze.base.model.primitives.WorkspacePath;
-import com.google.idea.blaze.base.model.primitives.WorkspaceRoot;
-import com.intellij.openapi.project.Project;
-import java.io.File;
-import java.io.IOException;
-import javax.annotation.Nullable;
-import org.jetbrains.annotations.NotNull;
-
-class FileSystemModifierImpl extends FileSystemModifier {
-
- private final Project project;
-
- public FileSystemModifierImpl(@NotNull Project project) {
- this.project = project;
- }
-
- @Nullable
- @Override
- public File makeWorkspacePathDirs(@NotNull WorkspacePath workspacePath) {
- WorkspaceRoot workspaceRoot = WorkspaceRoot.fromProject(project);
- File dir = workspaceRoot.fileForPath(workspacePath);
- boolean success = dir.mkdirs();
- return success ? dir : null;
- }
-
- @Nullable
- @Override
- public File createFile(@NotNull WorkspacePath parentDirectory, @NotNull String name) {
- WorkspaceRoot workspaceRoot = WorkspaceRoot.fromProject(project);
- File dir = workspaceRoot.fileForPath(parentDirectory);
- File f = new File(dir, name);
- boolean success;
- try {
- success = f.createNewFile();
- } catch (IOException e) {
- success = false;
- }
- return success ? f : null;
- }
-}
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 a44b056..6cb3b09 100644
--- a/base/src/com/google/idea/blaze/base/command/BlazeFlags.java
+++ b/base/src/com/google/idea/blaze/base/command/BlazeFlags.java
@@ -15,12 +15,11 @@
*/
package com.google.idea.blaze.base.command;
-import com.google.common.collect.ImmutableList;
+import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Lists;
import com.google.idea.blaze.base.projectview.ProjectViewSet;
import com.google.idea.blaze.base.projectview.section.sections.BuildFlagsSection;
-import com.google.idea.blaze.base.settings.Blaze;
-import com.google.idea.blaze.base.settings.Blaze.BuildSystem;
+import com.google.idea.blaze.base.projectview.section.sections.SyncFlagsSection;
import com.google.idea.common.experiments.BoolExperiment;
import com.intellij.execution.configurations.ParametersList;
import com.intellij.openapi.project.Project;
@@ -47,39 +46,50 @@
public static final String DISABLE_TEST_SHARDING = "--test_sharding_strategy=disabled";
// Filters the unit tests that are run (used with regexp for Java/Robolectric tests).
public static final String TEST_FILTER = "--test_filter";
- // 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
- // can be used for API 23 or higher, for which it is preferred to --incremental
- public static final String SPLIT_APKS = "--split_apks";
// Re-run the test even if the results are cached.
public static final String NO_CACHE_TEST_RESULTS = "--nocache_test_results";
+ public static final String LOCAL_TEST_EXECUTION = "--test_strategy=local";
public static final String EXPERIMENTAL_SHOW_ARTIFACTS = "--experimental_show_artifacts";
- public static List<String> buildFlags(Project project, ProjectViewSet projectViewSet) {
- BuildSystem buildSystem = Blaze.getBuildSystem(project);
+ public static final String DELETED_PACKAGES = "--deleted_packages";
+
+ public static List<String> blazeFlags(
+ Project project,
+ ProjectViewSet projectViewSet,
+ BlazeCommandName command,
+ BlazeInvocationContext context) {
List<String> flags = Lists.newArrayList();
for (BuildFlagsProvider buildFlagsProvider : BuildFlagsProvider.EP_NAME.getExtensions()) {
- buildFlagsProvider.addBuildFlags(buildSystem, projectViewSet, flags);
+ buildFlagsProvider.addBuildFlags(project, projectViewSet, command, flags);
}
flags.addAll(expandBuildFlags(projectViewSet.listItems(BuildFlagsSection.KEY)));
+ if (context == BlazeInvocationContext.Sync) {
+ flags.addAll(expandBuildFlags(projectViewSet.listItems(SyncFlagsSection.KEY)));
+ }
return flags;
}
- // Pass-through arg for sending adb options during mobile-install.
- public static final String ADB_ARG = "--adb_arg=";
-
- public static ImmutableList<String> adbSerialFlags(String serial) {
- return ImmutableList.of(ADB_ARG + "-s ", ADB_ARG + serial);
- }
+ public static final String ADB_PATH = "--adb_path";
+ public static final String DEVICE = "--device";
// Pass-through arg for sending test arguments.
public static final String TEST_ARG = "--test_arg=";
private static final String TOOL_TAG = "--tool_tag=ijwb:";
+ // TODO: remove these when mobile-install V1 is obsolete
+ // 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
+ // can be used for API 23 or higher, for which it is preferred to --incremental
+ public static final String SPLIT_APKS = "--split_apks";
+ // Pass-through arg for sending adb options during mobile-install.
+ public static final String ADB_ARG = "--adb_arg=";
+ public static final String ADB = "--adb";
+
// We add this to every single BlazeCommand instance. It's for tracking usage.
+ @VisibleForTesting
public static String getToolTagFlag() {
String platformPrefix = PlatformUtils.getPlatformPrefix();
diff --git a/base/src/com/google/idea/blaze/base/command/BlazeInvocationContext.java b/base/src/com/google/idea/blaze/base/command/BlazeInvocationContext.java
new file mode 100644
index 0000000..b1517b8
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/command/BlazeInvocationContext.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2017 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.base.command;
+
+/** The context in which a blaze command is invoked. */
+public enum BlazeInvocationContext {
+ RunConfiguration,
+ Sync,
+}
diff --git a/base/src/com/google/idea/blaze/base/command/BuildFlagsProvider.java b/base/src/com/google/idea/blaze/base/command/BuildFlagsProvider.java
index 469d663..3d1e133 100644
--- a/base/src/com/google/idea/blaze/base/command/BuildFlagsProvider.java
+++ b/base/src/com/google/idea/blaze/base/command/BuildFlagsProvider.java
@@ -16,15 +16,17 @@
package com.google.idea.blaze.base.command;
import com.google.idea.blaze.base.projectview.ProjectViewSet;
-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;
-/** Provides additional build flags for bazel/blaze commands. */
+/** Provides additional flags for bazel/blaze commands. */
public interface BuildFlagsProvider {
ExtensionPointName<BuildFlagsProvider> EP_NAME =
ExtensionPointName.create("com.google.idea.blaze.BuildFlagsProvider");
- void addBuildFlags(BuildSystem buildSystem, ProjectViewSet projectViewSet, List<String> flags);
+ /** Flags to add to blaze/bazel invocations of the given type. */
+ void addBuildFlags(
+ Project project, ProjectViewSet projectViewSet, BlazeCommandName command, List<String> flags);
}
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 fcdfc70..afb4157 100644
--- a/base/src/com/google/idea/blaze/base/command/BuildFlagsProviderImpl.java
+++ b/base/src/com/google/idea/blaze/base/command/BuildFlagsProviderImpl.java
@@ -16,7 +16,7 @@
package com.google.idea.blaze.base.command;
import com.google.idea.blaze.base.projectview.ProjectViewSet;
-import com.google.idea.blaze.base.settings.Blaze.BuildSystem;
+import com.intellij.openapi.project.Project;
import java.util.List;
/** Flags added to blaze/bazel build commands. */
@@ -24,7 +24,10 @@
@Override
public void addBuildFlags(
- BuildSystem buildSystem, ProjectViewSet projectViewSet, List<String> flags) {
+ Project project,
+ ProjectViewSet projectViewSet,
+ BlazeCommandName command,
+ List<String> flags) {
flags.add("--curses=no");
flags.add("--color=no");
flags.add("--noexperimental_ui");
diff --git a/base/src/com/google/idea/blaze/base/command/buildresult/BuildEventProtocolOutputReader.java b/base/src/com/google/idea/blaze/base/command/buildresult/BuildEventProtocolOutputReader.java
new file mode 100644
index 0000000..0320715
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/command/buildresult/BuildEventProtocolOutputReader.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright 2017 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.base.command.buildresult;
+
+import static com.google.idea.common.guava.GuavaHelper.toImmutableList;
+import static com.google.idea.common.guava.GuavaHelper.toImmutableSet;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.idea.blaze.base.model.primitives.Kind;
+import com.google.idea.blaze.base.model.primitives.Label;
+import com.google.idea.blaze.base.run.testlogs.BlazeTestResult;
+import com.google.idea.blaze.base.run.testlogs.BlazeTestResult.TestStatus;
+import com.google.idea.blaze.base.run.testlogs.BlazeTestResults;
+import com.google.idea.blaze.base.util.UrlUtil;
+import com.google.repackaged.devtools.build.lib.buildeventstream.BuildEventStreamProtos;
+import com.google.repackaged.devtools.build.lib.buildeventstream.BuildEventStreamProtos.BuildEventId.NamedSetOfFilesId;
+import com.google.repackaged.devtools.build.lib.buildeventstream.BuildEventStreamProtos.OutputGroup;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.function.Predicate;
+import javax.annotation.Nullable;
+
+/** Utility methods for reading Blaze's build event procotol output, in proto form. */
+public final class BuildEventProtocolOutputReader {
+
+ private BuildEventProtocolOutputReader() {}
+
+ /**
+ * Reads all test results from a BEP-formatted {@link InputStream}.
+ *
+ * @throws IOException if the BEP {@link InputStream} is incorrectly formatted
+ */
+ public static BlazeTestResults parseTestResults(InputStream inputStream) throws IOException {
+ Map<String, Kind> labelToTargetKind = new HashMap<>();
+ ImmutableList.Builder<BlazeTestResult> results = ImmutableList.builder();
+ BuildEventStreamProtos.BuildEvent event;
+ while ((event = BuildEventStreamProtos.BuildEvent.parseDelimitedFrom(inputStream)) != null) {
+ switch (event.getId().getIdCase()) {
+ case TARGET_COMPLETED:
+ String label = event.getId().getTargetCompleted().getLabel();
+ Kind kind = parseTargetKind(event.getCompleted().getTargetKind());
+ if (kind != null) {
+ labelToTargetKind.put(label, kind);
+ }
+ continue;
+ case TARGET_CONFIGURED:
+ label = event.getId().getTargetConfigured().getLabel();
+ kind = parseTargetKind(event.getConfigured().getTargetKind());
+ if (kind != null) {
+ labelToTargetKind.put(label, kind);
+ }
+ continue;
+ case TEST_RESULT:
+ label = event.getId().getTestResult().getLabel();
+ results.add(parseTestResult(label, labelToTargetKind.get(label), event.getTestResult()));
+ continue;
+ default: // continue
+ }
+ }
+ return BlazeTestResults.fromFlatList(results.build());
+ }
+
+ /** Convert BEP 'target_kind' to our internal format */
+ @Nullable
+ private static Kind parseTargetKind(String kind) {
+ return kind.endsWith(" rule")
+ ? Kind.fromString(kind.substring(0, kind.length() - " rule".length()))
+ : null;
+ }
+
+ private static BlazeTestResult parseTestResult(
+ String label, @Nullable Kind kind, BuildEventStreamProtos.TestResult testResult) {
+ ImmutableSet<File> files =
+ testResult
+ .getTestActionOutputList()
+ .stream()
+ .map(file -> parseFile(file, path -> path.endsWith(".xml")))
+ .filter(Objects::nonNull)
+ .collect(toImmutableSet());
+ return BlazeTestResult.create(
+ Label.create(label), kind, convertTestStatus(testResult.getStatus()), files);
+ }
+
+ private static TestStatus convertTestStatus(BuildEventStreamProtos.TestStatus protoStatus) {
+ if (protoStatus == BuildEventStreamProtos.TestStatus.UNRECOGNIZED) {
+ // for forward-compatibility
+ return TestStatus.NO_STATUS;
+ }
+ return TestStatus.valueOf(protoStatus.name());
+ }
+
+ /**
+ * Reads all output files listed in the BEP output that satisfy the specified predicate.
+ *
+ * @throws IOException if the BEP output file is incorrectly formatted
+ */
+ static ImmutableList<File> parseAllOutputFilenames(
+ InputStream inputStream, Predicate<String> fileFilter) throws IOException {
+ ImmutableList.Builder<File> files = ImmutableList.builder();
+ BuildEventStreamProtos.BuildEvent event;
+ while ((event = BuildEventStreamProtos.BuildEvent.parseDelimitedFrom(inputStream)) != null) {
+ files.addAll(parseFilenames(event, fileFilter));
+ }
+ return files.build();
+ }
+
+ /**
+ * Reads all artifacts associated with the given target that satisfy the specified predicate.
+ *
+ * @throws IOException if the BEP output file is incorrectly formatted
+ */
+ static ImmutableList<File> parseArtifactsForTarget(
+ InputStream inputStream, Label label, Predicate<String> fileFilter) throws IOException {
+ Map<String, List<BuildEventStreamProtos.File>> fileSets = new HashMap<>();
+ List<String> fileSetsForLabel = new ArrayList<>();
+ BuildEventStreamProtos.BuildEvent event;
+ while ((event = BuildEventStreamProtos.BuildEvent.parseDelimitedFrom(inputStream)) != null) {
+ if (event.getId().hasNamedSet() && event.hasNamedSetOfFiles()) {
+ fileSets.put(
+ event.getId().getNamedSet().getId(), event.getNamedSetOfFiles().getFilesList());
+ } else if (isTargetCompletedEvent(event, label)) {
+ fileSetsForLabel.addAll(getTargetFileSets(event));
+ }
+ }
+ return fileSetsForLabel
+ .stream()
+ .map(fileSets::get)
+ .flatMap(List::stream)
+ .map(file -> parseFile(file, fileFilter))
+ .collect(toImmutableList());
+ }
+
+ private static boolean isTargetCompletedEvent(
+ BuildEventStreamProtos.BuildEvent event, Label label) {
+ return event.getId().hasTargetCompleted()
+ && event.hasCompleted()
+ && label.toString().equals(event.getId().getTargetCompleted().getLabel());
+ }
+
+ /** Returns all file set IDs associated with the given target completed event. */
+ private static ImmutableList<String> getTargetFileSets(BuildEventStreamProtos.BuildEvent event) {
+ if (!event.hasCompleted()) {
+ return ImmutableList.of();
+ }
+ return event
+ .getCompleted()
+ .getOutputGroupList()
+ .stream()
+ .map(OutputGroup::getFileSetsList)
+ .flatMap(List::stream)
+ .map(NamedSetOfFilesId::getId)
+ .collect(toImmutableList());
+ }
+
+ /**
+ * If this is a NamedSetOfFiles event, reads all associated output files. Otherwise returns an
+ * empty list.
+ */
+ private static ImmutableList<File> parseFilenames(
+ BuildEventStreamProtos.BuildEvent event, Predicate<String> fileFilter) {
+ if (!event.hasNamedSetOfFiles()) {
+ return ImmutableList.of();
+ }
+ return event
+ .getNamedSetOfFiles()
+ .getFilesList()
+ .stream()
+ .map(f -> parseFile(f, fileFilter))
+ .filter(Objects::nonNull)
+ .collect(toImmutableList());
+ }
+
+ @Nullable
+ private static File parseFile(BuildEventStreamProtos.File file, Predicate<String> fileFilter) {
+ String filePath = UrlUtil.urlToFilePath(file.getUri());
+ return filePath != null && fileFilter.test(filePath) ? new File(filePath) : null;
+ }
+}
diff --git a/base/src/com/google/idea/blaze/base/command/buildresult/BuildEventProtocolUtils.java b/base/src/com/google/idea/blaze/base/command/buildresult/BuildEventProtocolUtils.java
new file mode 100644
index 0000000..6cdf791
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/command/buildresult/BuildEventProtocolUtils.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2017 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.base.command.buildresult;
+
+import com.google.common.collect.ImmutableList;
+import java.io.File;
+import java.util.UUID;
+
+/**
+ * Utility methods for accessing blaze build event data via the build event protocol (BEP for
+ * short).
+ */
+public final class BuildEventProtocolUtils {
+
+ // Instructs BEP to use local file paths (file://...) rather than objfs blobids.
+ private static final String LOCAL_FILE_PATHS =
+ "--noexperimental_build_event_binary_file_path_conversion";
+
+ private BuildEventProtocolUtils() {}
+
+ /**
+ * Creates a temporary output file to write the BEP data to. Callers are responsible for deleting
+ * this file after use.
+ */
+ public static File createTempOutputFile() {
+ File tempDir = new File(System.getProperty("java.io.tmpdir"));
+ String suffix = UUID.randomUUID().toString();
+ String fileName = "intellij-bep-" + suffix;
+ File tempFile = new File(tempDir, fileName);
+ // Callers should delete this file immediately after use. Add a shutdown hook as well, in case
+ // the application exits before then.
+ tempFile.deleteOnExit();
+ return tempFile;
+ }
+
+ /** Returns a build flag instructing blaze to write build events to the given output file. */
+ public static ImmutableList<String> getBuildFlags(File outputFile) {
+ return ImmutableList.of(
+ "--experimental_build_event_binary_file=" + outputFile.getPath(), LOCAL_FILE_PATHS);
+ }
+}
diff --git a/base/src/com/google/idea/blaze/base/command/buildresult/BuildResultHelper.java b/base/src/com/google/idea/blaze/base/command/buildresult/BuildResultHelper.java
index 9adf5e2..cce2e00 100644
--- a/base/src/com/google/idea/blaze/base/command/buildresult/BuildResultHelper.java
+++ b/base/src/com/google/idea/blaze/base/command/buildresult/BuildResultHelper.java
@@ -17,26 +17,25 @@
import com.google.common.collect.ImmutableList;
import com.google.idea.blaze.base.async.process.LineProcessingOutputStream.LineProcessor;
-import com.google.idea.common.experiments.BoolExperiment;
+import com.google.idea.blaze.base.model.BlazeVersionData;
+import com.google.idea.blaze.base.model.primitives.Label;
+import java.io.Closeable;
import java.io.File;
import java.io.OutputStream;
import java.util.List;
import java.util.function.Predicate;
/** Assists in getting build artifacts from a build operation. */
-public interface BuildResultHelper {
- // This experiment does *not* work yet and should remain off
- BoolExperiment USE_BEP = new BoolExperiment("use.bep", false);
+public interface BuildResultHelper extends Closeable {
/**
* Constructs a new build result helper.
*
+ * @param blazeVersion The blaze version used for the build invocation.
* @param files A filter for the output artifacts you are interested in.
*/
- static BuildResultHelper forFiles(Predicate<String> files) {
- return USE_BEP.getValue()
- ? new BuildResultHelperBep(files)
- : new BuildResultHelperStderr(files);
+ static BuildResultHelper forFiles(BlazeVersionData blazeVersion, Predicate<String> files) {
+ return new BuildResultHelperBep(files);
}
/**
@@ -62,4 +61,15 @@
* @return The build artifacts from the build operation.
*/
ImmutableList<File> getBuildArtifacts();
+
+ /**
+ * Returns the build artifacts, attempting to filter out all artifacts not directly produced by
+ * the specified target. Some implementations may return artifacts produced by other targets.
+ *
+ * <p>May only be called once the build is complete, or no artifacts will be returned.
+ */
+ ImmutableList<File> getBuildArtifactsForTarget(Label target);
+
+ @Override
+ void close();
}
diff --git a/base/src/com/google/idea/blaze/base/command/buildresult/BuildResultHelperBep.java b/base/src/com/google/idea/blaze/base/command/buildresult/BuildResultHelperBep.java
index 8a32e85..691e08d 100644
--- a/base/src/com/google/idea/blaze/base/command/buildresult/BuildResultHelperBep.java
+++ b/base/src/com/google/idea/blaze/base/command/buildresult/BuildResultHelperBep.java
@@ -18,9 +18,7 @@
import com.google.common.collect.ImmutableList;
import com.google.idea.blaze.base.async.process.LineProcessingOutputStream;
import com.google.idea.blaze.base.async.process.LineProcessingOutputStream.LineProcessor;
-import com.google.repackaged.devtools.build.lib.buildeventstream.BuildEventStreamProtos.BuildEvent;
-import com.google.repackaged.devtools.build.lib.buildeventstream.BuildEventStreamProtos.BuildEventId;
-import com.google.repackaged.devtools.build.lib.buildeventstream.BuildEventStreamProtos.BuildEventId.IdCase;
+import com.google.idea.blaze.base.model.primitives.Label;
import com.intellij.openapi.diagnostic.Logger;
import java.io.BufferedInputStream;
import java.io.File;
@@ -29,7 +27,7 @@
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
-import java.util.UUID;
+import java.util.Optional;
import java.util.function.Predicate;
/**
@@ -39,6 +37,7 @@
* build events.
*/
class BuildResultHelperBep implements BuildResultHelper {
+
private static final Logger logger = Logger.getInstance(BuildResultHelperBep.class);
private final File outputFile;
private final Predicate<String> fileFilter;
@@ -46,15 +45,12 @@
BuildResultHelperBep(Predicate<String> fileFilter) {
this.fileFilter = fileFilter;
- File tempDir = new File(System.getProperty("java.io.tmpdir"));
- String suffix = UUID.randomUUID().toString();
- String fileName = "intellij-bep-" + suffix;
- this.outputFile = new File(tempDir, fileName);
+ outputFile = BuildEventProtocolUtils.createTempOutputFile();
}
@Override
public List<String> getBuildFlags() {
- return ImmutableList.of("--experimental_build_event_binary_file=" + outputFile.getPath());
+ return BuildEventProtocolUtils.getBuildFlags(outputFile);
}
@Override
@@ -65,34 +61,47 @@
@Override
public ImmutableList<File> getBuildArtifacts() {
if (result == null) {
- result = readResult();
+ result =
+ readResult(
+ input ->
+ BuildEventProtocolOutputReader.parseAllOutputFilenames(input, fileFilter))
+ .orElse(ImmutableList.of());
}
return result;
}
- private ImmutableList<File> readResult() {
- ImmutableList.Builder<File> result = ImmutableList.builder();
+ @Override
+ public ImmutableList<File> getBuildArtifactsForTarget(Label target) {
+ if (result == null) {
+ result =
+ readResult(
+ input ->
+ BuildEventProtocolOutputReader.parseArtifactsForTarget(
+ input, target, fileFilter))
+ .orElse(ImmutableList.of());
+ }
+ return result;
+ }
+
+ private <V> Optional<V> readResult(BepReader<V> readAction) {
try (InputStream inputStream = new BufferedInputStream(new FileInputStream(outputFile))) {
- BuildEvent buildEvent;
- while ((buildEvent = BuildEvent.parseDelimitedFrom(inputStream)) != null) {
- BuildEventId buildEventId = buildEvent.getId();
- // Note: This doesn't actually work. BEP does not issue these for actions
- // that don't execute during the build, so we can't find the files
- // for a no-op build the way we can for --experimental_show_artifacts
- if (buildEventId.getIdCase() == IdCase.ACTION_COMPLETED) {
- String output = buildEventId.getActionCompleted().getPrimaryOutput();
- if (fileFilter.test(output)) {
- result.add(new File(output));
- }
- }
- }
+ return Optional.of(readAction.read(inputStream));
} catch (IOException e) {
logger.error(e);
- return ImmutableList.of();
+ return Optional.empty();
+ } finally {
+ close();
}
+ }
+
+ @Override
+ public void close() {
if (!outputFile.delete()) {
logger.warn("Could not delete BEP output file: " + outputFile);
}
- return result.build();
+ }
+
+ private interface BepReader<V> {
+ V read(InputStream inputStream) throws IOException;
}
}
diff --git a/base/src/com/google/idea/blaze/base/command/buildresult/BuildResultHelperStderr.java b/base/src/com/google/idea/blaze/base/command/buildresult/BuildResultHelperStderr.java
deleted file mode 100644
index ef34dd6..0000000
--- a/base/src/com/google/idea/blaze/base/command/buildresult/BuildResultHelperStderr.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright 2017 The Bazel Authors. All rights reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.google.idea.blaze.base.command.buildresult;
-
-import com.google.common.collect.ImmutableList;
-import com.google.idea.blaze.base.async.process.LineProcessingOutputStream;
-import com.google.idea.blaze.base.async.process.LineProcessingOutputStream.LineProcessor;
-import com.google.idea.blaze.base.command.BlazeFlags;
-import java.io.File;
-import java.io.OutputStream;
-import java.util.List;
-import java.util.function.Predicate;
-
-class BuildResultHelperStderr implements BuildResultHelper {
- private final ImmutableList.Builder<File> buildArtifacts = ImmutableList.builder();
- private final ExperimentalShowArtifactsLineProcessor experimentalShowArtifactsLineProcessor;
- private ImmutableList<File> result;
-
- BuildResultHelperStderr(Predicate<String> fileFilter) {
- experimentalShowArtifactsLineProcessor =
- new ExperimentalShowArtifactsLineProcessor(buildArtifacts, fileFilter);
- }
-
- @Override
- public List<String> getBuildFlags() {
- return ImmutableList.of(BlazeFlags.EXPERIMENTAL_SHOW_ARTIFACTS);
- }
-
- @Override
- public OutputStream stderr(LineProcessor... lineProcessors) {
- return LineProcessingOutputStream.of(
- ImmutableList.<LineProcessor>builder()
- .add(experimentalShowArtifactsLineProcessor)
- .add(lineProcessors)
- .build());
- }
-
- @Override
- public ImmutableList<File> getBuildArtifacts() {
- if (result == null) {
- result = buildArtifacts.build();
- }
- return result;
- }
-}
diff --git a/base/src/com/google/idea/blaze/base/command/info/BlazeConfigurationHandler.java b/base/src/com/google/idea/blaze/base/command/info/BlazeConfigurationHandler.java
new file mode 100644
index 0000000..ea99577
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/command/info/BlazeConfigurationHandler.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2017 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.base.command.info;
+
+import java.io.File;
+import javax.annotation.Nullable;
+
+/**
+ * Encodes the default configuration for a given Blaze sync, and provides configuration-related
+ * helper methods.
+ */
+public class BlazeConfigurationHandler {
+
+ public final String defaultConfigurationPathComponent;
+ private final String blazeOutPath;
+
+ public BlazeConfigurationHandler(BlazeInfo blazeInfo) {
+ // Would be simpler to use 'output_path' instead, but there's a Bazel-side bug causing that to
+ // point to the wrong place. Instead derive 'output_path' from 'blaze-out'.
+ File blazeOutDir = blazeInfo.getBlazeBinDirectory().getParentFile().getParentFile();
+ blazeOutPath = blazeOutDir + File.separator;
+ defaultConfigurationPathComponent =
+ getConfigurationPathComponent(blazeInfo.getBlazeBinDirectory());
+ assert (defaultConfigurationPathComponent != null);
+ }
+
+ @Nullable
+ public String getConfigurationPathComponent(File artifact) {
+ if (!artifact.getPath().startsWith(blazeOutPath)) {
+ return null;
+ }
+ String relativePath = artifact.getPath().substring(blazeOutPath.length());
+ int endIndex = relativePath.indexOf(File.separatorChar);
+ return endIndex == -1 ? relativePath : relativePath.substring(0, endIndex);
+ }
+}
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 bf9d694..479f373 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
@@ -30,6 +30,7 @@
public static final String PACKAGE_PATH_KEY = "package_path";
public static final String BUILD_LANGUAGE = "build-language";
public static final String OUTPUT_BASE_KEY = "output_base";
+ public static final String OUTPUT_PATH_KEY = "output_path";
public static final String MASTER_LOG = "master-log";
public static final String COMMAND_LOG = "command_log";
public static final String RELEASE = "release";
diff --git a/base/src/com/google/idea/blaze/base/console/BlazeConsoleView.java b/base/src/com/google/idea/blaze/base/console/BlazeConsoleView.java
index 58162c5..84a54f4 100644
--- a/base/src/com/google/idea/blaze/base/console/BlazeConsoleView.java
+++ b/base/src/com/google/idea/blaze/base/console/BlazeConsoleView.java
@@ -37,10 +37,8 @@
import com.intellij.openapi.wm.ToolWindow;
import com.intellij.ui.content.Content;
import com.intellij.ui.content.ContentFactory;
-import java.awt.BorderLayout;
import javax.annotation.Nullable;
import javax.swing.JComponent;
-import javax.swing.JPanel;
import org.jetbrains.annotations.NotNull;
class BlazeConsoleView implements Disposable {
@@ -55,14 +53,12 @@
@NotNull private final Project myProject;
@NotNull private final ConsoleViewImpl myConsoleView;
- private JPanel myConsolePanel;
private volatile Runnable myStopHandler;
public BlazeConsoleView(@NotNull Project project) {
myProject = project;
myConsoleView = new ConsoleViewImpl(myProject, false);
Disposer.register(this, myConsoleView);
- setupUI();
}
public static BlazeConsoleView getInstance(@NotNull Project project) {
@@ -106,7 +102,6 @@
group.add(new StopAction());
JComponent layoutComponent = layoutUi.getComponent();
- myConsolePanel.add(layoutComponent, BorderLayout.CENTER);
//noinspection ConstantConditions
Content content =
@@ -126,11 +121,6 @@
@Override
public void dispose() {}
- private void setupUI() {
- myConsolePanel = new JPanel();
- myConsolePanel.setLayout(new BorderLayout(0, 0));
- }
-
private class StopAction extends DumbAwareAction {
public StopAction() {
super(IdeBundle.message("action.stop"), null, AllIcons.Actions.Suspend);
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 19c2b10..361f0ff 100644
--- a/base/src/com/google/idea/blaze/base/ide/NewBlazePackageAction.java
+++ b/base/src/com/google/idea/blaze/base/ide/NewBlazePackageAction.java
@@ -20,7 +20,6 @@
import com.google.common.collect.Lists;
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.model.primitives.Kind;
import com.google.idea.blaze.base.model.primitives.Label;
import com.google.idea.blaze.base.model.primitives.WorkspacePath;
@@ -29,7 +28,6 @@
import com.google.idea.blaze.base.projectview.ProjectViewSet;
import com.google.idea.blaze.base.scope.BlazeContext;
import com.google.idea.blaze.base.scope.Scope;
-import com.google.idea.blaze.base.scope.ScopedOperation;
import com.google.idea.blaze.base.scope.output.PrintOutput;
import com.google.idea.blaze.base.scope.output.StatusOutput;
import com.google.idea.blaze.base.settings.Blaze;
@@ -42,6 +40,8 @@
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.LangDataKeys;
import com.intellij.openapi.actionSystem.Presentation;
+import com.intellij.openapi.application.Result;
+import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.project.Project;
@@ -52,7 +52,9 @@
import com.intellij.psi.PsiManager;
import com.intellij.util.PlatformIcons;
import java.io.File;
+import java.io.IOException;
import java.util.List;
+import java.util.Optional;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
import org.jetbrains.annotations.NotNull;
@@ -70,91 +72,81 @@
protected void actionPerformedInBlazeProject(Project project, AnActionEvent event) {
final IdeView view = event.getData(LangDataKeys.IDE_VIEW);
Scope.root(
- new ScopedOperation() {
- @Override
- public void execute(@NotNull final BlazeContext context) {
- if (view == null || project == null) {
- return;
- }
- PsiDirectory directory = getOrChooseDirectory(project, view);
-
- if (directory == null) {
- return;
- }
-
- NewBlazePackageDialog newBlazePackageDialog =
- new NewBlazePackageDialog(project, directory);
- boolean isOk = newBlazePackageDialog.showAndGet();
- if (!isOk) {
- return;
- }
-
- final Label newRule = newBlazePackageDialog.getNewRule();
- final Kind newRuleKind = newBlazePackageDialog.getNewRuleKind();
- // If we returned OK, we should have a non null result
- logger.assertTrue(newRule != null);
- logger.assertTrue(newRuleKind != null);
-
- context.output(
- new StatusOutput(
- String.format("Setting up a new %s package", Blaze.buildSystemName(project))));
-
- boolean success = createPackageOnDisk(project, context, newRule, newRuleKind);
-
- if (!success) {
- return;
- }
-
- File newDirectory =
- WorkspaceRoot.fromProject(project).fileForPath(newRule.blazePackage());
- VirtualFile virtualFile = VfsUtil.findFileByIoFile(newDirectory, true);
- // We just created this file, it should exist
- logger.assertTrue(virtualFile != null);
- PsiFile psiFile = PsiManager.getInstance(project).findFile(virtualFile);
- view.selectElement(psiFile);
+ context -> {
+ if (view == null || project == null) {
+ return;
}
+ PsiDirectory directory = getOrChooseDirectory(project, view);
+
+ if (directory == null) {
+ return;
+ }
+
+ NewBlazePackageDialog newBlazePackageDialog =
+ new NewBlazePackageDialog(project, directory);
+ boolean isOk = newBlazePackageDialog.showAndGet();
+ if (!isOk) {
+ return;
+ }
+
+ final Label newRule = newBlazePackageDialog.getNewRule();
+ final Kind newRuleKind = newBlazePackageDialog.getNewRuleKind();
+ // If we returned OK, we should have a non null result
+ logger.assertTrue(newRule != null);
+ logger.assertTrue(newRuleKind != null);
+
+ context.output(
+ new StatusOutput(
+ String.format("Setting up a new %s package", Blaze.buildSystemName(project))));
+
+ Optional<VirtualFile> virtualFile =
+ createPackageOnDisk(project, context, newRule, newRuleKind);
+ if (!virtualFile.isPresent()) {
+ return;
+ }
+ PsiFile psiFile = PsiManager.getInstance(project).findFile(virtualFile.get());
+ view.selectElement(psiFile);
});
}
- private static Boolean createPackageOnDisk(
- @NotNull Project project,
- @NotNull BlazeContext context,
- @NotNull Label newRule,
- @NotNull Kind ruleKind) {
- LocalHistoryAction action;
+ private Optional<VirtualFile> createPackageOnDisk(
+ Project project, BlazeContext context, Label newRule, Kind ruleKind) {
- String actionName =
+ String commandName =
String.format(
"Creating %s package: %s", Blaze.buildSystemName(project), newRule.toString());
- LocalHistory localHistory = LocalHistory.getInstance();
- action = localHistory.startAction(actionName);
- // Create the package + BUILD file + rule
- FileSystemModifier fileSystemModifier = FileSystemModifier.getInstance(project);
- WorkspacePath newWorkspacePath = newRule.blazePackage();
- File newDirectory = fileSystemModifier.makeWorkspacePathDirs(newWorkspacePath);
- if (newDirectory == null) {
- String errorMessage =
- "Could not create new package directory: " + newWorkspacePath.toString();
- context.output(PrintOutput.error(errorMessage));
- return false;
- }
- File buildFile = fileSystemModifier.createFile(newWorkspacePath, BUILD_FILE_NAME);
- if (buildFile == null) {
- String errorMessage =
- "Could not create new BUILD file in package: " + newWorkspacePath.toString();
- context.output(PrintOutput.error(errorMessage));
- return false;
- }
- BuildFileModifier buildFileModifier = BuildFileModifier.getInstance();
- buildFileModifier.addRule(project, context, newRule, ruleKind);
- action.finish();
+ return new WriteCommandAction<Optional<VirtualFile>>(project, commandName) {
- return true;
+ @Override
+ protected void run(@NotNull Result<Optional<VirtualFile>> result) throws Throwable {
+ LocalHistory localHistory = LocalHistory.getInstance();
+ LocalHistoryAction action = localHistory.startAction(commandName);
+
+ try {
+ WorkspaceRoot workspaceRoot = WorkspaceRoot.fromProject(project);
+ File dir = workspaceRoot.fileForPath(newRule.blazePackage());
+ try {
+ VirtualFile newDirectory = VfsUtil.createDirectories(dir.getPath());
+ VirtualFile newFile = newDirectory.createChildData(this, BUILD_FILE_NAME);
+ BuildFileModifier buildFileModifier = BuildFileModifier.getInstance();
+ buildFileModifier.addRule(project, context, newRule, ruleKind);
+ result.setResult(Optional.of(newFile));
+ } catch (IOException e) {
+ String errorMessage = "Error creating new package: " + e.getMessage();
+ context.output(PrintOutput.error(errorMessage));
+ logger.warn("Error creating new package", e);
+ result.setResult(Optional.empty());
+ }
+ } finally {
+ action.finish();
+ }
+ }
+ }.execute().getResultObject();
}
@Override
- protected void updateForBlazeProject(Project project, @NotNull AnActionEvent event) {
+ protected void updateForBlazeProject(Project project, AnActionEvent event) {
Presentation presentation = event.getPresentation();
if (isEnabled(event)) {
String text = String.format("New %s Package", Blaze.buildSystemName(project));
diff --git a/base/src/com/google/idea/blaze/base/ide/NewBlazePackageDialog.java b/base/src/com/google/idea/blaze/base/ide/NewBlazePackageDialog.java
index 7317f31..9615339 100644
--- a/base/src/com/google/idea/blaze/base/ide/NewBlazePackageDialog.java
+++ b/base/src/com/google/idea/blaze/base/ide/NewBlazePackageDialog.java
@@ -42,25 +42,24 @@
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JPanel;
-import org.jetbrains.annotations.NotNull;
class NewBlazePackageDialog extends DialogWrapper {
private static final Logger logger = Logger.getInstance(NewBlazePackageDialog.class);
- @NotNull private final Project project;
- @NotNull private final PsiDirectory parentDirectory;
+ private final Project project;
+ private final PsiDirectory parentDirectory;
@Nullable private Label newRule;
@Nullable private Kind newRuleKind;
private static final int UI_INDENT_LEVEL = 0;
private static final int TEXT_FIELD_LENGTH = 40;
- @NotNull private final JPanel component = new JPanel(new GridBagLayout());
- @NotNull private final JBLabel packageLabel = new JBLabel("Package name:");
- @NotNull private final JBTextField packageNameField = new JBTextField(TEXT_FIELD_LENGTH);
- @NotNull private final NewRuleUI newRuleUI = new NewRuleUI(TEXT_FIELD_LENGTH);
+ private final JPanel component = new JPanel(new GridBagLayout());
+ private final JBLabel packageLabel = new JBLabel("Package name:");
+ private final JBTextField packageNameField = new JBTextField(TEXT_FIELD_LENGTH);
+ private final NewRuleUI newRuleUI = new NewRuleUI(TEXT_FIELD_LENGTH);
- public NewBlazePackageDialog(@NotNull Project project, @NotNull PsiDirectory currentDirectory) {
+ public NewBlazePackageDialog(Project project, PsiDirectory currentDirectory) {
super(project);
this.project = project;
this.parentDirectory = currentDirectory;
@@ -72,16 +71,21 @@
component.add(packageLabel);
component.add(packageNameField, UiUtil.getFillLineConstraints(UI_INDENT_LEVEL));
newRuleUI.fillUI(component, UI_INDENT_LEVEL);
+ newRuleUI.syncRuleNameTo(packageNameField);
UiUtil.fillBottom(component);
init();
}
- @Nullable
@Override
protected JComponent createCenterPanel() {
return component;
}
+ @Override
+ public JComponent getPreferredFocusedComponent() {
+ return packageNameField;
+ }
+
@Nullable
@Override
protected ValidationInfo doValidate() {
@@ -126,7 +130,7 @@
super.doOKAction();
}
- private void showErrorDialog(@NotNull String message) {
+ private void showErrorDialog(String message) {
String title = CommonBundle.getErrorTitle();
Icon icon = Messages.getErrorIcon();
Messages.showMessageDialog(component, message, title, icon);
diff --git a/base/src/com/google/idea/blaze/base/ide/NewBlazeRuleDialog.java b/base/src/com/google/idea/blaze/base/ide/NewBlazeRuleDialog.java
index ae43540..30c3f37 100644
--- a/base/src/com/google/idea/blaze/base/ide/NewBlazeRuleDialog.java
+++ b/base/src/com/google/idea/blaze/base/ide/NewBlazeRuleDialog.java
@@ -24,16 +24,20 @@
import com.google.idea.blaze.base.scope.BlazeContext;
import com.google.idea.blaze.base.settings.Blaze;
import com.google.idea.blaze.base.ui.UiUtil;
+import com.intellij.history.LocalHistory;
+import com.intellij.history.LocalHistoryAction;
+import com.intellij.openapi.application.Result;
+import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.ValidationInfo;
import com.intellij.openapi.vfs.VirtualFile;
-import java.awt.Dimension;
import java.awt.GridBagLayout;
import java.io.File;
import javax.annotation.Nullable;
import javax.swing.JComponent;
import javax.swing.JPanel;
+import org.jetbrains.annotations.NotNull;
class NewBlazeRuleDialog extends DialogWrapper {
private static final int UI_INDENT = 0;
@@ -46,7 +50,6 @@
private JPanel component = new JPanel(new GridBagLayout());
private final NewRuleUI newRuleUI = new NewRuleUI(TEXT_BOX_WIDTH);
- private static final Dimension componentSize = new Dimension(500, 500);
public NewBlazeRuleDialog(BlazeContext context, Project project, VirtualFile buildFile) {
super(project);
@@ -62,9 +65,6 @@
setOKButtonText("Create");
setCancelButtonText("Cancel");
- component.setPreferredSize(componentSize);
- component.setMinimumSize(componentSize);
-
newRuleUI.fillUI(component, UI_INDENT);
UiUtil.fillBottom(component);
@@ -93,7 +93,22 @@
workspaceRoot.workspacePathFor(new File(buildFile.getParent().getPath()));
Label newRule = Label.create(workspacePath, targetName);
BuildFileModifier buildFileModifier = BuildFileModifier.getInstance();
- boolean success = buildFileModifier.addRule(project, context, newRule, ruleKind);
+
+ String commandName = String.format("Add %s %s rule '%s'", buildSystemName, ruleKind, newRule);
+
+ boolean success =
+ new WriteCommandAction<Boolean>(project, commandName) {
+ @Override
+ protected void run(@NotNull Result<Boolean> result) throws Throwable {
+ LocalHistory localHistory = LocalHistory.getInstance();
+ LocalHistoryAction action = localHistory.startAction(commandName);
+ try {
+ result.setResult(buildFileModifier.addRule(project, context, newRule, ruleKind));
+ } finally {
+ action.finish();
+ }
+ }
+ }.execute().getResultObject();
if (success) {
super.doOKAction();
diff --git a/base/src/com/google/idea/blaze/base/ide/NewRuleUI.java b/base/src/com/google/idea/blaze/base/ide/NewRuleUI.java
index bf0c26a..a2d95bc 100644
--- a/base/src/com/google/idea/blaze/base/ide/NewRuleUI.java
+++ b/base/src/com/google/idea/blaze/base/ide/NewRuleUI.java
@@ -15,54 +15,103 @@
*/
package com.google.idea.blaze.base.ide;
+import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.idea.blaze.base.model.primitives.Kind;
import com.google.idea.blaze.base.model.primitives.TargetName;
import com.google.idea.blaze.base.ui.BlazeValidationError;
import com.google.idea.blaze.base.ui.UiUtil;
import com.intellij.ide.IdeBundle;
+import com.intellij.ide.util.PropertiesComponent;
import com.intellij.openapi.ui.ComboBox;
import com.intellij.openapi.ui.ValidationInfo;
+import com.intellij.ui.DocumentAdapter;
import com.intellij.ui.components.JBLabel;
import com.intellij.ui.components.JBTextField;
import java.util.Collection;
import java.util.List;
import javax.annotation.Nullable;
import javax.swing.JPanel;
-import org.jetbrains.annotations.NotNull;
+import javax.swing.event.DocumentEvent;
final class NewRuleUI {
- private static final String[] POSSIBLE_RULES = {
- "android_library", "java_library", "cc_library", "cc_binary", "proto_library"
- };
+ private static final ImmutableSet<Kind> HANDLED_RULES =
+ ImmutableSet.of(
+ Kind.ANDROID_LIBRARY,
+ Kind.JAVA_LIBRARY,
+ Kind.CC_LIBRARY,
+ Kind.CC_BINARY,
+ Kind.PROTO_LIBRARY);
- @NotNull private final ComboBox ruleComboBox = new ComboBox(POSSIBLE_RULES);
- @NotNull private final JBLabel ruleNameLabel = new JBLabel("Rule name:");
- @NotNull private final JBTextField ruleNameField;
+ private static final String LAST_SELECTED_KIND = "Blaze.Rule.Kind";
+
+ private final ComboBox ruleComboBox = new ComboBox(HANDLED_RULES.toArray(new Kind[0]));
+ private final JBLabel ruleNameLabel = new JBLabel("Rule name:");
+ private final JBTextField ruleNameField;
+
+ private boolean ruleNameEditedByUser = false;
public NewRuleUI(int textFieldLength) {
this.ruleNameField = new JBTextField(textFieldLength);
+ Kind lastValue =
+ Kind.fromString(PropertiesComponent.getInstance().getValue(LAST_SELECTED_KIND));
+ if (HANDLED_RULES.contains(lastValue)) {
+ ruleComboBox.setSelectedItem(lastValue);
+ }
}
- public void fillUI(@NotNull JPanel component, int indentLevel) {
+ public void fillUI(JPanel component, int indentLevel) {
component.add(ruleNameLabel);
component.add(ruleNameField, UiUtil.getFillLineConstraints(indentLevel));
component.add(ruleComboBox, UiUtil.getFillLineConstraints(indentLevel));
}
- @NotNull
public Kind getSelectedRuleKind() {
- return Kind.fromString((String) ruleComboBox.getSelectedItem());
+ Kind kind = (Kind) ruleComboBox.getSelectedItem();
+ PropertiesComponent.getInstance().setValue(LAST_SELECTED_KIND, kind.toString());
+ return kind;
}
- @NotNull
public TargetName getRuleName() {
return TargetName.create(ruleNameField.getText());
}
+ void syncRuleNameTo(JBTextField textField) {
+ ruleNameField
+ .getDocument()
+ .addDocumentListener(
+ new DocumentAdapter() {
+ @Override
+ protected void textChanged(DocumentEvent e) {
+ ruleNameEditedByUser = true;
+ }
+ });
+
+ textField
+ .getDocument()
+ .addDocumentListener(
+ new DocumentAdapter() {
+ @Override
+ protected void textChanged(DocumentEvent e) {
+ if (!ruleNameEditedByUser) {
+ syncRuleName(textField.getText());
+ }
+ }
+ });
+ }
+
+ private void syncRuleName(String text) {
+ ruleNameField.setText(text);
+ // setText triggers an event which flips the field, so we'll set it back to false
+ this.ruleNameEditedByUser = false;
+ }
+
@Nullable
public ValidationInfo validate() {
+ if (ruleComboBox.getSelectedItem() == null) {
+ return new ValidationInfo("Select a rule type", ruleComboBox);
+ }
String ruleName = ruleNameField.getText();
List<BlazeValidationError> errors = Lists.newArrayList();
if (!validateRuleName(ruleName, errors)) {
@@ -73,7 +122,7 @@
}
private static boolean validateRuleName(
- @NotNull String inputString, @Nullable Collection<BlazeValidationError> errors) {
+ String inputString, @Nullable Collection<BlazeValidationError> errors) {
if (inputString.length() == 0) {
BlazeValidationError.collect(
errors, new BlazeValidationError(IdeBundle.message("error.name.should.be.specified")));
diff --git a/base/src/com/google/idea/blaze/base/ideinfo/CIdeInfo.java b/base/src/com/google/idea/blaze/base/ideinfo/CIdeInfo.java
index 215033d..7a993a5 100644
--- a/base/src/com/google/idea/blaze/base/ideinfo/CIdeInfo.java
+++ b/base/src/com/google/idea/blaze/base/ideinfo/CIdeInfo.java
@@ -21,9 +21,11 @@
/** Sister class to {@link JavaIdeInfo} */
public class CIdeInfo implements Serializable {
- private static final long serialVersionUID = 7L;
+ private static final long serialVersionUID = 8L;
public final ImmutableList<ArtifactLocation> sources;
+ public final ImmutableList<ArtifactLocation> headers;
+ public final ImmutableList<ArtifactLocation> textualHeaders;
public final ImmutableList<String> localDefines;
public final ImmutableList<ExecutionRootPath> localIncludeDirectories;
@@ -36,6 +38,8 @@
public CIdeInfo(
ImmutableList<ArtifactLocation> sources,
+ ImmutableList<ArtifactLocation> headers,
+ ImmutableList<ArtifactLocation> textualHeaders,
ImmutableList<String> localDefines,
ImmutableList<ExecutionRootPath> localIncludeDirectories,
ImmutableList<ExecutionRootPath> transitiveIncludeDirectories,
@@ -43,6 +47,8 @@
ImmutableList<String> transitiveDefines,
ImmutableList<ExecutionRootPath> transitiveSystemIncludeDirectories) {
this.sources = sources;
+ this.headers = headers;
+ this.textualHeaders = textualHeaders;
this.localDefines = localDefines;
this.localIncludeDirectories = localIncludeDirectories;
this.transitiveIncludeDirectories = transitiveIncludeDirectories;
@@ -58,6 +64,8 @@
/** Builder for c rule info */
public static class Builder {
private final ImmutableList.Builder<ArtifactLocation> sources = ImmutableList.builder();
+ private final ImmutableList.Builder<ArtifactLocation> headers = ImmutableList.builder();
+ private final ImmutableList.Builder<ArtifactLocation> textualHeaders = ImmutableList.builder();
private final ImmutableList.Builder<String> localDefines = ImmutableList.builder();
private final ImmutableList.Builder<ExecutionRootPath> localIncludeDirectories =
@@ -75,6 +83,31 @@
return this;
}
+ public Builder addSource(ArtifactLocation source) {
+ this.sources.add(source);
+ return this;
+ }
+
+ public Builder addHeaders(Iterable<ArtifactLocation> headers) {
+ this.headers.addAll(headers);
+ return this;
+ }
+
+ public Builder addHeader(ArtifactLocation header) {
+ this.headers.add(header);
+ return this;
+ }
+
+ public Builder addTextualHeaders(Iterable<ArtifactLocation> textualHeaders) {
+ this.textualHeaders.addAll(textualHeaders);
+ return this;
+ }
+
+ public Builder addTextualHeader(ArtifactLocation textualHeader) {
+ this.textualHeaders.add(textualHeader);
+ return this;
+ }
+
public Builder addLocalDefines(Iterable<String> localDefines) {
this.localDefines.addAll(localDefines);
return this;
@@ -111,6 +144,8 @@
public CIdeInfo build() {
return new CIdeInfo(
sources.build(),
+ headers.build(),
+ textualHeaders.build(),
localDefines.build(),
localIncludeDirectories.build(),
transitiveIncludeDirectories.build(),
@@ -127,6 +162,12 @@
+ " sources="
+ sources
+ "\n"
+ + " headers="
+ + headers
+ + "\n"
+ + " textualHeaders="
+ + textualHeaders
+ + "\n"
+ " localDefines="
+ localDefines
+ "\n"
diff --git a/base/src/com/google/idea/blaze/base/ideinfo/GoIdeInfo.java b/base/src/com/google/idea/blaze/base/ideinfo/GoIdeInfo.java
new file mode 100644
index 0000000..61689f3
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/ideinfo/GoIdeInfo.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2017 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.base.ideinfo;
+
+import com.google.common.collect.ImmutableList;
+import java.io.Serializable;
+
+/** Ide info specific to go rules. */
+public class GoIdeInfo implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ public final ImmutableList<ArtifactLocation> generatedSources;
+
+ public GoIdeInfo(ImmutableList<ArtifactLocation> generatedSources) {
+ this.generatedSources = generatedSources;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /** Builder for go rule info */
+ public static class Builder {
+ private final ImmutableList.Builder<ArtifactLocation> generatedSources =
+ ImmutableList.builder();
+
+ public Builder addGeneratedSources(Iterable<ArtifactLocation> generatedSources) {
+ this.generatedSources.addAll(generatedSources);
+ return this;
+ }
+
+ public GoIdeInfo build() {
+ return new GoIdeInfo(generatedSources.build());
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "GoIdeInfo{" + "\n" + " generatedSources=" + generatedSources + "\n" + '}';
+ }
+}
diff --git a/base/src/com/google/idea/blaze/base/ideinfo/JavaToolchainIdeInfo.java b/base/src/com/google/idea/blaze/base/ideinfo/JavaToolchainIdeInfo.java
index b9a9dca..c7faade 100644
--- a/base/src/com/google/idea/blaze/base/ideinfo/JavaToolchainIdeInfo.java
+++ b/base/src/com/google/idea/blaze/base/ideinfo/JavaToolchainIdeInfo.java
@@ -16,17 +16,21 @@
package com.google.idea.blaze.base.ideinfo;
import java.io.Serializable;
+import javax.annotation.Nullable;
/** Represents the java_toolchain class */
public class JavaToolchainIdeInfo implements Serializable {
- private static final long serialVersionUID = 1L;
+ private static final long serialVersionUID = 2L;
public final String sourceVersion;
public final String targetVersion;
+ @Nullable public final ArtifactLocation javacJar;
- public JavaToolchainIdeInfo(String sourceVersion, String targetVersion) {
+ public JavaToolchainIdeInfo(
+ String sourceVersion, String targetVersion, @Nullable ArtifactLocation javacJar) {
this.sourceVersion = sourceVersion;
this.targetVersion = targetVersion;
+ this.javacJar = javacJar;
}
@Override
@@ -39,6 +43,9 @@
+ " targetVersion="
+ targetVersion
+ "\n"
+ + " javacJar="
+ + javacJar
+ + "\n"
+ '}';
}
@@ -50,6 +57,7 @@
public static class Builder {
String sourceVersion;
String targetVersion;
+ ArtifactLocation javacJar;
public Builder setSourceVersion(String sourceVersion) {
this.sourceVersion = sourceVersion;
@@ -61,8 +69,13 @@
return this;
}
+ public Builder setJavacJar(ArtifactLocation javacJar) {
+ this.javacJar = javacJar;
+ return this;
+ }
+
public JavaToolchainIdeInfo build() {
- return new JavaToolchainIdeInfo(sourceVersion, targetVersion);
+ return new JavaToolchainIdeInfo(sourceVersion, targetVersion, javacJar);
}
}
}
diff --git a/base/src/com/google/idea/blaze/base/ideinfo/JsIdeInfo.java b/base/src/com/google/idea/blaze/base/ideinfo/JsIdeInfo.java
new file mode 100644
index 0000000..b191d22
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/ideinfo/JsIdeInfo.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2017 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.base.ideinfo;
+
+import com.google.common.collect.ImmutableList;
+import java.io.Serializable;
+
+/** Ide info specific to js rules. */
+public class JsIdeInfo implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ public final ImmutableList<ArtifactLocation> sources;
+
+ public JsIdeInfo(ImmutableList<ArtifactLocation> sources) {
+ this.sources = sources;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /** Builder for js rule info */
+ public static class Builder {
+ private final ImmutableList.Builder<ArtifactLocation> sources = ImmutableList.builder();
+
+ public Builder addSources(Iterable<ArtifactLocation> sources) {
+ this.sources.addAll(sources);
+ return this;
+ }
+
+ public JsIdeInfo build() {
+ return new JsIdeInfo(sources.build());
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "JsIdeInfo{" + "\n" + " sources=" + sources + "\n" + '}';
+ }
+}
diff --git a/base/src/com/google/idea/blaze/base/ideinfo/TargetIdeInfo.java b/base/src/com/google/idea/blaze/base/ideinfo/TargetIdeInfo.java
index ecd44c5..5e93271 100644
--- a/base/src/com/google/idea/blaze/base/ideinfo/TargetIdeInfo.java
+++ b/base/src/com/google/idea/blaze/base/ideinfo/TargetIdeInfo.java
@@ -15,6 +15,8 @@
*/
package com.google.idea.blaze.base.ideinfo;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.idea.blaze.base.ideinfo.Dependency.DependencyType;
import com.google.idea.blaze.base.model.primitives.Kind;
@@ -41,6 +43,9 @@
@Nullable public final AndroidIdeInfo androidIdeInfo;
@Nullable public final AndroidSdkIdeInfo androidSdkIdeInfo;
@Nullable public final PyIdeInfo pyIdeInfo;
+ @Nullable public final GoIdeInfo goIdeInfo;
+ @Nullable public final JsIdeInfo jsIdeInfo;
+ @Nullable public final TsIdeInfo tsIdeInfo;
@Nullable public final TestIdeInfo testIdeInfo;
@Nullable public final ProtoLibraryLegacyInfo protoLibraryLegacyInfo;
@Nullable public final JavaToolchainIdeInfo javaToolchainIdeInfo;
@@ -58,6 +63,9 @@
@Nullable AndroidIdeInfo androidIdeInfo,
@Nullable AndroidSdkIdeInfo androidSdkIdeInfo,
@Nullable PyIdeInfo pyIdeInfo,
+ @Nullable GoIdeInfo goIdeInfo,
+ @Nullable JsIdeInfo jsIdeInfo,
+ @Nullable TsIdeInfo tsIdeInfo,
@Nullable TestIdeInfo testIdeInfo,
@Nullable ProtoLibraryLegacyInfo protoLibraryLegacyInfo,
@Nullable JavaToolchainIdeInfo javaToolchainIdeInfo) {
@@ -73,6 +81,9 @@
this.androidIdeInfo = androidIdeInfo;
this.androidSdkIdeInfo = androidSdkIdeInfo;
this.pyIdeInfo = pyIdeInfo;
+ this.goIdeInfo = goIdeInfo;
+ this.jsIdeInfo = jsIdeInfo;
+ this.tsIdeInfo = tsIdeInfo;
this.testIdeInfo = testIdeInfo;
this.protoLibraryLegacyInfo = protoLibraryLegacyInfo;
this.javaToolchainIdeInfo = javaToolchainIdeInfo;
@@ -117,6 +128,9 @@
private JavaIdeInfo javaIdeInfo;
private AndroidIdeInfo androidIdeInfo;
private PyIdeInfo pyIdeInfo;
+ private GoIdeInfo goIdeInfo;
+ private JsIdeInfo jsIdeInfo;
+ private TsIdeInfo tsIdeInfo;
private TestIdeInfo testIdeInfo;
private ProtoLibraryLegacyInfo protoLibraryLegacyInfo;
private JavaToolchainIdeInfo javaToolchainIdeInfo;
@@ -135,8 +149,10 @@
return this;
}
- public Builder setKind(String kind) {
- return setKind(Kind.fromString(kind));
+ @VisibleForTesting
+ public Builder setKind(String kindString) {
+ Kind kind = Preconditions.checkNotNull(Kind.fromString(kindString));
+ return setKind(kind);
}
public Builder setKind(Kind kind) {
@@ -160,6 +176,9 @@
public Builder setCInfo(CIdeInfo cInfo) {
this.cIdeInfo = cInfo;
+ this.sources.addAll(cInfo.sources);
+ this.sources.addAll(cInfo.headers);
+ this.sources.addAll(cInfo.textualHeaders);
return this;
}
@@ -190,6 +209,21 @@
return this;
}
+ public Builder setGoInfo(GoIdeInfo.Builder goInfo) {
+ this.goIdeInfo = goInfo.build();
+ return this;
+ }
+
+ public Builder setJsInfo(JsIdeInfo.Builder jsInfo) {
+ this.jsIdeInfo = jsInfo.build();
+ return this;
+ }
+
+ public Builder setTsInfo(TsIdeInfo.Builder tsInfo) {
+ this.tsIdeInfo = tsInfo.build();
+ return this;
+ }
+
public Builder setTestInfo(TestIdeInfo.Builder testInfo) {
this.testIdeInfo = testInfo.build();
return this;
@@ -245,6 +279,9 @@
androidIdeInfo,
null,
pyIdeInfo,
+ goIdeInfo,
+ jsIdeInfo,
+ tsIdeInfo,
testIdeInfo,
protoLibraryLegacyInfo,
javaToolchainIdeInfo);
diff --git a/base/src/com/google/idea/blaze/base/ideinfo/TsIdeInfo.java b/base/src/com/google/idea/blaze/base/ideinfo/TsIdeInfo.java
new file mode 100644
index 0000000..49e054d
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/ideinfo/TsIdeInfo.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2017 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.base.ideinfo;
+
+import com.google.common.collect.ImmutableList;
+import java.io.Serializable;
+
+/** Ide info specific to typescript rules. */
+public class TsIdeInfo implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ public final ImmutableList<ArtifactLocation> sources;
+
+ public TsIdeInfo(ImmutableList<ArtifactLocation> sources) {
+ this.sources = sources;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /** Builder for js rule info */
+ public static class Builder {
+ private final ImmutableList.Builder<ArtifactLocation> sources = ImmutableList.builder();
+
+ public Builder addSources(Iterable<ArtifactLocation> sources) {
+ this.sources.addAll(sources);
+ return this;
+ }
+
+ public TsIdeInfo build() {
+ return new TsIdeInfo(sources.build());
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "TsIdeInfo{" + "\n" + " sources=" + sources + "\n" + '}';
+ }
+}
diff --git a/base/src/com/google/idea/blaze/base/issueparser/BlazeIssueParser.java b/base/src/com/google/idea/blaze/base/issueparser/BlazeIssueParser.java
index 55755ec..f743525 100644
--- a/base/src/com/google/idea/blaze/base/issueparser/BlazeIssueParser.java
+++ b/base/src/com/google/idea/blaze/base/issueparser/BlazeIssueParser.java
@@ -147,7 +147,7 @@
private final WorkspaceRoot workspaceRoot;
CompileParser(WorkspaceRoot workspaceRoot) {
- super("^([^/].*?):([0-9]+):(?:([0-9]+):)? (error|warning): (.*)$");
+ super("^([^/].*?):([0-9]+):(?:([0-9]+):)? (fatal error|error|warning): (.*)$");
this.workspaceRoot = workspaceRoot;
}
@@ -155,9 +155,9 @@
protected IssueOutput createIssue(Matcher matcher) {
final File file = fileFromRelativePath(workspaceRoot, matcher.group(1));
IssueOutput.Category type =
- matcher.group(4).equals("error")
- ? IssueOutput.Category.ERROR
- : IssueOutput.Category.WARNING;
+ matcher.group(4).equals("warning")
+ ? IssueOutput.Category.WARNING
+ : IssueOutput.Category.ERROR;
return IssueOutput.issue(type, matcher.group(5))
.inFile(file)
.onLine(Integer.parseInt(matcher.group(2)))
diff --git a/base/src/com/google/idea/blaze/base/issueparser/BlazeIssueParserProvider.java b/base/src/com/google/idea/blaze/base/issueparser/BlazeIssueParserProvider.java
new file mode 100644
index 0000000..513dc7e
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/issueparser/BlazeIssueParserProvider.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2017 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.base.issueparser;
+
+import com.google.common.collect.ImmutableList;
+import com.google.idea.blaze.base.issueparser.BlazeIssueParser.Parser;
+import com.intellij.openapi.extensions.ExtensionPointName;
+import com.intellij.openapi.project.Project;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Extension point for providing {@link
+ * com.google.idea.blaze.base.issueparser.BlazeIssueParser.Parser}s.
+ */
+public interface BlazeIssueParserProvider {
+
+ ExtensionPointName<BlazeIssueParserProvider> EP_NAME =
+ ExtensionPointName.create("com.google.idea.blaze.BlazeIssueParserProvider");
+
+ static List<Parser> getAllIssueParsers(Project project) {
+ return Arrays.stream(EP_NAME.getExtensions())
+ .map(provider -> provider.getIssueParsers(project))
+ .flatMap(Collection::stream)
+ .collect(Collectors.toList());
+ }
+
+ ImmutableList<BlazeIssueParser.Parser> getIssueParsers(Project project);
+}
diff --git a/base/src/com/google/idea/blaze/base/issueparser/IssueOutputLineProcessor.java b/base/src/com/google/idea/blaze/base/issueparser/IssueOutputLineProcessor.java
index a5588dc..0b3dc57 100644
--- a/base/src/com/google/idea/blaze/base/issueparser/IssueOutputLineProcessor.java
+++ b/base/src/com/google/idea/blaze/base/issueparser/IssueOutputLineProcessor.java
@@ -25,7 +25,6 @@
import com.google.idea.blaze.base.scope.output.PrintOutput;
import com.google.idea.blaze.base.scope.output.PrintOutput.OutputType;
import com.intellij.openapi.project.Project;
-import javax.annotation.Nullable;
/**
* Forwards output to PrintOutputs, colored by whether or not an issue is found per-line.
@@ -39,26 +38,28 @@
private final BlazeIssueParser blazeIssueParser;
public IssueOutputLineProcessor(
- @Nullable Project project, BlazeContext context, WorkspaceRoot workspaceRoot) {
+ Project project, BlazeContext context, WorkspaceRoot workspaceRoot) {
this.context = context;
- ProjectViewSet projectViewSet =
- project != null ? ProjectViewManager.getInstance(project).getProjectViewSet() : null;
+ ProjectViewSet projectViewSet = ProjectViewManager.getInstance(project).getProjectViewSet();
ImmutableList<BlazeIssueParser.Parser> parsers =
- ImmutableList.of(
- new BlazeIssueParser.CompileParser(workspaceRoot),
- new BlazeIssueParser.TracebackParser(),
- new BlazeIssueParser.BuildParser(),
- new BlazeIssueParser.SkylarkErrorParser(),
- new BlazeIssueParser.LinelessBuildParser(),
- new BlazeIssueParser.ProjectViewLabelParser(projectViewSet),
- new BlazeIssueParser.InvalidTargetProjectViewPackageParser(
- projectViewSet, "no such package '(.*)': BUILD file not found on package path"),
- new BlazeIssueParser.InvalidTargetProjectViewPackageParser(
- projectViewSet, "no targets found beneath '(.*)'"),
- new BlazeIssueParser.InvalidTargetProjectViewPackageParser(
- projectViewSet, "ERROR: invalid target format '(.*)'"),
- new BlazeIssueParser.FileNotFoundBuildParser(workspaceRoot));
+ ImmutableList.<BlazeIssueParser.Parser>builder()
+ .add(
+ new BlazeIssueParser.CompileParser(workspaceRoot),
+ new BlazeIssueParser.TracebackParser(),
+ new BlazeIssueParser.BuildParser(),
+ new BlazeIssueParser.SkylarkErrorParser(),
+ new BlazeIssueParser.LinelessBuildParser(),
+ new BlazeIssueParser.ProjectViewLabelParser(projectViewSet),
+ new BlazeIssueParser.InvalidTargetProjectViewPackageParser(
+ projectViewSet, "no such package '(.*)': BUILD file not found on package path"),
+ new BlazeIssueParser.InvalidTargetProjectViewPackageParser(
+ projectViewSet, "no targets found beneath '(.*)'"),
+ new BlazeIssueParser.InvalidTargetProjectViewPackageParser(
+ projectViewSet, "ERROR: invalid target format '(.*)'"),
+ new BlazeIssueParser.FileNotFoundBuildParser(workspaceRoot))
+ .addAll(BlazeIssueParserProvider.getAllIssueParsers(project))
+ .build();
this.blazeIssueParser = new BlazeIssueParser(parsers);
}
diff --git a/base/src/com/google/idea/blaze/base/lang/buildfile/actions/BuildFileModifierImpl.java b/base/src/com/google/idea/blaze/base/lang/buildfile/actions/BuildFileModifierImpl.java
index dd9178c..a8dc5b5 100644
--- a/base/src/com/google/idea/blaze/base/lang/buildfile/actions/BuildFileModifierImpl.java
+++ b/base/src/com/google/idea/blaze/base/lang/buildfile/actions/BuildFileModifierImpl.java
@@ -26,10 +26,8 @@
import com.google.idea.blaze.base.model.primitives.Kind;
import com.google.idea.blaze.base.model.primitives.Label;
import com.google.idea.blaze.base.scope.BlazeContext;
-import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
-import com.intellij.openapi.util.Computable;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.psi.PsiElement;
import java.io.File;
@@ -41,24 +39,19 @@
@Override
public boolean addRule(Project project, BlazeContext context, Label newRule, Kind ruleKind) {
- return WriteCommandAction.runWriteCommandAction(
- project,
- (Computable<Boolean>)
- () -> {
- BuildReferenceManager manager = BuildReferenceManager.getInstance(project);
- File file = manager.resolvePackage(newRule.blazePackage());
- if (file == null) {
- return null;
- }
- LocalFileSystem.getInstance().refreshIoFiles(ImmutableList.of(file));
- BuildFile buildFile = manager.resolveBlazePackage(newRule.blazePackage());
- if (buildFile == null) {
- logger.error("No BUILD file found at location: " + newRule.blazePackage());
- return false;
- }
- buildFile.add(createRule(project, ruleKind, newRule.targetName().toString()));
- return true;
- });
+ BuildReferenceManager manager = BuildReferenceManager.getInstance(project);
+ File file = manager.resolvePackage(newRule.blazePackage());
+ if (file == null) {
+ return false;
+ }
+ LocalFileSystem.getInstance().refreshIoFiles(ImmutableList.of(file));
+ BuildFile buildFile = manager.resolveBlazePackage(newRule.blazePackage());
+ if (buildFile == null) {
+ logger.error("No BUILD file found at location: " + newRule.blazePackage());
+ return false;
+ }
+ buildFile.add(createRule(project, ruleKind, newRule.targetName().toString()));
+ return true;
}
private PsiElement createRule(Project project, Kind ruleKind, String ruleName) {
diff --git a/base/src/com/google/idea/blaze/base/lang/buildfile/findusages/BuildFileGroupingRuleProvider.java b/base/src/com/google/idea/blaze/base/lang/buildfile/findusages/BuildFileGroupingRuleProvider.java
index 84d16cb..a085253 100644
--- a/base/src/com/google/idea/blaze/base/lang/buildfile/findusages/BuildFileGroupingRuleProvider.java
+++ b/base/src/com/google/idea/blaze/base/lang/buildfile/findusages/BuildFileGroupingRuleProvider.java
@@ -21,11 +21,13 @@
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.usages.Usage;
import com.intellij.usages.UsageGroup;
+import com.intellij.usages.UsageTarget;
import com.intellij.usages.UsageView;
import com.intellij.usages.impl.FileStructureGroupRuleProvider;
import com.intellij.usages.impl.rules.FileGroupingRule;
import com.intellij.usages.rules.UsageGroupingRule;
import com.intellij.usages.rules.UsageInFile;
+import javax.annotation.Nullable;
import javax.swing.Icon;
/**
@@ -52,8 +54,9 @@
this.project = project;
}
- @Override
- public UsageGroup groupUsage(Usage usage) {
+ @SuppressWarnings("MissingOverride") // #api171: added in 2017.2
+ @Nullable
+ public UsageGroup getParentGroupFor(Usage usage, UsageTarget[] targets) {
if (!(usage instanceof UsageInFile)) {
return null;
}
@@ -88,5 +91,10 @@
}
};
}
+
+ @Override
+ public UsageGroup groupUsage(Usage usage) {
+ return getParentGroupFor(usage, UsageTarget.EMPTY_ARRAY);
+ }
}
}
diff --git a/base/src/com/google/idea/blaze/base/lang/buildfile/psi/BuildElement.java b/base/src/com/google/idea/blaze/base/lang/buildfile/psi/BuildElement.java
index 7cfd64b..58bb3a1 100644
--- a/base/src/com/google/idea/blaze/base/lang/buildfile/psi/BuildElement.java
+++ b/base/src/com/google/idea/blaze/base/lang/buildfile/psi/BuildElement.java
@@ -32,6 +32,12 @@
String getPresentableText();
+ /** See {@link com.intellij.navigation.ItemPresentation#getLocationString}. */
+ @Nullable
+ default String getLocationString() {
+ return null;
+ }
+
@Nullable
PsiElement getReferencedElement();
diff --git a/base/src/com/google/idea/blaze/base/lang/buildfile/psi/BuildElementImpl.java b/base/src/com/google/idea/blaze/base/lang/buildfile/psi/BuildElementImpl.java
index 700f4dc..d6270b9 100644
--- a/base/src/com/google/idea/blaze/base/lang/buildfile/psi/BuildElementImpl.java
+++ b/base/src/com/google/idea/blaze/base/lang/buildfile/psi/BuildElementImpl.java
@@ -147,7 +147,7 @@
@Override
public String getLocationString() {
- return null;
+ return element.getLocationString();
}
@Override
diff --git a/base/src/com/google/idea/blaze/base/lang/buildfile/psi/FuncallExpression.java b/base/src/com/google/idea/blaze/base/lang/buildfile/psi/FuncallExpression.java
index 46701da..41f68fc 100644
--- a/base/src/com/google/idea/blaze/base/lang/buildfile/psi/FuncallExpression.java
+++ b/base/src/com/google/idea/blaze/base/lang/buildfile/psi/FuncallExpression.java
@@ -19,6 +19,7 @@
import com.google.idea.blaze.base.lang.buildfile.psi.util.PsiUtils;
import com.google.idea.blaze.base.lang.buildfile.references.FuncallReference;
import com.google.idea.blaze.base.lang.buildfile.references.LabelUtils;
+import com.google.idea.blaze.base.model.primitives.Kind;
import com.google.idea.blaze.base.model.primitives.Label;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.util.TextRange;
@@ -53,6 +54,12 @@
return node != null ? node.getText() : null;
}
+ @Nullable
+ public Kind getRuleKind() {
+ String functionName = getFunctionName();
+ return functionName != null ? Kind.fromString(functionName) : null;
+ }
+
@Override
@Nullable
public String getName() {
@@ -161,12 +168,15 @@
@Override
public String getPresentableText() {
- String name = getFunctionName();
- if (name == null) {
+ String functionName = getFunctionName();
+ if (functionName == null) {
return super.getPresentableText();
}
String targetName = getNameArgumentValue();
- return targetName != null ? name + "(\"" + targetName + "\")" : name;
+ if (targetName == null) {
+ return functionName;
+ }
+ return String.format("%s : %s", targetName, functionName);
}
@Override
diff --git a/base/src/com/google/idea/blaze/base/lang/buildfile/psi/LoadStatement.java b/base/src/com/google/idea/blaze/base/lang/buildfile/psi/LoadStatement.java
index 31dd222..3cd68f8 100644
--- a/base/src/com/google/idea/blaze/base/lang/buildfile/psi/LoadStatement.java
+++ b/base/src/com/google/idea/blaze/base/lang/buildfile/psi/LoadStatement.java
@@ -86,7 +86,12 @@
@Override
public String getPresentableText() {
- String path = LabelUtils.getNiceSkylarkFileName(getImportedPath());
- return path != null ? "load: " + path : "load";
+ return "load";
+ }
+
+ @Nullable
+ @Override
+ public String getLocationString() {
+ return LabelUtils.getNiceSkylarkFileName(getImportedPath());
}
}
diff --git a/base/src/com/google/idea/blaze/base/lang/buildfile/references/ExternalWorkspaceReferenceFragment.java b/base/src/com/google/idea/blaze/base/lang/buildfile/references/ExternalWorkspaceReferenceFragment.java
index 521f432..51d8750 100644
--- a/base/src/com/google/idea/blaze/base/lang/buildfile/references/ExternalWorkspaceReferenceFragment.java
+++ b/base/src/com/google/idea/blaze/base/lang/buildfile/references/ExternalWorkspaceReferenceFragment.java
@@ -65,7 +65,7 @@
@Nullable
private static BuildFile resolveProjectWorkspaceFile(Project project) {
- WorkspaceRoot projectRoot = WorkspaceRoot.fromProject(project);
+ WorkspaceRoot projectRoot = WorkspaceRoot.fromProjectSafe(project);
if (projectRoot == null) {
return null;
}
diff --git a/base/src/com/google/idea/blaze/base/lang/buildfile/sync/BuildLangSyncPlugin.java b/base/src/com/google/idea/blaze/base/lang/buildfile/sync/BuildLangSyncPlugin.java
index 4a0f12b..526a8cb 100644
--- a/base/src/com/google/idea/blaze/base/lang/buildfile/sync/BuildLangSyncPlugin.java
+++ b/base/src/com/google/idea/blaze/base/lang/buildfile/sync/BuildLangSyncPlugin.java
@@ -16,7 +16,9 @@
package com.google.idea.blaze.base.lang.buildfile.sync;
import com.google.common.util.concurrent.ListenableFuture;
+import com.google.idea.blaze.base.command.BlazeCommandName;
import com.google.idea.blaze.base.command.BlazeFlags;
+import com.google.idea.blaze.base.command.BlazeInvocationContext;
import com.google.idea.blaze.base.command.info.BlazeInfo;
import com.google.idea.blaze.base.command.info.BlazeInfoRunner;
import com.google.idea.blaze.base.ideinfo.TargetMap;
@@ -102,15 +104,14 @@
ProjectViewSet projectViewSet,
BlazeContext context) {
try {
- // it's wasteful converting to a string and back, but uses existing code,
- // and has a very minor cost (this is only run once per workspace)
ListenableFuture<byte[]> future =
BlazeInfoRunner.getInstance()
.runBlazeInfoGetBytes(
context,
Blaze.getBuildSystemProvider(project).getSyncBinaryPath(),
workspace,
- BlazeFlags.buildFlags(project, projectViewSet),
+ BlazeFlags.blazeFlags(
+ project, projectViewSet, BlazeCommandName.INFO, BlazeInvocationContext.Sync),
BlazeInfo.BUILD_LANGUAGE);
return BuildLanguageSpec.fromProto(Build.BuildLanguage.parseFrom(future.get()));
diff --git a/base/src/com/google/idea/blaze/base/lang/buildfile/views/BuildStructureViewElement.java b/base/src/com/google/idea/blaze/base/lang/buildfile/views/BuildStructureViewElement.java
index 7ae6217..6800f53 100644
--- a/base/src/com/google/idea/blaze/base/lang/buildfile/views/BuildStructureViewElement.java
+++ b/base/src/com/google/idea/blaze/base/lang/buildfile/views/BuildStructureViewElement.java
@@ -53,4 +53,9 @@
public String getPresentableText() {
return element.getPresentableText();
}
+
+ @Override
+ public String getLocationString() {
+ return element.getLocationString();
+ }
}
diff --git a/base/src/com/google/idea/blaze/base/model/BlazeVersionData.java b/base/src/com/google/idea/blaze/base/model/BlazeVersionData.java
index ded37ad..3356e8b 100644
--- a/base/src/com/google/idea/blaze/base/model/BlazeVersionData.java
+++ b/base/src/com/google/idea/blaze/base/model/BlazeVersionData.java
@@ -15,7 +15,6 @@
*/
package com.google.idea.blaze.base.model;
-import com.google.common.annotations.VisibleForTesting;
import com.google.idea.blaze.base.bazel.BazelVersion;
import com.google.idea.blaze.base.bazel.BuildSystemProvider;
import com.google.idea.blaze.base.command.info.BlazeInfo;
@@ -35,11 +34,6 @@
@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;
@@ -51,6 +45,10 @@
return blazeCl != null && blazeCl >= cl;
}
+ public boolean blazeClientIsKnown() {
+ return clientCl != null;
+ }
+
public boolean blazeClientIsAtLeastCl(long cl) {
return clientCl != null && clientCl >= cl;
}
@@ -59,24 +57,40 @@
return bazelVersion != null && bazelVersion.isAtLeast(major, minor, bugfix);
}
+ public boolean bazelIsAtLeastVersion(BazelVersion version) {
+ return bazelVersion != null && bazelVersion.isAtLeast(version);
+ }
+
public BuildSystem buildSystem() {
return bazelVersion != null ? BuildSystem.Bazel : BuildSystem.Blaze;
}
+ @Override
+ public String toString() {
+ if (bazelVersion != null) {
+ return bazelVersion.toString();
+ }
+ return String.format("Blaze CL: %s, Client CL: %s", blazeCl, clientCl);
+ }
+
public static BlazeVersionData build(
BuildSystem buildSystem, WorkspaceRoot workspaceRoot, BlazeInfo blazeInfo) {
- Builder builder = new Builder();
+ Builder builder = builder();
for (BuildSystemProvider provider : BuildSystemProvider.EP_NAME.getExtensions()) {
provider.populateBlazeVersionData(buildSystem, workspaceRoot, blazeInfo, builder);
}
return builder.build();
}
+ public static Builder builder() {
+ return new Builder();
+ }
+
/** Builder class for constructing the blaze version data */
public static class Builder {
- public Long blazeCl;
- public Long clientCl;
- public BazelVersion bazelVersion;
+ private Long blazeCl;
+ private Long clientCl;
+ private BazelVersion bazelVersion;
public Builder setBlazeCl(Long blazeCl) {
this.blazeCl = blazeCl;
diff --git a/base/src/com/google/idea/blaze/base/model/primitives/ExecutionRootPath.java b/base/src/com/google/idea/blaze/base/model/primitives/ExecutionRootPath.java
index 5e060b7..e299cbd 100644
--- a/base/src/com/google/idea/blaze/base/model/primitives/ExecutionRootPath.java
+++ b/base/src/com/google/idea/blaze/base/model/primitives/ExecutionRootPath.java
@@ -90,7 +90,9 @@
if (!isAncestor(root.getPath(), path.getPath(), /* strict */ false)) {
return null;
}
- String relativePath = FileUtil.getRelativePath(root, path);
+ String relativePath =
+ FileUtil.getRelativePath(
+ root.getAbsolutePath(), path.getAbsolutePath(), File.separatorChar);
if (relativePath == null) {
return null;
}
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 b5fbda8..7a041b8 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
@@ -20,50 +20,69 @@
import com.google.common.collect.ImmutableMultimap;
import java.util.Arrays;
import java.util.Collection;
+import javax.annotation.Nullable;
/** Wrapper around a string for a blaze kind (android_library, android_test...) */
public enum Kind {
- ANDROID_BINARY("android_binary", LanguageClass.ANDROID),
- ANDROID_LIBRARY("android_library", LanguageClass.ANDROID),
- ANDROID_TEST("android_test", LanguageClass.ANDROID),
- ANDROID_ROBOLECTRIC_TEST("android_robolectric_test", LanguageClass.ANDROID),
- ANDROID_SDK("android_sdk", LanguageClass.ANDROID),
- JAVA_LIBRARY("java_library", LanguageClass.JAVA),
- JAVA_TEST("java_test", LanguageClass.JAVA),
- JAVA_BINARY("java_binary", LanguageClass.JAVA),
- JAVA_IMPORT("java_import", LanguageClass.JAVA),
- JAVA_TOOLCHAIN("java_toolchain", LanguageClass.JAVA),
- PROTO_LIBRARY("proto_library", LanguageClass.GENERIC),
- JAVA_PLUGIN("java_plugin", LanguageClass.JAVA),
- ANDROID_RESOURCES("android_resources", LanguageClass.ANDROID),
- CC_LIBRARY("cc_library", LanguageClass.C),
- CC_BINARY("cc_binary", LanguageClass.C),
- CC_TEST("cc_test", LanguageClass.C),
- CC_INC_LIBRARY("cc_inc_library", LanguageClass.C),
- CC_TOOLCHAIN("cc_toolchain", LanguageClass.C),
- JAVA_WRAP_CC("java_wrap_cc", LanguageClass.JAVA),
- GWT_APPLICATION("gwt_application", LanguageClass.JAVA),
- GWT_HOST("gwt_host", LanguageClass.JAVA),
- GWT_MODULE("gwt_module", LanguageClass.JAVA),
- GWT_TEST("gwt_test", LanguageClass.JAVA),
- TEST_SUITE("test_suite", LanguageClass.GENERIC),
- PY_LIBRARY("py_library", LanguageClass.PYTHON),
- PY_BINARY("py_binary", LanguageClass.PYTHON),
- 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),
- INTELLIJ_PLUGIN_DEBUG_TARGET("intellij_plugin_debug_target", LanguageClass.JAVA),
- SCALA_BINARY("scala_binary", LanguageClass.SCALA),
- SCALA_LIBRARY("scala_library", LanguageClass.SCALA),
- SCALA_MACRO_LIBRARY("scala_macro_library", LanguageClass.SCALA),
- SCALA_TEST("scala_test", LanguageClass.SCALA),
+ ANDROID_BINARY("android_binary", LanguageClass.ANDROID, RuleType.BINARY),
+ ANDROID_LIBRARY("android_library", LanguageClass.ANDROID, RuleType.UNKNOWN),
+ ANDROID_TEST("android_test", LanguageClass.ANDROID, RuleType.TEST),
+ ANDROID_ROBOLECTRIC_TEST("android_robolectric_test", LanguageClass.ANDROID, RuleType.TEST),
+ ANDROID_SDK("android_sdk", LanguageClass.ANDROID, RuleType.UNKNOWN),
+ JAVA_LIBRARY("java_library", LanguageClass.JAVA, RuleType.UNKNOWN),
+ JAVA_TEST("java_test", LanguageClass.JAVA, RuleType.TEST),
+ JAVA_BINARY("java_binary", LanguageClass.JAVA, RuleType.BINARY),
+ JAVA_IMPORT("java_import", LanguageClass.JAVA, RuleType.UNKNOWN),
+ JAVA_TOOLCHAIN("java_toolchain", LanguageClass.JAVA, RuleType.UNKNOWN),
+ JAVA_PROTO_LIBRARY("java_proto_library", LanguageClass.JAVA, RuleType.UNKNOWN),
+ JAVA_PLUGIN("java_plugin", LanguageClass.JAVA, RuleType.UNKNOWN),
+ PROTO_LIBRARY("proto_library", LanguageClass.GENERIC, RuleType.UNKNOWN),
+ ANDROID_RESOURCES("android_resources", LanguageClass.ANDROID, RuleType.UNKNOWN),
+ CC_LIBRARY("cc_library", LanguageClass.C, RuleType.UNKNOWN),
+ CC_BINARY("cc_binary", LanguageClass.C, RuleType.BINARY),
+ CC_TEST("cc_test", LanguageClass.C, RuleType.TEST),
+ CC_INC_LIBRARY("cc_inc_library", LanguageClass.C, RuleType.UNKNOWN),
+ CC_TOOLCHAIN("cc_toolchain", LanguageClass.C, RuleType.UNKNOWN),
+ JAVA_WRAP_CC("java_wrap_cc", LanguageClass.JAVA, RuleType.UNKNOWN),
+ GWT_APPLICATION("gwt_application", LanguageClass.JAVA, RuleType.UNKNOWN),
+ GWT_HOST("gwt_host", LanguageClass.JAVA, RuleType.UNKNOWN),
+ GWT_MODULE("gwt_module", LanguageClass.JAVA, RuleType.UNKNOWN),
+ GWT_TEST("gwt_test", LanguageClass.JAVA, RuleType.TEST),
+ TEST_SUITE("test_suite", LanguageClass.GENERIC, RuleType.TEST),
+ PY_LIBRARY("py_library", LanguageClass.PYTHON, RuleType.UNKNOWN),
+ PY_BINARY("py_binary", LanguageClass.PYTHON, RuleType.BINARY),
+ PY_TEST("py_test", LanguageClass.PYTHON, RuleType.TEST),
+ PY_APPENGINE_BINARY("py_appengine_binary", LanguageClass.PYTHON, RuleType.BINARY),
+ PY_WRAP_CC("py_wrap_cc", LanguageClass.PYTHON, RuleType.UNKNOWN),
+ GO_TEST("go_test", LanguageClass.GO, RuleType.TEST),
+ GO_APPENGINE_TEST("go_appengine_test", LanguageClass.GO, RuleType.TEST),
+ GO_BINARY("go_binary", LanguageClass.GO, RuleType.BINARY),
+ GO_APPENGINE_BINARY("go_appengine_binary", LanguageClass.GO, RuleType.BINARY),
+ GO_LIBRARY("go_library", LanguageClass.GO, RuleType.UNKNOWN),
+ GO_APPENGINE_LIBRARY("go_appengine_library", LanguageClass.GO, RuleType.UNKNOWN),
+ GO_PROTO_LIBRARY("go_proto_library", LanguageClass.GO, RuleType.UNKNOWN),
+ GO_WRAP_CC("go_wrap_cc", LanguageClass.GO, RuleType.UNKNOWN),
+ INTELLIJ_PLUGIN_DEBUG_TARGET(
+ "intellij_plugin_debug_target", LanguageClass.JAVA, RuleType.UNKNOWN),
+ SCALA_BINARY("scala_binary", LanguageClass.SCALA, RuleType.BINARY),
+ SCALA_IMPORT("scala_import", LanguageClass.SCALA, RuleType.UNKNOWN),
+ SCALA_LIBRARY("scala_library", LanguageClass.SCALA, RuleType.UNKNOWN),
+ SCALA_MACRO_LIBRARY("scala_macro_library", LanguageClass.SCALA, RuleType.UNKNOWN),
+ SCALA_TEST("scala_test", LanguageClass.SCALA, RuleType.TEST),
+ SCALA_JUNIT_TEST("scala_junit_test", LanguageClass.SCALA, RuleType.TEST),
+ SH_TEST("sh_test", LanguageClass.GENERIC, RuleType.TEST),
+ SH_LIBRARY("sh_library", LanguageClass.GENERIC, RuleType.UNKNOWN),
+ SH_BINARY("sh_binary", LanguageClass.GENERIC, RuleType.BINARY),
+ JS_BINARY("js_binary", LanguageClass.JAVASCRIPT, RuleType.BINARY),
+ JS_MODULE_BINARY("js_module_binary", LanguageClass.JAVASCRIPT, RuleType.BINARY),
+ JS_LIBRARY("js_library", LanguageClass.JAVASCRIPT, RuleType.UNKNOWN),
+ JS_UNIT_TEST("jsunit_test", LanguageClass.JAVASCRIPT, RuleType.TEST),
+ JS_PUPPET_TEST("js_puppet_test", LanguageClass.JAVASCRIPT, RuleType.TEST),
+ PINTO_LIBRARY("pinto_library", LanguageClass.JAVASCRIPT, RuleType.UNKNOWN),
+ PINTO_LIBRARY_MOD("pinto_library_mod", LanguageClass.JAVASCRIPT, RuleType.UNKNOWN),
+ PINTO_MODULE("pinto_module", LanguageClass.JAVASCRIPT, RuleType.UNKNOWN),
+ TS_LIBRARY("ts_library", LanguageClass.TYPESCRIPT, RuleType.UNKNOWN),
+ TS_CONFIG("ts_config", LanguageClass.TYPESCRIPT, RuleType.BINARY),
;
static final ImmutableMap<String, Kind> STRING_TO_KIND = makeStringToKindMap();
@@ -86,6 +105,7 @@
return result.build();
}
+ @Nullable
public static Kind fromString(String kindString) {
return STRING_TO_KIND.get(kindString);
}
@@ -95,11 +115,13 @@
}
private final String kind;
- private final LanguageClass languageClass;
+ public final LanguageClass languageClass;
+ public final RuleType ruleType;
- Kind(String kind, LanguageClass languageClass) {
+ Kind(String kind, LanguageClass languageClass, RuleType ruleType) {
this.kind = kind;
this.languageClass = languageClass;
+ this.ruleType = ruleType;
}
@Override
@@ -107,10 +129,6 @@
return kind;
}
- public LanguageClass getLanguageClass() {
- return languageClass;
- }
-
public boolean isOneOf(Kind... kinds) {
return isOneOf(Arrays.asList(kinds));
}
diff --git a/base/src/com/google/idea/blaze/base/model/primitives/RuleType.java b/base/src/com/google/idea/blaze/base/model/primitives/RuleType.java
new file mode 100644
index 0000000..97e950c
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/model/primitives/RuleType.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2017 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.base.model.primitives;
+
+/** The general type of a rule (e.g. test, binary, etc.). */
+public enum RuleType {
+ TEST,
+ BINARY,
+ UNKNOWN,
+}
diff --git a/base/src/com/google/idea/blaze/base/plugin/BazelVersionChecker.java b/base/src/com/google/idea/blaze/base/plugin/BazelVersionChecker.java
new file mode 100644
index 0000000..78e146b
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/plugin/BazelVersionChecker.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2017 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.base.plugin;
+
+import com.google.idea.blaze.base.bazel.BazelVersion;
+import com.google.idea.blaze.base.model.BlazeVersionData;
+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.BuildSystem;
+
+/** Verifies that the available Bazel version is supported by this plugin. */
+public class BazelVersionChecker implements BuildSystemVersionChecker {
+
+ // prior to version 0.5 there was no BEP support
+ private static final BazelVersion OLDEST_SUPPORTED_VERSION = new BazelVersion(0, 5, 0);
+
+ @Override
+ public boolean versionSupported(BlazeContext context, BlazeVersionData version) {
+ if (version.buildSystem() != BuildSystem.Bazel) {
+ return true;
+ }
+ if (version.bazelIsAtLeastVersion(OLDEST_SUPPORTED_VERSION)) {
+ return true;
+ }
+ IssueOutput.error(
+ String.format(
+ "Bazel version %s is not supported by this version of the Bazel plugin. "
+ + "Please upgrade to Bazel version %s+.\n"
+ + "Upgrade instructions are available at https://bazel.build",
+ version, OLDEST_SUPPORTED_VERSION))
+ .submit(context);
+ return false;
+ }
+}
diff --git a/base/src/com/google/idea/blaze/base/plugin/BuildSystemVersionChecker.java b/base/src/com/google/idea/blaze/base/plugin/BuildSystemVersionChecker.java
new file mode 100644
index 0000000..6667b91
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/plugin/BuildSystemVersionChecker.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2017 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.base.plugin;
+
+import com.google.idea.blaze.base.model.BlazeVersionData;
+import com.google.idea.blaze.base.scope.BlazeContext;
+import com.intellij.openapi.extensions.ExtensionPointName;
+
+/**
+ * Verifies that the available Blaze version is supported by this plugin.
+ *
+ * <p>Notifies the user if they're using an unsupported version of Blaze.
+ */
+public interface BuildSystemVersionChecker {
+
+ ExtensionPointName<BuildSystemVersionChecker> EP_NAME =
+ ExtensionPointName.create("com.google.idea.blaze.BuildSystemVersionChecker");
+
+ static boolean verifyVersionSupported(BlazeContext context, BlazeVersionData version) {
+ for (BuildSystemVersionChecker checker : EP_NAME.getExtensions()) {
+ if (!checker.versionSupported(context, version)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns false if the blaze version is unsupported.
+ *
+ * <p>Also displays corresponding errors via the {@link BlazeContext}.
+ */
+ boolean versionSupported(BlazeContext context, BlazeVersionData version);
+}
diff --git a/base/src/com/google/idea/blaze/base/prefetch/FetchExecutor.java b/base/src/com/google/idea/blaze/base/prefetch/FetchExecutor.java
index c89cc4f..5011172 100644
--- a/base/src/com/google/idea/blaze/base/prefetch/FetchExecutor.java
+++ b/base/src/com/google/idea/blaze/base/prefetch/FetchExecutor.java
@@ -17,6 +17,7 @@
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
+import com.google.idea.common.concurrency.ConcurrencyUtil;
import com.intellij.util.concurrency.BoundedTaskExecutor;
import java.util.concurrent.Executors;
@@ -25,5 +26,10 @@
private static final int THREAD_COUNT = 32;
public static final ListeningExecutorService EXECUTOR =
MoreExecutors.listeningDecorator(
- new BoundedTaskExecutor(Executors.newFixedThreadPool(THREAD_COUNT), THREAD_COUNT));
+ new BoundedTaskExecutor(
+ // #api171 add this argument, the form without a name is deprecated
+ // FetchExecutor.class.getSimpleName(),
+ Executors.newFixedThreadPool(
+ THREAD_COUNT, ConcurrencyUtil.namedDaemonThreadPoolFactory(FetchExecutor.class)),
+ THREAD_COUNT));
}
diff --git a/base/src/com/google/idea/blaze/base/prefetch/PrefetchFileSource.java b/base/src/com/google/idea/blaze/base/prefetch/PrefetchFileSource.java
index 26c7491..1b6a3c2 100644
--- a/base/src/com/google/idea/blaze/base/prefetch/PrefetchFileSource.java
+++ b/base/src/com/google/idea/blaze/base/prefetch/PrefetchFileSource.java
@@ -15,25 +15,41 @@
*/
package com.google.idea.blaze.base.prefetch;
+import com.google.common.collect.ImmutableSet;
import com.google.idea.blaze.base.model.BlazeProjectData;
import com.google.idea.blaze.base.projectview.ProjectViewSet;
+import com.google.idea.blaze.base.sync.projectview.ImportRoots;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.project.Project;
import java.io.File;
-import java.util.Collection;
import java.util.Set;
/** Provides a source of files to prefetch */
public interface PrefetchFileSource {
+
ExtensionPointName<PrefetchFileSource> EP_NAME =
ExtensionPointName.create("com.google.idea.blaze.PrefetchFileSource");
- /** Adds any files or directories that we would be interested in prefetching. */
+
+ /** Returns all file extensions provided by available PrefetchFileSource implementations. */
+ static ImmutableSet<String> getAllPrefetchFileExtensions() {
+ ImmutableSet.Builder<String> extensionsToFetchContent = ImmutableSet.builder();
+ for (PrefetchFileSource fileSource : PrefetchFileSource.EP_NAME.getExtensions()) {
+ extensionsToFetchContent.addAll(fileSource.prefetchFileExtensions());
+ }
+ return extensionsToFetchContent.build();
+ }
+
+ /**
+ * Adds any files or directories that we would be interested in prefetching. Project source files
+ * should not be added here, as they're always prefetched.
+ */
void addFilesToPrefetch(
Project project,
ProjectViewSet projectViewSet,
+ ImportRoots importRoots,
BlazeProjectData blazeProjectData,
- Collection<File> files);
+ Set<File> files);
- /** Returns any source file extensions that are a good candidate for the {@link Prefetcher}. */
- Set<String> prefetchSrcFileExtensions();
+ /** Returns any file extensions that are a good candidate for the {@link Prefetcher}. */
+ Set<String> prefetchFileExtensions();
}
diff --git a/base/src/com/google/idea/blaze/base/prefetch/PrefetchService.java b/base/src/com/google/idea/blaze/base/prefetch/PrefetchService.java
index 726f970..c7ab12b 100644
--- a/base/src/com/google/idea/blaze/base/prefetch/PrefetchService.java
+++ b/base/src/com/google/idea/blaze/base/prefetch/PrefetchService.java
@@ -29,8 +29,14 @@
return ServiceManager.getService(PrefetchService.class);
}
- /** Instructs all prefetchers to prefetch these files. */
- ListenableFuture<?> prefetchFiles(Project project, Collection<File> files);
+ /**
+ * Instructs all prefetchers to prefetch these files.
+ *
+ * @param refetchCachedFiles True if all files should be fetched, regardless of whether they were
+ * recently fetched.
+ */
+ ListenableFuture<?> prefetchFiles(
+ Project project, Collection<File> files, boolean refetchCachedFiles);
ListenableFuture<?> prefetchProjectFiles(
Project project, ProjectViewSet projectViewSet, BlazeProjectData blazeProjectData);
diff --git a/base/src/com/google/idea/blaze/base/prefetch/PrefetchServiceImpl.java b/base/src/com/google/idea/blaze/base/prefetch/PrefetchServiceImpl.java
index 05e02a6..27d3c03 100644
--- a/base/src/com/google/idea/blaze/base/prefetch/PrefetchServiceImpl.java
+++ b/base/src/com/google/idea/blaze/base/prefetch/PrefetchServiceImpl.java
@@ -16,9 +16,11 @@
package com.google.idea.blaze.base.prefetch;
import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
+import com.google.idea.blaze.base.io.FileAttributeProvider;
import com.google.idea.blaze.base.model.BlazeProjectData;
import com.google.idea.blaze.base.model.primitives.WorkspacePath;
import com.google.idea.blaze.base.model.primitives.WorkspaceRoot;
@@ -26,24 +28,86 @@
import com.google.idea.blaze.base.settings.BlazeImportSettings;
import com.google.idea.blaze.base.settings.BlazeImportSettingsManager;
import com.google.idea.blaze.base.sync.projectview.ImportRoots;
+import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import java.io.File;
+import java.io.IOException;
import java.util.Collection;
import java.util.List;
+import java.util.Map;
import java.util.Set;
+import java.util.stream.Collectors;
+import javax.annotation.Nullable;
/** Implementation for prefetcher. */
public class PrefetchServiceImpl implements PrefetchService {
+ private static final Logger logger = Logger.getInstance(PrefetchServiceImpl.class);
+
+ private static final long REFETCH_PERIOD_MILLIS = 24 * 60 * 60 * 1000;
+ private final Map<Integer, Long> fileToLastFetchTimeMillis = Maps.newConcurrentMap();
+
@Override
- public ListenableFuture<?> prefetchFiles(Project project, Collection<File> files) {
+ public ListenableFuture<?> prefetchFiles(
+ Project project, Collection<File> files, boolean refetchCachedFiles) {
+ if (files.isEmpty() || !enabled(project)) {
+ return Futures.immediateFuture(null);
+ }
+ if (!refetchCachedFiles) {
+ long startTime = System.currentTimeMillis();
+ // ignore recently fetched files
+ files =
+ files
+ .stream()
+ .filter(file -> shouldPrefetch(file, startTime))
+ .collect(Collectors.toList());
+ }
+ FileAttributeProvider provider = FileAttributeProvider.getInstance();
+ List<ListenableFuture<File>> canonicalFiles =
+ files
+ .stream()
+ .map(file -> FetchExecutor.EXECUTOR.submit(() -> toCanonicalFile(provider, file)))
+ .collect(Collectors.toList());
List<ListenableFuture<?>> futures = Lists.newArrayList();
for (Prefetcher prefetcher : Prefetcher.EP_NAME.getExtensions()) {
- futures.add(prefetcher.prefetchFiles(project, files, FetchExecutor.EXECUTOR));
+ futures.add(prefetcher.prefetchFiles(project, canonicalFiles, FetchExecutor.EXECUTOR));
}
return Futures.allAsList(futures);
}
+ private static boolean enabled(Project project) {
+ for (Prefetcher prefetcher : Prefetcher.EP_NAME.getExtensions()) {
+ if (prefetcher.enabled(project)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Nullable
+ private static File toCanonicalFile(FileAttributeProvider provider, File file) {
+ try {
+ File canonicalFile = file.getCanonicalFile();
+ if (provider.exists(canonicalFile)) {
+ return canonicalFile;
+ }
+ } catch (IOException e) {
+ logger.warn(e);
+ }
+ return null;
+ }
+
+ /** Returns false if this file has been recently prefetched. */
+ private boolean shouldPrefetch(File file, long startTime) {
+ // Filter files that have been recently fetched
+ Long lastFetchTime = fileToLastFetchTimeMillis.get(file.hashCode());
+ if (lastFetchTime != null && (startTime - lastFetchTime < REFETCH_PERIOD_MILLIS)) {
+ return false;
+ }
+ fileToLastFetchTimeMillis.put(file.hashCode(), startTime);
+ return true;
+ }
+
@Override
public ListenableFuture<?> prefetchProjectFiles(
Project project, ProjectViewSet projectViewSet, BlazeProjectData blazeProjectData) {
@@ -53,6 +117,10 @@
return Futures.immediateFuture(null);
}
WorkspaceRoot workspaceRoot = WorkspaceRoot.fromImportSettings(importSettings);
+ if (!FileAttributeProvider.getInstance().exists(workspaceRoot.directory())) {
+ // quick sanity check before trying to prefetch each individual file
+ return Futures.immediateFuture(null);
+ }
ImportRoots importRoots =
ImportRoots.builder(workspaceRoot, importSettings.getBuildSystem())
.add(projectViewSet)
@@ -63,8 +131,8 @@
files.add(workspaceRoot.fileForPath(workspacePath));
}
for (PrefetchFileSource fileSource : PrefetchFileSource.EP_NAME.getExtensions()) {
- fileSource.addFilesToPrefetch(project, projectViewSet, blazeProjectData, files);
+ fileSource.addFilesToPrefetch(project, projectViewSet, importRoots, blazeProjectData, files);
}
- return prefetchFiles(project, files);
+ return prefetchFiles(project, files, false);
}
}
diff --git a/base/src/com/google/idea/blaze/base/prefetch/Prefetcher.java b/base/src/com/google/idea/blaze/base/prefetch/Prefetcher.java
index 64503e0..55b30c2 100644
--- a/base/src/com/google/idea/blaze/base/prefetch/Prefetcher.java
+++ b/base/src/com/google/idea/blaze/base/prefetch/Prefetcher.java
@@ -17,6 +17,7 @@
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.idea.blaze.base.settings.Blaze;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.project.Project;
import java.io.File;
@@ -28,10 +29,16 @@
ExtensionPointName.create("com.google.idea.blaze.Prefetcher");
/**
- * Prefetches the given list of files.
+ * Prefetches the given list of canonical files.
*
* <p>It is the responsibility of the prefetcher to filter out any files it isn't interested in.
*/
ListenableFuture<?> prefetchFiles(
- Project project, Collection<File> file, ListeningExecutorService executor);
+ Project project,
+ Collection<ListenableFuture<File>> fileFutures,
+ ListeningExecutorService executor);
+
+ default boolean enabled(Project project) {
+ return Blaze.isBlazeProject(project);
+ }
}
diff --git a/base/src/com/google/idea/blaze/base/prefetch/ProtoPrefetchFileSource.java b/base/src/com/google/idea/blaze/base/prefetch/ProtoPrefetchFileSource.java
new file mode 100644
index 0000000..efc4876
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/prefetch/ProtoPrefetchFileSource.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2017 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.base.prefetch;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.idea.blaze.base.model.BlazeProjectData;
+import com.google.idea.blaze.base.projectview.ProjectViewSet;
+import com.google.idea.blaze.base.sync.projectview.ImportRoots;
+import com.intellij.openapi.project.Project;
+import java.io.File;
+import java.util.Set;
+
+/** Requests that proto source files be prefetched during sync. */
+public class ProtoPrefetchFileSource implements PrefetchFileSource {
+
+ @Override
+ public void addFilesToPrefetch(
+ Project project,
+ ProjectViewSet projectViewSet,
+ ImportRoots importRoots,
+ BlazeProjectData blazeProjectData,
+ Set<File> files) {}
+
+ @Override
+ public Set<String> prefetchFileExtensions() {
+ return ImmutableSet.of("proto");
+ }
+}
diff --git a/base/src/com/google/idea/blaze/base/projectview/ProjectViewSet.java b/base/src/com/google/idea/blaze/base/projectview/ProjectViewSet.java
index e2e1211..29cc456 100644
--- a/base/src/com/google/idea/blaze/base/projectview/ProjectViewSet.java
+++ b/base/src/com/google/idea/blaze/base/projectview/ProjectViewSet.java
@@ -26,6 +26,7 @@
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
+import java.util.Optional;
import javax.annotation.Nullable;
/** A collection of project views and their file names. */
@@ -57,20 +58,12 @@
}
/** Gets the last value from any scalar sections */
- @Nullable
- public <T> T getScalarValue(SectionKey<T, ScalarSection<T>> key) {
- return getScalarValue(key, null);
- }
-
- /** Gets the last value from any scalar sections */
- @Nullable
- public <T> T getScalarValue(SectionKey<T, ScalarSection<T>> key, @Nullable T defaultValue) {
+ public <T> Optional<T> getScalarValue(SectionKey<T, ScalarSection<T>> key) {
Collection<ScalarSection<T>> sections = getSections(key);
if (sections.isEmpty()) {
- return defaultValue;
- } else {
- return Iterables.getLast(sections).getValue();
+ return Optional.empty();
}
+ return Optional.of(Iterables.getLast(sections).getValue());
}
public <T, SectionType extends Section<T>> Collection<SectionType> getSections(
diff --git a/base/src/com/google/idea/blaze/base/projectview/section/sections/AdditionalLanguagesSection.java b/base/src/com/google/idea/blaze/base/projectview/section/sections/AdditionalLanguagesSection.java
index 9e05e64..68ca00a 100644
--- a/base/src/com/google/idea/blaze/base/projectview/section/sections/AdditionalLanguagesSection.java
+++ b/base/src/com/google/idea/blaze/base/projectview/section/sections/AdditionalLanguagesSection.java
@@ -100,8 +100,9 @@
private static Set<LanguageClass> availableAdditionalLanguages(ProjectViewSet projectView) {
WorkspaceType workspaceType =
- projectView.getScalarValue(
- WorkspaceTypeSection.KEY, LanguageSupport.getDefaultWorkspaceType());
+ projectView
+ .getScalarValue(WorkspaceTypeSection.KEY)
+ .orElse(LanguageSupport.getDefaultWorkspaceType());
return LanguageSupport.availableAdditionalLanguages(workspaceType);
}
}
diff --git a/base/src/com/google/idea/blaze/base/projectview/section/sections/BuildFlagsSection.java b/base/src/com/google/idea/blaze/base/projectview/section/sections/BuildFlagsSection.java
index 51f0709..1cde7d7 100644
--- a/base/src/com/google/idea/blaze/base/projectview/section/sections/BuildFlagsSection.java
+++ b/base/src/com/google/idea/blaze/base/projectview/section/sections/BuildFlagsSection.java
@@ -21,6 +21,7 @@
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.settings.Blaze;
import javax.annotation.Nullable;
/** Section for blaze_flags */
@@ -29,7 +30,7 @@
public static final SectionParser PARSER = new BuildFlagsSectionParser();
static class BuildFlagsSectionParser extends ListSectionParser<String> {
- protected BuildFlagsSectionParser() {
+ BuildFlagsSectionParser() {
super(KEY);
}
@@ -51,7 +52,10 @@
@Override
public String quickDocs() {
- return "A set of flags that get passed to all build command invocations as arguments";
+ return String.format(
+ "A set of flags that get passed to all %s build command invocations as arguments. This"
+ + "includes both sync and run configuration actions.",
+ Blaze.guessBuildSystemName());
}
}
}
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 eda173f..3889951 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
@@ -33,11 +33,13 @@
AdditionalLanguagesSection.PARSER,
TestSourceSection.PARSER,
BuildFlagsSection.PARSER,
+ SyncFlagsSection.PARSER,
ImportTargetOutputSection.PARSER,
ExcludeTargetSection.PARSER,
ExcludedSourceSection.PARSER,
RunConfigurationsSection.PARSER,
- ShardBlazeBuildsSection.PARSER);
+ ShardBlazeBuildsSection.PARSER,
+ TargetShardSizeSection.PARSER);
public static List<SectionParser> getParsers() {
List<SectionParser> parsers = Lists.newArrayList(PARSERS);
diff --git a/base/src/com/google/idea/blaze/base/projectview/section/sections/SyncFlagsSection.java b/base/src/com/google/idea/blaze/base/projectview/section/sections/SyncFlagsSection.java
new file mode 100644
index 0000000..b2c4b11
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/projectview/section/sections/SyncFlagsSection.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2017 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.base.projectview.section.sections;
+
+import com.google.idea.blaze.base.projectview.parser.ParseContext;
+import com.google.idea.blaze.base.projectview.parser.ProjectViewParser;
+import com.google.idea.blaze.base.projectview.section.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.settings.Blaze;
+import javax.annotation.Nullable;
+
+/** Section for blaze_flags */
+public class SyncFlagsSection {
+ public static final SectionKey<String, ListSection<String>> KEY = SectionKey.of("sync_flags");
+ public static final SectionParser PARSER = new SyncFlagsSectionParser();
+
+ static class SyncFlagsSectionParser extends ListSectionParser<String> {
+ SyncFlagsSectionParser() {
+ super(KEY);
+ }
+
+ @Nullable
+ @Override
+ protected String parseItem(ProjectViewParser parser, ParseContext parseContext) {
+ return parseContext.current().text;
+ }
+
+ @Override
+ protected void printItem(String item, StringBuilder sb) {
+ sb.append(item);
+ }
+
+ @Override
+ public ItemType getItemType() {
+ return ItemType.Other;
+ }
+
+ @Override
+ public String quickDocs() {
+ return String.format(
+ "A set of flags that get passed to %s build during all sync actions. Unlike"
+ + "'build_flags', these are not used for run configurations, so use 'sync_flags' "
+ + "only when necessary, as they can defeat %<s caching.",
+ Blaze.guessBuildSystemName());
+ }
+ }
+}
diff --git a/base/src/com/google/idea/blaze/base/projectview/section/sections/TargetShardSizeSection.java b/base/src/com/google/idea/blaze/base/projectview/section/sections/TargetShardSizeSection.java
new file mode 100644
index 0000000..7d1b354
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/projectview/section/sections/TargetShardSizeSection.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2017 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.base.projectview.section.sections;
+
+import com.google.idea.blaze.base.projectview.parser.ParseContext;
+import com.google.idea.blaze.base.projectview.parser.ProjectViewParser;
+import com.google.idea.blaze.base.projectview.section.ScalarSection;
+import com.google.idea.blaze.base.projectview.section.ScalarSectionParser;
+import com.google.idea.blaze.base.projectview.section.SectionKey;
+import com.google.idea.blaze.base.projectview.section.SectionParser;
+import javax.annotation.Nullable;
+
+/** Allows the user to tune the maximum number of targets in each blaze build shard. */
+public class TargetShardSizeSection {
+ public static final SectionKey<Integer, ScalarSection<Integer>> KEY =
+ SectionKey.of("target_shard_size");
+ public static final SectionParser PARSER = new TargetShardSizeSectionParser();
+
+ private static class TargetShardSizeSectionParser extends ScalarSectionParser<Integer> {
+ TargetShardSizeSectionParser() {
+ super(KEY, ':');
+ }
+
+ @Nullable
+ @Override
+ protected Integer parseItem(ProjectViewParser parser, ParseContext parseContext, String rest) {
+ try {
+ return Integer.parseInt(rest);
+ } catch (NumberFormatException e) {
+ parseContext.addError(
+ String.format("Invalid shard size '%s': Shard size must be an integer", rest));
+ return null;
+ }
+ }
+
+ @Override
+ protected void printItem(StringBuilder sb, Integer value) {
+ sb.append(value.toString());
+ }
+
+ @Override
+ public ItemType getItemType() {
+ return ItemType.Other;
+ }
+
+ @Override
+ public String quickDocs() {
+ return "Sets the maximum number of targets per shard, when sharding build invocations during "
+ + "sync. Only relevant if 'shard_sync: true' is also set";
+ }
+ }
+}
diff --git a/base/src/com/google/idea/blaze/base/run/BlazeBuildTargetRunConfigurationFactory.java b/base/src/com/google/idea/blaze/base/run/BlazeBuildTargetRunConfigurationFactory.java
index afb6e79..f6fe1fb 100644
--- a/base/src/com/google/idea/blaze/base/run/BlazeBuildTargetRunConfigurationFactory.java
+++ b/base/src/com/google/idea/blaze/base/run/BlazeBuildTargetRunConfigurationFactory.java
@@ -15,9 +15,15 @@
*/
package com.google.idea.blaze.base.run;
+import com.google.common.collect.ImmutableSet;
+import com.google.idea.blaze.base.command.BlazeCommandName;
+import com.google.idea.blaze.base.ideinfo.TargetIdeInfo;
+import com.google.idea.blaze.base.ideinfo.TargetKey;
import com.google.idea.blaze.base.model.BlazeProjectData;
+import com.google.idea.blaze.base.model.primitives.Kind;
import com.google.idea.blaze.base.model.primitives.Label;
-import com.google.idea.blaze.base.run.producers.BlazeBuildFileRunConfigurationProducer;
+import com.google.idea.blaze.base.model.primitives.RuleType;
+import com.google.idea.blaze.base.run.state.BlazeCommandRunConfigurationCommonState;
import com.intellij.execution.configurations.ConfigurationFactory;
import com.intellij.execution.configurations.RunConfiguration;
import com.intellij.openapi.project.Project;
@@ -28,9 +34,14 @@
*/
public class BlazeBuildTargetRunConfigurationFactory extends BlazeRunConfigurationFactory {
+ // The rule types we auto-create run configurations for during sync.
+ private static final ImmutableSet<RuleType> HANDLED_RULE_TYPES =
+ ImmutableSet.of(RuleType.TEST, RuleType.BINARY);
+
@Override
public boolean handlesTarget(Project project, BlazeProjectData blazeProjectData, Label label) {
- return BlazeBuildFileRunConfigurationProducer.handlesTarget(project, label);
+ TargetIdeInfo target = blazeProjectData.targetMap.get(TargetKey.forPlainTarget(label));
+ return target != null && HANDLED_RULE_TYPES.contains(target.kind.ruleType);
}
@Override
@@ -40,6 +51,26 @@
@Override
public void setupConfiguration(RunConfiguration configuration, Label target) {
- BlazeBuildFileRunConfigurationProducer.setupConfiguration(configuration, target);
+ BlazeCommandRunConfiguration blazeConfig = (BlazeCommandRunConfiguration) configuration;
+ blazeConfig.setTarget(target);
+
+ BlazeCommandRunConfigurationCommonState state =
+ blazeConfig.getHandlerStateIfType(BlazeCommandRunConfigurationCommonState.class);
+ Kind kind = blazeConfig.getKindForTarget();
+ if (state != null && kind != null) {
+ state.getCommandState().setCommand(commandForRuleType(kind.ruleType));
+ }
+ blazeConfig.setGeneratedName();
+ }
+
+ private static BlazeCommandName commandForRuleType(RuleType ruleType) {
+ switch (ruleType) {
+ case BINARY:
+ return BlazeCommandName.RUN;
+ case TEST:
+ return BlazeCommandName.TEST;
+ default:
+ return BlazeCommandName.BUILD;
+ }
}
}
diff --git a/base/src/com/google/idea/blaze/base/run/BlazeRunConfigurationFactory.java b/base/src/com/google/idea/blaze/base/run/BlazeRunConfigurationFactory.java
index 6a8d573..9e40076 100644
--- a/base/src/com/google/idea/blaze/base/run/BlazeRunConfigurationFactory.java
+++ b/base/src/com/google/idea/blaze/base/run/BlazeRunConfigurationFactory.java
@@ -33,13 +33,8 @@
public abstract boolean handlesTarget(
Project project, BlazeProjectData blazeProjectData, Label label);
- /**
- * Returns whether this factory can initialize a configuration. <br>
- * The default implementation simply checks that the configuration has the same {@link
- * com.intellij.execution.configurations.ConfigurationType} as the type of {@link
- * #getConfigurationFactory()}.
- */
- public boolean handlesConfiguration(RunConfiguration configuration) {
+ /** Returns whether this factory is compatible with the given run configuration type. */
+ public final boolean handlesConfiguration(RunConfiguration configuration) {
return getConfigurationFactory().getType().equals(configuration.getType());
}
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 96be88d..f8a3b01 100644
--- a/base/src/com/google/idea/blaze/base/run/BlazeRunConfigurationSyncListener.java
+++ b/base/src/com/google/idea/blaze/base/run/BlazeRunConfigurationSyncListener.java
@@ -122,7 +122,6 @@
configurationFactory.createForTarget(project, runManager, label);
runManager.addConfiguration(settings, /* isShared */ false);
if (runManager.getSelectedConfiguration() == null) {
- // TODO(joshgiles): Better strategy for picking initially selected config.
runManager.setSelectedConfiguration(settings);
}
break;
diff --git a/base/src/com/google/idea/blaze/base/run/DistributedExecutorSupport.java b/base/src/com/google/idea/blaze/base/run/DistributedExecutorSupport.java
index c6991ca..a5ff002 100644
--- a/base/src/com/google/idea/blaze/base/run/DistributedExecutorSupport.java
+++ b/base/src/com/google/idea/blaze/base/run/DistributedExecutorSupport.java
@@ -23,7 +23,11 @@
import java.util.List;
import javax.annotation.Nullable;
-/** Information about any distributed executor available to the build system. */
+/**
+ * Information about any distributed executor available to the build system.
+ *
+ * <p>TODO(brendandouglas): Temporary migration code. Remove in 2017.09.XX+
+ */
public interface DistributedExecutorSupport {
ExtensionPointName<DistributedExecutorSupport> EP_NAME =
diff --git a/base/src/com/google/idea/blaze/base/run/TestTargetHeuristic.java b/base/src/com/google/idea/blaze/base/run/TestTargetHeuristic.java
index 74a46e4..f063415 100644
--- a/base/src/com/google/idea/blaze/base/run/TestTargetHeuristic.java
+++ b/base/src/com/google/idea/blaze/base/run/TestTargetHeuristic.java
@@ -24,7 +24,10 @@
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import java.io.File;
+import java.util.ArrayList;
import java.util.Collection;
+import java.util.List;
+import java.util.stream.Collectors;
import javax.annotation.Nullable;
/** Heuristic to match test targets to source files. */
@@ -64,22 +67,28 @@
File sourceFile,
Collection<TargetIdeInfo> targets,
@Nullable TestSize testSize) {
-
+ if (targets.isEmpty()) {
+ return null;
+ }
+ List<TargetIdeInfo> filteredTargets = new ArrayList<>(targets);
for (TestTargetHeuristic filter : EP_NAME.getExtensions()) {
- TargetIdeInfo match =
- targets
+ List<TargetIdeInfo> matches =
+ filteredTargets
.stream()
.filter(
target ->
filter.matchesSource(project, target, sourcePsiFile, sourceFile, testSize))
- .findFirst()
- .orElse(null);
-
- if (match != null) {
- return match.key.label;
+ .collect(Collectors.toList());
+ if (matches.size() == 1) {
+ return matches.get(0).key.label;
+ }
+ if (!matches.isEmpty()) {
+ // A higher-priority filter found more than one match -- subsequent filters will only
+ // consider these matches.
+ filteredTargets = matches;
}
}
- return targets.isEmpty() ? null : targets.iterator().next().key.label;
+ return filteredTargets.iterator().next().key.label;
}
/** Returns true if the rule and source file match, according to this heuristic. */
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 4605c17..5e77bfe 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
@@ -20,17 +20,18 @@
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.BlazeInvocationContext;
import com.google.idea.blaze.base.issueparser.IssueOutputLineProcessor;
import com.google.idea.blaze.base.model.primitives.WorkspaceRoot;
import com.google.idea.blaze.base.projectview.ProjectViewManager;
import com.google.idea.blaze.base.projectview.ProjectViewSet;
import com.google.idea.blaze.base.run.BlazeCommandRunConfiguration;
-import com.google.idea.blaze.base.run.DistributedExecutorSupport;
import com.google.idea.blaze.base.run.filter.BlazeTargetFilter;
import com.google.idea.blaze.base.run.processhandler.LineProcessingProcessAdapter;
import com.google.idea.blaze.base.run.processhandler.ScopedBlazeProcessHandler;
-import com.google.idea.blaze.base.run.smrunner.BlazeTestEventsHandler;
+import com.google.idea.blaze.base.run.smrunner.BlazeTestUiSession;
import com.google.idea.blaze.base.run.smrunner.SmRunnerUtils;
+import com.google.idea.blaze.base.run.smrunner.TestUiSessionProvider;
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;
@@ -48,6 +49,7 @@
import com.intellij.execution.configurations.WrappingRunConfiguration;
import com.intellij.execution.filters.Filter;
import com.intellij.execution.filters.TextConsoleBuilderImpl;
+import com.intellij.execution.filters.UrlFilter;
import com.intellij.execution.process.ProcessHandler;
import com.intellij.execution.process.ProcessListener;
import com.intellij.execution.runners.ExecutionEnvironment;
@@ -91,6 +93,7 @@
ImmutableList.<Filter>builder()
.addAll(consoleFilters)
.add(new BlazeTargetFilter(environment.getProject()))
+ .add(new UrlFilter())
.build();
}
@@ -121,18 +124,18 @@
assert projectViewSet != null;
ImmutableList<String> testHandlerFlags = ImmutableList.of();
- BlazeTestEventsHandler testEventsHandler =
+ BlazeTestUiSession testUiSession =
canUseTestUi()
- ? BlazeTestEventsHandler.getHandlerForTarget(project, configuration.getTarget())
+ ? TestUiSessionProvider.createForTarget(project, configuration.getTarget())
: null;
- if (testEventsHandler != null) {
- testHandlerFlags = BlazeTestEventsHandler.getBlazeFlags(project);
+ if (testUiSession != null) {
+ testHandlerFlags = testUiSession.getBlazeFlags();
setConsoleBuilder(
new TextConsoleBuilderImpl(project) {
@Override
protected ConsoleView createConsole() {
return SmRunnerUtils.getConsoleView(
- project, configuration, getEnvironment().getExecutor(), testEventsHandler);
+ project, configuration, getEnvironment().getExecutor(), testUiSession);
}
});
}
@@ -170,23 +173,23 @@
? handlerState.getBlazeBinaryState().getBlazeBinary()
: Blaze.getBuildSystemProvider(project).getBinaryPath();
- BlazeCommand.Builder command =
- BlazeCommand.builder(binaryPath, handlerState.getCommandState().getCommand())
- .addTargets(configuration.getTarget())
- .addBlazeFlags(BlazeFlags.buildFlags(project, projectViewSet))
- .addBlazeFlags(testHandlerFlags)
- .addBlazeFlags(handlerState.getBlazeFlagsState().getExpandedFlags())
- .addExeFlags(handlerState.getExeFlagsState().getExpandedFlags());
+ return BlazeCommand.builder(binaryPath, getCommand())
+ .addTargets(configuration.getTarget())
+ .addBlazeFlags(
+ BlazeFlags.blazeFlags(
+ project, projectViewSet, getCommand(), BlazeInvocationContext.RunConfiguration))
+ .addBlazeFlags(testHandlerFlags)
+ .addBlazeFlags(handlerState.getBlazeFlagsState().getExpandedFlags())
+ .addExeFlags(handlerState.getExeFlagsState().getExpandedFlags())
+ .build();
+ }
- command.addBlazeFlags(
- DistributedExecutorSupport.getBlazeFlags(
- project, handlerState.getRunOnDistributedExecutorState().runOnDistributedExecutor));
- return command.build();
+ private BlazeCommandName getCommand() {
+ return handlerState.getCommandState().getCommand();
}
private boolean canUseTestUi() {
- return BlazeCommandName.TEST.equals(handlerState.getCommandState().getCommand())
- && !handlerState.getRunOnDistributedExecutorState().runOnDistributedExecutor;
+ return BlazeCommandName.TEST.equals(getCommand());
}
}
}
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
index aed9db4..a52a756 100644
--- a/base/src/com/google/idea/blaze/base/run/exporter/RunConfigurationSerializer.java
+++ b/base/src/com/google/idea/blaze/base/run/exporter/RunConfigurationSerializer.java
@@ -17,6 +17,7 @@
import com.google.common.annotations.VisibleForTesting;
import com.google.idea.blaze.base.run.BlazeRunConfiguration;
+import com.google.idea.sdkcompat.run.RunnerAndConfigurationSettingsCompatUtils;
import com.intellij.execution.RunnerAndConfigurationSettings;
import com.intellij.execution.configurations.RunConfiguration;
import com.intellij.execution.impl.RunManagerImpl;
@@ -89,7 +90,7 @@
throws InvalidDataException {
RunManagerImpl manager = RunManagerImpl.getInstanceImpl(project);
RunnerAndConfigurationSettingsImpl settings = new RunnerAndConfigurationSettingsImpl(manager);
- settings.readExternal(element);
+ RunnerAndConfigurationSettingsCompatUtils.readConfiguration(settings, element);
RunConfiguration config = settings.getConfiguration();
if (config == null) {
return null;
diff --git a/base/src/com/google/idea/blaze/base/run/producers/BlazeBuildFileRunConfigurationProducer.java b/base/src/com/google/idea/blaze/base/run/producers/BlazeBuildFileRunConfigurationProducer.java
index 9a838ed..6ce791c 100644
--- a/base/src/com/google/idea/blaze/base/run/producers/BlazeBuildFileRunConfigurationProducer.java
+++ b/base/src/com/google/idea/blaze/base/run/producers/BlazeBuildFileRunConfigurationProducer.java
@@ -111,14 +111,11 @@
setupConfiguration(
configuration.getProject(), blazeProjectData, generatedConfiguration, target);
- // TODO This check should be removed once isTestRule is in a RuleFactory and
- // test rules' suggestedName is modified to account for test filter flags.
- if (Kind.isTestRule(target.ruleType)) {
- BlazeCommandRunConfigurationCommonState handlerState =
- configuration.getHandlerStateIfType(BlazeCommandRunConfigurationCommonState.class);
- if (handlerState != null && handlerState.getTestFilterFlag() != null) {
- return false;
- }
+ // ignore filtered test configs, produced by other configuration producers.
+ BlazeCommandRunConfigurationCommonState handlerState =
+ configuration.getHandlerStateIfType(BlazeCommandRunConfigurationCommonState.class);
+ if (handlerState != null && handlerState.getTestFilterFlag() != null) {
+ return false;
}
return Objects.equals(configuration.suggestedName(), generatedConfiguration.suggestedName())
diff --git a/base/src/com/google/idea/blaze/base/run/producers/BlazeFilterExistingRunConfigurationProducer.java b/base/src/com/google/idea/blaze/base/run/producers/BlazeFilterExistingRunConfigurationProducer.java
index 92089e4..d1941a1 100644
--- a/base/src/com/google/idea/blaze/base/run/producers/BlazeFilterExistingRunConfigurationProducer.java
+++ b/base/src/com/google/idea/blaze/base/run/producers/BlazeFilterExistingRunConfigurationProducer.java
@@ -31,7 +31,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
-import javax.annotation.Nullable;
+import java.util.Optional;
/**
* Handles the specific case where the user creates a run configuration by selecting test suites /
@@ -53,8 +53,8 @@
BlazeCommandRunConfiguration configuration,
ConfigurationContext context,
Ref<PsiElement> sourceElement) {
- String testFilter = getTestFilter(context);
- if (testFilter == null) {
+ Optional<String> testFilter = getTestFilter(context);
+ if (!testFilter.isPresent()) {
return false;
}
BlazeCommandRunConfigurationCommonState handlerState =
@@ -66,7 +66,7 @@
// replace old test filter flag if present
List<String> flags = new ArrayList<>(handlerState.getBlazeFlagsState().getRawFlags());
flags.removeIf((flag) -> flag.startsWith(BlazeFlags.TEST_FILTER));
- flags.add(testFilter);
+ flags.add(testFilter.get());
if (SmRunnerUtils.countSelectedTestCases(context) == 1
&& !flags.contains(BlazeFlags.DISABLE_TEST_SHARDING)) {
@@ -81,8 +81,8 @@
@Override
protected boolean doIsConfigFromContext(
BlazeCommandRunConfiguration configuration, ConfigurationContext context) {
- String testFilter = getTestFilter(context);
- if (testFilter == null) {
+ Optional<String> testFilter = getTestFilter(context);
+ if (!testFilter.isPresent()) {
return false;
}
BlazeCommandRunConfigurationCommonState handlerState =
@@ -90,28 +90,25 @@
return handlerState != null
&& Objects.equals(handlerState.getCommandState().getCommand(), BlazeCommandName.TEST)
- && Objects.equals(testFilter, handlerState.getTestFilterFlag());
+ && Objects.equals(testFilter.get(), handlerState.getTestFilterFlag());
}
- @Nullable
- private static String getTestFilter(ConfigurationContext context) {
+ private static Optional<String> getTestFilter(ConfigurationContext context) {
RunConfiguration base = context.getOriginalConfiguration(null);
if (!(base instanceof BlazeCommandRunConfiguration)) {
- return null;
+ return Optional.empty();
}
TargetExpression target = ((BlazeCommandRunConfiguration) base).getTarget();
if (target == null) {
- return null;
- }
- BlazeTestEventsHandler testEventsHandler =
- BlazeTestEventsHandler.getHandlerForTarget(context.getProject(), target);
- if (testEventsHandler == null) {
- return null;
+ return Optional.empty();
}
List<Location<?>> selectedElements = SmRunnerUtils.getSelectedSmRunnerTreeElements(context);
if (selectedElements.isEmpty()) {
return null;
}
- return testEventsHandler.getTestFilter(context.getProject(), selectedElements);
+ Optional<BlazeTestEventsHandler> testEventsHandler =
+ BlazeTestEventsHandler.getHandlerForTarget(context.getProject(), target);
+ return testEventsHandler.map(
+ handler -> handler.getTestFilter(context.getProject(), selectedElements));
}
}
diff --git a/base/src/com/google/idea/blaze/base/run/smrunner/BazelTestUiSessionProvider.java b/base/src/com/google/idea/blaze/base/run/smrunner/BazelTestUiSessionProvider.java
new file mode 100644
index 0000000..d9ec034
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/run/smrunner/BazelTestUiSessionProvider.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2017 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.base.run.smrunner;
+
+import com.google.common.collect.ImmutableList;
+import com.google.idea.blaze.base.command.buildresult.BuildEventProtocolUtils;
+import com.google.idea.blaze.base.model.BlazeVersionData;
+import com.google.idea.blaze.base.run.testlogs.BuildEventProtocolTestFinderStrategy;
+import com.google.idea.blaze.base.settings.Blaze.BuildSystem;
+import java.io.File;
+import javax.annotation.Nullable;
+
+/** Provides a {@link BlazeTestUiSession} for Bazel projects. */
+public class BazelTestUiSessionProvider implements TestUiSessionProvider {
+
+ @Nullable
+ @Override
+ public BlazeTestUiSession getTestUiSession(BlazeVersionData blazeVersion) {
+ if (blazeVersion.buildSystem() != BuildSystem.Bazel) {
+ return null;
+ }
+
+ File bepOutputFile = BuildEventProtocolUtils.createTempOutputFile();
+ ImmutableList<String> flags =
+ ImmutableList.<String>builder()
+ .add("--runs_per_test=1", "--flaky_test_attempts=1")
+ .addAll(BuildEventProtocolUtils.getBuildFlags(bepOutputFile))
+ .build();
+
+ return BlazeTestUiSession.create(
+ flags, new BuildEventProtocolTestFinderStrategy(bepOutputFile));
+ }
+}
diff --git a/base/src/com/google/idea/blaze/base/run/smrunner/BlazeCompositeTestEventsHandler.java b/base/src/com/google/idea/blaze/base/run/smrunner/BlazeCompositeTestEventsHandler.java
deleted file mode 100644
index 21bf76a..0000000
--- a/base/src/com/google/idea/blaze/base/run/smrunner/BlazeCompositeTestEventsHandler.java
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright 2017 The Bazel Authors. All rights reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.google.idea.blaze.base.run.smrunner;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Maps;
-import com.google.idea.blaze.base.model.primitives.Kind;
-import com.google.idea.blaze.base.run.smrunner.BlazeXmlSchema.TestSuite;
-import com.intellij.execution.Location;
-import com.intellij.execution.testframework.actions.AbstractRerunFailedTestsAction;
-import com.intellij.execution.testframework.sm.runner.SMTestLocator;
-import com.intellij.execution.ui.ConsoleView;
-import com.intellij.openapi.project.Project;
-import java.util.EnumSet;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.stream.Collectors;
-import javax.annotation.Nullable;
-
-/** Combines multiple language-specific handlers (e.g. to handle test_suite targets). */
-public class BlazeCompositeTestEventsHandler extends BlazeTestEventsHandler {
-
- private static ImmutableMap<Kind, BlazeTestEventsHandler> collectHandlers() {
- Map<Kind, BlazeTestEventsHandler> map = new HashMap<>();
- for (BlazeTestEventsHandler handler : BlazeTestEventsHandler.EP_NAME.getExtensions()) {
- if (handler instanceof BlazeCompositeTestEventsHandler) {
- continue;
- }
- for (Kind kind : handler.handledKinds()) {
- // earlier handlers get priority.
- map.putIfAbsent(kind, handler);
- }
- }
- return Maps.immutableEnumMap(map);
- }
-
- private static ImmutableMap<Kind, BlazeTestEventsHandler> handlers;
-
- private static ImmutableMap<Kind, BlazeTestEventsHandler> getHandlers() {
- if (handlers == null) {
- handlers = collectHandlers();
- }
- return handlers;
- }
-
- @Override
- public boolean handlesTargetKind(@Nullable Kind kind) {
- // composite handler specifically exists to handle test-suites and multi-target blaze
- // invocations, so must handle targets without a kind.
- return kind == null || kind == Kind.TEST_SUITE || handledKinds().contains(kind);
- }
-
- @Override
- protected EnumSet<Kind> handledKinds() {
- ImmutableSet<Kind> handledKinds = getHandlers().keySet();
- return !handledKinds.isEmpty() ? EnumSet.copyOf(handledKinds) : EnumSet.noneOf(Kind.class);
- }
-
- @Override
- public SMTestLocator getTestLocator() {
- return new CompositeSMTestLocator(
- ImmutableList.copyOf(
- getHandlers()
- .values()
- .stream()
- .map(BlazeTestEventsHandler::getTestLocator)
- .collect(Collectors.toList())));
- }
-
- @Nullable
- @Override
- public String getTestFilter(Project project, List<Location<?>> testLocations) {
- // We make no attempt to support re-running a subset of tests for test_suites or target patterns
- return null;
- }
-
- @Nullable
- @Override
- public AbstractRerunFailedTestsAction createRerunFailedTestsAction(ConsoleView consoleView) {
- return null;
- }
-
- @Override
- public boolean ignoreSuite(@Nullable Kind kind, TestSuite suite) {
- BlazeTestEventsHandler handler = kind != null ? getHandlers().get(kind) : null;
- return handler != null ? handler.ignoreSuite(kind, suite) : super.ignoreSuite(kind, suite);
- }
-
- /** Converts the testsuite name in the blaze test XML to a user-friendly format */
- @Override
- public String suiteDisplayName(@Nullable Kind kind, String rawName) {
- BlazeTestEventsHandler handler = kind != null ? getHandlers().get(kind) : null;
- return handler != null
- ? handler.suiteDisplayName(kind, rawName)
- : super.suiteDisplayName(kind, rawName);
- }
-
- /** Converts the testcase name in the blaze test XML to a user-friendly format */
- @Override
- public String testDisplayName(@Nullable Kind kind, String rawName) {
- BlazeTestEventsHandler handler = kind != null ? getHandlers().get(kind) : null;
- return handler != null
- ? handler.testDisplayName(kind, rawName)
- : super.testDisplayName(kind, rawName);
- }
-
- @Override
- public String suiteLocationUrl(@Nullable Kind kind, String name) {
- BlazeTestEventsHandler handler = kind != null ? getHandlers().get(kind) : null;
- return handler != null
- ? handler.suiteLocationUrl(kind, name)
- : super.suiteLocationUrl(kind, name);
- }
-
- @Override
- public String testLocationUrl(
- @Nullable Kind kind, String parentSuite, String name, @Nullable String className) {
- BlazeTestEventsHandler handler = getHandlers().get(kind);
- return handler != null
- ? handler.testLocationUrl(kind, parentSuite, name, className)
- : super.testLocationUrl(kind, parentSuite, name, className);
- }
-}
diff --git a/base/src/com/google/idea/blaze/base/run/smrunner/BlazeGenericTestEventsHandler.java b/base/src/com/google/idea/blaze/base/run/smrunner/BlazeGenericTestEventsHandler.java
new file mode 100644
index 0000000..996b756
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/run/smrunner/BlazeGenericTestEventsHandler.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2017 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.base.run.smrunner;
+
+import com.google.idea.blaze.base.model.primitives.Kind;
+import com.intellij.execution.Location;
+import com.intellij.execution.testframework.actions.AbstractRerunFailedTestsAction;
+import com.intellij.execution.testframework.sm.runner.SMTestLocator;
+import com.intellij.execution.ui.ConsoleView;
+import com.intellij.openapi.project.Project;
+import java.util.List;
+import javax.annotation.Nullable;
+
+/**
+ * Fallback handler for otherwise unsupported targets. Normally it's undesirable to have a test UI
+ * for such targets, but if they're part of a test_suite or multi-target Blaze invocation, we handle
+ * them in a best-effort way.
+ */
+public class BlazeGenericTestEventsHandler implements BlazeTestEventsHandler {
+
+ @Override
+ public boolean handlesKind(@Nullable Kind kind) {
+ // Generic handler specifically exists to handle test-suites and multi-target blaze
+ // invocations, so must handle any targets without a (known) kind.
+ return kind == null || kind == Kind.TEST_SUITE;
+ }
+
+ @Override
+ @Nullable
+ public SMTestLocator getTestLocator() {
+ return null;
+ }
+
+ @Nullable
+ @Override
+ public String getTestFilter(Project project, List<Location<?>> testLocations) {
+ // Test filters are language-specific, and don't work properly for multi-target invocations.
+ return null;
+ }
+
+ @Nullable
+ @Override
+ public AbstractRerunFailedTestsAction createRerunFailedTestsAction(ConsoleView consoleView) {
+ return null;
+ }
+}
diff --git a/base/src/com/google/idea/blaze/base/run/smrunner/BlazeRerunFailedTestsAction.java b/base/src/com/google/idea/blaze/base/run/smrunner/BlazeRerunFailedTestsAction.java
index 578bb9b..4fc47bb 100644
--- a/base/src/com/google/idea/blaze/base/run/smrunner/BlazeRerunFailedTestsAction.java
+++ b/base/src/com/google/idea/blaze/base/run/smrunner/BlazeRerunFailedTestsAction.java
@@ -42,7 +42,7 @@
private final BlazeTestEventsHandler eventsHandler;
- public BlazeRerunFailedTestsAction(
+ BlazeRerunFailedTestsAction(
BlazeTestEventsHandler eventsHandler, ComponentContainer componentContainer) {
super(componentContainer);
this.eventsHandler = eventsHandler;
diff --git a/base/src/com/google/idea/blaze/base/run/smrunner/BlazeTestConsoleProperties.java b/base/src/com/google/idea/blaze/base/run/smrunner/BlazeTestConsoleProperties.java
index f4c95b9..7e55569 100644
--- a/base/src/com/google/idea/blaze/base/run/smrunner/BlazeTestConsoleProperties.java
+++ b/base/src/com/google/idea/blaze/base/run/smrunner/BlazeTestConsoleProperties.java
@@ -15,8 +15,9 @@
*/
package com.google.idea.blaze.base.run.smrunner;
+import com.google.common.collect.ImmutableList;
+import com.google.idea.blaze.base.run.BlazeCommandRunConfiguration;
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;
@@ -24,34 +25,50 @@
import com.intellij.execution.testframework.sm.runner.SMTRunnerConsoleProperties;
import com.intellij.execution.testframework.sm.runner.SMTestLocator;
import com.intellij.execution.ui.ConsoleView;
+import java.util.Arrays;
+import java.util.Objects;
+import java.util.stream.Collectors;
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;
+ private final BlazeCommandRunConfiguration runConfiguration;
+ private final BlazeTestUiSession testUiSession;
public BlazeTestConsoleProperties(
- RunConfiguration runConfiguration, Executor executor, BlazeTestEventsHandler eventsHandler) {
+ BlazeCommandRunConfiguration runConfiguration,
+ Executor executor,
+ BlazeTestUiSession testUiSession) {
super(runConfiguration, SmRunnerUtils.BLAZE_FRAMEWORK, executor);
- this.eventsHandler = eventsHandler;
+ this.runConfiguration = runConfiguration;
+ this.testUiSession = testUiSession;
}
@Override
public OutputToGeneralTestEventsConverter createTestEventsConverter(
String framework, TestConsoleProperties consoleProperties) {
- return new BlazeXmlToTestEventsConverter(framework, consoleProperties, eventsHandler);
+ return new BlazeXmlToTestEventsConverter(
+ framework, consoleProperties, testUiSession.getTestResultFinderStrategy());
}
@Override
public SMTestLocator getTestLocator() {
- return eventsHandler.getTestLocator();
+ return new CompositeSMTestLocator(
+ ImmutableList.copyOf(
+ Arrays.stream(BlazeTestEventsHandler.EP_NAME.getExtensions())
+ .map(BlazeTestEventsHandler::getTestLocator)
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList())));
}
@Nullable
@Override
public AbstractRerunFailedTestsAction createRerunFailedTestsAction(ConsoleView consoleView) {
- return eventsHandler.createRerunFailedTestsAction(consoleView);
+ return BlazeTestEventsHandler.getHandlerForTarget(
+ runConfiguration.getProject(), runConfiguration.getTarget())
+ .map(handler -> handler.createRerunFailedTestsAction(consoleView))
+ .orElse(null);
}
}
diff --git a/base/src/com/google/idea/blaze/base/run/smrunner/BlazeTestEventsHandler.java b/base/src/com/google/idea/blaze/base/run/smrunner/BlazeTestEventsHandler.java
index 3ecdeea..47f229c 100644
--- a/base/src/com/google/idea/blaze/base/run/smrunner/BlazeTestEventsHandler.java
+++ b/base/src/com/google/idea/blaze/base/run/smrunner/BlazeTestEventsHandler.java
@@ -16,15 +16,12 @@
package com.google.idea.blaze.base.run.smrunner;
import com.google.common.base.Strings;
-import com.google.common.collect.ImmutableList;
import com.google.idea.blaze.base.ideinfo.TargetIdeInfo;
import com.google.idea.blaze.base.model.primitives.Kind;
import com.google.idea.blaze.base.model.primitives.Label;
import com.google.idea.blaze.base.model.primitives.TargetExpression;
import com.google.idea.blaze.base.run.smrunner.BlazeXmlSchema.TestSuite;
import com.google.idea.blaze.base.run.targetfinder.TargetFinder;
-import com.google.idea.blaze.base.settings.Blaze;
-import com.google.idea.blaze.base.settings.Blaze.BuildSystem;
import com.intellij.execution.Location;
import com.intellij.execution.testframework.actions.AbstractRerunFailedTestsAction;
import com.intellij.execution.testframework.sm.runner.SMTestLocator;
@@ -32,46 +29,60 @@
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.project.Project;
import com.intellij.util.io.URLUtil;
-import java.util.EnumSet;
+import java.util.Arrays;
import java.util.List;
+import java.util.Optional;
import javax.annotation.Nullable;
-/** Language-specific handling of SM runner test protocol */
-public abstract class BlazeTestEventsHandler {
+/** Stateless language-specific handling of SM runner test protocol */
+public interface BlazeTestEventsHandler {
- static final ExtensionPointName<BlazeTestEventsHandler> EP_NAME =
+ ExtensionPointName<BlazeTestEventsHandler> EP_NAME =
ExtensionPointName.create("com.google.idea.blaze.BlazeTestEventsHandler");
/**
- * Blaze/Bazel flags required for test UI.<br>
- * Forces local test execution, without retries.
+ * Whether there's a {@link BlazeTestEventsHandler} applicable to the given target.
+ *
+ * <p>Test results will still be displayed for unhandled kinds if they're included in a test_suite
+ * or multi-target Blaze invocation, where we don't know up front the languages involved.
*/
- public static ImmutableList<String> getBlazeFlags(Project project) {
- ImmutableList.Builder<String> flags =
- ImmutableList.<String>builder().add("--runs_per_test=1", "--flaky_test_attempts=1");
- if (Blaze.getBuildSystem(project) == BuildSystem.Blaze) {
- flags.add("--test_strategy=local");
- }
- if (Blaze.getBuildSystem(project) == BuildSystem.Bazel) {
- flags.add("--test_sharding_strategy=disabled");
- }
- return flags.build();
- }
-
- @Nullable
- public static BlazeTestEventsHandler getHandlerForTarget(
- Project project, TargetExpression target) {
+ static boolean targetSupported(Project project, TargetExpression target) {
Kind kind = getKindForTarget(project, target);
- for (BlazeTestEventsHandler handler : EP_NAME.getExtensions()) {
- if (handler.handlesTargetKind(kind)) {
- return handler;
- }
- }
- return null;
+ return Arrays.stream(EP_NAME.getExtensions()).anyMatch(handler -> handler.handlesKind(kind));
+ }
+
+ /**
+ * Returns a {@link BlazeTestEventsHandler} applicable to the given target.
+ *
+ * <p>If no such handler exists, falls back to returning {@link BlazeGenericTestEventsHandler}.
+ * This adds support for test suites / multi-target invocations, which can mix supported and
+ * unsupported target kinds.
+ */
+ static BlazeTestEventsHandler getHandlerForTargetKindOrFallback(@Nullable Kind kind) {
+ return getHandlerForTargetKind(kind).orElse(new BlazeGenericTestEventsHandler());
+ }
+
+ /**
+ * Returns a {@link BlazeTestEventsHandler} applicable to the given target or {@link
+ * Optional#empty()} if no such handler can be found.
+ */
+ static Optional<BlazeTestEventsHandler>