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