Conventions for recursive Makefiles and directories
We'll take a look at the supplementary files used in the BlackBerry 10 OS development environment. Although we use the standard make command to create libraries and executables, you'll notice we use some of our own conventions in the Makefile syntax. We'll start with a general description of a full, multiplatform source tree. Then we'll look at how you can build a tree for your products. Next, we'll discuss some advanced topics, including collapsing unnecessary levels and performing partial builds. Finally, we'll wrap up with some examples of creating Makefiles.
Although you're certainly not obliged to use our format for the directory structure and related tools, you may choose to use it because it's convenient for developing multiplatform code. If you do use this structure, you should use the addvariant command to create it; for more information, see the Utilities Reference as well as the examples.
Structure of a multiplatform source tree
Here's a sample directory tree for a product that can be built for two different operating systems (QNX 4 and Neutrino), on two CPU platforms (x86 and ARM):
We'll talk about the names of the directory levels shortly. At each directory level is a Makefile file that the make utility uses to determine what to do to make the final executable.
However, if you examine the makefiles, you can see that most of them simply contain:
Why do we have makefiles at every level? Because make can recurse into the bottommost directory level (the variant level in the diagram). That's where the actual work of building the product occurs. This means that you could type make at the topmost directory, and it would go into all the subdirectories and compile everything. Or you could type make from a particular point in the tree, and it would compile only what's needed from that point down.
You can learn how to cause make to compile only certain parts of the source tree, even if invoked from the top of the tree, in Performing partial builds.
When deciding where to place source files, as a rule of thumb you should place them as high up in the directory tree as possible. This not only reduces the number of directory levels to traverse when looking for source, but also encourages you to develop source that's as generic as possible (that is, that isn't specific to the OS, CPU, or board). Lower directory levels are reserved for more and more specific pieces of source code.
If you look at the source tree that we ship, you'll notice that we follow the directory structure defined above, but with a few shortcuts. You can learn about shortcuts in Advanced topics.
As mentioned earlier, the makefile structure is almost identical, regardless of the level that the makefile is found in. All makefiles (except the bottommost level) include the recurse.mk file and may set one or more macros.
Here's an example of one of our standard (non-bottommost) Makefiles:
LATE_DIRS=boards include recurse.mk
The recurse.mk file
The recurse.mk file resides under $QNX_TARGET/usr/include/mk. This directory contains other files that are included within makefiles. Note that while the make utility automatically searches $QNX_TARGET/usr/include, we've created symbolic links from there to $QNX_TARGET/usr/include/mk.
The recurse.mk include file is typically used by higher-level makefiles to recurse into lower-level makefiles. All subdirectories present are scanned for files called makefile or Makefile. Any subdirectories that contain such files are recursed into, then make is invoked from within those directories, and so on, down the directory tree.
You can create a special file named Makefile.dnm (dnm stands for Do Not Make) next to a real Makefile to cause recurse.mk not to descend into that directory. The contents of Makefile.dnm aren't examined in any way — you can use touch to create an empty file for it.
Last modified: 2015-05-07