1.Dos下运行VC++自带的Nmake,设置路径
==========================================
●C/C++ 编译器需要的环境变数设定
古早以来,PC 上的 C 编译器,就需要两个环境变数:
LIB:这个环境变数告诉编译器说,必要的 libraries 在哪里(哪个磁碟目录下)
INCLUDE:告诉编译器说,必要的 header files 在哪里(哪个磁碟目录下)
另外,为了让我们能够在任何 working directory 都叫得到编译器,当然我们必须设定 PATH。
从古早以来,一直到现在,C/C++ 编译器都需要这三个环境变数。
●以 Visual C++ 为例
以 Visual C++ 为例,如果安装后的档案布局如下:
C:/MSDEV/VC98/BIN : 这里放有编译器 CL.EXE
C:/MSDEV/VC98/INCLUDE : 这里放有 C/C++ header files
C:/MSDEV/VC98/LIB : 这里放有 C/C++ standard libraries
那么你可以写一个批次档如下:
set PATH=C:/MSDEV/VC98/BIN;C:/MSDEV/COMMON/MSDEV98/BIN
set INCLUDE=C:/MSDEV/VC98/INCLUDE
set LIB=C:/MSDEV/VC98/LIB
之所以需要另外设定 PATH=C:/MSDEV/COMMON/MSDEV98/BIN,是因为编译器 CL.EXE 执行时需要 MSPDB60.DLL,而它被安装于 C:/MSDEV/COMMON/MSDEV98/BIN 之中。
如果你写的程式不只是单纯的 C/C++ 程式,还用到了 MFC,一样可以在 console mode 下编译,这时候你的环境变数应该如此设定:
set PATH=C:/MSDEV/VC98/BIN;C:/MSDEV/COMMON/MSDEV98/BIN
set INCLUDE=C:/MSDEV/VC98/INCLUDE;C:/MSDEV/VC98/MFC/INCLUDE
set LIB=C:/MSDEV/VC98/LIB;C:/MSDEV/VC98/MFC/LIB
多指定了 MFC/INCLUDE 和 MFC/LIB,就可以让编译器和联结器找到 MFC 的 header files 和 libraries。如果你还需要用到 ATL,就得在 INCLUDE 环境变数中再加上 C:/MSDEV/VC98/ATL/INCLUDE。
=========================================
我的VC++安装在D:/Program Files/Microsoft Visual Studio下,所以改写批次档如下:
set PATH=D:/Program Files/Microsoft Visual Studio/VC98/Bin;D:/Program Files/Microsoft Visual Studio/Common/MSDev98/Bin
set INCLUDE=D:/Program Files/Microsoft Visual Studio/VC98/Include;D:/Program Files/Microsoft Visual Studio/VC98/MFC/Include
set LIB=D:/Program Files/Microsoft Visual Studio/VC98/Lib;D:/Program Files/Microsoft Visual Studio/VC98/MFC/Lib
然后运行cmd,将以上设置复制粘贴到鼠标闪烁处。如果想要确认路径更改正确,可以键入set命令查看。
注:这样的环境变量修改,仅对本次命令行窗口有效,因为它是一个虚拟设备。如果想要每次进入时,不做这个工作。可以运行VCVARS32.BAT然后设置你的环境变量。为了不影响VC++的原本设置方便集成环境的使用,我并没有实际操作,一个简单的复制粘贴也不见得麻烦。另外还可以在我的电脑-属性-高级-环境变量里直接修改,这个修改也是永久性的。到这里,路径就设置好了。下面试操作一下:
我在F:/盘保存了一个test.cpp文件作为测试文件。文件内容如下:
=======================================
#include <iostream.h>
void main()
{
cout<<"hello"<<endl;
}
=========================================
使用cd命令把当前命令行窗口路径切换到F:/>,然后执行cl test.cpp命令,在F:/盘路径下生成了两个文件,test.obj和test.exe。然后再运行test.exe,就可以看到结果了(输出hello)。
下面再举一个例子,也就是下面要学习的makefile文件。测试文件名为mypath.mak(你可以任意取名),依旧存储在当前路径下。
===========================
all:
@echo $(PATH)
==========================
运行命令nmake mypath.mak,你可以看到输出结果为你刚才设置过的路径(我的结果是D:/Program Files/Microsoft Visual Studio/VC98/Bin;D:/Program Files/Microsoft Visual Studio/Common/MSDev98/Bin)。
2.产生自己的makefile文件
下面是我在网上找到的一个简单的例子及其解说
==================================
下面来看一个简单的例子(以下内容均以Win32平台为例):
文件名:makefile
1. # makefile
2. # this is a example of make file
3. all:a1 a2
4. @echo this is all!
5. a1:
6. @echo this is a1!
7. a2:
8. @echo this is a2!
运行make后,结果如下:
this is a1!
this is a2!
this is all!
现在让我们来分析一下这个简单的规则文件。
第1、2行不用说,一眼就可以看出是注释。在Make规则文件中,注释是以“#”开始,是行注释,和C++中的“//”功能一样。不过你可不能把它放到其它的语句之后,否则就错了。第3行就是规则开始了!all:a1 a2一行中,规则的名字就是all,它通常是目标名(target)。一条规则可以有不止一个名字,像这一行,你也可以把它写成all all2:a1 a2。这时,规则就有了两个名称—all和all2。当然,还可以有更多,都看你自己。后面的5、7两行也分别是两条规则的起始。在“:”之后的,就是依赖项。在这一行里,依赖项有两个,分别是a1和a2。这些依赖项可以是其它的规则名(目标名),也可以是文件名。依赖和目标之间的关系就是“依赖关系”。一条规则中,可以有零个(像后面的两条规则)、一个或多个依赖。第4行@echo this is all!是命令行。它是执行all规则时要执行的命令。要注意的是,一条规则内的命令要以tab为一行的起始,以表示命令是属于一个规则。一条规则也可以有多条命令,每条命令占一行(要以tab开头)。至于可以使用哪些命令,这完全取决于你使用的OS和SHELL。
当执行make时,它会找到第一条规则。然后,make就会检查依赖和目标之间的关系。如果目标比依赖旧,就执行规则,以更新目标。执行完规则就结束。如何判定目标和依赖的新旧呢?如果目标(文件)不存在,目标的时间就为0;如果目标(文件存在),目标的时间就为文件的修改时间。如果依赖项是一条规则,就执行依赖的规则(这里是一个递归),然后依赖的时间就是当前最新时间;如果是一个存在的文件,就为文件的修改时间,否则就报错。之后,就可以比较目标和依赖之间的关系。不过,有一点特殊的是,在没有依赖项时,依赖的时间为1。
在这个例子中,make先找到规则“all”,发现目标不存在,所以目标的时间为0;然后在查找依赖“a1”,结果“a1”不存在;于是,执行规则“a1”。“a1”不存在,所以它的时间为0,而“a1”没有依赖,它的依赖时间为1;1>0,所以,执行规则“a1”。然后返回规则“all”,再检查依赖“a2”。“a2”执行过程同“a1”。这时,“all”的目标时间为0,依赖时间为最新时间。于是,执行规则“all”的命令。
当然,大家也可以指定一条规则让make执行,比如:make a1这个命令就是告诉make程序不去找第一条规则,而是规则“a1”来执行。并且我们还可以一次执行多条规则,比如:执行make a1 a2就会连续执行“a1”、“a2”两条规则。
OK,虽然讲得很混乱,但也费了我半天的力气。大家应该有一点了解make规则的执行过程了吧。
==============================================
依赖的基本写法如下:
Target:Dependence
Command
==============================================
Target可以是文件名。Dependence可以是其它的target名或文件名。Command就是操作系统所运行的命令行。
变量
一个make规则文件有这些内容就已经基本可以工作了。可是,当我们在编译一个程序时,如果有些内容要反复用到,每次都要写一长串的话,是很麻烦的。于是,make就引入了宏这个概念(其实也可以看成简单的脚本语言)。
宏变量的定义如下:
var1 = this is a macro demo!
var1就是变量名,它的值就是“this is a macro demo!”
如果我们要使用这个变量的值,那只有通过$这个运算符才行—$(var1)代表的就是“this is a macro demo!”。
如下makefile
1. var1 = this is a macro demo!
2. all:
3. @echo $(var1)
结果输出:
this is a macro demo!
用户在执行命令行时也可以定义宏变量。其形式如下:
make all var1=”this is a test”
执行结果为:
this is a test
我们不仅可以使用自定义变量,还可以通过这种方式使用系统环境变量。这样可以大大方便我们建议灵活的规则。如下makefile
1. all:
2. @echo $(WINDIR)
执行结果为:
C:/WINDOWS
(注意:makefile中的变量是大小写敏感的)
==========================================
下面介绍makefile文件的一些内置变量:
==========================================
$@代表规则中的目标名(也就是规则名)。
$<代表规则中的依赖项目。注意,它只代表规则所有依赖项目中的第一项!
其它还有:
$^代表规则中所有的依赖项目。
$?代表规则中时间新于目标的依赖项目。
不仅如此,还可以给这些特殊的变量加一些限制。
如:
在规则
debug/out.exe : out.obj
中,$@代表debug/out.exe,而$(@D)代表目录名debug,$(@F)代表文件名out.exe。其它如$(<D)、$(<F)、$(^D)、$(^F)、$(?D)、$(?F)与此类似。
=============================================
举个例子说明,下面是原始makefile文件内容:
==============================================
myprog : foo.o bar.o
gcc foo.o bar.o -o myprog
foo.o : foo.c foo.h bar.h
gcc -c foo.c -o foo.o
bar.o : bar.c bar.h
gcc -c bar.c -o bar.o
==============================================
用变量代替后的文件内容:
==============================================
OBJS = foo.o bar.o
CC = gcc
CFLAGS = -Wall -O -g
myprog : $(OBJS)
$(CC) $^ -o $@
foo.o : foo.c foo.h bar.h
$(CC) $(CFLAGS) -c $<-o $@
bar.o : bar.c bar.h
$(CC) $(CFLAGS) -c $<-o $@
==============================================
更多见
http://www.china-askpro.com/msg22/qa83.shtml
下面是另外一篇文章,主要是讲解一些语法规则
●注释和换行
① makefile 文件注释以“#”开头,一直到行尾的字符
② 当一行的内容过长的时候,可以用换行符“/”来继续。※在使用换行符的时候在“/” 后面不要再加上其他任何字符,包括注释和空格。因为只有“/”出现在一行的最后的 时候,nmake才把它当成换行符来解释。
●宏定义(也可称之为变量定义)
① 宏定义的语法如下:
变量名=变量内容
② 宏的引用
在引用宏时只需在变量前加“$”,被引用的变量名要加圆括号,如下:
#定义
EXE = Test.exe
#引用
$(EXE)
③ 用宏定义的好处不言而喻,但最大的好处是是可以直接在命令行中用新的宏定义覆盖:
比如在原makefile中有如下宏定义:
#编译选项
ML_FLAG = /c /coff
#在命令行中输入:
nmake ML_FLAG=" /c /coff /F1"
这里有必要解释一下,此处“覆盖”的意思并不是执行本条命令后makefile文件中宏ML_FLAG的值被物理的改变成“ /c /coff /F1”,执行本命令后查看makefile文件,发现宏ML_FLAG的值仍为“/c /coff ”。此处“覆盖”的真正含义是:仅在执行本条命令遇到makefile文件中宏ML_FLAG时,以“ /c /coff /F1”来替换执行。
④ 不过关于上述③的使用中有两点需要注意:
■宏名称要区分大小写,ML_FLAG与ml_flag是不一样的,一般习惯用大写
■定义值中有空格的时候要用双引号引起来(没有空格时可以不用双引号。(makfile中的宏定义不用遵守这种规则))
这使临时使用不同的参数编译文件时可以不必修改makefile
●显示规则
① makefile中包含有一些规则,这些规则定义了文件之间的依赖关系和产生命令,一个规则的格式是这样的:
目标文件:依赖文件;命令 (方法一)
或
目标文件:依赖文件 (方法二)
(Tab)命令
说明:
■规则可以用这两种方法,用方法二的时候,命令可以从第二行开始,第一行的“;”省略,但是这时命令前面必须有一个“Tab”字符,否则nmake无法区分这究竟是命 令还是别的定义。
■在同一个规则中目标文件可以有多个,依赖文件也可以有多个,同时命令也可以由多个命令行组成,当然这时候就必须用第二种方法定义了,否则无法在同一行中写入多 条命令。
■nmake默认将整个描述文件的第一条规则中的目标文件认为是最终文件,所以我们必须把最终需要生成的文件放在第一条规则定义
(当用户要求nmake去建造一个目标时,make会去找到这个目标的依赖规则,这时规则中定义的命令并不会立刻被执行,而是首先要做一些事情:▲nmake首先去检查依赖文件是否是另一条规则的目标文件,如果是,则先处理这一条规则,依次类推;▲如果不是,nmake再检查各个依赖文件的时间,看这些文件有没有比目标文件更新的,如果没有,nmake会决定不再建造目标文件,并给出提示:‘xxx文件’is up-to-date,如果依赖文件有比目标文件更新的,才执行命令、生成目标文件。一旦放在第一条的目标文件生成,如果还有其它尚未生成的目标文件没有生成的话,nmake并不去理会,这就说明了为什么我们要把我们想要生成的最终目标文件放在第一条的原因。)
■目标也可以没有依赖文件,而且目标也可以不是一个真正的文件,比如
……
Clean:
(Tab)del *.obj
del *.res
Clean是一个目标,但我们并不是要生成一个clean文件,而是希望在文件调试完毕后用nmake来清除临时文件,当我们键入nmake clean的时候,工作目录下并没有clean 文件,那么nmake就会去执行clean定义中的命令,因为nmake把每一个不存在的目 标当做是一个过时(out-of-date)的目标(需要重新生成),如此一来,就会执行clean 定义中的命令,如此一来,就会删除中间过程中的文件*.obj和*.res
■指出了目标文件全名的规则称为显示规则,便有些类别的文件的编译方法可以是雷同的,如从asm文件产生obj文件的命令总是用ml,从rc文件产生res文件的命令总 是用rc ,对于每个文件都写一条规则有些多余,这时候就要用到隐含规则。
●隐含规则
① 隐含规则可以为某一类的文件指出建立的命令,它具体定义了如何将带一个特定扩展名的文件转换成具有另一种扩展名的文件,定义的格式是:
.源扩展名.目标扩展名:;命令(方法一)
或
.源扩展名.目标扩展名: (方法二)
(Tab)命令
② 隐含规则不能有依赖文件,所以“:”下面没有内容。
.asm.obj:
Ml $(ML_FLAG) $<
.rc.res:
rc $<
隐含规则中无法指定确定的输入文件名,因为输入文件名是泛指的有相同扩展名的一整类文件,这时候就要用到几个特殊的内定宏来指定文件名:
▲ $@-----全路径的目标文件。
▲ $*-----除去扩展名的全路径的目标文件。
▲ $?-----所有源文件名。
▲ $<-----源文件名(只能用在隐含规则中)
所以上述的rc $<用于x.rc时就是 rc x.rc ,而用于 y.rc 时就是 rc y.rc 了。