1.示例1
#Makefile
CXXFLAGS := -O3 -g -Wall -shared std=c++11 -pthread -fPIC
CPPCOMP := g++
CCOMP := gcc
LINK_RPATH := ......
#动态库.so的路径用-L指定,库的名称用-l指定,之间可以没有空格
LINKFLAGS := -Wl,rpath=$(LINK_RPATH)/bin -L./bin -lstaticfb -lraw_fea -lconv_addnoise -lmvad -lfftw3f -lm -lcudart -lcufft
PYINCLUDE := $(shell python -m pybind11 --includes)
#头文件的路径用-I指定,之间可以没有空格
INCLUDEPATH := $(PYINCLUDE) -I. -I./static_fb/include -I./fftw-3.2.2 -I./raw_fea -I./conv_timerand
#.a和.so共同链接成可执行二进制文件
build: pybind.o libconv_addnoise.a libfftw3f.a
$(CPPCOMP) -o _C$(shell python3-config --extension-suffix) $(CXXFLAGS) pybind.o $(LINKFLAGS)
#编译生成.o文件
pybind.o:
$(CPPCOMP) -c -std=c++11 -fPIC $(INCLUDEPATH) pybind.cpp
#编译生成.a静态库,需要进入conv_timerand文件夹并执行其中的Makefile
libconv_addnoise.a:
@cd conv_timerand && make
@cd ..
clean:
-@rm -rf *.o
-@cd conv_timerand && make clean
上面Makefile是上一级目录的,与他同级目录有个conv_timerand目录,其中也有一个Makefile,在上面提到会进入该文件夹并执行该文件夹下的Makefile。
conv_timerand目录最终会生成一个.a的静态库,静态库就是简单的打包所有的.o的文件,所以在.a文件中不能链接.so动态库。
.a和.so文件应该被共同链接成可执行二进制文件。
#conv_timerand/Makefile
CXXFLAGS := -O3 -g std=c++11 -fPIC
CPPCOMP := g++
CCOMP := gcc
CONV_OBJ := complextion.o conv_addnoise.o fft_1.o cufft.o
libconv_addnoise.a: $(CONV_OBJ)
@ar -cr ../bin/libconv_addnoise.a $(CONV_OBJ)
#将cuda文件编译成.o,注意cuda编译的版本和运行版本要保持相同
$(CONV_OBJ):
nvcc --compiler-options -fPIC -c cufft.cu -I/opt/lib/cuda-10.1/include
$(CPPCOMP) $(CXXFLAGS) -I./conv_timerand -I../fftw-3.2.2 -c *.cpp
$(CCOMP) $(CXXFLAGS) -I../fftw-3.2.2 -c *.cpp
clean:
@rm -rf *.o
@rm -rf ../bin/libconv_addnoise.a
.PHONY: clean
这个Makefile就是生成libconv_addnoise.a
2.示例2
生成.o文件之间可以没有顺序。所以SOURCE变量中当前文件夹的cpp文件和CORE文件夹的cpp文件哪一个在前都可以。
CORE = $(PWD)/core
#wildcard表示将core文件夹和当前文件夹下的所有cpp文件展开
SOURCE = $(wildcard $(CORE)/*.cpp ./*cpp)
#patsubst表示将SOURCE变量中所有.cpp结尾的文件名替换成.o结尾的文件名
OBJS = $(patsubst %.cpp, %.o, $(SOURCE))
INCLUDES = -I./core -I.
libnccf_reaper.a : $(OBJS)
@ar -cr ../bin/libnccf_reaper.a $(OBJ)
$(OBJ) : %.o : %.cpp
g++ -std=c++11 $(INCLUDES) -c -fPIC $< -o $@
#$@表示目标文件,$^表示所有依赖文件,$<表示第一个依赖文件
#上一条语句表示将所有的cpp文件编译成.o文件
.PHONY : clean
rm -rf $(OBJ)
rm ../bin/libnccf_reaper.a
3.undefined reference to错误
假如有一个main.cpp,调用了qnnorm.cpp中的qnnorm函数,qnnorm函数又调用了utils.cpp中的utils函数。调用关系如下图:
文件目录如上图左,根目录的Makefile如上图右。首先在生成main.o的文件时要注意包含头文件和qnnorm静态库文件,其次在生成main二进制文件时,要注意包含两个静态库文件(第一行),且最被依赖的文件应该放在最后(第二行),最后要注意的是生成main两个库的顺序不影响(第一行),生成main.o那一行不需要包含静态库文件,编译两个库上下顺序也不影响。
另外尤其需要注意的一个坑是make -j,如果库之间存在依赖关系,不可以使用make -j,make -j表示启动多少个线程并行编译,虽然有的文章表示不会影响依赖关系,,但是我的Makefile确实会因为这个编不过,而且make是不限制启动线程个数的意思,但是就是可以编译过,可能是和机器配置有关,这个目前还没有完全搞懂。