介绍
在windows平台上,由于平台API差异过大,一般为linux设计的项目(POSIX兼容)无法通过MSVC的编译,而是会报非常多的头文件错误。如果要修改,工程量将巨大。Windows平台上,主要有两个类POSIX兼容平台,可以在Windows系统上编译运行为Linux编写的程序(功能支持不完整)。
- MinGW Minimalist GNU for Windows: 是将GCC编译器和GNU工具链套件移植到Windows平台下的产物。
- LLVM/Clang for Windows: Clang编译器对Windows Target的适配。由于LLVM框架的结构,只需要开发Windows平台对应的后端,而不需要进行迁移。LLVM在Windows平台的后端主要采用Visual Studio的MSVC编译器。
cmake是一个跨平台项目编译工具,通过CMakeLists.txt
文件,可以方便地管理项目文件,并灵活地组织编译。根据工具链的不同,编译结果可以应用于各个平台上。例如,Linux系统中,cmake可以编译出Makefile
,并交给linux平台的make程序进行编译。
一个项目需要在Windows平台上编译,但是是面向linux编写的(Makefile
),这时候就需要使用Windows平台上的工具链进行编译。但是,多文件项目管理复杂,一个一个文件进行编译较为繁琐,这时候,就可以使用cmake工具辅助进行编译。
笔者是VS Code重度依赖用户,所以在VS Code上利用cmake tools进行配置。当然也可以使用命令行或是cmake-gui进行配置,只不过cmake tools更加集成和方便。
安装Clang-CL & CMake
Clang-CL for MSVC
可以在Visual Studio的组件中找到
CMake
可以在官网下载,安装后注意检查PATH,是否添加cmake
CMake & Toolkits
我们应当明确几个编译概念:
- Build System 编译系统:又称为Generator 生成器,是编译工具根据规则编译、链接文件的系统,按照是否集成交互,可分为;
- IDE Build Tool Generators 用于IDE的Build System,生成的项目可以直接被IDE打开、编辑,例如Visual Studio Generators
- Command-Line Build Tool Generators,使用命令行进行编译构建,有编译工具链即可工作,不需要IDE支持,例如,
Make
构建系统(即Linux中使用Makefile
进行构建的系统),以及Ninja
(一套更轻量的命令行构建系统,是CMake Tools 的默认构建系统)
- Build Toolkits 编译工具链:是直接参与编译、生成可执行文件的一系列工具,包括Compiler 编译器, Linker 链接器等。主流的工具链如下:
- GNU/GCC: GNU项目组的编译工具链,主要运用于Linux系统,其在Windows上的迁移版本称为
MingGW
- LLVM/Clang:LLVM编译基础设施,采用模块化设计,前后端分离工作,能够很好地适配多种前端语言和后端平台,是越来越流行的编译工具链
- Visual C++:微软开发的C/C++编译工具链,集成在Visual Studio上,主要用于Windows平台下的程序开发,即MSVC
- GNU/GCC: GNU项目组的编译工具链,主要运用于Linux系统,其在Windows上的迁移版本称为
编译系统使用编译工具链,按照开发者指定的编译规则进行程序的编译链接。那么,CMake属于哪里?CMake 属于更上一层的编译工具,使用CMake,可以生成任何一种适配的Build System所需要的规则,然后再调用这些Build System进行程序编译。
配置 cmake toolkits
在VS Code中,安装插件CMake Tools
,并在项目目录中创建CMakeLists.txt
文件,插件被激活后,状态栏会显示CMake Toolkits, Build Variant, Build Target等信息。如果是第一次在项目中使用插件,Toolkits工具链会显示No Kit Selected
,单击该按钮,出现选择框。
初次配置,点击Scan for kits
,工具会自动扫描电脑上安装的工具链。
Clang for MSVC 对应有四条(名称已经被我修改,正常情况下还有版本信息),Build Target有x86和x64,命令行接口有GNU CLI和MSVC CLI。
接下来,检查工具链配置参数,Ctrl+Shift+P
,打开Command Palette,输入cmake kits
-> 编辑用户本地CMake 工具包,这会打开一个json文件。
部分配置如下:
{
"name": "Clang 14.0.5 (GNU CLI) for MSVC (VS Community 2022 Release - x86)",
"visualStudio": "8b689476",
"visualStudioArchitecture": "x86",
"compilers": {
"C": "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Tools\\Llvm\\bin\\clang.exe",
"CXX": "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Tools\\Llvm\\bin\\clang.exe"
}
},
{
"name": "Clang 14.0.5 (MSVC CLI) for MSVC (VS Community 2022 Release - x64)",
"visualStudio": "8b689476",
"visualStudioArchitecture": "x64",
"preferredGenerator": {
"name": "Visual Studio 17 2022",
"platform": "x64",
"toolset": "ClangCL"
},
"compilers": {
"C": "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Tools\\Llvm\\x64\\bin\\clang-cl.exe",
"CXX": "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Tools\\Llvm\\x64\\bin\\clang-cl.exe"
}
},
- 对于MSVC CLI,Scan kit默认配置为MSVC Generator,注意检查toolset,必须修改为
ClangCL
,若为host=xxx
,则仍将通过传统MSVC工具链编译,仍会遇到API不适配问题 - 对于GNU CLI,默认配置为Ninja Generator,是一套轻量级、高效率的命令行构建系统,也可以根据需要修改
preferredGenerator
为MSVC工具链
编译与常用选项
状态栏选项
- Build Variant (构建变体,编译选项……XD, anyway):选择工具链的编译输出模式,常见的有
Debug
调试模式(包含调试信息)、Release
发布模式(去除调试信息,增加优化) - ToolKits 工具链:为项目选择编译工具链(跨平台项目使用交叉编译工具链)
- Build 编译:点击编译(Not install)
- Build Target:编译目标,选择编译哪个项目,默认为
[all]
全部编译,但是速度很慢,注意更换 - Debug\Run:调试、运行,不多介绍
Command Palette 命令选项卡
使用命令选项卡可以进行更加完整的配置,常用的有两个:
- CMake Clean:清除编译文件
- CMake Install:进行编译安装,将按照
CMakeLists.txt
指定的安装配置进行生成工具的安装
示例
在一个目录下,创建如下文件
G:.
│ CMakeLists.txt
├─include
│ hello_lib.h
├─lib
│ │ CMakeLists.txt
│ ├─include
│ │ hello_lib.h
│ └─src
│ hello_lib.c
└─src
hello.c
// hello.c
#include <Windows.h>
#include <stdio.h>
#include "hello_lib.h"
int main() {
printf("Hello World!\n");
DbgPrint("%s\n", "Hello World"); // defined in hello_lib
return 0;
}
// hello_lib.h
#include <Windows.h>
#include <stdio.h>
#include <debugapi.h>
#include <stdarg.h>
void DbgPrint(char* FormatStr, ...);
// hello_lib.c
#include "hello_lib.h"
void DbgPrint(char* FormatStr, ...)
{
char dbgout[1000];
va_list vaList;
va_start(vaList, FormatStr);
sprintf(dbgout, FormatStr, vaList);
OutputDebugStringA(dbgout);
va_end(vaList);
}
然后配置CMakeLists:
root
cmake_minimum_required(VERSION 3.0)
project(HELLO)
add_subdirectory(lib)
aux_source_directory(src src_dir)
add_executable(hello ${src_dir})
target_include_directories(hello PUBLIC ${PROJECT_SOURCE_DIR}/include)
target_link_libraries(hello PUBLIC hello_lib)
add_subdirectory
增加子项目target_link_libraries
链接库,hello_lib
在子项目中定义
lib
cmake_minimum_required(VERSION 3.0)
project(HELLO_LIB)
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/src src_dir)
add_library(hello_lib STATIC ${src_dir})
target_include_directories(hello_lib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
add_library(STATIC)
添加静态库
最后,build,输出结果:
[build] hello_lib.vcxproj -> G:\C\Cmake Test\build\lib\Debug\hello_lib.lib
[build] hello.c
[build] hello.vcxproj -> G:\C\Cmake Test\build\Debug\hello.exe
[build] Build finished with exit code 0