Java has Maven and Gradle that serve as package managers. I'd say they are not any more complex than npm, except if you need complexity to do something very specific.
> except if you need complexity to do something very specific.
and i do argue that maven has the complexity correctly silo'ed - you need to write a maven plugin to do anything complex and custom, and fit that plugin into the maven ecosystem/api/framework (however deep you need to go).
As opposed to the 'normal' Makefile style build, where you add instructions and scripts (which i think both Ant and gradle inherited the idea from, and thus neither really makes for better builds imho).
You don’t even need GraalVM. Use jlink (part of the JDK) to create a JVM runtime with only the parts of it that you use, specially if you use the module system. Then you distribute your app as a binary basically, plus the resources it needs (the class files and images/text/etc files). I prefer that to using Graal because very often using Graal requires specifying extra metadata and behavior may differ from what you test, not to mention the atrocious compilation times and its lack of a JIT which means it may start faster, but peak performance is very likely lower.
The difference being that those projects required $$$$ that only big corps were willing to shell out.
Now you get free beer via GraalVM (evolution from MaximeVM efforts at Sun Labs), and OpenJ9, IBM's AOT compiler that traces back to Maestro realtime JVM for embedded development.
So complaining about $$$$ in AOT toolchains is no longer a reason.
Then we have the Android cousin that does a mix of JIT and AOT since Android 7.
I think you might need to look into java (or better kotlin) then. Is gradle not a package manager on top of a build system, just like npm?
Is a fatjar/shadowjar not a "single binary"?
Technically speaking, what’s the difference between native binary requiring some set of OS APIs or portable binary requiring a VM layer on top of OS? User experience can be the same, you can run those things from command line, pipe inputs and outputs etc.
You can compile java aot into binary executable native to your platform. There's many options to do it.
You can do it with a fatjar so that all the dependencies are there. You have all the dependencies clearly stated and versioned (and they are referenced globally - so you don't need to remember where you downloaded that one library from or how to compile that weird dll).
Of course if you actually care about being able to run the code in 20 years - you're far better off leaving it as a fatjar instead of making it an executable.
I have java programs that I made 20 years ago when I was at university that still run on my modern linux machine.
I also wrote C++ programs back then and none of the executables work out of the box (even building one of them is a struggle because half the libraries I used were abandoned - RIP libparagui, some you can't even download from the internet anymore, and most that you can download - don't have versions working with the most recent libc).
That despite the fact I tried to make a static executable for linux when I compiled it back then. I just failed.
I spent a week like 10 years ago trying to make my master's thesis program run again. I basically had to port it to a different graphic library because of dependency hell.
Granted this was years ago when I was a noob. I could not figure it out. But these days I can simply do `cargo test`, `cargo run`, and `cargo add`. I looked up what fat jar is and found this: https://stackoverflow.com/a/36539885
Seriously? This XML reminds me of the issues that I had when I first started Java, what the hell does any of these mean (yes, yes, RTFM) but then I could also just use another language and be done with it?
Fun fact - cargo, npm and other package managers for programming languages were inspired by java maven.
Java is still using maven architecture (even if you use gradle to interact with them).
I'd argue Java was the first language to get dependency management right - you have 1 place you put all your dependencies - globally uniquely identified and versioned, downloading and building them is automated. It's especially jarring when you compare to the chaos in C++ land.
I really want to like Java, or JVM-based languages specifically, but its extremely annoying and complicated to figure out how and why thing works.