0

I am using this Makefile Tutorial for understanding how to use Makefiles. This Question might be a duplicate for this thread but I think I need more clarity here.

My Project structure:

--exercise_14/
    --> ex14.c
    --> ex14.h
    --> main.c
    --> Makefile

There is nothing complex about ex14.*, just simple header file with 3 function declarations (ex14.h) and their implementation (ex14.c) and main.c calls them.

My Makefile is as follows:

CC = gcc
CFLAGS = -Wall -g

DEPS = ex14.h

ODIR=obj
_OBJ=ex14.o main.o
OBJ=$(patsubst %,$(ODIR)/%,$(_OBJ))

all: ex14

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

ex14: $(OBJ)
    $(CC) $(CFLAGS) -o $@ $^

clean:
    rm -f ex14 $(ODIR)*.o
    rm -rf $(ODIR)

I am currently understanding how the patsubst in the file should work and everytime I run

 make clean all

I get:

gcc -Wall -g -c -o obj/ex14.o ex14.c
Assembler messages:
Fatal error: can't create obj/ex14.o: No such file or directory
Makefile:16: recipe for target 'obj/ex14.o' failed
make: *** [obj/ex14.o] Error 1

Which makes sense that there is no obj/ folder created and no ex14.o is found for further compilation. A way around is to use mkdir obj and then perform make but I want to avoid that.

Question

What lines should be added to let my Makefile make a folder as ODIR=obj and put all the object files within it?

Community
  • 1
  • 1
Shan-Desai
  • 3,101
  • 3
  • 46
  • 89
  • The simplest way is to add `mkdir -p $(ODIR)` as the first command in the `$(ODIR)/%.o` rule. This is crude but effective. If you want something more sophisticated, you could write a rule to create the directory, and make the directory a prerequisite of the `$(ODIR)/%.o` rule. – Beta Dec 26 '16 at 15:18
  • Or can I make this run for `all:ex14` where the first thing after `make all` is to create a directory and then proceed further? – Shan-Desai Dec 26 '16 at 15:21
  • 1
    No, the error happens in the course of `make all`; *after* that is too late. – Beta Dec 26 '16 at 15:27
  • @Beta Just wanted to be brave and tried it and you are correct. How can I make this a prerequisite as you mentioned before? – Shan-Desai Dec 26 '16 at 15:28
  • 1
    Duplicate of http://stackoverflow.com/questions/1950926/create-directories-using-make-file – eyalm Dec 26 '16 at 15:40
  • @eyalm thanks for the link – Shan-Desai Dec 26 '16 at 15:46

2 Answers2

1

The correct solution is to make your object files depend on their directory via order-only dependency:

Consider an example where your targets are to be placed in a separate directory, and that directory might not exist before make is run. In this situation, you want the directory to be created before any targets are placed into it but, because the timestamps on directories change whenever a file is added, removed, or renamed, we certainly don’t want to rebuild all the targets whenever the directory’s timestamp changes. One way to manage this is with order-only prerequisites: make the directory an order-only prerequisite on all the targets:

$(ODIR)/%.o: %.c $(DEPS) | $(ODIR)
    <same-original-recipe>

$(ODIR):
    mkdir -p $@
Maxim Egorushkin
  • 131,725
  • 17
  • 180
  • 271
0

As commented by others, but you can explicitly put a dependency to the $(ODIR) directory (it must be created before any dependent files ---compilation):

$(OBJS): $(ODIR)

$(ODIR):
    mkdir -p $@

This will ensure you have created the $(ODIR) directory before any dependent files (any *.o file) and that it will be created only once.

The final contents of your Makefile should be as this:

CC = gcc
CFLAGS = -Wall -g

DEPS = ex14.h

ODIR=obj
_OBJ=ex14.o main.o
OBJ=$(patsubst %,$(ODIR)/%,$(_OBJ))

all: ex14

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

ex14: $(OBJ)
    $(CC) $(CFLAGS) -o $@ $^

clean:
    rm -f ex14 $(ODIR)*.o
    rm -rf $(ODIR)

$(OBJ): $(ODIR)

$(ODIR):
    mkdir -p $@

EDIT 2

After posting the correct rule I found some errors in the $(patsubst) variable expansion, that made make to fail when not everything was erased. Following is a correct, optimized Makefile:

$ cat Makefile
CC = gcc
CFLAGS = -Wall -g

DEPS = ex14.h

ODIR=obj
OBJS=ex14.o main.o
POBJS=$(foreach i,$(OBJS),$(ODIR)/$(i))
LIBS= # for example: -lm for math library.

.PHONY: all ex14 clean

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

all: ex14

ex14: $(POBJS)
    $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(POBJS) $(LIBS)

clean:
    rm -rf $(ODIR)

$(POBJS): $(ODIR) $(DEPS)

$(ODIR):
    mkdir -p $@

putting the dependency on $(DEPS) in the

$(OBJS): $(ODIR) $(DEPS)

makes the automatic default dependency rule for .c -> .o files valid, and not needed anymore in the makefile. Also, I have used P prefix to mean pathed file in POBJS against OBJS (which is the plain list of object files) Also removing recursively the objs subdirectory makes unnecessary to remove the object files first, and then the subdirectory (only the last command is needed) Also, when linking, it is common use, to pass the compiler the $(LDFLAGS) for it to pass them to the linker. And finally, if you have some libraries to include at the end, just use a $(LIBS) library (not needed for your sample).

As stated in one of the comments, the compilation of just one source file makes the $(ODIR) directory to be touched, and all the other files to be out of date, and this will make all the files but the one just compiled to be compiled next time. There's no solution to this problem as there's a circular dependency on this (the objects depend on the directory to exist, and the directory is touched on each compilation which makes the dependency to be triggered again for all the files compiled before the last one) The only possible solution to this problem is to eliminate the dependency on the directory and construct it by hand before calling make.

Berkeley make (pmake or bmake, it depends) has a workaround to this problem, by allowing you to define a .BEGIN dependency, that is to be solved before any other work. You can create the directory there. I think GNU make doesn't have this feature:

.BEGIN:
    mkdir -p $(ODIR)

But this is out of scope for this question that is directed to GNU make.

Luis Colorado
  • 10,974
  • 1
  • 16
  • 31
  • I am not sure where will this statement go in my Makefile. Can you please help me out here? – Shan-Desai Jan 02 '17 at 15:37
  • it is a complete dependency rule with its creation script (must be tabbed, not indented with spaces) and you can put anywhere but before the default rule (after it, it is the `all:` rule in your makefile) Rules are normally separated by a blank line and have a `:` char separating the left part from the right part. – Luis Colorado Jan 03 '17 at 11:33
  • 1
    Sorry, my dependency was not completely correct. `$(OBJ)` object files (with correct path in `$(ODIR)`) depend on `$(ODIR)` to be created first. And then, `$(ODIR)` depends on nothing to be created, so the correct dependencies are now in the edited answer (it has been tested) – Luis Colorado Jan 03 '17 at 11:46
  • Thanks this is an awesome answer. – Shan-Desai Jan 03 '17 at 11:48
  • Use the order-only dependency for the directory. The directory timestamp is updated when files are added/removed, causing unnecessary recompilation of object files here. – Maxim Egorushkin Jan 03 '17 at 12:51
  • @LuisColorado there seems to be a glitch here. your `POBJS` does not shift all the `*.o` files in the `obj` folder. – Shan-Desai Jan 03 '17 at 13:10
  • @Shan-Desai, no... it doesn't.... as the folder is anyway recursively (and forcibly) removed after deleting the `*.o`, there's no need to remove them in the first original command, as they are going to be erased by the `rm -rf $(ODIR)` command. Don't tell me that that's a reason to downvote my answer... just try the whole Makefile. – Luis Colorado Jan 08 '17 at 19:26
  • No I actually didn't down vote any of the answers on the Thread. In fact yours is a well described answer – Shan-Desai Jan 08 '17 at 19:28
  • Someone else maybe. – Shan-Desai Jan 08 '17 at 19:33
  • @MaximEgorushkin, you are right on that, but there's an issue here that cannot be solved by make, as there is a circular dependency graph if you try to consider all the dependencies. By the tests I have done, on the `make`'s I have tried, the timestamps are obtained in the first pass of `make` execution, and just only once, so the circular dependency of touching the directory timestamp, so you don't get all the `*.o` files recompiled each time you touch a source. By the way, this is the only way you can assure the directory is created _before_ any file on it. – Luis Colorado Jan 19 '17 at 05:58