1

I have a multi-module project, i.e.

parent
  module1
  module2

In one dev cycle, I added a class mod1.A to module1. Class mod2.B in module2 depends on it.

I do not have the artifacts in my local .m2/repository. Running this:

$ cd prj/module2
$ mvn -o exec:java -Dexec.mainClass=mod2.B

results in an error along the lines of:

The following artifacts could not be resolved: com.example:module1:jar:1.0-SNAPSHOT

After I install the artifacts via mvn install while in the prj folder, it all works as expected.

However, this presents an issue in at least two ways:

  • I have to go through the slower install phase instead of the faster compile phase
  • I have two versions of the same project and conflicting modifications in these. I cannot run the same Java class with their respective modifications, only the currently installed modifications, considering they are both the same SNAPSHOT version

There are workaround for both (skip parts of the build for the first, different snapshot versions for the second), but they are far from usable in practice.

Is there a way to make maven use the local modules, instead of using artifacts from local maven repository?

levant pied
  • 3,886
  • 5
  • 37
  • 56
  • Run Maven from the parent itself, see http://stackoverflow.com/questions/33131880/maven-multi-module-project-cannot-find-sibling-module. You can run `mvn clean package` on the POM of `parent` and Maven will resolve inter-module dependencies correctly, without installing anything. – Tunaki Aug 19 '16 at 09:56
  • Thanks @Tunaki - that works for `mvn clean package`, but looks like `mvn exec:java` doesn't work in that case. Maybe because it would not know which submodule to exec from in case there are multiple classes with the same name? I also tried http://stackoverflow.com/questions/11091311/maven-execjava-goal-on-a-multi-module-project, but that did not help, same artifact resolving error I gave in the question. – levant pied Aug 19 '16 at 13:57
  • 1
    Yes, that's normal. You should bind the execution `exec:java` goal to a specific phase (like `package`). Or you can build a single module, like shown here http://stackoverflow.com/a/3899772/1743880 – Tunaki Aug 19 '16 at 14:27

3 Answers3

2

If I understand your question correctly, it seems like you are living a bit outside the norm here: you have two local "copies" of the project with different modifications, that you want to work with alternately when running "exec:java". And Maven is getting in your way: it expects your local .m2 repository area to be in play, but the version strings in each copy are the same, so you end up with the changes interfering among the copies.

To me, it sounds like what you are trying to do is to test your changes. I suggest you just write an actual JUnit or TestNG test in module2 that tests what you want (it can just call mod2.B Main if you want). Then, from your chosen project directory, you can run mvn test -Dtest=MyTestName. It won't "install" anything and it will find the dependencies the way you want it to.

Otherwise, I can see three options.

  1. Change the version string locally in one of the copies (mvn versions:set -DnewVersion=B-SNAPSHOT can do this for you). That way any "installed" jars from your work on that copy will not be considered by the other copy, and vice-versa. You refer to this as being "far from usable" ... I think it should be fine? These are different versions of the project! They should have different version strings! I strongly recommend this option out of the three. (You can do mvn versions:revert when done if you used :set, or you can rely on version control to undo the change.)
  2. Select a different local repository used by Maven when working on one of the projects, with a command-line flag as per https://stackoverflow.com/a/7071791/58549. I don't really think this is a good solution, since you would have to be very careful about using the right flags every time with both projects. Also you'd end up having to re-download Maven plugins and any other dependencies into your new local repository anyway, which is kind of a waste of time.
  3. Try to avoid using any local repository at all. You seem to be trying to make this option work. I don't think this is a great approach either; you're fighting against Maven's expectations, and it limits your flexibility a lot. Maven will indeed find dependencies from the "reactor" (i.e., the executing mvn process) first, but this means all of the required modules must be available in the reactor to be found, which means you can only run mvn at the top level. So if instead you want to just do "mvn exec:java" inside a single module, mvn needs to find that module's dependencies somewhere ... and that's what the local repo is generally used for.

If you're dead set on going with option 3 (instead of option 1), then I suggest you follow the comments on your question and create a profile that runs your exec selectively against module2 and binds it to a lifecycle phase. But this is in practice very close to just wrapping it with a test.

Community
  • 1
  • 1
Zac Thompson
  • 12,401
  • 45
  • 57
  • Thanks Zac, a lot of useful suggestions and new information that removes some of the difficulties I originally had. I'll work through it to see which one fits best. – levant pied Aug 24 '16 at 12:59
  • For the record, I think this aspect of Maven sucks really bad and I'm not the only one that thinks this way, e.g. http://stackoverflow.com/questions/1677473/maven-doesnt-recognize-sibling-modules-when-running-mvn-dependencytree - see the various comments by the author post – levant pied Aug 24 '16 at 13:00
0

For IntelliJ users:

I solved this problem using IntelliJ's Run configuration. It has the options Resolve workspace artifacts and Add before launch task -> Build. See this picture for clarification:

Run configuration example

-1

The whole point of modules in Maven is to create decoupling between them. You either build each module independently, so that you can work on one module without touching the other, or include both modules as sub-modules in the parent pom and build the parent, which will resolve dependencies between its sub-modules and trigger their builds.

It looks like you have two options here:

  1. Review the structure of your project. Do you really need to split it into two separate modules, if you change code in both of them simultaneously?
  2. Import the project into a Maven-aware IDE (IntelliJ IDEA is very good at working with Maven), and let the IDE handle the compilation. Once finished and stabilized the code-base, build normally with Maven.
vempo
  • 3,093
  • 1
  • 14
  • 16
  • Thanks vempo. I'm doing things mostly as you suggested and the organization of my project is not a problem. I'm specifically asking about running two different versions at the same time without installing into repository for the reasons I mentioned (saving time and making it practical). Not sure how you answer the main question I have, can you clarify? – levant pied Aug 15 '16 at 21:25
  • Do you mean you have two modifications of the same module with the same version number? – vempo Aug 15 '16 at 21:37
  • Correct. Note that "same version number" doesn't mean much when we are talking about SNAPSHOT versions - 1.0-SNAPSHOT is a fluid version. – levant pied Aug 16 '16 at 16:03
  • Sorry, doesn't make much sense to me. How do you distinguish between the two? I mean when you are making changes, how do you know that you are modifying the correct version? Physical location? In this case I would go with the old good java compiler - write two scripts that run javac and are identical except for the location of that module you depend on. You can do the same for runtime. This will bypass Maven though - Maven is all about artifacts and dependencies between them, which apparently stands in your way in this case. – vempo Aug 16 '16 at 17:02
  • Oh, and you can run Maven with `-s` to pass custom settings where you can specify a local repository, or rather list of repositories. So you have two settings files that each points to a different local repo and to a shared one. Build the two versions of the module into the different repos, then build the shared module against either of them by having two run configurations, each pointing to a different local repo. – vempo Aug 16 '16 at 17:12
  • Or use Maven profiles. However, any Maven solution will require to build an artifact for the "duplicate" module at least once. – vempo Aug 16 '16 at 17:27
  • "How do you distinguish between the two?" When I run mvn in a project tree, I expect it to use things from the project tree, local repository and remote repository, in that order. Is there a way to do that? – levant pied Aug 16 '16 at 18:48
  • Yes, project tree of the module you are building (module2 in your example), then its dependencies from local and remote repositories. Not project trees of the dependencies. I think I've already explained that in my answer. – vempo Aug 16 '16 at 19:16