(c) 1990 S.Hawtin. Permission is granted to copy this file and make provided that: 1) Neither are used for commercial gain 2) This notice is included in all copies 3) Altered copies are marked as such. 4) Each copy of make is accompanied by a copy of this file. No liability is accepted for the contents of the file. make.doc within make MAKE make [-fmakefile][-?][target] Maintain an up-to-date version of a program. The options have the following meanings -f Take the description from the file rather than "Makefile". -? Print out the commands that would be executed but do not perform them. target Construct the file rather than the first item in the makefile. The special target "$*.blink" will cause the program to create a file that can be used by the "Blink" program to link the object files together. The make command keeps programs up to date, it does this by reading a file that describes how to construct a target program and executing all the commands required to update the program. When using 'C' and other compiled languages it is normal to divide the source code for a program into many files, this allows you to split the code into logical chunks. Most compiled languages allow you to recompile each source file independently to produce an "object" file, the user then combines all the object files to produce the actual program with a "linker". Whenever you alter a source file it must be recompiled and the resulting "object" file must be relinked into the executable program, it can be difficult to remember which files you have changed since the last build and what set of tools need to be run on which programs. The "make" program works out which commands need to be executed to bring a program up to date and executes them. An example will explain what the program does, assume you have a program called "jim" that combines three source files, "foo.c", "bar.c" and "baz.asm". To use "make" in its simplest form you should create a file called "Makefile" containing the lines jim : foo.o bar.o baz.o blink with $*.blink this tells the "make" program that "jim" depends on the object files "foo.o", "bar.o" and "baz.o", and that "jim" can be created by the CLI command "blink with jim.blink". You must now construct the file "jim.blink" that the "blink" command requires, this is done by the CLI command make $*.blink the "$*" construct will be explained later. Once the "Makefile" and "jim.blink" files have been created you can use the "make" program to construct "jim", just type the command make and it will call all the commands to make "jim". Now if you change the file "foo.c" and then type the "make" command it will only issue the commands required to bring the program up to date, the program will not bother to recompile "bar.c" or "baz.asm". The "make" program is clever enough to know that "bar.c" needs to be compiled and "baz.asm" needs to be assembled program. So every time you change a set of the source files you just type "make" and all the necessary compilations will be done. This is well and good, assuming we are only going to edit the ".c" and ".asm" files. If however if you change an include file you want "make" to recompile all the 'C' source files that include it. We get make to do this by adding some extra lines into the "Makefile". If the new "Makefile" contains jim : foo.o bar.o baz.o blink with $*.blink foo.o: fubar.h foo.h bar.o : fubar.h then every time "fubar.h" is altered "make" will recompile both "foo.c" and "bar.c". Notice that the three items "jim", "foo.o" and "bar.o" must be separated by blank lines. If we now change the file "fubar.h" then the "make" command will recompile both "foo.c" and "bar.c". By default the "make" program always tries to construct the first item in the "Makefile", this is why you must put the line foo.o: fubar.h after jim : foo.o bar.o baz.o if it was put in before then the "make" program would just make "foo.o" and not "jim". You can get "make" to construct a particular item by telling it which target item you want to update, for example to make "bar.o" just type the command make bar.o we have already seen this being used when we created the "jim.blink" file. So, to summarise, each item in the "Makefile" starts with a file name, then after the ':' a list of items that this item depends on, the following lines, up to the first blank line, tell "make" the commands required to construct the item. Each item can appear on the left hand side as many times as you want so long as you only specify the commands to create it once. You can of course add comments into the "Makefile", any line that starts with a '#' is treated as a comment. One problem you might come across is having too many dependencies to fit on a single line, the '\' character tells "make" that the dependencies are continued on the next line, so for example jim: foo.o \ bar.o \ baz.o blink with $*.blink is the same as the original entry for "jim". The '\' character must be the last character on the line. Sometimes you want to "make" an item every time you construct the program, for example if you have a file "date.c" that consists of #include print_date() {/* Print the date the program was last compiled */ printf("This version was compiled on %s\n",__DATE__); } then even though the actual text of the source file does not change you want to recompile it whenever the program is compiled. You can force "make" to always recompile this program by appending the special item "always", for example by adding the item date.o : always to the Makefile. "always" is considered to be newer than everything else. The "__DATE__" macro is an ANSI C extension that returns a string containing the compilation date. Although the "make" program knows how to convert a ".c" program into a ".o" program you can override this by adding an item such as foo.o: foo.c NorthC -I:incl/ foo.c A68k foo.s Delete foo.s whenever "foo.c" is changed the given commands will be executed to create "foo.o" rather than the normal commands. If you have a preferred way to convert ".c" files into ".o" files you could type this in after every ".o" file, however "make" will also allow you to define a "default" way to convert between the files, if you add the item .c.o: NorthC -I:incl/ $*.c A68K $*.s Delete $*.s to the "Makefile" this item tells "make" to construct ".o" files from ".c" files by calling the commands NorthC -I:incl/ $*.c A68K $*.s Delete $*.s The sequence "$*" is replaced by the base file name. The base file name consists of the name up to the final '.', so for example the base name of "jim.2.c" is "jim.2". "make" will always add some default items to the end of the "Makefile", if they do not clash with existing entries, the current default items are .c.o: NorthC -Ot:$*.s $*.c A68K -q -g -O$*.o t:$*.s Delete t:$*.s .s.o: A68K $*.s .asm.o: A68K $*.asm this tells the "make" program how to compile 'C' programs and assemble ".asm" and ".s" files. You can use the default mechanism to create files with any extension of up to four letters, for example .uil.uid: Delete $*.uid Motif:Uil/uil -o $*.uid $*.uil tells "make" that each time it finds a reference to a file such as "jim.uid" it should construct the file with the commands Delete jim.uid Motif:Uil/uil -o jim.uid jim.uil whenever the "jim.uil" file is altered. You can split your "Makefile" into multiple files. When you want to read a file, for example "make.depends", just include the command $ in "Makefile". Make will read the file "make.depends" at that point. If you wish to read a file other than "Makefile" when you call "make" add the "-f" option, for example to read the dependencies from "jim.make" make -fjim.make will make the first item in the file "jim.make". If you want to use the "top" optimiser you should add an item .c.o: NorthC -Ot:$*.s $*.c top t:$*.s t:$*.s1 a68k -g -q -O$*.o t:$*.s delete t:$*.s t:$*.s1 into your makefiles. How Make Works You don't actually have to read this bit but it might help if you cannot get "make" to do something complicated. The "make" command works by creating a dependency tree, for example the sequence jim: foo.o bar.o baz.o blink with $*.blink foo.o: fubar.h foo.h bar.o: fubar.h will create a dependency tree jim / | \ / | \ / | \ / | \ foo.o bar.o baz.o | \ / | fubar.h | foo.h "make" then scans this tree to find items that have no create command sequence defined. Any item that we have not yet been told how to make will either be a source file, for example "foo.h" or we will have to find out how to make it from the defaults list. So make will now scan the tree, when it comes to "foo.o" it discovers that it has a method for creating ".o" files from ".c" files, and a ".c" file exists, so it adds the default ".c.o" create method to "foo.o" and adds "foo.c" to its dependencies. When it comes to the file "bar.o" because it cannot find "bar.c" it looks further down the list of defaults and finds that a "bar.asm" file exists. Once the tree has been scanned it looks like jim / | \ / | \ / | \ / | \ foo.o bar.o baz.o / | \ / \ | foo.c | fubar.h \ baz.asm | \ foo.h bar.c "make" then goes over this tree to see if any item is older than one of its dependencies, when it finds such an item it adds the commands for creating this item to an intermediate file in the "t:" directory. Once it has gone over the tree it calls the AmigaDOS command "execute" to run the created command file, I have to call this function because I want "make" to stop as soon as one of the commands fails. I cannot find out how to determine the return code of a called program, if anyone out there can tell me how to do this please get in touch. The last thing the program does is to delete the command file it created in the "t:" directory.