3

I have searched and tried a few examples to get a simple project done using non-recursive makefiles. In the past, I had used simple single directory codebase but now I am putting together an environment for more than one engineer :-)

+--app
|  +-- obj/
|  +-- Makefile
|  +-- first.c
|  +-- second.c
|
+--lib1
|  +-- obj/
|  +-- Makefile
|  +-- foo.c
|  +-- bar.c
|
+--lib2
|  +-- obj/
|  +-- Makefile
|  +-- sample.c

Nothing fancy- just two directories (maybe a third later) with library code and multiple applications all in the "app" directory. I want to keep the .o and .d files in a separate obj/ directory for cleanliness.

  1. I would like to be able to do a "make" in each of the sub-directories like lib1 and lib2 to verify the libraries. They will produce libabc.a and libxyz.a respectively.
  2. I wrote some simple Makefile but my rules don't work and I tried to understand the GNU make manual but am getting lost.

lib1/Makefile:

lib_src = foo.c bar.c
lib_obj = $(patsubst %.c,obj/%.o,$(lib_src))
libabc.a: $(lib_obj)
    @echo [Archive... $(@F)]
    @$(AR) -cr libabc.a $^
obj/%.c : %.c 
    $(CC) $(CFLAGS) -c -o $@ $<

app/Makefile:

ALL_APP = first second
% : %.c libabc.a libxyz.a
     $(CC) $(CLFAGS) $^ -o $@
include ../lib1/Makefile
include ../lib2/Makefile

Now, I have trouble defining the same target in each Makefile (obviously). Like I couldn't define a clean in lib1/Makefile and app/Makefile because lib1 is included there. Makes sense though I was hoping that I could do a make clean in lib1 alone.

now when I do a make in "app", there is no rule to make obj/foo.o. I guess because the paths are all bogus. "obj/" refers to the lib1/obj/ but as the Makefile got included, that is all lost.

What am I doing wrong and can I get my project built using really simple Makefiles like I have above. Most examples online are fairly complex since they try to achieve more (I believe).

Thanks in advance (and sorry for a topic that has been discussed many times). I would rather not learn automake and cmake right now, if I can avoid it. I am hoping that my project is simple enough to not warrant use of these powerful tools.

Best regards,

guraaf
  • 163
  • 4
  • 12
  • I don't think including the library makefiles in your app makefile is the correct solution here. Here's what I would do: Write a fully functioning standalone makefile for each directory. Then for your app, write a simple build script that calls make for each of your libraries and then finally calls make for your app. You can also have the build script copy the built libraries into your app directory. – Brandon Yates Oct 21 '14 at 22:54
  • 1
    @BrandonYates That's more-or-less recursive make but one step more removed because you aren't even letting make do the recursive building. – Etan Reisner Oct 21 '14 at 23:30
  • Thanks Brandon. I think calling makes is not a good idea and doing include of Makefile is a better approach. Peter Miller wrote a paper explaining why. Dependencies are harder to manage with calling each Makefile independently. – guraaf Oct 21 '14 at 23:32
  • BTW: the paper is called [Recursive Make Considered Harmful](http://aegis.sourceforge.net/auug97.pdf). – reinierpost Oct 22 '14 at 09:45
  • Building this kind of non-recursive makefile system (with ability to include independent makefiles) is not trivial. I have done my own implementation and it took me several years to polish it up. You can find it here https://github.com/igagis/prorab/blob/master/wiki/TutorialBasicConcepts.md – igagis Feb 02 '20 at 20:11

1 Answers1

3

Let's start with lib1/Makefile:

lib_src = foo.c bar.c
lib_obj = $(patsubst %.c,obj/%.o,$(lib_src))

libabc.a: $(lib_obj)
    @echo [Archive... $(@F)]
    @$(AR) -cr libabc.a $^

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

We introduce the variable HERE, and make a couple of small changes:

HERE := ../lib1

lib_src := foo.c bar.c
lib_obj := $(patsubst %.c,$(HERE)/obj/%.o,$(lib_src))

$(HERE)/libabc.a: $(lib_obj)
    @echo [Archive... $(@F)]
    @$(AR) -cr $@ $^

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

This makefile will still work just as before when invoked from within lib1/. But once we make corresponding changes to lib2/Makefile, we can change app/Makefile:

ALL_APP = first second
% : %.c ../lib1/libabc.a ../lib2/libxyz.a
     $(CC) $(CLFAGS) $^ -o $@

include ../lib1/Makefile
include ../lib2/Makefile

Now for the clean rule. We rename lib1/Makefile => lib1/lib1.mak and lib2/Makefile => lib2/lib2.mak, write a new lib1/Makefile:

include lib1.mak

clean:
    @rm -f lib*.a obj/*

do the same in lib2/, and modify app/Makefile:

...

include ../lib1/lib1.mak
include ../lib1/lib1.mak

clean:
    @rm -f $(ALL_APP)
    @$(MAKE) -C ../lib1 clean
    @$(MAKE) -C ../lib2 clean

(We could do it without recursion, but it would be more complicated, and there's really nothing wrong with using recursive Make in this way.)

Some further refinements are possible. For instance, it's not good to have paths hard-coded into lib1/lib1.mak and lib2/lib2.mak this way, and that can be fixed. But this is enough for one day.

Beta
  • 96,650
  • 16
  • 149
  • 150