Makefile之清晰认知

Makefile之清晰认知

关于Makefile,之前我在初学makefile依旧没有清晰认知。所以这次,我将着重探究一下Makefile文件的编写。

0.Makefile是什么

Makefile可以理解为一个脚本的执行器,也可以理解为一种简单语言

很多人觉得很神奇,包括我也是,第一次接触makefile,觉得这个东西非常神奇,为什么可以自动编译多文件?

我们可以简单理解为这个文件可以自动帮你执行一些命令,例如Python也可以帮你做运维,这个就类似于运维中的脚本文件

1.一般使用

# This is a standard makefile
main:main.o module1.o module2.o
	gcc main.o module1.o module2.o -o main
main.o:main.c head1.h head2.h common_head.h
	gcc -c main.c
module1.o:module1.c head1.h
	gcc -c module1.c
module2.o:module2.c head2.h
	gcc -c module2.c

结构为

[目标文件列表] [分隔符] [依赖文件列表]
	[命令]

这个Makefile的HelloWorld其实已经困扰了很多人。困扰的点如下:

1.为什么gcc编译的时候不需要加头文件?

答:gcc在编译的时候,只需要进行编译C文件。

h文件的作用仅起到替换的作用。我们在引入#include <stdlib.h>之后,在预编译的过程中,会将stdlib.h中的声明完全替换到本文件中。

误解:很多人会将#include以为是引入库,但实际不是,这个命令的作用是在预编译过程中将.h文件进行替换,真正要编译的是C文件。

证明:我们使用gcc -E main.c试一下,查看预编译后的文件,发现,头文件已经替换到了预编译后的文件中

编写一个max.h文件

int max(int a, int b);

编写一个main.c文件

#include <max.h>
int main() {
    return 0;
}

编写一个max.c文件

int max(int a, int b) {
    return a > b ? a : b;
}

在Linux下进行gcc -E main.c max.c,输出

# 1 "t.c"
# 1 "<built-in>" 1
# 1 "<built-in>" 3
# 362 "<built-in>" 3
# 1 "<command line>" 1
# 1 "<built-in>" 2
# 1 "t.c" 2
# 1 "./max.h" 1
int max(int a, int b);
# 2 "t.c" 2
int main() {
    return 0;
}
# 1 "max.c"
# 1 "<built-in>" 1
# 1 "<built-in>" 3
# 362 "<built-in>" 3
# 1 "<command line>" 1
# 1 "<built-in>" 2
# 1 "max.c" 2
int max(int a, int b) {
    return a > b ? a : b;
}

在预编译过程中,进行了文件替换,在#include语句完全替换成了h文件中的语句,而在文件末也附加了新的max.c,由此我们可以深刻理解这个过程

2.gcc -c是什么?

gcc -c 执行了3步操作

gcc -E进行预处理,gcc -S进行翻译汇编操作,gcc -c进行编译成链接文件(.o文件)

3.我们为什么需要倒着写?

问这个问题的人应该已经发现了,我们是需要倒着进行运行命令的,这个是makefile的规范。

如果熟悉如何用gcc在命令行进行编译多文件,那么直接进行倒着书写makefile即可。

makefile在进行寻找的过程中,首先执行第一行,发现需要下面模块,再执行下面的,所以看起来倒着,假设a依赖b,c,d三个独立模块,我们测试进行输出,答案是b,c,d,a,案例:

# This is a Makefile
  
main:main.o printXX.o printXXX.o
        gcc main.o printXX.o printXXX.o
        echo "a"
main.o:main.c printXX.h printXXX.h
        gcc -c main.c
        echo "b"
printXX.o:printXX.c printXX.h
        gcc -c printXX.c
        echo "c"
printXXX.o:printXXX.c printXXX.h
        gcc -c printXXX.c
        echo "d"
4.第二行报错?

实际上gcc前面那个空格是一个tab键,如果你按了2下空格,会使其报错。

如果你想简单实用makefile,了解到这边,其实已经能够清晰的编写makefile文件了。下面是附加性知识。

2.语法

1.变量的定义

我们在使用过程中,会使用到大量的gcc命令,而实际生产中,我们不一定会用gcc进行编译,常见的有cc,g++,都可以进行编译,如果要进行替换,整篇Makefile都得进行替换,那样会极其不便,所以,我们进行了一个变量替换操作。

我们来看一个例子:

CC = gcc
all:
	echo $(CC)

我们在头部进行了替换操作。

2.预定义变量

all:
        echo $(CC) $(CFLAGS) $(MAKE)

我们的cc含有很多预定义变量,如CC、CFLAGS等,我们可以编写一个这样的文件进行打印

预定义变量如下:

  • CC 默认的cc命令

  • CFLAGS -o执行

  • MAKE make命令的地址

  • MAKEFLAGS make的选项

  • SHELL 默认shell类型

  • PWD 当前makefile的目录

  • AR 库管理

  • ARFLAGS 库管理选项

  • LIBSUFFIXE 库后缀

  • A 库拓展名

3.动态改变的变量

我们在查看Makefile的时候,常常看到$@ $% $< $> $? $^ $+ $*

  • $@ 表示目标文件名,Linux中,一般称.a文件为文档文件,或者静态库文件。
main: a.c b.h
	gcc a.c

上述makefile的main就是目标文件名,如果我们在makefile中echo $@,则打印的是main

所以我们可以进行

main: a.c b.h
	gcc a.c -o %@
  • $% 静态库的第一个成员名,如果链接的第一个静态库是foo.a,则会打印出来,这边不加演示,都可进行echo打印
  • $< 第一个依赖库名
  • $> 值是库名(适用库文件)
  • $? 所有比目标新的依赖文件列表
  • $^ 规则的所有依赖列表,去除重复文件
  • \(+ 可素\)^ 保留重复文件
  • $* 目标去除后缀,例如 main.o 这个后缀去掉
4.条件语句
ifeq(gcc,$(CC))
	# 执行编译
else
	echo "error"
endif

条件语句可以根据条件进行编译

5.定义条件
ifdef foo
	echo "YES"
ifndef foo
	foo = foo

定义条件,判定是否定义

posted @ 2020-02-18 12:21  SteveYu  阅读(175)  评论(0编辑  收藏  举报