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)
|