0

I have the following makefile

# project name (generate executable with this name)
TARGET   = tp3

CC       = gcc -std=c99 -c
# compiling flags here
CFLAGS   = -Wall -I. -Werror-implicit-function-declaration

LINKER   = gcc -o
# linking flags here
LFLAGS   = -Wall

# debug flags here
DFLAGS   = -g -DDEBUG

SOURCES  := $(shell find . -type f -name '*.c')
INCLUDES  := $(shell find . -type f -name '*.h')
OBJECTS  := $(SOURCES:.c=.o)
rm       = rm -rf

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

obj: $(SOURCES) $(INCLUDES)
    @$(CC) $(CFLAGS) -DNDEBUG $(SOURCES)
    @echo "Compilation complete!"

#debug:
#   gcc $(DFLAGS) $(SOURCES) -o $(TARGET)   

dobj: $(SOURCES) $(INCLUDES)
    @$(CC) $(CFLAGS) $(DFLAGS) $(SOURCES)
    @echo "dlinking complete!"

debug: dobj
    @$(LINKER) $(TARGET) $(LFLAGS) $(DFLAGS) $(OBJECTS) -o $(TARGET)
    @echo "dcompilation complete!"

run:
    ./tp3

clean:
    @$(rm) $(TARGET) $(OBJECTS) *.dSYM
    @echo "Cleanup complete!"

Problem is: I have files inside the folder CMM/CMM and OBJECTS assumes the objects to also be in the CMM/CMM folder, but the compiler is putting them in the root folder. How can I either get the compiler to compile the .o files in CMM/CMM or tell the pattern replacer OBJECTS := $(SOURCES:.c=.o) that everything is in the root folder?

1
  • Usually this problem is solved by placing Makefile's in every subdirectory you want to put your sources and chaining them with recursive make invocation (something like $(MAKE) -C $(dir) rule). Certainly you could make appropriate changes to your central Makefile and define additional rules to compile certain source files into a chosen location. Commented Nov 27, 2014 at 1:35

3 Answers 3

3

Your real problem here is that you're not using the tools make provides you to simplify your task.
Also, your compiler is putting all the .o files in the root folder because you didn't tell him not to do so, or let make do that for you.

Here is the working Makefile:

EXE :=  tp3
SRC :=  $(shell find . -type f -name '*.c')
OBJ :=  $(SRC:.c=.o)

# Preprocessor flags here
CPPFLAGS    :=  -I.
# Compiler flags here
CFLAGS      :=  -std=c99 -Wall -Werror-implicit-function-declaration

.PHONY: all debug run clean

all: CPPFLAGS += -DNDEBUG
all: $(EXE)

debug: CPPFLAGS += -DDEBUG
debug: CFLAGS += -g
debug: $(EXE)

$(EXE): $(OBJ)
    @echo "Compilation complete!"
    $(CC) $(LDFLAGS) $^ $(LDLIBS) -o $@
    @echo "Linking complete!"

run:
    ./$(EXE)

clean:
    @$(RM) $(EXE) $(OBJ) *.dSYM
    @echo "Cleanup complete!"

Make has a set of built-in variables and rules that you should use to avoid losing time and encountering simple mistakes.

Note that this Makefile does not handle dependencies well, and simply adding the list of .h files to the Makefile won't be enough. You can workaround this by letting your compiler create the dependency files on the fly along with the compilation of .o files like this:

  • Build the list of .d filenames: DEP := $(OBJ:.o=.d),
  • Tell the compiler to generate the corresponding files, add the -MMD -MP switches to the CPPFLAGS built-in variable,
  • Include them in the Makefile so it will parse their content: -include $(DEP),
  • Don't forget to clean them up, add $(DEP) to the $(RM) command in the clean target.

Result:

EXE :=  tp3
SRC :=  $(shell find . -type f -name '*.c')
OBJ :=  $(SRC:.c=.o)
DEP :=  $(OBJ:.o=.d)

# Preprocessor flags here
CPPFLAGS    :=  -MMD -MP -I.
# Compiler flags here
CFLAGS      :=  -std=c99 -Wall -Werror-implicit-function-declaration

.PHONY: all debug run clean

all: CPPFLAGS += -DNDEBUG
all: $(EXE)

debug: CPPFLAGS += -DDEBUG
debug: CFLAGS += -g
debug: $(EXE)

$(EXE): $(OBJ)
    @echo "Compilation complete!"
    $(CC) $(LDFLAGS) $^ $(LDLIBS) -o $@
    @echo "Linking complete!"

-include $(DEP)

run:
    ./$(EXE)

clean:
    @$(RM) $(EXE) $(OBJ) $(DEP) *.dSYM
    @echo "Cleanup complete!"

If you have any question, go ahead.

Sign up to request clarification or add additional context in comments.

5 Comments

What is $^, .d files and -include? Also, why can I find absolutely no documentation on make language?
All the documentation you need is in the manual. $^ is an automatic variable that contains the list of prerequisites. .d files are special files generated by your compiler to help handling header file dependencies. The include command is like an #include in C, when encountering it, makes reads from each listed file in turn.
on my compiler (gcc for embedded linux), duplicate target names result in the compiler raising errors and stopping the compile.. this line: -include $(DEP) will cause the dependancy files to be rebuilt every time, (see my answer for how to handle that problem) I do like this line: CPPFLAGS := -MMD -MP -I. as then no call to 'sed' is necessary
regarding this line: DEP := $(OBJ:.o=.d) object files do not have #includes, so are an unreliable method, however source files do have $includes so the line should be: DEP := $(SRC:.c=.d)
Variables SRC, OBJ and DEP only contains filenames, nothing gmore, mothing less, files are not parsed or anything. In your case .o files are placed in the same directory than their sources, so using SRC or OBJ as a source for DEP won't change anything. But if you chose to place the .o files in another directory, using OBJ as a source would have permitted to place the .d files in that same directory.
2

If you use GNU make, then you may use patters rules or even static patterns:

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

if not, use old syntax

.c.o:

2 Comments

Or better, use the right variables and do not write any rule at all (use implicit rules).
Well, if one need to define compilation rules explicitly, it's necessary to have them :) But if not, there're plenty of implicit rules :)
-1
here are the contents of two makefile items.
 a top level makefile the drives the makefile.bot
the makefile.bot handles files/executables in other directorys
these makefiles also use recursion to produce the dependency lists
so only those header files that are actually include'd in the source
are listed as dependencies for that source.
Note: there are several common .c and .h files in the top level directory
      so they are compiled first, then referenced later
      when performing the link activity in each of the sub directories
Note: the 'AllDirectorys' is a list of the sub directories 
      where source code is to be compiled/linked in individual executables
Note: this is setup to run on Linux, and uses linux shell commands
      and certain utilities, like 'sed'

file: makefile.mak (the top level file)

    SHELL = /bin/sh


#  note: this makefile.mak needs to be run from the ./src directory
# of the GOT4 directory tree


SRC := $(wildcard *.c)
OBJ := $(SRC:.c=.o)
DEP := $(SRC:.c=.d)
INC := $(SRC:.c=.h)


MAKE    :=  /usr/bin/make

CC      :=  /usr/bin/gcc

CP      :=  cp

MV      :=  mv

LDFLAGS :=  -L/usr/local/lib -L/usr/lib -L/lib

DEBUG   :=  -ggdb3

CCFLAGS :=  $(DEBUG) -Wall -W

#CPPFLAGS += =MD

LIBS    :=  -lssl -ldl -lrt -lz -lc -lm -lcrypto



.PHONY: AllDirectories
# the following statement needs to be edited as
# subdirectories are added/deleted/re-named
AllDirectories := \
    CommandConfiguration \
    Communication \
    MainScheduler \
    RetrieveCDSLog \
    RetrieveEventRecorderLog \
    RetrieveGPS \
    QESRouter



#AllDirectories :=  \
#    MainScheduler \
#    Communication  \
#    RetrieveGPS   \
#    TestCommunicationDev



.PHONY: all
#all: $(OBJ) $(AllDirectories)
#   $(foreach d,$(AllDirectories), \
#    ( cd $d && $(MAKE) -f makefile.mak name=Tsk_$d all ); )

all: $(OBJ) $(AllDirectories)
    $(foreach d,$(AllDirectories), \
    ( cd $d && $(MAKE) -f ../makefile.bot name=Tsk_$d all ); )



#
# create dependancy files
#
%.d: %.c
    #
    # ========= START $< TO $@ =========
    $(CC) -M $(CPPFLAGS) $< > $@.$$$$;                      \
    sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@;     \
    rm -f $@.$$$$
    # ========= END $< TO $@ =========



#
# compile the .c file into .o files using the compiler flags
#
%.o: %.c %.d
    #
    # ========= START $< TO $@ =========
    $(CC) $(CCFLAGS) -c $< -o $@ -I.
    # ========= END $< TO $@ =========
    #



.PHONY: clean
#clean: $(AllDirectories)
#   # ========== start clean activities ==========
#   rm -f *.o
#   rm -f $(name).map
#   rm -f $(name)
#   rm -f *.d
#   $(foreach d,$(AllDirectories), \
#    ( cd $d && $(MAKE) -f makefile.mak clean ); )
#   # ========== end clean activities ==========

clean: $(AllDirectories)
    # ========== start clean activities ==========
    rm -f *.o
    rm -f $(name).map
    rm -f $(name)
    rm -f *.d
    rm -f ../bin/Tsk_*
    $(foreach d,$(AllDirectories), \
    ( cd $d && $(MAKE) -f ../makefile.bot name=Tsk_$d clean ); )
    # ========== end clean activities ==========



.PHONY: install
#install: $(AllDirectories)
#   # ========== start install activities ==========
#   $(foreach d,$(AllDirectories), \
#    ( cd $d && $(MAKE) -f makefile.mak clean ); )
#   # ========== end install activities ==========

install: $(AllDirectories)
    # ========== start install activities ==========
    $(foreach d,$(AllDirectories), \
    ( cd $d && $(MAKE) -f ../makefile.bot name=Tsk_$d install ); )
    # ========== end install activities ==========



# include the contents of all the .d files
# note: the .d files contain:
# <filename>.o:<filename>.c plus all the dependancies for that file
# I.E. the #include'd header files
# wrap with ifneg... so will not rebuild *.d files when goal is 'clean'
#
ifneq "$(MAKECMDGOALS)" "clean"
-include $(DEP)
endif


file: makefile.bot  which only occurs once, in the top level directory
along side the above listed makefile.mak

SHELL = /bin/sh


BINDIR  :=  /home/user/bin


.PHONY: all
all : $(BINDIR)/$(name) ../makefile.mak ../makefile.bot


#
# macro of all *.c files 
# (NOTE:
# (the following 'wildcard' will pick up ALL .c files
# (like FileHeader.c and FunctionHeader.c 
# (which should not be part of the build
# (so be sure no unwanted .c files in directory
# (or change the extension
#
SRC := $(wildcard *.c)
OBJ := $(SRC:.c=.o)
DEP := $(SRC:.c=.d)
INC := $(SRC:.c=.h)


COMMON_OBJ := $(wildcard ../*.o)
#COMMON_SRC := $(wildcard ../*.c)
#COMMON_OBJ := $(COMMON_SRC:.c=.o)
#COMMON_DEP := $(COMMON_SRC:.c=.d)
#COMMON_INC := $(COMMON_SRC:.c=.h)

MAKE    :=  /usr/bin/make

CC      :=  /usr/bin/gcc

CP      :=  cp -f

MV      := mv

LDFLAGS :=  -L/usr/local/lib

DEBUG   :=  -ggdb3

CCFLAGS :=  $(DEBUG) -Wall -W

#CPPFLAGS += =MD

#LIBS    :=  -lidn -lssl -ldl -lrt -lz -lc -lm
LIBS    :=   -lssl -ldl -lrt -lz -lc -lm -lcrypto



#
# link the .o files into the executable 
# using the linker flags
# -- explicit rule
#
$(name): $(OBJ) $(COMMON_OBJ) ../makefile.mak ../makefile.bot
    #
    # ======= $(name) Link Start =========
    $(CC) $(LDFLAGS) -o $@ $(OBJ) $(COMMON_OBJ) $(LIBS)
    # ======= $(name) Link Done ==========
    #



# note:
# using MV rather than CP results in all executables being re-made everytime
$(BINDIR)/$(name): $(name)
    #
    # ======= $(name) Copy Start =========
    $(CP) $(name) $(BINDIR)/
    # ======= $(name) Copy Done ==========
    #



#
#create dependancy files -- inference rule
#
%.d: %.c 
    # 
    # ========= START $< TO $@ =========
    $(CC) -M $(CPPFLAGS) $< > $@.$$$$;                      \
    sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@;     \
    rm -f $@.$$$$
    # ========= END $< TO $@ =========



# 
# compile the .c file into .o files using the compiler flags
# -- inference rule
#
%.o: %.c %.d 
    # 
    # ========= START $< TO $@ =========
    $(CC) $(CCFLAGS) -c $< -o $@ -I. 
    # ========= END $< TO $@ =========
    # 



.PHONY: clean
clean: 
    # ========== CLEANING UP ==========
    rm -f *.o
    rm -f $(name).map
    rm -f $(name)
    rm -f *.d
    # ========== DONE ==========



.PHONY: install
install: all

# include the contents of all the .d files
# note: the .d files contain:
# <filename>.o:<filename>.c plus all the dependancies for that .c file 
# I.E. the #include'd header files
# wrap with ifneg... so will not rebuild *.d files when goal is 'clean'
#
ifneq "$(MAKECMDGOALS)" "clean"
-include $(DEP)
endif

2 Comments

Why the -1? this fully answers the OPs question and gives the OP a very flexible method of performing a comprehensive make in almost any situation/environment with only minor changes to the makefiles
I downvoted for multiple reasons: 1) Your answer appears as a code-only answer, even if there are comments inside. 2) The code you gave here has not even been modified to comply with the OP's situation, it really appears like you just throws code at him without any considerations. 3) Some of the methods you use, like the dependency files generation, is seriously outdated and should not appear in nowadays makefiles IMO. 4) Your Makefile is huge because you do too much pointless redefining. 5) Your solution needs multiple files when only one is required ...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.