I have a project called 'talktome', with no runtime dependencies.
Also I have project 'talktome-tools', which depends on 'talktome'.
No problems, until I realize that the unit-tests in 'talktome' depends on 'talktome-tools'.
What solutions are there?
If talktome is a general project where other (e.g talktome-tools) depends on, it should not be depend on the more specific projects. Then, it would be wise to get rid of dependency from talktome to talktome-tools.
Otherwise, you would create a more higher level project (e.g parent) and put necessary interfaces, classes that can be used by both talktome and talktome-tools to the new project.
You may move the tests to another maven module like talktome-tests that will do the integration testing. It makes sense especially it seems that talktome shouldn't depend on specific project like talktome-tools.
And, test dependencies like talktome-tools should be included with test scope.
Related
Having hard time to describe this and I bet this is something very simple, but I just can't google sollution.
I am using many modules in my project. For simple argument let's say I have modules A and B.
B depends on A.
When I add dependency to external library (using implementation keyword) in module A to use some of it's code in the module, I cannot access library's code in project B. How can I achieve that? I would like A to be my "base" project with all dependencies in that place rather than having to repeat myself in other modules, that depend on it.
The implementation configuration means the dependencies are internal (implementation specific) for the project and should not be exposed on the compilation classpath of other dependent projects. This helps encapsulate dependencies and speeds up the build as you don't need to recompile dependent projects if you only change internal dependencies.
If you want to expose them, you need to use the api configuration instead, along with the java-library plugin.
I have the following Java projects structure:
Util
|
-- Core
|
-- Services
|
-- Tools
The projects: Tools and Services references to Core and Util projects, the thing is that I ended up writing the same dependency over each project, there must be a better way to inherit the dependencies of the referenced projects and add new ones if needed.
I know about multi projects in Gradle, but this is not like a multi project, since I can basically take the Core library, compile it (which will then contain Core + Util libs) and use it in another project.
I wonder what would be the best way to approach this?
Repeating the same dependencies in every project is usually reasonable because in a bigger project you'll never know when they become different, and you don't want to deal with compilation/runtime problems when someone changes common dependencies list.
I believe that it is more pragmatic to add dependency analyser plugin to your build. It will help you to remove unnecessary dependencies and explicitly add transitive dependencies. And if you add this plugin to your build chain, it will help you to keep your dependencies healthy in the future. Pick this plugin here gradle-dependency-analyze, or maybe there is a better fork or equivalent somewhere.
You are actually out of options in your case because there are only two kinds of dependencies: (1) external (some other jar artefact) or (2) internal (another module in a multimodule build).
2.1 When you use an external maven-like dependency it will come to you with own dependencies (they are named "transitive dependencies"). It means that if you do compile 'yourgroup:Core:1.0' then you will get Util as a transitive dependency. But as I mentioned above, it is better to list transitive dependencies explicitly if they are used during compilation or to prevent them from being accidentally removed and crash your application in runtime.
2.2. If your projects live in the same version control repository and usually change and build together, then the multimodule layout is your best choice. In this case, you will refer to Core dependency like compile project(':Util:Core') and it will grab Util as a transitive dependency as well. And you will be able to do what you asked for and define dependencies for Services and Tools once - inside subprojects {} closure in the Core/build.gradle.
Having multimodule built doesn't limit you from using Core library elsewhere. No matter if it is a multimodule build or not, you can always add maven-publish plugin to Core/build.gradle, execute publishToMavenLocal task and reference to Core.jar from another project the same way you do for external dependencies.
You can always put your common code (like the one which will add common dependencies) in the external gradle file or custom plugin and apply it in Services and Tools.
I have a top-level maven project with submodules. The first submodule is a Java project which generates some JavaScript library code from the compiler's annotation processor. I want to include those generated JS files into the second submodule, a webpack NPM managed project, for the build and then publish the webpack BACK into the first submodule before packaging into a fat-jar. Does anyone know of a way to accomplish this?
What you describe is a circular dependency. You have to break this circular dependency. (You probably do similar things in your code all the time.)
I have solved the same problem by breaking up the Java project. Once you do that you will recognize that, in fact, your first project was serving two separate roles that then become separated:
From:
Java project with code and assembly
JavaScript project
To:
Java project with code
JavaScript project
JAR-packaged project with assembly
I've often seen this happen in multi-module projects that build into a WAR file, and I've adopted the rule of thumb that the WAR project should not have any Java (production) code, acknowledging its role as the aggregator/assembler.
Some folks will think this third project is frivolous, some always argue that you're easily getting too many Maven modules. I think that often stems from tooling or build pipeline limitations, once you break through those, you can just embrace a growing number of Maven modules, given that the boundaries are chosen well, and I see no problems here.
One hesitation, though: why is the JavaScript project separate to begin with? If it is not deployed separately (evidently it isn't since you're assembling the "fat-jar"), the JavaScript code is following the delivery lifecycle of the "fat-jar", which would benefit from being in the same source repository, so why even make it a separate Maven module? (If they were in separate source repositories, there's a cost in version management between the modules that I'm unsure you have reasons for.) There's no shame in having a monolith (if done well -- it happens, microservices make you believe otherwise, but in that world you'd probably deploy the JavaScript code separately anyway).
If you are using a module in multiple projects in IntelliJ IDEA is it common practice to make a new project for these modules or to make the modules in one of the projects they are included in?
It depends on how you're working, whether the common module is developed by you or another team, and other factors. Often, common code is put in a module which produces a jar that the other modules depend upon, and include them all in the same project. This is normal when you're developing the common-code module at the same time as the project and changes are typically committed together (although not necessarily to the same repository or branch). If multiple projects use the common-code module though, and it's developed as a separate library, then it should maybe have a project of its own.
Also, you should probably be using maven when things get this complicated.
What are the best practices for software versioning and multimodules projects with Maven?
I mean, when I create a multimodules project with Maven, what is the best approach for the versioning? To use a single version for all the modules (defined in the top project)? To use a version for each module (defined in the POM of each module)? Is there another approach that I'm missing? What are the pros and cons of each approach?
In general, are the different modules released together (possibly sharing the same version number)?
Thanks
Honestly it depends on what you would like to do. Multimodule projects are created for multiple reasons, one of them being you only need to deploy what has changed instead of all modules.
Think about it this way: if you had a non-multi-module project and you only had to change one line in the services layer, you have to rebuild the entire project and deploy all of the code again...even though only your services layer will change.
With multi-module projects, you can regenerate your project and deploy only what changed...your services. This reduces risk and you're assured that only your services module changed.
You also have a multitude of benefits to using multi-module projects that I'm not listing here but there is certainly a huge benefit to NOT keeping your version numbers of your modules in sync.
When you build your project, consider deploying it to a repository that will hold all compatible jars together for builds (each build creates a new folder with the parent-most pom version number). That way, you don't need to keep documentation about which jars are compatible...they're all just deployed together with a build number.
I was looking for a solution for this exact problem myself and versions-maven-plugin was exactly what I needed. I don't like the release plugin communicating with the SCM system. The versions plugin does just what we need: it sets a new version number in all poms of the project:
mvn versions:set -DnewVersion=2.0.0
Then I can proceed with commits, tags and an official build server build...
EDIT:
The versions plugin depends on how a maven multi-module project has been organised: as a result, it often does not update all POM files in a complex multi-module project.
I've found that sed and find do the job much more reliably:
sed -i 's/1.0.0-SNAPSHOT/1.0.0-RC1/g' `find . -name 'pom.xml'`
Typically you create a multi-module project because you have deemed that the various modules are parts of a single whole. Maybe the client-piece, the controller-piece and the services-piece. Or maybe the UI with services.
In any case, it makes sense to have the version numbers for the various modules to move in lock-step. However Maven does not enforce that as a rule.
As to your question
are the different modules released together (possibly sharing the same
version number)
I would think so. That is one of the reasons for having it a multi-module project. Otherwise you could have the modules as independent projects.
Of course this is the kind of stuff that is rife with edge cases and exceptions ;-)
I had the same problem with a project I`m working on. I also decided to use separate versions and even the dependency to the parent pom only has to be updated if some of the managed dependencies change. (so mostly as #vinnybad describes it)
Two additions
exists-maven-plugin
With the usage of the "org.honton.chas.exists-maven-plugin" only the modules will be deployed to the repository that have actually changed, which is really great, because also the according docker-images will only be published if something has changed on one of the service. This avoids "polluting" the image repository with different but unchanged versions.
versioning
One main downside of the "separated versions" approach are the questions regarding versioning:
What's the current version of my project?
Which module versions work with each other? (even thought they don't directly depend on each other, one does rely on what another does, e.g. they share the database schema)
To solve that I put all module versions into the dependency management part of the parent pom, even if no other module depends on them. A "integration-test" module could solve that by depending on all of the modules - and of course testing them together.
This way I would be "forced" to update the parent pom with every change, since it's referring the released module versions. This way the parent pom would have the "leading" version and at the dependency-management block state the versions of all modules that are compatible with each other (which will be ensured by the integration test).