0

i have a C project , all source file (C and H files)in src directory , have lots of subdirectories

now I want to

1 copy all h files to .\header, without folder struct
2 complier all c files to .\obj, without folder stuct
3 myproject.exe in .\bin

D:\myproject
+---bin
+---header
+---obj
\---src
    |   main.c
    |   main.h
    |
    +---sub1
    |   |   1.c
    |   |   1.h
    |   |
    |   \---sub11
    |       |   11.c
    |       |   11.h
    |       |
    |       \---sub111
    |               111.c
    |               111.h
    |
    \---sub2
        |   2.c
        |   2.h
        |
        \---sub22
            |   22.c
            |   22.h
            |
            \---sub221
                    221.c
                    221.h

the expected output as follows:

D:.
+---bin
|       myproject.exe
|
+---header
|       1.h
|       11.h
|       111.h
|       2.h
|       22.h
|       221.h
|       main.h
|
+---obj
|       1.o
|       11.o
|       111.o
|       2.o
|       22.o
|       221.o
|       main.o
|
\---src
    |   main.c
    |
    +---sub1
    |   |   1.c
    |   |   1.h
    |   |
    |   \---sub11
    |       |   11.c
    |       |   11.h
    |       |
    |       \---sub111
    |               111.c
    |               111.h
    |
    \---sub2
        |   2.c
        |   2.h
        |
        \---sub22
            |   22.c
            |   22.h
            |
            \---sub221
                    221.c
                    221.h

the follows posts give a good reference , but this post can not support if the source in multiple directory , all obj files is in the same directory as c file Makefile : Automatically compile all c files, keeping .o files in separate folder
How can I create a Makefile for C projects with SRC, OBJ, and BIN subdirectories?

can any give some example makefile?

chenx319
  • 35
  • 5
  • 1
    *Why* do you want to copy all of the source files into `src/`, and all of the header files into `header`? – Beta Mar 20 '22 at 12:09
  • @Beta all the source file exist in ./src , copy all header files to .\header can simply the compiler option -I – chenx319 Mar 20 '22 at 14:05
  • Sorry, my mistake; somehow I got the impression that you wanted to copy source files also. But simplifying the compiler option by copying the header files incurs a cost: the makefile will be more complicated. – Beta Mar 20 '22 at 14:14
  • I'm not a big fan of separate directories for object files and binaries, but copying around headers for compilation is just madness. If you want them all in one flat directory then put them there in the first place! On the other hand, if it's important to you to keep them together with the sources, then writing the `-I` options you need is the simplest alternative for handling that. – John Bollinger Mar 20 '22 at 14:21
  • Consider also reducing the height of your source tree. I'm not saying it should be completely flat, four levels (not even counting the project root) is a bit excessive. – John Bollinger Mar 20 '22 at 14:28
  • @JohnBollinger I AM a big fan of that, simply because in embedded engineering there is no single object file truth out there, due to a multitude of shipped binaries. Out-of-tree build should be the norm, not the exception, as the reverse, a build system which introduced the provisioning for that only in hindsight, never really does too well IMHO. – Vroomfondel Mar 21 '22 at 13:12
  • @Vroomfondel, designated object and binary directories does not provide out-of-source / out-of-tree building, at least not directly, and it therefore does not address the issue with building binaries for multiple targets from the same source tree. True, `VPATH`-based, out-of-source building is easier to set up and maintain, and addresses the multiple-target problem better. – John Bollinger Mar 21 '22 at 13:43
  • @JohnBollinger sorry, I read that as "object files not separate to their source" - I guess SO comments is not the place to discuss this. – Vroomfondel Mar 21 '22 at 14:35
  • I have another consideration for copy all header file to a specific folder : The maximum length of the string that you can use at the command prompt is 8191 characters. in a real project , there are thousands of packages(folders). then that's of -i will exceed 8191 – chenx319 Mar 29 '22 at 09:19

2 Answers2

1

First we construct a list of the sources in the tree:

SRCDIR := src
SOURCES := $(shell find $(SRCDIR) -name "*.c")

Then use that to construct a list of the object files we want:

OBJDIR := obj
OBJECTS := $(patsubst %.c,$(OBJDIR)/%.o,$(notdir $(SOURCES)))

If all the source files were in the working directory, we could use a simple static pattern rule:

$(OBJECTS): $(OBJDIR)/%.o: %.c
    blah blah building $@ from $<

And to get that to work when the sources are in different directories, all we need is the vpath directive:

SRCDIRS := $(dir $(SOURCES))
vpath %.c $(SRCDIRS)

Now for the headers. To steer the compiler toward the directories containing the headers, we could construct a string of -I flags in one line or we could copy all of the headers into header/ as follows.

To keep all of the headers up to date, and not copy them unnecessarily, we must treat them as targets. First we make a list of them, as we did with the objects:

HEADERS := $(shell find $(SRCDIR) -name "*.h")
HEADERDIR := header
HEADERTARGS := $(addprefix $(HEADERDIR)/,$(notdir $(HEADERS)))

Then we write a static pattern rule, just as we did for the objects:

$(HEADERTARGS): $(HEADERDIR)/%.h: %.h
    cp $< $@

vpath %.h $(SRCDIRS)

And finally add the headers as prerequisites of the objects:

$(OBJECTS): $(OBJDIR)/%.o: %.c $(HEADERTARGS)
    ...

This is slightly inefficient, as it will rebuild all objects if even one header has changed, but to correct that shortcoming would require a more complex makefile.

Beta
  • 96,650
  • 16
  • 149
  • 150
  • Thanks you very much , I discard the request that the copy head file . then your solution can satisfy my request, – chenx319 Mar 21 '22 at 06:47
0

You want to put a Makefile in each subdirectory where you want to compile some source. And put a Makefile in your project root directory. In your root Makefile, do this:

ALL : make1 make2 ... maken mv_obj

make1 :; make -C src/sub1
...
mv_obj :; mv `find . -name "*.o"` obj/

Alternatively, you can specify where to save your .o file in each Makefile in your subdirectory. E.g.

gcc -o ../../obj/foo.o 1.c
yzhang
  • 122
  • 3
  • in our team , each sub have a owner who only focus on C and don't want to maintain the makefile . only one person (it is me ) maintain the makfile – chenx319 Mar 21 '22 at 07:16