Visual Studio環境變數使用例項:使用環境變數來組織工程
前言
使用vs環境變數來組織工程
通常一個解決方案包含多個專案,這些專案相互之間可能存在依賴關係,以下面這個解決方案為例:
這個解決方案叫:CS.cpp, 包含了7個專案:
專案名 | 生成目標 | 描述 |
---|---|---|
Algorithm |
.exe | 演算法和資料結構實踐 |
c_language |
.exe | c語言實踐 |
TotalSTL |
.exe | STL實踐 |
TrainingGround |
.exe | C++語法自由訓練場 |
UnderstandingCpp11 |
.exe | 深入理解C++11程式碼實踐 |
gtest |
.lib | google c++單元測試框架,給其他幾個專案作為測試框架 |
util |
.lib | 個人積累工具類,為其他幾個專案提供util函式 |
其中五個專案是生成.exe檔案的應用程式,另外兩個gtest
和util
是服務於其他五個專案的,它倆生成的是.lib庫檔案,來為其他五個專案連結使用。
下圖是這個解決方案檔案的物理路徑:
可以看到,每個專案名稱對應一個同名資料夾。(Algorithm專案對應CS.cpp資料夾,因為Algorithm這個專案名字是中途修改的。)
除了7個專案名對應的目錄,其他幾個資料夾的作用如下表所示:
資料夾 | 作用 |
---|---|
include |
專案中使用到的標頭檔案存放於此 |
libs |
專案中使用到的庫檔案存放於此, gtest和util這種庫工程的輸出檔案也存放於此,如各種.lib檔案 |
intermediate |
所有專案的”中間目錄”集中存放於此 |
output |
所有應用程式專案的”輸出檔案”存放於此,如各種.exe檔案 |
res |
專案中用到的資原始檔存放於此,比如.txt, .json等檔案 |
_build |
與VC++專案無關,不需留意。 |
下面介紹下我是如何把這7個專案組織起來協同工作,並且做到沒有冗餘檔案。
其實,組織專案很簡單,僅需掌握C++程式構建的本質,關鍵的兩個階段:編譯和連結。
第一步,讓專案編譯通過
這一步的目標是:讓5個生成.exe的專案編譯通過。以其中任意一個為例講解,其他的與之類似。那麼我就以TotalSTL為例吧,
先保證TotalSTL其內部程式碼沒有語法錯誤。
其次,因為程式碼中使用了gtest和util兩個專案中的程式碼,因此需要確保TotalSTL專案能夠搜尋到gtest和util的標頭檔案。也就是說把gtest和util的標頭檔案所在目錄新增到TotalSTL專案的包含路徑裡即可。增加專案包含目錄的操作在上一篇文章中已經提到,這裡不再細說。
需要注意的一點是,由於gtest和util屬於公共使用的庫,所以最好是把它們的標頭檔案放在一個公共的路徑下,比如放在常見的以include命名的目錄。這正是前面表格中提到的include資料夾的作用,其物理結構如下圖所示:
可以看到,在include目錄下包含了gtest和util等子目錄,他們是按照專案來分類,除了gtest和util這兩個專案,還有其他的包含檔案也集中放在此處。
我要做的是把include新增到TotalSTL包含目錄中,運用上一篇文章學到的環境變數$(SolutionDir)
,我可以這樣編寫這個包含目錄:
$(SolutionDir)include/
新增完包含目錄,在TotalSTL專案的main.cpp中,就可以這樣引用gtest和util的標頭檔案:
main.cpp
#include "gtest/gtest.h" // gtest是include資料夾的子資料夾,gtest.h是在gtest資料夾下,因此要加上gtest/字首
#include "util/FileReader.h" // 同理,util是include的子資料夾,FileReader.h是在util資料夾下,因此加上util/字首
void dummyExitFunction()
{
elloop::FileReader::getInstance()->purege();
char c = getchar();
}
int main(int argc, char** argv) {
#if defined(_MSC_VER) && defined(_DEBUG)
// make program stop when debug.
atexit(dummyExitFunction);
turnOnMemroyCheck();
#endif
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
其中,#include "gtest/gtest.h"
, gtest是include資料夾的子資料夾,gtest.h是在gtest資料夾下,因此要加上gtest/字首
同理,#include "util/FileReader.h"
, util是include的子資料夾,FileReader.h是在util資料夾下,因此加上util/字首
包含目錄配置完畢,專案就能夠順利通過編譯了。其他四個專案的配置與TotalSTL的配置一樣,也把include加入到包含目錄即可。
第二步:讓專案連結通過
在配置完包含目錄,編譯通過之後,我如果點“生成”專案,在連結階段會報錯的,是因為五個.exe專案在連結時,沒有找到它們依賴的gtest和util庫檔案。
這一步就是配置庫搜尋路徑:
思路是,先確定gtest和util兩個專案生成的庫檔案存放在何處,然後把庫檔案所在路徑加入到其他五個專案的庫搜尋路徑即可。
1. 確定庫檔案位置
跟前面講的把公共標頭檔案統一放在include目錄類似,公共的庫檔案一般是放到名為lib或者library的資料夾下,正如前文的目錄結構圖所示,我把它們統一放到了$(SolutionDir)libs
目錄下,如圖,gtest.lib和util.lib就是gtest和util兩個專案生成的庫檔案:
要想做到讓gtest和util這倆專案”把蛋下到libs這裡”是需要設定的。以gtest專案為例,進行如下的設定:專案屬性 - 庫管理器 - 常規 - 輸出檔案 :
注意到其中對環境變數的使用,$(SolutionDir)libs/
就是我的目的地,$(TargetFileName) == gtest.lib
2. 把庫檔案所在目錄加入到庫搜尋路徑
現在確定了庫檔案路徑為:
$(SolutionDir)libs/
下面把它加入到專案的庫搜尋路徑,還是以TotalSTL專案為例,進行如下操作:專案屬性 - 配置屬性 - 連結器 - 常規 - 附加庫目錄
經過這兩個小步驟,就完成了庫檔案搜尋路徑的設定。其他的4個專案也按照TotalSTL這樣設定一下庫搜尋目錄也就完成了第二步,至此即可保證專案連結通過了。
調整專案生成順序
在設定完檔案包含目錄和庫檔案搜尋目錄之後,當我點選“生成解決方案”的時候,還是可能發生有些專案生成失敗的情況。在生成失敗之後,我什麼也不改,再點一次“生成解決方案”,第二次就生成成功了。這是為什麼呢?
這是因為專案生成順序問題造成的。我們知道,5個.exe專案依賴gtest和util這倆專案,如果在生成gtest和util之前,就開始生成其他專案,比如TotalSTL, 那麼當TotalSTL連結時,發現gtest.lib和util.lib還沒有生成,此時就生成失敗了。
而第二次點選生成的時候,此時,gtest和util在第一次生成時已經成功產生gtest.lib和util.lib,第二次生成時,TotalSTL等其他失敗的專案重新重試連結,這次找到了兩個.lib檔案,於是生成成功了。
怎麼能讓解決方案一次就生成成功呢?
這就需要調整專案的生成順序,很簡單,還是以TotalSTL為例,進行如下操作:專案屬性 - 通用屬性 - 引用 - 新增新引用
在彈出的列表中,選擇其依賴的專案。選擇gtest和util,確定即可。
其他四個.exe專案也做相應處理。設定完畢即可一次生成成功了。
管理專案的中間目錄和輸出目錄
在上文的解決方案物理路徑圖中,還有兩個資料夾:intermediate和output 值得介紹一下。
intermediate: 專案的中間目錄,生成過程中產生的一些中間檔案存放於此
output: 專案的輸出目錄,生成的結果檔案存放於此,比如TotalSTL.exe, TotalSTL.pdb, TotalSTL.ilk這些型別的檔案
設定這兩個目錄是為了方便所有專案統一管理,避免混亂。
下面是這兩個目錄的設定過程:專案屬性 - 配置屬性 - 常規 - 輸出目錄/中間目錄
輸出目錄的值為: $(SolutionDir)output/$(Configuration)/$(ProjectName)
中間目錄的值為:$(SolutionDir)intermediate/$(Configuration)/$(ProjectName)
注意其中環境變數的使用:$(SolutionDir)/intermediate
和 $(SolutionDir)output
分別定為到上面提到的兩個資料夾,然後按照編譯配置, 即$(Configuration)
(通常為Debug或者Release)來分目錄,最後以專案名稱來分目錄。
生成之後的目錄結構如下圖所示, 可以看到圖中路徑正是把$(Configuration)
(值為Debug), $(ProjectName)
(專案名字)代入之後的結果:
管理可執行檔案生成位置
上面在講到gtest和util這兩個專案的生成.lib的位置時,提到了改變專案的生成檔案位置。與之類似,其他5個生成.exe的專案,也可以做設定,使生成的.exe按照統一的目錄存放,方便查詢和管理。
以TotalSTL專案為例,具體操作如下:專案屬性 - 配置屬性 - 連結器 - 常規 - 輸出檔案
其值設定為:$(OutDir)$(TargetFileName)
注意到其中環境變數的使用,其中的$(OutDir)
就是上一小節提到的輸出目錄,其值剛才被設定為$(SolutionDir)output/$(Configuration)/$(ProjectName)
,把它代入到上面,展開為:
$(SolutionDir)output/$(Configuration)/$(ProjectName)$(TargetFileName)
生成之後的物理路徑結構為:
可以看到輸出的TotalSTL.exe的路徑正是”輸出目錄”,檔名TotalSTL.exe即$(TargetFileName)
。
管理工作目錄
工作目錄是程式執行時,搜尋資原始檔的路徑,具體設定在:專案屬性 - 配置屬性 - 除錯 - 工作目錄:
以TrainingGround專案為例:
其值為:$(SolutionDir)res/
, 即對應開篇解決方案圖中的res資料夾。
總結
本文展示瞭如何藉助Visual Studio的環境變數來組織一個VC++解決方案的工程目錄結構。提到了如何使用環境變數來編寫標頭檔案包含路徑、庫檔案搜尋路徑、中間目錄、輸出目錄、輸出檔案位置、工作目錄等。
解決方案程式碼地址:CS.cpp 1.0(visual-studio分支)
作者水平有限,對相關知識的理解和總結難免有錯誤,還望給予指正,非常感謝!