使用命令行中的g ++控制mex链接选项

时间:2015-11-06 19:05:44

标签: matlab linker g++ mex

我正在尝试从命令行使用mex链接库,或者更确切地说,从makefile链接库。我是从我在这里发布的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的调用。我的环境有

export LD_LIBRARY_PATH = / usr / local / cuda-7.0 / lib64:$ LD_LIBRARY_PATH:

我的制作规则产生

/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中遇到链接错误:

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?

2 个答案:

答案 0 :(得分:2)

只是为了阐明问题和解决方案,并提供一些避免类似的帮助:

与GNU链接器链接的基本规则 您的问题makefile被违反的是:在要链接的实体的命令行序列中, 需要 符号定义的实体 必须 之前 那些 提供 定义

链接序列中的目标文件(.o)将整个合并到输出可执行文件中, 无论它是否定义了可执行文件使用的任何符号。 另一方面,仅检查它是否提供符号的任何定义 因而至今未定义,只有它提供的这些定义被链接到 可执行文件(我有点简化)。因此,链接不会开始,直到看到某个目标文件, 并且任何库都必须出现在需要定义的所有内容之后。

违反这一原则通常是由于某些链接器标志选项的无效捆绑引起的 和一些库选项一起成为make - 变量及其在连接配方中的位置, 结果是捆绑选项被插入到有效的位置 标志但对库无效。在您的问题makefile中就是这样,LOP1 坏捆绑。

在典型情况下,捆绑会导致所有库放在所有目标文件之前, 再也没有提过。因此,目标文件会产生未定义的符号错误,因为库 它们要求链接器在发现任何未定义的符号之前看到它们,并被忽略。 在您的非典型案例中,结果显示libcudartlibcuda晚于您的唯一身份 目标文件test_LayeredEEG.o - 但它不需要它们的符号 - 但早于 唯一确实需要来自它们的符号,即libmpdcm库。所以他们被忽略了, 并且您构建了一个尚未与它们链接的.mex64共享库。

很久以前 - GCC之前的4.5版 - 共享库(如libcudartlibcuda)免除 根据他们应该需要的要求,在链接器看到它们时, 为了被联系起来。无论如何,它们都与目标文件相关联,并且相信它们 这是如此并没有完全消亡。不是这样。默认情况下,共享库和 当且仅当需要时才会链接静态库。

为了避免这种陷阱,理解规范的命名是非常有帮助的 编译和链接中涉及的make变量及其语义,以及 它们在make的编译和链接配方中的规范用法。梅克斯是一个 C / C ++ / Fortran编译器的操纵器,它添加了一些自己的命令行选项: 对于make目的,它是另一个编译器。对于它继承的选项和 传递给底层编译器,您希望在make recipes。

中遵守该编译器的用法

这些变量最重要的make变量及其含义:

  • CC =您的C编译器,例如gcc
  • FC =您的Fortran编译器,例如gfortran
  • CXX =您的C ++编译器,例如g++
  • LD =您的链接器,例如ld。但是你应该知道这只适用于特殊用途 应该直接调用链接器。通常,在您的。上调用真正的链接器 代表编译器。它可以从您传递的选项中推断出您是否 想要编译完成或链接完成,并将调用适当的工具。当你 想要完成链接,它会悄悄地增加你传递的链接器选项 额外的那些指定非常令人厌烦,但确保 链接获取所有正确的标志和库的语言 程序你正在链接。因此几乎总是,将您的编译器指定为您的 接头
  • AR =您的归档工具(静态库构建器)
  • CFLAGS = C编译选项
  • FFLAGS = Fortran编译的选项
  • CXXFLAGS = C ++编译选项
  • CPPFLAGS = C预处理器的选项,适用于使用它的任何编译器。 当你的意思CPPFLAGS
  • 时,避免写CXXFLAGS的常见错误
  • 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食谱,那么你的路径会更顺畅。

BTW ......

你的makefile有以下错误:

.PHONY = $(TDIR)

这显然是尝试$(TDIR)成为phony target, 但语法错了。它应该是:

.PHONY: $(TDIR)

作业的作用就是创建名为make的{​​{1}}变量,其值为.PHONY, 并且不会使$(TDIR)成为假目标。

哪个是幸运的,因为$(TDIR)是你的输出目录,而不是假的 目标

您希望在需要输入任何内容之前确保$(TDIR)创建make 它,但你不希望它成为那些文物的正常先决条件,这是必须的 只要触及$(TDIR)的时间戳,就make重建它们。大概是这样的 为什么你认为这是一个虚假的目标。

您真正想要的$(TDIR)order-only prerequsite 将在那里输出的$(TDIR)。这样做的方法是将$(mTESTS)规则修改为:

$(mTESTS)

如果需要,在$(mTESTS) : $(TDIR)/%.$(MEXEXT) : %.o | $(TDIR) 之前,这将导致$(TDIR),但是 但在确定$(mTESTS) 是否时,将不会考虑$(TDIR) 需要制作。

另一方面,目标$(mTESTS)all 虚假的目标:没有这样的人工制品 要制作,所以你应该告诉clean

make

答案 1 :(得分:1)

正如评论中所指出的,问题是编译标志中动态库的顺序。在搜索了原因之后,我在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)