Linux学习笔记——例说makefile 单个C文件

0.前言 0.前言
    从学习C语言开始就慢慢开始接触makefile,查阅了很多的makefile的资料但总感觉没有真正掌握makefile,如果自己动手写一个makefile总觉得非常吃力。所以特意借助博客总结makefile的相关知识,通过例子说明makefile的具体用法。 从学习C语言开始就慢慢开始接触makefile,查阅了很多的makefile的资料但总感觉没有真正掌握makefile,如果自己动手写一个makefile总觉得非常吃力。所以特意借助博客总结makefile的相关知识,通过例子说明makefile的具体用法。
    例说makefile分为以下几个部分,更多内容请参考【 】 例说makefile分为以下几个部分,更多内容请参考【 】
例说makefile分为以下几个部分,更多内容请参考【 】
    1.只有单个C文件    例说makefile分为以下几个部分,更多内容请参考【 】 1.只有单个C文件
    2.含有多个C文件     2.含有多个C文件
    3.需要包括头文件路径
    4.增加宏定义 4.增加宏定义
    5.增加系统共享库 5.增加系统共享库
    6.增加自定义共享库 6.增加自定义共享库
    7.一个实际的例子 7.一个实际的例子
4.增加宏定义 5.增加系统共享库 6.增加自定义共享库 7.一个实际的例子
3.需要包括头文件路径 4.增加宏定义 5.增加系统共享库 6.增加自定义共享库 7.一个实际的例子
    【代码仓库】—— 【代码仓库】——
    代码仓库位于bitbucket,可借助TortoiseHg(GUI工具)克隆代码或者在网页中直接下载zip包。 代码仓库位于bitbucket,可借助TortoiseHg(GUI工具)克隆代码或者在网页中直接下载zip包。
1.复习gcc指令 1.复习gcc指令
    一个非常简单的C文件——test.c 一个非常简单的C文件——test.c
    【test.c】 【test.c】
#include <stdio.h>
int main(void)
{
    int a = 3;
    int b = 2;
        
    printf("a=%d
",a);
    printf("b=%d
",b);
    return 0;
}
#include int main(void) { int a = 3; int b = 2; printf("a=%d ",a); printf("b=%d ",b); return 0; }
    【最简单方法】 【最简单方法】
    gcc test.c -o test gcc test.c -o test
    最终生成可执行文件 test。 最终生成可执行文件 test。
    【执行test】 【执行test】
    ./test ./test
    【输出结果】 【输出结果】
    a=3 a=3
    b=5 b=5
    【不正确的写法】 【不正确的写法】
    请注意以下写法并不正确。 请注意以下写法并不正确。
    gcc -c test.c -o test gcc -c test.c -o test
    【详细步骤分解】编译——链接 【详细步骤分解】编译——链接
    无论gcc指令的参数如何变化,从源文件变为可执行文件只需要两步,第一步源文件 编译为目标文件,第二步从目标文件 链接为可执行文件。在最简单的指令——gcc test.c -o test中使用了一处GCC的隐含规则,所有编译和链接这两个关键步骤展现的不明显。那么下面通过指令让“不明显”变得“明显”。 无论gcc指令的参数如何变化,从源文件变为可执行文件只需要两步,第一步源文件 编译为目标文件,第二步从目标文件 链接为可执行文件。在最简单的指令——gcc test.c -o test中使用了一处GCC的隐含规则,所有编译和链接这两个关键步骤展现的不明显。那么下面通过指令让“不明显”变得“明显”。
【1】由c文件编译为目标文件 【1】由c文件编译为目标文件
    【写法1】 【写法1】
    gcc -c test.c -o test.o gcc -c test.c -o test.o
    【写法2】——顺序可以颠倒 【写法2】——顺序可以颠倒
    gcc -o test.o -c test.c gcc -o test.o -c test.c
    【写法3】——适当简写 【写法3】——适当简写
    可以适当简写,充分利用GCC的默认规则,*.o文件由同名的*.c文件编译得到。 可以适当简写,充分利用GCC的默认规则,*.o文件由同名的*.c文件编译得到。
    gcc -c test.c gcc -c test.c
    【写法4】——有点奇怪 【写法4】——有点奇怪
    还可以这样写,虽然看起来有点奇怪,但只执行结果却是一样的。makefile文件似乎更喜欢这种方式。 还可以这样写,虽然看起来有点奇怪,但只执行结果却是一样的。makefile文件似乎更喜欢这种方式。
    gcc -c -o test.o test.c gcc -c -o test.o test.c
【2】把目标文件链接为可执行文件 【2】把目标文件链接为可执行文件
    【写法1】 【写法1】
    gcc test.o -o test gcc test.o -o test
    【写法2】——顺序可以颠倒 【写法2】——顺序可以颠倒
    顺序可以颠倒,makefile文件似乎更喜欢这种方式。 顺序可以颠倒,makefile文件似乎更喜欢这种方式。
    gcc -o test test.o gcc -o test test.o
2.编写makefile文件 2.编写makefile文件
    【makefile】 【makefile】
    请替换其中的[tab],并以代码仓库中的makefile文件为主。 请替换其中的[tab],并以代码仓库中的makefile文件为主。
# 可执行文件
TARGET = test
# 依赖目标
OBJS = test.o

# 指令编译器和选项
CC=gcc
CFLAGS=-Wall -std=gnu99

$(TARGET):$(OBJS)
# @echo TARGET:$@
# @echo OBJECTS:$^
 [tab]$(CC) -o $@ $^

clean:
 [tab]rm -rf $(TARGET) $(OBJS)
# 可执行文件 TARGET = test # 依赖目标 OBJS = test.o # 指令编译器和选项 CC=gcc CFLAGS=-Wall -std=gnu99 $(TARGET):$(OBJS) # @echo TARGET:$@ # @echo OBJECTS:$^ [tab]$(CC) -o $@ $^ clean: [tab]rm -rf $(TARGET) $(OBJS)
    【具体说明】 【具体说明】
    【1】TARGET=test test为最后可执行文件,linux中的可执行文件就是windows中的exe文件 【1】TARGET=test test为最后可执行文件,linux中的可执行文件就是windows中的exe文件
    【2】OBJS = test.o test.o对应test.c,利用makefile的隐含规则,test.o由test.c编译得到。
    【3】CC=gcc 指定编译器为gcc 【3】CC=gcc 指定编译器为gcc
    【4】CFLAGS=-Wall -std=gnu99 使能所有警告,指定编译器标准为gnu99 【4】CFLAGS=-Wall -std=gnu99 使能所有警告,指定编译器标准为gnu99
    【5】 $(CC) -o $@ $^  【5】 $(CC) -o $@ $^
            $@和$^为自动化变量,$@指目标文件,此处为可执行文件test,$^指去除重复的依赖文件,此处为test.o $@和$^为自动化变量,$@指目标文件,此处为可执行文件test,$^指去除重复的依赖文件,此处为test.o
            $(CC) -o $@ $^ 最终变化为 gcc -o test test.o。gcc -o test test.o和【详细步骤】链接部分的指令完全相同。那么makefile和gcc指令便建立了联系。 $(CC) -o $@ $^ 最终变化为 gcc -o test test.o。gcc -o test test.o和【详细步骤】链接部分的指令完全相同。那么makefile和gcc指令便建立了联系。
            可以通过@echo指令在makefile执行过程中打印自动化变量,通过这种方式调试makefile加速错误修正。 可以通过@echo指令在makefile执行过程中打印自动化变量,通过这种方式调试makefile加速错误修正。
    【编译】 【编译】
    make clean && make make clean && make
    先执行make clean再执行make生成可执行文件 先执行make clean再执行make生成可执行文件
    【控制台输出】 【控制台输出】
gcc -Wall -std=gnu99 -c -o test.o test.c gcc -Wall -std=gnu99 -c -o test.o test.c
gcc -o test test.o gcc -o test test.o
    【分析】 【分析】
    若去除-Wall -std=gnu99,那么以上两句简化为 若去除-Wall -std=gnu99,那么以上两句简化为
gcc -c -o test.o test.c 和编译过程方法【4】相同 gcc -c -o test.o test.c 和编译过程方法【4】相同
gcc -o test test.o       和执行过程方法【2】相同   gcc -o test test.o 和执行过程方法【2】相同
    那么makefile和gcc指令便建立了关系,理解起来也方便多了。           那么makefile和gcc指令便建立了关系,理解起来也方便多了。
gcc -c -o test.o test.c 和编译过程方法【4】相同 gcc -o test test.o 和执行过程方法【2】相同 那么makefile和gcc指令便建立了关系,理解起来也方便多了。
3.再完善一些 3.再完善一些
    【makefile】 【makefile】
    请替换其中的[tab],并以代码仓库中的makefile文件为主 请替换其中的[tab],并以代码仓库中的makefile文件为主
# 可执行文件
TARGET=test
# C文件
SRCS = test.c
# 目标文件
OBJS = $(SRCS:.c=.o)

# 指令编译器和选项
CC=gcc
CFLAGS=-Wall -std=gnu99

$(TARGET):$(OBJS)
#	@echo TARGET:$@
#	@echo OBJECTS:$^
 [tab]$(CC) -o $@ $^

clean:
 [tab]rm -rf $(TARGET) $(OBJS)

%.o:%.c
 [tab]$(CC) $(CFLAGS) -o $@ -c $<
# 可执行文件 TARGET=test # C文件 SRCS = test.c # 目标文件 OBJS = $(SRCS:.c=.o) # 指令编译器和选项 CC=gcc CFLAGS=-Wall -std=gnu99 $(TARGET):$(OBJS) # @echo TARGET:$@ # @echo OBJECTS:$^ [tab]$(CC) -o $@ $^ clean: [tab]rm -rf $(TARGET) $(OBJS) %.o:%.c [tab]$(CC) $(CFLAGS) -o $@ -c $<
# 可执行文件 TARGET=test # C文件 SRCS = test.c # 目标文件 OBJS = $(SRCS:.c=.o) # 指令编译器和选项 CC=gcc CFLAGS=-Wall -std=gnu99 $(TARGET):$(OBJS) # @echo TARGET:$@ # @echo OBJECTS:$^ [tab]$(CC) -o $@ $^ clean: [tab]rm -rf $(TARGET) $(OBJS) %.o:%.c [tab]$(CC) $(CFLAGS) -o $@ -c $<
    【变化说明】 【变化说明】
    【1】OBJS = $(SRCS:.c=.o) 变量替换函数,把所有的*.c文件替换为*.o文件。该函数和 【1】OBJS = $(SRCS:.c=.o) 变量替换函数,把所有的*.c文件替换为*.o文件。该函数和
            OBJS = $(patsubst %.c,%.o,$(SRCS))具有相同效果。 OBJS = $(patsubst %.c,%.o,$(SRCS))具有相同效果。
    【2】$<为自动化变量,指第一个目标文件,此处为test.c 【2】$
            替换变量和自动化变量之后: 替换变量和自动化变量之后:
            test.o:test.c test.o:test.c
            [tab]gcc -Wall -std=gnu99 -o test.o -c test.c [tab]gcc -Wall -std=gnu99 -o test.o -c test.c
4.总结 4.总结
    【1】gcc指令执行顺序——先编译目标文件,后链接成可执行文件 【1】gcc指令执行顺序——先编译目标文件,后链接成可执行文件
    【2】自动化变量 【2】自动化变量
            $@ 当前规则的目标文件 $@ 当前规则的目标文件
            $< 第一个依赖文件 $< 第一个依赖文件
            $^ 去除重复的所有依赖文件
$^ 去除重复的所有依赖文件
经验分享 程序员 微信小程序 职场和发展