Autoconf and automake are not always the answer.
For many projects, what you need is a simple Makefile that takes care of source code dependencies over a directory tree. My usual programming needs are more than adequately covered with this:
# The directories containing the source files, separated by ':' VPATH=src:../Library # To make "debug" the default configuration if invoked with just "make": # # ifeq ($(CFG),) # CFG=debug # endif # The source files: regardless of where they reside in the source tree, # VPATH will locate them... Group0_SRC = \ Source1.cpp \ Source2.cpp \ LibrarySource1.cpp \ LibrarySource2.cpp # Build a Dependency list and an Object list, by replacing the .cpp # extension to .d for dependency files, and .o for object files. Group0_DEP = $(patsubst %.cpp, deps.$(CFG)/Group0_%.d, ${Group0_SRC}) Group0_OBJ = $(patsubst %.cpp, objs.$(CFG)/Group0_%.o, ${Group0_SRC}) # Your final binary TARGET=applicationName # What compiler to use for generating dependencies: # it will be invoked with -MM -MP CXXDEP = g++ # What include flags to pass to the compiler INCLUDEFLAGS= -I ../Library -I src # Separate compile options per configuration ifeq ($(CFG),debug) CXXFLAGS += -g -Wall -D_DEBUG ${INCLUDEFLAGS} else CXXFLAGS += -O2 -Wall ${INCLUDEFLAGS} endif # A common link flag for all configurations LDFLAGS += -lSDL all: inform bin.$(CFG)/${TARGET} inform: ifneq ($(CFG),release) ifneq ($(CFG),debug) @echo "Invalid configuration "$(CFG)" specified." @echo "You must specify a configuration when running make, e.g." @echo "make CFG=debug" @echo @echo "Possible choices for configuration are 'release' and 'debug'" @exit 1 endif endif @echo "Configuration "$(CFG) @echo "------------------------" bin.$(CFG)/${TARGET}: ${Group0_OBJ} | inform @mkdir -p $(dir $@) $(CXX) -g -o $@ $^ ${LDFLAGS} objs.$(CFG)/Group0_%.o: %.cpp @mkdir -p $(dir $@) $(CXX) -c $(CXXFLAGS) -o $@ $< deps.$(CFG)/Group0_%.d: %.cpp @mkdir -p $(dir $@) @echo Generating dependencies for $< @set -e ; $(CXXDEP) -MM -MP $(INCLUDEFLAGS) $< > $@.$$$$; \ sed 's,\($*\)\.o[ :]*,objs.$(CFG)\/Group0_\1.o $@ : ,g' < $@.$$$$ > $@; \ rm -f $@.$$$$ clean: @rm -rf \ deps.debug objs.debug bin.debug \ deps.release objs.release bin.release # Unless "make clean" is called, include the dependency files # which are auto-generated. Don't fail if they are missing # (-include), since they will be missing in the first invocation! ifneq ($(MAKECMDGOALS),clean) -include ${Group0_DEP} endif
make CFG=debugor
make CFG=releaseYour output object files will be placed in objs.debug or objs.release, and your application under bin.debug or bin.release, respectively.
-include ${Group0_DEP}Group0_DEP has been calculated (see the top of the template) to mirror your set of source files. If you are compiling in CFG=debug mode, for example...
Group0_SRC entries | Group0_DEP entries |
Source1.cpp | deps.debug/Group0_Source1.d |
Source2.cpp | deps.debug/Group0_Source2.d |
LibrarySource1.cpp | deps.debug/Group0_LibrarySource1.d |
LibrarySource2.cpp | deps.debug/Group0_LibrarySource2.d |
make -j2...you'd find that the first CPU would try the inform rule (displaying the message about CFG missing) while the other would try to implement bin.$(CFG)/${TARGET}, which, with CFG missing, would fail! The " | inform" tells make that this rule has an order dependency on rule inform.
If the above sounds like gibberish, you'd better stop here and go read the Pragmatic Programmer.
If they don't, however, you may not know why code generators pose a problem when using Makefiles. If we use...
all: codeGen applicationName codeGen: robotMade.c robotMade.h robotMade.c robotMade.h: documentation.tex ./roboCoder.pl documentation.tex applicationName: $(OBJS) | codeGen ...You see, make interprets this last rule as two rules:
robotMade.c: documentation.tex ./roboCoder.pl documentation.tex robotMade.h: documentation.tex ./roboCoder.pl documentation.tex...which is probably not what you want. 'roboCoder.pl' will run twice, whenever you change documentation.tex. This might sound like a minor inconvenience, but if you use the "-j" options, you might jolly well have two make instances running one roboCoder each... All hell might break loose (racing conditions when roboCoder opens its output files, etc).
There's no point in repeating - please go and read the perfect description of the problem in automake's documentation (in the automake's FAQ section, read the "Multiple Outputs" entry).
If you adopt the solutions described there and try to put them to use in the template I offered above, you'll face a problem: calculation of the external dependencies (the .d files described in the previous section) needs access to the .h/.c/.cpp files generated, so the code generation must somehow run before the dependencies generation. The dependencies generation, however, is based on the inclusion of external makefiles; if you just add the 'codeGen' rule as a prerequisite of your 'applicationName', it won't do; included makefiles are handled by make before any other rules. The only workaround I can offer is to add your 'codeGen' rule, as an order dependency, on the .d files themselves:
deps.$(CFG)/Group0_%.d: %.cpp | codeGen @mkdir -p $(dir $@) @echo Generating dependencies for $< ...This will instruct make to invoke your code generation rule first, and then proceed to calculate the external dependencies (that is, after the .h/.c/.cpp files have been created).
Index | CV | Updated: Sat Oct 8 12:33:59 2022 |