makefile : 如何链接来自不同子目录的目标文件并包含不同的搜索路径

Posted

技术标签:

【中文标题】makefile : 如何链接来自不同子目录的目标文件并包含不同的搜索路径【英文标题】:makefile : How to link object files from different subdirectory and include different search paths 【发布时间】:2015-01-26 16:04:31 【问题描述】:

我想更改我的测试代码 (tsnnls_test_DKU.c) 的位置,但我无法在 makefile 中进行更改以正确反映此文件夹更改。一些帮助将不胜感激。

我有两个问题: 1)如何链接来自不同子目录的目标文件 2) 包括不同的搜索路径(在我的示例中为 3 个搜索路径)。

在我的原始设置中,makefile 工作正常,我将测试代码 tsnnls_test_DKU.c 放在以下位置(在第三方库中):

Dir1 = /home/dkumar/libtsnnls-2.3.3/tsnnls

我链接到的所有目标文件都在

OBJDir = /home/dkumar/libtsnnls-2.3.3/tsnnls

另外,tsnnls_test_DKU.c 中包含的一些包含文件位于以下三个位置(三个搜索路径):

Dir1 = /home/dkumar/libtsnnls-2.3.3/tsnnls  
Dir2 = /home/dkumar/libtsnnls-2.3.3
Dir3 = /home/dkumar/libtsnnls-2.3.3/tsnnls/taucs_basic

我的 makefile 工作正常。

但是,我想将测试代码的位置更改为:

Dir4 = /home/dkumar/CPP_ExampleCodes_DKU/Using_tsnnls_DKU/

这是我的 makefile 的样子(在其他用户输入后更新:

# A sample Makefile

VPATH = -L/home/dkumar/libtsnnls-2.3.3/tsnnls
INC_PATH  = -I/home/dkumar/libtsnnls-2.3.3/ -I/home/dkumar/libtsnnls-2.3.3/tsnnls/  -I/home/dkumar/libtsnnls-2.3.3/tsnnls/taucs_basic/

# Here is a simple Make Macro.
LINK_TARGET     = tsnnls_test_DKU
OBJS_LOC    = tsnnls_test_DKU.o

# Here is a Make Macro that uses the backslash to extend to multiple lines.
OBJS =  libtsnnls_la-taucs_malloc.o libtsnnls_la-taucs_ccs_order.o \
    libtsnnls_la-taucs_ccs_ops.o libtsnnls_la-taucs_vec_base.o \
    libtsnnls_la-taucs_complex.o libtsnnls_la-colamd.o \
    libtsnnls_la-amdbar.o libtsnnls_la-amdexa.o \
    libtsnnls_la-amdtru.o libtsnnls_la-genmmd.o \
    libtsnnls_la-taucs_timer.o libtsnnls_la-taucs_sn_llt.o \
    libtsnnls_la-taucs_ccs_base.o libtsnnls_la-tlsqr.o \
    libtsnnls_la-tsnnls.o libtsnnls_la-lsqr.o   \
    $(OBJS_LOC)

REBUILDABLES = $(LINK_TARGET) 

all : $(LINK_TARGET)
    echo All done

clean : 
    rm -f $(REBUILDABLES)   
    echo Clean done

#Inclusion of all libraries
RANLIB = ranlib
STATICLIB= /usr/local/lib/taucs_full/lib/linux/libtaucs.a 

tsnnls_test_LDADD = $(LDADD)
LIBS = -largtable2 -llapack -lblas -lquadmath -lm

$(LINK_TARGET) : $(OBJS)   $(tsnnls_test_LDADD) $(LIBS)  $(STATICLIB)
gcc -g $INC_PATH -o $@ $^

我在尝试运行“$make”时遇到的错误

make: *** No rule to make target `libtsnnls_la-taucs_malloc.o', needed by `tsnnls_test_DKU'.  Stop.

显然,我无法正确使用 VPATH。

更新: 感谢 Mike Kinghan 回答我的问题。

【问题讨论】:

您的 Makefile 似乎没有引用任何包含路径(因为它只使链接阶段显式)。如果您想链接位于不同子目录中的对象,您可能需要查看VPATH 文档。 @mfro 你有一些 VPATH 的工作示例吗?我用谷歌搜索;找到了很多链接,但没有什么对我有用。我不知道如何将 VPATH 传递给编译器。另外,我正在尝试在存在头文件和其他代码的地方包含 INC_PATH。请参阅我在更新的 makefile 中的尝试。那也不行。 如果我正确理解了您的问题,“$OBJS”中列出的文件名可以位于多个目录中的任何一个,因此现在可以告诉“gcc”这些文件在哪里(不是Makefile 问题,但 gcc 命令行问题)。如果这是实际问题,恐怕 '$VPATH' 不会有太大帮助,因为它仅由 'make' 用于定位先决条件。解决它的一种方法是对来自不同目录的“OBJS”列表使用不同的变量,并在目标先决条件中添加完整路径 @ComeRaczy '$OBJS' 中的所有文件都在单个文件夹中。此外,我的测试代码需要的头文件位于三个不同的文件夹中,都列在 INC_PATH 中。 @GarimaSingh 那么实际问题是什么(错误信息是什么)? 【参考方案1】:

Q1:如何链接来自不同子目录的目标文件?

假设您的程序 prog 是一个 C 程序,它将与对象 file0.ofile1.o 链接,其中 将被编译到子目录obj。这是您通常需要的东西 makefile 来执行该链接。

$(OBJS) = $(patsubst %.o,obj/%.o,file0.o file1.o)

prog: $(OBJS)
    gcc -o $@ $(CFLAGS) $(OBJS) $(LDFLAGS) $(LDLIBS)

这使得$(OBJS) = obj/file0.o, obj/file1.o 你只需传递对象 像这样的文件链接命令。 Documentation of patsubst

注意如果您想要创建obj 子目录不存在,这不足以创建 将目标文件编译到其中。您必须自己创建它或研究如何让make 做到这一点。

Q2:如何包含不同的搜索路径?

这是一个模棱两可的问题 - 模棱两可让你感到困惑 - 我们必须将其分解为 Q2.a、Q2.b、Q2.c

Q2.a:如何指定预处理器在源代码中查找#include-ed 的头文件的不同搜索路径?

默认情况下,预处理器将使用内置的标准搜索路径列表查找头文件。您可以通过以下方式查看它们 以详细模式运行预处理器,例如cpp -v(CTRL-C 终止)。输出将包含以下内容:

#include "..." search starts here:
#include <...> search starts here:
 /usr/lib/gcc/x86_64-linux-gnu/4.8/include
 /usr/local/include
 /usr/lib/gcc/x86_64-linux-gnu/4.8/include-fixed
 /usr/include/x86_64-linux-gnu
 /usr/include
End of search list.

假设您在子目录中有一些自己的头文件 incinc/foo/bar 并希望预处理器搜索这些目录 也是。然后你需要传递预处理器选项:

-I inc -I inc/foo/bar

到你的编译命令。预处理器选项通常分配给 make 变量CPPFLAGS,例如

CPPFLAGS = -I inc -I inc/foo/bar

(以及您需要的任何其他预处理器选项),并通过此 编译命令配方中的变量,例如

gcc -c -o $@ $(CPPFLAGS) $(CFLAGS) $<

注意 认为CPPFLAGS 是 C++ 编译器标志的常规 make 变量是一个常见的错误。 C++ 编译器标志的常规 make 变量是 CXXFLAGS

你可以通过运行查看-I选项的效果:

mkdir -p inc/foo/bar # Just to create the path
cpp -v -I inc -I inc/foo/bar

(CTRL-C 终止)。现在输出将包含以下内容:

#include "..." search starts here:
#include <...> search starts here:
 inc
 inc/foo/bar
 /usr/lib/gcc/x86_64-linux-gnu/4.8/include
 /usr/local/include
 /usr/lib/gcc/x86_64-linux-gnu/4.8/include-fixed
 /usr/include/x86_64-linux-gnu
 /usr/include
End of search list.

Q2.b:如何指定链接器查找库的不同搜索路径?

假设您有一个库 libfoobar.a,您需要与 prog 和 它位于一个目录 lib 中,该目录比您的 makefile 高 2 级。那么你 需要传递链接器选项:

-L ../../lib

-lfoobar

到您的链接命令。其中第一个将告诉链接器../../lib 是要查看的地方之一 对于图书馆。通常,您通过LDFLAGS 在链接器命令配方中传递此选项。第二个告诉 用于搜索名为 libfoobar.a(静态库)或 libfoobar.so 的库的链接器 (一个动态库)。按照惯例,您可以通过 LDLIBS 在链接器命令配方中传递此选项

正如预处理器有一个默认的搜索路径列表一样,也有一个默认的 链接器的搜索路径列表。您可以通过运行查看它们:

gcc -v -Wl,--verbose 2>&1 | grep 'LIBRARY_PATH'

输出将类似于:

LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/4.8/:/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu /: /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/: /usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../:/lib /:/usr/lib/

当您需要链接其中一个标准库时,例如libm(数学库),位于其中一个 默认库搜索路径,您不需要传递任何 -L 选项。 -lm 一个人就可以了。

Q2.c:如何指定 make 将在其中查找先决条件的不同搜索路径 目标?

注意这是一个关于 make 的问题,而不是关于预处理器、编译器或 链接器。

我们假设您的所有目标文件都将编译到子目录obj 中。 为了让它们在那里编译,使用模式规则会很简单:

obj/%.o:%.c
    gcc -c -o $@ $(CPPFLAGS) $(CFLAGS) $<

告诉make,例如obj/file0.o 是由 file0.c 通过配方制成的:

gcc -c -o obj/file0.o $(CPPFLAGS) $(CFLAGS) file0.c

同样适用于任何文件obj/*.o 和匹配文件*.c

只要file0.c 与 makefile 位于同一目录中就可以了,但是 假设您在其他地方有您的*.c 文件?假设你的源文件是有组织的 在子目录中,foo/file0.cbar/file1.c。那么make 将无法 满足该模式规则并会说“没有规则可以制作目标 obj/file0.o”, 等等

要解决此问题,请使用VPATH,这是一个具有特殊含义的make 变量。 如果您将目录名称列表(以“:”作为标点符号)分配给VPATH,则 make 将在每个列出的目录中搜索先决条件 它在当前目录中找不到它。所以:

VPATH = foo:bar

将导致make 查找当前目录,然后foobar 时 它试图找到.c 文件以匹配该模式规则。会成功满足 规则并将编译必要的源文件。

注意您在发布的代码中错误地使用了VPATH

VPATH = -L/home/dkumar/libtsnnls-2.3.3/tsnnls

你已经为它分配了一个链接器搜索路径,链接器选项-L,它没有 在那里做生意。

底线:

用于定位头文件的前驱搜索路径指定为 预处理器的-I&lt;dirname&gt; 选项。将这些选项传递给 CPPFLAGS

中的编译配方

用于定位库的链接器搜索路径由 链接器的-L&lt;dirname&gt; 选项。将这些选项传递给 LDFLAGS

中的链接配方

make 规则的先决条件的搜索路径在 make 变量 VPATH,作为 ':- 标点的目录名称列表。

【讨论】:

感谢您如此详细的回复。这正是我一直在寻找的。我真的非常感谢你。 如果可以的话,我会给 +10。这个答案总结了我关于 Makefile 的所有问题。我会一直作为参考。谢谢!【参考方案2】:

My answer to part of question 我可以找到如何链接object files from different subdirectory/ subdirectories;但是,还是could not figure out how to use different search path:

我当前的makefile是:

    # A sample Makefile

OBJS_PATH = /home/dkumar/libtsnnls-2.3.3/tsnnls/
C_INCLUDE_PATH  = -I/home/dkumar/libtsnnls-2.3.3/ -I/home/dkumar/libtsnnls-2.3.3/tsnnls/  -I/home/dkumar/libtsnnls-2.3.3/tsnnls/taucs_basic/
CPATH  = -I/home/dkumar/libtsnnls-2.3.3/ -I/home/dkumar/libtsnnls-2.3.3/tsnnls/  -I/home/dkumar/libtsnnls-2.3.3/tsnnls/taucs_basic/

# Here is a simple Make Macro.
LINK_TARGET     = tsnnls_test_DKU
OBJS_LOC    = tsnnls_test_DKU.o

# Here is a Make Macro that uses the backslash to extend to multiple lines.
OBJS =  libtsnnls_la-taucs_malloc.o libtsnnls_la-taucs_ccs_order.o \
    libtsnnls_la-taucs_ccs_ops.o libtsnnls_la-taucs_vec_base.o \
    libtsnnls_la-taucs_complex.o libtsnnls_la-colamd.o \
    libtsnnls_la-amdbar.o libtsnnls_la-amdexa.o \
    libtsnnls_la-amdtru.o libtsnnls_la-genmmd.o \
    libtsnnls_la-taucs_timer.o libtsnnls_la-taucs_sn_llt.o \
    libtsnnls_la-taucs_ccs_base.o libtsnnls_la-tlsqr.o \
    libtsnnls_la-tsnnls.o libtsnnls_la-lsqr.o   

 ## adding "$(OBJS_PATH)" to each word in "$(OBJS)"
# which in our case is basically to add the same folder in front of all "*.o" object files.
OBJS2 = $(addprefix $(OBJS_PATH)/, $(OBJS)) 


# OBJS_LOC is in current working directory,
OBJS_ALL = $(OBJS2) \
    $(OBJS_LOC)

## DKU IS COPING THIS FROM ORIGINAL MAKEFILE THAT ARE GENERATED USING /home/dkumar/libtsnnls-2.3.3/tsnnls/MAKEFILE.AM
RANLIB = ranlib
STATICLIB= /usr/local/lib/taucs_full/lib/linux/libtaucs.a 

tsnnls_test_LDADD = $(LDADD)
LIBS = -largtable2 -llapack -lblas -lquadmath -lm


REBUILDABLES = $(OBJS_LOC) $(LINK_TARGET) 


all : $(LINK_TARGET)
    echo All done

clean : 
    rm -f $(REBUILDABLES)   
    echo Clean done

# Here is a Rule that uses some built-in Make Macros in its command:
$(LINK_TARGET) : $(OBJS_ALL)   $(tsnnls_test_LDADD) $(LIBS)  $(STATICLIB)
    gcc -g -o $@ $^

【讨论】:

以上是关于makefile : 如何链接来自不同子目录的目标文件并包含不同的搜索路径的主要内容,如果未能解决你的问题,请参考以下文章

请教多文件夹makefile问题

使用 make 和 gcc 编译和链接来自不同目录的源代码

Makefile:来自相同源的两个目标使用不同的标志编译两次

makefile:如何生成目标文件到上层目录

makefile : 如何链接目标文件

C Makefile - 执行和目标文件到不同的目录