Command Line Arguments and Makefiles in C
Makefiles in C
CSE 2421
Linux/Unix Make Command
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
Makefile Overview
make is a Unix/Linux utility, which is used to manage building (compilation) of programs.
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.
Writing and executing a makefile
The name of your makefile has to 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.
Each command must be preceded by a TAB and is immediately followed by hitting the enter key.
MAKEFILES ARE UNFORGIVING WHEN IT COMES TO WHITESPACE!
To run make… you must be in the directory where the makefile is.
make is a Unix/Linux utility program
Makefiles can only accept single line comments and they start with a #
Command to run make on the makefile in the directory:
% make tag-name
Note: tag-name is also called section name
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
Example makefile: Suppose program with the following files
/* 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 < > */
int fact(int n) {
if (n != 1) {
return (n * fact(n – 1));
}
else return 1;
}
. . . . and the following files
/* 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;
}
Makefile details
Compiling our example would look like:
gcc -ansi -pedantic -o mkprog mkmain.c mkhello.c mkfact.c
The basic makefile is composed of lines:
target: dependencies
[tab] system command [These are known as rules]
“all” is the default target for makefiles
% make all
This executes:
gcc –ansi –pedantic -o mkprog mkmain.c mkhello.c mkfact.c
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 */
Makefile for the above program
# This is a makefile for an executable called mkprog
# mkprog is comprised of three .c files and one .h file
all: mkprog
mkprog: mkmain.o mkfact.o mkhello.o
gcc mkmain.o mkfact.o mkhello.o -o mkprog
mkmain.o: mkmain.c mkfunc.h
gcc –ansi –pedantic –c mkmain.c
mkfact.o: mkfact.c mkfunc.h
gcc –ansi –pedantic –c mkfact.c
mkhello.o: mkhello.c mkfunc.h
gcc –ansi –pedantic –c mkhello.c
clean:
rm –rf *.o mkprog
What if we added a new file?
# This is a makefile for an executable called mkprog
# mkprog is comprised of four .c files and one .h file
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 –ansi –pedantic –c mkmain.c
mkfact.o: mkfact.c mkfunc.h
gcc –ansi –pedantic –c mkfact.c
mkhello.o: mkhello.c mkfunc.h
gcc –ansi –pedantic –c mkhello.c
mk_newfunc.o: mk_newfunc.c mkfunc.h
gcc –ansi –pedantic –c mk_newfunc.c
clean:
rm –rf *.o mkprog
Dependency Table
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.
Examples from the command line
/home/4/jones.5684/cse2421
% make clean
rm –rf *.o mkprog
/home/4/jones.5684/cse2421
% 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
/home/4/jones.5684/cse2421
% mkprog
Hello World!
5 factorial is 120
/home/4/jones.5684/cse2421
% make
make: Nothing to be done for ‘all’.
Lab 2 Makefile
# comments in a Makefile start with sharp
# target all means all targets currently defined in this file
all: lab2.zip bit_encode1 bit_encode2
# this target is the .zip file that must be submitted to Carmen
lab2.zip: Makefile bit_encode.c
zip lab2.zip Makefile bit_encode.c
# this target is the bit cipher executable that requires redirected stdin
bit_encode1: bit_encode1.o
gcc bit_encode1.o -o bit_encode1
# this target is the dependency for bit_encode1
bit_encode1.o: bit_encode.c
gcc -ansi -pedantic -g -c -o bit_encode1.o bit_encode.c
# this target is the bit cipher 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
bit_encode2.o: bit_encode.c
gcc -ansi -pedantic -D PROMPT -g -c -o bit_encode2.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
What if we added a decode.c?
# comments in a Makefile start with sharp
# target all means all targets currently defined in this file
all: lab2.zip bit_encode1 bit_encode2 decode
# this target is the .zip file that must be submitted to Carmen
lab2.zip: Makefile bit_encode.c decode.c
zip lab2.zip Makefile bit_encode.c decode.c
# this target is the bit cipher executable that requires redirected stdin
bit_encode1: bit_encode1.o
gcc bit_encode1.o -o bit_encode1
# this target is the dependency for bit_encode1
bit_encode1.o: bit_encode.c
gcc -ansi -pedantic -g -c -o bit_encode1.o bit_encode.c
# this target is the decode program
decode: decode.o
gcc decode.o -o bit_decode
# this target is the dependency for bit_encode1
decode.o: decode.c
gcc -ansi -pedantic -g -c -o decode.o decode.c
# this target is the bit cipher 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
bit_encode2.o: bit_encode.c
gcc -ansi -pedantic -D PROMPT -g -c -o bit_encode2.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 decode lab2.zip