Making Make Automatically Compile Objs from Headers into Archive then into Main Program

Viewed 75

I have a large project with a lot of headers and source files. What I want is the following but automatically

Makefile

INCLUDE = -Iinc
CFLAGS = -g
LIB = lib/foo_bar.a

all: obj/foo_print_hello.o\
    obj/foo_print_goodbye.o\
    obj/bar_print_goodbye.o\
    obj/bar_print_hello.o\
    obj/main.o\
    lib/foo_bar.a\
    bin/main\


obj/foo_print_hello.o: src/foo_print_hello.c inc/foo.h
    $(CC) $(INCLUDE) -c $(CFLAGS) -o  $@ $<

obj/foo_print_goodbye.o: src/foo_print_goodbye.c inc/foo.h
    $(CC) $(INCLUDE) -c $(CFLAGS) -o  $@ $<

obj/bar_print_goodbye.o: src/bar_print_goodbye.c inc/bar.h
    $(CC) $(INCLUDE) -c $(CFLAGS) -o  $@ $<

obj/bar_print_hello.o: src/bar_print_hello.c inc/bar.h
    $(CC) $(INCLUDE) -c $(CFLAGS) -o  $@ $<

obj/main.o: src/main.c inc/foo.h inc/bar.h
    $(CC) $(INCLUDE) -c $(CFLAGS) -o $@ $<
    
    
lib/foo_bar.a:  obj/bar_print_hello.o obj/bar_print_goodbye.o obj/foo_print_goodbye.o obj/foo_print_hello.o inc/bar.h inc/foo.h
    ar -crv  $@ $^ 


bin/main: obj/main.o 
    $(CC) -o $@ $< $(LIB)

The file architecture is as follows

.
├── bin
│   └── main
├── inc
│   ├── bar.h
│   └── foo.h
├── lib
│   └── foo_bar.a
├── makefile
├── obj
│   ├── bar_print_goodbye.o
│   ├── bar_print_hello.o
│   ├── foo_print_goodbye.o
│   ├── foo_print_hello.o
│   └── main.o
└── src
    ├── bar_print_goodbye.c
    ├── bar_print_hello.c
    ├── foo_print_goodbye.c
    ├── foo_print_hello.c
    └── main.c

I want this exact compiling structure where it compiles them all into object files then the library function then the compiling with main. I've seen lots of templates on GNU and around the internet but nothing quite worked. How would I make my makefile automatically make the obj (placing them in the obj dir) then compiling into the archive?

What compiles

cc -Iinc -c -g -o  obj/foo_print_hello.o src/foo_print_hello.c
cc -Iinc -c -g -o  obj/foo_print_goodbye.o src/foo_print_goodbye.c
cc -Iinc -c -g -o  obj/bar_print_goodbye.o src/bar_print_goodbye.c
cc -Iinc -c -g -o  obj/bar_print_hello.o src/bar_print_hello.c
cc -Iinc -c -g -o obj/main.o src/main.c
ar -crv  lib/foo_bar.a obj/bar_print_hello.o obj/bar_print_goodbye.o obj/foo_print_goodbye.o obj/foo_print_hello.o inc/bar.h inc/foo.h 
r - obj/bar_print_hello.o
r - obj/bar_print_goodbye.o
r - obj/foo_print_goodbye.o
r - obj/foo_print_hello.o
r - inc/bar.h
r - inc/foo.h
cc -o bin/main obj/main.o lib/foo_bar.a

Main's output

Hello
Goodbye
Hello
Goodbye

Repo for code:

git@github.com:tlplayer/dummy_makefile_question.git

Follow-Up

So I tried the following makefile

SRCS := $(wildcard src/*.c)
OBJS := $(patsubt src/%.c, obj/%.o, $(SRCS))
LIB := lib/foo_bar.a

all: bin/main

obj/%.o: src/%.c
    $(CC) $(INCLUDE) -c $(CFLAGS) -o  $@ $<

$(LIB): obj/%.o
    ar -crv  $@ $^ 

bin/main: obj/main.o $(LIB) 
    $(CC) -o $@ $< $(LIB)

The problem is it doesn't know what I want it to. with or without adding the headers to the makefile (foo.h bar.h in the dependencies)

make: *** No rule to make target `obj/%.o', needed by `lib/foo_bar.a'.  Stop.

Follow Up 2

Current makefile

INCLUDE = -Iinc
CFLAGS = -g
LIB = lib/foo_bar.a

SRCS := $(wildcard src/*.c)
OBJS := $(patsubt src/%.c, obj/%.o, $(SRCS))
OBJS := $(filter-out obj/main.o, $(OBJS))

all: bin/main\

obj/%.o: src/%.c 
    $(CC) $(INCLUDE) -c $(CFLAGS) -o  $@ $<
 
$(LIB): $(OBJS)
    ar -crv $@ $^
 
bin/main: obj/main.o $(LIB)
    $(CC) -o $@ $^

What the error is

cc -Iinc -c -g -o  obj/main.o src/main.c
ar -crv lib/foo_bar.a 
cc -o bin/main obj/main.o lib/foo_bar.a
obj/main.o: In function `main':
test/src/main.c:6: undefined reference to `foo_print_hello'
test/src/main.c:7: undefined reference to `foo_print_goodbye'
test/src/main.c:8: undefined reference to `bar_print_hello'
test/src/main.c:9: undefined reference to `bar_print_goodbye'
collect2: error: ld returned 1 exit status
make: *** [bin/main] Error 1

For some reason it compiles main.o then the archive file. But the archive is missing all object files. So why doesn't it include the objects?

Solution

Thanks MadScientist and Topology

INCLUDE = -Iinc
CFLAGS = -g
LIB = lib/foo_bar.a

SRCS := $(wildcard src/*.c)
OBJS := $(patsubst src/%.c, obj/%.o, $(SRCS))
OBJS := $(filter-out obj/main.o, $(OBJS))

all:bin/main\

clean:
    rm -f obj/* bin/* lib/*

obj/%.o: src/%.c
    $(CC) $(INCLUDE) -c $(CFLAGS) -o  $@ $<

lib/foo_bar.a: $(OBJS)
    ar -crv $@ $^
 
bin/main: obj/main.o $(LIB)
    $(CC) -o $@ $^

1 Answers

Simply add lib/foo_bar.a to the dependencies of the bin/main rule.

bin/main: obj/main.o $(LIB) 
    $(CC) -o $@ $< $(LIB)

Also, you can further simplify your Makefile by using patterns and functions.

So the whole makefile can be turned into

INCLUDE = -Iinc
CFLAGS = -g
LIB = lib/foo_bar.a

SRCS := $(wildcard src/*.c)
SRCS := $(filter-out src/main.c $(SRCS))
LIBOBJS := $(patsubst src/%.c, obj/%.o, $(SRCS))

all: bin/main

obj/%.o: src/%.c
        $(CC) $(INCLUDE) -c $(CFLAGS) -o  $@ $<
 
$(LIB): $(OBJS)
    ar -crv $@ $^

bin/main: obj/main.o $(LIB)
    $(CC) -o $@ $^
Related