comparison lib-src/ellcc.c @ 428:3ecd8885ac67 r21-2-22

Import from CVS: tag r21-2-22
author cvs
date Mon, 13 Aug 2007 11:28:15 +0200
parents
children 84b14dcb0985
comparison
equal deleted inserted replaced
427:0a0253eac470 428:3ecd8885ac67
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