§38   Controlling compilation from within

Inform has a number of directives for controlling which pieces of source code are compiled: for instance, you can divide your source code into several files, compiled together or separately, or you can write source code in which different passages will be compiled on different occasions. Most of these directives are seldom seen, but almost every game uses:

Include "filename";

which instructs Inform to glue the whole of that source code file into the program right here. It is exactly equivalent to removing the Include directive and replacing it with the whole file "filename". (The rules for how Inform interprets "filename" vary from machine to machine: for instance, it may automatically add an extension such as “.inf” if your operating system normally uses filename extensions and it may look in some special directory. Run Inform with the -h1 switch for further information.) Note that you can write

Include ">shortname";

to mean “the file called "shortname" which is in the same directory that the present file came from”. This is convenient if all the files making up the source code of your game are housed together.

Next, there are a number of “conditional compilation” directives. They take the general form of a condition:

Ifdefname; Is ‹name› defined as having some meaning?
Ifndefname; Is ‹name› undefined?
Iftruecondition; Is this ‹condition› true?
Iffalsecondition; Is this ‹condition› false?

followed by a chunk of Inform and then, optionally,

Ifnot;

and another chunk of Inform; and finally

Endif;

At this point it is perhaps worth mentioning that (most) directives can also be interspersed with statements in routine declarations, provided they are preceded by a # sign. For example:

[ MyRoutine;
#Iftrue MAX_SCORE > 1000;
  print "My, what a long game we're in for!^";
#Ifnot;
  print "Let's have a quick game, then.^";
#Endif;
  PlayTheGame();
];

which actually only compiles one of the two print statements, according to what the value of the constant MAX_SCORE is.

One kind of “if-defined” manoeuvre is so useful that it has an abbreviation:

Defaultname› ‹value;

defines ‹name› as a constant if it wasn't already the name of something: so it's equivalent to

Ifndefname; Constantname=value; Endif;

Similarly, though far less often used, Stub <name> <number>; defines a do-nothing routine with this name and number (0 to 3) of local variables, if it isn't already the name of something: it is equivalent to

Ifndefname; [namex1 x2xnumber; ]; Endif;

· · · · ·

Large standard chunks of Inform source code are often bundled up into “libraries” which can be added to any Inform story file using the Include directive. Almost all Inform adventure games include three library files called “Parser”, “VerbLib” and “Grammar”, and several dozen smaller libraries have also been written. Sometimes, though, what you want to do is “include all of this library file except for the definition of SomeRoutine”. You can do this by declaring:

Replace SomeRoutine;

before the relevant library file is included. You still have to define your own SomeRoutine, hence the term “replace”.

▲▲ How does Inform know to ignore the SomeRoutine definition in the library file, but to accept yours as valid? The answer is that a library file is marked out as having routines which can be replaced, by containing the directive

System_file;

All eight of the standard Inform library files (the three you normally Include in games, plus the five others which they Include for you) begin with this directive. It also has the effect of suppressing all compilation warnings (but not errors) arising from the file.

· · · · ·

One way to follow what is being compiled is to use the Message directive. This makes the compiler print messages as it compiles:

Message "An informational message";
Message error "An error message";
Message fatalerror "A fatal error message";
Message warning "A warning message";

Errors, fatal errors and warnings are treated as if they had arisen from faults in the source code in the normal way. See §40 for more about the kinds of error Inform can produce, but for now, note that an error or fatal error will prevent any story file from being produced, and that messages issued by Message warning will be suppressed if they occur in a “system file” (one that you have marked with a System_file directive). Informational messages are simply printed:

Message "Geometry library by Boris J. Parallelopiped";

prints this text, followed by a new-line.

One reason to use this might be to ensure that a library file fails gracefully if it needs to use a feature which was only introduced on a later version of the Inform compiler than the one it finds itself running through. For example:

Ifndef VN_1610;
Message fatalerror
    "The geometry extension needs Inform 6.1 or later";
Endif;

By special rule, the condition “VN_1610 is defined” is true if and only if the compiler's release number is 6.10 or more; similarly for the previous releases 6.01, first to include message-sending, 6.02, 6.03, 6.04, 6.05, 6.10, which expanded numerous previous limits on grammar, 6.11, 6.12, which allowed Inform to read from non-English character sets, 6.13, 6.15, which allowed parametrised object creation, 6.20, which introduced strict error checking, and finally (so far) 6.21, the first to feature Infix. A full history can be found in the Technical Manual.

· · · · ·

Inform also has the ability to link together separately-compiled pieces of story file into the current compilation. This feature is provided primarily for users with slowish machines who would sooner not waste time compiling the standard Inform library routines over and over again. Linking isn't something you can do entirely freely, though, and if you have a fast machine you may prefer not to bother with it: the time taken to compile a story file is now often dominated by disc access times, so little or no time will be saved.

The pieces of pre-compiled story file are called “modules” and they cannot be interpreted or used for anything other than linking.

The process is as follows. A game being compiled (called the “external” program) may Link one or more pre-compiled sections of code called “modules”. Suppose the game Jekyll has a subsection called Hyde. Then these two methods of making Jekyll are, very nearly, equivalent:

  1. Putting Include "Hyde"; in the source for "Jekyll", and compiling "Jekyll".
  2. Compiling "Hyde" with the -M (“module”) switch set, then putting Link "Hyde"; into the same point in the source for "Jekyll", and compiling "Jekyll".

Option (2) is faster as long as "Hyde" does not change very often, since its ready-compiled module can be left lying around while "Jekyll" is being developed.

Because “linking the library” is by far the most common use of the linker, this is made simple. All you have to do is compile your game with the -U switch set, or, equivalently, to begin your source code with

Constant USE_MODULES;

This assumes that you already have pre-compiled copies of the two library modules: if not, you'll need to make them with

inform -M library/parserm.h
inform -M library/verblibm.h

where library/parserm.h should be replaced with whatever filename you keep the library file “parserm” in, and likewise for “verblibm”. This sounds good, but here are four caveats:

  1. You can only do this for a game compiled as a Version 5 story file. This is the version Inform normally compiles to, but some authors of very large games need to use Version 8. Such authors usually have relatively fast machines and don't need the marginal speed gain from linking rather than including.
  2. It's essential not to make any Attribute or Property declarations before the Include "Parser" line in the source code, though after that point is fine. Inform will warn you if you get this wrong.
  3. Infix debugging, -X, is not compatible with linking, and strict error checking -S does not apply within modules.
  4. The precompiled library modules always include the -D debugging verbs, so when you come to compile the final release version of a game, you'll have to compile it the slow way, i.e., without linking the library.

▲▲ If you intend to write your own pre-compilable library modules, or intend to subdivide a large game into many modular parts, you will need to know what the limitations are on linking. (In the last recourse you may want to look at the Technical Manual.) Here's a brief list:

  1. The module must make the same Property and Attribute directives as the main program and in the same order. Including the library file "linklpa.h" (“link library properties and attributes”) declares the library's own stock, so it might be sensible to do this first, and then include a similar file defining any extra common properties and attributes you need.
  2. The module cannot contain grammar (i.e., use Verb or Extend directives) or create “fake actions”.
  3. The module can only use global variables defined outside the module if they are explicitly declared before use using the Import directive. For example, writing Import global frog; allows the rest of the module's source code to refer to the variable frog (which must be defined in the outside program). Note that the Include file "linklv.h" (“link library variables”) imports all the library variables, so it would be sensible to include this.
  4. An object in the module can't inherit from a class defined outside the module. (But an object outside can inherit from a class inside.)
  5. Certain constant values in the module must be known at module-compile-time (and must not, for instance, be a symbol only defined outside the module). For instance: the size of an array must be known now, not later; so must the number of duplicate members of a Class; and the quantities being compared in an Iftrue or Iffalse.
  6. The module can't: define the Main routine; use the Stub or Default directives; or define an object whose parent object is not also in the same module.

These restrictions are mild in practice. As an example, here is a short module to play with:

Include "linklpa";        ! Make use of the properties, attributes
Include "linklv";         ! and variables from the Library
[ LitThings x;
  objectloop (x has light)
      print (The) x, " is currently giving off light.^";
];

It should be possible to compile this -M and then to Link it into another game, making the routine LitThings exist in that game.

· · · · ·

Every story file has a release number and a serial code. Games compiled with the Inform library print these numbers in one line of the “banner”. For instance, a game compiled in December 1998 might have the banner line:

Release 1 / Serial number 981218 / Inform v6.20 Library 6/8

The release number is 1 unless otherwise specified with the directive

Release <number>;

This can be any Inform number, but convention is for the first published copy of a game to be numbered 1, and releases 2, 3, 4,… to be amended re-releases.

The serial number is set automatically to the date of compilation in the form “yymmdd”, so that 981218 means “18th December 1998” and 000101 means “1st January 2000”. You can fix the date differently by setting

Serial "dddddd";

where the text must be a string of 6 (decimal) digits. Inform's standard example games do this, so that the serial number will be the date of last modification of the source code, regardless of when the story file is eventually compiled.

The Inform release number is written into the story file by Inform itself, and you can't change it. But you can make the story file print out this number with the special statement inversion;.