make相关 - HeavyYuan/A-CD-Record-Management-System GitHub Wiki

编译程序

make一般用来编译程序

一、背景

在构建大型程序时,会创建多个头文件的包含关系,被依赖的文件有变动,就需要编译依赖它的所有文件。

人为来做这些事,会存在很大工作量,且容易遗漏出错。

make工具可以解决上述问题,其在必要时编译所有受改动影响的所有源文件

二、makefile语法

make无法独自完成项目构建,需要makefile给出构建路径(如何构造)

makefile文件由一组依赖关系规则构成

1. 每个依赖关系由一个目标(即将要创建的文件)和一组该目标所依赖的源文件组成。如:

myapp: main.o 2.o 3.o
main.o: main.c a.h
2.o: 2.c a.h b.h
3.o: 3.c b.h c.h

make根据依赖关系,来判断目标文件是否需要因为源文件的改动而重新编译

测试:可以先构建一次myapp,然后修改2.o这一行为2.o: a.h b.b,然后在修改2.c,并构建。此时2.o就不会重新构建了。

2. 规则描述的是如何通过这些依赖关系文件创建目标,如:(gcc打头的就是规则)

myapp: main.o 2.o 3.o			
	gcc -o myapp main.o 2.o 3.o
main.o: main.c a.h
	gcc -c main.c
2.o: 2.c a.h b.h
	gcc -c 2.c
3.o: 3.c b.h c.h
	gcc -c 3.c

功能性规则:

clean:  #清理项目
	rm -f *.o myapp

test:   #测试
	./myapp

通过make clean/test来执行。

trick:由于make有对.c文件的内置规则,对于依赖.c文件的中间目标(main.o, 2.o, 3.o), 可以省略其对应的规则

只保留最终目标(myapp)的规则,make仍然可以正确运行。

3. 函数库管理

make的内置规则为:$(AR) $(ARFLAGS) $@ $<, AR = ar, ARFLAGS=rv

makefile中,的依赖关系为:

MYLIB = mylib.a

$(MYLIB): $(MYLIB)(2.o) $(MYLIB)(3.o)

4. makefile文件和子目录

对于大型项目中,我们希望将构成一个函数库的多个文件从主目录中分离出来单独放置与一个子目录中。

方法1:(该方法已经验证可行)

1)在子目录中创建构建该函数库的makefile(用默认规则编译)

mylib.a: mylib.a 2.o mylib.a 3.o
...

2)在主目录中的makefile中添加如下命令:

...
mylib.a:
      (cd subdir;$(MAKE))
      cp subdir/mylib.a ./
...

方法2:(已验证可行)

1)子目录中不需要创建makefile

2)主目录中的makefile,增加如下:

MYLIB = mylib.a

#替换.c到.o的内置规则,D代表目录,F代表文件
%.o: %c
    $(CC) $(FLAGS) -c $(@D)/$(<F) -o $(@D)/$(@F)

#mylib.a的创建规则
$(MYLIB): $(SUBDIR)/2.o $(SUBDIR)/3.o
    ar -rv $(MYLIB) $?

5.makefile的中的注释为#

三、make命令options和arguments

-k: 在编译出错时,仍然继续编译,而不是在检测到第一个错误就停止。

-n: 让make输出将要执行的操作步骤,而不是真正执行这些操作

-p: 打印出make的所有内置规则

-jN: N是具体数字,多任务编译,-j8就是8个任务同时编译

-f : 指定make要用的file。不指定filename时,默认用的文件时makefile(高优先级)和Makefile(低优先级)

make filename : 编译filename.c文件

另,gcc命令可以查看目标文件的依赖关系,如:

gcc -MM main.c

该依赖关系可以直接作为makefile的输入

四、宏应用

宏的应用,可以在大型项目中增加项目构建的弹性(方便修改构建相关的环境信息),其一般应用于设置编译器的选项。

宏可以分为make的内置宏和makefile中用户自定义的宏

1. make的内置特殊宏

$? - 当前目标所依赖的文件列表中比当前目标文件还要新的文件
$@ - 当前目标的名字
$< - 当前依赖文件的名字
$* - 不包括后缀名的当前依赖文件的名字

CFLAGS,make CFLAGS=-g filename 在构建时加上了debug信息

2. makefile中的特殊字符

- :告诉make命令忽略所有的错误
@ :告诉make在执行某条命令前不要将该命令显示在标准输出上。当想用echo命令给出说明信息时,比较实用

3. makefile宏的应用

#which compiler
CC = gcc

#where to install
INSTALL = /usr/local/bin

#Where are include files kept
INCLUDE =

#Options for development
CFALGS = -g -Wall -ansi

myapp : main.o 2.o 3.o
      $(CC) -o myapp main.o 2.o 3.o
main.o: main.c a.h
      $(CC) -I$(INCLUDE) $(CFLAGS) -c main.c

...

install: myapp
     @if [ -d $(INSTALL) ]; \
         then \
         cp myapp $(INSTALL); \
         ...
         fi

由于make命令在执行规则时会调用新的shell, 每条规则使用一个新shell, 所以必须在上述install的规则后都加上\,使所有命令

在一行,作为一个整体传给shell执行。也可以将两条命令放在()中,来实现在同一个shell中执行,如:(cmd1;cmd2)

五、make内置规则

内置规则极大简化了makefile的文件内容

make -p | grep COMPILE 可以看到不同后缀名文件的内置编译规则

这用依赖后缀名的规则也可以自己在makefile中定义,比如,可以重改.c文件到.o文件的内置规则:

.SUFFIXES: .c     //1
.c.o:             //2
     touch $*.o   //3

myapp: main.o 2.o 3.o
main.o: main.c a.h
2.o: 2.c a.h b.h
3.o: 3.c b.h c.h

执行make后,.o文件的生成都是touch命令来完成,该规则覆盖了make本身内置的关于.c到.o文件的规则。

新版本make,可以更简单来完成内置,上述1,2,3行可以修改成:

%.o: %.c
    touch $*.o

其他功能

makefile文件并不只用于编译源代码或创建函数库

任何可以通过一系列命令从某些类型的输入文件得到输出文件的任务,都可以通过makefile完成

⚠️ **GitHub.com Fallback** ⚠️