news 2026/5/16 19:10:02

动态库makefile生成模板手把手教学

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
动态库makefile生成模板手把手教学
# ============================================== # 基础配置(用户根据项目修改这部分即可) # ============================================== # 生成的动态库名称 TARGET := libipapply.so # 源文件目录(默认当前目录,多个目录用空格分隔) SRC_DIRS := . # 头文件搜索路径(通用路径,平台特有路径在下方配置) INCLUDES := . INCLUDES += ../include INCLUDES += $(LIB_ROOT)/include # 库文件搜索路径(通用路径,平台特有路径在下方配置) LIBPATH := . LIBPATH += ./lib LIBPATH += $(LIB_ROOT)/lib # 清理命令 RM := rm -f # ============================================== # 平台选择(取消注释你需要编译的平台,只能选一个) # ============================================== # PLATFORM := X86_64 # PC x86_64 平台 # PLATFORM := ARM64_GENERIC # 通用ARM64平台 PLATFORM := RK3588 # 瑞芯微RK3588平台 # PLATFORM := ARM64_RDBI # 自定义ARM64平台 # ============================================== # 分平台差异化配置(根据需要修改) # ============================================== ifeq ($(PLATFORM), X86_64) # 编译器 CC := gcc CXX := g++ AR := ar STRIP := strip # 编译选项 CFLAGS := -g -O1 -Wall -march=x86-64 -std=gnu99 CPPFLAGS := -g -O1 -Wall -march=x86-64 -std=c++11 CPPFLAGS += -D DEV_TYP_FT # 自定义宏定义 # Boost库路径(x86_64默认系统路径) BOOST_ROOT := /usr/local INCLUDES += $(BOOST_ROOT)/include LIBPATH += $(BOOST_ROOT)/lib # 链接库(静态链接boost_system,动态链接系统库) LIBS := -Wl,-Bstatic -lboost_system -Wl,-Bdynamic -lpthread -lm -ldl -lrt else ifeq ($(PLATFORM), ARM64_GENERIC) # 通用ARM64交叉编译器 CC := aarch64-linux-gnu-gcc CXX := aarch64-linux-gnu-g++ AR := aarch64-linux-gnu-ar STRIP := aarch64-linux-gnu-strip CFLAGS := -g -O3 -Wall -std=gnu99 CPPFLAGS := -g -O3 -Wall -std=c++11 # ARM64 Boost库路径 BOOST_ROOT := /usr/local/boost_arm64_none INCLUDES += $(BOOST_ROOT)/include LIBPATH += $(BOOST_ROOT)/lib LIBS := -Wl,-Bstatic -lboost_system -Wl,-Bdynamic -lpthread -lm -ldl -lrt else ifeq ($(PLATFORM), RK3588) # 瑞芯微RK3588专用交叉编译器 CC := aarch64-rockchip1031-linux-gnu-gcc CXX := aarch64-rockchip1031-linux-gnu-g++ AR := aarch64-rockchip1031-linux-gnu-ar STRIP := aarch64-rockchip1031-linux-gnu-strip CFLAGS := -g -O3 -Wall -std=gnu99 CPPFLAGS := -g -O3 -Wall -std=c++11 BOOST_ROOT := /usr/local/boost_arm64_none INCLUDES += $(BOOST_ROOT)/include LIBPATH += $(BOOST_ROOT)/lib LIBS := -Wl,-Bstatic -lboost_system -Wl,-Bdynamic -lpthread -lm -ldl -lrt else ifeq ($(PLATFORM), ARM64_RDBI) # 自定义ARM64平台配置 CC := aarch64-rockchip1031-linux-gnu-gcc CXX := aarch64-rockchip1031-linux-gnu-g++ AR := aarch64-rockchip1031-linux-gnu-ar STRIP := aarch64-rockchip1031-linux-gnu-strip CFLAGS := -g -O3 -Wall -std=gnu99 CPPFLAGS := -g -O3 -Wall -std=c++11 BOOST_ROOT := /home/rdci/usr1/tools/boost/boost_arm64_none INCLUDES += $(BOOST_ROOT)/include LIBPATH += $(BOOST_ROOT)/lib LIBS := -Wl,-Bstatic -lboost_system -Wl,-Bdynamic -lpthread -lm -ldl -lrt endif # ============================================== # 通用编译选项(所有平台共用,一般无需修改) # ============================================== # 添加头文件路径前缀 -I CPPFLAGS += $(addprefix -I,$(INCLUDES)) # 自动生成依赖文件(.d) CPPFLAGS += -MMD # 生成位置无关代码(动态库必须) CPPFLAGS += -fPIC # 隐藏内部符号,减小库体积 CPPFLAGS += -fvisibility=hidden # 链接选项:生成动态库 LDFLAGS := -shared # ============================================== # 自动查找源文件和生成目标文件(无需修改) # ============================================== # 查找所有.c/.cpp/.cc源文件 CSRCS := $(foreach dir,$(SRC_DIRS),$(wildcard $(dir)/*.c)) CPPSRCS := $(foreach dir,$(SRC_DIRS),$(wildcard $(dir)/*.cpp)) CCSRCS := $(foreach dir,$(SRC_DIRS),$(wildcard $(dir)/*.cc)) # 生成对应的目标文件(.o) COBJS := $(patsubst %.c,%.o,$(CSRCS)) CPPOBJS := $(patsubst %.cpp,%.o,$(CPPSRCS)) CCOBJS := $(patsubst %.cc,%.o,$(CCSRCS)) # 生成对应的依赖文件(.d) CDEPS := $(patsubst %.o,%.d,$(COBJS)) CPPDEPS := $(patsubst %.o,%.d,$(CPPOBJS)) CCDEPS := $(patsubst %.o,%.d,$(CCOBJS)) # ============================================== # 伪目标定义(无需修改) # ============================================== .PHONY: all deps objs clean rebuild # 默认目标:生成动态库 all: $(TARGET) # 生成依赖文件 deps: $(CDEPS) $(CPPDEPS) $(CCDEPS) $(CXX) $(CPPFLAGS) -MM $(CSRCS) $(CPPSRCS) $(CCSRCS) # 生成目标文件 objs: $(COBJS) $(CPPOBJS) $(CCOBJS) # 清理中间文件和目标文件 clean: @$(RM) $(COBJS) $(CPPOBJS) $(CCOBJS) @$(RM) $(CDEPS) $(CPPDEPS) $(CCDEPS) @$(RM) $(TARGET) # 重新编译 rebuild: clean all # ============================================== # 最终链接规则(无需修改) # ============================================== # 包含自动生成的依赖文件 -include $(CDEPS) $(CPPDEPS) $(CCDEPS) # 链接生成动态库并strip去掉调试信息 $(TARGET): $(COBJS) $(CPPOBJS) $(CCOBJS) $(CXX) $(CPPFLAGS) $(LDFLAGS) -o $@ $^ $(addprefix -L,$(LIBPATH)) $(LIBS) $(STRIP) $@ # 编译完成后自动清理中间文件(可选,注释掉保留.o和.d文件加速增量编译) @$(RM) $(COBJS) $(CPPOBJS) $(CCOBJS) @$(RM) $(CDEPS) $(CPPDEPS) $(CCDEPS)

预备知识

Makefile本质是一个**“自动化编译说明书”**,它解决的核心问题是:

当你有几十上百个C/C++文件时,不用手动敲几十行g++ xxx.cpp yyy.cpp -o xxx -Ixxx -Lxxx -lxxx,只要敲一个make,它就会自动帮你完成所有编译工作。

它的核心逻辑只有3条:

  1. 目标:我要生成什么文件(比如libipapply.so
  2. 依赖:生成这个文件需要哪些原材料(比如所有的.o目标文件)
  3. 命令:用什么命令把原材料加工成目标

第一部分:基础配置(你唯一需要经常改的部分)

# ============================================== # 基础配置(用户根据项目修改这部分即可) # ==============================================

👉 这是注释,#后面的所有内容Makefile都会忽略,只是给人看的说明文字。


# 生成的动态库名称 TARGET := libipapply.so

语法变量名 := 值—— 定义一个变量,把右边的值赋给左边的变量。
作用:告诉Makefile,我们最终要生成的文件名叫libipapply.so
什么时候改:当你要生成别的名字的动态库时,比如改成libmyutils.so
💡 小知识:Linux下动态库的命名规范是libxxx.so,这样别人才能用-lxxx链接它。


# 源文件目录(默认当前目录,多个目录用空格分隔) SRC_DIRS := .

语法.代表当前目录
作用:告诉Makefile,你的所有C/C++源文件(.c/.cpp/.cc)都放在哪些目录里。
什么时候改:如果你的源文件放在srcutils等子目录里,就改成SRC_DIRS := . src utils


# 头文件搜索路径(通用路径,平台特有路径在下方配置) INCLUDES := . INCLUDES += ../include INCLUDES += $(LIB_ROOT)/include

语法

  • +=:给已经存在的变量追加内容
  • $(变量名):引用一个变量的值,这里$(LIB_ROOT)就是引用LIB_ROOT这个变量的值
    作用:告诉C/C++编译器,当你在代码里写#include "xxx.h"时,去哪些目录里找这个头文件。
    什么时候改:当你新增了一个头文件目录时,就加一行INCLUDES += 你的头文件目录
    💡 小知识:编译器默认只会在当前目录找头文件,不在当前目录的必须在这里告诉它。

# 库文件搜索路径(通用路径,平台特有路径在下方配置) LIBPATH := . LIBPATH += ./lib LIBPATH += $(LIB_ROOT)/lib

作用:告诉链接器,当你要链接某个库(比如libboost_system.so)时,去哪些目录里找这个库文件。
什么时候改:当你新增了一个存放第三方库的目录时,就加一行LIBPATH += 你的库文件目录


# 清理命令 RM := rm -f

作用:定义一个删除文件的命令变量,rm -f是Linux下强制删除文件的命令(不会提示,也不会因为文件不存在而报错)。
什么时候改:几乎不用改,这是标准写法。


第二部分:平台选择(编译前必须选对)

# ============================================== # 平台选择(取消注释你需要编译的平台,只能选一个) # ============================================== # PLATFORM := X86_64 # PC x86_64 平台 # PLATFORM := ARM64_GENERIC # 通用ARM64平台 PLATFORM := RK3588 # 瑞芯微RK3588平台 # PLATFORM := ARM64_RDBI # 自定义ARM64平台

语法:这里的#注释掉一行代码,去掉#就是让这行代码生效。
作用:告诉Makefile,你要把代码编译成能在哪个CPU架构上运行的程序。
为什么要选平台

  • 你的电脑是x86_64架构(Intel/AMD CPU),手机和嵌入式板子是ARM64架构
  • 不同架构的CPU指令集完全不一样,所以需要用不同的编译器
    使用规则只能取消一个平台的注释,不能同时选多个。
    什么时候改
  • 想在自己电脑上编译调试 → 取消PLATFORM := X86_64的注释
  • 想编译给瑞芯微RK3588板子用 → 取消PLATFORM := RK3588的注释

第三部分:分平台差异化配置(一般不用改)

# ============================================== # 分平台差异化配置(根据需要修改) # ============================================== ifeq ($(PLATFORM), X86_64)

语法ifeq ($(变量名), 比较值)—— 条件判断,如果变量的值等于比较值,就执行下面的代码,直到遇到else ifeqendif
作用:根据你上面选的平台,自动加载对应的编译器和配置。


# 编译器 CC := gcc CXX := g++ AR := ar STRIP := strip

作用:定义4个工具变量:

  • CC:C语言编译器(编译.c文件用)
  • CXX:C++语言编译器(编译.cpp/.cc文件用)
  • AR:静态库打包工具(这个项目生成动态库,暂时用不上)
  • STRIP:程序瘦身工具(去掉调试信息,让生成的库体积变小)
    为什么不同平台不一样
  • PC上用系统自带的gcc/g++
  • 交叉编译ARM64时,要用专门的交叉编译器(名字很长的那个),它能在PC上编译出能在ARM板子上跑的程序

# 编译选项 CFLAGS := -g -O1 -Wall -march=x86-64 -std=gnu99 CPPFLAGS := -g -O1 -Wall -march=x86-64 -std=c++11

作用:给编译器传递参数,告诉它怎么编译代码。
每个参数的含义

参数作用
-g给生成的文件加调试信息,这样你才能用GDB调试程序
-O1代码优化级别(O0=不优化,调试用;O1=轻度优化;O3=最高优化,发布用)
-Wall显示所有编译警告(强烈建议保留,能帮你提前发现90%的低级bug)
-march=x86-64生成针对x86-64架构优化的代码
-std=gnu99用GNU扩展的C99标准编译C代码
-std=c++11用C++11标准编译C++代码

CPPFLAGS += -D DEV_TYP_FT # 自定义宏定义

语法-D 宏名—— 在代码中定义一个宏。
作用:你可以在代码里写#ifdef DEV_TYP_FT,然后写只有在这个平台下才会执行的代码。
什么时候改:当你需要给某个平台加专属宏定义时。


# Boost库路径(x86_64默认系统路径) BOOST_ROOT := /usr/local INCLUDES += $(BOOST_ROOT)/include LIBPATH += $(BOOST_ROOT)/lib

作用:定义第三方库Boost的根目录,然后把它的头文件和库文件路径加到搜索路径里。
什么时候改:当你的Boost库安装在别的目录时,修改BOOST_ROOT的值。


# 链接库(静态链接boost_system,动态链接系统库) LIBS := -Wl,-Bstatic -lboost_system -Wl,-Bdynamic -lpthread -lm -ldl -lrt

作用:告诉链接器,要链接哪些库文件。
语法解释

  • -lxxx:链接名为libxxx.so(动态库)或libxxx.a(静态库)的库
  • -Wl,-Bstatic:告诉链接器,后面的库用静态链接(把库的代码直接打包到你的程序里)
  • -Wl,-Bdynamic:告诉链接器,后面的库用动态链接(程序运行时才加载库文件)
    为什么这么写:静态链接Boost库,这样你的程序在板子上运行时不需要再安装Boost库;动态链接系统库(pthread、m等),因为系统库是所有程序共用的。

else ifeq ($(PLATFORM), ARM64_GENERIC) # ... 通用ARM64平台的配置 ... else ifeq ($(PLATFORM), RK3588) # ... RK3588平台的配置 ... else ifeq ($(PLATFORM), ARM64_RDBI) # ... 自定义ARM64平台的配置 ... endif

作用:这几个分支和上面的X86_64分支结构完全一样,只是编译器路径、Boost库路径和优化级别不同。
什么时候改:当你新增一个平台时,复制一个分支,修改对应的编译器和库路径即可。


第四部分:通用编译选项(所有平台共用,绝对不要改)

# ============================================== # 通用编译选项(所有平台共用,一般无需修改) # ============================================== # 添加头文件路径前缀 -I CPPFLAGS += $(addprefix -I,$(INCLUDES))

语法$(addprefix 前缀, 列表)—— Makefile的内置函数,给列表里的每个元素都加上指定的前缀。
作用:把INCLUDES里的每个路径前面都加上-I,变成编译器能识别的头文件路径格式。
比如INCLUDES = . ../include会变成-I. -I../include


# 自动生成依赖文件(.d) CPPFLAGS += -MMD

作用:告诉编译器,编译每个源文件时,自动生成一个对应的.d依赖文件。
为什么需要这个:依赖文件里记录了这个源文件包含了哪些头文件。当你修改了某个头文件时,Makefile会自动重新编译所有包含这个头文件的源文件,不用你手动全量编译。


# 生成位置无关代码(动态库必须) CPPFLAGS += -fPIC

作用:生成位置无关代码(Position Independent Code)
为什么动态库必须要有这个:动态库是在程序运行时才加载到内存的,加载的地址是不固定的。位置无关代码可以在任何内存地址运行,不需要修改。如果不加这个选项,生成的动态库会报错。


# 隐藏内部符号,减小库体积 CPPFLAGS += -fvisibility=hidden

作用:把库内部的函数和变量都隐藏起来,只暴露你明确指定要导出的接口。
好处:减小库的体积,提高运行速度,防止别人调用你不想暴露的内部函数。


# 链接选项:生成动态库 LDFLAGS := -shared

作用:告诉链接器,我们要生成的是动态库,而不是可执行程序。


第五部分:自动查找源文件和生成目标文件(绝对不要改)

# ============================================== # 自动查找源文件和生成目标文件(无需修改) # ============================================== # 查找所有.c/.cpp/.cc源文件 CSRCS := $(foreach dir,$(SRC_DIRS),$(wildcard $(dir)/*.c)) CPPSRCS := $(foreach dir,$(SRC_DIRS),$(wildcard $(dir)/*.cpp)) CCSRCS := $(foreach dir,$(SRC_DIRS),$(wildcard $(dir)/*.cc))

语法

  • $(foreach 变量, 列表, 执行语句):遍历列表里的每个元素,执行语句
  • $(wildcard 模式):匹配所有符合模式的文件
    作用:自动遍历SRC_DIRS里的所有目录,找到所有后缀为.c.cpp.cc的源文件,分别存到CSRCSCPPSRCSCCSRCS变量里。
    好处:你不用手动一个个把源文件名加到Makefile里,新增或删除源文件时,Makefile会自动识别。

# 生成对应的目标文件(.o) COBJS := $(patsubst %.c,%.o,$(CSRCS)) CPPOBJS := $(patsubst %.cpp,%.o,$(CPPSRCS)) CCOBJS := $(patsubst %.cc,%.o,$(CCSRCS))

语法$(patsubst 旧模式, 新模式, 列表):模式替换函数,把列表里所有符合旧模式的元素替换成新模式。
作用:把每个源文件的后缀改成.o,得到对应的目标文件列表。
比如main.c会变成main.outils.cpp会变成utils.o
💡 小知识:编译过程是先把每个源文件编译成.o目标文件,再把所有.o文件链接成最终的动态库或可执行程序。


# 生成对应的依赖文件(.d) CDEPS := $(patsubst %.o,%.d,$(COBJS)) CPPDEPS := $(patsubst %.o,%.d,$(CPPOBJS)) CCDEPS := $(patsubst %.o,%.d,$(CCOBJS))

作用:把每个目标文件的后缀改成.d,得到对应的依赖文件列表。


第六部分:伪目标定义(绝对不要改)

# ============================================== # 伪目标定义(无需修改) # ============================================== .PHONY: all deps objs clean rebuild

语法.PHONY: 目标名列表—— 声明这些目标是伪目标
什么是伪目标:这些目标不是真正的文件,只是一个命令的名字。比如clean不是一个叫clean的文件,只是执行删除命令的名字。
为什么要声明:如果你的目录里刚好有一个叫clean的文件,Makefile会以为clean目标已经是最新的,不会执行删除命令。声明为伪目标后,Makefile就不会检查文件是否存在,每次都会执行对应的命令。


# 默认目标:生成动态库 all: $(TARGET)

作用:定义默认目标。当你只敲make不加任何参数时,Makefile会自动执行这个目标。
依赖关系all目标依赖于$(TARGET)(也就是libipapply.so),所以执行make会先生成libipapply.so


# 生成依赖文件 deps: $(CDEPS) $(CPPDEPS) $(CCDEPS) $(CXX) $(CPPFLAGS) -MM $(CSRCS) $(CPPSRCS) $(CCSRCS)

作用:只生成所有依赖文件,不编译代码。一般用不上。


# 生成目标文件 objs: $(COBJS) $(CPPOBJS) $(CCOBJS)

作用:只把所有源文件编译成.o目标文件,不链接成最终的动态库。一般用不上。


# 清理中间文件和目标文件 clean: @$(RM) $(COBJS) $(CPPOBJS) $(CCOBJS) @$(RM) $(CDEPS) $(CPPDEPS) $(CCDEPS) @$(RM) $(TARGET)

语法:命令前面的@表示不显示命令本身,只显示命令的输出。
作用:删除所有编译生成的中间文件(.o.d)和最终的动态库文件。
什么时候用

  • 编译报错,想重新编译整个项目时
  • 切换平台编译前(比如从X86切换到ARM64)

# 重新编译 rebuild: clean all

作用:先执行clean清理所有文件,再执行all重新编译整个项目。
什么时候用:解决一些奇怪的编译问题(比如修改了头文件但Makefile没有重新编译)。


第七部分:最终链接规则(绝对不要改)

# ============================================== # 最终链接规则(无需修改) # ============================================== # 包含自动生成的依赖文件 -include $(CDEPS) $(CPPDEPS) $(CCDEPS)

语法-include 文件名列表—— 包含指定的文件,如果文件不存在也不报错。
作用:把前面自动生成的.d依赖文件包含进来,这样Makefile就能知道每个源文件依赖哪些头文件了。


# 链接生成动态库并strip去掉调试信息 $(TARGET): $(COBJS) $(CPPOBJS) $(CCOBJS) $(CXX) $(CPPFLAGS) $(LDFLAGS) -o $@ $^ $(addprefix -L,$(LIBPATH)) $(LIBS) $(STRIP) $@ # 编译完成后自动清理中间文件(可选,注释掉保留.o和.d文件加速增量编译) @$(RM) $(COBJS) $(CPPOBJS) $(CCOBJS) @$(RM) $(CDEPS) $(CPPDEPS) $(CCDEPS)

这是整个Makefile最核心的规则,我们逐行拆解:

  1. 依赖关系$(TARGET): $(COBJS) $(CPPOBJS) $(CCOBJS)
    👉 生成最终的动态库libipapply.so,需要所有的.o目标文件。

  2. 链接命令$(CXX) $(CPPFLAGS) $(LDFLAGS) -o $@ $^ $(addprefix -L,$(LIBPATH)) $(LIBS)
    自动变量

    • $@:代表目标文件,也就是libipapply.so
    • $^:代表所有依赖文件,也就是所有的.o目标文件
      作用:用C++编译器把所有.o文件链接成最终的动态库,同时链接指定的库文件。
  3. 瘦身命令$(STRIP) $@
    👉 用strip工具去掉动态库里的调试信息,让库的体积变小(一般能减小70%以上)。

  4. 清理命令:最后两行是编译完成后自动删除中间文件。
    建议:把这两行注释掉!这样下次编译时,只有修改过的源文件会被重新编译,编译速度会快很多。


最后:整个编译流程总结

当你敲make命令时,Makefile会按以下步骤执行:

  1. 读取你选择的平台,加载对应的编译器和配置
  2. 自动查找所有源文件,生成对应的目标文件和依赖文件列表
  3. 包含所有依赖文件,建立完整的依赖关系
  4. 逐个编译每个源文件,生成对应的.o目标文件
  5. 把所有.o文件链接成最终的libipapply.so动态库
  6. strip给动态库瘦身
  7. (可选)删除中间文件

小白必背3个命令

  1. make:编译生成动态库
  2. make clean:清理所有编译产物
  3. make rebuild:重新编译整个项目
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/16 19:06:13

多智能体 执行-辩论-校验 闭环架构

文章目录一、核心本质二、三大智能体权责划分1. 执行Agent(A/B/C)2. 辩论Agent3. 校验Agent(终审裁判)三、闭环流转逻辑四、关键设计规则五、TaskList 任务标准字段一、核心本质 整体架构本质:执行 → 辩论 → 校验 三…

作者头像 李华
网站建设 2026/5/16 19:05:43

2026届最火的十大降重复率网站横评

Ai论文网站排名(开题报告、文献综述、降aigc率、降重综合对比) TOP1. 千笔AI TOP2. aipasspaper TOP3. 清北论文 TOP4. 豆包 TOP5. kimi TOP6. deepseek 当下,学术创作效率需求持续提升,智能一键论文生成类工具,…

作者头像 李华
网站建设 2026/5/16 18:59:12

Debian系统部署Qt 5.15.2完整指南:从下载到解决依赖库报错

1. 为什么选择Qt 5.15.2? Qt作为跨平台的C图形用户界面应用程序开发框架,在工业控制、嵌入式设备、汽车仪表盘等领域有着广泛应用。5.15.2版本是长期支持版本(LTS)的一个重要更新,修复了大量bug并优化了性能。对于刚从…

作者头像 李华
网站建设 2026/5/16 18:58:24

Kubernetes资源管理:高效利用集群资源

Kubernetes资源管理:高效利用集群资源 一、Kubernetes资源管理概述 1.1 资源管理的定义 Kubernetes资源管理是指对集群中的CPU、内存、存储等资源进行合理分配和优化利用的过程。它通过资源请求、限制和调度策略,确保Pod获得所需资源,同时防止…

作者头像 李华