CPS 346 & 444/544 Lecture notes:
Compilation (Makefiles) and configuration (RCS) management
Coverage: [UPE] Chapter 8, §8.1 (pp. 241-242) and §8.3 (pp. 254-258) and
[USP] Appendix A, §A.3 (pp. 807-809)
Compilation management
Overview of make
touch
- change file access and modification times
- if file exists, touch brings its timestamp up-to-date
- if file does not exist, touch creates an empty file
$ touch foo.c
make is the standard compilation management tool for UNIX systems;
it is a program designed to simplify the maintenance of other
programs. Its input is a list of specifications as to the files upon which
programs and other files depend. This list is usually stored in a file named
Makefile or makefile.
When files (say, one or more source files used to build a given program) are
modified, you can use make to bring the executable version of the
program up to date. make uses the dependencies specified in the
Makefile to ensure that all derived files are brought up to date
after source files change, while doing the minimum possible amount of work.
make only recompiles when it is necessary.
Why Makefile?
Naming your Makefile Makefile typically ensures
that it will be listed first in an ls listing. However,
a makefile can have any filename when you use the -f
option to make (e.g., $ make -f mymakespecs)
Comment lines start with a # character (anywhere except in a
command line), and continue until the end of the line.
Directives
A directive describes the relationship between a target
(the file to be built), its dependencies or sources (the
files used to create the target), and zero or more command
lines which define the build process for the target:
target: source1 source2 ...
command1
command2
Long lists can be continued over multiple lines using \ as a line
continuation character. Command lines must begin with a <TAB> character.
By placing a @ character at the beginning of a command, you prevent
it from being echoed to stdout by make as it is executed. By placing
a - character at the beginning of a command, you can instruct make to
ignore any non-zero exit status for that command.
What will make do?
To see what make will do without having it actually invoke any
commands, use the -n command-line option. Then make will
echo the commands it would have performed on stdout, but it will not actually
invoke any of the commands. Use the -d to print debugging information
in addition to normal processing. Using the -d and -n options
together is helpful for debugging a Makefile.
Simple example
Create the following 3 files using touch:
button.c window.c window.h
This is our source code. These are zero length files; we are
simply using them for instructional purposes.
Dependency chart:
Assuming the following are the commands to build (make) the executable popup:
gcc -c button.c → button.o
gcc -c window.c → window.o
gcc -o popup button.o window.o → popup
Write a Makefile to build this program.
Since these are files are not really source code, we must use make -n.
#Makefile
all: popup
popup: button.o window.o
gcc -o popup button.o window.o
button.o: button.c
gcc -c button.c
window.o: window.c window.h
gcc -c window.c
[USP] §2.13 (pp. 55-56) dependency tree
[USP] §2.13 (pp. 55-56) Makefile
all: a.out
a.out: loggerlib.o logapp.o logapplib.o
$(CC) loggerlib.o logapplib.o logapp.o
logapp.o: logapp.c
$(CC) $(OPTS) logapp.c
logapplib.o: loggerlib.h logapplib.c
$(CC) $(OPTS) logapplib.c
loggerlib.o: loggerlib.h loggerlib.c
$(CC) $(OPTS) loggerlib.c
clean:
@-rm *.o a.out
Variables
You can define variables in a Makefile and use them:
CC = gcc
LIST_OF_FILES = file1.c file2.c \
file3.c file4.c
program1: $(LIST_OF_FILES)
$(CC) $(LIST_OF_FILES) -o program1
Environment variables
You can refer to environment variables defined in your shell within a
Makefile just as if they were declared right in the Makefile.
Further, environment variable definitions override any global
definitions appearing in the Makefile when make is invoked
with the -e option.
$ export LIST_OF_FILES="file1.c file2.c file3.c file4.c file5.c"
$ make -e program1
Variables on the command line
You can also define/redefine variables on the make command line:
$ make LIST_OF_FILES="file1.c file2.c file3.c file4.c file5.c" program1
These definitions override whatever appears in the Makefile itself.
Default suffix
rules
It is possible to define default rules for constructing
certain kinds of files from their dependencies. Such rules
will be used if no command lines are given for a particular
target. For example, to say how a C program should be
compiled into an object file:
.c.o:
$(CC) $(CFLAGS) $< -o $@
Here, the $< and $@ are special
variables which refer to the source file for the current line, and the
current target file, respectively. The default suffix rules for updating
an object library are:
.c.a:
$(CC) -c $(CFLAGS) $<
ar rv $@ $*.o
rm -f $*.o
Here $* refers to the filename part (without suffix) of
the prerequisite. An example of a library reference in a Makefile is:
prog: lib(sub1) lib(sub2) lib((module1)) prog.o
$(CC) -o $@ prog.o lib
System default
make definitions
make actually reads its default rules and default macro
definitions from system files stored in /usr/share/mk. The
file sys.mk in this directory is normally read by make.
It defines rules for C, C++, FORTRAN, Pascal, assembly, lex, yacc
files. It also defines basic macros/variables for the
compilers used for such files.
mkdep
By default, the FreeBSD version of make will read the file
.depend in the current directory in addition to
[Mm]akefile. It is normally invoked as:
mkdep [cc-options] file1.c file2.c ...
It automatically produces directives (with no command lines) for all listed
files, using the C preprocessor to determine the exact dependency list.
Correct use of default rules and variables can then be used to direct the
compilation of the corresponding C files.
The GNU version of mkdep is gccmkdep.
Configuration management (RCS)
RCS (Revision Control System) is used for managing software development
projects. It controls access to, and changes to, files by permitting only one
authorized user at a time to modify a file. By default, RCS controlled files
are stored in a directory RCS, and have the suffix ,v.
Version numbers have the form release.level.branch.sequence (e.g.,
4.2.1.20).
predecessor of CVS
Sample RCS session
127 Cayuga> mkdir RCS
128 Cayuga> rcs -i blitz # initialize file in RCS system
RCS file: RCS/blitz,v
enter description, terminated with single '.' or end of file:
NOTE: This is NOT the log message!
>> Shell script for blitzing directories, named after the
>> Wehrmacht Blitzkrieg tactic.
>> .
done
129 Cayuga> rcs -alat,egm,ribbens,mcquain blitz #authorize users
RCS file: RCS/blitz,v
done
130 Cayuga> rcs -elat blitz # deauthorize user
RCS file: RCS/blitz,v
done
131 Cayuga> ci blitz # check in file, version number assigned
RCS/blitz,v <-- blitz
initial revision: 1.1
done
131 Cayuga> ls -l RCS
132 Cayuga> co -l blitz # check out file with exclusive right to modify
RCS/blitz,v --> blitz
revision 1.1 (locked)
done
133 Cayuga> ex blitz # edit file
"blitz" 14 lines, 879 characters
:$a
Junk line at end.
.
:wq
"blitz" 15 lines, 897 characters
134 Cayuga> ci blitz # check modified file back in
RCS/blitz,v <-- blitz
new revision: 1.2; previous revision: 1.1
enter log message, terminated with single '.' or end of file:
>> Added junk line at end using ex.
>> .
done
135 Cayuga> rcs -o1.1 blitz # delete old version
RCS file: RCS/blitz,v
deleting revision 1.1
done
136 Cayuga> rlog blitz # gives modification history
Packaging and compression
utilities
- ar (maintain file library)
$ ar t /usr/lib/libc.a | grep '^printf.o'
printf.o
$ ar qv project.ar *.c
$ ar t project.ar
$ ar rvb foob.c project.ar fooa.c
$ ar xv project.ar fooa.c foob.c
- tar (tape archiver; create and manipulate tar archive files)
$ tar cvf p1.tar myshell.c helper.c other.c Makefile # creates the p1.tar archive
$ tar tf p1.tar # lists the contents of p1.tar
$ tar xvf p1.tar # extracts the p1.tar archive
$ tar cvpf p1.tar myshell.c helper.c other.c Makefile # preserves file permissions
$ tar cvzf p1.tgz myshell.c helper.c other.c Makefile # compresses the data
$ tar cvpzf p1.tgz myshell.c helper.c other.c Makefile # preserves and compresses
$ tar cvpzf p1.tgz p1 # creates archive rooted at directory p1
$ tar xvzf p1.tgz p1 # extracts the compressed archive p1.tgz
- gzip, gunzip, zcat
(compress or expand files)
$ gzip foo.tar
$ gunzip foo.tar.gz
$ zcat foo.tar.gz | tar tvf -
- compress, uncompress (compress and expand data)
$ compress foo.tar
$ uncompress foo.tar.Z
- zip, unzip
(package and extract compressed archive files)
References
| [UPE] |
B.W. Kernighan and R. Pike. The UNIX Programming Environment.
Prentice Hall, Upper Saddle River, NJ, Second edition, 1984.
|
|