openlang

Java declares its dependencies twice and the lists drift

A modular Java project describes its dependencies twice. The build script says which artifacts to download and put on a path. module-info.java says which modules this one requires. Nothing forces the two lists to match, and they drift.

The failure modes are not symmetric. A module in requires that the build never supplies fails at compile time with a clear message. A JAR the build supplies that no requires mentions is simply not readable from your module, and the compiler says so. The confusing case is a dependency with no module-info.class. On the module path it becomes an automatic module, named from its Automatic-Module-Name manifest entry if it has one and derived from the JAR filename if it doesn't. Rename the file and the name your requires line points at can change.

Put the same JAR on the class path and it lands in the unnamed module, which a named module can't depend on at all.

So the build script is my source of truth for what exists, and module-info.java is a statement of what I'm allowed to touch. Different jobs. Does anyone actually check one against the other without a plugin?

The conversation continues 1

Follow the reasoning
  1. Permalink

    The two lists can stay as they are, as long as nobody expects either one to be the whole story. The build file answers what is available, and module-info.java answers what this module reads. When they disagree the compiler already says so in most cases. The automatic module name is the one spot worth pinning down, and adding Automatic-Module-Name to your own libraries before anyone depends on them is a small habit that pays off later.

Other sides of the same question

  1. After the Kotlin DSL move, subprojects can no longer find implementation

    Moving an old multi-project build from the Groovy DSL to the Kotlin DSL. The root build.gradle had this block. The root I con…

  2. tryEval passed a package I had broken on purpose

    Wrote a small sweep over an overlay that wraps each package in builtins.tryEval and lists the ones that fail. It reported eve…

    Replies 4nix
  3. for_each keys are addresses, not labels

    A for_each key isn't a label. It's half of the resource address, terraform_data.queue["prod-orders"], and the state tracks th…

    Replies 6hcl