Feedback about using make on a project with many subdirectories - c++

To my Object Oriented Programming course, I must do a final proyect (academic purposes). I want to make a proyect "the right way" (ie: makefile, modular, DRY, easily scalable, etc) in order to better understand classes, makefile and C++.
The idea I've got is to have a "tree-source-file-structure-directory" so in each subfolder i'd got the source files with it's headers, test files and single makefile.
So if I want to work on the interface, I go to the subfolder interface, I edit the files, I run the tests, if everything is OK, simply I link the objects together on my root directory. Same thing if I want to work on my data structure, and so goes on. The nice feature is that in every subfolder resides along the source code and the object files, so the linker in my root directory would search for object files already compiled on subfolders and link them together
I've been searching on the internet, and I could see many different solutions:
-Doing make recursively, eg:
SUBDIRS=eda
.PHONY: subdirs $(SUBDIRS)
$(SUBDIRS):
$(MAKE) -C $#
The problem I found is that my prerequisites on "eda" folder would be "quirky"
-Using Automatic Variable $(#D), but I didn't quite understand how it works
-Maybe using wildcard function, but I am a little confused about this option.
Anyways, the most tempting solution for me was the first one (using make recursively), but I found lot of comments saying that it is not recommended to use make recursively Interesting article
So I want to ask you guys some advices: How can I accomplish my objectives and have every important module in a separate folder? is recursive make the best solution? Maybe I should dive in "automake"? Or perhaps it would be better to take all the object files to a new "object" subfolder on root directory and then link them together?
By the way, I took the inspiration to make my proyect with this tree structure by sniffing Amarok source code: it has a subfolder called "src", and when you enter there, you can see a lot of subfolders: equalizer, playlist, dynamic, statusbar, core, playlistgenerator, playlistmanager, etc. And many subfolders have their own subdirectories... and the result is an incredible music player. If this method works fine to the Amarok team... I could be able to do something similar!
Any comments, feedback, suggestions and others are welcome, thanks in advance!
EDIT #1
Beta, I have some implicit rules (suffix) and a target for the linker that needs a object on my eda folder. Every other prerequisite of this target is built on the current folder.
The problem I have, is that when I run make to build that target, it takes the name of my prerequisite on "eda" folder as a target to build with the implicit rule. That's the tricky/unclean part of the makefile recursive on my proyect: I guess I must create a special implicit rule for every object file that make must search in a subfolder.
That's why I want some feedback: ¿Are there better alternatives? Or the advantages of using make recursive in my proyects overwhelm the other alternatives?
Anyways, if gives you better understanding, here is my draft Makefile (it is in spnish-english :P )
#Makefile hecho para las pruebas de los archivos dentro de esta carpeta
FLAGS=-g -DDEBUG
OUT_TI=TIndividuo
OUT_TP=TProfesor
OUT_TA=TAula
.SUFFIXES: .cpp .c .h .o
.c.o: ; cc $(FLAGS) -c $*.c
.cc.o: ; gcc $(FLAGS) -c $*.cc
.cpp.o: ; g++ $(FLAGS) -c $*.cpp
SUBDIRS=eda
.PHONY: subdirs $(SUBDIRS)
$(OUT_TI): eda/TAula.o CandidatoHorario.o TIndividuo.o TIndividuoTest.o TGen.o
g++ CandidatoHorario.o TIndividuo.o TIndividuoTest.o TGen.o eda/TAula.o -o $#
CandidatoHorario.o: CandidatoHorario.cpp CandidatoHorario.h
TIndividuoTest.o: TIndividuoTest.cpp TIndividuo.h
TIndividuo.o: TIndividuo.cpp TIndividuo.h
TGen.o: TGen.cpp
#eda/TAula.o: eda/TAula.cpp eda/TAula.h
# g++ -c eda/TAula.cpp -o $#
$(SUBDIRS):
$(MAKE) -C $#
clean:
rm -f *.o $(OUT_TI) $(OUT_TA) eda/TAula.o

The "Recursive Make Considered Harmful" is certainly a paper to read and to understand. Afterwards, your selection of tools should really be tailored to your specific projects.
For small projects that you initiate (or where you have the influence to guide high-level decisions), I would recommend spending a bit of time identifying your preferences (project layout, directory structure, unit test framework, etc.) and writing a generic set of makefiles that you will use for all your projects. You could easily end up with a generic master makefile, possibly a few more generic included makefiles for modularity (e.g. to build libraries, or unit tests or automatic dependency detection). You could also provide some extra flexibility with optional included configuration makefiles (e.g. specifying the order of your libraries). Most of the DAG construction would rely heavily on the content of your project directories. An example could look like:
include config.mk
sources := $(wildcard *.cpp)
programs := $(sources:%.cpp=%)
lib_sources := $(wildcard lib/*/*.cpp)
lib_dirs := $(sort $(patsubst %/, %, $(dir $(lib_sources:lib/%=%))))
lib_objects := $(lib_sources:lib/%.cpp=$(BUILD)/%.o)
all: $(programs:%=$(BUILD)/%)
.PRECIOUS: %/.sentinel %.d
# for dependencies on directories in build tree
%/.sentinel:
#mkdir -p $* && touch $#
clean:
$(RM) -r $(BUILD)
programs_aux:=$(programs)
include $(foreach program, $(programs), program.mk)
lib_dirs_aux:=$(lib_dirs)
include $(foreach lib_dir, $(lib_dirs), lib.mk)
# this should probably be in lib.mk
-include $(lib_objects:%.o=%.d)
The included program.mk (and lib.mk) would contain some boilerplate code to iterate over the lists of programs (and lists of libraries) and would factor out the specific parts of the makefile to build programs (and libraries).
To help with the implementation of such makefiles, you could use some standard library like http://gmsl.sourceforge.net.
This approach has several issues:
* it leads to makefiles that require strong skills
* it doesn't always scale very well to very large projects
* it relies heavily on "convention instead of configuration" and requires a clear upfront definition of the conventions that you will use (IMO this is good others might think that it lacks flexibility)
* life is too short to mess around with makefiles
Otherwise, I would suggest using higher-level configuration tools such as SCons or CMake as they tend to be conceptually simpler and they also allow other flavours of generators.

Related

Retrieve used header and source file names by parsing Makefile

I would like to extract all the *.cc and *.h filenames from a Makefile that also contains unused targets. Example Makefile here : https://gist.github.com/berceanu/7554a9c4371b807e425259c7e99b5de9
I've tried running make -Bnd and looking at the pruned files but I don't know if this misses anything.
make -Bnd | grep "Pruning file" | sort | uniq
Expected result: list of all *.h and *.cc files used by make run on the above Makefile.
Your approach trying to extract this information from the Makefile is probably the wrong way. make doesn't know which header files are actually used. make only knows which header files you've explicitely told make about in the dependencies, and that's not very reliable. The information in the Makefile could be wrong in two ways. It could contain unused targets (as you have noticed) or unused header files. It could miss header files that are actually included but not mentioned in the Makefile. Even worse, what if the actual inclusion of header files depends on macros, like #ifdef XYZ_FEATURE #include "additionalHeaderFile.h" #endif.
There are at least three ways how you could generate the desired list, that is, list of .cc and .h files actually used during compilation:
(blindly trusts the Makefile) make -n --print-data-base
(follows the real files of the build, but also a bit difficult to parse) strace -f make
(relies on a feature present in GCC, clang, armcc and possibly other compilers for Makefile generation, very reliable) Add CPPFLAGS:=-MMD to the Makefile, run make clean, then make, then use cat *.d to get the list of all .cc and .h files used to build run. You could even do that without changing the Makefile: make clean; make CPPFLAGS:=-MMD && cat *.d | sed -e 's/\\//g' -e 's/:/ /g' -e 's/ \+/\n/g' | sort -u.
Also, the Makefile that you've shared in the gist has a large number of issues.
The default target should, by convention, be named all 1 2 3. That doesn't need to be the name of the binary, it would just be a .PHONY target.
The variable with the list of object files should be named OBJS or OBJECTS, not OBJ. The name OBJ is misleading, because it's singular.
Instead of rm use $(RM) which implies -f and thus will not trouble in case a file doesn't exist. (As a side-effect, the Makefile would become more portable, as not all platforms use rm for deleting files.)
clean is not a file and thus should be a .PHONY target.
clean should use :: instead of : for its recipe so that in future, when the Makefile is bigger and split into multiple files, each file can have their own clean target without problems.
Variables that are not rule-specific should be expanded at the moment of definition, not when they are referenced, and thus defined using := instead of =.
Instead of C++ use CXX which is already defined.
Instead of putting the options into C++/CXX, use LDFLAGS, because you're linking.
You should have a source file that has the same basename as the binary. Then you can use the built-in rule for linking.
Explicit dependencies in the Makefile are a pain for maintenance. Every time an #include statement for a project header file is added, removed, or changed, the Makefile would have to be updated, and that's easy to forget, and it's a pain, especially when the #include statement is in a header file. Even with due diligence, this is also an invisible merge conflict. Instead of having explicit dependencies in the Makefile, you should use CPPFLAGS+=-MMD at the start of your Makefile and append -include $(wildcard *.d) at the end of your Makefile, and add *.d to the list of files to delete in clean. You could then remove all dependency rules from the Makefile (except for the one for the linkage).
Naming the binary run is not a good idea. Users that see that your Makefile has a run target would expect this to run the actual program, not to link it.
It is better to have each object mentioned on a line of its own. That significantly reduces merge conflicts in projects when multiple developers change the object list at the same time.
Your actual Makefile should look like this, with the binary renamed from run to program:
LDFLAGS:=-Wno-deprecated -lm
CPPFLAGS+=-MMD
BINARY:=program
OBJECTS:= \
$(BINARY).o \
binomh.o \
# More objects here
.PHONY: all
all: $(BINARY)
$(BINARY): $(OBJECTS)
.PHONY: clean
clean::
$(RM) \
$(BINARY) \
*.[adios] \
-include $(wildcard *.d)
That Makefile will do the "same" thing as your Makefile, but it is almost maintenance-free. No need to update dependencies, as they are automatically taken from the dependency files generated by the C Preprocessor. The *.[adios] will also remove files created in case you added -save-temps to any of the CFLAGS, CXXFLAGS, or CPPFLAGS.
This type of Makefile is known to work for GCC, clang, AOCC (AMD Optimizing C Compiler), and armcc. It probably works for a number of other compilers and preprocessors as well, especially when they are based on, or try to be compatible with, GCC or clang/LLVM.
BTW in case you're interested in knowing that this works for you, with high confidence, besides experience: I've taken your Makefile and added the following lines to it in order to reproduce your source code structure. The header files would just be empty files. The C++ source files would be lists of #include statements taken from the dependencies in your Makefile.
%.cc:
grep '^$*\.o.*:' $(MAKEFILE_LIST) | sed -e 's/.*://' -e 's/.*$*\.cc//' -e 's/ \([^ ]\+\)/#include "\1"\n/g' >$#
%.h:
touch $#
Instead of -Bnd, I would suggest using --dry-run --print-data-base to dump the full database of targets, their dependencies, rules, variables, etc..

Structuring Makefiles with multiple directories

I am trying to compile my project which has the following structure
Project:
MakeFile
Executable
Source1
.cxx
.h
Source2
.cxx
.h
Build
*.o
And I'm having difficulty writting a Makefile to compile. I currently have commands like:
Src1 = $(wildcard $(SRCDIR1)/*.cxx)
Obj1 = $(patsubst $(SRCDIR1)/%.cxx, $(OBJDIR)/%.o, $(Src1))
But then I have difficulty making the compile rules for the object files a) Because I can no longer do:
$(Obj1): %.cxx
$(CXX) $(CFLAGS) -c $(#:.o=.cxx) -o $#
Because the '$#' command now includes the path of the build directory and b) because the prerequisites now include the build path and I should have a source path. I have read large bits of the make manual to try and find a solution but no luck.
Any help towards a solution appreciated!
Jack
From personal experience, after playing around a bit with "raw" Makefiles, I'd really recommend using some tool building the Makefiles for you, like automake or cmake.
You'll still have to specify all the source files manually - but at least I prefer that to manually fiddling around with the Makefiles.
One option I prefer is building an isomorphic directory structure in the build directory. That is, a source file ${src_dir}/project_x/main.cxx builds into ${build_dir}/project_x/main.o. This way you are protected from name clashes when there are source files with the same name in different source directories. The compiler rule would look something like:
${obj_dir}/%.o : ${src_dir}/%.cxx # % includes directory name, e.g. project_x/main
#-mkdir -p ${#D}
${CXX} -c -o $# ${CPPFLAGS} ${CXXFLAGS} $<
Notice how in the above it creates the target directory on the fly. This is a bit simplistic, in a real-world build system object files depend (using order-only dependency) on its directory, so that make automatically creates the destination directory if it does not exist instead of speculatively trying to create them for each target object file even if it already exists.

Which build system will do this the most 'naturally'?

Instead of the flat structure my code currently has, I want to organize it into modules contained in sub-folders (and perhaps sub-sub folders if the modules get big enough).
Each module will have one or more translation units which will each produce a .o file.
The final target would be to mash up all these object files into a static library (for now).
I am using plain 'make' and it is already complicated enough.
Is there a system in which the specified model comes naturally or with much less effort compared to writing makefiles by hand ?
(If you are going to recommend cmake, I need some hints as I have already tried and could not come up with a good solution.)
Some paraphrased bits from my current project's makefile that may help you out with good old fashioned GNU make:
SOURCEDIR := dir1 dir2/subdir1 dir3 dir4 dir5/subdir1 dir6/subdir1
SOURCES := $(foreach srcdir,$(SOURCEDIR),$(wildcard $(srcdir)/*.c))
OBJECTS := $(patsubst %.c,build/%.o,$(SOURCES))
OBJDIRS := $(addprefix build/,$(SOURCEDIR))
MAKEDEPS := $(patsubst %.c,build/%.d,$(SOURCES))
all: example
$(OBJDIRS):
-mkdir -p $#
build: $(OBJDIRS)
build/%.o : %.c | build
cc -MMD -c -o $# $<
example: $(OBJECTS)
cc -o $# $(OBJECTS)
-include $(MAKEDEPS)
In essence, it builds all of the source files found in the designated directories into object files located in subdirectories of the build directory in a hierarchy that parallels their source directory layout (important if multiple source files have the same name) and then links the results into an executable example.
As a bonus, dynamic dependency generation and inclusion via the MAKEDEPS variable and clang's -MMD flag.
It really depends upon your purposes: Build packages are generally intended for the audience rather than the performer. Often, they take into consideration the disparate environments into which people deploy. I played around with 'tup,' which seemed more a way of generating an executable as quickly as possible after an edit. 'Premake' seems to shoot at multiple platforms, but I found specifying compiler options no more enlightened than with Cmake.
It looks as though you've found a good Makefile tutor, so I'll leave my observations at that.

multi-directory "make" in linux programming

suppose we have a file-list:
src
a
b
c
Makefile
and there are many .cc & .h files in each folders.
how to write the Makefile??
when I write a path like this
./a/a.cc or ../a/a.cc
errors will be occurred.
but when i put the files on the same directory
and write as a.cc.
everything is ok.
I run it on cygwin.
could anyone help me?
The wildcard function is what you are looking for: http://www.gnu.org/software/make/manual/html_node/Wildcards.html
On Linux I would prefer Automake/Autoconf build systems: http://de.wikipedia.org/wiki/GNU_Build_System
An alternative could be CMake: http://www.cmake.org/
From the not-so-clear directory graph, it seems that you are missing the src/ directory prefix in your Makefile. E.g. to compile src/a/a.cc, use:
src/a/a.o: src/a/a.cc
$(CXX) $(CXXFLAGS) -c -o $# $<
BUT: Do yourself a favour, and use a real build system, such as CMake. It is cross-platform, can generate IDE project files (e.g. for Visual Studio, Xcode, Eclipse, CodeBlocks, etc.), is much higher level and does automatic dependency tracking for you.
You've left out a lot of details (e.g. where you want to put the object files), but VPATH is probably what you're looking for.
VPATH = src/a src/b src/c
# Now you can write rules as if the .cc & .h files were in the current directory.
%.o: %.cc
do something with $<
foo.o: foo.cc bar.h
do something special with $<

Portably Compile Entire Directory

Is there a clean/portable way to descend recursively from a given directory, compiling all found .cpp files into a single output file? I'm not sure if makefiles are capable of this sort of thing, or if it's a job for some kind of build script, but I'd like to avoid maintaining various IDEs' project files along with my code.
There are different things that you can do here. I would suggest that you use a multiplatform build system, and follow the documentation for it. I have used CMake in the past, but I wouldn't know how to tell it to compile all files in a directory.
The advantage is that the user can use CMake to generate project files for most common IDEs, so it would allow VisualStudio users to generate VS solutions, MacOSX users to generate Xcode projects, Eclipse CDK projects in pretty much any environment, Makefiles...
There's the wildcard function which can be used to match a pattern like so:
CXX_FILES = $(wildcard src/*.cpp) # All .cpp files in the directory
This is not recursive, but will at least save you from having to manually specify the files in a certain directory. The rule for building them would look something like this:
CXX_FILES = $(wildcard src/*.cpp) # All .cpp files in the directory
OBJ_FILES = $(CXX_FILES:src/%.cpp=$(OBJ_DIR)/%.o) # Corresponding .o files
# Rules
all: $(OBJ_FILES)
g++ $(OBJ_FILES) -o output_filename
$(OBJ_DIR)/%.o: src/%.cpp
g++ -c $< -o $#
Oh, and to answer your question, this method is completely portable.