Microsoft PowerPoint – 7_Makefiles
O
SU
C
SE
2
42
1
J.E.Jones
There’s a good demo at:
Likely, one of the best 15 minutes you will spend this semester (on coursework).
GNU Makefile manual: https://www.gnu.org/software/make/manual/make.html
For your reference
O
SU
C
SE
2
42
1
J.E.Jones
% gcc –o hello hello.c hello1.c hello2.c …
Source code
hello.i
Assembly Code (hello.s)
Libraries
(e.g., printf() code) Object Code (hello.o)
Executable Code
% hello [executes the program]
Preprocessor
Compiler
Assembler
Link Editor/
Linker
hello.c
hello
Type in program source code (file.c) using
an editor of your choice; plain text
.c + .h = .i which is the ultimate source
code – i.e., #includes expanded and
#defines replaced, comments removed
C syntax parser
.i → .s which is assembler source code
Assembler code parser
.s → .o which is an object file; fragments of
machine code with unresolved symbols, i.e.,
some addresses not yet known (vars/subrs).
.o + library links a.out (default name);
resolves symbols, generates an executable
Repeats for each
.c file until each
.o is created
O
SU
C
SE
2
42
1
J. E. Jones
make is a utility for building and maintaining groups of
programs (and other types of files) from source code
The purpose of the make utility is to determine
automatically which pieces of a large program need to
be re-compiled and issue the commands necessary to
recompile them.
To use make, you must create a file call Makefile (or
makefile)that describes the relationships among files in
your program
make decides whether a target needs to be regenerated
by comparing file modification times
gcc –ansi –pedantic –g –o system file1.c file2.c …
file42.c…file150.c
O
SU
C
SE
2
42
1
J. E. Jones
make is a Unix/linux utility, which is used to manage building
(compilation) of programs, but it can do other things, too.
It is very commonly used in industry in Unix/linux environments.
For a large program, it is more efficient for the developer and the
machine if the program is written as a number of smaller files;
recompiling each of these various files that make up the program is
only done when something changes which requires recompilation.
This also promotes reusability of code, because source code for
functions can be kept in separate files, and the file can be called and
compiled in any program for which it is useful.
Makefiles contain file dependency lists and UNIX commands and
will run them in a specified sequence.
Anything that can be entered at the UNIX/linux command prompt
can be in the Makefile.
O
SU
C
SE
2
42
1
J. E. Jones
The name of your Makefile must be: Makefile or makefile in this class (which are
the two default options)
The directory you put the Makefile in matters!
You can only have one Makefile per directory.
A simple makefile consists of “rules” with the following format:
target: dependencies …
Unix/linux commands…
…
…
where:
target is usually the name of a file that is generated by a program; examples of targets
are executable or object files. A target can also be the name of an action to carry out, such as
‘clean’. We are creating a .zip target in our Makefiles.
dependency is a file that is used as input to create the target. A target often depends on
several files. Thus “dependencies” above.
Unix/linux command is an action that make carries out. More than one command,
either on the same line or each on its own line can be listed
Note: you need to put a tab character at the beginning of every command line!
O
SU
C
SE
2
42
1
J. E. Jones
MAKEFILES ARE UNFORGIVING WHEN IT COMES TO
WHITESPACE!
To run make… you must be in the directory where the file Makefile
is. (there are command line options to change this)
make is a Unix/linux utility program
Makefiles can only accept single line comments and they start with
a #
The command to run make on the Makefile file in a directory:
% make target
Or
% make
Note: target is also called section name or tag-name, if no target is
referenced, the first target in the Makefile is used.
O
SU
C
SE
2
42
1
J. E. Jones
Let’s look at a program with multiple source files that could be built with
make. Let’s suppose we want the executable to be called mkprog
/* mkfunc.h */
/* The header file contains function
prototypes for all functions in the
program, except main() and
library functions
*/
void print_hello();
int fact(int n);
/* mkfact.c */
#include “mkfunc.h”
/* ” ”, not < > because it’s a local file */
int fact(int n) {
if (n != 1) {
return (n * fact(n – 1));
}
else return 1;
}
O
SU
C
SE
2
42
1
J. E. Jones
/* mkhello.c */
#include “mkfunc.h”
#include
void print_hello() {
printf(“Hello World!\n”);
}
/* mkmain.c */
#include “mkfunc.h”
#include
int main() {
print_hello();
printf(“5 factorial is %d”, fact(5));
return 0;
}
O
SU
C
SE
2
42
1
J. E. Jones
Compiling our example from the command line would look like:
gcc -ansi -pedantic -Wimplicit-function-declaration -Wreturn-type -g -c -o mkprog mkmain.c mkhello.c mkfact.c
The more files needed to create an executable module (e.g., mkprog), the
more likely it is that we will “fat finger” the command.
The history feature of the bash shell, helps this some, but what if we
haven’t compiled the program in the last 20 commands?
“all” should be the default target for a makefile
% make all
The make utility will execute this target, “all”, by default, if no other one is
specified, so the following command has the same effect:
% make /* target “all” is implied */
Using the –c option with gcc means that compilation of the specified file
will stop when the .o file of the same name has been created (for example,
gcc –c test.c creates test.o). No linking with other .o files is done.
O
SU
C
SE
2
42
1
J. E. Jones
# This is a Makefile for an executable called mkprog
gcc_opt = -ansi -pedantic -Wimplicit-function-declaration -Wreturn-type -g -c
# mkprog is comprised of three .c files and one .h file
all: mkprog # we are creating mkprog with this makefile
#target mkprog must be re-created if mkmain.o, mkfact.o, or mkhello.o change
mkprog: mkmain.o mkfact.o mkhello.o #mkprog needs 3 .o files
gcc mkmain.o mkfact.o mkhello.o -o mkprog #command to create mkprog
#mkmain.o must be re-created if mkmain.c or mkfunc.h change
mkmain.o: mkmain.c mkfunc.h
gcc $(gcc_opt) mkmain.c # this command creates mkmain.o
#mkfact.o must be re-created if mkfact.c or mkfunc.h change
mkfact.o: mkfact.c mkfunc.h
gcc $(gcc_opt) mkfact.c # this command creates mkfact.o
#mkhello.o must be re-created if mkhello.c or mkfunc.h change
mkhello.o: mkhello.c mkfunc.h
gcc $(gcc_opt) mkhello.c # this command creates mkhello.o
#clean is another target. It removes all created dependency files so that
#all files are forced to recompile
clean:
rm –rf *.o mkprog
O
SU
C
SE
2
42
1
J. E. Jones
mkfunc.h
#define MAX_ENTRIES 50
mkmain.c
main(){
int entries[MAX_ENTRIES];
for (i=0; i< MAX_ENTRIES; i++){ } } O SU C SE 2 42 1 J. E. Jones # This is a Makefile for an executable called mkprog gcc_opt = -ansi -pedantic -Wimplicit-function-declaration -Wreturn-type -g -c # mkprog is comprised of three .c files and one .h file all: mkprog # we are creating mkprog with this makefile #target mkprog must be re-created if mkmain.o, mkfact.o, or mkhello.o change mkprog: mkmain.o mkfact.o mkhello.o #mkprog needs 3 .o files gcc mkmain.o mkfact.o mkhello.o -o mkprog #command to create mkprog #mkmain.o must be re-created if mkmain.c or mkfunc.h change mkmain.o: mkmain.c mkfunc.h gcc $(gcc_opt) mkmain.c # this command creates mkmain.o #mkfact.o must be re-created if mkfact.c or mkfunc.h change mkfact.o: mkfact.c mkfunc.h gcc $(gcc_opt) mkfact.c # this command creates mkfact.o #mkhello.o must be re-created if mkhello.c or mkfunc.h change mkhello.o: mkhello.c mkfunc.h gcc $(gcc_opt) mkhello.c # this command creates mkhello.o #clean is another target. It removes all created dependency files so that #all files are forced to recompile clean: rm –rf *.o mkprog Must use a tab here NO spaces! O SU C SE 2 42 1 J. E. Jones # This is a Makefile for an executable called mkprog # mkprog is comprised of four .c files and one .h file gcc_opt = -ansi -pedantic -Wimplicit-function-declaration -Wreturn-type -g -c all: mkprog mkprog: mkmain.o mkfact.o mkhello.o mk_newfunc.o gcc mkmain.o mkfact.o mkhello.o mk_newfunc.o -o mkprog mkmain.o: mkmain.c mkfunc.h gcc $(gcc_opt) mkmain.c mkfact.o: mkfact.c mkfunc.h gcc $(gcc_opt) mkfact.c mkhello.o: mkhello.c mkfunc.h gcc $(gcc_opt) mkhello.c mk_newfunc.o: mk_newfunc.c mkfunc.h gcc $(gcc_opt) mk_newfunc.c clean: rm –rf *.o mkprog O SU C SE 2 42 1 J. E. Jones Based on the makefile above, make will create a dependency table: mkprog mkmain.o mkfact.o mkhello.o mkmain.c mkfact.c mkhello.c mkfunc.h mkfunc.h mkfunc.h *Using this table, make can determine what to recompile based on changes in any of the files. O SU C SE 2 42 1 J. E. Jones % make clean rm –rf *.o mkprog % make gcc -ansi -pedantic -c mkmain.c gcc -ansi -pedantic -c mkfact.c gcc -ansi -pedantic -c mkhello.c gcc mkmain.o mkfact.o mkhello.o -o mkprog % mkprog Hello World! 5 factorial is 120 % make make: Nothing to be done for ‘all’. O SU C SE 2 42 1 J. E. Jones # comments in a Makefile start with sharp gcc_opt = -ansi -pedantic -Wimplicit-function-declaration -Wreturn-type -g -c # target all means all targets currently defined in this file # we have 3 separate targets in this Makefile, our zip file and 2 executables all: lab2.zip bit_encode1 bit_encode2 # this target is the .zip file that must be submitted to Carmen # if the dependencies (Makefile, bit_encode.c or LAB2README) change, a new zip file is created. lab2.zip: Makefile bit_encode.c LAB2README zip lab2.zip Makefile bit_encode.c LAB2README # this target is the bit decryption executable that doesn't prompt for input bit_encode2: bit_decode2.o gcc bit_encode2.o -o bit_encode2 # this target is the dependency for bit_encode2 # we use "-o bit_encode2.o" here so that the .o file doesn't have the same name as the .c file # most of the time we can use the default .o file, but in this instance, we are creating # two separate .o files using the same .c file, so we must specify a unique name for the .o file bit_encode2.o: bit_encode.c gcc $(gcc_opt) -o bit_encode2.o bit_encode.c O SU C SE 2 42 1 J. E. Jones # this target is the bit decryption executable that prompts for input from the keyboard bit_encode1: bit_encode1.o gcc bit_encode1.o -o bit_encode1 # this target is the dependency for bit_encode1 # we use "-o bit_encode1.o" here so that the .o file doesn’t # have the same name as the .c file # note that this compile includes "-D PROMPT" so that our code # once the #ifdef’s are written in the code compile with the statements included bit_encode1.o: bit_encode.c gcc $(gcc_opt) -D PROMPT -o bit_encode1.o bit_encode.c # this target deletes all files produced from the Makefile # so that a completely new compile of all items is required clean: rm -rf *.o bit_encode1 bit_encode2 lab2.zip O SU C SE 2 42 1 J. E. Jones # comments in a Makefile start with sharp gcc_opt = -ansi -pedantic -Wimplicit-function-declaration -Wreturn-type -g -c # target all means all targets currently defined in this file # we have 3 separate targets in this Makefile, our zip file and 2 executables all: lab2.zip bit_encode1 bit_encode2 bit_decode # this target is the .zip file that must be submitted to Carmen # if the dependencies (Makefile, bit_decode.c or LAB2README) change, a new zip file is created. lab2.zip: Makefile bit_encode.c LAB2README bit_decode.c zip lab2.zip Makefile bit_encode.c LAB2README bit_decode.c # this target is the bit decryption executable that doesn't prompt for input bit_encode1: bit_encode1.o gcc bit_encode1.o -o bit_encode1 # this target is the dependency for bit_encode1 # we use "-o bit_encode2.o" here so that the .o file doesn't have the same name as the .c file # most of the time we can use the default .o file, but in this instance, we are creating # two separate .o files using the same .c file, so we must specify a unique name for the .o file bit_encode1.o: bit_encode.c gcc $(gcc_opt) -o bit_encode1.o bit_encode.c O SU C SE 2 42 1 J. E. Jones # this target is the bit decryption executable that prompts for input from the keyboard bit_encode2: bit_encode2.o gcc bit_encode2.o -o bit_encode2 # this target is the dependency for bit_encode2 # we use "-o bit_encode2.o" here so that the .o file doesn’t # have the same name as the .c file # note that this compile includes "-D PROMPT" so that our code # where we use ifdef's gets included bit_encode2.o: bit_encode.c gcc $(gcc_opt) -D PROMPT -o bit_encode2.o bit_encode.c # this target is the decode program bit_decode: bit_decode.o gcc bit_decode.o -o bit_decode # this target is the dependency for bit_decode.o bit_decode.o: bit_decode.c gcc $(gcc_opt) bit_decode.c # this target deletes all files produced from the Makefile # so that a completely new compile of all items is required clean: rm -rf *.o bit_encode1 bit_encode2 bit_decode lab2.zip Notice we are using the default .o file here No –o option listed.