| #!/usr/bin/env python3 |
| # Copyright (C) 2017 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 |
| import argparse |
| import os |
| import subprocess |
| import re |
| import sys |
| import xml.dom.minidom |
| |
| JRE = '/'.join([ |
| 'org.eclipse.jdt.launching.JRE_CONTAINER', |
| 'org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType', |
| 'JavaSE-11', |
| ]) |
| |
| class EclipseProject(): |
| |
| # Path to the Bazel/Bazelisk binary |
| bazel_exe = None |
| # Root of the Bazel project (holding WORKSPACE file) |
| ROOT = None |
| |
| def main(self): |
| self.bazel_exe = self.find_bazel(self.args.bazel_exe) |
| |
| self.ROOT = self.find_root(self.args.root) |
| |
| project_name = (self.args.name if self.args.name |
| else os.path.basename(self.ROOT)) |
| |
| self.gen_project(project_name, root=self.ROOT) |
| self.gen_classpath(self.retrieve_ext_location().decode('utf-8')) |
| |
| def _get_argument_parser(self): |
| opts = argparse.ArgumentParser("Create Eclipse Project") |
| opts.add_argument('-r', '--root', help='Root directory entry', |
| required=True) |
| opts.add_argument('-n', '--name', help='Project name') |
| opts.add_argument('-x', '--exclude', action='append', help='Exclude paths') |
| opts.add_argument('-b', '--batch', action='store_true', |
| dest='batch', help='Bazel batch option') |
| opts.add_argument('--bazel', |
| help=('name of the bazel executable. Defaults to using' |
| ' bazelisk if found, or bazel if bazelisk is not' |
| ' found.'), |
| action='store', default=None, dest='bazel_exe') |
| return opts |
| |
| def parse_args(self, args): |
| self.args = self._get_argument_parser().parse_args(args) |
| return self.args |
| |
| def find_root(self, root): |
| ROOT = os.path.abspath(root) |
| while not os.path.exists(os.path.join(ROOT, 'WORKSPACE')): |
| if ROOT == '/': |
| raise Exception( |
| 'Could not find root of project: no WORKSPACE file found') |
| ROOT = os.path.dirname(ROOT) |
| return ROOT |
| |
| def find_bazel(self, bazel_exe=None): |
| if bazel_exe: |
| try: |
| return subprocess.check_output( |
| ['which', bazel_exe]).strip().decode('UTF-8') |
| except subprocess.CalledProcessError: |
| raise Exception('Bazel command: %s not found' % bazel_exe) |
| try: |
| return subprocess.check_output( |
| ['which', 'bazelisk']).strip().decode('UTF-8') |
| except subprocess.CalledProcessError: |
| try: |
| return subprocess.check_output( |
| ['which', 'bazel']).strip().decode('UTF-8') |
| except subprocess.CalledProcessError: |
| raise Exception( |
| "Neither bazelisk nor bazel found. Please see" |
| " Documentation/dev-bazel for instructions on installing" |
| " one of them.") |
| |
| def _build_bazel_cmd(self, *args): |
| cmd = [self.bazel_exe] |
| if self.args.batch: |
| cmd.append('--batch') |
| for arg in args: |
| cmd.append(arg) |
| return cmd |
| |
| def retrieve_ext_location(self): |
| return subprocess.check_output( |
| self._build_bazel_cmd('info', 'output_base')).strip() |
| |
| def _query_classpath(self): |
| t = '//tools/eclipse:main_classpath_collect' |
| try: |
| cmd = self._build_bazel_cmd('build', t) |
| subprocess.check_call(cmd) |
| except subprocess.CalledProcessError: |
| raise Exception("Could not query classpath with:" % cmd) |
| name = 'bazel-bin/tools/eclipse/' + t.split(':')[1] + '.runtime_classpath' |
| return [line.rstrip('\n') for line in open(name)] |
| |
| def gen_project(self, name, root): |
| p = os.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_classpath(self, ext): |
| def make_classpath(): |
| impl = xml.dom.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() |
| rule_jvm_externals = set() |
| |
| rule_jvm_external = re.compile('bazel-out/.*?-fastbuild/bin/external/maven/(.*[.]jar)$') |
| java_library = re.compile('bazel-out/(?:.*)-fastbuild/bin(.*)/[^/]+[.]jar$') |
| srcs = re.compile('(.*/external/[^/]+)/jar/(.*)[.]jar') |
| for p in self._query_classpath(): |
| |
| m = rule_jvm_external.match(p) |
| if m and ext is not None: |
| p = os.path.join(ext, 'external/unpinned_maven', m.group(1)) |
| rule_jvm_externals.add(p) |
| continue |
| |
| m = java_library.match(p) |
| if m: |
| src.add(m.group(1).lstrip('/')) |
| else: |
| if ext is not None and p.startswith("external"): |
| p = os.path.join(ext, p) |
| lib.add(p) |
| |
| src_paths = {} |
| for s in sorted(src): |
| out = None |
| |
| if s.startswith('lib/'): |
| out = 'eclipse-out/lib' |
| |
| p = os.path.join(s, 'java') |
| if os.path.exists(p): |
| classpathentry('src', p, out=out) |
| continue |
| |
| for env in ['main', 'test', 'java', 'javatests']: |
| o = None |
| if out: |
| o = out + '/' + env |
| elif env == 'test' or env == 'javatests': |
| o = 'eclipse-out/test' |
| |
| if s.startswith(env + '/'): |
| src_paths[env] = o |
| continue |
| |
| for srctype in ['java', 'resources']: |
| p = os.path.join(s, 'src', env, srctype) |
| if os.path.exists(p): |
| src_paths[p] = o |
| |
| for s in src_paths: |
| classpathentry('src', s, out=src_paths[s]) |
| |
| for libs in [lib]: |
| for j in sorted(libs): |
| if self.excluded(j): |
| continue |
| s = None |
| m = srcs.match(j) |
| if m: |
| prefix = m.group(1) |
| suffix = m.group(2) |
| p = os.path.join(prefix, "src", "%s-src.jar" % suffix) |
| if os.path.exists(p): |
| s = p |
| classpathentry('lib', j, s) |
| |
| for r in sorted(rule_jvm_externals): |
| s = r.replace('.jar', '-sources.jar') |
| classpathentry('lib', r, s) |
| |
| classpathentry('con', JRE) |
| classpathentry('output', 'eclipse-out/classes') |
| |
| p = os.path.join(self.ROOT, '.classpath') |
| with open(p, 'w') as fd: |
| doc.writexml(fd, addindent='\t', newl='\n', encoding='UTF-8') |
| |
| def excluded(self, lib): |
| if self.args.exclude: |
| for x in self.args.exclude: |
| if x in lib: |
| return True |
| return False |
| |
| def main(): |
| try: |
| ec = EclipseProject() |
| ec.parse_args(args=sys.argv[1:]) |
| ec.main() |
| except KeyboardInterrupt: |
| print('Interrupted by user', file=sys.stderr) |
| exit(1) |
| |
| |
| if __name__ == '__main__': |
| main() |