大型工程一般都使用cmake編譯,cmake可以實現跨平台開發,同時,修改一個文件時,只需對該文件進行編譯鏈接就可以了,不需要對其他未修改文件編譯,可以節約時間。本文章重點介紹使用cmake和make進行C++開發。
使用VsCode插件C/C++ Program Generator進行開發可以查看這篇博客:https://www.cnblogs.com/Fight-go/p/15717719.html
本篇博客以一個簡單的交換兩個數的類Swap的程序為樣例,交換兩個數的程序的文件結構如下:
build文件夾用來存放Cmake產生的中間文件,out用來存放輸出的可執行文件。
樣例的源代碼如下:
#pragma once
#include <iostream>
class swap
{
private:
int _a;
int _b;
public:
swap(int a, int b) : _a(a), _b(b){ }
void run();
void printInfo();
};
#include "swap.h"
void swap::run()
{
int temp;
temp = _a;
_a = _b;
_b = temp;
}
void swap::printInfo()
{
std::cout << "_a = " << _a << std::endl;
std::cout << "_b = " << _b << std::endl;
}
#include "swap.h"
int main()
{
swap myswap123(10, 20);
std::cout << "Before swap:" << std::endl;
myswap123.printInfo();
myswap123.run();
std::cout << "After swap:" << std::endl;
myswap123.printInfo();
return 0;
}
1. CMAKE重要指令和常用變量
1.1 重要指令
- cmake_minimum_required -指定Cmake的最小版本要求
- 語法:cmake_minimum_required(VERSION versionNumber[FATAL_ERROR])
# CMake最小版本要求3.22.1
cmake_minimum_required (VERSION 3.22.1)
- project -定義工程名稱,並指定工程支持的語言
- 語法:project(projectname[CXX][C][Java])
# 指定工程名為HELLOWORLD
project(HELLOWORLD)
- set -顯式的定義變量
- 語法:set(VAR[VALUE][CACHE TYPE DOCSTRING [FORCE]])
# 定義SRC變量,其值為main.cpp hello.cpp
set(SRC main.cpp hello.cpp)
- include_directories -向工程添加多個特定的頭文件搜索路徑-->相當與g++的-I參數
- 語法:include_directories([AFTER|BEFORE] [SYSTEM] dir1 dir2)
# 將/usr/include/myincludefolder和./inculde添加到頭文件搜索路徑
include_directories(/usr/include/myincludefolder ./inculde)
- link_directories -向工程添加多個特定的庫文件搜索路徑-->相當與g++的-L參數
- 語法:link_directories(dir1 dir2 ...)
# 將/usr/include/myincludefolder和./inculde添加到庫文件搜索路徑
link_directories(/usr/include/myincludefolder ./inculde)
- add_library -生成庫文件
- 語法:add_library(libname[SHARED|STATIC|MODULE][EXCULDE_FROM_ALL]source1 source2 ...sourceN)
# 通過變量 SRC 生成 libhello.so 共享庫
add_library(hello SHARED ${SRC})
- add_compile_options -添加編譯參數
- 語法:add_compile_options(<option>...)
# 添加編譯參數
add_compile_options(-Wall -std=c++11 -O2)
- add_executable -生成可執行文件
- 語法:add_executable(exname source1 source2...sourceN)
# 編譯main.cpp生成可執行文件main
add_executable(main main.cpp)
- target_link_libraries -為target添加需要鏈接的共享庫--->相當於指定g++編譯-l參數
- 語法:target_link_libraries(target library1<debug|optimized> library2...)
# 將hello動態庫文件鏈接到可執行文件main
target_link_libraries(main hello)
- add_subdirectory -向當前工程目錄添加存放源文件的子目錄,並可以制定中間二進制和目標二進制存放的位置
- 語法:add_subdirectory(source_dir[binary_dir][EXCLUDE_FROM_ALL])
# 通過變量 SRC子目錄,src中需要一個CMakeLists.txt
add_subdirectory(src)
- aux_source_directory -發現一個目錄下所有的源代碼文件並將列表存儲在一個變量中,這個指令臨時被用來自動構建源文件列表
- 語法:aux_source_directory(dir VARIABLE)
# 定義SRC變量,其值為當前目錄下所有的源代碼文件
aux_source_directory(. SRC)
# 編譯SRC變量所代碼的源代碼文件,生成main可執行文件
add_excuatable(main ${SRC})
1.2 CMake常用變量
- CMAKE_C_FLAGS gcc編譯選項
- CMAKE_CXX_FLAGS g++編譯選項
# 在CMAKE_CXX_FLAGS編譯選項后追加-std=c++11
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
- CMAKE_BUILD_TYPE 編譯類型(Debug, Release)
# 設定編譯類型為debug,調試時需要選擇debug
set(CMAKE_BUILD_TYPE Debug)
# 設定編譯類型為release,發布時需要選擇release
set(CMAKE_BUILD_TYPE Release)
-
CMAKE_BINARY_DIR
PROJECT_BINARY_DIR
<projectname>_BINARY_DIR
1.這三個變量指代的內容是一致的。
2.如果是in source build,指的就是工程頂層目錄
3.如果是out-of-source編譯,指的是工程編譯發生的目錄
4.PROJECT_BINARY_DIR跟其他指令稍有區別,不過現在,你可以理解為他們是一致的。 -
CMAKE_SOURCE_DIR
PROJECT_SOURCE_DIR
<projectname>_SOURCE_DIR
1.這三個變量指代的內容是一致的,不論采用何種編譯方式,都是更成頂層目錄。
2.也就是在in source build時,它跟 CMAKE_BINARY_DIR等變量一致
3.如果是out-of-source編譯,指的是工程編譯發生的目錄
4.PROJECT_SOURCE_DIR跟其他指令稍有區別,現在,你可以理解為他們是一致的。 -
CMAKE_C_COMPILER: 指定C編譯器
-
CMAKE_CXX_COMPILER: 指定CXX編譯器
-
EXCUTABLE_OUTPUT_PATH: 可執行文件輸出的存放路徑
-
LIBRARY_OUTPUT_PATH: 庫文件輸出的存放路徑
2. 編寫CMakeLists.txt文件
首先,編寫CmakeLists.txt文件,在工作區文件夾創建CmakeLists.txt,編寫如下代碼:
# 最小版本,可以通過cmake --version查看
cmake_minimum_required(VERSION 3.22.1)
# 項目名稱,建議大寫
project(SWAP)
# 搜索自定義的頭文件目錄,可以使用絕對路徑 ${CMAKE_SOURCE_DIR}/include
include_directories(include)
# 設置編譯時的選項,如O2優化,開啟警告,使用的C++標准等
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
# 調試時使用該項
set(CMAKE_BUILD_TYPE Debug)
# 生成可執行文件命令,第一個參數是生成可執行文件的名稱,后面的參數是源文件代碼
add_executable(main main.cpp src/swap.cpp)
# 指定生成可執行文件的輸出目錄
set(EXECUTABLE_OUTPUT_PATH "../out")
編寫完成之后,可以使用終端命令,檢驗CMakeLists文件編寫是否正確,在終端進入bulid目錄,輸入以下指令:
cmake .. # 之所以使用cmake ..是因為CMakeLists.txt在build目錄的上級
make
../out/main # 運行生成的可執行文件
運行結果如下:
可以看到運行成功。
3. 配置launch.json文件和task.json文件
launch.json文件完成的是調試工作,有兩個參數很關鍵,分別是
program和preLaunchTask,兩個參數分別代表了調試運行程序的名稱和在調試運行前執行的任務。該兩項需要修改,如圖所示:
launch.json的代碼如下:
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "g++ - Build and debug active file",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/out/${fileBasenameNoExtension}",
"args": [],
"stopAtEntry": false,
"cwd": "${fileDirname}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
],
"preLaunchTask": "Build",
"miDebuggerPath": "/usr/bin/gdb"
}
]
}
task.json執行的是編譯產生可執行文件的工作,其代碼如下:
{
"version":"2.0.0",
"options": {
"cwd": "${workspaceFolder}/build"
},
"tasks": [
{
"type": "shell", // open the shell 打開終端
"label": "cmake",
// 執行命令 cmake ..
"command": "cmake",
"args": [
".."
]
},
{
"label": "make",
"group": "build",
"command": "make", // 執行命令make
"args": [
]
},
{
"label": "Build",
"dependsOrder": "sequence",
"dependsOn":[
"cmake",
"make"
]
}
],
}
配置好兩個文件就可以按F5調試運行了,運行結果和之前一樣,至此使用cmake和vscode編譯運行多文件C++程序完成。