A single application
Suppose we have a product (we'll use the archiver, lha for this example) that we'd like to make available on all the processors that the QNX OS supports. Unfortunately, we've been using our own custom Makefiles for gcc on x86_64, and we have no idea how to make binaries for other processors.
The QNX OS Makefile system makes it very easy for us to build different processor versions. Instead of writing the entire complicated Makefile ourselves, we simply include various QNX OS Makefile system files that will do most of the work for us. We just have to make sure the correct variables are defined, and variants are created.
tar -zxvf lha-114i.tar.gz
This creates a directory called lha-114i. If we run make here, everything will compile, and when it's done, we'll have an x86_64 binary called lha in the src directory.
gcc -O2 -DSUPPORT_LH7 -DMKSTEMP -DNEED_INCREMENTAL_INDICATOR \
-DTMP_FILENAME_TEMPLATE=""/tmp/lhXXXXXX"" \
-DSYSTIME_HAS_NO_TM -DEUC -DSYSV_SYSTEM_DIR -DMKTIME \
-c -o lharc.o lharc.c
We want to make sure our version compiles with the same options as the original version, so we have to make sure to include those compiler options somewhere.
cd src
mv Makefile Makefile.old
addvariant -i OS
ifndef QCONFIG
QCONFIG=qconfig.mk
endif
include $(QCONFIG)
You should never change these four lines. The default qconfig.mk defines a number of variables that the Makefile system uses.
INSTALLDIR=usr/bin
This defines where to install our binary. Third-party applications should go into usr/bin instead of bin, which is the default.
define PINFO
PINFO DESCRIPTION=Archiver using lha compression.
endef
If we define PINFO information like this in our common.mk file, a lha.pinfo file will be created in each of our variant directories. We'll look at this later.
NAME=lha
This tells the Makefile system what the name of our project is. Since we're building binary executables, this will be the name of our binary.
#EXTRA_INCVPATH=$(PROJECT_ROOT)/includes
EXCLUDE_OBJS=lhdir.o makezero.o
CCFLAGS=-O2 -DSUPPORT_LH7 -DMKSTEMP -DNEED_INCREMENTAL_INDICATOR
-DTMP_FILENAME_TEMPLATE=""/tmp/lhXXXXXX"" -DSYSTIME_HAS_NO_TM
-DEUC -DSYSV_SYSTEM_DIR -DMKTIME
include $(MKFILES_ROOT)/qtargets.mk
This does all the magic that figures out which CPU compiler to use, what binary to make, etc. You should never change this line.
ifndef QCONFIG
QCONFIG=qconfig.mk
endif
include $(QCONFIG)
INSTALLDIR=usr/bin
define PINFO
PINFO DESCRIPTION=Archiver using lha compression.
endef
NAME=lha
#EXTRA_INCVPATH=$(PROJECT_ROOT)/includes
EXCLUDE_OBJS=lhdir.o makezero.o
CCFLAGS=-O2 -DSUPPORT_LH7 -DMKSTEMP -DNEED_INCREMENTAL_INDICATOR
-DTMP_FILENAME_TEMPLATE=""/tmp/lhXXXXXX"" -DSYSTIME_HAS_NO_TM
-DEUC -DSYSV_SYSTEM_DIR -DMKTIME
include $(MKFILES_ROOT)/qtargets.mk
That's it for the common.mk file.
We'll see where it is included in Makefiles shortly.
How about the Makefile that was just created for us?
We'll very rarely have to change any of the Makefiles.
Usually they just contain a LIST=
line, depending on where they
are in our directory tree, and some Makefile code to include
the appropriate file that makes the recursion into subdirectories possible.
The exception is the Makefile at the very bottom.
More on this later.
./lha 2> lha.use
For our final binaries, when someone types use lha (assuming lha is in their path), they'll get the proper usage message.
As described earlier in this appendix, we use a lot of subdirectories. Here are the ones we need:
Directory | Level |
---|---|
nto | OS |
nto/x86_64/ | CPU |
nto/x86_64/o | Variant |
Unless we'll be releasing for QNX 4 as well as the QNX OS, we'll need only the nto directory for the OS level. For the CPU level, we'll have directories for anything we want to support: AArch64 and/or x86_64.
The final variant directory depends on what we're building, and what endianness we want to compile for:
- Since x86_64 only has little endianness, it doesn't have an extension.
- If there's a choice, the variant level directory name would have a .be or .le at the end (e.g., o.le).
- If we're building shared libraries, we'd replace the o variant with a so variant.
- If we were building shared objects that aren't meant to be linked directly with applications, we'd use a dll variant.
- If we were building static libraries, we'd use an a variant.
We're building just an executable binary, so we use the o variant.
Each directory and subdirectory needs to have a Makefile.
Again, for most of them we're simply including the recurse.mk
file, which contains everything needed to recurse down our tree until we
get to the o* directory, as well as setting a
LIST variable, which for general use indicates where we are
in our Makefile tree.
For example, if the directory contains variants, LIST is set
to VARIANT
.
addvariant nto
addvariant nto aarch64 o.le
addvariant nto x86_64 o
include ../../../common.mk
Since this is the bottom directory, we don't need to recurse any further, but rather we want to include the common.mk file we created earlier. We also don't need a LIST variable, since we have no subdirectories at this point. We now have a complete QNX OS-style Makefile tree.