| #!/usr/bin/env python | |
| # Copyright (C) 2016 The Android Open Source Project | |
| # | |
| # Licensed under the Apache License, Version 2.0 (the "License"); | |
| # you may not use this file except in compliance with the License. | |
| # You may obtain a copy of the License at | |
| # | |
| # http://www.apache.org/licenses/LICENSE-2.0 | |
| # | |
| # Unless required by applicable law or agreed to in writing, software | |
| # distributed under the License is distributed on an "AS IS" BASIS, | |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| # See the License for the specific language governing permissions and | |
| # limitations under the License. | |
| # | |
| from __future__ import print_function | |
| # TODO(davido): use Google style for importing instead: | |
| # import optparse | |
| # ... | |
| # optparse.OptionParser | |
| from optparse import OptionParser | |
| from os import environ, path, makedirs | |
| from subprocess import CalledProcessError, check_call, check_output | |
| from xml.dom import minidom | |
| import re | |
| import sys | |
| MAIN = '//tools/eclipse:classpath' | |
| GWT = '//gerrit-gwtui:ui_module' | |
| AUTO = '//lib/auto:auto-value' | |
| JRE = '/'.join([ | |
| 'org.eclipse.jdt.launching.JRE_CONTAINER', | |
| 'org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType', | |
| 'JavaSE-1.8', | |
| ]) | |
| # Map of targets to corresponding classpath collector rules | |
| cp_targets = { | |
| AUTO: '//tools/eclipse:autovalue_classpath_collect', | |
| GWT: '//tools/eclipse:gwt_classpath_collect', | |
| MAIN: '//tools/eclipse:main_classpath_collect', | |
| } | |
| ROOT = path.abspath(__file__) | |
| while not path.exists(path.join(ROOT, 'WORKSPACE')): | |
| ROOT = path.dirname(ROOT) | |
| opts = OptionParser() | |
| opts.add_option('--plugins', help='create eclipse projects for plugins', | |
| action='store_true') | |
| opts.add_option('--name', help='name of the generated project', | |
| action='store', default='gerrit', dest='project_name') | |
| opts.add_option('-b', '--batch', action='store_true', | |
| dest='batch', help='Bazel batch option') | |
| args, _ = opts.parse_args() | |
| batch_option = '--batch' if args.batch else None | |
| def _build_bazel_cmd(*args): | |
| cmd = ['bazel'] | |
| if batch_option: | |
| cmd.append('--batch') | |
| for arg in args: | |
| cmd.append(arg) | |
| return cmd | |
| def retrieve_ext_location(): | |
| return check_output(_build_bazel_cmd('info', 'output_base')).strip() | |
| def gen_bazel_path(): | |
| bazel = check_output(['which', 'bazel']).strip().decode('UTF-8') | |
| with open(path.join(ROOT, ".bazel_path"), 'w') as fd: | |
| fd.write("bazel=%s\n" % bazel) | |
| fd.write("PATH=%s\n" % environ["PATH"]) | |
| def _query_classpath(target): | |
| deps = [] | |
| t = cp_targets[target] | |
| try: | |
| check_call(_build_bazel_cmd('build', t)) | |
| except CalledProcessError: | |
| exit(1) | |
| name = 'bazel-bin/tools/eclipse/' + t.split(':')[1] + '.runtime_classpath' | |
| deps = [line.rstrip('\n') for line in open(name)] | |
| return deps | |
| def gen_project(name='gerrit', root=ROOT): | |
| p = path.join(root, '.project') | |
| with open(p, 'w') as fd: | |
| print("""\ | |
| <?xml version="1.0" encoding="UTF-8"?> | |
| <projectDescription> | |
| <name>%(name)s</name> | |
| <buildSpec> | |
| <buildCommand> | |
| <name>org.eclipse.jdt.core.javabuilder</name> | |
| </buildCommand> | |
| </buildSpec> | |
| <natures> | |
| <nature>org.eclipse.jdt.core.javanature</nature> | |
| </natures> | |
| </projectDescription>\ | |
| """ % {"name": name}, file=fd) | |
| def gen_plugin_classpath(root): | |
| p = path.join(root, '.classpath') | |
| with open(p, 'w') as fd: | |
| if path.exists(path.join(root, 'src', 'test', 'java')): | |
| testpath = """ | |
| <classpathentry excluding="**/BUILD" kind="src" path="src/test/java"\ | |
| out="eclipse-out/test"/>""" | |
| else: | |
| testpath = "" | |
| print("""\ | |
| <?xml version="1.0" encoding="UTF-8"?> | |
| <classpath> | |
| <classpathentry excluding="**/BUILD" kind="src" path="src/main/java"/>%(testpath)s | |
| <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> | |
| <classpathentry combineaccessrules="false" kind="src" path="/gerrit"/> | |
| <classpathentry kind="output" path="eclipse-out/classes"/> | |
| </classpath>""" % {"testpath": testpath}, file=fd) | |
| def gen_classpath(ext): | |
| def make_classpath(): | |
| impl = minidom.getDOMImplementation() | |
| return impl.createDocument(None, 'classpath', None) | |
| def classpathentry(kind, path, src=None, out=None, exported=None): | |
| e = doc.createElement('classpathentry') | |
| e.setAttribute('kind', kind) | |
| # TODO(davido): Remove this and other exclude BUILD files hack | |
| # when this Bazel bug is fixed: | |
| # https://github.com/bazelbuild/bazel/issues/1083 | |
| if kind == 'src': | |
| e.setAttribute('excluding', '**/BUILD') | |
| e.setAttribute('path', path) | |
| if src: | |
| e.setAttribute('sourcepath', src) | |
| if out: | |
| e.setAttribute('output', out) | |
| if exported: | |
| e.setAttribute('exported', 'true') | |
| doc.documentElement.appendChild(e) | |
| doc = make_classpath() | |
| src = set() | |
| lib = set() | |
| proto = set() | |
| gwt_src = set() | |
| gwt_lib = set() | |
| plugins = set() | |
| # Classpath entries are absolute for cross-cell support | |
| java_library = re.compile('bazel-out/.*?-fastbuild/bin/(.*)/[^/]+[.]jar$') | |
| srcs = re.compile('(.*/external/[^/]+)/jar/(.*)[.]jar') | |
| for p in _query_classpath(MAIN): | |
| if p.endswith('-src.jar'): | |
| # gwt_module() depends on -src.jar for Java to JavaScript compiles. | |
| if p.startswith("external"): | |
| p = path.join(ext, p) | |
| gwt_lib.add(p) | |
| continue | |
| m = java_library.match(p) | |
| if m: | |
| src.add(m.group(1)) | |
| # Exceptions: both source and lib | |
| if p.endswith('libquery_parser.jar') or \ | |
| p.endswith('libgerrit-prolog-common.jar'): | |
| lib.add(p) | |
| # JGit dependency from external repository | |
| if 'gerrit-' not in p and 'jgit' in p: | |
| lib.add(p) | |
| # Assume any jars in /proto/ are from java_proto_library rules | |
| if '/bin/proto/' in p: | |
| proto.add(p) | |
| else: | |
| # Don't mess up with Bazel internal test runner dependencies. | |
| # When we use Eclipse we rely on it for running the tests | |
| if p.endswith( | |
| "external/bazel_tools/tools/jdk/TestRunner_deploy.jar"): | |
| continue | |
| if p.startswith("external"): | |
| p = path.join(ext, p) | |
| lib.add(p) | |
| for p in _query_classpath(GWT): | |
| m = java_library.match(p) | |
| if m: | |
| gwt_src.add(m.group(1)) | |
| classpathentry('src', 'java') | |
| classpathentry('src', 'javatests', out='eclipse-out/test') | |
| classpathentry('src', 'resources') | |
| for s in sorted(src): | |
| out = None | |
| if s.startswith('lib/'): | |
| out = 'eclipse-out/lib' | |
| elif s.startswith('plugins/'): | |
| if args.plugins: | |
| plugins.add(s) | |
| continue | |
| out = 'eclipse-out/' + s | |
| p = path.join(s, 'java') | |
| if path.exists(p): | |
| classpathentry('src', p, out=out) | |
| continue | |
| for env in ['main', 'test']: | |
| o = None | |
| if out: | |
| o = out + '/' + env | |
| elif env == 'test': | |
| o = 'eclipse-out/test' | |
| for srctype in ['java', 'resources']: | |
| p = path.join(s, 'src', env, srctype) | |
| if path.exists(p): | |
| classpathentry('src', p, out=o) | |
| for libs in [lib, gwt_lib]: | |
| for j in sorted(libs): | |
| s = None | |
| m = srcs.match(j) | |
| if m: | |
| prefix = m.group(1) | |
| suffix = m.group(2) | |
| p = path.join(prefix, "jar", "%s-src.jar" % suffix) | |
| if path.exists(p): | |
| s = p | |
| if args.plugins: | |
| classpathentry('lib', j, s, exported=True) | |
| else: | |
| # Filter out the source JARs that we pull through transitive | |
| # closure of GWT plugin API (we add source directories | |
| # themselves). Exception is libEdit-src.jar, that is needed | |
| # for GWT SDM to work. | |
| m = java_library.match(j) | |
| if m: | |
| if m.group(1).startswith("gerrit-") and \ | |
| j.endswith("-src.jar") and \ | |
| not j.endswith("libEdit-src.jar"): | |
| continue | |
| classpathentry('lib', j, s) | |
| for p in sorted(proto): | |
| s = p.replace('-fastbuild/bin/proto/lib', '-fastbuild/genfiles/proto/') | |
| s = s.replace('.jar', '-src.jar') | |
| classpathentry('lib', p, s) | |
| for s in sorted(gwt_src): | |
| p = path.join(ROOT, s, 'src', 'main', 'java') | |
| if path.exists(p): | |
| classpathentry('lib', p, out='eclipse-out/gwtsrc') | |
| classpathentry('con', JRE) | |
| classpathentry('output', 'eclipse-out/classes') | |
| p = path.join(ROOT, '.classpath') | |
| with open(p, 'w') as fd: | |
| doc.writexml(fd, addindent='\t', newl='\n', encoding='UTF-8') | |
| if args.plugins: | |
| for plugin in plugins: | |
| plugindir = path.join(ROOT, plugin) | |
| try: | |
| gen_project(plugin.replace('plugins/', ""), plugindir) | |
| gen_plugin_classpath(plugindir) | |
| except (IOError, OSError) as err: | |
| print('error generating project for %s: %s' % (plugin, err), | |
| file=sys.stderr) | |
| def gen_factorypath(ext): | |
| doc = minidom.getDOMImplementation().createDocument(None, 'factorypath', | |
| None) | |
| for jar in _query_classpath(AUTO): | |
| e = doc.createElement('factorypathentry') | |
| e.setAttribute('kind', 'EXTJAR') | |
| e.setAttribute('id', path.join(ext, jar)) | |
| e.setAttribute('enabled', 'true') | |
| e.setAttribute('runInBatchMode', 'false') | |
| doc.documentElement.appendChild(e) | |
| p = path.join(ROOT, '.factorypath') | |
| with open(p, 'w') as fd: | |
| doc.writexml(fd, addindent='\t', newl='\n', encoding='UTF-8') | |
| try: | |
| ext_location = retrieve_ext_location().decode("utf-8") | |
| gen_project(args.project_name) | |
| gen_classpath(ext_location) | |
| gen_factorypath(ext_location) | |
| gen_bazel_path() | |
| # TODO(davido): Remove this when GWT gone | |
| gwt_working_dir = ".gwt_work_dir" | |
| if not path.isdir(gwt_working_dir): | |
| makedirs(path.join(ROOT, gwt_working_dir)) | |
| try: | |
| check_call(_build_bazel_cmd('build', MAIN, GWT, | |
| '//java/org/eclipse/jgit:libEdit-src.jar')) | |
| except CalledProcessError: | |
| exit(1) | |
| except KeyboardInterrupt: | |
| print('Interrupted by user', file=sys.stderr) | |
| exit(1) |