Subscribe to Anarion's Blog        RSS Feed
-----

CMake, An Extensible Build System

Icon Leave Comment
Makefile enables a programmer to organize a project into different units and join them all together to form one or more binary files. It seems reasonable and also easy to write a makefile by hand for small projects but what would the makefile look like when it comes to large projects? For instance, look at [This Example](although it is not that large). I cannot imagine myself spending time to write such file.
So, what's an alternative way to this?

I searched for and came up with the following answers: SCons, CMake, Autotools, and Waf(Sadly, I cannot access waf as it is forbidden for users from my country :whistling:).

Browsing the website, you can find this information in the "about" page:

Quote

CMake is an extensible, open-source system that manages the build process in an operating system and in a compiler-independent manner. Unlike many cross-platform systems, CMake is designed to be used in conjunction with the native build environment.

This is the main difference of CMake with SCons; CMake uses the native build environment. Great, isn't it?

Not 4 days ago, I started to experiment with Embedded InnoDB and wrote a basic C++ wrapper for it. Here's the structure:

Quote

Project Name: Innopp
|
+---- innopp.hpp
|
+---- main.cpp

As you can see, the structure of this project is very simple; A single source file plus a header file for the class definition. Because the innopp class's member functions should be inlined, the use of a separate compilation unit is not allowed.

In order to compile this little project, I wrote the following makefile:
innopp: main.o
    g++ -o innopp main.o -L /usr/local -linnodb
main.o: main.cpp innopp.hpp
    g++ -c main.cpp -I /usr/local/include/embedded_innodb-1.0
clean:
    rm -f *.o innopp


The equivalent job can be done through a CMakeLists.txt file(that is used by CMake) like this:
# Define minimum version of cmake allowed
cmake_minimum_required(VERSION 2.6)

# Define the name of this project
project(innopp)

# Include the directory in which innodb is placed
include_directories("${PROJECT_SOURCE_DIR}" "/usr/local/include/embedded_innodb-1.0")

# Describe the source files that form the final executable
add_executable(innopp main.cpp)

# Link with the innodb library
target_link_libraries(innopp "/usr/local/lib/libinnodb.so")

After CMake's done it's job, you will have a Makefile, with which you can compile the project. But the above CMakeLists.txt file is so simple, as the directory is predefined by myself. One improvement to this file would be finding out the paths automatically. In order to do so, a few things should be added to this file, and also, a find-module must be created for this library. First, let's write the module(named Findinnodb.cmake):
# try to find the directory in which the header is located
# the path will be put into INNODB_INCLUDE_DIR variable
# mentioning suffix is needed here to turn the following include(in source file):
# #include<embedded_innodb-1.0/innodb.h>
# into this:
# #include<innodb.h>
find_path(INNODB_INCLUDE_DIR NAMES innodb.h PATH_SUFFIXES embedded_innodb-1.0)
# try to find the library (shared or static)
# the resulting path would be put into INNODB_LIBRARY variable
find_library(INNODB_LIBRARY NAMES innodb)
include(FindPackageHandleStandardArgs)
# innodb is considered to be found if both INNODB_LIBRARY and INNODB_INCLUDE_DIR are valid
# if so, INNODB_FOUND variable is set to true, if not, the failure message is shown
find_package_handle_standard_args(innodb "Error: Could NOT find innodb library or header!" INNODB_LIBRARY INNODB_INCLUDE_DIR)
mark_as_advanced(INNODB_LIBRARY INNODB_INCLUDE_DIR)

Refer to the examples and documentations of CMake to understand the above lines.

Now, the CMakeLists.txt file should be updated to something like this:
# Define minimum version of cmake allowed
cmake_minimum_required(VERSION 2.6)

# Define the name of this project
project(innopp)

# Append directory of custom modules
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake/modules/")

# Use Findinnodb.cmake module to check for innodb library
find_package(innodb REQUIRED)

# Include the directory in which innodb is placed
include_directories("${PROJECT_SOURCE_DIR}" "${INNODB_INCLUDE_DIR}")

# Describe the source files that form the final executable
add_executable(innopp main.cpp)

# Link with the library found by the module
target_link_libraries(innopp "${INNODB_LIBRARY}")


Now, CMake finds out where the library and header file needed for this project is located, and it prints out an error if it could not find. How nice! :D
Here's the output of CMake while it is processing things:

Quote

***@****:/cpp/innodb/build$ cmake ../
-- The C compiler identification is GNU
-- The CXX compiler identification is GNU
-- Check for working C compiler: /usr/bin/gcc
-- Check for working C compiler: /usr/bin/gcc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Found innodb: /usr/local/lib/libinnodb.so
-- Configuring done
-- Generating done
-- Build files have been written to: /cpp/innodb/build


When your project depends on a couple of libraries, just throwing a mere makefile that does not check for them properly is not that nice. But with the help of build systems such as CMake, this task has become easy(just like above). Personally, I haven't worked with other build systems but CMake seems good enough for me. Will be happy to hear what others have experienced :)

0 Comments On This Entry

 

September 2014

S M T W T F S
 123456
78910111213
14 15 1617181920
21222324252627
282930    

Tags

    Recent Entries

    Recent Comments

    Search My Blog

    0 user(s) viewing

    0 Guests
    0 member(s)
    0 anonymous member(s)