A project file defines the working environment for AMC. All of the information to construct object from source modules should be contained in the project file. The project file is parsed with the same parsing engine as CGL and the actual source modules. Therefore, their lexical conventions also apply to the project file.
The majority of information about compiling modules are organized into
``module types.'' This is what is being referred to in the the
module type statement that is at the start of each source module.
Therefore, it is possible to have some modules compiled differently
that others. In fact, because the CGL code that parses each module is
specified as part of the module type, wildly different syntaxes can
be associated with each type of module.
A module type is defined with the type keyword:
type my_type
{
/* Type-specific keywords go here. */
}
auto_include(uses-spec)
It is often very useful to always have certain modules included
whenever a module is being compiled. The auto_include
directive specifies that a module should always be added as a
dependant for any modules of this type.
The uses-spec is the same as what can follow in a
uses clause.
make { cgl code }
This attribute is a block of CGL code that is used to generate a command to the operating system to compile the output of AMC's preprocessing.
On POSIX systems, the command is passed to a ``command interpreter.''
The default command interpreter is /bin/sh
with a default argument of -c followed by the command.
These defaults can be overriden via two environment variables. If
AMCSHELL is set, the value of that environment variable is
used as the command interpreter. In addition, if AMCSHELL
is set, the variable AMCSHELLARG may, optionally, be set.
If set, AMCSHELLARG is passed as the first argument to
the program specified by AMCSHELL.
make do { cgl code }
This attribute is similar to the make attribute above except
the CGL code is evaluated only for its side-effects. The result is
discarded and no command is submitted to the operating system.
compile { cgl code }
This attribute defines the CGL code that is actually executed when compiling this module. All of AMC's preprocessing is done at this point.
outext "string"
The basic premise is that AMC generates one output file for each input file. Because some programs (notably C compilers) require certain extensions or else they don't compile very well, AMC allows this to be settable on a per-module-type basis.
This statement defines a named location for modules. The concept of named locations is to provide a different mindset about programming in the large. In traditional programming environments code reuse is an exercise in file manipulation and version control. With named locations, it is expected that the programmer build his environment by getting working ``libraries'' of code that are useful and then just including them from the right location whenever they are needed (assuming the locations are added a standard project file for your work).
There are three fields that a location defines:
This is the path to where the source code actually resides. On UNIX platforms, this is actually not just a single directory but rather a search path (with individual directories separated by colons (":"). Thus, it is possible for a developer working on a team project to create a location that has his local source code directory in the search path before the actual project. Thus, a developer can test out changed modules without having to recompile the entire project and without disturbing the master source code or other developers trying to do the same.
In order to properly dependency check modules (and have access to their interfaces), pseudo-temporary files are created in this directory. The interface directory should be unique for both the location and (optionally) the production being compiled (e.g. ported software being compiled on two different architectures).
Compilation results in an output file generated by AMC that is
the output of the CGL code associated with the compile
keyword in the module type definition. This directory is where the
these output files are placed.
If the output of AMC is then passed through another compiler (say a C compiler), then it is probably desirable that the output of that process is placed in this directory for use by a librarian, linker, or loader
loc keyword:
loc mathlib=(
/* Source modules: */
@("HOME") "/exp/src/math:"
@("PRJROOT") "/lib/math/src",
/* Interface files: */
@("PRJROOT") "/lib/math/intfc",
/* Output files: */
@("PRJROOT") "/lib/math/obj");
This is done with the def_loc command. It references an
already defined location, for example:
def_loc junkloc;
If the location directive is followed by the keyword make, then
the output and interface directories will be constructed if they do not
exist. For example:
loc mathlib=(
/* Source modules: */
@("HOME") "/exp/src/math:"
@("PRJROOT") "/lib/math/src",
/* Interface files: */
@("PRJROOT") "/lib/math/intfc",
/* Output files: */
@("PRJROOT") "/lib/math/obj")
/* Construct directories */
make;
Some version control systems require that a command be executed to get a file out of revision control so that it can be compiled, edited, etc.
AMC allows a fetcher to be associated with a location to interface with a revision control system. A fetcher is a segment of CGL code that produces an operating system command that is executed if necessary.
As an example, let us re-define the mathlib location defined
above to include a fetcher that uses RCS.
loc mathlib=(
/* Source modules: */
@("HOME") "/exp/src/math:"
@("PRJROOT") "/lib/math/src",
/* Interface files: */
@("PRJROOT") "/lib/math/intfc",
/* Output files: */
@("PRJROOT") "/lib/math/obj")
fetcher { "co " fname ".d" };
The fetcher statement, which must come after the make
statement (if one is provided). If a requested source module can not
be found, AMC will execute the code within the fetcher block
and execute the resulting string as an operating system command. When
executing the fetcher block, AMC binds the name of the module
being searched for to CGL variable fname.
AMC will then attempt to find the source module again. If it can not be found and the fetcher has already been run once, it is assumed that the specified module really can not be found and AMC proceeds with its normal error handling.
fetcher blocks can be used for more than just interfacing with
revision control systems. They can also be used to invoke another macro
processor (external to AMC) or some other process which generates source
code mechanically.
Because AMC project files can actually get quite complex, an include directive allows another file to be included within the current one. This is especially handy when several projects share common libraries (stored in common locations).
The AMC project file include
directive is not processed seperatly from
other keywords. This means that you can not include a file for example
in the middle of a module type definition. This limitation is probably
more of a mechanism to keep project files clean than it is an actual
limitation of the AMC compiler.
The syntax for the include directive is as follows:
include("my_file_name");
Of course, if a file is included more than once this may cause errors
as symbols are defined more than once. To prevent this AMC automatically
tracks each file that is loaded as a project file and can skip files it
has already seen. To do this, the keyword once is placed
before the file name. For example:
include(once "my_file_name");
This is useful for complex project file structures where more than one project file may wish to have something defined and include a file but then a third file includes both of those files.
The AMC compiler keeps a running database for each project that it compiles. This is useful in cases such as automatically generating message or state numbers within the code. A small database engine (based on name-value pairs) manages a ``state file.'' This file should be private to each developer. This directive allows the project file to explicitly name its associated database file. However, if no file is explicitly named a suitable default is chosen.
The syntax for this directive is as follows:
state_file("my_file_name");
AMC traverses the dependency graph that is described in the header of
each module. The ``root modules'' are those that are not
referenced by any other module. In order for AMC to locate the
root modules they must be specified in the project file using the
root keyword. The root keyword has three components:
root.root command looks like this:
root my_module
in my_location
final ("some.command blah,blah");
in keyword must also
be omitted. The same is true for the final keyword. If you
wish to use a default module name of "root" the
root statement can not (obviously) be omitted.
In this case the final statement will execute an operating
system command. In order to run CGL code, use the following syntax:
root my_module
in my_location
final do
{
/* CGL code goes here. */
};
final statement specifies commands to be run after all
root modules are successfully compiled. This is a useful place to
put special commands for embedded systems such as downloading and
burning EPROM's. For example:
/* Now assemble the boot package: */
final
("make_boot_disk /dev/fd0,blah,blah")
Environment variables can be set using the set command.
This command can either set an environment variable to a constant
string:
set COMPILER="mycc -optimize:A1bxl -lang:x3j11";
set COMPILER={ compute_compiler_settings_from_cpu("LS1319A") }
During the parsing of the project file, the environment variable
PROJECT_FILE_LOC will be automatically defined to point to
the location of the current project file being parsed. If a file
is included from another project file, the variable is updated to
reflect the location of the included file. When parsing of the included
file is finished, the previous value of the variable (the location of
the including file) is restored.
It is important to understand that this variable lives only for the
lifetime of the parsing of the project file. If you reference it in a
CGL block that is executed after the parsing of the project file
(such as the make block of a module type) it will not have
the correct value because CGL will not fetch the value of the
variable until the code is actually executed.
Therefore, the value must be copied into an appropriate place holder
if it needs to be used; either via the set statement or by
a do statement (see the next section). For example:
set MY_PROJECT_LOCATION = @("PROJECT_FILE_LOC");
It is also possible to execute an arbitrary chunk of CGL code using the do statement. For example:
do
{
var1 = ("default")
var2 = (false)
var3 = (true)
}
After AMC has loaded and parses the project file a CGL procedure
(named main) is called (if defined). This gives project code a
chance to perform some additional initialization.
For example, a common thing to do in main is to clean up output files if
the project database needs to be re-built. A simple main to do
this is written like this:
proc main =
if(db_is_fresh, clean_output)
;