linux编译----makefile的思考与总结

在一个大型项目中,要知晓代码结构,顶层makefile的分析是比不可少的

首先先看顶层makefile的分析,这是一个实际的公司的makefile,可能回涉及一些专业东西看不太懂,忽略即可

先来分析顶层makefile

# define BNT6000 Terminal's release version.
VERSION = 3
PATCHLEVEL = 0
SUBLEVEL = 0
EXTRAVERSION = 1

ACA_RELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL).$(EXTRAVERSION)
#定义了一些参数,主要用于生成产品版本号
include Makefile.inc #在 Makefile 使用 include 关键字可以把别的 Makefile 包含进来,这很像 C 语言的 #include,被包含的文件会原模原样的放在当前文件的包含位置。 #makefile.inc其实就是定义了一些编译参数,目录,以及编译方法!

# directories visited install, programs, check, etc. # acadb # export TOPDIR :
= $(shell pwd) #如果你要传递变量到下级 Makefile 中,那么你可以使用这样的声明: export export MAINDIR := $(TOPDIR)/main export SDKDIR := $(TOPDIR)/sdk export INCDIR := $(TOPDIR)/util
#subdir都是一些子目录 SUBDIRS
=util protocol netcomm STM32COMM center Rs232 record record/2012 audio lcd printer update jilt BD $(module_dir) format apply main pack
all:     bnt6000
bnt6000:    copy_lib
    @echo "#define BUILD_DATE            \"`date +%Y-%m-%d`\"" > $(INCDIR)/version.h
    @echo "#define SW_VER                \"$(SOFTWARE_VERSION)\"" >> $(INCDIR)/version.h
    @echo "#define BUILD_SVN_NO        \"`svn info | grep "Last Changed Rev: " | sed -e "s/Last Changed Rev: //g"`\"" >> $(INCDIR)/version.h
    @echo "#define SW_VERSION            SW_VER\",build:\"BUILD_SVN_NO\":\"BUILD_DATE\"\\0\"" >> $(INCDIR)/version.h
    @echo "#define PRODUCT_TYPE            \"$(PRODUCT_TYPE)\"" >> $(INCDIR)/version.h
    
    @for d in $(SUBDIRS); do (cd $$d && $(MAKE)); done # 一种语法结构,进入子目录并且分别编译!
    
copy_lib:
ifeq ($(COMM_MODULE), G3)
    cp audio/libsqlite3-3g.so -dpRf audio/libsqlite3.so
    cp util/libcurl_3g.so.4 -dpRf util/libcurl.so
    cp util/libzlog_3g.so.1.1 -dpRf util/libzlog.so
else
    cp audio/libsqlite3-2g.so -dpRf audio/libsqlite3.so
    cp util/libcurl_2g.so.4 -dpRf util/libcurl.so
    cp util/libzlog_2g.so.1.1 -dpRf util/libzlog.so
endif

install:    auth
#    @if [ "$LOGNAME" != "root" ]; then echo "Only root can install, quit!"; exit 1; fi
#    mkdir -p $(HOME)/workspace
    mkdir -p $(PREFIX)
    mkdir -p $(PREFIX)/bin $(PREFIX)/etc $(PREFIX)/lib $(PREFIX)/include
    @for d in $(SUBDIRS); do (cd $$d && $(MAKE) install); done
#    chown root.root $(PREFIX)/bin/*

uninstall:
#    @if [ "$LOGNAME" != "root" ]; then echo "Only root can uninstall, quit!"; exit 1; fi
    @for d in $(SUBDIRS); do (cd $$d && $(MAKE) uninstall); done
    @rm -f $(PREFIX)/include/*.h
    @rm -f $(PREFIX)/lib/*.a

img:
    ./main/mkimg.ramfs

build:
    @echo "just for packing atomically---------------------------------------------start---------"
    cd ./bin&& ./build.sh

clean:
    @for d in $(SUBDIRS); do (cd $$d && $(MAKE) clean); done    
    make clean -C av

help:
    @echo "Auth Server makefile."
    @echo "version : $(ACA_RELEASE)"
    @echo
    @echo "Syntax:"
    @echo "    aca    -- make all auth source code. It is default make."
    @echo "    clean    -- make clean all auth source code."
    @echo "    install    -- make all auth source code, and then install programs to DESDIR."
    @echo "    uninstall -- uninstall programs from DESDIR."
    @echo "    help    -- print this help."

# Define ACA Release Version.
version.h: ./Makefile
    @echo "/*" > .ver
    @echo " * version.h" >> .ver
    @echo " *" >> .ver
    @echo " * Copyright (C) 2007    UNIS." >> .ver
    @echo " *" >> .ver
    @echo " * ACA Release Version." >> .ver
    @echo " * This file comes from Makefile. Do not modify it manually." >> .ver
    @echo " */" >> .ver
    @echo >> .ver
    @echo \#define ACA_RELEASE \"$(ACA_RELEASE)\" >> .ver
    @echo \#define ACA_VERSION_CODE `expr $(VERSION) \\* 65536 + $(PATCHLEVEL) \\* 256 + $(SUBLEVEL)` >> .ver
    @echo '#define ACA_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))' >>.ver
    @mv -f .ver $@
    @sed '/[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*/ c $(ACA_RELEASE)' etc/version > /tmp/version.1
    @cp -dpRf /tmp/version.1 etc/version
    @rm -f /tmp/version

 

include Makefile.inc

  在 Makefile 使用 include 关键字可以把别的 Makefile 包含进来,这很像 C 语言的 #include,被包含的文件会原模原样的放在当前文件的包含位置。

  include 的语法是: include filename

  可以是当前操作系统 Shell 的文件模式(可以保含路径和通配符) 在 include 前面可以有一些空字符,但是绝不能是[Tab]键开始。

  include 和可以用一个或 多个空格隔开。举个例子,你有这样几个 Makefile:a.mk、b.mk、c.mk,还有一个文件叫 foo.make,以及一个变量$(bar),其包含了 e.mk 和 f.mk,那么,下面的语句: include foo.make *.mk $(bar) 等价于: include foo.make a.mk b.mk c.mk e.mk f.mk make 命令开始时,会把找寻 include 所指出的其它 Makefile,并把其内容安置在当前 的位。

  就好像 C/C++的#include 指令一样。如果文件都没有指定绝对路径或是相对路径的话, make 会在当前目录下首先寻找,如果当前目录下没有找到,那么,make 还会在下面的几个 目录下找:

   1、如果 make 执行时,有“-I”或“--include-dir”参数,那么 make 就会在这个参数 所指定的目录下去寻找。

   2、如果目录/include(一般是:/usr/local/bin 或/usr/include)存在的话, make 也会去找。如果有文件没有找到的话,make 会生成一条警告信息,但不会马上出现致 命错误。它会继续载入其它的文件,一旦完成 makefile 的读取,make 会再重试这些没有找 到,或是不能读取的文件,如果还是不行,make 才会出现一条致命信息。如果你想让 make 不理那些无法读取的文件,而继续执行,你可以在 include 前加一个减号“-”。

  如: -include 其表示,无论 include 过程中出现什么错误,都不要报错继续执行。和其它版本 make 兼 容的相关命令是 sinclude,其作用和这一个是一样的。 

  

  export TOPDIR := $(shell pwd) #在makefile中执行shell命令,就是当前路径   export MAINDIR := $(TOPDIR)/main   export SDKDIR := $(TOPDIR)/sdk   export INCDIR := $(TOPDIR)/util

 

 

   如果你要传递变量到下级 Makefile 中,那么你可以使用这样的声明: export 如果你不想让某些变量传递到下级 Makefile 中,那么你可以这样声明: unexport

   如: 示例一: export variable = value 其等价于:

          variable = value      

          export variable 

    其等价于: export variable := value 其等价于:

          variable := value      

          export variable

      示例二: export variable += value 其等价于:

         variable += value

        export variable

    如果你要传递所有的变量,那么,只要一个 export 就行了。后面什么也不用跟,表示 传递所有的变量。 需要注意的是,有两个变量,一个是 SHELL,一个是 MAKEFLAGS,这两个变量不管你是 否 export,其总是要传递到下层 Makefile 中,特别是 MAKEFILES 变量,其中包含了 make 的参数信息,如果我们执行“总控 Makefile”时有 make 参数或是在上层 Makefile 中定义 了这个变量,那么 MAKEFILES 变量将会是这些参数,并会传递到下层 Makefile 中,这是一 个系统级的环境变量。

 

all:     bnt6000
bnt6000:    copy_lib #这而并没有用伪目标,因为只有一个需要生成,这种写法每次都会编译,因为all目标总是不存在的

   伪目标一般没有依赖的文件。但是,我们也可以为伪目标指定所依赖的文件。伪目标同 样可以作为“默认目标”,只要将其放在第一个。

  一个示例就是,如果你的 Makefile 需要 一口气生成若干个可执行文件,但你只想简单地敲一个 make 完事,并且,所有的目标文件 都写在一个 Makefile 中,那么你可以使用“伪目标”这个特性:

          all : prog1 prog2 prog3

          .PHONY : all

        prog1 : prog1.o utils.o cc -o prog1 prog1.o utils.o

        prog2 : prog2.o cc -o prog2 prog2.o

        prog3 : prog3.o sort.o utils.o cc -o prog3 prog3.o sort.o utils.o

   我们知道,Makefile 中的第一个目标会被作为其默认目标。我们声明了一个“all”的 伪目标,其依赖于其它三个目标。由于伪目标的特性是,总是被执行的,所以其依赖的那三 个目标就总是不如“all”这个目标新。所以,其它三个目标的规则总是会被决议。也就达 到了我们一口气生成多个目标的目的。“.PHONY : all”声明了“all”这个目标为“伪目 标”。 随便提一句,从上面的例子我们可以看出,目标也可以成为依赖。所以,伪目标同样也 可成为依赖。看下面的例子:

   .PHONY: cleanall cleanobj cleandiff

   cleanall : cleanobj cleandiff

    rm program

  cleanobj :

    rm *.o

  cleandiff :

    rm *.diff

  “make clean”将清除所有要被清除的文件。“cleanobj”和“cleandiff”这两个伪 目标有点像“子程序”的意思。我们可以输入“make cleanall”和“make cleanobj”和 “make cleandiff”命令来达到清除不同种类文件的目的。

copy_lib:
ifeq ($(COMM_MODULE), G3)
    cp audio/libsqlite3-3g.so -dpRf audio/libsqlite3.so
    cp util/libcurl_3g.so.4 -dpRf util/libcurl.so
    cp util/libzlog_3g.so.1.1 -dpRf util/libzlog.so   #做了一些拷贝库的工作
else
    cp audio/libsqlite3-2g.so -dpRf audio/libsqlite3.so
    cp util/libcurl_2g.so.4 -dpRf util/libcurl.so
    cp util/libzlog_2g.so.1.1 -dpRf util/libzlog.so
endif

我们可以从上面的示例中看到三个关键字:ifeq、else 和 endif。ifeq 的意思表示条 件语句的开始,并指定一个条件表达式,表达式包含两个参数,以逗号分隔,表达式以圆括 号括起。else 表示条件表达式为假的情况。endif 表示一个条件语句的结束,任何一个条件 表达式都应该以 endif 结束。

 第一个是我们前面所见过的“ifeq”

    ifeq (arg1,arg2 )

     ifeq 'arg1'  'arg2'

    ifeq "arg1"  "arg2"

    ifeq "arg1"  'arg2'

     ifeq 'arg1'  "arg2"

比较参数“arg1”和“arg2”的值是否相同。当然,参数中我们还可以使用 make 的函数。

 

 

#subdir都是一些子目录
SUBDIRS=util protocol   netcomm STM32COMM center Rs232  record record/2012 audio  lcd printer update jilt BD $(module_dir) format apply main pack

 

 

 

 

 

   @echo "#define BUILD_DATE            \"`date +%Y-%m-%d`\"" > $(INCDIR)/version.h
    @echo "#define SW_VER                \"$(SOFTWARE_VERSION)\"" >> $(INCDIR)/version.h
    @echo "#define BUILD_SVN_NO        \"`svn info | grep "Last Changed Rev: " | sed -e "s/Last Changed Rev: //g"`\"" >> $(INCDIR)/version.h
    @echo "#define SW_VERSION            SW_VER\",build:\"BUILD_SVN_NO\":\"BUILD_DATE\"\\0\"" >> $(INCDIR)/version.h
    @echo "#define PRODUCT_TYPE            \"$(PRODUCT_TYPE)\"" >> $(INCDIR)/version.h
生成一些参数到version.h中,这个与打包有关,暂时不用管
主要是
`date +%Y-%m-%d`用来取得命令的执行结果

显示命令
通常,make 会把其要执行的命令行在命令执行前输出到屏幕上。当我们用“@”字符在 命令行前,那么,这个命令将不被 make 显示出来,
最具代表性的例子是,我们用这个功能 来像屏幕显示一些信息。
如: @echo 正在编译 XXX 模块...... 当 make 执行时,会输出“正在编译 XXX 模块......”字串
但不会输出命令,如果没 有“@”,那么,make 将输出:
         echo 正在编译 XXX 模块......
         正在编译 XXX 模块......
如果 make 执行时,带入 make 参数“-n”或“--just-print”,那么其只是显示命令, 但不会执行命令,这个功能很有利于我们调试我们的 Makefile,看看我们书写的命令是执 行起来是什么样子的或是什么顺序的。 而 make 参数“-s”或“--slient”则是全面禁止命令的显示

 

 @for d in $(SUBDIRS); do (cd $$d && $(MAKE)); done # 一种语法结构,进入子目录并且分别编译!
其实可以优化成
@for d in $(SUBDIRS); do ( $(MAKE) -C $$d); done
使用“-C”参数来指定 make 下层 Makefile
 

 

$$表示$,用来shell下引用变量,而$A或者$(A)则是Makefile的变量。

rule_1

  for i in 1 2 3 4 5; do echo $(i); done

上面的代码不会连续打印 1 2 3 4 5 

但下面的代码会:

rule_1:
  for i in 1 2 3 4 5; do echo $$(i); done

如下实例:

  files=main.exe a.exe b.exe  

all:  

   for name in `echo $(files) | sed s/.exe//g`; \  

  do \  

         rm -f "$$name".o; \  

     done  

 

makefile.inc

SHELL        = /bin/bash

SOFTWARE_VERSION    = v4.3.18
#
# You can overwrite these flags with make ARCH=xxx CROSS_COMPILE=xxx
#
#ARCH        = x86
ARCH        = HI3515


# GSM = 中国移动GPRS, CDMA=支持中国电信CDMA网
#COMM_MODULE    = GSM
COMM_MODULE    = G3
#COMM_MODULE    = CDMA

#PRODUCT_TYPE    = BNT5000
PRODUCT_TYPE    = BNT4000HD
#PRODUCT_TYPE    = BNT4000HG
#PRODUCT_TYPE    = BNT4000HC


# V_GU=通用,V_WK=网阔, V_NQ=西藏纳曲
#Commpany    = -DV_GU
Commpany    = -DV_WK
#Commpany    = -DV_NQ
#Commpany    =     

# JT808=标准808协议, JT808_EX=支持补充JT808协议
#PROTOCOL = -DJT808
PROTOCOL = -DJT808_EX

ifeq ($(PRODUCT_TYPE), BNT4000HD)
CROSS_COMPILE        =    arm-hismall-linux-
CFLAGS                    =    -O -Wall -Wno-deprecated -DDEBUG -Dhi3515 -D$(PRODUCT_TYPE) $(PROTOCOL) $(D_MODULE) $(Commpany)
module_dir             += av 
CXX=
else
CROSS_COMPILE        = arm-none-linux-gnueabi-
CXX=
CFLAGS                    = -O -Wall -Wno-deprecated -DDEBUG  $(PROTOCOL)  $(D_MODULE) $(Commpany)
module_dir             = 
endif

# G网
ifeq ($(COMM_MODULE), GSM)
D_MODULE=-DGSM
MODULE_INC_DIR=-I../newgprs
MODULE_LIB_DIR=-L../newgprs
MODULE_LIB=-lnewgprs
module_dir += newgprs
# C网
else ifeq ($(COMM_MODULE), CDMA)
D_MODULE=-DCDMA
MODULE_INC_DIR=-I../cdma
MODULE_LIB_DIR=-L../cdma
MODULE_LIB=-lcdma
module_dir += cdma
else ifeq ($(COMM_MODULE), G3)
D_MODULE=  -DG3
MODULE_INC_DIR=-I../av/3G -I../av/upgrade
MODULE_LIB_DIR=-L../av/3G -L../av/upgrade
MODULE_LIB=../av/3G/lib3G.a ../av/upgrade/libupgrade.a 
module_dir += av/3G av/upgrade
endif
#
# Include the make variables (CC, etc...)
#

CXX        = $(CROSS_COMPILE)g++
AS        = $(CROSS_COMPILE)as
LD        = $(CROSS_COMPILE)ld
CC        = $(CROSS_COMPILE)g++
CPP        = $(CC) -E
AR        = $(CROSS_COMPILE)ar
RANLIB        = $(CROSS_COMPILE)ranlib
STRIP        = $(CROSS_COMPILE)strip
MAKE        = make


ALLFLAGS    = $(CFLAGS) $(CXXFLAGS) $(CPPFLAGS)


#
# Explicit rules
#
%.o: %.c
    $(CXX) $(ALLFLAGS) $(INCLUDEPATH) -o $@ $< -c

%.o: %.cpp
    $(CXX) $(ALLFLAGS) $(INCLUDEPATH) -o $@ $< -c

%.o: %.cc
    $(CXX) $(ALLFLAGS) $(INCLUDEPATH) -o $@ $< -c


#
# Directorys
#
# You can override this by pass DESTDIR=xxx to the make
AUTH_ROOT    = /auth
PREFIX        = /bnt6000
#

#
# dependency
#
ACE_ROOT=/usr/local/ace
#PGSQL_ROOT=/usr/local/pgsql
PGSQL_ROOT=/usr
MC_ROOT=/mc
BOOST_ROOT=/usr/local/boost
BOOST_INCLUDE=$(BOOST_ROOT)/include/boost-1_33_1
BOOST_LIB=$(BOOST_ROOT)/lib

 

#
# Explicit rules
#
%.o: %.c
    $(CXX) $(ALLFLAGS) $(INCLUDEPATH) -o $@ $< -c

%.o: %.cpp
    $(CXX) $(ALLFLAGS) $(INCLUDEPATH) -o $@ $< -c

%.o: %.cc
    $(CXX) $(ALLFLAGS) $(INCLUDEPATH) -o $@ $< -c
上面指定的隐式规则,说明,即如果makefile中没有说明如何生成.o 的默认生成规则,说明.o如何生成.o
那么在makefie中你就可以按如下规则
test:*.o
  
$(CXX) $(CPPFLAG) $< -o$@
而不用指明如何生成.o!
makefile也有自己的隐式规则
“$@”(自动化变量),这个变量表示着目前规则中所有的目标的集合,
$@ 表示规则中的目标文件集。在模式规则中,如果有多个目标,那么,"$@"就是匹配于 目标中模式定义的集合。
$< 依赖目标中的第一个目标名字。如果依赖目标是以模式(即"%")定义的,那么"$<"将 是符合模式的一系列的文件集。注意,其是一个一个取出来的。
  
%.o : %.c
  $(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@
其中,"$@"表示所有的目标,"$<"表示了所有依赖目标的挨个值。这些奇怪的 变量我们叫"自动化变量"
posted @ 2018-07-24 20:37  白伟碧一些小心得  阅读(8476)  评论(0编辑  收藏  举报