home *** CD-ROM | disk | FTP | other *** search
/ Solo Programadores 22 / SOLO_22.iso / docs / misc / old / gnote1.doc < prev    next >
Encoding:
Text File  |  1996-04-29  |  57.4 KB  |  1,187 lines

  1. GNAT-NOTE #1
  2. Jan 31, 1993
  3. Revised: April 8, 1993
  4. Revised: April 12, 09:51
  5. Robert B. K. Dewar
  6.  
  7. A LIBRARY DESIGN FOR GNAT
  8.  
  9. This design is based on discussions in the GNU-Ada design group at NYU, as
  10. well as taking into account contributions from others, including especially
  11. Richard Stallman. The basic philosophy is to provide an environment which
  12. is fully flexible, and at the same time has a natural and intuitive style
  13. of use both for Ada programmers used to the more conventional Ada library
  14. model, and to Unix programmers. The original version of this note was
  15. generated in January, but the design has undergone extensive modification
  16. since then. The approach described here is that implemented in GNAT as of
  17. April, 1993.
  18.  
  19.  
  20. Background -- The Ada Library Model of Compilation
  21. --------------------------------------------------
  22.  
  23. This document addresses the issue of representing what the Ada RM calls the
  24. library "file", and implementing the semantics associated with this entity.
  25. First, let's review the Ada model. We use the term Ada model to describe
  26. the common interpretation of the intention of the reference manual. As we
  27. shall see later, the RM can be read in a rather flexible manner (the basic
  28. issue being the extent to which its discussion of the library is talking
  29. about a conceptual or physical entity). Existing Ada implementations have
  30. in fact taken a particular interpretation, which is what we describe here.
  31.  
  32. An Ada library (we will always use this terminology to distinguish it from
  33. other uses of the word library) is a data structure that gathers the results
  34. of a set of compilations of Ada source files. A compilation is performed in
  35. the context of such a library, and the information in the library is used
  36. to enforce type consistency between separately compiled modules. Unlike some
  37. other language environments, all such type checking is performed at compile
  38. time, and Ada guarantees at the language level that separately compiled
  39. modules of a complete Ada program are type consistent.
  40.  
  41. Building an Ada program consists of selecting a main program (typically this
  42. is a parameterless procedure compiled into the Ada library), and all the other
  43. modules on which this main program depends. These modules are then bound into
  44. a single executable program. For the most part this process is similar to the
  45. normal link step which is familiar from other language environments, but there
  46. are some Ada-specific semantics which are intended to be enforced at link time.
  47.  
  48. Let's look at some specific examples of how the Ada library model works.
  49. Suppose that we have a program consisting of the following elements, called
  50. compilation units, each of which is separately compiled.
  51.  
  52.     1.    -- Specification of MAIN procedure
  53.     procedure MAIN;
  54.  
  55.     2.    -- Body (implementation) of MAIN procedure
  56.     with PROC1, PACKG1;   -- units needed by MAIN program
  57.     procedure MAIN is     -- not required to be called MAIN
  58.        ...
  59.     end;
  60.  
  61.     3.    -- Specification of PROC1 procedure
  62.     with PACKG1;
  63.     procedure PROC1 (....);
  64.  
  65.     4.    -- Body of PROC1 procedure
  66.     procedure PROC1 (....) is
  67.        ...
  68.     end;
  69.  
  70.     5    -- Specification of package PACKG1
  71.     package PACKG1 is
  72.        ...
  73.     end;
  74.  
  75.     6.    -- Body of package PACKG1
  76.     package body PACKG1 is
  77.        ...
  78.     end;
  79.  
  80. Note: in this discussion we use all upper case for unit names to clearly
  81. distinguish them from file names, which are all lower case. Actual casing
  82. requirements are more flexible of course. In particular, we prefer to use
  83. the mixed case convention (e.g. Utility_Package) in our actual Ada code, but
  84. the clear font difference helps avoid confusion in a document of this type.
  85.  
  86. Notice first of all that for each procedure and package, there are two
  87. separate parts. First we have the specification (which gives the name and
  88. types of the procedure parameters, and is essentially similar in function
  89. to a function prototype -- or collection of prototypes in the case of a
  90. package -- in C. The other part is the body which is the implementation.
  91. These two parts can in general be compiled separately.
  92.  
  93. A compilation unit may "depend" on other compilation units. The most typical
  94. way of creating such a dependence is by use of a "with" clause. For example,
  95. in the above set of units, procedure MAIN depends on procedure PROC1.
  96.  
  97. A definite order of compilation is enforced by the language semantics (and
  98. implemented by use of the Ada library). In our example here, the compilation
  99. order must respect the following partial ordering:
  100.  
  101.      Spec of MAIN must be compiled before Body of MAIN
  102.      Spec of PROC1 must be compiled before Body of PROC1
  103.      Spec of PACKG1 must be compiled before Body of PACKG1
  104.      Spec of PROC1 must be compiled before Body of MAIN
  105.      Spec of PACKG1 must be compiled before Body of MAIN
  106.      Spec of PACKG1 must be compiled before Spec of PROC1
  107.  
  108. Basically the idea is that you must compile the specs of anything you depend
  109. on before compiling the dependent unit, and in addition, the spec of a unit
  110. must be compiled before its corresponding body. Within these rules there is
  111. a fair amount of freedom in the compilation order. For example, in the current
  112. example, there is no rule about the order in which the bodies must be compiled.
  113.  
  114. An important idea here is one of "obsolete" units. If a unit is recompiled,
  115. then units which depend on it are obsolete, and must be recompiled. Again the
  116. Ada library is the data structure which is used to implement this requirement.
  117. For example, in our example here, if the spec of PACKG1 is recompiled, then
  118. the body of Main, and the spec and body of PROC1 must be recompiled (further-
  119. more, in accordance with the ordering rules given above, the spec of PROC1
  120. must be recompiled before the body of Main).
  121.  
  122. There are a few more fine points in the model.
  123.  
  124.    A compiler must be able to take as input a compilation, which is a series
  125.    of one or more compilation units. The normal model is that a single source
  126.    file can contain several compilation units, although the Ada RM says nothing
  127.    about source files, so this is not a necessary convention. In particular,
  128.    it would be possible to declare that the representation of a compilation
  129.    consisting of several units consists of a series of files, each containing
  130.    more than one unit. However, most, but not all implementations, have just
  131.    assumed that "compilation unit = file", so that submitting a file to the
  132.    compiler involves submitting a series of compilation units.
  133.  
  134.    If two files contain the same unit, then the one which gets into the
  135.    library is the one compiled latest. The meaning of the program thus depends
  136.    on the order of compilation of its components. A particularly confusing
  137.    case is when multiple units appear in a file. If file F1 contains units
  138.    A,B,C and file F2 contains unit B, then compiling F2 after F1 will remove
  139.    the old version of B from the library, but leave A and C intact.
  140.  
  141.    It is permissible to compile the body of a procedure without compiling
  142.    the corresponding spec. In this case the body acts as a spec, and has
  143.    the same dependencies as the spec. In the example above, we could omit
  144.    compilation unit number 1, and compilation unit 2 would act as the spec
  145.    for MAIN.
  146.  
  147.    The specification for a subprogram can be omitted, in which case the body
  148.    acts as a spec. The exact details of how this works are a little tricky.
  149.    In particular, when you have a body that is serving as a spec in this
  150.    way, it will be as usual by the introduction of a separate spec. Once
  151.    a spec has been introduced, compiling a body which is incompatible with
  152.    the spec must be rejected.
  153.  
  154.    In Ada/83, certain packages may have optional package bodies (these are
  155.    typically packages containing only type and variable declarations). In
  156.    Ada/9X, such packages may *not* have associated package bodies.
  157.  
  158.    If the specification of a procedure contains a pragma inline, or the
  159.    specification of a package contains one or more inlined procedures, then
  160.    any unit that depends on the specification also depends on its body, since
  161.    it needs the body to do the inlining. In this case the body containing the
  162.    inlined procedures must be compiled before the with'ing unit.
  163.  
  164. In the Ada Reference Manual, there are specific references to a "library file",
  165. and this is often taken to mean that the Ada library should be or must be
  166. represented using a file in the normal sense. Most Ada systems do in fact
  167. implement the Ada library in this manner, so that a compilation specifies
  168. a source file and an Ada library, and the effect of the compilation is to
  169. generate object and listing output *and* to update the library file. However,
  170. it is clearly accepted that the RM does not require this implementation
  171. approach. In this view, an Ada library is a conceptual entity that can be
  172. implemented in any manner that provides the required semantics.
  173.  
  174. Note: in the model where a library file is maintained, special Ada specific
  175. utilities are required to rename, move or copy units between libraries,
  176. since the Ada library information must be maintained in an Ada specific
  177. form known only to components of the Ada system.
  178.  
  179. Note: an Ada purist will note that the proper technical term for what we
  180. have called a specification or spec here is "declaration", but the (mis)use
  181. of the term spec(ification) is essentially universal in the Ada world, so we
  182. follow this de facto standard in our terminology, except that from now on
  183. we will adopt the internal GNAT terminology: specification, spelled out in
  184. full, is a syntactic term, referring to the defined Ada grammar. The
  185. abbreviation spec is reserved for referring to declarations of units.
  186. We actually find the use of spec in this context helpful, since for example
  187. if one refers to the spec for a given body, the meaning is clear, whereas
  188. if you refer to the declaration for a package body, it is not clear whether
  189. you are talking about the declaration of the body itself, or the package
  190. declaration.
  191.  
  192.  
  193. Some Relevant Ada Language Features
  194. -----------------------------------
  195.  
  196. This section summarizes some important features of Ada that are relevant to
  197. this discussion. Ada knowledgeable people can skip this, but it will be helpful
  198. to those whose knowledge comes from the non-Ada world.
  199.  
  200. Subunits and Stubs
  201.  
  202.    A nested body, such as a nested procedure body, or nested package body,
  203.    can be made into a subunit. This means that it is in a separate file,
  204.    and at least in some sense is compiled separately. We say in some sense
  205.    here because it must be compiled in the context of its parent, just as
  206.    though it had been inline. In the parent, we have a "stub" that stands
  207.    for the missing body, e.g.
  208.  
  209.     procedure JUNK is separate;
  210.  
  211.    The body is then placed in a separate compilation unit, typically in
  212.    a separate file, and looks like:
  213.  
  214.     separate (PARENT)
  215.     procedure JUNK is .. <normal procedure body code> ..
  216.  
  217.    where PARENT is the name of the unit containing the stub. Semantically
  218.    the overall effect of this structure should be semantically equivalent
  219.    to including the subunit inline, although that isn't quite exactly right
  220.    in Ada terms, since the subunit can have its own context clause (with'ed
  221.    units), and, although there is no conceptual reason for this restriction
  222.    (i.e. it stems from methodological considerations, rather than technical
  223.    considerations), Ada does not permit with clauses other than at the start
  224.    of the compilation.
  225.  
  226. Child Units
  227.  
  228.    A child unit in Ada 9X is an extension of its parent unit, which is a
  229.    library package. Child units have qualified names indicating the parent
  230.    (e.g. unit XYZ.ARN is a child of unit XYZ). A child unit has both a spec
  231.    and a body. The spec acts as an extension of the parent spec, and the
  232.    body acts as an extension of the parent body.
  233.  
  234. Inlined Subprograms
  235.  
  236.    The spec of a subprogram can be marked using a pragma Inline, which means
  237.    that an attempt should be made to inline the code of the body. This creates
  238.    a dependence of the unit containing a call on the body. Actually the rule
  239.    in the RM is that this dependence is only established if the body has been
  240.    compiled before the unit containing the call. This is a natural consequence
  241.    of the library model in the RM, and means for instance that if two packages
  242.    call inlined routines in one another, one can not expect both requests to
  243.    be satisfied (which one is satisfied depends on the order of compilation).
  244.  
  245. Generic Units
  246.  
  247.    A generic unit is essentially a macro for a subprogram or package where
  248.    the parameters can be types as well as normal procedure parameters. To
  249.    use a generic it must be instantiated giving specific values for the
  250.    parameters. This conceptually creates a copy of the spec and body which
  251.    are appropriately customized.
  252.  
  253.    An obvious implementation is simply to inline the customized copies at the
  254.    point of instantiation. However this creates a problem since it means that
  255.    a dependency is created from the unit containing the instantiation to the
  256.    body. As we discussed for the inlined subprogram case, that can cause some
  257.    restrictions in cases where two packages instantiate generics declared in
  258.    the other. In the case of inlined subprograms, we could just ignore the
  259.    inlining request, but in the generic case we get stuck.
  260.  
  261.    There are approaches for getting around these limitations, but they are
  262.    complicated. We won't go into them further here. We note that the Ada/83
  263.    RM specifically allows an implementation to place restrictions on the use
  264.    of generics consistent with this model of inline expansion, but in any
  265.    case the GNAT scheme is simple as we shall see and has no such restrictions.
  266.  
  267.  
  268. Background -- The GNU Model of compilation
  269. ------------------------------------------
  270.  
  271. The GNU model of compilation is that separate files which constitute the
  272. program are separately compiled and each compilation produces a corresponding
  273. object file. These object files are then linked together by specifying a list
  274. of object files in a program. A library consists of a set of such object files
  275. and there is no library file as such, although there is a notion of dependence
  276. on headers (which are of course source files).
  277.  
  278. In this model, standard system utilities (rm, mv, cp) can be used to remove,
  279. rename, and copy modules.
  280.  
  281. In the case of C and C++ programs, a given source file can #include header
  282. files. In this case to compile the file, the header files must be available.
  283. The make utility in GNU usage in general specifies for each object file
  284. which source files must be around to generate it, i.e. it establishes a
  285. dependency of the object file on a set of sources. As long as the dependencies
  286. in the make file are correct, and as long as all compilations are performed
  287. using this make file, then consistency of the system is guaranteed. However
  288. there is nothing to stop compilations being carried out without the use of
  289. make, and in such cases, it is possible to generate executables which are
  290. inconsistent, e.g. more than one incompatible version of a given header file
  291. appears in separate object modules.
  292.  
  293.  
  294. The Design Goal - Unification
  295. -----------------------------
  296.  
  297. The goal in this design is to reconcile the Ada and GNU models of compilation.
  298. On the one hand, we want the Ada guarantee of inter-module type integrity
  299. that is guaranteed by the Ada language specification -- in particular it
  300. should be essentially impossible to link a type inconsistent program. On
  301. the other hand, we want to fit into the GNU model in which separate
  302. compilations generate separate object files (and which has no place for a
  303. global library file).
  304.  
  305.  
  306. The Basic Model of GNAT Compilation
  307. -----------------------------------
  308.  
  309. In this section we will describe the basic module of GNAT compilation. Before
  310. starting, we should warn Ada programmers that they are likely to react that
  311. the GNAT approach is at best peculiar and at worst wrong, because it is quite
  312. different from conventional Ada models. However, we ask for such readers to
  313. read ahead with an open mind. Later on we will describe how the system can
  314. be used in a manner that has identical semantics to typical library based
  315. Ada systems if that is desirable.
  316.  
  317. The fundamental point is that we use the GNU view of compilation as our
  318. starting point, and in particular we are entirely source based. A GNAT
  319. compilation specifies a source file, and generates a single object file.
  320. There are *no* library files, or any centralized library information of any
  321. kind.
  322.  
  323. A GNAT source file contains a single compilation unit (a compilation is
  324. represented as a series of source files, each containing one compilation
  325. unit). Furthermore there is a mapping from unit names to file names, so
  326. that from a unit name one can always determine the file name. This mapping
  327. is quite flexible, as we shall describe later, but for the examples in this
  328. document we will use the default file naming convention as follows:
  329.  
  330.    The file name is the expanded name of the unit with dots replaced by
  331.    minus signs. An additional minus sign is appended to specs to distinguish
  332.    them from bodies. The extension .ada is included in all files.
  333.  
  334. Some examples of these default mapping rules are:
  335.  
  336.      Unit name              File name
  337.  
  338.    PACKGE1 (spec)         packge-.ada
  339.    PACKGE2 (body)         packge.ada
  340.    SCN.NLIT (subunit)         scn-nlit.ada
  341.    CHILD.PKG (child spec)     child-pkg-.ada
  342.    XYZ.ARG.LMS (subunit      xyz-arg-lms.ada
  343.    ABC.DEF.GHI (child spec)     abc-def.ghi-.ada
  344.  
  345. The corresponding object file has the same file name with the extension .o
  346. (which is why the spec and body of a file have to have different file names,
  347. not just different extensions).
  348.  
  349. As in a C file with #include'd header files, a GNAT source file may require
  350. other source files for its compilation. These include:
  351.  
  352.    The corresponding spec for a body. For example if we compile a package
  353.    body xyz.ada, we will reference the source of the package spec in xyz-.ada
  354.  
  355.    The parent spec of a child library spec. Child libraries are extensions of
  356.    their parent library, so to compile a child library, we must have the
  357.    files for its parent available (and since this principle is applied
  358.    recursively, the entire set of ancestors will be needed). For example,
  359.    if we are compiling the child spec abc-def-.ada, we will need the source
  360.    of its parent in abc-.ada.
  361.  
  362.    With'ed specifications. The context clause of an Ada compilation unit
  363.    specifies a series of units whose specs contain entities that may be
  364.    referenced in the compilation. The sources of all such specs must be
  365.    available. For example if we compile xyz.ada, and Unit XYZ with's unit
  366.    ABC, then we will need the source file abc-.ada containing the spec of ABC.
  367.  
  368.    Parent body for a subunit. If we are compiling a subunit, then it can
  369.    reference entities declared in its parent, so certainly we must have the
  370.    source of the parent around. For example, if we are compiling the subunit
  371.    in file abc-def.ada, then we will need the source of its parent in abc.ada
  372.  
  373.    Bodies of inlined subprograms. If we call an inlined procedure declared
  374.    in some spec, then we need not only the source of that spec, but also the
  375.    body. For example, if unit ABC with's the inlined subprogram RAPID, then
  376.    the compilation of abc.ada will require not only the spec of the source in
  377.    rapid-.ada, but also the body in the file rapid.ada
  378.  
  379.    Bodies of instantiated generics. This is exactly the same situation. For
  380.    example if unit TOP1 instantiates a generic subprogram GENERAL1, then
  381.    the compilation of top1.ada will require not only the spec of the source in
  382.    general1-.ada, but also the generic body in general1.ada
  383.  
  384.    Bodies of packages containing either inlined subprograms that are called,
  385.    of generic bodies that are instantiated. This is a similar case. Suppose
  386.    that unit JUNK1 with's the package PACK1, and makes a call to the inlined
  387.    subprogram XYZ declared in PACK1, or instantiates the generic spec GEN1
  388.    declared in PACK1, then the compilation of junk1.ada will require not only
  389.    the package spec in pack1-.ada, but also the package body in pack1.ada.
  390.  
  391. All these rules probably seem quite reasonable to a C programmer, since they
  392. are similar to the requirements that compilation of a C source containing
  393. a #include for a header requires the header to be around. However, an Ada
  394. programmer is likely to be puzzled.
  395.  
  396. The key understanding is that in GNAT, dependencies are not from one
  397. compilation unit to another, but from object files to corresponding sources.
  398. Let's take another look at the example at the start of this note:
  399.  
  400.     1.    -- Specification of MAIN procedure (in file main-.ada)
  401.     procedure MAIN;
  402.  
  403.     2.    -- Body (implementation) of MAIN procedure (in file main.ada)
  404.     with PROC1, PACKG1;   -- units needed by MAIN program
  405.     procedure MAIN is     -- not required to be called MAIN
  406.        ...
  407.     end;
  408.  
  409.     3.    -- Specification of PROC1 procedure (in file proc1-.ada)
  410.     with PACKG1;
  411.     procedure PROC1 (....);
  412.  
  413.     4.    -- Body of PROC1 procedure (in proc1.ada)
  414.     procedure PROC1 (....) is
  415.        ...
  416.     end;
  417.  
  418.     5    -- Specification of package PACKG1 (in file packg1-.ada)
  419.     package PACKG1 is
  420.        ...
  421.     end;
  422.  
  423.     6.    -- Body of package PACKG1 (in file packg1.ada)
  424.     package body PACKG1 is
  425.        ...
  426.     end;
  427.  
  428. Now we have a number of dependencies of object files on source files as
  429. follows:
  430.  
  431.     main-.o    depends on main-.ada
  432.     main.o     depends on main.ada, main-.ada, proc1-.ada, packg1-.ada
  433.     proc1-.o   depends on proc1-.ada, packg1-.ada
  434.     proc1.o    depends on proc1.ada proc1-.ada, packg1-.ada
  435.     packg1-.o  depends on packg1-.ada
  436.     packg1.o   depends on packg1.ada, packg1-.ada
  437.  
  438. Note that the dependencies are transitive, in this example the dependency
  439. of proc1.o on packg1-.ada is such a transitive dependence. This is similar
  440. to a situation in C where a header #include's another header, and of course
  441. both header files must be around to compile a file including the first header.
  442.  
  443. In this approach, we are reinterpreting the "order of compilation" rules
  444. to be "dependency on source files" rules. A rule that says that the body
  445. of MAIN cannot be compiled until the spec of MAIN has been compiled is
  446. reinterpreted to mean that the body of MAIN cannot be compiled unless the
  447. source of the spec of MAIN is available.
  448.  
  449. The rules about compilations obsoleting other compilations are similarly
  450. reinterpreted. The rule that says that recompiling the source of MAIN
  451. obsoletes the body is taken to mean that reediting the source of MAIN
  452. requires the body to be recompiled.
  453.  
  454. One interesting consequence of the GNAT approach is that if all the sources
  455. of a program are available, there are in fact no restrictions on the order
  456. of compilation, the units can be compiled in any order. We can even compile
  457. bodies before the corresponding specs if we want.
  458.  
  459. This model of source dependencies has a number of significant advantages.
  460. It's certainly much more familiar to non-Ada programmers, and we believe
  461. that it is fundamentally much simpler than conventional Ada library models.
  462. Furthermore, there are a number of technical difficulties relating to
  463. circular dependencies in the conventional model (where two units depend
  464. on one another) that completely disappear. For instance, consider the
  465. following situation:
  466.  
  467.     1.    -- Specification of PACKG1 (in file packg1-.ada)
  468.     package PACKG1 is
  469.       procedure PROC1;
  470.       pragma Inline (PROC1);
  471.       ...
  472.     end PACKG1;
  473.  
  474.     2.    -- Body (implementation) of PACKG1 (in file packg1.ada)
  475.     with PACKG2;
  476.     package body PACKG1 is
  477.        ...
  478.        PROC2;
  479.        ...
  480.     end PACKG1;
  481.  
  482.     3.    -- Specification of PACKG2 (in file packg2-.ada)
  483.     package PACKG2 is
  484.       procedure PROC2;
  485.       pragma Inline (PROC2);
  486.       ...
  487.     end PACKG2;
  488.  
  489.     4.    -- Body (implementation) of PACKG2 (in file packg2.ada)
  490.     with PACKG1;
  491.     package body PACKG2 is
  492.        ...
  493.        PROC1;
  494.        ...
  495.     end PACKG1;
  496.  
  497. This is the case of mutually recursive inline references that causes trouble
  498. in the conventional model, since to accomplish both inlining actions, the
  499. units for the bodies of the two packages would have to depend on one another.
  500. Note incidentally that we are not talking about a case of actual recursive
  501. inlining, we assume in this example that the call to PROC1 is not in the
  502. body of PROC2, but in some other subprogram, and similarly the call to PROC2
  503. is not in the body of PROC1, but also in some other subprogram, so this
  504. situation is perfectly sensible, and it would be desirable to have both
  505. inline actions achieved.
  506.  
  507. In the GNAT model there is no special problem, the dependencies are:
  508.  
  509.     packg1-.o  depends on packg1-.ada
  510.     packg1.o   depends on packg1.ada, packg1-.ada, packg2.ada, packg2-.ada
  511.     packg2-.o  depends on packg2-.ada
  512.     packg2.o   depends on packg1.ada, packg1-.ada, packg2.ada, packg2-.ada
  513.  
  514. No big surprises, no particular problems! It's just that, as one might expect
  515. any change to any of the four sources requires that the bodies of the two
  516. packages be recompiled.
  517.  
  518. Now the failure of the normal Ada library model in this case is not critical,
  519. since the semantic effect of failing to achieve inlining is just a loss of
  520. efficiency. However, consider a similar example with mutual generic
  521. instantiation:
  522.  
  523.     1.    -- Specification of PACKG1 (in file packg1-.ada)
  524.     package PACKG1 is
  525.       generic
  526.          type X is private;
  527.          procedure PROC1 (M : X);
  528.       ...
  529.     end PACKG1;
  530.  
  531.     2.    -- Body (implementation) of PACKG1 (in file packg1.ada)
  532.     with PACKG2;
  533.     package body PACKG1 is
  534.        ...
  535.        package NEW1 is new PROC1 (Integer);
  536.        ...
  537.     end PACKG1;
  538.  
  539.     3.    -- Specification of PACKG2 (in file packg2-.ada)
  540.     package PACKG2 is
  541.       generic
  542.          type X is private;
  543.          procedure PROC2 (M : X);
  544.       ...
  545.     end PACKG2;
  546.  
  547.     4.    -- Body (implementation) of PACKG2 (in file packg2.ada)
  548.     with PACKG1;
  549.     package body PACKG2 is
  550.        ...
  551.        package NEW2 is new PROC2 (Integer);
  552.        ...
  553.     end PACKG1;
  554.  
  555. Once again, we are not talking about an actual recursive instantiation, which
  556. would be illegal in Ada. The instantiation of PROC2 does not occur in the
  557. body of PROC1, and the instantiation of PROC1 does not occur in the body of
  558. PROC2, so this program is perfectly legal.
  559.  
  560. Now we are in trouble with the Ada dependency model if we are trying to
  561. inline generics, because once again this would generate a mutual dependency
  562. between the two package bodies. In the conventional Ada model, we have two
  563. ways out of this:
  564.  
  565.    o  Take advantage of the permission in Ada/83 to refuse to compile this
  566.       particular program. The Ada programmer may be annoyed, but you are
  567.       still conforming. This is a bit of "subsetting" that is specifically
  568.       permitted by the standard. Note however that it is either possible or
  569.       likely, depending on your point of view, that Ada/9X will withdraw
  570.       this subsetting permission, and in any case, this subsetting is not
  571.       desirable from an Ada programmer's point of view.
  572.  
  573.    o  Figure out how to avoid the dependencies. There are two approaches.
  574.       One is to use shared implementations of generics, which causes all
  575.       kinds of implementation problems. The other is to compile the
  576.       instantiated copies in separate object files, and then defer their
  577.       compilation till the necessary information is at hand. This approach
  578.       is also tricky, and certainly does not conform with our "one source,
  579.       one object" approach.
  580.  
  581. Now let's look at what happens in the GNAT model. We simply get the same
  582. set of dependencies as in the inline case:
  583.  
  584.     packg1-.o  depends on packg1-.ada
  585.     packg1.o   depends on packg1.ada, packg1-.ada, packg2.ada, packg2-.ada
  586.     packg2-.o  depends on packg2-.ada
  587.     packg2.o   depends on packg1.ada, packg1-.ada, packg2.ada, packg2-.ada
  588.  
  589. Again, no particular problems! It's just that we have to recompile both
  590. package bodies if any of the four sources is modified. Furthermore we can
  591. use the simple generic inlining model without introducing any of the
  592. restrictions usually associated with this model.
  593.  
  594.  
  595. Ensuring Consistency
  596. --------------------
  597.  
  598. One thing that will be worrying Ada programmers at this point is how we
  599. ensure that an executable Ada program is guaranteed to be consistent. In
  600. the C case, we answer this question by saying "generate a correct make
  601. file with the proper dependencies, preferably with a tool, and then jolly
  602. well use it whenever you compile -- caveat emptor those who don't follow
  603. this rule! Well that doesn't sound good enough for Ada programmer's who
  604. have a much more strenuous view of safety and correctness -- indeed this
  605. is a principle aspect of the appeal of Ada.
  606.  
  607. In particular, suppose we have the six files of our first example:
  608.  
  609.     1.    -- Specification of MAIN procedure
  610.     procedure MAIN;
  611.  
  612.     2.    -- Body (implementation) of MAIN procedure
  613.     with PROC1, PACKG1;   -- units needed by MAIN program
  614.     procedure MAIN is     -- not required to be called MAIN
  615.        ...
  616.     end;
  617.  
  618.     3.    -- Specification of PROC1 procedure
  619.     with PACKG1;
  620.     procedure PROC1 (....);
  621.  
  622.     4.    -- Body of PROC1 procedure
  623.     procedure PROC1 (....) is
  624.        ...
  625.     end;
  626.  
  627.     5    -- Specification of package PACKG1
  628.     package PACKG1 is
  629.        ...
  630.     end;
  631.  
  632.     6.    -- Body of package PACKG1
  633.     package body PACKG1 is
  634.        ...
  635.     end;
  636.  
  637. Now we do the following:
  638.  
  639.    Compile packg1-.ada to generate packg1-.o
  640.    Compile packg1.ada  to generate packg1.o
  641.    Compile proc1-.ada  to generate proc1-.o
  642.    Compile proc1.ada   to generate proc1.o
  643.    Compile main-.ada   to generate main-.o
  644.    Compile main.ada    to generate main.o
  645.  
  646. So far so good, six nice consistent object files. Now let's do the following:
  647.  
  648.    Edit source of packg1-.ada
  649.    Recompile packg1-.ada to generate new version of packg1-.o
  650.    Recompile packg1.ada to generate new version of packg1.o
  651.  
  652. Now if we were using a proper make file, the dependencies in this make file
  653. would force us to recompile the spec and body of PROC1 and the body of MAIN.
  654. But suppose we don't use the make file. Well we have six objects that are
  655. certainly NOT consistent.
  656.  
  657. GNAT has two lines of defence against an attempt to construct a program from
  658. a set of inconsistent objects. First, when we said we generated no centralized
  659. library information, the operable word was centralized. In fact we do generate
  660. some library information for each object file. We call this information the
  661. ADL (Ada Library) information, and the most important component is a recording
  662. of the time stamps of all sources on which this unit depends.
  663.  
  664. Before a program is linked, the Ada binder (you could also call it a prelinker
  665. to use the more familiar GU terminology) must be run. Ada semantics require
  666. this step for two reasons. First, initialization calls must be made to
  667. initialize unit specs and bodies (this initialization activity is called
  668. elaboration in Ada), and you can't tell the order of these calls until you
  669. have the whole program. Second, it is possible to construct a situation in
  670. which no possible order of elaboration exists. Such a situation is considered
  671. a compile time error, and must be diagnosed prior to execution.
  672.  
  673. Part of the processing in the GNAT binder makes sure that the program is
  674. consistent by looking at time stamps in the ADL information associated with
  675. the object modules of the program. In our attempted subversion of the system
  676. above, the binder will detect an error resulting from the time stamp of the
  677. source file packg1-.ada in the ADL for packg1-.o and packg1.o will not match
  678. the time stamp of this same source file in the ADL for the other modules. The
  679. binder will then give a message something like:
  680.  
  681.   Please recompile proc1-.ada (source of packg1-.ada has been modified)
  682.   Please recompile proc1.ada (source of packg1-.ada has been modified)
  683.   Please recompile main.ada (source of packg1-.ada has been modified)
  684.  
  685. These correspond to messages typically obtained from Ada library systems if
  686. they are kind enough to keep traces of obsoleted modules around. Many existing
  687. Ada libraries are *not* kind enough to do this, and so will simply generate
  688. messages saying that these three units are missing from the library (because
  689. they were removed from the library when packg1-.ada was recompiled).
  690.  
  691. Note that only the time stamps of the source files are relevant. The time
  692. when the source file was compiled is irrelevant, and in particular if you
  693. recompile the same source file without having edited anything, you'll get
  694. the same object file, and nothing will get obsoleted, which makes sense of
  695. course, but conventional Ada library systems will obsolete things in this
  696. situation and require quite unnecessary recompilations.
  697.  
  698. Suppose we have a more devious programmer, who has saved the object file from
  699. a previous bind operation on this program (the binder generates an object file
  700. containing the elaboration calls in the required order), and who tries to link
  701. the program without calling the binder. Well the second level of GNAT defence
  702. steps in. The object files themselves contain external references which include
  703. time stamp information, and the linker will not be able to link the program.
  704. The error messages are a little bit more mysterious, you will get something
  705. like:
  706.  
  707.    Unresolved external symbol: packg1%s-1993-04-03:00:00.00
  708.  
  709. which is to be interpreted to mean that someone wanted the version of the
  710. spec whose source has the given time stamp, but there is no corresponding
  711. object file, meaning that the source has been modified and recompiled.
  712.  
  713. These two lines of defence ensure the same level of security that is provided
  714. by conventional Ada library systems (actually some such systems don't provide
  715. the second level of defence).
  716.  
  717. A really determined programmer can still cheat by deliberately modifying the
  718. time stamps of files. We don't particularly encourage this, but we don't try
  719. to prevent it. After all, in an environment where the programmer can change
  720. any bits in sight, we can only make it harder to subvert the consistency
  721. requirement, not impossible. The important thing is to have sufficient
  722. defences that we could never get an inconsistent program other than by very
  723. deliberate subversion of the defences. As an example of the use of such
  724. subversion, consider a programmer who wants to add an entry to a spec, and
  725. guesses, correctly as it turns out, that files currently with'ing the old
  726. version of the spec don't really need to be compiled. Well it will in fact
  727. work to edit the spec, add the new declaration, and then change the time stamp
  728. of the source back to its original value. However, this sort of thing is
  729. obviously risky, not guaranteed to work, and definitely in the caveat emptor
  730. range!
  731.  
  732.  
  733. Order of Compilation Issues
  734. ---------------------------
  735.  
  736. As we have observed, the GNAT model doesn't really place restrictions on
  737. the order of compilation. In particular, if the sources are all around,
  738. it is perfectly possible to compile a package body before compiling the
  739. corresponding package spec.
  740.  
  741. However, a consequence of such an inverted compilation order maybe that when
  742. the package body is compiled, the package spec will be found to have syntax
  743. errors. Of course the compilation cannot proceed in this case. GNAT will
  744. generate messages clearly identifying the syntax errors in the spec, and
  745. will refuse to generate an object file.
  746.  
  747. Normal Ada practice is of course to compile the spec first, and then only
  748. compile the body if the spec is error free. This practice is still generally
  749. desirable in the GNAT environment. Furthermore, as a result of the Ada semantic
  750. requirements, if you compile a spec without errors, then you are absolutely
  751. guaranteed that any subsequent compilation that makes use of this spec will
  752. not encounter errors from the recompilation of the spec that occurs as a
  753. normal part of the GNAT processing.
  754.  
  755. Note the contrast here with the use of C headers, which one generally does
  756. not compile in isolation, and even if you can compile them in isolation, the
  757. fact that compiling a header generates no errors is no guarantee that its
  758. incorporation by #include into some other file will not generate additional
  759. context dependent errors.
  760.  
  761. It may be desirable in practice to enforce the spec-before-body order of
  762. compilation. That's easily done by using make files that introduce additional
  763. dependencies of object files on other object files for referenced specs. For
  764. instance, going back to our standard six file example, the normal GNAT make
  765. file looks like:
  766.  
  767.     main-.o    depends on main-.ada
  768.     main.o     depends on main.ada, main-.ada, proc1-.ada, packg1-.ada
  769.     proc1-.o   depends on proc1-.ada, packg1-.ada
  770.     proc1.o    depends on proc1.ada proc1-.ada, packg1-.ada
  771.     packg1-.o  depends on packg1-.ada
  772.     packg1.o   depends on packg1.ada, packg1-.ada
  773.  
  774. If you want to ensure that specs are compiled before bodies, additional
  775. dependencies can be added:
  776.  
  777.     main-.o    depends on main-.ada
  778.     main.o     depends on main.ada, main-.ada, proc1-.ada, packg1-.ada
  779.         and also on main-.o, proc1-.o, packg1-.o
  780.     proc1-.o   depends on proc1-.ada, packg1-.ada
  781.          and also on packg1-.o
  782.     proc1.o    depends on proc1.ada proc1-.ada, packg1-.ada
  783.          and also on proc1-.o, packg1-.o
  784.     packg1-.o  depends on packg1-.ada
  785.     packg1.o   depends on packg1.ada, packg1-.ada
  786.          and also on packg1-.o
  787.  
  788. Now if you run make using this set of dependencies you get the normal spec
  789. before body rules. Suppose for example you edit packg1-.o and run make.
  790. Clearly in the resulting make file packg1-.ada must be compiled before
  791. packg1.ada, since the compilation of packg1.ada depends on output from the
  792. compilation of packg1-.ada and therefore must be done after it.
  793.  
  794. We anticipate a make-depend type utility for GNAT that will have a switch
  795. to specify whether or not you want this type of enforcement of compilation
  796. order. The compiler itself certainly does not need this enforcement, and so
  797. our approach provides maximum flexibility for the programmer in this regard.
  798.  
  799. Note that you probably don't want to introduce dependencies on object files
  800. for bodies, even if you are dependent on the corresponding sources. Such
  801. additional dependencies wouldn't provide any methodological advantages, and
  802. would have the disadvantage of creating restrictions on the use of pragma
  803. Inline and generic instantiations.
  804.  
  805.  
  806. Handling Subunits
  807. -----------------
  808.  
  809. Subunits could be handled with no further special considerations in the above
  810. model. In particular, the object files for the subunit bodies would depend
  811. on the source files of their parents, and the usual GNAT model would apply,
  812. including the user option of whether or not to force the normal Ada order
  813. of compilation that requires the parent to be compiled first.
  814.  
  815. However, we take a much more radical view of subunits. The reasons for this
  816. view are essentially orthogonal to the considerations given so far, and
  817. are fundamentally the following:
  818.  
  819. 1. There are a number of situations where you would normally expect the
  820.    compiler to know things at compile time, e.g. which outer level variables
  821.    are referenced by inner level procedures, which packages declare tasks,
  822.    etc which you can't know in a conventional Ada system because there may
  823.    be subunits present which you can't see when you are compiling the parent.
  824.    This results in a degradation of the code. For example, consider the
  825.    following:
  826.  
  827.     procedure XYZ is
  828.        A : Integer;
  829.        B : Integer;
  830.  
  831.        package Inner is
  832.           procedure Munge;
  833.        end Inner;
  834.  
  835.        package body Inner is separate;
  836.  
  837.     begin
  838.        ...
  839.     end;
  840.  
  841.    Now we are compiling the parent. We would like to know if tasks are present
  842.    so that we know whether or not to establish a task master for this procedure
  843.    or we would like to know if A is referenced by an inner procedure, so that
  844.    we know if it can be kept in a register. Neither of these questions can be
  845.    answered in a conventional system when compiling the parent, so we have to
  846.    assume the worst, and the effect is that the presence of subunits can
  847.    degrade the code quality considerably.
  848.  
  849. 2. Package subunits are a huge mess to implement. Consider in the above example
  850.    that the body for Inner looks like:
  851.  
  852.     separate (XYZ)
  853.     package body Inner is
  854.        M : Integer;
  855.        ...
  856.     end;
  857.  
  858.    Semantically the integer M belongs to the stack frame of its enclosing
  859.    procedure, and in particular it has the lifetime of this stack frame.
  860.    Where the heck shall we put it? We can't easily put it in that stack
  861.    frame directly, since when we compiled the enclosing procedure, we
  862.    didn't know that M existed.
  863.  
  864.    This problem (one might say headache) is well known to Ada implementors.
  865.    There are a number of schemes, none of them fully satisfactory, and many
  866.    of them introduce significant implementation complexity.
  867.  
  868. 3. GNAT is making use of the existing backend of GCC, which certainly is not
  869.    set up for separate compilation of inner procedures, let alone package
  870.    subunits. We could presumably teach it what it needs to know, and make the
  871.    necessary modifications, but they are rather language specific, and we
  872.    prefer to avoid the need for making this kind of modification to the
  873.    backend of GCC.
  874.  
  875. These factors combine to make subunits a big headache. In GNAT we choose to
  876. get rid of all of them at a stroke by deciding that we will not attempt to
  877. generate an object file for a subunit tree unless the sources of all necessary
  878. subunits are present. We then essentially macro-substitute the bodies for
  879. their stubs, and all the above problems disappear. If you want to think of
  880. this in C terms, consider that the way you would model subunits in C is to
  881. use #include to drag in the separate bodies, and then of course all the sources
  882. would have to be around to compile the parent.
  883.  
  884. In the context of GNAT, there are two consequences. First subunits themselves
  885. do not generate object files and do not need to be separately compiled. In
  886. this respect they are similar to C include files, which are not separately
  887. compiled and do not have corresponding object files. Second, the parent unit
  888. can only be compiled to generate its object module if the sources of the
  889. subunits are all available.
  890.  
  891. There are two immediate reactions that an Ada programmer will have. First
  892. there are efficiency concerns -- "Boy, you're forcing a lot of extra
  893. compilation, that's going to be very slow!" We'll deal with this concern
  894. in a separate section. The more significant concern is that the whole point
  895. of using subunits is to separate concerns. Consider the following scenario:
  896.  
  897.    Susan develops the parent unit XYZ, which has two subunits XYZ.A and XYZ.B
  898.    she creates the source file xyz.ada and then gives the task of writing
  899.    the two subunits to Jose and Jack.
  900.  
  901.    Jose creates the source file xyz-a.ada containing the subunit XYZ.A
  902.  
  903.    Jack creates the source file xyz-b.ada containing the subunit XYZ.B
  904.  
  905. In a conventional Ada system, Susan will compile her parent unit before giving
  906. the tasks to Jose and Jack to be sure that it is syntactically and semantically
  907. correct. She can't test it, except possibly with dummy stubs, but she still
  908. wants to make sure it doesn't contain obvious compile errors before checking
  909. it into the configuration management system.
  910.  
  911. Similarly Jose and Jack will want to compile their subunits, using the compiled
  912. version of the parent, to check that they are syntactically and semantically
  913. correct. Again they can't easily test them, but they want to be able to catch
  914. obvious errors early on.
  915.  
  916. When all components of the system are ready, then testing can begin with the
  917. assurance that no syntax or semantic errors will appear when the system is
  918. assembled.
  919.  
  920. Are we going to lose that important capability in GNAT, given its approach of
  921. compiling the whole thing together? The answer is no. It's true that we can't
  922. make an object file of the whole structure until all units are there, but that
  923. of itself is not really a limitation, because we can't test things till we
  924. have all the subunits anyway.
  925.  
  926. What GNAT does permit is to run the compilations of the parent on its own,
  927. or the bodies of the subunits in the presence of their parent sources in
  928. syntax/semantic check only mode. No object file will be generated, but the
  929. same assurances that the component is syntactically and semantically correct
  930. apply. Since the primary purpose of the compilations that Susan, Jose and
  931. Jack did was to ensure freedom from such errors, the GNAT system has exactly
  932. the same functional capabilities as a conventional Ada system.
  933.  
  934.  
  935. What About Efficiency?
  936. ----------------------
  937.  
  938. There are two efficiency concerns presented by this source-based approach.
  939. First, we are constantly recompiling units in the simple case from their
  940. source. For example, given the package:
  941.  
  942.     with XYZ, MNO, TEXT_IO; use TEXT_IO;
  943.     procedure JFK is
  944.     begin
  945.        Put (XYZ.WHO);
  946.        Put (MNO.SHOT);
  947.            Put ("JFK?:");
  948.     end;
  949.  
  950. the GNAT compiler, asked to compile file jfk.ada, is going to have to
  951. recompile the specs of XYZ, MNO and TEXT_IO. That sounds bad, but let's
  952. look at the alternative. In conventional Ada library based systems, the
  953. result of a compilation is to place information, typically some kind of
  954. intermediate tree, in the library. A subsequent WITH then fetches this
  955. tree from the library. In practice, this tree information can be huge, often
  956. much bigger than the source. It's not at all clear that rereading and
  957. recompiling the source is less efficient than writing and reading back in
  958. these trees. It's true that recompiling means redoing syntax and semantic
  959. checking, but there may be less I/O to do, and reading and writing linked
  960. structures can be complex.
  961.  
  962. Of course we won't know how this really compares till we have detailed
  963. performance figures, but from the performance we see so far, we don't think
  964. our approach will be significantly slower than the conventional library
  965. approach, and it may well be faster.
  966.  
  967. The second efficiency concern has to do with our "recompile-the-whole-tree"
  968. approach to subunits. In the case where a complete program is being compiled
  969. anyway, there is of course no disadvantage in our approach, since each
  970. subunit has to be compiled once in any case.
  971.  
  972. The situation in which the GNAT approach is obviously "inefficient" is when
  973. a modification is made to a single subunit, and the whole tree must be
  974. recompiled. Obviously one can construct examples where the amount of extra
  975. recompilation required is significant. We know this, and it's a conscious
  976. trade off. In return for this extra recompilation effort, we are in a position
  977. to generate much more efficient code for subunits, and also we simplify our
  978. implementation effort considerably. Furthermore, we think that the GNAT
  979. compiler will be fast enough that in practice, there will be few cases in
  980. which the general performance of GNAT will not be competitive with, or better
  981. than conventional systems. Again, time will tell.
  982.  
  983. Note once more that there is nothing in the source based approach that mandates
  984. the compile-everything-at-once approach to subunits. This is a quite independent
  985. decision, and indeed we could revisit this decision later on, but remember that
  986. the only disadvantage in our approach is possible additional compilation time
  987. requirements. From every other point of view, we are clearly ahead in taking
  988. this approach to subunits.
  989.  
  990.  
  991. Finding Source Files
  992. --------------------
  993.  
  994. The GNAT approach involves the ability to find a source file given the Ada
  995. unit name. There are two issues to be addressed. First how do we find the
  996. file name from the unit name?
  997.  
  998.   There are two approaches in the GNAT system for addressing this question.
  999.   First algorithmic mappings are provided. The default mapping is the one
  1000.   we mentioned at the start of this document:
  1001.  
  1002.    The file name is the expanded name of the unit with dots replaced by
  1003.    minus signs. An additional minus sign is appended to specs to distinguish
  1004.    them from bodies. The extension .ada is included in all files.
  1005.  
  1006.   Via command line switches, this algorithm can be modified by specifying
  1007.   a different character than minus to replace dots (dot itself can be used),
  1008.   and different suffixes to distinguish bodies and specs. One interesting
  1009.   possibility is to specify that dots are to be converted to slashes (or
  1010.   whatever the system uses for subdirectory indications), in which case the
  1011.   subunits of a parent unit are gathered in a subdirectory of that name. This
  1012.   in fact may be a useful enough option to build into the compiler in some
  1013.   more direct form (e.g. if you can't find a-b.ada, then automatically go
  1014.   look for a/b.ada).
  1015.  
  1016.   The second approach, again activated by a command line switch or environment
  1017.   variable, a separate file can be constructed that provides mapping of unit
  1018.   names to file names. This mapping file is then consulted to determine the
  1019.   file name, given the unit name.
  1020.  
  1021. The second issue is how to find the source file, once the source file name
  1022. has been determined. In GNAT this is done using a search path which specifies
  1023. a list of directories to be checked in sequence to find the source file. This
  1024. is analogous to the method that some C compilers use to locate header files.
  1025.  
  1026. Advantages of the GNAT Model
  1027. ----------------------------
  1028.  
  1029. In addition to the advantages that have already been discussed, there are
  1030. two other respects in which the GNAT model is superior to the conventional
  1031. Ada library model.
  1032.  
  1033. First, all source files are simply normal system files, they can be copied
  1034. around, deleted or organized using normal system utilities. In the case of
  1035. a conventional library based system, the library is often an Ada-specific
  1036. object that has to be manipulated with special Ada-specific tools. For
  1037. instance, to delete a unit that is no longer needed in the GNAT system,
  1038. simply use the system delete command on its source and object files, but
  1039. in most Ada systems, a special library-delete command must be used.
  1040.  
  1041. Similarly, the effect of multiple libraries can be achieved simply by having
  1042. multiple directories of source files that are searched in an appropriate order.
  1043. The conventional Ada library system often requires complex, non-portable,
  1044. special features to support multiple libraries.
  1045.  
  1046. Second, many of the anomalies that arise from special cases in the Ada
  1047. library model are avoided. For example, suppose that there are two source
  1048. files that both contain the spec of a procedure Util. In a conventional
  1049. system, whichever source is compiled later "wins" without notification of
  1050. any kind, which means that the semantics of the program can silently depend
  1051. on the order of compilation. This can't happen in the normal use of GNAT,
  1052. since two files with the same unit have to have the same file name, and
  1053. can't accidentally coexist in the same directory.
  1054.  
  1055. Similarly, in a system that permits multiple units in the same file, various
  1056. anomalies arise as a result of other files which recompile some, but not all
  1057. of these units. You then get a program which does not correspond to any set
  1058. of coherent sources. That can never happen in GNAT. Every executable program
  1059. must correspond to a particular set of source files, and could be recreated
  1060. by compiling these source files without knowledge of the original order of
  1061. compilation.
  1062.  
  1063.  
  1064. Support of ASIS-Like Interfaces
  1065. -------------------------------
  1066.  
  1067. Specifications like ASIS provide an interface from Ada programs to information
  1068. stored in the Ada program library, and at least from a presentational point
  1069. of view seem to depend strongly on the notion of a program library which
  1070. contains all the necessary information.
  1071.  
  1072. The GNAT implementation of such an interface understands the library in this
  1073. case to be the set of source files used to compile the program. To access the
  1074. information in this "library" at the required semantic level, the source files
  1075. must be recompiled. Again, this may or may not be more efficient than reading
  1076. in the necessary information from the precompiled library file, but it's
  1077. certainly functionally and semantically equivalent.
  1078.  
  1079.  
  1080. But It Doesn't Sounds Like Ada to Me
  1081. ------------------------------------
  1082.  
  1083. We believe that the Ada/83 reference manual can be read in a sufficiently
  1084. flexible and abstract manner that nothing we are doing in the above approach
  1085. in any sense violates the requirements of Ada. Basically we consider that the
  1086. rules in the Ada/83 RM are essentially oriented to ensuring consistency in
  1087. an Ada program, and that a lot of the description in chapter 10 of the RM is
  1088. essentially the description of one possible approach to achieving this end.
  1089. Furthermore, the Ada/9X reference manual will be written in a way that tries
  1090. hard to avoid over-specification of the implementation approach.
  1091.  
  1092. Nevertheless, most, in fact essentially all, existing Ada compilers have
  1093. implemented the model in chapter 10 quite literally, and as a result, Ada
  1094. programmers have come to expect a model of the world in which the monolithic
  1095. library is the center of the Ada universe. Furthermore, some of our rules in
  1096. GNAT, in particular the rule about mapping of unit names to file names, and
  1097. the rule about only one compilation unit per source file, may seem to be
  1098. unacceptable restrictions.
  1099.  
  1100. However, GNAT is sufficiently flexible that in fact we think any particular
  1101. approach to Ada library maintenance, including the various multi-library
  1102. features provided by various vendors, can be faithfully copied from a
  1103. functional point of view by adopting appropriate procedures.
  1104.  
  1105. In particular, how would one model a conventional library system in which
  1106. source files can contain multiple compilation units and have no naming
  1107. restrictions. Here is one approach.
  1108.  
  1109.    Create a directory called Adalib, which will represent the library. In
  1110.    this directory we will place source files that meet the GNAT requirements
  1111.    and their corresponding object files.
  1112.  
  1113.    To compile an arbitrary Ada source file, first syntax check it. This can
  1114.    be done using GNAT, because in syntax check only, the restrictions on
  1115.    one unit per file, and on the names of the units, are ignored.
  1116.  
  1117.    If there are syntax errors, forget it (GNAT sets a return code indicating
  1118.    that syntax errors were found, so this is easy to implement in a shell
  1119.    script or batch file).
  1120.  
  1121.    Otherwise, run it through a utility which breaks it up in to separate
  1122.    source files with GNAT naming conventions. Put these source files in
  1123.    a temporary directory. Compile these source files with GNAT, but don't
  1124.    generate code yet. Instead just do syntax and semantic checking. (Note
  1125.    that the only required action of an Ada compiler at compile time is to
  1126.    generate error messages and not update the library if there are errors).
  1127.  
  1128.    If there are no syntax or semantic errors in any of the units, then copy
  1129.    the sources to the library directory.
  1130.  
  1131.    When the program is to be bound, first do the actual compilation of all
  1132.    the units (which we know will work because we did a syntax and semantics
  1133.    check already). Then bind the resulting objects and we are done.
  1134.  
  1135. Note that Ada does not specify the division of labor between the compiler
  1136. and binder, except to either require or strongly imply that syntax and
  1137. semantic errors should be caught at the compiler level. Thus the fact that
  1138. we are doing the actual code generation at what is logically bind time in
  1139. the above scheme is perfectly permissible (it just seems to a user that the
  1140. compilations are very quick and the binder somewhat slow!)
  1141.  
  1142. This entire procedure can be implemented by appropriate shell scripts or
  1143. batch files. We generally don't think that many people using GNAT will take
  1144. this approach. In particular it succeeds in faithfully reintroducing some
  1145. of the anomalies and limitations that we have worked to eliminate. However,
  1146. it may be useful for dealing with existing Ada source code, and in particular
  1147. the ACVC suite takes various liberties in its assumptions about chapter 10
  1148. implications. For example, it assumes that a source file can contain more than
  1149. one compilation unit. Thus this kind of mode will be helpful for running the
  1150. ACVC suite.
  1151.  
  1152. Of course this is just one possible scenario. Many others are possible. Since
  1153. the fundamental capabilities of the GNAT compiler are free of many restrictions
  1154. normally associated with Ada compilers, there is a lot of freedom in how such
  1155. scenarios might be constructed.
  1156.  
  1157.  
  1158. What do we Lose?
  1159. ----------------
  1160.  
  1161. We do lose one feature that some may consider important. It is impossible with
  1162. the GNAT approach to distribute a package for someone to use without at least
  1163. giving them the source of the package specification. There is no way to
  1164. distribute black-box libraries with this system that contain hidden
  1165. information. Clearly one can imagine proprietary software situations in
  1166. which this would seem like a restriction, but in the GCC world where we
  1167. are committed to the free distribution of sources, this seems like an
  1168. advantage.
  1169.  
  1170. Similarly, it's hard to make proprietary tools that read information from
  1171. our "library", since you have to use the compiler to read the library, because
  1172. the library has to be created by recompiling the source. That means that your
  1173. proprietary tool would have to include the GNAT compiler, and you can't do that
  1174. since the licensing of the GNAT source, while very liberal, has one important
  1175. restriction, namely that you can't incorporate it in proprietary products.
  1176. Again this "restriction" seems like an advantage to us, given our commitment
  1177. to maintaining full access to the sources of GNAT and related tools.
  1178.  
  1179. Summary
  1180. -------
  1181.  
  1182. Although somewhat radical by conventional Ada standards, we think that a good
  1183. case can be made that the GNAT approach is clearly superior. Certainly it meets
  1184. the important goals of being consistent with the Ada standard, and being far
  1185. less unfamiliar to non-Ada programmers. We also think it's much easier to
  1186. understand than the conventional library based model. 
  1187.