CMakeLists.txt官网教程 , cmake命令配置等教程
CMakeLists.txt文件使用的是Cmake命令完成的,那么我们来了解下常用的命令使用, 这是必须要会的,否则静态库,动态库,都没法导入,而且项目文件管理等都靠这个文件, 就算你代码学的在好,这个不懂最后都不可能是一个完整的项目,只能在编译工具跑跑而已,不能发布出去
CMake是跨平台编译工具,比make更高级一些。其编译的主要工作是生成CMakeLists.txt文件,然后根据该文件生成Makefile,最后调用make来生成可执行程序或者动态库,具体流程如下:
以下教程都是基于Clion工具进行的, Clion极大地简化了我们构造C的难度,我们大部分情况只需要关注开发代码就行, 构建的部分在项目开始的时候配置一下就行, 下面就是讲解如何进行配置 , 前提需要将Clion-MinGW配置好具体教程: http://t.csdn.cn/FnTra
入门cmake_minimum_required(VERSION 3.20) # 声明了需要使用的cmake的最低版本 project(example1 VERSION 1.0.0 LANGUAGES C) # 项目的名字 版本 编译语言 set(CMAKE_C_STANDARD 11) #c编译器的版本 add_executable(example1 main.c) # 通过源文件main.c生成可执行文件demo.exe
当我们运行编译后,在cmake-build-debug目录下就会出现example1.exe 可执行文件了 ,当然如果只是学习C这点默认配置其实就够了
但是如果是自己开发项目,因为文件多了,同时还需要引入外部的各种库文件,那么这点配置就根本不行了, 在学习配置CMakeLists.txt前我么需要先学习下cmake的一些语法,因为CMakeLists.txt里面内容就是使用cmake编写的
因为我们又不用把CMake语法都学完,我们单纯的只是想打包项目而已,所以我们就将打包项目常用的语法说下(没用到的就不写了)
注释行注释使用#;块注释使用#[[xxxxx]]。比如:
# Multi line comments follow #[[ xxxxx ]]变量 程序变量
CMake中程序变量使用set和unset命令设置或者取消设置变量。作用域只是在CMakeLists.txt里
设置的变量可以是字符串,数字或者列表 ,语法: set(变量名 变量值) 比如:
# Set variable set(AUTHOR_NAME Farmer) set(AUTHOR "Farmer Li") set(AUTHOR Farmer Li) # Set list set(SLOGAN_ARR To be) set(SLOGAN_ARR To;be) set(SLOGAN_ARR "To;be") set(NUM 30) # Saved as string, but can compare with other number string set(FLAG ON) # Bool value
主要有以下要点:
- 如果要设置的变量值包含空格,则需要使用双引号或者使用""转义,否则可以省略双引号;
- 如果设置多个值或者字符串值的中间有";“,则保存成list,同样是以”;"分割的字符串;
- 变量可以被list命令操作,单个值的变量相当于只有一个元素的列表;
- 引用变量:${variable},在if()条件判断中可以简化为只用变量名。
- unset(variable) 删除变量
CMake允许设置环境变量,环境变量通过特殊的形式$ENV{varName}获取,通过SET(ENV{变量名} "变量值")设置环境变量
注意: 设置环境变量作用只是在CMakeLists.txt编译结束后就没了, 所以一般我们就来获取环境变量而已,和判断是否存在
共有变量提供信息的变量可以提供某种信息,通常只需要读取变量即可,而不需要对变量进行修改。
PROJECT_SOURCE_DIR 工程顶层目录,也就是顶层 CMakeLists.txt 源码所在目录
PROJECT_BINARY_DIR 工 程 BINARY_DIR , 也 就 是 顶 层 CMakeLists.txt 源 码 的BINARY_DIR
CMAKE_SOURCE_DIR 与 PROJECT_SOURCE_DIR 等价
CMAKE_BINARY_DIR 与 PROJECT_BINARY_DIR 等价
CMAKE_CURRENT_SOURCE_DIR 当前源码所在路径
CMAKE_CURRENT_BINARY_DIR 当前源码的 BINARY_DIR
CMAKE_MAJOR_VERSION cmake 的主版本号
CMAKE_MINOR_VERSION cmake 的次版本号
CMAKE_VERSION cmake 的版本号(主+次+修订)
PROJECT_VERSION_MAJOR 工程的主版本号
PROJECT_VERSION_MINOR 工程的次版本号
PROJECT_VERSION 工程的版本号
CMAKE_PROJECT_NAME 工程的名字
PROJECT_NAME 工程名,与 CMAKE_PROJECT_NAME 等价
MESSAGE(打印内容) MESSAGE( STATUS 打印内容)执行系统命令
执行shell命令
execute_process(COMMAND <一句shell命令> WORKING_DIRECTORY <这句shell命令执行的工作目录>)
如果命令不依赖于某个目录,而是全局的话那么可以直接使用
execute_process(COMMAND echo "Hello")
执行shell脚本
execute_process(COMMAND sh test.sh WORKING_DIRECTORY查询文件)
file(GLOB_RECURSE ALL_SRC
src)(.*)" "\1" CURRENT_FOLDER ${CURRENT_FOLDER_ABSOLUTE})
list(APPEND include_h ${CURRENT_FOLDER})
endforeach()
# 目录去重
list(REMOVE_DUPLICATES include_h)
MESSAGE("include_directories" ${include_h})
# 指定.h头文件搜索路径
include_directories(${include_h})
# 递归查询文件
file(GLOB_RECURSE ALL_SRC
# 可以配置多个路径
${PROJECT_SOURCE_DIR}/src/**.c # 获取src下面的所有.c文件
# ${PROJECT_SOURCE_DIR}/module2/*.c
)
MESSAGE("add_executable" ${ALL_SRC})
# 手动添加ioc图标 ,等依赖文件
set(ICO ico/ico.o)
# 需要编译的文件 ,生成exe
add_executable(${PROJECT_NAME} ${ICO} ${ALL_SRC})
# 将环境变量路径下的所有库文件连接到项目里
if(DEFINED ENV{LIBRARY_CMAKE_PATH_FILES})
file(GLOB_RECURSE ALL_library
# 可以配置多个路径
$ENV{LIBRARY_CMAKE_PATH_FILES}/**.a # 获取lib下面的所有.a文件
$ENV{LIBRARY_CMAKE_PATH_FILES}/**.lib # 获取lib下面的所有.lib文件
$ENV{LIBRARY_CMAKE_PATH_FILES}/**.os # 获取lib下面的所有.lib文件
$ENV{LIBRARY_CMAKE_PATH_FILES}/**.dll # 获取lib下面的所有.lib文件
)
MESSAGE("target_link_libraries: " ${ALL_library})
# 链接静态库也可以链接动态库
target_link_libraries (${PROJECT_NAME} ${ALL_library})
endif()
# 将当前项目下lib所有的所有库文件(.os .dll .a .lib )连接到项目里
file(GLOB_RECURSE ALL_library
# 可以配置多个路径
${PROJECT_SOURCE_DIR}/lib/**.a # 获取lib下面的所有.a文件
${PROJECT_SOURCE_DIR}/lib/**.lib # 获取lib下面的所有.lib文件
${PROJECT_SOURCE_DIR}/lib/**.os # 获取lib下面的所有.lib文件
${PROJECT_SOURCE_DIR}/lib/**.dll # 获取lib下面的所有.lib文件
)
MESSAGE("target_link_libraries: " ${ALL_library})
# 链接静态库也可以链接动态库
target_link_libraries (${PROJECT_NAME} ${ALL_library})
如果有库文件在其他地方那么可以设置系统环境变量LIBRARY_CMAKE_PATH_FILES, 来连接,默认会添加将当前项目lib下所有库文件
注意:
-
静态库的文件代码不能和源码的冲突 (同名文件内不能有相同名称的方法), 否则在编译的时候就会报错
-
如果同时使用静态库和动态库,并且他们之间有同名文件而且内部有同名的方法, 那么默认以静态库为准,因为静态库在打包的时候会将所需代码拷贝进包中,而动态库在运行期间才会去动态库里找对应的代码, 当调用方法的时候优先在当前应用内部找如果存在了那么就不会去动态库找了
-
静态库可以打断点, 动态库是没法打断点(都是汇编)
静态动态都可以使用下面写好的CMakeLists.txt,在文件中有说明,不同的库只需要改下就行
cmake_minimum_required(VERSION 3.20) # 声明了需要使用的cmake的最低版本
project(tool C) # 项目名称 和 语言
set(CMAKE_C_STANDARD 11) # C编译器版本
# 搜索全部的.c文件
file(GLOB_RECURSE ALL_SRC
${PROJECT_SOURCE_DIR}/src/**.c
${PROJECT_SOURCE_DIR}/*.c
)
MESSAGE("add_library" ${ALL_SRC})
# 静态打包库文件 (自行选择)
add_library(${PROJECT_NAME} ${ALL_SRC})
# 动态打包库文件 (自行选择)
# add_library(${PROJECT_NAME} SHARED ${ALL_SRC})
# 将库文件和头文件都复制到一个目录下
## 在项目根下创建 ${PROJECT_NAME} 文件夹
set( public_include ${PROJECT_SOURCE_DIR}/cmake-build-debug/${PROJECT_NAME})
execute_process( COMMAND ${CMAKE_COMMAND} -E make_directory ${public_include})
## 拷贝头部文件,到到${PROJECT_NAME}文件夹
file(GLOB_RECURSE ALL_SRC
${PROJECT_SOURCE_DIR}/src/**.h
${PROJECT_SOURCE_DIR}/**.h
)
MESSAGE("copy" ${ALL_SRC})
execute_process( COMMAND ${CMAKE_COMMAND} -E copy ${ALL_SRC} ${public_include})
最后当运行打包后, 只需要将库文件和头文件夹,复制到需要的地方就行了
动态库导入无效报错问题静态库在编译的时候就会被打包到exe中
当我们项目使用的是动态库打包后,是运行不起来的 ,因为动态库打包的项目是在运行时候才会去找依赖 ,默认会去找exe当前目录下或者系统的PATH环境变量里的路径, 如果都没有那么就会报错
解决办法
如果当前是在本地开发代码那么直接在Clion的环境变量中配置就行了
如果当前需要打包项目的话,而dll文件和exe文件不是在同级目录下,那么以下有二种解决方案:
- 那么将相关的dll文件都复制到exe同级目录下,然后压缩为zip文件,客户下载解压后双击exe那么也是能跑的(不推荐)
- 设置我的电脑的环境变量中的系统变量的PATH, 将动态库的绝对路径添加进去就行了(不推荐,自己玩可以这样弄)
- 将dll和项目放入安装程序中,让用户下载后自己安装(目前主流)
我们很多时候导入一些文件,或者库的时候,发现编译错误了,但是实际上是没有错误的因为CMakeLists.txt(构建的有缓存,需要我们删除



