Working with CMake

From Inkscape Wiki
Revision as of 22:55, 17 January 2016 by BryceHarrington (talk | contribs)
Jump to navigation Jump to search

This is a developer-oriented introduction to CMake and how to modify the CMake build scripts.

We assume you already know the basics of how to use cmake to configure and build Inkscape (if not, see Inkscape's README).


Functions and Variables

cmake has its own language and syntax, but it's pretty simple. Essentially, everything is a function call.

   message("Hello world!")

Even just setting variables is a function call:

   set(MYVAR "foobar")

Function calls have parenthesis but arguments aren't separated with commas. CMake isn't strict about casing for function names, so SET() and set() are equivalent, but it is strict about variable name casing. We'll adopt the convention of keeping function names lower case, and variable names upper case.

Strings don't always have to be quoted, although it's also a good convention to follow. So, these are all equivalent:

   SET(MYVAR "foobar")
   set(MYVAR foobar)
   SET(MYVAR foobar)

Variables are referenced using a dollar sign and curly brackets:

   set(MYVAR "foobar")
   message("${MYVAR}")


Your First CMake Script

You now know enough to create a trivial cmake program.

Create an empty directory and open a file named 'CMakeLists.txt' in your favorite text editor.

   mkdir cmake-tutorial
   cd cmake-tutorial
   gedit CMakeLists.txt

Type the following lines into this CMakeLists.txt file:

   set(MYVAR "foobar")
   message("${MYVAR}")

Save the text file, exit your editor, and run cmake in this directory:

   $ cmake .
   foobar
   -- Configuring done
   -- Generating done
   -- Build files have been written to: /tmp/cmake-tutorial

Pre-defined Variables

As you can imagine, CMake provides a broad set of pre-defined variables that relate to build systems.

It has a bunch to help determine what platform the build is building for:

   UNIX
   APPLE
   WIN32
   MSVC
   etc.

and a slew of variables to keep track of directories that things should go to:

   PROJECT_SOURCE_DIR
   CMAKE_SOURCE_DIR
   CMAKE_CURRENT_SOURCE_DIR
   PROJECT_BINARY_DIR
   CMAKE_BINARY_DIR
   CMAKE_CURRENT_BINARY_DIR
   CMAKE_INSTALL_PREFIX

Let's take a closer look at these:

   message("PROJECT_NAME              ${PROJECT_NAME}")
   message("PROJECT_SOURCE_DIR        ${PROJECT_SOURCE_DIR}")
   message("CMAKE_SOURCE_DIR          ${CMAKE_SOURCE_DIR}")
   message("CMAKE_CURRENT_SOURCE_DIR  ${CMAKE_CURRENT_SOURCE_DIR}")
   message("PROJECT_BINARY_DIR        ${PROJECT_BINARY_DIR}")
   message("CMAKE_BINARY_DIR          ${CMAKE_BINARY_DIR}")
   message("CMAKE_CURRENT_BINARY_DIR  ${CMAKE_CURRENT_BINARY_DIR}")
   message("CMAKE_INSTALL_PREFIX      ${CMAKE_INSTALL_PREFIX}")

For me this prints out:

   PROJECT_NAME              Project
   PROJECT_SOURCE_DIR        /tmp/cmake-tutorial
   CMAKE_SOURCE_DIR          /tmp/cmake-tutorial
   CMAKE_CURRENT_SOURCE_DIR  /tmp/cmake-tutorial
   PROJECT_BINARY_DIR        /tmp/cmake-tutorial
   CMAKE_BINARY_DIR          /tmp/cmake-tutorial
   CMAKE_CURRENT_BINARY_DIR  /tmp/cmake-tutorial
   CMAKE_INSTALL_PREFIX      /usr/local

The 'source' vs. 'binary' variables are the same if the user is doing an in-tree build but differ if out-of-tree. Generally, if you do out-of-tree builds yourself, you can be reasonably confident your code will work either way.

The 'current' variables come into play when you have a directory hierarchy with CMakeList.txt files at various levels. Variables like CMAKE_CURRENT_SOURCE_DIR refer to the currently processing child location in the tree, whereas CMAKE_SOURCE_DIR will always reference the root of the source tree.

Also, be aware that the user is able to set their own installation prefix, so be careful about assumptions about where things will be installed.

Let's try building our example in a more complex way to see how these variables change:

   $ mkdir build install
   $ cd build && cmake .. -DCMAKE_INSTALL_PREFIX=../install
   ...
   PROJECT_NAME              Project
   PROJECT_SOURCE_DIR        /tmp/cmake-tutorial
   CMAKE_SOURCE_DIR          /tmp/cmake-tutorial
   CMAKE_CURRENT_SOURCE_DIR  /tmp/cmake-tutorial
   PROJECT_BINARY_DIR        /tmp/cmake-tutorial/build
   CMAKE_BINARY_DIR          /tmp/cmake-tutorial/build
   CMAKE_CURRENT_BINARY_DIR  /tmp/cmake-tutorial/build
   CMAKE_INSTALL_PREFIX      /tmp/cmake-tutorial/install
   ...


Conditionals

If statements are pretty fundamental in all languages, but particularly so for configuring software. Here's how to do an if statement in CMake:

   if(MYVAR STREQUAL "foobar")
       set(FOOBAR "yes")
   endif()

Couple things to note - there's none of the usual ==, <, etc. boolean operators. Instead use EQUAL and LESS for numerical comparisons, STREQUAL, and STRLESS for strings. Other frequently used tests include EXISTS, DEFINED, AND, OR, and NOT. MATCHES provides a handy way to do regular expression testing on strings. VERSION_EQUAL, VERSION_LESS, and VERSION_GREATER are helpful for testing release version strings. There's a bunch of tests relating to checking status on files. For a complete list of available unary and binary tests see https://cmake.org/cmake/help/v3.0/command/if.html.



Lists