2023年12月13日发(作者:)

nmake命令(windows下的makefile)

1. 如果已经有vc6的dsp工程,可直接导出nmake脚本文件(.mak)

“Project - ”

nmake -f CFG="nMakeTest - Win32 Debug"

nmake -f CFG="nMakeTest - Win32 Debug" all

nmake -f CFG="nMakeTest - Win32 Release" clean

注:如果未指定/F选项,则使用当前目录下的名为makefile的文件

【nmake /?】 获取更多帮助! vc6:【D:program filesMicrosoft Visual StudioVC98Bin】

vs2008:【D:program filesMicrosoft Visual Studio 9.0VCbin】

为了能正确地使用命令行工具及vc6或vs2008下的函数库,需要对一些环境变量进行设置,最快捷地方式是通过如下方式打开命令行窗口

(以vs2008为例):

2. vs的c++工程没有提供导出nmake脚本文件的功能,我们只有借助工具或手动编写nmake脚本文件了

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

3. 【将.rc资源文本转变成.res二进制文件】

所在路径:C:Program FilesMicrosoft

/l 0x804 // 默认语言ID(十六进制数表示) 0x804:简体中文 0x409:美国

/fo"" // 指定rc文件输出的res名称

例: /l 0x804 /fo"" /d "_DEBUG" /d "_AFXDLL" “”

4. 常见选项 【将.c,.cpp,.cxx编译成obj文件】

vs2008版本所在路径:D:Program Files (x86)Microsoft Visual Studio

命令行长度说明:

windows上整个命令行的长度不能超过260个字符(260 = 1+2+256+1 or [drive][:][path][null] ),问题详见,然而编译命令行往往会超过这

个限制,微软提供的解决方案是:将命令行参数写入一个response文件,然后将其传入编译器。

不过要注意的是:response文件仍然有32KB的限制。

如:"C:Program Files (x86)Microsoft Visual Studio "

@"C:UE4_15_0_se"

另外,从win10(version:1607)开始,可通过修改注册表来去除这个限制(将LongPathsEnabled改为1),详见

HKEY_LOCAL_MACHINESYSTEMCurrentControlSetControlFileSystem

注:可判断中是否提供了RtlAreLongPathsEnabled方法来判断当前系统是否支持长路径

/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优化了,它所带来的优化效果比起它所带来的

函数调用堆栈回溯的问题可以忽略。

/Oy- // 关闭帧指针省略【Frame pointer omission (FPO)】优化,更方便回溯堆栈

/W3  // 设置3级警告级别

/WX // 将Warining视为error

/wd4326 // 隐藏编译器警告C4326

/we4457 // 将编译器警告C4457视为错误

/Fp"" // 指定预编译文件名

/Yu"stdafx.h" // 在生成期间使用预编译头文件

预编译头文件技术,就是把一个工程(Project)中常用的一些头文件(如标准头文件Windows.h、Afxwin.h等,

也可以是自己定义的头文件)包含在stdafx.h中,并对载体编译单元(其中#include stdafx.h)预先编译(在所有的.cpp文件编译之前

进行编译),

得到编译结果.pch文件(默认名称为),后期该工程在编译其它.cpp文件时不再编译stdafx.h中的内容(即使include了它),仅

仅使用预编译的结果。

有2点需要注意:

1. 每个cpp的开头都必须#include "stdafx.h"

2. 由于预编译头文件编译很耗时,一旦改动就会引起整个工程重新编译,因此尽量把不易改动的代码包含其中

/FI "myheader.h" // 在每个源文件的第一行上的#include该文件

/Fd"vcpdb/testpdb" // 会将vc辅助编译的idb及pdb文件(见下面的/Gm选项)输入到vcpdb目录中,

并重命名为与(这里的pdb为project database文件,用于存工程的数据库信息)

/Fo"objFiles" // 将obj文件输出到objFiles目录中

/c   // 编译但不链接

/feMyTest // 编译后,输出可执行文件

/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   // 使用 创建单线程可执行文件

/MLd    // 使用 创建调试单线程可执行文件

/MT    // 使用 创建多线程可执行文件

/MTd    // 使用 创建调试多线程可执行文件 /MD    // 使用 / 创建多线程可执行文件

/MDd    // 使用 /创建调试多线程可执行文件

/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后指定的选项传递给

// 默认情况下,编译完后,会自动调用进行连接,

// 所以直接用编译带main函数的.c或.cpp后,会生成obj与exe文件。

例:cl /c // 编译,

例:cl *.cpp /MD /c /I"G:Visual C++VC98PlatformSDKInclude"

5. 常见选项 【将obj、lib、res链接成dll或exe等可执行文件】

vs2008版本所在路径:D:Program Files (x86)Microsoft Visual Studio

/dll  // 输出dll文件

-lib  // 生成lib静态库文件 例:link -lib *.obj /out:

/libpath:"..PublicSDKlib" // 指定外部lib查找路径(路径中不能带有空格,否则链接时会报LNK1181的错误)

/subsystem:windows[console] // 指定子系统

/machine 指定目标平台{AM33|ARM|EBC|IA64|M32R|MIPS|SH3|SH3DSP|SH4|SH5|THUMB|X86|X64},等

/NODEFAULTLIB: // 链接时,忽略库

/debug  // 生成调试信息

/export:myAdd=_Add,@1 // 导出extern "C" Add函数,并将符号名修改为myAdd,同时将导出序号设为1(一种dll动态库导出符号的方法)

/export:_g_isTest,@2 // 导出extern "C" g_isTest变量,并将导出序号设为2(一种dll动态库导出符号的方法)

/def:"" // 模块导出文件(另外一种dll动态库导出符号的方法)

注1:def文件名不要求与dll文件名或工程名一致

注2:一个dll工程最多只允许使用一个def文件

注3:vc:只需将def文件添加到工程中即可

注4:vs2008:项目 - 属性 - 连接器 - 输入 - 模块定义文件,填入def文件名(def文件要放在当前工程目录之下,def文件可不添加到工程中); 导出DLL函数

;作者:kekec

LIBRARY

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:"" // 重命名生成的pdb文件(Program Debug Database),保存调试符号等信息

/map:"" // 重命名生成的map文件

/out:"" // 重命名生成的exe文件

/implib:""  // 生成名为的导出库

/stack:0x300000 // 设置线程栈Reserve内存大小为3MB windows缺省是1MB -- 格式:/STACK:reserve[,commit]

/stack:0x500000,0x200000 // 设置线程栈Reserve内存大小为5MB,Commit内存大小为2MB windows缺省Reserve是1MB -- 格

式:/STACK:reserve[,commit]

/heap:0x300000,0x100000 // 设置默认堆Reserve内存大小为3MB,Commit内存大小为1MB windows缺省Reserve是1MB -- 格

式:/HEAP:reserve[,commit]

/entry:_DllMainCRTStartup@12 // 指定_DllMainCRTStartup函数dll的起始地址

/release // 生成checksum

/incremental:yes // 开启增量链接

incremental开关默认是开启的。

开启增量链接产生的exe或dll文件的size要大一些。

因为有代码和数据的填充,增量链接的exe或dll文件会包含跳转trunk来处理函数重定位到新地址。

MSDN上明确指出:为确保最终发布版本不包含填充或者trunk,请关闭增量链接。

例:link /subsystem:windows /out: // 生成名为的windows可执行程序

例:link /subsystem:console /out: *.obj // 生成名为的控制台可执行程序

例:link /subsytem:windows /dll /out: /implib: /def: *.obj // 生成名为动态库

例:link *.obj /LIBPATH:"G:Visual C++lib" /SUBSYSTEM:WINDOWS /MACHINE:X86

例:"D:/Program Files (x86)/Microsoft Visual Studio 9.0/VC/bin/" /MANIFEST:NO /NOLOGO /DEBUG /errorReport:prompt

/MACHINE:x86 /SUBSYSTEM:WINDOWS /FIXED:No /LARGEADDRESSAWARE /NXCOMPAT /STACK:5000000,5000000 /SAFESEH

/DEF: /DELAY:UNLOAD /RELEASE /OPT:REF /OPT:ICF /INCREMENTAL:NO /DELAYLOAD:""

/DELAYLOAD:"" /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:se"

/OUT:"g:" /IMPLIB:"g:"/PDB:"g:" /CLRTHREADATTRIBUTE:STA /FILEALIGN:0x1000

6. nmake指令说明

(1) 符号说明

# // 注释符(命令所在行不能使用注释符,命令应该与注释都独立使用一行进行书写;如:erase # 删除

文件【非法】)

^#abc // 表示#abc这个字符串

  // 连接符,用于将两行合并为一行;在宏中,分多行写时,一定要用""进行连接

%  // 文件说明符,表示其后的字符串为一文件名

---------------------

若文件名为 c:

%s 将为 c:

%:F 将为 c:

%:dF 将为 c

%:pF 将为 c:

%:fF 将为 prog

%:eF 将为 exe

---------------------

@  // 命令修饰符;防止修饰的命令的结果,被打印出来

! // 命令修饰符

  $  // 宏引用符

:  // 依赖符号

  ?【*】  // 通配符支持

++++++++++++++++++++++++++++++++++++

$@ // 表示所有目标全名(路径+文件名称+扩展名)的挨个值

$$@ // 与$@用法含义一致,但仅在作为依赖项中的依赖项时有效

$< // 表示所有依赖目标的挨个值,仅在推理规则的命令中有效

$^ // 表示所有依赖目标的集合,以空格分隔,若有重复,会被去重;

$+ // 与$^含义一致,只是不进行去重处理。

$? // 表示所有比目标心的依赖目标的集合,以空格分隔

$* // 当前目标的路径和文件名称,没有文件扩展名

$** // 当前目标的所有依赖项

----------------------------

修饰符 说明

D 驱动器和目录

B 文件名称

F 文件名称和扩展名

R 驱动器、目录和文件名称

----------------------------

(2) 长文件名用双引号引起来

  例:ALL :   // 文件名较短时,可不需要引号

  例:ALL : "$(OUTDIR)" // 文件名较长时,特别是路径中有空格的情况,一定要用引号

(3) 预定义规则

. // 默认操作:cl /c $*.c

也可对默认操作显示地重写:

.:

cl /c /Ox /DWIN32 $<

(4) 包含文件

!INCLUDE

include

(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命令

< :

  

(9) ALL / CLEAN

OUTDIR=.Release

INTDIR=.Release

ALL : "(OUTDIR)" CLEAN :   -@erase "(INTDIR)"

  -@erase "(INTDIR)"   -@erase "(INTDIR)"

  -@erase "(INTDIR)"   -@erase "(INTDIR)"

  -@erase "(INTDIR)"   -@erase "(OUTDIR)"

  -@erase "(OUTDIR)" "(OUTDIR)" :

  if not exist "(OUTDIR)/(NULL)" mkdir "$(OUTDIR)"

(10) 编译

CPP=

CPP_PROJ=/nologo /MD /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_AFXDLL" /D "_MBCS"

/Fp"(INTDIR)" /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=

LINK32_FLAGS=/nologo /subsystem:windows /incremental:no /pdb:"(OUTDIR)"

/map:"(INTDIR)" /machine:I386 /out:"(OUTDIR)" LINK32_OBJS=  

 "(INTDIR)"

  "(INTDIR)"   "(INTDIR)"   "(INTDIR)" "(OUTDIR)" : "(OUTDIR)" (DEF_FILE) (LINK32_OBJS)    (LINK32) @<<

  (LINK32_FLAGS) (LINK32_OBJS)

<<

(12) 文件依赖

SOURCE=.

"(INTDIR)" : (SOURCE) "(INTDIR)" "(INTDIR)"

SOURCE=.

"(INTDIR)" : (SOURCE) "(INTDIR)" (RSC) (RSC_PROJ) (SOURCE)

(13) 预编译文件

SOURCE=.

!IF "(CFG)" == "nMakeTest - Win32 Release" CPP_SWITCHES=/nologo /MD /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D

"_WINDOWS" /D "_AFXDLL" /D "_MBCS" /Fp"(INTDIR)" /Yc"stdafx.h" /Fo"(INTDIR)" /Fd"(INTDIR)" /FD /c

"(INTDIR)" "(INTDIR)" : (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)" /Yc"stdafx.h" /Fo"(INTDIR)" /Fd"(INTDIR)" /FD /GZ /c "(INTDIR)"

"(INTDIR)" : (SOURCE) "(INTDIR)"   (CPP) @<<

 (CPP_SWITCHES) (SOURCE)

<<

!ENDIF

# 参考

4.

Processing math: 0%