Search code examples
makefile

How to generate a Makefile with source in sub-directories using just one makefile


I have source in a bunch of subdirectories like:

src/widgets/apple.cpp
src/widgets/knob.cpp
src/tests/blend.cpp
src/ui/flash.cpp

In the root of the project I want to generate a single Makefile using a rule like:

%.o: %.cpp
   $(CC) -c $<

build/test.exe: build/widgets/apple.o build/widgets/knob.o build/tests/blend.o src/ui/flash.o
   $(LD) build/widgets/apple.o .... build/ui/flash.o -o build/test.exe

When I try this it does not find a rule for build/widgets/apple.o. Can I change something so that the %.o: %.cpp is used when it needs to make build/widgets/apple.o ?


Solution

  • The reason is that your rule

    %.o: %.cpp
           ...
    

    expects the .cpp file to reside in the same directory as the .o your building. Since test.exe in your case depends on build/widgets/apple.o (etc), make is expecting apple.cpp to be build/widgets/apple.cpp.

    You can use VPATH to resolve this:

    VPATH = src/widgets
    
    BUILDDIR = build/widgets
    
    $(BUILDDIR)/%.o: %.cpp
          ...
    

    When attempting to build "build/widgets/apple.o", make will search for apple.cpp in VPATH. Note that the build rule has to use special variables in order to access the actual filename make finds:

    $(BUILDDIR)/%.o: %.cpp
            $(CC) $< -o $@
    

    Where "$<" expands to the path where make located the first dependency.

    Also note that this will build all the .o files in build/widgets. If you want to build the binaries in different directories, you can do something like

    build/widgets/%.o: %.cpp
            ....
    
    build/ui/%.o: %.cpp
            ....
    
    build/tests/%.o: %.cpp
            ....
    

    I would recommend that you use "canned command sequences" in order to avoid repeating the actual compiler build rule:

    define cc-command
    $(CC) $(CFLAGS) $< -o $@
    endef
    

    You can then have multiple rules like this:

    build1/foo.o build1/bar.o: %.o: %.cpp
        $(cc-command)
    
    build2/frotz.o build2/fie.o: %.o: %.cpp
        $(cc-command)