Building with Non-Recursive Make
This website will teach you a method of building C-coded programs using GNU Make.
This method combines ideas and code from multiple sources, in order to satisfy the following four objectives:
1. Suitable for a large project
Geared toward building a project, that is comprised of user's own libraries, and the programs that use those libraries.
Source and header files reside in source directories, while the built objects and executables reside in their own build directories.
Multiple build directories can coexist, one for each build type, e.g. for debug, release and profiling purposes.
2. Use non-recursive make
Use a single, simple to follow, non-recursive Makefile to build an object, a library or a program.
Since that Makefile is long, it is divided into multiple smaller Makefiles. As it turns out, most of the code in those files is needed by all builds, and thus a great deal of code reuse is achieved too.
With this method,
- An object or a library is defined by a simple Makefile.
- Whoever depends on an object or a library has to include the latter's Makefile in its own.
The result is a modular approach for describing dependencies among objects, libraries and programs.
3. Call Makefile from the source directory
The top-level Makefile is called from the source directory, where the code is, but the built occurs in a separate build directory.
That Makefile gets executed twice:
During the first execution, it ensures the selected build directory exists (otherwise it gets created), and switches to it.
Then, the Makefile calls itself, this time indicating that the local directory is the build directory, so that all targets are built there.
4. Auto-generate dependency lists
Let GNU Make generate the source file dependency lists, and then use them as part of the compile rule, instead of the user manually including dependencies in the Makefile.
In a downloadable example, I demonstrate this method - building a library, and a project that uses it. Most of the "heavy lifting" code, that builds these items, is encapsulated in a set of common helper Makefiles, thus leaving the top-level Makefile and the object and library defining Makefiles small and simple to understand and edit.
This method combines ideas and code from multiple sources, in order to satisfy the following four objectives:
1. Suitable for a large project
Geared toward building a project, that is comprised of user's own libraries, and the programs that use those libraries.
Source and header files reside in source directories, while the built objects and executables reside in their own build directories.
Multiple build directories can coexist, one for each build type, e.g. for debug, release and profiling purposes.
2. Use non-recursive make
Use a single, simple to follow, non-recursive Makefile to build an object, a library or a program.
Since that Makefile is long, it is divided into multiple smaller Makefiles. As it turns out, most of the code in those files is needed by all builds, and thus a great deal of code reuse is achieved too.
With this method,
- An object or a library is defined by a simple Makefile.
- Whoever depends on an object or a library has to include the latter's Makefile in its own.
The result is a modular approach for describing dependencies among objects, libraries and programs.
3. Call Makefile from the source directory
The top-level Makefile is called from the source directory, where the code is, but the built occurs in a separate build directory.
That Makefile gets executed twice:
During the first execution, it ensures the selected build directory exists (otherwise it gets created), and switches to it.
Then, the Makefile calls itself, this time indicating that the local directory is the build directory, so that all targets are built there.
4. Auto-generate dependency lists
Let GNU Make generate the source file dependency lists, and then use them as part of the compile rule, instead of the user manually including dependencies in the Makefile.
In a downloadable example, I demonstrate this method - building a library, and a project that uses it. Most of the "heavy lifting" code, that builds these items, is encapsulated in a set of common helper Makefiles, thus leaving the top-level Makefile and the object and library defining Makefiles small and simple to understand and edit.