SlappyTheFish
SlappyTheFish

Reputation: 2384

C project: compiling multiple applications with shared sources

I am starting a project which has several applications. These applications share some common functionality in terms of source files. Each application should have its own make file but be able to link in these shared source files from a common location.

To illustrate this, here's my directory layout:

project/
    lib/
        shared1/
            shared1.c
            shared1.h
        shared2/
            shared2.c
            shared2.h
    app1/
        app1.c
        app1.h
        makefile
    app2/
        app2.c
        app2.h
        makefile

Currently one of the makefiles looks a bit like this:

CC=gcc
XXXCFLAGS=-Wall -Wextra -pedantic -I../../lib/shared1
CFLAGS=-I. $(XXXCFLAGS)
DEPS = app2.h ../../lib/shared1/shared1.h
OBJ = ${DEPS:.h=.o}
LIBS=-lpthread

%.o: %.c $(DEPS)
    $(CC) -c -o $@ $< $(CFLAGS)

app2: $(OBJ)
    $(CC) -o $@ $^ $(CFLAGS) $(LIBS)
  1. Is this roughly the right way to go about managing shared source, or is there a better way. Should they be separately compiled libraries? If so, how?

  2. Are relative paths like this ok?

  3. Should I be using autotools for something like this? After looking at examples I couldn't see how to link shared code or a library.

Any guidance is most appreciated.

Upvotes: 2

Views: 1560

Answers (2)

Beta
Beta

Reputation: 99094

This is a reasonable way to lay out your source tree, although you might consider separating source files and header files. If you want to build a library (if many apps use, say, shared1) you can write a rule for it, and put the rule in a makefile in shared1/. There are several ways to use such a rule, but don't worry about that until you're sure you want a library.

Paths are a necessary evil. They lock the makefile to a particular directory layout, but you can't entirely do without them. What you can do is keep them corralled:

SHARED1LIB = ../../lib/shared1
XXXCFLAGS=-Wall -Wextra -pedantic -I$(SHARED1DIR)
DEPS = app2.h $(SHARED1DIR)/shared1.h

Your use of dependencies is a little peculiar. For one thing, it's better to derive the list of objects from the list of sources, since you might have a source without a header or a header without a source:

SRCS = app2.c $(SHARED1DIR)/shared1.c
OBJ = ${SRCS:.c=.o}

Second, you make every object file depend on all headers, which is probably overkill. Suppose we can assume foo.o will depend on foo.h, and also that app2.o depends on shared1.h; then we can write a much better rule:

%.o: %.c %.h
    $(CC) -c -o $@ $< $(CFLAGS)

app2.o: $(SHARED1DIR)/shared1.h

If you have a lot of objects and this approach is too cumbersome, there is a very sophisticated method of handling these lists automatically, but it's an advanced technique you'd probably better not try just yet.

Finally, if you find that all of your app makefiles look much alike, you can take all of the common stuff and put it in a makefile in project/

SHARED1LIB = ../../lib/shared1
SHARED2LIB = ../../lib/shared2

CC=gcc
XXXCFLAGS=-Wall -Wextra -pedantic
CFLAGS=-I. $(XXXCFLAGS)
OBJ = ${SRCS:.c=.o}
LIBS=-lpthread

%.o: %.c %.h
    $(CC) -c -o $@ $< $(CFLAGS)

app%: $(OBJ)
    $(CC) -o $@ $^ $(CFLAGS) $(LIBS)

Then the app makefiles can look like this:

SRCS = app2.c $(SHARED1DIR)/shared1.c

app2:
include ../Makefile

XXXCFLAGS += -I$(SHARED1DIR)

app2.o: $(SHARED1DIR)/shared1.h

Upvotes: 2

Relative Paths are perfectly okay in a project... One of the easier ways to share code is simply exporting the headers and libraries into a common place, then linking the driver programs with them.

This approach is used in a lot of my personal projects, where sometimes I use CMake, and then link all the application targets against my own libraries.

Upvotes: 1

Related Questions