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)