Make file for larger directory structure - c

I've got several directories with subdirectories containing c or asm files and I want them all compiled/assembled and then linked. I'm not especially picky where the object files go (e.g. a special bin folder or in the src folder) as long as a make clean removes them all.
The structure would look something like this:
/src
/dir1
/dir1_1
+file1_1.s
+file1_2.s
+file1.s
/dir2
+file2.c
I'm sure there's some easy way to create a makefile that compiles all files without me having to specify where it should look (compiling all files in one directory is doable with wildcards, but what then?).

Do a Google search for 'recursive make considered harmful'. You'll find the original article which postulates that the recursive make procedure is a bad way of doing business, and you'll find some links to other places which debate the validity of the proposition.
Basically, there are two ways to do builds in a directory hierarchy (with make).
Recursive make: each directory contains a makefile which builds in sub-directories and then builds in the current directory.
Non-recursive make: the makefile includes all the dependent makefiles, and builds up the complete dependency structure for the entire project and only builds the requisite software.
I work routinely on a product where the main build sequence is driven by a hybrid system that uses a shell script plus one makefile for each directory. One section of the product is managed by a 'RMCH' makefile; most of it is not. The build script deals with phases of the build, and sequences the directories, and runs make in each directory when it is time to do so. (The source code is in 20k+ files spread over a multitude of directories - it is a big project/product.)
I've also converted a medium-small project (about 20 directories of relevance, and about 400 source files) to work with RMCH (from a script + makefile-per-directory system). It was a bit mind-blowing at first, but works pretty neatly now it is done. Whether I did it correctly is open for debate; it was primarily a learning exercise, though I also did some work modifying the code to work with a modern curses library instead of the archaic BSD library that was used as a part of the code (archaic, as in 1982-vintage - the code was last seriously developed in about 1986) and generally upgrading to modern (standard C) standards. It was also a chance to work with git - so, all in all, quite an extensive learning experience.
If you can wrap your brain around RMCH, it is a good system. If done correctly, with complete and accurate dependency tracking, it removes the guess-work from the build sequence, and it does run fast. However, migrating even a medium size project to it is fairly hard work - it would be a daunting task to do it on the main product I work on, though the system might well benefit from it.
An alternative is to look at other alternatives to make, such as cmake, rake, scons, bras, imake, or ant or whatever else takes your fancy. Most of those are easily discoverable via a Google search; the hard one is bras, which is based on Tcl (as in Tcl/Tk), but is probably largely dead now. And imake is mentioned more for completeness than as a serious suggestion. You might also look at the GNU Autotools. Those do not abandon make; they build atop make.

If your project is small enough, you might get away with using a single hand-crafted makefile instead of a more sophisticated build system: check out the manual page on transformation functions to see what's possible.
Your example project could be compiled with the following non-recursive makefile:
targets = $(patsubst %$(1),%$(2),$(foreach dir,$(3),$(wildcard $(dir)/*$(1))))
asmdirs := src/dir1 src/dir1/dir1_1
cdirs := src/dir2
asmobjects := $(call targets,.s,.o,$(asmdirs))
cobjects := $(call targets,.c,.o,$(cdirs))
.PHONY : all clean
all : $(asmobjects) $(cobjects)
clean :
rm -f $(asmobjects) $(cobjects)
$(cobjects) : %.o : %.c
gcc -o $# -c $<
$(asmobjects) : %.o : %.s
gcc -o $# -c $<
However, because make can access the shell, you could also use standard unix tools like find instead of the somewhat limited builtin functions, eg
asmsources := $(shell find src -name '*.s')
csources := $(shell find src -name '*.c')
asmobjects := $(asmsources:.s=.o)
cobjects := $(csources:.c=.o)

Related

How to include hundreds of folders in a Makefile?

I'm working on a C++ project at work where we need to develop a small piece for a larger application. We were given headers and static libraries for all of the code that we should need to reference. These are strewn throughout multiple folders and we placed all of that inside a common folder.
When writing our code, we'll need to include the headers and libraries as a part of our compilation process. Is there an elegant solution to doing this in a Makefile, or do I have to explicitly list each include folder with -I , each library folder with -L , and each library with -l?
Or is there an alternative to a Makefile that might make sense for this?
Edit: Here is an example of the folder structure:
common
folder1
subfolder1
include
libs
subfolder2
...
subfolder10
folder2
...
...
folder10
code
makefile
ourStuff
There are multiple levels of folders under common containing headers and libraries. We need to include code from there.
It was also asked why we don't just explicitly list the path in our #include statements. This code will be living in the main application once we're done, and it doesn't exactly follow the folder structure we were given.
Well, given the above structure it's simple enough to generate the things you want. For example if you want to add all -I... flags to CXXFLAGS, you can use:
INCDIRS := $(wildcard ../common/*/*/include)
CXXFLAGS += $(addprefix -I,$(INCDIRS))
Similar for -L flags:
LIBDIRS := $(wildcard ../common/*/*/libs)
LDFLAGS += $(addprefix -L,$(LIBDIRS))
Linking all the libraries is slightly more complicated. Assuming they're all static libraries you can do something like this:
LIBFILES := $(notdir $(wildcard ../common/*/*/libs/lib*.a))
LDLIBS += $(patsubst lib%.a,-l%,$(LIBFILES))
Of course this is assuming you don't have any naming conflicts / all libraries are unique.
Obviously, your question can be formulated like this: "Do I have to write a plethora of include paths or is there some managed/automatic way to do that". The question may pop up in the context of a makefile but this is mainly because make does not try to cloak the complexity of software building from the programmer. Trying to evade to another build system buys you nothing if the components you are using were not fitted into the larger build algorithm by their original programmers. If you receive pre-configured build parts (e.g. in form of a CMake project) then you save a great deal of work, needing only to tie together some abstraction level high up in the hierarchy. The downside of this is that you are locked in this build methodology now, possibly with more ramifications radiating out into parts of your project where they do as much harm as good. You may want to read this thread here: https://softwareengineering.stackexchange.com/questions/407056/the-case-against-path-expressions-in-include-directives
The cheapest way to at least partially achieve what you want to do in GNUmake is to use the function wildcard-rec (see https://github.com/markpiffer/gmtt#call-wildcard-reclist-of-globs) which has a fairly flexible input-output-relation. You can e.g. collect all paths which are of the form project/component_a/**/include/ in a whole subtree, or all header files in such a path with project/component_a/**/include/*.h.
PS: simply include gmtt.mk at the top of your makefiles.

Makefile arguments to C definitions - requires clean

I have a makefile that I use to build an embedded project in C. When I build the project I pass an argument like the following so that a #define is set.
make UID=ID123
In the makefile I have
ifdef UID
CFLAGS+=-DUID=\"$(UID)\"
endif
And in the source code e.g. app.h I have
#ifndef UID
#define UID NOUID
#endif
The problem I am facing is that this works only if I clean the project first. Since the project is quite big, this takes a lot of time between recompilations.
How can this be avoided? Can the make program selectively build the files that are affected? Like when a file gets edited? Does removing the object files that this #define affects help or is a bad idea?
The reason this is necessary is so that programming 100 devices, each will have a unique ID passed to the program at build time.
Thanks.
There are several things you can do, but some of there are pretty involved. It's a question of what your priorities are, and how much arcane Make-craft you want to do.
First you can figure out which object files depend on UID. Having such a list will save you a lot of work; for instance, you can remove only those object files and then rebuild them, instead of removing and rebuilding all object files with clean:
clean_uid_objects:
rm -f $(UID_OBJECTS)
You can maintain this list yourself:
OBJECTS := foo.o bar.o
UID_OBJECTS := baz.o qux.o
or with some cleverness you might have Make construct the list on the fly, but explaining that one would take a while.
You can also have Make keep track of which UID you used, the last time you rebuilt the object files. After all, if you haven't changed it, then you don't have to rebuild anything because of it. One way to do that is to record the last UID in a file called, say, UID_SAVE. Then you can include that file in the makefile, and Make will adjust it (and rebuild the makefile, and rerun itself) when you pass it a new UID:
-include UID_save
ifdef UID
#CFLAGS+=-DUID=\"$(UID)\"
ifneq ($(UID),$(OLDID))
.PHONY: UID_SAVE
endif
endif
UID_save:
#echo OLDID:=$(UID) > $#
Once you have that working, you can make UID_SAVE a prerequisite of the object files that depend on UID, so that no cleaning is necessary at all.
I ended up using
.PHONY: myfile1.c myfile2.c myfile1.h
to force rebuild the files that depend on the argument passed.
My answer is similar to Beta's last answer, but it shows the precise systematic method. What you want, is have UID behave as if it were a file, not a variable, so you could depend targets on it. Here is how to do it, see my answer in this post:
How do I add a debug option to Makefile
You write the function DEPENDABLE_VAR. Then, if you want a variable UID to be "dependable", you simply call that function:
$(eval $(call DEPENDABLE_VAR,UID))
and now you can write things like:
foobar.o: UID

makepp: how to manage several builds with common source dir?

I have a source tree:
/bootloader
/firmware
/system
and want to manage two separate builds for firmware and bootloader, each of them using common system sources but compiles them differently (i.e. with its own set of options).
Builds must be out-of-tree.
Obvious "repository" feature of makepp is not a solution here, because it breaks this principle. Symbolic links are not solution too, because it must work on Windows.
The problem actually is in shared system sources, whose relative path structure differs from others, causing common pattern rules not work for them:
BUILD_PATH = $(relative_to $(PROJECT_PATH), .)/BUILD/$(relative_to ., $(PROJECT_PATH)) # trick to be able to extend rules for specific files at different subtree levels (if we use Makeppfile for each level)
...
$(BUILD_PATH)/%.o : %.c
...
Approach with single RootMakeppfile and include *.mk files (instead loading them) also doesn't allow me to do something like that:
$(BUILD_ROOT_PATH)/*/%.o : %.c
I've tried a lot of totally different approaches. It's not such trivial, as it seems to be at first glance. Please, help.
I finally managed how to solve problem.
Solution is to divide Rootmakeppfile project to two Rootmakeppfile subprojects (both loading ../system makeppfile in its own way), and perform build separately for each subproject (in its own build directory). Project structure with makefiles:
/bootloader/
...
BUILD/
Rootmakeppfile
/firmware/
...
BUILD/
Rootmakeppfile
/system/
...
Makeppfile
/project.mk
Common definitions .mk file should contain following lines:
BUILD_ROOT_DIR = $(relative_to $(SUBPROJECT_PATH), .)/BUILD
BUILD_OBJ_REL_PATH = $(BUILD_ROOT_DIR)/$(relative_to ., $(BUILD_OBJ_REL_DIR))
BUILD_OBJ_REL_DIR ?= $(SUBPROJECT_PATH)
Each subproject should include system as follows:
load_makefile BUILD_OBJ_REL_DIR="$(SUBPROJECT_PATH)/.." ../system
and define:
global SUBPROJECT_PATH := $(abspath .)
This approach makes possible to define pattern rules as follows:
$(BUILD_OBJ_REL_PATH)/%.o : %.c
Hope, it helps someone!

Makefile.am commands to override library functions

I have an open source project that relies on another open source project (let's call that other project X). Both are written in C. I've had to hack pieces of X to get multi-threading to work. This causes some issues when trying to package up the code for distribution. To make things easier, I've just included the entirety of X within mine along with the few little hacks I've made.
I'd like to do something more sophisticated now in order to keep the improved functionality of X (it has frequent releases and mine does not) without having to repackage the whole project (with my hacks) within my project again each time that X has a release.
There are only 3 or 4 functions in that need to override. I can follow what is going on in this IBM Tutorial, but how can I modify my Makefile.am to generate the Makefile changes suggested in that article? To summarize, the article suggests writing my own functions with the same signatures as the ones I want to override (in a file called libfuncs.c) and then add the following 'libs' target to the makefile:
all: libs setresgid-tester
libs: libfuncs.c
gcc -shared -Wl,-soname,libfuncs.so.1 -o libfuncs.so.1.0 libfuncs.c
ln -s libfuncs.so.1.0 libfuncs.so.1
ln -s libfuncs.so.1 libfuncs.so
setresgid-tester: setresgid-tester.c
gcc -o setresgid-tester setresgid-tester.c
All of that makes sense to me. What I need to do, however, is to have this 'libs' target created for me with the autotools. I have a Makefile.am works well right now. How do I modify it to produce the desired results above? It was difficult to find in the autotools documentation, but I may just not have known exactly what to look for.
I'm happy to provide more details if helpful. Thanks in advance.

How to use autotools for deep projects?

I have a C project that has the following structure
Main/
Makefile.am
bin/
src/
Makefile.am
main.c
SomeLibrarySource/
SomeFuncs.c
SomeFuncs.h
The main.c contains the main function that uses functions defined in the SomeFuncs.{h/c} files.
I want to use autotools for this project. I read a couple of resources on autotools. But, I was only able to manage using autotools for a single level project where all source, object and other files reside in the same directory.
Then I got some links that talked about using autotools for deep projects like this one and then I got confused.
Right now I have two Makefile.am as follows
Makefile.am
SUBDIRS=src
src/Makefile.am
mainprgdir=../
mainprg_PROGRAMS=main
main_SOURCES=main.c
I am pretty sure that these files should not be as I have them now :P
How do I use autotools for the above project structure? (At least what should be there in those Makefile.am(s) and where should I place them.
EDIT:
One more thing! At the end I would like to have the object files created in the bin directory.
Thanks
mainprogdir=../ does not make a whole lot of sense (you don't know what it is relative to on installation). Probably intended:
# Main/Makefile.am
# .━━ target for `make install`
# |
# ↓ ↓━━ target for compilation
bin_PROGRAMS = bin/main
# ↓━━ based upon compilation target name
bin_main_SOURCES = src/main.c
There are two main approaches. If the functions in SomeLibrarySource are used only by main, then there's no need to build a separate library and you can simply specify the source files in src/Makefile.am
main_SOURCES = main.c SomeLibrarySource/SomeFuncs.c
However, if you actually want to use the functions in other code in your tree, you do not want to compile SomeFuncs.c multiple times but should use a convenience library.
# Assigning main_SOURCES is redundant
main_SOURCES = main.c
main_LDADD = SomeLibrarySource/libSomeFuncs.a
noinst_LIBRARIES = SomeLibrarySource/libSomeFuncs.a
AM_CPPFLAGS = -I$(srcdir)/SomeLibrarySource
(You'll need AC_PROG_RANLIB in configure.ac to use convenience libraries.)
If the source file is named SomeFuncs.c, automake will not need Makefile.am to specify SomeLibrarySource_libSomeFuncs_a_SOURCES, but if the name of the source code file does not match the name specified in noinst_LIBRARIES, SomeLibrarySource_libSomeFuncs_a_SOURCES should be set to the list of files used to build the library. Note that you do not need to specify main_SOURCES, since main.c is the default value if left unspecified (but it's not a bad idea to be explicit.) (In all of this, I am not comfortable use CamlCase names, but the system I'm using uses a case insensitive file system (biggest mistake apple ever made) and the examples I give here are working for me. YMMV)
You could of course do a recursive make, or build the library as a separate project and install it. (I like the final option. Libraries with useful features should exist on their own.)

Resources