-
-
Notifications
You must be signed in to change notification settings - Fork 652
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Java dependency analysis types and launcher using javaparser library. #12890
Conversation
@stuhood Can you take a look at
|
See https://docs.google.com/document/d/1HpJn2jTWf5sKob6zhe4SqHZ7KWBlX4a6OJOwnuw-IHo/edit#. Yes, this is okay thanks to "file targets". That concept will soon be easier to understand as we'll have dedicated |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great!
- Should I make this result "fallible" like you did with javac? My inclination is yes.
Yea, I believe so. Failing to extract imports from a file is something that we should either ignore or warn for (if we can be confident that the parser only fails for truly invalid syntax).
- Does the
pkg_resources
hack for on-the-fly compilation of Java seem reasonable?
Yes, very: for a few reasons. The big one is that it's somewhat likely that as with Python we'll need multiple versions of the parsing library to support new syntax introduced by new JVM versions (over time: not now). Maybe the syntax is stable enough that having a "very modern version" of the parsing library can deal with all previous syntaxes: but if the parser version is JVM dependent, we might not be able to rely on the very modern version of the parsing library working on, say: JDK8.
- Is there a cleaner way to dump the in-memory Java source into a "synthetic" target so I can just request the classfiles from the regular javac rules, rather than reimplement javac here?
There is not currently... as it stands, any "synthetic targets" (basically only file targets, but see Eric's comment) that exist are associated-with/parented-by a BUILD file target. Allowing for a target without any concrete BUILD target as its parent would require adding the big "is_synthetic" property that v1 had, which was a bit of a landmine for goals (since it wasn't typesafe that the target wasn't a "real" target).
We might be able to solve this in the medium term: but for now, some code duplication will be necessary.
- Do you see any issues with requiring that this always operate over a single source? I'm concerned that this is going to make using it awkward, but it seems like the whole idea here is that we can always guarantee that java source is reduced to a single source file, even if it later gets grouped back together by other rules.
No, operating on a single file is totally appropriate. nailgun
should make it fine from a performance perspective, and it's the right thing to do in general.
Also, as an aside: empty __init__.py
files shouldn't be necessary: did something break without them?
src/python/pants/backend/java/dependency_inference/__init__.py
from pants.engine.rules import Get, collect_rules, rule | ||
from pants.engine.target import Sources | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll ignore import_parser*.py
for now, since I don't see any blockers for using the java-based parser.
], | ||
input_digest=merged_digest, | ||
output_directories=("classfiles",), | ||
description=f"Compile {LAUNCHER_BASENAME} import processors with javac", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: import parser
would align with the Python side.
argv=[ | ||
coursier.coursier.exe, | ||
"java", | ||
"--system-jvm", # TODO(#12293): use a fixed JDK version from a subsystem. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The javac
subsystem should probably be renamed, and this and junit
should probably consume the flag for this. Could probably cheat and consume it even with its current name.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we need a jvm
subsystem?
|
||
|
||
@rule | ||
async def parse_java_package(request: ParseJavaPackageRequest) -> ParsedJavaPackage: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the medium (possibly even short?) term, it would be great to have an option that asserts that filenames will match packages so that we can skip parsing, and directly use the filename (by stripping source roots).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is that a restriction we're willing to add? In general, Java packages don't need to match source directories; that only matters for classfiles on the classpath (or in a jar). See the various unit tests in the Java subsystem that exercise this for examples
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Behind an option? Yea, I think so. Well behaved monorepos are very likely to follow it. But because it wouldn't be the default, it's probably not a priority.
a0bf6f5
to
b501a3f
Compare
I pushed another big update to this PR. I've got first party source analysis to a place I'm pretty happy with:
However I still have a few major issues I'm concerned about, and I'd like your thoughts, Stu:
|
Hm, @stuhood , note that we do fail for Python import parsing errors.
Indeed, it is not! But it very soon can be: #12957. We can do that same PR for Java, and because Java is still experimental, we don't need to do that weird hack where both the atom target and generator target use the same symbol name: we can have |
You could if we switch the Java support to use target generation and something similar to #12957. |
b501a3f
to
b8c2f6a
Compare
b8c2f6a
to
bfdd658
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm just some Java style comments plus the need to match current API of coursier_lockfile
.
src/python/pants/backend/java/dependency_inference/PantsJavaParserLauncher.java
Outdated
Show resolved
Hide resolved
src/python/pants/backend/java/dependency_inference/PantsJavaParserLauncher.java
Outdated
Show resolved
Hide resolved
src/python/pants/backend/java/dependency_inference/java_parser.py
Outdated
Show resolved
Hide resolved
argv=[ | ||
coursier.coursier.exe, | ||
"java", | ||
"--system-jvm", # TODO(#12293): use a fixed JDK version from a subsystem. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we need a jvm
subsystem?
src/python/pants/backend/java/dependency_inference/java_parser_launcher.py
Outdated
Show resolved
Hide resolved
src/python/pants/backend/java/dependency_inference/java_parser_test.py
Outdated
Show resolved
Hide resolved
src/python/pants/backend/java/dependency_inference/java_parser_test.py
Outdated
Show resolved
Hide resolved
src/python/pants/backend/java/dependency_inference/java_parser_test.py
Outdated
Show resolved
Hide resolved
…brary. # Rust tests and lints will be skipped. Delete if not intended. [ci skip-rust] # Building wheels and fs_util will be skipped. Delete if not intended. [ci skip-build-wheels]
…analysis (to satisfy MyPy) # Rust tests and lints will be skipped. Delete if not intended. [ci skip-rust] # Building wheels and fs_util will be skipped. Delete if not intended. [ci skip-build-wheels]
# Rust tests and lints will be skipped. Delete if not intended. [ci skip-rust] # Building wheels and fs_util will be skipped. Delete if not intended. [ci skip-build-wheels]
c6a49a0
to
14d5126
Compare
@patricklaw : Awesome stuff. Thanks!
That might be reasonable for Python. But it's overkill on the JVM I think, because the compiler will fail and give you a much better error message about invalid syntax than the parsing library is likely to. So, my recommendation would be to either ignore parsing errors entirely (if we aren't confident that the parsing library won't ever fail to parse something that I don't think that we should error on import extraction until we're very confident in the parser's ability to parse all Java code it encounters in the wild. |
This PR adds the core functionality for inferring from Java source:
It uses the
com.github.javaparser:javaparser-symbol-solver
Java library to do the heavy lifting. We have a custom launcher for calling into this library, and that launcher source is compiled on-the-fly by Pants (the source itself is loaded usingpkg_resources
). This is a novel way of calling into Pants-provided JVM code that will probably act as a template for similar situations, like custom JUnit launchers.[ci skip-rust]
[ci skip-build-wheels]