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