Maven 4 introduces a new way to describe the root directories of source code. The <sourceDirectory> element is deprecated and replaced by the following:

<build>
  <sources>
    <source>
      <directory>src/main/java</directory>
    </source>
  </sources>
<build>

This new <source> element accepts some additional child elements. Some of them are <scope> (with main as the default value), <lang> (with java as the default value) and <module>. The latter element is for specifying the name of the JPMS module for which the sources are provided.

If the <directory> element is absent, then its default value is derived from the values of the <scope> and <lang> elements as following: src/${scope}/${lang}. This default value is consistent with the current Maven usage and is fine for non-modular projects. However, in the context of a multi-modules project (in JPMS sense), the module names must appear somewhere in the directory layout. Therefore, a new default layout needs to be decided for the JPMS case.

NOTE 1: This new directory layout applies only to modular projects, when the <module> element has a value. Non-modular projects are not impacted and do not need to change anything.

NOTE 2: A new directory layout implies changes in users habits. However, the use of JPMS implies changes anyway. In particular, the directory layout of the target directory is also impacted because the classes/(packages) layout become classes/${module}/(packages) where the ${module} directory is automatically inserted by javac itself. The addition of a ${module} directory is not a Maven's invention.

Proposals

While the default directory needs the value of the <module> element somewhere, we have flexibility about where to insert the element relatively to the <scope> and <lang> elements. Various proposals have been discussed in MNG-2278 and are reported below.

src/${module}/${scope}/${lang}

This proposal is identical to the current well-known Maven default layout, with only ${module} inserted before the scope. The rational is that if a project has multiple modules, those modules kinda act as subprojects. Each Maven subproject contains main, test, resources and pom.xml files. Likewise, each Java module would contain its own main, test and resources files, with module-info.java somewhat playing the role of pom.xml. With ${module} before ${scope}, each module have their files grouped together rather than splitting main and tests.

Example (25 lines):

src
├─ org.module1
│  ├─ main
│  │  ├─ java
│  │  │  ├─ module-info.java
│  │  │  └─ org/module1/*.java
│  │  └─ resources
│  │     └─ org/module1/*.properties
│  └─ test
│     ├─ java
│     │  └─ org/module1/*Test.java
│     └─ resources
│        └─ org/module1/*Test.properties
└─ org.module2
   ├─ main
   │  ├─ java
   │  │  ├─ module-info.java
   │  │  └─ org/module2/*.java
   │  └─ resources
   │     └─ org/module2/*.properties
   └─ test
      ├─ java
      │  └─ org/module2/*Test.java
      └─ resources
         └─ org/module2/*Test.properties

src/${lang}/${module}/${scope}

This proposal is similar to above proposal, except that it moves the ${lang} element first. The rational for this change is because each language has its own concept of modules. For example, Java uses URL domain names (e.g. org.apache.maven) while Python uses shorter module names, without org or com prefix. If a Maven subproject mixes Java and Python modules, placing the modules of the two languages in the same directory is confusing. While Maven is probably rarely used for building pure Python applications, mixing Java with Python (or other languages) in the same application is more common, especially for scientific applications and applications using artificial intelligence toolboxes.

Example (20 lines, ignoring the Python case):

src
├─ java
│  ├─ org.module1
│  │  ├─ main
│  │  │  ├─ module-info.java
│  │  │  └─ org/module1/*.java
│  │  └─ test
│  │     └─ org/module1/*Test.java
│  └─ org.module2
│     ├─ main
│     │  ├─ module-info.java
│     │  └─ org/module2/*.java
│     └─ test
│        └─ org/module2/*Test.java
├─ resources   // (see discussion below)
│  └─ org.module1
│     ├─ main
│     │  └─ org/module1/*.properties
│     └─ test
│        └─ org/module1/*Test.properties
└─ python
   └─ moduleZ
      └─ main
         └─ moduleZ/*.py

Discussion

Having ${lang} last in the directory layout was not an issue until now because Maven 3 was not module-aware. But now that Maven 4 is becoming module-aware, the fact that modules are language-specific becomes an issue. We can ignore and let users sort themselves this issue by specifying explicitly the <directory> values. Or at the opposite, we can do more significant changes to old Maven users habits for taking in account this evolution of the software industry. The latter is what this src/${lang}/${module}/${scope} proposal tries to do.

However, in addition of biggest change to Maven users habits, this proposal has another problem. Because Maven considers resources as a language (a ${lang} value), the src/${lang}/${module}/${scope} layout would cause the resources to be stored apart (as shown in the example below), unless the users specify the directory themselves. Note that this difficulty come from the fact that in the current Maven 3 layout, resources is implicitly understood as "resources of the Java artifact". It is not really an independent "language". This issue could be addressed by adding a special rule for resources, but it would complicate more this layout.

This proposal produces the most compact directory layout: 20 lines compared to 25 in other proposals (it could be yet more compact if the resources issue is addressed). This is because this proposal avoids the repetition of both ${module} and ${lang} directories, while other proposals avoid the repetition of only one or the other.

src/${scope}/${lang}/${module}

This proposal is identical to the current well-known Maven default layout, with only ${module} added last. This proposal considers Java module layout as an extension to Java package layout, directly connected to it.

Example (25 lines):

src
├─ main
│  ├─ java
│  │  ├─ org.module1
│  │  │  ├─ module-info.java
│  │  │  └─ org/module1/*.java
│  │  └─ org.module2
│  │     ├─ module-info.java
│  │     └─ org/module2/*.java
│  └─ resources
│     ├─ org.module1
│     │  └─ org/module1/*.properties
│     └─ org.module2
│        └─ org/module2/*.properties
└─ test
   ├─ java
   │  ├─ org.module1
   │  │  └─ org/module1/*Test.java
   │  └─ org.module2
   │     └─ org/module2/*Test.java
   └─ resources
      ├─ org.module1
      │  └─ org/module1/*Test.properties
      └─ org.module2
         └─ org/module2/*Test.properties


src/${scope}/${module}/${lang}

This proposal is identical to the current well-known Maven default layout, with only ${module} added in the middle.

Example (25 lines):

src
├─ main
│  ├─ org.module1
│  │  ├─ java
│  │  │  ├─ module-info.java
│  │  │  └─ org/module1/*.java
│  │  └─ resources
│  │     └─ org/module1/*.properties
│ ├─ org.module2 │ │ ├─ java │ │ │ ├─ module-info.java │ │ │ └─ org/module2/*.java │ │ └─ resources │ │ └─ org/module2/*.properties └─ test ├─ org.module1 │ ├─ java │ │ └─ org/module1/*Test.java │ └─ resources │ └─ org/module1/*Test.properties └─ org.module2 ├─ java │ └─org/module2/*Test.java └─ resources └─ org/module2/*Test.properties


  • No labels