参考
makefile入门
首先要掌握gcc 命令、动态库静态库的编译链接过.
程;在此基础上,学习makefile的基本规则,语法,能够写一个冗余(难看)的makefile文件;最后逐渐掌握常用变量、模式匹配(公式)、函数的用法
1. gcc命令
要点:
1)编译要指定头文件,调试参数-g也是在此期间进行;
2)链接要指定库路径和库文件;
3)动态库要放在可执行文件可搜索到的路径,-L并不能指定搜索路径;
4)库的后缀不能错,linux(.a/.so)和win(.lib/.dll)后缀都不同;
5)指定库文件不用加后缀,如果要加格式为-l:libxxx.lib
#静态库制作
g++ -c src/*.cpp #生成二进制文件,如果debug记着加上 -g
ar rcs lib/libscalc.lib *.o #打包静态库,注意后缀名,linux是libcalc.a
#动态库制作
g++ -c -fpic src/*.cpp
g++ shared *.o lib/libdcalc.dll
#链接
g++ main.cpp -Iinclude -Llib -ilibscalc -o staic.exe
g++ main.cpp -Iinclude -Llib -ilibdcalc -o share.exe
g++ main.cpp -Iinclude -Llib -i:libdcalc.dll -o share.exe
2. 基本规则
目标:目标顶格写,后面是冒号(冒号后面是依赖)
依赖:依赖是用来产生目标的原材料。
命令:命令前面一定是Tab,不能是顶格,也不能说多个空格。命令就是要生成那个目标需要做的动作。
target1,target2... : depend1,depend2, ...
command
......
......
一个简单示例: 文件都在一个目录下:
# 生成可执行文件
static.exe : main.cpp libcalc.lib
g++ main.cpp -Iinclude -L. -llibcalc -o static.exe
#制作静态库
libcalc.lib : add.o div.o mult.o sub.o
ar rcs libcalc.lib add.o div.o mult.o sub.o
add.o : add.cpp
gcc add.cpp -c -Iinclude -o add.o
div.o : div.cpp
gcc div.cpp -c -Iinclude -o div.o
mult.o : mult.cpp
gcc mult.cpp -c -Iinclude -o mult.o
sub.o : sub.cpp
gcc sub.cpp -c -Iinclude -o sub.o
3. 变量
自定义变量:
obj+= a.o b.o c.o
src := a.cpp b.cpp c.cpp
target ?= app.exe
include = ./include
四个赋值符号依次为,追加,赋值(常规意义上的 =),如果被赋值过则忽略本次赋值,类似宏替换(影响全局)
预定义变量(静态库和g++编译选项):AR
;ARFLAGS
;CPPFLAGS
;
主要是掌握几个常用的自动变量(用来代替规则中的目标文件和依赖文件,只能在规则的命令中使用)
$* | 目标文件名称,不含扩展名 | |
---|---|---|
$@ | 目标文件名称,含扩展名 | |
$< | 依赖文件,依赖项中第一个 | |
$+ | 依赖文件,所有,包含重复,按出现的先后顺序 | |
$^ | 依赖文件,所有,不重复 | |
$? | 依赖文件,比目标文件时间戳晚,文件之间以空格分开 |
4. 模式匹配
用于根据文件名或路径的模式自动匹配文件并执行相应的操作。模式匹配使用特殊的语法来指定模式,并可以匹配文件名、路径和其他规则中的模式。
以下是一些常见的模式匹配语法:
简单的模式匹配:使用通配符来匹配文件名。例如,.c表示匹配所有以.c为后缀的源代码文件。
复杂模式匹配:使用更复杂的正则表达式来匹配文件名或路径。例如,[A-Z].c表示匹配所有以大写字母开头并以.c为后缀的源代码文件。
变量替换:使用波浪线(~)来表示变量的占位符,可以在匹配时将其替换为相应的变量值。例如,%.o: %.c表示将目标文件名的占位符%o替换为目标文件名,依赖项名的占位符%c替换为依赖项名。
,规则中使用 %.c;搜索可以使用 *.c ,变量替换使用%.c
CPPFLAGES = -c -O2 -g
%.o : %.cpp
g++ CPPFLAGES $< -o $@
5. 函数
几个基本函数要掌握: (实例看后面练习1)
将指定目录和要求的文件展开,返回值包含路径和文件名:$(wildcard *.cpp)
;
替换文件名()$(patsubst %.cpp,%.o,$(wildcard *.cpp))
; 可以用于条件替换:$(patsubst lib/libcalc.lib,lib/Specialities/libcalc.lib,lib/libcalc.lib)
还有一种文件名替换方式:$(SRC:%.cpp=%.o)
;
去掉路径dir=$(notdir ./test/src/a,cpp)
添加路径$(addprefix $(ROOT)/src/,$(OBJ))
6.其他
6.1 使用shell命令
makefile 可以执行 shell命令 比如常用的
rm -f
echo${CURDIR}
等,要使用.PHONY
声明伪目标;
ROOT:=$(shell pwd)
记录makefile所在路径 ;但如果是在windows的mingw环境下,使用该命令得到的路径无法使用。
rm -f $(shell find -name "*.o")
rm -f obj/*.o)
这里一定要加上-f,避免因为前面命令没有正确执行导致后面命令无法执行。
6.2 文件名
如果名字不是
makefile/Makefile
;则需要-f
参数 指定文件名:make -f Makefile_test
6.3 时间戳
make通过时间戳(文件生成的时间)来判断命令是否需要重新执行;
6.4 伪目标
clean:
rm -f $(shell find -name "*.o")
rm -f $(TARGET)
此时clean 没有依赖,时间戳检测不过,因此不会执行该命令,需要在shell中执行:
make clean
如果想要自动执行,需要将其声明为伪目标.PHONY:clean
;但是如果文件中存在其他规则,也不会自动执行;没搞明白
CFLAGS +=-I$(CURDIR)/include
CFLAGS += -Wall -Werror -O2
CFLAGS += -g
CFLAGS += -c -o
## 依赖项
LDFLAGS += -L$(CURDIR)/lib
LDFLAGS += -llibcalc
SRC = $(wildcard ./*.cpp ./src/*.cpp)
OBJ := $(SRC:%.cpp=%.o)
TARGET := bin/app
$(TARGET) : $(OBJ)
g++ $^ -o $@
%.o:%.cpp
#g++ $< -c -Iinclude -o $@
g++ $(CFLAGS) $@ $<
.PHONY:showpath
showpath:
@echo "CURDIR:"$(CURDIR)
@echo "CFLAGS:"$(CFLAGS)
@echo "LDFLAGS:"$(LDFLAGS)
@echo "SRC:"$(SRC)
@echo "OBJ:"$(OBJ)
#.PHONY:clean
clean:
rm -f src/*.o
rm -f bin/*.exe
7.关于路径问题
.
表示makefile文件当前路径,./../
表示上一级目录,$(CURDIR)
可以用来表示当前目录的路径;
总结
- 格式:
- 赋值:
- 自动变量
- 函数
- 模式匹配
练习/测试
tree D:\C++\CPP_EXERCISES\TEST_GCC /f
D:\C++\CPP_EXERCISES\TEST_GCC
| main.cpp
| makefile
|
+---bin #放可执行文件
+---build
+---include #头文件
| head.h
|
+---lib #库文件
\---src
add .cpp
div.cpp
mult.cpp
sub.cpp
测试1 :函数测试
### 测试,函数返回结果、变量值、生成文件的路径问题
ROOT := ${shell pwd}
## 编译项
CFLAGS +=-I$(ROOT)/include
CFLAGS += -Wall -Werror -O2
CFLAGS += -g
CFLAGS += -c -o
## 依赖项
LDFLAGS += -L$(ROOT)/lib
LDFLAGS += -llibcalc
# wildcard patsubst addprefix 等函数使用
SRC = $(wildcard ./src/*.cpp)
SRCS = $(notdir $(wildcard src/*.cpp ))
OBJ = $(patsubst %.cpp, %.o, $(SRCS))
OBJS := $(SRC:%.cpp=%.o)
OBJSS := $(addprefix $(ROOT)/src/,$(OBJ))
.PHONY:showpath
showpath:
@echo "CURDIR:"$(CURDIR)
@echo "ROOT:"$(ROOT) #windows 系统不能用这种路径,可以用CURDIR
@echo "CFLAGS:"$(CFLAGS)
@echo "LDFLAGS:"$(LDFLAGS)
@echo "SRC:"$(SRC)
@echo "SRCS:"$(SRCS)
@echo "OBJ:"$(OBJ)
@echo "OBJS:"$(OBJS)
@echo "OBJSS:"$(OBJSS)
#输出结果
$ MAKE
CURDIR:D:/c++/cpp_exercises/test_gcc
ROOT:/d/c++/cpp_exercises/test_gcc
CFLAGS:-I/d/c++/cpp_exercises/test_gcc/include -Wall -Werror -O2 -g -c -o
LDFLAGS:-L/d/c++/cpp_exercises/test_gcc/lib -llibcalc
SRC:./src/add.cpp ./src/mult.cpp ./src/sub.cpp ./src/div.cpp
SRCS:add.cpp mult.cpp sub.cpp div.cpp
OBJ: add.o mult.o sub.o div.o
OBJS:./src/add.o ./src/mult.o ./src/sub.o ./src/div.o
测试2: 编译、链接两步走
CFLAGS +=-I$(CURDIR)/include
CFLAGS += -Wall -Werror -O2
CFLAGS += -g
CFLAGS += -c -o
## 依赖项
LDFLAGS += -L$(CURDIR)/lib
LDFLAGS += -llibcalc
SRC = $(wildcard ./*.cpp ./src/*.cpp)
OBJ := $(SRC:%.cpp=%.o)
TARGET := bin/app
$(TARGET) : $(OBJ)
g++ $^ -o $@
%.o:%.cpp
#g++ $< -c -Iinclude -o $@
g++ $(CFLAGS) $@ $<
make后文件目录如下:多出来的用 ** **标记
D:\C++\CPP_EXERCISES\TEST_GCC
| main.cpp
| **main.o**
| makefile
|
+---bin
| **app.exe**
|
+---build
+---include
| head.h
|
+---lib
\---src
add.cpp
**add.o**
div.cpp
**div.o**
mult.cpp
**mult.o**
sub.cpp
**sub.o**