Using Makefiles With TeamWare |
![]() |
Configuring does not depend on any specific release of make or on any other system modeler, but your site's makefile hierarchy may affect the way you organize your Configuring workspaces. In addition, poorly written makefiles may keep you from realizing the full potential of DMake.
The following sections describe the different ways three software development groups structured their makefile hierarchies. The makefile organizations range from casual to formal, and each organization has different implications for Configuring. In each case, the source directory tree was structured along modular lines, with one module or deliverable in each subdirectory of the tree.
A major disadvantage of this organization was that minor unforeseen changes to the build (a new location for include files, for example) required a change to each makefile. Also, style consistency in makefiles was difficult to enforce, which resulted in makefiles that were difficult to maintain.
A final disadvantage became apparent as the Bantam project matured. Over time, the project became larger than first envisioned, and the source tree eventually required so many large makefiles that understanding and maintaining them became difficult.
Some low-level makefiles included makefiles from intermediate directories in the source tree. For example, one family of commands had so much in common that the makefile from a common parent directory was included in the makefile for each command. This case is shown in Figure 25, where cmd.Makefile is included in the makefile for the first_cmd module.
The group had to be careful not to include high-level makefiles more than once in lower-level makefiles. For example, Figure 25 shows master.Makefile being included in the lowest level makefile. It should not be included in an intermediate makefile such as cmd.Makefile that is also included at the lowest level, although such multiple inclusions rarely lead to fatal errors.
Figure 25 High-Level Makefiles Included Directly in Low-Level Makefiles
Nested Inclusion of Makefiles
The last case describes the experience of the Hefty project, which was large and required the most formal strategy in its makefile organization. The Hefty group decided to require that each makefile include the makefile immediately above it in the source tree. The most general makefiles were at the top level. Descending the source tree, each subdirectory contained a makefile with increasingly specific build instructions. The formal organization ensured that high-level makefiles were included only once in lower-level makefiles.
Figure 26 Nested Makefiles -- Each Makefile Includes the Makefile Above It
More About Configuring and Makefiles
When Configuring copies source files between workspaces in bringover and putback transactions, it relies on SCCS to identify, control, and track the files. In fact, Configuring operates exclusively on SCCS files, so the decision about what files to place under SCCS control is important. Makefile Contents
A makefile contains a list of derived modules (usually files), called targets, and the modules required to build them (called dependencies). A target may be the final result of a long series of compilations (a top-level target) or an intermediate result (secondary target) used to build a top-level target. Typical secondary targets are the object (.o) files produced by C compilers. A makefile may also contain explicit rules describing how a target is to be built from its dependencies. Makefiles as Derived Source
One form of secondary target is the derived source file, a source file that is generated automatically near the beginning of a build. At some sites, makefiles or parts of makefiles are written automatically at the beginning of the build process by a makefile generator and are themselves derived source. Generated Makefiles
As the Concoct group reviewed the original rationale for using makefile generators, they realized that the main reason was to read the contents of one or more directories, identify the source files in them, and explicitly list those file names in the generated makefile. For example, one makefile generator scanned the build directory for C++ source files of the form *.cc, built a list, and wrote a makefile that included the list explicitly. Controlling Generated Makefiles
The Concoct group actually used three different makefile generators:
Figure 27 Putting Makefile Generators Under SCCS Control
The GUI Design Editor provides a graphical way for developers to design user interfaces without writing code. After the interface has been designed on the screen, the result is saved to a GIL (Guide Interchange Language) file, an intermediate text-based representation of the interface. The GIL file is a source file -- the GUI Design Editor that creates it is analogous to the text editor a developer might use to write an ordinary source file. Because the Concoct group's executables are targeted for an XView application, they run the postprocessor GXV (Guide-to-X View) on the GIL file to generate XView source in the form of a header file, two source files, and a makefile. These derived sources are used by make to build the desired executable.
The use of GXV (or one of the other Devguide postprocessors) presents the same challenge to source code control as a makefile generator -- the files actually used by make to build the executable are derived sources. Because they are derived during the build process, they should not be placed under SCCS control. The GIL file created by the GUI Design Editor, however, can be considered true source because it is generated by the developer prior to the build process. As true source, the GIL file can be placed under SCCS control, and Configuring will bring it over and put it back along with other source.
The first step in the Concoct group's automated build process is to run the postprocessor GXV to regenerate the derived source. Once created, the derived source is never hand edited and is deleted along with other intermediate targets following the build.
In deciding not to hand edit derived source, the group avoided the following complications: if the derived source were to be hand edited, it would have to be placed under SCCS control (to make it eligible for Configuring bringovers). In order to allow the derived source to be rederived, the makefile would have to be enhanced to check out the derived source (making it writable), the hand edits would have to be merged into the checked out file, and the result checked back in to SCCS. Only then could the project build begin.
In designing its makefile hierarchy, the Concoct group adopted the makefile inclusion strategy used by the Midpoint group (shown in Figure 25 on page 96). In this strategy, one or more top-level makefiles are included in each makefile in the directory hierarchy.
The example makefiles are not explained in great detail. Refer to the make man page and the manuals SunOS 5.0 Programming Utilities and SunOS 5.0 User's Guide for more information on make and makefiles.
Master Makefile
The master makefile shown in Figure 28 defines several macros (LDLIBS, CC, CPPFLAGS, and so on). It also sets the special-function target .KEEP_STATE for all makefiles. Because the master makefile is included in all other makefiles, these definitions apply throughout each build.
# master.Makefile, to be included in all # subordinate makefiles. .KEEP_STATE: LDLIBS = -L/usr/lib CC = cc CPPFLAGS = -DSUN5_x CFLAGS = -g -xs LINK.c = ${CC} ${CFLAGS} ${CPPFLAGS} ${LDFLAGS} |
Figure 28 Top-Level Makefile for the Concoct Project
Low-Level Makefile
A typical low-level makefile for the Concoct project is shown in Figure 29. This makefile is used to build an executable named cogitate.
Figure 29 Low-Level Makefile for Building the cogitate Executable
Using Makefiles With DMake
DMake, the make utility bundled with Sun WorkShop TeamWare, can build several targets simultaneously in parallel processes. This new capability contrasts with earlier versions of make, which build targets one at a time in the sequence in which they appear in the makefile. DMake is a syntactic superset of the standard SunTM version of make. It supports all the syntax of standard make and works with existing makefiles. Improving Performance With DMake
Most build processes are I/O bound, which means that they are limited by the speed at which data can be read and written from mass storage devices, not by CPU performance. Therefore, building targets in parallel results in performance increases on all machines, even single-processor ones, but the greatest improvements are seen in the new classes of multiprocessor servers and workstations. Listing Dependencies Explicitly in Makefiles
Most makefiles that were written for earlier versions of make will work with DMake. When problems occur, they usually result from a reliance on the order in which targets appear in a makefile to establish the build order. These problems can be avoided by explicitly listing each target's dependencies rather than relying on the build of another target to bring the dependencies up to date. For example, consider the following makefile fragment, which the Concoct group inherited from an early version of the project:
all:prog1 prog2 prog1: prog1.o aux.o prog2: prog2.o |
When built in serial, the target aux.o was built as a dependent of prog1 and was up to date for the build of prog2. However, when built in parallel, the link of prog2 sometimes began before aux.o had been built, and was therefore incorrect. The group corrected the dependency list for the prog2 target to read:
prog2: prog2.0 aux.o
Controlling DMake With Special Targets
Other cases of the implicit ordering of dependencies were more difficult for the Concoct group to identify and correct. The ParallelMake User's Guide discusses these cases in detail. In the end, the group used the following special-function targets to handle their most difficult problems:
all: hdrs .WAIT libs functions
.NO_PARALLEL: prog1 prog2
.NO_PARALLEL:
.PARALLEL: prog3 prog4