Related tools are the language compilers (cc, f77, lex, yacc, etc.)
and shell programming tools (eg. awk, sed, cp, rm, etc.). You need to
know how to use these.
Important adjuncts are lint (source code checking for obvious errors)
ctags (locate functions, etc. in source code) and mkdepend. These are
nice, and good programmers use them.
Important, and related tools, are the software revision systems SCCS (Source Code Control System) and RCS (Revision Control System -- the recommended choice)
The idea is to automate and optimize the construction of
programs/files -- ie. to leave enough foot prints so that others can
follow.
make is going to look for a file called Makefile, if not found then a
file called makefile. Use the first (so the name stands out in
listings).
You can get away without any Makefile (but shouldn't)! Make has
default rules it knows about.
Comments are any text beginning with the pound (#) sign. A
comment can start anywhere on a line and continue until the end of the
line. For example:
# $Id: slides,v 1.2 1992/02/14 21:00:58 reggers Exp $
Make has a simple macro definition and substitution mechanism. Macros are defined in a Makefile as
MACROS= -me
PSROFF= groff -Tps
DITROFF= groff -Tdvi
CFLAGS= -O -systype bsd43
There are lots of default macros -- you should honor the existing naming conventions. To find out what rules/macros make is using type:
% make -p
NOTE: That your environment variables are exported into the make as macros. They will override the defaults.
You can set macros on the make command line:
% make "CFLAGS= -O" "LDFLAGS=-s" printenv
cc -O printenv.c -s -o printenv
You make a particular target (eg. make all), in none specified then the first target found:
paper.dvi: $(SRCS)
$(DITROFF) $(MACROS) $(SRCS) >paper.dvi
NOTE: The the line beginning with $(DITROFF) begins with TAB not spaces.
The target is made if any of the dependent files have changed. The dependent files in this case are represented by the $(SRCS) statement.
Use a back slash (\). This is important for long macros and/or rules.
There are lots of default macros (type "make -p" to print out the defaults). Most are pretty obvious from the rules in which they are used:
AR = ar
GFLAGS =
GET = get
ASFLAGS =
MAS = mas
AS = as
FC = f77
CFLAGS =
CC = cc
LDFLAGS =
LD = ld
LFLAGS =
LEX = lex
YFLAGS =
YACC = yacc
LOADLIBS =
MAKE = make
MAKEARGS = 'SHELL=/bin/sh'
SHELL = /bin/sh
MAKEFLAGS = b
Before issuing any command in a target rule set there are certain special macros predefined.
So, for example, we could use a rule
printenv: printenv.c
$(CC) $(CFLAGS) $? $(LDFLAGS) -o $@
printenv: printenv.c
$(CC) $(CFLAGS) $@.c $(LDFLAGS) -o $@
The general syntax of a Makefile Target Rule is
target [target...] : [dependent ....]
[command ...]
The semantics is pretty simple. When you say "make target"
make finds the target rule that applies and, if any of the
dependents are newer than the target, make executes the com-
mands one at a time (after macro substitution). If any
dependents have to be made, that happens first (so you have
a recursion).
A make will terminate if any command returns a failure sta-
tus. That's why you see rules like:
clean:
-rm *.o *~ core paper
Make will echo the commands, after macro substition to show
you what's happening as it happens. Sometimes you might want
to turn that off. For example:
install:
@echo You must be root to install
For example, to manage sources stored within RCS (sometimes
you'll need to "check out" a source file):
SRCS=x.c y.c z.c$(SRCS):
co $@
$(SRCS):
sccs get $@
SRCS=x.c y.c z.c
# GET= sccs get
GET= co$(SRCS):
$(GET) $@
lib.a: x.o y.o z.o
ar rvu lib.a x.o y.o z.o
ranlib lib.a
OBJ=x.o y.o z.o
AR=arlib.a: $(OBJ)
$(AR) rvu $@ $(OBJ)
ranlib $@
If you get used to using macros you'll be able to make a few
rules that you can use over and over again.
For example, to construct a library in some other directory
INC=../misc
OTHERS=../misc/lib.a$(OTHERS):
cd $(INC); make lib.a
INC=../misc
OTHERS=../misc/lib.a$(OTHERS):
cd $(INC)
make lib.a
To generate a tags file
SRCS=x.c y.c z.c
CTAGS=ctags -x >tagstags: $(SRCS)
${CTAGS} $(SRCS)
lint:
lint $(CFLAGS) $(SRCS)
People have come to expect certain targets in Makefiles. You
should always browse first, but it's reasonable to expect
that the targets all (or just make), install, and clean will
be found.
You may encounter other common targets, some have been
already mentioned (tags and lint).
# make the printenv command
#
OWNER=bin
GROUP=bin
CTAGS= ctags -x >tags
CFLAGS= -O
LDFLAGS= -s
CC=cc
GET=co
SRCS=printenv.c
OBJS=printenv.o
SHAR=shar
MANDIR=/usr/man/manl/printenv.l
BINDIR=/usr/local/bin
DEPEND= makedepend $(CFLAGS)
all: printenv# To get things out of the revision control system
$(SRCS):
$(GET) $@
# To make an object from source
$(CC) $(CFLAGS) -c $*.c# To make an executable
printenv: $(OBJS)
$(CC) $(LDFLAGS) -o $@ $(OBJS)# To install things in the right place
install: printenv printenv.man
$(INSTALL) -c -o $(OWNER) -g $(GROUP) -m 755 printenv $(BINDIR)
$(INSTALL) -c -o $(OWNER) -g $(GROUP) -m 644 printenv.man $(MANDIR)# where are functions/procedures?
tags: $(SRCS)
$(CTAGS) $(SRCS)# what have I done wrong?
lint: $(SRCS)
lint $(CFLAGS) $(SRCS)# what are the source dependencies
depend: $(SRCS)
$(DEPEND) $(SRCS)# to make a shar distribution
shar: clean
$(SHAR) README Makefile printenv.man $(SRCS) >shar# clean out the dross
clean:
-rm printenv *~ *.o *.bak core tags shar# DO NOT DELETE THIS LINE -- make depend depends on it.
printenv.o: /usr/include/stdio.h
Consider the rule we used for printenv
printenv: printenv.c
$(CC) $(CFLAGS) printenv.c $(LDFLAGS) -o printenv
printenv: printenv.c
$(CC) $(CFLAGS) $@.c $(LDFLAGS) -o $@
.c:
$(CC) $(CFLAGS) $@.c $(LDFLAGS) -o $@
Another common implicit rule is for the construction of .o
(object) files out of .c (source files).
.o.c:
$(CC) $(CFLAGS) -c $<
alternatively
.o.c:
$(CC) $(CFLAGS) -c $*.c
It's pretty common to have source code that uses include
files. For example:
% cat program.c#include
#include "defs.h"
#include "glob.h"
etc....
main(argc,argv)
etc...
The implicit rule only covers part of the source code depen-
dency (it only knows that program.o depends on program.c).
The usual method for handling this is to list the dependen-
cies separately;
etc...
$(CC) $(CFLAGS) -c $*.c
etc...
program.o: program.c defs.h glob.h
However, there are a number of nice tools around that will
automatically generate dependency lists for you. For example
(trivial):
DEPEND= makedepend $(CFLAGS)
etc...
# what are the source dependenciesdepend: $(SRCS)
$(DEPEND) $(SRCS)etc....
# DO NOT DELETE THIS LINE -- ....printenv.o: /usr/include/stdio.h