Area Man (Kinda) Figures out How to Use CPack
Lately I have been working on a C++11 project that uses CMake. I find CMake to be approachable, useful, and sometimes somewhat weird. But it gets the job done.
I suppose I should document what I have managed to learn so far (which is: just enough to be useful), and I will do that in due time, in a regularly scheduled future weblog update (haha).
For now here are some notes about a thing I struggled with, for longer than I should have: making an RPM package from ye olde CMake project.
CMake ships with a companion program, CPack, which makes creating
binary packages easy. All you need is a line like this in your
top-level CMakeLists.txt
:
include(CPack)
Our project generates the good ole Unix Makefiles, so I should have been able to do just run some commands and get on with my day:
$ cd $BUILDDIR
$ cmake $SOURCEDIR
$ make package # or just run 'cpack'
As we know nothing in computering land works that way.
With the basic configuration, the above produced three packages: a
self-extracting shell script, a tar.gz
package, and a Unix
compressed .Z
package. But here’s a problem:
$ ls -l project-0.1.1-Linux.*
-rwxrwxrwx 1 sajith sajith 3818 Sep 8 10:35 project-0.1.1-Linux.sh
-rw-rw-r-- 1 sajith sajith 29 Sep 8 10:35 project-0.1.1-Linux.tar.gz
-rw-rw-r-- 1 sajith sajith 54 Sep 8 10:35 project-0.1.1-Linux.tar.Z
They are all empty! Also I still do not have that RPM package I
needed. So I set the CPACK_GENERATOR
variable before the
include(CPack)
line:
set(CPACK_GENERATOR "RPM")
include(CPack)
In order to have the intended artifacts (binaries, documentation, etc)
inside the archives, I also needed some install
lines in the
appropriate CMakeLists.txt
files. (We have not been “make
install
”-ing our project, so we had omitted the install
lines.
CMake-built binary packages get their contents from the install step,
and it took a bit of figuring out.)
install(TARGETS binary DESTINATION bin)
install(DIRECTORY project-config-files DESTINATION etc)
install(FILES README.txt DESTINATION share/doc/project)
With that, I have a basic RPM file. For further customization, there are a bunch of extra variables that we can set:
set(CPACK_GENERATOR "RPM")
set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR})
set(CPACK_PACKAGE_RELEASE 1)
set(CPACK_PACKAGE_NAME "usual-widgets")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Some usual widgets for day-to-day use")
set(CPACK_PACKAGE_VENDOR "Usual Business Corp")
set(CPACK_PACKAGE_CONTACT "Your Humble Packager <packager@example.net>")
set(CPACK_PACKAGING_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX})
set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-${CPACK_PACKAGE_RELEASE}.${CMAKE_SYSTEM_PROCESSOR}")
set(CPACK_RPM_PACKAGE_LICENSE "GPLv3")
set(CPACK_RPM_PACKAGE_GROUP "Applications/Internet")
set(CPACK_RPM_PACKAGE_URL "https://project.example.net/packages")
We can also instruct CPack to generate additional kinds of packages:
set(CPACK_GENERATOR "RPM;DEB;TGZ")
And there are more knobs to turn:
set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "amd64")
set(CPACK_DEBIAN_PACKAGE_VERSION 1)
set(CPACK_DEBIAN_PACKAGE_SECTION "Network")
set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "https://project.example.net/packages")
Another thing I figured out is that I can set CMAKE_INSTALL_PREFIX
to somewhere other than the default /usr/
, when running cmake
:
$ cmake -DCMAKE_INSTALL_PREFIX=/opt/UsualBusinessCorp/ $SOURCEDIR
I still do not know how to sign the packages, etc., but then again, this is just enough to be useful, and that is all I need.
Extra: verbose output
What if I need a little more verbose output from CPack?
cpack -V -G RPM -D CPACK_RPM_PACKAGE_DEBUG=1
cpack -V -G DEB -D CPACK_DEBIAN_PACKAGE_DEBUG=1
Extra: the dreaded comma
Here’s a frustrating but really silly thing I dealt with: I inserted
an extra comma in one of the set()
directives by mistake, and then
spent an inordinate amount of time figuring out why things weren’t
working the way I thought they should be working.
CMake silently ignores the directive when you do set(VAR, VAL)
: it
has to be set(VAR VAL)
or CMake will do its thing and not the thing
you’re asking it to do. You do not want this to happen.