├── .bazelci └── presubmit.yml ├── .classpath ├── .gitignore ├── .project ├── AUTHORS ├── BUILD ├── CONTRIBUTING.md ├── CONTRIBUTORS ├── LICENSE.txt ├── META-INF └── MANIFEST.MF ├── README.md ├── WORKSPACE ├── build.properties ├── java └── com │ └── google │ └── devtools │ └── bazel │ └── e4b │ ├── Activator.java │ ├── BazelAspectLocationImpl.java │ ├── BazelProjectSupport.java │ ├── CommandConsoleFactoryImpl.java │ ├── ProjectNature.java │ ├── builder │ └── BazelBuilder.java │ ├── classpath │ ├── BazelClasspathContainer.java │ └── BazelClasspathContainerInitilalizer.java │ ├── command │ ├── BUILD │ ├── BazelAspectLocation.java │ ├── BazelCommand.java │ ├── BazelNotFoundException.java │ ├── Command.java │ ├── CommandConsole.java │ ├── IdeBuildInfo.java │ └── SelectOutputStream.java │ ├── preferences │ ├── BazelPreferenceInitializer.java │ └── BazelPreferencePage.java │ ├── projectviews │ ├── BUILD │ ├── Builder.java │ ├── ProjectView.java │ ├── ProjectViewImpl.java │ └── ProjectViewParseException.java │ └── wizard │ ├── BazelTargetCompletionContentProposalProvider.java │ ├── BazelWizard.java │ ├── DirectoryTreeContentProvider.java │ └── WorkspaceWizardPage.java ├── javatests └── com │ └── google │ └── devtools │ └── bazel │ └── e4b │ ├── command │ ├── BUILD │ └── CommandTest.java │ ├── integration │ ├── AspectIntegrationTest.java │ └── BUILD │ └── projectviews │ ├── BUILD │ ├── ProjectViewTest.java │ └── bazel.projectview ├── plugin.xml ├── resources ├── BUILD ├── WORKSPACE ├── e4b_aspect.bzl └── icon.ico ├── runfiles ├── tools ├── build_defs │ ├── BUILD │ ├── eclipse.bzl │ ├── eclipse_platform.bzl │ ├── feature_builder.py │ └── site_builder.py └── release │ ├── BUILD │ ├── release.sh │ ├── release.yaml │ └── unzip-updatesite.sh └── version.bzl /.bazelci/presubmit.yml: -------------------------------------------------------------------------------- 1 | --- 2 | platforms: 3 | ubuntu1404: 4 | build_targets: 5 | - "..." 6 | test_targets: 7 | - "..." 8 | ubuntu1604: 9 | build_targets: 10 | - "..." 11 | test_targets: 12 | - "..." 13 | macos: 14 | build_targets: 15 | - "..." 16 | test_targets: 17 | - "..." 18 | -------------------------------------------------------------------------------- /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /bin 2 | .settings 3 | /bazel-* 4 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | com.google.devtools.bazel.e4b 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | org.eclipse.pde.ManifestBuilder 15 | 16 | 17 | 18 | 19 | org.eclipse.pde.SchemaBuilder 20 | 21 | 22 | 23 | 24 | org.eclipse.pde.api.tools.apiAnalysisBuilder 25 | 26 | 27 | 28 | 29 | 30 | org.eclipse.pde.PluginNature 31 | org.eclipse.jdt.core.javanature 32 | org.eclipse.pde.api.tools.apiAnalysisNature 33 | 34 | 35 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | # This the official list of e4b authors for copyright purposes. 2 | # This file is distinct from the CONTRIBUTORS files. 3 | # See the latter for an explanation. 4 | 5 | # Names should be added to this file as: 6 | # Name or Organization 7 | # The email address is not required for organizations. 8 | 9 | Google Inc. 10 | -------------------------------------------------------------------------------- /BUILD: -------------------------------------------------------------------------------- 1 | load( 2 | "//tools/build_defs:eclipse.bzl", 3 | "eclipse_plugin", 4 | "eclipse_feature", 5 | "eclipse_p2updatesite", 6 | ) 7 | load(":version.bzl", "VERSION") 8 | 9 | eclipse_plugin( 10 | name = "com.google.devtools.bazel.e4b", 11 | srcs = glob(["java/**/*.java"]), 12 | activator = "com.google.devtools.bazel.e4b.Activator", 13 | bundle_name = "Eclipse 4 Bazel", 14 | resources = ["//resources:srcs"], 15 | vendor = "The Bazel Authors", 16 | version = VERSION, 17 | visibility = ["//visibility:public"], 18 | deps = [ 19 | "//java/com/google/devtools/bazel/e4b/command", 20 | "//java/com/google/devtools/bazel/e4b/projectviews", 21 | "@com_google_guava//jar", 22 | ], 23 | ) 24 | 25 | eclipse_feature( 26 | name = "com.google.devtools.bazel.e4b.feature", 27 | copyright = "Copyright 2016 The Bazel Authors", 28 | description = "Integrate Eclipse with the Bazel build system.", 29 | label = "Eclipse 4 Bazel", 30 | license = ":LICENSE.txt", 31 | license_url = "http://www.apache.org/licenses/LICENSE-2.0", 32 | plugins = ["//:com.google.devtools.bazel.e4b"], 33 | provider = "The Bazel Authors", 34 | sites = {"Bazel": "https://bazel.build"}, 35 | url = "https://github.com/bazelbuild/e4b", 36 | version = VERSION, 37 | visibility = ["//visibility:public"], 38 | ) 39 | 40 | eclipse_p2updatesite( 41 | name = "p2updatesite", 42 | description = "Eclipse plugin for Bazel", 43 | eclipse_features = [":com.google.devtools.bazel.e4b.feature"], 44 | label = "Eclipse 4 Bazel", 45 | url = "https://eclipse.bazel.build", 46 | visibility = ["//tools/release:__pkg__"], 47 | ) 48 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Want to contribute? Great! First, read this page (including the small print at the end). 2 | 3 | ### Before you contribute 4 | **Before we can use your code, you must sign the 5 | [Google Individual Contributor License Agreement](https://developers.google.com/open-source/cla/individual?csw=1) 6 | (CLA)**, which you can do online. 7 | 8 | The CLA is necessary mainly because you own the copyright to your changes, 9 | even after your contribution becomes part of our codebase, so we need your 10 | permission to use and distribute your code. We also need to be sure of 11 | various other things — for instance that you'll tell us if you know that 12 | your code infringes on other people's patents. You don't have to sign 13 | the CLA until after you've submitted your code for review and a member has 14 | approved it, but you must do it before we can put your code into our codebase. 15 | 16 | Before you start working on a larger contribution, you should get in touch 17 | with us first. Use the issue tracker to explain your idea so we can help and 18 | possibly guide you. 19 | 20 | ### The small print 21 | Contributions made by corporations are covered by a different agreement than 22 | the one above, the 23 | [Software Grant and Corporate Contributor License Agreement](https://cla.developers.google.com/about/google-corporate). 24 | 25 | 26 | ## Contributing 27 | 28 | Please see the [Contributing to Bazel](https://bazel.build/contributing.html) 29 | on more practical matters on how to contribute to e4b. 30 | -------------------------------------------------------------------------------- /CONTRIBUTORS: -------------------------------------------------------------------------------- 1 | # People who have agreed to one of the CLAs and can contribute patches. 2 | # The AUTHORS file lists the copyright holders; this file 3 | # lists people. For example, Google employees are listed here 4 | # but not in AUTHORS, because Google holds the copyright. 5 | # 6 | # https://developers.google.com/open-source/cla/individual 7 | # https://developers.google.com/open-source/cla/corporate 8 | # 9 | # Names should be added to this file as: 10 | # Name 11 | 12 | Damien Martin-Guillerez 13 | Dmitry Lomov 14 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Bundle-ManifestVersion: 2 3 | Bundle-Name: Eclipse 4 Bazel 4 | Bundle-SymbolicName: com.google.devtools.bazel.e4b;singleton:=true 5 | Bundle-Version: 0.0.3.qualifier 6 | Bundle-Activator: com.google.devtools.bazel.e4b.Activator 7 | Bundle-Vendor: The Bazel Authors 8 | Require-Bundle: org.eclipse.ui.console, 9 | org.eclipse.core.jobs, 10 | org.eclipse.jface, 11 | javax.inject, 12 | org.eclipse.core.runtime, 13 | org.eclipse.ui, 14 | org.eclipse.core.runtime, 15 | org.eclipse.jdt.core, 16 | org.eclipse.jdt, 17 | org.eclipse.core.resources 18 | Bundle-RequiredExecutionEnvironment: JavaSE-1.8 19 | Bundle-ActivationPolicy: lazy 20 | Bundle-ClassPath: runfiles/com_google_guava/jar/guava-21.0.jar, 21 | runfiles/org_json/jar/json-20160212.jar, 22 | bin/ 23 | Import-Package: org.eclipse.ui.dialogs 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build status](https://badge.buildkite.com/7d78219bdc587a47f48cc96314f36f0f25ed9d0145f56b12ab.svg)](https://buildkite.com/bazel/eclipse-plugin-postsubmit) 2 | 3 | # Eclipse plugin for [Bazel](http://bazel.io) (**Highly experimental**) 4 | 5 | ## Current status 6 | 7 | __This project is unmaintained__. See https://github.com/salesforce/bazel-eclipse for the current Eclipse Feature for developing projects in Eclipse. 8 | 9 | ## Overview 10 | 11 | e4b is an Eclipse plugin for Bazel. It is really rudimentary. 12 | It simply supports creating the classpath from a project and using 13 | Bazel for incremental builds. 14 | 15 | You can create a new Bazel project with New project > Import Bazel project 16 | Then you can select the list of targets you want to build and the list 17 | of directories you want to track. 18 | 19 | It is highly experimental and support is minimal. However, we are happy 20 | to accept contribution to make this support grows to a better shape. 21 | 22 | ## Installation 23 | 24 | This plugin was tested with Eclipse Mars (4.5) but should also work with Eclipse Neon (4.6). 25 | 26 | * Start Eclipse 27 | * Select "Help" > "Install Software" 28 | * Add the update 29 | site 30 | [https://eclipse.bazel.build/updatesite](https://eclipse.bazel.build/updatesite). 31 | * Select "Uncategorized category" > "Eclipse 4 Bazel" 32 | 33 | ## Using the plugin 34 | 35 | To import an existing Bazel project or start a new one, go to "New" > "Project..." > "Others". 36 | 37 | ## Contributing 38 | 39 | See our [contributing 40 | page](https://github.com/bazelbuild/eclipse/blob/master/CONTRIBUTING.md) for how 41 | to contribute. 42 | -------------------------------------------------------------------------------- /WORKSPACE: -------------------------------------------------------------------------------- 1 | workspace(name = "build_bazel_eclipse") 2 | 3 | git_repository( 4 | name = "bazel_skylib", 5 | remote = "https://github.com/bazelbuild/bazel-skylib", 6 | commit = "2169ae1c374aab4a09aa90e65efe1a3aad4e279b", 7 | ) 8 | 9 | load("@bazel_skylib//:lib.bzl", "versions") 10 | versions.check("0.5.0") 11 | 12 | # TODO(dmarting): switch to release version of integration testing 13 | http_archive( 14 | name = "build_bazel_integration_testing", 15 | url = "https://github.com/bazelbuild/bazel-integration-testing/archive/7ace2e7fbfc32f89222a85804b574a1e07583ddc.zip", 16 | sha256 = "44de2377d38ad2386be132a21bae9b61291d546303747d72b454a735e18fa923", 17 | strip_prefix = "bazel-integration-testing-7ace2e7fbfc32f89222a85804b574a1e07583ddc", 18 | ) 19 | 20 | load("@build_bazel_integration_testing//tools:bazel_java_integration_test.bzl", "bazel_java_integration_test_deps") 21 | bazel_java_integration_test_deps() 22 | 23 | load("//tools/build_defs:eclipse.bzl", "load_eclipse_deps") 24 | load_eclipse_deps() 25 | 26 | new_http_archive( 27 | name = "com_google_python_gflags", 28 | url = "https://github.com/google/python-gflags/archive/python-gflags-2.0.zip", 29 | strip_prefix = "python-gflags-python-gflags-2.0", 30 | build_file_content = """ 31 | py_library( 32 | name = "gflags", 33 | srcs = [ 34 | "gflags.py", 35 | "gflags_validators.py", 36 | ], 37 | visibility = ["//visibility:public"], 38 | ) 39 | """, 40 | sha256 = "344990e63d49b9b7a829aec37d5981d558fea12879f673ee7d25d2a109eb30ce", 41 | ) 42 | 43 | # TODO(dmarting): Use http_file and relies on a mirror instead of maven_jar 44 | maven_jar( 45 | name = "org_json", 46 | artifact = "org.json:json:jar:20160212", 47 | ) 48 | 49 | maven_jar( 50 | name = "com_google_truth", 51 | artifact = "com.google.truth:truth:jar:0.31", 52 | ) 53 | -------------------------------------------------------------------------------- /build.properties: -------------------------------------------------------------------------------- 1 | source.. = java/,\ 2 | javatests/ 3 | output.. = bin/ 4 | bin.includes = META-INF/,\ 5 | resources/,\ 6 | plugin.xml,\ 7 | bazel-bin/javatests/com/google/devtools/bazel/e4b/command/CommandTest.runfiles/com_google_guava/jar/guava-21.0.jar,\ 8 | bazel-bin/javatests/com/google/devtools/bazel/e4b/command/CommandTest.runfiles/org_json/jar/json-20160212.jar,\ 9 | bin/ 10 | -------------------------------------------------------------------------------- /java/com/google/devtools/bazel/e4b/Activator.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Bazel Authors. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package com.google.devtools.bazel.e4b; 16 | 17 | import org.eclipse.core.runtime.Status; 18 | import org.eclipse.jface.util.IPropertyChangeListener; 19 | import org.eclipse.jface.util.PropertyChangeEvent; 20 | import org.eclipse.ui.plugin.AbstractUIPlugin; 21 | import org.osgi.framework.BundleContext; 22 | 23 | import com.google.devtools.bazel.e4b.command.BazelCommand; 24 | 25 | /** 26 | * The activator class controls the plug-in life cycle 27 | */ 28 | public class Activator extends AbstractUIPlugin { 29 | 30 | // The plug-in ID 31 | public static final String PLUGIN_ID = "com.google.devtools.bazel.e4b"; //$NON-NLS-1$ 32 | 33 | // The shared instance 34 | private static Activator plugin; 35 | 36 | private BazelCommand command; 37 | 38 | /** 39 | * The constructor 40 | */ 41 | public Activator() {} 42 | 43 | /* 44 | * (non-Javadoc) 45 | * 46 | * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext) 47 | */ 48 | @Override 49 | public void start(BundleContext context) throws Exception { 50 | plugin = this; 51 | super.start(context); 52 | this.command = new BazelCommand(new BazelAspectLocationImpl(), new CommandConsoleFactoryImpl()); 53 | // Get the bazel path from the settings 54 | this.command.setBazelPath(getPreferenceStore().getString("BAZEL_PATH")); 55 | getPreferenceStore().addPropertyChangeListener(new IPropertyChangeListener() { 56 | @Override 57 | public void propertyChange(PropertyChangeEvent event) { 58 | if (event.getProperty().equals("BAZEL_PATH")) { 59 | command.setBazelPath(event.getNewValue().toString()); 60 | } 61 | } 62 | }); 63 | } 64 | 65 | /* 66 | * (non-Javadoc) 67 | * 68 | * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext) 69 | */ 70 | @Override 71 | public void stop(BundleContext context) throws Exception { 72 | plugin = null; 73 | this.command = null; 74 | super.stop(context); 75 | } 76 | 77 | /** 78 | * Returns the shared instance 79 | * 80 | * @return the shared instance 81 | */ 82 | public static Activator getDefault() { 83 | return plugin; 84 | } 85 | 86 | /** 87 | * Returns the unique instance of {@link BazelCommand}. 88 | */ 89 | public BazelCommand getCommand() { 90 | return command; 91 | } 92 | 93 | /** 94 | * Log an error to eclipse. 95 | */ 96 | public static void error(String message) { 97 | plugin.getLog().log(new Status(Status.ERROR, PLUGIN_ID, message)); 98 | } 99 | 100 | /** 101 | * Log an error to eclipse, with an attached exception. 102 | */ 103 | public static void error(String message, Throwable exception) { 104 | plugin.getLog().log(new Status(Status.ERROR, PLUGIN_ID, message, exception)); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /java/com/google/devtools/bazel/e4b/BazelAspectLocationImpl.java: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Bazel Authors. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package com.google.devtools.bazel.e4b; 16 | 17 | import java.io.File; 18 | import java.io.IOException; 19 | import java.net.URL; 20 | 21 | import org.eclipse.core.runtime.FileLocator; 22 | import org.eclipse.core.runtime.Platform; 23 | 24 | import com.google.devtools.bazel.e4b.command.BazelAspectLocation; 25 | 26 | /** Implementation of {@link BazelAspectLocation} using Eclipse OSGi Bundle locations */ 27 | class BazelAspectLocationImpl implements BazelAspectLocation { 28 | 29 | // Returns the path of the resources file from this plugin. 30 | private static File getAspectWorkspace() { 31 | try { 32 | URL url = Platform.getBundle(Activator.PLUGIN_ID).getEntry("resources"); 33 | URL resolved = FileLocator.resolve(url); 34 | return new File(resolved.getPath()); 35 | } catch (IOException e) { 36 | throw new RuntimeException(e); 37 | } 38 | } 39 | 40 | private static File WORKSPACE_DIRECTORY = getAspectWorkspace(); 41 | 42 | @Override 43 | public File getWorkspaceDirectory() { 44 | return WORKSPACE_DIRECTORY; 45 | } 46 | 47 | @Override 48 | public String getAspectLabel() { 49 | return "//:e4b_aspect.bzl%e4b_aspect"; 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /java/com/google/devtools/bazel/e4b/BazelProjectSupport.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Bazel Authors. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package com.google.devtools.bazel.e4b; 16 | 17 | import java.io.File; 18 | import java.io.IOException; 19 | import java.net.URI; 20 | import java.util.LinkedList; 21 | import java.util.List; 22 | 23 | import org.eclipse.core.resources.ICommand; 24 | import org.eclipse.core.resources.IFolder; 25 | import org.eclipse.core.resources.IProject; 26 | import org.eclipse.core.resources.IProjectDescription; 27 | import org.eclipse.core.resources.IResource; 28 | import org.eclipse.core.resources.IWorkspaceRoot; 29 | import org.eclipse.core.resources.ProjectScope; 30 | import org.eclipse.core.resources.ResourcesPlugin; 31 | import org.eclipse.core.runtime.CoreException; 32 | import org.eclipse.core.runtime.IPath; 33 | import org.eclipse.core.runtime.Path; 34 | import org.eclipse.core.runtime.preferences.IScopeContext; 35 | import org.eclipse.jdt.core.IClasspathEntry; 36 | import org.eclipse.jdt.core.IJavaProject; 37 | import org.eclipse.jdt.core.JavaCore; 38 | import org.eclipse.jdt.core.JavaModelException; 39 | import org.osgi.service.prefs.BackingStoreException; 40 | import org.osgi.service.prefs.Preferences; 41 | 42 | import com.google.common.collect.ImmutableList; 43 | import com.google.devtools.bazel.e4b.classpath.BazelClasspathContainer; 44 | import com.google.devtools.bazel.e4b.command.BazelCommand; 45 | import com.google.devtools.bazel.e4b.command.BazelCommand.BazelInstance; 46 | import com.google.devtools.bazel.e4b.command.BazelNotFoundException; 47 | import com.google.devtools.bazel.e4b.projectviews.ProjectView; 48 | 49 | /** 50 | * A utility class to create e4b projects. 51 | */ 52 | public class BazelProjectSupport { 53 | 54 | private static final String STANDARD_VM_CONTAINER_PREFIX = 55 | "org.eclipse.jdt.launching.JRE_CONTAINER/" 56 | + "org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1."; 57 | 58 | /** 59 | * Create a e4b project. This method adds the natures to the project, saves the list of targets 60 | * and the workspace root to the project settings, make Bazel the default builder instead of ECJ 61 | * and create the classpath using ide build informations from Bazel. 62 | */ 63 | public static IProject createProject(String projectName, URI location, String workspaceRoot, 64 | List paths, List targets, int javaLanguageVersion) { 65 | 66 | IProject project = createBaseProject(projectName, location); 67 | try { 68 | addNature(project, ProjectNature.NATURE_ID); 69 | addNature(project, JavaCore.NATURE_ID); 70 | addSettings(project, workspaceRoot, targets, ImmutableList.of()); 71 | setBuilders(project); 72 | createClasspath(new Path(workspaceRoot), paths, JavaCore.create(project), 73 | javaLanguageVersion); 74 | } catch (CoreException e) { 75 | e.printStackTrace(); 76 | project = null; 77 | } catch (BackingStoreException e) { 78 | e.printStackTrace(); 79 | project = null; 80 | } 81 | 82 | return project; 83 | } 84 | 85 | private static void addSettings(IProject project, String workspaceRoot, List targets, 86 | List buildFlags) throws BackingStoreException { 87 | IScopeContext projectScope = new ProjectScope(project); 88 | Preferences projectNode = projectScope.getNode(Activator.PLUGIN_ID); 89 | int i = 0; 90 | for (String target : targets) { 91 | projectNode.put("target" + i, target); 92 | i++; 93 | } 94 | projectNode.put("workspaceRoot", workspaceRoot); 95 | i = 0; 96 | for (String flag : buildFlags) { 97 | projectNode.put("buildFlag" + i, flag); 98 | i++; 99 | } 100 | projectNode.flush(); 101 | } 102 | 103 | private static void setBuilders(IProject project) throws CoreException { 104 | IProjectDescription description = project.getDescription(); 105 | final ICommand buildCommand = description.newCommand(); 106 | buildCommand.setBuilderName("com.google.devtools.bazel.e4b.builder"); 107 | description.setBuildSpec(new ICommand[] {buildCommand}); 108 | project.setDescription(description, null); 109 | } 110 | 111 | private static void createClasspath(IPath root, List paths, IJavaProject javaProject, 112 | int javaLanguageLevel) throws CoreException { 113 | String name = root.lastSegment(); 114 | IFolder base = javaProject.getProject().getFolder(name); 115 | if (!base.isLinked()) { 116 | base.createLink(root, IResource.NONE, null); 117 | } 118 | List list = new LinkedList<>(); 119 | for (String path : paths) { 120 | IPath workspacePath = base.getFullPath().append(path); 121 | list.add(JavaCore.newSourceEntry(workspacePath)); 122 | } 123 | list.add(JavaCore.newContainerEntry(new Path(BazelClasspathContainer.CONTAINER_NAME))); 124 | 125 | list.add( 126 | JavaCore.newContainerEntry(new Path(STANDARD_VM_CONTAINER_PREFIX + javaLanguageLevel))); 127 | IClasspathEntry[] newClasspath = list.toArray(new IClasspathEntry[0]); 128 | javaProject.setRawClasspath(newClasspath, null); 129 | } 130 | 131 | private static IProject createBaseProject(String projectName, URI location) { 132 | IProject newProject = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName); 133 | 134 | if (!newProject.exists()) { 135 | URI projectLocation = location; 136 | IProjectDescription desc = 137 | newProject.getWorkspace().newProjectDescription(newProject.getName()); 138 | if (location != null 139 | && ResourcesPlugin.getWorkspace().getRoot().getLocationURI().equals(location)) { 140 | projectLocation = null; 141 | } 142 | 143 | desc.setLocationURI(projectLocation); 144 | try { 145 | newProject.create(desc, null); 146 | if (!newProject.isOpen()) { 147 | newProject.open(null); 148 | } 149 | } catch (CoreException e) { 150 | e.printStackTrace(); 151 | } 152 | } 153 | 154 | return newProject; 155 | } 156 | 157 | private static void addNature(IProject project, String nature) throws CoreException { 158 | if (!project.hasNature(nature)) { 159 | IProjectDescription description = project.getDescription(); 160 | String[] prevNatures = description.getNatureIds(); 161 | String[] newNatures = new String[prevNatures.length + 1]; 162 | System.arraycopy(prevNatures, 0, newNatures, 0, prevNatures.length); 163 | newNatures[prevNatures.length] = nature; 164 | description.setNatureIds(newNatures); 165 | 166 | project.setDescription(description, null); 167 | } 168 | } 169 | 170 | /** 171 | * List targets configure for project. Each project configured for Bazel is 172 | * configured to track certain targets and this function fetch this list from the project 173 | * preferences. 174 | */ 175 | public static List getTargets(IProject project) throws BackingStoreException { 176 | // Get the list of targets from the preferences 177 | IScopeContext projectScope = new ProjectScope(project); 178 | Preferences projectNode = projectScope.getNode(Activator.PLUGIN_ID); 179 | ImmutableList.Builder builder = ImmutableList.builder(); 180 | for (String s : projectNode.keys()) { 181 | if (s.startsWith("target")) { 182 | builder.add(projectNode.get(s, "")); 183 | } 184 | } 185 | return builder.build(); 186 | } 187 | 188 | /** 189 | * List of build flags for project, taken from the project configuration 190 | */ 191 | public static List getBuildFlags(IProject project) throws BackingStoreException { 192 | // Get the list of targets from the preferences 193 | IScopeContext projectScope = new ProjectScope(project); 194 | Preferences projectNode = projectScope.getNode(Activator.PLUGIN_ID); 195 | ImmutableList.Builder builder = ImmutableList.builder(); 196 | for (String s : projectNode.keys()) { 197 | if (s.startsWith("buildArgs")) { 198 | builder.add(projectNode.get(s, "")); 199 | } 200 | } 201 | return builder.build(); 202 | } 203 | 204 | /** 205 | * Return the {@link BazelInstance} corresponding to the given project. It looks for 206 | * the instance that runs for the workspace root configured for that project. 207 | * 208 | * @throws BazelNotFoundException 209 | */ 210 | public static BazelCommand.BazelInstance getBazelCommandInstance(IProject project) 211 | throws BackingStoreException, IOException, InterruptedException, BazelNotFoundException { 212 | IScopeContext projectScope = new ProjectScope(project.getProject()); 213 | Preferences projectNode = projectScope.getNode(Activator.PLUGIN_ID); 214 | File workspaceRoot = 215 | new File(projectNode.get("workspaceRoot", project.getLocation().toFile().toString())); 216 | return Activator.getDefault().getCommand().getInstance(workspaceRoot); 217 | } 218 | 219 | /** 220 | * Convert an Eclipse JDT project into an IntelliJ project view 221 | */ 222 | public static ProjectView getProjectView(IProject project) 223 | throws BackingStoreException, JavaModelException { 224 | com.google.devtools.bazel.e4b.projectviews.Builder builder = ProjectView.builder(); 225 | IScopeContext projectScope = new ProjectScope(project); 226 | Preferences projectNode = projectScope.getNode(Activator.PLUGIN_ID); 227 | for (String s : projectNode.keys()) { 228 | if (s.startsWith("buildArgs")) { 229 | builder.addBuildFlag(projectNode.get(s, "")); 230 | } else if (s.startsWith("target")) { 231 | builder.addTarget(projectNode.get(s, "")); 232 | } 233 | } 234 | 235 | IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); 236 | for (IClasspathEntry entry : ((IJavaProject) project).getRawClasspath()) { 237 | switch (entry.getEntryKind()) { 238 | case IClasspathEntry.CPE_SOURCE: 239 | IResource res = root.findMember(entry.getPath()); 240 | if (res != null) { 241 | builder.addDirectory(res.getProjectRelativePath().removeFirstSegments(1).toOSString()); 242 | } 243 | break; 244 | case IClasspathEntry.CPE_CONTAINER: 245 | String path = entry.getPath().toOSString(); 246 | if (path.startsWith(STANDARD_VM_CONTAINER_PREFIX)) { 247 | builder.setJavaLanguageLevel( 248 | Integer.parseInt(path.substring(STANDARD_VM_CONTAINER_PREFIX.length()))); 249 | } 250 | break; 251 | } 252 | } 253 | return builder.build(); 254 | } 255 | } 256 | -------------------------------------------------------------------------------- /java/com/google/devtools/bazel/e4b/CommandConsoleFactoryImpl.java: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Bazel Authors. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package com.google.devtools.bazel.e4b; 16 | 17 | import com.google.devtools.bazel.e4b.command.CommandConsole; 18 | import com.google.devtools.bazel.e4b.command.CommandConsole.CommandConsoleFactory; 19 | import java.io.IOException; 20 | import java.io.OutputStream; 21 | import org.eclipse.swt.graphics.Color; 22 | import org.eclipse.swt.widgets.Display; 23 | import org.eclipse.ui.console.ConsolePlugin; 24 | import org.eclipse.ui.console.IConsole; 25 | import org.eclipse.ui.console.IConsoleManager; 26 | import org.eclipse.ui.console.MessageConsole; 27 | import org.eclipse.ui.console.MessageConsoleStream; 28 | 29 | /** Implementation of {@link CommandConsoleFactory} using Eclipse's console */ 30 | class CommandConsoleFactoryImpl implements CommandConsoleFactory { 31 | 32 | private static class CommandConsoleImpl implements CommandConsole { 33 | 34 | private MessageConsole console; 35 | 36 | CommandConsoleImpl(MessageConsole console) { 37 | this.console = console; 38 | } 39 | 40 | @Override 41 | public OutputStream createOutputStream() { 42 | return console.newMessageStream(); 43 | } 44 | 45 | @Override 46 | public OutputStream createErrorStream() { 47 | // Get the error stream for the given console (a stream that print in red). 48 | final MessageConsoleStream errorStream = console.newMessageStream(); 49 | Display display = Display.getCurrent(); 50 | if (display == null) { 51 | display = Display.getDefault(); 52 | } 53 | display.asyncExec(() -> errorStream.setColor(new Color(null, 255, 0, 0))); 54 | return errorStream; 55 | } 56 | } 57 | 58 | @Override 59 | public CommandConsole get(String name, String title) throws IOException { 60 | MessageConsole console = findConsole(name); 61 | MessageConsoleStream stream = console.newMessageStream(); 62 | stream.setActivateOnWrite(true); 63 | stream.write("*** " + title + " ***\n"); 64 | return new CommandConsoleImpl(console); 65 | } 66 | 67 | // Taken from the eclipse website, find a console 68 | private static MessageConsole findConsole(String name) { 69 | ConsolePlugin plugin = ConsolePlugin.getDefault(); 70 | IConsoleManager conMan = plugin.getConsoleManager(); 71 | IConsole[] existing = conMan.getConsoles(); 72 | for (int i = 0; i < existing.length; i++) { 73 | if (name.equals(existing[i].getName())) { 74 | return (MessageConsole) existing[i]; 75 | } 76 | } 77 | // no console found, so create a new one 78 | MessageConsole myConsole = new MessageConsole(name, null); 79 | conMan.addConsoles(new IConsole[] {myConsole}); 80 | return myConsole; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /java/com/google/devtools/bazel/e4b/ProjectNature.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Bazel Authors. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package com.google.devtools.bazel.e4b; 16 | 17 | import org.eclipse.core.resources.IProject; 18 | import org.eclipse.core.resources.IProjectNature; 19 | import org.eclipse.core.runtime.CoreException; 20 | 21 | /** 22 | * Project nature for e4b. 23 | */ 24 | public class ProjectNature implements IProjectNature { 25 | 26 | public static final String NATURE_ID = "com.google.devtools.bazel.e4b.projectNature"; //$NON-NLS-1$ 27 | 28 | @Override 29 | public void configure() throws CoreException {} 30 | 31 | @Override 32 | public void deconfigure() throws CoreException {} 33 | 34 | @Override 35 | public IProject getProject() { 36 | return null; 37 | } 38 | 39 | @Override 40 | public void setProject(IProject project) {} 41 | } 42 | -------------------------------------------------------------------------------- /java/com/google/devtools/bazel/e4b/builder/BazelBuilder.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Bazel Authors. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package com.google.devtools.bazel.e4b.builder; 16 | 17 | import java.io.IOException; 18 | import java.util.Map; 19 | 20 | import org.eclipse.core.resources.IProject; 21 | import org.eclipse.core.resources.IResourceDelta; 22 | import org.eclipse.core.resources.IncrementalProjectBuilder; 23 | import org.eclipse.core.runtime.CoreException; 24 | import org.eclipse.core.runtime.IProgressMonitor; 25 | import org.osgi.service.prefs.BackingStoreException; 26 | 27 | import com.google.devtools.bazel.e4b.Activator; 28 | import com.google.devtools.bazel.e4b.BazelProjectSupport; 29 | import com.google.devtools.bazel.e4b.command.BazelCommand.BazelInstance; 30 | import com.google.devtools.bazel.e4b.command.BazelNotFoundException; 31 | 32 | public class BazelBuilder extends IncrementalProjectBuilder { 33 | 34 | @Override 35 | protected IProject[] build(int kind, Map args, IProgressMonitor monitor) 36 | throws CoreException { 37 | IProject project = getProject(); 38 | try { 39 | BazelInstance instance = BazelProjectSupport.getBazelCommandInstance(project); 40 | if (kind == INCREMENTAL_BUILD || kind == AUTO_BUILD) { 41 | IResourceDelta delta = getDelta(getProject()); 42 | if (delta == null || delta.getAffectedChildren().length == 0) { 43 | // null build, skip calling Bazel. 44 | return null; 45 | } 46 | } 47 | instance.markAsDirty(); 48 | instance.build(BazelProjectSupport.getTargets(project), 49 | BazelProjectSupport.getBuildFlags(project)); 50 | } catch (BackingStoreException | IOException | InterruptedException e) { 51 | Activator.error("Failed to build " + project.getName(), e); 52 | } catch (BazelNotFoundException e) { 53 | Activator.error("Bazel not found: " + e.getMessage()); 54 | } 55 | return null; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /java/com/google/devtools/bazel/e4b/classpath/BazelClasspathContainer.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Bazel Authors. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package com.google.devtools.bazel.e4b.classpath; 16 | 17 | import java.io.File; 18 | import java.io.IOException; 19 | import java.nio.file.FileSystems; 20 | import java.nio.file.Path; 21 | import java.nio.file.PathMatcher; 22 | import java.util.HashSet; 23 | import java.util.List; 24 | import java.util.Map; 25 | import java.util.Set; 26 | 27 | import org.eclipse.core.resources.IResource; 28 | import org.eclipse.core.resources.IWorkspaceRoot; 29 | import org.eclipse.core.resources.ResourcesPlugin; 30 | import org.eclipse.core.runtime.IPath; 31 | import org.eclipse.jdt.core.IClasspathContainer; 32 | import org.eclipse.jdt.core.IClasspathEntry; 33 | import org.eclipse.jdt.core.IJavaProject; 34 | import org.eclipse.jdt.core.JavaCore; 35 | import org.eclipse.jdt.core.JavaModelException; 36 | import org.osgi.service.prefs.BackingStoreException; 37 | 38 | import com.google.devtools.bazel.e4b.Activator; 39 | import com.google.devtools.bazel.e4b.BazelProjectSupport; 40 | import com.google.devtools.bazel.e4b.command.BazelCommand.BazelInstance; 41 | import com.google.devtools.bazel.e4b.command.BazelNotFoundException; 42 | import com.google.devtools.bazel.e4b.command.IdeBuildInfo; 43 | import com.google.devtools.bazel.e4b.command.IdeBuildInfo.Jars; 44 | 45 | public class BazelClasspathContainer implements IClasspathContainer { 46 | public static final String CONTAINER_NAME = "com.google.devtools.bazel.e4b.BAZEL_CONTAINER"; 47 | 48 | private final IPath path; 49 | private final IJavaProject project; 50 | private final BazelInstance instance; 51 | 52 | public BazelClasspathContainer(IPath path, IJavaProject project) 53 | throws IOException, InterruptedException, BackingStoreException, JavaModelException, 54 | BazelNotFoundException { 55 | this.path = path; 56 | this.project = project; 57 | this.instance = BazelProjectSupport.getBazelCommandInstance(project.getProject()); 58 | } 59 | 60 | private boolean isSourcePath(String path) throws JavaModelException, BackingStoreException { 61 | Path pp = new File(instance.getWorkspaceRoot().toString() + File.separator + path).toPath(); 62 | IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); 63 | for (IClasspathEntry entry : project.getRawClasspath()) { 64 | if (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE) { 65 | IResource res = root.findMember(entry.getPath()); 66 | if (res != null) { 67 | String file = res.getLocation().toOSString(); 68 | if (!file.isEmpty() && pp.startsWith(file)) { 69 | IPath[] inclusionPatterns = entry.getInclusionPatterns(); 70 | if (!matchPatterns(pp, entry.getExclusionPatterns()) && (inclusionPatterns == null 71 | || inclusionPatterns.length == 0 || matchPatterns(pp, inclusionPatterns))) { 72 | return true; 73 | } 74 | } 75 | } 76 | } 77 | } 78 | return false; 79 | } 80 | 81 | private boolean matchPatterns(Path path, IPath[] patterns) { 82 | if (patterns != null) { 83 | for (IPath p : patterns) { 84 | PathMatcher matcher = FileSystems.getDefault().getPathMatcher("glob:" + p.toOSString()); 85 | if (matcher.matches(path)) { 86 | return true; 87 | } 88 | } 89 | } 90 | return false; 91 | } 92 | 93 | private boolean isSourceInPaths(List sources) 94 | throws JavaModelException, BackingStoreException { 95 | for (String s : sources) { 96 | if (isSourcePath(s)) { 97 | return true; 98 | } 99 | } 100 | return false; 101 | } 102 | 103 | @Override 104 | public IClasspathEntry[] getClasspathEntries() { 105 | try { 106 | List targets = BazelProjectSupport.getTargets(project.getProject()); 107 | Map infos = instance.getIdeInfo(targets); 108 | Set jars = new HashSet<>(); 109 | for (IdeBuildInfo s : infos.values()) { 110 | jars.addAll(s.getGeneratedJars()); 111 | if (!isSourceInPaths(s.getSources())) { 112 | jars.addAll(s.getJars()); 113 | } 114 | } 115 | return jarsToClasspathEntries(jars); 116 | } catch (JavaModelException | BackingStoreException | IOException | InterruptedException e) { 117 | Activator.error("Unable to compute classpath containers entries.", e); 118 | return new IClasspathEntry[] {}; 119 | } catch (BazelNotFoundException e) { 120 | Activator.error("Bazel not found: " + e.getMessage()); 121 | return new IClasspathEntry[] {}; 122 | } 123 | } 124 | 125 | private IClasspathEntry[] jarsToClasspathEntries(Set jars) { 126 | IClasspathEntry[] entries = new IClasspathEntry[jars.size()]; 127 | int i = 0; 128 | File execRoot = instance.getExecRoot(); 129 | for (Jars j : jars) { 130 | entries[i] = JavaCore.newLibraryEntry(getJarIPath(execRoot, j.getJar()), 131 | getJarIPath(execRoot, j.getSrcJar()), null); 132 | i++; 133 | } 134 | return entries; 135 | } 136 | 137 | private static IPath getJarIPath(File execRoot, String file) { 138 | if (file == null) { 139 | return null; 140 | } 141 | File path = new File(execRoot, file); 142 | return org.eclipse.core.runtime.Path.fromOSString(path.toString()); 143 | } 144 | 145 | @Override 146 | public String getDescription() { 147 | return "Bazel Classpath Container"; 148 | } 149 | 150 | @Override 151 | public int getKind() { 152 | return K_APPLICATION; 153 | } 154 | 155 | @Override 156 | public IPath getPath() { 157 | return path; 158 | } 159 | 160 | 161 | public boolean isValid() { 162 | return instance != null; 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /java/com/google/devtools/bazel/e4b/classpath/BazelClasspathContainerInitilalizer.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Bazel Authors. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package com.google.devtools.bazel.e4b.classpath; 16 | 17 | import java.io.IOException; 18 | 19 | import org.eclipse.core.runtime.CoreException; 20 | import org.eclipse.core.runtime.IPath; 21 | import org.eclipse.jdt.core.ClasspathContainerInitializer; 22 | import org.eclipse.jdt.core.IClasspathContainer; 23 | import org.eclipse.jdt.core.IJavaProject; 24 | import org.eclipse.jdt.core.JavaCore; 25 | import org.osgi.service.prefs.BackingStoreException; 26 | 27 | import com.google.devtools.bazel.e4b.Activator; 28 | import com.google.devtools.bazel.e4b.command.BazelNotFoundException; 29 | 30 | public class BazelClasspathContainerInitilalizer extends ClasspathContainerInitializer { 31 | 32 | @Override 33 | public void initialize(IPath path, IJavaProject project) throws CoreException { 34 | try { 35 | BazelClasspathContainer container = new BazelClasspathContainer(path, project); 36 | if (container.isValid()) { 37 | JavaCore.setClasspathContainer(path, new IJavaProject[] {project}, 38 | new IClasspathContainer[] {container}, null); 39 | } else { 40 | Activator.error("Unable to create classpath container (Not a Bazel workspace?)"); 41 | } 42 | } catch (IOException | InterruptedException | BackingStoreException e) { 43 | Activator.error("Error while creating Bazel classpath container.", e); 44 | } catch (BazelNotFoundException e) { 45 | Activator.error("Bazel not found: " + e.getMessage()); 46 | } 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /java/com/google/devtools/bazel/e4b/command/BUILD: -------------------------------------------------------------------------------- 1 | java_library( 2 | name = "command", 3 | srcs = glob(["*.java"]), 4 | visibility = [ 5 | "//:__pkg__", 6 | "//javatests/com/google/devtools/bazel/e4b:__subpackages__", 7 | ], 8 | deps = [ 9 | "@com_google_guava//jar", 10 | "@org_json//jar", 11 | ], 12 | ) 13 | -------------------------------------------------------------------------------- /java/com/google/devtools/bazel/e4b/command/BazelAspectLocation.java: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Bazel Authors. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package com.google.devtools.bazel.e4b.command; 16 | 17 | import java.io.File; 18 | 19 | /** 20 | * Define the location of the aspect to use to analyze a Bazel project. 21 | */ 22 | public interface BazelAspectLocation { 23 | 24 | /** Returns a {@link File} object that points to the Bazel workspace containing the aspect. */ 25 | public File getWorkspaceDirectory(); 26 | 27 | /** Returns the label of the aspect in the Bazel workspace (with the function name). */ 28 | public String getAspectLabel(); 29 | } 30 | -------------------------------------------------------------------------------- /java/com/google/devtools/bazel/e4b/command/BazelCommand.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Bazel Authors. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package com.google.devtools.bazel.e4b.command; 16 | 17 | import java.io.File; 18 | import java.io.IOException; 19 | import java.io.OutputStream; 20 | import java.util.Collection; 21 | import java.util.HashMap; 22 | import java.util.List; 23 | import java.util.Map; 24 | import java.util.function.Function; 25 | import java.util.regex.Matcher; 26 | import java.util.regex.Pattern; 27 | 28 | import com.google.common.base.Joiner; 29 | import com.google.common.collect.ImmutableList; 30 | import com.google.common.collect.ImmutableMap; 31 | import com.google.devtools.bazel.e4b.command.CommandConsole.CommandConsoleFactory; 32 | 33 | /** 34 | * Main utility to call bazel commands, wrapping its input and output to the message console. 35 | */ 36 | public class BazelCommand { 37 | 38 | private static Joiner NEW_LINE_JOINER = Joiner.on("\n"); 39 | private static Pattern VERSION_PATTERN = 40 | Pattern.compile("^([0-9]+)\\.([0-9]+)\\.([0-9]+)([^0-9].*)?$"); 41 | 42 | // Minimum bazel version needed to work with this plugin (currently 0.5.0) 43 | private static int[] MINIMUM_BAZEL_VERSION = {0, 5, 0}; 44 | 45 | private static enum ConsoleType { 46 | NO_CONSOLE, SYSTEM, WORKSPACE 47 | } 48 | 49 | private final BazelAspectLocation aspectLocation; 50 | private final CommandConsoleFactory consoleFactory; 51 | 52 | private final List buildOptions; 53 | private final List aspectOptions; 54 | 55 | private final Map instances = new HashMap<>(); 56 | private File bazel = null; 57 | 58 | /** 59 | * Create a {@link BazelCommand} object, providing the implementation for locating aspect and 60 | * getting console streams. 61 | */ 62 | public BazelCommand(BazelAspectLocation aspectLocation, CommandConsoleFactory consoleFactory) { 63 | this.aspectLocation = aspectLocation; 64 | this.consoleFactory = consoleFactory; 65 | this.buildOptions = ImmutableList.of("--watchfs", 66 | "--override_repository=local_eclipse_aspect=" + aspectLocation.getWorkspaceDirectory(), 67 | "--aspects=@local_eclipse_aspect" + aspectLocation.getAspectLabel()); 68 | this.aspectOptions = ImmutableList.builder().addAll(buildOptions).add("-k", 69 | "--output_groups=ide-info-text,ide-resolve,-_,-defaults", "--experimental_show_artifacts") 70 | .build(); 71 | } 72 | 73 | private String getBazelPath() throws BazelNotFoundException { 74 | if (bazel == null || !bazel.exists() || !bazel.canExecute()) { 75 | throw new BazelNotFoundException.BazelNotSetException(); 76 | } 77 | 78 | return bazel.toString(); 79 | } 80 | 81 | /** 82 | * Set the path to the Bazel binary. 83 | */ 84 | public synchronized void setBazelPath(String bazel) { 85 | this.bazel = new File(bazel); 86 | } 87 | 88 | /** 89 | * Check the version of Bazel: throws an exception if the version is incorrect or the path does 90 | * not point to a Bazel binary. 91 | */ 92 | public void checkVersion(String bazel) throws BazelNotFoundException { 93 | File path = new File(bazel); 94 | if (!path.exists() || !path.canExecute()) { 95 | throw new BazelNotFoundException.BazelNotExecutableException(); 96 | } 97 | try { 98 | Command command = Command.builder(consoleFactory).setConsoleName(null) 99 | .setDirectory(aspectLocation.getWorkspaceDirectory()).addArguments(bazel, "version") 100 | .setStdoutLineSelector((s) -> s.startsWith("Build label:") ? s.substring(13) : null) 101 | .build(); 102 | if (command.run() != 0) { 103 | throw new BazelNotFoundException.BazelNotExecutableException(); 104 | } 105 | List result = command.getSelectedOutputLines(); 106 | if (result.size() != 1) { 107 | throw new BazelNotFoundException.BazelTooOldException("unknown"); 108 | } 109 | String version = result.get(0); 110 | Matcher versionMatcher = VERSION_PATTERN.matcher(version); 111 | if (versionMatcher == null || !versionMatcher.matches()) { 112 | throw new BazelNotFoundException.BazelTooOldException(version); 113 | } 114 | int[] versionNumbers = {Integer.parseInt(versionMatcher.group(1)), 115 | Integer.parseInt(versionMatcher.group(2)), Integer.parseInt(versionMatcher.group(3))}; 116 | if (compareVersion(versionNumbers, MINIMUM_BAZEL_VERSION) < 0) { 117 | throw new BazelNotFoundException.BazelTooOldException(version); 118 | } 119 | } catch (IOException | InterruptedException e) { 120 | throw new BazelNotFoundException.BazelNotExecutableException(); 121 | } 122 | } 123 | 124 | private static int compareVersion(int[] version1, int[] version2) { 125 | for (int i = 0; i < Math.min(version1.length, version2.length); i++) { 126 | if (version1[i] < version2[i]) { 127 | return -1; 128 | } else if (version1[i] > version2[i]) { 129 | return 1; 130 | } 131 | } 132 | return Integer.compare(version1.length, version2.length); 133 | } 134 | 135 | /** 136 | * Returns a {@link BazelInstance} for the given directory. It looks for the enclosing workspace 137 | * and returns the instance that correspond to it. If not in a workspace, returns null. 138 | * 139 | * @throws BazelNotFoundException 140 | */ 141 | public BazelInstance getInstance(File directory) 142 | throws IOException, InterruptedException, BazelNotFoundException { 143 | File workspaceRoot = getWorkspaceRoot(directory); 144 | if (workspaceRoot == null) { 145 | return null; 146 | } 147 | if (!instances.containsKey(workspaceRoot)) { 148 | instances.put(workspaceRoot, new BazelInstance(workspaceRoot)); 149 | } 150 | return instances.get(workspaceRoot); 151 | } 152 | 153 | /** 154 | * An instance of the Bazel interface for a specific workspace. Provides means to query Bazel on 155 | * this workspace. 156 | */ 157 | public class BazelInstance { 158 | private final File workspaceRoot; 159 | private final File execRoot; 160 | 161 | private final Map> buildInfoCache = new HashMap<>(); 162 | 163 | private BazelInstance(File workspaceRoot) 164 | throws IOException, InterruptedException, BazelNotFoundException { 165 | this.workspaceRoot = workspaceRoot; 166 | this.execRoot = new File(String.join("", runBazel("info", "execution_root"))); 167 | } 168 | 169 | /** 170 | * Returns the list of targets present in the BUILD files for the given sub-directories. 171 | * 172 | * @throws BazelNotFoundException 173 | */ 174 | public synchronized List listTargets(File... directories) 175 | throws IOException, InterruptedException, BazelNotFoundException { 176 | StringBuilder builder = new StringBuilder(); 177 | for (File f : directories) { 178 | builder.append(f.toURI().relativize(workspaceRoot.toURI()).getPath()).append("/... "); 179 | } 180 | return runBazel("query", builder.toString()); 181 | } 182 | 183 | private synchronized List runBazel(String... args) 184 | throws IOException, InterruptedException, BazelNotFoundException { 185 | return runBazel(ImmutableList.builder().add(args).build()); 186 | } 187 | 188 | private synchronized List runBazel(List args) 189 | throws IOException, InterruptedException, BazelNotFoundException { 190 | return BazelCommand.this.runBazelAndGetOuputLines(ConsoleType.WORKSPACE, workspaceRoot, args); 191 | } 192 | 193 | /** 194 | * Returns the IDE build information from running the aspect over the given list of targets. The 195 | * result is a list of of path to the output artifact created by the build. 196 | * 197 | * @throws BazelNotFoundException 198 | */ 199 | private synchronized List buildIdeInfo(Collection targets) 200 | throws IOException, InterruptedException, BazelNotFoundException { 201 | return BazelCommand.this.runBazelAndGetErrorLines(ConsoleType.WORKSPACE, workspaceRoot, 202 | ImmutableList.builder().add("build").addAll(aspectOptions).addAll(targets) 203 | .build(), 204 | // Strip out the artifact list, keeping the e4b-build.json files. 205 | t -> t.startsWith(">>>") ? (t.endsWith(".e4b-build.json") ? t.substring(3) : "") : null); 206 | } 207 | 208 | /** 209 | * Runs the analysis of the given list of targets using the IDE build information aspect and 210 | * returns a map of {@link IdeBuildInfo}-s (key is the label of the target) containing the 211 | * parsed form of the JSON file created by the aspect. 212 | * 213 | *

214 | * This method cache it results and won't recompute a previously computed version unless 215 | * {@link #markAsDirty()} has been called in between. 216 | * 217 | * @throws BazelNotFoundException 218 | */ 219 | public synchronized Map getIdeInfo(Collection targets) 220 | throws IOException, InterruptedException, BazelNotFoundException { 221 | String key = NEW_LINE_JOINER.join(targets); 222 | if (!buildInfoCache.containsKey(key)) { 223 | buildInfoCache.put(key, IdeBuildInfo.getInfo(buildIdeInfo(targets))); 224 | } 225 | return buildInfoCache.get(key); 226 | } 227 | 228 | /** 229 | * Clear the IDE build information cache. This cache is filled upon request and never emptied 230 | * unless we call that function. 231 | * 232 | *

233 | * This function totally clear the cache and that might leads to useless rebuilds when several 234 | * eclipse project points to the same workspace but that is a rare case. 235 | */ 236 | public synchronized void markAsDirty() { 237 | buildInfoCache.clear(); 238 | } 239 | 240 | /** 241 | * Build a list of targets in the current workspace. 242 | * 243 | * @throws BazelNotFoundException 244 | */ 245 | public synchronized int build(List targets, String... extraArgs) 246 | throws IOException, InterruptedException, BazelNotFoundException { 247 | return BazelCommand.this.runBazel(workspaceRoot, ImmutableList.builder().add("build") 248 | .addAll(buildOptions).add(extraArgs).add("--").addAll(targets).build()); 249 | } 250 | 251 | /** 252 | * Build a list of targets in the current workspace. 253 | * 254 | * @throws BazelNotFoundException 255 | */ 256 | public synchronized int build(List targets, List extraArgs) 257 | throws IOException, InterruptedException, BazelNotFoundException { 258 | return BazelCommand.this.runBazel(workspaceRoot, ImmutableList.builder().add("build") 259 | .addAll(buildOptions).addAll(extraArgs).add("--").addAll(targets).build()); 260 | } 261 | 262 | /** 263 | * Run test on a list of targets in the current workspace. 264 | * 265 | * @throws BazelNotFoundException 266 | */ 267 | public synchronized int tests(List targets, String... extraArgs) 268 | throws IOException, InterruptedException, BazelNotFoundException { 269 | return BazelCommand.this.runBazel(workspaceRoot, ImmutableList.builder().add("test") 270 | .addAll(buildOptions).add(extraArgs).add("--").addAll(targets).build()); 271 | } 272 | 273 | /** 274 | * Returns the workspace root corresponding to this object. 275 | */ 276 | public File getWorkspaceRoot() { 277 | return workspaceRoot; 278 | } 279 | 280 | /** 281 | * Returns the execution root of the current workspace. 282 | */ 283 | public File getExecRoot() { 284 | return execRoot; 285 | } 286 | 287 | /** 288 | * Gives a list of target completions for the given beginning string. The result is the list of 289 | * possible completion for a target pattern starting with string. 290 | * 291 | * @throws BazelNotFoundException 292 | */ 293 | public List complete(String string) 294 | throws IOException, InterruptedException, BazelNotFoundException { 295 | if (string.equals("/") || string.isEmpty()) { 296 | return ImmutableList.of("//"); 297 | } else if (string.contains(":")) { 298 | // complete targets using `bazel query` 299 | int idx = string.indexOf(':'); 300 | final String packageName = string.substring(0, idx); 301 | final String targetPrefix = string.substring(idx + 1); 302 | ImmutableList.Builder builder = ImmutableList.builder(); 303 | builder.addAll( 304 | BazelCommand.this.runBazelAndGetOuputLines(ConsoleType.NO_CONSOLE, workspaceRoot, 305 | ImmutableList.builder().add("query", packageName + ":*").build(), line -> { 306 | int i = line.indexOf(':'); 307 | String s = line.substring(i + 1); 308 | return !s.isEmpty() && s.startsWith(targetPrefix) ? (packageName + ":" + s) 309 | : null; 310 | })); 311 | if ("all".startsWith(targetPrefix)) { 312 | builder.add(packageName + ":all"); 313 | } 314 | if ("*".startsWith(targetPrefix)) { 315 | builder.add(packageName + ":*"); 316 | } 317 | return builder.build(); 318 | } else { 319 | // complete packages 320 | int lastSlash = string.lastIndexOf('/'); 321 | final String prefix = lastSlash > 0 ? string.substring(0, lastSlash + 1) : ""; 322 | final String suffix = lastSlash > 0 ? string.substring(lastSlash + 1) : string; 323 | final String directory = (prefix.isEmpty() || prefix.equals("//")) ? "" 324 | : prefix.substring(string.startsWith("//") ? 2 : 0, prefix.length() - 1); 325 | File file = directory.isEmpty() ? workspaceRoot : new File(workspaceRoot, directory); 326 | ImmutableList.Builder builder = ImmutableList.builder(); 327 | File[] files = file.listFiles((f) -> { 328 | // Only give directories whose name starts with suffix... 329 | return f.getName().startsWith(suffix) && f.isDirectory() 330 | // ...that does not start with '.'... 331 | && !f.getName().startsWith(".") 332 | // ...and is not a Bazel convenience link 333 | && (!file.equals(workspaceRoot) || !f.getName().startsWith("bazel-")); 334 | }); 335 | if (files != null) { 336 | for (File d : files) { 337 | builder.add(prefix + d.getName() + "/"); 338 | if (new File(d, "BUILD").exists()) { 339 | builder.add(prefix + d.getName() + ":"); 340 | } 341 | } 342 | } 343 | if ("...".startsWith(suffix)) { 344 | builder.add(prefix + "..."); 345 | } 346 | return builder.build(); 347 | } 348 | } 349 | } 350 | 351 | private File getWorkspaceRoot(File directory) 352 | throws IOException, InterruptedException, BazelNotFoundException { 353 | List result = runBazelAndGetOuputLines(ConsoleType.SYSTEM, directory, 354 | ImmutableList.of("info", "workspace")); 355 | if (result.size() > 0) { 356 | return new File(result.get(0)); 357 | } 358 | return null; 359 | } 360 | 361 | private List runBazelAndGetOuputLines(ConsoleType type, File directory, List args) 362 | throws IOException, InterruptedException, BazelNotFoundException { 363 | return runBazelAndGetOuputLines(type, directory, args, (t) -> t); 364 | } 365 | 366 | private synchronized List runBazelAndGetOuputLines(ConsoleType type, File directory, 367 | List args, Function selector) 368 | throws IOException, InterruptedException, BazelNotFoundException { 369 | Command command = Command.builder(consoleFactory) 370 | .setConsoleName(getConsoleName(type, directory)).setDirectory(directory) 371 | .addArguments(getBazelPath()).addArguments(args).setStdoutLineSelector(selector).build(); 372 | if (command.run() == 0) { 373 | return command.getSelectedOutputLines(); 374 | } 375 | return ImmutableList.of(); 376 | } 377 | 378 | private synchronized List runBazelAndGetErrorLines(ConsoleType type, File directory, 379 | List args, Function selector) 380 | throws IOException, InterruptedException, BazelNotFoundException { 381 | Command command = Command.builder(consoleFactory) 382 | .setConsoleName(getConsoleName(type, directory)).setDirectory(directory) 383 | .addArguments(getBazelPath()).addArguments(args).setStderrLineSelector(selector).build(); 384 | if (command.run() == 0) { 385 | return command.getSelectedErrorLines(); 386 | } 387 | return ImmutableList.of(); 388 | } 389 | 390 | private synchronized int runBazel(ConsoleType type, File directory, List args, 391 | OutputStream stdout, OutputStream stderr) 392 | throws IOException, InterruptedException, BazelNotFoundException { 393 | return Command.builder(consoleFactory).setConsoleName(getConsoleName(type, directory)) 394 | .setDirectory(directory).addArguments(getBazelPath()).addArguments(args) 395 | .setStandardOutput(stdout).setStandardError(stderr).build().run(); 396 | } 397 | 398 | private int runBazel(File directory, List args) 399 | throws IOException, InterruptedException, BazelNotFoundException { 400 | return runBazel(ConsoleType.WORKSPACE, directory, args, null, null); 401 | } 402 | 403 | private String getConsoleName(ConsoleType type, File directory) { 404 | switch (type) { 405 | case SYSTEM: 406 | return "Bazel [system]"; 407 | case WORKSPACE: 408 | return "Bazel [" + directory.toString() + "]"; 409 | case NO_CONSOLE: 410 | default: 411 | return null; 412 | } 413 | } 414 | 415 | } 416 | -------------------------------------------------------------------------------- /java/com/google/devtools/bazel/e4b/command/BazelNotFoundException.java: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Bazel Authors. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package com.google.devtools.bazel.e4b.command; 16 | 17 | public class BazelNotFoundException extends Exception { 18 | private static final long serialVersionUID = 1L; 19 | 20 | private BazelNotFoundException(String msg) { 21 | super(msg); 22 | } 23 | 24 | public static final class BazelNotSetException extends BazelNotFoundException { 25 | private static final long serialVersionUID = 1L; 26 | 27 | public BazelNotSetException() { 28 | super("Path to Bazel binary is not set, please set it " 29 | + "(Preferences... > Bazel Plugins Preferences)"); 30 | } 31 | } 32 | 33 | public static final class BazelNotExecutableException extends BazelNotFoundException { 34 | private static final long serialVersionUID = 1L; 35 | 36 | public BazelNotExecutableException() { 37 | super("Path to Bazel is wrong (does not point to a binary), please set it " 38 | + "(Preferences... > Bazel Plugins Preferences)"); 39 | } 40 | } 41 | 42 | public static final class BazelTooOldException extends BazelNotFoundException { 43 | private static final long serialVersionUID = 1L; 44 | 45 | public BazelTooOldException(String version) { 46 | super("Bazel version (" + version + ") is unsupported (too old or development version), " 47 | + "please update your Bazel binary."); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /java/com/google/devtools/bazel/e4b/command/Command.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Bazel Authors. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package com.google.devtools.bazel.e4b.command; 16 | 17 | import java.io.File; 18 | import java.io.IOException; 19 | import java.io.InputStream; 20 | import java.io.OutputStream; 21 | import java.util.function.Function; 22 | 23 | import com.google.common.base.Preconditions; 24 | import com.google.common.collect.ImmutableList; 25 | import com.google.devtools.bazel.e4b.command.CommandConsole.CommandConsoleFactory; 26 | 27 | /** 28 | * A utility class to spawn a command and parse its output. It allow to filter the output, 29 | * redirecting part of it to the console and getting the rest in a list of string. 30 | * 31 | *

32 | * This class can only be initialized using a builder created with the {@link #builder()} method. 33 | */ 34 | final public class Command { 35 | 36 | private final File directory; 37 | private final ImmutableList args; 38 | private final SelectOutputStream stdout; 39 | private final SelectOutputStream stderr; 40 | private boolean executed = false; 41 | 42 | private Command(CommandConsole console, File directory, ImmutableList args, 43 | Function stdoutSelector, Function stderrSelector, 44 | OutputStream stdout, OutputStream stderr) throws IOException { 45 | this.directory = directory; 46 | this.args = args; 47 | if (console != null) { 48 | if (stdout == null) { 49 | stdout = console.createOutputStream(); 50 | } 51 | if (stderr == null) { 52 | stderr = console.createErrorStream(); 53 | } 54 | } 55 | this.stderr = new SelectOutputStream(stderr, stderrSelector); 56 | this.stdout = new SelectOutputStream(stdout, stdoutSelector); 57 | } 58 | 59 | /** 60 | * Executes the command represented by this instance, and return the exit code of the command. 61 | * This method should not be called twice on the same object. 62 | */ 63 | public int run() throws IOException, InterruptedException { 64 | Preconditions.checkState(!executed); 65 | executed = true; 66 | ProcessBuilder builder = new ProcessBuilder(args); 67 | builder.directory(directory); 68 | builder.redirectOutput(ProcessBuilder.Redirect.PIPE); 69 | builder.redirectError(ProcessBuilder.Redirect.PIPE); 70 | Process process = builder.start(); 71 | Thread err = copyStream(process.getErrorStream(), stderr); 72 | // seriously? That's stdout, why is it called getInputStream??? 73 | Thread out = copyStream(process.getInputStream(), stdout); 74 | int r = process.waitFor(); 75 | if (err != null) { 76 | err.join(); 77 | } 78 | if (out != null) { 79 | out.join(); 80 | } 81 | synchronized (stderr) { 82 | stderr.close(); 83 | } 84 | synchronized (stdout) { 85 | stdout.close(); 86 | } 87 | return r; 88 | } 89 | 90 | private static class CopyStreamRunnable implements Runnable { 91 | private InputStream inputStream; 92 | private OutputStream outputStream; 93 | 94 | CopyStreamRunnable(InputStream inputStream, OutputStream outputStream) { 95 | this.inputStream = inputStream; 96 | this.outputStream = outputStream; 97 | } 98 | 99 | @Override 100 | public void run() { 101 | byte[] buffer = new byte[4096]; 102 | int read; 103 | try { 104 | while ((read = inputStream.read(buffer)) > 0) { 105 | synchronized (outputStream) { 106 | outputStream.write(buffer, 0, read); 107 | } 108 | } 109 | } catch (IOException ex) { 110 | // we simply terminate the thread on exceptions 111 | } 112 | } 113 | } 114 | 115 | // Launch a thread to copy all data from inputStream to outputStream 116 | private static Thread copyStream(InputStream inputStream, OutputStream outputStream) { 117 | if (outputStream != null) { 118 | Thread t = new Thread(new CopyStreamRunnable(inputStream, outputStream), "CopyStream"); 119 | t.start(); 120 | return t; 121 | } 122 | return null; 123 | } 124 | 125 | /** 126 | * Returns the list of lines selected from the standard error stream. Lines printed to the 127 | * standard error stream by the executed command can be filtered to be added to that list. 128 | * 129 | * @see {@link Builder#setStderrLineSelector(Function)} 130 | */ 131 | public ImmutableList getSelectedErrorLines() { 132 | return stderr.getLines(); 133 | } 134 | 135 | /** 136 | * Returns the list of lines selected from the standard output stream. Lines printed to the 137 | * standard output stream by the executed command can be filtered to be added to that list. 138 | * 139 | * @see {@link Builder#setStdoutLineSelector(Function)} 140 | */ 141 | public ImmutableList getSelectedOutputLines() { 142 | return stdout.getLines(); 143 | } 144 | 145 | /** 146 | * A builder class to generate a Command object. 147 | */ 148 | public static class Builder { 149 | 150 | private String consoleName = null; 151 | private File directory; 152 | private ImmutableList.Builder args = ImmutableList.builder(); 153 | private OutputStream stdout = null; 154 | private OutputStream stderr = null; 155 | private Function stdoutSelector; 156 | private Function stderrSelector; 157 | private final CommandConsoleFactory consoleFactory; 158 | 159 | private Builder(final CommandConsoleFactory consoleFactory) { 160 | // Default to the current working directory 161 | this.directory = new File(System.getProperty("user.dir")); 162 | this.consoleFactory = consoleFactory; 163 | } 164 | 165 | /** 166 | * Set the console name. 167 | * 168 | *

169 | * The console name is used to print result of the program. Only lines not filtered by 170 | * {@link #setStderrLineSelector(Function)} and {@link #setStdoutLineSelector(Function)} are 171 | * printed to the console. If {@link #setStandardError(OutputStream)} or 172 | * {@link #setStandardOutput(OutputStream)} have been used with a non null value, then they 173 | * intercept all output from being printed to the console. 174 | * 175 | *

176 | * If name is null, no output is written to any console. 177 | */ 178 | public Builder setConsoleName(String name) { 179 | this.consoleName = name; 180 | return this; 181 | } 182 | 183 | /** 184 | * Set the working directory for the program, it is set to the current working directory of the 185 | * current java process by default. 186 | */ 187 | public Builder setDirectory(File directory) { 188 | this.directory = directory; 189 | return this; 190 | } 191 | 192 | /** 193 | * Set an {@link OutputStream} to receive non selected lines from the standard output stream of 194 | * the program in lieu of the console. If a selector has been set with 195 | * {@link #setStdoutLineSelector(Function)}, only the lines not selected (for which the selector 196 | * returns null) will be printed to the {@link OutputStream}. 197 | */ 198 | public Builder setStandardOutput(OutputStream stdout) { 199 | this.stdout = stdout; 200 | return this; 201 | } 202 | 203 | /** 204 | * Set an {@link OutputStream} to receive non selected lines from the standard error stream of 205 | * the program in lieu of the console. If a selector has been set with 206 | * {@link #setStderrLineSelector(Function)}, only the lines not selected (for which the selector 207 | * returns null) will be printed to the {@link OutputStream}. 208 | */ 209 | public Builder setStandardError(OutputStream stderr) { 210 | this.stderr = stderr; 211 | return this; 212 | } 213 | 214 | /** 215 | * Add arguments to the command line. The first argument to be added to the builder is the 216 | * program name. 217 | */ 218 | public Builder addArguments(String... args) { 219 | this.args.add(args); 220 | return this; 221 | } 222 | 223 | /** 224 | * Add a list of arguments to the command line. The first argument to be added to the builder is 225 | * the program name. 226 | */ 227 | public Builder addArguments(Iterable args) { 228 | this.args.addAll(args); 229 | return this; 230 | } 231 | 232 | /** 233 | * Set a selector to accumulate lines that are selected from the standard output stream. 234 | * 235 | *

236 | * The selector is passed all lines that are printed to the standard output. It can either 237 | * returns null to say that the line should be passed to the console or to a non null value that 238 | * will be stored. All values that have been selected (for which the selector returns a non-null 239 | * value) will be stored in a list accessible through {@link Command#getSelectedOutputLines()}. 240 | * The selected lines will not be printed to the console. 241 | */ 242 | public Builder setStdoutLineSelector(Function selector) { 243 | this.stdoutSelector = selector; 244 | return this; 245 | } 246 | 247 | /** 248 | * Set a selector to accumulate lines that are selected from the standard error stream. 249 | * 250 | *

251 | * The selector is passed all lines that are printed to the standard error. It can either 252 | * returns null to say that the line should be passed to the console or to a non null value that 253 | * will be stored. All values that have been selected (for which the selector returns a non-null 254 | * value) will be stored in a list accessible through {@link Command#getSelectedErrorLines()}. 255 | * The selected lines will not be printed to the console. 256 | */ 257 | public Builder setStderrLineSelector(Function selector) { 258 | this.stderrSelector = selector; 259 | return this; 260 | } 261 | 262 | /** 263 | * Build a Command object. 264 | */ 265 | public Command build() throws IOException { 266 | Preconditions.checkNotNull(directory); 267 | ImmutableList args = this.args.build(); 268 | CommandConsole console = consoleName == null ? null 269 | : consoleFactory.get(consoleName, 270 | "Running " + String.join(" ", args) + " from " + directory.toString()); 271 | return new Command(console, directory, args, stdoutSelector, stderrSelector, 272 | stdout, stderr); 273 | } 274 | } 275 | 276 | /** 277 | * Returns a {@link Builder} object to use to create a {@link Command} object. 278 | */ 279 | public static Builder builder(CommandConsoleFactory consoleFactory) { 280 | return new Builder(consoleFactory); 281 | } 282 | } 283 | -------------------------------------------------------------------------------- /java/com/google/devtools/bazel/e4b/command/CommandConsole.java: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Bazel Authors. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package com.google.devtools.bazel.e4b.command; 16 | 17 | import java.io.IOException; 18 | import java.io.OutputStream; 19 | 20 | /** 21 | * An interface to describe an output console to stream output from a command. A command console 22 | * should be able to provide output stream for both normal ouput and error output. 23 | */ 24 | public interface CommandConsole { 25 | 26 | /** Create an {@link OuputStream} suitable to print standard output of a command. */ 27 | OutputStream createOutputStream(); 28 | 29 | /** Create an {@link OuputStream} suitable to print standard error output of a command. */ 30 | OutputStream createErrorStream(); 31 | 32 | /** A factory that returns a command console by name */ 33 | interface CommandConsoleFactory { 34 | /** 35 | * Returns a {@link CommandConsole} that has the name {@code name}. {@code title} will be 36 | * written at the beginning of the console. 37 | */ 38 | CommandConsole get(String name, String title) throws IOException; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /java/com/google/devtools/bazel/e4b/command/IdeBuildInfo.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Bazel Authors. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package com.google.devtools.bazel.e4b.command; 16 | 17 | import java.io.FileInputStream; 18 | import java.io.IOException; 19 | import java.util.List; 20 | import java.util.Objects; 21 | 22 | import org.json.JSONArray; 23 | import org.json.JSONObject; 24 | import org.json.JSONTokener; 25 | 26 | import com.google.common.annotations.VisibleForTesting; 27 | import com.google.common.base.Joiner; 28 | import com.google.common.collect.ImmutableList; 29 | import com.google.common.collect.ImmutableMap; 30 | 31 | /** 32 | * A parsed version of the JSON files returned by the application of the IDE build information 33 | * aspect. 34 | */ 35 | public final class IdeBuildInfo { 36 | 37 | private static final Joiner COMMA_JOINER = Joiner.on(","); 38 | 39 | /** 40 | * A structure containing the list of jar files generated by a target (interface, class and source 41 | * jars). 42 | */ 43 | public static final class Jars { 44 | private final String ijar; // interface_jar 45 | private final String jar; // jar 46 | private final String srcjar; // source_jar 47 | 48 | Jars(JSONObject obj) { 49 | this.ijar = obj.has("interface_jar") ? obj.getString("interface_jar") : null; 50 | this.jar = obj.getString("jar"); 51 | this.srcjar = obj.has("srcjar") ? obj.getString("srcjar") : null; 52 | } 53 | 54 | @Override 55 | public String toString() { 56 | StringBuffer builder = new StringBuffer(); 57 | builder.append("Jars(jar = ").append(jar); 58 | if (ijar != null) { 59 | builder.append(", ijar = ").append(ijar); 60 | } 61 | if (srcjar != null) { 62 | builder.append(", srcjar = ").append(srcjar); 63 | } 64 | return builder.append(")").toString(); 65 | } 66 | 67 | @Override 68 | public int hashCode() { 69 | return Objects.hash(ijar, jar, srcjar); 70 | } 71 | 72 | public String getInterfaceJar() { 73 | return ijar; 74 | } 75 | 76 | public String getJar() { 77 | return jar; 78 | } 79 | 80 | public String getSrcJar() { 81 | return srcjar; 82 | } 83 | } 84 | 85 | private final String location; // build_file_artifact_location 86 | private final ImmutableList deps; // dependencies 87 | private final String kind; // kind 88 | private final String label; // label 89 | 90 | private final ImmutableList generatedJars; // generated_jars 91 | private final ImmutableList jars; // jars 92 | private final ImmutableList sources; // sources 93 | 94 | /** 95 | * Construct an {@link IdeBuildInfo} object from a {@link JSONObject}. 96 | */ 97 | IdeBuildInfo(JSONObject object) { 98 | jars = jsonToJarArray(object.getJSONArray("jars")); 99 | generatedJars = jsonToJarArray(object.getJSONArray("generated_jars")); 100 | location = object.getString("build_file_artifact_location"); 101 | kind = object.getString("kind"); 102 | label = object.getString("label"); 103 | this.deps = jsonToStringArray(object.getJSONArray("dependencies")); 104 | this.sources = jsonToStringArray(object.getJSONArray("sources")); 105 | } 106 | 107 | @Override 108 | public String toString() { 109 | StringBuffer builder = new StringBuffer(); 110 | builder.append("IdeBuildInfo(\n"); 111 | builder.append(" label = ").append(label).append(",\n"); 112 | builder.append(" location = ").append(location).append(",\n"); 113 | builder.append(" kind = ").append(kind).append(",\n"); 114 | builder.append(" jars = [").append(COMMA_JOINER.join(jars)).append("],\n"); 115 | builder.append(" generatedJars = [").append(COMMA_JOINER.join(generatedJars)).append("],\n"); 116 | builder.append(" deps = [").append(COMMA_JOINER.join(deps)).append("],\n"); 117 | builder.append(" sources = [").append(COMMA_JOINER.join(sources)).append("])"); 118 | return builder.toString(); 119 | } 120 | 121 | /** 122 | * Constructs a map of label -> {@link IdeBuildInfo} from a list of files, parsing each files into 123 | * a {@link JSONObject} and then converting that {@link JSONObject} to an {@link IdeBuildInfo} 124 | * object. 125 | */ 126 | @VisibleForTesting 127 | public static ImmutableMap getInfo(List files) 128 | throws IOException, InterruptedException { 129 | ImmutableMap.Builder infos = ImmutableMap.builder(); 130 | for (String s : files) { 131 | if (!s.isEmpty()) { 132 | IdeBuildInfo buildInfo = 133 | new IdeBuildInfo(new JSONObject(new JSONTokener(new FileInputStream(s)))); 134 | infos.put(buildInfo.label, buildInfo); 135 | } 136 | } 137 | return infos.build(); 138 | } 139 | 140 | private ImmutableList jsonToJarArray(JSONArray array) { 141 | ImmutableList.Builder builder = ImmutableList.builder(); 142 | for (Object o : array) { 143 | builder.add(new Jars((JSONObject) o)); 144 | } 145 | return builder.build(); 146 | } 147 | 148 | private ImmutableList jsonToStringArray(JSONArray array) { 149 | ImmutableList.Builder builder = ImmutableList.builder(); 150 | for (Object o : array) { 151 | builder.add(o.toString()); 152 | } 153 | return builder.build(); 154 | } 155 | 156 | /** 157 | * Location of the target (build file). 158 | */ 159 | public String getLocation() { 160 | return location; 161 | } 162 | 163 | /** 164 | * List of dependencies of the target. 165 | */ 166 | public List getDeps() { 167 | return deps; 168 | } 169 | 170 | /** 171 | * Kind of the target (e.g., java_test or java_binary). 172 | */ 173 | public String getKind() { 174 | return kind; 175 | } 176 | 177 | /** 178 | * Label of the target. 179 | */ 180 | public String getLabel() { 181 | return label; 182 | } 183 | 184 | /** 185 | * List of jars generated by annotations processors when building this target. 186 | */ 187 | public List getGeneratedJars() { 188 | return generatedJars; 189 | } 190 | 191 | /** 192 | * List of jars generated by building this target. 193 | */ 194 | public List getJars() { 195 | return jars; 196 | } 197 | 198 | /** 199 | * List of sources consumed by this target. 200 | */ 201 | public List getSources() { 202 | return sources; 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /java/com/google/devtools/bazel/e4b/command/SelectOutputStream.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Bazel Authors. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package com.google.devtools.bazel.e4b.command; 16 | 17 | import java.io.ByteArrayOutputStream; 18 | import java.io.IOException; 19 | import java.io.OutputStream; 20 | import java.io.UnsupportedEncodingException; 21 | import java.nio.charset.StandardCharsets; 22 | import java.util.LinkedList; 23 | import java.util.List; 24 | import java.util.function.Function; 25 | 26 | import com.google.common.base.Preconditions; 27 | import com.google.common.collect.ImmutableList; 28 | 29 | /** 30 | * A wrapper output stream to output part of the result to a given output and extracting the other 31 | * part with a selector function. The other part is return as a list of string. 32 | */ 33 | public class SelectOutputStream extends OutputStream { 34 | 35 | private OutputStream output; 36 | private Function selector; 37 | private boolean closed = false; 38 | private List lines = new LinkedList<>(); 39 | private ByteArrayOutputStream stream = new ByteArrayOutputStream(); 40 | 41 | /** 42 | * Create a SelectOutputStream. output is the output stream where non-selected lines 43 | * will be printed. selector is a function that will be called on each line. If 44 | * selector returns a non null value, then the resulting value will be stored in a 45 | * lines buffer that can be consumed with the {@link #getLines()} method. If selector 46 | * returns a null value, the corresponding line will be send to output. 47 | * 48 | *

49 | * Both output and selector can be null. If output is null, 50 | * unselected lines will be discarded. If selector is null, all lines will be 51 | * considered as unselected. 52 | */ 53 | public SelectOutputStream(OutputStream output, Function selector) { 54 | super(); 55 | this.output = output; 56 | this.selector = selector; 57 | } 58 | 59 | @Override 60 | public void write(int b) throws IOException { 61 | Preconditions.checkState(!closed, "Attempted to write on a closed stream"); 62 | byte b0 = (byte) b; 63 | if (b0 == '\n') { 64 | select(true); 65 | } else { 66 | stream.write(b); 67 | } 68 | } 69 | 70 | private void select(boolean appendNewLine) throws UnsupportedEncodingException, IOException { 71 | String line = null; 72 | if (selector != null) { 73 | line = selector.apply(stream.toString(StandardCharsets.UTF_8.name())); 74 | } 75 | 76 | if (line != null) { 77 | lines.add(line); 78 | } else if (output != null) { 79 | if (appendNewLine) { 80 | stream.write('\n'); 81 | } 82 | output.write(stream.toByteArray()); 83 | } 84 | stream.reset(); 85 | } 86 | 87 | @Override 88 | public void close() throws IOException { 89 | Preconditions.checkState(!closed); 90 | super.close(); 91 | select(false); 92 | closed = true; 93 | } 94 | 95 | /** 96 | * Returns the list of selected lines. 97 | */ 98 | ImmutableList getLines() { 99 | return ImmutableList.copyOf(lines); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /java/com/google/devtools/bazel/e4b/preferences/BazelPreferenceInitializer.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Bazel Authors. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package com.google.devtools.bazel.e4b.preferences; 16 | 17 | import java.io.File; 18 | 19 | import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer; 20 | import org.eclipse.jface.preference.IPreferenceStore; 21 | 22 | import com.google.devtools.bazel.e4b.Activator; 23 | 24 | /** 25 | * Initialize the preferences of Bazel. The only preferences stored for now is the path to the Bazel 26 | * binary, which is expected to be in /usr/local/bin/bazel by default. 27 | */ 28 | public class BazelPreferenceInitializer extends AbstractPreferenceInitializer { 29 | 30 | private static String which(String name, String def) { 31 | for (String dirname : System.getenv("PATH").split(File.pathSeparator)) { 32 | File file = new File(dirname, name); 33 | if (file.isFile() && file.canExecute()) { 34 | return file.getAbsolutePath(); 35 | } 36 | } 37 | return def; 38 | } 39 | 40 | @Override 41 | public void initializeDefaultPreferences() { 42 | IPreferenceStore store = Activator.getDefault().getPreferenceStore(); 43 | store.setDefault("BAZEL_PATH", which("bazel", "/usr/local/bin/bazel")); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /java/com/google/devtools/bazel/e4b/preferences/BazelPreferencePage.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Bazel Authors. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package com.google.devtools.bazel.e4b.preferences; 16 | 17 | import com.google.devtools.bazel.e4b.Activator; 18 | import com.google.devtools.bazel.e4b.command.BazelNotFoundException; 19 | import org.eclipse.jface.preference.FieldEditorPreferencePage; 20 | import org.eclipse.jface.preference.FileFieldEditor; 21 | import org.eclipse.swt.widgets.Composite; 22 | import org.eclipse.ui.IWorkbench; 23 | import org.eclipse.ui.IWorkbenchPreferencePage; 24 | 25 | /** 26 | * Page to configure the e4b plugin. The only configuration parameter is the path to the Bazel 27 | * binary so this page provide a file field to specify it. 28 | */ 29 | public class BazelPreferencePage extends FieldEditorPreferencePage 30 | implements 31 | IWorkbenchPreferencePage { 32 | 33 | private static class BazelBinaryFieldEditor extends FileFieldEditor { 34 | BazelBinaryFieldEditor(Composite parent) { 35 | super("BAZEL_PATH", "Path to the &Bazel binary:", true, parent); 36 | setValidateStrategy(VALIDATE_ON_KEY_STROKE); 37 | } 38 | 39 | @Override 40 | protected boolean doCheckState() { 41 | try { 42 | Activator.getDefault().getCommand().checkVersion(getTextControl().getText()); 43 | return true; 44 | } catch (BazelNotFoundException e) { 45 | setErrorMessage(e.getMessage()); 46 | return false; 47 | } 48 | } 49 | } 50 | 51 | public BazelPreferencePage() { 52 | super(GRID); 53 | } 54 | 55 | public void createFieldEditors() { 56 | addField(new BazelBinaryFieldEditor(getFieldEditorParent())); 57 | } 58 | 59 | @Override 60 | public void init(IWorkbench workbench) { 61 | setPreferenceStore(Activator.getDefault().getPreferenceStore()); 62 | setDescription("Bazel plugin settings"); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /java/com/google/devtools/bazel/e4b/projectviews/BUILD: -------------------------------------------------------------------------------- 1 | java_library( 2 | name = "projectviews", 3 | srcs = glob(["*.java"]), 4 | visibility = [ 5 | "//:__pkg__", 6 | "//javatests/com/google/devtools/bazel/e4b/projectviews:__pkg__", 7 | ], 8 | deps = [ 9 | "@com_google_guava//jar", 10 | ], 11 | ) 12 | -------------------------------------------------------------------------------- /java/com/google/devtools/bazel/e4b/projectviews/Builder.java: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Bazel Authors. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package com.google.devtools.bazel.e4b.projectviews; 16 | 17 | import java.io.BufferedReader; 18 | import java.io.File; 19 | import java.io.IOException; 20 | import java.io.InputStreamReader; 21 | import java.net.URL; 22 | import java.nio.file.Files; 23 | 24 | import com.google.common.base.Preconditions; 25 | import com.google.common.collect.ImmutableList; 26 | 27 | /** A class to create a {@link ProjectView} */ 28 | public class Builder { 29 | private ImmutableList.Builder buildFlags = ImmutableList.builder(); 30 | private ImmutableList.Builder directories = ImmutableList.builder(); 31 | private ImmutableList.Builder targets = ImmutableList.builder(); 32 | private int javaLanguageLevel = 0; // <= 0 means take it from java_toolchain. 33 | 34 | Builder() {} 35 | 36 | public Builder addBuildFlag(String... flag) { 37 | buildFlags.add(flag); 38 | return this; 39 | } 40 | 41 | public Builder addDirectory(String... dir) { 42 | directories.add(dir); 43 | return this; 44 | } 45 | 46 | public Builder addTarget(String... target) { 47 | targets.add(target); 48 | return this; 49 | } 50 | 51 | public Builder setJavaLanguageLevel(int level) { 52 | Preconditions.checkArgument(level > 0, "Can only set java language level to a value > 0"); 53 | Preconditions.checkArgument(javaLanguageLevel == 0, "Java language level was already set"); 54 | javaLanguageLevel = level; 55 | return this; 56 | } 57 | 58 | // State object using while parsing a stream 59 | private String currentSection = null; 60 | 61 | /** 62 | * Parse the project view given in view, following also imports. 63 | * 64 | * @throws IOException 65 | * @throws ProjectViewParseException 66 | */ 67 | public Builder parseView(File view) throws IOException, ProjectViewParseException { 68 | int linenb = 0; 69 | for (String line : Files.readAllLines(view.toPath())) { 70 | linenb++; 71 | parseLine(view.getPath(), view.getParentFile(), line, linenb); 72 | } 73 | currentSection = null; 74 | return this; 75 | } 76 | 77 | /** 78 | * Parse the project view at the given {@code url}. 79 | * 80 | * @throws IOException 81 | * @throws ProjectViewParseException 82 | */ 83 | public Builder parseView(URL url) throws IOException, ProjectViewParseException { 84 | int linenb = 0; 85 | try (BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()))) { 86 | for (String line : reader.lines().toArray(String[]::new)) { 87 | linenb++; 88 | parseLine(url.toString(), null, line, linenb); 89 | } 90 | } 91 | currentSection = null; 92 | return this; 93 | } 94 | 95 | private void parseLine(String fileName, File parentFile, String line, int linenb) 96 | throws ProjectViewParseException, IOException { 97 | if (line.isEmpty()) { 98 | currentSection = null; 99 | } else if (line.startsWith(" ")) { 100 | if (currentSection == null) { 101 | throw new ProjectViewParseException( 102 | "Line " + linenb + " of project view " + fileName + " is not in a section"); 103 | } 104 | if (currentSection.equals("directories")) { 105 | directories.add(line.substring(2)); 106 | } else if (currentSection.equals("targets")) { 107 | targets.add(line.substring(2)); 108 | } else if (currentSection.equals("build_flags")) { 109 | buildFlags.add(line.substring(2)); 110 | } // else ignoring other sections 111 | } else if (line.startsWith("import ")) { 112 | // imports 113 | String path = line.substring(7); 114 | if (path.startsWith("/")) { 115 | parseView(new File(path)); 116 | } else { 117 | parseView(new File(parentFile, path)); 118 | } 119 | } else if (line.contains(":")) { 120 | // section declaration 121 | line = line.trim(); 122 | if (line.endsWith(":")) { 123 | currentSection = line.substring(0, line.length() - 1); 124 | if (currentSection.equals("java_language_level")) { 125 | throw new ProjectViewParseException("Line " + linenb + " of project view " 126 | + fileName + ": java_language_level cannot be a section name"); 127 | } 128 | } else { 129 | int colonIndex = line.indexOf(':'); 130 | String label = line.substring(0, colonIndex).trim(); 131 | String value = line.substring(colonIndex + 1).trim(); 132 | if (label.equals("directories") || label.equals("import") || label.equals("targets") 133 | || label.equals("build_flags")) { 134 | throw new ProjectViewParseException("Line " + linenb + " of project view " 135 | + fileName + ": " + label + " cannot be a label name"); 136 | } 137 | if (label.equals("java_language_level")) { 138 | if (!value.matches("^[0-9]+$")) { 139 | throw new ProjectViewParseException("Line " + linenb + " of project view " 140 | + fileName + ": java_language_level should be an integer."); 141 | } 142 | javaLanguageLevel = Integer.parseInt(value); 143 | } 144 | } 145 | } else if (!line.trim().startsWith("#")) { 146 | throw new ProjectViewParseException( 147 | "Project view " + fileName + " contains a syntax error at line " + linenb); 148 | } 149 | } 150 | 151 | public ProjectView build() { 152 | return new ProjectViewImpl(directories.build(), targets.build(), javaLanguageLevel, 153 | buildFlags.build()); 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /java/com/google/devtools/bazel/e4b/projectviews/ProjectView.java: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Bazel Authors. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package com.google.devtools.bazel.e4b.projectviews; 16 | 17 | import java.util.List; 18 | 19 | /** 20 | * This is an interface to defined a project view from the IntelliJ plugin for Bazel 21 | * (https://ij.bazel.build) so that project view can be shared between IntelliJ and Eclipse users. 22 | * 23 | *

24 | * See http://ij.bazel.build/docs/project-views.html for the specification of the project view. This 25 | * project view support only a subset relevant for the Eclipse plugin. 26 | */ 27 | public interface ProjectView { 28 | /** 29 | * List of directories defined in the {@code directories} section of the project view. These are 30 | * the directories to include as source directories. 31 | */ 32 | public List getDirectories(); 33 | 34 | /** 35 | * List of targets to build defined in the {@code targets} section of the project view. 36 | */ 37 | public List getTargets(); 38 | 39 | /** 40 | * Return a number (e.g. 7) giving the java version that the IDE should support (section 41 | * {@code java_language_level} of the project view). 42 | */ 43 | public int getJavaLanguageLevel(); 44 | 45 | /** 46 | * List of build flags to pass to Bazel defined in the {@code build_flags} section of the project 47 | * view. 48 | */ 49 | public List getBuildFlags(); 50 | 51 | /** Returns a builder to construct an project view object. */ 52 | public static Builder builder() { 53 | return new Builder(); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /java/com/google/devtools/bazel/e4b/projectviews/ProjectViewImpl.java: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Bazel Authors. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package com.google.devtools.bazel.e4b.projectviews; 16 | 17 | import com.google.common.collect.ImmutableList; 18 | import java.util.List; 19 | 20 | /** Implementation of the ProjectView object */ 21 | final class ProjectViewImpl implements ProjectView { 22 | 23 | private final ImmutableList directories; 24 | private final ImmutableList targets; 25 | private final int javaLanguageLevel; 26 | private final ImmutableList buildFlags; 27 | 28 | public ProjectViewImpl(List directories, List targets, int javaLanguageLevel, 29 | List buildFlags) { 30 | this.directories = ImmutableList.copyOf(directories); 31 | this.targets = ImmutableList.copyOf(targets); 32 | this.javaLanguageLevel = javaLanguageLevel; 33 | this.buildFlags = ImmutableList.copyOf(buildFlags); 34 | } 35 | 36 | @Override 37 | public List getDirectories() { 38 | return directories; 39 | } 40 | 41 | @Override 42 | public List getTargets() { 43 | return targets; 44 | } 45 | 46 | @Override 47 | public int getJavaLanguageLevel() { 48 | return javaLanguageLevel; 49 | } 50 | 51 | @Override 52 | public List getBuildFlags() { 53 | return buildFlags; 54 | } 55 | 56 | @Override 57 | public String toString() { 58 | StringBuffer result = new StringBuffer(); 59 | addList(result, directories, "directories"); 60 | addList(result, targets, "targets"); 61 | if (javaLanguageLevel > 0) { 62 | result.append("java_language_level: ").append(javaLanguageLevel).append("\n\n"); 63 | } 64 | addList(result, buildFlags, "build_flags"); 65 | return result.toString(); 66 | } 67 | 68 | private static void addList(StringBuffer result, List list, String header) { 69 | if (!list.isEmpty()) { 70 | result.append(header).append(":\n"); 71 | for (String el : list) { 72 | result.append(" ").append(el).append("\n"); 73 | } 74 | result.append("\n"); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /java/com/google/devtools/bazel/e4b/projectviews/ProjectViewParseException.java: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Bazel Authors. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package com.google.devtools.bazel.e4b.projectviews; 16 | 17 | /** 18 | * An exception thrown when failing to parse a project view. 19 | */ 20 | public class ProjectViewParseException extends Exception { 21 | private static final long serialVersionUID = 1L; 22 | 23 | ProjectViewParseException(String msg) { 24 | super(msg); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /java/com/google/devtools/bazel/e4b/wizard/BazelTargetCompletionContentProposalProvider.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Bazel Authors. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package com.google.devtools.bazel.e4b.wizard; 16 | 17 | import java.io.IOException; 18 | import java.util.List; 19 | 20 | import org.eclipse.jface.fieldassist.ContentProposal; 21 | import org.eclipse.jface.fieldassist.IContentProposal; 22 | import org.eclipse.jface.fieldassist.IContentProposalProvider; 23 | 24 | import com.google.devtools.bazel.e4b.Activator; 25 | import com.google.devtools.bazel.e4b.command.BazelCommand.BazelInstance; 26 | import com.google.devtools.bazel.e4b.command.BazelNotFoundException; 27 | 28 | /** 29 | * A {@link IContentProposalProvider} to provide completion for Bazel. Use the 30 | * {@link #setBazelInstance(BazelInstance)} method to provide with the {@link BazelInstance} 31 | * interface to Bazel. 32 | */ 33 | public class BazelTargetCompletionContentProposalProvider implements IContentProposalProvider { 34 | 35 | private BazelInstance bazel = null; 36 | 37 | @Override 38 | public IContentProposal[] getProposals(String contents, int position) { 39 | if (bazel == null) { 40 | return null; 41 | } 42 | try { 43 | List completions = bazel.complete(contents.substring(0, position)); 44 | if (completions != null) { 45 | IContentProposal[] result = new IContentProposal[completions.size()]; 46 | int i = 0; 47 | for (String s : completions) { 48 | result[i] = new ContentProposal(s); 49 | i++; 50 | } 51 | return result; 52 | } 53 | } catch (IOException e) { 54 | Activator.error("Failed to run Bazel to get completion information", e); 55 | } catch (InterruptedException e) { 56 | Activator.error("Bazel was interrupted", e); 57 | } catch (BazelNotFoundException e) { 58 | Activator.error("Bazel not found: " + e.getMessage()); 59 | } 60 | return null; 61 | } 62 | 63 | /** 64 | * Set the {@link BazelInstance} to use to query for completion targets. 65 | */ 66 | public void setBazelInstance(BazelInstance bazel) { 67 | this.bazel = bazel; 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /java/com/google/devtools/bazel/e4b/wizard/BazelWizard.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Bazel Authors. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package com.google.devtools.bazel.e4b.wizard; 16 | 17 | import org.eclipse.jface.viewers.IStructuredSelection; 18 | import org.eclipse.jface.wizard.Wizard; 19 | import org.eclipse.ui.IWorkbench; 20 | import org.eclipse.ui.IWorkbenchWizard; 21 | import org.eclipse.ui.dialogs.WizardNewProjectCreationPage; 22 | 23 | import com.google.devtools.bazel.e4b.BazelProjectSupport; 24 | 25 | /** 26 | * A wizard to create a Bazel import project. 27 | */ 28 | public class BazelWizard extends Wizard implements IWorkbenchWizard { 29 | 30 | protected WorkspaceWizardPage page2; 31 | protected WizardNewProjectCreationPage page1; 32 | 33 | @Override 34 | public void addPages() { 35 | page1 = new WizardNewProjectCreationPage("Creating a new Bazel import project"); 36 | page2 = new WorkspaceWizardPage(); 37 | addPage(page1); 38 | addPage(page2); 39 | } 40 | 41 | @Override 42 | public String getWindowTitle() { 43 | return "Import Bazel Workspace..."; 44 | } 45 | 46 | @Override 47 | public boolean performFinish() { 48 | BazelProjectSupport.createProject(page1.getProjectName(), page1.getLocationURI(), 49 | page2.getWorkspaceRoot(), page2.getDirectories(), page2.getTargets(), 50 | page2.getJavaLanguageVersion()); 51 | return true; 52 | } 53 | 54 | @Override 55 | public void init(IWorkbench workbench, IStructuredSelection selection) {} 56 | 57 | } 58 | -------------------------------------------------------------------------------- /java/com/google/devtools/bazel/e4b/wizard/DirectoryTreeContentProvider.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Bazel Authors. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package com.google.devtools.bazel.e4b.wizard; 16 | 17 | import java.io.File; 18 | 19 | import org.eclipse.jface.viewers.CheckboxTreeViewer; 20 | import org.eclipse.jface.viewers.ILabelProvider; 21 | import org.eclipse.jface.viewers.ILabelProviderListener; 22 | import org.eclipse.jface.viewers.ITreeContentProvider; 23 | import org.eclipse.jface.viewers.Viewer; 24 | import org.eclipse.swt.SWT; 25 | import org.eclipse.swt.graphics.Image; 26 | import org.eclipse.swt.widgets.Composite; 27 | 28 | import com.google.common.collect.ImmutableList; 29 | 30 | /** 31 | * A tree content provider that enable selecting a list of sub-directories of a directory root (the 32 | * Bazel workspace root). 33 | */ 34 | public class DirectoryTreeContentProvider implements ITreeContentProvider { 35 | 36 | private File root; 37 | 38 | public DirectoryTreeContentProvider(File root) { 39 | this.root = root; 40 | } 41 | 42 | @Override 43 | public void dispose() {} 44 | 45 | @Override 46 | public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {} 47 | 48 | @Override 49 | public Object[] getElements(Object inputElement) { 50 | if (root == null) { 51 | return new Object[] {}; 52 | } 53 | // We only have one root 54 | return new Object[] {root}; 55 | } 56 | 57 | @Override 58 | public Object[] getChildren(Object parentElement) { 59 | return ((File) parentElement).listFiles((f) -> (f.isDirectory() && !f.getName().startsWith(".") 60 | && !f.getName().startsWith("bazel-"))); 61 | } 62 | 63 | @Override 64 | public Object getParent(Object element) { 65 | File file = (File) element; 66 | if (file.equals(root)) { 67 | return null; 68 | } 69 | return file.getParentFile(); 70 | } 71 | 72 | @Override 73 | public boolean hasChildren(Object element) { 74 | Object[] childrens = getChildren(element); 75 | return (childrens != null) && (childrens.length > 0); 76 | } 77 | 78 | public File getRoot() { 79 | return root; 80 | } 81 | 82 | public void setRoot(File root) { 83 | this.root = root; 84 | } 85 | 86 | 87 | /** 88 | * Create a tree view that use a FileTreeContentProvider for its content. The created tree view 89 | * can be used to select branches of a directory tree. 90 | * 91 | * @param container The parent composite for the created tree view. 92 | * @return A checkbox tree view 93 | */ 94 | static CheckboxTreeViewer createTreeView(Composite container) { 95 | final CheckboxTreeViewer tv = new CheckboxTreeViewer(container, SWT.BORDER); 96 | tv.setContentProvider(new DirectoryTreeContentProvider(null)); 97 | tv.setLabelProvider(new ILabelProvider() { 98 | 99 | @Override 100 | public void removeListener(ILabelProviderListener listener) { 101 | // we do not have event notifying listeners, ignore. 102 | } 103 | 104 | @Override 105 | public boolean isLabelProperty(Object element, String property) { 106 | return false; 107 | } 108 | 109 | 110 | @Override 111 | public void dispose() {} 112 | 113 | @Override 114 | public void addListener(ILabelProviderListener listener) { 115 | // we do not have event notifying listeners, ignore. 116 | } 117 | 118 | @Override 119 | public Image getImage(Object element) { 120 | return null; 121 | } 122 | 123 | @Override 124 | public String getText(Object element) { 125 | return ((File) element).getName(); 126 | } 127 | }); 128 | tv.setInput("root"); // pass a non-null that will be ignored 129 | 130 | tv.addCheckStateListener(event -> setChecked(tv, event.getElement())); 131 | 132 | return tv; 133 | } 134 | 135 | private static void setChecked(final CheckboxTreeViewer tv, Object element) { 136 | // When user checks a checkbox in the tree, check all its children and mark parent as greyed 137 | // When a user uncheck a checkbox, mark the subtree as unchecked and ungrayed and if unique 138 | // sibling parent as grayed. 139 | DirectoryTreeContentProvider provider = (DirectoryTreeContentProvider) tv.getContentProvider(); 140 | 141 | boolean isChecked = tv.getChecked(element); 142 | if (tv.getGrayed(element)) { 143 | isChecked = !isChecked; 144 | } 145 | tv.setChecked(element, isChecked); 146 | tv.setGrayed(element, false); 147 | if (isChecked) { 148 | tv.setSubtreeChecked(element, true); 149 | } else { 150 | tv.setSubtreeChecked(element, false); 151 | } 152 | setGrayed(tv, provider.getParent(element)); 153 | } 154 | 155 | private static void setGrayed(CheckboxTreeViewer tv, Object element) { 156 | if (element == null) { 157 | return; 158 | } 159 | DirectoryTreeContentProvider provider = (DirectoryTreeContentProvider) tv.getContentProvider(); 160 | boolean checked = tv.getChecked(element); 161 | boolean grayed = false; 162 | for (Object object : provider.getChildren(element)) { 163 | grayed = grayed || tv.getGrayed(object) || tv.getChecked(object); 164 | checked = checked && tv.getChecked(object) && !tv.getGrayed(element); 165 | } 166 | if (checked) { 167 | tv.setChecked(element, true); 168 | tv.setGrayed(element, false); 169 | } else if (grayed) { 170 | tv.setGrayChecked(element, true); 171 | } else { 172 | tv.setChecked(element, false); 173 | tv.setGrayed(element, false); 174 | } 175 | setGrayed(tv, provider.getParent(element)); 176 | } 177 | 178 | /** 179 | * Set the root of the directory tree view and refresh the view if appropriate 180 | */ 181 | static void setFileTreeRoot(CheckboxTreeViewer tv, File root) { 182 | DirectoryTreeContentProvider provider = (DirectoryTreeContentProvider) tv.getContentProvider(); 183 | if ((root == null && provider.getRoot() != null) || !root.equals(provider.getRoot())) { 184 | provider.setRoot(root); 185 | tv.refresh(); 186 | } 187 | } 188 | 189 | /** 190 | * Returns the list of path selected in tv. It returns the list of checked path 191 | * without the children of the checked path. Each path is returned as a string giving the relative 192 | * path from the root of the tree. 193 | */ 194 | static ImmutableList getSelectPathsRelativeToRoot(CheckboxTreeViewer tv) { 195 | DirectoryTreeContentProvider provider = (DirectoryTreeContentProvider) tv.getContentProvider(); 196 | String root = provider.root.getAbsolutePath(); 197 | ImmutableList.Builder builder = ImmutableList.builder(); 198 | for (Object element : tv.getCheckedElements()) { 199 | if (!tv.getGrayed(element)) { 200 | Object parent = provider.getParent(element); 201 | if (parent == null || tv.getGrayed(parent)) { 202 | // Only add this element if its parent is not selected (so it's the root). 203 | String path = ((File) element).getAbsolutePath(); 204 | // Strip root from path 205 | if (path.startsWith(root)) { 206 | path = path.substring(root.length()); 207 | if (path.startsWith("/")) { 208 | path = path.substring(1); 209 | } 210 | builder.add(path); 211 | } 212 | } 213 | } 214 | } 215 | return builder.build(); 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /java/com/google/devtools/bazel/e4b/wizard/WorkspaceWizardPage.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Bazel Authors. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package com.google.devtools.bazel.e4b.wizard; 16 | 17 | import java.io.File; 18 | import java.io.IOException; 19 | import java.util.function.Consumer; 20 | 21 | import org.eclipse.jface.bindings.keys.KeyStroke; 22 | import org.eclipse.jface.dialogs.MessageDialog; 23 | import org.eclipse.jface.fieldassist.ContentProposalAdapter; 24 | import org.eclipse.jface.fieldassist.TextContentAdapter; 25 | import org.eclipse.jface.viewers.CheckboxTreeViewer; 26 | import org.eclipse.jface.wizard.WizardPage; 27 | import org.eclipse.swt.SWT; 28 | import org.eclipse.swt.events.KeyAdapter; 29 | import org.eclipse.swt.events.KeyEvent; 30 | import org.eclipse.swt.events.SelectionAdapter; 31 | import org.eclipse.swt.events.SelectionEvent; 32 | import org.eclipse.swt.events.SelectionListener; 33 | import org.eclipse.swt.layout.GridData; 34 | import org.eclipse.swt.layout.GridLayout; 35 | import org.eclipse.swt.widgets.Button; 36 | import org.eclipse.swt.widgets.Composite; 37 | import org.eclipse.swt.widgets.Control; 38 | import org.eclipse.swt.widgets.DirectoryDialog; 39 | import org.eclipse.swt.widgets.Label; 40 | import org.eclipse.swt.widgets.List; 41 | import org.eclipse.swt.widgets.Text; 42 | 43 | import com.google.common.collect.ImmutableList; 44 | import com.google.devtools.bazel.e4b.Activator; 45 | import com.google.devtools.bazel.e4b.command.BazelNotFoundException; 46 | 47 | /** 48 | * This is a quick wizard page that ask the user for the various targets and source path he wants to 49 | * include. 50 | */ 51 | public class WorkspaceWizardPage extends WizardPage { 52 | 53 | private Label workspaceRoot; 54 | private List targets; 55 | private Text target; 56 | private Button addTargetButton; 57 | private Button removeTargetButton; 58 | private CheckboxTreeViewer directories; 59 | private Composite container; 60 | private Button workspaceRootButton; 61 | private DirectoryDialog dialog; 62 | private BazelTargetCompletionContentProposalProvider completionProvider; 63 | 64 | protected WorkspaceWizardPage() { 65 | super("Import Bazel project"); 66 | setTitle("Import Bazel project"); 67 | } 68 | 69 | /** 70 | * Returns the list of targets selected by the user. 71 | */ 72 | ImmutableList getTargets() { 73 | return ImmutableList.copyOf(targets.getItems()); 74 | } 75 | 76 | /** 77 | * Returns the list of directories selected by the user. 78 | */ 79 | ImmutableList getDirectories() { 80 | return DirectoryTreeContentProvider.getSelectPathsRelativeToRoot(directories); 81 | } 82 | 83 | /** 84 | * Returns the workspace root selected by the user. 85 | */ 86 | public String getWorkspaceRoot() { 87 | return workspaceRoot.getText(); 88 | } 89 | 90 | /** 91 | * Returns the language version for the new project. 92 | */ 93 | int getJavaLanguageVersion() { 94 | return 8; 95 | } 96 | 97 | 98 | @Override 99 | public void createControl(Composite parent) { 100 | container = new Composite(parent, SWT.NONE); 101 | GridLayout layout = new GridLayout(); 102 | layout.numColumns = 3; 103 | container.setLayout(layout); 104 | 105 | createWorkspaceSelectionControls(); 106 | 107 | addLabel("Directories: ", 1, 5); 108 | directories = DirectoryTreeContentProvider.createTreeView(container); 109 | setControlGridData(directories.getTree(), 2, 5, true); 110 | directories.addCheckStateListener(e -> updateControls()); 111 | 112 | new Label(container, SWT.NONE).setText("Targets:"); 113 | createTargetTextField(); 114 | setControlGridData(target, 1, 1, false); 115 | addTargetButton = createButton("+", e -> addTarget()); 116 | 117 | targets = new List(container, SWT.SINGLE | SWT.BORDER); 118 | setControlGridData(targets, 2, 5, true); 119 | removeTargetButton = createButton("-", e -> deleteTarget()); 120 | targets.addSelectionListener(createSelectionListener( 121 | e -> removeTargetButton.setEnabled(targets.getSelectionCount() > 0))); 122 | 123 | setControl(container); 124 | updateControls(); 125 | } 126 | 127 | // A simple helper to use lambdas to create a SelectionListener that does the same action on all 128 | // cases. 129 | private static SelectionListener createSelectionListener(Consumer f) { 130 | return new SelectionListener() { 131 | @Override 132 | public void widgetSelected(SelectionEvent e) { 133 | f.accept(e); 134 | 135 | } 136 | 137 | @Override 138 | public void widgetDefaultSelected(SelectionEvent e) { 139 | f.accept(e); 140 | } 141 | }; 142 | } 143 | 144 | private Button createButton(String text, Consumer handler) { 145 | Button result = new Button(container, SWT.DEFAULT); 146 | result.setText(text); 147 | result.addSelectionListener(createSelectionListener(handler)); 148 | result.setEnabled(false); 149 | return result; 150 | } 151 | 152 | private void createWorkspaceSelectionControls() { 153 | Label label = new Label(container, SWT.NONE); 154 | label.setText("Workpsace root: "); 155 | 156 | workspaceRoot = new Label(container, SWT.BORDER); 157 | workspaceRoot.setText(""); 158 | workspaceRoot.setLayoutData(new GridData(GridData.FILL, GridData.CENTER, true, false)); 159 | 160 | dialog = new DirectoryDialog(getShell(), SWT.OPEN); 161 | workspaceRootButton = new Button(container, SWT.DEFAULT); 162 | workspaceRootButton.setText("..."); 163 | workspaceRootButton.addSelectionListener(new SelectionAdapter() { 164 | @Override 165 | public void widgetSelected(SelectionEvent e) { 166 | String wr = dialog.open(); 167 | if (wr != null) { 168 | workspaceRoot.setText(wr); 169 | DirectoryTreeContentProvider.setFileTreeRoot(directories, new File(wr)); 170 | try { 171 | completionProvider.setBazelInstance( 172 | Activator.getDefault().getCommand().getInstance(new File(getWorkspaceRoot()))); 173 | } catch (IOException e1) { 174 | MessageDialog.openError(getShell(), "Error", 175 | getWorkspaceRoot() + " does not seems to be a Bazel workspace"); 176 | } catch (InterruptedException e1) { 177 | Activator.error("Bazel was interrupted", e1); 178 | } catch (BazelNotFoundException e1) { 179 | MessageDialog.openError(getShell(), "Error", "Cannot found Bazel: " + e1.getMessage()); 180 | } 181 | 182 | } 183 | updateControls(); 184 | } 185 | }); 186 | workspaceRootButton.setLayoutData(new GridData(GridData.END, GridData.CENTER, false, false)); 187 | } 188 | 189 | private void setControlGridData(Control control, int horizontalSpan, int verticalSpan, 190 | boolean verticalGrow) { 191 | GridData data = new GridData(GridData.FILL, verticalGrow ? GridData.FILL : GridData.CENTER, 192 | true, verticalGrow); 193 | data.horizontalSpan = horizontalSpan; 194 | data.verticalSpan = verticalSpan; 195 | control.setLayoutData(data); 196 | } 197 | 198 | private void addLabel(String labelText, int horizontalSpan, int verticalSpan) { 199 | Label label = new Label(container, SWT.NONE); 200 | label.setText(labelText); 201 | GridData data = new GridData(GridData.BEGINNING, GridData.FILL, false, true); 202 | data.horizontalSpan = horizontalSpan; 203 | data.verticalSpan = verticalSpan; 204 | label.setLayoutData(data); 205 | } 206 | 207 | private void updateControls() { 208 | boolean enabled = !workspaceRoot.getText().isEmpty(); 209 | directories.getTree().setEnabled(enabled); 210 | targets.setEnabled(enabled); 211 | target.setEnabled(enabled); 212 | addTargetButton.setEnabled(enabled && !target.getText().isEmpty()); 213 | removeTargetButton.setEnabled(enabled && targets.getSelectionCount() > 0); 214 | setPageComplete( 215 | enabled && (directories.getCheckedElements().length > 0) && (targets.getItemCount() > 0)); 216 | } 217 | 218 | private void setAutoCompletion() { 219 | try { 220 | ContentProposalAdapter adapter = null; 221 | completionProvider = new BazelTargetCompletionContentProposalProvider(); 222 | KeyStroke ks = KeyStroke.getInstance("Ctrl+Space"); 223 | adapter = new ContentProposalAdapter(target, new TextContentAdapter(), completionProvider, ks, 224 | null); 225 | adapter.setProposalAcceptanceStyle(ContentProposalAdapter.PROPOSAL_REPLACE); 226 | } catch (Exception e) { 227 | e.printStackTrace(); 228 | } 229 | } 230 | 231 | 232 | private void createTargetTextField() { 233 | target = new Text(container, SWT.BORDER); 234 | setAutoCompletion(); 235 | target.addKeyListener(new KeyAdapter() { 236 | @Override 237 | public void keyReleased(KeyEvent ke) { 238 | if (ke.keyCode == '\r' && (ke.stateMask & SWT.SHIFT) != 0 && !target.getText().isEmpty()) { 239 | addTarget(); 240 | } 241 | } 242 | }); 243 | target.addModifyListener(e -> updateControls()); 244 | } 245 | 246 | private void addTarget() { 247 | targets.add(target.getText()); 248 | target.setText(""); 249 | setAutoCompletion(); 250 | updateControls(); 251 | } 252 | 253 | private void deleteTarget() { 254 | targets.remove(targets.getSelectionIndices()); 255 | updateControls(); 256 | } 257 | } 258 | -------------------------------------------------------------------------------- /javatests/com/google/devtools/bazel/e4b/command/BUILD: -------------------------------------------------------------------------------- 1 | java_test( 2 | name = "CommandTest", 3 | srcs = ["CommandTest.java"], 4 | deps = [ 5 | "//java/com/google/devtools/bazel/e4b/command", 6 | "@com_google_truth//jar", 7 | "@org_hamcrest_core//jar", 8 | "@org_junit//jar", 9 | ], 10 | ) 11 | -------------------------------------------------------------------------------- /javatests/com/google/devtools/bazel/e4b/command/CommandTest.java: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Bazel Authors. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package com.google.devtools.bazel.e4b.command; 16 | 17 | import static com.google.common.truth.Truth.assertThat; 18 | 19 | import com.google.devtools.bazel.e4b.command.CommandConsole.CommandConsoleFactory; 20 | import java.io.ByteArrayOutputStream; 21 | import java.io.IOException; 22 | import java.io.OutputStream; 23 | import java.nio.charset.StandardCharsets; 24 | import java.util.LinkedList; 25 | import java.util.List; 26 | import java.util.function.Function; 27 | import org.junit.Before; 28 | import org.junit.Rule; 29 | import org.junit.Test; 30 | import org.junit.rules.TemporaryFolder; 31 | 32 | /** @{link Command}Test */ 33 | public class CommandTest { 34 | 35 | private static Function NON_EMPTY_LINES_SELECTOR = 36 | (x) -> x.trim().isEmpty() ? null : x; 37 | 38 | @Rule 39 | public TemporaryFolder folder = new TemporaryFolder(); 40 | 41 | private class MockCommandConsole implements CommandConsole { 42 | final ByteArrayOutputStream stdout = new ByteArrayOutputStream(); 43 | final ByteArrayOutputStream stderr = new ByteArrayOutputStream(); 44 | final String name; 45 | final String title; 46 | 47 | public MockCommandConsole(String name, String title) { 48 | this.title = title; 49 | this.name = name; 50 | } 51 | 52 | @Override 53 | public OutputStream createOutputStream() { 54 | return stdout; 55 | } 56 | 57 | @Override 58 | public OutputStream createErrorStream() { 59 | return stderr; 60 | } 61 | } 62 | 63 | private class MockConsoleFactory implements CommandConsoleFactory { 64 | final List consoles = new LinkedList<>(); 65 | 66 | @Override 67 | public CommandConsole get(String name, String title) throws IOException { 68 | MockCommandConsole console = new MockCommandConsole(name, title); 69 | consoles.add(console); 70 | return console; 71 | } 72 | } 73 | 74 | public MockConsoleFactory mockConsoleFactory; 75 | 76 | @Before 77 | public void setup() { 78 | mockConsoleFactory = new MockConsoleFactory(); 79 | } 80 | 81 | @Test 82 | public void testCommandWithStream() throws IOException, InterruptedException { 83 | ByteArrayOutputStream stdout = new ByteArrayOutputStream(); 84 | ByteArrayOutputStream stderr = new ByteArrayOutputStream(); 85 | Function stdoutSelector = 86 | (x) -> (x.trim().isEmpty() || x.equals("a")) ? null : x; 87 | Function stderrSelector = 88 | (x) -> (x.trim().isEmpty() || x.equals("b")) ? null : x; 89 | 90 | Command.Builder builder = Command.builder(mockConsoleFactory) 91 | .setConsoleName("test") 92 | .setDirectory(folder.getRoot()) 93 | .setStandardError(stderr) 94 | .setStandardOutput(stdout) 95 | .setStderrLineSelector(stderrSelector) 96 | .setStdoutLineSelector(stdoutSelector); 97 | builder.addArguments("bash", "-c", "echo a; echo b; echo a >&2; echo b >&2"); 98 | Command cmd = builder.build(); 99 | assertThat(cmd.run()).isEqualTo(0); 100 | String stdoutStr = new String(stdout.toByteArray(), StandardCharsets.UTF_8).trim(); 101 | String stderrStr = new String(stderr.toByteArray(), StandardCharsets.UTF_8).trim(); 102 | 103 | assertThat(stdoutStr).isEqualTo("a"); 104 | assertThat(stderrStr).isEqualTo("b"); 105 | assertThat(cmd.getSelectedErrorLines()).containsExactly("a"); 106 | assertThat(cmd.getSelectedOutputLines()).containsExactly("b"); 107 | assertThat(mockConsoleFactory.consoles).hasSize(1); 108 | MockCommandConsole console = mockConsoleFactory.consoles.get(0); 109 | assertThat(console.name).isEqualTo("test"); 110 | assertThat(console.title).isEqualTo( 111 | "Running bash -c echo a; echo b; echo a >&2; echo b >&2 " + "from " + folder.getRoot()); 112 | stdoutStr = new String(console.stdout.toByteArray(), StandardCharsets.UTF_8).trim(); 113 | stderrStr = new String(console.stderr.toByteArray(), StandardCharsets.UTF_8).trim(); 114 | assertThat(stdoutStr).isEmpty(); 115 | assertThat(stderrStr).isEmpty(); 116 | } 117 | 118 | @Test 119 | public void testCommandNoStream() throws IOException, InterruptedException { 120 | Command.Builder builder = 121 | Command.builder(mockConsoleFactory).setConsoleName(null).setDirectory(folder.getRoot()); 122 | builder.addArguments("bash", "-c", "echo a; echo b; echo a >&2; echo b >&2"); 123 | builder.setStderrLineSelector(NON_EMPTY_LINES_SELECTOR) 124 | .setStdoutLineSelector(NON_EMPTY_LINES_SELECTOR); 125 | Command cmd = builder.build(); 126 | assertThat(cmd.run()).isEqualTo(0); 127 | assertThat(cmd.getSelectedErrorLines()).containsExactly("a", "b"); 128 | assertThat(cmd.getSelectedOutputLines()).containsExactly("a", "b"); 129 | } 130 | 131 | @Test 132 | public void testCommandStreamAllToConsole() throws IOException, InterruptedException { 133 | Command.Builder builder = 134 | Command.builder(mockConsoleFactory).setConsoleName("test").setDirectory(folder.getRoot()); 135 | builder.addArguments("bash", "-c", "echo a; echo b; echo a >&2; echo b >&2"); 136 | Command cmd = builder.build(); 137 | assertThat(cmd.run()).isEqualTo(0); 138 | MockCommandConsole console = mockConsoleFactory.consoles.get(0); 139 | assertThat(console.name).isEqualTo("test"); 140 | assertThat(console.title).isEqualTo( 141 | "Running bash -c echo a; echo b; echo a >&2; echo b >&2 " + "from " + folder.getRoot()); 142 | String stdoutStr = new String(console.stdout.toByteArray(), StandardCharsets.UTF_8).trim(); 143 | String stderrStr = new String(console.stderr.toByteArray(), StandardCharsets.UTF_8).trim(); 144 | assertThat(stdoutStr).isEqualTo("a\nb"); 145 | assertThat(stderrStr).isEqualTo("a\nb"); 146 | } 147 | 148 | @Test 149 | public void testCommandWorkDir() throws IOException, InterruptedException { 150 | Command.Builder builder = 151 | Command.builder(mockConsoleFactory).setConsoleName(null).setDirectory(folder.getRoot()); 152 | builder.setStderrLineSelector(NON_EMPTY_LINES_SELECTOR) 153 | .setStdoutLineSelector(NON_EMPTY_LINES_SELECTOR); 154 | builder.addArguments("pwd"); 155 | Command cmd = builder.build(); 156 | assertThat(cmd.run()).isEqualTo(0); 157 | assertThat(cmd.getSelectedErrorLines()).isEmpty(); 158 | assertThat(cmd.getSelectedOutputLines()).containsExactly(folder.getRoot().getCanonicalPath()); 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /javatests/com/google/devtools/bazel/e4b/integration/AspectIntegrationTest.java: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Bazel Authors. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package com.google.devtools.bazel.e4b.integration; 16 | 17 | import static com.google.common.truth.Truth.assertThat; 18 | import static org.junit.Assert.assertEquals; 19 | 20 | import java.io.File; 21 | import java.io.IOException; 22 | import java.nio.file.Path; 23 | 24 | import org.junit.Before; 25 | import org.junit.BeforeClass; 26 | import org.junit.Test; 27 | 28 | import build.bazel.tests.integration.Command; 29 | import build.bazel.tests.integration.WorkspaceDriver; 30 | 31 | import com.google.common.base.Joiner; 32 | import com.google.common.collect.ImmutableList; 33 | import com.google.common.collect.ImmutableMap; 34 | import com.google.devtools.bazel.e4b.command.IdeBuildInfo; 35 | 36 | /** Integration test for the aspect used by the plugin. */ 37 | public final class AspectIntegrationTest { 38 | 39 | private WorkspaceDriver driver = new WorkspaceDriver(); 40 | private Path aspectWorkspace; 41 | 42 | @BeforeClass 43 | public static void setUpClass() throws IOException { 44 | WorkspaceDriver.setUpClass(); 45 | } 46 | 47 | @Before 48 | public void setUp() throws Exception { 49 | driver.setUp(); 50 | aspectWorkspace = driver.currentWorkspace(); 51 | driver.copyFromRunfiles("build_bazel_eclipse/resources/e4b_aspect.bzl", "e4b_aspect.bzl"); 52 | driver.scratchFile("BUILD"); 53 | driver.newWorkspace(); 54 | createJavaProgram(); 55 | } 56 | 57 | private void createJavaProgram() throws Exception { 58 | driver.scratchFile("java/my/pkg/Main.java", // force-new-line 59 | "package my.pkg;", // force-new-line 60 | "import my.other.pkg.Annex;", // force-new-line 61 | "public class Main {", // force-new-line 62 | " public static void main(String[] args) {", // force-new-line 63 | " System.out.println(new Annex().helloWorld());", // force-new-line 64 | " }", // force-new-line 65 | "}"); 66 | driver.scratchFile("java/my/pkg/BUILD", // force-new-line 67 | "java_binary(name='pkg',", // force-new-line 68 | " srcs=['Main.java'],", // force-new-line 69 | " deps=['//java/my/other/pkg:Annex'])"); 70 | driver.scratchFile("java/my/other/pkg/Annex.java", // force-new-line 71 | "package my.other.pkg;", // force-new-line 72 | 73 | 74 | "public class Annex {", // force-new-line 75 | " public Annex() {}", // force-new-line 76 | 77 | " public String helloWorld() {", // force-new-line 78 | " return \"Hello, World!\";", // force-new-line 79 | " }", // force-new-line 80 | "}"); 81 | driver.scratchFile("java/my/other/pkg/BUILD", // force-new-line 82 | "java_library(name='Annex',", // force-new-line 83 | " srcs=['Annex.java'],", // force-new-line 84 | " visibility = ['//visibility:public'])"); 85 | driver.scratchFile("javatests/my/other/pkg/AnnexTest.java", // force-new-line 86 | "package my.other.pkg;", // force-new-line 87 | 88 | "import static org.junit.Assert.assertEquals;", // force-new-line 89 | "import org.junit.Test;", // force-new-line 90 | 91 | 92 | "public class AnnexTest {", // force-new-line 93 | " @Test", // force-new-line 94 | " public void testAnnex() {", // force-new-line 95 | " assertEquals(\"Hello, World!\", new Annex().helloWorld());", // force-new-line 96 | " }", // force-new-line 97 | "}"); 98 | driver.scratchFile("javatests/my/other/pkg/BUILD", // force-new-line 99 | "java_test(name='AnnexTest',", // force-new-line 100 | " srcs=['AnnexTest.java'],", // force-new-line 101 | " deps=['//java/my/other/pkg:Annex'])"); 102 | } 103 | 104 | @Test 105 | public void testAspectGenerateJson() throws Exception { 106 | Command cmd = driver.bazelCommand("build", "--override_repository=local_eclipse_aspect=" + aspectWorkspace, 107 | "--aspects=@local_eclipse_aspect//:e4b_aspect.bzl%e4b_aspect", "-k", 108 | "--output_groups=ide-info-text,ide-resolve,-_,-defaults", "--experimental_show_artifacts", 109 | "//...").build(); 110 | int retCode = cmd.run(); 111 | assertEquals("Bazel failed to build, stderr: " + Joiner.on("\n").join(cmd.getErrorLines()), 112 | 0, retCode); 113 | String[] jsonFiles = cmd.getErrorLines().stream().filter((s) -> { 114 | return s.startsWith(">>>") && s.endsWith(".json"); 115 | }).map((s) -> { 116 | return s.substring(3); 117 | }).toArray(String[]::new); 118 | assertThat(jsonFiles).hasLength(3); 119 | 120 | ImmutableMap infos = 121 | IdeBuildInfo.getInfo(ImmutableList.copyOf(jsonFiles)); 122 | 123 | assertThat(infos).hasSize(3); 124 | 125 | assertThat(infos.get("//java/my/pkg:pkg").getLabel()).isEqualTo("//java/my/pkg:pkg"); 126 | assertThat(infos.get("//java/my/pkg:pkg").getLocation()).isEqualTo("java/my/pkg/BUILD"); 127 | assertThat(infos.get("//java/my/pkg:pkg").getKind()).isEqualTo("java_binary"); 128 | assertThat(infos.get("//java/my/pkg:pkg").getGeneratedJars()).isEmpty(); 129 | assertThat(infos.get("//java/my/pkg:pkg").getDeps()) 130 | .containsExactly("//java/my/other/pkg:Annex"); 131 | assertThat(infos.get("//java/my/pkg:pkg").getSources()) 132 | .containsExactly("java/my/pkg/Main.java"); 133 | 134 | assertThat(infos.get("//java/my/other/pkg:Annex").getLabel()) 135 | .isEqualTo("//java/my/other/pkg:Annex"); 136 | assertThat(infos.get("//java/my/other/pkg:Annex").getLocation()) 137 | .isEqualTo("java/my/other/pkg/BUILD"); 138 | assertThat(infos.get("//java/my/other/pkg:Annex").getKind()).isEqualTo("java_library"); 139 | assertThat(infos.get("//java/my/other/pkg:Annex").getGeneratedJars()).isEmpty(); 140 | assertThat(infos.get("//java/my/other/pkg:Annex").getDeps()).isEmpty(); 141 | assertThat(infos.get("//java/my/other/pkg:Annex").getSources()) 142 | .containsExactly("java/my/other/pkg/Annex.java"); 143 | 144 | assertThat(infos.get("//javatests/my/other/pkg:AnnexTest").getLabel()) 145 | .isEqualTo("//javatests/my/other/pkg:AnnexTest"); 146 | assertThat(infos.get("//javatests/my/other/pkg:AnnexTest").getLocation()) 147 | .isEqualTo("javatests/my/other/pkg/BUILD"); 148 | assertThat(infos.get("//javatests/my/other/pkg:AnnexTest").getKind()).isEqualTo("java_test"); 149 | assertThat(infos.get("//javatests/my/other/pkg:AnnexTest").getGeneratedJars()).isEmpty(); 150 | assertThat(infos.get("//javatests/my/other/pkg:AnnexTest").getDeps()) 151 | .containsExactly("//java/my/other/pkg:Annex"); 152 | assertThat(infos.get("//javatests/my/other/pkg:AnnexTest").getSources()) 153 | .containsExactly("javatests/my/other/pkg/AnnexTest.java"); 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /javatests/com/google/devtools/bazel/e4b/integration/BUILD: -------------------------------------------------------------------------------- 1 | load("@build_bazel_integration_testing//tools:bazel_java_integration_test.bzl", "bazel_java_integration_test") 2 | 3 | bazel_java_integration_test( 4 | name = "AspectIntegrationTest", 5 | srcs = ["AspectIntegrationTest.java"], 6 | data = ["//resources:srcs"], 7 | deps = [ 8 | "//java/com/google/devtools/bazel/e4b/command", 9 | "@org_junit//jar", 10 | "@org_hamcrest_core//jar", 11 | "@com_google_guava//jar", 12 | "@com_google_truth//jar", 13 | ], 14 | ) 15 | -------------------------------------------------------------------------------- /javatests/com/google/devtools/bazel/e4b/projectviews/BUILD: -------------------------------------------------------------------------------- 1 | java_test( 2 | name = "ProjectViewTest", 3 | srcs = ["ProjectViewTest.java"], 4 | resources = ["bazel.projectview"], 5 | deps = [ 6 | "//java/com/google/devtools/bazel/e4b/projectviews", 7 | "@com_google_truth//jar", 8 | "@org_hamcrest_core//jar", 9 | "@org_junit//jar", 10 | ], 11 | ) 12 | -------------------------------------------------------------------------------- /javatests/com/google/devtools/bazel/e4b/projectviews/ProjectViewTest.java: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Bazel Authors. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package com.google.devtools.bazel.e4b.projectviews; 16 | 17 | import static com.google.common.truth.Truth.assertThat; 18 | 19 | import java.io.IOException; 20 | import java.net.URISyntaxException; 21 | 22 | import org.junit.Test; 23 | 24 | /** {@link ProjectView}Test */ 25 | public class ProjectViewTest { 26 | 27 | @Test 28 | public void testBazelView() throws IOException, ProjectViewParseException, URISyntaxException { 29 | ProjectView view = ProjectView.builder() 30 | .parseView(ProjectViewTest.class.getResource("bazel.projectview")).build(); 31 | assertThat(view.getBuildFlags()).isEmpty(); 32 | assertThat(view.getDirectories()).containsExactly("."); 33 | assertThat(view.getJavaLanguageLevel()).isEqualTo(0); 34 | assertThat(view.getTargets()).containsAllOf("//src:bazel", "//src/test/..."); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /javatests/com/google/devtools/bazel/e4b/projectviews/bazel.projectview: -------------------------------------------------------------------------------- 1 | # Setup IntelliJ for Bazel development using the IntelliJ Bazel Plugin. 2 | # From github.com/bazelbuild/bazel's scripts/ij.projectview at commit 815bd634df0350a227133244c734b5c3672776ab. 3 | directories: 4 | . 5 | 6 | test_sources: 7 | src/java_tools/buildjar/javatests/* 8 | src/java_tools/junitrunner/javatests/* 9 | src/java_tools/singlejar/javatests/* 10 | src/test/* 11 | 12 | targets: 13 | //src:bazel 14 | //src/java_tools/buildjar:JavaBuilder 15 | //src/java_tools/buildjar:VanillaJavaBuilder 16 | //src/java_tools/buildjar/javatests/... 17 | //src/java_tools/junitrunner/java/com/google/testing/junit/runner:Runner 18 | //src/java_tools/junitrunner/javatests/... 19 | //src/java_tools/singlejar:SingleJar 20 | //src/java_tools/singlejar:tests 21 | //src/test/... 22 | //src/tools/remote_worker -------------------------------------------------------------------------------- /plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 9 | 10 | 11 | 13 | 19 | Import a list of sources from a Bazel workspace into a Java project 20 | 21 | 22 | 25 | 26 | 28 | 29 | 30 | 31 | 33 | 37 | 38 | 39 | 41 | 43 | 44 | 45 | 48 | 53 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /resources/BUILD: -------------------------------------------------------------------------------- 1 | # This is a build file for creating a corresponding package. This package will be a local repository 2 | # added with --override_repository when using the aspect so we can load the e4b_aspect.bzl Skylark 3 | # aspect extension when calling build from Eclipse. 4 | 5 | filegroup( 6 | name = "srcs", 7 | srcs = glob(["**"]), 8 | visibility = ["//visibility:public"], 9 | ) 10 | -------------------------------------------------------------------------------- /resources/WORKSPACE: -------------------------------------------------------------------------------- 1 | # This is a workspace file for creating a local repository added with --override_repository when 2 | # using the aspect so we can load the e4b_aspect.bzl Skylark aspect extension when calling build 3 | # from Eclipse. -------------------------------------------------------------------------------- /resources/e4b_aspect.bzl: -------------------------------------------------------------------------------- 1 | # Copyright 2016 The Bazel Authors. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | # Aspect for e4b, taken from intellij_info.bzl 16 | 17 | DEPENDENCY_ATTRIBUTES = [ 18 | "deps", 19 | "runtime_deps", 20 | "exports", 21 | ] 22 | 23 | def struct_omit_none(**kwargs): 24 | d = {name: kwargs[name] for name in kwargs if kwargs[name] != None} 25 | return struct(**d) 26 | 27 | def artifact_location(file): 28 | return None if file == None else file.path 29 | 30 | def library_artifact(java_output): 31 | if java_output == None or java_output.class_jar == None: 32 | return None 33 | return struct_omit_none( 34 | jar = artifact_location(java_output.class_jar), 35 | interface_jar = artifact_location(java_output.ijar), 36 | source_jar = artifact_location(java_output.source_jar), 37 | ) 38 | 39 | def annotation_processing_jars(annotation_processing): 40 | return struct_omit_none( 41 | jar = artifact_location(annotation_processing.class_jar), 42 | source_jar = artifact_location(annotation_processing.source_jar), 43 | ) 44 | 45 | def jars_from_output(output): 46 | """ Collect jars for ide-resolve-files from Java output. 47 | """ 48 | if output == None: 49 | return [] 50 | return [jar 51 | for jar in [output.class_jar, output.ijar, output.source_jar] 52 | if jar != None and not jar.is_source] 53 | 54 | def java_rule_ide_info(target, ctx): 55 | if hasattr(ctx.rule.attr, "srcs"): 56 | sources = [artifact_location(file) 57 | for src in ctx.rule.attr.srcs 58 | for file in src.files] 59 | else: 60 | sources = [] 61 | 62 | jars = [library_artifact(output) for output in target.java.outputs.jars] 63 | ide_resolve_files = depset([jar 64 | for output in target.java.outputs.jars 65 | for jar in jars_from_output(output)]) 66 | 67 | gen_jars = [] 68 | if target.java.annotation_processing and target.java.annotation_processing.enabled: 69 | gen_jars = [annotation_processing_jars(target.java.annotation_processing)] 70 | ide_resolve_files = ide_resolve_files + depset([ jar 71 | for jar in [target.java.annotation_processing.class_jar, 72 | target.java.annotation_processing.source_jar] 73 | if jar != None and not jar.is_source]) 74 | 75 | return (struct_omit_none( 76 | sources = sources, 77 | jars = jars, 78 | generated_jars = gen_jars 79 | ), 80 | ide_resolve_files) 81 | 82 | 83 | def _aspect_impl(target, ctx): 84 | kind = ctx.rule.kind 85 | rule_attrs = ctx.rule.attr 86 | 87 | ide_info_text = depset() 88 | ide_resolve_files = depset() 89 | all_deps = [] 90 | 91 | for attr_name in DEPENDENCY_ATTRIBUTES: 92 | if hasattr(rule_attrs, attr_name): 93 | deps = getattr(rule_attrs, attr_name) 94 | if type(deps) == 'list': 95 | for dep in deps: 96 | if hasattr(dep, "intellij_info_files"): 97 | ide_info_text = ide_info_text + dep.intellij_info_files.ide_info_text 98 | ide_resolve_files = ide_resolve_files + dep.intellij_info_files.ide_resolve_files 99 | all_deps += [str(dep.label) for dep in deps] 100 | 101 | if hasattr(target, "java"): 102 | (java_rule_ide_info_struct, java_ide_resolve_files) = java_rule_ide_info(target, ctx) 103 | info = struct( 104 | label = str(target.label), 105 | kind = kind, 106 | dependencies = all_deps, 107 | build_file_artifact_location = ctx.build_file_path, 108 | ) + java_rule_ide_info_struct 109 | ide_resolve_files = ide_resolve_files + java_ide_resolve_files 110 | output = ctx.new_file(target.label.name + ".e4b-build.json") 111 | ctx.file_action(output, info.to_json()) 112 | ide_info_text += depset([output]) 113 | 114 | return struct( 115 | output_groups = { 116 | "ide-info-text" : ide_info_text, 117 | "ide-resolve" : ide_resolve_files, 118 | }, 119 | intellij_info_files = struct( 120 | ide_info_text = ide_info_text, 121 | ide_resolve_files = ide_resolve_files, 122 | ) 123 | ) 124 | 125 | e4b_aspect = aspect(implementation = _aspect_impl, 126 | attr_aspects = DEPENDENCY_ATTRIBUTES 127 | ) 128 | """Aspect for Eclipse 4 Bazel plugin. 129 | 130 | This aspect produces information for IDE integration with Eclipse. This only 131 | produces information for Java targets. 132 | 133 | This aspect has two output groups: 134 | - ide-info-text produces .e4b-build.json files that contains information 135 | about target dependencies and sources files for the IDE. 136 | - ide-resolve build the dependencies needed for the build (i.e., artifacts 137 | generated by Java annotation processors). 138 | 139 | An e4b-build.json file is a json blob with the following keys: 140 | ```javascript 141 | { 142 | // Label of the corresponding target 143 | "label": "//package:target", 144 | // Kind of the corresponding target, e.g., java_test, java_binary, ... 145 | "kind": "java_library", 146 | // List of dependencies of this target 147 | "dependencies": ["//package1:dep1", "//package2:dep2"], 148 | "Path, relative to the workspace root, of the build file containing the target. 149 | "build_file_artifact_location": "package/BUILD", 150 | // List of sources file, relative to the execroot 151 | "sources": ["package/Test.java"], 152 | // List of jars created when building this target. 153 | "jars": [jar1, jar2], 154 | // List of jars generated by java annotation processors when building this target. 155 | "generated_jars": [genjar1, genjar2] 156 | } 157 | ``` 158 | 159 | Jar files structure has the following keys: 160 | ```javascript 161 | { 162 | // Location, relative to the execroot, of the jar file or null 163 | "jar": "bazel-out/host/package/libtarget.jar", 164 | // Location, relative to the execroot, of the interface jar file, 165 | // containing only the interfaces of the target jar or null. 166 | "interface_jar": "bazel-out/host/package/libtarget.interface-jar", 167 | // Location, relative to the execroot, of the source jar file, 168 | // containing the sources used to generate the target jar or null. 169 | "source_jar": "bazel-out/host/package/libtarget.interface-jar", 170 | } 171 | ``` 172 | """ 173 | -------------------------------------------------------------------------------- /resources/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bazelbuild/eclipse/055ccebf7221dfbcab67204ca210f98cba5bec6a/resources/icon.ico -------------------------------------------------------------------------------- /runfiles: -------------------------------------------------------------------------------- 1 | bazel-bin/javatests/com/google/devtools/bazel/e4b/integration/AspectIntegrationTest/bazel0.10.0.runfiles -------------------------------------------------------------------------------- /tools/build_defs/BUILD: -------------------------------------------------------------------------------- 1 | package(default_visibility = ["//visibility:public"]) 2 | 3 | py_binary( 4 | name = "feature_builder", 5 | srcs = ["feature_builder.py"], 6 | deps = ["@com_google_python_gflags//:gflags"], 7 | ) 8 | 9 | py_binary( 10 | name = "site_builder", 11 | srcs = ["site_builder.py"], 12 | deps = ["@com_google_python_gflags//:gflags"], 13 | ) 14 | -------------------------------------------------------------------------------- /tools/build_defs/eclipse.bzl: -------------------------------------------------------------------------------- 1 | # Copyright 2017 The Bazel Authors. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # TODO(dmarting): Provide checksums for those files. 16 | _EQUINOX_MIRROR_URL="https://storage.googleapis.com/bazel-mirror/download.eclipse.org/eclipse/updates" 17 | _ECLIPSE_VERSION="4.5.2-201602121500" 18 | _DOWNLOAD_URL = "%s/%s/R-%s/plugins/%s_%s.jar" % ( 19 | _EQUINOX_MIRROR_URL, 20 | ".".join(_ECLIPSE_VERSION.split(".", 3)[0:2]), 21 | _ECLIPSE_VERSION, 22 | "%s", 23 | "%s") 24 | 25 | # TODO(dmarting): make this configurable? 26 | _DECLARED_DEPS = [ 27 | "org.eclipse.ui.console", 28 | "org.eclipse.ui", 29 | "org.eclipse.core.resources", 30 | "org.eclipse.ui.ide", 31 | "org.eclipse.jdt.core", 32 | "org.eclipse.core.runtime", 33 | "javax.inject", 34 | ] 35 | 36 | _ECLIPSE_PLUGIN_DEPS = { 37 | # Declared deps. 38 | "org.eclipse.ui.console": "3.6.100.v20150822-1912", 39 | "javax.inject": "1.0.0.v20091030", 40 | "org.eclipse.core.runtime": "3.11.1.v20150903-1804", 41 | "org.eclipse.ui": "3.107.0.v20150507-1945", 42 | "org.eclipse.jdt.core": "3.11.2.v20160128-0629", 43 | "org.eclipse.core.resources": "3.10.1.v20150725-1910", 44 | "org.eclipse.ui.ide": "3.11.0.v20150825-2158", 45 | # implicit deps 46 | "org.eclipse.swt": "3.104.2.v20160212-1350", 47 | # TODO(dmarting): make it works cross platform. This is not a problem while 48 | # we are using the dependency to compile but this might become an issue if 49 | # we need to run the plugin (e.g. to test it). 50 | # 51 | # Available platforms: cocoa.macosx.x86_64 gtk.aix.ppc gtk.aix.ppc64 52 | # gtk.hpux.ia64 gtk.linux.ppc gtk.linux.ppc64 gtk.linux.ppc64le gtk.linux.s390 53 | # gtk.linux.s390x gtk.linux.x86 gtk.linux.x86_64 gtk.solaris.sparc 54 | # gtk.solaris.x86 win32.win32.x86 win32.win32.x86_64 55 | "org.eclipse.swt.gtk.linux.ppc": "3.104.2.v20160212-1350", 56 | "org.eclipse.jface": "3.11.1.v20160128-1644", 57 | "org.eclipse.core.commands": "3.7.0.v20150422-0725", 58 | "org.eclipse.ui.workbench": "3.107.1.v20160120-2131", 59 | "org.eclipse.e4.ui.workbench3": "0.13.0.v20150422-0725", 60 | "org.eclipse.jdt.compiler.apt": "1.2.0.v20150514-0146", 61 | "org.eclipse.jdt.compiler.tool": "1.1.0.v20150513-2007", 62 | "javax.annotation": "1.2.0.v201401042248", 63 | "org.eclipse.osgi": "3.10.102.v20160118-1700", 64 | "org.eclipse.osgi.compatibility.state": "1.0.100.v20150402-1551", 65 | "org.eclipse.equinox.common": "3.7.0.v20150402-1709", 66 | "org.eclipse.core.jobs": "3.7.0.v20150330-2103", 67 | "org.eclipse.core.runtime.compatibility.registry": "3.6.0.v20150318-1505", 68 | "org.eclipse.equinox.registry": "3.6.0.v20150318-1503", 69 | "org.eclipse.equinox.preferences": "3.5.300.v20150408-1437", 70 | "org.eclipse.core.contenttype": "3.5.0.v20150421-2214", 71 | "org.eclipse.equinox.app": "1.3.300.v20150423-1356", 72 | "org.eclipse.ui.views": "3.8.0.v20150422-0725", 73 | } 74 | 75 | 76 | def _load_eclipse_dep(plugin, version): 77 | native.http_file( 78 | name = plugin.replace(".", "_"), 79 | url = _DOWNLOAD_URL % (plugin, version), 80 | ) 81 | 82 | load("//tools/build_defs:eclipse_platform.bzl", "eclipse_platform") 83 | 84 | def load_eclipse_deps(): 85 | """Load dependencies of the Eclipse plugin.""" 86 | for plugin in _ECLIPSE_PLUGIN_DEPS: 87 | _load_eclipse_dep(plugin, _ECLIPSE_PLUGIN_DEPS[plugin]) 88 | eclipse_platform(name="org_eclipse_equinox", version=_ECLIPSE_VERSION) 89 | 90 | 91 | def eclipse_plugin(name, version, bundle_name, activator=None, 92 | vendor=None, **kwargs): 93 | """A macro to generate an eclipse plugin (see java_binary).""" 94 | jars = ["@%s//file" % plugin.replace(".", "_") 95 | for plugin in _ECLIPSE_PLUGIN_DEPS] 96 | native.java_import( 97 | name = name + "-deps", 98 | neverlink = 1, 99 | jars = jars, 100 | ) 101 | deps = [name + "-deps"] 102 | if "deps" in kwargs: 103 | deps = deps + kwargs["deps"] 104 | args = {k: kwargs[k] 105 | for k in kwargs 106 | if k not in [ 107 | "deps", 108 | "classpath_resources", 109 | "deploy_manifest_lines", 110 | "visibility", 111 | "main_class"]} 112 | visibility = kwargs["visibility"] if "visibility" in kwargs else None 113 | # Generate the .api_description to put in the final jar 114 | native.genrule( 115 | name = name + ".api_description", 116 | srcs = [], 117 | outs = [name + "/.api_description"], 118 | cmd = """ 119 | cat <$@ 120 | 121 | 122 | 123 | 124 | EOF 125 | """ % (name, version, name, version)) 126 | # Generate the final jar (a deploy jar) 127 | native.java_binary( 128 | name = name + "-bin", 129 | main_class = "does.not.exist", 130 | classpath_resources = [ 131 | ":%s/.api_description" % name, 132 | # TODO(dmarting): this add the plugin.xml dependency here, maybe we 133 | # should move that to the BUILD file to avoid surprise? 134 | "plugin.xml", 135 | ] + (kwargs["classpath_resources"] 136 | if "classpath_resources" in kwargs else []), 137 | deploy_manifest_lines = [ 138 | "Bundle-ManifestVersion: 2", 139 | "Bundle-Name: " + bundle_name, 140 | # TODO(dmarting): We mark always as singleton, make it configurable? 141 | "Bundle-SymbolicName: %s;singleton:=true" % name, 142 | "Bundle-Version: " + version, 143 | "Require-Bundle: " + ", ".join(_DECLARED_DEPS), 144 | # TODO(dmarting): Take the java version from java_toolchain. 145 | "Bundle-RequiredExecutionEnvironment: JavaSE-1.8", 146 | "Bundle-ActivationPolicy: lazy", 147 | "Bundle-ClassPath: .", 148 | ] + ( 149 | ["Bundle-Activator: " + activator] if activator else [] 150 | ) + ( 151 | ["Bundle-Vendor: " + vendor] if vendor else [] 152 | ) + (kwargs["deploy_manifest_lines"] 153 | if "deploy_manifest_lines" in kwargs else []), 154 | deps = deps, 155 | **args) 156 | # Rename the output to the correct name 157 | native.genrule( 158 | name = name, 159 | srcs = [":%s-bin_deploy.jar" % name], 160 | outs = ["%s_%s.jar" % (name, version)], 161 | cmd = "cp $< $@", 162 | output_to_bindir = 1, 163 | visibility = visibility, 164 | ) 165 | 166 | 167 | def _eclipse_feature_impl(ctx): 168 | feature_xml = ctx.new_file(ctx.outputs.out, ctx.label.name + ".xml") 169 | ctx.action( 170 | outputs = [feature_xml], 171 | inputs = [ctx.file.license], 172 | executable = ctx.executable._builder, 173 | arguments = [ 174 | "--output=" + feature_xml.path, 175 | "--id=" + ctx.label.name, 176 | "--label=" + ctx.attr.label, 177 | "--version=" + ctx.attr.version, 178 | "--provider=" + ctx.attr.provider, 179 | "--url=" + ctx.attr.url, 180 | "--description=" + ctx.attr.description, 181 | "--copyright=" + ctx.attr.copyright, 182 | "--license_url=" + ctx.attr.license_url, 183 | "--license=" + ctx.file.license.path] + [ 184 | "--site=%s=%s" % (site, ctx.attr.sites[site]) 185 | for site in ctx.attr.sites] + [ 186 | "--plugin=" + p.basename for p in ctx.files.plugins]) 187 | ctx.action( 188 | outputs = [ctx.outputs.out], 189 | inputs = [feature_xml], 190 | executable = ctx.executable._zipper, 191 | arguments = ["c", 192 | ctx.outputs.out.path, 193 | "feature.xml=" + feature_xml.path], 194 | ) 195 | return struct( 196 | eclipse_feature=struct( 197 | file=ctx.outputs.out, 198 | id=ctx.label.name, 199 | version=ctx.attr.version, 200 | plugins=ctx.files.plugins 201 | ) 202 | ) 203 | 204 | 205 | eclipse_feature = rule( 206 | implementation=_eclipse_feature_impl, 207 | attrs = { 208 | "label": attr.string(mandatory=True), 209 | "version": attr.string(mandatory=True), 210 | "provider": attr.string(mandatory=True), 211 | "description": attr.string(mandatory=True), 212 | "url": attr.string(mandatory=True), 213 | "copyright": attr.string(mandatory=True), 214 | "license_url": attr.string(mandatory=True), 215 | "license": attr.label(mandatory=True, allow_single_file=True), 216 | "sites": attr.string_dict(), 217 | # TODO(dmarting): restrict what can be passed to the plugins attribute. 218 | "plugins": attr.label_list(), 219 | "_zipper": attr.label(default=Label("@bazel_tools//tools/zip:zipper"), 220 | executable=True, 221 | cfg="host"), 222 | "_builder": attr.label(default=Label("//tools/build_defs:feature_builder"), 223 | executable=True, 224 | cfg="host"), 225 | }, 226 | outputs = {"out": "%{name}_%{version}.jar"}) 227 | """Create an eclipse feature jar.""" 228 | 229 | 230 | def _eclipse_p2updatesite_impl(ctx): 231 | feat_files = [f.eclipse_feature.file for f in ctx.attr.eclipse_features] 232 | args = [ 233 | "--output=" + ctx.outputs.out.path, 234 | "--java=" + ctx.executable._java.path, 235 | "--eclipse_launcher=" + ctx.file._eclipse_launcher.path, 236 | "--name=" + ctx.attr.label, 237 | "--url=" + ctx.attr.url, 238 | "--description=" + ctx.attr.description] 239 | 240 | _plugins = {} 241 | for f in ctx.attr.eclipse_features: 242 | args.append("--feature=" + f.eclipse_feature.file.path) 243 | args.append("--feature_id=" + f.eclipse_feature.id) 244 | args.append("--feature_version=" + f.eclipse_feature.version) 245 | for p in f.eclipse_feature.plugins: 246 | if p.path not in _plugins: 247 | _plugins[p.path] = p 248 | plugins = [_plugins[p] for p in _plugins] 249 | 250 | ctx.action( 251 | outputs=[ctx.outputs.out], 252 | inputs=[ 253 | ctx.executable._java, 254 | ctx.file._eclipse_launcher, 255 | ] + ctx.files._jdk + ctx.files._eclipse_platform + feat_files + plugins, 256 | executable = ctx.executable._site_builder, 257 | arguments = args + ["--bundle=" + p.path for p in plugins]) 258 | 259 | 260 | eclipse_p2updatesite = rule( 261 | implementation=_eclipse_p2updatesite_impl, 262 | attrs = { 263 | "label": attr.string(mandatory=True), 264 | "description": attr.string(mandatory=True), 265 | "url": attr.string(mandatory=True), 266 | "eclipse_features": attr.label_list(providers=["eclipse_feature"]), 267 | "_site_builder": attr.label( 268 | default=Label("//tools/build_defs:site_builder"), 269 | executable=True, 270 | cfg="host"), 271 | "_zipper": attr.label( 272 | default=Label("@bazel_tools//tools/zip:zipper"), 273 | executable=True, 274 | cfg="host"), 275 | "_java": attr.label( 276 | default=Label("@bazel_tools//tools/jdk:java"), 277 | executable=True, 278 | cfg="host"), 279 | "_jdk": attr.label(default=Label("@bazel_tools//tools/jdk:jdk")), 280 | "_eclipse_launcher": attr.label( 281 | default=Label("@org_eclipse_equinox//:launcher"), 282 | allow_single_file=True), 283 | "_eclipse_platform": attr.label(default=Label("@org_eclipse_equinox//:platform")), 284 | }, 285 | outputs = {"out": "%{name}.zip"}) 286 | """Create an eclipse p2update site inside a ZIP file.""" 287 | -------------------------------------------------------------------------------- /tools/build_defs/eclipse_platform.bzl: -------------------------------------------------------------------------------- 1 | # Copyright 2017 The Bazel Authors. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # The Eclipse website provides SHA-512 but Bazel only support SHA256. 16 | # Really Bazel should start supporting all "safe" checksum (and also 17 | # drop support for SHA-1). 18 | SHA256_SUM={ 19 | # TODO(dmarting): we only support 4.5.2 right now because we need to 20 | # download all version of eclipse to provide those checksums... 21 | "4.5.2": { 22 | "macosx-cocoa-x86_64": "755f8a75075f6310a8d0453b5766a84aca2fcc687808341b7a657259230b490f", 23 | "linux-gtk-x86_64": "87f82b0c13c245ee20928557dbc4435657d1e029f72d9135683c8d585c69ba8d" 24 | } 25 | } 26 | 27 | def _get_file_url(version, platform, t): 28 | drop = "drops" 29 | if int(version.split(".", 1)[0]) >= 4: 30 | drop = "drops4" 31 | short_version = version.split("-", 1)[0] 32 | sha256 = "" 33 | if short_version in SHA256_SUM: 34 | if platform in SHA256_SUM[short_version]: 35 | sha256 = SHA256_SUM[short_version][platform] 36 | 37 | filename = "eclipse-SDK-%s-%s.%s" % (short_version, platform, t) 38 | file = "/eclipse/downloads/%s/R-%s/%s" % ( 39 | drop, 40 | version, 41 | filename) 42 | # This is a mirror, original base url is http://www.eclipse.org/downloads/download.php?file= 43 | base_url = "https://storage.googleapis.com/bazel-mirror/download.eclipse.org" 44 | return (base_url + file, sha256) 45 | 46 | 47 | def _eclipse_platform_impl(rctx): 48 | version = rctx.attr.version 49 | os_name = rctx.os.name.lower() 50 | if os_name.startswith("mac os"): 51 | platform = "macosx-cocoa-x86_64" 52 | t = "tar.gz" 53 | elif os_name.startswith("linux"): 54 | platform = "linux-gtk-x86_64" 55 | t = "tar.gz" 56 | else: 57 | fail("Cannot fetch Eclipse for platform %s" % rctx.os.name) 58 | url, sha256 = _get_file_url(version, platform, t) 59 | rctx.download_and_extract(url=url, type=t, sha256=sha256) 60 | rctx.file("BUILD.bazel", """ 61 | package(default_visibility = ["//visibility:public"]) 62 | filegroup(name = "platform", srcs = glob(["**"], exclude = ["BUILD.bazel", "BUILD"])) 63 | filegroup(name = "launcher", srcs = glob(["**/plugins/org.eclipse.equinox.launcher_*.jar"])) 64 | """) 65 | 66 | 67 | eclipse_platform = repository_rule( 68 | implementation = _eclipse_platform_impl, 69 | attrs = { 70 | "version": attr.string(mandatory=True), 71 | }, local=False) 72 | """A repository for downloading the good version eclipse depending on the platform.""" 73 | -------------------------------------------------------------------------------- /tools/build_defs/feature_builder.py: -------------------------------------------------------------------------------- 1 | # Copyright 2017 The Bazel Authors. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | """This tool build feature.xml files describing an Eclipse feature.""" 15 | 16 | from xml.etree import ElementTree 17 | from xml.dom import minidom 18 | import gflags 19 | import sys 20 | 21 | gflags.DEFINE_string("output", None, "The output file, mandatory") 22 | gflags.MarkFlagAsRequired("output") 23 | 24 | gflags.DEFINE_string("id", None, "The feature ID, mandatory") 25 | gflags.MarkFlagAsRequired("id") 26 | 27 | gflags.DEFINE_string( 28 | "label", 29 | None, 30 | "The feature label (i.e. short description), mandatory") 31 | gflags.MarkFlagAsRequired("label") 32 | 33 | gflags.DEFINE_string("version", None, "The feature version, mandatory") 34 | gflags.MarkFlagAsRequired("version") 35 | 36 | gflags.DEFINE_string( 37 | "provider", None, "The provider (i.e. the vendor) of the feature, mandatory") 38 | gflags.MarkFlagAsRequired("provider") 39 | 40 | gflags.DEFINE_string( 41 | "url", None, "A URL associated to the description, optional") 42 | 43 | gflags.DEFINE_string( 44 | "description", None, "Description of the feature, mandatory") 45 | gflags.MarkFlagAsRequired("description") 46 | 47 | gflags.DEFINE_string( 48 | "copyright", None, "Copyright line for the repository, mandatory") 49 | gflags.MarkFlagAsRequired("copyright") 50 | 51 | gflags.DEFINE_string( 52 | "license_url", None, "URL pointing to the license, mandatory") 53 | gflags.MarkFlagAsRequired("license_url") 54 | 55 | gflags.DEFINE_string( 56 | "license", None, "Text file of the license of the feature, mandatory") 57 | gflags.MarkFlagAsRequired("license") 58 | 59 | gflags.DEFINE_multistring( 60 | "site", [], "Sites related to the plugin, in the form `label=url`") 61 | gflags.DEFINE_multistring( 62 | "plugin", [], "List of plugins that this feature contains (filename).") 63 | 64 | FLAGS=gflags.FLAGS 65 | 66 | def _plugins(parent, plugins): 67 | for plugin in plugins: 68 | if plugin.endswith(".jar"): 69 | id, version = plugin[:-4].split("_", 1) 70 | p = ElementTree.SubElement(parent, "plugin") 71 | p.set("id", id) 72 | p.set("download-size", "0") 73 | p.set("install-size", "0") 74 | p.set("version", version) 75 | 76 | 77 | def _sites(parent, sites): 78 | for site in sites: 79 | label, url = site.split("=", 1) 80 | p = ElementTree.SubElement(parent, "discovery") 81 | p.set("label", label) 82 | p.set("url", url) 83 | 84 | 85 | def main(unused_argv): 86 | feature = ElementTree.Element("feature") 87 | feature.set("id", FLAGS.id) 88 | feature.set("label", FLAGS.label) 89 | feature.set("version", FLAGS.version) 90 | feature.set("provider-name", FLAGS.provider) 91 | description = ElementTree.SubElement(feature, "description") 92 | if FLAGS.url: 93 | description.set("url", FLAGS.url) 94 | description.text = FLAGS.description 95 | copyright = ElementTree.SubElement(feature, "copyright") 96 | copyright.text = FLAGS.copyright 97 | license = ElementTree.SubElement(feature, "license") 98 | license.set("url", FLAGS.license_url) 99 | with open(FLAGS.license, "r") as f: 100 | license.text = f.read() 101 | _sites(ElementTree.SubElement(feature, "url"), FLAGS.site) 102 | _plugins(feature, FLAGS.plugin) 103 | 104 | # Pretty print the resulting tree 105 | output = ElementTree.tostring(feature, "utf-8") 106 | reparsed = minidom.parseString(output) 107 | with open(FLAGS.output, "w") as f: 108 | f.write(reparsed.toprettyxml(indent=" ", encoding="UTF-8")) 109 | 110 | if __name__ == "__main__": 111 | main(FLAGS(sys.argv)) 112 | -------------------------------------------------------------------------------- /tools/build_defs/site_builder.py: -------------------------------------------------------------------------------- 1 | # Copyright 2017 The Bazel Authors. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | """This tool build a zipped Eclipse p2 update site from features and plugins.""" 15 | 16 | import gflags 17 | import os 18 | import os.path 19 | import shutil 20 | import subprocess 21 | import sys 22 | import tempfile 23 | import zipfile 24 | 25 | from xml.etree import ElementTree 26 | from xml.dom import minidom 27 | 28 | gflags.DEFINE_string("output", None, "The output files, mandatory") 29 | gflags.MarkFlagAsRequired("output") 30 | 31 | gflags.DEFINE_multistring( 32 | "feature_id", 33 | [], 34 | "Feature id to include in the site, should come " 35 | "along with --feature and --feature_version.") 36 | gflags.DEFINE_multistring( 37 | "feature", 38 | [], 39 | "Feature file to include in the site, should come " 40 | "along with --feature_id and --feature_version") 41 | gflags.DEFINE_multistring( 42 | "feature_version", 43 | [], 44 | "Version of a feature to include in the site, should " 45 | "come along with --feature and --feature_id") 46 | 47 | gflags.DEFINE_multistring( 48 | "bundle", 49 | [], 50 | "Bundle file to include in the sit") 51 | 52 | gflags.DEFINE_string( 53 | "name", 54 | None, 55 | "The site name (i.e. short description), mandatory") 56 | gflags.MarkFlagAsRequired("name") 57 | 58 | gflags.DEFINE_string("url", None, "A URL for the site, mandatory") 59 | gflags.MarkFlagAsRequired("url") 60 | 61 | gflags.DEFINE_string( 62 | "description", None, "Description of the site, mandatory") 63 | gflags.MarkFlagAsRequired("description") 64 | 65 | gflags.DEFINE_string( 66 | "java", 67 | "java", 68 | "Path to java, optional") 69 | 70 | gflags.DEFINE_string( 71 | "eclipse_launcher", 72 | None, 73 | "Path to the eclipse launcher, mandatory") 74 | gflags.MarkFlagAsRequired("eclipse_launcher") 75 | 76 | FLAGS=gflags.FLAGS 77 | 78 | def _features(parent): 79 | if (len(FLAGS.feature) != len(FLAGS.feature_id)) or ( 80 | len(FLAGS.feature) != len(FLAGS.feature_version)): 81 | raise Exception( 82 | "Should provide the same number of " 83 | "time --feature, --feature_id and " 84 | "--feature_version") 85 | for i in range(0, len(FLAGS.feature)): 86 | p = ElementTree.SubElement(parent, "feature") 87 | p.set("url", "feature/%s" % os.path.basename(FLAGS.feature[i])) 88 | p.set("id", FLAGS.feature_id[i]) 89 | p.set("version", FLAGS.feature_version[i]) 90 | 91 | 92 | def create_site_xml(tmp_dir): 93 | site = ElementTree.Element("site") 94 | description = ElementTree.SubElement(site, "description") 95 | description.set("name", FLAGS.name) 96 | description.set("url", FLAGS.url) 97 | description.text = FLAGS.description 98 | _features(site) 99 | 100 | # Pretty print the resulting tree 101 | output = ElementTree.tostring(site, "utf-8") 102 | reparsed = minidom.parseString(output) 103 | with open(os.path.join(tmp_dir, "site.xml"), "w") as f: 104 | f.write(reparsed.toprettyxml(indent=" ", encoding="UTF-8")) 105 | 106 | 107 | def copy_artifacts(tmp_dir): 108 | feature_dir = os.path.join(tmp_dir, "features") 109 | bundle_dir = os.path.join(tmp_dir, "plugins") 110 | os.mkdir(feature_dir) 111 | os.mkdir(bundle_dir) 112 | for f in FLAGS.feature: 113 | shutil.copyfile(f, os.path.join(feature_dir, os.path.basename(f))) 114 | for p in FLAGS.bundle: 115 | shutil.copyfile(p, os.path.join(bundle_dir, os.path.basename(p))) 116 | 117 | 118 | def generate_metadata(tmp_dir): 119 | tmp_dir2 = tempfile.mkdtemp() 120 | 121 | args = [ 122 | FLAGS.java, 123 | "-jar", FLAGS.eclipse_launcher, 124 | "-application", "org.eclipse.equinox.p2.publisher.FeaturesAndBundlesPublisher", 125 | "-metadataRepository", "file:/" + tmp_dir, 126 | "-artifactRepository", "file:/" + tmp_dir, 127 | "-configuration", tmp_dir2, 128 | "-source", tmp_dir, 129 | "-compress", "-publishArtifacts"] 130 | process = subprocess.Popen(args, stdout=subprocess.PIPE) 131 | stdout, _ = process.communicate() 132 | if process.returncode: 133 | sys.stdout.write(stdout) 134 | for root, dirs, files in os.walk(tmp_dir2): 135 | for f in files: 136 | if f.endswith(".log"): 137 | with open(os.path.join(root, f), "r") as fi: 138 | sys.stderr.write("Log %s: %s\n" % (f, fi.read())) 139 | shutil.rmtree(tmp_dir) 140 | sys.exit(process.returncode) 141 | shutil.rmtree(tmp_dir2) 142 | 143 | 144 | def _zipinfo(filename): 145 | result = zipfile.ZipInfo(filename, (1980, 1, 1, 0, 0, 0)) 146 | result.external_attr = 0o644 << 16L 147 | return result 148 | 149 | def zip_all(tmp_dir): 150 | with zipfile.ZipFile(FLAGS.output, "w", zipfile.ZIP_DEFLATED) as zf: 151 | for root, dirs, files in os.walk(tmp_dir): 152 | reldir = os.path.relpath(root, tmp_dir) 153 | if reldir == ".": 154 | reldir = "" 155 | for f in files: 156 | with open(os.path.join(root, f), "r") as fi: 157 | zf.writestr(_zipinfo(os.path.join(reldir, f)), fi.read()) 158 | 159 | 160 | def main(unused_argv): 161 | tmp_dir = tempfile.mkdtemp() 162 | create_site_xml(tmp_dir) 163 | copy_artifacts(tmp_dir) 164 | generate_metadata(tmp_dir) 165 | zip_all(tmp_dir) 166 | shutil.rmtree(tmp_dir) 167 | 168 | 169 | if __name__ == "__main__": 170 | main(FLAGS(sys.argv)) 171 | -------------------------------------------------------------------------------- /tools/release/BUILD: -------------------------------------------------------------------------------- 1 | sh_binary( 2 | name = "unzip-updatesite", 3 | srcs = ["unzip-updatesite.sh"], 4 | data = ["//:p2updatesite.zip"], 5 | ) 6 | -------------------------------------------------------------------------------- /tools/release/release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ROOTDIR="$(dirname $(dirname "$(cd "$(dirname "$0")" && pwd -P)"))" 4 | cd "$ROOTDIR" 5 | RELEASE_ORIGIN=${RELEASE_ORIGIN:-"git@github.com:bazelbuild/eclipse"} 6 | 7 | source version.bzl 8 | # Remove qualifier from VERSION if present 9 | VERSION="${VERSION//\.qualifier}" 10 | 11 | make_version_bzl() { 12 | cat <version.bzl 13 | # Note: do not edit, use tools/release/release.sh script instead. 14 | # This file is both a shell script and a skylark file. 15 | VERSION="$1" 16 | EOF 17 | } 18 | 19 | # Create the release tag 20 | make_release_tag() { 21 | # Detach head 22 | git checkout -q --detach 23 | # Update the version.bzl file 24 | make_version_bzl "${VERSION}" 25 | # Create the commit 26 | git commit -q -m "Release ${VERSION}" version.bzl 27 | # Create the tag 28 | git tag "${VERSION}" 29 | # Checkout back master 30 | git checkout -q master 31 | } 32 | 33 | # Create a commit for increasing the version number 34 | make_release_commit() { 35 | git checkout -q master 36 | make_version_bzl "${1}.qualifier" 37 | git commit -q -m "Update version to ${1}.qualifer after release ${2}" \ 38 | version.bzl 39 | } 40 | 41 | # Read the version number from the input 42 | read_version() { 43 | while true; do 44 | echo -n "$1 [$VERSION] " 45 | read ans 46 | if [ -n "$ans" ]; then 47 | if [[ "$ans" =~ [0-9]+(\.[0-9]+)* ]]; then 48 | VERSION="$ans" 49 | return 50 | else 51 | echo "Please enter a version number (e.g. 0.3.0)." >&2 52 | fi 53 | else 54 | return 55 | fi 56 | done 57 | } 58 | 59 | # Produces all possible new versions 60 | incr_version() { 61 | local v=(${1//./ }) 62 | for (( i=${#v[@]}-1; $i >= 0; i=$i-1 )); do 63 | local new_v="" 64 | for (( j=0; $j < ${#v[@]}; j=$j+1 )); do 65 | local vj=${v[$j]} 66 | if (( $j == $i )); then 67 | vj=$(( ${v[$j]} + 1)) 68 | fi 69 | if [ -n "${new_v}" ]; then 70 | new_v="${new_v}.${vj}" 71 | else 72 | new_v="${vj}" 73 | fi 74 | done 75 | echo "${new_v}" 76 | done 77 | } 78 | 79 | # Push the master branch and the given tag 80 | push_master_and_tag() { 81 | git push "${RELEASE_ORIGIN}" master 82 | git push "${RELEASE_ORIGIN}" "$1" 83 | } 84 | 85 | # Do the release itself, the update site is build on GCCB 86 | release() { 87 | read_version "About to release, which version?" 88 | make_release_tag 89 | local old_version="${VERSION}" 90 | local new_versions=($(incr_version "${VERSION}")) 91 | VERSION=${new_versions[0]} 92 | echo "Possible versions for next releases: ${new_versions[@]}" 93 | read_version "Next version will be?" 94 | make_release_commit "${VERSION}" "${old_version}" 95 | push_master_and_tag "${old_version}" 96 | } 97 | 98 | release 99 | -------------------------------------------------------------------------------- /tools/release/release.yaml: -------------------------------------------------------------------------------- 1 | steps: 2 | - name: gcr.io/cloud-builders/bazel 3 | args: ['run', '//tools/release:unzip-updatesite', '--', '/workspace/bazel-updatesite'] 4 | - name: gcr.io/cloud-builders/gsutil 5 | args: ['-m', 'rsync', '-r', '-c', '-d', '/workspace/bazel-updatesite', 'gs://eclipse.bazel.build/updatesite'] 6 | - name: gcr.io/cloud-builders/gsutil 7 | args: ['web', 'set', '-m', 'index.html', '-e', '404.html', 'gs://eclipse.bazel.build'] 8 | - name: gcr.io/cloud-builders/gsutil 9 | args: ['-m', 'acl', 'ch', '-R', '-u', 'AllUsers:R', 'gs://eclipse.bazel.build'] 10 | 11 | timeout: 3600s 12 | -------------------------------------------------------------------------------- /tools/release/unzip-updatesite.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # A small target to run in Google Cloud Container Builder so the result is unzipped. 3 | 4 | OUTPUT_DIR="${1:-bazel-updatesite}" 5 | RUNFILES="${JAVA_RUNFILES:-$0.runfiles}" 6 | 7 | unzip -d "${OUTPUT_DIR}" "${RUNFILES}/build_bazel_eclipse/p2updatesite.zip" 8 | -------------------------------------------------------------------------------- /version.bzl: -------------------------------------------------------------------------------- 1 | # Note: do not edit, use tools/release/release.sh script instead. 2 | # This file is both a shell script and a skylark file. 3 | VERSION="0.0.4.qualifier" 4 | --------------------------------------------------------------------------------