(ref.doc)grahamd 051093

Next meyer 191193 Prev: bs 021093 Up: Usenet

From: [email protected] (Graham Dumpleton)
Newsgroups: comp.lang.c++
Subject: Re: Automatic dependency checking & alternatives to make
Date: 5 Oct 1993 10:32:47 GMT
Organization: Technical Development Group, Telstra International
Distribution: world
NNTP-Posting-Host: zeus.research.otc.com.au

In article <[email protected]>, [email protected] (Fergus James HENDERSON) writes:
 > >You missed my point.  Give *me* a few days with good old make and I will
 > >*then* also be able to say "Here are are my source files: A, B, C, ... Z.
 > >Give me my program."
 > 
 > However, it is a lot harder with `good old make' than with say GNU Make.
 > And it's harder with GNU Make than with cake.
 > 
 > If it really is so easy with make that it takes only a few days work,
 > why is it that >95% of people using make still don't generate their
 > dependencies for C files completely automatically?
 > If it was only going to take a few days to solve this problem, why hasn't
 > someone solved it and made their solution available for others to use?
 > The answer is that the so-called `good old make' makes it very difficult
 > to solve problems like these and even harder to solve them in such a way
 > that the solutions can easily be reused by other people for different
 > projects.  

You might want to look at "makeit". "makeit" is available and supplied with
OSE although it can be installed indepently of the C++ libraries. "makeit"
is a set of predefined makefiles for GNU make. With "makeit", in the
majority of cases the only thing you have to do is list the names of the
programs which should be created. For example, if you had two program code
files called A.cc and B.cc, and source code files C.cc to Z.cc. All that
the makefile need contain is:

  MODULES := cc

  include makeit/init.mk

  PROGRAMS := A B

  include makeit/modules.mk

As this is a standard makefile, there is no running programs on description
files to create a makefile and also generate dependencies. Once you have
created the makefile you can go into that directory and type:

  makeit

and it will build everything. You can also be explicit and tell it to build
only certain things, for example:

  makeit all		- same as having no arguments
  makeit programs	- build the library and the programs
  makeit lib		- build just the library
  makeit A		- build program "A"
  makeit A.o		- build object file for program "A" but don't link it
  makeit C.o		- build ordinary object file from C.cc

There are a number of other standard targets included also, some of these
are:

  makeit clean		- remove everything that could be build
  makeit depend		- create a file of dependencies

Other special features/targets are included when you say that you want
special modules to be included. Optional features include a testing
environment, combining of libraries, forming template closure on C++
libraries, generating template repositories from C++ libraries ...

Anyway, if you want more information then pick up the documentation for
"makeit" (approx 100 pages). The description of the whole OSE package and
more information on "makeit" is included below.




WHAT:

  Telstra International is making available publicly, the OTC Software
  Environment (OSE). OSE has been developed over the past 2-3 years at
  Telstra International, previously known as OTC, with the next major
  release being version 3.0. The version currently available is 3.0b10.
  This version is expected to be the last BETA version before version 3.0.

  The package consists of three C++ class libraries, a build environment
  based on GNU make, programs for documentation extraction and other
  miscellaneous tools.

  The C++ libraries use templates compatable with AT&T C++ 3.0. If you have
  a C++ compiler which does not support templates then a template
  preprocessor which is supplied can be used. C++ compilers which have
  been successfully used in the past include:

    AT&T C++ 2.1
    AT&T C++ 3.0.1
    Borland C++ 3.0 (OTCLIB only)
    HP C++ 3.0
    ObjectCenter 1.1
    ObjectStore C++ 2.0
    Sun C++ 2.1
    Sun C++ 3.0.1
    IBM XL C++ 1.1

  Platforms which OSE has been ported to previously include:

    HP 700 - HPUX 8.0
    Sun 4 - Solaris 2.X
    Sun 4 - SunOS 4.1.X
    Pyramid - OS5.1 (UCB/ATT)
    SCO UNIX
    Tandem
    IBM AIX

  Support is also included within the make environment and libraries
  for some third party products. These include:

    Purify/Quantify/PureLink
    ObjectStore OODBMS


WHERE:

  OSE version 3.0b10 can be obtained via anonymous ftp from:

    Australia:

      csis.dit.csiro.au [192.41.146.1]
      directory pub/otc

    Europe:

      ftp.th-darmstadt.de [130.83.55.75]
      directory pub/programming/languages/C++/class-libraries/OSE

  In the directory you will find the following files:

    ose-3.0b10.tar.Z	OSE 3.0b10 src code.

    ose-doc-1.0.tar.Z	OSE 1.0 documentation.

    ose-doc-3.0b.tar.Z	OSE 3.0 (DRAFT) documentation.

  Please note that it may take a couple of days for the European site to
  get updated.


REGISTRATION:

  If you decide to use this software, please let us now who you are, what
  C++ compiler you use, and on which platforms you install OSE, by sending
  email to:

    [email protected]

  This information will be used if we need to contact someone with access
  to a specific C++ compiler or platform, to which we do not have ready
  access, in order to resolve any problems we may have with support for
  that compiler or platform.
 
  When you register, also indicate which version of OSE you have collected,
  as we will then be able to notify you of fixes to any known problems which
  may already exist for that version.

  In addition to registration, the above address can also be used to report
  problems, bugs, suggestions and to send general comments.

  We ask that if you make extensions or modifications to this source
  release, please make these extensions available to others, by sending
  details of the changes you have made or a copy of the modified sources to
  the above email address. This will help us evaluate your extensions for
  inclusion in a future version. It also ensures your investment in these
  extensions when new versions of the software are released.

  A mailing list has been set up. If you would like to be placed on the
  mailing list then let us know by sending email to the above address. The
  address of the mailing list is:

    [email protected]

  The mailing list is used to distribute announcements of new versions when
  they are available, and for current versions, descriptions of known
  problems and how to fix them. It is recommended that you subscribe to the
  mailing list, as ftp sites will not always be updated quickly, therefore
  the mailing list will be the only mechanism for getting fixes quickly.

  We ask you not to redistribute this software in source or binary form
  without our permission. Although, if you use the software in a product,
  you may distribute copies of the shared libraries.
  
  The only recognised ftp sites for OSE are those listed above. Please do
  not place OSE on your own or other ftp sites as we will not be able to
  ensure that other sites have up to date copies of OSE.


COPYRIGHT:

  TELSTRA CORPORATION LIMITED ACN 051 775 556 - 1993

  This software is copyright.

  Any person supplied this software by Telstra Corporation Limited may make
  such use of it including copying and modification as that person desires
  providing the copyright notice above appears on all copies and
  modifications including supporting documentation AND PROVIDED ALWAYS that
  Telstra Corporation Limited prohibits the redistribution or sale of
  this software in source or binary form without its express permission.
  (Contact Commercial Manager Tel: +61 2 287 3140)

  The only conditions and warranties which are binding on Telstra
  Corporation Limited in respect of the state, quality, condition or
  operation of this software are those imposed and required to be binding
  by statute (including the Trade Practices Act 1974) and to the extent
  permitted thereby the liability, if any, of Telstra Corporation Limited
  arising from the breach of such conditions or warranties shall be limited
  to and completely discharged by the replacement of this software and
  otherwise all other conditions and warranties whether express or implied
  by law in respect of the state, quality, condition or operation of this
  software which may apart from this paragraph be binding on Telstra
  Corporation Limited are hereby expressly excluded and negatived.

  Except to the extent provided in the paragraph immediately above Telstra
  Corporation Limited shall have no liability (including liability in
  negligence) to any person for any loss or damage consequential or
  otherwise howsoever suffered or incurred by any such person in relation
  to the software and without limiting the generality thereof in particular
  any loss or damage consequential or otherwise howsoever suffered or
  incurred by any such person caused by or resulting directly or indirectly
  from any failure, breakdown, defect or deficiency of whatsoever nature or
  kind of or in the software.


PREREQUISITES:

  To compile the OSE C++ libraries requires a C++ compiler compatible with
  version 2.1 or 3.0 of the AT&T compiler. Compilers which we know have
  been successfully used to compile the library include:

    AT&T C++ 2.1
    AT&T C++ 3.0.1
    Borland C++ 3.0 (OTCLIB only)
    HP C++ 3.0
    ObjectCenter 1.1
    ObjectStore C++ 2.0
    Sun C++ 2.1
    Sun C++ 3.0.1
    IBM XL C++ 1.1

  Since the library uses templates, a modified version of the COOL template
  preprocessor from Texas Instruments is provided for use with version 2.1
  compilers. The main differences between the template preprocessor supplied
  and the original COOL preprocessor are:

    Supports ARM syntax for template classes and member functions.

    Supports initialisers for static members of a template class.

  The mechanism used to force templates to be expanded has also been
  updated to make it is easier to use. New features have also been added to
  allow overriding of template classes by users and for precompiling of
  templates into libraries. The end result of all the changes is that it is
  possible to write code which can be compiled with either a 2.1 or 3.0
  compiler. Note though that in order to use the template preprocessor with
  a 2.1 compiler, the compiler must provide a way of specifying an
  alternate preprocessor to use.

  Due to bugs or unimplemented features some C++ compilers cannot be used
  to compile the library. The list of compilers, which we know cannot be
  used, are:

    DEC C++ 1.0
    GNU C++ 2.0.0 -> 2.4.5

  It is believed that DEC C++ 1.2 fixes the problem which prevented their
  version 1.0 compiler from being used; we don't know if their 1.1 compiler
  included the same fix. The problems with the GNU compiler have been
  reported but as of version 2.4.5 it was still not capable of compiling
  the library.


C++ LIBRARIES:

  The three C++ class libraries which form the core of the package are
  OTCLIB, OUXLIB and OTKLIB. The main classes in these libraries are:


    Debugging and Error Reporting Classes

      OTC_Logger

      OTC_LogStream

      OTC_Terminate

      OTC_Tracer

      OTC_TraceSwitch

	  ^-- OTC_TraceTag
	  ^
	  ^-- OTC_TracePattern

    Memory Management Classes

      OTC_Arena

      OTC_CommonPool

      OTC_Heap

      OTC_Pool

    Resource Management Classes

      OTC_Resource

      OTC_CtrPtr<T>

      OTC_CtrVecPtr<T>

      OTC_ResPtr<T>

    Collection Helper Classes

      OTC_HashActions<T>

      OTC_RankActions<T>

    Simple Collection Classes

      OTC_BoundedQueue<T>

      OTC_BoundedStack<T>

      OTC_PriorityQueue<T>

	  ^-- OTC_AscendingQueue<T>
	  ^
	  ^-- OTC_DescendingQueue<T>

      OTC_Queue<T>

      OTC_SimpleList<T>

      OTC_Stack<T>

      OTC_Vector<T>

    Resource Managed Collection Classes

      OTC_BaseActions<T>

      OTC_Collection<T>

	  ^-- OTC_Bag<T>
	  ^
	  ^-- OTC_Deque<T>
	  ^
	  ^-- OTC_List<T>
	  ^
	  ^-- OTC_Map<T1,T2>
	  ^
	  ^-- OTC_OrderList<T>
	  ^
	  ^-- OTC_Set<T>
	  ^
	  ^-- OTC_UniqMap<T1,T2>

      OTC_Iterator<T>

      OTC_Modifier<T>

    Text Manipulation Classes

      OTC_Buffer

      OTC_Pattern

	  ^-- OTC_Globex
	  ^
	  ^-- OTC_Regex
	  ^
	  ^-- OTC_Regexp

      OTC_Record

      OTC_String

      OTC_Symbol

      OTC_SymbolTable

	  ^-- OTC_InMemSymbolTable

    Program Options Classes

      OTC_Options

      OTC_Program

	  ^-- OUX_Program

    Filesystem Classes

      OTC_Pathname

	  ^-- OUX_Pathname

      OTC_Directory

	  ^-- OUX_Directory

      OTC_Stat

	  ^-- OUX_Stat

    UNIX Specific Classes

      OUX_User

      OUX_Group

      OUX_SignalBlock

    Construction Classes

      OTC_AVLNode

	  ^-- OTC_AVLLinkNode

      OTC_AVLTree

      OTC_Cursor<T>

	  ^-- OTC_BucketCursor<T>
	  ^
	  ^-- OTC_RangeCursor

      OTC_Linkable

	  ^-- OTC_Anchor
	  ^
	  ^-- OTC_Link

		  ^-- OTC_Bucket<T>
		  ^
		  ^-- OTC_Holder<T>

      OTC_LinkIterator

      OTC_LinkList

      OTC_Range

    Event Driven Systems Classes

      OTC_Agent

      OTC_Dispatcher

	  ^-- OUX_Dispatcher
	  ^
	  ^-- OTK_Dispatcher

      OTC_Event

	  ^-- OTCEV_Alarm
	  ^
	  ^-- OTCEV_IOEvent
	  ^
	  ^-- OTCEV_Timeout
	  ^
	  ^-- OUXEV_Signal

      OTC_Job

	  ^-- OTC_EventJob

      OTC_JobQueue

	  ^-- OUX_JobQueue
	  ^
	  ^-- OTK_JobQueue

    ObjectStore Classes

      OTC_TypeSpec

  Some of the major features of the library which distinguish this library
  from others, include the fact that all major collection classes in the
  libraries are templated. In addition, all collection classes may hold
  either objects or pointers to objects. In other words you can write
  OTC_List<int> or OTC_List<int*>. For the main collection class hierarchy,
  ie., those derived from OTC_Collection, support is provided so that the
  user can dictate special actions to be carried out when an item is added
  or removed from a collection. This is important in the case of a
  collection holding pointers to objects and provides a means to ensure
  that objects are not deleted before they should be; conversely that
  they are deleted, when no longer required.

  An example illustrating this scheme makes use of the class OTC_Resource,
  which allows an object to know how many times it is referenced and
  deletes itself, when no longer referenced.

    class EX_Object : public virtual OTC_Resource
    {
      public:

	EX_Object(int theId)
	 : myId(theId) {}

	int id() const
	 { return myId; }

      private:

	int myId;
    };

    class OTC_BaseActions<EX_Object*>
    {
      public:

	static EX_Object* add(EX_Object* theObject)
	 { theObject->reference(); return theObject; }

	static void removed(EX_Object* theObject)
	 { theObject->unReference(); }
    };

    main()
    {
      OTC_List<EX_Object*> list;

      for (int i=0; i<10; i++)
      {
	EX_Object* anObject;
	anObject = new EX_Object(i);
	list.addLast(anObject);
      }
    }

  In normal situations, because the collection class can't know that it was
  holding a pointer or an object, it wouldn't be able to invoke delete on an
  item held by the collection, when the collection is destroyed. There is
  also the problem of whether it should delete it anyway as something else
  could still be using it.

  Reference counting is one solution to this and the OTC_BaseActions class
  allows one to provide special knowledge to a collection class, about what
  it should do with an item of a particular type. Although the OTC_Resource
  class is used here, you could use your own scheme for keeping a count of
  the number of references to an object.

  Note that it is only necessary to provide an explicit version of
  OTC_BaseActions when you want to. If an explicit version for a type isn't
  provided, then the default template version of OTC_BaseActions is used,
  which does nothing. For example, you do not need to do anything special
  to use OTC_List<int>.

  A similar scheme to OTC_BaseActions is used to provide information about
  how to generate a hash value for an item, or how to compare two items of
  the same type to a collection class. The classes controlling this are
  OTC_HashActions and OTC_RankActions. the default implementation of these
  classes will attempt to convert an item into an int to generate a hash
  value, or in order to compare items. As above, the default template
  classes can be overriden for a particular type. Using EX_Object again we
  can write:

    class OTC_RankActions<EX_Object*>
    {
      public:

	static int rank(EX_Object* item1, EX_Object* item2)
	 { return OTC_RankActions<int>::rank(item1->id(),item2->id()); }
    };

    main()
    {
      OTC_OrderedList<EX_Object*> list;

      for (int i=0; i<10; i+=2)
      {
	EX_Object* anObject;
	anObject = new EX_Object(i);
	list.add(anObject);
      }

      for (int j=1; j<10; j+=2)
      {
	EX_Object* anObject;
	anObject = new EX_Object(j);
	list.add(anObject);
      }

      OTC_Iterator<EX_Object*> iter;
      iter = list.items();
      for (iter.reset(); iter.isValid(); iter.next())
	cout << iter.item()->id() << endl;
    }

  and have it write out the ID of each object in order from 0 to 9 rather
  than in the order added to the list.

  This scheme means that in the case of pointers it is possible for the
  ranking of objects in an ordered collection to be based on some part of
  the object being pointed at, instead of the value of the actual pointer.
  Similarly, this applies to collections based on hashing, ie., the hash
  value and comparision of objects can be based on the object pointed at,
  instead of the address of the object in memory. Note though, that the use
  of these classes is not restricted to pointers. If you need to hold
  actual objects of type EX_Object in a collection you could just as easily
  write:

    class OTC_RankActions<EX_Object>
    {
      public:

	static int rank(EX_Object const& item1, EX_Object const& item2)
	 { return OTC_RankActions<int>::rank(item1.id(),item2.id()); }
    };

  As shown in the above example, the library also provides the ability to
  obtain an iterator for any collection, or more precisely any collection
  derived from OTC_Collection. From the user's perspective these iterators
  are independent of the type of collection they are iterating over. Once
  obtained, the iterators do not have to be maintained in the same scope as
  the collection they came from, but may be passed between functions and may
  even be passed back up to an enclosing scope. This is partially possible
  due to the iterator state being maintained, independently of the actual
  iterator object, with the state being reference counted by that object.
  This ensures that the actual iterator state is only deleted when
  absolutely no longer required. It also means that the user never has to
  worry about deleting an iterator explicitly.

  The iterator class is only available for collection classes derived from
  OTC_Collection as it uses the OTC_BaseActions class to ensure integrity
  in the case of collections holding pointers. In particular if we write:

    OTC_List<EX_Object*> list;
    EX_Object* anObject = new EX_Object(1);
    list.add(anObject);

    OTC_Iterator<EX_Object*> iter;
    iter = list.items();

    iter.reset();
    list.removeAll();
    cout << iter.item()->id() << endl;

  what would usually happen is that the program would die due to the
  iterator state being corrupted after the items had been removed from the
  list, or the iterator would still be okay but the object being pointed at
  would have been deleted resulting in an access to invalid memory.

  Provided that OTC_BaseActions is defined appropriately for a pointer type
  the integrity of iterators is always ensured. In particular an item held
  by a collection will not be removed while an iterator is located on it.
  An item and the bucket containing it are only removed from a collection
  when nothing is interested in. It is only at this time that the remove()
  function provided by an OTC_BaseActions class is applied to an object.
  This scheme means that iterators will never be disassociated from the
  collection it was created from and also means that it is safe to have
  multiple iterators on one collection.


BUILD ENVIRONMENT:

  The build environment, known as "makeit", is essentially a collection of
  predefined makefiles for GNU make. The build environment has been
  structured to remove as much as possible from developers the task of
  writing makefiles. This is achieved through use of the GNU make "include"
  directive and controlling the actions of the build environment through
  variable settings.

  A simple example of a makefile for makeit is:

    MODULES := cc ose

    include makeit/init.mk

    PROGRAMS := foobar

    include makeit/modules.mk

  This simple makefile results in the inclusion of support for C++ code
  with a ".cc" extension and will also result in the code in that directory
  being compiled against, and linked with the OSE C++ class libraries. More
  specifically, when makeit is run, all C++ code with an extension of ".cc"
  in that directory, except for the program file "foobar.cc" will be
  compiled and placed into a library. The program file "foobar.cc", when
  compiled, will be linked against the library created from code in this
  directory, as well as the OSE C++ class libraries.

  Although some variables need to be set, such as PROGRAMS, to control how
  targets are built, the majority of what to do is calculated by makeit
  looking at what is in a directory in conjunction with which modules you
  have enabled. As can be expected makeit, is not all seeing and so it is
  necessary to following file naming conventions. Beyond that, there are few
  requirements upon the developer in the majority of cases.

  As well as support for C++ code, compilation of programs, generation of
  libraries and linking of programs against the OSE C++ class libraries,
  there is also support in the form of modules or options to makeit for:

    C, Yacc, Lex, Rpcgen

    Creation of Shell Scripts

    Dependency Generation

    Purify/Quantify Memory Analysis Tool

    PureLink Incremental Linker

    Building Shared Libraries

    Combining of Libraries

    Forming Closure on Libraries

    Template Repositories

    Program/Library Installation

    Unit Testing

    ObjectStore Database Schema Generation

  By replacing the top level modules file with a customised version it is
  possible for users to extend the set of modules available by
  supplementing those available with others of their own creation. The user
  still selects those required through the MODULES variable though, meaning
  that any ordering constraints on the inclusion of predefined makefiles
  can be hidden.

  As well as the features listed above, makeit embodies two other
  significant concepts. These are:

    Variants or Compilation Environments

    Selection of C++ Compiler

  The first of these, allows the developer to hold the results of multiple
  compilations, using different compiler options, in the same directory.
  This is useful, as it allows inclusion of debugger support as the default
  while developing code, but then allows you to build an optimised version
  for release, without having to clean out the debugger version beforehand.

  In makeit, there are four default variants. These are:

    dbg - includes debugger support
    opt - invokes the optimiser
    prf - includes profiler support
    std - no special options

  A particular variant is selected by including in the make file

    VARIANT := opt

  or by defining VARIANT when makeit is invoked.

    makeit all VARIANT=opt

  If no variant is defined in the makefile, or on the command line, then the
  default is used which is generally "dbg".

  The compilation results for different variants are placed into separate
  subdirectories to keep them apart. The appropriate compiler options
  required for that variant are automatically provided by makeit, eg., -O
  to enable the optimiser.

  The second of the features highlighted is the ability to select a
  particular C++ compiler to use. This is provided, as makeit supports a
  site having a number of different C++ compilers available under different
  names and locations. Since not all C++ compilers are the same, it is quite
  often the case that different users have to use different compilers,
  depending on what they are doing. So that users do not have to know where
  the compilers are located, or what options are required to be used for a
  compiler while working in a particular variant on a particular operating
  system, the information is included in the makeit configuration when
  installed. The user then needs only define in the makefile which
  compiler is wanted by defining the C++COMPILER variable. For example:

    C++COMPILER := SUN3.0.1

  In this case the compiler selected would be Sun C++ 3.0.1.

  Because makeit supports multiple C++ compilers it is possible to install
  the OSE C++ class libraries for multiple compilers also. When linking
  with the libraries, the correct version of the libraries for the compiler
  being used will be linked in.

  Note that makeit has to be used to compile the OSE C++ class libraries
  and install OSE. Although it is feasible to use the OSE C++ class
  libraries without using makeit, it is recomended that you do use it
  unless you already have some build environment which you have to use. The
  reason for this recommendation is that not all C++ compilers handle
  template expansion the same and makeit hides all the differences such
  that the user never needs to know how a particular compiler actually
  handles it. There are also a number of bugs or unimplemented features in
  various compilers which makeit silently works around.


DOCUMENTATION TOOLS:

  It is important that documentation for classes in a C++ library be up to
  date with respect to the code it describes. To assist in this task the
  tool "class2man" is provided which is capable of extracting comments from
  C++ class header files and formatting that into online or printed
  documentation. Having the documentation with the actual classes makes
  it easier to update the documentation when changes are made to code.

  At present the documentation generated is in the form of UNIX style
  manual pages. This does not however require the user to know anything
  about using troff man macros though as class2man does all necessary
  conversions of your comments into troff formatting commands, including
  the conversion of generic font and structure directives. As well as
  converting comments class2man also knows about the structure of C++
  classes and is able automatically to associate a comment with the
  function it is mean't to describe as long as the user follows some simple
  formatting rules.

  An example of a class commented in the style accepted by class2man is:

    template<class T>
    class OTC_BaseActions
	// = TITLE
	//     Default actions for an item contained in a bucket.
	//     
	// = DESCRIPTION
	//     This class encapsulates the default actions which are
	//     executed when an item is placed into a bucket and when a
	//     bucket is deleted.
	//     
	//     These may be overridden for a particular type in order to
	//     produce some class specific actions. For example, if <T> is
	//     a pointer to a class derived from <OTC_Resource> the actions
	//     may increment and decrement the reference count.
	//
	// = SEE ALSO
	//     <OTC_Bucket>
    {
      public:

	// = ACTIONS

	static T const&	add(T const& theItem)
				    { return theItem; }
				    // Executed when an item is placed into a
				    // bucket.

	static void	remove(T&)
				    {}
				    // Executed when a bucket is deleted.
    };

  All classes in the OSE C++ class libraries are documented in this way and
  all manual pages for classes in the libraries are generated directly from
  the comments in the header files, using class2man.


MISCELLANEOUS TOOLS:

  In addition to the build environment and documentation tools, a number of
  other useful tools are provided. These include "class2src" which is
  capable of parsing a C++ header file and producing member function stubs
  for use in a C++ code file, and "mksrc", a program to generate new header
  files, code files or make files from prototypes. The files generated by
  mksrc include headers describing the author and copyright holder. Also,
  in the case of header files appropriate guards will be placed around the
  header file to prevent multiple inclusion. Although defaults are provided
  the tool can be extended to allow for new file formats and types.

-- 
Graham Dumpleton ([email protected])



automatically generated by info2www version 1.2.2.8