Maven profiles – Operating Systems (OS) specific build profiles

Maven is a powerful build framework and it allows us to customize every aspect of build with simplicity.

In this article, we’ll learn about how to create different build profiles for windows and Mac OS. The same concept can be replicated to other operating systems or environment specific builds.

Let’s define a use case.

Let’s say we have project in which we want to compile and package Angular 2+ project using maven build. Angular 2+ project is dependent on Node.js (npm) and Angular CLI (ng CLI). We can use Maven Exec plugin for invoking npm and ng CLI commands.

On Mac OS “npm” and “ng” commands works without any additional extension. To run same commands on Windows operating system we need to append .cmd extension. This means if we have mixed folks in development team who uses both Windows and Mac OSx then every time pom.xml file needs to be updated. What if someone pushes this file to central code repository? This will obviously break the builds on CI/CD (e.g. Jenkins server). This is just one scenario, there may be other use cases where build command/process has to be different based on operating systems or environment and it is not good idea to modify pom.xml file locally for build and leave room for accidental commits/push. This is where Maven profiles comes handy.

Let’s continue with same Angular 2+ build example. On Mac OS, the build section (that is responsible for building Angular project using Maven exec plugin) of pom.xml file would look like:
        

<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/maven-v4_0_0.xsd">
 <modelVersion>4.0.0</modelVersion>

 <!-- Omitted remaining pom.xml for simplicity -->

 <plugin>
  <groupId>org.codehaus.mojo</groupId>
  <artifactId>exec-maven-plugin</artifactId>
  <executions>
   <!-- Execute UI Build as part of standard Maven "generate-resource" phase -->
   <execution>
    <id>ui-npm-install</id>
    <configuration>
     <workingDirectory>.</workingDirectory>
     <executable>npm</executable>
     <arguments>
      <argument>install</argument>
     </arguments>
     <workingDirectory>${basedir}</workingDirectory>
    </configuration>
    <phase>validate</phase>
    <goals>
     <goal>exec</goal>
    </goals>
   </execution>
   <execution>
    <id>ui-aem-build</id>
    <configuration>
     <workingDirectory>.</workingDirectory>
     <executable>ng</executable>
     <arguments>
      <argument>build</argument>
      <argument>--prod</argument>
      <argument>--outputHashing</argument>
      <argument>none</argument>
     </arguments>
     <workingDirectory>${basedir}</workingDirectory>
    </configuration>
    <!--<phase>prepare-package</phase> -->
    <phase>generate-sources</phase>
    <goals>
     <goal>exec</goal>
    </goals>
   </execution>
   <execution>
    <id>npm-clean</id>
    <configuration>
     <workingDirectory>.</workingDirectory>
     <executable>rm</executable>
     <arguments>
      <argument>-rf</argument>
      <argument>node_modules</argument>
     </arguments>
     <workingDirectory>${basedir}</workingDirectory>
    </configuration>
    <!--<phase>prepare-package</phase> -->
    <phase>install</phase>
    <goals>
     <goal>exec</goal>
    </goals>
   </execution>
  </executions>
 </plugin>

 <!-- Omitted remaining pom.xml for simplicity -->
</project>


Look at the line # 18, 33, 52 and 54. These commands are OS dependent and if we run a maven build with this configuration on Windows, it’ll fail.

To handle this in Maven way, we could create separate profile for Max OS and Windows. Following pom.xml file shows an example of OS specific build profiles.
        
<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/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>  
    <!-- Omitted remaining pom.xml for simplicity -->
   <profiles>
     <!-- Max OSX specific build profile -->
    <profile>
     <id>macOSBuild</id>
     <activation>
       <!-- Profile is active by default -->
      <activeByDefault>true</activeByDefault> 
     </activation>
     <build>
         <plugin>
           <groupId>org.codehaus.mojo</groupId>
           <artifactId>exec-maven-plugin</artifactId>
           <executions>
             <!-- Execute UI Build as part of standard Maven "generate-resource" phase -->
             <execution>
               <id>ui-npm-install</id>
               <configuration>
                 <workingDirectory>.</workingDirectory>
                 <executable>npm</executable>
                 <arguments>
                   <argument>install</argument>
                 </arguments>
                 <workingDirectory>${basedir}</workingDirectory>
               </configuration>
               <phase>validate</phase>
               <goals>
                 <goal>exec</goal>
               </goals>
             </execution>
             <execution>
               <id>ui-aem-build</id>
               <configuration>
                 <workingDirectory>.</workingDirectory>
                 <executable>ng</executable>
                 <arguments>
                   <argument>build</argument>
                   <argument>--prod</argument>
                   <argument>--outputHashing</argument>
                   <argument>none</argument>
                 </arguments>
                 <workingDirectory>${basedir}</workingDirectory>
               </configuration>
               <!--<phase>prepare-package</phase> -->
               <phase>generate-sources</phase>
               <goals>
                 <goal>exec</goal>
               </goals>
             </execution>
             <execution>
               <id>npm-clean</id>
               <configuration>
                 <workingDirectory>.</workingDirectory>
                 <executable>rm</executable>
                 <arguments>
                   <argument>-rf</argument>
                   <argument>node_modules</argument>
                 </arguments>
                 <workingDirectory>${basedir}</workingDirectory>
               </configuration>
               <!--<phase>prepare-package</phase> -->
               <phase>install</phase>
               <goals>
                 <goal>exec</goal>
               </goals>
             </execution>
           </executions>
         </plugin>   
     </build>
    </profile>
    
    <!-- Windows OS specific build profile -->
      <profile>
        <id>winOSBuild</id>
        <activation>
          <!-- Profile is NOT active by default -->
          <activeByDefault>false</activeByDefault>
        </activation>
        <build>
          <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>exec-maven-plugin</artifactId>
            <executions>
              <!-- Execute UI Build as part of standard Maven "generate-resource" phase -->
              <execution>
                <id>ui-npm-install</id>
                <configuration>
                  <workingDirectory>.</workingDirectory>
                  <executable>npm.cmd</executable>
                  <arguments>
                    <argument>install</argument>
                  </arguments>
                  <workingDirectory>${basedir}</workingDirectory>
                </configuration>
                <phase>validate</phase>
                <goals>
                  <goal>exec</goal>
                </goals>
              </execution>
              <execution>
                <id>ui-aem-build</id>
                <configuration>
                  <workingDirectory>.</workingDirectory>
                  <executable>ng.cmd</executable>
                  <arguments>
                    <argument>build</argument>
                    <argument>--prod</argument>
                    <argument>--outputHashing</argument>
                    <argument>none</argument>
                  </arguments>
                  <workingDirectory>${basedir}</workingDirectory>
                </configuration>
                <!--<phase>prepare-package</phase> -->
                <phase>generate-sources</phase>
                <goals>
                  <goal>exec</goal>
                </goals>
              </execution>
              <execution>
                <id>npm-clean</id>
                <configuration>
                  <workingDirectory>.</workingDirectory>
                  <executable>rmdir</executable>
                  <arguments>
                    <argument>/s</argument>
                    <argument>node_modules</argument>
                  </arguments>
                  <workingDirectory>${basedir}</workingDirectory>
                </configuration>
                <!--<phase>prepare-package</phase> -->
                <phase>install</phase>
                <goals>
                  <goal>exec</goal>
                </goals>
              </execution>
            </executions>
          </plugin>     
        </build>
      </profile>  
   </profiles>
   <!-- Omitted remaining pom.xml for simplicity -->
  </project>

Few important observations

·      We have moved the plugin “exec-maven-plugin” configuration inside XML tag
·      Created two profiles “macOSBuild” and “winOSBuild” (line 9 and 78 respectively)
·      macOSBuild” profile is active by default (line 12). This means on Mac OS, we can simply run the “mvn clean install” command and Mac profile will be automatically picked up for build
·      To execute build on Windows OS, we need to explicitly tell maven to use “winOSBuild” profile. This can be done via “mvn clean install -PwinOSBuild

I hope this simple concept will help you to avoid manual changes of pom.xml and keep your project away from accidental risk of build failures.

Comments

Popular posts from this blog

AEM as a Cloud Service (AEMaaCS) – Architecture Overview

AEM 6.3 - Bundle Whitelisting - Deprecation of administrative authentication

AEM - Query list of components and templates

Custom synchronisation or rollout action for blueprint ?