在有Makefile文件的目录下直接运行即可。make -j16代表使用16个核心去编译,根据自己机器的配置选择即可。有的时候编译的时候会出现类似“文件太大”的错误,看着不像是代码的问题,其实就是编译的临时文件太大了,编译选项里面加上-Wa,-mbig-obj即可。
缩进必须使用Tab,不能使用空格 命令前加@表示执行该命令时不显示 $(CC):读取变量的值 CC=gcc:设置变量的值 目标<冒号>依赖<换行TAB>命令:描述规则的基本结构 目标(target):规则的目标,可以是中间文件、标签或者可执行文件 依赖(prerequisites):要生成Targets需要的文件 命令(recipe):任意的Shell命令,用于生成目标文件 $(<function> <arguments>):函数调用格式 ifeq、ifneq、else、endif:条件判断 $@、$%、$<、$?:部分预定义的宏、自动化变量 *、?、[]、%:四种通配符
makefile的基本语法是这样的:
目标: 依赖
然后make通过时间戳判断是否需要执行命令,“时间戳机制允许 make 检查目标文件是否比其依赖文件(通常是源代码或其他目标文件)更旧。这是 make 如何决定是否需要重新构建目标文件的基础逻辑”
如果没有写[依赖]的话?
(待验证)这组命令就一定会执行
make可以自己手动指定h文件的依赖关系(每个o文件的编译依赖于那些h文件),确保h文件有修改的时候重新编译某些文件,但是这样很繁琐(include的关系可能很复杂);另一种方式是每个o文件的编译都依赖于所有h文件,这样的坏处是h文件有任何一处变动,整个项目都要重新编译;还是一种方式是使用编译器生成的依赖关系(参数是:-MMD -MP,编译器会生成d文件表示依赖关系;引入依赖关系到Makefile:参照下方代码;这里暂且记录到此,后面再细看)
DEPS = $(OBJS:.o=.d) -include $(DEPS)
make 后面不跟路径,并没有 make .. 这种写法,make命令就是读取当前目录的Makefile然后执行
cmake 后面可以跟路径,比如 cmake ..
伪目标:phony targets
伪目标像这样声明:.PHONY: all clean。这样声明之后,all 和 clean 就不会被识别为路径或者文件名之类的(即使同名的文件或者路径存在),这里有点乱,待求证
伪目标对于嵌套调用还挺重要的,看下面的内容就理解了
怎么嵌套的?
每一个子文件夹(或者部分的子文件夹)下都有一个Makefile,负责一部分的构建任务。我们从源码的根目录执行make,然后根据Makefile中描述的依赖关系,子文件夹下的make也会被自动执行
变量可以传递吗?
每个 Makefile 默认都是上下文无关的(也就是没有变量传递),不管它们的调用关系是怎样的。但是如果需要我们也可以实现变量传递,比如使用export导出环境变量,那么后面执行的make是可以读取到这个环境变量的;或者将公用的部分写入一个公用文件然后需要的时候都include进来
那么如何调用子文件夹的Make呢?
简单来说就一句命令:
$(MAKE) -C sub_dir_name
还有一个问题是什么时候执行上面这句命令呢?我个人经验认为可以分为3类,以下举例说明:
比如当前我们在/,/src下面有一堆源文件都需要编译,这些编译的工作交给/src/Makefile,我们/Makefile只负责链接,比如我们想链接得到一个xxx.lib
# 代码的意思是,我要构建xxx.lib,虽外需要src内部编译好之后,我们才能开始构建xxx.lib # 但是我们不管这个依赖关系,我构建xxx.lib的时候,不管src内部是不是构建好了,反正我都手动执行一下src的make xxx.lib: $(MAKE) -C src <link command>
# 正确的实现 # 代码的意思是:我们需要构建xxx.lib,但是构建之前需要先完成【build_src】这个任务 xxx.lib: build_src <link command> # 代码的意思是:为了完成【build_src】这个任务,我们执行下面的命令就可以了 build_src: $(MAKE) -C src # 错误的实现 # 代码的意思是:xxx.lib的构建依赖于【src这个文件夹】(因为src这个文件夹是存在的) # 而src早就创建了,时间戳是很旧的,所以这条构建命令不会触发 xxx.lib: src <link command> # 同理,这条构建命令也不会触发 src: $(MAKE) -C src
.PHONY: src xxx.lib: src <link command> src: $(MAKE) -C src
关于(3),AI:
“伪目标(phony targets),它们不与实际的文件关联,每次运行make时总是被执行。”
“.PHONY: 声明all 和 clean为伪目标,确保即使存在同名文件夹或文件时,这些目标也会被执行。”
Makefile
all: Project CC = g++ Target = Engine InclDir = -I Support/Include LinkDir = Support/Lib CompFlag = -Wall -O2 -c --std=c++17 $(InclDir) LinkFlag = -l pthread Objects = Main.o Objects += SupportA.o Objects += SupportB.o %.o: Project/%.cpp; -@ $(CC) -o $@ $< $(CompFlag) Project: $(Objects); -@ $(CC) $(LinkFlag) $(Objects) -o $(Target) Auto = @set -e; rm -f $@; Auto += $(CC) -MM $(InclDir) $< > $@.$$$$; Auto += sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; Auto += rm -f $@.$$$$ %.d: Project/%.cpp; $(Auto) -include $(Objects:.o=.d) clean: ; -@ rm $(Target) $(Objects) $(Objects:.o=.d)
如果你在windows上使用mingw-make编译动态库,那么生成的是a文件和dll文件,哈哈哈混搭风格 因为编译按照mingw-make来,所以需要a文件,但是运行的时候就和编译器无关了, 需要的是dll,总不能让windows找so文件吧~ linux上的动态库没有a文件吗?只有so文件? MinGW支持链接lib文件,像这样:-lOpenImageDenoise,OpenImageDenoise.lib,太强了