[an error occurred while processing this directive]
make
Program But for larger programs, higher-level components are needed. Consider a calculator program that calculates the values of C-like real expressions involving variables and can assign values to variables. A program designer can come up with a reasonable decomposition of this program into high-level components without having to consider details about how those components are implemented.
First of all, since the expressions involve variables, there is a need to be able to save and retrieve values of variables. The need for this kind of functionality arises often in programs, and there are well-known data structures called tables that provide that kind of functionality.
Second, file input is in terms of characters, but understanding expressions and how they are evaluated is easiest with units called tokens. A token is a unit of text, such as a number or identifier or an operator that may contain more than one character. For a calculator program, it is useful to have a high-level component that breaks up a stream of characters into these tokens and classifies them.
With these two components, the work of the main program is much easier to accomplish, with the responsibility of token classification and variable value storage and retrieval delegated to other components.
Both components involve several functions that work together in a
coordinated way.
For example, the table component will require a function for assigning a
value to a variable and another for retrieving the value of a variable, and
it will also need some sort of body of data (perhaps an array) for saving
the data.
Since these components have distinct responsibilities, it is desirable to
be able to put them into separate code files table.C
and
tokens.C
, and compile them and test them separately.
The main program is then written in a third file
calculator.C
, and it is tested and compiled after the
others have been thoroughly tested.
executable file
.
An object file contains coding of the source code file into language that
the machine understands.
It is incomplete in that it may contain calls to functions that are in
other files, and it need not contain a main function.
When Unix compilers produce an object file, it has the same name as the
source code file except that the suffix is changed to .o
.
Suppose you want to compile prog.C
to produce the
executable program prog
.
Normally, you would give the command
g++ -Wall -o prog prog.C
g++
program compiles prog.C
and creates
the object file prog.o
.
Then it links prog.o
with the standard C libraries and creates
the executable file prog
.
When called with a single .C
argument, the g++
program removes the .o
file.
The compilation and linking steps can be executed separately as follows:
g++ -Wall -c prog.C g++ -o prog prog.o
g++
command just compiles prog.C
into
prog.o
, and the second links it to create prog
.
The g++
program recognizes the .o
suffix so that
it doesn't try to compile prog.o
.
Now suppose you have C code files described above for the calculator program. Then the following commands will create the corresponding object files:
g++ -Wall -c calculator.C g++ -Wall -c table.C g++ -Wall -c tokens.C
g++
program is called with a list of .o
files it will link them all together to form an executable program.
So the following command creates the calculator program:
g++ -o calculator calculator.o table.o tokens.o
#include
s .h
suffix.
In the example calculator program, the files table.C
, and
tokens.C
should have header files table.h
and tokens.h
.
These header files define types and functions that are shared with other
files.
A pair of files consisting of a code file and its associated header file
are referred to as a module.
When a shared type or function is declared in a header file, the
declaration should be the same as a declaration in a C file that is not
separately compiled.
#include <header-file>
#include "header-file"
The file calculator.C
should include both of the header files
since it will use types and functions from both of the other modules.
The file tokens.C
does not need any types or functions from
the tables module, so it should only include
tokens.h
.
Finally, the file tables.C
does not need any types or
functions from the tokens module, so it should only include
tables.h
.
make
program g++
to compile all of your pieces.
This means you have a lot of opportunities for mistyping commands.
Secondly, you must remember which files are up-to-date, and which need to
be recompiled.
Forgetting to recompile something which has been corrected means the
mistake doesn't go away, even though you think you've fixed it.
This can lead to terrible confusion.
The solution to these difficulties is to use the Unix tool
make
to manage your compilations for you.
When you type the Unix command make
, the make program looks in
the current working directory for a file named either makefile
or Makefile
.
The make program then uses the makefile to determine how to update your
program files to produce a completely compiled package.
targets : prerequisites commands
The commands part of each entry consists of zero or more lines, each starting with a tab character, followed by a command to be executed. These commands should tell the make program how to remake target files. The tab is crucial; if it's not there then the make program does not know that it is reading a command line.
When the make program starts up, it determines the age of each file in the current working directory by looking at time stamp information, which is a part of all Unix files. If a target file for an entry does not exist, or if it is older than any of the prerequisite files, then the make program issues all of the commands.
In simple makefiles there will be two kinds of entries. One kind creates an object file by compiling a C source code file. The second kind links object files into an executable program.
table.o : table.C table.h g++ -Wall -c table.C
table.o
is dependent on
the files table.C
and table.h
.
When the make program starts up, it determines the age of the files
table.o
, table.C
, and table.h
.
If table.o
does not exist, or if it is older than either of
the files table.C
or table.h
, then the make
program issues the command g++ -Wall -c table.C
to make or
remake table.o
.
When writing a program in pieces, each .o
file should be made
dependent on all of the header files that are included in the corresponding
.C
file.
The reason for this is that if the header file is changed, the
.o
file needs to be remade.
If the .o
file is not remade, it is likely that different
versions of the header file will be used in different parts of the program.
The symptoms of this problem are unpredictable, so take care in setting up
dependencies.
The make program should not be responsible for making .C
and
.h
files.
These are made by the programmer using an editor.
Thus .C
files and .h
files should not be target files.
The make program also should not be responsible for making .o
files if their .C
file is not available.
For example, if you are working on a team and you are using a
.o
file provided by someone else, then your makefile should
not contain an entry for that .o
file.
calculator : calculator.o tokens.o table.o g++ -o calculator calculator.o tokens.o table.o
calculator
should be
remade whenever it is older than any of the object files
calculator.o
, tokens.o
or table.o
.
The compiler, when given a list of object files, will just link them to
form the executable program.
If the makefile also contains entries in which calculator.o
,
tokens.o
, or table.o
appear as targets then the
make
program will try to make them first.
calculator.C
) and the table module
(table.C
and table.h
), but someone else has
provided you with the object file tokens.o
and the header file
tokens.h
.
Then the following makefile could be used.
calculator : calculator.o tokens.o table.o g++ -o calculator calculator.o tokens.o table.o calculator.o : calculator.C tokens.h table.h g++ -Wall -c calculator.C table.o : table.C table.h g++ -Wall -c table.C
tokens.o
as a target since it
is provided by another person; there is no way that the make program can
recreate it.
It is best to leave tokens.o
as a prerequisite for
calculator
in case you get an updated version.
Usually the command make
is given by itself in response to the
Unix prompt.
When the make program is run this way, it tries to make the file or files
appearing before the colon in the first entry of the makefile.
It will check the files after the colon to see if they are up to date (as
determined by other entries in the makefile) and, if necessary, update
them.
Thus with the given makefile, the make program will first check the object
files to see if they are up-to-date.
If not, or if they don't exist, they will be created using the commands
given in the later entries.
Once these files are made, the executable program calculator
can be made using the command in the first entry of the makefile.
The make program can also be run with the command
make target-name
make table.o
table.o
without going on to make the rest of the
package.
[an error occurred while processing this directive]