-->

Enforce exactly one of two Maven profiles

2019-04-25 06:10发布

问题:

I have a Maven project that defines two separate profiles, developer and release (surely you get the drift, here). I want one of these two profiles to be activated at any time, but never both. If both are somehow activated, this build makes no sense and should fail. If neither is activated, this build also makes no sense and should fail.

I'm sure I can write some custom plugin code to achieve this, and I might very well end up going that way, but I'd be interested in achieving this using POM configuration (could be using existing plugins from Maven Central).

It should be possible to activate plugins using -P (--activate-profiles) so <activation> through properties would not be a valid solution. Solutions using activeByDefault would not be valid either, since activeByDefault is generally known as a pitfall, unreliable (and we may in fact activate other profiles, thus rendering activateByDefault unusable).

Your suggestions much appreciated.

回答1:

The simplest solution for this kind of problem would be to use the maven-enforcer-plugin which exactly has such a rule to force to activate at least one of two or more profiles.

Unfortunately the requireActiveProfile has currently a bug. But currently a preparation for a new release is on going which solves this.



回答2:

I always issue a build command like so:

mvn package -P-dev,prod

It explicitly disables the dev profile and enables the production one. To my knowledge, you can not conditionally enable one build profile if another is active (which is a bit unfortunate), and because of that you can't ensure that the profiles are mutually exclusive.



回答3:

I had a similar need (i.e. for mutual exclusivity of two profiles) and solved it by considering the two target profiles to be internal profiles that shouldn't be specified on the command line: Instead, a controlling system property can either be specified or not. E.g. let's assume that by default you want the "dev" profile to be active. We can then activate/deactivate the relevant internal profiles based on whether the -Drelease option is specified as follows:

<!-- Internal profile: FOR INTERNAL USE ONLY - active if -Drelease is *not* specified. -->
<profile>
  <id>internal-dev</id>
  <activation>
    <!-- Activation via *absence* of a system property to ensure mutual exclusivity
         of this profile with internal-release -->
    <property>
      <name>!release</name>
    </property>
  </activation>
  ...
</profile>

<!-- Internal profile: FOR INTERNAL USE ONLY - active if -Drelease *is* specified. -->
<profile>
  <id>internal-release</id>
  <activation>
    <!-- Activation via *presence* of a system property to ensure mutual exclusivity
         of this profile with internal-dev -->
    <property>
      <name>release</name>
    </property>
  </activation>
  ...
</profile>


回答4:

I needed a slightly more advanced version of this rule. I ended up writing it myself. I've submitted a patch to them that includes the following 2 rules:

  • The ability to specify a set of mutually-exclusive profiles (p1,p2:p1,p3 would mean p1 can't be active with either p2 or p3).

  • The ability to ban profiles (the contrary of requireActiveProfile). p1, p2 would mean neither p1 nor p2 can be active for this build.

Both of these rules support wildcards and consider inherited profiles as well. These are built on v1.4 of the rules.

http://jira.codehaus.org/browse/MENFORCER-225



回答5:

This can still be done with Maven Enforcer plugin

Although mutual exclusion, <requireActiveProfile>...<all>false</all>... is buggy as reported by @khmarbaise, there's still the <evaluateBeanshell/> built-in rule that lets one do whatever he wants.

I wrote one especially for this case: XOR of two profiles. I hope it helps.

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-enforcer-plugin</artifactId>
                <version>1.4.1</version>
                <executions>
                    <execution>
                        <id>enforce-PROFILE_ONE-XOR-PROFILE_TWO-is-active</id>
                        <goals>
                            <goal>enforce</goal>
                        </goals>
                        <configuration>
                            <rules>
                                <requireActiveProfile>
                                    <profiles>PROFILE_ONE,PROFILE_TWO</profiles>
                                    <all>false</all>
                                </requireActiveProfile>
                                <evaluateBeanshell>
                                    <condition><![CDATA[
                                        // ensure PROFILE_ONE XOR PROFILE_TWO
                                        print("Checking if only one of PROFILE_ONE and PROFILE_TWO profiles is active ...");
                                        boolean profile1 = false, profile2 = false;
                                        for(s: "${project.activeProfiles}".replaceAll("\\[?\\s?Profile \\{id: (?<profile>\\w+), source: \\w+\\}\\]?", "${profile}").split(",")) {
                                            if("PROFILE_ONE".equalsIgnoreCase(s)){ profile1 = true;}
                                            if("PROFILE_TWO".equalsIgnoreCase(s)){ profile2 = true;}
                                        }
                                        print("PROFILE_ONE XOR PROFILE_TWO: "+(profile1 != profile2));
                                        return profile1 != profile2;
                                    ]]></condition>
                                </evaluateBeanshell>
                            </rules>
                            <failFast>true</failFast>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

The tricky part is looping over active profiles, which I've already done here. You can extend it to more than two profiles if you need. But you'll have to write the long xor expression, since beanshell doesn't implement Java xor ^ operator.