I've got a multi-module Maven project, consisting in the following - simplified - structure
parent
|__ aggregator
|__ project1
|__ aggregator
|__ project2
|__ aggregator
|__ project3
where project3 depends on project2, and project2 depends on project1.
I'd like to be able to invoke lifecycle phases such as initialize, generate-sources, process-resources, without having to compile, or install locally beforehand.
To be practical, this is what a new developer should do to be able to start coding:
git clone my-project
cd my-project
mvn initialize
After reading half a hundred answers about multi-module Maven builds on StackOverflow and a dozen blog posts, and after debugging the dependencies resolution process myself in search for the miracle, I came to the realization this is not possible by default.
I could, however, use the pluggable Extensions mechanism to inject my own Plexus Component to change a default behavior.
Maven uses a WorkspaceReader to try and resolve dependencies in the local workspace, when they cannot be found in remote or local repositories.
Specifically the default reader is ReactorReader.
If we take a look a the ReactorReader#findArtifact method, and at the private ReactorReader#find method, we can clearly see that what Maven evaluates is the lifecycle phase, which must be at least compile, and the presence of the packaging type in the COMPILE_PHASE_TYPES list.
Since I want to invoke, e.g. initialize, and since my packaging type is not on that list,
ReactorReader#findArtifact returns null, telling Maven "hey I can't find this dependency, abort".
But damn, I'm not trying to compile! I'm just trying to invoke some goals on initialize, which do not require dependencies.
And here comes the idea: contributing my own WorkspaceReader, which for phases before compile and for workspace project only returns a dummy File instance.
A simplified snippet is:
@Override
public File findArtifact(final Artifact artifact) {
// defaultReader is ReactorReader, and is injected through @Requirement or @Named
final var file = defaultReader.findArtifact(artifact);
// If the dependency has not been found by the default ReactorReader,
// and if it meets certain conditions, let's return a dummy file
if (file == null && isForcible(artifact)) {
final var localProject = getProject(artifact);
return new File(localProject.getBuild().getOutputDirectory());
}
return file;
}
And that's it. What's cool is that it works, I'm able to execute whatever phase prior to compile without errors.
The real question is, how dangerous is this? What could I break? What could go wrong?
Funny journey tho, spent an entire week on this.