In rare cases, project build may require different configuration depending on operating system that runs the build.
In Maven, it can be done with Maven Profiles.
- how to configure profiles per operating system
- the trick to activate profile on Linux but not on Mac
- how to enforce presence of active profile with
maven-enforcer-plugin
- how to run build on different operating systems with GitHub action
Configure Profiles
You can define profile for each supported operating system and provide different configuration, which includes dependencies, build plugins or just setting different OS specific property value.
<profiles>
<profile>
<id>windows</id>
<properties>
<my.property>running.on.windows</my.property>
</properties>
</profile>
<profile>
<id>mac</id>
<properties>
<my.property>running.on.mac</my.property>
</properties>
</profile>
<profile>
<id>unix</id>
<properties>
<my.property>running.on.linux</my.property>
</properties>
</profile>
</profiles>
Profiles can be activated manually by setting -P
flag to Maven command:
$ mvn package -Pwindows
Likely, you would like to avoid the need for user to set this profile. Maven can figure it out by checking the operating system the build is running on and activating a profile for this OS:
<profile>
<id>windows</id>
<activation>
<os>
<family>windows</family>
</os>
</activation>
<properties>
<my.property>running.on.windows</my.property>
</properties>
</profile>
Activation can be narrowed to specific operating system name, architecture or even exact version. To not end up with dozens of profiles, assuming we are running builds only on x86
64bit
architectures we can use family
to let Maven activate the profile.
Family may have one of the following values:
- dos
- mac
- netware
- os/2
- tandem
- unix
- windows
- win9x
- z/os
- os/400
- openvms
If you look closely, you may have noticed that there is no family for Linux. There is one for unix, but Mac is unix too.
Activate profile on Linux but not on Mac
When we use following profiles' config:
<profiles>
<profile>
<id>windows</id>
<activation>
<os>
<family>windows</family>
</os>
</activation>
<properties>
<my.property>running.on.windows</my.property>
</properties>
</profile>
<profile>
<id>mac</id>
<activation>
<os>
<family>mac</family>
</os>
</activation>
<properties>
<my.property>running.on.mac</my.property>
</properties>
</profile>
<profile>
<id>linux</id>
<activation>
<os>
<family>unix</family>
</os>
</activation>
<properties>
<my.property>running.on.linux</my.property>
</properties>
</profile>
</profiles>
Build will run as expected on Windows and Linux, but on Mac both mac and unix profile will be active.
To properly narrow it down to activate linux
profile only when running on Linux, and mac
profile only when running on Mac, the Linux profile must be activated by operating system name:
<profiles>
<profile>
<id>windows</id>
<activation>
<os>
<family>windows</family>
</os>
</activation>
<properties>
<my.property>running.on.windows</my.property>
</properties>
</profile>
<profile>
<id>mac</id>
<activation>
<os>
<family>mac</family>
</os>
</activation>
<properties>
<my.property>running.on.mac</my.property>
</properties>
</profile>
<profile>
<id>linux</id>
<activation>
<os>
<name>Linux</name>
</os>
</activation>
<properties>
<my.property>running.on.linux</my.property>
</properties>
</profile>
</profiles>
How to prevent from running on different OS or on unsupported architecture?
With such configuration, when build runs on the OS that does not match any of the activations, simply no profile will be active and likely build fails in unexpected way due to a missing whatever-you-defined-in-profile-configuration.
To avoid it, you can use maven-enforcer-plugin to ensure that one of the profiles is active:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<id>enforce-os-profile</id>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<requireActiveProfile>
<profiles>windows,mac,linux</profiles>
<all>false</all>
</requireActiveProfile>
</rules>
<fail>true</fail>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
Run build on different operating systems with GitHub action
If your build depends on the operating system, it is worth to run it on the CI server against each of the supported OS.
GitHub Actions supports running jobs on Ubuntu, Windows and Mac OS. A sample action that runs Maven build on each of these is:
name: Build
on: [push]
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
steps:
- uses: actions/checkout@v3
- uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '17'
cache: maven
- run: ./mvnw package
Unfortunately at this stage there is no out-of-the-box support for running jobs on runners with ARM processors, such as Apple M1. If that's what you need, you must use a self hosted runner on GitHub and one of the providers that offer M1 - but it is not free.