2 min read

Python & Java: a unified build process (2/4)

In our previous blog post dedicated to Python and Java, we saw how Maven can orchestrate a unified build process for these two languages.

But most of the time, all artifacts within a build should take the same version. It was not the case in the sample project of our previous blog post, so let's find a way to unify this.

For Java it's easy: JARs and WARs are using the version located in their respective POMs, 0.1 in this case:

<project>
  <groupid>com.shiningpanda</groupid>
  <artifactid>jsample</artifactid>
  <version>0.1</version>
  <packaging>jar</packaging>
  <!-- [...] -->
</project>

It would be great if Python projects could also get their versions from the POM. Let's see how to do that.

The POM version has to be propagated when calling setup.py. The easiest way is to set an environment variable with exec-maven-plugin. Modify setuptools/pom.xml in the sample project as follows:

<project>
  <!-- [...] -->
  <build>
    <pluginmanagement>
      <plugins>
        <plugin>
          <groupid>org.codehaus.mojo</groupid>
          <artifactid>exec-maven-plugin</artifactid>
          <configuration>
            <!-- Mappings [...] -->
            <environmentvariables>
              <VERSION>${project.version}</VERSION>
            </environmentvariables>
          </configuration>
        </plugin>
      </plugins>
    </pluginmanagement>
  </build>
</project>

Now a VERSION environment variable containing the value of the POM's version tag is available for setup.py. It can be used to generate a __version__ module containing the project's version. The setup.py script of the pysample project would be modified like this:

# Folder containing setup.py
root = os.path.dirname(os.path.abspath(__file__))
# Path to __version__ module
version_file = os.path.join(root, 'pysample', '__version__.py')
# Check if this is a source distribution.
# If not create the __version__ module containing the version
if not os.path.exists(os.path.join(root, 'PKG-INFO')):
    fd = codecs.open(version_file, 'w', 'utf-8')
    fd.write('version = %r\n' % os.getenv('VERSION', '?'))
    fd.close()
# Load version
exec(open(version_file).read())
# Setup
setup(
    name = 'pysample',
    version = version,
    packages = find_packages(),
)

Note that we only generate the __version__ module if the PKG-INFO file does not exist. Indeed, an existing PKG-INFO file means that we're installing a source distribution previously generated by the setup.py sdist command.

Now all our artifacts are getting their version number from their POM. The versions in the POMs are easily handled thanks to the maven-release-plugin, but we will cover this in another blog post.

A Maven convention wants that version numbers are postfixed with a -SNAPSHOT between two releases. Setuptools uses more likely a .dev one, so feel free to process your POM version in your setup.py, for instance with:

os.getenv('VERSION', '?').replace('-SNAPSHOT', '.dev')

It can also be useful to get the revision version from your source code management tool. To do so, use the buildnumber-maven-plugin. Following the same principle, export an environment variable containing the revision that can be used in the setup.py to compute an artifact version (with a 0.1.dev-r1989 or 0.1.dev-rb0c1c6 pattern for example).

In addition to a Hosted Continuous Integration Service, ShiningPanda CI is also offering build and release management expertise, so if you have questions or if you are stuck with your internal build process do not hesitate to contact our service team!