Search code examples
makefilebuildnon-recursive

Non Recursive Make


I have a project that I would like to convert from recursive to non recursive make. The structure looks like the following

+--app
|  +-- Makefile
+--lib1
|  +-- Makefile
|  +-- x.c
|  +-- y.c
|
+--lib2
|  +-- Makefile
|  +-- x.c

What I am trying to do is after a build is have a structure like this

+--app
|  +-- build/
|  |  +-- debug(or release or test)/
|  |  |  +-- lib1/
|  |  |  |  +-- *.o
|  |  |  |  +-- *.d
|  |  |  +-- lib2/
|  |  |  |  +-- *.o
|  |  |  |  +-- *.d
|  |
|  +-- target/
|  | +-- main.bin
|  |
|  +-- Makefile
|
+--lib1
|  +-- module.mk
|  +-- x.c
|  +-- y.c
|
+--lib2
|  +-- module.mk
|  +-- x.c

The main idea is that the build folder contains all object and dependency files and and target has the program file that should be loaded.

The issue that I am having is that make will never want to create this structure. When I define my rules make will only run implicit rules and not my defined rules.

I have read every resource on non-recursive make and right now it just has not clicked yet. Any help is much appreciated.


Solution

  • For build results to have the same directory structure like that of the sources, the pattern rules must be in the form of:

    ${obj_dir}/%.o : ${src_dir}/%.c
    

    Where % part includes all the subdirectories. The object file must also depend on its directory for make to build the directory first:

    .SECONDEXPANSION:
    ${obj_dir}/%.o : ${src_dir}/%.c | $$(dir $$@)
    ${obj_dir}/% : mkdir -p $@ 
    

    Depending on the target, one source file can be compiled with/without multi-threading, as position-independent/non-position-independent code, etc.. One way to cope with that is to have separate top-level object file directories (in addition to debug/release top-level directory), e.g.:

    ${obj_dir}/obj
    ${obj_dir}/obj-pic
    ${obj_dir}/obj-mt
    ${obj_dir}/obj-mt-pic
    

    Once you have these rules working correctly and try a parallel build you will notice that mkdir -p fails when two of them race to create /a/b/a and /a/b/b. The fix is:

    ${obj_dir}/% : while ! mkdir -p $@; do echo -n ""; done