• 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 $@
    其中,"$@"表示所有的目标的挨个值,"$<"表示了所有依赖目标的挨个值。这些奇怪的 变量我们叫"自动化变量"
  • 相关阅读:
    Euclid's Game (简单博弈)
    “科大讯飞杯”第十七届同济大学程序设计预选赛暨高校网络友谊赛(部分题解)
    牛客小白月赛4 (B 博弈论)
    博弈--尼姆博弈
    C# .net中获取台式电脑中串口设备的名称
    打印出C# 中float ,double 在内存中的存放形式
    VS2010 C++ 创建COM组件
    .net 中两个日期算经过的月份数
    一种计算MD5的实现方法
    将文件从程序集中复原
  • 原文地址:https://www.cnblogs.com/bwbfight/p/9362454.html
Copyright © 2020-2023  润新知