Arie Charfnadel
Arie Charfnadel

Reputation: 158

searching solution for creating generic makefile for compilation of multiple programs

Till now, I was using the following makefile that I have generated somehow for my school projects:

my makefile

But now I have a different situation: I am supposed to compile 4 programs for one project, while part of the code is supposed to be compiled as .so, for use for the 4 programs. like described here:

my project's dependencies

1 - all the parts that are supposed to be compiled together as one .so file, using for example:

gcc -shared -fPIC src/file1.c src/file2.c src/file3.c -o libutils.so

3,4,5 should be compiled and linked together with this .so file, using for example:

gcc src/file4.c -L'pwd' lutils -o file4.out

the same way for all the 3 projects, and one more simple compilation of project 2.

I wandered across the net, google, your site, etc. tried to find a solution for this situation, without any luck.

already seen solutions like this one: solution example where you supply makefile with the details of the entire project structure.

I thought about dividing all the files into 4 folders, below the main folder, and creating a loop inside makefile that will compile each program in each cycle, with "if" statements to make a different compilation, according to the index. but I had no luck, it seems very complicated (maybe someone can show me a solution like that one...).

I am wondering if there is a way of making this whole compilation process generic and automatic like the current file (maybe little less),

if there is a way, I would like to study and discover it.

thank you in advance!!! Arie

Upvotes: 0

Views: 245

Answers (1)

the busybee
the busybee

Reputation: 12600

Since you have a nicely drawn tree of dependencies, you "just" need to translate this into a Makefile.

You might like to start with this:

.PHONY: all

all: reloader.exe block_finder.exe formatter.exe printdb.exe

MODULES = reloader block_finder formatter printdb linked_list bitcoin file_handler
SRCS = $(MODULES:%=%.c)

reloader.exe block_finder.exe formatter.exe printdb.exe: libbitcoin_manager.so

reloader.exe: reloader.o
block_finder.exe: block_finder.o
formatter.exe: formatter.o
printdb.exe: printdb.o

libbitcoin_manager.so: linked_list.o bitcoin.o file_handler.o
    gcc -shared -fPIC $^ -o $@

%.exe: %.o
    gcc $< -L. -lbitcoin_manager -o $@

%.o: %.c
    gcc -c $< -o $@

%.d: %.c
    gcc -MM -MT $@ -MT $*.o -MF $@ $<

include $(SRCS:%.c=%.d)

Because you don't have a loop in the diagram, you don't need a loop in the Makefile. Instead you put all dependent files on the left of a colon and the file they depend on on the right.

You might like to collect more "objects" in variables, for example the programs to build, the modules in the library, and so on.

I have also used a common pattern to generate the dependencies from the header files. The way shown is just one way to do it. It uses files with a ".d" extension, for "dependency." GCC has options to build these files, it scans the source and collects all included headers even if "stacked."

For example, "bitcoin.d" looks like this:

bitcoin.d bitcoin.o: bitcoin.c bitcoin.h linked_list.h definitions.h \
 file_handler.h

The re-generate the dependency file on changes in the sources it is also a target, not only the object file.


EDIT:

First, using directories makes Makefiles more difficult. I don't like such structures not only for that reason, but also because they separate header files and implementation files that clearly belong to each other.

Anyway, here is an enhanced Makefile:

.PHONY: all

SRCDIR = src
INCDIR = include
BLDDIR = build

APPS = reloader block_finder formatter printdb
MODULES = reloader block_finder formatter printdb linked_list bitcoin file_handler
LIBNAME = bitcoin_manager
LIBMODULES = linked_list bitcoin file_handler

VPATH = $(SRCDIR)

SRCS = $(MODULES:%=%.c)
LIB = $(LIBNAME:%=lib%.so)
#win LIB = $(LIBNAME:%=%.lib)
EXES = $(APPS:%=%.exe)

all: $(BLDDIR) $(EXES)

$(BLDDIR):
    mkdir $@

$(LIB): $(LIBMODULES:%=$(BLDDIR)/%.o)
    gcc -shared -fPIC $^ -o $@

$(EXES): $(LIB)

$(EXES): %.exe: $(BLDDIR)/%.o
    gcc $< -L. -l$(LIBNAME) -o $@

$(BLDDIR)/%.o: %.c
    gcc -I$(INCDIR) -c $< -o $@

$(SRCDIR)/%.d: %.c
    gcc -I$(INCDIR) -MM -MT $@ -MT $(BLDDIR)/$*.o -MF $@ $<

include $(SRCS:%.c=$(SRCDIR)/%.d)

It uses a lot more variables to simplify renaming and managing a growing library and application.

One important issue is the use of VPATH. This makes make search for sources in the list of paths assigned to it. Make sure you understand it thoroughly, search for articles and documentation. It is easy to use it wrong.

The pattern $(EXES): %.exe: $(BLDDIR)/%.o is a nice one. It consists of three parts, first a list of targets, second a generic pattern with a single target and its source. Here is means that for all executables each of them is built from its object file.

Now to your questions:

  1. Is answered by the new proposal. I didn't add the directory but use VPATH.

  2. Make stopped not because the exe-from-o pattern was wrong, but because it didn't find a way to build the object file needed. This is solved by the new proposal, too. To find out what happens if you delete these 4 recipes in the old proposal: you can experiment, so do it!

  3. The dot is, like user3629249 tried to say, the present working directory. You had it in your Makefile with 'pwd' and I replaced it. This is not special to make, it is common in all major operating systems, including Windows. You might know .. which designates the parent directory.

  4. When make starts it reads the Makefile or any given file. If this file contains include directives the files listed are checked if they need to be rebuild. make does this even if you call it with -n! After (re-)building all files to be included they are included finally. Now make has all recipes and continues with its "normal" work.

Upvotes: 1

Related Questions