CMake官网Training Materials:
- https://cmake.org/cmake/help/latest/guide/tutorial/index.html
- https://cmake.org/cmake/help/book/mastering-cmake/
CMake官网Reference:https://cmake.org/documentation/
Make官网及文档:
- http://www.gnu.org/software/make/
- http://www.gnu.org/software/make/manual/
1. gcc、make和cmake简介
gcc(GNU Compiler Collection)将源文件编译(Compile)成可执行文件或者库文件;
而当需要编译的东西很多时,需要说明先编译什么,后编译什么。常用的工具是make,对应的定义构建(Build)过程的文件为Makefile;
而编写Makefile对于大型项目又比较复杂,通过CMake就可以使用更加简洁的语法定义构建的流程,CMake定义构建过程的文件为CMakeLists.txt。
GCC可以换成其它编译器。
CMake提供cmake、ctest和cpack三个命令行工具分别负责构建、测试和打包。本文只关心cmake的使用。
除了makefile,现在cmake也能生成vs和xcode的项目文件。
2. cmake语法
命令文档:https://cmake.org/cmake/help/v3.22/manual/cmake-commands.7.html
CMake语言在项目配置中组织为三种源文件类型:
- 目录:CMakeLists.txt,针对的是一个目录,描述如何针对目录(Source tree)生成构建系统,会用到项目配置命令;
- 脚本:
2. cmake变量
如PROJECT_BINARY_DIR, PROJECT_SOURCE_DIR
set指令可以设置变量。注意双引号,空格和分号的用法。
${}
访问变量。
https://cmake.org/cmake/help/v3.10/manual/cmake-variables.7.html
3. 常用指令
https://cmake.org/cmake/help/v3.10/manual/cmake-commands.7.html
一般用message输出信息。
注意不要用逗号来分隔参数!!!
4. 内外部构建
内部构建就是直接cmake . && make
,会在当前目录产生很多临时文件。
外部构建需要先创建一个build文件夹,执行mkdir build && cd build && cmake .. && make
。
一般当然是外部构建。
5. 完整工程
mkdir src && mkdir build && touch COPYRIGHT README && touch doc && touch CMakeLists.txt && touch src/CMakeLists.txt
.
├── build
├── CMakeLists.txt
├── COPYRIGHT
├── doc
│ └── intro.txt
├── README
├── run.sh
└── src
├── 1.c
└── CMakeLists.txt
doc, COPYRIGHT以及README,要拷贝到/usr/share/doc/cmake.
add_subdirectory指令可以指定src和bin目录。
外部CMakeLists.txt:
cmake_minimum_required(VERSION 3.10)
# set the project name
PROJECT(Tutorial)
add_subdirectory(src bin)
MESSAGE(STATUS "Binary dir:" ${PROJECT_BINARY_DIR})
MESSAGE(STATUS "Source dir:" ${PROJECT_SOURCE_DIR})
src下CMakeLists.txt:
# add the executable
add_executable(Tutorial 1.c)
执行cd build && cmake ..
,然后build下会生成许多文件,需要关注的是MakeFile。
然后执行make
, bin下就会生成Tutorial可执行文件了。
6. 安装
cmake install指令:https://cmake.org/cmake/help/v3.10/command/install.html
cmake其它相关文档:
- https://cmake.org/cmake/help/v3.10/variable/CMAKE_INSTALL_PREFIX.html
- https://cmake.org/cmake/help/latest/guide/tutorial/Installing%20and%20Testing.html
几种make安装指令:
make install
make install DESTDIR=/tmp
make install ./configure -prefix=/tmp
make相关文档:http://www.gnu.org/software/make/manual/make.html#Directory-Variables
外部的CMakeLists.txt:
cmake_minimum_required(VERSION 3.10)
# set the project name
PROJECT(Tutorial)
add_subdirectory(src bin)
MESSAGE(STATUS "Binary dir:" ${PROJECT_BINARY_DIR})
MESSAGE(STATUS "Source dir:" ${PROJECT_SOURCE_DIR})
install(FILES COPYRIGHT README DESTINATION share/doc/Tutorial)
install(DIRECTORY doc/ DESTINATION share/doc/Tutorial) # "doc/", not "doc"
install(PROGRAMS run.sh DESTINATION bin)
make之后执行make install
starr@starr-VirtualBox:~/projects/CMakeTest/build$ sudo make install
[sudo] password for starr:
[100%] Built target Tutorial
Install the project...
-- Install configuration: ""
-- Installing: /usr/local/share/doc/Tutorial/COPYRIGHT
-- Installing: /usr/local/share/doc/Tutorial/README
-- Up-to-date: /usr/local/share/doc/Tutorial
-- Installing: /usr/local/share/doc/Tutorial/intro.txt
-- Installing: /usr/local/bin/run.sh
7. 库
一般lib和src两个文件夹是分开的。lib下面也要有CMakeLists.txt.
add_library指令:https://cmake.org/cmake/help/v3.10/command/add_library.html
lib/libtest.h
#ifndef LIBTEST_H
#define LIBTEST_H
void PrintLib(char* pStr);
#endif
lib/libtest.c
#include <stdio.h>
#include "libtest.h"
void PrintLib(char* pStr)
{
printf("I am %s\n", pStr);
}
CMakeLists.txt
add_subdirectory(src bin)
# library
add_subdirectory(lib lib)
需要注意的是,不用的源码目录不能使用同一个binary_dir(因为会冲突), 所以这里生成目录为(build/)lib。
lib/CMakeLists.txt:
# library
set(LIB_SO_SRC libtest.c) # Using set is a good habit
set(LIB_SO_NAME shared)
add_library(${LIB_SO_NAME} SHARED ${LIB_SO_SRC})
set_target_properties(${LIB_SO_NAME} PROPERTIES OUTPUT_NAME "libtest")
set_target_properties(${LIB_SO_NAME} PROPERTIES CLEAN_DIRECT_OUTPUT 1)
# .so usually has the build and API version
set_target_properties(${LIB_SO_NAME} PROPERTIES VERSION 1 SOVERSION 1)
set(LIB_STATIC_SRC libtest.c)
set(LIB_STATIC_NAME static)
add_library(${LIB_STATIC_NAME} STATIC ${LIB_STATIC_SRC})
set_target_properties(${LIB_STATIC_NAME} PROPERTIES OUTPUT_NAME "libtest") # same name as .so
set_target_properties(${LIB_STATIC_NAME} PROPERTIES CLEAN_DIRECT_OUTPUT 1)
# headers
install(FILES libtest.h DESTINATION include/libtest)
# ${CMAKE_INSTALL_PREFIX}/lib
install(TARGETS ${LIB_SO_NAME} LIBRARY DESTINATION lib)
install(TARGETS ${LIB_STATIC_NAME} ARCHIVE DESTINATION lib)
一般一个项目会提供同名的动态库和静态库。
在构建libtest.a时,可能会清理掉libtest.so。set_target_properties可以规避这个问题。
install指令,LIBRARY对应.so,ARCHIVE对应.a。
starr@starr-VirtualBox:~/projects/CMakeTest/build/lib$ sudo make install
[sudo] password for starr:
[ 50%] Built target static
[100%] Built target shared
Install the project...
-- Install configuration: ""
-- Installing: /usr/local/include/libtest/libtest.h
-- Installing: /usr/local/lib/liblibtest.so.1
-- Installing: /usr/local/lib/liblibtest.so
-- Installing: /usr/local/lib/liblibtest.a
主程序调用
src/1.c:
#include <libtest.h>
int main(int argc, char **argv)
{
PrintLib("hello");
}
src/CMakeLists.txt也要添加对库的链接:
# add the executable
add_executable(Tutorial 1.c)
target_include_directories(Tutorial PUBLIC
${CMAKE_INSTALL_PREFIX}/${INCLUDE_DIR}
)
target_link_libraries(Tutorial liblibtest.a)
# target_link_libraries(Tutorial liblibtest.so)