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