makefile使cpp文件依赖头文件

读代码时发现目前工程中的makefile文件中有一个问题或bug,.即cpp文件没有和该.cpp文件include的头文件建立依赖性。这个危害是,当头文件被修改后,受影响的.cpp文件对应的.o文件不会被重新创建。

 

而目前的代码中大量使用C++模板,模板的实现全部写在.h文件中.

我做了个实验,修改了单件模板的实现文件 SingletonHolder.h。虽然这个修改影响了大量include了SingletonHolder.h的.cpp文件,但它们对应的.o全被没有被重新编译。

 

解决方法是用g++编译器提供的 -MM 选项。这个选项会让g++在把.cpp依赖的头文件(除系统头文件)全部列出来。具体的使用方法可以查阅手册。思路是,把cpp文件和头文件的依赖性写入到.d文件,在主makefile文件中include所有的.d文件。

 

以tcpd工程为列,修改后的makefile如下(亦见附件),该makefile可以检查到.cpp依赖的头文件被修改,使得重新生成目标文件。

 

 

include ./makefile.def

SRCS = $(wildcard *.cpp)

OBJS = $(patsubst %.cpp,%.o,$(SRCS))

# 每个.cpp都对应一个.d文件,.d文件由g++ -MM生成,列出了.cpp依赖的头文件.

DEPENDENCIES = $(subst .cpp,.d,$(SRCS))

TARGET = tcpd

OK = \\e[1m\\e[32m OK \\e[m

FAILURE = \\e[1m\\e[31m FAILURE \\e[m

all:$(TARGET)

 

# 使包含.d文件,.d文件指定了.o文件依赖哪些头文件。

# 如果不是make clean 就include所有的.d文件。

ifneq "$(MAKECMDGOALS)" "clean"

  -include $(DEPENDENCIES)

endif

 

# 此函数生成.d文件,参考g++的 -MM -MF -MT选项

#$(call make-depend,source-file,object-file,depend-file)

define make-depend

  g++ -MM -MF $3 -MT $2 $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH) $1

endef

 

 

$(TARGET):$(OBJS)

              @echo -ne Linking $(TARGET) …

              @$(CXX) $(C_FLAGS)  $(INC) -fPIC -o $@ $^ $(LIB) && echo  -e $(OK) || echo -e $(FAILURE)

 

%.o:%.cpp

#如果.cpp文件被修改,就重新生成.d文件.

              $(call make-depend, $<,$@,$(subst .o,.d,$@))

              @echo -ne Compiling $<  …

              @$(CXX) $(C_FLAGS)  $(INC) -fPIC -c -o $@ $< && echo  -e $(OK) || echo -e $(FAILURE)

             

clean:

       @rm -f *.o

       @rm -f ./$(TARGET)

 

install:clean all

#     strip $(TARGET)

 

 

 

Linux内核Makefiles教程(二)

接第一篇 

3.3.可加载模块目标 obj-m (Loadable module goals – obj-m)

obj-m指定将被构建为可加载内核模块的对象文件(object file)。

一个模块可以从一个源文件构建或者从多个源文件构建。如果模块仅仅从一个源文件构建,kbuild makefile仅仅把对象文件加到obj-m。如下:

       #drivers/isdn/i4l/Makefile

        obj-$(CONFIG_ISDN_PPP_BSDCOMP) += isdn_bsdcomp.o

在这个例子中,$(CONFIG_ISDN_PPP_BSDCOMP)被求值为m.

If a kernel module is built from several source files, you specify that you want to build a module in the same way as above.

如果一个内核模块需要从多个源文件构建,你也是按照刚才提到的那一行指定。不过,除此之外,还要额外地指定,你的内核模块需要哪些对象文件。所以还需要设置<模块名>-objs变量。如下:

       #drivers/isdn/i4l/Makefile

        obj-$(CONFIG_ISDN) += isdn.o

        isdn-objs := isdn_net_lib.o isdn_v110.o isdn_common.o

在这个例子中,模块名是 isdn 。Kbuild将编译isdn-objs列出的所有对象文件,然后运行$(LD) –r把这些对象文件链接成一个文件isdn.o。

Kbuild用-objs后缀或者-y后缀识别构成复合对象文件(composite objects)的对象文件。这样,Makefile就可以使用CONFIG_* 符号来确定一个对象文件是不是一个复合对象文件的一部分。如下:

       #fs/ext2/Makefile

        obj-$(CONFIG_EXT2_FS)        += ext2.o

        ext2-y                       := balloc.o bitmap.o

        ext2-$(CONFIG_EXT2_FS_XATTR) += xattr.o

在上例中,如果$(CONFIG_EXT2_FS_XATTR)求值为y,则xattr.o是复合对象文件ext2.o的组成部分。

当然,如果你要把对象文件编译进入内核映象文件。也可以应用上述语法。比如,如果CONFIG_EXT2_FS=y,kbuild就会构建一个ext2.o文件,如你所想,再把这个文件链接到build-in.o。

3.4. 导出符号的对象文件

并不需要在Makefile中为模块使用特殊的标记来导出符号。

3.5. 库文件目标(Library file goals – lib-y)

在obj-*中指定的对象文件列表或者被用来构建内核模块,或者被内建到内核映象文件之中。Makefile还能指定创建库文件的对象文件。所有在lib-y中指定的对象文件会被用来在对应文件夹下创建一个库文件。注意已经在obj-y中列出过的对象文件,如果再在lib-y中列出,则不会被用来创建库文件,因为这些对象文件将是全局可见的。为了一致性,lib-m中列出的对象文件会被包含到lib.a。

注意,同一个kbuild makefile会同时指定被内建到内核映象中的对象文件,也会指定用来创建库文件的对象文件。因此,在同一个文件夹下面有可能同时存在built-in.o和lib.a文件。

       #arch/i386/lib/Makefile

        lib-y    := checksum.o delay.o

上例中,会基于checksum.o和delay.o创建一个库文件lib.a。为了让kbuild意识到该目录下有lib.a库文件被创建,这个目录需要被列入到libs-y。本教程将在6.3节”列出需要遍历的路径”中详述。

lib-y的使用通常受限于 lib/ 和 arch/*/lib。

3.6. 深入遍历子目录夹

每个Makefile文件只负则在当前文件夹下面构建对象。在子目录下面的构建则由子目录下面的对象Makefile负责。不过你需要使用使用obj-y或者obj-m告诉kbuild系统将进入哪些子目录。比如话:当前目录是fs/,ext2/是当前目录下面的一个子目录,为了告诉kbuild进入ext2/目录,使用以下的语句:

        #fs/Makefile

        obj-$(CONFIG_EXT2_FS) += ext2/

如果CONFIG_EXT2_FS被求值为y(内建)或者m(模块),那么obj-*变量就会告诉kbuild需要进入ext2/子目录继续编译。

Kbuild仅仅使用这个信息来通知是否进入某个子目录,而进入后的子目录下面是用内建方式编译还是编译成模块则是由子目录下面的Makefile文件指定。最好像上例那个使用CONFIG_*变量。这样的好处是,如果CONFIG_*变量的求值既不是y也不是m,那么kbuild就完全可以忽略掉某子目录了。

3.7. 编译选项/标识

EXTRA_CFLAGS, EXTRA_AFLAGS, EXTRA_LDFLAGS, EXTRA_ARFLAGS

所有的EXTRA_VARIABLES仅仅作用于它们被赋值的kbuild makefile,应用于所有该makefile文件中执行的命令。

$(EXTRA_CFLAGS)指定了编译C文件的C编译器的编译选项。

Example:

        # drivers/sound/emu10k1/Makefile

        EXTRA_CFLAGS += -I$(obj)

        ifdef DEBUG

            EXTRA_CFLAGS += -DEMU10K1_DEBUG

        endif

变量$( EXTRA_CFLAGS)是必要的,因为最上级的Makefile定义的$(CFLAGS)作用于整个linux内核代码树。

而$(EXTRA_AFLAGS)的作用域则是每个文件夹下面单独的Makefile。

比如下面的例子编译汇编语言程序。

        #arch/x86_64/kernel/Makefile

        EXTRA_AFLAGS := -traditional

$(EXTRA_LDFLAGS) 用于当前Makefile文件中的$(LD)。

$(EXTRA_ARFLAGS) 则用于当前Makefile文件中的$(AR)。

Example:

        #arch/m68k/fpsp040/Makefile

        EXTRA_LDFLAGS := -x

未完待续

copyright ykyi.net

Linux内核Makefiles教程(一)

 

Linux内核Makefiles

本教程描述了了Linux内核的Makefile是如何工作的。

1. 概述

Linux内核的Makefile包括五个部分。

Makefile

最上层Makefile

.config

内核配置文件。

arch/$(ARCH)/Makefile

CPU架构相关Makefile

scripts/Makefile.*

对于所有Kbuild Makefile的公共方法。.

kbuild Makefiles

有将近500个kbuild Makefiles。

最上层Makefile读取.config文件。.config文件是由内核配置过程生成的。

最上级的Makefile负责生成linux的内核映象文件vmlinux和其它所有的模块。最上级Makefile递归地进入内核代码树的各级子目录分别执行子目录下的Makefile。究竟要进入哪些子目录是由内核配置阶段决定的,即配置在.cncfig文件中。最上级Makefile提定了一个路径为arch/$(ARCH)/Makefile的子目录Makefile。这个Makefile中定义了与处理器架构紧密相关的信息。

每一个子目录下面都有一个kbuild Makefile,这里都定义了一些makefile命令和变量可以被更下级的Makefile继承。Kbuild Makefile使用来自.config配置的信息生成各种文件列表用来构建内建的或者模块化的目标文件。Scripts/Makefile.*包括了很多被其它Makefile使用定义和规则。

2. 谁使用内核Makefile

内核Makefile的使用者可以大致分为四种。

用户(Userse)

这类使用者仅仅敲入一些命令,如“make menuconfig”或者“make”。他们很少会阅读甚修改内核Makefile和源代码文件。

一般开发者(Normal Developers)

一般开发者一般编写一些设备驱动程序,文件系统,网络协议。他们需要维护他们编写的代码涉及的子系统的kbuild Makefiles。为了工作地更有效率,他们甚至还需要对整个内核的Makefile有全局的把握,包括对kbuild的公共接口的详尽了解。

处理器架构开发者(Arch Developers)

这部分开发者关心与某个处理器架构紧密相关的代码部分,比如PC上最广泛使用的x86,以及IA64,还有ARM,Sparc,PowerPC,Alpha,s390,MIPS。Linux支持几百种处理器架构。从内核2.6.24开始,i386和x86_64合并为x86。

KBuild的开发者(KBuild Developers)

KBuild的开发者就是维护内核构建系统的开发者,他们需要知道内核Makefile的各个方面。

——

本篇教程主要面向第二类开发者和第三类开发者。即一般开发者和处理器体系架构开发者。

3. kbuild文件

大多数内核Makefile文件是kbuild Makefile文件,它们使用不同于一般make文件的kbuild语法。下面会介绍kbuild makefile的语法。Kbuild文件一般被命名为Mafile,但是也可以使用Kbuild名字。如果已经存在一个Makefile文件了,那么就只好用Kbuild名字了。

3.1节 "目标定义"是一个快速简介,更详细的介绍会在下面的章节陆续介绍并有真实的例子。

3.1. 目标定义(Goal Definitions)

目标定义是kbuild Makefile的最重要的部分。它们定义了哪些文件会被构建,并指定一些特殊的选项组合以及将递归进入到哪些子目录。

最最简单的makefile只有一行代码,如:

       obj-y += foo.o

这行代码告诉kbuild系统当前目录下有一个目标要构建,它的名字是foo.o。foo.o将从foo.c或者foo.S生成。

 

如果foo.o需要被构建成一个内核模块,那么应该使用obj-m。因此一般使用下面的模式:

       obj-$(CONFIG_FOO) += foo.o

$(CONFIG_FOO)是一个makefile变量,在makefile执行时被替换为y(如果内建到内核)或者m(如果构建成模块)。如果CONFIG_FOO既不是y也不是m,那个这一行代码会被忽略,指定的文件不会被编译也不会被链接。

 

3.2 内建的对象目标(object goals) obj-y

Kbuild Makefile通过obj-y列表为linux内核映象vmlinux指定目标文件(.o)。究竟是哪些目标文件则取决于内核配置。Kbuild系统会编译所有obj-y列表中的文件。然后Kbuild系统会调用$(LD) -r把所有的这系文件合并到一个built-in.o文件。跟住呢,built-in.o被最上级Makefile链接到linux内核映象文件vmlinux。

在obj-y列表中指定的文件顺序是很重要的。列表中的重复项也是被充许的。首先出现的会被首先链接到built-in.o,后面的重复项会被忽略。

链接的顺序当然也是有重要意义的。因为一些函数调用,如module_init(),_initcall会在系统启动阶段按照他们被链接的先后顺序被调用。所以改变链接的顺序当然会造成意义重大的影响。比如:会改变SCSI控制器被内核发现的先后顺序,因此磁盘的编号则会改变。

       #drivers/isdn/i4l/Makefile

        # Makefile for the kernel ISDN subsystem and device drivers.

        # Each configuration option enables a list of files.

        obj-$(CONFIG_ISDN)             += isdn.o

        obj-$(CONFIG_ISDN_PPP_BSDCOMP) += isdn_bsdcomp.o

(第一篇完)   第二篇

copyright ykyi.net

 

linux的soname,链接库的问题.

第一次看到soname是读<程序员的自我修养.第一册:链接库>看来的。顺便说一下,这本书确实是本好书吖,国内出的难得技术好书。后来在公司的项目中把它从windows平台下移植到linux下,写makefile时用到过。

 

再后来又忘记了。今天再次看到soname的时候就有点记不起来了。又只好搜索了一些资料看。趁热打铁把学会的东西记下来。

linux为尝试解决动态链接库版本冲突的问题(windows平台下称之为DLL HELL),引入了soname进制。linux的动态链接库的扩展名约定为.so 即shared object的缩写。下面的makefile语句来自于哥今年上半年写的makefile文件:

VERSION_NUM := 1.0.0
soname = lib$(dirname).so.$(firstword $(subst ., ,$(VERSION_NUM)))
LINKFLAGS := -shared -Wl,-soname,$(soname) -L. -lpthread -lrt -lpentagon -lcross_platform
其中的变量$(soname)会被替换为eibcomm.so.1。再看看LINKFLAGS变量指定的几个选项. -shared 表示要生成一个shared object,即动态共享库. -pthread -lrt -lpentagon -lcross_platform 是要链接其它几个库,thread和linux的posix线程库,rt是一个和实时系统相关的库它提供了高精度的时间函数。

比较奇怪的是 -Wl,-soname,$(soname) 它们之前用逗号分开,而不是用空格分开。实际上这几个东东会被gcc传给ld(linux下的链接器)作为链接器的参数。gcc规定的写法就是这样的。指定的soname会被链接器写入到最终生成的elf文件中(linux下的可执行文件格式)。要查看elf文件的段布局什么的,可以用readelf工具。

 

最终生成的文件将命名为 libname.so.x.y.z 的形式。soname是libname.so.x的格式(也不尽然,这只是一个convetion,并非在语法或机制上强行要求)。接着把生成的so文件拷贝到/usr/lib 或者 /lib,这两个文件夹是系统用来放置动态链接库的地方。debian下没有把/usr/local/lib作为默认的动态链接库文件夹.可以通过改/etc/ld.so.conf 文件把更多的路径加入。再运行 ldconfig,更新cache中的库文件信息。为什么要用cache呢,有人就问了!因为library实在太多了咯,cache就被引用了。cache是个好东西啊!!!

这个时候依赖 libname.so.x 的文件就可以找到这个library并加载起来了。但是编译程序时还是找不到呢.编译程序时通常会写 -lname 这个时候会先找 libname.so 再找 libname.a 文件,结果找不到。link就会抱怨没有找到这个库文件。为了让编译通过,于是你就用 ln -sf libname.so libname.so.x.y.z (这个是realname,版本号部分可能只有x.y两个部分) 建一个软链接!搞定!

copyright ykyi.net