This commit was generated by cvs2svn to compensate for changes in r1479,
[chise/xemacs-chise.git.1] / lib-src / ellcc.c
1 /* ellcc.c - front-end for compiling Emacs modules
2 Copyright (C) 1998, 1999 J. Kean Johnston.
3
4 This file is part of XEmacs.
5
6 XEmacs is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by the
8 Free Software Foundation; either version 2, or (at your option) any
9 later version.
10
11 XEmacs is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with XEmacs; see the file COPYING.  If not, write to
18 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA.
20
21 Author: J. Kean Johnston (jkj@sco.com).
22 Please mail bugs and suggestions to the XEmacs maintainer.
23 */
24
25 /*
26 Here's the scoop. We would really like this to be a shell script, but
27 the various Windows platforms don't have reliable scripting that suits
28 our needs. We don't want to rely on perl or some other such language
29 so we have to roll our own executable to act as a front-end for the
30 compiler.
31
32 This program is used to invoke the compiler, the linker and to generate
33 the module specific documentation and initialization code.  We assume we
34 are in 'compile' mode unless we encounter an argument which tells us
35 that we're not.  We take all arguments and pass them on directly to the
36 compiler, except for a few which are specific to this program:
37
38   --mode=VALUE      This sets the program mode. VALUE can be one of
39                     compile, link, init or verbose.
40   --mod-name=NAME   Sets the module name to the string NAME.
41   --mod-title=TITLE Sets the module title to the string TITLE.
42   --mod-version=VER Sets the module version to the string VER.
43
44 The idea is that Makefiles will use ellcc as the compiler for making
45 dynamic Emacs modules, and life should be as simple as:
46
47   make CC=ellcc LD='ellcc --mode=link'
48
49 The only additional requirement is an entry in the Makefile to produce
50 the module initialization file, which will usually be something along
51 the lines of:
52
53   modinit.c: $(SRCS)
54              ellcc --mode=init --mod-name=\"$(MODNAME)\" \
55                --mod-title=\"$(MODTITLE)\" --mod-version=\"$(MODVERSION)\" \
56                -o $@ $(SRCS)
57
58 See the samples for more details.
59 */
60
61 #include <config.h>
62 #include <stdio.h>
63 #include <stdlib.h>
64 #include <string.h>
65 #include <ctype.h>
66 #include <errno.h>
67 #include <sys/types.h>
68
69 #ifdef HAVE_UNISTD_H
70 # include <unistd.h>
71 #endif /* HAVE_UNISTD_H */
72
73 #define EMODULES_GATHER_VERSION
74
75 #include <emodules.h>
76 #include <ellcc.h> /* Generated files must be included using <...> */
77
78 #define DEBUG
79
80 #ifndef HAVE_SHLIB
81 int
82 main (int argc, char *argv[])
83 {
84   fprintf (stderr, "Dynamic modules not supported on this platform\n");
85   return EXIT_FAILURE;
86 }
87 #else
88
89 /*
90  * Try to figure out the commands we need to use to create shared objects,
91  * and how to compile for PIC mode.
92  */
93
94 /*
95  *      xnew, xrnew -- allocate, reallocate storage
96  *
97  * SYNOPSIS:    Type *xnew (int n, Type);
98  *              Type *xrnew (OldPointer, int n, Type);
99  */
100 #ifdef chkmalloc
101 # include "chkmalloc.h"
102 # define xnew(n,Type)     ((Type *) trace_malloc (__FILE__, __LINE__, \
103                                                   (n) * sizeof (Type)))
104 # define xrnew(op,n,Type) ((Type *) trace_realloc (__FILE__, __LINE__, \
105                                                    (op), (n) * sizeof (Type)))
106 #else
107 # define xnew(n,Type)     ((Type *) xmalloc ((n) * sizeof (Type)))
108 # define xrnew(op,n,Type) ((Type *) xrealloc ((op), (n) * sizeof (Type)))
109 #endif
110 static void *xmalloc (size_t);
111 static void fatal (char *, char *);
112 static void add_to_argv (const char *);
113 static void do_compile_mode (void);
114 static void do_link_mode (void);
115 static void do_init_mode (void);
116
117 #define SSTR(S) ((S)?(S):"")
118
119 #define ELLCC_COMPILE_MODE      0
120 #define ELLCC_LINK_MODE         1
121 #define ELLCC_INIT_MODE         2
122
123 int ellcc_mode = ELLCC_COMPILE_MODE;
124 char *progname;
125 char *mod_name = (char *)0, *mod_version = (char *)0, *mod_title = (char *)0;
126 char *mod_output = (char *)0;
127 int verbose = 0;
128 char **exec_argv;
129 int exec_argc = 1, *exec_args;
130 int real_argc = 0;
131 int prog_argc;
132 char **prog_argv;
133
134 /*
135  * We allow the user to over-ride things in the environment
136  */
137 char *ellcc, *ellld, *ellcflags, *ellldflags, *ellpicflags, *elldllflags;
138 #define OVERENV(STR,EVAR,DFLT) \
139   STR = getenv(EVAR); \
140   if ((STR) == (char *)0) \
141     STR = DFLT
142
143 int
144 main (int argc, char *argv[])
145 {
146   char *tmp;
147   int i, done_mode = 0;
148
149   prog_argc = argc;
150   prog_argv = argv;
151
152 #if defined(MSDOS) || defined(WINDOWSNT)
153   tmp = strrchr (argv[0], '\\');
154   if (tmp != (char *)0)
155     tmp++;
156 #elif !defined (VMS)
157   tmp = strrchr (argv[0], '/');
158   if (tmp != (char *)0)
159     tmp++;
160 #else
161   tmp = argv[0];
162 #endif
163
164   if (tmp != (char *)0)
165     progname = tmp;
166   else
167     progname = argv[0];
168
169   tmp = &progname[strlen(progname)-2];
170   if (strcmp (tmp, "cc") == 0)
171     ellcc_mode = ELLCC_COMPILE_MODE;
172   else if (strcmp (tmp, "ld") == 0)
173     ellcc_mode = ELLCC_LINK_MODE;
174   else if (strcmp (tmp, "it") == 0)
175     ellcc_mode = ELLCC_INIT_MODE;
176
177   exec_argv = xnew(argc + 20, char *);
178   exec_args = xnew(argc, int);
179   for (i = 0; i < argc; i++)
180     exec_args[i] = -1;
181
182   if (argc < 2)
183     fatal ("too few arguments", (char *)0);
184
185   exec_args[0] = 0;
186
187   for (i = 1; i < argc; i++)
188     {
189       if (strncmp (argv[i], "--mode=", 7) == 0)
190         {
191           char *modeopt = argv[i] + 7;
192
193           if (done_mode && strcmp (modeopt, "verbose"))
194             fatal ("more than one mode specified", (char *) 0);
195           if (strcmp (modeopt, "link") == 0)
196             {
197               done_mode++;
198               ellcc_mode = ELLCC_LINK_MODE;
199             }
200           else if (strcmp (modeopt, "compile") == 0)
201             {
202               done_mode++;
203               ellcc_mode = ELLCC_COMPILE_MODE;
204             }
205           else if (strcmp (modeopt, "init") == 0)
206             {
207               done_mode++;
208               ellcc_mode = ELLCC_INIT_MODE;
209             }
210           else if (strcmp (modeopt, "verbose") == 0)
211             verbose += 1;
212         }
213       else if (strcmp (argv[i], "--mod-location") == 0)
214         {
215           printf ("%s\n", ELLCC_MODDIR);
216           return 0;
217         }
218       else if (strcmp (argv[i], "--mod-site-location") == 0)
219         {
220           printf ("%s\n", ELLCC_SITEMODS);
221           return 0;
222         }
223       else if (strcmp (argv[i], "--mod-archdir") == 0)
224         {
225           printf ("%s\n", ELLCC_ARCHDIR);
226           return 0;
227         }
228       else if (strcmp (argv[i], "--mod-config") == 0)
229         {
230           printf ("%s\n", ELLCC_CONFIG);
231           return 0;
232         }
233       else if (strncmp (argv[i], "--mod-name=", 11) == 0)
234         mod_name = argv[i] + 11;
235       else if (strncmp (argv[i], "--mod-title=", 12) == 0)
236         mod_title = argv[i] + 12;
237       else if (strncmp (argv[i], "--mod-version=", 14) == 0)
238         mod_version = argv[i] + 14;
239       else if (strncmp (argv[i], "--mod-output=", 13) == 0)
240         mod_output = argv[i] + 13;
241       else
242         {
243           exec_args[exec_argc] = i;
244           exec_argc++;
245         }
246     }
247
248   if (ellcc_mode == ELLCC_LINK_MODE && mod_output == (char *)0)
249     fatal ("must specify --mod-output when linking", (char *)0);
250   if (ellcc_mode == ELLCC_INIT_MODE && mod_output == (char *)0)
251     fatal ("must specify --mod-output when creating init file", (char *)0);
252   if (ellcc_mode == ELLCC_INIT_MODE && mod_name == (char *)0)
253     fatal ("must specify --mod-name when creating init file", (char *)0);
254
255   /*
256    * We now have the list of arguments to pass to the compiler or
257    * linker (or to process for doc files). We can do the real work
258    * now.
259    */
260   if (verbose)
261     printf ("ellcc driver version %s for EMODULES version %s (%ld)\n",
262             ELLCC_EMACS_VER, EMODULES_VERSION, EMODULES_REVISION);
263 #ifdef DEBUG
264   if (verbose >= 2)
265     {
266       printf ("              mode = %d (%s)\n", ellcc_mode,
267               ellcc_mode == ELLCC_COMPILE_MODE ? "compile" :
268               ellcc_mode == ELLCC_LINK_MODE ? "link" : "init");
269       printf ("       module_name = \"%s\"\n", SSTR(mod_name));
270       printf ("      module_title = \"%s\"\n", SSTR(mod_title));
271       printf ("    module_version = \"%s\"\n", SSTR(mod_version));
272
273       printf ("                CC = %s\n", ELLCC_CC);
274       printf ("            CFLAGS = %s\n", ELLCC_CFLAGS);
275       printf ("      CC PIC flags = %s\n", ELLCC_DLL_CFLAGS);
276       printf ("                LD = %s\n", ELLCC_DLL_LD);
277       printf ("           LDFLAGS = %s\n", ELLCC_DLL_LDFLAGS);
278       printf ("      architecture = %s\n", ELLCC_CONFIG);
279       printf (" Include directory = %s/include\n", ELLCC_ARCHDIR);
280       printf ("\n");
281     }
282 #endif
283
284   if (exec_argc < 2)
285     fatal ("too few arguments", (char *) 0);
286
287   /*
288    * Get the over-rides from the environment
289    */
290   OVERENV(ellcc, "ELLCC", ELLCC_CC);
291   OVERENV(ellld, "ELLLD", ELLCC_DLL_LD);
292   OVERENV(ellcflags, "ELLCFLAGS", ELLCC_CFLAGS);
293   OVERENV(ellldflags, "ELLLDFLAGS", ELLCC_LDFLAGS);
294   OVERENV(elldllflags, "ELLDLLFLAGS", ELLCC_DLL_LDFLAGS);
295   OVERENV(ellpicflags, "ELLPICFLAGS", ELLCC_DLL_CFLAGS);
296
297   if (ellcc_mode == ELLCC_COMPILE_MODE)
298     do_compile_mode();
299   else if (ellcc_mode == ELLCC_LINK_MODE)
300     do_link_mode();
301   else
302     do_init_mode();
303
304   /*
305    * The arguments to pass on to the desired program have now been set
306    * up and we can run the program.
307    */
308   if (verbose)
309     {
310       for (i = 0; i < real_argc; i++)
311         printf ("%s ", exec_argv[i]);
312       printf ("\n");
313       fflush (stdout);
314     }
315   exec_argv[real_argc] = (char *)0; /* Terminate argument list */
316
317   i = execvp (exec_argv[0], exec_argv);
318   if (verbose)
319     printf ("%s exited with status %d\n", exec_argv[0], i);
320   return i;
321 }
322
323 /* Like malloc but get fatal error if memory is exhausted.  */
324 static void *
325 xmalloc (size_t size)
326 {
327   void *result = malloc (size);
328   if (result == NULL)
329     fatal ("virtual memory exhausted", (char *)0);
330   return result;
331 }
332
333 /* Print error message and exit.  */
334 static void
335 fatal (char *s1, char *s2)
336 {
337   fprintf (stderr, "%s: ", progname);
338   fprintf (stderr, s1, s2);
339   fprintf (stderr, "\n");
340   exit (EXIT_FAILURE);
341 }
342
343 /*
344  * Add a string to the argument vector list that will be passed on down
345  * to the compiler or linker. We need to split individual words into
346  * arguments, taking quoting into account. This can get ugly.
347  */
348 static void
349 add_to_argv (const char *str)
350 {
351   int sm = 0;
352   const char *s = (const char *)0;
353
354   if ((str == (const char *)0) || (str[0] == '\0'))
355     return;
356
357   while (*str)
358     {
359       switch (sm)
360         {
361         case 0: /* Start of case - string leading whitespace */
362           if (isspace ((unsigned char) *str))
363             str++;
364           else
365             {
366               sm = 1; /* Change state to non-whitespace */
367               s = str; /* Mark the start of THIS argument */
368             }
369           break;
370
371         case 1: /* Non-whitespace character. Mark the start */
372           if (isspace ((unsigned char) *str))
373             {
374               /* Reached the end of the argument. Add it. */
375               int l = str-s;
376               exec_argv[real_argc] = xnew (l+2, char);
377               strncpy (exec_argv[real_argc], s, l);
378               exec_argv[real_argc][l] = '\0';
379               real_argc++;
380               sm = 0; /* Back to start state */
381               s = (const char *)0;
382               break;
383             }
384           else if (*str == '\\')
385             {
386               sm = 2; /* Escaped character */
387               str++;
388               break;
389             }
390           else if (*str == '\'')
391             {
392               /* Start of quoted string (single quotes) */
393               sm = 3;
394             }
395           else if (*str == '"')
396             {
397               /* Start of quoted string (double quotes) */
398               sm = 4;
399             }
400           else
401             {
402               /* This was just a normal character. Advance the pointer. */
403               str++;
404             }
405           break;
406
407         case 2: /* Escaped character */
408           str++; /* Preserve the quoted character */
409           sm = 1; /* Go back to gathering state */
410           break;
411
412         case 3: /* Inside single quoted string */
413           if (*str == '\'')
414             sm = 1;
415           str++;
416           break;
417
418         case 4: /* inside double quoted string */
419           if (*str == '"')
420             sm = 1;
421           str++;
422           break;
423         }
424     }
425
426   if (s != (const char *)0)
427     {
428       int l = str-s;
429       exec_argv[real_argc] = xnew (l+2, char);
430       strncpy (exec_argv[real_argc], s, l);
431       exec_argv[real_argc][l] = '\0';
432       real_argc++;
433       s = (const char *)0;
434     }
435 }
436
437 /*
438  * For compile mode, things are pretty straight forward. All we need to do
439  * is build up the argument vector and exec() it. We must just make sure
440  * that we get all of the required arguments in place.
441  */
442 static void
443 do_compile_mode (void)
444 {
445   int i;
446   char ts[4096]; /* Plenty big enough */
447
448   add_to_argv (ellcc);
449   add_to_argv (ellcflags);
450   add_to_argv (ellpicflags);
451   add_to_argv ("-DPIC");
452   add_to_argv ("-DEMACS_MODULE");
453 #ifdef XEMACS
454   add_to_argv ("-DXEMACS_MODULE"); /* Cover both cases */
455   add_to_argv ("-Dxemacs");
456 #endif
457   add_to_argv ("-Demacs");
458   sprintf (ts, "-I%s/include", ELLCC_ARCHDIR);
459   add_to_argv (ts);
460   add_to_argv (ELLCC_CF_ALL);
461   for (i = 1; i < exec_argc; i++)
462     exec_argv[real_argc++] = strdup (prog_argv[exec_args[i]]);
463 }
464
465 /*
466  * For link mode, things are a little bit more complicated. We need to
467  * insert the linker commands first, replace any occurrence of ELLSONAME
468  * with the desired output file name, insert the output arguments, then
469  * all of the provided arguments, then the final post arguments. Once
470  * all of this has been done, the argument vector is ready to run.
471  */
472 static void
473 do_link_mode (void)
474 {
475   int i,x;
476   char *t, ts[4096]; /* Plenty big enough */
477
478   add_to_argv (ellld);
479   add_to_argv (ellldflags);
480   add_to_argv (elldllflags);
481   add_to_argv (ELLCC_DLL_LDO);
482   add_to_argv (mod_output);
483   for (i = 1; i < exec_argc; i++)
484     exec_argv[real_argc++] = strdup (prog_argv[exec_args[i]]);
485   add_to_argv (ELLCC_DLL_POST);
486
487   /*
488    * Now go through each argument and replace ELLSONAME with mod_output.
489    */
490   for (i = 0; i < real_argc; i++)
491     {
492       x = 0;
493       ts[0] = '\0';
494
495       t = exec_argv[i];
496       while (*t)
497         {
498           if (*t == 'E')
499             {
500               if (strncmp (t, "ELLSONAME", 9) == 0)
501                 {
502                   strcat (ts, mod_output);
503                   t += 8;
504                   x += strlen (mod_output);
505                 }
506               else
507                 {
508                   ts[x] = *t;
509                   x++;
510                   ts[x] = '\0';
511                 }
512             }
513           else
514             {
515               ts[x] = *t;
516               x++;
517               ts[x] = '\0';
518             }
519           t++;
520         }
521       free (exec_argv[i]);
522       exec_argv[i] = strdup (ts);
523     }
524 }
525
526 /*
527  * In init mode, things are a bit easier. We assume that the only things
528  * passed on the command line are the names of source files which the
529  * make-doc program will be processing. We prepare the output file with
530  * the header information first, as make-doc will append to the file by
531  * special dispensation.
532  */
533 static void
534 do_init_mode (void)
535 {
536   int i;
537   char ts[4096]; /* Plenty big enough */
538   char *mdocprog;
539   FILE *mout = fopen (mod_output, "w");
540
541   if (mout == (FILE *)0)
542     fatal ("failed to open output file", mod_output);
543   fprintf (mout, "/* DO NOT EDIT - AUTOMATICALLY GENERATED */\n\n");
544   fprintf (mout, "#include <emodules.h>\n\n");
545   fprintf (mout, "const long emodule_compiler = %ld;\n", EMODULES_REVISION);
546   fprintf (mout, "const char *emodule_name = \"%s\";\n", SSTR(mod_name));
547   fprintf (mout, "const char *emodule_version = \"%s\";\n", SSTR(mod_version));
548   fprintf (mout, "const char *emodule_title = \"%s\";\n", SSTR(mod_title));
549   fprintf (mout, "\n\n");
550   fprintf (mout, "void docs_of_%s()\n", SSTR(mod_name));
551   fclose (mout);
552
553   sprintf (ts, "%s/make-docfile", ELLCC_ARCHDIR);
554   OVERENV(mdocprog, "ELLMAKEDOC", ts);
555   add_to_argv (mdocprog);
556   sprintf (ts, "-E %s", mod_output);
557   add_to_argv (ts);
558   for (i = 1; i < exec_argc; i++)
559     exec_argv[real_argc++] = strdup (prog_argv[exec_args[i]]);
560 }
561
562 #endif /* HAVE_SHLIB */
563