Makefile使用

参考视频及博客

https://www.bilibili.com/video/BV1Xt4y1h7rH/?buvid=XU932919AEC08339E30CE57D39A2BABF6A44F&from_spmid=search.search-result.0.0&is_story_h5=false&mid=rSmqqQLB5PQj7nof4wjfpQ%3D%3D&p=4&plat_id=114&share_from=ugc&share_medium=android&share_plat=android&share_session_id=2a5b3942-3b2b-4fc4-9fd7-732da207d478&share_source=WEIXIN&share_tag=s_i&spmid=united.player-video-detail.0.0&timestamp=1730082843&unique_k=UoT3HRP&up_id=438662048
https://www.cnblogs.com/paul-617/p/15501875.html
  1. 概述

想要掌握makefile,首先需要了解两个概念,⼀个是⽬标(target),另⼀个就是依赖(dependency)。⽬标就是指要⼲什么,或说运⾏ make 后⽣成什么,⽽依赖是告诉 make 如何去做以实现⽬标。在 Makefile 中,⽬标和依赖是通过规则(rule)来表达的。

Makefile文件名:
	GNUmakefile
	makefile
	Makefile
	//通常使用Makefile

Makefile中的注释使用:#

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
使用man make可以查看帮助文档
在这里插入图片描述

  1. 基本使用
make  //默认执行当前文件夹下边的Makefile文件
make name //执行文件名为name的makefile文件
$@   自动变量
$^   所有依赖文件
$<   第一个依赖文件
make -f filename  指定makefile的文件名
make -s 执行,但不显示
make -n 打印运行时输出,但是不显示
make -c   指定makefile运行的目录
可以使用通配符% 来简化
  1. g++/gcc编译流程

    3.1一次执行

gcc mian.c

在这里插入图片描述
在这里插入图片描述
3.2将上述过程拆分:

gcc -E main.c > main.i   //预处理
gcc -S main.i  //编译:得到main.S的汇编文件
gcc -c main.s  //汇编:得到main.o的二进制文件
gcc main.o    //链接:得到a.out的可执行文件

3.2.1预处理:
在这里插入图片描述
在这里插入图片描述
3.2.2编译
在这里插入图片描述
在这里插入图片描述

3.2.3汇编
在这里插入图片描述
在这里插入图片描述

3.2.4链接
在这里插入图片描述
在这里插入图片描述
程序运行结果:
在这里插入图片描述

  1. Makefiel中变量
使用 make -p查看

5.伪目标和模式匹配
5.1伪目标
比如执行makefile中的clean目标;如果执行目录中含有clean文件,这时候会出现执行错误;可以在代码中将clean定义为伪目标来解决这个问题。

.PHONY := clean

5.2模式匹配

%.o: %.cpp #.o依赖于对应的.cpp
wildcard的使用:
	$(wildcard ./*.cpp) #获取当前目录下的所有.cpp文件
patsubst的使用:
	($patsubst %.c, %.o, ./*.c)  #获取当前文件夹中的.c文件,然后替换后,创建对应的.o文件
	#比如根据 xxx.c  创建  xxx.o

6.Makefile运行流程

保证目标是用最新的依赖生成的;
第一次完全编译,后边是部分编译;
达到第一次编译时间久,后边编译时间短的效果。

7.Makefile中动态链接库

windows:  .dll
linux:    .so
采用动态链接库:
	好处是程序和库文件分离,库文件可以共享;
	动态是运行时加载,动态加载
	链接:指的是库文件和二进制程序分离,用某种特殊手段维护二者关系。
	
g++ -shared -fPIC SoTestcpp -o libSoTest.so
g++ -lSoTest -L./ test.cpp test

8.Makefile中公共部分做头文件
就是将不同子文件夹中的Makefile写成一个公共部分,然后在子文件夹中的Makefile中引用该文件夹。
在这里插入图片描述

具体代码:

//001
//
//a.cpp
#include<iostream>
void func1(){
    printf("func1-cpp\n");
}

//
//b.cpp
#include<iostream>
void func2(){
    printf("func2-cpp\n");
}

//
//c.cpp
extern void func1();
extern void func2();
#include<iostream>
using namespace std;
int main(){
    func1();
    func2();
    cout<<"std"<<endl;
    return 0;
}


//002
//
//x.c
#include<stdio.h>
void func1(){
    printf("func1-c\n");
}

//
//y.c
#include<stdio.h>
void func2(){
    printf("func2-c\n");
}

//
//z.c
extern void func1();
extern void func2();
#include<stdio.h>
int main(){
    func1();
    func2();
    return 0;
}

由子文件001和子文件002的代码可知,代码的结构非常像。都是第三个函数来调用第一个函数中的子函数。因此Makefile中的共性可以抽取出来。
Makefile

#文件夹1中Makefile
TARGET:=c
LDLIBS:=-lstdc++
include ../makefile-head#包含公共的Makefile
#文件夹2中Makefile


TARGET=z

include ../makefile-head
#公共Makefile


#公共

SOURCE=$(wildcard  ./*.cpp ./*.c)#寻找文件夹中以.cpp和.c结尾的文件
OBJ=$(patsubst  %.cpp,%.o,$(SOURCE))#将.cpp文件截取文件名,得到*.o
OBJ:=$(patsubst  %.c,%.o,$(OBJ))#将.c文件截取文件名,得到*.o


.PHONY:clean

#如果在子文件夹中没有定义目标变量,这里定义为test
ifndef TARGET
TARGET:=test
endif
#如果在子文件夹中没有定义动态链接库文件,这里定义为空
#编译cpp文件会使用到,但是编译.c文件不会使用到
ifndef LDLIBS
LDLIBS:=
endif



$(TARGET):$(OBJ)
	$(CXX) $(LDLIBS) $^ -o $@#编译,
	#gcc $(LDLIBS) $^ -o $@


clean:
	$(RM) $(TARGET) $(OBJ)#删除文件,$(RM) 代表rm -f


show:
	echo $(SOURCE)
	echo $(OBJ)
#总的Makefile


.PHONY:001 002 clean

DIR = 001 002

all:$(DIR)

$(DIR):
	make -C $@#进入子文件夹中执行Makefile操作


clean:
	echo $(shell for dir in $(DIR); do make -C $$dir clean;done)# $$表示展开shell中的变量#分别进入每一个子文件夹中执行删除操作





# -C 指定工作目录

all-v1:
	$(MAKE) -C ./001 -f Makefile#进入文件夹执行Makefile
	$(MAKE) -C ./002 -f Makefile

clean-v1:
	$(MAKE) -C ./001 -f Makefile clean#进入文件夹执行删除操作
	$(MAKE) -C ./002 -f Makefile clean

9.在Makefile中调用shell命令
例子:

A:= $(shell ls )
B:= $(shell pwd)
all:
	@echo $(A)
	@echo $(B)

结果
在这里插入图片描述
10.Makefile中嵌套调用

就是《8》中通过顶层目录中的Makefile来分别调用子文件夹001002中的Makefile的过程,就叫嵌套调用,这里不再赘述。

11.Makefile中的条件判断

ifeq
ifneq
ifdef
ifndef

在这里插入图片描述
在这里插入图片描述

其他三个的用法与此类似。

12.Makefile中的循环
在这里插入图片描述
13.Makefile中自定义函数调用与实现
在这里插入图片描述

14.Makefile中install的实现

make  1
make install 2 3 4
make clean  5
实现完整的项目编译、安装过程
1.make 将源文件编译成二进制可执行文件(包括各种库文件)
2.创建目录,将可执行文件拷贝到指定目录(安装目录)
3.加全局可执行路径
4.加全局启停脚本
5.重置编译环境,删除无关文件

14.1源代码

//main.cpp
#include<iostream>
#include<unistd.h>
using namespace std;



int main(){

    int i=0;
    while (true){
        i++;
        cout<<"006-main-running-"<<i<<endl;
        sleep(1);
    }

    return 0;
}

14.2Makefile


TARGET:=006_main
OBJ:=$(TARGET).o

CC:=g++

PATHS:=/tmp/006_main/
BIN:=/usr/local/bin/


START_SH:=$(TARGET)_start
STOP_SH:=$(TARGET)_stop
LOG:=$(PATHS)$(TARGET).log


$(TARGET):$(OBJ)


install:$(TARGET)
	if [ -d $(PATHS) ];\
		then echo $(PATHS) exist; \
	else \
	  	mkdir $(PATHS);\
	  	cp $(TARGET) $(PATHS);\
  		ln -sv $(PATHS)$(TARGET) $(BIN);\
  		touch $(LOG);\
  		chmod a+rwx $(LOG);\
  		echo "$(TARGET)>$(LOG) & echo $(TARGET) running...">$(PATHS)$(START_SH);\
  		echo "killall $(TARGET)">$(PATHS)$(STOP_SH);\
  		chmod a+x $(PATHS)$(START_SH);\
  		chmod a+x $(PATHS)$(STOP_SH);\
  		ln -sv $(PATHS)$(START_SH) $(BIN);\
  		ln -sv $(PATHS)$(STOP_SH) $(BIN);\
  	fi;

clean:
	$(RM) $(TARGET) $(OBJ) $(BIN)$(TARGET) $(BIN)$(START_SH) $(BIN)$(STOP_SH)
	$(RM) -rf $(PATHS)

.PHONY:clean install

注意:

上边的Makefile中,PATHS不能用PATH表示,因为会和系统中的PATH冲突。
其次,采用/连接每一行的时候,/后边不能有空格 回车符这些的,不然会报错,而且这个错误比较难以找到。

TARGET:=006_main#定义目标文件
OBJ:=$(TARGET).o#定义依赖文件

CC:=g++#设置编译器为g++

PATHS:=/tmp/006_main/#定义拷贝文件的路径
BIN:=/usr/local/bin/#定义实现  006_main直接调用,存放的路径


START_SH:=$(TARGET)_start#开始脚本
STOP_SH:=$(TARGET)_stop#停止脚本
LOG:=$(PATHS)$(TARGET).log#存放程序的输出数据


$(TARGET):$(OBJ)#自动推导


install:$(TARGET)
	if [ -d $(PATHS) ];\
		then echo $(PATHS) exist; \
	else \
	  	mkdir $(PATHS);\#创建文件夹
	  	cp $(TARGET) $(PATHS);\#将生成的006_main拷贝到路径中
  		ln -sv $(PATHS)$(TARGET) $(BIN);\#创建软链接
  		touch $(LOG);\#创建存放输出信息的文件
  		chmod a+rwx $(LOG);\#改变文件路径
  		echo "$(TARGET)>$(LOG) & echo $(TARGET) running...">$(PATHS)$(START_SH);\#创建开始脚本
  		echo "killall $(TARGET)">$(PATHS)$(STOP_SH);\#创建停止脚本
  		chmod a+x $(PATHS)$(START_SH);\#改变文件权限
  		chmod a+x $(PATHS)$(STOP_SH);\#改变文件权限
  		ln -sv $(PATHS)$(START_SH) $(BIN);\#创建软链接
  		ln -sv $(PATHS)$(STOP_SH) $(BIN);\#创建软链接
  	fi;

clean:
	$(RM) $(TARGET) $(OBJ) $(BIN)$(TARGET) $(BIN)$(START_SH) $(BIN)$(STOP_SH)#删除不需要的文件
	$(RM) -rf $(PATHS)#删除文件夹

.PHONY:clean install#定义为目标

注意:上述的Makefile文件需要再root 用户下运行。

上一篇:深入理解 Logback.xml文件及运用


下一篇:构建生产级的 RAG 系统