赞
踩
本篇博客对C/C++程序的编译环境、库创建链接、Make和CMake工具的使用进行介绍,以便加深理解和记忆
C编译环境
GNU(GNU’s Not Unix):GNU是一个自由软件运动的项目,旨在开发一个类Unix操作系统。GNU项目创建了一系列工具和库,为开发者提供了自由的软件开发环境。其中包括GCC编译器、MinGW和其他开发工具。GNU以开源和自由软件的理念而闻名,为用户提供了更大的自由度和可定制性。
GCC(GNU Compiler Collection):GCC是GNU项目的核心组件之一,也是一个开源的编译器集合。它支持多种编程语言,包括C、C++、Objective-C、Fortran等。GCC是一个跨平台的编译器,提供了许多优化选项和功能,以生成高质量的可执行文件。(最早名为GNU Compiler C,针对C)
MinGW(Minimalist GNU for Windows):MinGW是一个开源的软件开发工具集,旨在为Windows提供GNU开发环境。它包含了一组用于Windows的头文件和库文件,以及GCC(GNU Compiler Collection)编译器。MinGW使得开发者能够在Windows上开发和编译使用GNU工具链的应用程序,它提供了一种轻量级的方式在Windows环境下进行开发。
MSVC(Microsoft Visual C++):MSVC是微软公司开发的一款C++编译器和集成开发环境(IDE)。它是Windows平台上最常用的C++开发工具之一。MSVC提供了丰富的开发工具、调试功能和图形化界面,使得Windows开发变得更加便捷。
区别与联系:MinGW和GCC都属于GNU项目的一部分,它们提供了在Windows环境下进行GNU开发的工具和编译器。GCC是跨平台的编译器集合,MinGW专注于在Windows上提供GNU工具链的支持
库是写好的,成熟的,可复用的代码。本质上来说,库是一种可执行代码的二进制形式,可以被OS载入内存执行。库有两种:静态库(.a、.lib)和动态库(.so、.dll)
静态库
动态库
为何需要动态库:动态库的出现是为了解决静态库的一些不足:
特点
动态库包含的内容
gcc与g++的联系与区别
gcc与g++都是GNU(组织)的一个编译器
gcc与g++都可以编译c代码与c++代码。但是:后缀为.c的,gcc把它当做C程序,而g++当做是C++程序;后缀为.cpp的,两者都会认为是C++程序
编译可以用gcc/g++,而链接可以用g++或者gcc -lstdc++。因为gcc命令不能自动和C++
程序使用的库联接(当然可以选择手动链接,使用命令如下),所以通常使用g++来完成联接。但在编译阶段,g++会自动调用gcc,二者等价
gcc编译过程
gcc -E hello.c -o hello.i
gcc -S hello.i -o hello.s
gcc -c hello.s -o hello.o
gcc hello.o -o hello
静态链接库
# 将所有指定的源文件,都编译成相应的目标文件
g++ -c greeting.cpp name.cpp
# greeting.cpp name.cpp greeting.o name.o greeting.h name.h
# 将生成的目标文件打包成静态链接库(可以将多个目标文件打成一个链接库)
# 静态链接库的不能随意起名,需遵循如下的命名规则:libxxx.a/libxxx.lib
ar rcs libmyfunction.a name.o greeting.o
# 将主文件编译为目标文件
g++ -c main.cpp
# 链接
g++ -static main.o libmyfunction.a
# -L注定路径,-l指定库名称,中间一般不加空格
# g++ -static main.o -L /home/wohu/cpp/src -lmyfunction
动态链接库
# 1.直接使用源文件创建动态链接库
# -shared 选项用于生成动态链接库
# -fpic(还可写成 -fPIC)选项的功能是,令 GCC 编译器生成动态链接库(多个目标文件的压缩包)时,表示各目标文件中函数、类等功能模块的地址使用相对地址,而非绝对地址。这样,无论将来链接库被加载到内存的什么位置,都可以正常使用
gcc -fpic -shared 源文件名... -o 动态链接库名
# 2.先使用 gcc -c 指令将指定源文件编译为目标文件,再由目标文件生成动态链接库
g++ -c -fPIC name.cpp greeting.cpp
# 生成动态链接库
g++ -shared greeting.o name.o -o libmyfunction.so
g++ main.cpp libmyfunction.so -o main
静态库
使用VS新建项目 → 选择Win32控制台程序 → 选择应用程序设置 → 勾选静态库 → 编写静态库 → Build项目
链接静态库
方法一:引用的静态库是同一解决方案下的子工程
方法二:右键欲引用静态库的项目 → 属性 → 配置属性 → 链接器 → 命令行:在其他选项中键入完整的静态库.lib路径
方法三
动态库
创建动态库
pch是预编译头文件,通常将一些不怎么变动的头文件预先编译,加快工程编译速度
dllmain.cpp是dll程序的主文件,其中DllMain函数是dll的入口点,每次这个dll被加载都会执行DllMain,然后根据运行时状态执行不同的命令
// dllmain.cpp : 定义 DLL 应用程序的入口点。 #include "pch.h" BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: //被程序加载时执行 case DLL_THREAD_ATTACH: //被线程加载时执行 case DLL_THREAD_DETACH: //被线程卸载时执行 case DLL_PROCESS_DETACH: //被程序卸载时执行 break; } return TRUE; }
```c // dllmain.cpp : 定义 DLL 应用程序的入口点。 #include "pch.h" BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; } //编写函数 pch.h里面记得要添加include<iostream> void test1() { std::cout << "test1 is worked\n"; } void test2() { std::cout << "test2 is worked\n"; }
将函数或对象暴露给外界:编写完函数或对象,外界还是无法执行的,这就像js里的模块化编程,需要将想要给外界使用的功能暴露(export)出来
方法一:在函数名(对象)前加入暴露给外界的关键字
__declspec(dllexport) void test1()
{
std::cout << "test1 is worked\n";
}
?test1@@YAXXZ
这样的名字,这也就导致了我们暴露出去的函数名其实根本不是test1。为了解决这个问题:只需要在编写dll的时候在函数前告诉编译器,用C风格来暴露test函数,就不会被粉碎函数名了extern "C" __declspec(dllexport) void test1()
{
std::cout << "test1 is worked\n";
}
实际在C++中,我们更倾向于通过对象的方式将方法暴露给外界:
class __declspec(dllexport) test
{
test() {};
};
方法二 :使用模块定义文件(.def):右键项目 → 添加项 → 新建项 → 选择模块定义文件创建
自定义名称,并在文件中写入(.def文件中以";"作为注释符):
LIBRARY {dll_project_name} ;LIBRAY后面跟dll的项目名称
EXPORTS ;EXPORTS代表后面的都是要export出去的函数
test2 ;一行一个函数名
生成dll:在vs的顶部工具栏,依次点击生成 → 生成dll测试 → dll文件在项目文件夹的debug目录
链接动态库
方法一:同一解决方案
方法二:不要求同一解决方案
右键项目 → 属性 → 配置属性 → 链接器 → 常规 → 附加依赖库目录 键入动态库所在目录
右键项目 → 属性 → 配置属性 → 连接器 → 输入 → 附加目录 键入动态库对应的.lib文件(dll的入口)
Linux:#include <dlfcn.h>
void * dlopen( const char * pathname, int mode ):函数以指定模式打开指定的动态连接库文件,并返回一个句柄给调用进程
void* dlsym(void* handle,const char* symbol):dlsym根据动态链接库操作句柄(pHandle)与符号(symbol),返回符号对应的地址。使用这个函数不但可以获取函数地址,也可以获取变量地址
int dlclose (void *handle):dlclose用于关闭指定句柄的动态链接库,只有当此动态链接库的使用计数为0时,才会真正被系统卸载
const char *dlerror(void):当动态链接库操作函数执行失败时,dlerror可以返回出错信息,返回值为NULL时表示操作函数执行成功
Windows
//main.cpp : 测试动态链接库 #include <iostream> // 函数指针:定义了一个类型名为fun的,指向空的函数指针 // https://blog.csdn.net/qq_35621436/article/details/106085752 typedef void(*func)(); int main(void) { /**引入要加载的动态链接库 HMODULE点进去看的话其实是HINSTANCE的一个别名,就是一个句柄 如果获取到了,会返回这个动态库的句柄,否则返回NULL**/ HMODULE dlltest = LoadLibraryW(L"{name}.dll"); if (dlltest) { /**获取函数名所在的地址,即函数指针 获取到的地址默认是void类型,因此要自己定义一个函数指针,进行强制类型转换**/ func test = (func)GetProcAddress(dlltest, "test"); if (test) { test(); } else { MessageBoxW(NULL, L"找不到test方法", L"ERROR", NULL); } } else { MessageBoxW(NULL,L"找不到dll",L"ERROR",NULL); } }
Make - GNU Project - Free Software Foundation 跟我一起写Makefile — 跟我一起写Makefile 1.0 文档 (seisman.github.io)
Make 是一种流行的构建工具,常用于将源代码转换成可执行文件或者其他形式的输出文件(如库文件、文档等)。Make 可以自动化地执行编译、链接等一系列操作,提高开发效率。
Make 使用 Makefile 文件描述项目的构建过程,其中包含了源文件、目标文件以及编译和链接的命令等信息。Makefile 按照一定的规则解析,将源码和构建过程相互关联起来,执行具体的构建操作,生成目标文件或可执行文件
Make 工具的优势在于它可以识别哪些文件被修改了,只编译修改过的部分,以提高构建速度。此外,Make 工具还支持基于条件的编译,也就是预处理器(preprocessor)功能,可以生成不同的输出文件用于不同的平台或不同的运行环境
Make 工具具有很好的跨平台性,可以在 Unix/Linux、Windows、Mac 等多种操作系统上使用,并且可以与多种编程语言搭配使用,如 C、C++、Java 等
Linux
Windows
Make for Windows (sourceforge.net):实际基于MinGW,推荐后者
MinGW - Minimalist GNU for Windows download | SourceForge.net
下载安装,将Bin目录加入到系统环境变量中
运行MinGW Installer,勾选所需的包,点击左上角的Installation,点击Apply Change
mingw32-make.exe
更名为make.exe
方便使用CMake官网 CMake官方中文文档 CMake-Practice-zh-CN: CMake 实践 (github.com) 《CMake Best Practices》的非专业个人翻译 (github.com)
CMake是一种管理源代码构建的工具,被广泛用于C/C++项目
使用的工具链:cmake + make。通过cmake语法编写CMakeLists.txt文件,描述项目的构建属性和配置,并进行自动化的项目构建(可执行二进制文件、静/动态库)
开源、高效率
跨平台:在Linux/Unix平台,生成makefile;在Mac平台生成xcode;在Windows平台可以生成MSVC工程文件
可扩展:可为cmake编写特定功能的模块,扩展cmake功能
各大Linux发行版都集成了cmake,无需手动安装。如需安装可从官网上下载安装
内部构建
外部构建:推荐的方式,在源CMakeLists.txt所在的文件夹下新建build目录(或其他任意位置),在该位置下键入cmake ..
或cmake 源工程路径
,此时区分出了PROJECT_SOURCE_DIR和PROJCT_BINARY_DIR
源码和构建中间、目标在同一目录下,将hello.c和CMakeLists.txt放入同一目录
# 注意:命令和括号之间没有空格 CMAKE_MINIMUM_REQUIRED(VERSION 3.29) #[[ 指定项目信息:该声明隐式地定义了两个变量:项目名_BINARY_DIR和项目名_BINARY_DIR, 它们指向了当前CMakeLists.txt文件的路径,更好的替代方式是PROJECT_SOURCE_DIR和 PROJCT_BINARY_DIR,它们可以避免由项目名称更改造成的问题 PROJECT (项目名 VERSION 版本号 DESCRIPTION "项目介绍" LANGUAGE C) ]] PROJECT(hello) #[[ 打印信息:MESSAGE([SEND_ERROR | STATUS | FATAL_ERROR] "message to display") SEND_ERROR,产生错误,生成过程被跳过 SATUS,输出前缀为—的信息 FATAL_ERROR,立即终止所有 cmake 过程 ]] # 注意除IF语句中,变量的使用方式为:${} MESSAGE(STATUS "This is Projct BINARY dir" ${项目名_BINARY_DIR}) MESSAGE(STATUS "This is Projct SOURCE dir" ${项目名_SOURCE_DIR}) # 显示定义变量:SET(VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE]]) # 变量值可以用双引号标识,以应对存在空格的情况:SET(SRC_LIST “fu nc.c”) SET(SRC_LIST hello.c) # 添加要构建可执行文件的源文件和输出文件,此时表示可执行文件为hello,源文件列表为SRC_LIST变量 # 多个参数可以使用空格或分号分隔:ADD_EXECUTABLE(hello main.c;func.c) ADD_EXECUTABLE(hello ${SRC_LIST})
cmake .
注意:CMake需要在任何子目录中建立一个CMakeLists.txt,如下将源文件放入src文件夹中,src文件夹中也需要创建一个
- root # 源文件 - src # 源码 hello.c CMakeLists.txt # 文档 - doc # 版权说明、自述文件 COPYRIGHT.txt README.txt # bat脚本,用于调用二进制可执行文件 run.bat # 构建后的目标文件夹 - bin # 构建目录 - build CMakeLists.txt
# /src/CMakeLists.txt
ADD_EXECUTABLE(hello hello.c)
# 指定最终的目标二进制的位置(指最终生成的 hello 或者最终的共享库,不包含编译生成的中间文件)
# 内部编译下,PROJECT_BINARY_DIR是当前目录,外部编译下,PROJECT_BINARY_DIR是构建目录
# 与下面的ADD_SUBDIRECTORY用途相同(在哪里 ADD_EXECUTABLE 或 ADD_LIBRARY,就在哪改变路径)
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
# /CMakeLists.txt CMAKE_MINIMUM_REQUIRED(VERSION 3.12...3.29) Project(hello) # 向当前工程添加存放源文件的子目录,并可以指定中间二进制和目标二进制存放的位置 # ADD_SUBDIRECTORY(source_dir [binary_dir] [EXCLUDE_FROM_ALL]) # EXCLUDE_FROM_ALL 参数的含义是将这个目录从编译过程中排除,比如,工程的 example,可能就需要工程构建完成后,再进入 example 目录单独进行构建 # 将 src 子目录加入工程,并指定编译输出(包含编译中间结果)路径为bin 目录。如果不进行 bin 目录的指定,那么编译结果(包括中间结果)都将存放在build/src 目录(这个目录跟原有的 src 目录对应) ADD_SUBDIRECTORY(src bin) # 指定安装目录的变量:默认值/usr/local,注意Windows下转义字符 SET(CMAKE_INSTALL_PREFIX C:\\Users\\liyifan31\\Desktop\\des) # 安装命令:注意FILES、PROGRAMS、DIRECTORY是标识符 #[[ # 目标文件安装:TARGETS # TARGET参数即是ADD_EXECUTABLE、ADD_LIBRARY定义的目标文件 INSTALL(TARGETS targets... # 静态库、动态库、可执行二进制文件 [[ARCHIVE|LIBRARY|RUNTIME] # 定义安装路径,若以/开头则是绝对路径,CMAKE_INSTALL_PREFIX失效 [DESTINATION <dir>] [PERMISSIONS permissions...] [CONFIGURATIONS [Debug|Release|...]] [COMPONENT <component>] [OPTIONAL] ] [...]) # 普通文件安装 INSTALL(FILES files... DESTINATION <dir> # 定义文件权限,默认为:OWNER_WRITE,OWNER_READ,GROUP_READ,WORLD_READ,即644权限 [PERMISSIONS permissions...] [CONFIGURATIONS [Debug|Release|...]] [COMPONENT <component>] [RENAME <name>] [OPTIONAL]) # 非目标文件的可执行程序安装(比如脚本之类),与FILES相比,默认为755权限 INSTALL(PROGRAMS files... DESTINATION <dir> [PERMISSIONS permissions...] [CONFIGURATIONS [Debug|Release|...]] [COMPONENT <component>] [RENAME <name>] [OPTIONAL]) # 文件夹 INSTALL(DIRECTORY dirs... DESTINATION <dir> [FILE_PERMISSIONS permissions...] # 所在 Source 目录的相对路径。注意:abc 和 abc/有很大的区别 # 如果目录名不以/结尾,那么这个目录将被安装为目标路径下的 abc, # 如果目录名以/结尾,代表将这个目录中的内容安装到目标路径,但不包括这个目录本身 [DIRECTORY_PERMISSIONS permissions...] [USE_SOURCE_PERMISSIONS] [CONFIGURATIONS [Debug|Release|...]] [COMPONENT <component>] # 使用正则表达式进行过滤 [[PATTERN <pattern> | REGEX <regex>] [EXCLUDE] [PERMISSIONS permissions...]] [...]) # e.g. # 将 icons 目录安装到 <prefix>/share/myproj,将 scripts/中的内容安装到<prefix>/share/myproj INSTALL(DIRECTORY icons scripts/ DESTINATION share/myproj # 不包含目录名为 CVS 的目录 PATTERN "CVS" EXCLUDE # 对于 scripts/*文件指定权限为 OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ PATTERN "scripts/*" PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ) ]] INSTALL(DIRECTORY doc DESTINATION doc) INSTALL(DIRECTORY data DESTINATION data) INSTALL(TARGETS ${PROJECT_BINARY_DIR}\\bin\\Debug\\ DESTINATION bin)
cd build
cmake ..
cmake -DCMAKE_INSTALL_PREFIX=/usr
make
make install
目录结构
CMakeLists.txt
- lib
CMakeLists.txt
hello.c
hello.h
- build
# /lib/CMakeLists.txt SET(LIBHELLO_SRC hello.c) #[[ ADD_LIBRARY(libname # SHARED,动态库 STATIC,静态库 MODULE,在使用 dyld 的系统有效, # 如果不支持 dyld,则被当作 SHARED 对待 [SHARED|STATIC|MODULE] # 该库不会被默认构建,除非有其他的组件依赖或者手工构建 [EXCLUDE_FROM_ALL] source1 source2 ... sourceN ) ]] ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC}) ADD_LIBRARY(hello_static STATIC ${LIBHELLO_SRC}) #[[ # 设置输出属性 SET_TARGET_PROPERTIES(target1 target2 ... PROPERTIES prop1 value1 prop2 value2 ...) ]] # 解决静态库和动态库重名/名称不一致的问题 # OUTPUT_NAME 设置输出名称。解决名称不一致问题 # CLEAN_DIRECT_OUTPUT 解决输出时,同名文件清除问题 # VERSION 库版本号 # SOVERSION api版本号 SET_TARGET_PROPERTIES(hello PROPERTIES CLEAN_DIRECT_OUTPUT 1 VERSION 1.2 SOVERSION 1) SET_TARGET_PROPERTIES(hello_static PROPERTIES OUTPUT_NAME "hello" CLEAN_DIRECT_OUTPUT 1 VERSION 1.2 SOVERSION 1) # 获取输出属性 GET_TARGET_PROPERTY(OUTPUT_VALUE hello_static OUTPUT_NAME) MESSAGE(STATUS “This is the hello_static OUTPUT_NAME:” ${OUTPUT_VALUE})
# /CMakeLists.txt
Project(hello_lib)
ADD_SUBDIRECTORY(lib lib)
INSTALL(TARGETS hello hello_static
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)
INSTALL(FILES hello.h DESTINATION /include)
新建项目,在根路径下创建src文件夹和CMakeLists.txt,在src文件夹下创建sort_test.c和CMakeLists.txt,sort.c中引用刚才的库头文件
# /src/CMakeLists.txt #[[ # 向工程添加多个特定的头文件搜索路径,路径之间用空格分割 # 通过 AFTER 或者 BEFORE 参数,控制追加到当前的头文件搜索路径的后面还是前面 INCLUDE_DIRECTORIES([AFTER|BEFORE] [SYSTEM] dir1 dir2 ...) ]] INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}/extra/sort/include") # 通过 SET 这个 cmake 变量为 on,可以将添加的头文件搜索路径放在已有路径的前面 # SET(CMAKE_INCLUDE_DIRECTORIES_BEFORE ON) # 为target添加共享库,路径之间用空格分割 LINK_DIRECTORIES("${CMAKE_SOURCE_DIR}/extra/sort/lib/WIN32") #[[链接 TARGET_LINK_LIBRARIES(target library1 <debug | optimized> library2 ...) ]] # 动态 TARGET_LINK_LIBRARIES(sort_test sort.dll) # 静态 TARGET_LINK_LIBRARIES(sort_test sort.lib) #[[ 特殊的系统环境变量:注意不是CMake变量CMAKE_INCLUDE_PATH、CMAKE_LIBRARY_PATH # 修改这两个环境变量到系统共存的第三方库环境变量 ]] # 在环境变量中找sort.h的路径添加到myHeader变量中 FIND_PATH(myHeader sort.h) # 指定路径 # FIND_PATH(myHeader NAMES sort.h PATHS /usr/include /usr/include/sort) # 判空 IF(myHeader) # 添加到include目录 INCLUDE_DIRECTORIES(${myHeader}) ENDIF(myHeader) # 注意搜索路径应该添加到可执行文件前 ADD_EXECUTABLE(sort_test sort_test.c)
/CmakeLists.txt
Project(sort_lib)
ADD_SUBDIRECTORY(src bin)
常用变量 | 含义 | 说明 |
---|---|---|
CMAKE_BINARY_DIR PROJECT_BINARY_DIR < < <projectname>_BINARY_DIR | 工程编译发生的目录 | |
CMAKE_SOURCE_DIR PROJECT_SOURCE_DIR _SOURCE_DIR | 工程顶层目录 | |
CMAKE_CURRENT_SOURCE_DIR | 当前处理的 CMakeLists.txt 所在的路径 | |
CMAKE_CURRRENT_BINARY_DIR | target 编译目录 | ADD_SUBDIRECTORY(src bin)可以更改这个变量的值 |
CMAKE_CURRENT_LIST_FILE | 调用这个变量的 CMakeLists.txt 的完整路径 | |
CMAKE_CURRENT_LIST_LINE | 输出这个变量所在的行 | |
CMAKE_MODULE_PATH | 定义自己的 cmake 模块所在的路径 | 如果工程比较复杂,有可能会自己编写一些 cmake 模块,这些 cmake 模块是随你的工程发布的,为了让 cmake 在处理 CMakeLists.txt 时找到这些模块,你需要通过 SET 指令,将自己的 cmake 模块路径设置一下 |
EXECUTABLE_OUTPUT_PATH LIBRARY_OUTPUT_PATH | 重新定义最终结果的存放目录 | |
PROJECT_NAME | 返回通过 PROJECT 指令定义的项目名称 |
# 调用系统的环境变量
$ENV{NAME}
# 设置环境变量
SET(ENV{变量名} 值)
环境变量 | 含义 | 说明 |
---|---|---|
CMAKE_INCLUDE_CURRENT_DIR | 自动添加 CMAKE_CURRENT_BINARY_DIR 和CMAKE_CURRENT_SOURCE_DIR 到当前处理的 CMakeLists.txt | 相当于在每个 CMakeLists.txt 加入: INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_BINARY_DIR}``${CMAKE_CURRENT_SOURCE_DIR} ) |
CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE | 将工程提供的头文件目录始终至于系统头文件目录的前面 | 当定义的头文件确实跟系统发生冲突时可以提供一些帮助 |
系统信息 | 含义 |
---|---|
CMAKE_MAJOR_VERSION | CMAKE 主版本号,比如 2.4.6 中的 2 |
CMAKE_MINOR_VERSION | CMAKE 次版本号,比如 2.4.6 中的 4 |
CMAKE_PATCH_VERSION | CMAKE 补丁等级,比如 2.4.6 中的 6 |
CMAKE_SYSTEM | 系统名称,比如 Linux-2.6.22 |
CMAKE_SYSTEM_NAME | 不包含版本的系统名,比如 Linux |
CMAKE_SYSTEM_VERSION | 系统版本,比如 2.6.22 |
CMAKE_SYSTEM_PROCESSOR | 处理器名称,比如 i686 |
UNIX | 在所有的类 UNIX 平台为 TRUE,包括 OS X 和 cygwin |
WIN32 | 在所有的 win32 平台为 TRUE,包括 cygwin |
开关选项 | 含义 |
---|---|
MAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS | 用来控制 IF ELSE 语句的书写方式 |
BUILD_SHARED_LIBS | 控制默认的库编译方式,默认为静态库 |
CMAKE_C_FLAGS | 设置C编译选项,也可以通过指令ADD_DEFINITIONS()添加 |
CMAKE_CXX_FLAGS | 设置C++编译选项,也可以通过指令ADD_DEFINITIONS()添加 |
# 1.向C,C++编译器添加-D定义 # 如果你的代码中定义了#ifdef ENABLE_DEBUG #endif,这个代码块就会生效。 # 如果要添加其他的编译器开关,可以通过 CMAKE_C_FLAGS 变量和 CMAKE_CXX_FLAGS 变量设置 ADD_DEFINITIONS(-DENABLE_DEBUG -DABC) # 2.定义 target 依赖的其他 target,确保在编译本 target 之前,其他的 target 已经被构建 ADD_DEPENDENCIES(target-name depend-target1 depend-target2 ...) # 3.ADD_TEST(testname Exename arg1 arg2 ...) # testname 是自定义的 test 名称,Exename 可以是构建的目标文件也可以是外部脚本等等,后面连接传递给可执行文件的参数。 # 如果没有在同一个 CMakeLists.txt 中打开ENABLE_TESTING()指令,任何 ADD_TEST 都是无效的 ADD_TEST(mytest ${PROJECT_BINARY_DIR}/bin/main) # 4.控制 Makefile 是否构建 test 目标,涉及工程所有目录,一般情况这个指令放在工程的主CMakeLists.txt 中 ENABLE_ESTING() # 5.发现一个目录下所有的源代码文件并将列表存储在一个变量中,这个指令临时被用来自动构建源文件列表 AUX_SOURCE_DIRECTORY(dir VARIABLE) # 6.在指定的目录运行某个程序,通过 ARGS 添加参数,如果要获取输出和返回值 # 可通过 OUTPUT_VARIABLE 和 RETURN_VALUE 分别定义两个变量 # 该指令可以在 CMakeLists.txt 处理过程中支持任何命令,如根据系统情况去修改代码文件 # 在 CMakeLists.txt 处理过程中执行命令,并不会在生成的 Makefile 中执行 EXEC_PROGRAM(Executable [directory in which to run] [ARGS <arguments to executable>] [OUTPUT_VARIABLE <var>] [RETURN_VALUE <var>]) # 在 src 目录执行 ls 命令,并把结果和返回值存下来 EXEC_PROGRAM(ls ARGS "*.c" OUTPUT_VARIABLE LS_OUTPUT RETURN_VALUELS_RVALUE) IF(not LS_RVALUE) MESSAGE(STATUS "ls result: " ${LS_OUTPUT}) ENDIF(not LS_RVALUE) # 7.文件操作指令 FILE(WRITE filename "message to write"... ) FILE(APPEND filename "message to write"... ) FILE(READ filename variable) FILE(GLOB variable [RELATIVE path] [globbing expressions]...) FILE(GLOB_RECURSE variable [RELATIVE path][globbing expressions]...) FILE(REMOVE [directory]...) FILE(REMOVE_RECURSE [directory]...) FILE(MAKE_DIRECTORY [directory]...) FILE(RELATIVE_PATH variable directory file) FILE(TO_CMAKE_PATH path result) FILE(TO_NATIVE_PATH path result) # 8.载入 CMakeLists.txt 文件,也用于载入预定义的 cmake 模块 # OPTIONAL 参数的作用是文件不存在也不会产生错误 # 可以指定载入一个文件,如果定义的是一个模块,那么将在CMAKE_MODULE_PATH中 # 搜索这个模块并入。载入的内容将在处理到 INCLUDE 语句是直接执行。 INCLUDE(file1 [OPTIONAL]) INCLUDE(module [OPTIONAL]) # 9.Find # VAR 变量代表找到的文件全路径,包含文件名 FIND_FILE(<VAR> name1 path1 path2 ...) # VAR 变量表示找到的库全路径,包含库文件名 FIND_LIBRARY(<VAR> name1 path1 path2 ...) # e.g. FIND_LIBRARY(libX X11 /usr/lib) IF(NOT libX) MESSAGE(FATAL_ERROR “libX not found”) ENDIF(NOT libX) # VAR 变量代表包含这个文件的路径 FIND_PATH(<VAR> name1 path1 path2 ...) # VAR 变量代表包含这个程序的全路径 FIND_PROGRAM(<VAR> name1 path1 path2 ...) # 调用预定义在 CMAKE_MODULE_PATH 下的 Find<name>.cmake 模块, # 也可以自己 定义Find<name>模块,通过SET(CMAKE_MODULE_PATH dir)将其放入工程的某个目录中供工程使用 FIND_PACKAGE(<name> [major.minor] [QUIET] [NO_MODULE] [[REQUIRED|COMPONENTS] [componets...]]) # 10.IF指令 IF(expression) # THEN section. COMMAND1(ARGS ...) COMMAND2(ARGS ...) ... ELSE(expression) # ELSE section. COMMAND1(ARGS ...) COMMAND2(ARGS ...) ... ENDIF(expression) # 凡是出现 IF 的地方一定要有对应的 ENDIF。出现 ELSEIF 的地方,ENDIF 是可选的 # IF(var),如果变量不是空,0,N, NO, OFF, FALSE, NOTFOUND 或<var>_NOTFOUND 时,表达式为真。 # IF(NOT var ),与上述条件相反 # IF(var1 AND var2),当两个变量都为真是为真。 # IF(var1 OR var2),当两个变量其中一个为真时为真。 # IF(COMMAND cmd),当给定的cmd确实是命令并可以调用是为真。 # IF(EXISTS dir)或者IF(EXISTS file),当目录名或者文件名存在时为真。 # IF(file1 IS_NEWER_THAN file2),当 file1 比 file2 新,或者 file1/file2 其 中有一个不存在时为真,文件名请使用完整路径。 # IF(IS_DIRECTORY dirname),当dirname是目录时,为真 # 正则匹配 IF(variable MATCHES regex) IF(string MATCHES regex) # IF("hello" MATCHES "ell") MESSAGE("true") ENDIF("hello" MATCHES "ell") # 数字比较表达式 IF(variable LESS number) IF(string LESS number) IF(variable GREATER number) IF(string GREATER number) IF(variable EQUAL number) IF(string EQUAL number)) # 按照字母序的排列进行比较 IF(variable STRLESS string) IF(string STRLESS string) IF(variable STRGREATER string) IF(string STRGREATER string) IF(variable STREQUAL string) IF(string STREQUAL string) # 判断变量定义:IF(DEFINED variable) IF(WIN32) MESSAGE(STATUS “This is windows.”) # 作一些 Windows 相关的操作 ELSE(WIN32) MESSAGE(STATUS “This is not windows”) # 作一些非 Windows 相关的操作 ENDIF(WIN32) # 简化ELSE、ENDIF,使用开关语句 SET(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS ON) # 简化为 IF(WIN32) ELSE() ENDIF() # 配合ELSEIF使用 IF(WIN32) #do something related to WIN32 ELSEIF(UNIX) #do something related to UNIX ELSEIF(APPLE) #do something related to APPLE ENDIF(WIN32) # 11.WHILE WHILE(condition) COMMAND1(ARGS ...) COMMAND2(ARGS ...) ... ENDWHILE(condition) # 12.FOREACH # 列表 AUX_SOURCE_DIRECTORY(. SRC_LIST) FOREACH(F ${SRC_LIST}) MESSAGE(${F}) ENDFOREACH(F) # 范围 FOREACH(VAR RANGE 10) MESSAGE(${VAR}) ENDFOREACH(VAR) # 范围和步进 FOREACH(A RANGE 5 15 3) MESSAGE(${A}) ENDFOREACH(A)
# /src/CMakeLists.txt
INCLUDE_DIRECTORIES(/usr/include)
TARGET_LINK_LIBRARIES(curltest curl)
让工程找到 FindHELLO.cmake 模块(存放在工程中的 cmake 目录)
# /CMakeLists.txt
SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
FIND_PACKAGE
可以直接调用预定义的模块
# /src/CMakeLists.txt
# 对于系统预定义的 Find<name>.cmake 模块,都会定义以下几个变量:
# 判断模块是否被找到,如果没有找到,按照工程的需要关闭某些特性、给出提醒或者中止编译
# <name>_FOUND
# 如果 <name>_FOUND 为真,则将 <name>_INCLUDE_DIR 加入 INCLUDE_DIRECTORIES
# 将 <name>_LIBRARY 加入 TARGET_LINK_LIBRARIES 中
# FIND_PACKAGE(<name> [major.minor] [QUIET] [NO_MODULE]
# [[REQUIRED|COMPONENTS] [componets...]])
FIND_PACKAGE(CURL)
IF(CURL_FOUND)
INCLUDE_DIRECTORIES(${CURL_INCLUDE_DIR})
TARGET_LINK_LIBRARIES(curltest ${CURL_LIBRARY})
ELSE(CURL_FOUND)
MESSAGE(FATAL_ERROR ”CURL library not found”)
ENDIF(CURL_FOUND)
编库时,项目结构
- CMakeLists.txt
- src
CMakeLists.txt
curl.c
- cmake
FindCURL.cmake
- build
# /cmake/FindCURL.cmake FIND_PATH(CURL_INCLUDE_DIR curl.h /usr/include/curl /usr/local/include/curl) FIND_LIBRARY(CURL_LIBRARY NAMES curl PATH /usr/lib /usr/local/lib) IF(CURL_INCLUDE_DIR AND CURL_LIBRARY) SET(CURL_FOUND TRUE) ENDIF(CURL_INCLUDE_DIR AND CURL_LIBRARY) IF(CURL_FOUND) # 根据FIND_PACKAGE中的QUIET参数判断 IF(NOT CURL_FIND_QUIETLY) MESSAGE(STATUS "Found CURL: ${CURL_LIBRARY}") ENDIF(NOT CURL_FIND_QUIETLY) ELSE(CURL_FOUND) # 根据FIND_PACKAGE中的REQUIRED参数判断 IF(CURL_FIND_REQUIRED) MESSAGE(FATAL_ERROR "Could not find CURL library") ENDIF(CURL_FIND_REQUIRED) ENDIF(CURL_FOUND)
Make:用来处理编译顺序和依赖关系并构建最终项目的工具
CMake:本身不执行Make过程,而是根据不同平台的特性,生成对应平台的Makefie,这样我们每个工程只要写一个CMake文件即可了,其余的交给不同平台的处理器来产生不同的Makefile文件即可。而且CMake的语法也更加简洁
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。