为工程增加条件编译开关

在最近的项目中,要求把录制程序移植到嵌入式板卡上,原来的vlc模块无法被支持(板卡上无法安装vlc程序以及相应的库),因此要求编译一个去掉vlc模块的版本。折腾半天弄出来了,由于对makefile不熟,很多地方实现的不够理想,仅仅是能用而已。

 

下面是步骤:

(1)首先编译源代码

在代码中增加编译开关(宏定义):MODULE_WITHOUT_VLC

在源代码中所有涉及到vlc模块的 代码处使用该宏定义重写,确保如果定义了MODULE_WITHOUT_VLC,代码也能正常运行(只是不支持vlc模块而已)

 注意:宏MODULE_WITHOUT_VLC并不需要在源代码中定义,下面会做说明

 

(2)编辑Makefile 

由于去掉了vlc模块,所有使用到vlc模块的源文件都不需要编译, 我们将所有待编译的目标文件存放到变量$(ALL_OBJFILE)中,这样的话我们需要从中剔除不需要编译的目标文件:

OBJ_COMP_NOVLC = $(filter-out $(OBJ_VLC), $(ALL_OBJFILE))

这样, $(OBJ_COMP_NOVLC)中存放的就是去除vlc模块的所有需要编译的目标文件

 

同时,在连接时去我们也需要去除vlc模块。

 

由于在代码中需要条件编译开关,我把它放在Makefile中作为g++的参数传进来:

 $(CXX) --verbose -DMODULE_WITHOUT_VLC -c -o $@ $< $(INCPATHS) -g -Wall -rdynamic

 

最后我们的Makefile如下:

 1 ##################################################################################
2 #
3 #编译Record工程
4 #
5 ##################################################################################
6 #编译选项
7 CXX = g++
8 OPTI = -o3
9 CXXFLAGS = -Wall -Wno-strict-aliasing -Wno-unused-variable
10
11 LIBPATH = /usr/local/lib
12 LIBS = -lpthread -lhcnetsdk -llog4cpp
13 EXE = Record
14 EXE_novlc = Record.novlc
15 DIST_PATH = http://www.cnblogs.com/Record/
16
17 #mysql模块
18 LIB_MYSQL = -rdynamic -L/usr/lib/mysql -lmysqlclient_r -lz -lcrypt -lnsl -lm -lpthread
19 LIBS += $(LIB_MYSQL)
20
21 #日志模块
22
23 #包含头文件路径
24 SUBDIR = $(shell ls http://www.cnblogs.com/src -R | grep /)
25 SUBDIRS = $(subst :,/,$(SUBDIR))
26 INCPATHS = $(subst http://www.cnblogs.com/,-Ihttp://www.cnblogs.com/,$(SUBDIRS))
27 INCPATHS += -I http://www.cnblogs.com/include/
28
29 VPATH = $(subst : ,:,$(SUBDIR))./
30 SOURCE = $(foreach dir,$(SUBDIRS),$(wildcard $(dir)*.cpp))
31
32 #使用vlc模块的目标文件
33 OBJ_VLC = CInputVideoVLC.o COutputVideoVLC.o
34 LIB_VLC = -lvlc
35
36 #根据cpp文件名生成对应的目标文件名.o
37 OBJS = $(patsubst %.cpp,%.o,$(SOURCE))
38 #所有待编译的目标文件
39 ALL_OBJFILE = $(foreach dir,$(OBJS),$(notdir $(dir)))
40 OBJSPATH = $(addprefix obj/,$(OBJFILE))
41 ALL_LIBS = $(LIBS)
42 ALL_LIBS += $(LIB_VLC)
43
44 #without vlc support
45 #最终编译时使用的目标文件
46 OBJ_COMP_NOVLC = $(filter-out $(OBJ_VLC), $(ALL_OBJFILE))
47 LIBS_COMP_NOVLC = $(LIBS)
48
49 $(EXE):$(ALL_OBJFILE)
50 $(CXX) -L$(LIBPATH) -Wl,-rpath=/usr/local/lib/ -o $(EXE) $(ALL_OBJFILE) $(INCPATHS) $(ALL_LIBS)
51 mv $(EXE) $(DIST_PATH)
52
53 $(ALL_OBJFILE):%.o:%.cpp
54 $(CXX) --verbose -c -o $@ $< $(INCPATHS) -g -Wall -rdynamic
55
56
57 novlc:$(EXE_novlc)
58 @echo "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
59 @echo "NOTICE:compiled without vlv support."
60 @echo "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
61
62 $(EXE_novlc):$(OBJ_COMP_NOVLC)
63 $(CXX) -L$(LIBPATH) -Wl,-rpath=/usr/local/lib/ -o $(EXE_novlc) $(OBJ_COMP_NOVLC) $(INCPATHS) $(LIBS_COMP_NOVLC)
64 mv $(EXE_novlc) $(DIST_PATH)$(EXE)
65
66 $(OBJ_COMP_NOVLC):%.o:%.cpp
67 $(CXX) --verbose -DMODULE_WITHOUT_VLC -c -o $@ $< $(INCPATHS) -g -Wall -rdynamic
68
69
70 #依赖文件
71 DPPS = $(patsubst %.cpp,%.dpp,$(SOURCE))
72 include $(DPPS)
73 %.dpp: %.cpp
74 g++ $(INCPATHS) -MM $(CPPFLAGS) $< > $@.$$$$; \
75 sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
76 rm -f $@.$$$$
77
78
79 .PHONY:clean
80 clean:
81 rm -rf $(ALL_OBJFILE)
82 rm -rf $(DPPS)
83 rm -rf $(EXE) $(EXE_novlc)


 正常编译时直接make即可;

不需要vlc模块,则执行 make novlc就搞定了,不需要做任何其他改动. 

 

Updated:2012-01-05

存在bug,直接make时将会出错,修改后如下: 

 1 ##################################################################################
2 #
3 #编译Record工程
4 #
5 ##################################################################################
6 #编译目标
7 #默认编译全部
8 target =
9
10 #编译选项
11 CXX = g++
12 OPTI = -o3
13 CXXFLAGS = -Wall -Wno-strict-aliasing -Wno-unused-variable -Wl,-rpath=/usr/local/lib/
14
15 LIBPATH = /usr/local/lib
16 LIBS = -lpthread -lhcnetsdk -llog4cpp
17 EXE = Record
18 DIST_PATH = http://www.cnblogs.com/Record/
19
20 #mysql模块
21 LIB_MYSQL = -rdynamic -L/usr/lib/mysql -lmysqlclient_r -lz -lcrypt -lnsl -lm -lpthread
22 LIBS += $(LIB_MYSQL)
23
24 #日志模块
25
26 #包含头文件路径
27 SUBDIR = $(shell ls http://www.cnblogs.com/src -R | grep /)
28 SUBDIRS = $(subst :,/,$(SUBDIR))
29 INCPATHS = $(subst http://www.cnblogs.com/,-Ihttp://www.cnblogs.com/,$(SUBDIRS))
30 INCPATHS += -Ihttp://www.cnblogs.com/include/
31
32 VPATH = $(subst : ,:,$(SUBDIR))./
33 SOURCE = $(foreach dir,$(SUBDIRS),$(wildcard $(dir)*.cpp))
34
35 #使用vlc模块的目标文件
36 OBJ_VLC = CInputVideoVLC.o COutputVideoVLC.o
37 LIB_VLC = -lvlc
38
39 #根据cpp文件名生成对应的目标文件名.o
40 TMP_OBJS = $(patsubst %.cpp,%.o,$(SOURCE))
41 #所有待编译的目标文件
42 OBJFILE = $(foreach dir,$(TMP_OBJS),$(notdir $(dir)))
43 OBJSPATH = $(addprefix obj/,$(OBJFILE))
44
45 #without vlc support
46 ifeq ($(target), novlc)
47 OBJS = $(filter-out $(OBJ_VLC), $(OBJFILE))
48 CXXFLAGS += -DMODULE_WITHOUT_VLC
49 else
50 OBJS = $(OBJFILE)
51 LIBS += $(LIB_VLC)
52 endif
53
54 $(EXE):$(OBJS)
55 $(CXX) -o $(EXE) $(CXXFLAGS) -L$(LIBPATH) $(INCPATHS) $(OBJS) $(LIBS)
56 mv $(EXE) $(DIST_PATH)
57
58 $(OBJS):%.o:%.cpp
59 $(CXX) -c $(CXXFLAGS) -o $@ $< $(INCPATHS) -g -Wall -rdynamic
60
61 #依赖文件
62 DPPS = $(patsubst %.cpp,%.dpp,$(SOURCE))
63 include $(DPPS)
64 %.dpp: %.cpp
65 g++ $(INCPATHS) -MM $(CPPFLAGS) $< > $@.$$$$; \
66 sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
67 rm -f $@.$$$$
68
69
70 .PHONY:clean
71 clean:
72 rm -rf $(OBJS)
73 rm -rf $(DPPS)
74 rm -rf $(EXE) $(EXE_novlc)

 

一般情况直接make;

去掉vlc模块时make target=novlc。
 

 Updated:2012-02-02

去年的最后的一个工作日,将程序移植到了新借测的64位NAS上,有个小问题:录制的海康摄像头在linux下没有64位的sdk,该功能也就无法使用,需要把相应的模块给禁用掉。

当时简单的做了一下处理,编了一个版本给测试的,让他们春节期间测试性能看看。

今天上班,第一件事就是修改Makefile,使之支持多个编译开关--可以禁用更多的模块。经过一上午的折腾,终于弄好了:

  1 ##################################################################################
2 #
3 #预定义编译参数
4 #
5 ##################################################################################
6 #编译目标
7 #默认编译全部
8 #支持vlc模块
9 enable_vlc =
10 #支持海康摄像头
11 enable_hik =
12
13 #编译选项
14 CXX = g++
15 OPTI = -o3
16 CXXFLAGS = -Wall -Wno-strict-aliasing -Wno-unused-variable -Wl,-rpath=/usr/local/lib/
17
18 #基础模块
19 LIBPATH = /usr/local/lib
20 LIBS = -lpthread -llog4cpp
21 EXE = Record
22 DIST_PATH = http://www.cnblogs.com/Record/
23
24 #mysql模块
25 LIB_MYSQL = -rdynamic -L/usr/lib/mysql -lmysqlclient_r -lz -lcrypt -lnsl -lm -lpthread
26 LIBS += $(LIB_MYSQL)
27
28 #VLC模块
29 OBJ_VLC = CInputVideoVLC.o COutputVideoVLC.o VLC.o
30 LIB_VLC = -lvlc
31
32 #海康模块
33 OBJ_HIK = CInputVideoDriver.o COutputVideoDriverRcrd.o
34 LIB_HIK = -lhcnetsdk
35
36
37
38 ##################################################################################
39 #
40 #处理编译参数
41 #
42 ##################################################################################
43
44 #包含头文件路径
45 SUBDIR = $(shell ls http://www.cnblogs.com/src -R | grep /)
46 SUBDIRS = $(subst :,/,$(SUBDIR))
47 INCPATHS = $(subst http://www.cnblogs.com/,-Ihttp://www.cnblogs.com/,$(SUBDIRS))
48 INCPATHS += -Ihttp://www.cnblogs.com/include/
49
50 VPATH = $(subst : ,:,$(SUBDIR))./
51 SOURCE = $(foreach dir,$(SUBDIRS),$(wildcard $(dir)*.cpp))
52
53 #根据cpp文件名生成对应的目标文件名.o
54 TMP_OBJS = $(patsubst %.cpp,%.o,$(SOURCE))
55 #所有待编译的目标文件
56 OBJFILE = $(foreach dir,$(TMP_OBJS),$(notdir $(dir)))
57 OBJSPATH = $(addprefix obj/,$(OBJFILE))
58
59 #支持vlc模块
60 ifeq ($(enable_vlc), no)
61 OBJS = $(filter-out $(OBJ_VLC), $(OBJFILE))
62 CXXFLAGS += -DMODULE_WITHOUT_VLC
63 else
64 OBJS = $(OBJFILE)
65 LIBS += $(LIB_VLC)
66 endif
67 OBJFILE := $(OBJS)
68
69 #支持海康模块
70 ifeq ($(enable_hik), no)
71 OBJS = $(filter-out $(OBJ_HIK), $(OBJFILE))
72 CXXFLAGS += -DMODULE_WITHOUT_HIK
73 else
74 LIBS += $(LIB_HIK)
75 endif
76 OBJFILE := $(OBJS)
77
78
79
80 ##################################################################################
81 #
82 #执行编译过程
83 #
84 ##################################################################################
85
86 $(EXE):$(OBJS)
87 $(CXX) -o $(EXE) $(CXXFLAGS) -L$(LIBPATH) $(INCPATHS) $(OBJS) $(LIBS)
88 mv $(EXE) $(DIST_PATH)
89
90 $(OBJS):%.o:%.cpp
91 $(CXX) -c $(CXXFLAGS) -o $@ $< $(INCPATHS) -g -Wall -rdynamic
92
93 #依赖文件
94 DPPS = $(patsubst %.cpp,%.dpp,$(SOURCE))
95 include $(DPPS)
96 %.dpp: %.cpp
97 g++ $(INCPATHS) -MM $(CPPFLAGS) $< > $@.$$$$; \
98 sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
99 rm -f $@.$$$$
100
101
102 .PHONY:clean
103 clean:
104 rm -rf $(OBJS)
105 rm -rf $(DPPS)
106 rm -rf $(EXE) $(EXE_novlc)

照上个版本做了不小改动,就不细说了。

一般情况直接make;

禁用vlc模块: make enable_vlc=no

禁用海康模块: make enable_hik=no

都禁用:make enable_vlc=no enable_hik=no

以后如果需要禁用更多的模块,依次添加即可。 

 

ps:在修改的时候, 遇到一个Makefile的语法问题。说明如下:

OBJFILE = $(OBJS) 

OBJS = $(filter-out $(OBJ_HIK), $(OBJFILE))

在make时会报错:“递归变量,最终引用自己”,经查发现这是由于“递归展开式变量”给自己赋值是不支持的,而应该采用如下方式: 

OBJFILE := $(OBJS)

OBJS = $(filter-out $(OBJ_HIK), $(OBJFILE))

 

 6.2 两种变量定义(赋值)
在GNU make中,变量的定义有两种方式(或者称为风格)。我们把使用这两种方式定义的变量可以看作变量的两种不同风格。变量的这两种不同的风格的区别在于:1. 定义方式;2. 展开时机。下边我们分别对这两种不同的风格进行详细地讨论。


6.2.1 递归展开式变量
 第一种风格的变量是递归方式扩展的变量。这一类型变量的定义是通过“=”或者使用指示符“define”定义的。这种变量的引用,在引用的地方是严格的文本替换过程,此变量值的字符串原模原样的出现在引用它的地方。如果此变量定义中存在对其他变量的引用,这些被引用的变量会在它被展开的同时被展开。就是说在变量定义时,变量值中对其他变量的引用不会被替换展开;而是变量在引用它的地方替换展开的同时,它所引用的其它变量才会被一同替换展开。语言的描述可能比较晦涩,让我们来看一个例子:
foo = $(bar)
bar = $(ugh)
ugh = Huh?

all:;echo $(foo)
执行“make”将会打印出“Huh?”。整个变量的替换过程时这样的:首先“$(foo)”被替换为“$(bar)”,接下来“$(bar)”被替换为“$(ugh)”,最后“$(ugh)”被替换为“Hug?”。整个替换的过程是在执行“echo $(foo)”时完成的。
这种类型的变量是其它版本的make所支持的类型。我们可以把这种类型的变量称为“递归展开”式变量。此类型变量存有它的优点同时也存在其缺点。其优点是:
这种类型变量在定义时,可以引用其它的之前没有定义的变量(可能在后续部分定义,或者是通过make的命令行选项传递的变量)。看一个这样的例子:
CFLAGS = $(include_dirs) -O
include_dirs = -Ifoo -Ibar

“CFLAGS”会在命令中被展开为“-Ifoo -Ibar -O”。而在“CFLAGS”的定义中使用了其后才定义的变量“include_dirs”。
 其缺点是:
 1. 使用此风格的变量定义,可能会由于出现变量的递归定义而导致make陷入到无限的变量展开过程中,最终使make执行失败。例如,接上边的例子,我们给这个变量追加值: 
CFLAGS = $(CFLAGS) –O
它将会导致make对变量“CFLAGS”的无限展过程中去(这种定义就是变量的递归定义)。因为一旦后续同样存在对“CLFAGS”定义的追加,展开过程将是套嵌的、不能终止的(在发生这种情况时,make会提示错误信息并结束)。一般书写Makefile时,这种追加变量值的方法很少使用(也不是我们推荐的方式)。看另外一个例子:
x = $(y)
y = $(x) $(z)
这种情况下变量在进行展开时,同样会陷入死循环。所以对于此风格的变量,当在一个变量的定义中需要引用其它的同类型风格的变量时需特别注意,防止变量展开过程的死循环。

2. 第二个缺点:这种风格的变量定义中如果使用了函数,那么包含在变量值中的函数总会在变量被引用的地方执行(变量被展开时)。
这是因为在这种风格变量的定义中,对函数引用的替换展开发生在变量展开的过程中,而不是在定义这个变量的时候。这样所带来的问题是:使make的执行效率降低(每一次在变量被展开时都要展开他所引用的函数);另外在某些时候会出现一些变量和函数的引用出现非预期的结果。特别是当变量定义中引用了“shell”和“wildcard”函数的情况,可能出现不可控制或者难以预料的错误,因为我们无法确定它在何时会被展开。

6.2.2 直接展开式变量
为了避免“递归展开式”变量存在的问题和不方便。GNU make支持另外一种风格的变量,称为“直接展开”式。这种风格的变量使用“:=”定义。在使用“:=”定义变量时,变量值中对其他量或者函数的引用在定义变量时被展开(对变量进行替换)。所以变量被定义后就是一个实际需要的文本串,其中不再包含任何变量的引用。因此
x := foo
y := $(x) bar
x := later

就等价于:
y := foo bar
x := later


和递归展开式变量不同:此风格变量在定义时就完成了对所引用变量和函数的展开,因此不能实现对其后定义变量的引用。如:
CFLAGS := $(include_dirs) -O
include_dirs := -Ifoo -Ibar
由于变量“include_dirs”的定义出现在“CFLAGS”定义之后。因此在“CFLAGS”的定义中,“include_dirs”的值为空。“CFLAGS”的值为“-O”而不是“-Ifoo -Ibar -O”。这一点也是直接展开式和递归展开式变量的不同点。注意这里的两个变量都是“直接展开”式的。大家不妨试试将其中某一个变量使用递归展开式定义后看一下又会出现什么样的结果。

 下边我们来看一个复杂一点的例子。分析一下直接展开式变量定义(:=)的用法,这里也用到了make的shell函数和变量“MAKELEVEL”(此变量在make的递归调用时代表make的调用深度)。其中包括了对函数、条件表达式和系统变量“MAKELEVEL”的使用:

ifeq (0,${MAKELEVEL})
cur-dir := $(shell pwd)
whoami := $(shell whoami)
host-type := $(shell arch)
MAKE := ${MAKE} host-type=${host-type} whoami=${whoami}
endif

第一行是一个条件判断,如果是顶层Makefile,就定义下列变量。否则不定义任何变量。第二、三、四、五行分别定义了一个变量,在进行变量定义时对引用到的其它变量和函数展开。最后结束定义。利用直接展开式的特点我们可以书写这样一个规则:
${subdirs}:
${MAKE} cur-dir=${cur-dir}/$@ -C $@ all


它实现了在不同子目录下变量“cur_dir”使用不同的值(为当前工作目录)。
在复杂的Makefile中,推荐使用直接展开式变量。因为这种风格变量的使用方式和大多数编程语言中的变量使用方式基本上相同。它可以使一个比较复杂的Makefile在一定程度上具有可预测性。而且这种变量允许我们利用之前所定义的值来重新定义它(比如使用某一个函数来对它以前的值进行处理并重新赋值),此方式在Makefile中经常用到。尽量避免和减少递归式变量的使用。 

详见:http://www.linuxsir.org/main/doc/gnumake/GNUmake_v3.80-zh_CN_html/make-06.html


 

 

posted @ 2012-01-04 14:49    阅读(626)  评论(0编辑  收藏  举报