Skip to content
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

Running formatter:format goal in a multimodule project skips all submodules #261

Closed
hea3ven opened this issue Nov 24, 2017 · 27 comments
Closed
Assignees
Milestone

Comments

@hea3ven
Copy link

hea3ven commented Nov 24, 2017

I've been trying to configure the formatter plugin for a multimodule project, the thing is that I don't want to integrate it to the project's lifecycle, I just want to be able to run it manually. The problem is that when I run mvn formatter:format, all the submodules are skipped and no formatting is done, if I instead run the goal in a specific project mvn -pl <submodule id> formatter:format, it works correctly.

I've tried with a minimal project both, configuring formatter-maven-plugin in the parent pom, and configuring it in the children: formatter-in-multimodule-project.zip, with the following output:

[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Build Order:
[INFO]
[INFO] Multiproject Parent
[INFO] Build Tools
[INFO] Module A
[INFO] Module B
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building Multiproject Parent 1.0.0
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- formatter-maven-plugin:2.7.1:format (default-cli) @ multiproject-parent ---
[INFO] Using 'UTF-8' encoding to format source files.
[INFO] Number of files to be formatted: 0
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO]
[INFO] Multiproject Parent ................................ SUCCESS [  0.577 s]
[INFO] Build Tools ........................................ SKIPPED
[INFO] Module A ........................................... SKIPPED
[INFO] Module B ........................................... SKIPPED
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.796 s
[INFO] Finished at: 2017-11-24T13:00:34-03:00
[INFO] Final Memory: 6M/155M
[INFO] ------------------------------------------------------------------------

The parent pom looks like this:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

        <modelVersion>4.0.0</modelVersion>

        <groupId>com.example.multiproject</groupId>
        <artifactId>multiproject-parent</artifactId>
        <version>1.0.0</version>
        <packaging>pom</packaging>
        <name>Multiproject Parent</name>
        <build>
                <plugins>
                        <plugin>
                                <groupId>net.revelc.code.formatter</groupId>
                                <artifactId>formatter-maven-plugin</artifactId>
                                <version>2.7.1</version>
                                <configuration>
                                        <configFile>eclipse/kn_formatter.xml</configFile>
                                        <encoding>UTF-8</encoding>
                                </configuration>
                                <dependencies>
                                        <dependency>
                                                <groupId>com.example.multiproject</groupId>
                                                <artifactId>build-tools</artifactId>
                                                <version>1.0.0</version>
                                        </dependency>
                                </dependencies>
                        </plugin>
                </plugins>
        </build>
        <modules>
                <module>build-tools</module>
                <module>moduleA</module>
                <module>moduleB</module>
        </modules>
</project>```

Am I doing something wrong, or is this use case not supported?
@hazendaz
Copy link
Member

I was able to confirm in another project that this in fact does fail to properly run when issued directory as indicated here. If I run with normal build cycle, formatter works fine in all modules. In my example I have 22 modules that nest pretty deep in some cases.

@hea3ven Thanks for the report. What I would suggest though is just integrating this into your build. Once you have done so, its not a significant step and doesn't cause much hassle. The biggest problem of being integrated is that it exposes developers ability to write code without running a full build, commit that to the repo, and move on. We are trying to resolve that as well so it just always formats but there are memory leaks that are currently preventing it being turned on in IDE such as m2e integration.

@ctubbsii
Copy link
Member

@hea3ven mvn formatter:format does not execute any part of the maven build lifecycle which is multi-module aware. It simply executes the formatter plugin directly. Instead, try executing as part of a maven lifecycle which is multi-module aware, such as: mvn clean formatter:format or mvn compile formatter:format.

I highly recommend reading more about Maven lifecycles and plugin execution at: Maven – Introduction to the Build Lifecycle

@hazendaz
Copy link
Member

@ctubbsii

Clean does not work since that is not the cycle this is in. Compile does. This still seems weird solution. Most other plugins I've always seen just run the plugin name. Is this something we can enhance?

@ctubbsii
Copy link
Member

@hazendaz I didn't think it would matter which lifecycle the plugin normally executes in, since the plugin is being executed directly, and not part of a lifecycle. What matters is that the reactor build plan has been set up to execute the specified lifecycles/goals, and both the clean and default lifecycles should prepare a reactor build. So, I'm not sure why the clean lifecycle wouldn't work, but I haven't tested it.

I strongly suspect the reason this isn't working as expected, is because we've explicitly said it does not need to be executed in a project-context with requiresProject = false in the @Mojo. So, Maven doesn't create a build plan... it just executes the plugin and that's it. If it's reading the POM at all, it's probably only doing so so it can get configuration from it for that single non-project execution.

If I am right, then executing mvn compile formatter:format (assuming pluginGroups is configured correctly for net.revelc.code.formatter, mvn compile net.revelc.code:formatter-maven-plugin:2.7.0:format otherwise) should be a sufficient workaround.

@hazendaz
Copy link
Member

Yes mvn compile formatter:format does work. With 'clean' it does not. I tested that much on your feedback. It is suffficient for a work around but now questioning if we can make it work better. Are you thinking if the requires project is true it might? I can test that shortly.

@ctubbsii
Copy link
Member

requiresProject = true is default. I tested impsort-maven-plugin on a multi-module project and it worked on all modules. So, if formatter isn't working, it could be that. If it is, then we're going to have to decide which behavior we want more: formatting outside of projects or more intuitive handling of multi-module projects, because we might not be able to have both.

The other thing it could be is that there's no standard MavenProject parameter in formatter, like there normally is, so Maven doesn't bother constructing it so it can be injected into the plugin. The following taken from impsort-maven-plugin:

  @Parameter(defaultValue = "${project}", readonly = true)                                          
  protected MavenProject project;                                                                   
                                                                                                    
  @Parameter(defaultValue = "${plugin}", readonly = true)                                           
  protected PluginDescriptor plugin;                                                                

Honestly, I hope the latter fixes the problem, because it's quite convenient to execute outside of a project, and if we don't have to give that up, that's better.

@hazendaz
Copy link
Member

@ctubbsii What does it mean to execute outside of a project? I don't understand that.

I tested 'requires_project = true', that fixes the issue entirely.

@ctubbsii
Copy link
Member

Executing outside of a project means Maven is used to resolve plugin dependencies and execute the plugin code, but it can be executed from any directory, not just one containing a valid pom.xml file. This would be useful, for example, if you wanted to do a one-time format of code in an ant project. It's also used by things like the jetty maven plugin, to run a jetty server which serves a war, but not as part of any project.

It's mostly used for executing services or to validate or transform a project without actually parsing that project. For example, you can use the eclipse maven plugin to materialize a blank eclipse project from the air. This might be how Maven archetypes work, too.

@hazendaz
Copy link
Member

Ah so this all makes sense now. Sort of wish maven handled both appropriately. But now I understand why it did nothing for user here. Internally we are telling it the source package locations. So if it doesn't have src/main/java and same on test side, it won't actually format anything as indicated and that would be expected given how that actually works.

I'm not sure if the way it is possible to run is actually used as I've never heard or seen any feedback on that ability. If we simply flip it to require a project, issue solved here but no clue on impact. Maybe another mojo potential is possible for that type of feature to run in non maven projects?

@ctubbsii
Copy link
Member

I've been kind of wanting to try adding the snippet above to add the MavenProject variable, rather than making the requiresProject true, just to see if that would work. My theory is that it might create a sort of "soft" dependency on the project, triggering Maven to resolve the project if it is run within a project. I'm not actually sure, though, since I've been hit with some sort of seasonal illness and haven't been feeling well enough to try it out.

If that doesn't work, and it's kind of a long shot anyway, then I think we should just make it require the project. If there's a use case for running it outside of a project, we can create a separate Mojo with a different name for that.

@hea3ven
Copy link
Author

hea3ven commented Dec 12, 2017

Thanks for the feedback. I tried both options and injecting the Maven project into the mojo didn't seem to do anything. But setting requiresProject did make it work as expected:

$ mvn formatter:format
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Build Order:
[INFO]
[INFO] Multiproject Parent
[INFO] Build Tools
[INFO] Module A
[INFO] Module B
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building Multiproject Parent 1.0.0
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- formatter-maven-plugin:2.7.1:format (default-cli) @ multiproject-parent ---
[INFO] Project is multiproject-parent
[INFO] Using 'UTF-8' encoding to format source files.
[INFO] Number of files to be formatted: 0
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building Build Tools 1.0.0
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- formatter-maven-plugin:2.7.1:format (default-cli) @ build-tools ---
[INFO] Project is build-tools
[INFO] Using 'UTF-8' encoding to format source files.
[INFO] Number of files to be formatted: 0
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building Module A 1.0.0
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- formatter-maven-plugin:2.7.1:format (default-cli) @ moduleA ---
[INFO] Project is moduleA
[INFO] Using 'UTF-8' encoding to format source files.
[INFO] Number of files to be formatted: 0
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building Module B 1.0.0
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- formatter-maven-plugin:2.7.1:format (default-cli) @ moduleB ---
[INFO] Project is moduleB
[INFO] Using 'UTF-8' encoding to format source files.
[INFO] Number of files to be formatted: 0
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO]
[INFO] Multiproject Parent ................................ SUCCESS [  0.498 s]
[INFO] Build Tools ........................................ SUCCESS [  0.003 s]
[INFO] Module A ........................................... SUCCESS [  0.002 s]
[INFO] Module B ........................................... SUCCESS [  0.002 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.703 s
[INFO] Finished at: 2017-12-12T09:59:38-03:00
[INFO] Final Memory: 6M/155M
[INFO] ------------------------------------------------------------------------

I think having the project-less formatting in a separate command would be useful to publicize the feature, otherwise, I don't know how anyone would have known this feature existed.

@ctubbsii
Copy link
Member

I think setting requiresProject = true would be good for this mojo. Another mojo could be called "format-directory" with requiresProject = false, and with fewer assumptions about which directories are being formatted (src/main/*), more required parameters for non-project executions.

@ctubbsii
Copy link
Member

ctubbsii commented Jan 8, 2018

Setting requiresProject = true would likely conflict with the feature request in #265, but a separate mojo would still address the problem. I think this mojo should be deprecated, and replaced with a formatDirectory (with requiresProject = false) and formatProject (with requiresProject = true).

@victornoel
Copy link

I couldn't make this work even with mvn compile formatter:validate: maven insists on executing validate only for the aggregator module and not for the submodules, even though it does execute compile for them.

Can someone confirm they can make this work with the workaround?

@ctubbsii
Copy link
Member

@victornoel I suspect there's something else going on in your project. Do you have an example project which demonstrates the problem?

@victornoel
Copy link

@ctubbsii you right: I didn't add the execution for the validate goal in the plugin configuration. It is was on purpose (I was expecting that calling the goal explicitly would be enough) but I understand why it didn't work then. I suppose you have to add it. thanks

@ctubbsii
Copy link
Member

Ah yes. That's another way to make it behave as though it's not part of a project build. If we set require project to true, then it probably would have worked the way you expected.

@bryceChernecki
Copy link

I was able to get the workaround mvn compile formatter:validate to execute. Is there a way when running mvn compile to have it skip this execution to have it fit nicer into our CI environment? Something like -DSkipTests? The reason is we don't want compilation issues getting mixed in with formatting issues. (We have explicit Jenkins Stages to clarify what went wrong with the build)
Thanks

@ctubbsii
Copy link
Member

ctubbsii commented Mar 1, 2018

You could put the validation in a profile, which is active normally (based on presence of pom.xml file or something), but can be explicitly disabled with -P!validateFormattingProfile in CI.

@bryceChernecki
Copy link

bryceChernecki commented Mar 1, 2018

@ctubbsii thanks I'll give that a try.

I've noticed something as well. When mvn compile formatter:validate it appears (at least for me) to be reporting validation of the sub modules but not actually validating them. My test case is below

  1. explicitly violate formatting in file in sub-module
  2. run mvn compile formatter:validate from root (get described results above -- all passing ??)
  3. CD to sub-module and execute mvn compile formatter:validate sub module fails as expected.

@ctubbsii
Copy link
Member

ctubbsii commented Mar 1, 2018

@bryceChernecki That sounds like an entirely new issue. Can you create an example project which demonstrates this bug, so I can take a look?

@schuettec
Copy link

schuettec commented Apr 10, 2018

Hey guys, whats up with this one?

I have the same problem. I'm trying to setup formatting for a large multi module project. The plugin is configured to validate the formatting in phase validate. When the formatting rules are violated I want to trigger the format goal on the parent module and I would expect the child modules to be formatted, but mvn formatter:format only triggers the formatting for the parent module.

Here is a sample project that demonstrates the problem: https://github.com/schuettec/multimodule-formatter

My thoughts about the discussion above: I think the plugin should work with multi module support. If there is a requirement to provide a plugin that is working outside a project, it should be a separate Mojo.
A Maven plugin should work in a Maven project and support all basic features like multi-modules in the first place. If there is a chance to reuse a plugin feature in a non-Maven situation, this should be separated. Otherwise your are about to implement a Maven plugin that does not support Maven Projects but works for non-Maven uses cases,....which is weird ¯_(ツ)_/¯

@hazendaz
Copy link
Member

hazendaz commented Apr 10, 2018 via email

@schuettec
Copy link

Have you tried the example project? It works. My last checks also made me believe that it picks the correct formatter. I don't want those build-tools artifacts in my build because that is completely unnecessary overhead in my opinion. The only thing that does not work is calling formatter:format from the parent module.

@ctubbsii
Copy link
Member

@schuettec mvn formatter:format will not process submodules, because, as mentioned earlier, this does not build within a project lifecycle which is aware of submodules. Use mvn compile formatter:format instead, or put the format goal in a profile which will execute at a certain point in the build lifecycle while executing a standard build goal (like compile, package, or verify). If you read through this whole thread, you can find where this is discussed in more detail.

@schuettec
Copy link

I strongly suspect the reason this isn't working as expected, is because we've explicitly said it does not need to be executed in a project-context with requiresProject = false in the @mojo. So, Maven doesn't create a build plan...

I understand this as the an explanation of why the plugin is not executed within a lifecycle.

I tested 'requires_project = true', that fixes the issue entirely.

What's the reason for not applying this mentioned fix here?

The command mvn compile formatter:format does not work and this is no workaround for me. What happens is, that the phase validate detects formatting violations, so compile will never be executed. I explicitly want the formatting to be validated as early as possible in the CI build because compiling the sources is entirely unecessary.

@ctubbsii
Copy link
Member

What's the reason for not applying this mentioned fix here?

Doing this will prevent other valid use cases, so it's not been my highest priority, personally, especially since it's not that hard to force it into a build lifecycle with a profile if the command line workaround doesn't work for whatever reason.

I explicitly want the formatting to be validated as early as possible in the CI build because compiling the sources is entirely unecessary.

If I were you, I would bind validate to process-resources and format to process-sources or similar, and put the latter in a profile. Or put them both in their own profiles. Running as early as possible, before compilation, doesn't mean you're restricted to the validate phase. There's plenty of other build lifecycle phases before compilation to use. https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html

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

No branches or pull requests

6 participants