Difference between revisions of "Working with CMake"
(Created page with "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...") |
|||
Line 50: | Line 50: | ||
-- Generating done | -- Generating done | ||
-- Build files have been written to: /tmp/cmake-tutorial | -- 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 == |
Revision as of 22:55, 17 January 2016
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.