Overview
CMake is a cross-platform build tool like GNU build system, but it's much easier to use and faster than GNU build system. It is designed to build software written by C, C++ or Java. It adds a lot of implicit rules to help programmer with few configurations. This article will explain how to use this powerful tool step by step with workable examples. We will talk about the details of each syntax we use here but not try to exhaust all syntax CMake has. If you want to dig into CMake, please refer to its document.
The version of CMake we use is 2.8.7 on Ubuntu 12.04. However, we just use the basic syntax. It sould be okay if you use other versions and on other platforms.
First example - say hello
Following the great tradion, our first example will use CMake to build a simple C++ program to say hello. Before we add any CMake configuration files, we have following files:
mars@dream$ pwd /home/mars/try/cmake/C++Hello mars@dream$ tree . └── main.cxx 0 directories, 1 file mars@dream$ cat main.cxx #include <iostream> int main(void) { std::cout << "Hello, SMAP." << std::endl; }
To get CMake's great capabilities, we should add a special file named "CMakeLists.txt" in the directory. Here comes the file with comments:
#CMake's instructions are not case-sensitive, but the convention is to use the upper case #CMAKE_MINIMUM_REQUIRED requires the minimum version of CMake #if you do not specify this, cmake will complain you CMAKE_MINIMUM_REQUIRED(VERSION 2.8) #give this project a name PROJECT(hello) #use main.cxx to build an executable file named hello ADD_EXECUTABLE(hello main.cxx)
OK, that's all. use the following commands to trigger CMake and make things happen:
mars@dream$ ls CMakeLists.txt main.cxx mars@dream$ 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 -- Configuring done -- Generating done -- Build files have been written to: /home/mars/try/cmake/C++Hello mars@dream$ ls CMakeCache.txt CMakeFiles cmake_install.cmake CMakeLists.txt main.cxx Makefile mars@dream$ make Scanning dependencies of target hello [100%] Building CXX object CMakeFiles/hello.dir/main.cxx.o Linking CXX executable hello [100%] Built target hello mars@dream$ ls CMakeCache.txt CMakeFiles cmake_install.cmake CMakeLists.txt hello main.cxx Makefile mars@dream$ ./hello Hello, SMAP. mars@dream$ make clean mars@dream$ ls CMakeCache.txt CMakeFiles cmake_install.cmake CMakeLists.txt main.cxx Makefile mars@dream$
As we observed, cmake detects what toolchain should be used and generates a makefile after using "cmake .". The makefile it generates will contain many makefile targets, like all, clean...etc.
Second example - say hello with hierarchy directories
Previous example is too simple to be true. When we develop a software, we usually need to break functionalities into different directories. One of the powers of CMake is its flexibility to handle this. We will seperate some functions from main function and try to build a library. The main program will include the library's declarations(header file) and be linked against it. In addition, the previous example shows that CMake will generate some files which might mess with our source tree. We will see how to get all of those things done.
Before CMakeLists.txt comes in, we have the following directories and files:
mars@dream$ pwd /home/mars/try/cmake/C++HelloHierarchy mars@dream$ tree . ├── build ├── include │ └── hello.hxx ├── lib │ └── hello.cxx └── src └── main.cxx 4 directories, 3 files mars@dream$ cat include/hello.hxx #ifndef _HELLO_HXX_ #define _HELLO_HXX_ void hello(void); void smap(void); #endif //end of _HELLO_HXX_ mars@dream$ cat lib/hello.cxx #include <iostream> #include <hello.hxx> void hello(void) { std::cout << "Hello, "; } void smap(void) { std::cout << "SMAP." << std::endl; } mars@dream$ cat src/main.cxx #include <hello.hxx> int main(void) { hello(); smap(); }
The build directory is for clean build. CMake can not remove the extra files it generates due to some reasons. CMake solves this problem by an elegant way - out-of-source build. Briefly speaking, out-of-source means you can build the software in any directory without building software in source tree directory. Therefore the files CMake generates will only reside in the build directory. Our empty "build" directory is for this approach.
Now we can add some CMakeLists.txt files into source tree:
mars@dream$ pwd /home/mars/try/cmake/C++HelloHierarchy mars@dream$ tree . ├── build ├── CMakeLists.txt ├── include │ └── hello.hxx ├── lib │ ├── CMakeLists.txt │ └── hello.cxx └── src ├── CMakeLists.txt └── main.cxx 4 directories, 6 files mars@dream$ cat ./CMakeLists.txt #I just add comments with new instructions. CMAKE_MINIMUM_REQUIRED(VERSION 2.8) PROJECT(hello_hierarchy) #CMake's way to define a variable. #we define a list variable containing each subdirectories to build SET(SUBDIRS lib src) #use foreach instruction to tell CMake what subdirectories it should build foreach(dir ${SUBDIRS}) ADD_SUBDIRECTORY(${dir}) endforeach() mars@dream$ cat lib/CMakeLists.txt #${PROJECT_SOURCE_DIR} would be the top level of the source tree #in this case, it is /home/mars/try/cmake/C++HelloHierarchy #INCLUDE_DIRECTORIES instruction means CMake should add an include path #before doing the following process INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/include) #ask CMake to generate both shared and static version of this library ADD_LIBRARY(hello_shared SHARED hello.cxx) ADD_LIBRARY(hello_static STATIC hello.cxx) #By default, CMake will generate library based on the first argument of ADD_LIBRARY #However, that's not we want. What we want is libhello.a and libhello.so #not libhello_static.a nor libhello_shared.so #here comes the idiom to solve the odds SET_TARGET_PROPERTIES(hello_static PROPERTIES OUTPUT_NAME "hello") SET_TARGET_PROPERTIES(hello_shared PROPERTIES OUTPUT_NAME "hello") #in addition, shared library in Unix system usually has version with it #we can mark its version by the following instruction SET_TARGET_PROPERTIES(hello_shared PROPERTIES VERSION 1.1 SOVERSION 1) mars@dream$ cat src/CMakeLists.txt INCLUDE_DIRECTORIES (${PROJECT_SOURCE_DIR}/include) #${PROJECT_BINARY_DIR}'s value is the build directory's path #since we use out-of-source build, #it might be /home/mars/try/cmake/C++HelloHierarchy #LINK_DIRECTORIES instruction will add the path to search link path LINK_DIRECTORIES(${PROJECT_BINARY_DIR}/lib) ADD_EXECUTABLE(hello_hie main.cxx) #since we have the link path of libhello.[a|so], we should ask CMake to link it TARGET_LINK_LIBRARIES(hello_hie hello)
Now, change to build directory and type as the following:
mars@dream$ 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 -- Configuring done -- Generating done -- Build files have been written to: /home/mars/try/cmake/C++HelloHierarchy/build mars@dream$ make Scanning dependencies of target hello_shared [ 33%] Building CXX object lib/CMakeFiles/hello_shared.dir/hello.cxx.o Linking CXX shared library libhello.so [ 33%] Built target hello_shared Scanning dependencies of target hello_static [ 66%] Building CXX object lib/CMakeFiles/hello_static.dir/hello.cxx.o Linking CXX static library libhello.a [ 66%] Built target hello_static Scanning dependencies of target hello_hie [100%] Building CXX object src/CMakeFiles/hello_hie.dir/main.cxx.o Linking CXX executable hello_hie [100%] Built target hello_hie mars@dream$ ./src/hello_hie Hello, SMAP. mars@dream$ ls lib/ CMakeFiles cmake_install.cmake libhello.a libhello.so libhello.so.1 libhello.so.1.1 Makefile
留言
張貼留言