UNIX provides a way to automate the building of programs. We will see a simple example here. The Half Adder code example consists of several programs. Here is a listing of the directory:
--> ls Makefile halfadder.java halfadder3.c main.java halfadder.c halfadder2.c halfadder3.exe* outfile halfadder.exe* halfadder2.exe* index.htmlNormally, we would compile the c files into executables by executing:
g++ -o halfadder halfadder.cWe would have to do this for each .c file. This is already tedious and a little error prone. In this case, we have to repeat this three times using different filenames. Also, we have to remember to re-do this when we make changes.
The make program uses a file of rules that we define to determine which files have to be re-built and how to do it. It uses the date stamp of the file to determine if the executable is older than the source file. It it ise, it follows the instructions we give to build the executable from the source.
The build rules have two basic parts: the dependency and the rule. The dependency relates the source to the result. It has to parts as well, the target and the list of files or other targets it is dependent on. The rule part is pretty much some shell code to build the program.
This example shows the rules for building halfadder.c into the executable.
halfadder: halfadder.c g++ -o halfadder halfadder.cThe target is halfadder. It is dependant on the file halfadder.c. If the executable (halfadder) is older than the .c file, then the executable must be re-built because the .c file has changed. The rule to build the executable in this case is the familiar
g++ -o halfadder halfadder.cIn front of the build commands, there must be a tab character. Not all the targets need to be files nor do the targets need to have dependencies.
Usually, the file that contains the build rules is called either Makefile or makefile. If you use these default names, then running make just involves executing the make program. This reads the make file and checks the first target it sees. If you want to check a specific target, you put its name on the command line. If the list of dependencies for a target include other targets, make checks those as well. In our example, we want to check all the versions of halfadder by default. So the first line in the file is
all : halfadder halfadder2 halfadder3Since all the dependencies are targets, they will all get checked. Note that there are no build commands here. The actual building will be handled by the halfadder, halfadder2 and halfadder3 targets. If we just want to check halfadder2, we run make like this
make halfadder2This checks the dependencies of the target halfadder2 which are:
halfadder2: halfadder2.c g++ -o halfadder2 halfadder2.cIf halfadder2.c has changed, then the compile line gets run.
We are not limited to compiling things. In this example, I want to run all the programs. So I added a target called run. It has no dependencies, so if I run make like
make runIt executes the commands that come after it, each line starting with a tab character.
run: echo "----halfadder output" > outfile ./halfadder >> outfile echo "----halfadder2 output" >> outfile ./halfadder2 >> outfile echo "----halfadder3 output" >> outfile ./halfadder3 >> outfileNote that the output fom running the versions of halfadder is redirected into a file outfile. The double > tell the shell to append the output to the file. But first, we insert a message into the file like this:
echo "----halfadder output" > outfileecho is a shell command that simply prints the command line to the output. In this case, the quoted string goes into the file outfile. The single > means to first create or erase the file before writing the message.
We have one more target without dependencies in here. This is used to remove the exceutables.
clean : rm -f halfadder halfadder2 halfadder3 rm -f halfadder.exe halfadder2.exe halfadder3.exeThe -f option tell remove to delete the file without messages. In this case, we use it because we don't want to see error messages if the files don't exist. There are two remove lines because on a real UNIX system, the executables have no extension but under Cygwin, they end in .exe.
These are just some of the basic uses and operation of make. There are many builtin rules to make automating program compilation easier. There are whole books written on how to use it.
Here is the complete make file.
all : halfadder halfadder2 halfadder3 halfadder: halfadder.c g++ -o halfadder halfadder.c halfadder2: halfadder2.c g++ -o halfadder2 halfadder2.c halfadder3: halfadder3.c g++ -o halfadder3 halfadder3.c run: echo "----halfadder output" > outfile ./halfadder >> outfile echo "----halfadder2 output" >> outfile ./halfadder2 >> outfile echo "----halfadder3 output" >> outfile ./halfadder3 >> outfile clean : rm -f halfadder halfadder2 halfadder3 rm -f halfadder.exe halfadder2.exe halfadder3.exe