使用GNU Make 管理你的D语言工程

D语言程序的构建方法很多,Windows下最简单的方法是用一个bat批处理文件。D语言的一个GUI项目DFL就是用这个方法编译的。还可以用DSSS, 但DSSS编译的速度很慢,用来构建exe简直是浪费生命。就我个人的了解,最好的方法是用现成的成熟的编译工具,比如GNU Make.

在SciTE4D的安装目录下就有一个mingw32-make.exe, 我主要用它来编译D程序。有些人喜欢命名为Make.exe, 但是D语言的老爸Walter自己写了一个make.exe, 为了避免名字冲突,还是用mingw32-make. 在命令行中敲入mingw32-make有点嫌长,但在最好的D语言编辑器D语言编辑器SciTE4D   http://scite4d.d-programming-language-china.org/中一键搞定,不存在这个问题。

https://www.d-programming-language-china.org/gnu-make-shi-yong/

新建一个项目,在SciTE4D中打开源文件, 一按快捷键,SciTE4D如果找到Makefile,就会自动调用下面的命令来编译项目:

mingw32-make -f Makefile

当然,你可以在SciTE4D的配置文件ybud.conf中指定Make程序路径和默认的Makefile名称。经自定义的Make命令可能是这样:

D:\bin\myMake.exe -f DMakefile

为什么要用GNU Make来编译D语言程序

我现在主要使用的是Windows平台,但不保证哪天切换到Linux平台。如果学会了在Windows下平台使用GNU Make, 将来使用Linux平台上的Make工具就会很容易上手。而编译Windows批处理的经验并不能平移应用到Linux上。

重要的是,GNU Make功能十分强大,足以应付比较复杂的D语言项目的编译。

什么是Makefile, 怎么写Makefile, 这可能需要写一本书来完全说明。事实上,Make就象一门小型的程序语言。Make功能强大,但有个学习的过程。

什么是makefile?或许很多Winodws的程序员都不知道这个东西,因为那些 Windows的IDE都为你做了这个工作,但我觉得要作一个好的和professional的程序员,makefile还是要懂。这就好像现在有这么多的HTML的编辑器,但如果你想成为一个专业人士,你还是要了解HTML的标识的含义。特别在Unix下的软件编译,你就不能不自己写makefile 了,会不会写makefile,从一个侧面说明了一个人是否具备完成大型工程的能力。

因为,makefile关系到了整个工程的编译规则。一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为makefile就像一个Shell脚本一样,其中也可以执行操作系统的命令。

makefile带来的好处就是——“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率。make是一个命令工具,是一个解释makefile中指令的命令工具,一般来说,大多数的IDE都有这个命令,比如:Delphi的make,Visual C++的nmake,Linux下GNU的make。可见,makefile都成为了一种在工程方面的编译方法。

———————————————————跟我一起写 Makefile,陈皓

第一次使用Makefile 构建 简单D语言程序

如果从头到尾去看GNU Make的手册,会觉得很枯燥。最好的方式是先套用模仿一个Makefile编译简单的D程序,编译成功后,再学习更多的指令。

假如你的项目结构如下:

D:\myProject\
D:\myProject\myFile.d
D:\myProject\a.d
D:\myProject\b.d
...
D:\myProject\res\
D:\myProject\res\my.res
D:\myProject\obj\

D:\myProject\Makefile

主文件是myFile.d, 另外还有十来个D源文件,都在同级目录。资源文件放在res子目录,编译器生生的obj文件放到obj子目录。这是一个很小项目可能的目录结构。

D:\myProject\Makefile可以这样写:

# by D语言中国    https://www.d-programming-language-china.org/
RES=res\my.res
OBJ=$(wildcard *.d)
LIB=dwin.lib
DFLAG=-debug -g
RFLAG=-release -O
FLAG=-L/SUBSYSTEM:windows:5 -L/rc:res\my.res -odobj $(LIB) $(RFLAG)
myFile.exe: $(OBJ) $(RES) Makefile
    dmd $(OBJ) $(FLAG) -of$@

我们使用通配符$(wildcard *.d) 把当前目录下的所有D源文件都包含进来了,新增源文件,Makefile并不需要修改。

编译flags中加上-L/SUBSYSTEM:windows:5, 程序运行时DOS窗口就不会出现。

上面使用了Make的特定宏,用$@来指定要编译的文件,上例就是myFile.exe

要注意的是, 在这个Makefile中, 命令行dmd的前面是一个tab,而不是空格

我在使用时,还有自动编译res资源文件的规则。

D语言编辑器SciTE4D   http://scite4d.d-programming-language-china.org/中打开myFile.d, 一按F7, 如果找到Makefile并且内容中包含字符串"myFile.d"(旧版) 或者“myFile"(新版), SciTE4D就自动调用Make程序执行Makefile。

只重新编译修改过的D源文件,GNU Make使用进阶

上面的方法,每次构建都重新编译所有的D源文件。能不能只重新编译修改过的D源文件呢?答案是肯定的。

# by D语言中国    https://www.d-programming-language-china.org/
RES = res\my.res
ODIR = obj
OBJ = $(patsubst %.d, $(ODIR)/%.obj, $(wildcard *.d))
LIB = dwin.lib
LFLAG = -L/rc:$(RES) $(LIB) -of$@
DFLAG = -debug -unittest -g
RFLAG = -release -O
CFLAG = -c -od$(ODIR) $(RFLAG)
myFile.exe: $(OBJ) | Makefile
    dmd $(OBJ) $(LFLAG)
$(ODIR)/%.obj : %.d
    dmd $^ $(CFLAG)

第一次见到比较复杂的Makefile会觉得象读天书。如果知道了各个指令的含义就比较好理解了。上面的Makefile我详细解释一下。

ODIR
obj文件的输出目录
OBJ=
用通配符和替换的方法得到所有obj文件的路径。先用$(wildcard *.d)得到当前目录下的所有.d文件,patsubst大概是pattern substitute的意义,把d后缀替换成obj后缀,并在obj文件前面加上obj目录。
LFLAG
把obj文件链接成binary用的参数
RFLAG
release编译参数
DFLAG
debug编译参数
CFLAG
编译obj文件的参数
$(OBJ) | Makefile
列出目标myFile.exe的依赖文件,|前面表示改动后就要重新构建exe, |后面表示改动后不需要重新构建exe
$^
所有的prerequisites(但不包括重复的prerequisites). 这里是.d源文件
$@
目标文件,这里是myFile.exe

每次构建都重新编译所有源文件,或者只重新编译修改过的源文件,两种文件的构建速度,在源文件小于10个时差别不是很明显,如果源文件很多,有必要用后一种方法。

源文件放在不同子目录下的Makefile 示例

在上面的例子,源文件都是在同一目录下的,稍微大点的项目,源文件肯定按不同文件夹分门别类,否则一个文件夹里上百个D语言源文件,在找一个文件时会影响coding心情的。

在下面的Makefile例子中,我们要用到GNU 的find,在Windows下有移植的版本,也就是find.exe, 但是Windows自己也有一个find, 如果你在Makefile调用find, 可能调用了Windows的find, 而不是我们要用的GNU find, 因此,在下面的例子中,我用把GNU find.exe 重命名为gnufind.exe

gnu find工具包下载:
http://gnuwin32.sourceforge.net/packages/findutils.htm

这个页面上的版本是4.2.20,我用4.1版本似乎扫描速度更快。

工作原理是这样,用gnufind.exe找出当前目录(Makefile所在目录)下的所有.d文件,然后经过替换后缀得到输出obj文件名,都保存到private\obj目录下。注意,所有输出obj文件都保存在同目录,因此工程中不能有同名的源文件。解决重命的办法,有的是在输出obj文件时保持同样的目录结构,这样就要用到自动创建目录的指令,使Makefile看上去更加复杂;还有的是把目录符\替换成别的符号。

D语言编辑器SciTE4D   http://scite4d.d-programming-language-china.org/里一按快捷键, make自动查找更新的D语言源文件,如果存在,就重新编译更新文件,没有更新的文件就不编译。因此,在比较大的项目,这样做是很节省时间的。

Makefile文件示例如下:

// by D语言中国    https://www.d-programming-language-china.org/
ODIR := private\obj
RES := res\my.res
SRC := $(shell gnufind . -name "*.d")
OBJ := $(patsubst %.d, $(ODIR)\\%.obj, $(notdir $(SRC)))
LIB := dwin.lib pcre.lib
LFLAG = -L/rc:$(RES) $(LIB) -of$@
DEBUG := -debug -unittest -g
RELEASE := -release -O
CFLAG := -c -od$(ODIR) $(RELEASE)
myFile.exe:  $(RES) FORCE
    dmd $(OBJ) $(LFLAG)
$(ODIR)\\%.obj : %.d
    dmd $? $(CFLAG)
FORCE:

. 表示在当前目录查找
“*.d” 是通配符,要用"括起来
$(notdir) 函数去掉路径中的目录部分,只保留文件名。 $(ODIR)\%.obj 是obj输出路径 LIB := dwin.lib pcre.lib 把要用到的lib都写在这里用空格分隔

这是一个很实用的Makefile示例。我在一开始写Makefile时,是bat文件的写法,每次添加新的源文件时,都要到Makefile里添加同样的文件。现在一切都让Makefile代劳了。

GNU Make 网络学习教程

http://sourceforge.net/project/showfiles.php?group_id=2435&package_id=23918
http://www.gnu.org/software/make/manual/
http://www.gnu.org/software/make/manual/html_node/index.html

https://www.d-programming-language-china.org/gnu-make-shi-yong/
使用GNU Make 管理你的D语言工程

相关文章: