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

Gar*_*ngh 6 c c++ makefile search-path

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

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

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

Dir1 = /home/dkumar/libtsnnls-2.3.3/tsnnls
Run Code Online (Sandbox Code Playgroud)

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

OBJDir = /home/dkumar/libtsnnls-2.3.3/tsnnls
Run Code Online (Sandbox Code Playgroud)

此外,包含在其中的一些包含文件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
Run Code Online (Sandbox Code Playgroud)

和我的makefile工作正常.

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

Dir4 = /home/dkumar/CPP_ExampleCodes_DKU/Using_tsnnls_DKU/
Run Code Online (Sandbox Code Playgroud)

这是我的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 $@ $^
Run Code Online (Sandbox Code Playgroud)

尝试运行"$ make"时出现的错误

make: *** No rule to make target `libtsnnls_la-taucs_malloc.o', needed by `tsnnls_test_DKU'.  Stop.
Run Code Online (Sandbox Code Playgroud)

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

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

Mik*_*han 19

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

比方说,你的计划prog是将对象链接的C程序file0.o,file1.o将被编译成子目录obj.以下是您在makefile中执行该链接时通常需要的东西.

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

prog: $(OBJS)
    gcc -o $@ $(CFLAGS) $(OBJS) $(LDFLAGS) $(LDLIBS)
Run Code Online (Sandbox Code Playgroud)

这使$(OBJS)= obj/file0.o, obj/file1.o和你只需将这样的目标文件传递给link命令.文件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.
Run Code Online (Sandbox Code Playgroud)

让我们假设你有一些自己的头文件中的子目录 incinc/foo/bar和想要的预处理器搜索这些目录也.然后,您需要传递预处理器选项:

-I inc -I inc/foo/bar
Run Code Online (Sandbox Code Playgroud)

你的编译命令.预处理程序选项通常分配给make变量CPPFLAGS,例如

CPPFLAGS = -I inc -I inc/foo/bar
Run Code Online (Sandbox Code Playgroud)

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

gcc -c -o $@ $(CPPFLAGS) $(CFLAGS) $<
Run Code Online (Sandbox Code Playgroud)

NB认为这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
Run Code Online (Sandbox Code Playgroud)

(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.
Run Code Online (Sandbox Code Playgroud)

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

假设您有一个库,libfoobar.a您需要链接prog并且它位于目录中lib距您的makefile 2级以上中.然后,您需要传递链接器选项:

-L ../../lib
Run Code Online (Sandbox Code Playgroud)

-lfoobar
Run Code Online (Sandbox Code Playgroud)

到您的链接命令.第一个将告诉链接器,它../../lib是寻找库的地方之一.通常,您通过链接器命令配方传递此选项LDFLAGS.第二个告诉链接器搜索一些名为libfoobar.a(静态库)或libfoobar.so (动态库)的库.通常,您通过链接器命令配方传递此选项LDLIBS

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

gcc -v -Wl,--verbose 2>&1 | grep 'LIBRARY_PATH'
Run Code Online (Sandbox Code Playgroud)

输出将是这样的:

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) $<
Run Code Online (Sandbox Code Playgroud)

这告诉make我,例如obj/file0.o是由file0.c食谱制成的:

gcc -c -o obj/file0.o $(CPPFLAGS) $(CFLAGS) file0.c
Run Code Online (Sandbox Code Playgroud)

对于任何文件obj/*.o和匹配文件也是如此*.c

这很好,因为长时间file0.c驻留在与makefile相同的目录中,但是假设你的*.c文件在其他地方?假设您的源文件在子目录中组织,foo/file0.c并且bar/file1.c.然后make将无法满足该模式规则,并将说"没有规则使目标obj/file0.o"等.

要解决此问题,请使用具有特殊含义VPATHmake变量.如果您分配目录名称列表,以":"为标点VPATH, make则会在每个列出的目录中搜索先决条件,只要它在当前目录中找不到它.所以:

VPATH = foo:bar
Run Code Online (Sandbox Code Playgroud)

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

注意VPATH在发布的代码中使用错误:

VPATH = -L/home/dkumar/libtsnnls-2.3.3/tsnnls
Run Code Online (Sandbox Code Playgroud)

您已使用链接器选项为其分配了链接器搜索路径,该选项-L在那里没有业务.

底线:

  • 用于定位头文件的预处理器搜索路径是使用预处理器-I<dirname>选项指定的.将这些选项传递给编译配方CPPFLAGS

  • 用于定位库的链接器搜索路径是使用链接器-L<dirname>选项指定的.将这些选项传递给链接配方LDFLAGS

  • make规则预先存储的搜索路径在make变量中指定VPATH,作为目录名称的': - punctuated列表.