861 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			Groff
		
	
	
	
	
	
			
		
		
	
	
			861 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			Groff
		
	
	
	
	
	
.TH ACD 1
 | 
						|
.SH NAME
 | 
						|
acd \- a compiler driver
 | 
						|
.SH SYNOPSIS
 | 
						|
.B acd
 | 
						|
\fB\-v\fR[\fIn\fR]
 | 
						|
\fB\-vn\fR[\fIn\fR]
 | 
						|
.BI \-name " name"
 | 
						|
.BI \-descr " descr"
 | 
						|
.BI \-T " dir"
 | 
						|
.RI [ arg " ...]"
 | 
						|
.SH DESCRIPTION
 | 
						|
.de SP
 | 
						|
.if t .sp 0.4
 | 
						|
.if n .sp
 | 
						|
..
 | 
						|
.B Acd
 | 
						|
is a compiler driver, a program that calls the several passes that are needed
 | 
						|
to compile a source file.  It keeps track of all the temporary files used
 | 
						|
between the passes.  It also defines the interface of the compiler, the
 | 
						|
options the user gets to see.
 | 
						|
.PP
 | 
						|
This text only describes
 | 
						|
.B acd
 | 
						|
itself, it says nothing about the different options the C-compiler accepts.
 | 
						|
(It has nothing to do with any language, other than being a tool to give
 | 
						|
a compiler a user interface.)
 | 
						|
.SH OPTIONS
 | 
						|
.B Acd
 | 
						|
itself takes five options:
 | 
						|
.TP
 | 
						|
\fB\-v\fR[\fIn\fR]
 | 
						|
Sets the diagnostic level to
 | 
						|
.I n
 | 
						|
(by default
 | 
						|
.BR 2 ).
 | 
						|
The higher
 | 
						|
.I n
 | 
						|
is, the more output
 | 
						|
.B acd
 | 
						|
generates:
 | 
						|
.B \-v0
 | 
						|
does not produce any output.
 | 
						|
.B \-v1
 | 
						|
prints the basenames of the programs called.
 | 
						|
.B \-v2
 | 
						|
prints names and arguments of the programs called.
 | 
						|
.B \-v3
 | 
						|
shows the commands executed from the description file too.
 | 
						|
.B \-v4
 | 
						|
shows the program read from the description file too.  Levels 3 and 4 use
 | 
						|
backspace overstrikes that look good when viewing the output with a smart
 | 
						|
pager.
 | 
						|
.TP
 | 
						|
\fB\-vn\fR[\fIn\fR]
 | 
						|
Like
 | 
						|
.B \-v
 | 
						|
except that no command is executed.  The driver is just play-acting.
 | 
						|
.TP
 | 
						|
.BI \-name " name"
 | 
						|
.B Acd
 | 
						|
is normally linked to the name the compiler is to be called with by the
 | 
						|
user.  The basename of this, say
 | 
						|
.BR cc ,
 | 
						|
is the call name of the driver.  It plays a role in selecting the proper
 | 
						|
description file.  With the
 | 
						|
.B \-name
 | 
						|
option one can change this.
 | 
						|
.B Acd \-name cc
 | 
						|
has the same effect as calling the program as
 | 
						|
.BR cc .
 | 
						|
.TP
 | 
						|
.BI \-descr " descr"
 | 
						|
Allows one to choose the pass description file of the driver.  By default
 | 
						|
.I descr
 | 
						|
is the same as
 | 
						|
.IR name ,
 | 
						|
the call name of the program.  If
 | 
						|
.I descr
 | 
						|
doesn't start with
 | 
						|
.BR / ,
 | 
						|
.BR ./ ,
 | 
						|
or
 | 
						|
.BR ../
 | 
						|
then the file
 | 
						|
.BI /usr/lib/ descr /descr
 | 
						|
will be used for the description, otherwise
 | 
						|
.I descr
 | 
						|
itself.  Thus
 | 
						|
.B cc \-descr newcc
 | 
						|
calls the C-compiler with a different description file without changing the
 | 
						|
call name.  Finally, if
 | 
						|
.I descr
 | 
						|
is \fB"\-"\fP, standard input is read.  (The default lib directory
 | 
						|
.BR /usr/lib ,
 | 
						|
may be changed to
 | 
						|
.I dir
 | 
						|
at compile time by \fB\-DLIB=\e"\fP\fIdir\fP\fB\e"\fP.  The default
 | 
						|
.I descr
 | 
						|
may be set with \fB\-DDESCR=\e"\fP\fIdescr\fP\fB\e"\fP for simple
 | 
						|
installations on a system without symlinks.)
 | 
						|
.TP
 | 
						|
.BI \-T " dir"
 | 
						|
Temporary files are made in
 | 
						|
.B /tmp
 | 
						|
by default, which may be overridden by the environment variable
 | 
						|
.BR TMPDIR ,
 | 
						|
which may be overridden by the
 | 
						|
.B \-T
 | 
						|
option.
 | 
						|
.SH "THE DESCRIPTION FILE"
 | 
						|
The description file is a program interpreted by the driver.  It has variables,
 | 
						|
lists of files, argument parsing commands, and rules for transforming input
 | 
						|
files.
 | 
						|
.SS Syntax
 | 
						|
There are four simple objects:
 | 
						|
.PP
 | 
						|
.RS
 | 
						|
Words, Substitutions, Letters, and Operators.
 | 
						|
.RE
 | 
						|
.PP
 | 
						|
And there are two ways to group objects:
 | 
						|
.PP
 | 
						|
.RS
 | 
						|
Lists, forming sequences of anything but letters,
 | 
						|
.SP
 | 
						|
Strings, forming sequences of anything but Words and Operators.
 | 
						|
.RE
 | 
						|
.PP
 | 
						|
Each object has the following syntax:
 | 
						|
.IP Words
 | 
						|
They are sequences of characters, like
 | 
						|
.BR cc ,
 | 
						|
.BR \-I/usr/include ,
 | 
						|
.BR /lib/cpp .
 | 
						|
No whitespace and no special characters.  The backslash character
 | 
						|
.RB ( \e )
 | 
						|
may be used to make special characters common, except whitespace.  A backslash
 | 
						|
followed by whitespace is completely removed from the input.  The sequence
 | 
						|
.B \en
 | 
						|
is changed to a newline.
 | 
						|
.IP Substitutions
 | 
						|
A substitution (henceforth called 'subst') is formed with a
 | 
						|
.BR $ ,
 | 
						|
e.g.
 | 
						|
.BR $opt ,
 | 
						|
.BR $PATH ,
 | 
						|
.BR ${lib} ,
 | 
						|
.BR $\(** .
 | 
						|
The variable name after the
 | 
						|
.B $
 | 
						|
is made of letters, digits and underscores, or any sequence of characters
 | 
						|
between parentheses or braces, or a single other character.  A subst indicates
 | 
						|
that the value of the named variable must be substituted in the list or string
 | 
						|
when fully evaluated.
 | 
						|
.IP Letters
 | 
						|
Letters are the single characters that would make up a word.
 | 
						|
.IP Operators
 | 
						|
The characters
 | 
						|
.BR = ,
 | 
						|
.BR + ,
 | 
						|
.BR \- ,
 | 
						|
.BR \(** ,
 | 
						|
.BR < ,
 | 
						|
and
 | 
						|
.B >
 | 
						|
are the operators.  The first four must be surrounded by whitespace if they
 | 
						|
are to be seen as special (they are often used in arguments).  The last two
 | 
						|
are always special.
 | 
						|
.IP Lists
 | 
						|
One line of objects in the description file forms a list.  Put parentheses
 | 
						|
around it and you have a sublist.  The values of variables are lists.
 | 
						|
.IP Strings
 | 
						|
Anything that is not yet a word is a string.  All it needs is that the substs
 | 
						|
in it are evaluated, e.g.
 | 
						|
.BR $LIBPATH/lib$key.a .
 | 
						|
A single subst doesn't make a string, it expands to a list.  You need at
 | 
						|
least one letter or other subst next to it.  Strings (and words) may also
 | 
						|
be formed by enclosing them in double quotes.  Only
 | 
						|
.B \e
 | 
						|
and
 | 
						|
.B $
 | 
						|
keep their special meaning within quotes.
 | 
						|
.SS Evaluation
 | 
						|
One thing has to be carefully understood: Substitutions are delayed until
 | 
						|
the last possible moment, and description files make heavy use of this.
 | 
						|
Only if a subst is tainted, either because its variable is declared local, or
 | 
						|
because a subst in its variable's value is tainted, is it immediately
 | 
						|
substituted.  So if a list is assigned to a variable then this list is only
 | 
						|
checked for tainted substs.  Those substs are replaced by the value
 | 
						|
of their variable.  This is called partial evaluation.
 | 
						|
.PP
 | 
						|
Full evaluation expands all substs, the list is flattened, i.e. all
 | 
						|
parentheses are removed from sublists.
 | 
						|
.PP
 | 
						|
Implosive evaluation is the last that has to be done to a list before it
 | 
						|
can be used as a command to execute.  The substs within a string have been
 | 
						|
evaluated to lists after full expansion, but a string must be turned into
 | 
						|
a single word, not a list.  To make this happen, a string is first exploded
 | 
						|
to all possible combinations of words choosing one member of the lists within
 | 
						|
the string.  These words are tried one by one to see if they exist as a
 | 
						|
file.  The first one that exists is taken, if none exists than the first
 | 
						|
choice is used.  As an example, assume
 | 
						|
.B LIBPATH
 | 
						|
equals
 | 
						|
.BR "(/lib /usr/lib)" ,
 | 
						|
.B key
 | 
						|
is
 | 
						|
.B (c)
 | 
						|
and
 | 
						|
.B key
 | 
						|
happens to be local.  Then we have:
 | 
						|
.PP
 | 
						|
.RS
 | 
						|
\fB"$LIBPATH/lib$key.a"\fP
 | 
						|
.RE
 | 
						|
.PP
 | 
						|
before evaluation,
 | 
						|
.PP
 | 
						|
.RS
 | 
						|
\fB"$LIBPATH/lib(c).a"\fP
 | 
						|
.RE
 | 
						|
.PP
 | 
						|
after partial evaluation,
 | 
						|
.PP
 | 
						|
.RS
 | 
						|
\fB"(/lib/libc.a /usr/lib/libc.a)"\fP
 | 
						|
.RE
 | 
						|
.PP
 | 
						|
after full evaluation, and finally
 | 
						|
.PP
 | 
						|
.RS
 | 
						|
.B /usr/lib/libc.a
 | 
						|
.RE
 | 
						|
.PP
 | 
						|
after implosion, if the file exists.
 | 
						|
.SS Operators
 | 
						|
The operators modify the way evaluation is done and perform a special
 | 
						|
function on a list:
 | 
						|
.TP
 | 
						|
.B \(**
 | 
						|
Forces full evaluation on all the list elements following it.  Use it to
 | 
						|
force substitution of the current value of a variable.  This is the only
 | 
						|
operator that forces immediate evaluation.
 | 
						|
.TP
 | 
						|
.B +
 | 
						|
When a
 | 
						|
.B +
 | 
						|
exists in a list that is fully evaluated, then all the elements before the
 | 
						|
.B +
 | 
						|
are imploded and all elements after the
 | 
						|
.B +
 | 
						|
are imploded and added to the list if they are not already in the list.  So
 | 
						|
this operator can be used either for set addition, or to force implosive
 | 
						|
expansion within a sublist.
 | 
						|
.TP
 | 
						|
.B \-
 | 
						|
Like
 | 
						|
.BR + ,
 | 
						|
except that elements after the
 | 
						|
.B \-
 | 
						|
are removed from the list.
 | 
						|
.PP
 | 
						|
The set operators can be used to gather options that exclude each other
 | 
						|
or for their side effect of implosive expansion.  You may want to write:
 | 
						|
.PP
 | 
						|
.RS
 | 
						|
\fBcpp \-I$LIBPATH/include\fP
 | 
						|
.RE
 | 
						|
.PP
 | 
						|
to call cpp with an extra include directory, but
 | 
						|
.B $LIBPATH
 | 
						|
is expanded using a filename starting with
 | 
						|
.B \-I
 | 
						|
so this won't work.  Given that any problem in Computer Science can be solved
 | 
						|
with an extra level of indirection, use this instead:
 | 
						|
.PP
 | 
						|
.RS
 | 
						|
.ft B
 | 
						|
cpp \-I$INCLUDE
 | 
						|
.br
 | 
						|
INCLUDE = $LIBPATH/include +
 | 
						|
.ft P
 | 
						|
.RE
 | 
						|
.SS "Special Variables"
 | 
						|
There are three special variables used in a description file:
 | 
						|
.BR $\(** ,
 | 
						|
.BR $< ,
 | 
						|
and
 | 
						|
.BR $> .
 | 
						|
These variables are always local and mostly read-only.  They will be
 | 
						|
explained later.
 | 
						|
.SS "A Program"
 | 
						|
The lists in a description file form a program that is executed from the
 | 
						|
first to the last list.  The first word in a list may be recognized as a
 | 
						|
builtin command (only if the first list element is indeed simply a word.)
 | 
						|
If it is not a builtin command then the list is imploded and used as a
 | 
						|
\s-2UNIX\s+2 command with arguments.
 | 
						|
.PP
 | 
						|
Indentation (by tabs or spaces) is not just makeup for a program, but are
 | 
						|
used to group lines together.  Some builtin commands need a body.  These
 | 
						|
bodies are simply lines at a deeper indentation.
 | 
						|
.PP
 | 
						|
Empty lines are not ignored either, they have the same indentation level as
 | 
						|
the line before it.  Comments (starting with a
 | 
						|
.B #
 | 
						|
and ending at end of line) have an indentation of their own and can be used
 | 
						|
as null commands.
 | 
						|
.PP
 | 
						|
.B Acd
 | 
						|
will complain about unexpected indentation shifts and empty bodies.  Commands
 | 
						|
can share the same body by placing them at the same indentation level before
 | 
						|
the indented body.  They are then "guards" to the same body, and are tried
 | 
						|
one by one until one succeeds, after which the body is executed.
 | 
						|
.PP
 | 
						|
Semicolons may be used to separate commands instead of newlines.  The commands
 | 
						|
are then all at the indentation level of the first.
 | 
						|
.SS "Execution phases"
 | 
						|
The driver runs in three phases: Initialization, Argument scanning, and
 | 
						|
Compilation.  Not all commands work in all phases.  This is further explained
 | 
						|
below.
 | 
						|
.SS "The Commands"
 | 
						|
The commands accept arguments that are usually generic expressions that
 | 
						|
implode to a word or a list of words.  When
 | 
						|
.I var
 | 
						|
is specified, then a single word or subst needs to be given, so
 | 
						|
an assignment can be either
 | 
						|
.I name
 | 
						|
.B =
 | 
						|
.IR value ,
 | 
						|
or
 | 
						|
.BI $ name
 | 
						|
.B =
 | 
						|
.IR value .
 | 
						|
.TP
 | 
						|
.IB "var " = " expr ..."
 | 
						|
The partially evaluated list of expressions is assigned to
 | 
						|
.IR var .
 | 
						|
During the evaluation is
 | 
						|
.I var
 | 
						|
marked as local, and after the assignment set from undefined to defined.
 | 
						|
.TP
 | 
						|
.BI unset " var"
 | 
						|
.I Var
 | 
						|
is set to null and is marked as undefined.
 | 
						|
.TP
 | 
						|
.BI import " var"
 | 
						|
If
 | 
						|
.I var
 | 
						|
is defined in the environment of
 | 
						|
.B acd
 | 
						|
then it is assigned to
 | 
						|
.IR var .
 | 
						|
The environment variable is split into words at whitespace and colons.  Empty
 | 
						|
space between two colons
 | 
						|
.RB ( :: )
 | 
						|
is changed to a dot.
 | 
						|
.TP
 | 
						|
.BI mktemp " var " [ suffix ]
 | 
						|
Assigns to
 | 
						|
.I var
 | 
						|
the name of a new temporary file, usually something like /tmp/acd12345x.  If
 | 
						|
.I suffix
 | 
						|
is present then it will be added to the temporary file's name.  (Use it
 | 
						|
because some programs require it, or just because it looks good.)
 | 
						|
.B Acd
 | 
						|
remembers this file, and will delete it as soon as you stop referencing it.
 | 
						|
.TP
 | 
						|
.BI temporary " word"
 | 
						|
Mark the file named by
 | 
						|
.I word
 | 
						|
as a temporary file.  You have to make sure that the name is stored in some
 | 
						|
list in imploded form, and not just temporarily created when
 | 
						|
.I word
 | 
						|
is evaluated, because then it will be immediately removed and forgotten.
 | 
						|
.TP
 | 
						|
.BI stop " suffix"
 | 
						|
Sets the target suffix for the compilation phase.  Something like
 | 
						|
.B stop .o
 | 
						|
means that the source files must be compiled to object files.  At least one
 | 
						|
.B stop
 | 
						|
command must be executed before the compilation phase begins.  It may not be
 | 
						|
changed during the compilation phase.  (Note: There is no restriction on
 | 
						|
.IR suffix ,
 | 
						|
it need not start with a dot.)
 | 
						|
.TP
 | 
						|
.BI treat " file suffix"
 | 
						|
Marks the file as having the given suffix for the compile phase.  Useful
 | 
						|
for sending a
 | 
						|
.B \-l
 | 
						|
option directly to the loader by treating it as having the
 | 
						|
.B .a
 | 
						|
suffix.
 | 
						|
.TP
 | 
						|
.BI numeric " arg"
 | 
						|
Checks if
 | 
						|
.I arg
 | 
						|
is a number.  If not then
 | 
						|
.B acd
 | 
						|
will exit with a nice error message.
 | 
						|
.TP
 | 
						|
.BI error " expr ..."
 | 
						|
Makes the driver print the error message
 | 
						|
.I "expr ..."
 | 
						|
and exit.
 | 
						|
.TP
 | 
						|
.BI if " expr " = " expr"
 | 
						|
.B If
 | 
						|
tests if the two expressions are equal using set comparison, i.e. each
 | 
						|
expression should contain all the words in the other expression.  If the
 | 
						|
test succeeds then the if-body is executed.
 | 
						|
.TP
 | 
						|
.BI ifdef " var"
 | 
						|
Executes the ifdef-body if
 | 
						|
.I var
 | 
						|
is defined.
 | 
						|
.TP
 | 
						|
.BI ifndef " var"
 | 
						|
Executes the ifndef-body if
 | 
						|
.I var
 | 
						|
is undefined.
 | 
						|
.TP
 | 
						|
.BI iftemp " arg"
 | 
						|
Executes the iftemp-body if
 | 
						|
.I arg
 | 
						|
is a temporary file.  Use it when a command has the same file as input and
 | 
						|
output and you don't want to clobber the source file:
 | 
						|
.SP
 | 
						|
.RS
 | 
						|
.nf
 | 
						|
.ft B
 | 
						|
transform .o .o
 | 
						|
	iftemp $\(**
 | 
						|
		$> = $\(**
 | 
						|
	else
 | 
						|
		cp $\(** $>
 | 
						|
	optimize $>
 | 
						|
.ft P
 | 
						|
.fi
 | 
						|
.RE
 | 
						|
.TP
 | 
						|
.BI ifhash " arg"
 | 
						|
Executes the ifhash-body if
 | 
						|
.I arg
 | 
						|
is an existing file with a '\fB#\fP' as the very first character.  This
 | 
						|
usually indicates that the file must be pre-processed:
 | 
						|
.SP
 | 
						|
.RS
 | 
						|
.nf
 | 
						|
.ft B
 | 
						|
transform .s .o
 | 
						|
	ifhash $\(**
 | 
						|
		mktemp ASM .s
 | 
						|
		$CPP $\(** > $ASM
 | 
						|
	else
 | 
						|
		ASM = $\(**
 | 
						|
	$AS \-o $> $ASM
 | 
						|
	unset ASM
 | 
						|
.ft P
 | 
						|
.fi
 | 
						|
.RE
 | 
						|
.TP
 | 
						|
.B else
 | 
						|
Executes the else-body if the last executed
 | 
						|
.BR if ,
 | 
						|
.BR ifdef ,
 | 
						|
.BR ifndef ,
 | 
						|
.BR iftemp ,
 | 
						|
or
 | 
						|
.B ifhash
 | 
						|
was unsuccessful.  Note that
 | 
						|
.B else
 | 
						|
need not immediately follow an if, but you are advised not to make use of
 | 
						|
this.  It is a "feature" that may not last.
 | 
						|
.TP
 | 
						|
.BI apply " suffix1 suffix2"
 | 
						|
Executed inside a transform rule body to transform the input file according
 | 
						|
to another transform rule that has the given input and output suffixes.  The
 | 
						|
file under
 | 
						|
.B $\(**
 | 
						|
will be replaced by the new file.  So if there is a
 | 
						|
.B .c .i
 | 
						|
preprocessor rule then the example of
 | 
						|
.B ifhash
 | 
						|
can be replaced by:
 | 
						|
.SP
 | 
						|
.RS
 | 
						|
.nf
 | 
						|
.ft B
 | 
						|
transform .s .o
 | 
						|
	ifhash $\(**
 | 
						|
		apply .c .i
 | 
						|
	$AS \-o $> $*
 | 
						|
.ft P
 | 
						|
.fi
 | 
						|
.RE
 | 
						|
.TP
 | 
						|
.BI include " descr"
 | 
						|
Reads another description file and replaces the
 | 
						|
.B include
 | 
						|
with it.  Execution continues with the first list in the new program.  The
 | 
						|
search for
 | 
						|
.I descr
 | 
						|
is the same as used for the
 | 
						|
.B \-descr
 | 
						|
option.  Use
 | 
						|
.B include
 | 
						|
to switch in different front ends or back ends, or to call a shared
 | 
						|
description file with a different initialization.  Note that
 | 
						|
.I descr
 | 
						|
is only evaluated the first time the
 | 
						|
.B include
 | 
						|
is called.  After that the
 | 
						|
.B include
 | 
						|
has been replaced with the included program, so changing its argument won't
 | 
						|
get you a different file.
 | 
						|
.TP
 | 
						|
.BI arg " string ..."
 | 
						|
.B Arg
 | 
						|
may be executed in the initialization and scanning phase to post an argument
 | 
						|
scanning rule, that's all the command itself does.  Like an
 | 
						|
.B if
 | 
						|
that fails it allows more guards to share the same body.
 | 
						|
.TP
 | 
						|
.BI transform " suffix1 suffix2"
 | 
						|
.BR Transform ,
 | 
						|
like
 | 
						|
.BR arg ,
 | 
						|
only posts a rule to transform a file with the suffix
 | 
						|
.I suffix1
 | 
						|
into a file with the suffix
 | 
						|
.IR suffix2 .
 | 
						|
.TP
 | 
						|
.BI prefer " suffix1 suffix2"
 | 
						|
Tells that the transformation rule from
 | 
						|
.I suffix1
 | 
						|
to
 | 
						|
.I suffix2
 | 
						|
is to be preferred when looking for a transformation path to the stop suffix.
 | 
						|
Normally the shortest route to the stop suffix is used.
 | 
						|
.B Prefer
 | 
						|
is ignored on a
 | 
						|
.BR combine ,
 | 
						|
because the special nature of combines does not allow ambiguity.
 | 
						|
.SP
 | 
						|
The two suffixes on a
 | 
						|
.B transform
 | 
						|
or
 | 
						|
.B prefer
 | 
						|
may be the same, giving a rule that is only executed when preferred.
 | 
						|
.TP
 | 
						|
.BI combine " suffix-list suffix"
 | 
						|
.B Combine
 | 
						|
is like
 | 
						|
.B transform
 | 
						|
except that it allows a list of input suffixes to match several types of
 | 
						|
input files that must be combined into one.
 | 
						|
.TP
 | 
						|
.B scan
 | 
						|
The scanning phase may be run early from the initialization phase with the
 | 
						|
.B scan
 | 
						|
command.  Use it if you need to make choices based on the arguments before
 | 
						|
posting the transformation rules.  After running this,
 | 
						|
.B scan
 | 
						|
and
 | 
						|
.B arg
 | 
						|
become no-ops.
 | 
						|
.TP
 | 
						|
.B compile
 | 
						|
Move on to the compilation phase early, so that you have a chance to run
 | 
						|
a few extra commands before exiting.  This command implies a
 | 
						|
.BR scan .
 | 
						|
.PP
 | 
						|
Any other command is seen as a \s-2UNIX\s+2 command.  This is where the
 | 
						|
.B <
 | 
						|
and
 | 
						|
.B >
 | 
						|
operators come into play.  They redirect standard input and standard output
 | 
						|
to the file mentioned after them, just like the shell.
 | 
						|
.B Acd
 | 
						|
will stop with an error if the command is not successful.
 | 
						|
.SS The Initialization Phase
 | 
						|
The driver starts by executing the program once from top to bottom to
 | 
						|
initialize variables and post argument scanning and transformation rules.
 | 
						|
.SS The Scanning Phase
 | 
						|
In this phase the driver makes a pass over the command line arguments to
 | 
						|
process options.  Each
 | 
						|
.B arg
 | 
						|
rule is tried one by one in the order they were posted against the front of
 | 
						|
the argument list.  If a match is made then the matched arguments are removed
 | 
						|
from the argument list and the arg-body is executed.  If no match can be made
 | 
						|
then the first argument is moved to the list of files waiting to be
 | 
						|
transformed and the scan is restarted.
 | 
						|
.PP
 | 
						|
The match is done as follows: Each of the strings after
 | 
						|
.B arg
 | 
						|
must match one argument at the front of the argument list.  A character
 | 
						|
in a string must match a character in an argument word, a subst in a string
 | 
						|
may match 1 to all remaining characters in the argument, preferring the
 | 
						|
shortest possible match.  The hyphen in a argument starting with a hyphen
 | 
						|
cannot be matched by a subst.  Therefore:
 | 
						|
.PP
 | 
						|
.RS
 | 
						|
.B arg \-i
 | 
						|
.RE
 | 
						|
.PP
 | 
						|
matches only the argument
 | 
						|
.BR \-i .
 | 
						|
.PP
 | 
						|
.RS
 | 
						|
.B arg \-O$n
 | 
						|
.RE
 | 
						|
.PP
 | 
						|
matches any argument that starts with
 | 
						|
.B \-O
 | 
						|
and is at least three characters long.  Lastly,
 | 
						|
.PP
 | 
						|
.RS
 | 
						|
.B arg \-o $out
 | 
						|
.RE
 | 
						|
.PP
 | 
						|
matches
 | 
						|
.B \-o
 | 
						|
and the argument following it, unless that argument starts with a hyphen.
 | 
						|
.PP
 | 
						|
The variable
 | 
						|
.B $\(**
 | 
						|
is set to all the matched arguments before the arg-body is executed.  All
 | 
						|
the substs in the arg strings are set to the characters they match.  The
 | 
						|
variable
 | 
						|
.B $>
 | 
						|
is set to null.  All the values of the variables are saved and the variables
 | 
						|
marked local.  All variables except
 | 
						|
.B $>
 | 
						|
are marked read-only.  After the arg-body is executed is the value of
 | 
						|
.B $>
 | 
						|
concatenated to the file list.  This allows one to stuff new files into the
 | 
						|
transformation phase.  These added names are not evaluated until the start
 | 
						|
of the next phase.
 | 
						|
.SS The Compilation Phase
 | 
						|
The files gathered in the file list in the scanning phase are now transformed
 | 
						|
one by one using the transformation rules.  The shortest, or preferred route
 | 
						|
is computed for each file all the way to the stop suffix.  Each file is
 | 
						|
transformed until it lands at the stop suffix, or at a combine rule.  After
 | 
						|
a while all files are either fully transformed or at a combine rule.
 | 
						|
.PP
 | 
						|
The driver chooses a combine rule that is not on a path from another combine
 | 
						|
rule and executes it.  The file that results is then transformed until it
 | 
						|
again lands at a combine rule or the stop suffix.  This continues until all
 | 
						|
files are at the stop suffix and the program exits.
 | 
						|
.PP
 | 
						|
The paths through transform rules may be ambiguous and have cycles, they will
 | 
						|
be resolved.  But paths through combines must be unambiguous, because of
 | 
						|
the many paths from the different files that meet there.  A description file
 | 
						|
will usually have only one combine rule for the loader.  However if you do
 | 
						|
have a combine conflict then put a no-op transform rule in front of one to
 | 
						|
resolve the problem.
 | 
						|
.PP
 | 
						|
If a file matches a long and a short suffix then the long suffix is preferred.
 | 
						|
By putting a null input suffix (\fB""\fP) in a rule one can match any file
 | 
						|
that no other rule matches.  You can send unknown files to the loader this
 | 
						|
way.
 | 
						|
.PP
 | 
						|
The variable
 | 
						|
.B $\(**
 | 
						|
is set to the file to be transformed or the files to be combined before the
 | 
						|
transform or combine-body is executed.
 | 
						|
.B $>
 | 
						|
is set to the output file name, it may again be modified.
 | 
						|
.B $<
 | 
						|
is set to the original name of the first file of
 | 
						|
.B $\(**
 | 
						|
with the leading directories and the suffix removed.
 | 
						|
.B $\(**
 | 
						|
will be made up of temporary files after the first rule.
 | 
						|
.B $>
 | 
						|
will be another temporary file or the name of the target file
 | 
						|
.RB ( $<
 | 
						|
plus the stop suffix), if the stop suffix is reached.
 | 
						|
.PP
 | 
						|
.B $>
 | 
						|
is passed to the next rule; it is imploded and checked to be a single word.
 | 
						|
This driver does not store intermediate object files in the current directory
 | 
						|
like most other compilers, but keeps them in
 | 
						|
.B /tmp
 | 
						|
too.  (Who knows if the current directory can have files created in?)  As an
 | 
						|
example, here is how you can express the "normal" method:
 | 
						|
.PP
 | 
						|
.RS
 | 
						|
.nf
 | 
						|
.ft B
 | 
						|
transform .s .o
 | 
						|
	if $> = $<.o
 | 
						|
		# Stop suffix is .o
 | 
						|
	else
 | 
						|
		$> = $<.o
 | 
						|
		temporary $>
 | 
						|
	$AS \-o $> $\(**
 | 
						|
.ft P
 | 
						|
.fi
 | 
						|
.RE
 | 
						|
.PP
 | 
						|
Note that
 | 
						|
.B temporary
 | 
						|
is not called if the target is already the object file, or you would lose
 | 
						|
the intended result!
 | 
						|
.B $>
 | 
						|
is known to be a word, because
 | 
						|
.B $<
 | 
						|
is local.  (Any string whose substs are all expanded changes to a word.)
 | 
						|
.SS "Predefined Variables"
 | 
						|
The driver has three variables predefined:
 | 
						|
.BR PROGRAM ,
 | 
						|
set to the call name of the driver,
 | 
						|
.BR VERSION ,
 | 
						|
the driver's version number, and
 | 
						|
.BR ARCH ,
 | 
						|
set to the name of the default output architecture.  The latter is optional,
 | 
						|
and only defined if
 | 
						|
.B acd
 | 
						|
was compiled with \fB\-DARCH=\e"\fP\fIarch-name\fP\fB\e"\fP.
 | 
						|
.SH EXAMPLE
 | 
						|
As an example a description file for a C compiler is given.  It has a
 | 
						|
front end (ccom), an intermediate code optimizer (opt), a code generator (cg),
 | 
						|
an assembler (as), and a loader (ld).  The compiler can pre-process, but
 | 
						|
there is also a separate cpp.  If the
 | 
						|
.B \-D
 | 
						|
and options like it are changed to look like
 | 
						|
.B \-o
 | 
						|
then this example is even as required by \s-2POSIX\s+2.
 | 
						|
.RS
 | 
						|
.nf
 | 
						|
 | 
						|
# The compiler support search path.
 | 
						|
C =	/lib /usr/lib /usr/local/lib
 | 
						|
 | 
						|
# Compiler passes.
 | 
						|
CPP =	$C/cpp $CPP_F
 | 
						|
CCOM =	$C/ccom $CPP_F
 | 
						|
OPT =	$C/opt
 | 
						|
CG =	$C/cg
 | 
						|
AS =	$C/as
 | 
						|
LD =	$C/ld
 | 
						|
 | 
						|
# Predefined symbols.
 | 
						|
CPP_F =	\-D__EXAMPLE_CC__
 | 
						|
 | 
						|
# Library path.
 | 
						|
LIBPATH = $USERLIBPATH $C
 | 
						|
 | 
						|
# Default transformation target.
 | 
						|
stop .out
 | 
						|
 | 
						|
# Preprocessor directives.
 | 
						|
arg \-D$name
 | 
						|
arg \-U$name
 | 
						|
arg \-I$dir
 | 
						|
	CPP_F = $CPP_F $\(**
 | 
						|
 | 
						|
# Stop suffix.
 | 
						|
arg \-c
 | 
						|
	stop .o
 | 
						|
 | 
						|
arg \-E
 | 
						|
	stop .E
 | 
						|
 | 
						|
# Optimization.
 | 
						|
arg \-O
 | 
						|
	prefer .m .m
 | 
						|
	OPT = $OPT -O1
 | 
						|
 | 
						|
arg \-O$n
 | 
						|
	numeric $n
 | 
						|
	prefer .m .m
 | 
						|
	OPT = $OPT $\(**
 | 
						|
 | 
						|
# Add debug info to the executable.
 | 
						|
arg \-g
 | 
						|
	CCOM = $CCOM -g
 | 
						|
 | 
						|
# Add directories to the library path.
 | 
						|
arg \-L$dir
 | 
						|
	USERLIBPATH = $USERLIBPATH $dir
 | 
						|
 | 
						|
# \-llib must be searched in $LIBPATH later.
 | 
						|
arg \-l$lib
 | 
						|
	$> = $LIBPATH/lib$lib.a
 | 
						|
 | 
						|
# Change output file.
 | 
						|
arg \-o$out
 | 
						|
arg \-o $out
 | 
						|
	OUT = $out
 | 
						|
 | 
						|
# Complain about a missing argument.
 | 
						|
arg \-o
 | 
						|
	error "argument expected after '$\(**'"
 | 
						|
 | 
						|
# Any other option (like \-s) are for the loader.
 | 
						|
arg \-$any
 | 
						|
	LD = $LD $\(**
 | 
						|
 | 
						|
# Preprocess C-source.
 | 
						|
transform .c .i
 | 
						|
	$CPP $\(** > $>
 | 
						|
 | 
						|
# Preprocess C-source and send it to standard output or $OUT.
 | 
						|
transform .c .E
 | 
						|
	ifndef OUT
 | 
						|
		$CPP $\(**
 | 
						|
	else
 | 
						|
		$CPP $\(** > $OUT
 | 
						|
 | 
						|
# Compile C-source to intermediate code.
 | 
						|
transform .c .m
 | 
						|
transform .i .m
 | 
						|
	$CCOM $\(** $>
 | 
						|
 | 
						|
# Intermediate code optimizer.
 | 
						|
transform .m .m
 | 
						|
	$OPT $\(** > $>
 | 
						|
 | 
						|
# Intermediate to assembly.
 | 
						|
transform .m .s
 | 
						|
	$CG $\(** > $>
 | 
						|
 | 
						|
# Assembler to object code.
 | 
						|
transform .s .o
 | 
						|
	if $> = $<.o
 | 
						|
		ifdef OUT
 | 
						|
			$> = $OUT
 | 
						|
	$AS \-o $> $\(**
 | 
						|
 | 
						|
# Combine object files and libraries to an executable.
 | 
						|
combine (.o .a) .out
 | 
						|
	ifndef OUT
 | 
						|
		OUT = a.out
 | 
						|
	$LD \-o $OUT $C/crtso.o $\(** $C/libc.a
 | 
						|
.fi
 | 
						|
.RE
 | 
						|
.SH FILES
 | 
						|
.TP 25n
 | 
						|
.RI /usr/lib/ descr /descr
 | 
						|
\- compiler driver description file.
 | 
						|
.SH "SEE ALSO"
 | 
						|
.BR cc (1).
 | 
						|
.SH ACKNOWLEDGEMENTS
 | 
						|
Even though the end result doesn't look much like it, many ideas were
 | 
						|
nevertheless derived from the ACK compiler driver by Ed Keizer.
 | 
						|
.SH BUGS
 | 
						|
\s-2POSIX\s+2 requires that if compiling one source file to an object file
 | 
						|
fails then the compiler should continue with the next source file.  There is
 | 
						|
no way
 | 
						|
.B acd
 | 
						|
can do this, it always stops after error.  It doesn't even know what an
 | 
						|
object file is!  (The requirement is stupid anyhow.)
 | 
						|
.PP
 | 
						|
If you don't think that tabs are 8 spaces wide, then don't mix them with
 | 
						|
spaces for indentation.
 | 
						|
.SH AUTHOR
 | 
						|
Kees J. Bot (kjb@cs.vu.nl)
 |