Next Previous Contents

8. Project files

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.

8.1 Module types

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. */
 }

The type-specific keywords are then used to define the various attributes of the type. They are as follows:

8.2 Locations

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:

Source file path

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.

Interface file location

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

Output file location

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

Locations are defined with the 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");

For the sake of simplicity (and to ease the construction of simple projects that do not require locations) it is not necessary to specify the location of a module when using it. For these modules, you can specify a ``default location'' that will be used when no location is specified.

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;

This will cause AMC to create the directories if they don't already exist.

8.3 Interfacing to configuration managment (version control)

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.

8.4 File inclusion

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

Will only include that file if it has not been included before.

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.

8.5 State 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");

8.6 Specifying root modules

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:

Syntactically, the root command looks like this:
  root       my_module 
       in    my_location 
       final ("some.command blah,blah");

If the location is omitted, then the 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. */
       };

A project may have more than one root module. A lone 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")

8.7 Environment variables

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";

or this command can evaluate some CGL code and place the results into the environment variable:
  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");

8.8 Executing arbitrary CGL code

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

However, when executing any CGL code in the project file, the environment available is very limited; not all CGL-built ins will work.

8.9 Initialization

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


Next Previous Contents