When I first started developing on Linux and using Makefiles, I started with simple Makefile scripts and later built upon each preceding one. What eventuated is the attached set of files that should make compilation of multiple sources into a single target an easier exercise. There are, of course, many other alternatives, such as Automake, Bakefiles and CMake (all of which are far more sophisticated). If their 'sophistication' causes too much of a headache when you simply want to compile a couple of files, this could help - or could aid you in writing another Makefile-based solution for yourself.
The files are:
The simplest Makefile must contain something akin to the following:
# Makefile include shared.mk TARGET = <myapp> SOURCES = \ a.cpp \ b.cpp include defs.mk # 'sinclude' tells Make to include the file if it exists. If not, then it should # run Make with the goal by the same name (which should create the file) and then include it. sinclude depend
The top include
and bottom include & sinclude
are mandatory, and everything else must be inbetween.
defs.mk has the following input arguments that can be defined either in shared.mk or in the invoking Makefile:
# ADDITIONAL_INCLUDEPATHS # ADDITIONAL_LIBRARIES # ADDITIONAL_LIBRARYPATHS # OBJPATH (override default) # ADDITIONAL_PREREQPATHS (adds to VPATH) # TARGETDIR (override default) # SOURCES (list of sources files) # NOOBJPATH (set to override creation of OBJPATH) # TARGET (binary target without directory) # CVSIGNORE (additional files to add to .cvsignore) # EXTERNALOBJECTS (external object files with path) # SKIPDEPEND (set to anything to skip DEPEND) # CUSTOMCLEAN (additional commands to be run with 'clean') # CUSTOMDISTCLEAN (additional commands to be run with 'distclean') # SO_TARGET (if we're compiling a shared object) # NOCVS (don't create cvs files) # NOTARGETDIR (don't use the default target directory) # NODELETEALL (don't delete OBJPATH, only the sources' .o files inside) # SO_PARAMS (parsed in from directive in a RDF) # DEPEND_SOURCES (only create dependency file for these sources, not all sources) # # Available implicit source rules: # cpp -> o # c -> o
Some of these are 'booleans' in that if the variable is declared (the value is irrelevant) then a certain action will be taken (or not), eg: NOCVS
.
shared.mk should at the very least least contain:
# Sets various global variables DEPEND = depend
This could also be an opportunity to define some global include directories that your current target/down-level Makefiles might also need. A more complete example could be:
# Sets various global variables SERVERDIR = server CLIENTDIR = client GENERATORDIR = generator IDLDIR = idl INCLUDEDIR = include SRCDIR = src RULESDIR = rules DEPEND = depend ifndef SE3010DIR SE3010DIR = /home/se3010/soft endif # SE3010DIR ifndef ACE_ROOT ACE_ROOT = $(SE3010DIR)/src/ACE_wrappers endif # ACE_ROOT ifndef TAO_ROOT TAO_ROOT = $(ACE_ROOT)/TAO endif # TAO_ROOT XMLINCPATH = $(SE3010DIR)/include/libxml++-2.6/ \ $(SE3010DIR)/include/libxml2/ \ $(SE3010DIR)/include/libxml++-2.6/libxml++ \ $(SE3010DIR)/include/libxml2/libxml \ $(SE3010DIR)/include/glibmm-2.3/ \ $(SE3010DIR)/lib/glibmm-2.3/include/ \ /usr/include/glib-2.0/ \ /usr/lib/glib-2.0/include/ XMLLIBRARIES = glibmm-2.3 xml++-2.5 xml2
Now say that the server
, client
, etc, components are in lower-level directories by the same name (as can be seen at the top of the above sample code), which contain their own Makefiles. The top-level master Makefile could contain:
# Makefile for the BART System include shared.mk SUBDIRS = $(SERVERDIR) $(CLIENTDIR) $(GENERATORDIR) $(IDLDIR) # This is supposed to contain all the global rules in defs.mk all depend install clean distclean: $(SUBDIRS) # If this Makefile is supplied with a goal that is actually a sub-directory, # make that sub-directory, but with an *empty* goal (so it makes the default # goal for that sub-directory!) $(filter-out $(IDLDIR), $(SUBDIRS)): $(MAKE) -C $@ $(filter-out $(SUBDIRS), $(MAKECMDGOALS)) $(IDLDIR): $(MAKE) -C $@ $(filter-out $(SUBDIRS), $(filter-out depend, $(MAKECMDGOALS))) # Dependencies: $(DEPEND): idl $(SERVERDIR): idl $(CLIENTDIR): idl $(GENERATORDIR): idl # Empty goal for first deliverable #generator: .PHONY: $(SUBDIRS) all depend install clean distclean
This sets up a nice way of calling the down-level Makefiles automatically or manually, depending on the make
arguments.
The client
's Makefile, for example, could contain:
# Makefile for BARTClient include ../shared.mk PREFIX = ../ TARGET = ../src/BARTClient SOURCES = Main.cpp ClientCommandParser.cpp ../src/ClientInitialiser.cpp $(PREFIX)$(SRCDIR)/Log.cpp ADDITIONAL_INCLUDEPATHS = ADDITIONAL_LIBRARYPATHS = $(TAO_ROOT)/tao $(ACE_ROOT)/ace ADDITIONAL_LIBRARIES = TAO TAO_PortableServer TAO_CosNaming EXTERNALOBJECTS = $(PREFIX)$(IDLDIR)/$(OBJPATH)/BARTServiceC.o include ../defs.mk # 'sinclude' tells Make to include the file if it exists. If not, then it should # run Make with the goal by the same name (which should create the file) and then include it. sinclude depend
Attachment | Size |
---|---|
SimpleMakefileSystem.zip | 2.66 KB |