UX of command line dependency management


#1

Coming from Python world, we’ve got a pip installer there, which takes dependencies info from requirements.txt file in human readable format and installs them, often in virtual environment, which is isolated from the system location with packages.

In Java world everything is more complicated. Some projects are using Maven and its pom.xml, which is not intuitive without a tutorial. Some are using Gradle, and I am not yet ready to spend another day learning about it. User story I’ve asked to deal with is the hurdle of managing dependencies both for compilation and for linking into executable.

I’ve got a a simple Hello script to fetch data using 3rd party lib in .jars that I’ve downloaded.

import org.knowm.xchange.ExchangeFactory
import org.knowm.xchange.bitstamp.BitstampExchange;
import org.knowm.xchange.currency.CurrencyPair;

fun main(args: Array<String>) {

    val bitstamp = ExchangeFactory.INSTANCE.createExchange(BitstampExchange::class.java.getName());
    val marketDataService = bitstamp.getMarketDataService();
    val ticker = marketDataService.getTicker(CurrencyPair.BTC_USD);

    println(ticker)
}

Compiling it requires:

kotlin hello.kt -include-runtime -cp xchange-core-4.2.0.jar;xchange-bitstamp-4.2.0.jar -d hello.jar

But executing requires even more libs:

java -cp xchange-core-4.2.0.jar;xchange-bitstamp-4.2.0.jar;slf4j-api-1.7.21.jar;slf4j-simple-1.7.21.jar;rescu-1.9.0.jar;commons-io-2.5.jar;jackson-databind-2.8.4.jar;jackson-core-2.8.4.jar;jackson-annotations-2.8.0.jar;jsr311-api-1.1.1.jar;signpost-core-1.2.1.2.jar;hello.jar HelloKt

Can kotlin just fetch all the necessary stuff and cache it locally for me? Similar how golang does with its go get? That would save me a lot of headache and let concentrate on the code instead of constructing these obscure command lines.


#2

Speaking about caching format, a simple human readable requirements.txt with one line would be nice, but maintaining indented npm-like tree would be more useful to understand what is actual and what is not. Maybe even create this cache file per one module/executable, with a dedicated section about cached libs that are no more actual/not used for this specific build/library.


#3

The Groovy language has a dependency manager built in.

It lets you say things like:

@Grab(‘org.springframework:spring-orm:3.2.5.RELEASE’)
import org.springframework.jdbc.core.JdbcTemplate


#4

You should actually learn about Maven and Gradle. Both of these tools solve the issue of fetching all the necessary stuff, caching it locally and giving you simple command lines to build and run your project. We have no plans of duplicating their functionality in kotlinc.


#5

If I understand it correctly, Maven and Gradle are full-fledged build tools created solely for Java world. Kotlin is a language with many targets, so requiring users to learn all bells and whistles of Java world (which Kotlin promises to escape) looks like an oversight in the long term strategy.

I would add more to this. JetBrains develops IDEs, where key part is static analysis, which is possible due to strong typed nature of Kotlin. It is not a big deal for JetBrain to dump all dependencies that a package is using recursively to implement automatic dependency tracking. Am I right?

Automatic dependency tracking can save many hours during development and testing concepts, and will definitely help to lower entry barrier to Kotlin as it helps to lower entry barrier to Go.


#6

While Maven is indeed a build tool created primarily for the Java world, Gradle is designed to target multiple platforms. Learning to use Gradle doesn’t require learning all the bells and whistles of the Java world. And indeed, we’re focusing on Gradle as the primary build tool for both Kotlin/JS and Kotlin/Native.

If I understand it correctly, you’re offering us strategy advice without actually being informed about the benefits and disadvantages of our existing strategy. While we appreciate the advice, we’d appreciate an informed discussion much more.


#7

I am all for informed discussion, that’s why I said “looks like”. If there is a plan to implement automatic dependency management - I am all ears. =)

benefits and disadvantages of our existing strategy.

That would be interesting to see it even if it omits this particular feature.


#8

For large applications, our strategy is to focus on Gradle as the primary build tool for all target platforms of Kotlin. Automatic discovery of dependencies is not even possible for such applications, and when possible, it’s rarely desirable (not all requirements can be inferred from the source code, and the versions of dependencies you use matter quite a lot).

For small scripts, we plan to provide the possibility to specify dependencies in the source code (similar to the @Grab example above), so that kotlinc -script would download them automatically to your local Maven repository if needed.


#9

The problem is that Java build tools such as maven, gradle, kobalt, etc conflate the dependency/package management aspects with the builld/assembly aspects. Ideally these should have been separated so that one could install/un-install dependencies via a command line tool. maven/gradle/kobalt/etc could then leverage the dependencies to build/assemble units.

Interestingly Ant/Ivy offers such separation. Unfortunately, no one uses it. Ivy came in too late IMHO and by that time, Maven was already too heavily established to be effectively replaced.

So if one needs to write a command-line package manager tool that will be adopted in Java community, one will need to integrate into maven pom.xml, gradle’s build.gradle, kobalt’s Build.kt, etc. Not an easy job.


#10

Thanks for the info. Still is it not clear why automatic discovery of dependencies are not possible for Java and Kotlin native target. Again, Go lang does this. As a matter of fact, dependency management is so essential that no real world computer program is possible without it.

XChange - upstream library for my example, uses Maven. Kotlin is focused on Gradle. Can I use Gradle transparently to manage this Maven dependency?

Speaking of UX, current documentation on Maven usage with Kotlin is made more for seasoned Java developers than for people who want to understand how to manage external libraries for their Kotlin experiments.

Actually, I think that if Kotlin ships as a compiler only, maybe the correct way for command line usage tutorial is to install it through https://gradle.org/ then? OpenFL does this setup and it is then very convenient to update compiler together with libraries and other tools required for development.


#11

Um, no. I do not want to install/uninstall, say, Hibernate. I want to use it in my project, and I want to make sure that I have the correct version that’s compatible with other dependencies of my project. Gradle/Maven takes care of that for me. Using something like a Linux package management tool to manage my Hibernate dependency would be much more work for zero benefit.


#12

Gradle uses exactly the same infrastructure for identifying, managing and downloading dependencies as Maven, so yes, you can totally use Gradle to manage this dependency transparently.

Not sure what you mean by “installing Kotlin through Gradle”. Gradle doesn’t let you install binaries; it lets you refer to plugins in your build.gradle file and automatically downloads the plugins as needed.

Once again: please consider reading some documentation on Gradle.


#13

UX of that Ivy is doubtful. Like many (all?) Java based designs it is too more academical than practical. Installing Ant to install Ivy makes the competence debt even worse - if anything is broken down a toolchain you need skills in every component over this toolchain to debug it. It is exactly why I am here, and it is exactly why I kind of dissatisfied that I need to learn Kotlin, Maven and Java, when all I wanted is to call some functions from .jar file and wrap it as a single executable.

XMLisms like dependency org="commons-lang" name="commons-lang" rev="2.0"/> are really really hard to understand after getting deep into the world of scripting languages, where the same thing is expressed just as commons-lang==2.0 and could be typed and remembered by humans easily.

Interface is important. Entry barrier height is important. Ability to reverse existing project from scratch without reading boring manuals is important. Maybe Ivy finally does provide automatic dependency management, but the way to it is already too complicated for the first look.


#14

So, if I use Gradle plugin for Kotlin, I don’t need to directly download and use Kotlin command line tools at all? That may be helpful.


#15

Yes, this is correct.


#16

I didn’t mean using a linux package manager. I meant something specialized for Java similar to node’s yarn or rusts cargo. So you could do something like jdep add hibernate and it would add hibernate and its dependencies to the pom.xml/build.gradle/Build.kt, etc. Ideally the dependency information should have been in a separate simple standardized file independent of build tool implementation and then imported into the build script. But this standardization never happened in the java world. Anyways, most developers from other programming communities find this quite convenient.


#17

It will probably take a day to write such a tool that would add a dependency to your build.gradle or pom.xml. Or you can use a feature in your IDE which already exists and does the same thing. You don’t actually need to add Hibernate’s dependencies; Maven/Gradle will take care of those for you.

Given that dependencies of a Java project can have different scopes (test/production) or apply only to a specific Maven profile or Gradle flavor, a “standardized” file would be neither simple nor independent of the concepts of the build tool it was made for.


#18

With respect to Go, it doesn’t actually do dependencies very well, so it’s not something to hold up as an ideal. The problem being its disinterest in versioning (because inside Google all dependencies are copied into their internal source tree, which is enormous).

I’ve often thought that a simple “starter edition” build tool would be a nice thing to have. It could always generate a gradle project later if you outgrew it.


#19

I tried to pick up gradle, but because dependencies are managed using Maven, most of the time was spent learning about mvn phases and goals. I still struggle trying to figure out how to pack all needed dependencies with it into one .jar together. As I understand dependencies that are required to compile Java sources and libs may be different from dependencies that are needed to run those compiled binaries. Does the latter set (run-time dependencies) includes the former (compilation deps)?


#20

I would second both learning to use Gradle, and using Gradle for native builds. My employer uses Gradle for a mix of C/C++, Java, and JS/HTML builds, using a set of plugins we developed in-house (https://bitbucket.org/nm2501/holy-gradle-plugins). The main thing those plugins do, though, is just zip and unzip the multiple output files of C-style builds, and symlink the unzipped folders into your build folder tree, which would be not too hard to implement directly. (They also do Windows authentication and multi-repo, multi-repo-type builds, among other things … but are currently stuck on Gradle 1.4. Still works well, though.)

I’ve also used Ant+Ivy in a previous job. Any significant build becomes a program in its own right. Trying to write a program, or even a build configuration file, in XML is about as much fun as you would imagine. (See also: MSBuild.)

To use Gradle for your own builds, I recommend using IntelliJ to create a Gradle project with Kotlin enabled, then just add to the dependencies block. For Kotlin JVM, I assume the meanings of the configurations follow the Gradle Java plugin, as documented at https://docs.gradle.org/3.5/userguide/java_plugin.html#sec:java_plugin_and_dependency_management; specifically, the runtime configuration includes (extends) the compile one.

A Gradle Configuration object implements Enumerable<File> to give you the contents of that configuration more-or-less as a classpath, though there are other ways to get that information. See the DSL doc for the JavaExec task (https://docs.gradle.org/3.5/dsl/org.gradle.api.tasks.JavaExec.html) for an example of how to launch a particular Java main class with the appropriate classpath.

If you’re really keen, you can write your Gradle build files in Kotlin these days :slight_smile: See https://github.com/gradle/gradle-script-kotlin.