前言
本文主要實作的是在windows10下使用VSCode編輯器以及cmake工具來建構一個包含第三方庫檔案(此處為SFML)的項目的配置和調試。(因為SFML庫檔案的cmake比較難搞,,是以我自認為重點是其cmakelists.txt的編寫)。
首先要保證VSCode可以正常使用MinGW編譯器來編譯運作C++程式、已經安裝并能使用cmake。
建構基礎項目結構
首先貼一下項目的大緻結構:
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiIXZ05WZj91YpB3In5GcuYzN0MzMxYTMxMDMxAjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
其中:
-
一般按照項目的習慣是作為整個項目編譯後的檔案所在的檔案夾,可以了解為變異的得到的可執行程式以及需要的資源的檔案夾(即main.exe)build
-
這個是cmake執行時自動生成的一些檔案,不用管CMakeFiles
-
這個一般就是我們項目引入第三方庫檔案的所在的檔案夾,這裡的檔案一般要去你所需的庫檔案的官網下載下傳 适合自己平台 的編譯好的檔案。(這裡後面再說)external
-
這個一般是包含在編寫代碼時為了自動補全等需要的頭檔案等等内容。include
-
一般是一些項目的資源檔案,如圖檔、視訊、音頻、紋理、模型等等。res
-
即我們項目自己編寫的源碼檔案夾。src
-
這個檔案是項目的一個配置檔案,對于不同平台環境的使用者,都可以使用CMakeLists.txt
指令來解釋此檔案來生成适合自己環境的項目編譯等的cmake
檔案。比如,我們win下完成的項目,隻要編寫一個合适的Makefile
,使用CMakeLists.txt
指令就可以生成 win下的一個編譯項目的方式(包括cmake
和資源檔案、依賴檔案等等的内容,一般就在Makefile
檔案夾中) 。而另一個 unix 使用者,也可以通過其 cmake 指令來生成自己平台下的build
檔案夾。這樣無論是什麼平台下的使用者,都可以使用本平台的build
指令來編譯運作我們的項目。這也是使用make
和cmake
指令來管理項目的一個原因吧。make
- 其他幾個檔案示項目不同。
- 此外,一般還包含單元測試
檔案夾等内容。test
關于 external
檔案夾、SFML以及cmake指令:
external
對于
SFML
來說,需要去 SFML Downloads 下載下傳适合本平台的版本,(比如我,裝置安裝的是MinGW64,是以我下載下傳的就是GCC 7.3.0 MinGW (SEH) - 64-bit這個版本的)然後将其解壓并複制到
external
檔案夾下就行了,
其實就是相當于引入第三方庫的 頭檔案 以及 需要連結的.a 檔案,至于一些 .dll 檔案,我們可以直接複制到編譯好的程式的同級目錄下即可,
解釋
對于前面兩個引入,我們可以通過
g++ main.cpp -IIncludePath -LLibsPath -o main.exe
來搞定,當然為了簡化輸入指令的過程,可以通過編寫
Makefile
檔案來簡化,,這樣通過
make
指令就可以編譯、運作、清理環境等等。
對于最後一個引入,我們可以通過編寫 cmake 的
CMakeLists.txt
來實作将确定位置的
.dll
檔案拷貝的操作。
整理一下這個流程:
- 我們需要簡化
等源檔案和第三方檔案的編譯、連結操作,可以通過main.cpp
指令對make
檔案解釋來實作;Makefile
- 我們需要簡化對整個項目中的第三方檔案以及不同平台上的項目的配置操作,是以通過
來解釋cmake
檔案來生成适合本平台下的CMakeLists.txt
檔案。Makefile
(對于java系,引入第三方包,直接将jar包拖到IDE就能配置好,,當然C++下的IDE也可以實作這樣的操作,,僅僅是IDE實作了
CMakeLists.txt
的一些功能,将其內建到一個按鈕上)
接下來進入到具體到 使用SFML庫 的
CMakeLists.txt
的編寫。
CMakeLists.txt
的編寫
CMakeLists.txt
首先貼一下我目前
SFML
在項目中的位置:(上文提到的下載下傳位置、解壓、将
然後在項目的根目錄中添加一個
項目名Config.h.in
檔案,其中的内容如下(否則可能後續cmake會報錯):
#define 項目名_VERSION_MAJOR @項目名_VERSION_MAJOR@
#define 項目名_VERSION_MINOR @項目名_VERSION_MINOR@
接着,在根目錄中添加
CMakeLists.txt
檔案,檔案的内容如下(參考):
# cmake最低版本号要求
cmake_minimum_required (VERSION 3.0)
# 設定項目名
set(PROJECT_NAME sfml_learning_project)
project (${PROJECT_NAME})
set (${PROJECT_NAME}_VERSION_MAJOR 1)
set (${PROJECT_NAME}_VERSION_MINOR 0)
# 設定編譯器參數,可以自己更改
set (CMAKE_CXX_STAND 11)
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
# 配置一個頭檔案來傳遞一個cmake設定到源代碼
configure_file (
"${PROJECT_SOURCE_DIR}/${PROJECT_NAME}Config.h.in"
"${PROJECT_SOURCE_DIR}/${PROJECT_NAME}Config.h"
)
# 設定引入的外部第三方庫的路徑
set (EXTERNAL_DIR ${PROJECT_SOURCE_DIR}/external)
# set(CMAKE_MODULE_PATH ${EXTERNAL_DIR})
# sfml
# SFML庫的路徑,要精确到官方下載下傳的檔案/lib/cmake/SFML
set (SFML_DIR ${EXTERNAL_DIR}/SFML-2.5.1/lib/cmake/SFML)
# set(SFML_STATIC_LIBRARIES TRUE)
# 輸出路徑,檢查是否正确
message(${SFML_DIR})
# 調用需要的子產品,此項指令依賴于上面那個路徑的設定,對于SFML,項目用到啥 調用啥
find_package(SFML 2.5 COMPONENTS system window graphics network audio REQUIRED)
# 查找目前目錄下的所有源檔案并存入DIR_SRCS變量
aux_source_directory(src SRC_DIR)
# 遞歸列出所有源檔案
file (GLOB_RECURSE SOURCE_FILES main.cpp)
# 添加一個可編譯的目标到工程
add_executable (${PROJECT_NAME} ${SOURCE_FILES})
# 設定工作目錄
set_property(TARGET ${PROJECT_NAME} PROPERTY VS_DEBUGGER_WORKING_DIRECTORY
${CMAKE_SOURCE_DIR}/res
)
# 添加連結庫,項目需要啥,調用啥
target_link_libraries (${PROJECT_NAME} sfml-window sfml-graphics sfml-audio)
# target_compile_definitions(${PROJECT_NAME} PRIVATE SFML_INCLUDE_NONE)
# 将SFML/bin下的所有*.dll複制到 /build檔案下,保證編譯程式正常運作,不報缺少dll錯誤
file(GLOB_RECURSE PROJECT_DLL ${EXTERNAL_DIR}/*.dll)
message(${PROJECT_DLL})
# 複制庫到工作目錄
# POST_BUILD 在編譯後操作
# copy指令拷貝單個檔案,下面的 copy_directory是拷貝目錄
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
${PROJECT_DLL}
$<TARGET_FILE_DIR:${PROJECT_NAME}>
)
# 複制資源檔案到工作目錄
# 當項目中有用到素材時需要
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_SOURCE_DIR}/res
$<TARGET_FILE_DIR:${PROJECT_NAME}>/res
)
# 針對蘋果系統,未測試
if(APPLE)
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND install_name_tool -change /usr/local/lib/libirrklang.dylib @executable_path/libirrklang.dylib ${PROJECT_NAME}
)
endif()
對于SFML,重要的是路徑的配置(即 .hpp等頭檔案以及 .a連結檔案):
# sfml
# SFML庫的路徑,要精确到官方下載下傳的檔案/lib/cmake/SFML
set (SFML_DIR ${EXTERNAL_DIR}/SFML-2.5.1/lib/cmake/SFML)
# set(SFML_STATIC_LIBRARIES TRUE)
# 輸出路徑,檢查是否正确
message(${SFML_DIR})
# 調用需要的子產品,此項指令依賴于上面那個路徑的設定,對于SFML,項目用到啥 調用啥
find_package(SFML 2.5 COMPONENTS system window graphics network audio REQUIRED)
以及對 .dll 檔案的拷貝:
# 将SFML/bin下的所有*.dll複制到 /build檔案下,保證編譯程式正常運作,不報缺少dll錯誤
file(GLOB_RECURSE PROJECT_DLL ${EXTERNAL_DIR}/*.dll)
message(${PROJECT_DLL})
# 複制庫到工作目錄
# POST_BUILD 在編譯後操作
# copy指令拷貝單個檔案,下面的 copy_directory是拷貝目錄
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
${PROJECT_DLL}
$<TARGET_FILE_DIR:${PROJECT_NAME}>
)
當編輯完畢
CMakeLists.txt
後,打開
cmake-gui
,選擇項目的路徑,以及編譯後的路徑,點選
Configure
沒有錯誤後點選
Generate
即可在
build
檔案夾生成适合本平台的整個項目:
此時,,我們可以在
build
檔案夾中看到生成的檔案(主要是
Makefile
)
到這一步就相當于是我們在IDE中配置好了整個項目的環境,如項目的名稱、路徑、引入的第三方庫檔案的路徑等等内容,是以 隻要build不删,我們隻需執行一次cmake指令
之後我們就可以在
src
中編寫項目的代碼,測試時就到
build
檔案夾下,(因為我們已經通過
cmake
生成了項目的
Makefile
檔案,是以直接 make就行了),執行
mingw32-make.exe -j8 V=1
即可完成編譯(MinGW把自帶的make更名了,其中
-j8
表示并行調用8和核心編譯,
V=1
表示顯示中間資訊):
在
build
檔案夾下可以看到編譯後的程式所需的檔案:
其中有源碼編譯後的程式、有所需的資源檔案夾、有所需的 .dll 檔案等等。
這裡使用的源程式為:
#include <iostream>
#include <cmath>
#include <SFML/Graphics.hpp>
using namespace std;
sf::RenderWindow window(sf::VideoMode(1920,1080), "Tree");
void start(float inix, float iniy, float degree, float length)
{
if (length < 1.0f) return;
else
{
float newlength=0.67f * length;
float finx = inix + (newlength * (float)cos(degree * (3.14 / 180.0f)));
float finy = iniy + (newlength * (float)sin(degree * (3.14 / 180.0f)));
sf::Vertex line[]= {
sf::Vertex(sf::Vector2f(inix, iniy)),
sf::Vertex(sf::Vector2f(finx, finy))
};
start(finx, finy, degree+60.0f, newlength);
start(finx, finy, degree-30.0f, newlength);
window.draw(line, 2, sf::Lines);
//line.setFillColor(sf::Color::Blue);
//window.display(); // Uncomment for animation
}
}
int main()
{
window.clear();
start(1000, 799, -90, 350);
window.display();
sf::Event event;
while (window.isOpen())
{
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed) window.close();
}
}
return 0;
}
進入
build
檔案後運作即可:
背景會不斷的輸出
Failed to set DirectInput device axis mode: 1
這個可以貌似是官方的一個bug,,,不影響測試我就沒管。
參考
- 一個詳細講cmake的 cmakelists.txt編寫方法的部落格
- 官方給出的一個 SFML 的CMakeLists.txt 例子和讨論
- 同上,隻是這種配置SFML已經失效,對于最後的其他庫的引用給出了一種方式
- 在學習CMakeLists.txt中很大一部分參考的項目配置
- 一個很不錯的項目結構
- 最後的測試代碼的來源
整個配置操作應該就結束了,如果有錯誤歡迎指出交流!(author: 31415926535x)