Plan 9 Mkfiles

Bob Flandrena

bobf@plan9.bell-labs.com

Introduction

Every Plan 9 source directory contains a file, called mkfile, specifying the rules for building the executable or library that is the product of the directory. Mk(1) interprets the rules in the file, calculates the dependencies, and executes an rc(1) script to construct the product. If necessary components are supplied by neighboring directories or sub-directories, the mkfiles in those directories are first executed to build the components before the local construction proceeds.

Most application source directories produce one of four types of product: a single executable, several executables, a local library, or a system library. Four generic mkfiles define the normal rules for building each type of product. The simplest mkfiles need only list the components and include the appropriate generic mkfile to do the work. More complex mkfiles may supply additional rules to augment, modify, or override the generic rules.

Using a Mkfile

To build a product, change to the directory containing its source and invoke mk with the appropriate target as an argument. All mkfiles provide the following standard targets:

If no target is specified on the mk command line, the all target is built by default. In a directory producing multiple executables, there is no default target.

In addition to the five standard targets, additional targets may be supplied by each generic mkfile or by the directory’s mkfile.

The environment variable NPROC is set by the system to the number of available processors. Setting this variable, either in the environment or in a mkfile, controls the amount of parallelism in the build. For example, the command

    NPROC=1 mk

restricts a build to a single thread of execution.

Creating a Mkfile

The easiest way to build a new mkfile is to copy and modify an existing mkfile of the same type. Failing that, it is usually possible to create a new mkfile with minimal effort, since the appropriate generic mkfile predefines the rules that do all the work. In the simplest and most common cases, the new mkfile need only define a couple of variables and include the appropriate architecture-specific and generic mkfiles.

There are four generic mkfiles containing commonly used rules for building a product: mkone, mkmany, mklib, and mksyslib. These rules perform such actions as compiling C source files, loading object files, archiving libraries, and installing executables in the bin directory of the appropriate architecture. The generic mkfiles are stored in directory /sys/src/cmd. Mkfile mkone builds a single executable, mkmany builds several executables from the source in a single directory, and mklib and mksyslib, maintain local and system libraries, respectively. The rules in the generic mkfiles are driven by the values of variables, some of which must be set by the product mkfile and some of which are supplied by the generic mkfile. Variables in the latter class include:

The following variables are set by the product mkfile and used by the generic mkfile. Any may be empty depending on the specific product being made.

Mkfile Organization

All mkfiles share the following common structure:

</$objtype/mkfile   # architecture-dependent definitions

variable definitions        # TARGOFILESHFILES, etc.

</sys/src/cmd/generic   # mkonemkmanymklib, or mksyslib

variable overrides      # CFLAGSobjtype, etc.

extra rules         # overrides, augmented rules, additional targets

Note that the architecture-dependent mkfiles include file /sys/src/mkfile.proto for system-wide variables that are common to all architectures.

The variables driving the expansion of the generic mkfile may be specified in any order as long as they are defined before the inclusion of the generic mkfile. The value of a variable may be changed by assigning a new value following the inclusion of the generic mkfile, but the effects are sometimes counter-intuitive. Such variable assignments do not apply to the target and prerequisite portions of any previously defined rules; the new values only apply to the recipes of rules preceding the assignment statement and to all parts of any rules following it.

The rules supplied by the generic mkfile may be overridden or augmented. The new rules must be specified after the inclusion of the generic mkfile. If the target and prerequisite portion of the rule exactly match the target and prerequisite portion of a previously defined rule and the new rule contains a recipe, the new rule replaces the old one. If the target of a new rule exactly matches the target of a previous rule and one or more new prerequisites are specified and the new rule contains no recipe, the new prerequisites are added to the prerequisites of the old rule.

Following sections discuss each generic mkfile in detail.

Mkone

The mkone generic mkfile contains rules for building a single executable from one or more files in a directory. The variable TARG specifies the name of the executable and variables OFILES and YFILES specify the object files and yacc source files used to build it. HFILES contains the names of the local header files included in all source files. BIN is the name of the directory where the executable is installed. LIB contains the names of local libraries used by the linker. This variable is rarely needed as libraries referenced by a #pragma directive in an associated header file, including all system libraries, are automatically searched by the loader.

If mk is executed without a target, the all target is built; it produces an executable in $O.out. Variable HFILES identifies the header files that are included in all or most or the C source files. Occasionally, a program has other header files that are only used in some source files. A header can be added to the prerequisites for those object files by adding a rule of the following form following the inclusion of generic mkfile mkone:

file.$O:    header.h

The mkfile for a directory producing a single executable using the normal set of rules is trivial: a list of some files followed by the inclusion of mkone. For example, /sys/src/cmd/diff/mkfile contains:

< /$objtype/mkfile

TARG=diff

OFILES=\

    diffdir.$O\

    diffio.$O\

    diffreg.$O\

    main.$O\

HFILES=diff.h

BIN=/$objtype/bin

</sys/src/cmd/mkone

The more complex mkfile in /sys/src/cmd/awk overrides compiler and loader variables to select the ANSI/POSIX Computing Environment with appropriately defined command line variables. It also overrides the default yacc rule to place the output soure in file awkgram.c and the clean and nuke rules, so it can remove the non-standard intermediate files. Finally, the last three rules build a version of maketab appropriate for the architecture where the mk is being run and then executes it to create source file proctab.c:

</$objtype/mkfile

TARG=awk

OFILES=re.$O\

    lex.$O\

    main.$O\

    parse.$O\

    proctab.$O\

    tran.$O\

    lib.$O\

    run.$O\

    awkgram.$O\

HFILES=awk.h\

    y.tab.h\

    proto.h\

YFILES=awkgram.y

BIN=/$objtype/bin

</sys/src/cmd/mkone

CFLAGS=-c -D_REGEXP_EXTENSION -D_RESEARCH_SOURCE \

    -D_BSD_EXTENSION -DUTF

YFLAGS=-S -d -v

CC=pcc

LD=pcc

cpuobjtype=‘{sed -n ’s/^O=//p’ /$cputype/mkfile}

y.tab.h awkgram.c:  $YFILES

    $YACC -o awkgram.c $YFLAGS $prereq

clean:V:

    rm -f *.[$OS] [$OS].out [$OS].maketab y.tab.? y.debug\