Hamburg (Germany), the 18th November 1997.
Written by Nils Pipenbrinck aka Submissive/Cubic & $eeN
Ok, since I think there are still some things you should know about these useful MAKEFILES I decided to set up another web-page. This page may be viewed as Chapter II of the previous page. If you've missed it, or you don't know what I'm talking about, better check it out here. Again this tutorial will only deal with WMAKE (the WATCOM MAKE utility). Whenever I say MAKE instead of WMAKE I still mean the WATCOM MAKE. The compatibility issues of other MAKE utilities are discussed on my Makefile Compatibility Chart Page.
For my coding work I make an alias. Whenever I type make, WMAKE will automatically be invoked. (and when you've used it a lot you'll know why. Just try to type WMAKE very fast... it sucks! I usually end up with stuff like WMAEK or WMKE. Typing MAKE is much easier).
Power-Tip for 4DOS and NDOS users:
If you code a lot, you can put MAKE and other useful utilities on the unused
f-keys. I added the following lines to my autoexec.bat. I'll tell you, I can't
code without them anymore..
alias @@F9=wmake make current project alias @@SHIFT-F9=wmake -a build current project alias @@CTRL-F9=edit makefile edit your makefile alias @@F2=edit edit last open sourcecode (my editor does this!) alias @@F6=ultrinit^mode co80 init screen and soundcard (if your program crashed)
Maybe you've alredy found that it's currently only possible to declare a target that has to be created during the make process. However, sometimes it might be useful to create a target that's symbolic. That means the target might not have dependencies, and it might not exist after the make-progress. Ok, this is a little bit strange. Again an example will help:
clean: del *.obj del *.lst del *.map del *.exe del *.err
This target is very useful if you want to clean your directory of all the crap the compiler generated during the development. However, if you add this piece into your makefile and just type MAKE clean (yes, you can specify the target at the command line) MAKE will issue an error and tell you that it wasn't able to create the target clean. Right! how should it generate a target? This rule is symbolic! You only have to add .SYMBOLIC to your target-header, and it'll work:
clean : .SYMBOLIC del *.obj del *.lst del *.map del *.exe del *.err
Now the target is valid because MAKE doesn't expect the target to exist. Important: Put that Target at the bottom of your makefile. MAKE will always execute the first defined target of your makefile. The order of the declarations IS important. Wouldn't it be cool to write a Target to make a backup? Zip all the sourcefiles, rename the archive to the current Data/Time and copy it into your backup-directory? If you think so, then write one!
Let's go back to our game example. Say you've written a setup-program, and used some of the modules for game and setup (the mouse and video might be good canditates for this). If you now simply define two targets, one for game and one for setup, your makefile won't do what you expect:
coptions = /5r /s /ox obj_setup = setup.obj obj_game = game.obj joystick.obj obj_common = video.obj mouse.obj .cpp.obj wpp386 $(coptions) $< game.exe: $(obj_game) $(obj_common) %write temp.lnk NAME $@ %write temp.lnk SYSTEM DOS4G %write temp.lnk FILE {$(obj_game)} %write temp.lnk FILE {$(obj_common)} wlink @temp.lnk setup.exe: $(obj_setup) $(obj_common) %write temp.lnk NAME $@ %write temp.lnk SYSTEM DOS4G %write temp.lnk FILE {$(obj_setup)} %write temp.lnk FILE {$(obj_common)} wlink @temp.lnk
If you type MAKE -a, or simply make, only GAME.EXE will be made (Remember... the first target is the default). But hell... If you've updated mouse.cpp you want all targets up to date, don't you? This is where the ALL target is for: just declare a target called ALL, and put it before your other targets. If you do so, everything will work as expected. The complete makefile will now look like this:
coptions = /5r /s /ox obj_setup = setup.obj obj_game = game.obj joystick.obj obj_common = video.obj mouse.obj .cpp.obj wpp386 $(coptions) $< all: game.exe setup.exe game.exe: $(obj_game) $(obj_common) %write temp.lnk NAME $@ %write temp.lnk SYSTEM DOS4G %write temp.lnk FILE {$(obj_game)} %write temp.lnk FILE {$(obj_common)} wlink @temp.lnk setup.exe: $(obj_setup) $(obj_common) %write temp.lnk NAME $@ %write temp.lnk SYSTEM DOS4G %write temp.lnk FILE {$(obj_setup)} %write temp.lnk FILE {$(obj_common)} wlink @temp.lnk
ALL is symbolic by default, so you don't have to add .SYMBOLIC to it (If you do so, make will exit with an error). Now you can either compile the whole project with a single MAKE, or you can choose one of your targets to update and type (for example) MAKE game.exe. MAKE -A will build everything from scratch... Perfect!
Now it's time to add the makefile itself to the dependency list. Why should you? If you change the makefile (add a module for example) all dependencies are out of date... adding makefile to the dependency list of all targets that need a relink will do the trick (no example here... just try it yourself). If you do so you'll soon notice that if you changed the makefile and call MAKE, only the setup.exe and game.exe are not valid anymore. The object files are still up to date. This is a problem when you change the compiler options (and want a recompile of your sourcecode). How can we handle this situation? Either we call MAKE -A or we let MAKE do the work for you... All we have to do is to add a new symolic target to our makefile. It should look like this:
all: dummy game.exe setup.exe dummy: makefile .symbolic del *.obj
This will cause MAKE to delete all objectfiles whenever you've changed the makefile. The targets game.exe and setup.exe become invalid, because the objectfiles don't exist anymore. MAKE will therefore recompile your modules and relink the applications. This might sound cool, but it has a drawback: If you haven't changed your compiler options but only added another module MAKE will still recompile everything. That's the reason why I prefer to type MAKE -A.
All the rules we've discussed so far are implicit rules (you know, the .cpp.obj stuff). They're very useful if all your sourcefiles should be compiled using the same options. In some rare cases however you might need other compiler options for a single module. Changing the extension of these modules might work, but it's better to tell make how to do it. A good example of this situation is when you work with the MIDAS sound-library. The MIDAS library needs a special compiler option whenever you write code that's executed from an interrupt. You can override the implicit rule with an explicit one:
.cpp.obj wpp386 $(coptions) $< music.obj : music.cpp wpp386 $(coptions) -zu music.cpp
Now MAKE will call all .cpp dependencies except music.cpp using the implicit rule and use the explicit for music.cpp. (the -zu is a WATCOM switch which is nessesary when compiling code that's called from interrupt routines). Explicit rules also have another nice property: You can add dependencies to several modules. When you write C(++) code you usually deal a lot with header files. You can now add the headerfiles included by your module into the dependency list:
video.obj : video.cpp video.h vesa.h global.h wpp386 $(coptions) music.cpp
If you change the vesa.h headerfile video.obj gets invalid and will be rebuild. This is very nice to use, but it blows up your makefiles (adding an explicit rule for every module makes your makefile hard to maintain and is a place for evil and hard to find bugs (a hello to Crash/Polka Brother at this point. He favors this kind of dependencies)). I prefer to rebuild my whole project whenever I change headerfiles. This doesn't happen too often, so it doesn't hurt. If you have a cool idea which avoids these monster-makefiles then write me an email!
You can do more things with macros.
When you use a macro, you can do a direct text replacement. for example, when you
define a macro like this:
obj_common = video.obj mouse.obj
and use it with a colon you can replace parts of the text. For example, try this:
tasm $(obj_common:.obj=.asm)
This will replace all .obj strings in the macro with .asm. The
expanded macro is "video.asm
mouse.asm". I've never found a place where I could use
it, but maybe you know a killer use for it.
If you want to use an enviroment-variable as a macro you can simply use it in the same way you would use a user-defined macro, but be aware that as soon as you define a macro with the same name you're not able to access the enviroment-variable anymore. Again an example:
tasmopts = /m4 .asm.obj tasm $(tasmopts) /i$(include) <&
This will add the watcom-include enviroment to the tasm include search-path.
WMAKE is much more powerful and has a lot of stuff I won't describe here. Again, if you're interested look into the TOOLS helpfile and dig out that stuff yourself. For completeness I'll now list the most common WMAKE special commands.
Command | What it does / Where to use it. |
---|---|
.SYMBOLIC | Makes a target symbolic (explained) Insert it after the Dependencies into the Header of the Target definition. |
.AUTODEPEND | Makes an implicit rule use autodependencies (I had problems with this) Insert it at the end of the implicit rule header |
.IGNORE | Ignore return-Code of all compiler/utilities Insert it at the start of a line |
.SILENT | Suppress all screen-output if make is successful Insert it at the start of a line |
.ERASE | Erase target, if make-process is not successful Insert it at the start of a line |
.HOLD | Opposite of .ERASE Insert it at the start of a line |
.BEFORE | Instructions to be invoked before MAKE does anything Insert it at the start of a line |
.AFTER | Instructions to be invoked when MAKE has finished it at the start of a line |
# | Comments out a line Insert it at the first column of the line you want to comment out |
Now I've written everything important on makefiles. I hope you learned something. If you have a good tip or found a bug in this page, then write me a mail.
© by submissive