HiveBrain v1.2.0
Get Started
← Back to all entries
patternModerate

Makefile that places object files into an alternate directory (bin/)

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
placesdirectorymakefilebinintofilesthatobjectalternate

Problem

I'm trying to learn how to use makefiles, and this is my attempt at making a makefile that compiles source files in a source directory (src/) into object files that are created in a bin directory.

##########################################
#           Editable options             #
##########################################

# Compiler options
CC=g++
CFLAGS=-c -Wall
LDFLAGS=
EXECUTABLE_NAME=testes

# Folders
SRC=src
BIN=bin
OBJ=$(BIN)/obj

# Files
SOURCE_FILES=\
    main.cpp \
    test.cpp

##########################################
#    Don't touch anything below this     #
##########################################
OBJECT_FILES=$(addprefix $(OBJ)/, $(SOURCE_FILES:.cpp=.o))

build: create_directories create_executable
    @echo "Build successful!"

create_executable: create_objects
    @$(CC) $(LDFLAGS) $(OBJECT_FILES) -o $(BIN)/$(EXECUTABLE_NAME)
    @echo "Created executable."

create_objects: $(SOURCE_FILES)
    @echo "Created objects."

create_directories: 
    @mkdir -p $(OBJ)

%.cpp:
    @echo "Compiling "$@
    @$(CC) $(LDFLAGS) -c $(SRC)/$@ -o $(OBJ)/$(patsubst %.cpp,%.o,$@)

clean:
    @rm -r -f $(BIN)


This makefile works, I just want to make sure I'm doing everything correctly. Any changes you guys would suggest?

Solution

You are not using the make system properly. As a result, it will always relink the executable, even when everything is already up to date. On the other hand, it might fail to recompile some source files into object files.

When you run make

-
It will try to create the build target. No file named build exists, so…

-
It will try to create the prerequisites of build, namely create_directories and create_executable, neither of which are names of existing files.

  • In an attempt to create a file called create_directories, it runs mkdir -p $(OBJ).



-
For create_executable, it needs to rebuild create_objects.

  • For create_objects, it just verifies that all of the source files are present.



  • For any of the $(SOURCE_FILES) that is missing, it "creates" it using the %.cpp: rule, which actually tries to compile the missing .cpp into a .o.



  • Once all of the $(SOURCE_FILES) are present, it echoes "Created objects.". That's a false claim due to the error above.



Having thus falsely satisfied the prerequisites for create_executable, it proceeds to attempt to link the executable, even if any of the $(OBJECT_FILES) is missing.

The main remedy is to specify real targets and real dependencies, rather than phony labels like create_executable.

Here is how I would write it. Other than the major problems cited above, I have also noted many minor issues in the comments.

#########################################################
# Replacing everything that you told me not to touch... #
#########################################################
EXECUTABLE_FILES = $(EXECUTABLE_NAME:%=$(BIN)/%)
OBJECT_FILES     = $(SOURCE_FILES:%.cpp=$(OBJ)/%.o)
# ^^^ A more succinct expression for $(OBJECT_FILES), using
#     http://www.gnu.org/software/make/manual/make.html#Substitution-Refs

build: $(EXECUTABLE_FILES)

clean:
    rm -r -f $(BIN)
    @# ^^^ I don't recommend suppressing the echoing of the command using @

# http://www.gnu.org/software/make/manual/make.html#Phony-Targets
.PHONY: build clean

$(EXECUTABLE_FILES): $(OBJECT_FILES)
    @$(CC) $(LDFLAGS) -o $@ $^
    @# ^^^ http://www.gnu.org/software/make/manual/make.html#Automatic-Variables
    @echo "Build successful!"

# http://www.gnu.org/software/make/manual/make.html#Static-Pattern
$(OBJECT_FILES): $(OBJ)/%.o: %.cpp
    @echo Compiling %%CODEBLOCK_0%%lt;
    @# ^^^ Your terminology is weird: you "compile a .cpp file" to create a .o file.
    @mkdir -p $(@D)
    @# ^^^ http://www.gnu.org/software/make/manual/make.html#index-_0024_0028_0040D_0029
    @$(CC) $(CFLAGS) -o $@ %%CODEBLOCK_0%%lt;
    @# ^^^ Use $(CFLAGS), not $(LDFLAGS), when compiling.

Code Snippets

#########################################################
# Replacing everything that you told me not to touch... #
#########################################################
EXECUTABLE_FILES = $(EXECUTABLE_NAME:%=$(BIN)/%)
OBJECT_FILES     = $(SOURCE_FILES:%.cpp=$(OBJ)/%.o)
# ^^^ A more succinct expression for $(OBJECT_FILES), using
#     http://www.gnu.org/software/make/manual/make.html#Substitution-Refs

build: $(EXECUTABLE_FILES)

clean:
    rm -r -f $(BIN)
    @# ^^^ I don't recommend suppressing the echoing of the command using @

# http://www.gnu.org/software/make/manual/make.html#Phony-Targets
.PHONY: build clean

$(EXECUTABLE_FILES): $(OBJECT_FILES)
    @$(CC) $(LDFLAGS) -o $@ $^
    @# ^^^ http://www.gnu.org/software/make/manual/make.html#Automatic-Variables
    @echo "Build successful!"

# http://www.gnu.org/software/make/manual/make.html#Static-Pattern
$(OBJECT_FILES): $(OBJ)/%.o: %.cpp
    @echo Compiling $<
    @# ^^^ Your terminology is weird: you "compile a .cpp file" to create a .o file.
    @mkdir -p $(@D)
    @# ^^^ http://www.gnu.org/software/make/manual/make.html#index-_0024_0028_0040D_0029
    @$(CC) $(CFLAGS) -o $@ $<
    @# ^^^ Use $(CFLAGS), not $(LDFLAGS), when compiling.

Context

StackExchange Code Review Q#74136, answer score: 16

Revisions (0)

No revisions yet.