#!/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. # # TODO(sop): Remove hack after Buck supports Eclipse 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') args, _ = opts.parse_args() def retrieve_ext_location(): return check_output(['bazel', 'info', 'output_base']).strip() def gen_primary_build_tool(): bazel = check_output(['which', 'bazel']).strip() with open(path.join(ROOT, ".primary_build_tool"), '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(['bazel', '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("""\ %(name)s org.eclipse.jdt.core.javabuilder org.eclipse.jdt.core.javanature \ """ % {"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 = """ """ else: testpath = "" print("""\ %(testpath)s """ % {"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() gwt_src = set() gwt_lib = set() plugins = set() # Classpath entries are absolute for cross-cell support java_library = re.compile('bazel-out/local-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('prolog/libcommon.jar'): lib.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)) 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 themself). 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 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() gen_project(args.project_name) gen_classpath(ext_location) gen_factorypath(ext_location) gen_primary_build_tool() # 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(['bazel', 'build', MAIN, GWT, '//gerrit-patch-jgit:libEdit-src.jar']) except CalledProcessError: exit(1) except KeyboardInterrupt: print('Interrupted by user', file=sys.stderr) exit(1)