Kaldi的编译过程-makefile
条评论kaldi 的安装
通过git从github上将kaldi拉下来之后,kald的目录如下:1
COPYING INSTALL README.md egs misc scripts src tools window
kaldi安装需要分别编译两个目录,分别是src和tools目录。
安装步骤如下
- 进入tools目录,依次执行
- extras/check_dependencies.sh //检查依赖,根据对应的提示,安装相应的依赖
- make //make可以指定通过-j的选项,指定job数,加快编译速度
- 进入src目录,依次执行
- ./configure –shared
- make depend //以指定通过-j的选项,指定job数
- make //以指定通过-j的选项,指定job数
kaldi的编译过程
这里主要关注src目录下的编译过程,从前面的安装过程可以看到,第一步主要是参数的配置。
下面主要关注第二步make depend和第三步make。src目录的编译主要依赖于四个Makefile文件和一个生成的Makefile文件,四个Makefile文件分别是:
- src/Makefile
- src/kaldi.mk
- src/${subdir}/Makefile 这里的subdir指的是src下的子目录。
- src/makefiles/default_rules.mk
src的目录如下:
第一个Makefile是执行make命令所使用的Makefile,是整个kaldi程序编译的入口;第二个Makefile则是公共的Makefile,被其他Makefile通过include包含,主要是设置一些编译选项;第三个Makefile则是src子目录下,负责子目录的编译;第四个Makefile位于src/makefiles下,是make的默认规则,是真正执行编译的Makefile。这四个Makefile的关系是,src/Makefile 和src/${subdir}/Makefile都包含src/kaldi.mk, make命令使用src/Makefile, src/Makefile则分别对src的子目录分别执行make,所以src/Makefile调用src/${subdir}/Makefile,src/${subdir}/Makefile则包含src/makefiles/default_rules.mk, 第四个Makefile会生成kaldi的库和可执行文件。
从以上的分析可以知道
- 在src目录下执行make, 会编译src下大部分目录(有些目录不会编译)。
- 在各个子目录下分别执行make, 则只会编译当前子目录下的文件。
前面提到src编译还会生成一个Makefile文件,这个Makefile文件为.depend.mk,这个Makefile文件主要是生成各个.o文件的依赖。 每个需要编译的子目录都会生成这样一个Makefile文件。
下面解释安装过程中make depend, make这两个命令的作用,make depend执行src/下的Makefile中的depend的目录,其效果就是生成各个需要编译的目录下的.depend.mk文件,之后执行make命令,开始真正编译src目录。
下面对这五个Makefile分别解读。
(1)src/Makefile1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122# This is the top-level Makefile for Kaldi.
# Also see kaldi.mk which supplies options and some rules
# used by the Makefiles in the subdirectories.
#Shell
SHELL := /bin/bash
#src下的子目录,将被编译
SUBDIRS = base matrix util feat tree gmm transform \
fstext hmm lm decoder lat kws cudamatrix nnet \
bin fstbin gmmbin fgmmbin featbin \
nnetbin latbin sgmm2 sgmm2bin nnet2 nnet3 rnnlm chain nnet3bin nnet2bin kwsbin \
ivector ivectorbin online2 online2bin lmbin chainbin rnnlmbin \
cudadecoder cudadecoderbin
MEMTESTDIRS = base matrix util feat tree gmm transform \
fstext hmm lm decoder lat nnet kws chain \
bin fstbin gmmbin fgmmbin featbin \
nnetbin latbin sgmm2 nnet2 nnet3 rnnlm nnet2bin nnet3bin sgmm2bin kwsbin \
ivector ivectorbin online2 online2bin lmbin
CUDAMEMTESTDIR = cudamatrix
#过滤掉*bin目录
SUBDIRS_LIB = $(filter-out %bin, $(SUBDIRS))
#如果KALDI_SONAME没有赋值,则赋值
KALDI_SONAME ?= libkaldi.so
# Optional subdirectories
EXT_SUBDIRS = online onlinebin # python-kaldi-decoding
#EXT_SUBDIRS_LIB = online
EXT_SUBDIRS_LIB = $(filter-out %bin, $(EXT_SUBDIRS))
#生成gcc的配置,以及一些依赖的库的路径
include kaldi.mk
# 设置Makefile的目标
# Reset the default goal, so that the all target will become default
.DEFAULT_GOAL :=
all: $(SUBDIRS) matrix/test
-echo Done
#检查KALDLIBDIR是否存在
mklibdir:
test -d $(KALDILIBDIR) || mkdir $(KALDILIBDIR)
#I don't want to call rm -rf
rmlibdir:
ifneq ($(KALDILIBDIR), )
-rm $(KALDILIBDIR)/*{.so,.a,.o}
-rmdir $(KALDILIBDIR)
else
@true
endif
#检查版本是否一致
.PHONY: checkversion
checkversion:
ifeq ($(shell ./configure --version),$(CONFIGURE_VERSION))
@echo "The version of configure script matches kaldi.mk version. Good."
else
@echo ""
@echo "The kaldi.mk file was generated using a different version of configure script. Please rerun the configure again"
@test -f ./kaldi.mk && echo "Hint: Previous configure command line: " && head -n 2 ./kaldi.mk | grep configure | sed 's/^# *//g'
@echo ""
@false
endif
...
#检查kaldi.mk文件是否存在
kaldi.mk:
@[ -f kaldi.mk ] || { echo "kaldi.mk does not exist; you have to run ./configure"; exit 1; }
# Compile optional stuff
ext: ext_depend $(SUBDIRS) $(EXT_SUBDIRS)
-echo Done
check_portaudio:
@[ -d ../tools/portaudio ] || ( cd ../tools; ./install_portaudio.sh )
#清理
clean: rmlibdir
-for x in $(SUBDIRS) $(EXT_SUBDIRS); do $(MAKE) -C $$x clean; done
distclean: clean
-for x in $(SUBDIRS) $(EXT_SUBDIRS); do $(MAKE) -C $$x distclean; done
#添加后缀 /test, 执行测试
test: $(addsuffix /test, $(SUBDIRS_LIB))
ext_test: $(addsuffix /test, $(EXT_SUBDIRS_LIB))
# Define an implicit rule, expands to e.g.:
# base/test: base
# $(MAKE) -C base test
%/test: % mklibdir
$(MAKE) -C $< test
cudavalgrind:
-for x in $(CUDAMEMTESTDIR); do $(MAKE) -C $$x valgrind || { echo "valgrind on $$x failed"; exit 1; }; done
valgrind:
-for x in $(MEMTESTDIRS); do $(MAKE) -C $$x valgrind || { echo "valgrind on $$x failed"; exit 1; }; done
base/.depend.mk:
$(MAKE) depend
depend: $(addsuffix /depend, $(SUBDIRS))
#$(MAKE)就相当于make,-C 选项的作用是指将当前工作目录转移到你所指定的位置。
%/depend:
$(MAKE) -C $(dir $@) depend
ext_depend: check_portaudio
-for x in $(EXT_SUBDIRS); do $(MAKE) -C $$x depend; done
#这个伪目标会在子目录分别执行make
.PHONY: $(SUBDIRS)
$(SUBDIRS) : checkversion kaldi.mk mklibdir
$(MAKE) -C $@
.PHONY: $(EXT_SUBDIRS)
$(EXT_SUBDIRS) : checkversion kaldi.mk mklibdir ext_depend
$(MAKE) -C $@
...
这个常用的五个目标如下:
- make depend: 重新生成依赖项。
- make all:(跟直接执行make的结果一样),编译所有需要编译的目录,包括测试代码。
- make test:生成测试程序,执行测试代码
- make clean:清理所有编译生成的文件,包括可执行文件,.o文件,.a文件等
- make valgrind:将在valgrind下运行测试程序,以检查内存泄漏。
- make cudavalgrind: 运行测试程序(在cudamatrix中),检查支持NVIDIA CUDA和CUDA安装的GPU卡机器的内存泄漏情况。
从上述Makefile文件中可以看到make depend执行的动作,分别在SUBDIRS下执行make depend目录, 通过.DEFAULT_GOAL 重新定义此Makefile的目标,执行make后分别在SUBDIRS下执行make命令。
(2)src/kaldi.mk
这个Makefile是一个公共的Makefile文件,被其他Makefile文件包含,其中主要是设置编译器的选项(比如g++选项,链接器的选项,cuda的编译选项)和一些依赖的库(比如openfst, mkl)的路径。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137# This file was generated using the following command:
# ./configure --shared
CONFIGURE_VERSION := 10
# Toolchain configuration
CXX = g++
AR = ar
AS = as
RANLIB = ranlib
# Base configuration
#编译类型
KALDI_FLAVOR := dynamic
#lib 目录,在这个目录建立软链接
KALDILIBDIR := /home/cca01/luoxiaojie/luoxj/kaldi/src/lib
DOUBLE_PRECISION = 0
#openfst include目录
OPENFSTINC = /home/cca01/hdd190404/luoxj/kaldi/tools/openfst-1.6.7/include
#openfst 静态链接库
OPENFSTLIBS = /home/cca01/hdd190404/luoxj/kaldi/tools/openfst-1.6.7/lib/libfst.so
#为程序添加一个运行时库文件搜索路径的命令,在使用gcc编译链接时添加即可。
OPENFSTLDFLAGS = -Wl,-rpath=/home/cca01/hdd190404/luoxj/kaldi/tools/openfst-1.6.7/lib
CUBROOT = /home/cca01/hdd190404/luoxj/kaldi/tools/cub-1.8.0
#mkl 根目录
MKLROOT = /opt/intel/mkl
#mkl lib目录
MKLLIB = /opt/intel/mkl/lib/intel64
# MKL specific Linux configuration
# We have tested Kaldi with MKL version 10.2 on Linux/GCC and Intel(R) 64
# architecture (also referred to as x86_64) with LP64 interface layer.
#检查DOUBLE_PRECISIO, OPENFSTINC, OPENFSTLIBS, MKLROOT变量是否已经定义
ifndef DOUBLE_PRECISIO
$(error DOUBLE_PRECISION not defined.)
endif
ifndef OPENFSTINC
$(error OPENFSTINC not defined.)
endif
ifndef OPENFSTLIBS
$(error OPENFSTLIBS not defined.)
endif
ifndef MKLROOT
$(error MKLROOT not defined.)
endif
#MKLIB 如果没有赋值,则进行赋值
MKLLIB ?= $(MKLROOT)/lib/intel64
#CXXFLAGS: C++ 编译器的选项。
#-std:指明C++版本
#-isystem: 指定一个系统目录
#-Wall:提示所有的warning
#-Wno-sign-compare 关闭当有符号转换为无符号时,有符号和无符号值比较产生的错误警告。
#-Wno-unused-local-typedefs: 忽略本地未使用的类型定义警告。
#-Wno-deprecated-declarations: 关闭使用废弃API的警告
#-Winit-self:关闭自己初始化自己的警告。
#-msse, -msse2 让编译器使用cpu的sse, see2指令集
#-pthread: 多线程
#-g:生成操作系统调试信息
#-O0: 禁止编译器进行优化
CXXFLAGS = -std=c++11 -I.. -isystem $(OPENFSTINC) -O1 $(EXTRA_CXXFLAGS) \
-Wall -Wno-sign-compare -Wno-unused-local-typedefs \
-Wno-deprecated-declarations -Winit-self \
-DKALDI_DOUBLEPRECISION=$(DOUBLE_PRECISION) \
-DHAVE_EXECINFO_H=1 -DHAVE_CXXABI_H -DHAVE_MKL -I$(MKLROOT)/include \
-m64 -msse -msse2 -pthread \
-g # -O0 -DKALDI_PARANOID
#如果是动态编译,加上-fPIC
ifeq ($(KALDI_FLAVOR), dynamic)
CXXFLAGS += -fPIC
endif
# Compiler specific flags 标准错误重定向到标准输出
COMPILER = $(shell $(CXX) -v 2>&1)
ifeq ($(findstring clang,$(COMPILER)),clang)
# Suppress annoying clang warnings that are perfectly valid per spec.
CXXFLAGS += -Wno-mismatched-tags
endif
## Use the following for STATIC LINKING of the SEQUENTIAL version of MKL
MKL_STA_SEQ = $(MKLLIB)/libmkl_solver_lp64_sequential.a -Wl,--start-group \
$(MKLLIB)/libmkl_intel_lp64.a $(MKLLIB)/libmkl_sequential.a \
$(MKLLIB)/libmkl_core.a -Wl,--end-group -lpthread
## Use the following for STATIC LINKING of the MULTI-THREADED version of MKL
MKL_STA_MUL = $(MKLLIB)/libmkl_solver_lp64.a -Wl,--start-group \
$(MKLLIB)/libmkl_intel_lp64.a $(MKLLIB)/libmkl_intel_thread.a \
$(MKLLIB)/libmkl_core.a -Wl,--end-group $(MKLLIB)/libiomp5.a -lpthread
## Use the following for DYNAMIC LINKING of the SEQUENTIAL version of MKL
MKL_DYN_SEQ = -L$(MKLLIB) -lmkl_solver_lp64_sequential -Wl,--start-group \
-lmkl_intel_lp64 -lmkl_sequential -lmkl_core -Wl,--end-group -lpthread
## Use the following for DYNAMIC LINKING of the MULTI-THREADED version of MKL
MKL_DYN_MUL = -L$(MKLLIB) -lmkl_solver_lp64 -Wl,--start-group -lmkl_intel_lp64 \
-lmkl_intel_thread -lmkl_core -Wl,--end-group -liomp5 -lpthread
# MKLFLAGS = $(MKL_DYN_MUL)
#LDFLAGS:链接器的选项,也可以在里面指定库文件的位置
LDFLAGS = $(EXTRA_LDFLAGS) $(OPENFSTLDFLAGS) -rdynamic
LDLIBS = $(EXTRA_LDLIBS) $(OPENFSTLIBS) $(MKLFLAGS) -lm -lpthread -ldl
MKLFLAGS = -L/opt/intel/mkl/lib/intel64 -Wl,-rpath=/opt/intel/mkl/lib/intel64 -lmkl_intel_lp64 -lmkl_core -lmkl_sequential -ldl -lpthread -lm
# CUDA configuration
CUDA = true
#cuda 目录
CUDATKDIR = /usr/local/cuda
CUDA_ARCH = -gencode arch=compute_30,code=sm_30 -gencode arch=compute_35,code=sm_35 -gencode arch=compute_50,code=sm_50 -gencode arch=compute_52,code=sm_52 -gencode arch=compute_60,code=sm_60 -gencode arch=compute_61,code=sm_61
#检查DOUBLE_PRECISION,CUDATKDIR
ifndef DOUBLE_PRECISION
$(error DOUBLE_PRECISION not defined.)
endif
ifndef CUDATKDIR
$(error CUDATKDIR not defined.)
endif
#CXXFLAG
CXXFLAGS += -DHAVE_CUDA -I$(CUDATKDIR)/include -fPIC -pthread -isystem $(OPENFSTINC)
CUDA_INCLUDE= -I$(CUDATKDIR)/include -I$(CUBROOT)
CUDA_FLAGS = --machine 64 -DHAVE_CUDA \
-ccbin $(CXX) -DKALDI_DOUBLEPRECISION=$(DOUBLE_PRECISION) \
-std=c++11 -DCUDA_API_PER_THREAD_DEFAULT_STREAM -lineinfo \
--verbose -Xcompiler "$(CXXFLAGS)"
CUDA_LDFLAGS += -L$(CUDATKDIR)/lib64 -Wl,-rpath,$(CUDATKDIR)/lib64
CUDA_LDLIBS += -lcublas -lcusparse -lcudart -lcurand -lnvToolsExt #LDLIBS : The .so libs are loaded later than static libs in implicit rule
# Environment configuration
(3)src/${subdir}/Makefile
这里以featbin中的Makefile为例。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30all:
EXTRA_CXXFLAGS = -Wno-sign-compare
include ../kaldi.mk
BINFILES = add-deltas add-deltas-sdc append-post-to-feats \
append-vector-to-feats apply-cmvn apply-cmvn-sliding compare-feats \
compose-transforms compute-and-process-kaldi-pitch-feats \
compute-cmvn-stats compute-cmvn-stats-two-channel \
compute-fbank-feats compute-kaldi-pitch-feats compute-mfcc-feats \
compute-plp-feats compute-spectrogram-feats concat-feats copy-feats \
copy-feats-to-htk copy-feats-to-sphinx extend-transform-dim \
extract-feature-segments extract-segments feat-to-dim \
feat-to-len fmpe-acc-stats fmpe-apply-transform fmpe-est \
fmpe-init fmpe-sum-accs get-full-lda-mat interpolate-pitch \
modify-cmvn-stats paste-feats post-to-feats \
process-kaldi-pitch-feats process-pitch-feats \
select-feats shift-feats splice-feats subsample-feats \
subset-feats transform-feats wav-copy wav-reverberate \
wav-to-duration
OBJFILES =
TESTFILES =
ADDLIBS = ../hmm/kaldi-hmm.a ../feat/kaldi-feat.a \
../transform/kaldi-transform.a ../gmm/kaldi-gmm.a \
../tree/kaldi-tree.a ../util/kaldi-util.a ../matrix/kaldi-matrix.a \
../base/kaldi-base.a
include ../makefiles/default_rules.mk
可以看到,这个Makefile中包含src/kaldi.mk和src/makefiles/default_rules.mk这两个文件,同时定义了几个变量,主要有五个变量,根据需要选择定义其中的变量。kaldi_rules.mk将根据这些变量生成对应的文件。下面解释这些变量的的作用。
- BINFILES: 需要生成的可执行文件,对于以bin结果的子目录会定义这个变量
- OBJFILES: 需要生成的.o文件
- TESTFILES: 需要生成的测试文件
- ADDLIBS: 依赖的库文件
- LIBNAME: 生成的库的名称,如果定义了这个变量,则会根据这里LIBNAME生成的对应的动态链接库。
(4)src/makefiles/default_rules.mk
这个Makefile是真正执行编译的Makefile文件,同时也是个公共的Makefile文件。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
SHELL := /bin/bash
ifeq ($(KALDI_FLAVOR), dynamic)
ifeq ($(shell uname), Darwin)
ifdef LIBNAME
LIBFILE = lib$(LIBNAME).dylib
endif
LDFLAGS += -Wl,-rpath -Wl,$(KALDILIBDIR)
EXTRA_LDLIBS += $(foreach dep,$(ADDLIBS), $(dir $(dep))lib$(notdir $(basename $(dep))).dylib)
else ifeq ($(shell uname), Linux)
ifdef LIBNAME
LIBFILE = lib$(LIBNAME).so
endif
LDFLAGS += -Wl,-rpath=$(shell readlink -f $(KALDILIBDIR))
EXTRA_LDLIBS += $(foreach dep,$(ADDLIBS), $(dir $(dep))lib$(notdir $(basename $(dep))).so)
else # Platform not supported
$(error Dynamic libraries not supported on this platform. Run configure with --static flag.)
endif
XDEPENDS =
else
ifdef LIBNAME
LIBFILE = $(LIBNAME).a
endif
XDEPENDS = $(ADDLIBS)
endif
all: $(LIBFILE) $(BINFILES)
#如果定义了LIBNAME
ifdef LIBNAME
#ar 创建静态库.a文件
#ranlib 更新静态库的符号索引表
#这里会依赖.o文件,会根据.depend.mk中的依赖,根据Makefile的隐含规则创建对应的目标文件
$(LIBNAME).a: $(OBJFILES)
$(AR) -cr $(LIBNAME).a $(OBJFILES)
$(RANLIB) $(LIBNAME).a
ifeq ($(KALDI_FLAVOR), dynamic)
# the LIBFILE is not the same as $(LIBNAME).a
$(LIBFILE): $(LIBNAME).a
ifeq ($(shell uname), Darwin)
$(CXX) -dynamiclib -o $@ -install_name @rpath/$@ $(LDFLAGS) $(OBJFILES) $(LDLIBS)
ln -sf $(shell pwd)/$@ $(KALDILIBDIR)/$@
else ifeq ($(shell uname), Linux)
# Building shared library from static (static was compiled with -fPIC)
$(CXX) -shared -o $@ -Wl,--no-undefined -Wl,--as-needed -Wl,-soname=$@,--whole-archive $(LIBNAME).a -Wl,--no-whole-archive $(LDFLAGS) $(LDLIBS)
ln -sf $(shell pwd)/$@ $(KALDILIBDIR)/$@
else # Platform not supported
$(error Dynamic libraries not supported on this platform. Run configure with --static flag.)
endif
endif # ifeq ($(KALDI_FLAVOR), dynamic)
endif # ifdef LIBNAME
# By default (GNU) make uses the C compiler $(CC) for linking object files even
# if they were compiled from a C++ source. Below redefinition forces make to
# use the C++ compiler $(CXX) instead.
#把.o文件链接在一起的命令行,缺省值是$(CC) $(LDFLAGS) $(TARGET_ARCH)
LINK.o = $(CXX) $(LDFLAGS) $(TARGET_ARCH)
#make最终的目标, 这里根据Makefile的隐含规则创建可执行文件
$(BINFILES): $(LIBFILE) $(XDEPENDS)
# When building under CI, CI_NOLINKBINARIES is set to skip linking of binaries.
ifdef CI_NOLINKBINARIES
$(BINFILES): %: %.o
touch $@
endif
# Rule below would expand to, e.g.:
# ../base/kaldi-base.a:
# make -C ../base kaldi-base.a
# -C option to make is same as changing directory.
%.a:
$(MAKE) -C ${@D} ${@F}
%.so:
$(MAKE) -C ${@D} ${@F}
clean:
-rm -f *.o *.a *.so $(TESTFILES) $(BINFILES) $(TESTOUTPUTS) tmp* *.tmp *.testlog
distclean: clean
-rm -f .depend.mk
$(TESTFILES): $(LIBFILE) $(XDEPENDS)
test_compile: $(TESTFILES)
#编译测试文件,并执行
test: test_compile
@{ result=0; \
for x in $(TESTFILES); do \
printf "Running $$x ..."; \
timestamp1=$$(date +"%s"); \
./$$x >$$x.testlog 2>&1; \
ret=$$? \
timestamp2=$$(date +"%s"); \
time_taken=$$[timestamp2-timestamp1]; \
if [ $$ret -ne 0 ]; then \
echo " $${time_taken}s... FAIL $$x"; \
result=1; \
if [ -n "$TRAVIS" ] && [ -f core ] && command -v gdb >/dev/null 2>&1; then \
gdb $$x core -ex "thread apply all bt" -batch >>$$x.testlog 2>&1; \
rm -rf core; \
fi; \
else \
echo " $${time_taken}s... SUCCESS $$x"; \
rm -f $$x.testlog; \
fi; \
done; \
exit $$result; }
# Rules that enable valgrind debugging ("make valgrind")
valgrind: .valgrind
.valgrind: $(TESTFILES)
echo -n > valgrind.out
for x in $(TESTFILES); do \
echo $$x >>valgrind.out; \
valgrind ./$$x >/dev/null 2>> valgrind.out; \
done
! ( grep 'ERROR SUMMARY' valgrind.out | grep -v '0 errors' )
! ( grep 'definitely lost' valgrind.out | grep -v -w 0 )
rm valgrind.out
touch .valgrind
#buid up dependency commands
CC_SRCS=$(wildcard *.cc)
#check if files exist to run dependency commands on
ifneq ($(CC_SRCS),)
CC_DEP_COMMAND=$(CXX) -M $(CXXFLAGS) $(CC_SRCS)
endif
#kaldi.mk中定义了CUDA=true
ifeq ($(CUDA), true)
#统配符*.cu文件
CUDA_SRCS=$(wildcard *.cu)
#check if files exist to run dependency commands on
ifneq ($(CUDA_SRCS),)
NVCC_DEP_COMMAND = $(CUDATKDIR)/bin/nvcc -M $(CUDA_FLAGS) $(CUDA_INCLUDE) $(CUDA_SRCS)
endif
endif
depend:
rm -f .depend.mk
ifneq ($(CC_DEP_COMMAND),)
#生成.depend.mk 文件
$(CC_DEP_COMMAND) >> .depend.mk
endif
ifneq ($(NVCC_DEP_COMMAND),)
$(NVCC_DEP_COMMAND) >> .depend.mk
endif
# removing automatic making of "depend" as it's quite slow.
#.depend.mk: depend
-include .depend.mk
这个Makefile可以说是最重要的Makefile,所有的Makefile的最终目标都是通过这个Makefiel生成,包括生成depend,测试,编译,清理这些动作,都是在这里完成,当执行depend目标时,先列出当前目录的所有.cc文件,然后通过CC_DEP_COMMAND命令生成.depend.mk文件。当执行make时,将会根据上一个文件定义的变量执行响应的逻辑生成响应的文件,其中需要注意的是生成可执行文件时,并没有显示的命令用于生成相应的文件,因为这里利用的Makefile的隐含规则,当没有显式的如何生成可执行文件时,会在寻找目录和文件中定义的同名的.o文件生成对应的可执行文件,如果没有找不到,就会报错。
可以通过make -p查看make执行过程中查找隐含规则的过程。
(5)src/${subdir}/.depend.mk
这个文件主要包括目标的依赖。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26feature-sdc-test.o: feature-sdc-test.cc /usr/include/stdc-predef.h \
/usr/include/c++/5/iostream \
/usr/include/x86_64-linux-gnu/c++/5/bits/c++config.h \
/usr/include/x86_64-linux-gnu/c++/5/bits/os_defines.h \
/usr/include/features.h /usr/include/x86_64-linux-gnu/sys/cdefs.h \
/usr/include/x86_64-linux-gnu/bits/wordsize.h \
/usr/include/x86_64-linux-gnu/gnu/stubs.h \
/usr/include/x86_64-linux-gnu/gnu/stubs-64.h \
/usr/include/x86_64-linux-gnu/c++/5/bits/cpu_defines.h \
/usr/include/c++/5/ostream /usr/include/c++/5/ios \
/usr/include/c++/5/iosfwd /usr/include/c++/5/bits/stringfwd.h \
/usr/include/c++/5/bits/memoryfwd.h /usr/include/c++/5/bits/postypes.h \
/usr/include/c++/5/cwchar /usr/include/wchar.h /usr/include/stdio.h \
...
signal-test.o: signal-test.cc /usr/include/stdc-predef.h \
../base/kaldi-common.h /usr/include/c++/5/cstddef \
/usr/include/x86_64-linux-gnu/c++/5/bits/c++config.h \
/usr/include/x86_64-linux-gnu/c++/5/bits/os_defines.h \
/usr/include/features.h /usr/include/x86_64-linux-gnu/sys/cdefs.h \
/usr/include/x86_64-linux-gnu/bits/wordsize.h \
/usr/include/x86_64-linux-gnu/gnu/stubs.h \
/usr/include/x86_64-linux-gnu/gnu/stubs-64.h \
/usr/include/x86_64-linux-gnu/c++/5/bits/cpu_defines.h \
/usr/lib/gcc/x86_64-linux-gnu/5/include/stddef.h \
/usr/include/c++/5/cstdlib /usr/include/stdlib.h \
...
以上便是kald的src目录的编译过程。
- 本文链接:http://joefi.github.io/2019/05/17/Kaldi的编译过程-makefile/
- 版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC-SA 3.0 CN 许可协议。转载请注明出处!