Initial import of e4b, an Eclipse plugin for Bazel

This is really rudimentory. It simply supports creating the
classpath from a project.

You can create a new Bazel project with New project > Import Bazel project
Then you can select the list of targets you want to build and the list
of directories you want to track.

Fixes bazelbuild/bazel#12.

Change-Id: I7b33931ebc4539ff3070c6e68d43587a52674d07
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..117330a
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+bin
+.settings
+.DS_Store
+/.metadata
+/.recommenders
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..a0c5cc1
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,9 @@
+# This the official list of e4b authors for copyright purposes.
+# This file is distinct from the CONTRIBUTORS files.
+# See the latter for an explanation.
+
+# Names should be added to this file as:
+# Name or Organization <email address>
+# The email address is not required for organizations.
+
+Google Inc.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..9d3d1aa
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,27 @@
+Want to contribute? Great! First, read this page (including the small print at the end).
+
+### Before you contribute
+**Before we can use your code, you must sign the
+[Google Individual Contributor License Agreement](https://developers.google.com/open-source/cla/individual?csw=1)
+(CLA)**, which you can do online.
+
+The CLA is necessary mainly because you own the copyright to your changes,
+even after your contribution becomes part of our codebase, so we need your
+permission to use and distribute your code. We also need to be sure of
+various other things — for instance that you'll tell us if you know that
+your code infringes on other people's patents. You don't have to sign
+the CLA until after you've submitted your code for review and a member has
+approved it, but you must do it before we can put your code into our codebase.
+
+Before you start working on a larger contribution, you should get in touch
+with us first. Use the issue tracker to explain your idea so we can help and
+possibly guide you.
+
+### Code reviews and other contributions.
+**All submissions, including submissions by project members, require review.**
+Please follow the instructions in [the contributors documentation](http://bazel.io/contributing.html).
+
+### The small print
+Contributions made by corporations are covered by a different agreement than
+the one above, the
+[Software Grant and Corporate Contributor License Agreement](https://cla.developers.google.com/about/google-corporate).
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
new file mode 100644
index 0000000..70420a1
--- /dev/null
+++ b/CONTRIBUTORS
@@ -0,0 +1,13 @@
+# People who have agreed to one of the CLAs and can contribute patches.
+# The AUTHORS file lists the copyright holders; this file
+# lists people.  For example, Google employees are listed here
+# but not in AUTHORS, because Google holds the copyright.
+#
+# https://developers.google.com/open-source/cla/individual
+# https://developers.google.com/open-source/cla/corporate
+#
+# Names should be added to this file as:
+#     Name <email address>
+
+Damien Martin-Guillerez <dmarting@google.com>
+Dmitry Lomov <dslomov@google.com>
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   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.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..0471a9c
--- /dev/null
+++ b/README.md
@@ -0,0 +1,23 @@
+# Eclipse plugin for [Bazel](http://bazel.io) (**Highly experimental**)
+
+e4b is an Eclipse plugin for Bazel. It is really rudimentary.
+It simply supports creating the classpath from a project and using
+Bazel for incremental builds..
+
+You can create a new Bazel project with New project > Import Bazel project
+Then you can select the list of targets you want to build and the list
+of directories you want to track.
+
+It is highly experimental and support is minimal. However, we are happy
+to accept contribution to make this support grows to a better shape.
+
+## Missing features
+
+Interesting feature to add is first to be able to launch test from
+Eclipse, correctly adding the eclipse debugger to the JVM.
+
+Second, better support for BUILD file would be interesting. Maybe
+using Xtext.
+
+Finally, the code is completely missing tests and improving the test
+coverage would be great.
diff --git a/com.google.devtools.bazel.e4b.feature/.project b/com.google.devtools.bazel.e4b.feature/.project
new file mode 100644
index 0000000..7eb8ae8
--- /dev/null
+++ b/com.google.devtools.bazel.e4b.feature/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>com.google.devtools.bazel.e4b.feature</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.pde.FeatureBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.pde.FeatureNature</nature>
+	</natures>
+</projectDescription>
diff --git a/com.google.devtools.bazel.e4b.feature/build.properties b/com.google.devtools.bazel.e4b.feature/build.properties
new file mode 100644
index 0000000..64f93a9
--- /dev/null
+++ b/com.google.devtools.bazel.e4b.feature/build.properties
@@ -0,0 +1 @@
+bin.includes = feature.xml
diff --git a/com.google.devtools.bazel.e4b.feature/feature.xml b/com.google.devtools.bazel.e4b.feature/feature.xml
new file mode 100644
index 0000000..930919a
--- /dev/null
+++ b/com.google.devtools.bazel.e4b.feature/feature.xml
@@ -0,0 +1,230 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<feature
+      id="com.google.devtools.bazel.e4b.feature"
+      label="Eclipse 4 Bazel"
+      version="0.0.1.qualifier"
+      provider-name="The Bazel Authors">
+
+   <description url="http://bazel.io">
+      Integrate Eclipse with the Bazel build system.
+   </description>
+
+   <copyright>
+      Copyright 2016 The Bazel Authors
+   </copyright>
+
+   <license url="http://www.apache.org/licenses/LICENSE-2.0">
+      Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      &quot;License&quot; shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      &quot;Licensor&quot; shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      &quot;Legal Entity&quot; shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      &quot;control&quot; means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      &quot;You&quot; (or &quot;Your&quot;) shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      &quot;Source&quot; form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      &quot;Object&quot; form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      &quot;Work&quot; shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      &quot;Derivative Works&quot; shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      &quot;Contribution&quot; shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, &quot;submitted&quot;
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as &quot;Not a Contribution.&quot;
+
+      &quot;Contributor&quot; shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a &quot;NOTICE&quot; text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an &quot;AS IS&quot; BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets &quot;[]&quot;
+      replaced with your own identifying information. (Don&apos;t include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same &quot;printed page&quot; as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+   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 &quot;AS IS&quot; 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.
+   </license>
+
+   <url>
+      <discovery label="Bazel" url="http://bazel.io"/>
+   </url>
+
+   <plugin
+         id="com.google.devtools.bazel.e4b"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"/>
+
+</feature>
diff --git a/com.google.devtools.bazel.e4b.p2updatesite/.project b/com.google.devtools.bazel.e4b.p2updatesite/.project
new file mode 100644
index 0000000..5d42537
--- /dev/null
+++ b/com.google.devtools.bazel.e4b.p2updatesite/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>com.google.devtools.bazel.e4b.p2updatesite</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.pde.UpdateSiteBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.pde.UpdateSiteNature</nature>
+	</natures>
+</projectDescription>
diff --git a/com.google.devtools.bazel.e4b.p2updatesite/site.xml b/com.google.devtools.bazel.e4b.p2updatesite/site.xml
new file mode 100644
index 0000000..754bec8
--- /dev/null
+++ b/com.google.devtools.bazel.e4b.p2updatesite/site.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<site>
+   <description name="Eclipse 4 Bazel" url="https://bazelbuild.github.io/e4b">
+      Eclipse plugin for Bazel
+   </description>
+   <feature url="features/com.google.devtools.bazel.e4b.feature_0.0.1.qualifier.jar" id="com.google.devtools.bazel.e4b.feature" version="0.0.1.qualifier"/>
+</site>
diff --git a/com.google.devtools.bazel.e4b/.classpath b/com.google.devtools.bazel.e4b/.classpath
new file mode 100644
index 0000000..0d67874
--- /dev/null
+++ b/com.google.devtools.bazel.e4b/.classpath
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry exported="true" kind="lib" path="libs/guava-19.0.jar"/>
+	<classpathentry exported="true" kind="lib" path="libs/json-20160212.jar"/>
+	<classpathentry exported="true" kind="lib" path="bin" sourcepath="bin"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/com.google.devtools.bazel.e4b/.project b/com.google.devtools.bazel.e4b/.project
new file mode 100644
index 0000000..d2246b4
--- /dev/null
+++ b/com.google.devtools.bazel.e4b/.project
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>com.google.devtools.bazel.e4b</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.ManifestBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.SchemaBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.api.tools.apiAnalysisBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.pde.PluginNature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.eclipse.pde.api.tools.apiAnalysisNature</nature>
+	</natures>
+</projectDescription>
diff --git a/com.google.devtools.bazel.e4b/META-INF/MANIFEST.MF b/com.google.devtools.bazel.e4b/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..52c2073
--- /dev/null
+++ b/com.google.devtools.bazel.e4b/META-INF/MANIFEST.MF
@@ -0,0 +1,23 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Eclipse 4 Bazel
+Bundle-SymbolicName: com.google.devtools.bazel.e4b;singleton:=true
+Bundle-Version: 0.0.1.qualifier
+Bundle-Activator: com.google.devtools.bazel.e4b.Activator
+Bundle-Vendor: The Bazel Authors
+Require-Bundle: org.eclipse.ui.console,
+ org.eclipse.core.jobs,
+ org.eclipse.jface,
+ javax.inject,
+ org.eclipse.core.runtime,
+ org.eclipse.ui,
+ org.eclipse.core.runtime,
+ org.eclipse.jdt.core,
+ org.eclipse.jdt,
+ org.eclipse.core.resources
+Bundle-RequiredExecutionEnvironment: JavaSE-1.8
+Bundle-ActivationPolicy: lazy
+Bundle-ClassPath: libs/guava-19.0.jar,
+ libs/json-20160212.jar,
+ bin/
+Import-Package: org.eclipse.ui.dialogs
diff --git a/com.google.devtools.bazel.e4b/build.properties b/com.google.devtools.bazel.e4b/build.properties
new file mode 100644
index 0000000..23afbb5
--- /dev/null
+++ b/com.google.devtools.bazel.e4b/build.properties
@@ -0,0 +1,8 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+               resources/,\
+               plugin.xml,\
+               libs/guava-19.0.jar,\
+               libs/json-20160212.jar,\
+               bin/
diff --git a/com.google.devtools.bazel.e4b/libs/LICENSE.guava b/com.google.devtools.bazel.e4b/libs/LICENSE.guava
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/com.google.devtools.bazel.e4b/libs/LICENSE.guava
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   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.
diff --git a/com.google.devtools.bazel.e4b/libs/LICENSE.json b/com.google.devtools.bazel.e4b/libs/LICENSE.json
new file mode 100644
index 0000000..c04b6d2
--- /dev/null
+++ b/com.google.devtools.bazel.e4b/libs/LICENSE.json
@@ -0,0 +1,9 @@
+Copyright (c) 2002 JSON.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+The Software shall be used for Good, not Evil.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/com.google.devtools.bazel.e4b/libs/README.md b/com.google.devtools.bazel.e4b/libs/README.md
new file mode 100644
index 0000000..ddeb561
--- /dev/null
+++ b/com.google.devtools.bazel.e4b/libs/README.md
@@ -0,0 +1,15 @@
+This file lists license and version information of all code we did not
+author, but ship together with the source so building e4b requires
+a minimal set of extra dependencies.
+
+
+## [guava](https://code.google.com/p/guava-libraries/)
+
+* Version: 19.0
+* License: Apache License 2.0
+
+## [json-java](https://github.com/douglascrockford/JSON-java)
+
+* Version: 20160212
+* License: The JSON License
+
diff --git a/com.google.devtools.bazel.e4b/libs/guava-19.0.jar b/com.google.devtools.bazel.e4b/libs/guava-19.0.jar
new file mode 100644
index 0000000..b175ca8
--- /dev/null
+++ b/com.google.devtools.bazel.e4b/libs/guava-19.0.jar
Binary files differ
diff --git a/com.google.devtools.bazel.e4b/libs/json-20160212.jar b/com.google.devtools.bazel.e4b/libs/json-20160212.jar
new file mode 100644
index 0000000..21e09db
--- /dev/null
+++ b/com.google.devtools.bazel.e4b/libs/json-20160212.jar
Binary files differ
diff --git a/com.google.devtools.bazel.e4b/plugin.xml b/com.google.devtools.bazel.e4b/plugin.xml
new file mode 100644
index 0000000..b938b9c
--- /dev/null
+++ b/com.google.devtools.bazel.e4b/plugin.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.4"?>
+<plugin>
+   <extension
+         point="org.eclipse.jdt.core.classpathContainerInitializer">
+      <classpathContainerInitializer
+            class="com.google.devtools.bazel.e4b.classpath.BazelClasspathContainerInitilalizer"
+            id="com.google.devtools.bazel.e4b.BAZEL_CONTAINER">
+      </classpathContainerInitializer>
+   </extension>
+   <extension
+         point="org.eclipse.ui.newWizards">
+    <wizard
+          class="com.google.devtools.bazel.e4b.wizard.BazelWizard"
+          icon="resources/icon.ico"
+          id="com.google.devtools.bazel.e4b.wizard.bazelWizard"
+          name="Import Bazel Workspace"
+          project="true">
+           <description>Import a list of sources from a Bazel workspace into a Java project</description>
+    </wizard>
+   </extension>
+   <extension
+         id="com.google.devtools.bazel.e4b.projectNature"
+         point="org.eclipse.core.resources.natures">
+      <runtime>
+         <run
+               class="com.google.devtools.bazel.e4b.ProjectNature">
+         </run>
+      </runtime>
+   </extension>
+   <extension
+         point="org.eclipse.ui.preferencePages">
+      <page
+            class="com.google.devtools.bazel.e4b.preferences.BazelPreferencePage"
+            id="com.google.devtools.bazel.e4b.preferences"
+            name="Bazel Plugin Preferences">
+      </page>
+   </extension>
+   <extension
+         point="org.eclipse.core.runtime.preferences">
+      <initializer
+            class="com.google.devtools.bazel.e4b.preferences.BazelPreferenceInitializer">
+      </initializer>
+   </extension>
+   <extension
+         id="com.google.devtools.bazel.e4b.builder"
+         point="org.eclipse.core.resources.builders">
+      <builder
+            callOnEmptyDelta="false"
+            hasNature="false"
+            isConfigurable="false"
+            supportsConfigurations="false">
+         <run
+               class="com.google.devtools.bazel.e4b.builder.BazelBuilder">
+         </run>
+      </builder>
+   </extension>
+
+</plugin>
diff --git a/com.google.devtools.bazel.e4b/resources/icon.ico b/com.google.devtools.bazel.e4b/resources/icon.ico
new file mode 100644
index 0000000..97fc067
--- /dev/null
+++ b/com.google.devtools.bazel.e4b/resources/icon.ico
Binary files differ
diff --git a/com.google.devtools.bazel.e4b/resources/tools/must/be/unique/BUILD b/com.google.devtools.bazel.e4b/resources/tools/must/be/unique/BUILD
new file mode 100644
index 0000000..a9cd58e
--- /dev/null
+++ b/com.google.devtools.bazel.e4b/resources/tools/must/be/unique/BUILD
@@ -0,0 +1,4 @@
+# This is a build file for creating a corresponding package. This package will be added to the
+# package path when using the aspect so we can load the e4b_aspect.bzl Skylark aspect extension
+# when calling build from Eclipse. This package has an improbable name to avoid collision with
+# packages in the main repository.
\ No newline at end of file
diff --git a/com.google.devtools.bazel.e4b/resources/tools/must/be/unique/e4b_aspect.bzl b/com.google.devtools.bazel.e4b/resources/tools/must/be/unique/e4b_aspect.bzl
new file mode 100644
index 0000000..59b5d41
--- /dev/null
+++ b/com.google.devtools.bazel.e4b/resources/tools/must/be/unique/e4b_aspect.bzl
@@ -0,0 +1,171 @@
+# 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.
+#
+# Aspect for e4b, taken from intellij_info.bzl
+
+DEPENDENCY_ATTRIBUTES = [
+  "deps",
+  "runtime_deps",
+]
+
+def struct_omit_none(**kwargs):
+    d = {name: kwargs[name] for name in kwargs if kwargs[name] != None}
+    return struct(**d)
+
+def artifact_location(file):
+  if file == None:
+    return None
+  return ((file.root.path + "/") if not file.is_source else "") + file.short_path
+
+def library_artifact(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(java_output.source_jar),
+  )
+
+def annotation_processing_jars(annotation_processing):
+  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 ide-resolve-files from Java output.
+  """
+  if output == None:
+    return []
+  return [jar
+          for jar in [output.class_jar, output.ijar, output.source_jar]
+          if jar != None and not jar.is_source]
+
+def java_rule_ide_info(target, ctx):
+  if hasattr(ctx.rule.attr, "srcs"):
+     sources = [artifact_location(file)
+                for src in ctx.rule.attr.srcs
+                for file in src.files]
+  else:
+     sources = []
+
+  jars = [library_artifact(output) for output in target.java.outputs.jars]
+  ide_resolve_files = set([jar
+       for output in target.java.outputs.jars
+       for jar in jars_from_output(output)])
+
+  gen_jars = []
+  if target.java.annotation_processing and target.java.annotation_processing.enabled:
+    gen_jars = [annotation_processing_jars(target.java.annotation_processing)]
+    ide_resolve_files = ide_resolve_files | set([ jar
+        for jar in [target.java.annotation_processing.class_jar,
+                    target.java.annotation_processing.source_jar]
+        if jar != None and not jar.is_source])
+
+  return (struct_omit_none(
+                 sources = sources,
+                 jars = jars,
+                 generated_jars = gen_jars
+          ),
+          ide_resolve_files)
+
+
+def _aspect_impl(target, ctx):
+  kind = ctx.rule.kind
+  rule_attrs = ctx.rule.attr
+
+  ide_info_text = set()
+  ide_resolve_files = set()
+  all_deps = []
+
+  for attr_name in DEPENDENCY_ATTRIBUTES:
+    if hasattr(rule_attrs, attr_name):
+      deps = getattr(rule_attrs, attr_name)
+      for dep in deps:
+        ide_info_text = ide_info_text | dep.intellij_info_files.ide_info_text
+        ide_resolve_files = ide_resolve_files | dep.intellij_info_files.ide_resolve_files
+      all_deps += [str(dep.label) for dep in deps]
+
+  if hasattr(target, "java"):
+    (java_rule_ide_info, java_ide_resolve_files) = java_rule_ide_info(target, ctx)
+    info = struct(
+        label = str(target.label),
+        kind = kind,
+        dependencies = all_deps,
+        build_file_artifact_location = ctx.build_file_path,
+    ) + java_rule_ide_info
+    ide_resolve_files = ide_resolve_files | java_ide_resolve_files
+    output = ctx.new_file(target.label.name + ".e4b-build.json")
+    ctx.file_action(output, info.to_json())
+    ide_info_text += set([output])
+
+  return struct(
+      output_groups = {
+        "ide-info-text" : ide_info_text,
+        "ide-resolve" : ide_resolve_files,
+      },
+      intellij_info_files = struct(
+        ide_info_text = ide_info_text,
+        ide_resolve_files = ide_resolve_files,
+      )
+    )
+
+e4b_aspect = aspect(implementation = _aspect_impl,
+    attr_aspects = DEPENDENCY_ATTRIBUTES
+)
+"""Aspect for Eclipse 4 Bazel plugin.
+
+This aspect produces information for IDE integration with Eclipse. This only
+produces information for Java targets.
+
+This aspect has two output groups:
+  - ide-info-text produces .e4b-build.json files that contains information
+    about target dependencies and sources files for the IDE.
+  - ide-resolve build the dependencies needed for the build (i.e., artifacts
+    generated by Java annotation processors).
+
+An e4b-build.json file is a json blob with the following keys:
+```javascript
+{
+  // Label of the corresponding target
+  "label": "//package:target",
+  // Kind of the corresponding target, e.g., java_test, java_binary, ...
+  "kind": "java_library",
+  // List of dependencies of this target
+  "dependencies": ["//package1:dep1", "//package2:dep2"],
+  "Path, relative to the workspace root, of the build file containing the target.
+  "build_file_artifact_location": "package/BUILD",
+  // List of sources file, relative to the execroot
+  "sources": ["package/Test.java"],
+  // List of jars created when building this target.
+  "jars": [jar1, jar2],
+  // List of jars generated by java annotation processors when building this target.
+  "generated_jars": [genjar1, genjar2]
+}
+```
+
+Jar files structure has the following keys:
+```javascript
+{
+  // Location, relative to the execroot, of the jar file or null
+  "jar": "bazel-out/host/package/libtarget.jar",
+  // Location, relative to the execroot, of the interface jar file,
+  // containing only the interfaces of the target jar or null.
+  "interface_jar": "bazel-out/host/package/libtarget.interface-jar",
+  // Location, relative to the execroot, of the source jar file,
+  // containing the sources used to generate the target jar or null.
+  "source_jar": "bazel-out/host/package/libtarget.interface-jar",
+}
+```
+"""
diff --git a/com.google.devtools.bazel.e4b/src/com/google/devtools/bazel/e4b/Activator.java b/com.google.devtools.bazel.e4b/src/com/google/devtools/bazel/e4b/Activator.java
new file mode 100644
index 0000000..69aa39a
--- /dev/null
+++ b/com.google.devtools.bazel.e4b/src/com/google/devtools/bazel/e4b/Activator.java
@@ -0,0 +1,148 @@
+// 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.devtools.bazel.e4b;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.ProjectScope;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.preferences.IScopeContext;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.prefs.BackingStoreException;
+import org.osgi.service.prefs.Preferences;
+
+import com.google.common.collect.ImmutableList;
+import com.google.devtools.bazel.e4b.command.BazelCommand;
+import com.google.devtools.bazel.e4b.command.BazelCommand.BazelInstance;
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+public class Activator extends AbstractUIPlugin {
+
+  // The plug-in ID
+  public static final String PLUGIN_ID = "com.google.devtools.bazel.e4b"; //$NON-NLS-1$
+
+  // The shared instance
+  private static Activator plugin;
+
+  private BazelCommand command;
+
+  public static final String DEFAULT_BAZEL_PATH = "/usr/local/bin/bazel";
+
+  /**
+   * The constructor
+   */
+  public Activator() {}
+
+  /*
+   * (non-Javadoc)
+   *
+   * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext)
+   */
+  public void start(BundleContext context) throws Exception {
+    plugin = this;
+    super.start(context);
+    this.command = new BazelCommand();
+    // Get the bazel path from the settings
+    this.command.setBazelPath(getPreferenceStore().getString("BAZEL_PATH"));
+    getPreferenceStore().addPropertyChangeListener(new IPropertyChangeListener() {
+      @Override
+      public void propertyChange(PropertyChangeEvent event) {
+        if (event.getProperty().equals("BAZEL_PATH")) {
+          command.setBazelPath(event.getNewValue().toString());
+        }
+      }
+    });
+  }
+
+  /*
+   * (non-Javadoc)
+   *
+   * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext)
+   */
+  public void stop(BundleContext context) throws Exception {
+    plugin = null;
+    this.command = null;
+    super.stop(context);
+  }
+
+  /**
+   * Returns the shared instance
+   *
+   * @return the shared instance
+   */
+  public static Activator getDefault() {
+    return plugin;
+  }
+
+  /**
+   * Returns the unique instance of {@link BazelCommand}.
+   */
+  public BazelCommand getCommand() {
+    return command;
+  }
+
+
+  /**
+   * List targets configure for <code>project</code>. Each project configured for Bazel is
+   * configured to track certain targets and this function fetch this list from the project
+   * preferences.
+   */
+  public static ImmutableList<String> getTargets(IProject project) throws BackingStoreException {
+    // Get the list of targets from the preferences
+    IScopeContext projectScope = new ProjectScope(project);
+    Preferences projectNode = projectScope.getNode(PLUGIN_ID);
+    ImmutableList.Builder<String> builder = ImmutableList.builder();
+    for (String s : projectNode.keys()) {
+      if (s.startsWith("target")) {
+        builder.add(projectNode.get(s, ""));
+      }
+    }
+    return builder.build();
+  }
+
+  /**
+   * Return the {@link BazelInstance} corresponding to the given <code>project</code>. It looks for
+   * the instance that runs for the workspace root configured for that project.
+   */
+  public static BazelCommand.BazelInstance getBazelCommandInstance(IProject project)
+      throws BackingStoreException, IOException, InterruptedException {
+    IScopeContext projectScope = new ProjectScope(project.getProject());
+    Preferences projectNode = projectScope.getNode(Activator.PLUGIN_ID);
+    File workspaceRoot =
+        new File(projectNode.get("workspaceRoot", project.getLocation().toFile().toString()));
+    return getDefault().getCommand().getInstance(workspaceRoot);
+  }
+
+  /**
+   * Log an error to eclipse.
+   */
+  public static void error(String message) {
+    plugin.getLog().log(new Status(Status.ERROR, PLUGIN_ID, message));
+  }
+
+  /**
+   * Log an error to eclipse, with an attached exception.
+   */
+  public static void error(String message, Throwable exception) {
+    plugin.getLog().log(new Status(Status.ERROR, PLUGIN_ID, message, exception));
+  }
+}
diff --git a/com.google.devtools.bazel.e4b/src/com/google/devtools/bazel/e4b/ProjectNature.java b/com.google.devtools.bazel.e4b/src/com/google/devtools/bazel/e4b/ProjectNature.java
new file mode 100644
index 0000000..b7bd69e
--- /dev/null
+++ b/com.google.devtools.bazel.e4b/src/com/google/devtools/bazel/e4b/ProjectNature.java
@@ -0,0 +1,41 @@
+// Copyright 2016 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.devtools.bazel.e4b;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IProjectNature;
+import org.eclipse.core.runtime.CoreException;
+
+/**
+ * Project nature for e4b.
+ */
+public class ProjectNature implements IProjectNature {
+
+  public static final String NATURE_ID = "com.google.devtools.bazel.e4b.projectNature"; //$NON-NLS-1$
+
+  @Override
+  public void configure() throws CoreException {}
+
+  @Override
+  public void deconfigure() throws CoreException {}
+
+  @Override
+  public IProject getProject() {
+    return null;
+  }
+
+  @Override
+  public void setProject(IProject project) {}
+}
diff --git a/com.google.devtools.bazel.e4b/src/com/google/devtools/bazel/e4b/builder/BazelBuilder.java b/com.google.devtools.bazel.e4b/src/com/google/devtools/bazel/e4b/builder/BazelBuilder.java
new file mode 100644
index 0000000..2faa4c4
--- /dev/null
+++ b/com.google.devtools.bazel.e4b/src/com/google/devtools/bazel/e4b/builder/BazelBuilder.java
@@ -0,0 +1,52 @@
+// 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.devtools.bazel.e4b.builder;
+
+import java.io.IOException;
+import java.util.Map;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResourceDelta;
+import org.eclipse.core.resources.IncrementalProjectBuilder;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.osgi.service.prefs.BackingStoreException;
+
+import com.google.devtools.bazel.e4b.Activator;
+import com.google.devtools.bazel.e4b.command.BazelCommand.BazelInstance;
+
+public class BazelBuilder extends IncrementalProjectBuilder {
+
+  @Override
+  protected IProject[] build(int kind, Map<String, String> args, IProgressMonitor monitor)
+      throws CoreException {
+    IProject project = getProject();
+    try {
+      BazelInstance instance = Activator.getBazelCommandInstance(project);
+      if (kind == INCREMENTAL_BUILD || kind == AUTO_BUILD) {
+        IResourceDelta delta = getDelta(getProject());
+        if (delta == null || delta.getAffectedChildren().length == 0) {
+          // null build, skip calling Bazel.
+          return null;
+        }
+      }
+      instance.markAsDirty();
+      instance.build(Activator.getTargets(project));
+    } catch (BackingStoreException | IOException | InterruptedException e) {
+      Activator.error("Failed to build " + project.getName(), e);
+    }
+    return null;
+  }
+}
diff --git a/com.google.devtools.bazel.e4b/src/com/google/devtools/bazel/e4b/classpath/BazelClasspathContainer.java b/com.google.devtools.bazel.e4b/src/com/google/devtools/bazel/e4b/classpath/BazelClasspathContainer.java
new file mode 100644
index 0000000..ec18b89
--- /dev/null
+++ b/com.google.devtools.bazel.e4b/src/com/google/devtools/bazel/e4b/classpath/BazelClasspathContainer.java
@@ -0,0 +1,159 @@
+// 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.devtools.bazel.e4b.classpath;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.FileSystems;
+import java.nio.file.Path;
+import java.nio.file.PathMatcher;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspaceRoot;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.jdt.core.IClasspathContainer;
+import org.eclipse.jdt.core.IClasspathEntry;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.core.JavaModelException;
+import org.osgi.service.prefs.BackingStoreException;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.devtools.bazel.e4b.Activator;
+import com.google.devtools.bazel.e4b.command.BazelCommand.BazelInstance;
+import com.google.devtools.bazel.e4b.command.IdeBuildInfo;
+import com.google.devtools.bazel.e4b.command.IdeBuildInfo.Jars;
+
+public class BazelClasspathContainer implements IClasspathContainer {
+  public static final String CONTAINER_NAME = "com.google.devtools.bazel.e4b.BAZEL_CONTAINER";
+
+  private final IPath path;
+  private final IJavaProject project;
+  private final BazelInstance instance;
+
+  public BazelClasspathContainer(IPath path, IJavaProject project)
+      throws IOException, InterruptedException, BackingStoreException, JavaModelException {
+    this.path = path;
+    this.project = project;
+    this.instance = Activator.getBazelCommandInstance(project.getProject());
+  }
+
+  private boolean isSourcePath(String path) throws JavaModelException, BackingStoreException {
+    Path pp = new File(instance.getWorkspaceRoot().toString() + File.separator + path).toPath();
+    IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
+    for (IClasspathEntry entry : project.getRawClasspath()) {
+      if (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
+        IResource res = root.findMember(entry.getPath());
+        if (res != null) {
+          String file = res.getLocation().toOSString();
+          if (!file.isEmpty() && pp.startsWith(file)) {
+            IPath[] inclusionPatterns = entry.getInclusionPatterns();
+            if (!matchPatterns(pp, entry.getExclusionPatterns()) && (inclusionPatterns == null
+                || inclusionPatterns.length == 0 || matchPatterns(pp, inclusionPatterns))) {
+              return true;
+            }
+          }
+        }
+      }
+    }
+    return false;
+  }
+
+  private boolean matchPatterns(Path path, IPath[] patterns) {
+    if (patterns != null) {
+      for (IPath p : patterns) {
+        PathMatcher matcher = FileSystems.getDefault().getPathMatcher("glob:" + p.toOSString());
+        if (matcher.matches(path)) {
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
+  private boolean isSourceInPaths(List<String> sources)
+      throws JavaModelException, BackingStoreException {
+    for (String s : sources) {
+      if (isSourcePath(s)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  @Override
+  public IClasspathEntry[] getClasspathEntries() {
+    try {
+      ImmutableList<String> targets = Activator.getTargets(project.getProject());
+      ImmutableMap<String, IdeBuildInfo> infos = instance.getIdeInfo(targets);
+      Set<Jars> jars = new HashSet<>();
+      for (IdeBuildInfo s : infos.values()) {
+        jars.addAll(s.getGeneratedJars());
+        if (!isSourceInPaths(s.getSources())) {
+          jars.addAll(s.getJars());
+        }
+      }
+      return jarsToClasspathEntries(jars);
+    } catch (JavaModelException | BackingStoreException | IOException | InterruptedException e) {
+      Activator.error("Unable to compute classpath containers entries.", e);
+      return new IClasspathEntry[] {};
+    }
+  }
+
+  private IClasspathEntry[] jarsToClasspathEntries(Set<Jars> jars) {
+    IClasspathEntry[] entries = new IClasspathEntry[jars.size()];
+    int i = 0;
+    File execRoot = instance.getExecRoot();
+    for (Jars j : jars) {
+      entries[i] = JavaCore.newLibraryEntry(getJarIPath(execRoot, j.getJar()),
+          getJarIPath(execRoot, j.getSrcJar()), null);
+      i++;
+    }
+    return entries;
+  }
+
+  private static IPath getJarIPath(File execRoot, String file) {
+    if (file == null) {
+      return null;
+    }
+    File path = new File(execRoot, file);
+    return org.eclipse.core.runtime.Path.fromOSString(path.toString());
+  }
+
+  @Override
+  public String getDescription() {
+    return "Bazel Classpath Container";
+  }
+
+  @Override
+  public int getKind() {
+    return K_APPLICATION;
+  }
+
+  @Override
+  public IPath getPath() {
+    return path;
+  }
+
+
+  public boolean isValid() {
+    return instance != null;
+  }
+}
diff --git a/com.google.devtools.bazel.e4b/src/com/google/devtools/bazel/e4b/classpath/BazelClasspathContainerInitilalizer.java b/com.google.devtools.bazel.e4b/src/com/google/devtools/bazel/e4b/classpath/BazelClasspathContainerInitilalizer.java
new file mode 100644
index 0000000..204b0dd
--- /dev/null
+++ b/com.google.devtools.bazel.e4b/src/com/google/devtools/bazel/e4b/classpath/BazelClasspathContainerInitilalizer.java
@@ -0,0 +1,46 @@
+// Copyright 2016 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.devtools.bazel.e4b.classpath;
+
+import java.io.IOException;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.jdt.core.ClasspathContainerInitializer;
+import org.eclipse.jdt.core.IClasspathContainer;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.JavaCore;
+import org.osgi.service.prefs.BackingStoreException;
+
+import com.google.devtools.bazel.e4b.Activator;
+
+public class BazelClasspathContainerInitilalizer extends ClasspathContainerInitializer {
+
+  @Override
+  public void initialize(IPath path, IJavaProject project) throws CoreException {
+    try {
+      BazelClasspathContainer container = new BazelClasspathContainer(path, project);
+      if (container.isValid()) {
+        JavaCore.setClasspathContainer(path, new IJavaProject[] {project},
+            new IClasspathContainer[] {container}, null);
+      } else {
+        Activator.error("Unable to create classpath container (Not a Bazel workspace?)");
+      }
+    } catch (IOException | InterruptedException | BackingStoreException e) {
+      Activator.error("Error while creating Bazel classpath container.", e);
+    }
+  }
+
+}
diff --git a/com.google.devtools.bazel.e4b/src/com/google/devtools/bazel/e4b/command/BazelCommand.java b/com.google.devtools.bazel.e4b/src/com/google/devtools/bazel/e4b/command/BazelCommand.java
new file mode 100644
index 0000000..dfe2d8d
--- /dev/null
+++ b/com.google.devtools.bazel.e4b/src/com/google/devtools/bazel/e4b/command/BazelCommand.java
@@ -0,0 +1,333 @@
+// 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.devtools.bazel.e4b.command;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.URL;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+
+import org.eclipse.core.runtime.FileLocator;
+import org.eclipse.core.runtime.Platform;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.devtools.bazel.e4b.Activator;
+
+/**
+ * Main utility to call bazel commands, wrapping its input and output to the message console.
+ */
+public class BazelCommand {
+
+  private static Joiner NEW_LINE_JOINER = Joiner.on("\n");
+
+  // Returns the path of the resources file from this plugin.
+  private static File getAspectWorkspace() {
+    try {
+      URL url = Platform.getBundle(Activator.PLUGIN_ID).getEntry("resources");
+      URL resolved = FileLocator.resolve(url);
+      return new File(resolved.getPath());
+    } catch (IOException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  private static enum ConsoleType {
+    NO_CONSOLE, SYSTEM, WORKSPACE
+  }
+
+  private static final File ASPECT_WORKSPACE = getAspectWorkspace();
+  private static final List<String> BUILD_OPTIONS =
+      ImmutableList.of("--experimental_interleave_loading_and_analysis", "--strategy=Javac=worker",
+          "--noexperimental_check_output_files",
+          "--aspects=tools/must/be/unique/e4b_aspect.bzl%e4b_aspect");
+  private static final List<String> ASPECT_OPTIONS = ImmutableList
+      .<String>builder().addAll(BUILD_OPTIONS).add("-k",
+          "--output_groups=ide-info-text,ide-resolve,-_,-defaults", "--experimental_show_artifacts")
+      .build();
+
+  private final Map<File, BazelInstance> instances = new HashMap<>();
+  private String bazel = Activator.DEFAULT_BAZEL_PATH;
+
+  /**
+   * Set the path to the Bazel binary (/usr/local/bin/bazel by default).
+   */
+  public synchronized void setBazelPath(String bazel) {
+    this.bazel = bazel;
+  }
+
+  /**
+   * Returns a {@link BazelInstance} for the given directory. It looks for the enclosing workspace
+   * and returns the instance that correspond to it. If not in a workspace, returns null.
+   */
+  public BazelInstance getInstance(File directory) throws IOException, InterruptedException {
+    File workspaceRoot = getWorkspaceRoot(directory);
+    if (workspaceRoot == null) {
+      return null;
+    }
+    if (!instances.containsKey(workspaceRoot)) {
+      instances.put(workspaceRoot, new BazelInstance(workspaceRoot));
+    }
+    return instances.get(workspaceRoot);
+  }
+
+  /**
+   * An instance of the Bazel interface for a specific workspace. Provides means to query Bazel on
+   * this workspace.
+   */
+  public class BazelInstance {
+    private final File workspaceRoot;
+    private final String packagePath;
+    private final File execRoot;
+
+    private final Map<String, ImmutableMap<String, IdeBuildInfo>> buildInfoCache = new HashMap<>();
+
+    private BazelInstance(File workspaceRoot) throws IOException, InterruptedException {
+      this.workspaceRoot = workspaceRoot;
+      this.packagePath =
+          String.join("", runBazel("info", "package_path")) + ":" + ASPECT_WORKSPACE.toString();
+      this.execRoot = new File(String.join("", runBazel("info", "execution_root")));
+    }
+
+    /**
+     * Returns the list of targets present in the BUILD files for the given sub-directories.
+     */
+    public synchronized List<String> listTargets(File... directories)
+        throws IOException, InterruptedException {
+      StringBuilder builder = new StringBuilder();
+      for (File f : directories) {
+        builder.append(f.toURI().relativize(workspaceRoot.toURI()).getPath()).append("/... ");
+      }
+      return runBazel("query", builder.toString());
+    }
+
+    private synchronized List<String> runBazel(String... args)
+        throws IOException, InterruptedException {
+      return runBazel(ImmutableList.<String>builder().add(args).build());
+    }
+
+    private synchronized List<String> runBazel(List<String> args)
+        throws IOException, InterruptedException {
+      return BazelCommand.this.runBazelAndGetOuputLines(ConsoleType.WORKSPACE, workspaceRoot, args);
+    }
+
+    /**
+     * Returns the IDE build information from running the aspect over the given list of targets. The
+     * result is a list of of path to the output artifact created by the build.
+     */
+    private synchronized List<String> buildIdeInfo(Collection<String> targets)
+        throws IOException, InterruptedException {
+      return BazelCommand.this.runBazelAndGetErrorLines(ConsoleType.WORKSPACE, workspaceRoot,
+          ImmutableList.<String>builder().add("build").add("--package_path", packagePath)
+              .addAll(ASPECT_OPTIONS).addAll(targets).build(),
+          // Strip out the artifact list, keeping the e4b-build.json files.
+          t -> t.startsWith(">>>") ? (t.endsWith(".e4b-build.json") ? t.substring(3) : "") : null);
+    }
+
+    /**
+     * Runs the analysis of the given list of targets using the IDE build information aspect and
+     * returns a map of {@link IdeBuildInfo}-s (key is the label of the target) containing the
+     * parsed form of the JSON file created by the aspect.
+     *
+     * <p>
+     * This method cache it results and won't recompute a previously computed version unless
+     * {@link #markAsDirty()} has been called in between.
+     */
+    public synchronized ImmutableMap<String, IdeBuildInfo> getIdeInfo(Collection<String> targets)
+        throws IOException, InterruptedException {
+      String key = NEW_LINE_JOINER.join(targets);
+      if (!buildInfoCache.containsKey(key)) {
+        buildInfoCache.put(key, IdeBuildInfo.getInfo(buildIdeInfo(targets)));
+      }
+      return buildInfoCache.get(key);
+    }
+
+    /**
+     * Clear the IDE build information cache. This cache is filled upon request and never emptied
+     * unless we call that function.
+     *
+     * <p>
+     * This function totally clear the cache and that might leads to useless rebuilds when several
+     * eclipse project points to the same workspace but that is a rare case.
+     */
+    public synchronized void markAsDirty() {
+      buildInfoCache.clear();
+    }
+
+    /**
+     * Build a list of targets in the current workspace.
+     */
+    public synchronized int build(List<String> targets, String... extraArgs)
+        throws IOException, InterruptedException {
+      return BazelCommand.this.runBazel(workspaceRoot,
+          ImmutableList.<String>builder().add("build", "--package_path", packagePath)
+              .addAll(BUILD_OPTIONS).add(extraArgs).addAll(targets).build());
+    }
+
+    /**
+     * Run test on a list of targets in the current workspace.
+     */
+    public synchronized int tests(List<String> targets, String... extraArgs)
+        throws IOException, InterruptedException {
+      return BazelCommand.this.runBazel(workspaceRoot,
+          ImmutableList.<String>builder().add("test").add("--package_path", packagePath)
+              .addAll(BUILD_OPTIONS).add(extraArgs).addAll(targets).build());
+    }
+
+    /**
+     * Returns the workspace root corresponding to this object.
+     */
+    public File getWorkspaceRoot() {
+      return workspaceRoot;
+    }
+
+    /**
+     * Returns the execution root of the current workspace.
+     */
+    public File getExecRoot() {
+      return execRoot;
+    }
+
+    /**
+     * Gives a list of target completions for the given beginning string. The result is the list of
+     * possible completion for a target pattern starting with string.
+     */
+    public ImmutableList<String> complete(String string) throws IOException, InterruptedException {
+      if (string.equals("/") || string.isEmpty()) {
+        return ImmutableList.of("//");
+      } else if (string.contains(":")) {
+        // complete targets using `bazel query`
+        int idx = string.indexOf(':');
+        final String packageName = string.substring(0, idx);
+        final String targetPrefix = string.substring(idx + 1);
+        ImmutableList.Builder<String> builder = ImmutableList.builder();
+        builder.addAll(
+            BazelCommand.this.runBazelAndGetOuputLines(ConsoleType.NO_CONSOLE, workspaceRoot,
+                ImmutableList.<String>builder().add("query", packageName + ":*").build(), line -> {
+                  int i = line.indexOf(':');
+                  String s = line.substring(i + 1);
+                  return !s.isEmpty() && s.startsWith(targetPrefix)
+                      ? (packageName + ":" + s)
+                      : null;
+                }));
+        if ("all".startsWith(targetPrefix)) {
+          builder.add(packageName + ":all");
+        }
+        if ("*".startsWith(targetPrefix)) {
+          builder.add(packageName + ":*");
+        }
+        return builder.build();
+      } else {
+        // complete packages
+        int lastSlash = string.lastIndexOf('/');
+        final String prefix = lastSlash > 0 ? string.substring(0, lastSlash + 1) : "";
+        final String suffix = lastSlash > 0 ? string.substring(lastSlash + 1) : string;
+        final String directory = (prefix.isEmpty() || prefix.equals("//"))
+            ? ""
+            : prefix.substring(string.startsWith("//") ? 2 : 0, prefix.length() - 1);
+        File file = directory.isEmpty() ? workspaceRoot : new File(workspaceRoot, directory);
+        ImmutableList.Builder<String> builder = ImmutableList.builder();
+        File[] files = file.listFiles((f) -> {
+          // Only give directories whose name starts with suffix...
+          return f.getName().startsWith(suffix) && f.isDirectory()
+          // ...that does not start with '.'...
+              && !f.getName().startsWith(".")
+              // ...and is not a Bazel convenience link
+              && (!file.equals(workspaceRoot) || !f.getName().startsWith("bazel-"));
+        });
+        if (files != null) {
+          for (File d : files) {
+            builder.add(prefix + d.getName() + "/");
+            if (new File(d, "BUILD").exists()) {
+              builder.add(prefix + d.getName() + ":");
+            }
+          }
+        }
+        if ("...".startsWith(suffix)) {
+          builder.add(prefix + "...");
+        }
+        return builder.build();
+      }
+    }
+  }
+
+  private File getWorkspaceRoot(File directory) throws IOException, InterruptedException {
+    List<String> result = runBazelAndGetOuputLines(ConsoleType.SYSTEM, directory,
+        ImmutableList.of("info", "workspace"));
+    if (result.size() > 0) {
+      return new File(result.get(0));
+    }
+    return null;
+  }
+
+  private ImmutableList<String> runBazelAndGetOuputLines(ConsoleType type, File directory,
+      List<String> args) throws IOException, InterruptedException {
+    return runBazelAndGetOuputLines(type, directory, args, (t) -> t);
+  }
+
+  private synchronized ImmutableList<String> runBazelAndGetOuputLines(ConsoleType type,
+      File directory, List<String> args, Function<String, String> selector)
+          throws IOException, InterruptedException {
+    Command command = Command.builder().setConsoleName(getConsoleName(type, directory))
+        .setDirectory(directory).addArguments(bazel.toString(), "--watchfs").addArguments(args)
+        .setStdoutLineSelector(selector).build();
+    if (command.run() == 0) {
+      return command.getSelectedOutputLines();
+    }
+    return ImmutableList.of();
+  }
+
+  private synchronized ImmutableList<String> runBazelAndGetErrorLines(ConsoleType type,
+      File directory, List<String> args, Function<String, String> selector)
+          throws IOException, InterruptedException {
+    Command command = Command.builder().setConsoleName(getConsoleName(type, directory))
+        .setDirectory(directory).addArguments(bazel.toString(), "--watchfs").addArguments(args)
+        .setStderrLineSelector(selector).build();
+    if (command.run() == 0) {
+      return command.getSelectedErrorLines();
+    }
+    return ImmutableList.of();
+  }
+
+  private synchronized int runBazel(ConsoleType type, File directory, List<String> args,
+      OutputStream stdout, OutputStream stderr) throws IOException, InterruptedException {
+    return Command.builder().setConsoleName(getConsoleName(type, directory)).setDirectory(directory)
+        .addArguments(bazel.toString(), "--watchfs").addArguments(args).setStandardOutput(stdout)
+        .setStandardError(stderr).build().run();
+  }
+
+  private int runBazel(File directory, List<String> args) throws IOException, InterruptedException {
+    return runBazel(ConsoleType.WORKSPACE, directory, args, null, null);
+  }
+
+  private String getConsoleName(ConsoleType type, File directory) {
+    switch (type) {
+      case SYSTEM:
+        return "Bazel [system]";
+      case WORKSPACE:
+        return "Bazel [" + directory.toString() + "]";
+      case NO_CONSOLE:
+      default:
+        return null;
+    }
+  }
+
+}
diff --git a/com.google.devtools.bazel.e4b/src/com/google/devtools/bazel/e4b/command/Command.java b/com.google.devtools.bazel.e4b/src/com/google/devtools/bazel/e4b/command/Command.java
new file mode 100644
index 0000000..cd4cccf
--- /dev/null
+++ b/com.google.devtools.bazel.e4b/src/com/google/devtools/bazel/e4b/command/Command.java
@@ -0,0 +1,292 @@
+// Copyright 2016 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.devtools.bazel.e4b.command;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.function.Function;
+
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.console.ConsolePlugin;
+import org.eclipse.ui.console.IConsole;
+import org.eclipse.ui.console.IConsoleManager;
+import org.eclipse.ui.console.MessageConsole;
+import org.eclipse.ui.console.MessageConsoleStream;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+
+/**
+ * A utility class to spawn a command and parse its output. It allow to filter the output,
+ * redirecting part of it to the console and getting the rest in a list of string.
+ *
+ * <p>
+ * This class can only be initialized using a builder created with the {@link #builder()} method.
+ */
+final class Command {
+
+  private final File directory;
+  private final ImmutableList<String> args;
+  private final SelectOutputStream stdout;
+  private final SelectOutputStream stderr;
+  private boolean executed = false;
+
+  private Command(String consoleName, File directory, ImmutableList<String> args,
+      Function<String, String> stdoutSelector, Function<String, String> stderrSelector,
+      OutputStream stdout, OutputStream stderr) throws IOException {
+    this.directory = directory;
+    this.args = args;
+    if (consoleName != null) {
+      MessageConsole console = findConsole(consoleName);
+      MessageConsoleStream stream = console.newMessageStream();
+      stream.setActivateOnWrite(true);
+      stream.write("*** Running " + String.join("", args.toString()) + " from "
+          + directory.toString() + " ***\n");
+      if (stdout == null) {
+        stdout = console.newMessageStream();
+      }
+      if (stderr == null) {
+        stderr = getErrorStream(console);
+      }
+    }
+    this.stderr = new SelectOutputStream(stderr, stderrSelector);
+    this.stdout = new SelectOutputStream(stdout, stdoutSelector);
+  }
+
+  /**
+   * Executes the command represented by this instance, and return the exit code of the command.
+   * This method should not be called twice on the same object.
+   */
+  public int run() throws IOException, InterruptedException {
+    Preconditions.checkState(!executed);
+    executed = true;
+    ProcessBuilder builder = new ProcessBuilder(args);
+    builder.directory(directory);
+    Process process = builder.start();
+    copyStream(process.getErrorStream(), stderr);
+    // seriously? That's stdout, why is it called getInputStream???
+    copyStream(process.getInputStream(), stdout);
+    int r = process.waitFor();
+    synchronized (stderr) {
+      stderr.close();
+    }
+    synchronized (stdout) {
+      stdout.close();
+    }
+    return r;
+  }
+
+  // Taken from the eclipse website, find a console
+  private static MessageConsole findConsole(String name) {
+    ConsolePlugin plugin = ConsolePlugin.getDefault();
+    IConsoleManager conMan = plugin.getConsoleManager();
+    IConsole[] existing = conMan.getConsoles();
+    for (int i = 0; i < existing.length; i++) {
+      if (name.equals(existing[i].getName())) {
+        return (MessageConsole) existing[i];
+      }
+    }
+    // no console found, so create a new one
+    MessageConsole myConsole = new MessageConsole(name, null);
+    conMan.addConsoles(new IConsole[] {myConsole});
+    return myConsole;
+  }
+
+  // Get the error stream for the given console (a stream that print in red).
+  private static MessageConsoleStream getErrorStream(MessageConsole console) {
+    final MessageConsoleStream errorStream = console.newMessageStream();
+    Display display = Display.getCurrent();
+    if (display == null) {
+      display = Display.getDefault();
+    }
+    display.asyncExec(() -> errorStream.setColor(new Color(null, 255, 0, 0)));
+    return errorStream;
+  }
+
+  // Launch a thread to copy all data from inputStream to outputStream
+  private static void copyStream(InputStream inputStream, OutputStream outputStream) {
+    if (outputStream != null) new Thread(new Runnable() {
+      @Override
+      public void run() {
+        byte[] buffer = new byte[4096];
+        int read;
+        try {
+          while ((read = inputStream.read(buffer)) > 0) {
+            synchronized (outputStream) {
+              outputStream.write(buffer, 0, read);
+            }
+          }
+        } catch (IOException ex) {
+          // we simply terminate the thread on exceptions
+        }
+      }
+    }).start();
+  }
+
+  /**
+   * Returns the list of lines selected from the standard error stream. Lines printed to the
+   * standard error stream by the executed command can be filtered to be added to that list.
+   *
+   * @see {@link Builder#setStderrLineSelector(Function)}
+   */
+  ImmutableList<String> getSelectedErrorLines() {
+    return stderr.getLines();
+  }
+
+  /**
+   * Returns the list of lines selected from the standard output stream. Lines printed to the
+   * standard output stream by the executed command can be filtered to be added to that list.
+   *
+   * @see {@link Builder#setStdoutLineSelector(Function)}
+   */
+  ImmutableList<String> getSelectedOutputLines() {
+    return stdout.getLines();
+  }
+
+  /**
+   * A builder class to generate a Command object.
+   */
+  static class Builder {
+
+    private String consoleName = null;
+    private File directory;
+    private ImmutableList.Builder<String> args = ImmutableList.builder();
+    private OutputStream stdout = null;
+    private OutputStream stderr = null;
+    private Function<String, String> stdoutSelector;
+    private Function<String, String> stderrSelector;
+
+    private Builder() {
+      // Default to the current working directory
+      this.directory = new File(System.getProperty("user.dir"));
+    }
+
+    /**
+     * Set the console name.
+     *
+     * <p>
+     * The console name is used to print result of the program. Only lines not filtered by
+     * {@link #setStderrLineSelector(Function)} and {@link #setStdoutLineSelector(Function)} are
+     * printed to the console. If {@link #setStandardError(OutputStream)} or
+     * {@link #setStandardOutput(OutputStream)} have been used with a non null value, then they
+     * intercept all output from being printed to the console.
+     *
+     * <p>
+     * If name is null, no output is written to any console.
+     */
+    public Builder setConsoleName(String name) {
+      this.consoleName = name;
+      return this;
+    }
+
+    /**
+     * Set the working directory for the program, it is set to the current working directory of the
+     * current java process by default.
+     */
+    public Builder setDirectory(File directory) {
+      this.directory = directory;
+      return this;
+    }
+
+    /**
+     * Set an {@link OutputStream} to receive non selected lines from the standard output stream of
+     * the program in lieu of the console. If a selector has been set with
+     * {@link #setStdoutLineSelector(Function)}, only the lines not selected (for which the selector
+     * returns null) will be printed to the {@link OutputStream}.
+     */
+    public Builder setStandardOutput(OutputStream stdout) {
+      this.stdout = stdout;
+      return this;
+    }
+
+    /**
+     * Set an {@link OutputStream} to receive non selected lines from the standard error stream of
+     * the program in lieu of the console. If a selector has been set with
+     * {@link #setStderrLineSelector(Function)}, only the lines not selected (for which the selector
+     * returns null) will be printed to the {@link OutputStream}.
+     */
+    public Builder setStandardError(OutputStream stderr) {
+      this.stderr = stderr;
+      return this;
+    }
+
+    /**
+     * Add arguments to the command line. The first argument to be added to the builder is the
+     * program name.
+     */
+    public Builder addArguments(String... args) {
+      this.args.add(args);
+      return this;
+    }
+
+    /**
+     * Add a list of arguments to the command line. The first argument to be added to the builder is
+     * the program name.
+     */
+    public Builder addArguments(Iterable<String> args) {
+      this.args.addAll(args);
+      return this;
+    }
+
+    /**
+     * Set a selector to accumulate lines that are selected from the standard output stream.
+     *
+     * <p>
+     * The selector is passed all lines that are printed to the standard output. It can either
+     * returns null to say that the line should be passed to the console or to a non null value that
+     * will be stored. All values that have been selected (for which the selector returns a non-null
+     * value) will be stored in a list accessible through {@link Command#getSelectedOutputLines()}.
+     * The selected lines will not be printed to the console.
+     */
+    public Builder setStdoutLineSelector(Function<String, String> selector) {
+      this.stdoutSelector = selector;
+      return this;
+    }
+
+    /**
+     * Set a selector to accumulate lines that are selected from the standard error stream.
+     *
+     * <p>
+     * The selector is passed all lines that are printed to the standard error. It can either
+     * returns null to say that the line should be passed to the console or to a non null value that
+     * will be stored. All values that have been selected (for which the selector returns a non-null
+     * value) will be stored in a list accessible through {@link Command#getSelectedErrorLines()}.
+     * The selected lines will not be printed to the console.
+     */
+    public Builder setStderrLineSelector(Function<String, String> selector) {
+      this.stderrSelector = selector;
+      return this;
+    }
+
+    /**
+     * Build a Command object.
+     */
+    public Command build() throws IOException {
+      Preconditions.checkNotNull(directory);
+      return new Command(consoleName, directory, args.build(), stdoutSelector, stderrSelector,
+          stdout, stderr);
+    }
+  }
+
+  /**
+   * Returns a {@link Builder} object to use to create a {@link Command} object.
+   */
+  static Builder builder() {
+    return new Builder();
+  }
+}
diff --git a/com.google.devtools.bazel.e4b/src/com/google/devtools/bazel/e4b/command/IdeBuildInfo.java b/com.google.devtools.bazel.e4b/src/com/google/devtools/bazel/e4b/command/IdeBuildInfo.java
new file mode 100644
index 0000000..c361b42
--- /dev/null
+++ b/com.google.devtools.bazel.e4b/src/com/google/devtools/bazel/e4b/command/IdeBuildInfo.java
@@ -0,0 +1,172 @@
+// 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.devtools.bazel.e4b.command;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.List;
+import java.util.Objects;
+
+import org.json.JSONArray;
+import org.json.JSONObject;
+import org.json.JSONTokener;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+/**
+ * A parsed version of the JSON files returned by the application of the IDE build information
+ * aspect.
+ */
+public final class IdeBuildInfo {
+
+  /**
+   * A structure containing the list of jar files generated by a target (interface, class and source
+   * jars).
+   */
+  public static final class Jars {
+    private final String ijar; // interface_jar
+    private final String jar; // jar
+    private final String srcjar; // source_jar
+
+    Jars(JSONObject obj) {
+      this.ijar = obj.has("interface_jar") ? obj.getString("interface_jar") : null;
+      this.jar = obj.getString("jar");
+      this.srcjar = obj.has("srcjar") ? obj.getString("srcjar") : null;
+    }
+
+    @Override
+    public int hashCode() {
+      return Objects.hash(ijar, jar, srcjar);
+    }
+
+    public String getInterfaceJar() {
+      return ijar;
+    }
+
+    public String getJar() {
+      return jar;
+    }
+
+    public String getSrcJar() {
+      return srcjar;
+    }
+  }
+
+  private final String location; // build_file_artifact_location
+  private final ImmutableList<String> deps; // dependencies
+  private final String kind; // kind
+  private final String label; // label
+
+  private final ImmutableList<Jars> generatedJars; // generated_jars
+  private final ImmutableList<Jars> jars; // jars
+  private final ImmutableList<String> sources; // sources
+
+  /**
+   * Construct an {@link IdeBuildInfo} object from a {@link JSONObject}.
+   */
+  IdeBuildInfo(JSONObject object) {
+    jars = jsonToJarArray(object.getJSONArray("jars"));
+    generatedJars = jsonToJarArray(object.getJSONArray("generated_jars"));
+    location = object.getString("build_file_artifact_location");
+    kind = object.getString("kind");
+    label = object.getString("label");
+    this.deps = jsonToStringArray(object.getJSONArray("dependencies"));
+    this.sources = jsonToStringArray(object.getJSONArray("sources"));
+  }
+
+  /**
+   * Constructs a map of label -> {@link IdeBuildInfo} from a list of files, parsing each files into
+   * a {@link JSONObject} and then converting that {@link JSONObject} to an {@link IdeBuildInfo}
+   * object.
+   */
+  static ImmutableMap<String, IdeBuildInfo> getInfo(List<String> files)
+      throws IOException, InterruptedException {
+    ImmutableMap.Builder<String, IdeBuildInfo> infos = ImmutableMap.builder();
+    for (String s : files) {
+      if (!s.isEmpty()) {
+        IdeBuildInfo buildInfo =
+            new IdeBuildInfo(new JSONObject(new JSONTokener(new FileInputStream(s))));
+        infos.put(buildInfo.label, buildInfo);
+      }
+    }
+    return infos.build();
+  }
+
+  private ImmutableList<Jars> jsonToJarArray(JSONArray array) {
+    ImmutableList.Builder<Jars> builder = ImmutableList.builder();
+    for (Object o : array) {
+      builder.add(new Jars((JSONObject) o));
+    }
+    return builder.build();
+  }
+
+  private ImmutableList<String> jsonToStringArray(JSONArray array) {
+    ImmutableList.Builder<String> builder = ImmutableList.builder();
+    for (Object o : array) {
+      builder.add(o.toString());
+    }
+    return builder.build();
+  }
+
+  /**
+   * Location of the target (build file).
+   */
+  public String getLocation() {
+    return location;
+  }
+
+  /**
+   * List of dependencies of the target.
+   */
+  public ImmutableList<String> getDeps() {
+    return deps;
+  }
+
+  /**
+   * Kind of the target (e.g., java_test or java_binary).
+   */
+  public String getKind() {
+    return kind;
+  }
+
+  /**
+   * Label of the target.
+   */
+  public String getLabel() {
+    return label;
+  }
+
+  /**
+   * List of jars generated by annotations processors when building this target.
+   */
+  public ImmutableList<Jars> getGeneratedJars() {
+    return generatedJars;
+  }
+
+  /**
+   * List of jars generated by building this target.
+   */
+  public ImmutableList<Jars> getJars() {
+    return jars;
+  }
+
+  /**
+   * List of sources consumed by this target.
+   */
+  public ImmutableList<String> getSources() {
+    return sources;
+  }
+}
diff --git a/com.google.devtools.bazel.e4b/src/com/google/devtools/bazel/e4b/command/SelectOutputStream.java b/com.google.devtools.bazel.e4b/src/com/google/devtools/bazel/e4b/command/SelectOutputStream.java
new file mode 100644
index 0000000..8514d5e
--- /dev/null
+++ b/com.google.devtools.bazel.e4b/src/com/google/devtools/bazel/e4b/command/SelectOutputStream.java
@@ -0,0 +1,100 @@
+// Copyright 2016 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.devtools.bazel.e4b.command;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.StandardCharsets;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.function.Function;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+
+/**
+ * A wrapper output stream to output part of the result to a given output and extracting the other
+ * part with a selector function. The other part is return as a list of string.
+ */
+public class SelectOutputStream extends OutputStream {
+
+  private OutputStream output;
+  private Function<String, String> selector;
+  private boolean closed = false;
+  private List<String> lines = new LinkedList<>();
+  private ByteArrayOutputStream stream = new ByteArrayOutputStream();
+
+  /**
+   * Create a SelectOutputStream. <code>output<code> is the output stream where non-selected lines
+   * will be printed. <code>selector<code> is a function that will be called on each line. If
+   * <code>selector</code> returns a non null value, then the resulting value will be stored in a
+   * lines buffer that can be consumed with the {@link #getLines()} method. If <code>selector</code>
+   * returns a null value, the corresponding line will be send to <code>output</code>.
+   *
+   * <p>
+   * Both <code>output</code> and <code>selector</code> can be null. If <code>output</code> is null,
+   * unselected lines will be discarded. If <code>selector</code> is null, all lines will be
+   * considered as unselected.
+   */
+  public SelectOutputStream(OutputStream output, Function<String, String> selector) {
+    super();
+    this.output = output;
+    this.selector = selector;
+  }
+
+  @Override
+  public void write(int b) throws IOException {
+    byte b0 = (byte) b;
+    if (b0 == '\n') {
+      select(true);
+    } else {
+      stream.write(b);
+    }
+  }
+
+  private void select(boolean appendNewLine) throws UnsupportedEncodingException, IOException {
+    String line = null;
+    if (selector != null) {
+      line = selector.apply(stream.toString(StandardCharsets.UTF_8.name()));
+    }
+
+    if (line != null) {
+      lines.add(line);
+    } else if (output != null) {
+      if (appendNewLine) {
+        stream.write('\n');
+      }
+      output.write(stream.toByteArray());
+    }
+    stream.reset();
+  }
+
+  @Override
+  public void close() throws IOException {
+    Preconditions.checkState(!closed);
+    super.close();
+    select(false);
+    closed = true;
+  }
+
+  /**
+   * Returns the list of selected lines.
+   */
+  public ImmutableList<String> getLines() {
+    return ImmutableList.copyOf(lines);
+  }
+}
diff --git a/com.google.devtools.bazel.e4b/src/com/google/devtools/bazel/e4b/preferences/BazelPreferenceInitializer.java b/com.google.devtools.bazel.e4b/src/com/google/devtools/bazel/e4b/preferences/BazelPreferenceInitializer.java
new file mode 100644
index 0000000..519538b
--- /dev/null
+++ b/com.google.devtools.bazel.e4b/src/com/google/devtools/bazel/e4b/preferences/BazelPreferenceInitializer.java
@@ -0,0 +1,34 @@
+// 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.devtools.bazel.e4b.preferences;
+
+import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer;
+import org.eclipse.jface.preference.IPreferenceStore;
+
+import com.google.devtools.bazel.e4b.Activator;
+
+/**
+ * Initialize the preferences of Bazel. The only preferences stored for now is the path to the Bazel
+ * binary, which is expected to be in /usr/local/bin/bazel by default.
+ */
+public class BazelPreferenceInitializer extends AbstractPreferenceInitializer {
+
+  @Override
+  public void initializeDefaultPreferences() {
+    IPreferenceStore store = Activator.getDefault().getPreferenceStore();
+    store.setDefault("BAZEL_PATH", Activator.DEFAULT_BAZEL_PATH);
+  }
+
+}
diff --git a/com.google.devtools.bazel.e4b/src/com/google/devtools/bazel/e4b/preferences/BazelPreferencePage.java b/com.google.devtools.bazel.e4b/src/com/google/devtools/bazel/e4b/preferences/BazelPreferencePage.java
new file mode 100644
index 0000000..1619e80
--- /dev/null
+++ b/com.google.devtools.bazel.e4b/src/com/google/devtools/bazel/e4b/preferences/BazelPreferencePage.java
@@ -0,0 +1,47 @@
+// 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.devtools.bazel.e4b.preferences;
+
+import org.eclipse.jface.preference.FieldEditorPreferencePage;
+import org.eclipse.jface.preference.FileFieldEditor;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPreferencePage;
+
+import com.google.devtools.bazel.e4b.Activator;
+
+/**
+ * Page to configure the e4b plugin. The only configuration parameter is the path to the Bazel
+ * binary so this page provide a file field to specify it.
+ */
+public class BazelPreferencePage extends FieldEditorPreferencePage
+    implements
+      IWorkbenchPreferencePage {
+
+  public BazelPreferencePage() {
+    super(GRID);
+  }
+
+  public void createFieldEditors() {
+    addField(new FileFieldEditor("BAZEL_PATH", "Path to the &Bazel binary:", true,
+        getFieldEditorParent()));
+  }
+
+  @Override
+  public void init(IWorkbench workbench) {
+    setPreferenceStore(Activator.getDefault().getPreferenceStore());
+    setDescription("Bazel plugin settings");
+  }
+
+}
diff --git a/com.google.devtools.bazel.e4b/src/com/google/devtools/bazel/e4b/wizard/BazelProjectSupport.java b/com.google.devtools.bazel.e4b/src/com/google/devtools/bazel/e4b/wizard/BazelProjectSupport.java
new file mode 100644
index 0000000..5bcd9b9
--- /dev/null
+++ b/com.google.devtools.bazel.e4b/src/com/google/devtools/bazel/e4b/wizard/BazelProjectSupport.java
@@ -0,0 +1,153 @@
+// 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.devtools.bazel.e4b.wizard;
+
+import java.net.URI;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.eclipse.core.resources.ICommand;
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IProjectDescription;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.ProjectScope;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.preferences.IScopeContext;
+import org.eclipse.jdt.core.IClasspathEntry;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.JavaCore;
+import org.osgi.service.prefs.BackingStoreException;
+import org.osgi.service.prefs.Preferences;
+
+import com.google.devtools.bazel.e4b.Activator;
+import com.google.devtools.bazel.e4b.ProjectNature;
+import com.google.devtools.bazel.e4b.classpath.BazelClasspathContainer;
+
+/**
+ * A utility class to create e4b projects.
+ */
+public class BazelProjectSupport {
+
+  /**
+   * Create a e4b project. This method adds the natures to the project, saves the list of targets
+   * and the workspace root to the project settings, make Bazel the default builder instead of ECJ
+   * and create the classpath using ide build informations from Bazel.
+   */
+  public static IProject createProject(String projectName, URI location, String workspaceRoot,
+      List<String> paths, List<String> targets) {
+
+    IProject project = createBaseProject(projectName, location);
+    try {
+      addNature(project, ProjectNature.NATURE_ID);
+      addNature(project, JavaCore.NATURE_ID);
+      addSettings(project, workspaceRoot, targets);
+      setBuilders(project);
+      createClasspath(new Path(workspaceRoot), paths, JavaCore.create(project));
+    } catch (CoreException e) {
+      e.printStackTrace();
+      project = null;
+    } catch (BackingStoreException e) {
+      e.printStackTrace();
+      project = null;
+    }
+
+    return project;
+  }
+
+  private static void addSettings(IProject project, String workspaceRoot, List<String> targets)
+      throws BackingStoreException {
+    IScopeContext projectScope = new ProjectScope(project);
+    Preferences projectNode = projectScope.getNode(Activator.PLUGIN_ID);
+    int i = 0;
+    for (String target : targets) {
+      projectNode.put("target" + i, target);
+      i++;
+    }
+    projectNode.put("workspaceRoot", workspaceRoot);
+    projectNode.flush();
+  }
+
+  private static void setBuilders(IProject project) throws CoreException {
+    IProjectDescription description = project.getDescription();
+    final ICommand buildCommand = description.newCommand();
+    buildCommand.setBuilderName("com.google.devtools.bazel.e4b.builder");
+    description.setBuildSpec(new ICommand[] {buildCommand});
+    project.setDescription(description, null);
+  }
+
+  private static void createClasspath(IPath root, List<String> paths, IJavaProject javaProject)
+      throws CoreException {
+    String name = root.lastSegment();
+    IFolder base = javaProject.getProject().getFolder(name);
+    if (!base.isLinked()) {
+      base.createLink(root, IResource.NONE, null);
+    }
+    List<IClasspathEntry> list = new LinkedList<>();
+    for (String path : paths) {
+      IPath workspacePath = base.getFullPath().append(path);
+      list.add(JavaCore.newSourceEntry(workspacePath));
+    }
+    list.add(JavaCore.newContainerEntry(new Path(BazelClasspathContainer.CONTAINER_NAME)));
+    // TODO(dmarting): we should add otherwise. Best way is to get the bootclasspath from Bazel.
+    list.add(JavaCore.newContainerEntry(new Path("org.eclipse.jdt.launching.JRE_CONTAINER/"
+        + "org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8")));
+    IClasspathEntry[] newClasspath = (IClasspathEntry[]) list.toArray(new IClasspathEntry[0]);
+    javaProject.setRawClasspath(newClasspath, null);
+  }
+
+  private static IProject createBaseProject(String projectName, URI location) {
+    IProject newProject = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName);
+
+    if (!newProject.exists()) {
+      URI projectLocation = location;
+      IProjectDescription desc =
+          newProject.getWorkspace().newProjectDescription(newProject.getName());
+      if (location != null
+          && ResourcesPlugin.getWorkspace().getRoot().getLocationURI().equals(location)) {
+        projectLocation = null;
+      }
+
+      desc.setLocationURI(projectLocation);
+      try {
+        newProject.create(desc, null);
+        if (!newProject.isOpen()) {
+          newProject.open(null);
+        }
+      } catch (CoreException e) {
+        e.printStackTrace();
+      }
+    }
+
+    return newProject;
+  }
+
+  private static void addNature(IProject project, String nature) throws CoreException {
+    if (!project.hasNature(nature)) {
+      IProjectDescription description = project.getDescription();
+      String[] prevNatures = description.getNatureIds();
+      String[] newNatures = new String[prevNatures.length + 1];
+      System.arraycopy(prevNatures, 0, newNatures, 0, prevNatures.length);
+      newNatures[prevNatures.length] = nature;
+      description.setNatureIds(newNatures);
+
+      project.setDescription(description, null);
+    }
+  }
+
+}
diff --git a/com.google.devtools.bazel.e4b/src/com/google/devtools/bazel/e4b/wizard/BazelTargetCompletionContentProposalProvider.java b/com.google.devtools.bazel.e4b/src/com/google/devtools/bazel/e4b/wizard/BazelTargetCompletionContentProposalProvider.java
new file mode 100644
index 0000000..ef69e60
--- /dev/null
+++ b/com.google.devtools.bazel.e4b/src/com/google/devtools/bazel/e4b/wizard/BazelTargetCompletionContentProposalProvider.java
@@ -0,0 +1,67 @@
+// 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.devtools.bazel.e4b.wizard;
+
+import java.io.IOException;
+
+import org.eclipse.jface.fieldassist.ContentProposal;
+import org.eclipse.jface.fieldassist.IContentProposal;
+import org.eclipse.jface.fieldassist.IContentProposalProvider;
+
+import com.google.common.collect.ImmutableList;
+import com.google.devtools.bazel.e4b.Activator;
+import com.google.devtools.bazel.e4b.command.BazelCommand.BazelInstance;
+
+/**
+ * A {@link IContentProposalProvider} to provide completion for Bazel. Use the
+ * {@link #setBazelInstance(BazelInstance)} method to provide with the {@link BazelInstance}
+ * interface to Bazel.
+ */
+public class BazelTargetCompletionContentProposalProvider implements IContentProposalProvider {
+
+  private BazelInstance bazel = null;
+
+  @Override
+  public IContentProposal[] getProposals(String contents, int position) {
+    if (bazel == null) {
+      return null;
+    }
+    try {
+      ImmutableList<String> completions = bazel.complete(contents.substring(0, position));
+      if (completions != null) {
+        IContentProposal[] result = new IContentProposal[completions.size()];
+        int i = 0;
+        for (String s : completions) {
+          result[i] = new ContentProposal(s);
+          i++;
+        }
+        return result;
+      }
+    } catch (IOException e) {
+      Activator.error("Failed to run Bazel to get completion information", e);
+    } catch (InterruptedException e) {
+      Activator.error("Bazel was interrupted", e);
+    }
+    return null;
+  }
+
+  /**
+   * Set the {@link BazelInstance} to use to query for completion targets.
+   */
+  public void setBazelInstance(BazelInstance bazel) {
+    this.bazel = bazel;
+  }
+
+}
diff --git a/com.google.devtools.bazel.e4b/src/com/google/devtools/bazel/e4b/wizard/BazelWizard.java b/com.google.devtools.bazel.e4b/src/com/google/devtools/bazel/e4b/wizard/BazelWizard.java
new file mode 100644
index 0000000..32c338b
--- /dev/null
+++ b/com.google.devtools.bazel.e4b/src/com/google/devtools/bazel/e4b/wizard/BazelWizard.java
@@ -0,0 +1,54 @@
+// Copyright 2016 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.devtools.bazel.e4b.wizard;
+
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.wizard.Wizard;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchWizard;
+import org.eclipse.ui.dialogs.WizardNewProjectCreationPage;
+
+/**
+ * A wizard to create a Bazel import project.
+ */
+public class BazelWizard extends Wizard implements IWorkbenchWizard {
+
+  protected WorkspaceWizardPage page2;
+  protected WizardNewProjectCreationPage page1;
+
+  @Override
+  public void addPages() {
+    page1 = new WizardNewProjectCreationPage("Creating a new Bazel import project");
+    page2 = new WorkspaceWizardPage();
+    addPage(page1);
+    addPage(page2);
+  }
+
+  @Override
+  public String getWindowTitle() {
+    return "Import Bazel Workspace...";
+  }
+
+  @Override
+  public boolean performFinish() {
+    BazelProjectSupport.createProject(page1.getProjectName(), page1.getLocationURI(),
+        page2.getWorkspaceRoot(), page2.getDirectories(), page2.getTargets());
+    return true;
+  }
+
+  @Override
+  public void init(IWorkbench workbench, IStructuredSelection selection) {}
+
+}
diff --git a/com.google.devtools.bazel.e4b/src/com/google/devtools/bazel/e4b/wizard/DirectoryTreeContentProvider.java b/com.google.devtools.bazel.e4b/src/com/google/devtools/bazel/e4b/wizard/DirectoryTreeContentProvider.java
new file mode 100644
index 0000000..126da48
--- /dev/null
+++ b/com.google.devtools.bazel.e4b/src/com/google/devtools/bazel/e4b/wizard/DirectoryTreeContentProvider.java
@@ -0,0 +1,217 @@
+// 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.devtools.bazel.e4b.wizard;
+
+import java.io.File;
+
+import org.eclipse.jface.viewers.CheckboxTreeViewer;
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.jface.viewers.ILabelProviderListener;
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Composite;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * A tree content provider that enable selecting a list of sub-directories of a directory root (the
+ * Bazel workspace root).
+ */
+public class DirectoryTreeContentProvider implements ITreeContentProvider {
+
+  private File root;
+
+  public DirectoryTreeContentProvider(File root) {
+    this.root = root;
+  }
+
+  @Override
+  public void dispose() {}
+
+  @Override
+  public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {}
+
+  @Override
+  public Object[] getElements(Object inputElement) {
+    if (root == null) {
+      return new Object[] {};
+    }
+    // We only have one root
+    return new Object[] {root};
+  }
+
+  @Override
+  public Object[] getChildren(Object parentElement) {
+    return ((File) parentElement).listFiles((f) -> (f.isDirectory() && !f.getName().startsWith(".")
+        && !f.getName().startsWith("bazel-")));
+  }
+
+  @Override
+  public Object getParent(Object element) {
+    File file = (File) element;
+    if (file.equals(root)) {
+      return null;
+    }
+    return file.getParentFile();
+  }
+
+  @Override
+  public boolean hasChildren(Object element) {
+    Object[] childrens = getChildren(element);
+    return (childrens != null) && (childrens.length > 0);
+  }
+
+  public File getRoot() {
+    return root;
+  }
+
+  public void setRoot(File root) {
+    this.root = root;
+  }
+
+
+  /**
+   * Create a tree view that use a FileTreeContentProvider for its content. The created tree view
+   * can be used to select branches of a directory tree.
+   * 
+   * @param container The parent composite for the created tree view.
+   * @return A checkbox tree view
+   */
+  static CheckboxTreeViewer createTreeView(Composite container) {
+    final CheckboxTreeViewer tv = new CheckboxTreeViewer(container, SWT.BORDER);
+    tv.setContentProvider(new DirectoryTreeContentProvider(null));
+    tv.setLabelProvider(new ILabelProvider() {
+
+      @Override
+      public void removeListener(ILabelProviderListener listener) {
+        // we do not have event notifying listeners, ignore.
+      }
+
+      @Override
+      public boolean isLabelProperty(Object element, String property) {
+        return false;
+      }
+
+
+      @Override
+      public void dispose() {}
+
+      @Override
+      public void addListener(ILabelProviderListener listener) {
+        // we do not have event notifying listeners, ignore.
+      }
+
+      @Override
+      public Image getImage(Object element) {
+        return null;
+      }
+
+      @Override
+      public String getText(Object element) {
+        return ((File) element).getName();
+      }
+    });
+    tv.setInput("root"); // pass a non-null that will be ignored
+
+    tv.addCheckStateListener(event -> setChecked(tv, event.getElement()));
+
+    return tv;
+  }
+
+  private static void setChecked(final CheckboxTreeViewer tv, Object element) {
+    // When user checks a checkbox in the tree, check all its children and mark parent as greyed
+    // When a user uncheck a checkbox, mark the subtree as unchecked and ungrayed and if unique
+    // sibling parent as grayed.
+    DirectoryTreeContentProvider provider = (DirectoryTreeContentProvider) tv.getContentProvider();
+
+    boolean isChecked = tv.getChecked(element);
+    if (tv.getGrayed(element)) {
+      isChecked = !isChecked;
+    }
+    tv.setChecked(element, isChecked);
+    tv.setGrayed(element, false);
+    if (isChecked) {
+      tv.setSubtreeChecked(element, true);
+    } else {
+      tv.setSubtreeChecked(element, false);
+    }
+    setGrayed(tv, provider.getParent(element));
+  }
+
+  private static void setGrayed(CheckboxTreeViewer tv, Object element) {
+    if (element == null) {
+      return;
+    }
+    DirectoryTreeContentProvider provider = (DirectoryTreeContentProvider) tv.getContentProvider();
+    boolean checked = tv.getChecked(element);
+    boolean grayed = false;
+    for (Object object : provider.getChildren(element)) {
+      grayed = grayed || tv.getGrayed(object) || tv.getChecked(object);
+      checked = checked && tv.getChecked(object) && !tv.getGrayed(element);
+    }
+    if (checked) {
+      tv.setChecked(element, true);
+      tv.setGrayed(element, false);
+    } else if (grayed) {
+      tv.setGrayChecked(element, true);
+    } else {
+      tv.setChecked(element, false);
+      tv.setGrayed(element, false);
+    }
+    setGrayed(tv, provider.getParent(element));
+  }
+
+  /**
+   * Set the root of the directory tree view and refresh the view if appropriate
+   */
+  static void setFileTreeRoot(CheckboxTreeViewer tv, File root) {
+    DirectoryTreeContentProvider provider = (DirectoryTreeContentProvider) tv.getContentProvider();
+    if ((root == null && provider.getRoot() != null) || !root.equals(provider.getRoot())) {
+      provider.setRoot(root);
+      tv.refresh();
+    }
+  }
+
+  /**
+   * Returns the list of path selected in <code>tv</code>. It returns the list of checked path
+   * without the children of the checked path. Each path is returned as a string giving the relative
+   * path from the root of the tree.
+   */
+  static ImmutableList<String> getSelectPathsRelativeToRoot(CheckboxTreeViewer tv) {
+    DirectoryTreeContentProvider provider = (DirectoryTreeContentProvider) tv.getContentProvider();
+    String root = provider.root.getAbsolutePath();
+    ImmutableList.Builder<String> builder = ImmutableList.builder();
+    for (Object element : tv.getCheckedElements()) {
+      if (!tv.getGrayed(element)) {
+        Object parent = provider.getParent(element);
+        if (parent == null || tv.getGrayed(parent)) {
+          // Only add this element if its parent is not selected (so it's the root).
+          String path = ((File) element).getAbsolutePath();
+          // Strip root from path
+          if (path.startsWith(root)) {
+            path = path.substring(root.length());
+            if (path.startsWith("/")) {
+              path = path.substring(1);
+            }
+            builder.add(path);
+          }
+        }
+      }
+    }
+    return builder.build();
+  }
+}
diff --git a/com.google.devtools.bazel.e4b/src/com/google/devtools/bazel/e4b/wizard/WorkspaceWizardPage.java b/com.google.devtools.bazel.e4b/src/com/google/devtools/bazel/e4b/wizard/WorkspaceWizardPage.java
new file mode 100644
index 0000000..350e596
--- /dev/null
+++ b/com.google.devtools.bazel.e4b/src/com/google/devtools/bazel/e4b/wizard/WorkspaceWizardPage.java
@@ -0,0 +1,246 @@
+// 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.devtools.bazel.e4b.wizard;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.function.Consumer;
+
+import org.eclipse.jface.bindings.keys.KeyStroke;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.fieldassist.ContentProposalAdapter;
+import org.eclipse.jface.fieldassist.TextContentAdapter;
+import org.eclipse.jface.viewers.CheckboxTreeViewer;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.KeyAdapter;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.DirectoryDialog;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.List;
+import org.eclipse.swt.widgets.Text;
+
+import com.google.common.collect.ImmutableList;
+import com.google.devtools.bazel.e4b.Activator;
+
+/**
+ * This is a quick wizard page that ask the user for the various targets and source path he wants to
+ * include.
+ */
+public class WorkspaceWizardPage extends WizardPage {
+
+  private Label workspaceRoot;
+  private List targets;
+  private Text target;
+  private Button addTargetButton;
+  private Button removeTargetButton;
+  private CheckboxTreeViewer directories;
+  private Composite container;
+  private Button workspaceRootButton;
+  private DirectoryDialog dialog;
+  private BazelTargetCompletionContentProposalProvider completionProvider;
+
+  protected WorkspaceWizardPage() {
+    super("Import Bazel project");
+    setTitle("Import Bazel project");
+  }
+
+  /**
+   * Returns the list of targets selected by the user.
+   */
+  public ImmutableList<String> getTargets() {
+    return ImmutableList.copyOf(targets.getItems());
+  }
+
+  /**
+   * Returns the list of directories selected by the user.
+   */
+  public ImmutableList<String> getDirectories() {
+    return DirectoryTreeContentProvider.getSelectPathsRelativeToRoot(directories);
+  }
+
+  /**
+   * Returns the workspace root selected by the user.
+   */
+  public String getWorkspaceRoot() {
+    return workspaceRoot.getText();
+  }
+
+
+  @Override
+  public void createControl(Composite parent) {
+    container = new Composite(parent, SWT.NONE);
+    GridLayout layout = new GridLayout();
+    layout.numColumns = 3;
+    container.setLayout(layout);
+
+    createWorkspaceSelectionControls();
+
+    addLabel("Directories: ", 1, 5);
+    directories = DirectoryTreeContentProvider.createTreeView(container);
+    setControlGridData(directories.getTree(), 2, 5, true);
+    directories.addCheckStateListener(e -> updateControls());
+
+    new Label(container, SWT.NONE).setText("Targets:");
+    createTargetTextField();
+    setControlGridData(target, 1, 1, false);
+    addTargetButton = createButton("+", e -> addTarget());
+
+    targets = new List(container, SWT.SINGLE | SWT.BORDER);
+    setControlGridData(targets, 2, 5, true);
+    removeTargetButton = createButton("-", e -> deleteTarget());
+    targets.addSelectionListener(createSelectionListener(
+        e -> removeTargetButton.setEnabled(targets.getSelectionCount() > 0)));
+
+    setControl(container);
+    updateControls();
+  }
+
+  // A simple helper to use lambdas to create a SelectionListener that does the same action on all
+  // cases.
+  private static SelectionListener createSelectionListener(Consumer<SelectionEvent> f) {
+    return new SelectionListener() {
+      @Override
+      public void widgetSelected(SelectionEvent e) {
+        f.accept(e);
+
+      }
+
+      @Override
+      public void widgetDefaultSelected(SelectionEvent e) {
+        f.accept(e);
+      }
+    };
+  }
+
+  private Button createButton(String text, Consumer<SelectionEvent> handler) {
+    Button result = new Button(container, SWT.DEFAULT);
+    result.setText(text);
+    result.addSelectionListener(createSelectionListener(handler));
+    result.setEnabled(false);
+    return result;
+  }
+
+  private void createWorkspaceSelectionControls() {
+    Label label = new Label(container, SWT.NONE);
+    label.setText("Workpsace root: ");
+
+    workspaceRoot = new Label(container, SWT.BORDER);
+    workspaceRoot.setText("");
+    workspaceRoot.setLayoutData(new GridData(GridData.FILL, GridData.CENTER, true, false));
+
+    dialog = new DirectoryDialog(getShell(), SWT.OPEN);
+    workspaceRootButton = new Button(container, SWT.DEFAULT);
+    workspaceRootButton.setText("...");
+    workspaceRootButton.addSelectionListener(new SelectionAdapter() {
+      @Override
+      public void widgetSelected(SelectionEvent e) {
+        String wr = dialog.open();
+        if (wr != null) {
+          workspaceRoot.setText(wr);
+          DirectoryTreeContentProvider.setFileTreeRoot(directories, new File(wr));
+          try {
+            completionProvider.setBazelInstance(
+                Activator.getDefault().getCommand().getInstance(new File(getWorkspaceRoot())));
+          } catch (IOException e1) {
+            MessageDialog.openError(getShell(), "Error",
+                getWorkspaceRoot() + " does not seems to be a Bazel workspace");
+          } catch (InterruptedException e1) {
+            Activator.error("Bazel was interrupted", e1);
+          }
+
+        }
+        updateControls();
+      }
+    });
+    workspaceRootButton.setLayoutData(new GridData(GridData.END, GridData.CENTER, false, false));
+  }
+
+  private void setControlGridData(Control control, int horizontalSpan, int verticalSpan,
+      boolean verticalGrow) {
+    GridData data = new GridData(GridData.FILL, verticalGrow ? GridData.FILL : GridData.CENTER,
+        true, verticalGrow);
+    data.horizontalSpan = horizontalSpan;
+    data.verticalSpan = verticalSpan;
+    control.setLayoutData(data);
+  }
+
+  private void addLabel(String labelText, int horizontalSpan, int verticalSpan) {
+    Label label = new Label(container, SWT.NONE);
+    label.setText(labelText);
+    GridData data = new GridData(GridData.BEGINNING, GridData.FILL, false, true);
+    data.horizontalSpan = horizontalSpan;
+    data.verticalSpan = verticalSpan;
+    label.setLayoutData(data);
+  }
+
+  private void updateControls() {
+    boolean enabled = !workspaceRoot.getText().isEmpty();
+    directories.getTree().setEnabled(enabled);
+    targets.setEnabled(enabled);
+    target.setEnabled(enabled);
+    addTargetButton.setEnabled(enabled && !target.getText().isEmpty());
+    removeTargetButton.setEnabled(enabled && targets.getSelectionCount() > 0);
+    setPageComplete(
+        enabled && (directories.getCheckedElements().length > 0) && (targets.getItemCount() > 0));
+  }
+
+  private void setAutoCompletion() {
+    try {
+      ContentProposalAdapter adapter = null;
+      completionProvider = new BazelTargetCompletionContentProposalProvider();
+      KeyStroke ks = KeyStroke.getInstance("Ctrl+Space");
+      adapter = new ContentProposalAdapter(target, new TextContentAdapter(), completionProvider, ks,
+          null);
+      adapter.setProposalAcceptanceStyle(ContentProposalAdapter.PROPOSAL_REPLACE);
+    } catch (Exception e) {
+      e.printStackTrace();
+    }
+  }
+
+
+  private void createTargetTextField() {
+    target = new Text(container, SWT.BORDER);
+    setAutoCompletion();
+    target.addKeyListener(new KeyAdapter() {
+      public void keyReleased(KeyEvent ke) {
+        if (ke.keyCode == '\r' && (ke.stateMask & SWT.SHIFT) != 0 && !target.getText().isEmpty()) {
+          addTarget();
+        }
+      }
+    });
+    target.addModifyListener(e -> updateControls());
+  }
+
+  private void addTarget() {
+    targets.add(target.getText());
+    target.setText("");
+    setAutoCompletion();
+    updateControls();
+  }
+
+  private void deleteTarget() {
+    targets.remove(targets.getSelectionIndices());
+    updateControls();
+  }
+}