Museum

Home

Lab Overview

Retrotechnology Articles

⇒ Online Manual

Media Vault

Software Library

Restoration Projects

Artifacts Sought

Related Articles

make(1)

touch(1)

sh(1)

regexp(3)

MK(1)                                BSD                                 MK(1)



NAME
     mk, mkconv, membername - maintain (make) related files

SYNOPSIS
     mk [ -f mkfile ] ...  [ option ... ] [ name ... ]

     mkconv makefile

     membername aggregate ...

DESCRIPTION
     mk is most often used to keep object files current with the source they
     depend on.

     mk reads mkfile and builds and executes dependency dags (directed acyclic
     graphs) for the target names.  If no target is specified, the targets of
     the first non-metarule in the first mkfile are used.  If no -f option is
     present, is tried.  Other options are:
     -a       Assume all targets to be out of date.  Thus, everything gets
              made.
     -d[egp]  Produce debugging output (p is for parsing, g for graph
              building, e for execution).
     -e       Explain why each target is made.
     -i       Force any missing intermediate targets to be made.
     -k       Do as much work as possible in the face of errors.
     -n       Print, but do not execute, the commands needed to update the
              targets.
     -t       Touch (update the modified date of) non-virtual targets, without
              executing any recipes.
     -u       Produce a table of clock seconds spent with n recipes running.
     -wname1,name2,...
              Set the initial date stamp for each name to the current time.
              The names may also be separated by blanks or newlines.  (Use
              with -n to find what else would need to change if the named
              files were modified.)

     mkconv does a syntactic conversion from a make(1) makefile to a mkfile on
     standard output.  Because of the semantic differences, the resulting
     mkfile may behave differently than the original makefile.

     The shell script membername extracts member names (see `Aggregates'
     below) from its arguments.

   Definitions
     A mkfile consists of assignments (described under `Environment') and
     rules.  A rule contains targets and a tail. A target is a literal string,
     or label, and is normally a file name.  The tail contains zero or more
     prerequisites and an optional recipe, which is a shell script.

     A metarule has a target of the form A%B where A and B are (possibly
     empty) strings.  A metarule applies to any label that matches the target
     with % replaced by an arbitrary string, called the stem.  In interpreting
     a metarule, the stem is substituted for all occurrences of % in the
     prerequisite names.  The dependencies may refer to subexpressions in the
     normal way, using \n.  The dependency dag for a target consists of nodes
     connected by directed arcs.  A node consists of a label and a set of arcs
     leading to prerequisite nodes.  The root node is labeled with an original
     target name.

   Building the Dependency Dag
     Read the mkfiles in command line order and distribute rule tails over
     targets to get single-target rules.

     For a node n, for every rule r that matches n's label generate an arc to
     a prerequisite node.  The node n is then marked as done.  The process is
     then repeated for each of the prerequisite nodes.  The process stops if n
     is already done, or if n has no prerequisites, or if any rule would be
     used more than $NREP times on the current path in the dag.  A probable
     node is one where the label exists as a file or is a target of a non-
     metarule.

     After the graph is built, it is checked for cycles, and subdags
     containing no probable nodes are deleted.  Also, for any node with arcs
     generated by a non-metarule with a recipe, arcs generated by a metarule
     with a recipe are deleted.  Disconnected subdags are deleted.

   Execution
     Labels have an associated date stamp.  A label is ready if it has no
     prerequisites, or all its prerequisites are made.  A ready label is
     trivially uptodate if it is not a target and has a nonzero date stamp, or
     it has a nonzero date stamp, and all its prerequisites are made and
     predate the ready label.  A ready label is marked made (and given a date
     stamp) if it is trivially uptodate or by executing the recipe associated
     with the arcs leading from the node associated with the ready label.  The
     P attribute can be used to generalise mk's notion of determining if
     prerequisites predate a label.  Rather than comparing date stamps, it
     executes a specified program and uses the exit status.

     Date stamps are calculated differently for virtual labels, for labels
     that correspond to extant files, and for labels that don't.  If a label
     is virtual (target of a rule with the V attribute), its date stamp is
     initially zero and when made set to the most recent date stamp of its
     prerequisites.  Otherwise, if a label is nonexistent (does not exist as a
     file), its date stamp is set to the most recent date stamp of its
     prerequisites, or zero if it has no prerequisites.  Otherwise, the label
     is the name of a file and the label's date stamp is always that file's
     modification date.

     Nonexistent labels which have prerequisites and are prerequisite to other
     label(s) are treated specially unless the -i flag is used.  Such a label
     l is given the date stamp of its most recent prerequisite.  If this
     causes all the labels which have l as a prerequisite to be trivially
     uptodate, l is considered to be trivially uptodate.  Otherwise, l is made
     in the normal fashion.

     Two recipes are called identical if they arose by distribution from a
     single rule as described above.  Identical recipes may be executed only
     when all their prerequisite nodes are ready, and then just one instance
     of the identical recipes is executed to make all their target nodes.

     Files may be made in any order that respects the preceding restrictions.

     A recipe is executed by execing the command
             /bin/sh -ce recipe
     The environment is augmented by the following variables:

     $alltarget    all the targets of this rule.

     $newprereq    the prerequisites that caused this rule to execute.

     $nproc        the process slot for this recipe.  It satisfies
                   0<$nproc<$NPROC, where $NPROC is the maximum number of
                   recipes that may be executing simultaneously.

     $pid          the process id for the mk forking the recipe.

     $prereq       all the prerequisites for this rule.
     $stem         if this is a metarule, $stem is the string that matched %.
                   Otherwise, it is empty.

     $target       the targets for this rule that need to be remade.

     Unless the rule has the Q attribute, the recipe is printed prior to
     execution with recognizable shell variables expanded.  To see the
     commands print as they execute, include a in your rule.  Commands
     returning nonzero status (see intro(1)) cause mk to terminate.

   Aggregates
     Names of the form a(b) refer to member b of the aggregate a.  Currently,
     the only aggregates supported are archives.

   Environment
     Rules may make use of shell (or environment) variables.  Any string which
     is a legal shell variable reference such as $OBJ or ${name} is expanded.
     Variables can be set by assignments of the form
             var=[attr=]tokens
     where tokens and the optional attributes are defined below (under
     `Syntax').  The environment is exported to recipe executions.  Variable
     values are taken from (in increasing order of precedence) the below
     default values, the environment, the mkfiles, and any command line
     assignment.  A variable assignment argument overrides the first (but not
     any subsequent) assignment to that variable.
     AS=as                     FFLAGS=                   NPROC=1
     CC=cc                     LEX=lex                   NREP=1
     CFLAGS=                   LFLAGS=                   YACC=yacc
     FC=f77                    LDFLAGS=                  YFLAGS=
     BUILTINS='
     %.o:    %.c
             $CC $CFLAGS -c $stem.c
     %.o:    %.s
             $AS -o $stem.o $stem.s
     %.o:    %.f
             $FC $FFLAGS -c $stem.f
     %.o:    %.y
             $YACC $YFLAGS $stem.y &&
             $CC $CFLAGS -c y.tab.c && mv y.tab.o $stem.o; rm y.tab.c
     %.o:    %.l
             $LEX $LFLAGS -t $stem.l > $stem.c &&
             $CC $CFLAGS -c $stem.c && rm $stem.c'
     ENVIRON=


     The builtin rules are obtained from the variable BUILTINS after all input
     has been processed.  The contents of the ENVIRON variable are split into
     parts separated by bytes with the 0200 bit set.  These parts are then
     added to the environment without further processing.  The variable
     MKFLAGS contains all the option arguments (arguments starting with a - or
     containing a =) and MKARGS contains all the targets in the call to mk.

   Syntax
     Leading white space (blank or tab) is ignored.  Input after an unquoted #
     (a comment) is ignored as are blank lines.  Lines can be spread over
     several physical lines by placing a \ before newlines to be elided.
     Non-recipe lines are processed by substituting for `cmd` and then
     substituting for variable references.  Finally, the filename
     metacharacters []*? are expanded.  Quoting by '', "", and \ is supported.
     The semantics for substitution and quoting are given in sh(1).

     Assignments and rule header lines are distinguished by the first unquoted
     occurrence of : (rule header) or = (assignment).

     A rule definition consists of a header line followed by a recipe.  The
     recipe consists of all lines following the header line that start with
     white space.  The recipe may be empty.  The first character on every line
     of the recipe is elided.  The header line consists of at least one target
     followed by the rule separator : and a possibly empty list of
     prerequisites.  The rule may have attributes specified in a : terminated
     list immediately following the : separator.  The rule means if any
     prerequisite is more recent than any of the targets, the recipe is
     executed.  This meaning is modified by the following attributes
     <  The standard output of the recipe is read by mk as an additional
        mkfile.  Assignments take effect immediately.  Rule definitions are
        used when a new dependency dag is constructed.
     D  If the recipe exits with an error status, the target is deleted.
     N  If there is no recipe, the target has its time updated.
     P  The characters after the P until the terminating : are taken as a
        program name.  It will be invoked as sh -c prog 'arg1' 'arg2' and
        should return 0 exit status if and only if arg1 is not out of date
        with respect to arg2.  Date stamps are still propagated in the normal
        way.
     Q  The recipe is not printed prior to execution.
     U  The targets are considered to have been updated even if the recipe did
        not do so.
     V  The targets of this rule are marked as virtual.  They are distinct
        from files of the same name.

     Similarly, assignments may have attributes terminated by a trailing =.
     The attributes are:
     U  Do not export this variable to recipe executions.  This may be
        necessary if you have many or large variables.
EXAMPLES
     A simple mkfile to compile a program.
          prog:   a.o b.o c.o
                  $CC $CFLAGS -o $target $prereq

     Override flag settings in the mkfile.
          $ mk target CFLAGS='-O -s'

     To get the prerequisites for an aggregate.
          $ membername 'libc.a(read.o)' 'libc.a(write.o)' read.o write.o

     Maintain a library.
          libc.a(%.o):N:%.o
          libc.a: libc.a(abs.o) libc.a(access.o) libc.a(alarm.o) ...
                  names=`membername $newprereq`
                  ar r libc.a $names && rm $names

     Backquotes can be used to generate similar lists from a master list.
          NAMES=alloc arc bquote builtins expand main match mk var word
          OBJ=`echo $NAMES|sed -e 's/[^ ][^ ]*/&.o/g'`
          COBJ=`echo $NAMES|sed -e 's/[^ ][^ ]*/&.O/g'`

     A correct way to deal with yacc(1) grammars.  The file lex.c includes the
     file x.tab.h rather than y.tab.h in order to reflect changes in content,
     not just modifcation time.
          YFLAGS=-d
          lex.o:  x.tab.h
          x.tab.h:y.tab.h
                  cmp -s x.tab.h y.tab.h || cp y.tab.h x.tab.h
          y.tab.c y.tab.h:gram.y
                  $YACC $YFLAGS gram.y

     The above example could also use the P attribute for the x.tab.h rule:
          x.tab.h:Pcmp -s:y.tab.h
                  cp y.tab.h x.tab.h

SEE ALSO
     make(1), touch(1), sh(1), regexp(3)
BUGS
     None of the conditional variable forms (such as ${var:-word}) are
     supported.
     Seemingly appropriate input like CFLAGS=-DHZ=60 is correctly parsed as a
     (attribute) syntax error.

Typewritten Software • bear@typewritten.org • Edmonds, WA 98026