Skip to content

[MNG-8713] SourceRoot.directory() default value should include the module when present #2278

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

desruisseaux
Copy link
Contributor

If a <source> element in POM does not provide any value for the <directory> element, the default value is currently defined as src/${scope}/${lang}. This default value provides the familiar src/main/java and src/test/java default directories. However, if the <source> element contains a <module> element, the module name must appear somewhere in the default directory name, otherwise the source code of multi-modular projects will collide.

Different conventions are possible:

  • OpenJDK uses src/${module}/ for the main code and a bazar of directories for the tests with no obvious order.
  • Project Jigsaw: Module System Quick-Start Guide uses src/${module}/ for main code and does not mention tests.
  • junit5-modular-world uses src/main/${module}/ for the main code and src/test/${module}/ for the test code. However, it also uses module-info.java in test code, which I would like to discourage.
  • NetBeans Ant modular project uses src/${module}/main for the main code and src/${module}/test for the test code.

I propose the NetBeans's convention, which is implemented in this pull request as a default directory of src/${lang}/${module}/${scope} when the <module> element is present.

Alternative

If it is considered too premature to adopt a default directory name for modular project, we should at least throw an exception asking user to specify a directory explicitly if a <module> element is present.

@@ -79,7 +79,12 @@ public DefaultSourceRoot(final Session session, final Path baseDir, final Source
if (value != null) {
directory = baseDir.resolve(value);
} else {
directory = baseDir.resolve("src").resolve(scope.id()).resolve(language.id());
var src = baseDir.resolve("src");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we use var in maven codebase? I kinda remember we agreed not to? (but I may be missing some later agreement).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, it was my mistake.

Personally, I sometime use var in expressions such as var x = new Foo() when there is no way for the type to be something else than what we can read on the line, and usually refrain to use it in other contexts. I do not know if there is a Maven policy.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I strongly prefer to avoid var. IMHO it should never have been added to the language. I want to know the types of things when I'm reading the code. var hides that info. I also don't want method return types to change without explicit consideration and inspection of every call point.

@desruisseaux desruisseaux force-pushed the module-in-default-directory branch from 71b9748 to 90933fe Compare April 27, 2025 21:33
Copy link
Contributor

@elharo elharo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A change this significant requires a linked JIRA issue.

@desruisseaux
Copy link
Contributor Author

@elharo elharo changed the title SourceRoot.directory() default value should include the module when present [MNG-8713] SourceRoot.directory() default value should include the module when present Apr 29, 2025
* The default value depends on whether a {@linkplain #module() module name} is specified in this source root:
* <ul>
* <li>
* If no module name, then the default directory is
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shifting from main/java to java/main feels like a really big change. Is there a very compelling reason to do this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reminder: this pull request does not change the convention of non-modular projects or projects that do not use the module source hierarchy: for them, the default stay src/main/java. Only for projects that use the module source hierarchy, we would "flip" from (nothing that exist currently in Maven) to src/java/org.my.module/main.

The reason for putting ${lang} before ${module} is because the module names tend to be language-dependent. 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.

The reason for putting ${scope} after ${module} is for grouping together the files related to each module. This is the same division as Maven subprojects (previously named "modules"): each subproject contains main, test, resource and pom.xml files. Likewise, each Java module would contain its own main, test, resource files, with module-info somewhat playing the role of pom.xml. In other words, we are still splitting the projects in modules, but shifting from a world where modules are managed by Maven to a world were modules are managed by Java.

There is precedents: ${module}/${scope} is the directory layout of NetBeans's Ant modular project, of Christian Stein's blog (except for ${lang}) and of the example given in Oracle's documentation (except for src and ${lang}).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense to me. @elharo ?

Copy link
Contributor

@elharo elharo May 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not convinced. That feels very wrong to me, and I expect users will be unnecessarily confused. What we've had in the past is src/main/java/package-structure and the natural extension of that is src/main/java/module/package-structure

If other languages come into play (which I really don't think will happen or is something we need to worry about) then src/main/python/module or whatever

Changing anything to src/java/main introduces inconsistency we don't have now.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure that's the right angle to look at.

If a given project has multiple modules, those modules kinda act as subprojects, so it makes sense to group their files together, rather than splitting main / tests. In that case, each test directory is testing the code of the module, so having them close to each other makes more sense to me.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, the rational is as Guillaume said. While we try to avoid disruption for users who are not interested in JPMS, those who want to embrace fully JPMS will have to change some habits anyway, some of them out of our control (e.g., the directory layout of javac output).

There is a difficulty with resources, which are considered as a language and therefore would be stored apart with a src/${lang}/${module}/${scope} layout if the users do not specify the directory themselves. An alternative staying closer to current Maven practice may be src/${module}/${scope}/${lang}.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This layout is closer to the usual maven layout and may be less disruptive. It just adds the module before the scope. I like it better actually.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The inconvenient of the alternative proposal is that if a subproject has two or more languages, the modules of different languages will be mixed in the same directory. However, it may not be important if, as suggested in other comments, we choose to not let the multi-languages issue influences the decision.

Note: this difficulty come from the fact that in the current non-modular layout, resources is implicitly understood as "resources of the Java artifact". It is not really an independent "language".

If there is no objection or alternative proposal, I will change the layout to src/${module}/${scope}/${lang} in a few days.

@gnodet gnodet added this to the 4.0.0-rc-4 milestone May 9, 2025
@elharo
Copy link
Contributor

elharo commented May 10, 2025

Maybe introduce a new modules directory and put all the non-defallt modules there? E.g. modules/{module-name}/src/...

That is, today's src/... would contain only code for the default module, no others.

@gnodet
Copy link
Contributor

gnodet commented May 10, 2025

Maybe introduce a new modules directory and put all the non-defallt modules there? E.g. modules/{module-name}/src/...

That is, today's src/... would contain only code for the default module, no others.

I don't think you could mix both in the same maven subproject, so not sure that would help.

@elharo
Copy link
Contributor

elharo commented May 10, 2025

Since this is a major user facing design decision, this should be written up in a detailed proposal, not in code, and sent to the dev list for broader discussion. Code can come after there's reasonable consensus on the design.

@desruisseaux
Copy link
Contributor Author

An advantage of src/${module}/${scope}/${lang} is that it is close to the javac output, which is target/classes/${module}. The ${module} part is generated by javac itself and cannot be changed.

@desruisseaux
Copy link
Contributor Author

Okay, I will write a document. Do we have a wiki or something like that for this kind of discussion?

@desruisseaux
Copy link
Contributor Author

That is, today's src/... would contain only code for the default module, no others.

don't think you could mix both in the same maven subproject, so not sure that would help.

I confirm. When Java Module Source Hierarchy is used, there is no default module.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants