学习笔记|Makefile示例

About Makefile

Posted by LGT on March 23, 2023

Makefile

这里主要通过示例展示makefile的用法

make(GNU make) 是一个项目构建工具,即方便地编译、链接多个源代码文件,自动决定哪些源文件需要重新编译,从而高效地构建自己的项目。

  • 基本规则
1
2
3
4
5
目标文件:依赖文件
     命令1
     命令2
     ...
     命令n
  • 一些符号含义
    • $* 不包含扩展名的目标文件名称。
    • $+ 所有的依赖文件,以空格分开,并以出现的先后为序,可能包含重复的依赖文件。
    • $< 第一个依赖文件的名称。
    • $? 所有的依赖文件,以空格分开,这些依赖文件的修改日期比目标的创建日期晚。
    • $@ 目标的完整名称。
    • $^ 所有的依赖文件,以空格分开,不包含重复的依赖文件。
    • $% 如果目标是归档成员,则该变量表示目标的归档成员名称。

Example 1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# 变量定义
INCL=-I${HOME}/incl
BIN=$(HOME)/bin
OBJ1=hellocpp.o
OBJ2=hello.o

# 隐含规则,即所有的.o文件都首先通过以下规则编译生成
.SUFFIXES: .cpp .c
.cpp.o:
	g++ ${INCL} -c $<

.c.o:
	gcc ${INCL} -c $<

# 目标可执行文件
all: hellocpp hello

# C++编译
hellocpp:${OBJ1}
	# @表示当前命令不显示
	@echo "开始编译"
	g++ -o $@ $?
	@rm -f ${OBJ1}
	@mv $@ ${BIN}
	@echo "编译结束"
	@echo ""

#C编译
hello:${OBJ2}
	@echo "开始编译"
	gcc -o $@ $?
	@rm -f ${OBJ2}
	@mv $@ ${BIN}
	@echo "编译结束"
	@echo ""

Reference: Makefile由浅入深

Example 2

示例项目:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
.
    ├── include         # 本文件下包含构建目标文件所需的头文件
    │   ├── become_daemon.h
    │   ├── error_functions.h
    │   ├── get_num.h
    │   ├── inet_sockets.h
    │   └── tlpi_hdr.h
    ├── lib             # 本文件夹下包含构建目标文件所需的库文件和依赖文件
    │   ├── become_daemon.c
    │   ├── ename.c.inc
    │   ├── error_functions.c
    │   ├── get_num.c
    │   └── inet_sockets.c
    └── src             # 本文件夹包含项目的源文件、Makefile、目标文件以及可执行文件
        ├── obj
        │   ├── become_daemon.o
        │   ├── client.o
        │   ├── error_functions.o
        │   ├── get_num.o
        │   ├── inet_sockets.o
        │   └── server.o
        ├── Makefile
        ├── client
        ├── client.c
        ├── server
        └── server.c
  • simple Makefile

    • 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      
      # compile
      obj/error_functions.o: ../lib/error_functions.c
          gcc -c -o obj/error_functions.o ../lib/error_functions.c 
          
      obj/get_num.o: ../lib/get_num.c
          gcc -c -o obj/get_num.o ../lib/get_num.c 
          
      obj/inet_sockets.o: ../lib/inet_sockets.c
          gcc -c -o obj/inet_sockets.o ../lib/inet_sockets.c 
          
      obj/become_daemon.o: ../lib/become_daemon.c
          gcc -c -o obj/become_daemon.o ../lib/become_daemon.c 
          
      obj/client.o: client.c
          gcc -c -o obj/client.o client.c 
          
      obj/server.o: server.c
          gcc -c -o obj/server.o server.c 
          
      # link
      client: obj/client.o  obj/error_functions.o  obj/get_num.o  obj/inet_sockets.o  obj/become_daemon.o 
          gcc -o client obj/client.o  obj/error_functions.o  obj/get_num.o  obj/inet_sockets.o  obj/become_daemon.o 
          
      server: obj/server.o  obj/error_functions.o  obj/get_num.o  obj/inet_sockets.o  obj/become_daemon.o 
          gcc -o server obj/server.o  obj/error_functions.o  obj/get_num.o  obj/inet_sockets.o  obj/become_daemon.
          
      # .PHONY表示clean不是一个文件而是一个命令的名字。
      .PHONY: clean
      clean:
          rm obj/*.o client server
      
  • 使用变量、模式规则、函数后的Makefile

    • make内建函数的语法为:

      • 1
        
        $(function arguments)
        
    • 如下,make内建函数patsubst,它的作用是给_DEPS变量的每个元素(空格分开)添加$(ODIR)/前缀。

    • 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      
      CC=gcc
      CFLAGS=-Wall -Wformat=0
      ODIR=obj
      IDIR=../include
      LDIR=../lib
      PROGRAM=client server
          
      _DEPS = error_functions.o get_num.o inet_sockets.o become_daemon.o
      DEPS = $(patsubst %, $(ODIR)/%,$(_DEPS))
          
      all: $(PROGRAM)
          
      # compile
      # 模式规则,将$(LDIR)中的所有.c和当前文件夹下的.c文件编译生成.o文件
      $(ODIR)/%.o: $(LDIR)/%.c
          $(CC) -c -o $@ $<
          
      $(ODIR)/%.o: %.c
          $(CC) -c -o $@ $< 
          
      # link
      # addsuffix函数表示给$(PROGRAM)每个元素添加.o后缀
      $(PROGRAM): $(patsubst %, $(ODIR)/%,$(addsuffix .o,$(PROGRAM))) $(DEPS)
          $(CC) -o $@ $(ODIR)/$@.o $(DEPS) 
          
      .PHONY: clean
      clean: 
          rm $(ODIR)/*.o $(PROGRAM)
      

Reference: GNU make/Makefile 简明实用教程

Example 3

  • 更简单的例子

  • 1
    2
    3
    4
    5
    6
    
    .
        ├── hello.c
        ├── hellomake.c
        ├── helloworld.c
        ├── thread.c
        └── multithreads.c
    
  • 1
    2
    3
    4
    5
    6
    7
    
    CC=gcc
    CFLAGS=-O2 -pthread
    TRAGET=hello hellomake helloworld thread multithreads
    all:$(TRAGET)
      
    clean:
    	rm $(TRAGET)