CMake's documentation is rare, I'd say mesed up. I'm currently digesting it and this is what's coming out! Lose no time and learn CMake quickly.

CMake Tutorial For The Lazy Part 1: Part One

Cmake

I'm not here to talk you about what CMake is because that is extensively described in the internet. You only need to know that it is a meta build system, that is, it builds build projects for a bunch of platforms (windows, linux, osx). What I'm here to tell you is how to use it in a sane way. Sure, there is the the official tutorial but you may have think of it as very bad and confusing material (as well as filled with gems of terrible code to add a little extra aggravation). This is the reason I'm creating this tutorial.

I wanted precise, cut to the chase learning material and couldn't find any. I want you to find such, so I create this for you. Let's stop chatting and let's get cmaking things.

Introduction to CMake.

CMakeLists.txt is to CMake what Makefile is to make. In CMakeLists.txt you describe a set of configurations, files and outputs. Remember that you're actually building build project files, so the syntax is more oriented to make you feel you're building an configuration file, not an executable.

Adding an executable

Let's define a source file called tutorial.cpp

#include <iostream>
#include <cstdlib>
#include <cmath>
//#include <TutorialConfig.h>

using namespace std;

int main (int argc, char* argv[])
{
    //cout << "sqrtr versión " << Tutorial_VERSION_MAJOR << "." << 
    //    Tutorial_VERSION_MINOR << endl;
    if (argc < 2)
    {
        cout << "Usage: "<< argv[0] << " number" << endl;
        return 1;
    }

    double inputValue = atof(argv[1]);
    double outputValue = sqrt(inputValue);

    cout << "The square root of " << inputValue << " is " << outputValue <<
        "." << endl;

    return 0;
}

Now, we create our CMakeLists.txt. We'll add the following:

cmake_minimum_required (VERSION 2.6)
project (Tutorial)
add_executable(tutorial tutorial.cpp)

This is a good start for a simple CMakeLists.txt. What we are doing there is:

  1. Requiring a minimum version for CMake.
  2. Setting a name for the project we're going to build.
  3. Adding an executable called tutorial built from the file tutorial.cpp to our build projects.

run

cmake /path/to/project

Project files for your platform will be built. Don't bother to check them, you shouldn't

Adding configuration

CMake is complex. It let's you do a little meta programming. CMake is complex. It has it's own programming language and variables.

Let's define a couple variables! Let's define code constants from CMake!

Change your CMakeLists.txt to look like this:

cmake_minimum_required (VERSION 2.6)
project (Tutorial)

set (Tutorial_VERSION_MAJOR 1) #new
set (Tutorial_VERSION_MINOR 0) #new
set (Tutorial_VERSION_BUGFIX 0) #new

#configure header file to pass some of the CMake settings 
#to the source code
configure_file(  #new
    "${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"  #new
    "${PROJECT_BINARY_DIR}/TutorialConfig.h"  #new
)  #new

# add the binary tree to the search path for include files
# so that we will find TutorialConfig.h
include_directories("${PROJECT_BINARY_DIR}")  #new

add_executable(tutorial tutorial.cpp)

What we did here is the following:

  1. We set three variables. Very much like Make variables.
  2. We used the directive configure_file. With this directive we can do a little templating. configure_file will make CMake to output a file called TutorialConfig.h from a template called TutorialConfig.h.in . We'll look later at the template. Don't worry about PROJECT_SOURCE_DIR and PROJECT_BINARY_DIR. They are cmake variables wich will point to your src root for now.
  3. If we want to do something useful with the generated TutorialConfig.h, we better use it in our code. Let's add it's parent directory to our include directories using the include_directories directive. Do note how we are expanding the variable PROJECT_BINARY_DIR.

Let's take a look at our TutorialConfig.h.in:

//The configured  options and settings for tutorial
#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@

Remember your friends Tutorial_VERSION_MAJOR and Tutorial_VERSION_MINOR? They are from CMakeLists.txt. You defined them there. When you run cmake it will create the following TutorialConfig.h file:

//The configured  options and settings for tutorial
#define Tutorial_VERSION_MAJOR 1
#define Tutorial_VERSION_MINOR 0

You just created a file from a tempĺate!

Well, but it is of no good use if we don't get him to do some work. Let's use it in our code! Uncomment every commented line in tutorial.cpp so it looks like this:

#include <iostream>
#include <cstdlib>
#include <cmath>
#include <TutorialConfig.h>

using namespace std;

int main (int argc, char* argv[])
{
    cout << "sqrtr versión " << Tutorial_VERSION_MAJOR << "." << 
        Tutorial_VERSION_MINOR << endl;
    if (argc < 2)
    {
        cout << "Usage: "<< argv[0] << " number" << endl;
        return 1;
    }

    double inputValue = atof(argv[1]);
    double outputValue = sqrt(inputValue);

    cout << "The square root of " << inputValue << " is " << outputValue <<
        "." << endl;

    return 0;
}

What we did was simply:

  1. include our generated TutorialConfig.h
  2. Print constants defined in TutorialConfig.h

You're ready for the next step.

Building a library

Buildig an executable is fine. For a 3th grader.

We all remember our green years, building big, monolitic .c (or .cpp) files with thousands of lines and nestes if scopes for more than ten levels. Oh sweet sweet times, don't we miss them?. No.

To be all hipey and cool, boys around the block they all've got their libraries build modulary for the sake of reuse (as if such thing even exist). I am one of those guys, and I'll teach you how to do it. Just like me.

First. Let's add a directory in our root. We'll call it MathFunctions. That will be the name of our subproject which will build a library. Inside math functions we'' create three files:

  1. mysqrt.hpp
  2. mysqrt.cpp
  3. CMakeLists.txt

I hope I don't have to explain you why you want mysqrt.h and mysqrt.cpp. If I do, then get away and code a little, you poser. We'll define a function called mysqrt inside mysqrt.hpp, and we'll implement it in mysqrt.cpp. Then we'll tell cmake how to create build files to create a library called libMathFunctions.so.

I would advise you to implement mysqrt.cpp it as you like it, but here's how I did.

// mysqrt.hpp
double mysqrt(double in);


// mysqrt.cpp
#include <mysqrt.hpp>
double mysqrt(double in)
{
    return in / 2;
}

Now, let's define the library's CMakeLists.txt:

cmake_minimum_required(VERSION 2.8)
project(MathFunctions)

include_directories(${PROJECT_SOURCE_DIR})

SET (HEADER_FILES mysqrt.hpp)
add_library(MathFunctions SHARED mysqrt.cpp ${HEADER_FILES})

What happened here?

  1. The first 2 lines are pretty much like the first CMakeLists.txt that we made. We define which version of cmake do we need, and a name for our project,
  2. In 3rd line we added the project's source dir (MathFunctions) to our include search path. And in 4th line we set the variable HEADER_FILES to the value mysqrt.hpp.
  3. In the last line, we add a shared library to our project build files. The library will be named MathFunctions and will be compiled from the files mysqrt.cpp and HEADER_FILES. The header file is found thanks to 3rd line include_directories.

If we run cmake on this directory, now we'll create correctly a [ Visual Studio Project | Makefile ] that will successfuly create a [ MathFunctions.dll | libMathFunctions.so ].

But this is not all we want. We want more. We want linking. We want to use this library in our executable.

Linking a library

Let's go to the nasty tasty part. Back in our executable's CMakeLists.txt we will add some lines. After our original

include_directories("${PROJECT_BINARY_DIR}")

Add this:

# add the include directories necessary to build library
include_directories("${PROJECT_SOURCE_DIR}/MathFunctions}")
add_subdirectory(MathFunctions)

What happened here?

  1. include_directories will help use find, in our tutorial.cpp file, the header mysqrt.hpp when we include it.
  2. add_subdirectory tell cmake to build also the subdirectory.

Now, still in our original CMakeLists.txt, well add at the end the following line:

target_link_libraries(tutorial MathFunctions)

This lines tells cmake to set our library MathFunctions to our tutorial executable. We'll also modify our existing add_executable directive and tell it to include MathFunctions/mysqrt.hpp while building our executable. Let's do something like this:

SET (MATHFUNCTIONS_DIR ${PROJECT_SOURCE_DIR}/MathFunctions)
add_executable(tutorial tutorial.cpp ${MATHFUNCTIONS_DIR}/mysqrt.hpp)

If you build your project right now, everything must go smooth. Now let's change tutorial.cpp and consume our library!

At the beginning of the file, use this include directive:

#include <MathFunctions/mysqrt.hpp>
//#include <mysqrt.hpp> you could also do this because we added `MathFunctions` to our include path.

Now go you can use mysqrt instead of sqrt!!! Linking done!!!!

Posted by: fabzter
Last revised: 27 Feb, 2014 01:00 PM History

Comments

Anonymous
Anonymous
08 Feb, 2014 05:51 PM @ version 6

Thanks for this. Can we expect a part 2 soon in the future?

27 Feb, 2014 12:52 PM @ version 6

Of course! if you're interested just comment back or email me. I'll try to have it this week if you need so just let me know ;)

jmcarter17
jmcarter17
07 Apr, 2015 09:38 PM

Hi, just found your tutorial. I also hope to see a part 2 for this.

Quick question, how come I cannot use #include ? When it builds, it doesn't find the Header file in the MathFunctions subdirectory. It works if I use the #include directive, but I want to use the other one.

Cheers

jmcarter
jmcarter
08 Apr, 2015 12:59 PM

Sorry, just realized it didn't show up properly.

Quick question, how come I cannot use #include When it builds, it doesn't find the Header file in the MathFunctions subdirectory. It works if I use the #include "MathFunctions/mysqrt.hpp" directive, but I want to use the other one.And it works only with "" and not with the angled brackets.

Cheers

jmcarter
jmcarter
08 Apr, 2015 03:11 PM

Sorry, just realized it didn't show up properly.

Quick question, how come I cannot use

#include <mysqrt.hpp>

When it builds, it doesn't find the Header file in the MathFunctions subdirectory. It works if I use the

#include "MathFunctions/mysqrt.hpp"

directive, but I want to use the other one. And it works only with "" and not with the angled brackets.

Cheers

30 Jun, 2015 03:41 PM

Hi jmcarter!

You cannot use your first option

#include <mysqrt.hpp>

Because you must first tell the compiler's preprocessor to add "MathFunctions" to its search path. In the tutorial, I point how to add such directory to the preprocessor's search path. Feel free to ask if its it's not clear yet.

Your Comments

Used for your gravatar. Not required. Will not be public.
Posting code? Indent it by four spaces to make it look nice. Learn more about Markdown.

Preview