1JPM: A Maven/Gradle alternative in a single Java file
github.com
github.com
I don't know how many folks know, although its been there for a bit, that you no longer have to specifically compile single file java programs.
You can just run them with with "java prog.java".
(Honestly, I haven't used the technique myself, still old school -- even with silly small x.java tests and such. Muscle memory and all that.)
$ ./shebang.java
./shebang.java:1: error: illegal character: '#'
#!/usr/bin/java
^
./shebang.java:1: error: class, interface, enum, or record expected
#!/usr/bin/java
^
2 errors
error: compilation failed
Self-executable jars though, that's an interesting idea. No reason why it can't work, zip archives are read from the end, you can put anything in the beginning, including "#!/usr/bin/java -jar" java -jar $0
The rest of it sets up the environment (JAVA_OPTS, -Xmx, etc).Oh so actually, you can use a shebang, just without the .java extension and with the "--source" flag, it's specifically written out in that spec. I was doing my experiment wrong!
(my hate for new build tools https://javarants.com/why-your-new-jvm-build-tool-is-making-...)
Search for Gradle discussions on HN if you're interested, the rough consensus of this site (if there is such a thing) seems to confirm my experience.
What I really hate though is that it's designed around a principle of everything happening 'magically' such that when something goes wrong it is nearly impossible to mechanistically understand what happened and how to fix it. It really leans into the most negative aspects of Groovy and amplifies them.
The downside is that it is insanely complex, and nearly impenetrable for newbies. It’s also for the same reason pretty Perl-like (a “write-only” language)
I guess there is only bazel and gradle that are on this level of capability, being able to compile polyglot code bases. Probably also cmake, but compared to that, gradle looks as if it was designed by God itself, even though it severely lacks in ergonomics.
I would argue Buildkit is also trying to be this kind of tool but is significantly less mature.
That, and the fact that anything slightly out of the ordinary that you'd want to do with maven requires a plugin - or, since most people won't bother, just extra scripts on top of maven, or no automation at all.
And now they end up with what Ant was like.
Only the config step is “turing-complete”, which outputs a build graph that is cached and used to do every further step, finding the tasks that need recompilation, etc.
If config is a code, then it makes it more complicated in the long run (people are people and will add complexity to it) - there is a reason why they work on declarative syntax.
I also don't understand how your comment isn't in direct contradiction to the link you posted (which is from 2013!). At any rate, that there is no innovation in Gradle respective to maven is demonstrably false - you don't have to like these innovations, but they're there.
I know definitively that for me, personally, the quality of my build tool significantly impacts my productivity. Maven does not meet my personal bar and if I needed to use java for whatever reason, I'm not making a sacrifice to the altar of the jvm ecosystem purity and subjecting myself to maven.
> 1JPM is a comparatively new project, thus does not contain all the functionalities of the other major build tools like Maven and Gradle, however should provide the basic and most used functions.
Gradle can do an insane amount of things. I would be surprised if this tool is capable of even 5% what Gradle can do.
That being said, I actually see this as a feature. I think Gradle would actually be better if it could only do 10% of what it currently does. As is it’s so complex and powerful it’s tempting to pack all of your build logic into some impossible to understand and maintain Gradle configuration. Having a pure standalone Java file that people who are familiar with Java can work with seamlessly and is easily integrated with existing test suites is probably better in the long run. I’d rather see the “cruft” of build tooling lifted into a higher level of abstraction than the quasi DSL that is Gradle - IMO it’s too similar yet different to Java/Groovy/Scala that it’s too tempting to create an unmaintainable mess otherwise.
Fit the build process into the build tool, rather than try to contort the build tool to suit the project. After all, it's unlikely that the project is such a special snowflake that it needs custom build steps, rather than just rely on the standard build steps provided in maven.
i really fail to believe that tbh. Making a maven plugin, in the worst case, works.
The flexibility of gradle (or any other procedural build tools) is to make the project _easy_ to customize, which means the tool encourages it.
Maven deliberately forces _all_ builds to conform to their set of phases, and assign meaning to each phase. If you're coming in fresh to a project, it's easy to see what phases are being attached to, and guess what's being done without having to have intimate understanding of the actual work.
For simple projects it looks great. I'll give it a try.
Sure, but resolving and fetching transitive dependencies is part of that 10%.
Moreover, many of those transitive dependencies aren't actually needed (they might be needed for some obscure feature that you don't use). Good libraries might declare those dependencies as optional, but not always.
And almost all dependency conflicts arise from multi-level transitive dependency problems.
May be it's better to just instruct user which transitive dependencies he has to install and let him do the job.
I, myself, miss dumb simple java build tool which would to the very minimum of job. I'm happy to assist it when necessary. Maven is nightmare and Gradle is hell of a nightmare. I don't need that complexity for my projects but I have to pay the price. I often think to downgrade to Makefile or shell script.
I did that in ~2007 with apache ant.
I barely understood the tool (have mercy, I was 15 at the time) and had cobbled up a very basic build.xml file. It did build and it did assemble a runnable jar file, given a classpath containing the dependency jars (i wasn't using many, like 1 or 2).
I still had to download the jars and manually place them in a sub-directory.
if you want a build tool that can be very dumb I'd say that ant is what you need.
It can be as dumb or as smart as you want it to be.
Which would have been great if I found it earlier, since it allows you to use maven but write Java.
The only edge 1JPM has compared to that project is that auto-complete and even more advanced features of your IDE work with it, since you can also move your JPM.java file into ./src/main/java.
You won't need that pom.xml though since 1JPM is only dependent on Java 8.
You can have xml with code-like-structures, e.g. Ant
Quite the opposite, I'd say? Everything needed is source controlled (and signed, if commits are signed).
Also, all the metadata can be queried by the ide (any ide) via an LSP. can you do that with the xml from maven/ant ?
I like this 1JPM so much I might adopt it for all my personal java projects.
Contributions are welcome and I am happy to work on this project.
isn't this just an arbitrary distinction regarding complexity, because i can look at the gradle build script code and - theoretically - vet it in the same way i can vet the xml build file of a maven project. the main difference is just that the one's a relatively simple XML document and the other is a comparable more complex groovy or kotlin program.
or am i missing something else?
Build plugins are the code I was thinking of, you can check the signatures of all the build plugins and whitelist only plugins signed with particular keys, or only specific versions of them. Of course some plugins allow executing arbitrary code, so you'd have to not whitelist those plugins, and if you whitelist keys then you have to trust the holders of those keys not to publish plugins that allow running arbitrary code. But that's reasonably practical, because most maven builds don't use that kind of plugin and maven culture is generally that plugins should only do one specific thing.
> isn't this just an arbitrary distinction regarding complexity, because i can look at the gradle build script code and - theoretically - vet it in the same way i can vet the xml build file of a maven project. the main difference is just that the one's a relatively simple XML document and the other is a comparable more complex groovy or kotlin program.
Theoretically you can't, because it's code in a Turing-complete programming language, and it's theoretically impossible to check any nontrivial property of general programs reliably, because you run into the halting problem.
More practically, it's very easy to safely e.g. pull out the list of dependencies of a Maven build, you just parse the XML (you can even use an XPath expression or something if you want). How would you even begin to do that for a Gradle build? You can't do anything with it until you've executed it as code (you can do a really awful hack like regex search for common patterns, but that's not going to be reliable), and once you've executed it as code it's game over.
(Of course if the maven build is using a plugin that adds extra undeclared dependencies then you'll miss those. But that's not a common thing in maven culture, and we've already said you'd need to check and whitelist plugins)
I'm enjoying this approach. It can likely pull most of the Maven functionality needed for my (simple) projects.
Worked on transitive dependencies, and let me tell you, the version of such a dependency can be literally anywhere.
https://jeka-dev.github.io/jeka/tutorials/build-projects/#ex...
On build tools: I don't actually mind Maven: I use a proper editor that can read and understand an XSD schema. But if it weren't for that I'd totally understand why people dislike it!
Gradle uses "a full programming language" and I hate it but I have to use it for Android projects. Debugging Gradle problems is as fun as you'd imagine. There's a lot of dark matter in your build scripts that you don't see but that influences what happens. And because Android tooling is a clusterfuck ever since the demise of Eclipse+ADT, any change you make to your build process runs the risk of the entire thing exploding in your face.
This one is at least fully predictable, self-contained and doesn't randomly download 150 MB of crap when you're on a slow coffee shop wifi. I'm gonna try it sometime.
Yep, that's exactly why you're much better off with Maven. Last I knew it was still possible to build Android apps with Maven, even if it's not the primary/recommended way.
I agree that gradle is not the most user-friendly software out there, but I can only think of bazel that would be up for the task of building such a complex project as android, so it’s also a testament to its strength.
Maven was okay, Gradle less so. It comes down to being opinionated or infinitely flexible, and being infinitely flexible means that Gradle can be infinitely complex if you have "one of those people" on your team.
Gradle, in my opinion, was a bad move.
It took a while to find an example:
https://github.com/edwardw/high-scale-java-lib/blob/master/b...
Those variable names are madness though haha. Or a single letter class is also pretty wild.
"Install bld through Homebrew, SDKMAN!, JBang, ..."
Literally the first option they list is to just download the jar and run it manually, because this tool truly doesn't need any installation.
Maven itself, afterall, was nothing more than a java application (as was Ant before it).
no interest in build tools, so let's create our own. Baffling.
I'm not saying that there's not accidental complexity in current build tools and that a new build tool couldn't strike the balance better, but a rarely used build tool being simple doesn't really say much (and this much is true for almost any kind of software).
I don't want to have to defend their choice or explain why build tools are often complex and cumbersome.
For example, I wanted a standalone database migration task that wouldn't be run by default. The magic for that is "doLast", which I don't think describes what it's actually doing particularly well.
tasks.register("db-migrate", DatabaseSetup::class) {
doLast {
println("Now migrating")
migrate()
jooqGenerate()
}
}But then again, this is well explained in the docs, it's just that, sadly, today nobody has any time to read docs for the 5000 tools that we use anymore (which I can't fault individual engineers for).
val databaseSetup = tasks.register<DatabaseSetup>("databaseSetup")
tasks.register<GenerateJooq>("generateJooq") {
dependsOn(databaseSetup)
}
class GenerateJooq : DefaultTask() {
@TaskAction fun run() {
println("Now migrating")
migrate()
jooqGenerate()
}
}
It is more code to define the custom task, but then allows for adding command-line input options. The doFirst / doLast are script shortcuts for quick proof-of-concept projects, whereas custom tasks are preferred for maintainability of long-lived ones.Another difference is also that 1JPM is a single file, which has its advantages and disantwages, checkout the README for more details.
Anyone have more examples?
Every dev at some stage early in their career always thinks that they can create a better one.