-
-
Notifications
You must be signed in to change notification settings - Fork 366
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
Less user-facing dependencies via classloader isolation #941
Conversation
Overall, this allows not to expose plenty of dependencies, most notably fastparse, upickle, jline, etc. Users can in turn load their own versions of those if they want to. This diff shows which ones are removed: $ diff <(coursier resolve com.lihaoyi:ammonite_2.12.8:1.6.4-6-b7dafbe5 |sort) <(coursier resolve com.lihaoyi:ammonite-repl-api_2.12.8:1.6.4-6-b7dafbe5 |sort)
1d0
< com.github.javaparser:javaparser-core:3.2.5:default
3d1
< com.lihaoyi:acyclic_2.12:0.1.5:default
5d2
< com.lihaoyi:ammonite-interp_2.12.8:1.6.4-6-b7dafbe5:compile
8,10d4
< com.lihaoyi:ammonite-repl_2.12.8:1.6.4-6-b7dafbe5:compile
< com.lihaoyi:ammonite-runtime_2.12:1.6.4-6-b7dafbe5:compile
< com.lihaoyi:ammonite-terminal_2.12:1.6.4-6-b7dafbe5:compile
12d5
< com.lihaoyi:ammonite_2.12.8:1.6.4-6-b7dafbe5:compile
14d6
< com.lihaoyi:fastparse_2.12:2.1.0:default
18,25c10
< com.lihaoyi:scalaparse_2.12:2.1.0:default
< com.lihaoyi:sourcecode_2.12:0.1.5:default
< com.lihaoyi:ujson_2.12:0.7.1:default
< com.lihaoyi:upack_2.12:0.7.1:default
< com.lihaoyi:upickle-core_2.12:0.7.1:default
< com.lihaoyi:upickle-implicits_2.12:0.7.1:default
< com.lihaoyi:upickle_2.12:0.7.1:default
< com.lihaoyi:utest_2.12:0.6.4:default
---
> com.lihaoyi:sourcecode_2.12:0.1.3:default
29,33d13
< net.java.dev.jna:jna:4.2.2:default
< org.javassist:javassist:3.21.0-GA:default
< org.jline:jline-reader:3.6.2:default
< org.jline:jline-terminal-jna:3.6.2:default
< org.jline:jline-terminal:3.6.2:default
38d17
< org.scala-sbt:test-interface:1.0:default |
Ultimately, after some more clean-up of user facing stuff, we could enable mima on the user-facing modules, to ensure the API exposed to users doesn't change much. |
8d8e9eb
to
166b6e2
Compare
So a bit of explanation. This PR does two things:
The first point mostly moves things around (f0e41d9, 0d6c3ed, dd12153, 5e168d5, 972562a). It also introduces new bridges (ecc100b, 834fc4b), so that the internals and the dependencies of the frontend and source-related stuff can be hidden too (users only interact with these APIs, rather than by directly tangling with some internal stuff). The second point is also two-fold, and some aspects can probably be tweaked or even done other ways. It consists in ensuring, both in the tests and with the Ammonite launcher, that Ammonite is started with a two classloader setup (with a first classloader with the Getting a two classloader setup in the tests is done by commit 0bb3b6e. This relies on com-lihaoyi/mill#572. This basically passes the Getting those two classloaders with the Ammonite launcher is done in 238c166. This basically replaces the assembly, which merges all dependencies together, by "bootstraps" from coursier. Bootstraps embed the various dependency JARs as resources, along with files listing which ones should be loaded together (see cf5405a then ensures that compilation and classloading of user code starts from the classloader of Lastly, 4631aac, 35389c3, and 166b6e2, do minor refactorings that make things easier for other commits here. And 19f012d proceeds with hiding the upickle dependencies from users, by moving bits of the (user-facing) |
Overall, this adds a bit of complexity (new modules, only a subset of those are accessible to users, classloader hacks in tests and in the launcher). My opinion is it's worth it… This should make the user-facing stuff of Ammonite thinner, and hopefully more stable (in the sense "less changing"). Which should in turn make scripts (and, hmm, notebooks) easier to maintain. |
@lihaoyi If you're ok with the general idea, I can totally split that PR, for easier review: |
Lastly, about the bootstrap stuff replacing assemblies, I went for that as I'm familiar with bootstraps (and they have the advantage of not tangling with bytecode too). But I guess using stuff like jarjar to just rename a bunch of classes (those not accessible to users) could work too. |
@alexarchambault I'm fine with the general idea, but this is a large PR and I might take some time to get around to reviewing it. Hope that's ok! |
Yep, no problem! (This doesn't block anything on my side…) |
@alexarchambault I skimmed this, it looks good to me. My one request would be to make the classpath-isolation configurable, and maybe opt-in from the command line via a flag in My own usage involves files/subprocesses/http/json reasonably heavily even if it isn't necessary for ammonite's own user-facing API, and if want to use Ammonite as a tool for learners/small-projects then the benefit of having all those utilities already bundled outweighs the costs of a slightly polluted classpath. Another place where the classpath pollution is helpful is when embedding Ammonite in a larger application: in this scenario having access to all your application classpath is the point of the embedding. Advanced users like yourself who want a cleaner classpath to better control the runtime environment can then just flip the switch. If you can get tests passing, I think I would be happy to merge this |
So that the actual FrontEnd implems dependencies can be hidden from users
So that the source-related dependencies can be hidden from end-users
18258a5
to
00ca1ae
Compare
Moves stuff available from the test sessions to a new test-api module in particular.
d912777
to
42d950e
Compare
There's still some slight perf regression due to the use of "bootstraps" (that embed the dependencies as resources, so we have "jar-in-jars") instead of assemblies (with no such indirection). I have some workarounds in the making… |
Sounds good! Before merging, also please update the PR description with a more verbose description of the significant changes in the PR. Ideally someone reading would be able to get a good summary of the changes without wading through the large number of changed files |
Pass `--thin` to enable it.
A new line can be inserted right after "default" if its line is too long.
This enables classloader isolation when using the launcher.
Ok, I updated the description. |
@alexarchambault I've noticed some performance regressions that I think arise from this PR. The default REPL (without |
Now that we have this infrastructure in place, here is a bunch more packages it might make sense to keep out of the
|
@lihaoyi You're probably seeing the same kind of slowdown I saw before https://github.com/lihaoyi/Ammonite/pull/941#issuecomment-505542171… I mostly fixed it when The old assembly logic is still around if needed ( I'll add a lengthier explanation here tomorrow about how the "bootstrap" works, to give a better idea about what (I think) is going on. |
About hiding more stuff when
I think os-lib could be removed too. I managed to do it locally some time ago, that requires changing some |
Swapping some of the APIs to |
Couldn't an implicit If necessary, it could even be restricted to the API calls, with some indirection (like defining a |
This PR manages to isolate the core modules and dependencies of Ammonite (
repl
,interp
, upickle, fastparse, …) from the user-facing API. Users effectively don't see the former, and can even load other versions of those if they want.It moves the user-facing parts of
interp
andrepl
to new modulesinterp-api
andrepl-api
.It then manages to:
interp-api
,repl-api
, etc., second one the rest, including the core of Ammonite), by using a custom test runner for the tests, and by packaging Ammonite via a coursier bootstrap rather than an assembly,--thin
option to the Ammonite launcher.