vs的環境變量


vs的環境變量

https://zhuanlan.zhihu.com/p/34203415

https://wiki.jikexueyuan.com/project/visual-studio

https://blog.csdn.net/elloop/article/details/51010151

https://blog.csdn.net/elloop/article/details/51019110

http://elloop.github.io/tools/2016-03-28/visual-studio-predefined-macros

http://elloop.github.io/tools/2016-03-29/using-vs-environmental-variable

 

本文總結了Visual Studio中常見的環境變量及其在組織解決方案、工程中的作用。

注:本文使用的是Visual Studio 2013,由於作者主要從事C/C++開發,所以是以Visual C++的工作環境配置來描述。

什么是vs的環境變量?

先看圖吧,圖中以美元符號$開頭 + 一對括號,這樣進行引用的就是我所謂的環境變量,

圖中出現的幾個環境變量含義如下:

環境變量名 含義
$(SolutionDir) 解決方案目錄:即.sln文件所在路徑
$(Configuration) 當前的編譯配置名稱,比如Debug,或Release
$(ProjectName) 當前項目名稱,圖中即為Game


在中文版的VS中,環境變量翻譯為“宏”,為了避免與C/C++語言中的宏(Macro)搞混,我在本文中把它叫做“vs環境變量”,簡稱環境變量。

環境變量有什么用?

使用環境變量來組織工程目錄

vs作為一個IDE,其天職在於幫開發者組織好工程,主要包括對工程中源文件、庫文件的組織。(本質上是提供一個可視化的操作界面,讓開發者方便的定義編譯器和鏈接器的參數。)在使用vs來組織工程目錄時候最常用到的兩個目錄是:

  • 頭文件包含目錄 (對應於編譯器命令的:-I 參數)
  • 庫文件搜索路徑 (對應於編譯器的:-l 參數)

vs中默認的頭文件搜索路徑是: 工程路徑 – 即,.vcxproj(不同版本的VC++后綴名稱不同,如vs2010中后綴為.vcproj) 文件所在路徑。比如如下的目錄結構:

–Root/
—-Test.vcxproj
—-hello.cpp
—-hello.h
—-world.cpp
—-world.h
—-main.cpp
—-/subdir
——sub.h

在Root目錄包含了.vcxproj文件,所以Root就是工程路徑,在vs中,這個目錄下面的.h文件可以直接使用include包含進來, 比如在main.cpp中我可以寫:

#include "hello.h"
#include "world.h"

但是對於sub.h,我們就不能直接寫#include "sub.h", 因為工程路徑下面不能搜索到這個文件,我要告訴編譯器這個文件在哪里,通常有以下兩種方法:

  • 寫成#include "subdir/sub.h"
  • 把subdir目錄加入到頭文件搜索路徑

Google的C++編程風格鼓勵第一種做法,好處是可以看到文件相對完整的路徑,如果頭文件搜索路徑只有一個根目錄,那么這個路徑就是文件的相對於根目錄的物理路徑,方便定位文件。

如果你覺得這樣寫很麻煩,並且路徑深度可能有多層,不同深度的路徑下又通常包含大量的文件,那么就可以選擇第二種做法,把每個子目錄統統加入到搜索路徑中,這樣,就可以不用帶着路徑,直接#include "filename.h"就可以了。具體在VS中要怎么合理的添加文件包含目錄呢?由此,便引出了本節問題的答案:環境變量有什么用?用途之一就是用來編寫頭文件的搜索路徑。

相信大家都知道如何在vs中添加一個頭文件搜索路徑這個常識,在此還是為初學者嘮叨一下具體做法:工程屬性 - 配置屬性 - C/C++ - 常規 - 編輯右側的”附加包含目錄”取值即可。

具體如下圖所示:

點擊編輯之后,彈出如下圖所示的編輯窗口:

在這里可以新建、刪除包含路徑、調整包含順序。

點擊新建按鈕或者雙擊列表空白處即可添加一條包含路徑,在編輯新添加的路徑時,可以看到列表條目右側有一個瀏覽按鈕,

點擊按鈕可以從打開文件對話框里選擇路徑,點擊確定后,會看到新添加的路徑名。雖然通過瀏覽來定位文件夾比較容易,免去了自己編寫,但是你會發現,通過瀏覽添加的路徑是絕對路徑

如果你是項目的唯一開發者,並且僅僅使用這一台電腦來開發的話,那么使用絕對路徑也沒什么大問題。但是如果這個項目是個團隊協作項目,或者你需要在好幾台電腦之間切換,那么這個包含路徑如果寫絕對路徑就不夠靈活了,如果別人的路徑配置或別的電腦的路徑配置不同,那么要重新修改包含路徑。

一個比較合理的編寫包含路徑的方法是: 使用相對路徑

相對誰呢? 相對項目根目錄或者解決方案根目錄。

為什么呢?因為不管別人的電腦有什么盤符、不管別人的項目放在何處,要包含的文件都可以通過項目所在位置來計算出來。

當然前提是,項目開發者們事先約定好被包含文件相對於項目根目錄的位置。通常是放在項目根目錄(或者解決方案根目錄)的某個子目錄里。

具體怎么做呢?這就需要用到本文的主題:環境變量了。剛才提到的兩個相對目錄所對應的環境變量如下表所示:

目錄 對應的環境變量名稱
項目根目錄 $(ProjectDir)
解決方案根目錄 $(SolutionDir)

要解決剛才小例子中的問題,

–Root/
—-Test.vcxproj
—-hello.cpp
—-hello.h
—-world.cpp
—-world.h
—-main.cpp
—-/subdir
——sub.h

注意到.vcxproj所在目錄即項目根目錄,也就是$(ProjectDir)的取值等於Root/。所以要把subdir放在包含目錄里,可以新建這樣一條包含路徑:

$(ProjectDir)subdir

這樣,在main.cpp里就可以直接寫#include "sub.h"了。不管項目被拷貝到哪里,都不用修改包含路徑。

上面就是環境變量使用的一個小例子。使用環境變量來編寫文件包含路徑的好處是: 包含路徑獨立於工程所在的路徑,無論工程被移動到哪里,都不需要重新修改包含路徑,因為使用環境變量來編寫的文件包含路徑是一種相對路徑。

其它vs環境變量

如何查看所有的環境變量值呢?

有好多個地方都可以查看,比如剛才在添加包含目錄時候,彈出的窗口,注意其右下方,有個“宏”按鈕

點擊它就能看到所有的“宏” (即vs環境變量的值):

在上方的輸入框可以進行過濾。

下面的表格給出了常用的環境變量的含義:

環境變量名 含義
$(SolutionDir) 解決方案目錄:即.sln文件所在路徑
$(ProjectDir) 項目根目錄:, 即.vcxproj文件所在路徑
$(Configuration) 當前的編譯配置名稱,比如Debug,或Release
$(ProjectName) 當前項目名稱
$(SolutionName) 解決方案名稱
$(OutDir) 項目輸出文件目錄
$(TargetDir) 項目輸出文件目錄
$(TargetName) 項目生成目標文件, 通常和$(ProjectName)同名, 如Game
$(TargetExt) 項目生成文件后綴名,如.exe, .lib具體取決於工程設置
$(TargetFileName) 項目輸出文件名字。比如Game.exe, 等於 $(TargetName) + $(TargetExt)
$(ProjectExt) 工程文件后綴名,如.vcxproj

 

 

本文將以實際例子說明如何合理使用這些環境變量來組織VC++工程。

使用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文件的應用程序,另外兩個gtestutil是服務於其他五個項目的,它倆生成的是.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  ( https://github.com/elloop/CS.cpp )1.0(visual-studio分支)

 

============= End

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM