I want to compile my source code and place object files into release and debug folders using make. Is it possible to do it without duplicating rules?
Here is what I tried:
SRCS = a.c b.c c.c
OBJS = $(patsubst %.c,$(BUILD_TYPE)/%.o,$(SRCS))
TARGET = $(BUILD_TYPE)/main
.PHONY: all
all: release
.PHONY: debug
debug: BUILD_TYPE = debug
debug: CFLAGS = -g
debug: $(TARGET)
.PHONY: release
release: BUILD_TYPE = release
release: CFLAGS = -O3
release: $(TARGET)
$(TARGET): $(OBJS)
$(CC) -o $# $(OBJS)
$(BUILD_TYPE)/%.o: %.c
$(CC) $(CFLAGS) -c $< -o $#
Edit:
I actually did not want to create the different "main" in the same folder. I wanted them in separate folders. My bad. I changed the example above.
Looks like that there is not a clean way to do this. I need to write multiple rules.
You seem to want this makefile to be able to build two different versions of main, and put them in the same place. The trouble is that ordinarily if Make sees a main, it concludes that main need not be rebuilt, but you might want it rebuilt if you want the other version. Unless you want a mechanism to allow Make to remember which version the extant main is, you must suffer the inefficiency of rebuilding it every time.
First we change the object rule:
release/%.o debug/%.o: %.c
$(CC) $(CFLAGS) -c $< -o $#
Then we make lists of the objects:
debug_OBJS=$(patsubst %.c,debug/%.o,$(SRCS))
release_OBJS=$(patsubst %.c,release/%.o,$(SRCS))
Then the main targets:
.PHONY: debug release
debug: CFLAGS = -g
debug: $(debug_OBJS)
release: CFLAGS = -O3
release: $(release_OBJS)
debug release:
$(CC) -o main $^
Yes, there is still a little bit of redundancy. Yes, it is possible to squeeze it out with some clever coding. But with only two variants, the simple approach is much easier to read and maintain.
Related
Considering a Makefile similar to the following:
CC = gcc
CXX = g++
CXXFLAGS += -std=c++11
SRCDIR = .
OBJECTS = \
file1.o \
file2.o \
file3.o
THREADS = 1
all: release
release: CXXFLAGS += -DNUM_THREADS=$(THREADS)
release: main
debug: CXXFLAGS += -g -DDEBUG -DVERBOSENESS=3 -DNUM_THREADS=$(THREADS)
debug: main
# Build main executable...
main: $(OBJECTS)
$(CXX) $(CXXFLAGS) -o $# $^
# Compile all object files...
file1.o: $(SRCDIR)/file1.cpp
$(CXX) $(CXXFLAGS) -c $^
file2.o: $(SRCDIR)/file2.cpp
$(CXX) $(CXXFLAGS) -c $^
file3.o: $(SRCDIR)/file3.cpp
$(CXX) $(CXXFLAGS) -c $^
This works properly only for the first make: using other words, object files are not recompiled when I launch make with a different target respect to the previous one.
That said, how could I make the object files recompile if I change between debug or release target?
If you want to force recompilation strictly based on one label, say "Debug" vs. "Release", then you can do it by writing and depending on appropriate timestamp files, like so:
TYPE = Debug
#
# ... conditional settings based on $(TYPE) ...
#
OBJS = prog.o
all: test
prog: $(OBJS)
$(CC) -o $# $(OBJS)
$(OBJS): $(TYPE)-mode-stamp
$(TYPE)-mode-stamp: last-mode-stamp
touch $#
touch -r $# last-mode-stamp
last-mode-stamp:
touch $#
clean:
#rm *-mode-stamp $(OBJS) prog
That supposes you select the build type by setting variable $(TYPE), possibly via the command line. All the object files have $(TYPE)-mode-stamp as a prerequisite, so if that file is out of date then all of them will be rebuilt, along with anything that has any of them as a prerequisite. $(TYPE)-mode-stamp itself has last-mode-stamp as a prerequisite, so the former is updated if it is older than the latter, or if the latter itself is out of date. The recipe for $(TYPE)-mode-stamp sets both files timestamps to the current time, so that
$(TYPE)-mode-stamp is no longer out of date with respect to last-mode-stamp, and
last-mode-stamp is newer than any OtherType-mode-stamp that may be present.
A rule without any prerequisites (but with a recipe) creates last-mode-stamp if it does not initially exist.
Note well that this is altogether different from and orthogonal to monitoring whether any build tools or flags change.
I'm trying to write a makefile which supports release mode and debug mode, while in debug mode, I want to link an extra object debug.o to override functions for debugging.
For example:
CFLAGS = -Wall -I$(INCPATH)
INCPATH = include
TARGET = foo
OBJ = foo.o bar.o
# release mode
all: build run clear
# debug mode
debug: CFLAGS += -g -DDEBUG
debug: OBJ += debug.o
debug: build gdb-run clear
# link objects
build: $(OBJ)
gcc $(CFLAGS) -o $(TARGET) $(OBJ)
# compile source code
%.o: %.c $(INCPATH)/*.h
gcc $(CFLAGS) -c $# $<
# default run mode
run:
./$(TARGET)
# debug run mode
gdb-run:
gdb --args $(TARGET)
clear:
rm -f $(OBJ) $(TARGET)
I expected it expand $(OBJ) to foo.o bar.o debug.o when I call make debug, but instead it only expands to foo.o bar.o, because targets are expanded immediately when parsed.
I've tried using .SECONDEXPANSION:, but couldn't work it out.
And I've also tried $(eval OBJ += debug.o), but that resulted in expanding $(OBJ) to foo.o bar.o debug.o even while running make all.
Is this possible, or should I just work around?
edit: fixed a typo, thanks to #matt
I use the GNU make "conditional" mechanism with a make variable named MODE for this. In your case, what about
MODE = RELEASE
OBJ = foo.o bar.o
ifeq ($(MODE),DEBUG)
OBJ += debug.o
endif
[...]
Then build with either
make # a MODE=RELEASE build by default
or
make MODE=DEBUG
I expected it expand $(OBJ) to foo.o bar.o debug.o
It does this inside the recipe, but not in the prerequisite list.
6.11 Target-specific Variable Values
As with automatic variables, these values are only available within the context of a target’s recipe (and in other target-specific assignments).
So you have to stick with conditionals to achieve your goal.
BTW. CFLAGS = -Wall -I(INCPATH) is a typo. %.o: %.c $(INCPATH)/*.h is plainly wrong - use $(wildcard ...) when really needed. Also change all occurences of build to $(TARGET), as it's really foo what you're building. Then revise your makefile - it's probably not a good idea to clean everything after every run.
I have the following makefile but it just executes the 1st command where it builds me the .o files and not the .so files. What am I doing wrong?
Thanks,
SHELL = /bin/sh
CC = gcc
CFLAGS = -g -Wall
LDFLAGS = -shared
TARGET = Stepper.so
SOURCES = $(shell echo ./*.c)
HEADERS = $(shell echo ./*.h)
OBJECTS = $(SOURCES:.c=.o)
LIBS = liblua523.a
PREFIX = $(DESTDIR)/usr/local
BINDIR = $(PREFIX)/bin
$(OBJECTS): $(SOURCES) $(HEADERS)
$(CC) $(CFLAGS) -c $(SOURCES) -o $(OBJECTS)
$(TARGET): $(OBJECTS)
$(CC) $(LDFLAGS) -o $(TARGET) $(OBJECTS) $(LIBS)
clean:
rm $(OBJECTS)
Unless you specify a different target on the command line, make always builds the first real target found in the makefile. In this case, the first real target is the first object file, so that's all that's built.
This is why you typically see makefiles with a first target of all or similar, which just depends on the various other targets you want built during a standard invocation of "make" with no arguments.
However, your makefile is really not right, in a number of ways. The fact that it's running it all means you actually only have one source file. As soon as you have >1 it will fail.
This:
SOURCES = $(shell echo ./*.c)
is not very efficient; you should use wildcard here:
SOURCES = $(wildcard ./*.c)
This rule:
$(OBJECTS): $(SOURCES) $(HEADERS)
$(CC) $(CFLAGS) -c $(SOURCES) -o $(OBJECTS)
Tells make, "for every object file, if any source file or any header file has changed, recompile it". Basically, it means that if you change ANYTHING in the directory, EVERYTHING will rebuild. If you want that you might as well write a shell script and not bother with make at all.
Further, the compiler will fail when you have >1 source file, as it will try to run:
gcc -g -Wall -c foo.c bar.c -o foo.o bar.o
which is not right.
You don't need to define this rule at all; make has a built-in rule which knows how to build an object file from a source file. Just replace it with this:
$(OBJECTS): $(HEADERS)
(no recipe) so make knows that the objects depend on the headers as well as the source. Note this is not ideal since all objects rebuild if any header changes but it's fine for a simple program.
Related: Target-specific Variables as Prerequisites in a Makefile
I'm trying to craft a Makefile which uses a target-specific-variable to specify the output directory for the object files and the final executable. The idea is to maintain two separate binary versions, a 'release' version and a 'debug' version with extra debugging information.
My problem is that 'make' does a clean build every time, even if I haven't changed a thing. I'm pretty sure it's because 'make' is evaluating the prerequisites of the target 'corewars' before the variable declaration in the prerequisites for the 'debug' or 'release' target.
The Makefile is presented below.
CXX=g++
LD=g++
LDFLAGS=
CXXFLAGS=-Iinclude -Wall -Wextra
OBJECTS=main.o Machine.o Core.o ProcessQueue.o Instruction.o
OUTPUT_DIR:=Test/
.PHONY: default
.PHONY: all
.PHONY: release
default: release
all: release
release: OUTPUT_DIR:=Release/
release: corewars
.PHONY: debug
debug: CXXFLAGS+=-DDEBUG -g
debug: OUTPUT_DIR:=Debug/
debug: corewars
corewars: $(OUTPUT_DIR) $(addprefix $(OUTPUT_DIR),$(OBJECTS))
$(LD) -o $(addprefix $(OUTPUT_DIR),corewars) $(addprefix $(OUTPUT_DIR),$(OBJECTS))
Release:
mkdir -p $#
Debug:
mkdir -p $#
%.o: %.cpp include/%.h
$(CXX) -c $(CXXFLAGS) $< -o $(OUTPUT_DIR)$#
.PHONY: clean
clean:
$(RM) -r Release
$(RM) -r Debug
First of all, a non-phony recipe must create a target, $#, not $(OUTPUT_DIR)$#. Also consider converting directory dependencies into order-only prerequisites.
In order to get a proper value of $(OUTPUT_DIR) inside the list of prerequisites, you would have to use secondary expansion, because otherwise, during the primary expansion, the global definition OUTPUT_DIR:=Test/ is used instead of the target-specific one.
Unfortunately, I can't think of a sane way to make it work using target specific variables, without resorting to secondary expansion and vpath magic. Personally I would rather setup the environment first (find out the value of OUTPUT_DIR, etc.) and then re-execute Make with the proper values.
ifndef OUTPUT_DIR
.PHONY: default all release debug
default all: release
release: export OUTPUT_DIR := Release/
debug: export OUTPUT_DIR := Debug/
debug: export EXTRA_CXXFLAGS := -DDEBUG -g
release debug:
#$(MAKE)
else
# ...
CXXFLAGS := -Iinclude -Wall -Wextra $(EXTRA_CXXFLAGS)
PROGRAM := $(OUTPUT_DIR)corewars
OBJECTS := $(addprefix $(OUTPUT_DIR), \
main.o Machine.o Core.o ProcessQueue.o Instruction.o)
# Default target.
$(PROGRAM): $(OBJECTS) | $(OUTPUT_DIR)
$(LD) -o $# $<
$(OUTPUT_DIR)%.o: %.cpp | $(OUTPUT_DIR)
$(CXX) -c $(CXXFLAGS) $< -o $#
$(OUTPUT_DIR):
mkdir -p $#
endif # OUTPUT_DIR
The two parts could them be split into separate makefiles, the root (starter) one, and the one that does the real work, to make the whole thing more manageable.
Target-specific variables are only available within the context of the recipes of the target and its recursive prerequisites. That is, target-specific variables cannot be used as targets nor prerequisites.
One workaround is the makefile there.
I'm relatively new to (GNU) Make, and find it incedibly difficult. I consider switching to SCons, but still, I'd like to understand.
I have a makefile in a folder, that contains subdirectories ./src, ./obj/[release|debug] and ./bin[release|debug]. The makefile should be able to grab the C++ sources in ./src, compile them into object files in the appropriate ./obj directory, and link these object files and put the result in the appropriate ./bin directory. Here is my makefile (edited for simplicity):
CONFIG = release
#CONFIG = debug
OBJS = Container.o
OBJDIR = obj/$(CONFIG)
BINDIR = bin/$(CONFIG)
VPATH = src $(BINDIR)
vpath %.o $(OBJDIR)
.PHONY: release
release: $(OBJS)
$(CXX) $(LXXFLAGS) -o $(BINDIR)/$# $^
Container.o: Container.cpp Container.hpp
$(CXX) -c $(CXXFLAGS) -o $(OBJDIR)/$# $<
The first time I run make, the "release" target will search for "Container.o" in the current folder, as well as in $(OBJDIR). Failing to find it, the secong target will be correctly executed, generating the object file in the correct folder. The "release" target will then execute, but the linker will complain that "Container.o" is not found...
The second time I run make, the "release" target will search for "Container.o" and find it in $(OBJDIR). The linker will then execute correctly (the path where "Container.o" has been found is prepended to the filename).
Is there a way to make it work in a single pass? It drives me crazy!
Make does have a long learning curve, and you're attempting something tricky (and which runs right into one of Make's big weaknesses, poor wildcard handling). I'm not sure that my answer will help more than confuse, but at least it will solve your specific problem.
If you want to use the CONFIG approach, this will do it:
CONFIG = release
#CONFIG = debug
OBJS = Container.o
TRUE_OBJS = $(addprefix obj/$(CONFIG)/, $(OBJS))
vpath %.cpp src
.PHONY: $(CONFIG)
$(CONFIG): bin/$(CONFIG)/$(CONFIG)
bin/$(CONFIG)/$(CONFIG): $(TRUE_OBJS)
$(CXX) $(LXXFLAGS) -o $# $^
$(TRUE_OBJS): obj/$(CONFIG)/%.o : %.cpp
$(CXX) -c $(CXXFLAGS) -o $# $<
But you can do without it (and without the chore of editing the makefile whenever you want to change configurations):
OBJS = Container.o
vpath %.cpp src
.PHONY: release debug
release: bin/release/release
debug: bin/debug/debug
bin/release/release: $(addprefix obj/release/, $(OBJS))
bin/debug/debug: $(addprefix obj/debug/, $(OBJS))
bin/release/release bin/debug/debug:
$(CXX) $(LXXFLAGS) -o $# $^
obj/release/%.o obj/debug/%.o: %.cpp
$(CXX) -c $(CXXFLAGS) -o $# $<