USING MAKEFILES TO SIMPLIFY PROGRAM DEVELOPMENT
Many people new to QNX have come from an integrated environment which maintained information on projects and compiler options are selected by clicking them in a dialog. While this is especially easy to use, it may lack some flexibility for more advanced projects. QNX is identical to the UNIX environment, in which a Makefile is used to intelligently decide which source modules need to be compiled and linked and running the tools to accomplish that.
This document is a brief description of how to use make and provides some more advanced examples which illustrate it's flexibility.
Avoid directly running the Watcom tools (wcc386 etc.). Many options are interdependent and may change with different releases. cc is always used to run the compiler and the linker and it will pass the appropriate options as necessary.
In general, a Makefile includes two things. Lists of which files depend on which other files, so make will know what needs to be updated, and lists of commands to run in order to do so. Since many things are repeated throughout, variables may be used. There are also a number of built-in rules, which are used whenever the lists do not provide direct instructions.
Make by default, looks in the current directory for a file called Makefile or makefile, so you can run Make just by typing make.
The structure of a Makefile is:
result_file: dependency_file1 dependency_file2 dependency_file3 <tab> command <tab> command <tab> command dependency_file2: dependency_file4 <tab> command
If an instruction line is too long, use \ as the last character and continue on the next line. Lines beginning with # are comments.
Unless you specify otherwise on the command line, make will locate the first result_file and try to create it. First it will parse the entire Makefile. It will examine the timestamp of each dependency file, plus those on which they in turn depend. Make will rebuild files as necessary to create the result_file, in the necessary order, by executing the commands listed. Command lines are identified by the first character on the line being a tab (not a space). Processing will abort if a command returns an error, unless the command is preceeded with a -:
<tab> -command
Normally Make echoes the line to the screen prior to executing it. An @ can be used to hide the echo.
<tab> @command
The following is a simple Makefile, which you can use to begin your project. It creates two executables: x10drv, which is a driver and x10 which a user runs to control it. The first result_file listed is all, so just typing make will create both. There are no commands to run for all, so make just needs to update x10 and x10drv.
# Makefile for X10 OPTIMIZE = -Oamrtx #OPTIMIZE = -Od -g2 -M -D DEBUG CFLAGS = -5 -w9 -Wc,-we -Wc,-e6 $(OPTIMIZE) ISR = -Wc,-zu -Wc,-s LDFLAGS= all: x10 x10drv x10: x10.h x10.c $(CC) $(CFLAGS) $(LDFLAGS) -o x10 x10.c usemsg x10 x10.c chmod 755 x10 x10drv: var.o drv.o int.o $(CC) $(CFLAGS) $(OPTIMIZE) $(LDFLAGS) -T1 -o x10drv var.o drv.o int.o usemsg x10drv drv.c chmod 4755 x10drv var.o: var.c $(CC) $(CFLAGS) $(OPTIMIZE) -c var.c -o var.o drv.o: var.c drv.c $(CC) $(CFLAGS) $(OPTIMIZE) -c drv.c -o drv.o int.o: var.c int.c $(CC) $(CFLAGS) $(OPTIMIZE) $(ISR) -c int.c -o int.o
x10 is quite simple, depending on only a header and a single C source file. It is compiled and linked in a single command. So both the compiler flags CFLAGS and the linker flags LDFLAGS variables are used. Since the names of these variables are used in the default rules, do not change them. I define my own variable OPTIMIZE, to make it easy to switch between full blown optimization and debug options. I specify a number of options to be included in CFLAGS, which defines Pentium optimization, full warnings, treat warnings as errors and abort after 6 errors. Note the method of passing parameters directly to the compiler and linker: -Wc,### and -Wl,### where ### represents the options to be passed. Typically few options are passed directly, since cc is aware of almost all of them.
x10drv is a little more complicated, since it includes an Interrupt Service Routine that reads and writes I/O Ports. An interrupt routine has special compilation requirements. It is running with Proc's stack, so stack checking must be off. For convenience, I define a variable ISR to include these options. A special flag -T1 must be used to compile a program which will access hardware. I used it on the link line, but did not include it in LDFLAGS, since it is not required for other executables. I set the permissions to include the setuid bit, so non-root users can run it.
The following is a far more complex Makefile, using principles described above.
# Makefile for Dev.arnet with Watcom 9.5/10.6 for QNX 4.2 # RELEASE = m VERSION = 100 # DEV16 Version #SIZE = 16 #MODEL = -2 # DEV32 Version SIZE = 32 MODEL = -5 NAME = Dev${SIZE}.arnet COMPILER = 10.6 -ltermlib #COMPILER = 9.52 #OPTIMIZE = -Oamrtx OPTIMIZE = -Od -g2 -D DEBUG -M CFLAGS = $(MODEL) -ms -w9 -Wc,-we -Wc,-e6 -ze -v$(COMPILER) -D DEV$(SIZE) -D NAME=\"$(NAME)\" -D VERSION=$(VERSION) -D RELEASE=\'$(RELEASE)\' $(OPTIMIZE) LDFLAGS = -T1 -N 2048 "-Wl,op H=1024" ISR = -Wc,-zu -Wc,-s VER_REL = $(VERSION)$(RELEASE) ARCHIVE = //2/backups/arnet/arn$(VER_REL).pax.F HEADERS = arndef.h arnfep.h arnbox.h arnstruct.h arnproto.h target: $(NAME) clean: -rm *.x *.o *.map Dev*.arnet archive Ship/* install: /etc/install -u $(ARCHIVE) ################################################################################ # INSTALLATION # PRODUCTION DISKETTES disk: disk144 #disk144: $(ARCHIVE) disk144: echo Insert 1.44M diskette in drive 0 format 0 1.44 cat $(ARCHIVE) | vol -vws2 /dev/fd0 vol -rs2 /dev/fd0 | melt | pax -v disk720: archive echo Insert 720K diskette in drive 0 format 0 720 cat $(ARCHIVE) | vol -vws2 /dev/fd0 vol -rs2 /dev/fd0 | melt | pax -v # DOWNLOAD ARCHIVE archive: $(ARCHIVE) date >archive $(ARCHIVE): \ Ship/Dev16.arnet.$(VER_REL) Ship/Dev32.arnet.$(VER_REL) \ Ship/arncpsload \ Ship/arnquery Ship/arnquery.h Ship/arnquery.c Ship/Makefile \ Ship/arnethelp Ship/arnet.help \ Ship/~setup Ship/0install_msg cp bin/spfep.bin Ship cp bin/csbios.bin Ship cp bin/csfep.bin Ship cp bin/cpbios.bin Ship cp bin/cpfep.bin Ship cp bin/cpcon*.bin Ship cp bin/ppbios.bin Ship cp bin/ppfep.bin Ship chmod 600 Ship/*.bin chmod 700 Ship/Dev*.arnet* chmod 700 Ship/arncpsload chmod 700 Ship/arnquery chmod 600 Ship/arnquery.c chmod 600 Ship/arnquery.h chmod 600 Ship/Makefile chmod 711 Ship/arnethelp chmod 644 Ship/arnet.help chmod 700 Ship/~setup chmod 600 Ship/0install_msg ls -p Ship >/ram/arnet.files @echo Archive: $(ARCHIVE) cat /ram/arnet.files | pax -wv -s,Ship/0install_msg,install_msg, \ -s,Ship/~setup,setup, -s,Ship,/usr/lib/arnet, | freeze > $(ARCHIVE) Ship/Dev16.arnet.$(VER_REL): Dev16.arnet cp -c Dev16.arnet Ship/Dev16.arnet.$(VER_REL) Ship/Dev32.arnet.$(VER_REL): Dev32.arnet cp -c Dev32.arnet Ship/Dev32.arnet.$(VER_REL) Ship/~setup: ~setup cp ~setup Ship/~setup Ship/0install_msg: 0install_msg cp 0install_msg Ship/0install_msg Ship/arncpsload: arncpsload cp arncpsload Ship/arncpsload Ship/arnethelp: arnethelp cp arnethelp Ship/arnethelp Ship/arnet.help: arnet.help ln -f arnet.help Ship/arnet.help Ship/Makefile: Makefile.query cp Makefile.query Ship/Makefile Ship/arnquery.h: arnquery.h cp arnquery.h Ship ################################################################################ # THE DRIVER $(NAME): arnvar$(SIZE).o arnmain$(SIZE).o arnini$(SIZE).o arnprc$(SIZE).o \ arndev$(SIZE).o arnirq$(SIZE).o arnsub$(SIZE).o obj/devdrive$(SIZE).o \ Usage $(CC) $(CFLAGS) $(LDFLAGS) -o $(NAME) \ arnvar$(SIZE).o arnmain$(SIZE).o arnini$(SIZE).o arnprc$(SIZE).o arndev$(SIZE).o arnirq$(SIZE).o arnsub$(SIZE).o obj/devdrive$(SIZE).o usemsg $(NAME) Usage arnvar.x: arnvar.c arnquery.h $(HEADERS) makextrn arnvar.c arnvar.x arnvar$(SIZE).o: arnvar.c arnquery.h $(HEADERS) $(CC) $(CFLAGS) -c arnvar.c -o arnvar$(SIZE).o arnmain$(SIZE).o: arnmain.c arnvar.x $(CC) $(CFLAGS) -c arnmain.c -o arnmain$(SIZE).o arnini$(SIZE).o: arnini.c arnvar.x $(CC) $(CFLAGS) -c arnini.c -o arnini$(SIZE).o arnprc$(SIZE).o: arnprc.c arnvar.x $(CC) $(CFLAGS) -c arnprc.c -o arnprc$(SIZE).o arnsub$(SIZE).o: arnsub.c arnvar.x $(CC) $(CFLAGS) -c arnsub.c -o arnsub$(SIZE).o arndev$(SIZE).o: arndev.c arnvar.x $(CC) $(CFLAGS) $(ISR) -c arndev.c -o arndev$(SIZE).o arnirq$(SIZE).o: arnirq.c arnvar.x $(CC) $(CFLAGS) $(ISR) -c arnirq.c -o arnirq$(SIZE).o ################################################################################ # SUPPLIMENTAL UTILITIES # HELP UTILITY arnethelp: arnethelp.c $(CC) $(CFLAGS) -2 -o arnethelp arnethelp.c usemsg arnethelp arnethelp.c # CLUSTERPORT CONCENTRATOR BINARY IMAGE MANAGER arncpsload: arncpsload.c arndef.h arnconc.h $(CC) $(CFLAGS) -2 -o arncpsload arncpsload.c usemsg arncpsload arncpsload.c # QUERY MESSAGE EXAMPLE PROGRAM arnquery: arnquery.c arnquery.h $(CC) $(CFLAGS) -2 -o arnquery arnquery.c usemsg arnquery arnquery.c
Note: These instructions
are for typical situations. Individual configuration may differ.
If you have any questions, please contact
us for assistance.
Copyright © 1998 Qenesis Inc. All rights reserved.