Aurélien Gâteau

CMake and "make dist"

written on Sunday, August 9, 2009

Update: Here is a simpler way to create a "make dist" target.

Creating a source archive

The other day at work I needed to create a release for some packages I have been working on (more on that later). Since I am using CMake for these projects, I looked around at how it could help me generate my source archives. CMake has a tool called CPack, which can generate cross-platform binary packages (.msi for Windows, .dmg for Mac OS, .rpm, .deb or binary tarballs for Unix) as well as source archives.

I found little documentation on how to tweak the way CPack generate source archives, so I am going to describe how I solved my problem. Maybe it can help others, or you can point me to smarter ways.

Here is a short extract of what I ended up with:

set(CPACK_PACKAGE_VERSION_MAJOR "0")
set(CPACK_PACKAGE_VERSION_MINOR "2")
set(CPACK_PACKAGE_VERSION_PATCH "3")
set(CPACK_SOURCE_GENERATOR "TBZ2")
set(CPACK_SOURCE_PACKAGE_FILE_NAME
  "${CMAKE_PROJECT_NAME}-${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}")
set(CPACK_SOURCE_IGNORE_FILES
  "/build/;/.bzr/;~$;${CPACK_SOURCE_IGNORE_FILES}")
include(CPack)

Once you add this to your project, running make package_source will create an archive named "foo-0.2.3.tar.bz2" (assuming your CMakeLists.txt file contains a project(foo) line).

Let's detail these lines:

set(CPACK_PACKAGE_VERSION_MAJOR "0")
set(CPACK_PACKAGE_VERSION_MINOR "2")
set(CPACK_PACKAGE_VERSION_PATCH "3")

Nothing fancy here, we just define the version number of our package.

set(CPACK_SOURCE_GENERATOR "TBZ2")

By default CPack generates .tar.Z, .tar.gz and .tar.bz2 archives. Set this variable to only generate .tar.bz2 archives.

set(CPACK_SOURCE_PACKAGE_FILE_NAME
  "${CMAKE_PROJECT_NAME}-${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}")

By default CPack creates an archive named "foo-0.2.3-Source.tar.bz2". The only way I found to get rid of the "-Source" suffix was to redefine the CPACK_SOURCE_PACKAGE_FILE_NAME variable.

set(CPACK_SOURCE_IGNORE_FILES
  "/build/;/.bzr/;~$;${CPACK_SOURCE_IGNORE_FILES}")

If you create your build dir inside the source dir, CPack will do stupid things such as including the content of the build dir in the archive. Fortunately you can tell it to ignore files with CPACK_SOURCE_IGNORE_FILES. I added my build and .bzr dirs (my work projects are managed with Bazaar).

include(CPack)

This is where the magic happen. Including "CPack" creates the package and package_source targets. It is important to add this line after the various "set(CPACK..." lines, otherwise they will be ignored.

Creating a "dist" target

This setup is nice, but it has two problems:
  1. Running make package_source do not update CMake cache, which is painful when you are adjusting the various CPACK_ vars
  2. make dist is more natural than make package_source
To fix both of those, I created a dist target like this:

add_custom_target(dist COMMAND ${CMAKE_MAKE_PROGRAM} package_source)

This line creates a dist target, and ensures the CMake cache is updated if you run make dist after changing the CMakeLists.txt (not sure why... Can somebody explain?)

Be careful...

CPack puts everything from the source tree inside the archive, including any file lying around. This is probably not what you want... To ensure you create clean archives, always run make dist from a clean source tree. A nice way to do this with git or bzr is to create a local clone of your working tree. The procedure is thus the following:

{bzr,git} clone <path/to/source/tree> tmp
cd tmp
mkdir build
cd build
cmake ..
make dist

You just have to make sure the "build", ".bzr" or ".git" dirs are ignored in CPACK_SOURCE_IGNORE_FILES.

Hope this helps! Maybe I'll come back later with a recipe for make distcheck, if I find the time to setup such a target.

This post was tagged cmake, make, make dist and tips