I have a project that presently builds in Xcode on OS X. I'm trying to construct a makefile to allow it to build on other Un*x systems. I'm a novice at writing makefiles, so I have been cobbling together a makefile from various examples on the web, and perhaps unsurprisingly, I can't get the result to work. I've looked at other similar questions on SO and gleaned what I can from them, but my situation seems to be a bit different because
- I have a large number of source files, so specifying all sources and objects explicitly is not practical, I need to use wildcards
- Some of my sources are in subdirectories, and the same filename can be used in different subdirectories so objects need to be placed by their source files within the file hierarchy to avoid collisions
- I'm building both C and C++ and linking it together at the end
- I want to be able to build two different targets, named "slim" and "eidos", using different subsets of the source files in the directory.
So please don't mark this as a dup unless the other question really addresses all of these requirements; thanks.
I would also like to avoid the use of makefile-generation tools, partly because it adds another level of complexity that I don't presently understand, and partly because it seems like overkill since I'm not interested in handling header dependencies and such (see discussion below), and partly because I need maximum portability (I don't want to depend on particular tools being present beyond vanilla make
).
The error I'm getting from make -n
is a simple one, so my mistake is probably very dumb:
make: *** No rule to make target `gsl/complex/*.c.o', needed by `slim'. Stop.
But it seems to me that *.c.o
targets ought to be handled by my %.c.o
rule, so I'm puzzled. My makefile:
SHELL = /bin/sh
CC = gcc
CXX = g++
INCLUDES = -iquote./eidos -iquote./gsl -iquote./gsl/rng -iquote./gsl/randist -iquote./gsl/sys -iquote./gsl/specfunc -iquote./gsl/complex
CCFLAGS = -O3 -v $(INCLUDES)
CXXFLAGS = -O3 -v $(INCLUDES) -std=c++11
OUTPUTDIR = ./bin/
MKDIR = mkdir -p $(OUTPUTDIR)
CSOURCES = ./gsl/*/*.c
SLIM_CXXSOURCES = ./core/*.cpp ./eidos/*.cpp
EIDOS_CXXSOURCES = ./eidostool/*.cpp ./eidos/*.cpp
COBJECTS = $(patsubst %.c, %.c.o, $(CSOURCES))
SLIM_CXXOBJECTS = $(patsubst %.cpp, %.cpp.o, $(SLIM_CXXSOURCES))
EIDOS_CXXOBJECTS = $(patsubst %.cpp, %.cpp.o, $(EIDOS_CXXSOURCES))
all: slim eidos FORCE
slim: $(COBJECTS) $(SLIM_CXXOBJECTS) FORCE
$(MKDIR)
$(CXX) $(COBJECTS) $(SLIM_CXXOBJECTS) -o ./bin/slim
eidos: $(COBJECTS) $(EIDOS_CXXOBJECTS) FORCE
$(MKDIR)
$(CXX) $(COBJECTS) $(EIDOS_CXXOBJECTS) -o ./bin/eidos
%.cpp.o : %.cpp
$(CXX) -c $(CXXFLAGS) -o $@ $<
%.c.o : %.c
$(CC) -c $(CCFLAGS) -o $@ $<
clean: FORCE
$(RM) -rf $(OUTPUTDIR)
$(RM) ./*.o
# Would use .PHONY, but we don't want to depend on GNU make.
# See http://www.gnu.org/software/make/manual/make.html#Phony-Targets
# and http://www.gnu.org/software/make/manual/make.html#Force-Targets
FORCE:
I'm not trying to establish per-file header dependencies or anything smart like that. Instead, I'm just trying to use FORCE to force a full rebuild each time one of the top-level targets (slim, eidos, all) is built. This is because I don't expect people to use this makefile during development; all development is done on OS X in Xcode. So it doesn't need to be minimal/efficient, it just needs to work reliably to produce a final product. I'm not sure that I'm using FORCE correctly, though.
I also have a couple of side questions.
- I took the
$<
endings for the two compilation commands from example makefiles on the web, but I don't know what they do. What's going on there? It looks like a redirection, but it's not a redirection command I'm familiar with, and of course Google is useless for searching for symbolic things like this. - The
patsubst
business is a bit tricky, and I don't know how to tell whether it's working or not. I triedmake -np
to print outmake
's internal info, but it seems to show variables with their original definitions, not with their resolved definitions, so I can't tell what the final value is thatmake
will actually use for my variables. How can I debug this? And ispatsubst
a standard part ofmake
? Is there a better way to do what I'm trying to do there?
Thanks. Sorry for the hectic multi-part question. By the way, all I'm really trying to do is convert the previously existing makefile to build source files individually, producing .o object files, and then link at the end. The previously existing makefile built everything in a single call to g++, which is causing problems for some users because of excessive memory usage and build time. Here's the old Makefile:
SHELL = /bin/sh
CC = g++
CFLAGS = -O3 -Wno-deprecated-register
INCLUDES = -iquote./eidos -iquote./gsl -iquote./gsl/rng -iquote./gsl/randist -iquote./gsl/sys -iquote./gsl/specfunc -iquote./gsl/complex
ALL_CFLAGS = $(CFLAGS) $(INCLUDES) -std=c++11
all: slim eidos FORCE
slim: FORCE
mkdir -p ./bin
$(CC) $(ALL_CFLAGS) ./core/*.cpp ./eidos/*.cpp ./gsl/*/*.c -o ./bin/slim
eidos: FORCE
mkdir -p ./bin
$(CC) $(ALL_CFLAGS) ./eidostool/*.cpp ./eidos/*.cpp ./gsl/*/*.c -o ./bin/eidos
clean: FORCE
rm -f ./bin/slim
rm -f ./bin/eidos
# Would use .PHONY, but we don't want to depend on GNU make.
# See http://www.gnu.org/software/make/manual/make.html#Phony-Targets
# and http://www.gnu.org/software/make/manual/make.html#Force-Targets
FORCE:
That works fine, apart from the aforementioned issue of being slow and using a ton of memory.