• make命令和Makefile文件


    转自:https://blog.csdn.net/twc829/article/details/72729799

    make命令是一个常用的编译命令,尤其在C/C++开发中,make命令通过makefile文件中描述源程序之间的依赖关系进行自动编译;

    makefile文件是按照规定格式编写,需说明如何编译各个源文件并连接生成可执行文件,并要求定义源文件之间的依赖关系;

    首次执行make命令时,编译所有相关文件,之后再执行make命令时,以增量方式进行编译,即只对修改的源文件相关的目标文件进行编译;

    注:许多tarball格式的开源软件,解压后先执行./configure,再执行make,然后执行make install进行安装;

     

    makefile文件支持include,即把一些基本依赖规则写在一个公共文件中,其他makefile文件包含此文件;

    通常公共makefile文件命名为common.mk;


    一、make命令
    make命令后接参数,称为目标;

    1 常见目标
    make all:编译所有目标

    make install:安装已编译的程序

    make uninstall:卸载已安装的程序

    make clean:删除由make命令产生的文件,通常删除目标文件.o

    make distclean:删除由./configure产生的文件

    make check:测试刚编译的软件

    make installcheck:检查安装的库和程序

    make dist:重新打包成packname-version.tar.gz


    执行make命令时,需要一个Makefile文件,以告诉make命令如何编译和链接程序;


    2 参数
    -B:重新建立所有目标

    -d:打印调试信息

    -C:切换到指定路径下寻找Makefile

    -f:将指定文件看做Makefile

    -j:同时运行命令的个数,即多线程执行Makefile,后接的个数可由nproc命令返回值来指定

    注:nproc命令打印当前进程可用的处理数(线程数);


    二、程序的编译和链接
    一般在C/C++开发中,首先将源文件编译成目标文件(Windows下.obj文件,Unix下.o文件)——编译compile,再将目标文件合成执行文件——链接link;


    三、make命令如何工作?
    1 make在当前目录下寻找“Makefile”或“makefile”文件

    2 若找到,查找文件中的第一个目标文件.o

    3 若目标文件不存在,根据依赖关系查找.s文件

    4 若.s文件不存在,根据依赖关系查找.i文件

    5 若.i文件不存在,根据依赖关系查找.c文件,此时.c文件一定存在,于是生成一个.o文件,再去执行


    四、Makefile文件格式
    1 概述
    Makefile文件由一系列规则rules构成,每条规则形式如下:

    <target>: <prerequisites>
    [Tab]<commands>

    第一行冒号前为目标,冒号后为前置条件;第二行必须由一个Tab键起首,后接命令;

    目标是必须的,不可省略;前置条件和命令是可选的,但两者必须至少存在一个;

    每条规则明确两件事——构建目标的前置条件是什么?如何构建?


    2 目标target
    目标可以是文件名,指明make命令所要构建的对象;也可以是某个操作名称,称“伪目标”;

    clean:
    rm *.o
    以上代码目标是clean,命令是rm *.o;

    执行make clean命令,实现对象文件的删除;


    为避免设置的伪目标名称在当前路径下有相同名称的文件,make命令发现该名称的文件已存在,便不再构建,也就不执行rm操作的情况发生,先将该名称声明为伪目标,因此make命令不会检查是否存在该名称的文件,每次执行对应的操作;

    .PHONY:clean
    clean:
    rm *.o

    若make命令没有指定目标,默认执行Makefile文件中第一个目标;


    3 前置条件prerequisites
    前置条件通常是一组文件名,用空格隔开;

    指定目标是否重新构建的判断标准——只要有一个前置条件不存在或有更新,则该目标需重新构建;


    result.txt:source.txt
    cp source.txt result.txt
    若当前路径下source.txt存在,make result.txt可正常执行,否则需再写一条规则,用于生成source.txt;


    source.txt:
    echo "This is a source file." > source.txt
    source.txt没有前置条件,与其他文件文官,只要该文件不存在,每次执行make source.txt命令都会生成该文件;


    若要生成多个文件,写法如下:

    source:file1 file2 file3

    source是伪目标,只有3个前置条件,没有对应命令;执行make source命令后一次性生成file1 file2 file3文件,比如下写法方便:

    make file1
    make file2
    make file3

    4 命令commands
    命令表示如何更新目标文件,由一行或多行shell命令组成;

    注:

    shell命令一定是写在命令中,否则会被make忽略;

    每行命令前必须有一个Tab键;

    每行命令在一个独立的shell中执行,shell之间没有继承关系,因此上一行为的变量赋值,在下一行无效;

    若前后两条命令有共享数据,可写在一行,用分号隔开;

    var-kept:
    export foo=bar;echo "foo=[$$foo]"


    或,在换行符前加反斜杠\进行转义;

    var-kept:
    export foo=bar;\
    echo "foo=[$$foo]"

    或,加上.ONSHELL命令;

    .ONESHELL:
    var-kept:
    export foo=bar;
    echo "foo=[$$foo]"

    五、Makefile文件语法
    1 注释
    每行以#开头的为注释;


    2 回声echoing
    正常情况下,make打印每条命令,再执行该命令,称回声;

    在命令前加@,关闭回声,即只输出命令的执行结果,出错则停止执行;

    注:

    前缀-表示命令执行有错,忽略错误,继续执行;

    不加前缀输出执行的命令和命令执行的结果,出错则停止执行;

    3 通配符
    通配符指定一组符合条件的文件;

    Makefile通配符与bash一致;

    *:任意0个或多个字符

    ?:任意一个字符

    [...]:方括号内任意一个字符


    4 模式匹配
    make命令允许对文件名进行类似正则运算的匹配,主要用到%;


    5 变量和赋值符
    自定义变量,使用=赋值;

    调用变量,将变量名放在$()中;

    Makefile提供四种赋值运算符——=、:=、?=、+=;

    (1)=

    递归展开赋值,默认赋值方式;

    var2=$(var1)
    var1="TEST"
    all:
    echo $(var2)
    输出:TEST


    (2):=

    直接赋值,不会递归展开,若引用的变量不存在,则为空串;

    var2:=$(var1)
    var1="TEST"
    all:
    echo $(var2)
    输出:(空串)


    (3)?=

    若未初始化,则赋值;

    var1="test"
    var1?="TEST"
    var2?="TEST"
    all:
    <span style="white-space:pre"> </span>echo $(var1) and $(var2)
    输出:test and TEST


    (4)+=

    将值追加到现有内容末尾;

    var1="TEST"
    var1+="test"
    all:
    <span style="white-space:pre"> </span>echo var1
    输出:TESTtest


    六、内置变量
    make命令提供一系列内置变量,如$(CC)指向当前使用的编译器,$(MAKE)指向当前使用的make工具;

    RM:rm -f

    AR:ar

    CC:cc

    CXX:g++


    七、自动变量
    自动变量的值与当前规则有关;

    1 $@

    表示当前目标;


    2 $<

    表示第一个前置条件;


    3 $?

    表示所有比目标更新的前置条件;


    4 $^

    表示所有前置条件;


    5 $(@D)和$(@F)

    $(@D)表示$@的目标名,$(@F)表示$@的文件名;


    八、判断
    Makefile文件使用bash语法,完成判断;

    <条件语句>
    <条件为真,执行程序段>
    else
    <条件为假,执行程序段>
    endif

    1 条件语句

    (1)ifeq 比较两个参数值是否相等

    ifeq (arg1, arg2)

    ifeq 'arg1' 'arg2'

    ifeq "arg1" "arg2"

    ifeq 'arg1' "arg2"

    ifeq "arg1" 'arg2'

    注:参数还可用make函数,如ifeq ($(strip $(foo)),);


    (2)ifneq 比较两个参数值是否不等

    ifneq (arg1, arg2)

    ifneq 'arg1' 'arg2'

    ifneq "arg1" "arg2"

    ifneq 'arg1' "arg2"

    ifneq "arg1" 'arg2'


    (3)ifdef 判断变量是否有值

    ifdef var


    (4)ifndef 判断变量是否无值

    ifndef var


    举例1:

    bar=
    foo=$(bar)
    ifdef foo
    frobozz=yes
    else
    frobozz=no
    endif
    #frobozz值为yes

    举例2:

    foo=
    ifdef foo
    frobozz=yes
    else
    frobozz=no
    endif
    #frobozz值为no
    注:以上举例说明ifdef只是测试一个变量是否有值,而非把变量扩展到当前位置!


    九、循环
    Makefile文件使用bash语法,完成判断;
    1 写法一
    LIST变量是Makefile变量,引用Makefile变量需使用$()括起来;

    而all目标后的命令是shell命令,其中定义的变量也是shell变量,引用shell变量需使用$$作为开头,但shell变量不需括号;

    LIST = one two three
    all:
    for i in $(LIST); do \
    echo $$i; \
    done

    2 写法二
    all:
    for i in one two three; do \
    echo $$i; \
    done

    十、函数
    1 函数的调用语法
    函数调用,类似变量使用,语法如下:

    $(func args) # 推荐!
    # 或
    ${func args}
    其中,args参数之间以","隔开,func和args之间以" "隔开;


    2 字符串处理函数
    (1)$(subst <from>,<to>,<text>)

    字符串替换,将字符串<text>中的<from>转换成<to>,返回替换后的字符串;


    (2)$(patsubst <pattern>,<replacement>,<text>)

    模式字符串替换,查找字符串<text>中符合模式<pattern>的单词,并替换成<replacement>;

    注:<pattern>可包括通配符%,表示任意长度的字符串;

    若<replacement>也包括%,则这里的%是<pattern>中%代表的字符串;


    (3)$(strip <string>)

    去空格,去掉<string>中开头和结尾的空白符,返回去掉空格的字符串;


    (4)$(findstring <find>,<in>)

    查找字符串,在字符串<in>中查找<find>字符串,若找到则返回<find>,否则返回空字符串;


    (5)$(filter <pattern...>,<text>)

    过滤,以<pattern>模式过滤<text>字符串中的单词,保留符合模式的单词,返回符合模式的字符串;

    可有多个模式,模式之间以" "隔开;


    (6)$(filter-out <pattern...>,<text>)

    反向过滤,以<pattern>模式过滤<text>字符串中的单词,去掉符合模式的单词,返回不符合模式的字符串;

    可有多个模式,模式之间以" "隔开;


    (7)$(sort <list>)

    排序,给字符串<list>中的单词以升序排序,返回排序后的字符串;


    (8)$(word <n>,<text>)

    取单词,获取并返回字符串<text>中第<n>个单词(从1开始),若<n>比<text>中的单词数大,则返回空字符串;


    (9)$(wordlist <s>,<e>,<text>)

    取单词串,获取并返回字符串<text>中第<s>个到第<e>个的单词串;


    (10)$(words <text>)

    单词个数统计,统计并返回字符串<text>中单词个数;

    注:获取字符串中最后一个单词,使用$(word $(words <text>),<text>)


    (11)$(firstword <text>)

    首单词,获取并返回字符串<text>中第一个单词;

    注:等价于$(word 1,<text>)


    3 文件名处理函数
    (1)$(dir <names...>)

    取目录,从文件名序列<names>中获取并返回目录部分;

    注:目录部分指最后一个反斜杠之前的部分,若无反斜杠,则返回"./";


    (2)$(notdir <names...>)

    取文件,从文件名序列<names>中获取并返回文件部分;

    注:文件部分指最后一个反斜杠之后的部分;


    (3)$(suffix <names...>)

    取后缀,从文件名序列<names>中获取并返回各文件的后缀序列,若无后缀,则返回空字符串;


    (4)$(basename <names>)

    取前缀,从文件名序列<names>中获取并返回各文件的前缀序列,若无前缀,则返回空字符串;


    (5)$(addsuffix <suffix>,<names...>)

    加后缀,把<suffix>加到<names>中每个单词后面并返回;


    (6)$(addprefix <prefix>,<names...>)

    加前缀,把<prefix>加到<names>中每个单词前面并返回;


    (7)$(join <list1>,<list2>)

    连接,将<list2>中的单词对应的加到<list1>中单词之后;


    (8)$(wildcard <pattern...>)

    拓展通配符,用于定义变量和引用函数时通配符失效的情况,获取符合模式的字符串并返回;


    4 其他函数
    (1)$(foreach <var>,<list>,<text>)

    把<list>中单词逐一取出放到<var>所指定的变量中,再执行<text>所包含的表达式,每次执行<text>都会返回一个字符串,执行完foreach后返回由多个字符串组成、空格隔开的字符串;

    ### Makefile 内容
    targets := a b c d
    objects := $(foreach i,$(targets),$(i).o)

    all:
    @echo $(targets)
    @echo $(objects)

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

    (2)$(if <condition>,<then-part>,<else-part>)

    ### Makefile 内容
    val := a
    objects := $(if $(val),$(val).o,nothing)
    no-objects := $(if $(no-val),$(val).o,nothing)

    all:
    @echo $(objects)
    @echo $(no-objects)

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

    (3)$(shell <shell-command>)

    执行一个shell命令,将执行结果作为函数返回;


    (4)make控制函数

    $(error <text>):产生一个致命错误,Makefile停止执行

    $(warning <text>):输出警告信息,Makefile继续执行


    (5)$(call <expression>,<para1>,<para2>,...)

    <expression>表达式中的变量,如$(1)、$(2)、...等被参数<para1>、<para2>、...依次取代,<expression>的返回值就是call函数的返回值;

    ### Makefile 内容
    log = "====debug====" $(1) "====end===="

    all:
    @echo $(call log,"正在 Make")

    ### bash 中执行 make
    $ make
    ====debug==== 正在 Make ====end====

    十一、Makefile实例
    1 编译C语言项目
    edit : main.o kbd.o command.o display.o
    cc -o edit main.o kbd.o command.o display.o

    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
    cc -c display.c

    clean :
    rm edit main.o kbd.o command.o display.o

    .PHONY: edit clean

    2 公共Makefile文件common.mk
    #This is the common part for makefile

    SOURCE := $(wildcard *.c) $(wildcard *.cc) $(wildcard *.cpp)
    OBJS := $(patsubst %.c,%.o,$(patsubst %.cc,%.o,$(patsubst %.cpp,%.o,$(SOURCE))))
    DEPS := $(patsubst %.o,%.d,$(OBJS))
    MISSING_DEPS := $(filter-out $(wildcard $(DEPS)),$(DEPS))
    CPPFLAGS += -MD

    .PHONY : everything objs clean veryclean vc rebuild ct rl

    everything : $(TARGETS)

    objs : $(OBJS)

    clean :
    @$(RM) *.o
    @$(RM) *.d

    veryclean: clean
    @$(RM) $(TARGETS)
    @$(RM) cscope.out
    @$(RM) core*

    vc: veryclean

    ct:
    @$(RM) $(TARGETS)

    rl: ct everything

    rebuild: veryclean everything

    ifneq ($(MISSING_DEPS),)
    $(MISSING_DEPS) :
    @$(RM) $(patsubst %.d,%.o,$@)
    endif

    -include $(DEPS)

    3 Makefile文件编写训练
    (1)准备工作

    准备三个文件file1.c、file2.c、file2.h:

    file1.c:

    #include <stdio.h>
    #include "file2.h"
    int main()
    {
    printf("print file1\n");
    File2Print();
    return 0;
    }
    file2.c:

    #include "file2.h"
    void File2Print()
    {
    printf("Print file2\n");
    }
    file2.h:

    #ifndef FILE2_H_
    #define FILE2_H_

    #ifdef __cplusplus

    extern "C" {

    #endif

    void File2Print();

    #ifdef __cplusplus

    }

    #endif

    #endif

    (2)Makefile文件基础编程

    helloworld: file1.o file2.o
    gcc file1.o file2.o -o helloworld

    file1.o: file1.c file2.h
    gcc -c file1.c -o file1.o

    file2.o: file2.c file2.h
    gcc -c file2.c -o file2.o

    clean:
    rm -rf *.o helloworld
    注:

    gcc命令中,-c参数将后接文件(.c或.cc)编译成目标文件(.o),-o参数指定输出文件名(默认为.o文件);


    (3)Makefile文件进阶编程一——使用变量

    OBJS = file1.o file2.o
    CC = gcc
    CFLAGS = -Wall -O -g

    helloworld : $(OBJS)
    $(CC) $(OBJS) -o helloworld

    file1.o : file1.c file2.h
    $(CC) $(CFLAGS) -c file1.c -o file1.o

    file2.o : file2.c file2.h
    $(CC) $(CFLAGS) -c file2.c -o file2.o

    clean:
    rm -rf *.o helloworld
    注:

    gcc命令中,-Wall -O -g参数用于配置编译器,-Wall参数输出所有警告信息,-O参数在编译时优化,-g参数编译debug版本;

    ARFLAGS:ar命令的参数

    CFLAGS:C语言编译器的参数

    CXXFLAGS:C++语言编译器的参数


    (4)Makefile文件进阶编程二——使用函数

    CC = gcc
    XX = g++
    CFLAGS = -Wall -O –g
    TARGET = ./helloworld

    %.o: %.c
    $(CC) $(CFLAGS) -c $< -o $@

    %.o:%.cpp
    $(XX) $(CFLAGS) -c $< -o $@

    SOURCES = $(wildcard *.c *.cpp)
    OBJS = $(patsubst %.c,%.o,$(patsubst %.cpp,%.o,$(SOURCES)))

    $(TARGET) : $(OBJS)
    $(XX) $(OBJS) -o $(TARGET)
    chmod a+x $(TARGET)

    clean:
    rm -rf *.o helloworld
    注:

    $(wildcard *.c *.cpp)函数获取并返回所有以.c或.cpp结尾的文件列表;

    $(patsubst %.c,%o,$(SOURCE))函数获取所有以.c结尾的字符串并替换成以.o结尾的新的文件列表;

  • 相关阅读:
    (杭电 1014)Uniform Generator
    错排公式浅谈(推导+应用)
    (杭电 2045)不容易系列之(3)—— LELE的RPG难题
    (杭电 2046)骨牌铺方格
    (补题 杭电 2044)一只小蜜蜂...
    (杭电 1097)A hard puzzle
    Linux内核实验作业六
    《Linux内核设计与实现》第十八章读书笔记
    实验作业:使gdb跟踪分析一个系统调用内核函数
    k8s标签
  • 原文地址:https://www.cnblogs.com/coolYuan/p/15801748.html
Copyright © 2020-2023  润新知