# cmake 学习笔记1 Geant4、ROOT 等项目使用 cmake 来作为构建工具,对项目管理能力具有相当的优越性,只要简单的几句话就能代替一长篇的 Makefile,其实我最早看到了 Geant4 的 CmakeLists.txt,被它的便捷性所吸引了,以致我先会使用 cmake 来管理项目之后才对 Makefile 有进一步的了解。 CMake 是一个跨平台的安装(编译)工具,可以用简单的语句来描述所有平台的安装(编译过程)。他能够输出各种各样的 makefile 或者 project 文件,能测试编译器所支持的 C++ 特性,类似 UNIX 下的 automake。只是 CMake 的组态档取名为 CmakeLists.txt。Cmake 并不直接建构出最终的软件,而是产生标准的建构档(如 Unix 的 Makefile 或 Windows Visual C++ 的 projects/workspaces),然后再依一般的建构方式使用。这使得熟悉某个集成开发环境(IDE)的开发者可以用标准的方式建构他的软件,这种可以使用各平台的原生建构系统的能力是 CMake 和 SCons 等其他类似系统的区别之处。 参考了网上一些博客,对 cmake 常用语句有了了解,对理工科的我们学会简单使用就行,下面是参考了一些博客写出来的,只要这几个语句学会基本就够用了,其实只要花几天学习下,做好一个自己的通用模板基本就一劳永逸了。 | 例子 | 说明 | | :-------: | :-------: | | 例子一 | 单个源文件 main.c | | 例子二 | ==>分解成多个 main.c hello.h hello.c | | 例子三 | ==>先生成一个静态库,链接该库 | | 例子四 | ==>将源文件放置到不同的目录 | | 例子五 | ==>控制生成的程序和库所在的目录 | ---- ## 例子一 用 cmake 来构建一个经典的 C++ 程序: ```cpp //main.cpp #include int main() { std::cout<<"Hello World!"< #include "hello.h" void hello(const char * name) { printf ("Hello %s!/n", name); } ~~~ - main.cpp ```cpp #include "hello.h" int main() { hello("World"); return 0; } ``` - 准备好 CMakeList.txt 文件 ```cmake project(HELLO) set(CODE_LIST main.cpp hello.cpp) add_executable(hello ${CODE_LIST}) ``` 执行 cmake 的过程同上, **目录结构** ``` + | +--- main.cpp +--- hello.h +--- hello.cpp +--- CMakeList.txt | /--+ build/ | +--- hello ``` ---- ## 例子三 如果先将 helio.cpp 生成一个库,然后再使用呢? 修改一下前面的 CMakeList.txt 文件试试: ```cmake project(HELLO) set(LIB_SRC hello.cpp) set(APP_SRC main.cpp) add_library(libhello ${LIB_SRC}) add_executable(hello ${APP_SRC}) target_link_libraries(hello libhello) ``` 和前面相比,我们添加了一个新的目标 libhello,并将其链接进 hello 程序。 同前面一样运行 cmake,结果: **目录结构** ``` + | +--- main.cpp +--- hello.h +--- hello.cpp +--- CMakeList.txt | /--+ build/ | +--- hello +--- liblibhello.so ``` 因为我的可执行程序(add_executable)占据了 hello 这个名字,所以 add_library 就不能使用这个名字了,所以,我们才取了个 libhello 的名字,这将导致生成的库为 liblibhello.so(或 liblibhello.a),很不爽,想生成 hello.so(或hello.a) 怎么办? 添加一行: ```cmake set_target_properties(libhello PROPERTIES OUTPUT_NAME "hello") ``` ---- ## 例子四 在前面,我们成功地使用了库,可是源代码放在同一个路径下,不大规范,于是下面就是分开放了。 我们想要以下这样一种结构: ``` + | +--- CMakeList.txt +--+ src/ | | | +--- main.cpp | /--- CMakeList.txt | +--+ libhello/ | | | +--- hello.h | +--- hello.cpp | /--- CMakeList.txt | /--+ build/ ``` 现在需要 3 个 CMakeList.txt 文件了,每个源文件目录都需要一个,每一个都不是太复杂。 - 顶层的CMakeList.txt 文件 ```cmake project(HELLO) add_subdirectory(src) add_subdirectory(libhello) ``` - src 中的 CMakeList.txt 文件 ```cmake include_directories(${PROJECT_SOURCE_DIR}/libhello) set(APP_SRC main.cpp) add_executable(hello ${APP_SRC}) target_link_libraries(hello libhello) ``` - libhello 中的 CMakeList.txt 文件 ```cmake set(LIB_SRC hello.cpp) add_library(libhello ${LIB_SRC}) set_target_properties(libhello PROPERTIES OUTPUT_NAME "hello") ``` 和前面一样,建立一个 build 目录,在其内运行 cmake,然后可以得到 - build/src/hello - build/libhello/hello.so 顶层的 CMakeList.txt 文件中使用 add\_subdirectory 告诉 cmake 去子目录寻找新的 CMakeList.txt 子文件。在 src 的 CMakeList.txt 文件中,新增加了include_directories,用来指明头文件所在的路径。 ---- ## 例子五 前面还是有一点不爽:如果想让可执行文件在 bin 目录,库文件在 lib 目录怎么办? 想要以下的目录结构: ``` + build/ | +--+ bin/ | | | /--- hello.exe | /--+ lib/ | /--- hello.lib ``` - 一种办法:修改顶级的 CMakeList.txt 文件 ```cmake project(HELLO) add_subdirectory(src bin) add_subdirectory(libhello lib) ``` 这样一来:build/src 就成了 build/bin 了,可是除了 hello,中间产物也进来了。还不是我们最想要的。 - 另一种方法:不修改顶级的文件,修改其他两个文件 - src/CMakeList.txt 文件 ```cmake include_directories(${PROJECT_SOURCE_DIR}/libhello) #link_directories(${PROJECT_BINARY_DIR}/lib) set(APP_SRC main.cpp) set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) add_executable(hello ${APP_SRC}) target_link_libraries(hello libhello) ``` - libhello/CMakeList.txt 文件 ```cmake set(LIB_SRC hello.cpp) add_library(libhello ${LIB_SRC}) set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib) set_target_properties(libhello PROPERTIES OUTPUT_NAME "hello") ```