(二)同级目录下makefile应用进阶实例分析

前言

假设一个工程包含 main.cpp / dog.cpp / cat.cpp / snake.cpp 以及 dog.h / cat.h / snake.h等文件在同一级目录下面,文件结构如下所示

  • window目录下查看

  • linux平台下查看

 

其中程序如下所示:

/* dog.cpp */ 
#include <stdio.h>                                           
void dogPrintf(void)        
{                              
    printf("I am a dog !\n");  
}

/* dog.h */              
#ifndef _DOG_H 
#define _DOG_H 
void dogPrintf(void);
#endif
/* cat.cpp */ 
#include <stdio.h>                                           
void catPrintf(void)        
{                              
    printf("I am a cat!\n");  
}

/* cat.h */              
#ifndef _CAT_H 
#define _CAT_H 
void catPrintf(void);
#endif
/* snake.cpp */ 
#include <stdio.h>                                           
void snakePrintf(void)        
{                              
    printf("I am a snake!\n");  
}

/* snake.h */              
#ifndef _SNAKE_H 
#define _SNAKE_H 
void snakePrintf(void);
#endif
/* main.c */

#include <iostream>
#include "dog.h"
#include "cat.h"
#include "snake.h"

 

int main(void)
{
dogPrintf();
catPrintf();
snakePrintf();
return 0;
}

 (1)不使用makefile,直接编译

在linux环境下使用g++编译流程如下所示:

shenqian5@Cpl-MT-General-74-39:~/work/test$ ls
cat.cpp  cat.h  dog.cpp  dog.h  main.cpp  snake.cpp  snake.h
shenqian5@Cpl-MT-General-74-39:~/work/test$ g++ -c dog.cpp
shenqian5@Cpl-MT-General-74-39:~/work/test$ g++ -c cat.cpp
shenqian5@Cpl-MT-General-74-39:~/work/test$ g++ -c snake.cpp
shenqian5@Cpl-MT-General-74-39:~/work/test$ g++ -c main.cpp
shenqian5@Cpl-MT-General-74-39:~/work/test$ ls
cat.cpp  cat.h  cat.o  dog.cpp  dog.h  dog.o  main.cpp  main.o  snake.cpp  snake.h  snake.o
shenqian5@Cpl-MT-General-74-39:~/work/test$ g++ -o main main.o dog.o cat.o snake.o
shenqian5@Cpl-MT-General-74-39:~/work/test$ ls
cat.cpp  cat.h  cat.o  dog.cpp  dog.h  dog.o  main  main.cpp  main.o  snake.cpp  snake.h  snake.o
shenqian5@Cpl-MT-General-74-39:~/work/test$ ./main
I am a dog !
I am a cat!
I am a snake!
shenqian5@Cpl-MT-General-74-39:~/work/test$ 

总结:不适用makefile直接编译,每次编译都要经过繁杂的步骤,对于大型项目基本不可能得到推广,因此必须引入makefile

(2)初级makefile,进行编译

此时需要在同级目录下增加一个makefile文件(makefile文件没有后缀名),如图所示:

 

然后在makefile文件中编写编译规则,初级makefile内容如下所示:

main : main.o dog.o cat.o snake.o
    g++ -o main main.o dog.o cat.o snake.o
main.o : main.cpp dog.h cat.h snake.h
    g++ -c main.cpp
dog.o : dog.cpp dog.h
    g++ -c dog.cpp
cat.o : cat.cpp cat.h
    g++ -c cat.cpp
snake.o : snake.cpp snake.h
    g++ -c snake.cpp

.PHONY : clean
clean:
    rm -rf *.o main

在linux平台下执行如下:

shenqian5@Cpl-MT-General-74-39:~/work/test$ make
g++ -c main.cpp
g++ -c dog.cpp
g++ -c cat.cpp
g++ -c snake.cpp
g++ -o main main.o dog.o cat.o snake.o
shenqian5@Cpl-MT-General-74-39:~/work/test$ ./main
I am a dog !
I am a cat!
I am a snake!
shenqian5@Cpl-MT-General-74-39:~/work/test$ 

(3)进阶makefile,进行编译

使用定义变量进行替代,以及$@   $^   $<  三个自动化变量进一步的进行简化初级makefile文件,如下所示:

$@ ——  表示规则中的目标文件集

$^   ——  表示所有的依赖目标的集合  

$<  ——   表示依赖目标中的第一个目标名字

TARGET = test
CC = g++
CFLAGS = -c -Wall -g

$(TARGET) : main.o dog.o cat.o snake.o
    g++ -o $@ $^
main.o : main.cpp dog.h cat.h snake.h
    g++ -c $<
dog.o : dog.cpp dog.h
    g++ -c $<
cat.o : cat.cpp cat.h
    g++ -c $<
snake.o : snake.cpp snake.h
    g++ -c $<

.PHONY : clean
clean:
    rm -rf *.o $(TARGET)

(4)高级makefile,进行编译

TARGET = test
CC = g++
CFLAGS = -c -Wall -g

$(TARGET) : main.o dog.o cat.o snake.o
    g++ -o $@ $^
.c .o : 
    g++ -c $<

.PHONY : clean
clean:
    rm -rf *.o $(TARGET)

这里需要注意的一点是:

.c .o : 
    g++ -c $<

在这里表示的是,所有的.o文件都是依赖于相应的.c文件,这是Makefile的“后缀规则”,是一种比较古老的规则,可以使用“模式规则”来实现:

 1 TARGET = test
 2 CC = g++
 3 CFLAGS = -c -Wall -g
 4 
 5 $(TARGET) : main.o dog.o cat.o snake.o
 6     g++ -o $@ $^
 7 %.c : %.o
 8     g++ -c $<
 9 
10 .PHONY : clean
11 clean:
12     rm -rf *.o $(TARGET)

(5)终极makefile,进行编译

 1 TARGET := test
 2 CC = g++
 3 CFLAGS = -c -Wall -g
 4 
 5 %.o : %.c
 6     $(CC) -c $< -o $@
 7 SOURCE := $(wildcard *.cpp)
 8 OBJS := $(patsubst %.cpp, %.o, $(SOURCE))
 9 
10 $(TARGET) : $(OBJS)
11     $(CC) -g -o ./$(TARGET) $(OBJS)
12  
13 .PHONY : clean
14 clean:
15     $(RM) -rf *.o $(TARGET)

第1行     :=  表示前面的变量不能使用后面的变量,比如 TARGET :=  $(OBJS) 将会出错。

第5行     %.o : %.c  这个是模式规则

第6行     自动化变量 $<  在模式规则中,表示依赖集合, $@ 表示目标集合

第7行     wildcard是扩展通配符,是Makefile的一个函数,这里是Makefile的函数调用。在这里是将当前目录下所有 .cpp 结尾的文件展开

第8行     patsubst 是替换通配符,将SOURCE中以 *cpp 结尾的名字替换为 *.o 其实这里OBJ的值就是 main.o dog.o cat.o snake.o 而SOURCE的值为:main.cpp dog.cpp cat.cpp snake.cpp

第11行   正常的Makefile书写,目标:依赖,规则,只是这里都是用变量代替了。

第13行   使用了伪目标clean,用来执行清除工作。

第15行   清除编译产生的中间文件。RM 的默认值是 rm -f 这里执行的是:rm -f *.o test

posted @ 2021-10-16 14:46  clearwaterBay  阅读(160)  评论(0)    收藏  举报