最近学习 opencv、ffmpeg,这些优秀的库都是使用 c/c++ 来编写的,这些项目都是由多个项目组成的,若是没有一个很好的工具去管理它们之间的关系,这对于每一个开发者来讲,若是没有原做者的帮助都是不可能完成搭建的。所以就引入了 CMake ,咱们在下载它们的源码时也会带着 CMakeLists.txt 文档,这样开发者只要使用 CMake 工具就能够完成源码的构建工做。c++
那么下面就分几个步骤讲述通常用法git
咱们须要将源文件 main.c 构建成可执行文件,只须要3行配置就能够完成。这里的 main.c 是求数的次方,这两个数由运行时手动给出。其代码以下:github
#include <stdio.h>
#include <stdlib.h>
/**
* power - Calculate the power of number.
* @param base: Base value.
* @param exponent: Exponent value.
*
* @return base raised to the power exponent.
*/
double power(double base, int exponent)
{
int result = base;
int i;
if (exponent == 0) {
return 1;
}
for(i = 1; i < exponent; ++i){
result = result * base;
}
return result;
}
int main(int argc, char *argv[])
{
if (argc < 3){
printf("Usage: %s base exponent \n", argv[0]);
return 1;
}
double base = atof(argv[1]);
int exponent = atoi(argv[2]);
double result = power(base, exponent);
printf("%g ^ %d is %g\n", base, exponent, result);
return 0;
}
复制代码
接着要确保源文件和 CMakeLists.txt 文档在同级目录, 以下图所示。bash
CMakeLists.txt 配置文件函数
# 支持 cmake 最小版本为 2.6
cmake_minimum_required(VERSION 2.6)
# 项目名称
project(Demo1)
# 指定源代码,生成可执行文件 Demo1
add_executable(Demo1 main.c)
复制代码
经过这几行配置就能够生成一个可执行文件 Demo1, 你须要使用以下命令构建工具
// 在 CMakeLists.txt 统计目录执行
1. cmake .
2. make
复制代码
多数状况下,咱们的代码都是由不少目录构成。咱们须要在构建的子目录下也加入 CMakeLists.txt 文档来描述源码的关系。例如:咱们将 main.c 中的 power 函数抽离到一个新目录 math 下,以下图所示:学习
math/CMakeLists.txt 内容如测试
# 查找全部源文件, 将其存放到 SRC_DIR 变量中.
aux_source_directory(. SRC_DIR)
# 将源文件生成连接库
add_library(MathFunctions ${SRC_DIR})
复制代码
主目录下的 CMakeLists.txt 加入如下几行ui
# 将源文件下 math 目录头文件添加到编译器头文件搜索目录.
include_directories ("${PROJECT_SOURCE_DIR}/math")
# 将 math 目录加入到 cmake 构建. 会执行子目录中的 CMakeLists.txt
add_subdirectory(math)
# 指定源代码,生成可执行文件 Demo2
add_executable(Demo2 main.c)
# 将 MathFunctions 连接到 Demo1
target_link_libraries(Demo2 MathFunctions)
复制代码
最后就是执行 cmake . 和 make 命令便可生成可执行文件。spa
在某些状况下,咱们须要对源码进行控制,好比咱们上面的代码中,求数的次方。咱们也可使用库函数来实现。咱们能够在构建的时候进行控制。那么咱们改进下主目录下的 CMakeLists.txt 文件。
# 引入头文件,对源码进行配置, 从 config.h.in 配置的变量会输出到 config.h 中
# 这点就是利用宏定义 #define
configure_file(
"${PROJECT_SOURCE_DIR}/config.h.in"
"${PROJECT_BINARY_DIR}/config.h"
)
# 可选配置,可对变量 MY_MATH_FLAG 进行设置开关 ON/OFF
option(MY_MATH_FLAG
"是否使用自定义函数实现开平方计算"
OFF
)
if (MY_MATH_FLAG)
# 将源文件下 math 目录头文件添加到编译器头文件搜索目录.
include_directories ("${PROJECT_SOURCE_DIR}/math")
# 将 math 目录加入到 cmake 构建. 会执行子目录中的 CMakeLists.txt
add_subdirectory(math)
set(EXTRA_LIBS ${EXTRA_LIBS} MathFunctions)
endif(MY_MATH_FLAG)
# 指定源代码,生成可执行文件 Demo3
# 这里也可使用 aux_source_directory(. SRC_DIR) 来替换,这样的好处就是不用写多个源文件
add_executable(Demo3 main.c)
# 将 MathFunctions 连接到 Demo3
target_link_libraries(Demo3 ${EXTRA_LIBS})
复制代码
咱们可使用 ccmake 命令来进行编译,经过它能够指定 MY_MATH_FLAG 变量 ON/OFF 状态。而后最终就会决定 config.h 中生成的的内容,若是 OFF 会生成
/* #undef MY_MATH_FLAG */
复制代码
为 ON 会生成
#define MY_MATH_FLAG
复制代码
有了 config.h,咱们就能够在源码中进行条件编译
#include <stdio.h>
#include <stdlib.h>
#include "config.h"
// MY_MATH_FLAG 条件编译
#ifdef MY_MATH_FLAG
#include "CalcPower.h"
#else
#include <math.h>
#endif
int main(int argc, char *argv[])
{
if (argc < 3){
printf("Usage: %s base exponent \n", argv[0]);
return 1;
}
double base = atof(argv[1]);
int exponent = atoi(argv[2]);
#ifdef MY_MATH_FLAG
double result = power(base, exponent);
printf("使用自定义函数实现平方计算: %g ^ %d is %g\n", base, exponent, result);
#else
double result = pow(base, exponent);
printf("使用系统函数实现平方计算: %g ^ %d is %g\n", base, exponent, result);
#endif
printf("%g ^ %d is %g\n", base, exponent, result);
return 0;
}
复制代码
一些状况下,咱们须要构建完成后进行测试,咱们可使用 make install 来实现,改造一下主目录下的 CMakeLists.txt 文件
# 引入头文件,对源码进行配置
configure_file(
"${PROJECT_SOURCE_DIR}/config.h.in"
"${PROJECT_BINARY_DIR}/config.h"
)
# 可选配置,可对变量 MY_MATH_FLAG 进行设置开关 ON/OFF
option(MY_MATH_FLAG
"是否使用自定义函数实现开平方计算"
ON
)
if (MY_MATH_FLAG)
# 将源文件下 math 目录头文件添加到编译器头文件搜索目录.
include_directories ("${PROJECT_SOURCE_DIR}/math")
# 将 math 目录加入到 cmake 构建. 会执行子目录中的 CMakeLists.txt
add_subdirectory(math)
set(EXTRA_LIBS ${EXTRA_LIBS} MathFunctions)
endif(MY_MATH_FLAG)
# 指定源代码,生成可执行文件 Demo1
# 这里也可使用 aux_source_directory(. SRC_DIR) 来替换,这样的好处就是不用写多个源文件
add_executable(Demo4 main.c)
# 将 MathFunctions 连接到 Demo1
target_link_libraries(Demo4 ${EXTRA_LIBS})
# 指定 Demo4 安装路径
install (TARGETS Demo4 DESTINATION bin)
install (FILES "${PROJECT_BINARY_DIR}/config.h"
DESTINATION include)
# 添加测试
enable_testing()
#[[
add_test (test_run5_2 Demo4 5 2)
set_tests_properties (test_run5_2 PROPERTIES PASS_REGULAR_EXPRESSION "is 25")
add_test (test_run2_2 Demo4 2 2)
set_tests_properties (test_run2_2 PROPERTIES PASS_REGULAR_EXPRESSION "is 4")
add_test (test_run3_2 Demo4 3 2)
set_tests_properties (test_run3_2 PROPERTIES PASS_REGULAR_EXPRESSION "is 9")
add_test (test_run10_2 Demo4 10 2)
set_tests_properties (test_run10_2 PROPERTIES PASS_REGULAR_EXPRESSION "is 100")
]]
# 使用宏定义简化测试配置的书写
macro(do_test arg1 arg2 result)
add_test(test_run${arg1}_${arg2} Demo4 ${arg1} ${arg2})
set_tests_properties(test_run${arg1}_${arg2} PROPERTIES PASS_REGULAR_EXPRESSION ${result}
)
endmacro(do_test arg1 arg2 result)
do_test(5 2 "is 25")
do_test(2 2 "is 4")
do_test(3 2 "is 9")
do_test(10 2 "is 100")
复制代码
先记录到这里,后续再补充。有须要代码的能够 点击这里, 代码都经过按 tag 区分不一样的分之。能够切换到对应的分之查看。