tools/eclipse: find jar from rules_jvm_external
The Eclipse project.py script crawls through Bazel `output_base` to find
the project jar dependencies. It recognizes location written by
`maven_jar`.
When using `rules_jvm_external` artifacts are symlinked under different
paths and the utility is unable to find them. It results in a generated
`.classpath` missing most entries.
`rules_jvm_external` symlinks the cached jar under:
bazel-out/k8-fastbuild/bin/external/maven
This change adds support to find them there.
In order to have the JavaDoc displayed, the source jar have to be
retrieved as well which `rules_jvm_external` does not do by default. One
has to turn that on explicitly:
load("@rules_jvm_external//:defs.bzl", "maven_install")
maven_install(
artifacts = ['x', 'y', 'z' ],
fetch_sources = True,
)
The source jar will then be made available with the suffix
`-sources.jar`, unlike `maven_jar` which uses `-src.jar`.
Change-Id: Ifb56b2f84b5f38a700362c1de436cd4b4714dc2f
diff --git a/tools/eclipse/project.py b/tools/eclipse/project.py
index 93eeb3a..d92dbb6 100755
--- a/tools/eclipse/project.py
+++ b/tools/eclipse/project.py
@@ -116,7 +116,7 @@
name = 'bazel-bin/tools/eclipse/' + t.split(':')[1] + '.runtime_classpath'
return [line.rstrip('\n') for line in open(name)]
- def gen_project(name, root):
+ def gen_project(self, name, root):
p = os.path.join(root, '.project')
with open(p, 'w') as fd:
print("""\
@@ -159,10 +159,19 @@
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('/'))
@@ -216,6 +225,10 @@
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')
diff --git a/tools/eclipse/test_project.py b/tools/eclipse/test_project.py
index be73474..4fba1a1 100755
--- a/tools/eclipse/test_project.py
+++ b/tools/eclipse/test_project.py
@@ -11,7 +11,7 @@
from io import StringIO
import unittest
-from unittest.mock import call, patch
+from unittest.mock import call, mock_open, patch
from subprocess import CalledProcessError
import project
@@ -38,7 +38,7 @@
project.main()
self.assertIn('Interrupted by user\n', stderr.getvalue())
- self.assertEquals(c.exception.code, 1)
+ self.assertEqual(c.exception.code, 1)
@patch('sys.stderr', new_callable=StringIO)
def test_requires_root_option(self, stderr):
@@ -55,10 +55,10 @@
ep = EclipseProject()
ep.parse_args(['-r', '/dev/null'])
ep.bazel_exe = 'my_bazel'
- self.assertEquals(ep._build_bazel_cmd(), ['my_bazel'])
+ self.assertEqual(ep._build_bazel_cmd(), ['my_bazel'])
ep.parse_args(['-r', '/dev/null', '--batch'])
- self.assertEquals(ep._build_bazel_cmd(), ['my_bazel', '--batch'])
+ self.assertEqual(ep._build_bazel_cmd(), ['my_bazel', '--batch'])
def test_find_root_raises_when_no_WORKSPACE_found(self):
with patch('os.path.exists') as exists:
@@ -127,6 +127,80 @@
ep.parse_args(['-r' '/dev/null', '--bazel', 'my_bazel'])
assert not ep.retrieve_ext_location().endswith('\n')
+class GenClassPathTestCase(unittest.TestCase):
+
+ maxDiff = None
+
+ def gen_classpath(self, classpath):
+ ep = EclipseProject()
+ ep.parse_args(['-r', '/dev/null'])
+ ep.ROOT = '/path/to'
+
+ with patch.object(ep, '_query_classpath', return_value=[classpath]):
+ opener = mock_open()
+ with patch('builtins.open', opener):
+ ep.gen_classpath(ext='ext_loc')
+
+ written = ''
+ for write_call in opener().write.mock_calls:
+ written += write_call[1][0]
+
+ return written
+
+ def test_includes_external_gerrit_plugin_api(self):
+ self.assertIn(
+ ('<classpathentry kind="lib" '
+ 'path="ext_loc/external/gerrit_plugin_api/jar/gerrit-plugin-api-3.4.0.jar"'
+ ),
+ # TODO we should check the sourcepath has been detected by mocking os.path.exists
+ self.gen_classpath(
+ 'external/gerrit_plugin_api/jar/gerrit-plugin-api-3.4.0.jar'
+ ),
+ msg='plugin code is included'
+ )
+
+ def test_recognizes_maven_jar_dependencies(self):
+ self.assertIn(
+ ('<classpathentry kind="lib" '
+ 'path="ext_loc/external/gerrit_plugin_api/jar/'
+ 'gerrit-plugin-api-X.X.X.jar"/>'),
+ self.gen_classpath(
+ 'external/gerrit_plugin_api/jar/gerrit-plugin-api-X.X.X.jar',
+ ),
+ msg='maven_jar() dependency listed as a "lib" classpathentry'
+ )
+
+ def test_finds_rules_jvm_external_dependencies(self):
+ self.assertIn(
+ ('<classpathentry kind="lib" '
+ 'path="ext_loc/external/unpinned_maven/v1/https/repo1.maven.org'
+ '/maven2/com/fasterxml/classmate/1.5.1/classmate-1.5.1.jar" '
+ 'sourcepath="ext_loc/external/unpinned_maven/v1/https/repo1.maven.org'
+ '/maven2/com/fasterxml/classmate/1.5.1/classmate-1.5.1-sources.jar"/>'),
+ self.gen_classpath(
+ ('bazel-out/k8-fastbuild/bin/external/maven/v1/'
+ 'https/repo1.maven.org/maven2/'
+ 'com/fasterxml/classmate/1.5.1/classmate-1.5.1.jar')
+ ),
+ msg='artifact on Maven Central'
+ )
+
+ def test_finds_rules_jvm_external_dependencies_out_of_maven_central(self):
+ self.assertIn(
+ ('<classpathentry kind="lib" '
+ 'path="ext_loc/external/unpinned_maven/v1/https/archiva.wikimedia.org'
+ '/repository/releases/org/wikimedia/eventutilities/1.1.0/eventutilities-1.1.0.jar" '
+ 'sourcepath="ext_loc/external/unpinned_maven/v1/https/archiva.wikimedia.org'
+ '/repository/releases/org/wikimedia/eventutilities/1.1.0/eventutilities-1.1.0-sources.jar"/>'
+ ),
+ self.gen_classpath(
+ ('bazel-out/k8-fastbuild/bin/external/maven/v1/'
+ 'https/archiva.wikimedia.org/repository/releases/'
+ 'org/wikimedia/eventutilities/1.1.0/eventutilities-1.1.0.jar')
+ ),
+ msg='artifact hosted outside of Maven Central'
+ )
+
if __name__ == "__main__":
unittest.main(verbosity=2)