0

I've been pondering the Java module system and had a question I didn't find addressed here. I poked around a bit more and worked out an answer, so I guess I'll post the info, in case it shortens anyone else's search.

Traditionally, tests are kept in a separate source tree from the main body of a project's code, so the test binaries are easy to separate out of the resulting project's final deliverable. I was unsure how to achieve this with the module system, but it turns out it's still easy.

Our sources for (potentially multi-module) module projects are generally laid out something like "sources/MyModule/src/main/java". We would then specify compilation using --module-source-path set to "source/*/src/main/java" (Note that the quotes are important or your OS will try to expand the asterisk. You don't want that, you want to hand it to javac). And if there are other modules under source, provided they also have the same overall structure, just differing by their names, they will be found and compiled. But, it turns out we seem to be able to split a module across two trees. So, this source layout:

.
└── sources
    ├── ModOne
    │   ├── java
    │   │   ├── module-info.java
    │   │   └── pkg
    │   │       └── Prod.java
    │   └── test
    │       └── pkg
    │           └── Test.java
    └── ModTwo
        └── java
            ├── module-info.java
            └── p2
                └── RunMe.java

Compiles correctly in one go with this command: javac -d out --module-source-path "./sources/*/java:./sources/*/test" --module ModTwo,ModOne Notice I don't specify ModOne the test part doesn't get compiled, but so long as I do so explicitly, it is found, and the output created this directory tree:

.
├── out
│   ├── ModOne
│   │   ├── module-info.class
│   │   └── pkg
│   │       ├── Prod.class
│   │       └── Test.class
│   └── ModTwo
│       ├── module-info.class
│       └── p2
│           └── RunMe.class

The main method in RunMe executes like this: java --module-path out --module ModTwo/p2.RunMe and there's actually a main in my Test class, which runs and successfully has package level access to the rest of ModOne's code.

Contents of the source files in the trees:

sources/ModOne/java/module-info.java: 
module ModOne {
  exports pkg;
}

sources/ModOne/java/pkg/Prod.java:
package pkg;
public class Prod {
  public static String name = "Fred";
}

sources/ModOne/test/pkg/Test.java:
package pkg;
public class Test {
  public static void main(String [] args) {
    System.out.println("Value is " + Prod.name);
  }
}

sources/ModTwo/java/module-info.java:
module ModTwo {
  requires ModOne;
}

sources/ModTwo/java/p2/RunMe.java:
package p2;
public class RunMe {
  public static void main(String [] args) {
    System.out.println("Pulling from ModOne I get name is " + pkg.Prod.name);
  }
}

Toby Eggitt
  • 1,806
  • 19
  • 24
  • 1
    Self-answered questions might just survive; I doubt a bit this "answer" will remain. Nevertheless nice work on the basics. One might compare it with say **maven**, and it is more for some blog. There are miscellaneous java expert/guru sites. – Joop Eggen Jan 14 '20 at 18:01
  • 1
    You should edit your question and extract the answer out into an actual answer, while also making the question an actual question. – Slaw Jan 14 '20 at 20:16

1 Answers1

0

In real life there is more to it, just think of the test framework (e.g., junit) that your test sources must see, but which should not be accessible for your main sources.

I made this use case my main example in a recent presentation titled "Tweak Your Modules":

In a nutshell:

  1. It takes two compilations with different parameters for main & test
  2. Wiring of dependencies is done with options like --patch-module, --add-reads etc.

The good news: tools like Maven or Eclipse do most of this for your automatically.

Stephan Herrmann
  • 7,963
  • 2
  • 27
  • 38