Portable [Mindy] Hacking

This is a brief guide to maintaining the portability we strove to put
in Mindy.


Portable Makefiles

In general, there are only three things you can count on every ``make''
program supporting: rules, variables, and comments.  (A sample rule is

	mindy: $(OBJS) list.h
		$(CC) ...

)  That's it.  VPATH, subst, implicit rules (.o.c), and anything else
that seems even remotely useful, are unportable.

Good makefiles give a variable to every command name.  $(CC), for
instance, holds the name of the C compiler.  This way, users can
override our choice of C compilers.  Always use these variables when
writing rules, and if a variable doesn't exist for the command you
want, create a new variable.

As a corollary to that, never rely on the #! mechanism.  If you use
#!, the user can't override your choice of commands.

The Mindy Makefile.in's contain, in this order: boilerplate, variable
definitions, hand-written rules, and machine-generated rules.  In the
variable definitions, $(VAR) and ${VAR} denote ``make'' variables (in
fact, the same make variable), while @VAR@ denotes an Autoconf
variable that will be expanded when the configure script is run.
Handwritten rules are rules that a person wrote.  The remaining rules
(if any) are generated by machine.  I have in the appropriate
Makefile.in's the following comment:

	# It's easiest to generate the rest by machine.
	# Try gcc -MM -E *.c | perl ../etc/generate-depends

This will generate the machine generated rules for C programs.  If you
ever modify the dependencies of a .c file (read: add, delete or change
the #include's), make sure to run the above sequence and paste in an
updated version of the machine generated rules.

When you are writing rules, put them above the machine generated rules
so that people can easily cut and paste in the machine generated
rules. Also, make sure to precede all references to source files with
``$(SRCDIR)/''.  This way the user can run make in a directory other
than the source directory.  For instance, here is a well written rule:

	${SRCDIR}/parser.tab.c: ${SRCDIR}/parser.y
		${YACC} -d ${SRCDIR}/parser.y
		mv y.tab.c ${SRCDIR}/parser.tab.c
		mv y.tab.h ${SRCDIR}/parser.tab.h

All source files are preceded by ${SRCDIR}, and ${YACC} is given its
own variable.  (mv is standard enough you don't need to give it a
variable)

This example demonstrates another trick: Rules for files that we will
distribute compiled.  parser.tab.c is a portable C file that is
generated by $(YACC).  After we build it, we move it into the source
directory.  In this case, $(YACC) generates *two* files, y.tab.c and
y.tab.h.  We move them *both* into the source directory, and rename
them at the same time.  When you are writing rules like this, you
*must* rename the file when you move it.  (The reason for this is that
mv refuses to move a file onto itself.  If $(SRCDIR) was ``.'', and one
were to generate a file ``foo.c'' followed by 
``mv foo.c $(SRCDIR)/foo.c'', the build would fail )

Had the above example not been for a file we will distribute
pre-compiled, the first line would have been
	parser.tab.c:           ${SRCDIR}/parser.y
rather than
	${SRCDIR}/parser.tab.c: ${SRCDIR}/parser.y


Portable C Programs

The configure script figures out what is needed to make the current
computer look like a POSIX compliant Unix box.  The files in the
compat subdirectory implements this illusion.  To maintain this
illusion, C files simply #include "../compat/std-c.h" for standard C
functions, and #include "../compat/std-os.h" for standard operating
systems.  Pretty much all C files need to #include std-c.h, but only
some files will need std-os.h.  You should never #include something
between < > braces; if you want something like that and it isn't
already in the compate directory, create a new file in the compat
subdirectory.  As much as possible, #ifdef statements should be
limited to the compat subdirectory.

C-isms to avoid:
   - Zero length arrays (A GNU CC extension; not legal ANSI C)
   - select() only works on network sockets under Windows/NT, but not
     with pipes or files.  Use select() accordingly.
   - Statements that assume integers and void pointers are the same
     size (if you need an integer version of a pointer, use a long int)
   - Labels that are immediately followed by a closing brace.  (Legal,
     but breaks the SGI compiler.  Stick a dummy statement after the
     label) 
   - Passing va_list's as parameters (legal C, but brokenly
     implemented on the Alpha)
