今天是中秋佳节,但是写完已经是第二天凌晨了,还是祝大家中秋快乐!

VS对C++的支持相较GCC太弱了,连续几个VS版本对C++的改进都很小、很少。对Cpper也许是一种痛,我们也许希望能使用VS的强大编辑功能,组合GCC以及GDB的强大编译器以及调试功能进行开发。自从有了VisualGDB,这一切都变成真的了,那VS默认会有预编译头,GCC呢?VisualGDB呢?我们只能手动去写Makefile,不错,手动去写,下面就与大家分享VisualGDB中的预编译头设置。

在之前的博客( VisualGDB使用随笔)中提到,我初尝螃蟹,发现了VisualGDB这个超级好用的VS插件后,一直是爱不释手。但后面的几年时间里一直没新项目可以使用这个工具,一展其光辉。博客最后也提到了要分享VisualGDB中添加预编译以及类似VS的Compile输出而不是一长串命令行的修改。实在不好意思,事隔三年了,今天才将它分享出来。

在大项目中添加预编译头的好处,我就不多说了。

说起预编译头,让我想起了一个我亲身经历的事:前几年初到一家公司做项目,由于该项目比较庞大,机器配置也比较差,当时要全部重新编译一次项目大概需要半小时(也许有人说可以使用联合编译,确实,公司就是使用的联合编译,但编译一次也得在十几分钟的样子),为什么会这样,我看了一下项目配置,居然没配置预编译头。对我而言,编译一次需要最少需要10几分钟,去上个厕所,去冲杯咖啡回来都还没编译完成,我实在是受不了。于是我决定加上预编译头,由于项目比较大,文件比较多,头文件的包含顺序错综复杂,不想手工去一个一个理(估计得理个几天吧),就写了一个小工具去整理这些包含顺序,自动生成 一个预编译头文件。最后弄下来,编译速度得到了极大提高,直接单机编译只需要三五几分钟就编译完成(为此公司还给我发了一笔奖金)。

好了,言归正传,我们知道VisualGDB自动生成的Makefile中并不包括预编译头的任何选项,只能通过自己修改Makefile来达到目的。对于初学者或者根本不懂Makefile的同学来说是有一定难度的。使用过VS的同学一定知道,VS的项目配置中一个项目只能配置一个预编译头,如果这个项目既使用了C又使用了C++那么两者是不能同时生成预编译头的。而GCC,准确的说应该是使用Makefile的方式就可以做到,下面就为大家分享VisualGDB项目生成的Makefile如何同时生成和使用C以及C++的预编译头。

关于普通的GCC项目使用预编译头加快编译速度可以参考之前我转的一篇文章《 使用 GNU CC 的预编译头文件加快编译速度》,VisualGDB生成的Makefile与文中所示的Makefile类似,只是VisualGDB的Makefile会根据项目中的文件自动修改其中的SOURCEFILES参数,但规则部分是一经生成就不会被修改,而我们要做的也是修改规则。

BTW:当然如果不想让VisualGDB自动添加项目文件到SOURCEFILES项,可以把Makefile中的

 1#VisualGDB: AutoSourceFiles		#<--- remove this line to disable auto-updating of SOURCEFILES and EXTERNAL_LIBS```
 2
 3
 4
 5
 6删除掉即可(不建议删除)
 7
 8
 9
10我们看一下VisualGDB自动生成的Makefile编译规则部分:

#VisualGDB: FileSpecificTemplates #<— VisualGDB will use the following lines to define rules for source files in subdirectories \((BINARYDIR)/%.o : %.cpp \)(all_make_files) |\((BINARYDIR) \)(CXX) \((CXXFLAGS) -c \)< -o \(@ -MD -MF \)(@:.o=.dep)

\((BINARYDIR)/%.o : %.c \)(all_make_files) |\((BINARYDIR) \)(CC) \((CFLAGS) -c \)< -o \(@ -MD -MF \)(@:.o=.dep)

\((BINARYDIR)/%.o : %.S \)(all_make_files) |\((BINARYDIR) \)(CC) \((CFLAGS) \)(ASFLAGS) -c \(< -o \)@ -MD -MF $(@:.o=.dep)

\((BINARYDIR)/%.o : %.s \)(all_make_files) |\((BINARYDIR) \)(CC) \((CFLAGS) \)(ASFLAGS) -c \(< -o \)@ -MD -MF $(@:.o=.dep)

\((BINARYDIR)/%.o : %.cc \)(all_make_files) |\((BINARYDIR) \)(CC) \((CFLAGS) \)(CXXFLAGS) -c \(< -o \)@ -MD -MF $(@:.o=.dep)

\((BINARYDIR)/%.o : %.cxx \)(all_make_files) |\((BINARYDIR) \)(CC) \((CFLAGS) \)(CXXFLAGS) -c \(< -o \)@ -MD -MF $(@:.o=.dep)

#VisualGDB: GeneratedRules #<— All lines below are auto-generated```

这里列出了以下几种源文件的编译规则:

1.扩展名为cpp、cc、cxx的C++源文件

2.扩展名为c的C源文件

3.扩展名为s、S的汇编源文件,这种汇编文件一般是AT&T格式的汇编文件而不是Intel格式的汇编文件。

为了让项目同时支持C以及C++的预编译头,首先我们假定C及C++的头文件使用如下选项来定义:

 1# C Header
 2PCH_H = prec.h
 3PCH = prec.h.gch
 4
 5# C++ Header
 6PCH_X_H = prec.hpp  
 7PCH_X = prec.hpp.gch```
 8
 9
10这部分代码可以放在Makefile的生成规则之前的某个适当位置,也可以放在debug.mak和release.mak中,需要特别注意的是预编译生成的文件一定是在预编译头文件后加gch后缀,同时要求预编译文件与头文件在同一个目录,不然GCC找不到预编译文件。然后在Makefile

#VisualGDB: GeneratedRules #<— All lines below are auto-generated```

这行之前加上生成预编译头的规则,

1# C Prec Header Rule
2$(BINARYDIR)/$(PCH) : $(PCH_H)
3	$(CC) $(CFLAGS) $> $^
4	
5# C++ Prec Header Rule
6$(BINARYDIR)/$(PCH_X) : $(PCH_X_H)
7	$(CXX) $(CXXFLAGS) $> $^

添加好预编译头的规则后,我们还需要使用生成的预编译头,注意C与C++的预编译头不能混合使用,不能编译会报错。例如添加cpp以及c源文件的预编译头使用规则:

 1$(BINARYDIR)/%.o : %.cpp $(all_make_files) |$(BINARYDIR) $(BINARYDIR)/$(PCH_X)
 2	$(CXX) $(CXXFLAGS) -c $< -o $@ -MD -MF $(@:.o=.dep)
 3
 4$(BINARYDIR)/%.o : %.c $(all_make_files) |$(BINARYDIR) $(BINARYDIR)/$(PCH)
 5	$(CC) $(CFLAGS) -c $< -o $@ -MD -MF $(@:.o=.dep)```
 6
 7
 8
 9
10此时我们在项目中添加两个头文件prec.h以及prec.hpp就可以自动生成并使用各自的预编译头了。
11
12最后,使用惯了VS的同学可能不太习惯编译时的一大串命令行,没关系,我们也可以通过修改Makefile来做到只显示简要信息。我们规则部分的命令使用@符号作前缀即可,知道DOS命令的同学应该比较熟悉如下命令

@echo off @echo on```

然后加上我们想要显示的信息比如Compile XXX

好了,下面我就直接把完整的Makefile规则部分贴出来:

 1#VisualGDB: FileSpecificTemplates		#<--- VisualGDB will use the following lines to define rules for source files in subdirectories
 2$(BINARYDIR)/%.o : %.cpp $(all_make_files) |$(BINARYDIR) $(BINARYDIR)/$(PCH_X)
 3	@$(CXX) $(CXXFLAGS) -c $< -o $@ -MD -MF $(@:.o=.dep)
 4	@echo Compile $<
 5
 6$(BINARYDIR)/%.o : %.c $(all_make_files) |$(BINARYDIR) $(BINARYDIR)/$(PCH)
 7	@$(CC) $(CFLAGS) -c $< -o $@ -MD -MF $(@:.o=.dep)
 8	@echo Compile $<
 9
10$(BINARYDIR)/%.o : %.S $(all_make_files) |$(BINARYDIR)
11	$(CC) $(CFLAGS) $(ASFLAGS) -c $< -o $@ -MD -MF $(@:.o=.dep)
12
13$(BINARYDIR)/%.o : %.s $(all_make_files) |$(BINARYDIR)
14	$(CC) $(CFLAGS) $(ASFLAGS) -c $< -o $@ -MD -MF $(@:.o=.dep)
15
16$(BINARYDIR)/%.o : %.cc $(all_make_files) |$(BINARYDIR) $(BINARYDIR)/$(PCH_X)
17	$(CC) $(CFLAGS) $(CXXFLAGS) -c $< -o $@ -MD -MF $(@:.o=.dep)
18
19$(BINARYDIR)/%.o : %.cxx $(all_make_files) |$(BINARYDIR) $(BINARYDIR)/$(PCH_X)
20	$(CC) $(CFLAGS) $(CXXFLAGS) -c $< -o $@ -MD -MF $(@:.o=.dep)
21	
22# C Prec Header Rule
23$(BINARYDIR)/$(PCH) : $(PCH_H)
24	@$(CC) $(CFLAGS) $> $^
25	@echo Compile $^
26	
27# C++ Prec Header Rule
28$(BINARYDIR)/$(PCH_X) : $(PCH_X_H)
29	@$(CXX) $(CXXFLAGS) $> $^
30	@echo Compile $^
31
32#VisualGDB: GeneratedRules				#<--- All lines below are auto-generated```
33
34
35
36 祝大家玩得愉快!