您的位置:首页 > 产品设计 > UI/UE

APUE和UNP12(Makefile)

2018-02-09 08:14 351 查看
本文内容:
- 最简单的Makefile知识点归纳,通过对apue-Makefile的内容,解剖出Makefile机制,并在此过程中,实践自己的algs4-hhc算法库工程(当然,是以学习Makefile为开始)- 这编写过程中,为了方便本人复习,会适当提供参考资料,部分内容超出Makefile知识范围,自行跳过。
阅读人群:
- 读者没有任何编译原理知识,可以先去补充(推荐阅读《嵌入式linux开发教程 上册》10.1.3.2:gcc编译过程;这极其重要。- 读者刚从windows转过来,一直使用IDE,也可以去补充。
apue.3e--Makefile解剖顶层Makefile
DIRS = lib intro sockets advio daemons datafiles db environ \    fileio filedir ipc1 ipc2 proc pty relation signals standards \    stdio termios threadctl threads printer exercises
all:    for i in $(DIRS); do \        (cd $$i && echo "making $$i" && $(MAKE) ) || exit 1; \    done
clean:    for i in $(DIRS); do \        (cd $$i && echo "cleaning $$i" && $(MAKE) clean) || exit 1; \    done 解析如下:使用shell脚本的循环语句for,逐次进入各子目录里面执行make,并打印出making xxx。(其实该文件不算是一个Makefile,它更像是一个脚本,该脚本会在make的命令下执行,以便于提供子层make的环境)
而循环的次序也是有讲究的,所有的子层都会调用到libapue.a,所以第一个make的对象就是lib,接着可以随意。
cd xxx || exit 1;意味着进入脚本目录,否则退出。在make过程中,会因为缺失某个目录而直接make error停止编译。链接,需要注意的是,这里使用到了逻辑或运算符,执行有先后顺序,前者为真则不需要之后后者。
cd &&i,因为&是在Makefile中有特殊应用(自动变量),想执行shell命令,需要引用i,所以连续使用&符号。
参考链接:shell--for循环写法,链接
通用lib--Makefile
## Makefile for misc library.#ROOT=..PLATFORM=$(shell $(ROOT)/systype.sh)include $(ROOT)/Make.defines.$(PLATFORM)
LIBMISC    = libapue.aOBJS   = bufargs.o cliconn.o clrfl.o \            daemonize.o error.o errorlog.o lockreg.o locktest.o \            openmax.o pathalloc.o popen.o prexit.o prmask.o \            ptyfork.o ptyopen.o readn.o recvfd.o senderr.o sendfd.o \            servaccept.o servlisten.o setfd.o setfl.o signal.o signalintr.o \            sleepus.o spipe.o tellwait.o ttymodes.o writen.o
all:    $(LIBMISC) sleep.o
$(LIBMISC):    $(OBJS)    $(AR) rv $(LIBMISC) $?    $(RANLIB) $(LIBMISC)
clean:    rm -f *.o a.out core temp.* $(LIBMISC)
include $(ROOT)/Make.libapue.inc解析如下:定位顶层(编译根目录root),执行shell脚本,获取平台信息,然后include包含顶层Makefile(定义编译参数)
定义打包变量LIBMISC、打包变量的依赖文件OBJS(所需依赖的.o文件),多行连续语句只用\链接。
通过依赖关系自动生成的中间文件(.o),默认执行命令:CC CFLAGS -c -o xxx.o xxx.c,所以下面的make过程(第一加粗部分)就不难理解为何是统一的格式编译libapue.a的依赖对象。
all:    $(LIBMISC) sleep.o过程是先执行对象LIBMISC,然再执行sleep.o,Makefile都有按照编写顺序去执行。
打包ar过程,自动变量(LIBMISC所依赖的OBJS)的时间戳要比LIBMISC要新的时候进行打包(或更新打包)
clean规则,-f强制执行rm,*.o(这里作为中间文件),core是内存映像文件(程序崩溃产生),core链接,temp*是顶层传入
最后(所有子层都有)包含的Make.libapue.inc,确保规则的目标能够正确找到libapue.a(子层详细解析),因为使用include命令,所以该行(哪怕是在文件最后一行)都会首先执行的(第二次include执行)。
下面提供编译过程,详细对照思考
h265@H265:algs4-hhc$ make -C apue.3e/lib/ (单独编译lib,方便观察)make: Entering directory '/home/h265/sharefile/repos/trunk/algs4-hhc/apue.3e/lib' (make自动产生,无需编写)gcc -ansi -g -I../include -Wall -DLINUX -D_GNU_SOURCE   -c -o bufargs.o bufargs.cgcc -ansi -g -I../include -Wall -DLINUX -D_GNU_SOURCE   -c -o cliconn.o cliconn.c····省略相同格式但不同对象的编译过程,都生成对应的.o文件,提供ar打包使用····gcc -ansi -g -I../include -Wall -DLINUX -D_GNU_SOURCE   -c -o ttymodes.o ttymodes.cgcc -ansi -g -I../include -Wall -DLINUX -D_GNU_SOURCE   -c -o writen.o writen.car rv libapue.a bufargs.o cliconn.o clrfl.o daemonize.o error.o errorlog.o lockreg.o locktest.o openmax.o pathalloc.o popen.o prexit.o prmask.o ptyfork.o ptyopen.o readn.o recvfd.o senderr.o sendfd.o servaccept.o servlisten.o setfd.o setfl.o signal.o signalintr.o sleepus.o spipe.o tellwait.o ttymodes.o writen.oar: 正在创建 libapue.aa - bufargs.oa - cliconn.o····省略相同格式但不同对象,都是打包过程····a - ttymodes.oa - writen.oecho libapue.alibapue.agcc -ansi -g -I../include -Wall -DLINUX -D_GNU_SOURCE   -c -o sleep.o sleep.cmake: Leaving directory '/home/h265/sharefile/repos/trunk/algs4-hhc/apue.3e/lib'
Make.libapue.inc
$(LIBAPUE):    (cd $(ROOT)/lib && $(MAKE))解析如下:类似顶层目录,进入lib目录,执行make(子层详细解析该文件作用)
这其实也是一个规则,每个子层里面被包含都会被展开,那么也将会成为子层Makefile里面的一个规则,所以能够实现解决libapue.a缺失的情况。

顶层Makefile参数定义文件
# Common make definitions, customized for each platform
# Definitions required in all program directories to compile and link# C programs using gcc.
CC=gccCOMPILE.c=$(CC) $(CFLAGS) $(CPPFLAGS) -cLINK.c=$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS)LDFLAGS=LDDIR=-L$(ROOT)/libLDLIBS=$(LDDIR) -lapue $(EXTRALIBS)CFLAGS=-ansi -g -I$(ROOT)/include -Wall -DLINUX -D_GNU_SOURCE $(EXTRA)RANLIB=echoAR=arAWK=awkLIBAPUE=$(ROOT)/lib/libapue.a
# Common temp files to delete from each directory.TEMPFILES=core core.* *.o temp.* *.out参数解析:
#编译器CC:编译器COMPILE.c和LINK.c是内置变量COMPILE.c指明了自动依赖生成的执行命令,也就是CC CFLAGS -c -o xxx.o xxx.c的前面,后面是依赖关系导入的。(在以后,对CFLAGS的修改,不会改变自动依赖生成的执行命令)
后来测试发现,即时去掉这两个内置变量,自动推导关系依然不变,说明默认与上述设置是一致的,apue只是为了确保正确而编写。
LDFLAGS链接选项:缺省#链接LDDIR链接目录:lib目录LDLIBS链接参数:指定链接库-lapue及其所在目录#编译标志CFLAGS编译参数:指定-ansi(C89)标准、加入调试信息-g、包含头文件-I、打开所有警告-Wall、     -DLINUX:相当于在头文件上添加#define LINUX;根据需要,在头文件条件内容,链接#其它实用工具RANLIB仅打印作用AR打包AWK(暂时未明白)
LIBAPUE和TEMPFILES全局的编译自定义变量搜索路径解析:如果读者需要查看的话,可以在makefile里面的CFLAGS添加-v
gcc搜索头文件过程,配合编译选项-I#include "..." search starts here:#include <...> search starts here: ../include /usr/lib/gcc/i686-linux-gnu/6/include /usr/local/include /usr/lib/gcc/i686-linux-gnu/6/include-fixed /usr/include/i386-linux-gnu /usr/includeEnd of search list.
gcc搜索库过程,配合编译选项-LLIBRARY_PATH=/usr/lib/gcc/i686-linux-gnu/6/:/usr/lib/gcc/i686-linux-gnu/6/../../../i386-linux-gnu/:/usr/lib/gcc/i686-linux-gnu/6/../../../../lib/:/lib/i386-linux-gnu/:/lib/../lib/:/usr/lib/i386-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/i686-linux-gnu/6/../../../:/lib/:/usr/lib/
链接,头文件与库文件、搜索顺序、环境变量解析如下:完整定义CFLAGS,是因为子层都利用了依赖关系自动生成.o文件,命令格式如CC CFLAGS -c -o xxx.o xxx.c,所以需要赋予足够的自动编译信息来提供编译。CC和CFLAGS都是make命令默认的变量,由用户修改设定。

一般子层Makefile(以sockets为例)apue.3e/sockets/makefile
ROOT=..PLATFORM=$(shell $(ROOT)/systype.sh)include $(ROOT)/Make.defines.$(PLATFORM)
ifeq "$(PLATFORM)" "solaris"  EXTRALIBS = -lsocket -lnslendif
PROGS = ruptime ruptimed ruptimed-fd ruptimed-dgMOREPROGS = findsvc ruptime-dg
all:    $(PROGS) $(MOREPROGS) clconn.o clconn2.o initsrv1.o initsrv2.o
%:    %.c $(LIBAPUE)
ruptime:    ruptime.o clconn2.o $(LIBAPUE)        $(CC) $(CFLAGS) -o ruptime ruptime.o clconn2.o $(LDFLAGS) $(LDLIBS)
ruptimed:    ruptimed.o initsrv2.o $(LIBAPUE)        $(CC) $(CFLAGS) -o ruptimed ruptimed.o initsrv2.o $(LDFLAGS) $(LDLIBS)
ruptimed-fd:    ruptimed-fd.o initsrv2.o $(LIBAPUE)        $(CC) $(CFLAGS) -o ruptimed-fd ruptimed-fd.o initsrv2.o $(LDFLAGS) $(LDLIBS)
ruptimed-dg:    ruptimed-dg.o initsrv2.o $(LIBAPUE)        $(CC) $(CFLAGS) -o ruptimed-dg ruptimed-dg.o initsrv2.o $(LDFLAGS) $(LDLIBS)
clean:    rm -f $(PROGS) $(MOREPROGS) $(TEMPFILES) *.o
include $(ROOT)/Make.libapue.inc解析如下:make的条件语句,判断是否相等if-eq,对象是两个字符串
%:    %.c $(LIBAPUE)的意思是:可能是搜索到所有目前的.c和libapue文件
ruptime:    ruptime.o clconn2.o $(LIBAPUE)每一个子规则都添加上LIBAPUE,以此实现勘测到LIBAPUE是否存在的功能。
证明include $(ROOT)/Make.libapue.inc作用(不编译libapue.a情况下),并且将include $(ROOT)/Make.libapue.inc注释掉
h265@H265:algs4-hhc$ make -C apue.3e/sockets/make: Entering directory '/home/h265/sharefile/repos/trunk/algs4-hhc/apue.3e/sock etsgcc -ansi -g -I../include -Wall -DLINUX -D_GNU_SOURCE   -c -o ruptime.o ruptime.cgcc -ansi -g -I../include -Wall -DLINUX -D_GNU_SOURCE   -c -o clconn2.o clconn2.cmake: *** No rule to make target '../lib/libapue.a', needed by 'ruptime'。 停止。make: Leaving directory '/home/h265/sharefile/repos/trunk/algs4-hhc/apue.3e/socke ts验证过程:ruptime目标编译自动寻找依赖文件libapue.a(由顶层自定义变量传递LIBAPUE=$(ROOT)/lib/libapue.a),发现并没有(因为我们还没有编译过lib子层),所以失败了。
先对此子层执行clean,然后编译lib,在编译此子层,成功编译。
结论:include $(ROOT)/Make.libapue.inc能够在某目录缺少libapue.a依赖的情况下,重新编译lib子层,生成libapue.a。单独编译某个规则的目标,其包含的依赖,基本上是自身的.o和子层的公共模块(clconn_x和initsrv_x)以及liapue.a。
编译all目标,则会导入所有对象,并在文件当中找到对应的子目标(all-ruptime这个路径),然后执行。
通常,依赖库.a等还未编译,都可以从新包含库.a的Makefile,实现编译,并将.a放到规则的依赖上,实现自动寻找。

自动依赖生成(注意当前目录是子层sockets)单一编译对象h265@H265:sockets$ make ruptime (先编译好lib,不编译也可以,同样可以找到这些信息)gcc -ansi -g -I../include -Wall -DLINUX -D_GNU_SOURCE   -c -o ruptime.o ruptime.cgcc -ansi -g -I../include -Wall -DLINUX -D_GNU_SOURCE   -c -o clconn2.o clconn2.cgcc -ansi -g -I../include -Wall -DLINUX -D_GNU_SOURCE  -o ruptime ruptime.o clconn2.o  -L../lib -lapueall编译对象h265@H265:sockets$ make allgcc -ansi -g -I../include -Wall -DLINUX -D_GNU_SOURCE   -c -o ruptime.o ruptime.cgcc -ansi -g -I../include -Wall -DLINUX -D_GNU_SOURCE   -c -o clconn2.o clconn2.cgcc -ansi -g -I../include -Wall -DLINUX -D_GNU_SOURCE  -o ruptime ruptime.o clconn2.o  -L../lib -lapuegcc -ansi -g -I../include -Wall -DLINUX -D_GNU_SOURCE   -c -o ruptimed.o ruptimed.cgcc -ansi -g -I../include -Wall -DLINUX -D_GNU_SOURCE   -c -o initsrv2.o initsrv2.cgcc -ansi -g -I../include -Wall -DLINUX -D_GNU_SOURCE  -o ruptimed ruptimed.o initsrv2.o  -L../lib -lapuegcc -ansi -g -I../include -Wall -DLINUX -D_GNU_SOURCE   -c -o ruptimed-fd.o ruptimed-fd.cgcc -ansi -g -I../include -Wall -DLINUX -D_GNU_SOURCE  -o ruptimed-fd ruptimed-fd.o initsrv2.o  -L../lib -lapuegcc -ansi -g -I../include -Wall -DLINUX -D_GNU_SOURCE   -c -o ruptimed-dg.o ruptimed-dg.cgcc -ansi -g -I../include -Wall -DLINUX -D_GNU_SOURCE  -o ruptimed-dg ruptimed-dg.o initsrv2.o  -L../lib -lapuegcc -ansi -g -I../include -Wall -DLINUX -D_GNU_SOURCE    findsvc.c  -L../lib -lapue  -o findsvcgcc -ansi -g -I../include -Wall -DLINUX -D_GNU_SOURCE    ruptime-dg.c  -L../lib -lapue  -o ruptime-dggcc -ansi -g -I../include -Wall -DLINUX -D_GNU_SOURCE   -c -o clconn.o clconn.cgcc -ansi -g -I../include -Wall -DLINUX -D_GNU_SOURCE   -c -o initsrv1.o initsrv1.c
好了,到目前为止,已经基本解析了apue-Makefile的所有层了,如果你觉得还需要继续加深,可以往下看。
下面,是抽出部分值得思考的子层Makefile,都可以涉及不同的make知识点。子层apue.3e/datafiles/MakefileROOT=..PLATFORM=$(shell $(ROOT)/systype.sh)include $(ROOT)/Make.defines.$(PLATFORM)
PROGS =    strftime
all:    $(PROGS) getpwnam.o
%:    %.c $(LIBAPUE)    $(CC) $(CFLAGS) $@.c -o $@ $(LDFLAGS) $(LDLIBS)
clean:    rm -f $(PROGS) $(TEMPFILES) *.o
include $(ROOT)/Make.libapue.inc解析如下:%:    %.c $(LIBAPUE)    $(CC) $(CFLAGS) $@.c -o $@ $(LDFLAGS) $(LDLIBS)通用程序的处理:%作为通配符*,意在将所有%.c都按照下面的命令执行,并生成.o文件,同时也提供了libapue链接。

从这里开始,是从原理上去讲述Make的知识,仅供本人复习参考,无关人事可以离场。确实,不管Makefile如何复制,都不会脱离它的基本(也是唯一)的规则(条件):target: prerequisites     command·····················编译目标:依赖关系文件     执行命令
make的自动变量
自动变量含义举例
$@规则的目标
$<
$^规则的所有依赖文件
$?规则的所有依赖有更新的文件
$(@D)
$(@F)
下面是零散的Makefile知识点,无关人事请散场Makefile赋值运算符(:= ?= += =)链接 。即时、条件,追加,延迟、四种类型。
在Hi3516A/mpp/sample/venc/Makefile里面,有
$(TARGET):%:%.o $(COMM_OBJ)规则的目标为TARGET,所依赖为规则的依赖.o文件%:%.o(先%:搜索到TARGET的依赖文件,然后选择%.o文件),加上通用依赖COMM_OBJ比方说,TARGET为abc,那么%:%.o的结果就为abc.o编译过程:预处理--编译--汇编--链接
     .c——.i——.s——.o——.out(文件后缀变换过程)     在.c——.o,直接编译即可,不需要加入lib库,假设ab.c里面使用到线程,.c——.o过程,不需要加入-lpthread,因为这仅仅是汇编过程,不需要将库链接进去。每个子目录makefile改变某变量,不会影响到其他子目录makefile中相同的变量。
├── lib│   └── Makefile├── Makefile├── Makefile.param├── Make.libcomm.inc└── test    └── Makefile
Makefile.param--LDFLAGS为空测试过程:     lib/Makefile修改LDFLAGS+=-lrt,先编译lib,后编译test,在test中没有修改LDFLAGS,随后在test中任一目标的规则下打印LDFLAGS的值,发现为空。结论:成立。GNU 的 make 工作时的执行步骤入下:(想来其它的 make 也是类似)
1、读入所有的 Makefile。2、读入被 include 的其它 Makefile。3、初始化文件中的变量。4、推导隐晦规则,并分析所有规则。5、为所有的目标文件创建依赖关系链。6、根据依赖关系,决定哪些目标要重新生成。7、执行生成命令。
1-5 步为第一个阶段,6-7 为第二个阶段。第一个阶段中,如果定义的变量被使用了,那么,
make把其展开在使用的位置。但make并不会完全马上展开,make使用的是拖延战术,如 果变量出现在依赖关系的规则中,那么仅当这条依赖被决定要使用了,变量才会在其内部展开。
当然,这个工作方式你不一定要清楚,但是知道这个方式你也会对make更为熟悉。有了这个基础,后续部分也就容易看懂了。在makefile过程中,声明的变量(或者使用export声明),在当前makefile结束后,将被清理。不会对后来的makefile造成影响。

示例工程(Linux-32位编译通过)下载链接

总结Makefile工程编写依赖关系,您的工程结构要合理,有开发经验就很好理解了。每个子目录有当前的SRCS、OBJS、PROGS、MOREPROGS
几乎是必备的all、clean

编译选项,关键是CFLAGS、LDFLAGS、LIBS、REL_DIR等关键指定
编译平台,关键是CC、AR、LD等关键参数
编译参数,这个太多了,也是必须的,有些关键点是-g -O等等

解析的参考文档Makefile简要解析:http://blog.csdn.net/huyansoft/article/details/8924624http://blog.chinaunix.net/uid-21948941-id-3061864.html关于apue的解剖示例:http://onetracy.com/2015/10/25/Makefile/http://www.cnblogs.com/emrick/p/4960720.html在自定义工程中直接是使用apue.h与libapue.a,链接
随后,UNIX圣经还两本:《UNIX网络编程》卷1:套接字编程 卷2:进程间通信下面是编译指导:《网络编程》关于 UNIX网络编程 卷1 的 unp.h 和源码编译问题Unix网络编程 卷2:进程间通信(linux环境下源代码使用)(测试通过)本文内容:
- 最简单的Makefile知识点归纳,通过对apue-Makefile的内容,解剖出Makefile机制,并在此过程中,实践自己的algs4-hhc算法库工程(当然,是以学习Makefile为开始)- 这编写过程中,为了方便本人复习,会适当提供参考资料,部分内容超出Makefile知识范围,自行跳过。
阅读人群:
- 读者没有任何编译原理知识,可以先去补充(推荐阅读《嵌入式linux开发教程 上册》10.1.3.2:gcc编译过程;这极其重要。- 读者刚从windows转过来,一直使用IDE,也可以去补充。
apue.3e--Makefile解剖顶层Makefile
DIRS = lib intro sockets advio daemons datafiles db environ \    fileio filedir ipc1 ipc2 proc pty relation signals standards \    stdio termios threadctl threads printer exercises
all:    for i in $(DIRS); do \        (cd $$i && echo "making $$i" && $(MAKE) ) || exit 1; \    done
clean:    for i in $(DIRS); do \        (cd $$i && echo "cleaning $$i" && $(MAKE) clean) || exit 1; \    done 解析如下:使用shell脚本的循环语句for,逐次进入各子目录里面执行make,并打印出making xxx。(其实该文件不算是一个Makefile,它更像是一个脚本,该脚本会在make的命令下执行,以便于提供子层make的环境)
而循环的次序也是有讲究的,所有的子层都会调用到libapue.a,所以第一个make的对象就是lib,接着可以随意。
cd xxx || exit 1;意味着进入脚本目录,否则退出。在make过程中,会因为缺失某个目录而直接make error停止编译。链接,需要注意的是,这里使用到了逻辑或运算符,执行有先后顺序,前者为真则不需要之后后者。
cd &&i,因为&是在Makefile中有特殊应用(自动变量),想执行shell命令,需要引用i,所以连续使用&符号。
参考链接:shell--for循环写法,链接
通用lib--Makefile
## Makefile for misc library.#ROOT=..PLATFORM=$(shell $(ROOT)/systype.sh)include $(ROOT)/Make.defines.$(PLATFORM)
LIBMISC    = libapue.aOBJS   = bufargs.o cliconn.o clrfl.o \            daemonize.o error.o errorlog.o lockreg.o locktest.o \            openmax.o pathalloc.o popen.o prexit.o prmask.o \            ptyfork.o ptyopen.o readn.o recvfd.o senderr.o sendfd.o \            servaccept.o servlisten.o setfd.o setfl.o signal.o signalintr.o \            sleepus.o spipe.o tellwait.o ttymodes.o writen.o
all:    $(LIBMISC) sleep.o
$(LIBMISC):    $(OBJS)    $(AR) rv $(LIBMISC) $?    $(RANLIB) $(LIBMISC)
clean:    rm -f *.o a.out core temp.* $(LIBMISC)
include $(ROOT)/Make.libapue.inc解析如下:定位顶层(编译根目录root),执行shell脚本,获取平台信息,然后include包含顶层Makefile(定义编译参数)
定义打包变量LIBMISC、打包变量的依赖文件OBJS(所需依赖的.o文件),多行连续语句只用\链接。
通过依赖关系自动生成的中间文件(.o),默认执行命令:CC CFLAGS -c -o xxx.o xxx.c,所以下面的make过程(第一加粗部分)就不难理解为何是统一的格式编译libapue.a的依赖对象。
all:    $(LIBMISC) sleep.o过程是先执行对象LIBMISC,然再执行sleep.o,Makefile都有按照编写顺序去执行。
打包ar过程,自动变量(LIBMISC所依赖的OBJS)的时间戳要比LIBMISC要新的时候进行打包(或更新打包)
clean规则,-f强制执行rm,*.o(这里作为中间文件),core是内存映像文件(程序崩溃产生),core链接,temp*是顶层传入
最后(所有子层都有)包含的Make.libapue.inc,确保规则的目标能够正确找到libapue.a(子层详细解析),因为使用include命令,所以该行(哪怕是在文件最后一行)都会首先执行的(第二次include执行)。
下面提供编译过程,详细对照思考
h265@H265:algs4-hhc$ make -C apue.3e/lib/ (单独编译lib,方便观察)make: Entering directory '/home/h265/sharefile/repos/trunk/algs4-hhc/apue.3e/lib' (make自动产生,无需编写)gcc -ansi -g -I../include -Wall -DLINUX -D_GNU_SOURCE   -c -o bufargs.o bufargs.cgcc -ansi -g -I../include -Wall -DLINUX -D_GNU_SOURCE   -c -o cliconn.o cliconn.c····省略相同格式但不同对象的编译过程,都生成对应的.o文件,提供ar打包使用····gcc -ansi -g -I../include -Wall -DLINUX -D_GNU_SOURCE   -c -o ttymodes.o ttymodes.cgcc -ansi -g -I../include -Wall -DLINUX -D_GNU_SOURCE   -c -o writen.o writen.car rv libapue.a bufargs.o cliconn.o clrfl.o daemonize.o error.o errorlog.o lockreg.o locktest.o openmax.o pathalloc.o popen.o prexit.o prmask.o ptyfork.o ptyopen.o readn.o recvfd.o senderr.o sendfd.o servaccept.o servlisten.o setfd.o setfl.o signal.o signalintr.o sleepus.o spipe.o tellwait.o ttymodes.o writen.oar: 正在创建 libapue.aa - bufargs.oa - cliconn.o····省略相同格式但不同对象,都是打包过程····a - ttymodes.oa - writen.oecho libapue.alibapue.agcc -ansi -g -I../include -Wall -DLINUX -D_GNU_SOURCE   -c -o sleep.o sleep.cmake: Leaving directory '/home/h265/sharefile/repos/trunk/algs4-hhc/apue.3e/lib'
Make.libapue.inc
$(LIBAPUE):    (cd $(ROOT)/lib && $(MAKE))解析如下:类似顶层目录,进入lib目录,执行make(子层详细解析该文件作用)
这其实也是一个规则,每个子层里面被包含都会被展开,那么也将会成为子层Makefile里面的一个规则,所以能够实现解决libapue.a缺失的情况。

顶层Makefile参数定义文件
# Common make definitions, customized for each platform
# Definitions required in all program directories to compile and link# C programs using gcc.
CC=gccCOMPILE.c=$(CC) $(CFLAGS) $(CPPFLAGS) -cLINK.c=$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS)LDFLAGS=LDDIR=-L$(ROOT)/libLDLIBS=$(LDDIR) -lapue $(EXTRALIBS)CFLAGS=-ansi -g -I$(ROOT)/include -Wall -DLINUX -D_GNU_SOURCE $(EXTRA)RANLIB=echoAR=arAWK=awkLIBAPUE=$(ROOT)/lib/libapue.a
# Common temp files to delete from each directory.TEMPFILES=core core.* *.o temp.* *.out参数解析:
#编译器CC:编译器COMPILE.c和LINK.c是内置变量COMPILE.c指明了自动依赖生成的执行命令,也就是CC CFLAGS -c -o xxx.o xxx.c的前面,后面是依赖关系导入的。(在以后,对CFLAGS的修改,不会改变自动依赖生成的执行命令)
后来测试发现,即时去掉这两个内置变量,自动推导关系依然不变,说明默认与上述设置是一致的,apue只是为了确保正确而编写。
LDFLAGS链接选项:缺省#链接LDDIR链接目录:lib目录LDLIBS链接参数:指定链接库-lapue及其所在目录#编译标志CFLAGS编译参数:指定-ansi(C89)标准、加入调试信息-g、包含头文件-I、打开所有警告-Wall、     -DLINUX:相当于在头文件上添加#define LINUX;根据需要,在头文件条件内容,链接#其它实用工具RANLIB仅打印作用AR打包AWK(暂时未明白)
LIBAPUE和TEMPFILES全局的编译自定义变量搜索路径解析:如果读者需要查看的话,可以在makefile里面的CFLAGS添加-v
gcc搜索头文件过程,配合编译选项-I#include "..." search starts here:#include <...> search starts here: ../include /usr/lib/gcc/i686-linux-gnu/6/include /usr/local/include /usr/lib/gcc/i686-linux-gnu/6/include-fixed /usr/include/i386-linux-gnu /usr/includeEnd of search list.
gcc搜索库过程,配合编译选项-LLIBRARY_PATH=/usr/lib/gcc/i686-linux-gnu/6/:/usr/lib/gcc/i686-linux-gnu/6/../../../i386-linux-gnu/:/usr/lib/gcc/i686-linux-gnu/6/../../../../lib/:/lib/i386-linux-gnu/:/lib/../lib/:/usr/lib/i386-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/i686-linux-gnu/6/../../../:/lib/:/usr/lib/
链接,头文件与库文件、搜索顺序、环境变量解析如下:完整定义CFLAGS,是因为子层都利用了依赖关系自动生成.o文件,命令格式如CC CFLAGS -c -o xxx.o xxx.c,所以需要赋予足够的自动编译信息来提供编译。CC和CFLAGS都是make命令默认的变量,由用户修改设定。

一般子层Makefile(以sockets为例)apue.3e/sockets/makefile
ROOT=..PLATFORM=$(shell $(ROOT)/systype.sh)include $(ROOT)/Make.defines.$(PLATFORM)
ifeq "$(PLATFORM)" "solaris"  EXTRALIBS = -lsocket -lnslendif
PROGS = ruptime ruptimed ruptimed-fd ruptimed-dgMOREPROGS = findsvc ruptime-dg
all:    $(PROGS) $(MOREPROGS) clconn.o clconn2.o initsrv1.o initsrv2.o
%:    %.c $(LIBAPUE)
ruptime:    ruptime.o clconn2.o $(LIBAPUE)        $(CC) $(CFLAGS) -o ruptime ruptime.o clconn2.o $(LDFLAGS) $(LDLIBS)
ruptimed:    ruptimed.o initsrv2.o $(LIBAPUE)        $(CC) $(CFLAGS) -o ruptimed ruptimed.o initsrv2.o $(LDFLAGS) $(LDLIBS)
ruptimed-fd:    ruptimed-fd.o initsrv2.o $(LIBAPUE)        $(CC) $(CFLAGS) -o ruptimed-fd ruptimed-fd.o initsrv2.o $(LDFLAGS) $(LDLIBS)
ruptimed-dg:    ruptimed-dg.o initsrv2.o $(LIBAPUE)        $(CC) $(CFLAGS) -o ruptimed-dg ruptimed-dg.o initsrv2.o $(LDFLAGS) $(LDLIBS)
clean:    rm -f $(PROGS) $(MOREPROGS) $(TEMPFILES) *.o
include $(ROOT)/Make.libapue.inc解析如下:make的条件语句,判断是否相等if-eq,对象是两个字符串
%:    %.c $(LIBAPUE)的意思是:可能是搜索到所有目前的.c和libapue文件
ruptime:    ruptime.o clconn2.o $(LIBAPUE)每一个子规则都添加上LIBAPUE,以此实现勘测到LIBAPUE是否存在的功能。
证明include $(ROOT)/Make.libapue.inc作用(不编译libapue.a情况下),并且将include $(ROOT)/Make.libapue.inc注释掉
h265@H265:algs4-hhc$ make -C apue.3e/sockets/make: Entering directory '/home/h265/sharefile/repos/trunk/algs4-hhc/apue.3e/sock etsgcc -ansi -g -I../include -Wall -DLINUX -D_GNU_SOURCE   -c -o ruptime.o ruptime.cgcc -ansi -g -I../include -Wall -DLINUX -D_GNU_SOURCE   -c -o clconn2.o clconn2.cmake: *** No rule to make target '../lib/libapue.a', needed by 'ruptime'。 停止。make: Leaving directory '/home/h265/sharefile/repos/trunk/algs4-hhc/apue.3e/socke ts验证过程:ruptime目标编译自动寻找依赖文件libapue.a(由顶层自定义变量传递LIBAPUE=$(ROOT)/lib/libapue.a),发现并没有(因为我们还没有编译过lib子层),所以失败了。
先对此子层执行clean,然后编译lib,在编译此子层,成功编译。
结论:include $(ROOT)/Make.libapue.inc能够在某目录缺少libapue.a依赖的情况下,重新编译lib子层,生成libapue.a。单独编译某个规则的目标,其包含的依赖,基本上是自身的.o和子层的公共模块(clconn_x和initsrv_x)以及liapue.a。
编译all目标,则会导入所有对象,并在文件当中找到对应的子目标(all-ruptime这个路径),然后执行。
通常,依赖库.a等还未编译,都可以从新包含库.a的Makefile,实现编译,并将.a放到规则的依赖上,实现自动寻找。

自动依赖生成(注意当前目录是子层sockets)单一编译对象h265@H265:sockets$ make ruptime (先编译好lib,不编译也可以,同样可以找到这些信息)gcc -ansi -g -I../include -Wall -DLINUX -D_GNU_SOURCE   -c -o ruptime.o ruptime.cgcc -ansi -g -I../include -Wall -DLINUX -D_GNU_SOURCE   -c -o clconn2.o clconn2.cgcc -ansi -g -I../include -Wall -DLINUX -D_GNU_SOURCE  -o ruptime ruptime.o clconn2.o  -L../lib -lapueall编译对象h265@H265:sockets$ make allgcc -ansi -g -I../include -Wall -DLINUX -D_GNU_SOURCE   -c -o ruptime.o ruptime.cgcc -ansi -g -I../include -Wall -DLINUX -D_GNU_SOURCE   -c -o clconn2.o clconn2.cgcc -ansi -g -I../include -Wall -DLINUX -D_GNU_SOURCE  -o ruptime ruptime.o clconn2.o  -L../lib -lapuegcc -ansi -g -I../include -Wall -DLINUX -D_GNU_SOURCE   -c -o ruptimed.o ruptimed.cgcc -ansi -g -I../include -Wall -DLINUX -D_GNU_SOURCE   -c -o initsrv2.o initsrv2.cgcc -ansi -g -I../include -Wall -DLINUX -D_GNU_SOURCE  -o ruptimed ruptimed.o initsrv2.o  -L../lib -lapuegcc -ansi -g -I../include -Wall -DLINUX -D_GNU_SOURCE   -c -o ruptimed-fd.o ruptimed-fd.cgcc -ansi -g -I../include -Wall -DLINUX -D_GNU_SOURCE  -o ruptimed-fd ruptimed-fd.o initsrv2.o  -L../lib -lapuegcc -ansi -g -I../include -Wall -DLINUX -D_GNU_SOURCE   -c -o ruptimed-dg.o ruptimed-dg.cgcc -ansi -g -I../include -Wall -DLINUX -D_GNU_SOURCE  -o ruptimed-dg ruptimed-dg.o initsrv2.o  -L../lib -lapuegcc -ansi -g -I../include -Wall -DLINUX -D_GNU_SOURCE    findsvc.c  -L../lib -lapue  -o findsvcgcc -ansi -g -I../include -Wall -DLINUX -D_GNU_SOURCE    ruptime-dg.c  -L../lib -lapue  -o ruptime-dggcc -ansi -g -I../include -Wall -DLINUX -D_GNU_SOURCE   -c -o clconn.o clconn.cgcc -ansi -g -I../include -Wall -DLINUX -D_GNU_SOURCE   -c -o initsrv1.o initsrv1.c
好了,到目前为止,已经基本解析了apue-Makefile的所有层了,如果你觉得还需要继续加深,可以往下看。
下面,是抽出部分值得思考的子层Makefile,都可以涉及不同的make知识点。子层apue.3e/datafiles/MakefileROOT=..PLATFORM=$(shell $(ROOT)/systype.sh)include $(ROOT)/Make.defines.$(PLATFORM)
PROGS =    strftime
all:    $(PROGS) getpwnam.o
%:    %.c $(LIBAPUE)    $(CC) $(CFLAGS) $@.c -o $@ $(LDFLAGS) $(LDLIBS)
clean:    rm -f $(PROGS) $(TEMPFILES) *.o
include $(ROOT)/Make.libapue.inc解析如下:%:    %.c $(LIBAPUE)    $(CC) $(CFLAGS) $@.c -o $@ $(LDFLAGS) $(LDLIBS)通用程序的处理:%作为通配符*,意在将所有%.c都按照下面的命令执行,并生成.o文件,同时也提供了libapue链接。

从这里开始,是从原理上去讲述Make的知识,仅供本人复习参考,无关人事可以离场。确实,不管Makefile如何复制,都不会脱离它的基本(也是唯一)的规则(条件):target: prerequisites     command·····················编译目标:依赖关系文件     执行命令
make的自动变量
自动变量含义举例
$@规则的目标
$<
$^规则的所有依赖文件
$?规则的所有依赖有更新的文件
$(@D)
$(@F)
下面是零散的Makefile知识点,无关人事请散场Makefile赋值运算符(:= ?= += =)链接 。即时、条件,追加,延迟、四种类型。
在Hi3516A/mpp/sample/venc/Makefile里面,有
$(TARGET):%:%.o $(COMM_OBJ)规则的目标为TARGET,所依赖为规则的依赖.o文件%:%.o(先%:搜索到TARGET的依赖文件,然后选择%.o文件),加上通用依赖COMM_OBJ比方说,TARGET为abc,那么%:%.o的结果就为abc.o编译过程:预处理--编译--汇编--链接
     .c——.i——.s——.o——.out(文件后缀变换过程)     在.c——.o,直接编译即可,不需要加入lib库,假设ab.c里面使用到线程,.c——.o过程,不需要加入-lpthread,因为这仅仅是汇编过程,不需要将库链接进去。每个子目录makefile改变某变量,不会影响到其他子目录makefile中相同的变量。
├── lib│   └── Makefile├── Makefile├── Makefile.param├── Make.libcomm.inc└── test    └── Makefile
Makefile.param--LDFLAGS为空测试过程:     lib/Makefile修改LDFLAGS+=-lrt,先编译lib,后编译test,在test中没有修改LDFLAGS,随后在test中任一目标的规则下打印LDFLAGS的值,发现为空。结论:成立。GNU 的 make 工作时的执行步骤入下:(想来其它的 make 也是类似)
1、读入所有的 Makefile。2、读入被 include 的其它 Makefile。3、初始化文件中的变量。4、推导隐晦规则,并分析所有规则。5、为所有的目标文件创建依赖关系链。6、根据依赖关系,决定哪些目标要重新生成。7、执行生成命令。
1-5 步为第一个阶段,6-7 为第二个阶段。第一个阶段中,如果定义的变量被使用了,那么,
make把其展开在使用的位置。但make并不会完全马上展开,make使用的是拖延战术,如 果变量出现在依赖关系的规则中,那么仅当这条依赖被决定要使用了,变量才会在其内部展开。
当然,这个工作方式你不一定要清楚,但是知道这个方式你也会对make更为熟悉。有了这个基础,后续部分也就容易看懂了。在makefile过程中,声明的变量(或者使用export声明),在当前makefile结束后,将被清理。不会对后来的makefile造成影响。

示例工程(Linux-32位编译通过)下载链接

总结Makefile工程编写依赖关系,您的工程结构要合理,有开发经验就很好理解了。每个子目录有当前的SRCS、OBJS、PROGS、MOREPROGS
几乎是必备的all、clean

编译选项,关键是CFLAGS、LDFLAGS、LIBS、REL_DIR等关键指定
编译平台,关键是CC、AR、LD等关键参数
编译参数,这个太多了,也是必须的,有些关键点是-g -O等等

解析的参考文档Makefile简要解析:http://blog.csdn.net/huyansoft/article/details/8924624http://blog.chinaunix.net/uid-21948941-id-3061864.html关于apue的解剖示例:http://onetracy.com/2015/10/25/Makefile/http://www.cnblogs.com/emrick/p/4960720.html在自定义工程中直接是使用apue.h与libapue.a,链接
随后,UNIX圣经还两本:《UNIX网络编程》卷1:套接字编程 卷2:进程间通信下面是编译指导:《网络编程》关于 UNIX网络编程 卷1 的 unp.h 和源码编译问题Unix网络编程 卷2:进程间通信(linux环境下源代码使用)(测试通过)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: