从命令行使用 g++ 控制 mex link 选项
Control mex link options with g++ from command line
我正在尝试 link 从命令行使用 mex 的库,或者更确切地说,从 makefile。我在 post 此处从 Makefile 执行此操作:
BDDM_MATLAB = @matlabhome@
MEXCC = $(BDDM_MATLAB)/bin/mex
MEXFLAGS = -v -largeArrayDims -O
MEXEXT = mexa64
TDIR = $(abs_top_srcdir)/test
IDIR = $(abs_top_srcdir)/src
LDIR = $(abs_top_srcdir)/lib
LOP1 = $(CUDA_LDFLAGS) $(LIBS)
SOURCES := $(wildcard *.cpp)
OBJS = $(SOURCES:.cpp=.o)
mTESTS = $(addprefix $(TDIR)/, $(SOURCES:.cpp=.$(MEXEXT)))
all: $(TDIR) $(mTESTS)
$(OBJS) : %.o : %.cpp
$(MEXCC) $(MEXFLAGS) -c -outdir ./ -output $@ $(CUDA_CFLAGS) -I$(IDIR) CFLAGS="$$CFLAGS -std=c99" $^
$(mTESTS) : $(TDIR)/%.$(MEXEXT) : %.o
$(MEXCC) $(MEXFLAGS) -L$(LDIR) -outdir $(TDIR) $^ $(LOP1) -lmpdcm LDFLAGS="-lcudart -lcuda"
.PHONY = $(TDIR)
$(TDIR):
$(MKDIR_P) $@
clean:
$(RM) *.o
libmpdcm 是一个静态库,包括对两个共享库 libcuda 和 libcudart 的调用。我的环境有
导出LD_LIBRARY_PATH=/usr/local/cuda-7.0/lib64:$LD_LIBRARY_PATH:
我的 make 规则生成
/usr/local/MATLAB/R2014a/bin/mex -v -largeArrayDims -O -L/home/eaponte/projects/test_cpp/lib -outdir /home/eaponte/projects/test_cpp/test test_LayeredEEG.o -L/usr/local/cuda/lib64 -lcudart -lcuda -lmpdcm LDFLAGS="-lcudart -lcuda"
这会产生以下 g++ 命令:
/usr/bin/gcc -lcudart -lcuda -shared -O -Wl,--version-script,"/usr/local/MATLAB/R2014a/extern/lib/glnxa64/mexFunction.map" test_LayeredEEG.o -lcudart -lcuda -lmpdcm -L/home/eaponte/projects/test_cpp/lib -L/usr/local/cuda/lib64 -L"/usr/local/MATLAB/R2014a/bin/glnxa64" -lmx -lmex -lmat -lm -lstdc++ -o /home/eaponte/projects/test_cpp/test/test_LayeredEEG.mexa64
问题是之后我在 Matlab 中遇到 linking 错误:
Invalid MEX-file '/home/eaponte/projects/test_cpp/test/test_Fmri.mexa64': /home/eaponte/projects/test_cpp/test/test_Fmri.mexa64: undefined symbol: cudaFree
我知道解决方案就是将 cuda 库放在 g++ 命令的末尾
/usr/bin/gcc -lcudart -lcuda -shared -O -Wl,--version-script,"/usr/local/MATLAB/R2014a/extern/lib/glnxa64/mexFunction.map" test_LayeredEEG.o -lmpdcm -L/home/eaponte/projects/test_cpp/lib -L/usr/local/cuda/lib64 -L"/usr/local/MATLAB/R2014a/bin/glnxa64" -lmx -lmex -lmat -lm -lstdc++ -lcudart -lcuda -o /home/eaponte/projects/test_cpp/test/test_LayeredEEG.mexa64
如何从命令行(或 Makefile)实现 运行 mex?
正如评论中所指出的,问题在于编译标志中动态库的顺序。在搜索了这个原因之后,我发现在 SO 中需要考虑依赖顺序来链接静态库。就我而言,库 libmpdc 依赖于 libcuda 和 libcudart 但在左侧。解决办法是把makefile里的顺序换成:
$(mTESTS) : $(TDIR)/%.$(MEXEXT) : %.o
$(MEXCC) $(MEXFLAGS) -L$(LDIR) -outdir $(TDIR) $^ $(LOP1) -lmpdcm LDFLAGS="-lcudart -lcuda"
至
$(mTESTS) : $(TDIR)/%.$(MEXEXT) : %.o
$(MEXCC) $(MEXFLAGS) -L$(LDIR) -outdir $(TDIR) $^ -lmpdcm $(LOP1)
只是为了阐明问题和解决方案,并提供一些帮助来避免类似的情况:
与 GNU 链接器链接的基本规则
您的问题 makefile 违规是:在要链接的实体的命令行序列中,那些 需要 符号定义
必须出现在 之前 那些 提供 定义.
链接序列中的一个目标文件(.o
)将被整个合并到输出可执行文件中,
不管它是否定义了可执行文件使用的任何符号。一个图书馆
另一方面,仅检查它是否提供任何符号定义
迄今为止未定义的,只有它提供的定义被链接到
可执行文件(我正在做一些简化)。因此,在看到 some 目标文件之前,链接不会开始,
并且任何库都必须出现在需要从中定义的所有内容之后。
违反此原则的原因通常是某些链接器标志选项的不当捆绑
和一些库选项一起组成一个 make
变量及其在链接配方中的位置,
结果是捆绑选项被插值到一个对以下内容有效的位置
标志但对图书馆无效。这在你的问题 makefile 中是这样的,LOP1
捆绑包不好。
在典型情况下,捆绑会导致所有库都放在所有目标文件之前,
并且再也没有提到过。所以目标文件会产生未定义的符号错误,因为库
它们要求在链接器发现任何未定义的符号之前被链接器看到,并被忽略。
在您的非典型情况下,它导致 libcudart
和 libcuda
比您唯一看到的晚
目标文件 test_LayeredEEG.o
- 但是不需要它们的符号 - 但早于
唯一需要他们提供符号的东西是图书馆 libmpdcm
。所以他们被忽略了,
并且您构建了一个 .mex64
共享库,但尚未与它们链接。
很久以前 - GCC 4.5 之前 - 共享库(如 libcudart
和 libcuda
)是豁免的
根据链接器看到它们时 需要 的要求,
为了被链接。它们无论如何都被链接在一起,就像目标文件一样,并且相信
这种情况并没有完全消失。不是这样。默认情况下,共享库和
当且仅当需要时才链接静态库。
为避免此类陷阱,理解规范的命名法非常有帮助
make
编译和链接中涉及的变量及其语义,以及
它们在 make
的编译和链接配方中的规范使用。墨西哥是一个
C/C++/Fortran 编译器的操纵器,它添加了一些自己的命令行选项:
出于 make
目的,它是另一个编译器。对于它继承自的选项和
传递给底层编译器,您想遵守 make
食谱中该编译器的用法。
这些 make
变量可能对您及其含义最重要:
CC
= 您的 C 编译器,例如gcc
FC
= 您的 Fortran 编译器,例如gfortran
CXX
= 您的 C++ 编译器,例如g++
.
LD
= 您的链接器,例如ld
。但你应该知道,仅用于专门用途
是否应该直接调用链接器。通常,真正的链接器是在你的
由编译器代表。它可以从你传递的选项中推断出你是否
希望编译完成或链接完成,并将调用适当的工具。当你
想要链接完成,它会悄悄地增加你传递的链接器选项
额外的一些,指定起来会很烦人,但可以确保
链接获取所有正确的标志和库的语言
您正在链接的程序。因此,几乎总是 将您的编译器指定为您的
链接器.
AR
= 您的归档工具(静态库生成器)
CFLAGS
= C 编译选项
FFLAGS
= Fortran 编译选项
CXXFLAGS
= C++ 编译选项
CPPFLAGS
= C 预处理器选项,适用于任何使用它的编译器。 避免在意思是 CXXFLAGS
时写 CPPFLAGS
的常见错误
LDFLAGS
= 链接选项,N.B。排除库选项、-l<name>
LDLIBS
= 用于链接的库选项,-l<name>
编译和链接的规范 make
规则:
C 源文件 $<
到目标文件 $@
:
$(CC) $(CPPFLAGS) $(CFLAGS) -c $@ $<
从 Fortran 文件 $<
到目标文件 $@
,预处理:
$(FC) $(CPPFLAGS) $(FFLAGS) -c $@ $<
(没有预处理,去掉$(CPPFLAGS)
)
C++ 源文件 $<
到目标文件 $@
:
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $@ $<
将目标文件 $^ 链接到可执行文件 $@:
$(<compiler>) $(LDFLAGS) -o $@ $^ $(LDLIBS)
如果您可以尽可能多地编写 makefile,以便 a) 您已将正确的选项分配给来自
这个词汇表,并且 b) 使用了规范的 make
食谱,那么你的道路会更顺畅。
顺便说一句...
您的 makefile 存在以下错误:
.PHONY = $(TDIR)
这显然是试图让 $(TDIR)
变成 phony target,
但语法错误。应该是:
.PHONY: $(TDIR)
赋值只是创建一个名为 .PHONY
的 make
变量,其值为 $(TDIR)
,
并且不会使 $(TDIR)
成为虚假目标。
这是幸运的,因为 $(TDIR)
是你的输出目录而不是假的
目标。
您希望确保 make
在您需要输出任何内容之前创建 $(TDIR)
它,但你不希望它成为那些人工制品的正常先决条件,这将迫使
make
以在触及 $(TDIR)
的时间戳时重建它们。那大概是
为什么你想把它变成一个假目标。
你真正想要的 $(TDIR)
是一个 order-only prerequsite
将在那里输出的 $(mTESTS)
。这样做的方法是将 $(mTESTS)
规则修改为:
$(mTESTS) : $(TDIR)/%.$(MEXEXT) : %.o | $(TDIR)
如果需要,这将导致在 $(mTESTS)
之前创建 $(TDIR)
,但是
尽管如此,在确定 $(mTESTS)
是否 时将不会考虑 $(TDIR)
需要制作。
另一方面,目标 all
和 clean
是 虚假目标:没有这样的人工制品
将被制作,所以你应该告诉 make
所以:
.PHONY: all clean
我正在尝试 link 从命令行使用 mex 的库,或者更确切地说,从 makefile。我在 post 此处从 Makefile 执行此操作:
BDDM_MATLAB = @matlabhome@
MEXCC = $(BDDM_MATLAB)/bin/mex
MEXFLAGS = -v -largeArrayDims -O
MEXEXT = mexa64
TDIR = $(abs_top_srcdir)/test
IDIR = $(abs_top_srcdir)/src
LDIR = $(abs_top_srcdir)/lib
LOP1 = $(CUDA_LDFLAGS) $(LIBS)
SOURCES := $(wildcard *.cpp)
OBJS = $(SOURCES:.cpp=.o)
mTESTS = $(addprefix $(TDIR)/, $(SOURCES:.cpp=.$(MEXEXT)))
all: $(TDIR) $(mTESTS)
$(OBJS) : %.o : %.cpp
$(MEXCC) $(MEXFLAGS) -c -outdir ./ -output $@ $(CUDA_CFLAGS) -I$(IDIR) CFLAGS="$$CFLAGS -std=c99" $^
$(mTESTS) : $(TDIR)/%.$(MEXEXT) : %.o
$(MEXCC) $(MEXFLAGS) -L$(LDIR) -outdir $(TDIR) $^ $(LOP1) -lmpdcm LDFLAGS="-lcudart -lcuda"
.PHONY = $(TDIR)
$(TDIR):
$(MKDIR_P) $@
clean:
$(RM) *.o
libmpdcm 是一个静态库,包括对两个共享库 libcuda 和 libcudart 的调用。我的环境有
导出LD_LIBRARY_PATH=/usr/local/cuda-7.0/lib64:$LD_LIBRARY_PATH:
我的 make 规则生成
/usr/local/MATLAB/R2014a/bin/mex -v -largeArrayDims -O -L/home/eaponte/projects/test_cpp/lib -outdir /home/eaponte/projects/test_cpp/test test_LayeredEEG.o -L/usr/local/cuda/lib64 -lcudart -lcuda -lmpdcm LDFLAGS="-lcudart -lcuda"
这会产生以下 g++ 命令:
/usr/bin/gcc -lcudart -lcuda -shared -O -Wl,--version-script,"/usr/local/MATLAB/R2014a/extern/lib/glnxa64/mexFunction.map" test_LayeredEEG.o -lcudart -lcuda -lmpdcm -L/home/eaponte/projects/test_cpp/lib -L/usr/local/cuda/lib64 -L"/usr/local/MATLAB/R2014a/bin/glnxa64" -lmx -lmex -lmat -lm -lstdc++ -o /home/eaponte/projects/test_cpp/test/test_LayeredEEG.mexa64
问题是之后我在 Matlab 中遇到 linking 错误:
Invalid MEX-file '/home/eaponte/projects/test_cpp/test/test_Fmri.mexa64': /home/eaponte/projects/test_cpp/test/test_Fmri.mexa64: undefined symbol: cudaFree
我知道解决方案就是将 cuda 库放在 g++ 命令的末尾
/usr/bin/gcc -lcudart -lcuda -shared -O -Wl,--version-script,"/usr/local/MATLAB/R2014a/extern/lib/glnxa64/mexFunction.map" test_LayeredEEG.o -lmpdcm -L/home/eaponte/projects/test_cpp/lib -L/usr/local/cuda/lib64 -L"/usr/local/MATLAB/R2014a/bin/glnxa64" -lmx -lmex -lmat -lm -lstdc++ -lcudart -lcuda -o /home/eaponte/projects/test_cpp/test/test_LayeredEEG.mexa64
如何从命令行(或 Makefile)实现 运行 mex?
正如评论中所指出的,问题在于编译标志中动态库的顺序。在搜索了这个原因之后,我发现在 SO 中需要考虑依赖顺序来链接静态库。就我而言,库 libmpdc 依赖于 libcuda 和 libcudart 但在左侧。解决办法是把makefile里的顺序换成:
$(mTESTS) : $(TDIR)/%.$(MEXEXT) : %.o
$(MEXCC) $(MEXFLAGS) -L$(LDIR) -outdir $(TDIR) $^ $(LOP1) -lmpdcm LDFLAGS="-lcudart -lcuda"
至
$(mTESTS) : $(TDIR)/%.$(MEXEXT) : %.o
$(MEXCC) $(MEXFLAGS) -L$(LDIR) -outdir $(TDIR) $^ -lmpdcm $(LOP1)
只是为了阐明问题和解决方案,并提供一些帮助来避免类似的情况:
与 GNU 链接器链接的基本规则 您的问题 makefile 违规是:在要链接的实体的命令行序列中,那些 需要 符号定义 必须出现在 之前 那些 提供 定义.
链接序列中的一个目标文件(.o
)将被整个合并到输出可执行文件中,
不管它是否定义了可执行文件使用的任何符号。一个图书馆
另一方面,仅检查它是否提供任何符号定义
迄今为止未定义的,只有它提供的定义被链接到
可执行文件(我正在做一些简化)。因此,在看到 some 目标文件之前,链接不会开始,
并且任何库都必须出现在需要从中定义的所有内容之后。
违反此原则的原因通常是某些链接器标志选项的不当捆绑
和一些库选项一起组成一个 make
变量及其在链接配方中的位置,
结果是捆绑选项被插值到一个对以下内容有效的位置
标志但对图书馆无效。这在你的问题 makefile 中是这样的,LOP1
捆绑包不好。
在典型情况下,捆绑会导致所有库都放在所有目标文件之前,
并且再也没有提到过。所以目标文件会产生未定义的符号错误,因为库
它们要求在链接器发现任何未定义的符号之前被链接器看到,并被忽略。
在您的非典型情况下,它导致 libcudart
和 libcuda
比您唯一看到的晚
目标文件 test_LayeredEEG.o
- 但是不需要它们的符号 - 但早于
唯一需要他们提供符号的东西是图书馆 libmpdcm
。所以他们被忽略了,
并且您构建了一个 .mex64
共享库,但尚未与它们链接。
很久以前 - GCC 4.5 之前 - 共享库(如 libcudart
和 libcuda
)是豁免的
根据链接器看到它们时 需要 的要求,
为了被链接。它们无论如何都被链接在一起,就像目标文件一样,并且相信
这种情况并没有完全消失。不是这样。默认情况下,共享库和
当且仅当需要时才链接静态库。
为避免此类陷阱,理解规范的命名法非常有帮助
make
编译和链接中涉及的变量及其语义,以及
它们在 make
的编译和链接配方中的规范使用。墨西哥是一个
C/C++/Fortran 编译器的操纵器,它添加了一些自己的命令行选项:
出于 make
目的,它是另一个编译器。对于它继承自的选项和
传递给底层编译器,您想遵守 make
食谱中该编译器的用法。
这些 make
变量可能对您及其含义最重要:
CC
= 您的 C 编译器,例如gcc
FC
= 您的 Fortran 编译器,例如gfortran
CXX
= 您的 C++ 编译器,例如g++
.LD
= 您的链接器,例如ld
。但你应该知道,仅用于专门用途 是否应该直接调用链接器。通常,真正的链接器是在你的 由编译器代表。它可以从你传递的选项中推断出你是否 希望编译完成或链接完成,并将调用适当的工具。当你 想要链接完成,它会悄悄地增加你传递的链接器选项 额外的一些,指定起来会很烦人,但可以确保 链接获取所有正确的标志和库的语言 您正在链接的程序。因此,几乎总是 将您的编译器指定为您的 链接器.AR
= 您的归档工具(静态库生成器)CFLAGS
= C 编译选项FFLAGS
= Fortran 编译选项CXXFLAGS
= C++ 编译选项CPPFLAGS
= C 预处理器选项,适用于任何使用它的编译器。 避免在意思是CXXFLAGS
时写 LDFLAGS
= 链接选项,N.B。排除库选项、-l<name>
LDLIBS
= 用于链接的库选项,-l<name>
CPPFLAGS
的常见错误
编译和链接的规范 make
规则:
C 源文件 $<
到目标文件 $@
:
$(CC) $(CPPFLAGS) $(CFLAGS) -c $@ $<
从 Fortran 文件 $<
到目标文件 $@
,预处理:
$(FC) $(CPPFLAGS) $(FFLAGS) -c $@ $<
(没有预处理,去掉$(CPPFLAGS)
)
C++ 源文件 $<
到目标文件 $@
:
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $@ $<
将目标文件 $^ 链接到可执行文件 $@:
$(<compiler>) $(LDFLAGS) -o $@ $^ $(LDLIBS)
如果您可以尽可能多地编写 makefile,以便 a) 您已将正确的选项分配给来自
这个词汇表,并且 b) 使用了规范的 make
食谱,那么你的道路会更顺畅。
顺便说一句...
您的 makefile 存在以下错误:
.PHONY = $(TDIR)
这显然是试图让 $(TDIR)
变成 phony target,
但语法错误。应该是:
.PHONY: $(TDIR)
赋值只是创建一个名为 .PHONY
的 make
变量,其值为 $(TDIR)
,
并且不会使 $(TDIR)
成为虚假目标。
这是幸运的,因为 $(TDIR)
是你的输出目录而不是假的
目标。
您希望确保 make
在您需要输出任何内容之前创建 $(TDIR)
它,但你不希望它成为那些人工制品的正常先决条件,这将迫使
make
以在触及 $(TDIR)
的时间戳时重建它们。那大概是
为什么你想把它变成一个假目标。
你真正想要的 $(TDIR)
是一个 order-only prerequsite
将在那里输出的 $(mTESTS)
。这样做的方法是将 $(mTESTS)
规则修改为:
$(mTESTS) : $(TDIR)/%.$(MEXEXT) : %.o | $(TDIR)
如果需要,这将导致在 $(mTESTS)
之前创建 $(TDIR)
,但是
尽管如此,在确定 $(mTESTS)
是否 时将不会考虑 $(TDIR)
需要制作。
另一方面,目标 all
和 clean
是 虚假目标:没有这样的人工制品
将被制作,所以你应该告诉 make
所以:
.PHONY: all clean