从命令行使用 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 捆绑包不好。

在典型情况下,捆绑会导致所有库都放在所有目标文件之前, 并且再也没有提到过。所以目标文件会产生未定义的符号错误,因为库 它们要求在链接器发现任何未定义的符号之前被链接器看到,并被忽略。 在您的非典型情况下,它导致 libcudartlibcuda 比您唯一看到的晚 目标文件 test_LayeredEEG.o - 但是不需要它们的符号 - 但早于 唯一需要他们提供符号的东西是图书馆 libmpdcm。所以他们被忽略了, 并且您构建了一个 .mex64 共享库,但尚未与它们链接。

很久以前 - GCC 4.5 之前 - 共享库(如 libcudartlibcuda)是豁免的 根据链接器看到它们时 需要 的要求, 为了被链接。它们无论如何都被链接在一起,就像目标文件一样,并且相信 这种情况并没有完全消失。不是这样。默认情况下,共享库和 当且仅当需要时才链接静态库。

为避免此类陷阱,理解规范的命名法非常有帮助 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)

赋值只是创建一个名为 .PHONYmake 变量,其值为 $(TDIR), 并且不会使 $(TDIR) 成为虚假目标。

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

您希望确保 make 在您需要输出任何内容之前创建 $(TDIR) 它,但你不希望它成为那些人工制品的正常先决条件,这将迫使 make 以在触及 $(TDIR) 的时间戳时重建它们。那大概是 为什么你想把它变成一个假目标。

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

$(mTESTS) : $(TDIR)/%.$(MEXEXT) : %.o | $(TDIR)

如果需要,这将导致在 $(mTESTS) 之前创建 $(TDIR),但是 尽管如此,在确定 $(mTESTS) 是否 时将不会考虑 $(TDIR) 需要制作。

另一方面,目标 allclean 虚假目标:没有这样的人工制品 将被制作,所以你应该告诉 make 所以:

.PHONY: all clean