Have you ever wanted a generic, simple, easy to configure, flexible makefile for everyday development? Don’t want to have to invoke automake/autoconf or construct new makefiles by hand for each program/project? This may be what you’re looking for:

edam’s general-purpose makefile!

Features:

  • Suited to development, not distribution
  • Easy configuration (documented below, reference in the makefile itself)
  • Support for C, C++, assembly (nasm only) and D (gdc only) source files
  • Build modes (release, debug and profile mode)
  • Subdirectories/sub-projects (for multiple build targets and automatic recursion when building/cleaning)
  • A sophisticated dependency file generation system
  • Can live alongside another build system, such as a GNU autotools one.

Disclaimer:

This is only really meant to facilitate development and it is obviously still worth learning to use the GNU autotools. If you want a quick-start guild for autoconf and automake, you should read this great little guide instead. Also, this book, while a little uncoordinated IMO, is a much more thorough guide.

Download

You can get it here…

Download General-purpose Makefileversion 3.512K

changes

Documentation

Usage is simple. Create a file named “Makefile” alongside your source code. In the Makefile, specify your project’s settings (by defining some variables that describe it; documentation follows) and then include edam.mk at the end. It couldn’t be simpler.

Here is an example Makefile for a test program:

TARGET = my_program
SOURCES = main.cc foo.cc
LIBRARIES = bar
include ~/src/edam.mk

As you can see from the include line at the end, I keep the general-purpose makefile at ~/src/edam.mk. This means I can easily replace it globally when bugs are fixed and improvements made. You can just as easily keep a separate copy of edam.mk with each project’s source code though, if that is preferred.

Configuration

First, a couple of points:

Whenever a setting is “boolean”, you should set it to “1” to enable it and leave it unset (or set to an empty string) to disable it. Setting it to “0” will not unset it.

There is reference documentation at the top of edam.mk for all configuration variables to save you having to refer to this site.

Ok, on to the settings…

Target Settings

The TARGET variable specifies the name of the target file. By default an executable is built, so if you want to build a static or a shared library you will need to set the boolean MKSTATICLIB or MKSHAREDLIB variables.

For example, to build your shared libtim.so, you might go:

MKSHAREDLIB = 1
TARGET = libtim.so

You can only build one target per Makefile. If you need to build more, use the SUBPROJS variable (see below).

Also, you don’t need to specify the “.so” or “.a” suffixes in the TARGET as they are automatically appended. The same is true for the “lib” prefix for shared libraries. If you don’t want to have “lib” automatically prepended to the TARGET, you can set the boolean NOLIBPREFIX variable.

Specifying Build Input

Specify all source files you want to build and libraries you want to link against in the variables SOURCES and LIBRARIES. Both variables are space-separated lists.

For example:

SOURCES = foo.cc bar.cc
LIBRARIES = gl glu

How a file is compiled depends on it’s extension. The following extension types are recognised:

C source files.c
C++ source files.cc, .C, .cpp
assembly source files.s, .S, .asm
D source files.d

While you can happily mix different source file types in the SOURCES variable, you can not have two files named the same but with a different extension (such as “foo.cc” and “foo.c“). When compiled, they would both produce files of the same name (such as the object file “foo.o“).

Note that, as with the -l linker option, you don’t use a “lib” prefix when specifying LIBRARIES.

When the target is built, you can also tell the linker that you want to statically link against any libraries specified in the LIBRARIES variable. This is usually not desired (it may make the target very large and it negates the benefits of using shared libraries) but can be achieved by setting the boolean LINKSTATIC variable.

Building sub-projects and subdirectories

You may also want to build sub-projects or subdirectories. These can be specified with the SUBPROJS and SUBDIRS variables. Sub-projects and subdirectories are always built before the target since the target may depend on them (and subdirectories before sub-projects for the same reason). Both the SUBPROJS and SUBDIRS variables are space-separated lists.

The purpose of sub-projects is to allow you to build more than one target in a directory. Since you can only specify one target in your Makefile, the way to build more than one it is to create a separate makefile for each target and specify these makefiles as sub-projects in the main Makefile. So, for example, you might create the makefiles “proj1.mk” and “proj2.mk” which each build their own target. Then, in the main Makefile, you can specify proj1 and proj2 as sub-projects (note that “.mk” is automatically appended to sub-projects in the SUBPROJS variable).

For example:

SUBPROJS = proj1 proj2
SUBDIRS = subdir1 subdir2

would cause make to be run four times: first in the subdirectories subdir1 and subdir2 and then with the makefiles proj1.mk and proj2.mk.

Additionally, when subdirectories are built, if a file named “emake.mk” exists in a subdirectory, then this will be used instead of the default Makefile (or other files) that make defaults to using. This is to allow the use of this general-purpose makefile alongside other build systems.

Build Flags

If you need more control over build flags, use the following variables:

CPPFLAGSpassed to the C, C++ and D compiler
CFLAGSpassed to the C compiler only
CXXFLAGSpassed to the C++ compiler only
DFLAGSpassed to the D compiler only
ASFLAGSpassed to the assembler
LDFLAGSpassed to the linker before the list of object files
LDPOSTFLAGSpassed to the linker after the list of object files

For example:

CPPFLAGS = `pkg-config --cflags gtkmm-2.4`
LDPOSTFLAGS = `pkg-config --libs gtkmm-2.4`

Build Modes

When running make, there are three build modes:

  • release — this is the default. Output is optimised and symbol information stripped
  • debug — for use in everyday development with gdb.
  • profile — for profilling with gprof

Specifying the Build Mode

With no overriding options, the project will build in release-mode. There are several ways to initiate a build other than the default. This allows for some fairly flexible configurations. You can:

  • Create the environment variables DEBUGMODE and PROFILEMODE and set them to the value “1“, like this:
    $ export DEBUGMODE=1
    Or, alternatively, unset them like this:
    $ unset DEBUGMODE
  • Specify a build mode on the make command line, like this:
    $ make DEBUGMODE=1
    or, like this:
    $ make DEBUGMODE=
    This overrides any environment variables set (as above).
  • Specify values for DEBUGMODE or PROFILEMODE in your Makefile to hard-code the build mode (although this is probably a less preferable option).

The arrangement I would suggest for the purposes of development is to add the line

export DEBUGMODE=1

to your ~/.bashrc so that you are always building in debug-mode by default. Then, when you want to build in release-mode, run make like this:

$ make DEBUGMODE=

or to build in profile-mode, like this:

$ make PROFILEMODE=1

Build Mode Specific Files

When building your code, object files are generated. The release-, debug- and profile-mode versions of these files must be kept separate, as must the resulting executable or library.

To differentiate between build modes, debug-mode files have “_d” appended to them and profile-mode files have “_p” appended to them. So, for example, “test.cc” will compile into “test_d.o” in debug-mode. This, in turn, will compile into the executable “test_d“. Similarly, in profile-mode, “test.cc” will compile in to “test_p.o” which will become part of the executable “test_p“.

Dependency Files

In addition to the .o files, .dep files are generated to track the build dependencies between files. Unlike the .o files, these are not generated separately for each build-mode. In fact, they are only built in debug-mode.

In a nutshell, dependency files are built as a by-product of compilation (rather than separately to it), do not include system headers, and include dummy targets to prevent make complaining when depended-upon files are deleted or renamed. For more information about the dependency file generation system, see this excellent article on advanced auto-dependency generation. The full system outlined in the article is implemented with the slight improvement that dummy targets are created at compile-time, not in post-processing by a rather unsightly sed invocation. That said, the sed command is still used for other, less-able compilers/assemblers.

Finally, dependency files are not (currently) yet generated for D.

Make Goals

If you’ve used make before, you will know that you can specify a “goal” when running make, like this:

$ make subdirs

The full list of goals for the general-purpose makefile are:

allthe default; builds any subdirectories and sub-projects and then any target
subdirsbuilds the subdirectories (but not any target)
subprojsbuilds the sub-projects (but not any target)
targetbuilds the target (but not any subdirectories or sub-projects)
runbuilds the target (but not any subdirectories or sub-projects) and, on success, executes it
cleanremoves intermediate build files for the target only
clean_allremoves intermediate build files for the target and any subdirectories and sub-projects
<subdir>you can specify a specific subdirectory to build (so long as it is defined in SUBDIRS)
<subproj>you can specify a specific sub-project to build (so long as it is defined in SUBPROJS)
<file>you can also specify any object files, or the target, as with most makefiles

Co-existence with GNU autotools

If you are using this general-purpose makefile on a project that you want to release, it is likely that you will want to also use the GNU autotools along side it and package the familiar configure script. This, in turn, is likely to generate a Makefile of it’s own, which would obviously clash with the one you have created that uses this general-purpose makefile. Here is how I get them to play nicely together:

Firstly, I rename all the Makefiles that use this general-purpose makefile to emake.mk. If a subdirectory mentioned in the SUBDIRS variable contains a file named emake.mk, then it will be used instead of that subdirectory’s Makefile. This frees the Makefiles for use with GNU autotools, but creates another problem. Where before you would simply have needed to type

$ make

in any directory or subdirectory, you now have to type

$ make -f emake.mk

instead.

So, secondly, I also created the bash alias emake that tells make to use the emake.mk makefile:

$ alias emake='make -f emake.mk'

Development

Bugs

If you discover any bugs/issues, I would like to hear about them. Drop me an email with the details. I am also open to suggestions on how to improve this makefile.

Repository

You can obtain the latest development code from the bazaar repository at:

http://bzr.ed.am/make/edam-mk

Email this to someoneShare on RedditShare on FacebookTweet about this on TwitterShare on Google+

Comments

No comments

Leave a Reply




  • Categories
  • Archives
  • Meta
  • Thanks!
    • Send me bitcoins
    • Flattr this