[Previous] [Contents] [Index] [Next]

Caution: This version of this document is no longer maintained. For the latest documentation, see http://www.qnx.com/developers/docs.

Conventions for Makefiles and Directories

This chapter includes:

In this chapter, we'll take a look at the supplementary files used in the Neutrino 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. Finally, we'll wrap up with a discussion of some advanced topics, including collapsing unnecessary levels and performing partial builds.

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.

Structure

Here's a sample directory tree for a product that can be built for two different operating systems (QNX 4 and Neutrino), on five CPU platforms (x86, MIPS, PowerPC, ARM, and SH4), with both endian combinations on the MIPS and PowerPC:


Figure showing a full source tree


Source tree for a multiplatform project.


We'll talk about the names of the directory levels shortly. At each directory level is a Makefile file used by the make utility to determine what to do in order to make the final executable.

However, if you examine the makefiles, you can see that most of them simply contain:

include recurse.mk

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.

We'll discuss how to cause make to compile only certain parts of the source tree, even if invoked from the top of the tree, in the "Advanced topics" section.


Note: 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 (i.e. non-OS, non-CPU, and non-board-specific) as possible. 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. We'll cover those shortcuts in the "Advanced Topics" section.

Makefile structure

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 (nonbottommost) Makefiles:

LATE_DIRS=boards
include recurse.mk

The recurse.mk file

The recurse.mk file resides under /usr/include/mk. This directory contains other files that are included within makefiles. Note that while the make utility automatically searches /usr/include, we've created symbolic links from there to /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.

The special filename Makefile.dnm ("dnm" stands for "Do Not Make") can be placed 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.

Macros

The example given above uses the LATE_DIRS macro. Here are the macros that can be placed within a makefile:

The EARLY_DIRS and LATE_DIRS macros

To give you some control over the ordering of the directories, the macros EARLY_DIRS and LATE_DIRS specify directories to be recursed into before or after all others. You'd use this facility with directory trees that contain one directory that depends on another directory at the same level -- you want the independent directory to be done first, followed by the dependent directory.

In our example above, we've specified a LATE_DIRS value of boards, because the boards directory depends on the library directory (lib).

Note that the EARLY_DIRS and LATE_DIRS macros accept a list of directories. The list is treated as a group, with no defined ordering within that group.

The LIST macro

The LIST macro serves as a tag for the particular directory level that the makefile is found in.

The LIST macro can contain a list of names that are separated by spaces. This is used when we squash directory levels together; see "Advanced Topics," later in this chapter.

Here are the common values corresponding to the directory levels:

Note that you're free to define whatever values you wish -- these are simply conventions that we've adopted for the three directory levels specified. See the section on "More uses for LIST," below.

Once the directory has been identified via a tag in the makefile, you can specifically exclude or include the directory and its descendents in a make invocation. See "Performing partial builds" below.

The MAKEFILE macro

The MAKEFILE macro identifies the name of the makefile that recurse.mk should search for in the child directories. Normally this is [Mm]akefile, but you can set it to anything you wish by changing the MAKEFILE macro. For example, in a GNU configure-style makefile, you'd set it to GNUmakefile (see "GNU configure," later in this chapter.

The CHECKFORCE macro

The CHECKFORCE macro is a trigger. Its actual value is unimportant, but if you set it, the recurse.mk file looks for Makefile.force files in the subdirectories. If it finds one, that directory is recursed into, even if the LIST macro settings would normally prevent this from happening.

Directory structure

Let's look at the directory levels themselves in some detail. Note that you can add as many levels as you want above the levels described here -- these levels would reflect your product. For example, in a factory automation system, the product would consist of the entire system -- you would then have several subdirectories under that directory level to describe various projects within that product (e.g. gui, pidloop, robot_plc, etc.).

The project level

The project level directory is used mainly to store the bulk of the source code and other directories. These directories would be structured logically around the project being developed. For our factory-automation example, a particular project level might be the gui directory, which would contain the source code for the graphical user interface as well as further subdirectories.

The section level (optional)

The section level directory is used to contain the source base relevant to a part of the project. It may be omitted if not required; see "Collapsing unnecessary directory levels," below.

The OS level

If you were building products to run on multiple operating systems, you'd include an OS level directory structure. This would serve as a branchpoint for OS-specific subdirectories. In our factory-floor example, the gui section might be built for both QNX 4 and Neutrino, whereas the other sections might be built just for Neutrino.

If no OS level is detected, Neutrino is assumed.

The CPU level

Since we're building executables and libraries for multiple platforms, we need a place to serve as a branchpoint for the different CPUs. Generally, the CPU level would contain nothing but subdirectories for the various CPUs, but it may also contain CPU-specific source files.

The variant level

Finally, the variant level contains object, library, or executable files specific to a particular variant of the processor. For example, a MIPS processor could operate in big-endian or little-endian mode. In that case, we'd have to generate two different sets of output modules. On the other hand, an x86 processor is a little-endian machine only, so we need to build only one set of output modules.

Specifying options

At the project level, there's a file called common.mk. This file contains any special flags and settings that need to be in effect in order to compile and link.

At the bottommost level (the variant level), the format of the makefile is different -- it doesn't include recurse.mk, but instead includes common.mk (from the project level).

The common.mk file

The common.mk include file is where you put the traditional makefile options, such as compiler options.

In order for the common.mk makefile to be able to determine which system to build the particular objects, libraries, or executables for, we analyze the pathname components in the bottommost level in reverse order as follows:

For example, if we have a pathname of /source/factory/robot_plc/driver/nto/mips/o.be, then the macros are set as follows:

Macro Value
VARIANT1 o.be
CPU mips
OS nto
SECTION driver
PROJECT robot_plc

The variant-level makefile

The variant-level makefile (i.e. the bottommost makefile in the tree) contains the single line:

include ../../common.mk

The number of ../ components must be correct to get at the common.mk include file, which resides in the project level of the tree. The reason that the number of ../ components isn't necessarily the same in all cases has to do with whether directory levels are being collapsed.

Recognized variant names

Variant names can be combined into a compound variant; use a period (.), dash (-) or slash (/) between the variants.

The common makefiles are triggered by a number of distinguished variant names:

a
The image being built is an object library.
so
The image being built is a shared object.
dll
The image being built is a DLL; it's linked with the -Bsymbolic option (see ld in the Utilities Reference).

If the compound variant doesn't include a, so, or dll, an executable is being built.

shared
Compile the object files for .so use, but don't create an actual shared object. You typically use this name in an a.shared variant to create a static link archive that can be linked into a shared object.
g
Compile and link the source with the debugging flag set.
be, le
Compile and link the source to generate big (if be) or little (if le) endian code. If a CPU supports bi-endian operation, one of these variants should always be present in the compound variant name. Conversely, if the CPU is mono-endian, neither be nor le should be specified in the compound variant.
gcc
Use the GCC (gcc) compiler to compile the source. If you don't specify a compiler, the makefiles provide a default.
o
This is the NULL variant name. It's used when building an image that doesn't really have any variant components to it (e.g an executable for an x86 CPU, which doesn't support bi-endian operation).

Variant names can be placed in any order in the compound variant, but to avoid confusing a source configuration management tool (e.g. CVS), make sure that the last variant in the list never looks like a generated file suffix. In other words, don't use variant names ending in .a, .so, or .o.

The following table lists some examples:

Variant Purpose
g.le A debugging version of a little-endian executable.
so.be A big-endian version of a shared object.
403.be A user-defined "403" variant for a big-endian system.

Note: The only valid characters for variant names are letters, digits, and underscores (_).

In order for the source code to tell what variant(s) it's being compiled for, the common makefiles arrange for each variant name to be postfixed to the string VARIANT_ and have that defined as a C or assembler macro on the command line. For example, if the compound variant is so.403.be, the following C macros are defined: VARIANT_so, VARIANT_403, and VARIANT_be. Note that neither VARIANT_be nor VARIANT_le is defined on a CPU that doesn't support bi-endian operation, so any endian-specific code should always test for the C macros __LITTLEENDIAN__ or __BIGENDIAN__ (instead of VARIANT_le or VARIANT_be) to determine what endianness it's running under.

Using the standard macros and include files

We've described the pieces you'll provide when building your system, including the common.mk include file. There are two other include files to discuss:

We'll also look at some of the macros that are set or used by those include files.

The qconfig.mk include file

Since the common makefiles have a lot of defaults based on the names of various directories, you can simplify your life enormously in the common.mk include file if you choose your directory names to match what the common makefiles want. For example, if the name of the project directory is the same as the name of the image, you don't have to set the NAME macro in common.mk.

The prototypical common.mk file looks like this:

ifndef QCONFIG
QCONFIG=qconfig.mk
endif
include $(QCONFIG)

# Preset make macros go here

include $(MKFILES_ROOT)/qtargets.mk

# Post-set make macros go here

The qconfig.mk include file provides the root paths to various install, and usage trees on the system, along with macros that define the compilers and some utility commands that the makefiles use. The purpose of the qconfig.mk include file is to let you tailor the root directories, compilers, and commands used at your site, if they differ from the standard ones that we use and ship. Therefore, nothing in a project's makefiles should refer to a compiler name, absolute path, or command name directly. Always use the qconfig.mk macros.

The qconfig.mk file resides in /usr/include/mk as qconf-os.mk (where os is the host OS, e.g. nto, qnx4, solaris, NT), which is a symbolic link from the place where make wants to find it (namely /usr/include/qconfig.mk). You can override the location of the include file by specifying a value for the QCONFIG macro.

If you wish to override the values of some of the macros defined in qconfig.mk without modifying the contents of the file, set the QCONF_OVERRIDE environment variable (or make macro) to be the name of a file to include at the end of the main qconfig.mk file.

Preset macros

Before including qtargets.mk, some macros need to be set to determine things like what additional libraries need to be searched in the link, the name of the image (if it doesn't match the project directory name), and so on. This would be done in the area tagged as "Preset make macros go here" in the sample above.

Postset macros

Following the include of qtargets.mk, you can override or (more likely) add to the macros set by qtargets.mk. This would be done in the area tagged as "Post-set make macros go here" in the sample above.

qconfig.mk macros

Here's a summary of the macros available from qconfig.mk:

CP_HOST
Copy files from one spot to another.
LN_HOST
Create a symbolic link from one file to another.
RM_HOST
Remove files from the filesystem.
TOUCH_HOST
Update a file's access and modification times.
PWD_HOST
Print the full path of the current working directory.
CL_which
Compile and link.
CC_which
Compile C/C++ source to an object file.
AS_which
Assemble something to an object file.
AR_which
Generate an object file library (archive).
LR_which
Link a list of objects/libraries to a relocatable object file.
LD_which
Link a list of objects/libraries to a executable/shared object.
UM_which
Add a usage message to an executable.

The which parameter can be either the string HOST for compiling something for the host system or a triplet of the form os_cpu_compiler to specify a combination of target OS and CPU, as well as the compiler to be used.

The os would usually be the string nto to indicate Neutrino. The cpu would be one of x86, mips, ppc, arm or sh. Finally, the compiler would be one of gcc.

For example, the macro CC_nto_x86_gcc would be used to specify:

The following macro would contain the command-line sequence required to invoke the GCC compiler:

CC_nto_x86_gcc = qcc -Vgcc_ntox86 -c

The macros CP_HOST, LN_HOST, RM_HOST, TOUCH_HOST, and PWD_HOST are used by the various makefiles to decouple the OS commands from the commands used to perform the given actions. For example, under most POSIX systems, the CP_HOST macro expands to the cp utility. Under other operating systems, it may expand to something else (e.g. copy).

In addition to the macros mentioned above, you can use the following macros to specify options to be placed at the end of the corresponding command lines:

The parameter "which" is the same as defined above: either the string "HOST" or the ordered triplet defining the OS, CPU, and compiler.

For example, specifying the following:

CCPOST_nto_x86_gcc = -ansi

would cause the command line specified by CC_nto_x86_gcc to have the additional string "-ansi" appended after it.

The qrules.mk include file

The qrules.mk include file has the definitions for compiling.

The following macros can be set and/or inspected when qrules.mk is used. Since the qtargets.mk file includes qrules.mk, these are available there as well. Don't modify those that are marked "(read-only)."

VARIANT_LIST (read-only)
A space-separated list of the variant names macro. Useful with the $(filter ...) make function for picking out individual variant names.
CPU
The name of the target CPU. Defaults to the name of the next directory up with all parent directories stripped off.
CPU_ROOT (read-only)
The full pathname of the directory tree up to and including the OS level.
OS
The name of the target OS. Defaults to the name of the directory two levels up with all parent directories stripped off.
OS_ROOT (read-only)
The full pathname of the directory tree up to and including the OS level.
SECTION
The name of the section. Set only if there's a section level in the tree.
SECTION_ROOT (read-only)
The full pathname of the directory tree up to and including the section level.
PROJECT (read-only)
The basename() of the directory containing the common.mk file.
PROJECT_ROOT (read-only)
The full pathname of the directory tree up to and including the project level.
PRODUCT (read-only)
The basename() of the directory above the project level.
PRODUCT_ROOT (read-only)
The full pathname of the directory tree up to and including the product level.
NAME
The basename() of the executable or library being built. Defaults to $(PROJECT).
SRCVPATH
A space-separated list of directories to search for source files. Defaults to all the directories from the current working directory up to and including the project root directory. You'd almost never want to set this; use EXTRA_SRCVPATH to add paths instead.
EXTRA_SRCVPATH
Added to the end of SRCVPATH. Defaults to none.
INCVPATH
A space-separated list of directories to search for include files. Defaults to $(SRCVPATH) plus $(USE_ROOT_INCLUDE). You'd almost never want to set this; use EXTRA_INCVPATH to add paths instead.
EXTRA_INCVPATH
Added to INCVPATH just before the $(USE_ROOT_INCLUDE). Default is none.
LIBVPATH
A space-separated list of directories to search for library files. Defaults to:
. $(INSTALL_ROOT_support)/$(OS)/$(CPUDIR)/lib $(USE_ROOT_LIB).
      

You'll almost never want to use this; use EXTRA_LIBVPATH to add paths instead.

EXTRA_LIBVPATH
Added to LIBVPATH just before $(INSTALL_ROOT_support)/$(OS)/$(CPUDIR)/lib. Default is none.
DEFFILE
The name of an assembler define file created by mkasmoff. Default is none.
SRCS
A space-separated list of source files to be compiled. Defaults to all *.s, *.S, *.c, and *.cc files in SRCVPATH.
EXCLUDE_OBJS
A space-separated list of object files not to be included in the link/archive step. Defaults to none.
EXTRA_OBJS
A space-separated list of object files to be added to the link/archive step even though they don't have corresponding source files (or have been excluded by EXCLUDE_OBJS). Default is none.
OBJPREF_object, OBJPOST_object
Options to add before or after the specified object:
OBJPREF_object = options
OBJPOST_object = options
      

The options string is inserted verbatim. Here's an example:

OBJPREF_libc_cut.a = -Wl,--whole-archive
OBJPOST_libc_cut.a = -Wl,--no-whole-archive
      
LIBS
A space-separated list of library stems to be included in the link. Default is none.
LIBPREF_library, LIBPOST_library
Options to add before or after the specified library:
LIBPREF_library = options
LIBPOST_library = options
      

The options string is inserted verbatim.

You can use these macros to link some libraries statically and others dynamically. For example, here's how to bind libmystat.a and libmydyn.so to the same program:

LIBS += mystat mydyn

LIBPREF_mystat = -Bstatic
LIBPOST_mystat = -Bdynamic
      

This places the -Bstatic option just before -lmystat, and -Bdynamic right after it, so that only that library is linked statically.

CCFLAGS
Flags to add to the C compiler command line.
ASFLAGS
Flags to add to the assembler command line.
LDFLAGS
Flags to add to the linker command line.
VFLAG_which
Flags to add to the command line for C compiles, assemblies, and links; see below.
CCVFLAG_which
Flags to add to C compiles; see below.
ASVFLAG_which
Flags to add to assemblies; see below.
LDVFLAG_which
Flags to add to links; see below.
OPTIMIZE_TYPE
The optimization type; one of:

Note that for the VFLAG_which, CCVFLAG_which, ASVFLAG_which, and LDVFLAG_which macros, the which part is the name of a variant. This combined macro is passed to the appropriate command line. For example, if there were a variant called "403," then the macro VFLAG_403 would be passed to the C compiler, assembler, and linker.


Note: Don't use this mechanism to define a C macro constant that you can test in the source code to see if you're in a particular variant. The makefiles do that automatically for you. Don't set the *VFLAG_* macros for any of the distinguished variant names (listed in the "Recognized variant names" section, above). The common makefiles will get confused if you do.

The qtargets.mk include file

The qtargets.mk include file has the linking and installation rules.

The following macros can be set and/or inspected when qtargets.mk is used:

INSTALLDIR
Subdirectory where the executable or library is to be installed. Defaults to bin for executables and lib/dll for DLLs. If set to /dev/null, then no installation is done.
USEFILE
The file containing the usage message for the application. Defaults to none for archives and shared objects and to $(PROJECT_ROOT)/$(NAME).use for executables. The application-specific makefile can set the macro to a null string, in which case nothing is added to the executable.
LINKS
A space-separated list of symbolic link names that are aliases for the image being installed. They're placed in the same directory as the image. Default is none.
PRE_TARGET, POST_TARGET
Extra steps to do before/after the main target.
PRE_CLEAN, POST_CLEAN
Extra steps to do before/after clean target.
PRE_ICLEAN, POST_ICLEAN
Extra steps to do before/after iclean target.
PRE_HINSTALL, POST_HINSTALL
Extra steps to do before/after hinstall target.
PRE_CINSTALL, POST_CINSTALL
Extra steps to do before/after cinstall target.
PRE_INSTALL, POST_INSTALL
Extra steps to do before/after install target.
PRE_BUILD, POST_BUILD
Extra steps to do before/after building the image.
SO_VERSION
Set the SONAME version number when building a shared object (the default is 1).
PINFO
Define information to go into the *.pinfo file.

For example, you can use the PINFO NAME option to to keep a permanent record of the original filename of a binary. If you use this option, the name that you specify appears in the information from the use -i filename command. Otherwise, the information from use -i contains the NAME entry specified outside of the PINFO define.

For more information about PINFO, see the hook_pinfo() function described below for the GNU configure command.

Advanced topics

In this section, we'll discuss how to:

Collapsing unnecessary directory levels

The directory structure shown above (in "Structure") defines the complete tree -- every possible directory level is shown. In the real world, however, some of these directory levels aren't required. For example, you may wish to build a particular module for a PowerPC in little-endian mode and never need to build it for anything else (perhaps due to hardware constraints). Therefore, it seems a waste to have a variant level that has only the directory o.le and a CPU level that has only the directory ppc.

In this situation, you can collapse unnecessary directory components out of the tree. You do this by simply separating the name of the components with dashes (-) rather than slashes (/).

For example, in our source tree (/usr/src/hardware), let's look at the startup/boards/800fads/ppc-be makefile:

include ../common.mk

In this case, we've specified both the variant (as "be" for big-endian) and the CPU (as "ppc" for PowerPC) with a single directory.

Why did we do this? Because the 800fads directory refers to a very specific board -- it's not going to be useful for anything other than a PowerPC running in big-endian mode.

In this case, the makefile macros would have the following values:

Macro Value
VARIANT1 ppc-be
CPU ppc
OS nto (default)
SECTION 800fads
PROJECT boards

The addvariant command knows how to create both the squashed and unsquashed versions of the directory tree. You should always use it when creating the OS, CPU, and variant levels of the tree.

Performing partial builds

By using the LIST tag in the makefile, you can cause the make command to perform a partial build, even if you're at the top of the source tree.

If you were to simply type make without having used the LIST tag, all directories would be recursed into and everything would be built.

However, by defining a macro on make's command line, you can:

Let's consider an example. The following (issued from the top of the source tree):

make CPULIST=x86

causes only the directories that are at the CPU level and below (and tagged as LIST=CPU), and that are called x86, to be recursed into.

You can specify a space-separated list of directories (note the use of quoting in the shell to capture the space character):

make "CPULIST=x86 mips"

This causes the x86 and MIPS versions to be built.

There's also the inverse form, which causes the specific lists not to be built:

make EXCLUDE_CPULIST=ppc

This causes everything except the PowerPC versions to be built.

As you can see from the above examples, the following are all related to each other via the CPU portion:

More uses for LIST

Besides using the standard LIST values that we use, you can also define your own. Therefore, in certain makefiles, you'd put the following definition:

LIST=CONTROL

Then you can decide to build (or prevent from building) various subcomponents marked with CONTROL. This might be useful in a very big project, where compilation times are long and you need to test only a particular subsection, even though other subsections may be affected and would ordinarily be made.

For example, if you had marked two directories, robot_plc and pidloop, with the LIST=CONTROL macro within the makefile, you could then make just the robot_plc module:

make CONTROLLIST=robot_plc

Or make both (note the use of quoting in the shell to capture the space character):

make "CONTROLLIST=robot_plc pidloop"

Or make everything except the robot_plc module:

make EXCLUDE_CONTROLLIST=robot_plc

Or make only the robot_plc module for MIPS big-endian:

make CONTROLLIST=robot_plc CPULIST=mips VARIANTLIST=be

GNU configure

The way things are being done now can be used with any future third-party code that uses a GNU ./configure script for configuration.


Note: The steps given below shouldn't overwrite any existing files in the project; they just add new ones.

Here's how to set up a project:

  1. Go to the root directory of your project.
  2. Use addvariant to create a Makefile in the project root directory that looks like this:
    LIST=OS CPU VARIANT
    MAKEFILE=GNUmakefile
    include recurse.mk
  3. Now, create a directory (or directories) of the form os-cpu-variant, e.g. nto-x86-o or nto-mips-le. This the same as our common makefiles, except that rather than being in different directories, all the levels are squashed together (which recurse.mk knows because it has multiple recursion control variables specified).

    You can add further variants following the first ones, if there are additional different variations that you need to build.

    For example, the GCC directories look like: nto-x86-o-ntoarm for the Neutrino/X86 hosted, Neutrino/ARM targeted compiler, or solaris-sparc-o-ntox86 for the Solaris/Sparc hosted, Neutrino/X86 targeted compiler.

  4. In each of the new directories, use addvariant to create a file called a GNUmakefile (note the name!) that looks like this:
    ifndef QCONFIG
    QCONFIG=qconfig.mk
    endif
    include $(QCONFIG)
    
    include $(MKFILES_ROOT)/qmake-cfg.mk
  5. In the root of the project, create a build-hooks file. It's a shell script, so it needs be marked as executable. It needs to define one or more of the following shell functions (described in more detail below):

Every time that you type make in one of the newly created directories, the GNUmakefile is read (a small trick that works only with GNU make). GNUmakefile in turn invokes the /usr/include/mk/build-cfg script, which notices whether or not configure has been run in the directory:

If a function isn't defined in build-hooks, build-cfg doesn't bother trying to invoke it.

Within the build-hooks script, the following variables are available:

SYSNAME
This is the host OS (e.g. nto, solaris) that we're running on. This is automatically set by build-cfg based on the results of uname.
TARGET_SYSNAME
This is the target OS (e.g. nto, win32) that we're going to be generating executables for. It's set automatically by build-cfg, based on the directory that you're in.
make_CC
This variable is used to set the CC make variable when we invoke make. This typically sets the compiler that make uses. It's set automatically by build-cfg, based on the directory that you're in.
make_opts
Any additional options that you want to pass to make (the default is "").
make_cmds
The command goals passed to make (e.g. all). It's set automatically by build-cfg what you passed on the original make command line.
configure_opts
The list of options that should be passed to configure. The default is "", but --srcdir=.. is automatically added just before configure is called.

hook_preconfigure()

This function is invoked just before we run the project's configure script. Its main job is to set the configure_opts variable properly. Here's a fairly complicated example (this is from GCC):

# The "target" variable is the compilation target: "ntoarm", "ntox86", etc.
function hook_preconfigure {
    case ${SYSNAME} in
    nto)
        case "${target}" in
        nto*)   basedir=/usr ;;
        *)      basedir=/opt/QNXsdk/host/qnx6/x86/usr ;;
        esac
        ;;
    solaris)
        host_cpu=$(uname -p)
        case ${host_cpu} in
        i[34567]86) host_cpu=x86 ;;
        esac
        basedir=/opt/QNXsdk/host/solaris/${host_cpu}/usr
        ;;
    *)
        echo "Don't have config for ${SYSNAME}"
        exit 1
        ;;
    esac
    configure_opts="${configure_opts} --target=${target}"
    configure_opts="${configure_opts} --prefix=${basedir}"
    configure_opts="${configure_opts} --exec-prefix=${basedir}"
    configure_opts="${configure_opts} --with-local-prefix=${basedir}"
    configure_opts="${configure_opts} --enable-haifa"
    configure_opts="${configure_opts} --enable-languages=c++"
    configure_opts="${configure_opts} --enable-threads=posix"
    configure_opts="${configure_opts} --with-gnu-as"
    configure_opts="${configure_opts} --with-gnu-ld"
    configure_opts="${configure_opts} --with-as=${basedir}/bin/${target}-as"
    configure_opts="${configure_opts} --with-ld=${basedir}/bin/${target}-ld"
    if [ ${SYSNAME} == nto ]; then
        configure_opts="${configure_opts} --enable-multilib"
        configure_opts="${configure_opts} --enable-shared"
    else
        configure_opts="${configure_opts} --disable-multilib"
    fi
}

hook_postconfigure()

This is invoked after configure has been successfully run. Usually you don't need to define this function, but sometimes you just can't quite convince configure to do the right thing, so you can put some hacks in here to munge things appropriately. For example, again from GCC:

function hook_postconfigure {
    echo "s/^GCC_CFLAGS *=/&-I\$\(QNX_TARGET\)\/usr\/include /"  >/tmp/fix.$$
    if [ ${SYSNAME} == nto ]; then
        echo "s/OLDCC = cc/OLDCC = .\/xgcc -B.\/ -I \$\(QNX_TARGET\)\/usr\/include/" >>/tmp/fix.$$
        echo "/^INCLUDES = /s/\$/ -I\$\(QNX_TARGET\)\/usr\/include/" >>/tmp/fix.$$
        if [ ${target} == ntosh ]; then
            # We've set up GCC to support both big and little endian, but
            # we only actually support little endian right now. This will
            # cause the configures for the target libraries to fail, since
            # it will test the compiler by attempting a big endian compile
            # which won't link due to a missing libc & crt?.o files.
            # Hack things by forcing compiles/links to always be little endian
            sed -e "s/^CFLAGS_FOR_TARGET *=/&-ml /"  <Makefile >1.$$
            mv 1.$$ Makefile
        fi
    else
        # Only need to build libstdc++ & friends on one host
        rm -Rf ${target}

        echo "s/OLDCC = cc/OLDCC = .\/xgcc -B.\//" >>/tmp/fix.$$
    fi
    cd gcc
    sed -f/tmp/fix.$$ <Makefile >1.$$
    mv 1.$$ Makefile
    cd ..
    rm /tmp/fix.$$
}

hook_premake()

This function is invoked just before the make. You don't usually need it.

hook_postmake()

This function is invoked just after the make. We haven't found a use for this one yet, but included it for completeness.

hook_pinfo()

This function is invoked after hook_postmake(). Theoretically, we don't need this hook at all and we could do all its work in hook_postmake(), but we're keeping it separate in case we get fancier in the future.

This function is responsible for generating all the *.pinfo files in the project. It does this by invoking the gen_pinfo() function that's defined in build-cfg, which generates one .pinfo. The command line for gen_pinfo() is:

gen_pinfo [-nsrc_name ] install_name install_dir pinfo_line...

The arguments are:

src_name
The name of the pinfo file (minus the .pinfo suffix). If it's not specified, gen_pinfo() uses install_name.
install_name
The basename of the executable when it's installed.
install_dir
The directory the executable should be installed in. If it doesn't begin with a /, the target CPU directory is prepended to it. For example, if install_dir is usr/bin and you're generating an X86 executable, the true installation directory is /x86/usr/bin.
pinfo_line
Any additional pinfo lines that you want to add. You can repeat this argument as many times as required. Favorites include:

Here's an example from the nasm project:

function hook_pinfo {
    gen_pinfo nasm    usr/bin LIC=NASM DESCRIPTION="Netwide X86 Assembler"
    gen_pinfo ndisasm usr/bin LIC=NASM DESCRIPTION="Netwide X86 Disassembler"
}

[Previous] [Contents] [Index] [Next]