Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 809
Garima Singh Avatar asked Jan 26 '15 16:01

Garima Singh


People also ask

Does makefile have to be in the same directory?

The makefile most often resides in the same directory as the other source files for the project. You can have many different makefiles on your machine at any one time. Indeed, if you have a large project, you may choose to manage it using separate makefiles for different parts of the project.

What is $@ in makefile?

The file name of the target of the rule. If the target is an archive member, then ' $@ ' is the name of the archive file. In a pattern rule that has multiple targets (see Introduction to Pattern Rules), ' $@ ' is the name of whichever target caused the rule's recipe to be run.

How object files are linked?

A shared object file holds code and data suitable to be linked in two contexts. First, the link-editor can process it with other relocatable and shared object files to create other object files. Second, the runtime linker combines it with a dynamic executable file and other shared objects to create a process image.

Does order matter makefile?

The order of rules is not significant, except for determining the default goal: the target for make to consider, if you do not otherwise specify one. The default goal is the target of the first rule in the first makefile. If the first rule has multiple targets, only the first target is taken as the default.


1 Answers

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.

like image 194
Mike Kinghan Avatar answered Oct 19 '22 22:10

Mike Kinghan