Scenario:
Given
Parent POM which defines a profile and a child (as module)
Child project(s) that will be using the profile by referring to the parent POM.
The intent is to skip profile execution in the parent and execute it in the child only
Profile has activation section <activation><property><name>foo</name></property><activation>
Since parent does not define foo property - the profile is inactive and will not be executed for the parent build
Now, I'm defining <properties><foo>true</foo></properties> in the child with hope that the property will be picked up when child build is executed and profile will be activated. No such luck. Profile is never activated, which tells me that property is never set.
Just to note: mvn package -Dfoo=true activates profile in both parent and child
Am I trying to do the impossible or just doing it wrong?
P.S. Hmmm - even if I define property in the parent, the profile is not triggered. What gives?
To expand on #rich-seller's answer, and #Bostone's self-answer, it seems to be impossible to have a setup where the parent POM defines a few profiles as alternatives, and child POMs select one of these profiles by default, while allowing you to override the choice for a child temporarily (i.e. on the CLI). Consider a parent POM for projects which use some framework and an associated plugin, both of whose versions we can assume are defined by properties:
<profiles>
<profile>
<id>newest</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<framework.version>2.0</framework.version>
<plugin.version>2.0</plugin.version>
</properties>
</profile>
<profile>
<id>older</id>
<activation>
<property>
<name>older.framework</name>
<value>true</value>
</property>
</activation>
<properties>
<framework.version>1.1</framework.version>
<plugin.version>1.1</plugin.version>
</properties>
</profile>
</profiles>
Now a child inheriting from this parent POM by default will use 2.0 as you would expect, and -Polder or -Dolder.framework=true will work to try building it with the older framework (e.g. to test compatibility). However you cannot write in the child POM
<properties>
<older.framework>true</older.framework>
</properties>
and have the older profile be activated automatically. You could use file-based activation to make this module build against 1.1 if newest were not active by default, but then it is not easy to temporarily run it against 2.0: as far as I know both older and newest profiles would be active if you passed -Pnewest, so you need to explicitly disable other profiles which is unreasonable if you have a dozen of them. So there is just no solution except to copy the profile information to the child POM:
<properties>
<framework.version>1.1</framework.version>
<plugin.version>1.1</plugin.version>
</properties>
at which point -Pnewest will not work to override these properties, so you need to use -Dframework.version=2.0 -Dplugin.version=2.0.
In other words, the profiles are only useful if all the child modules can use the same profile (here newest) by default. If some of them are normally built with 1.1 and some with 2.0, the profiles are useless.
Seems like this is a use case for a Maven core enhancement, or perhaps a Maven 3 build extension. http://docs.codehaus.org/display/MAVEN/Custom+Profile+Activators and https://github.com/maoo/maven-tiles come to mind.
The profile can only be activated by properties passed from the command line. This is because properties in the POM can only be processed once the POM has been parsed, at which point it is too late to resolve the profile activation.
You're in a bit of a catch-22 with this approach unless you are able to pass the property from the command line, specify profile activation in your settings.xml (generally not a great idea), or use the workaround in my previous answer to use the presence of a marker file.
One final alternative if you're on Maven 2.1.0+ is to deactivate the profile via the command line for the parent POM only, this is still obviously not ideal.
You can deactivate a profile with either the character '!' or '-' like this:
mvn install -P !profile-1,!profile-2
To directly answer my own question: in multi-module build all properties are set before build is run so it is impossible to activate/deactivate profile in one of the modules during the build based on setting the propety in the child POM. However if you are looking for way of doing it by using other means please read this comment
Related
I'm working on getting the rpm-maven plugin setup in a project. In our staging and production environments, the build occurs on Red Hat boxes, but we have several Windows boxes that are used for development and testing so I wanted the RPM build process to be part of a profile that is only active on a box that has rpmbuild installed.
This was my first attempt at an activation condition:
<activation>
<os>
<family>unix</family>
</os>
<file>
<exists>/usr/bin/rpmbuild</exists>
</file>
</activation>
My initial testing only involved building on a Windows box and building on a CentOS box, and both gave me the results I expected. Later, the build broke on a Linux machine that didn't have rpmbuild available. It looks like having two conditions like this isn't supported. Is this the case? I realize I can probably just get rid of the <os/> element and get the results I want, but for future reference is there a better way to create profiles with multiple activation conditions?
Maven <activation> block is a list of OR -- the profile will be activated as soon as the first criteria is met. So, it is less likely that your problem has a solution at least until this bug-report gets fixed https://issues.apache.org/jira/browse/MNG-4565
Update:
it's fixed in 3.2.2 now – sfussenegger (via comment)
And worst you can mix condition of different type for example file, jdk and property as described here http://www.sonatype.com/books/mvnref-book/reference/profiles-sect-activation.html, but you can't even put two condition of same type, for example two properties
<activation>
<property>
<name>integrationTest</name>
</property>
<property>
<name>packaging</name>
<value>swf</value>
</property>
</activation>
This won't work as only one <property> tag will be allowed.
Associated JIRA : https://issues.apache.org/jira/browse/MNG-3328
And the bug described above is still open... 5 years it's just a shame !
Just fixed by me :)
Starting from 3.2.2 it will work as expected: multiple conditions will be ANDed
Reference - https://github.com/apache/maven/commits/master, search by MNG-4565
Commit URL - https://github.com/apache/maven/commit/c6529932f9e3efdfc86ed73f59a307a8f8b6ea5f
I think this is what these Maven extensions do:
Maven EL Profile Activator Extension
This one is pretty simple, have a look at the source
Maven Profile Activation Extension
This one has more options for the actual activation expression, including Scala.
However, since it's an extension (not a plugin), every project using it will have to register the extension. And there's a risk that the project author will abandon it and it won't work in future maven versions.
I'm using "mvn dependency:copy-dependencies" to get all sources of the dependencies my project uses.
I use it from command line, so it looks like this:
mvn dependency:copy-dependencies -Dclassifier=sources -DincludeScope=compile
It works fine except of a small problem: For some reason the version is removed by this plugin. So instead of commons-logging-1.1.1-sources.jar, I'm getting commons-logging-sources.jar
The documentation says that "stripVersion" argument should effect this behavior, but the default value is false. Moreover, I tried to set it explicitly and it didn't help.
I'm using apache-maven-2.2.1 with jdk1.6.0_21
Thanks,
Tarlog.
That's very strange, you can see in the source (Mojo, Parent Mojo, DependencyUtil) that stripVersion does what it says it does.
Several possibilities:
a) Are you using the current version of the dependencies plugin?
Either configure your pom
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.1</version>
</plugin>
</plugins>
</build>
or use the fully qualified goal name:
mvn org.apache.maven.plugins:maven-dependency-plugin:2.1:copy-dependencies
b) stripVersion is the parameter name in XML configuration. The command line version is
-Dmdep.stripVersion=false
Try using that.
I found the root cause of a problem. Somewhere in the parent of parent of parent of ... my pom, stripVersion was set to true.
I didn't know about it and anyway thought that setting the system property must override the xml. I even tried to put this property in my pom and it still didn't help. Somehow, the parent pom stayed the dominant one, so I thought that the property doesn't work.
When I ran with -X param I saw it no changing despite my configuration, so I started digging in the parent poms unless I found the problem.
Thanks to everyone!
I suppose this is kinda obvious, but I still judge it as a shortcoming...
I have 23 Mavenized projects. I'm now adding the <scm> bit because I've started using the release plugin. Here was my thought process:
I'll add the <scm> section only in my company base POM, and parameterise the URLs with properties, e.g.
<scm>
<connection>${scmBaseConnection}/${scm.module}/${scm.edition}</connection>
<developerConnection>${scmBaseConnection}/${scm.module}/${scm.edition}</developerConnection>
<url>${fisheyeBaseUrl}/${scm.module}</url>
</scm>
Then each project root (aggregator) POM need only declare its scm. <properties> accordingly (and not have to re-declare the whole <scm> section), e.g.:
<scm.module>sharktopus</scm.module>
<scm.edition>trunk</scm.edition>
But I soon realised that I can't do that: the release plugin rewrites each POM with the tag and next versions of the SCM info, so each such POM needs its own <scm> section.
Fine, so I decided I'll store the common SCM details in base POM properties, and have each project root POM declare its <scm> section using those props, plus its own specifics e.g.:
<scm>
<connection>${scmBaseConnection}/sharktopus/trunk</connection>
<developerConnection>${scmBaseConnection}/sharktopus/trunk</developerConnection>
<url>${fisheyeBaseUrl}/sharktopus</url>
</scm>
But that doesn't work either, because the release plugin rewrites using the resolved values (which is kinda obvious, in hindsight). So, e.g. for the release tag POM, the info above would be rewritten as:
<scm>
<connection>scm:svn:https://mysvnhost.net/sharktopus/tags/R1_NewStuff</connection>
<developerConnection>scm:svn:https://mysvnhost.net/sharktopus/tags/R1_NewStuff</developerConnection>
<url>https://mysvnhost.net/sharktopus</url>
</scm>
This means that each POM must have its own <scm> section with hardcoded URLs.
Is this what everyone does?
What happens if your SCM URL(s) change - do you just search/replace across all your projects?
Could it be a feature request to the release plugin to rewrite partial URLs, e.g. to keep the property references, but overwrite the 'final' specifics?
The only place to define scm part is in the project root not company root. In a multimodule build it's needed to have only a single scm part. The reason that the release plugin will replace the properties is very simple. After a release these pom's must represent that state for that software. If they would have properties in it would be imposible to gurantee the corret values etc. So the result would not reproducible...
If the SCM URL changes it will be valid only for new projects and not for the old ones, cause they are already been deployed etc.
For now it seems that there's no solution: http://jira.codehaus.org/browse/MRELEASE-128
I have defined a local mirror for all repositories in the settings.xml file:
<mirror>
<id>myMirror</id>
<mirrorOf>*</mirrorOf>
<url>file://${mypath}/maven/.m2/repository</url>
</mirror>
I want that my mirror to point to a local path, in this case the path is:
file://${mypath}/maven/.m2/repository
Where ${mypath} is a variable that I pass when I invoke Maven:
mvn -Dmypath="/D:/test" package
The problem is that Maven does not replace the variable when it is invoked. I can see that this error is happening by inspection of the build log. For example, Maven reports that it is downloading a file from file://${mypath}/maven/.m2/repository when the correct would be file:///D:/test/maven/.m2/repository.
I have also noted that Maven replaces correctly my variable when it is inserted in the url child tag of the repository tag:
<repository>
<id>central</id>
<url>http://${mypath}/maven/.m2/repository</url>
</repository>
The build works correctly when I replace the variable in my settings.xml by the complete URL like in the example below:
<mirror>
<id>myMirror</id>
<mirrorOf>*</mirrorOf>
<url>file:///D:test/maven/.m2/repository</url>
</mirror>
Property substitution into settings.xml doesn't work as you would expect.
It will substitute properties inside the profiles element (as you've seen it substitutes into your repository url, which would be defined inside a profile), but not to elements outside of profiles (as you've seen happening in the mirrors section). This distinction is made because the profile element in the settings.xml is a truncated version of the pom.xml profile element. It is a mechanism to allow configuration to be set into your POM, so property substitution is allowed within the profiles elements as they are effectively part of the POM.
The parts of the settings outside of the profiles element represent the platform configuration, these aren't supposed to be affected by individual builds, so are not substituted for command-line properties. This makes sense but isn't really made clear anywhere.
EDIT: in the settings page of mavens documentation, in the last sentence of the quick overview section (quite hidden) it states:
Note that properties defined in profiles within the settings.xml cannot be used for interpolation.
There is a workaround though, you can substitute environment variables into the settings.xml. If you set the environment variable:
set M2_MIRROR=D:\test
and configure the repository url as follows:
<url>file://${M2_MIRROR}/maven/.m2/repository</url>
Then invoke Maven as normal, the environment variable is substituted and your build should work as required.
This is an old question now, but as of Maven 3, and probably before, you can refer to environment vars, if you prefix with 'env'
I do so like this:
<localRepository>${env.M2_LOCAL_REPO}</localRepository>
Then each developer sets M2_LOCAL_REPO to an appropriate location.
I had a slightly different experience here from Rich's answer, so I think it's worth mentioning in a different one.
Variable substitution does indeed work inside a profile, but it seems to fail if you need them replaced before parent pom resolution happens.
I had the following configuration here:
<profiles>
<profile>
<id>company-dev</id>
<properties>
<company.repo.deployment.endpoint>https://companyartifacts.jfrog.io/companyartifacts</company.repo.deployment.endpoint>
<company.repo.download.endpoint>https://companyartifacts.jfrog.io/companyartifacts</company.repo.download.endpoint>
</properties>
<repositories>
...
<repository>
<id>company-repo-snap</id>
<name>libs-snapshots</name>
<url>${company.repo.download.endpoint}/libs-snapshots</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
<releases>
<enabled>false</enabled>
</releases>
</repository>
</repositories>
</pluginRepositories>
</profile>
</profiles>
It's been like that for years now, every place where I use company.repo.download.endpoint var seems to work great until I needed it to resolve a parent pom like this:
<project ...>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.company.jcompany</groupId>
<artifactId>persistence</artifactId>
<version>5-SNAPSHOT</version>
</parent>
<artifactId>persistence-api</artifactId>
<version>5.0.0-SNAPSHOT</version>
...
</project>
Then, I started having this exception, even running something as simple as mvn -X help:effective-pom only:
(notice variables are not replaced in URL)
[ERROR] The project com.company.jcompany:persistence-api:5.0.0-SNAPSHOT (/var/lib/jenkins/jobs/jcompany_persistence_api-trunk/workspace/pom.xml) has 1 error
[ERROR] Non-resolvable parent POM: Could not transfer artifact com.company.jcompany:persistence:pom:5-SNAPSHOT from/to company-repo-snap (${company.repo.download.endpoint}/libs-snapshots): No connector available to access repository company-repo-snap (${company.repo.download.endpoint}/libs-snapshots) of type default using the available factories WagonRepositoryConnectorFactory and 'parent.relativePath' points at wrong local POM # line 5, column 10 -> [Help 2]
org.apache.maven.model.resolution.UnresolvableModelException: Could not transfer artifact com.company.jcompany:persistence:pom:5-SNAPSHOT from/to company-repo-snap (${company.repo.download.endpoint}/libs-snapshots): No connector available to access repository company-repo-snap (${company.repo.download.endpoint}/libs-snapshots) of type default using the available factories WagonRepositoryConnectorFactory
at org.apache.maven.project.ProjectModelResolver.resolveModel(ProjectModelResolver.java:159)
at org.apache.maven.model.building.DefaultModelBuilder.readParentExternally(DefaultModelBuilder.java:813)
at org.apache.maven.model.building.DefaultModelBuilder.readParent(DefaultModelBuilder.java:664)
at org.apache.maven.model.building.DefaultModelBuilder.build(DefaultModelBuilder.java:310)
at org.apache.maven.model.building.DefaultModelBuilder.build(DefaultModelBuilder.java:232)
The only reasonable explanation I find for this problem is that parent pom resolution happens so early in the process that variables are not replaced yet. When I don't have to fetch snapshots for parent poms, then all variables are replaced successfully, as expected. Not sure if I should report this as a bug.
Using Maven 3.6.3 at time of answer, if that matters.
The settings.xml is not interpolated like the pom is, so the property can't be used like shown above.
It is probably a bug - unfortunately property replacement does not seem to be consistent across Maven plugins. I have encountered a bug myself around specifying more than two properties in a configuration item in another plugin.
We have a multi module build with modules using different technologies, like Java and Flex. Is it somehow possible to activate different profiles based on the module that is compiled currently?
I tried it with an activation like
<profile>
<id>flex</id>
<activation>
<file>
<exists>${basedir}/src/main/flex</exists>
</file>
</activation>
...
</profile
But it didn't work, although the use of ${basedir} is documented in the Maven documentation (this is a bug in Maven). Is there a different possibility to have different activations based on the current module? Or does Maven only allow to activate a profile for all modules or not at all?
After some more research I finally came to the conclusion that this is not possible for two reasons in the current Maven version (2.1.0):
Maven profiles are not inherited, so you can't define a profile in a parent POM and activate that in a child POM.
I haven't found a possibility to activate a profile from a POM itself. The activation does not work with ${basedir} and the property activation response only to system settings, which are globally specified through the -D option.
For those like myself reading this question looking for answers, this use case now works in Maven 3.
There is was a bug affecting this feature in early versions of 3 (see http://jira.codehaus.org/browse/MNG-2363) but it works for me correctly using Maven 3.0.4.
In 2.2.1, profiles are inherited but the ${basedir} issue is still there. I'm in the same boat - I need to activate a profile based on the existence of a file in a given project. My child builds run individually just fine (inherited profile activated by local file existance), but if I run the build from the top parent, they fail because the file isn't found.
With regard to file-based activation, you can try removing ${basedir}. We use it like this:
<activation>
<file>
<missing>target/jboss/conf/jboss-service.xml</missing>
</file>
</activation>
I dont know if this helps, but I solved a similar problem with the following approach:
I created and described the profile in the parent POM, which has activeByDefault=false. The PluginManagement-Section then contains the configurations for different plugins.
The children can reuse this profile, and set activeByDefault=true
This makes the profile active, but still none of the plugins are activated.
But fortunately the described plugin configurations are available. You can reuse them in children by defining them in the Plugins-Section. You just provide the group- and the artifactID, and set inherited=true for each plugin you want to reuse in the children.
I hope that helps. Sorry for not including any code snippets, but I hope even so the soutions is understandable.
You can set a property in each module that you want to use the profile, and then use "property" activation in your profiles.