martes, 20 de octubre de 2009

cmake tutorial. First steps

We all have messed up with makefiles, and most of us have been really annoyed with them. Writing them is awful (and all that space-vs-tab thing...yuk!), and maintaining them for medium projects is not very pleasant either.

I've tried to understand autotools a couple of times, but never made through. Difficult and outdated docs, and when I passed the 'hello world' project, I didn't find it very intuitive. It's a pity when you have to study a tool for a month to get simple things done.

cmake tries to make the process easier while improving the dependency tracking between files. It's also multiplatform and multi-compiler , so you can use it for later use of gnu make, or VC++ projects.

First, we can start with a trivial use case. Good old 'Hello World'. Well, you imagine the code.


#include <stdio.h>
int main(int argc, char const* argv[])
{
printf("Hello world!\n");
return 0;
}

Save it in a file called helloworld.c and we're ready to write a file for cmake configuration.

Create and edit a file called CmakeLists.txt and add the following lines to it
PROJECT(helloworld C)

SET(SRC helloworld)

ADD_EXECUTABLE(helloworld ${SRC})

Then just execute "cmake ." in that directory, and you'll see you have a Makefile (and lots of other files and dirs. For the moment, what you have there is already usable. For example you can use make clean too.

After this baby steps, we can go to a more complex example. Let's use a c++ example, with a main file and a class file.


/*****Hola.cpp ****/
#include "Hola.h"

using namespace std;
void Hola::saluda(){
std::cout << "saludo" << endl;
return;
}
/*******************/
/*****Hola.h ****/
#ifndef HOLA_H
#define HOLA_H

#include <iostream>
class Hola{
public:
Hola() {};
~Hola() {};
void saluda(void);
};

#endif /* ----- #ifndef HOLA_H ----- */

/*******************/
/*****projecte.cpp ****/
#include <stdio.h>
#include <iostream>
#include "Hola.cpp"

using namespace std;

int main(int argc, char const* argv[])
{
Hola* a = new Hola();
a->saluda();
return 0;
}


Then we'll have a little more complex CMakeLists.txt file


PROJECT(projecte CXX)

add_definitions(-Wall -W -ggdb)

SET(SRC projecte)

INSTALL(FILES projecte DESTINATION bin)

ADD_EXECUTABLE(projecte ${SRC})

A few observations:

  • Note the change in the first line from C to CXX.
  • add_definitions directive is to add flags to the compiling process.
  • INSTALL directive enables 'make install'. DESTINATION bin tells to install in $prefix/bin (usr/local/bin normally) .
  • We don't even have to mention Hola file, because it's included by projecte.cpp, and cmake will track the dependency, and build only in case of modification.
Now we'll build the same program compiling Hola class into a separate library. That's how the CMakeLists.txt file looks like


PROJECT(projecte CXX)

add_definitions(-Wall -W -ggdb)

SET(LIBSRC
Hola
)

SET(SRC
projecte
)

INSTALL(FILES libHola.so DESTINATION lib)
INSTALL(FILES projecte DESTINATION bin)


ADD_LIBRARY(Hola SHARED ${LIBSRC})

ADD_EXECUTABLE(projecte ${SRC})

TARGET_LINK_LIBRARIES(projecte Hola)

Everything is nearly self explanatory, right? well, you can then rerun 'cmake .' , and a new Makefile will be generated.

There are some good tutorials out there. Most of what I found explain the same in different blogposts with some example CMakeLists.txt

Cya!