• Makefile编写


    简介

    Makefile 是和 make 命令一起配合使用的,很多大型项目的编译都是通过 Makefile 来组织的,。
    我建立工程的方法有以下三点:
    1.makefile:
    优点:使用非常广泛,通用性强,可跨平台。
    缺点:语法比较蛋疼。要写出一个通用,便于管理,且兼容性强的makefile比较困难。

    2.cmake:
    优点:简单易用,使用较广泛,方便管理,可跨平台。
    缺点:自动生成的makefile太臃肿。

    3.sh脚本:
    优点:自由,高度定制。简单易用,可操作性强。方便维护。(甚至还可以生成makefile)
    缺点:sh建的工程太少了(估计没人这么搞吧)

    但我考虑到方便移植和管理其他人的工程还是选择了第一种,以makefile建立管理工程。(其实我的内心是比较向往第三种sh脚本建工程的)。下面来介绍下makefile的规则以及语法

    PS:我本意是想写短一点的,只是想写点常用的东西,方便大家查阅。精炼了这么久,可还有这么多内容(15000字)。所以,大家还是耐心的学习吧,想学好linux这是必不可少的一步。


    规则

    说明

    <target1 > <target2>.... : <prerequisites1> <prerequisites2>...
    [TAB] <command1>
    [TAB] <command2>
    ...

    target可以是一个object file(目标文件),也可以是一个执行文件,还可以是一个标签(label)。对于标签这种特性,在后续的“伪目标”章节中会有叙述。

    <prerequisites>就是,要生成那个target所需要的文件或是目标。

    <command>也就是make需要执行的命令。(任意的shell命令)

    这是一个文件的依赖关系,也就是说,<target>这一个或多个的目标文件依赖于<prerequisites>中的文件,其生成规则定义在 <command>中。说白一点就是说,<prerequisites>中如果有一个以上的文件比<target>文件要新的话,<command>所定义的命令就会被执行。这就是makefile的规则。也就是makefile中最核心的内容。

    经典示例

    原始版本

    objects = main.o kbd.o command.o display.o 
    		insert.o search.o files.o utils.o
    

    edit : $(objects)
    cc -o edit $(objects)
    main.o : main.c defs.h
    cc -c main.c
    kbd.o : kbd.c defs.h command.h
    cc -c kbd.c
    command.o : command.c defs.h command.h
    cc -c command.c
    display.o : display.c defs.h buffer.h
    cc -c display.c
    insert.o : insert.c defs.h buffer.h
    cc -c insert.c
    search.o : search.c defs.h buffer.h
    cc -c search.c
    files.o : files.c defs.h buffer.h command.h
    cc -c files.c
    utils.o : utils.c defs.h
    cc -c utils.c
    clean :
    rm edit $(objects)

    自动推导版本

    objects = main.o kbd.o command.o display.o 
    		insert.o search.o files.o utils.o
     cc = gcc
    

    edit : $(objects)
    cc -o edit $(objects)

    main.o : defs.h
    kbd.o : defs.h command.h
    command.o : defs.h command.h
    display.o : defs.h buffer.h
    insert.o : defs.h buffer.h
    search.o : defs.h buffer.h
    files.o : defs.h buffer.h command.h
    utils.o : defs.h

    .PHONY : clean
    clean :
    rm edit $(objects)

    理解

    1.内容展开

    list_a = 1.a 2.a
    list_b = 1.b 2.b 3.b
    

    #list_a中的元素每次单个展开连接list_b中的所有元素
    #结果如下
    test:list_a
    %.a:list_b #展开:
    1.a:1.b 2.b 3.b #此时&@=1.a $<=1.b 2.b 3.b
    2.a:1.b 2.b 3.b #此时&@=2.a $<=1.b 2.b 3.b

    #内容还可以继续展开
    %.b:%.x #自动推导展开
    1.b:1.x
    2.b:2.x
    3.b:3.x

    2.include 相当于将内容复制展开,export可以共享全局变量。

    3.用make遍历子目录makefile时相当于,用新进程开启make。因此export不能共享全局变量。

    注意

    1.注意空格与TAB(有些编辑器会自动将TAB转换成4个空格,难以发现)

    2.注意 = 与 := 的区别
    包括export 在内的所有变量都不能共享
    如果使用cd &&make进行遍历。那么最好转换成绝对路径

    3.如果使用cd &&make进行遍历。那么最好转换成绝对路径


    语法

    1.符号

    1) 命令前缀

    [不用前缀 ]输出执行的命令以及命令执行的结果, 出错的话停止执行
    [前缀 @]只输出命令执行的结果, 出错的话停止执行
    [前缀 -]命令执行有错的话, 忽略错误, 继续执行

    2) 通配符

    *表示任意一个或多个字符
    ? 表示任意一个字符
    [...] [abcd] 表示a,b,c,d中任意一个字符, [^abcd]表示除a,b,c,d以外的字符, [0-9]表示 0~9中任意一个数字

    3) 自动变量

    在下文的 自动变量

    4) 赋值

    = :=

    相同点:都是给内容赋值;
    区别:与Verilog的=和<=类似。
    其中 = 和 := 的区别在于 :

    = 只能使用前面定义好的变量,
    = 可以使用后面定义的变量

    赋值符号 [=]

    = 最后再赋值

    # Makefile内容
    OBJS2 = $(OBJS1) programC.o
    OBJS1 = programA.o programB.o
    

    all:
    @echo $(OBJS2)

    # bash中执行 make, 可以看出虽然 OBJS1 是在 OBJS2 之后定义的, 但在 OBJS2中可以提前使用
    $ make
    programA.o programB.o programC.o

    赋值符号 [:=]

    := 立即赋值

    # Makefile内容
    OBJS2 := $(OBJS1) programC.o
    OBJS1 := programA.o programB.o
    

    all:
    @echo $(OBJS2)

    # bash中执行 make, 可以看出 OBJS2 中的 $(OBJS1) 为空
    $ make
    programC.o

    赋值符号 [+=]

    += 变量追加值

    # Makefile内容
    SRCS := programA.c programB.c programC.c
    SRCS += programD.c
    

    all:
    @echo "SRCS: " $(SRCS)

    # bash中运行make
    $ make
    SRCS: programA.c programB.c programC.c programD.c

    5) 伪目标

    .PHONY

    .PHONY : clean
    clean :
    	-rm edit $(objects)
    

    2. 隐含规则

    1) 自动推倒命名:

    编译C时,*.o 的目标会自动推导为 *.c

    2) 隐含变量

    [RM] rm -f
    [AR] ar
    [CC] cc
    [CXX] g++
    [ARFLAGS] AR命令的参数
    [CFLAGS] 语言编译器的参数
    [CXXFLAGS] C++语言编译器的参数

    3) 自动变量

    [$@] 目标集合
    [$%] 当目标是函数库文件时, 表示其中的目标文件名
    [$<] 第一个依赖目标. 如果依赖目标是多个, 逐个表示依赖目标
    [$?] 比目标新的依赖目标的集合
    [$^] 所有依赖目标的集合, 会去除重复的依赖目标
    [$+] 所有依赖目标的集合, 不会去除重复的依赖目标
    [$*] 这个是GNU make特有的, 其它的make不一定支持

    1.o: 1.c
    	@echo $@ : $< 
    

    # bash中执行 make:
    1.o : 1.c

    3.定义

    不限于makefile还有部分shell指令

    1) 查看依赖关系

    gcc -MM

    $ gcc -MM kvm_main.c 
    kvm_main.o: kvm_main.c iodev.h coalesced_mmio.hasync_pf.h  
    
    #这句就可以加到 Makefile 中作为编译 kvm_main.o 的依赖关系
    

    2) 定义命令包

    define <command-name>
    command
    ...
    endef

    # Makefile 内容
    define run-hello-makefile
    @echo -n "Hello"
    @echo " Makefile!"
    @echo "这里可以执行多条 Shell 命令!"
    endef
    

    all:
    $(run-hello-makefile)

    # bash 中运行make
    $ make
    Hello Makefile!
    这里可以执行多条 Shell 命令!

    3) 条件判断

    # Makefile 内容
    all:
    ifeq ("aa", "bb")
        @echo "equal"
    else
        @echo "not equal"
    endif
    

    # bash 中执行 make
    $ make
    not equal

    4) 自定义函数

    $(call <expression>,<parm1>,<parm2>,<parm3>...)

    # Makefile 内容
    hello = "hello " $(1) " world"
    

    all:
    @echo $(call hello,"我是参数1")

    # bash 中执行 make
    $ make
    hello 我是参数1 world

    5) 执行shell指令

    $(shell <shell command>) 与 `<shell command>`
    作用:执行一个shell命令, 并将shell命令的结果作为返回.
    注意:` 是反引号(英文状态下,键盘的ESC下面那个键)

    4.函数

    不限于makefile还有部分shell指令

    1) 传参(同一个进程)

    export

    export EX_VAR = value
    export EX_VAR := value
    export EX_VAR += value
    #=  :=  +=  与上面的描述基本相同
    

    注意:是同一个进程下的make才有用。当多级遍历make时是无法全局的。

    2) 字符串处理

    (1) 字符串替换函数

    $( subst<source>,<new>,<text>)

    功能: 把字符串<text> 中的<source> 替换为<new>
    返回: 替换过的字符串

    # Makefile 内容
    all:
        @echo $(subst t,e,maktfilt)  <-- 将t替换为e
    

    # bash 中执行 make
    $ make
    makefile

    (2) 字符串中每个元素替换函数

    $( patsubst <source>,<new>,<text>)

    功能: 把字符串 <text> 中的的每个元素 <source> 替换为<new>
    返回: 替换过的字符串

    # Makefile 内容
    all:
        @echo $(patsubst %.c,%.o,programA.c programB.c)
    

    # bash 中执行 make
    $ make
    programA.o programB.o

    (3) 去空格函数

    $(strip <text>)

    功能: 去掉 <string> 字符串中开头和结尾的空字符
    返回: 被去掉空格的字符串值

    # Makefile 内容
    VAL := "       aa  bb  cc "
    

    all:
    @echo "去除空格前: " $(VAL)
    @echo "去除空格后: " $(strip $(VAL))

    # bash 中执行 make
    $ make
    去除空格前: aa bb cc
    去除空格后: aa bb cc

    (4) 判断字符串内是否存在特定字符串

    $(findstring<text>,<elem>)

    功能: 在字符串 <text> 中查找 <elem> 字符串是否存在
    返回: 如果找到, 返回 <elem> 字符串, 否则返回空字符串

    # Makefile 内容
    VAL := "       aa  bb  cc "
    

    all:
    @echo $(findstring aa,$(VAL))
    @echo $(findstring ab,$(VAL))

    # bash 中执行 make
    $ make
    aa

    3) 文件元素相关

    (1) 取文件函数

    保留符合条件的元素

    $(filter <elem1 elem2...>,<text>)

    # Makefile 内容
    all:
        @echo $(filter %.o %.a,program.c program.o program.a)
    

    # bash 中执行 make
    $ make
    program.o program.a
    program.c

    去掉符合条件的元素

    $(filter-out <elem1 elem2...>,<text>)

    # Makefile 内容
    all:
        @echo $(filter-out %.o %.a,program.c program.o program.a)
    

    # bash 中执行 make
    $ make
    program.c

    获取该目录下所有文件

    获取该目录下所有.x文件
    $(wildcard *.x)

    # Makefile 内容
    all:
        @echo $(wildcard *.c)
    

    # bash 中执行 make
    $ make
    a.x b.x c.x

    (2) 路径函数

    去掉路径

    $(notdir <elem elem...>)

    功能: 去掉序列的路径
    返回: 文件名序列 <elem> 中的非目录部分

    # Makefile 内容
    all:
        @echo $(notdir /home/a.c ./bb.c ../c.c d.c)
    

    # bash 中执行 make
    $ make
    a.c bb.c c.c d.c

    取路径

    $(dir <elem1 elem2...>)

    功能: 从文件名序列 <elem> 中取出目录部分
    返回: 文件名序列 <elem> 中的目录部分

    # Makefile 内容
    all:
        @echo $(dir /home/a.c ./bb.c ../c.c d.c)
    

    # bash 中执行 make
    $ make
    /home/ ./ ../ ./

    (3) 取前后缀

    取后缀函数

    $(suffix <elem1 elem2...>)

    功能: 从文件名序列 <elem> 中取出各个文件名的后缀
    返回: 文件名序列 <elem> 中各个文件名的后缀, 没有后缀则返回空字符串

    # Makefile 内容
    all:
        @echo $(suffix /home/a.c ./b.o ../c.a d)
    

    # bash 中执行 make
    $ make
    .c .o .a

    取前缀函数

    $(basename <elem1 elem2...>)

    功能: 从文件名序列 <elem> 中取出各个文件名的前缀
    返回: 文件名序列 <elem> 中各个文件名的前缀, 没有前缀则返回空字符串

    # Makefile 内容
    all:
        @echo $(basename /home/a.c ./b.o ../c.a /home/.d .e)
    

    # bash 中执行 make
    $ make
    /home/a ./b ../c /home/

    (3) 增添前后缀

    增添后缀函数

    $(addsuffix <suffix>,<elem1 elem2...>)

    功能: 把后缀 <suffix> 加到 <elem> 中的每个单词后面
    返回: 加过后缀的文件名序列

    # Makefile 内容
    all:
        @echo $(addsuffix .c,/home/a b ./c.o ../d.c)
    

    # bash 中执行 make
    $ make
    /home/a.c b.c ./c.o.c ../d.c.c

    增添前缀函数

    $(addprefix <prefix>, <elem1 elem2...>)

    功能: 把前缀 <prefix> 加到 <elem> 中的每个单词前面
    返回: 加过前缀的文件名序列

    # Makefile 内容
    all:
        @echo $(addprefix test_,/home/a.c b.c ./d.c)
    

    # bash 中执行 make
    $ make
    test_/home/a.c test_b.c test_./d.c

    我写的管理大型项目的makefile

    以下内容为基础版本。可以混编C和C++。
    但没有加入平台兼容性验证等功能。
    在ubuntu16.04下测试通过。

    tree
    .
    ── app
       ── app1
          ── main.c
          └── makefile
       └── makefile
    ── make_conf.mk
    ── makefile
    ── make_fun.mk
    ── module
       ── makefile
       └── test
           ── drive
              ── cpp_test.cpp
              ── cpp_test.h
              ── test.c
              └── test.h
           ── hal
              ── test_hal.c
              └── test_hal.h
           └── makefile
    └── output
        ── bin
           ── app1.bin
           └── app2.bin
        ── lib
           └── libobj.a
        └── obj
            ── app
               ── app1
                  └── main.o
               └── app2
                   └── main.o
            └── module
                └── test
                    ── drive
                       ── cpp_test.o
                       └── test.o
                    └── hal
                        └── test_hal.o
    

    17 directories, 22 files

    顶层 makefile

    # -------------------------------------------
    # FileName      :makefile
    # Author        :wind 
    # Date          :2018-1-16
    # Description 
    #
    # Top makefile.
    #
    # ------------------------------------------- 
    

    # 设置当前路径
    DIR_ROOT:=.

    # 设置递归子目录
    DIR_LIST_SUB :=module app

    include $(DIR_ROOT)/make_conf.mk
    all:make_root
    clean:make_clean
    include $(DIR_ROOT)/make_fun.mk

    中间遍历层 makefile

    # -------------------------------------------
    # FileName      :xx/makefile
    # Author        :wind 
    # Date          :2018-1-16
    # Description 
    #
    # Level1 makefile.
    #
    # ------------------------------------------- 
    

    # 设置当前路径
    DIR_ROOT:=..

    # 添加递归子目录
    DIR_LIST_SUB :=
    app1
    app2

    # 添加源文件
    FILE_LIST_C +=

    FILE_LIST_CPP +=

    FILE_LIST_S +=

    # 添加头文件路径
    DIR_LIST_INCLUDE+=

    include $(DIR_ROOT)/make_conf.mk
    all:make_show make_subdir
    clean:make_clean
    include $(DIR_ROOT)/make_fun.mk

    app的makefile

    # -------------------------------------------
    # FileName      :xx/xx/makefile
    # Author        :wind 
    # Date          :2018-1-16
    # Description 
    #
    # Level2 makefile.
    #
    # ------------------------------------------- 
    

    # 设置当前路径
    DIR_ROOT:=../..

    # 添加递归子目录
    DIR_LIST_SUB :=

    # 添加源文件
    FILE_LIST_C +=
    main.c

    FILE_LIST_CPP +=

    FILE_LIST_S +=

    # 添加头文件路径
    DIR_LIST_INCLUDE+=
    $(DIR_ROOT)/module/test/hal
    $(DIR_ROOT)/module/test/drive

    include $(DIR_ROOT)/make_conf.mk
    all:make_show make_app
    clean:make_clean
    include $(DIR_ROOT)/make_fun.mk

    drive的makefile

    # -------------------------------------------
    # FileName      :xx/xx/makefile
    # Author        :wind 
    # Date          :2018-1-16
    # Description 
    #
    # Level2 makefile.
    #
    # ------------------------------------------- 
    

    # 设置当前路径
    DIR_ROOT:=../..

    # 添加递归子目录
    DIR_LIST_SUB :=

    # 添加源文件
    FILE_LIST_C +=
    drive/test.c
    hal/test_hal.c

    FILE_LIST_CPP +=
    drive/cpp_test.cpp

    FILE_LIST_S +=

    # 添加头文件路径
    DIR_LIST_INCLUDE+=
    hal
    drive

    include $(DIR_ROOT)/make_conf.mk
    all:make_show make_lib_a
    clean:make_clean
    include $(DIR_ROOT)/make_fun.mk

    配置文件 make_conf.mk

    # -------------------------------------------
    # FileName      :make_set.inc
    # Author        :wind 
    # Date          :2018-1-16
    # Description 
    #
    # 工程相关设置。
    #
    # -------------------------------------------
    

    # 设置常用指令
    # -------------------------------------------
    RM = rm -f
    MV = mv -f
    MKDIR = mkdir -p
    RMDIR = rm -rf

    # 颜色输出
    # -------------------------------------------
    ECHO_END:=33[0m"
    ECHO_GREEN:=echo "
    33[32m
    ECHO_RED:=echo "33[31m
    ECHO_YELLOW:=echo "
    33[33m
    ECHO_BLUE:=echo "33[34m
    ECHO_GREEN_YELLOW:=echo "
    33[42;30m

    # 编译缺省设置
    # -------------------------------------------

    # 默认编译
    CXX:=g++

    #编译选项
    COMPILE_C ?= $(CXX)
    COMPILE_CXX ?= $(CXX)
    COMPILE_ASM ?= $(CXX)
    COMPILE_AR ?= ar

    # 设置优化等级
    OPT ?=0

    # 设置警告开关
    COMPILE_WARN ?= -Wall

    # 设置静态编译
    COMPILE_STATIC ?= -s

    # 在环境基础下添加设置
    CFLAGS+= $(DIR_LIST_INCLUDE_I) $(COMPILE_WARN) -O$(OPT) $(COMPILE_STATIC)
    CXXFLAGS+= $(DIR_LIST_INCLUDE_I) $(COMPILE_WARN) -O$(OPT) $(COMPILE_STATIC)
    ASFLAGS+= -Wa,-adhlns=$(<:.S=.lst),-gstabs $(DIR_LIST_INCLUDE_I)

    # 编译设置汇总
    COMPILE_CFLAGS = $(CFLAGS)
    COMPILE_CXXFLAGS = $(CXXFLAGS)
    COMPILE_ASFLAGS = $(ASFLAGS)

    #链接选项
    LDFLAGS+= -lstdc++#编译C++
    LDFLAGS+= -lpthread#使用了线程
    LDFLAGS+= -fPIC#编译为位置独立的代码
    LDFLAGS+= -ldl#引用动态库
    LDFLAGS+= $(DIR_LIST_INCLUDE_I)

    #引用其他静态库
    FILE_LIST_LIB_A+=

    功能文件 make_fun.mk

    # -------------------------------------------
    # FileName      :make_fun.mk
    # Author        :wind 
    # Date          :2018-1-16
    # Description 
    #
    # 实际编译过程。
    #s
    # ------------------------------------------- 
    

    # 路径关系
    # -------------------------------------------
    DIR_ROOT_REAL=$(realpath $(DIR_ROOT))
    NAME_MODULE := $(notdir $(CURDIR))#所在的文件夹名称
    DIR_OUTPUT:=$(DIR_ROOT)/output
    DIR_BIN:=$(DIR_OUTPUT)/bin
    DIR_OBJ:=$(DIR_OUTPUT)/obj
    DIR_LIB:=$(DIR_OUTPUT)/lib

    # 路径处理成可用参数
    # -------------------------------------------
    DIR_LIST_INCLUDE_I:=$(addprefix -I,$(DIR_LIST_INCLUDE))#添加编译选项-I
    DIR_LIST_SUB:=$(addprefix $(CURDIR)/,$(DIR_LIST_SUB))#转换成绝对路径
    DIR_CURDIR:=$(subst $(DIR_ROOT_REAL),,$(CURDIR))#相对路径
    DIR_OBJ_OUT:=$(DIR_OBJ)$(DIR_CURDIR)

    #文件处理
    FILE_LIST_OBJ:=$(FILE_LIST_C:%.c=%.o)
    FILE_LIST_OBJ+=$(FILE_LIST_CPP:%.cpp=%.o)
    FILE_LIST_OBJ+=$(FILE_LIST_S:%.s=%.o)

    FILE_LIST_OBJ:=$(addprefix $(DIR_OBJ_OUT)/,$(FILE_LIST_OBJ))#转换成带绝对路径的中间文件
    FILE_LIB_A:=$(DIR_LIB)/libobj.a
    FILE_LIST_LIB_APP:=$(DIR_BIN)/$(NAME_MODULE).bin

    # 函数库
    # -------------------------------------------
    make_root:make_start make_subdir make_end

    # 工程开始
    make_start:
    @$(ECHO_BLUE) -----------------------------$(ECHO_END)
    @$(ECHO_BLUE) - [编译开始] -$(ECHO_END)
    @$(ECHO_BLUE) -----------------------------$(ECHO_END)
    @$(ECHO_BLUE)[COMPILE_C]$(COMPILE_C) [COMPILE_CFLAGS]$(COMPILE_CFLAGS)$(ECHO_END)
    @$(ECHO_BLUE)[COMPILE_CXX]$(COMPILE_CXX) [COMPILE_CXXFLAGS]$(COMPILE_CXXFLAGS)$(ECHO_END)
    @$(ECHO_BLUE)[COMPILE_ASM]$(COMPILE_ASM) [COMPILE_ASFLAGS]$(COMPILE_ASFLAGS)$(ECHO_END)
    @$(ECHO_BLUE)[COMPILE_AR]$(COMPILE_AR) [LDFLAGS]$(LDFLAGS)$(ECHO_END)
    @$(ECHO_BLUE)[OPT]$(OPT) $(ECHO_END)
    @$(ECHO_BLUE)[COMPILE_STATIC]$(COMPILE_STATIC) $(ECHO_END)
    @$(ECHO_BLUE)[FILE_LIST_LIB_A]$(FILE_LIST_LIB_A) $(ECHO_END)

    # 工程完成
    make_end:
    @$(ECHO_BLUE)[编译完成]$(ECHO_END)

    # 递归子目录
    make_subdir:
    @for list in $(DIR_LIST_SUB);
    do
    cd $$list && make all|| exit 1;
    done

    # 生成可执行文件
    make_app:make_lib_a make_bin

    # 生成静态链接库
    make_lib_a:make_obj
    @$(MKDIR) dirname <span class="hljs-variable" style="overflow: visible !important; color: #d54e53;"><span class="hljs-variable">$(FILE_LIB_A)</span></span>
    @$(ECHO_YELLOW)[$(COMPILE_AR)]-rcs $(FILE_LIB_A) $(FILE_LIST_OBJ)$(ECHO_END)
    $(COMPILE_AR) -rcs $(FILE_LIB_A) $(FILE_LIST_OBJ)

    # 链接
    make_bin:$(FILE_LIST_LIB_APP)

    # 编译
    make_obj:$(FILE_LIST_OBJ)

    # 清除
    make_clean:
    $(RMDIR) $(DIR_OUTPUT)

    # 显示参数,方便调试
    make_show:
    @$(ECHO_GREEN_YELLOW)[DIR_CURDIR] $(DIR_CURDIR) $(ECHO_END)
    @$(ECHO_GREEN)[FILE_LIST_C] $(FILE_LIST_C) $(ECHO_END)
    @$(ECHO_GREEN)[DIR_LIST_INCLUDE] $(subst $(DIR_ROOT)/,[DIR_ROOT]/,$(DIR_LIST_INCLUDE)) $(ECHO_END)

    # 文件操作过程
    # -------------------------------------------

    # 编译过程成中间文件
    $(DIR_OBJ_OUT)/%.o: %.c
    @$(MKDIR) dirname <span class="hljs-variable" style="overflow: visible !important; color: #d54e53;"><span class="hljs-variable">$@</span></span>
    @$(ECHO_YELLOW)[$(COMPILE_C)]$< -o $@ $(ECHO_END)
    $(COMPILE_C) -c $(COMPILE_CFLAGS) $< -o $@

    $(DIR_OBJ_OUT)/%.o: %.cpp
    @$(MKDIR) dirname <span class="hljs-variable" style="overflow: visible !important; color: #d54e53;"><span class="hljs-variable">$@</span></span>
    @$(ECHO_YELLOW)[$(COMPILE_CXX)]$< -o $@ $(ECHO_END)
    $(COMPILE_CXX) -c $(COMPILE_CXXFLAGS) $< -o $@

    $(DIR_OBJ_OUT)/%.o: %.s
    @$(MKDIR) dirname <span class="hljs-variable" style="overflow: visible !important; color: #d54e53;"><span class="hljs-variable">$@</span></span>
    @$(ECHO_YELLOW)[$(COMPILE_C)]$< -o $@ $(ECHO_END)
    $(COMPILE_ASM) -c $(COMPILE_ASFLAGS) $< -o $@

    # 链接成二进制文件
    $(FILE_LIST_LIB_APP): $(FILE_LIB_A)
    @$(MKDIR) dirname <span class="hljs-variable" style="overflow: visible !important; color: #d54e53;"><span class="hljs-variable">$@</span></span>
    @$(ECHO_YELLOW)[$(COMPILE_C)]$< -o $@ $(ECHO_END)
    $(COMPILE_C) $(LDFLAGS) -L$(DIR_LIB) -lobj $< $(FILE_LIST_LIB_A) -o $@

    内容推荐

    跟我一起写Makefile
    Makefile 使用总结
    项目实用makefile
    Makefile之大型工程项目子目录Makefile的一种通用写法

    引用本地址

    https://www.cnblogs.com/wittxie/p/9836097.html

  • 相关阅读:
    leetcode-442-数组中重复的数据
    leetcode-83-删除排序链表中的重复元素
    leetcode-80-删除排序数组中的重复项 II
    leetcode-26-删除排序数组中的重复项
    leetcode-889-105-106-根据前-中-后遍历构造二叉树
    leetcode-88-合并两个有序数组
    凉了!张三同学没答好「进程间通信」,被面试官挂了....
    多线程为了同个资源打起架来了,该如何让他们安定?
    Xlua中LuaBehaviour的实现
    Elasticsearch实战一
  • 原文地址:https://www.cnblogs.com/zhihaospace/p/12233385.html
Copyright © 2020-2023  润新知