makefile : How to link object files from different subdirectory and include different search paths
Asked Answered
M

2

12

I want to change the location of my test code (tsnnls_test_DKU.c) and I am unable to make change in makefile to reflect this folder change properly. Some help would be appreciated.

I have two questions: 1) How to link object files from different subdirectory 2) include different search paths (3 search paths in my example).

In my orinal setup, where makefile works fine, I put my test code tsnnls_test_DKU.c at following location (inside the third party libraries):

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

All object files, that I am linking to, are at

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

Also, some include file contained in tsnnls_test_DKU.c are at following three locations (three search paths):

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

and my makefile works fine.

However, I would like to change the location of test code to:

Dir4 = /home/dkumar/CPP_ExampleCodes_DKU/Using_tsnnls_DKU/

Here is how my makefile looks like (Updated after inputs from other user:

# 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 $@ $^

The error that I get when trying to run "$make"

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

Obviously, I have not been able to use VPATH properly.

UPDATE: Thanks to Mike Kinghan for answering my question.

Myelencephalon answered 26/1, 2015 at 16:4 Comment(9)
Your Makefile does not seem to refer any include paths (since it only makes the link stage explicit). If you want to link objects residing in different subdirs, you might want to look into VPATH documentation.Catechism
@Catechism Do you have some working example of VPATH? I googled; found many link, but nothing that works for me. I could not figured out how to pass VPATH to compiler. Also, I am trying to include INC_PATH where header files and other codes exist. Please see my attempt in updated makefile. That's not working either.Myelencephalon
If I understand correctly your problem, the file names listed in '${OBJS}' can be in any of several directories and therefore there is now way to tell 'gcc' where these files are (not a Makefile problem but a gcc command line problem). If this is the actual problem, I am afraid '${VPATH}' won't help much as it is only used by 'make' to locate prerequisites. One way to resolve it is to use different variables for the list of 'OBJS' from different directories and to prepend the full path in the target prerequisitesQuotidian
@ComeRaczy All files in '${OBJS}' is in single folder. Also, the header files, that my test code needs, are located in three different folders all listed in INC_PATH.Myelencephalon
@GarimaSingh what is the actual problem then (whats's the error message)?Quotidian
The error message basically states that you miss to tell make which source files your object files are dependend on. Also, INC_PATH is not the default name for extending the compiler's include path. Try C_INCLUDE_PATH or CPATH if you're using gcc.Catechism
@Catechism I could figure out how to include object files contained in different directory or directories. Still, I could not used C_INCLUDE_PATH and CPATH to include file.Myelencephalon
Where are the source files (*.c/*.cpp) that correspond to the object files? E.g. where is the source file that corresponds to libtsnnls_la-taucs_malloc.o?Coeternal
@MikeKinghan I have been able to include object files located in different directory. Please see my partial answer in answer section. I still can not include the search file properly. Please see the update regarding this.Myelencephalon
C
39

Q1: How to link object files from a different subdirectory?

Let's say your program prog is a C program that will be linked from object file0.o, file1.o which are to be compiled into subdirectory obj. Here is the sort of thing you typically need in your makefile to perform that linkage.

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

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

This makes $(OBJS) = obj/file0.o, obj/file1.o and you simply pass the object files like that to the link command. Documentation of patsubst

N.B. This is not sufficient to create the obj subdirectory if it doesn't exist when you want to compile an object file into it. You'll have to create it yourself or study how to make make do it.

Q2: How to include different search paths?

This is an ambiguous question - the ambiguity is confusing you - and we must break it down into Q2.a, Q2.b, Q2.c:

Q2.a: How to specify different search paths where the preprocessor will look for header files that are #include-ed in the source code?

By default, the preprocessor will look for header files using a built-in a list of standard search paths. You can see them by running the preprocessor in verbose mode, e.g.cpp -v (CTRL-C to terminate). The output will contain something like:

#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.

Let's suppose you have some header files of your own in subdirectories inc and inc/foo/bar and want the preprocessor to search these directories as well. Then you need to pass the preprocessor options:

-I inc -I inc/foo/bar

to your compile command. Preprocessor options are conventionally assigned to the make variable CPPFLAGS, e.g.

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

(along with any other preprocessor options you require), and passed via this variable in the compile command recipe, e.g.

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

N.B. It is a common mistake to think that CPPFLAGS is the conventional make variable for C++ compiler flags. The conventional make variable for C++ compiler flags is CXXFLAGS.

You can see the effect of the -I option by running:

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

(CTRL-C to terminate). Now the output will contain the like of:

#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: How to specify different search paths where the the linker will look for libraries?

Let's suppose you are have a library, libfoobar.a that you need to link with prog and that it resides in a directory lib that is 2 levels up from your makefile. Then you need to pass the linker options:

-L ../../lib

and

-lfoobar

to your link command. The first of these will tell the linker that ../../lib is one of the places to look for libraries. Conventionally, you pass this option in the linker command recipe via LDFLAGS. The second tells the linker to search for some library called libfoobar.a (a static library) or libfoobar.so (a dynamic library). Conventionally, you pass this option in the linker command recipe via LDLIBS

Just as there is a default list of search paths for the preprocessor, there is a default list of search paths for the linker. You can see them by running:

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

The output will be something like:

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/

When you need to link one of the standard libraries, e.g. libm (the math library), that resides in one of the the default library search paths, you don't need to pass any -L option. -lm alone will do.

Q2.c: How to specify different search paths where make will look for the prerequisites of targets?

N.B. This is a question about make, not a question about the preprocessor, compiler, or linker.

We have assumed that all of your object files will be compiled into the subdirectory obj. To get them compiled there, it would be nice and simple just to use the pattern rule:

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

which tell make that, e.g. obj/file0.o is made from file0.c by the recipe:

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

and similarly for any file obj/*.o and matching file *.c

This is fine as long file0.c resides in the same directory as the makefile, but suppose you have your *.c files somewhere else? Say your source files are organised in subdirectories, foo/file0.c and bar/file1.c. Then make will be unable to satisfy that pattern rule and will say there is "no rule to make target obj/file0.o", etc.

To solve this problem use VPATH, a make variable that has a special meaning. If you assign a list of directory names, punctuated by ':', to VPATH, then make will search for a prerequisite in each of the listed directories whenever it can't find it in the current directory. So:

VPATH = foo:bar

will cause make to look in the current directory, and then foo and bar when it tries to find .c files to match that pattern rule. It will succeed in satisfying the rule and will compile the necessary source files.

N.B. You have used VPATH wrongly in your posted code:

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

You have asigned a linker search path to it, with the linker option -L, which has no business there.

Bottom line:

  • The preprecessor search paths, for locating header files, are specified with the preprocessor's -I<dirname> option. Pass these options to the compile recipe in CPPFLAGS

  • The linker search paths, for locating libraries, are specified with the linker's -L<dirname> option. Pass these options to the linkage recipe in LDFLAGS

  • The search paths for the preprequisities of make rules are specified in the make variable VPATH, as a ':-punctuated list of directory names.

Coeternal answered 27/1, 2015 at 14:35 Comment(2)
Thanks for such a detailed response. This is exactly what I was looking for. I can not really thank you enough.Myelencephalon
I would give +10 if I could. This answer sums up every question I ever had about Makefiles. I will keep it as reference for ever. Thank you!Hunger
M
0

My answer to part of question I could find out how to link object files from different subdirectory/ subdirectories; but, still could not figure out how to use different search path:

My current makefile is:

    # 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 $@ $^
Myelencephalon answered 26/1, 2015 at 16:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.