1. 如果已經有vc6的dsp工程,可直接導出nmake腳本文件(.mak)
“Project - Export Makefile...”
nmake -f nMakeTest.mak CFG="nMakeTest - Win32 Debug"
nmake -f nMakeTest.mak CFG="nMakeTest - Win32 Debug" all
nmake -f nMakeTest.mak CFG="nMakeTest - Win32 Release" clean
注:如果未指定/F選項,則使用當前目錄下的名為makefile的文件
【nmake /?】 獲取更多幫助! vc6:【D:\program files\Microsoft Visual Studio\VC98\Bin】
vs2008:【D:\program files\Microsoft Visual Studio 9.0\VC\bin】
為了能正確地使用命令行工具及vc6或vs2008下的函數庫,需要對一些環境變量進行設置,最快捷地方式是通過如下方式打開命令行窗口(以vs2008為例):
2. vs的c++工程沒有提供導出nmake腳本文件的功能,我們只有借助工具或手動編寫nmake腳本文件了
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3. rc.exe 【將.rc資源文本轉變成.res二進制文件】
所在路徑:C:\Program Files\Microsoft SDKs\Windows\v6.0A\Bin\rc.exe
/l 0x804 // 默認語言ID(十六進制數表示) 0x804:簡體中文 0x409:美國 更多...
/fo"nMakeTest.res" // 指定rc文件輸出的res名稱
例:rc.exe /l 0x804 /fo"nMakeTest.res" /d "_DEBUG" /d "_AFXDLL" “nMakeTest.rc”
4. cl.exe 常見選項 【將.c,.cpp,.cxx編譯成obj文件】 更多...
vs2008版本所在路徑:D:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\bin\cl.exe
命令行長度說明:
windows上整個命令行的長度不能超過260個字符,然而編譯命令行往往會超過這個限制,微軟提供的解決方案是:將命令行參數寫入一個response文件,然后將其傳入編譯器。
不過要注意的是:response文件仍然有32KB的限制。
如:"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\amd64\cl.exe" @"C:\UE4_15_0_Release\Build\Win64\PhysXVehicles.generated.cpp.obj.response"
/nologo // 不打印版權申明信息
/I "../include" // 添加頭文件查找路徑(如果路徑中帶有空格,一定要用引號括起來)
/DWIN32 // 預編譯宏定義(win32程序)
/D_CONSOLE // 預編譯宏定義(控制台程序)
/D "_DEBUG" // 預編譯宏定義(Debug版本)
/D_CRT_SECURE_NO_DEPRECATE // 預編譯宏定義(關閉C4996警告。使用strcpy、strcat等不安全函數時會報C4996警告)
/D_CRT_NONSTDC_NO_DEPRECATE // 預編譯宏定義(關閉C4996警告。使用strcpy、strcat等不安全函數時會報C4996警告)
/DLIB_VER=0x2000 // 預編譯自定義宏LIB_VER為0x2000 等價於#define LIB_VER 0x2000
/Od // 優化選項:帶入Debug信息
/O2 // 優化選項:最快速度
/O1 // 優化選項:最小尺寸
/Oy // 啟用幀指針省略【Frame pointer omission (FPO)】優化
// FPO是一種二進制程序代碼優化方法,它壓縮或者省略了在棧上為該函數創建框架指針的過程。這個選項加速了函數調用,因為不需要建立和移除框架指針(ESP,EBP)了。
// 同時,它還解放出了EBP寄存器,用來存儲使用頻率較高的變量。只在Intel CPU的架構上才有這種優化。
// 在Vista之前的os中,開啟編譯優化就會默認進行FPO優化,在Vista以后默認就沒有FPO優化了,它所帶來的優化效果比起它所帶來的函數調用堆棧回溯的問題可以忽略。
/W3 // 設置3級警告級別
/WX // 將Warining視為error
/wd4326 // 隱藏編譯器警告C4326
/we4457 // 將編譯器警告C4457視為錯誤
/Fp"nMakeTest.pch" // 指定預編譯文件名
/Yu"stdafx.h" // 在生成期間使用預編譯頭文件
預編譯頭文件技術,就是把一個工程(Project)中常用的一些頭文件(如標准頭文件Windows.h、Afxwin.h等,
也可以是自己定義的頭文件)包含在stdafx.h中,並對stdafx.cpp載體編譯單元(其中#include stdafx.h)預先編譯(在所有的.cpp文件編譯之前進行編譯),
得到編譯結果.pch文件(默認名稱為ProjectName.pch),后期該工程在編譯其它.cpp文件時不再編譯stdafx.h中的內容(即使include了它),僅僅使用預編譯的結果。
有2點需要注意:
1. 每個cpp的開頭都必須#include "stdafx.h"
2. 由於預編譯頭文件編譯很耗時,一旦改動就會引起整個工程重新編譯,因此盡量把不易改動的代碼包含其中
/FI "myheader.h" // 在每個源文件的第一行上的#include該文件
/Fd"vcpdb/testpdb" // 會將vc輔助編譯的idb及pdb文件(見下面的/Gm選項)輸入到vcpdb目錄中,
並重命名為testpdb.idb與testpdb.pdb(這里的pdb為project database文件,用於存工程的數據庫信息)
/Fo"objFiles" // 將obj文件輸出到objFiles目錄中
/c // 編譯但不鏈接
/feMyTest // 編譯后,輸出MyTest.exe可執行文件
/EHsc // 啟用"C++異常(Exceptions)"的stack unwind,編譯器會插入一些代碼保證發生異常后所有try塊中的對象都能調用析構函數來做清理工作
/EHa // 該參數能讓C++ try catch,不但能捕捉標准的C++的異常,還能用catch(...)捕獲SEH的異常 如:整數除0,access violation (AV)等
注:將"Enable C++ Exceptions"設置為No,並不是在代碼中不能使用c++異常。
c++異常拋出能夠正確地被catch捕獲,只是沒有了stack unwind,try塊中對象不會調用析構函數,可能會引發泄漏
/LD // 創建動態鏈接庫
/LDd // 創建調試動態鏈接庫
/ML // 使用 libc.lib 創建單線程可執行文件
/MLd // 使用 libcd.lib 創建調試單線程可執行文件
/MT // 使用 libcmt.lib 創建多線程可執行文件
/MTd // 使用 libcmtd.lib 創建調試多線程可執行文件
/MD // 使用 msvcrt.lib/msvcrt.dll 創建多線程可執行文件
/MDd // 使用 msvcrtd.lib/msvcrtd.dll創建調試多線程可執行文件
/Z7 //生成與 C7.0兼容的調試信息
/Zd //生成行號
/Zi //生成完整的調試信息
/Za // 禁用語言擴展(不支持ANSI C89 及 C++11) --使用該參數時,編譯器會自動定義__STDC__宏
/Ze // 啟用語言擴展(默認)
/Zl // ZL 非ZI; 告訴編譯器從.obj文件中移除默認C庫鏈接指令(注:該鏈接指令會被傳給連接器 形如:/DEFAULTLIB:'LIBCMT')
/Gm // 啟用最小重新生成
編譯器在.idb文件中存儲源文件和類定義之間的依賴關系。
使用.idb 文件的信息來確定是否需要編譯某個源文件。
而不是該源文件只要包含了被修改的.h文件,就必須重新編譯。
/GR // 啟用運行時類型識別信息(RTTI,Run-Time Type Information) 編譯器會在exe或dll文件中增加一個.rdata的段來存放變量的類型信息
例:int a; bool b = (typeid(a)==typeid(int)); // b為true
/GR- // 關閉運行時類型識別信息,exe或dll會更小
/Zp8 // 結構體、類等復合類型的數據成員以8字節進行內存對齊
/bigobj // 提高obj文件中Section數的上限 65,536 (2 ^16) --> 4294967296(2 ^32) 注:vs2005及以上的編譯器才支持
/RTCs // 啟用棧幀運行時錯誤檢查。主要體現在:1. 當使用的變量尚未初始化時進行報告 2.檢測局部變量(如數組)的溢出和不足 3. 檢測ESP損壞,如檢測調用約定不匹配可能導致ESP損壞
/Ob2 // 啟用函數內聯
/link // 將/link后指定的選項傳遞給link.exe
// 默認情況下,cl.exe編譯完后,會自動調用link.exe進行連接,
// 所以直接用cl.exe編譯帶main函數的.c或.cpp后,會生成obj與exe文件。
例:cl /c test1.cpp test2.cpp // 編譯test1.cpp,test2.cpp
例:cl *.cpp /MD /c /I"G:\Visual C++\VC98\PlatformSDK\Include"
5. link.exe常見選項 【將obj、lib、res鏈接成dll或exe等可執行文件】
vs2008版本所在路徑:D:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\bin\link.exe
/dll // 輸出dll文件
-lib // 生成lib靜態庫文件 例:link -lib *.obj /out:test.lib
/libpath:"..\PublicSDK\lib" // 指定外部lib查找路徑(路徑中不能帶有空格,否則鏈接時會報LNK1181的錯誤)
/subsystem:windows[console] // 指定子系統
/machine 指定目標平台{AM33|ARM|EBC|IA64|M32R|MIPS|SH3|SH3DSP|SH4|SH5|THUMB|X86|X64},等
/NODEFAULTLIB:libcd.lib // 鏈接時,忽略libcd.lib庫
/debug // 生成調試信息
/export:myAdd=_Add,@1 // 導出extern "C" Add函數,並將符號名修改為myAdd,同時將導出序號設為1(一種dll動態庫導出符號的方法)
/export:_g_isTest,@2 // 導出extern "C" g_isTest變量,並將導出序號設為2(一種dll動態庫導出符號的方法)
/def:"nMakeTest.def" // 模塊導出文件(另外一種dll動態庫導出符號的方法)
注1:def文件名不要求與dll文件名或工程名一致
注2:一個dll工程最多只允許使用一個def文件
注3:vc:只需將def文件添加到工程中即可
注4:vs2008:項目 - 屬性 - 連接器 - 輸入 - 模塊定義文件,填入def文件名(def文件要放在當前工程目錄之下,def文件可不添加到工程中)
;nMakeTest.lib 導出DLL函數
;作者:kekec
LIBRARY nMakeTest.def
EXPORTS
Add @ 1
g_isTest @ 2
注:還可以在代碼中使用__declspec(dllexport)進行符號的導出
#ifdef WIN32DLL_EXPORTS
#define WIN32DLL_API __declspec(dllexport)
#else
#define WIN32DLL_API __declspec(dllimport)
#endif
/************** export.c ***************/
#ifdef __cplusplus
extern "C"
{
#endif
WIN32DLL_API int __stdcall Add(int a, int b)
{
return (a + b);
}
WIN32DLL_API int g_isTest = 0;
#ifdef __cplusplus
}
#endif
/pdb:"nMakeTest.pdb" // 重命名生成的pdb文件(Program Debug Database),保存調試符號等信息
/map:"nMakeTest.map" // 重命名生成的map文件
/out:"nMakeTest.exe" // 重命名生成的exe文件
/implib:"test.lib" // 生成名為test.lib的導出庫
/stack:0x300000 // 設置線程棧Reserve內存大小為3MB windows缺省是1MB -- 格式:/STACK:reserve[,commit]
/stack:0x500000,0x200000 // 設置線程棧Reserve內存大小為5MB,Commit內存大小為2MB windows缺省是1MB -- 格式:/STACK:reserve[,commit]
/entry:_DllMainCRTStartup@12 // 指定_DllMainCRTStartup函數dll的起始地址
/release // 生成checksum
/incremental:yes // 開啟增量鏈接
incremental開關默認是開啟的。
開啟增量鏈接產生的exe或dll文件的size要大一些。
因為有代碼和數據的填充,增量鏈接的exe或dll文件會包含跳轉trunk來處理函數重定位到新地址。
MSDN上明確指出:為確保最終發布版本不包含填充或者trunk,請關閉增量鏈接。
例:link gdiplus.lib /subsystem:windows /out:test.exe file1.obj file2.lib file3.res // 生成名為test.exe的windows可執行程序
例:link gdiplus.lib /subsystem:console /out:test.exe *.obj file2.lib file3.res // 生成名為test.exe的控制台可執行程序
例:link gdiplus.lib /subsytem:windows /dll /out:test.dll /implib:test.lib /def:test.def *.obj file2.lib file3.res // 生成名為test.dll動態庫
例:link *.obj rc.res /LIBPATH:"G:\Visual C++\lib" /SUBSYSTEM:WINDOWS /MACHINE:X86 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib OpenGL32.Lib
例:"D:/Program Files (x86)/Microsoft Visual Studio 9.0/VC/bin/link.exe" /MANIFEST:NO /NOLOGO /DEBUG /errorReport:prompt /MACHINE:x86 /SUBSYSTEM:WINDOWS /FIXED:No /LARGEADDRESSAWARE /NXCOMPAT /STACK:5000000,5000000 /SAFESEH /DEF:UnrealEngine3.def /DELAY:UNLOAD /RELEASE /OPT:REF /OPT:ICF /INCREMENTAL:NO /DELAYLOAD:"dxgi.dll" /DELAYLOAD:"d3d10.dll" /LIBPATH:"../External/zlib/Lib" /LIBPATH:"D:\Program Files (x86)\Microsoft DirectX SDK (June 2010)\/Lib/x86" /NODEFAULTLIB:"MSVCRTD" /NODEFAULTLIB:"MSVCPRTD" /NODEFAULTLIB:"LIBC" /NODEFAULTLIB:"LIBCMT" /NODEFAULTLIB:"LIBCPMT" /NODEFAULTLIB:"LIBCP" /NODEFAULTLIB:"LIBCD" /NODEFAULTLIB:"LIBCMTD" /NODEFAULTLIB:"LIBCPMTD" /NODEFAULTLIB:"LIBCPD" @"g:\svn\MyGame\Binaries\Win32\MyGameRelease.exe.response" /OUT:"g:\svn\MyGame\Binaries\Win32\MyGameRelease.exe" /IMPLIB:"g:\svn\MyGame\Binaries\Win32\MyGameRelease.lib" /PDB:"g:\svn\MyGame\Binaries\Win32\MyGameRelease.pdb" /CLRTHREADATTRIBUTE:STA /FILEALIGN:0x1000
6. nmake指令說明
(1) 符號說明
# // 注釋符(命令所在行不能使用注釋符,命令應該與注釋都獨立使用一行進行書寫;如:erase nMakeTest.obj # 刪除nMakeTest.obj文件【非法】)
^#abc // 表示#abc這個字符串
\ // 連接符,用於將兩行合並為一行;在宏中,分多行寫時,一定要用"\"進行連接
% // 文件說明符,表示其后的字符串為一文件名
---------------------
若文件名為 c:\prog.exe
%s 將為 c:\prog.exe
%:F 將為 c:\prog.exe
%:dF 將為 c
%:pF 將為 c:\
%:fF 將為 prog
%:eF 將為 exe
---------------------
@ // 命令修飾符;防止修飾的命令的結果,被打印出來
! // 命令修飾符
$ // 宏引用符
: // 依賴符號
?【*】 // 通配符支持
++++++++++++++++++++++++++++++++++++
$@ // 表示所有目標全名(路徑+文件名稱+擴展名)的挨個值
$$@ // 與$@用法含義一致,但僅在作為依賴項中的依賴項時有效
$< // 表示所有依賴目標的挨個值,僅在推理規則的命令中有效
$^ // 表示所有依賴目標的集合,以空格分隔,若有重復,會被去重;
$+ // 與$^含義一致,只是不進行去重處理。
$? // 表示所有比目標心的依賴目標的集合,以空格分隔
$* // 當前目標的路徑和文件名稱,沒有文件擴展名
$** // 當前目標的所有依賴項
----------------------------
修飾符 說明
D 驅動器和目錄
B 文件名稱
F 文件名稱和擴展名
R 驅動器、目錄和文件名稱
----------------------------
(2) 長文件名用雙引號引起來
例:ALL : nMakeTest.dll // 文件名較短時,可不需要引號
例:ALL : "$(OUTDIR)\nMakeTest.exe" // 文件名較長時,特別是路徑中有空格的情況,一定要用引號
(3) 預定義規則
.c.obj // 默認操作:cl /c $*.c
也可對默認操作顯示地重寫:
.c.obj:
cl /c /Ox /DWIN32 $<
(4) 包含文件
!INCLUDE nmake.opt
include makefile.mak
(5) 條件判斷 - 01
!IF "$(CFG)" == ""
CFG=nMakeTest - Win32 Debug
!MESSAGE No configuration specified. Defaulting to nMakeTest - Win32 Debug.
!ELSE
!MESSAGE Be specified.
!ENDIF
(6) 條件判斷 - 02 【!IFNDEF !IFDEF】
!IFNDEF PRIVATE_RUNTIMEMODE_DEBUG
RUNTIMEMODE_DEBUG = /MDd
!ELSE
RUNTIMEMODE_DEBUG = $(PRIVATE_RUNTIMEMODE_DEBUG)
!ENDIF
(7) 輸出消息日志
!MESSAGE Invalid configuration "$(CFG)" specified.
(8) 描述塊 - makefile的核心 【注:在依賴項(或規則)和命令塊之間不能出現空行,commands之前為一個tab字符,多條command之間用;分割】
只要dependences中任意一個文件比targets新,就執行commands命令
targets... : dependences...
commands...
(9) ALL / CLEAN
OUTDIR=.\Release
INTDIR=.\Release
ALL : "$(OUTDIR)\nMakeTest.exe"
CLEAN :
-@erase "$(INTDIR)\nMakeTest.obj"
-@erase "$(INTDIR)\nMakeTest.pch"
-@erase "$(INTDIR)\nMakeTest.res"
-@erase "$(INTDIR)\nMakeTestDlg.obj"
-@erase "$(INTDIR)\StdAfx.obj"
-@erase "$(INTDIR)\vc60.idb"
-@erase "$(OUTDIR)\nMakeTest.exe"
-@erase "$(OUTDIR)\nMakeTest.map"
"$(OUTDIR)" :
if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
(10) 編譯
CPP=cl.exe
CPP_PROJ=/nologo /MD /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_AFXDLL" /D "_MBCS" /Fp"$(INTDIR)\nMakeTest.pch" /Yu"stdafx.h" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c
.c{$(INTDIR)}.obj::
$(CPP) @<<
$(CPP_PROJ) $<
<<
.cpp{$(INTDIR)}.obj::
$(CPP) @<<
$(CPP_PROJ) $<
<<
.cxx{$(INTDIR)}.obj::
$(CPP) @<<
$(CPP_PROJ) $<
<<
(11) 鏈接
LINK32=link.exe
LINK32_FLAGS=/nologo /subsystem:windows /incremental:no /pdb:"$(OUTDIR)\nMakeTest.pdb" /map:"$(INTDIR)\nMakeTest.map" /machine:I386 /out:"$(OUTDIR)\nMakeTest.exe"
LINK32_OBJS= \
"$(INTDIR)\nMakeTest.obj" \
"$(INTDIR)\nMakeTestDlg.obj" \
"$(INTDIR)\StdAfx.obj" \
"$(INTDIR)\nMakeTest.res"
"$(OUTDIR)\nMakeTest.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
$(LINK32) @<<
$(LINK32_FLAGS) $(LINK32_OBJS)
<<
(12) 文件依賴
SOURCE=.\nMakeTest.cpp
"$(INTDIR)\nMakeTest.obj" : $(SOURCE) "$(INTDIR)" "$(INTDIR)\nMakeTest.pch"
SOURCE=.\nMakeTest.rc
"$(INTDIR)\nMakeTest.res" : $(SOURCE) "$(INTDIR)"
$(RSC) $(RSC_PROJ) $(SOURCE)
(13) 預編譯文件
SOURCE=.\StdAfx.cpp
!IF "$(CFG)" == "nMakeTest - Win32 Release"
CPP_SWITCHES=/nologo /MD /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_AFXDLL" /D "_MBCS" /Fp"$(INTDIR)\nMakeTest.pch" /Yc"stdafx.h" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c
"$(INTDIR)\StdAfx.obj" "$(INTDIR)\nMakeTest.pch" : $(SOURCE) "$(INTDIR)"
$(CPP) @<<
$(CPP_SWITCHES) $(SOURCE)
<<
!ELSEIF "$(CFG)" == "nMakeTest - Win32 Debug"
CPP_SWITCHES=/nologo /MDd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_AFXDLL" /D "_MBCS" /Fp"$(INTDIR)\nMakeTest.pch" /Yc"stdafx.h" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /GZ /c
"$(INTDIR)\StdAfx.obj" "$(INTDIR)\nMakeTest.pch" : $(SOURCE) "$(INTDIR)"
$(CPP) @<<
$(CPP_SWITCHES) $(SOURCE)
<<
!ENDIF
# 參考
1. http://msdn.microsoft.com/zh-cn/library/dd9y37ha(v=vs.80).aspx
2. http://c2.com/cgi/wiki?UsingNmake
3. http://zh.wikipedia.org/wiki/Make
5. http://blog.codingnow.com/2008/09/replacement_of_ide_1.html
6. http://blog.codingnow.com/2008/09/replacement_of_ide_2
7. http://blog.codingnow.com/2008/09/replacement_of_ide_3
8. http://blog.codingnow.com/2008/09/replacement_of_ide_4