前往
大廳
主題

【程式】Visual C++的命令列工具

Shark | 2020-12-06 17:28:34 | 巴幣 124 | 人氣 1198

筆者的程式教學常用VC的命令列工具來編譯程式,寫一篇用法介紹。
筆者寫只有一個檔案的小程式時還是用命令列來build,寫小程式就要開IDE未免太費工,寫規模大一點的程式才用IDE。

不論Visual Studio、Code::Blocks或Eclipse,IDE實際上也是用這些命令列工具在build程式,IDE會把哪個原始碼檔案有更新、選項、還有依存的header與library整理好,產生命令列,然後執行命令列工具。

VC可以用免費的Express或Community版,先下載並安裝好再照本篇寫的操作。
寫這篇時去看了一下Visual Studio官網……,這裡可以下載最新版,但想下載舊版不是很容易。
https://visualstudio.microsoft.com/zh-hant/vs/community/



VC的命令列編譯器是cl.exe,但是直接打cl.exe會找不到這個檔案,即使找到檔案位置,用完整路徑執行它也會跳「找不到header或library」之類的錯誤,要動一些手續。

首先在Visual Studio的資料夾找到vcvars64.bat這個檔案。
如筆者的電腦上Visual Studio 2013的在這裡
C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin\amd64\vcvars64.bat
其他版本的話把12.0換成不同的數字。
amd64代表產生的exe檔是x86_64架構,在bin資料夾翻一下可以看到其他架構的,如果想build x86 32bit的程式要用這個。
C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin\vcvars32.bat

把vcvars64.bat複製到一個比較好找的地方,例如直接放在D:\下面。
在檔案總管「不要選取任何檔案或資料夾」,按「Shift+滑鼠右鍵」,Windows 7會出現「在此處開啟命令視窗」,Windows 8以後會出現「在這裡開啟 PowerShell 視窗」,如下圖。


選這一項。Windows 8以後由於開啟的是PowerShell,要打cmd、按Enter切換成傳統命令列。Windows 7不用做這一步。
打命令列執行剛剛的vcvars64.bat,看起來什麼都沒發生,實際上會設定必要的環境變數。
執行cl,如果出現「Microsoft (R) ...」的訊息就成功了,現在在任何資料夾下都可以執行VC的工具。
在Windows 10上操作的樣子:


用滑鼠直接點vcvars64.bat執行是無效的。而且vcvars64.bat只在這個命令列視窗有效,每次開新的命令列視窗都要重新執行一次vcvars64.bat。

附帶一提,Visual Studio產品名稱、內部版本號、還有VC版本號這三個是不一樣的
產品名稱 VS版本號 _MSC_VER
Visual Studio 2012 11.0 1700
Visual Studio 2013 12.0 1800
Visual Studio 2015 14.0 1900
Visual Studio 2017 15.0 1910~1916
Visual Studio 2019 16.0 1920~1928

_MSC_VER是Visual C自動產生的macro,如果要根據VC版本做不同處理,或是檢查目前編譯器是不是VC要用到它。
#ifdef _MSC_VER
  //Visual C
  #if _MSC_VER<=1800
    //VS2013以前
  #else
    //VS2015以後
  #endif
#else

  //Visual C以外的compiler
#endif
詳細說明看微軟官方的文件
Predefined macros



介紹筆者常用的工具:
cl.exe
主要的編譯器,基本用法:
cl 程式碼檔名 /Fe輸出檔名 優化選項 /MD /link 要連結的函式庫
/MD:連結C/C++標準函式庫時(如printf、malloc這些C語言教學書會介紹的函式),連結至DLL,不要靜態連結。建議寫任何程式都加上/MD,因為靜態連結會發生很多鳥事。
/link:之後的參數會原封不動傳給linker,一般是填要連結的函式庫,有需要也可以填其他參數。
/link之前的參數順序可互換。

例如「如何建一個視窗—Windows API篇」,程式碼是window.c,想要的輸出檔名是window.exe,要連結user32.lib,就這樣打
cl window.c /Fewindow.exe /O2 /MD /link user32.lib
除了exe以外還會同時產生window.obj,在這裡沒用處,可以刪除,但好像沒有方法叫它不產生。

C/C++編譯過程是「preprocessor做字串代換 → 產生組合語言 → 產生目的檔(obj) → linker產生可執行檔」,在「C/C++的static保留字」有寫到把途中的組合語言輸出的方法,如下,研究效能優化有時候需要看組合語言。
cl /c function_static.c /Fafunction_static.asm /O2 /MD
/c:只做到編譯成obj這一步,不要產生exe檔。因此不需要/link的參數。
/Fa:指定組合語言檔名。

這是官方的命令列參數說明,想看其他VC版本可按左邊的Version切換。
Compiler options listed by category
Linker options

nmake.exe
一個很古老的工具:make的VC版。如果覺得每次都重新打命令列很麻煩,可以把命令列存在一個檔案裡,以後用nmake自動執行。
開一個純文字檔寫以下內容,其中cl前面的空白是Tab字元(巴哈姆特不能顯示Tab字元,所以不要複製貼上,請自己按Tab鍵輸入),檔名存成makefile (不加附檔名),放在跟window.c同一資料夾。
window.exe: window.c
    cl window.c /Fewindow.exe /O2 /MD /link user32.lib
然後在命令列打nmake就會執行這一行指令。它會檢查程式碼有沒有變更過,方法是比較window.c和window.exe的修改時間,如果window.exe比較新就不重新編譯。

如果資料夾裡有很多.c或.cpp檔,一個一個打指令有點費事,有個方法自動化:把makefile寫成這樣
.c.exe :
    cl $< /Foa.obj /Fe$(@) /O2 /MD /link user32.lib
.cpp.exe :
    cl $< /Foa.obj /Fe$(@) /O2 /MD /link user32.lib
打「nmake exe檔名稱」就會把同名的.c或.cpp檔編譯成exe檔。
/Foa.obj是把產生的obj檔命名為a.obj,這樣obj檔只會有一個,沒有這個的話每個exe都會產生一個同名的obj檔,多出一堆無用的檔案。
這個語法是VC特有,GCC的make要用另一種寫法。

也可以像一般程式語言一樣用變數和判斷式,更詳細的用法請自己查makefile的語法,網路上GCC版的也可以參考,有些語法可以用在VC。
官方的nmake說明
自動化的用法是參考這兩篇
Predefined Rules
Filename Macros

dumpbin.exe
看obj、lib、exe或dll的資訊,「C/C++的static保留字」有用它看obj檔含有的符號。
dumpbin /symbols static_global2.obj

也可以看DLL有哪些exported function,下面是隨便找一個Windows內建DLL來看。
dumpbin /exports c:\windows\System32\user32.dll


執行vcvars64.bat也會把Windows SDK的工具加入PATH環境變數,變成各處都可以執行,例如VS2013的放在這裡
C:\Program Files (x86)\Windows Kits\8.1\bin\x64\
以下兩個是筆者比較常用的。
rc.exe
名稱是resource compiler的縮寫,跟Windows exe檔的特殊功能:資源(resource)有關。可以把icon、圖像、對話框配置、或任意資料嵌入exe或dll檔,之後可用LoadImage之類的函式讀取。
目前寫的文章還沒有用到資源的功能,以後寫到相關教學時再補上。

fxc.exe
寫Direct3D程式時用來編譯shader。一樣等寫到Direct3D教學時再補上。

創作回應

相關創作

更多創作