Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I create a Makefile for C projects with SRC, OBJ, and BIN subdirectories?

Tags:

c

makefile

A few months ago, I came up with the following generic Makefile for school assignments:

# ------------------------------------------------ # Generic Makefile # # Author: [email protected] # Date  : 2010-11-05 # # Changelog : #   0.01 - first version # ------------------------------------------------  # project name (generate executable with this name) TARGET   = projectname  CC       = gcc -std=c99 -c # compiling flags here CFLAGS   = -Wall -I.  LINKER   = gcc -o # linking flags here LFLAGS   = -Wall  SOURCES  := $(wildcard *.c) INCLUDES := $(wildcard *.h) OBJECTS  := $(SOURCES:.c=*.o) rm       = rm -f  $(TARGET): obj     @$(LINKER) $(TARGET) $(LFLAGS) $(OBJECTS)     @echo "Linking complete!"  obj: $(SOURCES) $(INCLUDES)     @$(CC) $(CFLAGS) $(SOURCES)     @echo "Compilation complete!"  clean:     @$(rm) $(TARGET) $(OBJECTS)     @echo "Cleanup complete!" 

This will basically compile every .c and .h file to generate .o files and the executable projectname all in the same folder.

Now, I'd like to push this a little. How can I write a Makefile to compile a C project with the following directory structure?

 ./  ./Makefile  ./src/*.c;*.h  ./obj/*.o  ./bin/<executable> 

In other words, I'd like to have a Makefile that compiles C sources from ./src/ into ./obj/ and then link everything to create the executable in ./bin/.

I've tried to read different Makefiles, but I simply can't make them work for the project structure above; instead, the project fails to compile with all sorts of errors. Sure, I could use full blown IDE (Monodevelop, Anjuta, etc.), but I honestly prefer to stick with gEdit and the good ol' terminal.

Is there a guru who can give me a working solution, or clear information about how this can be done? Thank you!

** UPDATE (v4) **

The final solution :

# ------------------------------------------------ # Generic Makefile # # Author: [email protected] # Date  : 2011-08-10 # # Changelog : #   2010-11-05 - first version #   2011-08-10 - added structure : sources, objects, binaries #                thanks to http://stackoverflow.com/users/128940/beta #   2017-04-24 - changed order of linker params # ------------------------------------------------  # project name (generate executable with this name) TARGET   = projectname  CC       = gcc # compiling flags here CFLAGS   = -std=c99 -Wall -I.  LINKER   = gcc # linking flags here LFLAGS   = -Wall -I. -lm  # change these to proper directories where each file should be SRCDIR   = src OBJDIR   = obj BINDIR   = bin  SOURCES  := $(wildcard $(SRCDIR)/*.c) INCLUDES := $(wildcard $(SRCDIR)/*.h) OBJECTS  := $(SOURCES:$(SRCDIR)/%.c=$(OBJDIR)/%.o) rm       = rm -f   $(BINDIR)/$(TARGET): $(OBJECTS)     @$(LINKER) $(OBJECTS) $(LFLAGS) -o $@     @echo "Linking complete!"  $(OBJECTS): $(OBJDIR)/%.o : $(SRCDIR)/%.c     @$(CC) $(CFLAGS) -c $< -o $@     @echo "Compiled "$<" successfully!"  .PHONY: clean clean:     @$(rm) $(OBJECTS)     @echo "Cleanup complete!"  .PHONY: remove remove: clean     @$(rm) $(BINDIR)/$(TARGET)     @echo "Executable removed!" 
like image 441
Yanick Rochon Avatar asked Aug 10 '11 00:08

Yanick Rochon


People also ask

How do you make a makefile in C project?

Makefile is a set of commands (similar to terminal commands) with variable names and targets to create object file and to remove them. In a single make file we can create multiple targets to compile and to remove object, binary files. You can compile your project (program) any number of times by using Makefile.

Should makefile be in src directory?

Since these files normally appear in the source directory, they should always appear in the source directory, not in the build directory. So Makefile rules to update them should put the updated files in the source directory.

What is Obj in makefile?

The third variable used in this file is OBJS. In this makefile, this variable specifies all the object files required to construct the main program.

What is $@ makefile?

The variable $@ represents the name of the target and $< represents the first prerequisite required to create the output file.


1 Answers

First, your $(OBJECTS) rule is problematic, because:

  1. it's kind of indiscriminate, making all sources prerequisites of every object,
  2. it often uses the wrong source (as you discovered with file1.o and file2.o)
  3. it tries to build executables instead of stopping at objects, and
  4. the name of the target (foo.o) is not what the rule will actually produce (obj/foo.o).

I suggest the following:

OBJECTS  := $(SOURCES:$(SRCDIR)/%.c=$(OBJDIR)/%.o)  $(OBJECTS): $(OBJDIR)/%.o : $(SRCDIR)/%.c     $(CC) $(CFLAGS) -c $< -o $@     @echo "Compiled "$<" successfully!" 

The $(TARGET) rule has the same problem that the target name does not actually describe what the rule builds. For that reason, if you type make several times, Make will rebuild the target each time, even though there is no reason to. A small change fixes that:

$(BINDIR)/$(TARGET): $(OBJECTS)     $(LINKER) $@ $(LFLAGS) $(OBJECTS)     @echo "Linking complete!" 

Once that's all in order, you might consider more sophisticated dependency handling; if you modify one of the header files, this makefile will not know which objects/executables must be rebuilt. But that can wait for another day.

EDIT:
Sorry, I omitted part of the $(OBJECTS) rule above; I've corrected it. (I wish I could use "strike" inside a code sample.)

like image 113
Beta Avatar answered Oct 08 '22 05:10

Beta