OSDN Git Service

2013.10.24
[uclinux-h8/uClinux-dist.git] / tools / ucfront / ucfront.c
1 /*
2   A front end for gcc which can work with a custom uClibc or glibc library
3
4   Steve Bennett <steveb@snapgear.com>
5
6   Argument processing code based on ccache
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 #include "ucfront.h"
24
25 #define MYNAME "ucfront"
26
27 /* the debug logfile name, if set */
28 char *cache_logfile = NULL;
29
30 /* the argument list after processing */
31 static ARGS *stripped_args;
32
33 /* contains just some basic args needed when running -print-file-name
34  * such as -mbig-endian
35  */
36 static ARGS *basic_args;
37
38 /* the original argument list */
39 static ARGS *orig_args;
40
41 /* Are we in link mode? */
42 static enum {
43         MODE_COMPILE,
44         MODE_LINK,
45         MODE_LINK_SHARED,
46         MODE_DEPEND,
47 } mode = MODE_LINK;
48
49 /* Are we using uClibc or glibc? */
50 static enum {
51         LIBTYPE_NONE,
52         LIBTYPE_GLIBC,
53         LIBTYPE_UCLIBC,
54         LIBTYPE_LIBC,
55 } libtype = LIBTYPE_NONE;
56
57 /* Are we generating a flat executable file, if we are do not ever use
58  * crtbegin.o/crtend.o as the flat handling does contructors for us
59  */
60 static int flat_executable = 0;
61
62 static int pthread = 0;
63
64 static int cplusplus = 0;
65
66 static int ucfront_debug = 0;
67
68 static const char *libpaths[100];
69 static int num_lib_paths = 0;
70
71 static struct {
72         const char *old;
73         const char *new;
74 } map_dirs[100];
75 static int num_map_dirs = 0;
76
77 static char *rootdir;
78 static char *stagedir;
79 static const char *argv0;
80 static char *libc_libdir = 0;
81 static char *libc_incdir = 0;
82
83 #if 0
84 /* Print the given args */
85 static void print_args(FILE *fh, ARGS *args)
86 {
87         int i;
88         for (i = 0; i < args->argc; i++) {
89                 if (i != 0) {
90                         fputc(' ', fh);
91                 }
92                 fputs(args->argv[i], fh);
93         }
94         fputc('\n', fh);
95         fflush(fh);
96 }
97 #endif
98
99 static void log_args(ARGS *args)
100 {
101         int i;
102         for (i = 0; i < args->argc; i++) {
103                 if (i != 0) {
104                         cc_log(" ");
105                 }
106                 cc_log("%s", args->argv[i]);
107         }
108         cc_log("\n");
109 }
110
111
112 /*
113  * Invoke the compiler with the original arguments (minus --ucfront- prefixes)
114 */
115 static void invoke_original_compiler(void)
116 {
117         char *e;
118
119         /* strip any local args */
120         args_strip(orig_args, "--ucfront-");
121
122         if ((e=getenv("UCFRONT_PREFIX"))) {
123                 char *p = find_executable(e, MYNAME);
124                 if (!p) {
125                         perror(e);
126                         exit(1);
127                 }
128                 args_add_prefix(orig_args, p);
129         }
130         
131         if (ucfront_debug) {
132                 cc_log("Bypass: ");
133                 log_args(orig_args);
134         }
135
136         execv(orig_args->argv[0], orig_args->argv);
137         cc_log("execv returned (%s)!\n", strerror(errno));
138         perror(orig_args->argv[0]);
139         exit(1);
140 }
141
142 /* find the real compiler. We just search the PATH to find a executable of the 
143    same name that isn't a link to ourselves */
144 static void find_compiler(int argc, char **argv)
145 {
146         char *base;
147         char *path;
148
149         orig_args = args_init(argc, argv);
150
151         base = str_basename(argv[0]);
152
153         /* we might be being invoked like "ucfront gcc -c foo.c" or "ucfront-gcc gcc ..." */
154         if (argc > 1 && strncmp(base, MYNAME, sizeof(MYNAME) - 1) == 0) {
155                 args_remove_first(orig_args);
156                 free(base);
157                 if (strchr(argv[1],'/')) {
158                         /* a full path was given */
159                         cc_log("Found full path to compiler afer removing myname: %s\n", argv[1]);
160                         return;
161                 }
162                 base = str_basename(argv[1]);
163
164                 cc_log("Found base compiler name afer removing myname: %s\n", base);
165         }
166
167         /* support user override of the compiler */
168         if ((path=getenv("UCFRONT_CC"))) {
169                 base = strdup(path);
170
171                 cc_log("Using explicit UCFRONT_CC=%s compiler\n", base);
172         }
173
174         orig_args->argv[0] = find_executable(base, MYNAME);
175
176         cc_log("Searching for executable %s gave %s\n", base, orig_args->argv[0]);
177
178         /* can't find the compiler! */
179         if (!orig_args->argv[0]) {
180                 perror(base);
181                 exit(1);
182         }
183 }
184
185 /**
186  * Uses the 'gcc -print-file-name=<filename>' syntax to find
187  * the path of the given compiler-supplied file.
188  * Adds in the basic_args in case it contains things like -mbig-endian
189  */
190 static char *find_gcc_file(const char *path, const char *filename)
191 {
192         pid_t pid;
193         int status;
194         char buf[256];
195         int ret;
196         char *pt;
197
198         int fds[2];
199
200         if (pipe(fds) < 0) {
201                 fatal("Failed to create pipe");
202         }
203
204         pid = fork();
205         if (pid == -1) {
206                 fatal("Failed to fork");
207         }
208         
209         if (pid == 0) {
210                 char *arg;
211
212                 args_add_prefix(basic_args, path);
213
214                 x_asprintf(&arg, "-print-file-name=%s", filename);
215                 args_add(basic_args, arg);
216
217                 close(fds[0]);
218                 dup2(fds[1], 1);
219                 close(fds[1]);
220
221                 exit(execv(path, basic_args->argv));
222         }
223
224         close(fds[1]);
225
226         if (waitpid(pid, &status, 0) != pid) {
227                 fatal("waitpid failed");
228         }
229
230         if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
231                 fatal("gcc -print-file-name failed");
232         }
233
234         ret = read(fds[0], buf, sizeof(buf) - 1);
235         close(fds[0]);
236
237         if (ret <= 0) {
238                 fatal("gcc -print-file-name=%s failed to return a result", filename);
239         }
240
241         buf[ret] = 0;
242
243         pt = strchr(buf, '\n');
244         if (pt) {
245                 *pt = 0;
246         }
247
248         if (buf[0] != '/' && strcmp(buf, filename) == 0)
249                 return NULL;
250
251         return strdup(buf);
252 }
253
254
255 static void parse_map_dirs(const char *mapping)
256 {
257         const char *cp, *ep;
258         int n;
259
260         if (!mapping)
261                 return;
262
263         cp = mapping;
264         while (*cp) {
265                 /* skip whitespace */
266                 if (*cp == ' ' || *cp == '\t') {
267                         cp++;
268                         continue;
269                 }
270                 /* dir=newdir */
271                 ep = strchr(cp, '=');
272                 if (!ep)
273                         break;
274                 n = strcspn(ep+1, " \t");
275                 map_dirs[num_map_dirs].old = strndup(cp, ep - cp);
276                 map_dirs[num_map_dirs].new = strndup(ep+1, n);
277                 num_map_dirs++;
278                 cp = ep + (1 + n);
279         }
280 }
281
282 /**
283  * 'lib' is something like -labc
284  * 
285  */
286 static void add_shared_lib(ARGS *args, const char *lib)
287 {
288         struct stat st;
289         char *e;
290         int j;
291
292         /* Special flat mode shared library. Need to add something like
293          * -Wl,-R,<libpath>lib<name>.gdb
294          * if we find that the file exists
295          */
296
297         /* Look for this lib along the lib path */
298         for (j = 0; j < num_lib_paths; j++) {
299                 x_asprintf(&e, "%s/lib%s.gdb", libpaths[j], lib + 2);
300
301                 if (stat(e, &st) == 0 && S_ISREG(st.st_mode)) {
302                         /* Found this lib */
303                         free(e);
304                         x_asprintf(&e, "-Wl,-R,%s/lib%s.gdb", libpaths[j], lib + 2);
305                         args_add(args, e);
306                         break;
307                 }
308         }
309         args_add(args, lib);
310 }
311
312 /* Search for an executable 'prog' along the given path.
313  * Returns the full path to the executable as an allocated string,
314  * or 0 if not found.
315  *
316  * If 'prog' is already an absolute path, just returns it.
317  */
318 const char *find_on_path(const char *prog, const char *path)
319 {
320         char *pathstr;
321         char *pt;
322         char *next;
323         uid_t   uid;
324         gid_t   gid;
325
326         if (*prog == '/') {
327                 return strdup(prog);
328         }
329
330         pathstr = strdup(path);
331         uid = getuid();
332         gid = getgid();
333
334         for (pt = pathstr; pt; pt = next) {
335                 char *fullpath;
336                 struct stat st;
337
338                 next = strchr(pt, ':');
339                 if (next) {
340                         *next++ = 0;
341                 }
342
343                 x_asprintf(&fullpath, "%s/%s", pt, prog);
344
345                 if (stat(fullpath, &st) == 0 && ((uid == 0 && (st.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))) || 
346                                             (uid == st.st_uid && (st.st_mode & S_IXUSR)) ||
347                                             (gid == st.st_gid && (st.st_mode & S_IXGRP)) ||
348                                             (st.st_mode & S_IXOTH))) {
349                         /* Found an executable */
350                         free(pathstr);
351                         return fullpath;
352                 }
353                 free(fullpath);
354         }
355
356         free(pathstr);
357
358         return 0;
359 }
360
361 /**
362  * Determines the environment in the following ways:
363  *
364  * 1. If environment variables such as $ROOTDIR, $CONFIG_DEFAULTS_LIBC_UCLIBC and $CONFIG_LIBCDIR
365  *    are set, works out libc from there.
366  *
367  * 2. If $ROOTDIR is set, but not the others, look in $ROOTDIR/.config for settings
368  *
369  * 3. If $ROOTDIR is not set, look at our path (which we expect to be $ROOTDIR/tools) to
370  *    determine ROOTDIR and then proceed as in (2).
371  */
372 static void find_lib_env(void)
373 {
374         char *config_libcdir = getenv("CONFIG_LIBCDIR");
375
376         rootdir = getenv("FAKE_ROOTDIR");
377         if (!rootdir)
378                 rootdir = getenv("ROOTDIR");
379
380         if (!rootdir) {
381                 char *pt;
382
383                 rootdir = strdup(argv0);
384
385                 pt = strstr(rootdir, "/tools/");
386                 if (!pt) {
387                         fatal("Could not determine ROOTDIR from argv[0]=%s\n", argv0);
388                 }
389                 *pt = 0;
390         }
391
392         stagedir = getenv("STAGEDIR");
393         if (!stagedir)
394                 x_asprintf(&stagedir, "%s/staging", rootdir);
395
396         if (!config_libcdir) {
397                 /* Not set, so read $ROOTDIR/.config and set the environment */
398                 char *dot_config;
399                 FILE *fh;
400                 char buf[256];
401
402                 x_asprintf(&dot_config, "%s/.config", rootdir);
403
404                 fh = fopen(dot_config, "r");
405                 if (!fh) {
406                         fatal("Failed to open %s\n", dot_config);
407                 }
408
409                 while (fgets(buf, sizeof(buf), fh) != 0) {
410                         char *pt;
411
412                         if (buf[0] == '#') {
413                                 continue;
414                         }
415                         pt = strchr(buf, '\n');
416                         if (pt) {
417                                 *pt = 0;
418                         }
419                         if (!buf[0]) {
420                                 continue;
421                         }
422                         pt = strchr(buf, '=');
423                         if (pt) {
424                                 *pt++ = 0;
425                                 setenv(buf, pt, 1);
426                         }
427                 }
428                 fclose(fh);
429                 config_libcdir = getenv("CONFIG_LIBCDIR");
430         }
431
432         if (getenv("CONFIG_DEFAULTS_LIBC_UCLIBC")) {
433                 if (config_libcdir) {
434                         libtype = LIBTYPE_UCLIBC;
435                         x_asprintf(&libc_libdir, "%s/%s/lib", rootdir, config_libcdir);
436                         x_asprintf(&libc_incdir, "%s/uClibc/include", stagedir);
437                 }
438         }
439         else if (getenv("CONFIG_DEFAULTS_LIBC_GLIBC")) {
440                 if (config_libcdir) {
441                         libtype = LIBTYPE_GLIBC;
442                         x_asprintf(&libc_libdir, "%s/%s/install/lib", rootdir, config_libcdir);
443                         x_asprintf(&libc_incdir, "%s/%s/install/include", rootdir, config_libcdir);
444                 }
445         }
446         else if (getenv("CONFIG_DEFAULTS_LIBC_UC_LIBC")) {
447                 if (config_libcdir) {
448                         libtype = LIBTYPE_LIBC;
449                         x_asprintf(&libc_libdir, "%s/lib/%s", rootdir, config_libcdir);
450                         x_asprintf(&libc_incdir, "%s/lib/%s/include", rootdir, config_libcdir);
451                 }
452         }
453         else if (getenv("CONFIG_DEFAULTS_LIBC_NONE")) {
454                 if (config_libcdir) {
455                         libtype = LIBTYPE_NONE;
456                         libc_libdir = 0;
457                         libc_incdir = 0;
458                 }
459         }
460         else {
461                 fatal("Could not determine libc. Are $CONFIG_DEFAULTS_LIBC_... and $CONFIG_LIBCDIR set correctly?"); 
462         }
463 }
464
465 /**
466  * Process the compiler options, to determine what mode
467  * we were called in and insert extra arguments as necessary.
468  *
469  * For compiling: add -nostdinc and -isystem <uclibc-include-dir>
470  * For linking: add -nostartfiles -nostdlib as well as the appropriate start files
471  *              along with -lc and -lgcc
472  *              Note that we detect whether linking a shared library and choose different
473  *              start files.
474  */
475 static void process_args(int argc, char **argv)
476 {
477         static const char *opts[] = {
478                                   "-iprefix", "-imacros",
479                                   "-iwithprefix", "-iwithprefixbefore",
480                                   "-D", "-U", "-x", "-MF", 
481                                   "-MT", "-MQ", "-aux-info",
482                                   "--param", "-A", "-Xlinker", "-u",
483                                   "-x",
484                                   NULL};
485         static const char *includes[] = {
486                                   "-I", "-include",
487                                   "-isystem", "-idirafter",
488                                   NULL};
489
490         int i;
491         int j;
492         int k;
493         int input_files = 0;
494         struct stat st;
495         char *e;
496         const char *compiler = argv[0];
497         int nostartfiles = 0;
498         int nostdinc = 0;
499         int nodefaultlibs = 0;
500         int id_shared_library = 0;
501
502         find_lib_env();
503
504         stripped_args = args_init(0, NULL);
505         basic_args = args_init(0, NULL);
506
507         for (i=1; i<argc; i++) {
508                 /* we must have -c or -S or -E */
509                 if (strcmp(argv[i], "-c") == 0 || strcmp(argv[i], "-S") == 0 || strcmp(argv[i], "-E") == 0) {
510                         args_add(stripped_args, argv[i]);
511                         mode = MODE_COMPILE;
512                         continue;
513                 }
514
515                 /* -shared changes the mode */
516                 if (strcmp(argv[i], "-shared") == 0) {
517                         args_add(stripped_args, argv[i]);
518                         mode = MODE_LINK_SHARED;
519                         continue;
520                 }
521
522                 if (strcmp(argv[i], "-nostartfiles") == 0) {
523                         nostartfiles = 1;
524                         continue;
525                 }
526
527                 if (strcmp(argv[i], "-nodefaultlibs") == 0) {
528                         nodefaultlibs = 1;
529                         continue;
530                 }
531
532                 if (strcmp(argv[i], "-nostdlib") == 0) {
533                         nodefaultlibs = 1;
534                         nostartfiles = 1;
535                         continue;
536                 }
537
538                 if (strcmp(argv[i], "-nostdinc") == 0) {
539                         nostdinc = 1;
540                         continue;
541                 }
542
543                 if (strcmp(argv[i], "-mid-shared-library") == 0) {
544                         args_add(stripped_args, argv[i]);
545                         id_shared_library = 1;
546                         continue;
547                 }
548
549                 /* Need to remember lib paths */
550                 if (strncmp(argv[i], "-L", 2) == 0) {
551                         if (strcmp(argv[i], "-L") == 0) {
552                                 if (i == argc-1) {
553                                         fatal("missing argument to -L\n");
554                                 }
555                                 e = argv[++i];
556                         }
557                         else {
558                                 e = argv[i] + 2;
559                         }
560                         for (k = 0; k < num_map_dirs; k++) {
561                                 char *newval;
562                                 int n = strlen(map_dirs[k].old);
563
564                                 if (strncmp(map_dirs[k].old, e, n) == 0) {
565                                         x_asprintf(&newval, "%s%s",
566                                                 map_dirs[k].new, &e[strlen(map_dirs[k].old)]);
567                                         e = newval;
568                                         break;
569                                 }
570                                 /* special case so that /lib/ will match /lib on the cmd line */
571                                 if (n && map_dirs[k].old[n-1] == '/'
572                                                 && strlen(e) == n - 1
573                                                 && strncmp(map_dirs[k].old, e, n-1) == 0) {
574                                         x_asprintf(&newval, "%s", map_dirs[k].new);
575                                         e = newval;
576                                         break;
577                                 }
578                         }
579                         libpaths[num_lib_paths++] = e;
580                         args_add(stripped_args, "-L");
581                         args_add(stripped_args, e);
582                         continue;
583                 }
584
585                 /* The user knows best: just swallow the next arg */
586                 if (strcmp(argv[i], "--ucfront-skip") == 0) {
587                         i++;
588                         if (i == argc) {
589                                 invoke_original_compiler();
590                         }
591                         args_add(stripped_args, argv[i]);
592                         continue;
593                 }
594
595                 /* include processing */
596                 for (j=0;includes[j];j++) {
597                         if (strcmp(argv[i], includes[j]) == 0) {
598                                 if (i == argc-1) {
599                                         fatal("missing argument to %s\n", includes[j]);
600                                 }
601                                 e = argv[++i];
602                         } else if (strncmp(argv[i],includes[j],strlen(includes[j])) == 0) {
603                                 e = argv[i] + strlen(includes[j]);
604                         } else {
605                                 continue;
606                         }
607                         for (k = 0; k < num_map_dirs; k++)
608                                 if (strncmp(map_dirs[k].old, e, strlen(map_dirs[k].old)) == 0) {
609                                         char *newval;
610                                         x_asprintf(&newval, "%s%s",
611                                                 map_dirs[k].new, &e[strlen(map_dirs[k].old)]);
612                                         e = newval;
613                                         break;
614                                 }
615                         args_add(stripped_args, includes[j]);
616                         args_add(stripped_args, e);
617                         break;
618                 }
619                 if (includes[j]) {
620                         continue;
621                 }
622
623                 /* options that take an argument */
624                 for (j=0;opts[j];j++) {
625                         if (strcmp(argv[i], opts[j]) == 0) {
626                                 if (i == argc-1) {
627                                         fatal("missing argument to %s\n", argv[i]);
628                                 }
629                                         
630                                 args_add(stripped_args, argv[i]);
631                                 args_add(stripped_args, argv[i+1]);
632                                 i++;
633                                 break;
634                         }
635                 }
636                 if (opts[j]) {
637                         continue;
638                 }
639
640                 if (strncmp(argv[i], "-M", 2) == 0) {
641                         /* These are for dependency generation. */
642                         args_add(stripped_args, argv[i]);
643                         mode = MODE_DEPEND;
644                         continue;
645                 }
646
647                 if (strncmp(argv[i], "-m", 2) == 0) {
648                         args_add(stripped_args, argv[i]);
649                         /* Remember this arg in case we do -print-file-name */
650                         args_add(basic_args, argv[i]);
651                         continue;
652                 }
653
654                 if (id_shared_library && strncmp(argv[i], "-l", 2) == 0) {
655                         add_shared_lib(stripped_args, argv[i]);
656                         continue;
657                 }
658
659                 if (strstr(argv[i], "-pthread")) {
660                         pthread = 1;
661                         args_add(stripped_args, argv[i]);
662                         continue;
663                 }
664
665                 if (strstr(argv[i], "-elf2flt")) {
666                         flat_executable = 1;
667                         args_add(stripped_args, argv[i]);
668                         continue;
669                 }
670                         
671                 /* other options */
672                 if (argv[i][0] == '-') {
673                         args_add(stripped_args, argv[i]);
674                         continue;
675                 }
676
677                 /* if an argument isn't a plain file then assume its
678                    an option, not an input file. This allows us to
679                    cope better with unusual compiler options */
680                 if (stat(argv[i], &st) != 0 || !S_ISREG(st.st_mode)) {
681                         args_add(stripped_args, argv[i]);
682                         continue;                       
683                 }
684
685                 /* Not an option, so this as an input file */
686                 args_add(stripped_args, argv[i]);
687                 input_files++;
688         }
689
690         if (mode != MODE_DEPEND && !input_files) {
691                 cc_log("No input files found\n");
692                 invoke_original_compiler();
693         }
694
695         if (mode == MODE_COMPILE) {
696                 cc_log("Found -c or -S option, so assuming compile mode\n");
697         }
698         else if (mode == MODE_DEPEND) {
699                 cc_log("Found -M option, so assuming dependency mode\n");
700         }
701         else {
702                 char *startfile;
703                 char *rpath;
704
705                 if (mode == MODE_LINK) {
706                         cc_log("Assuming link mode\n");
707                 }
708                 else {
709                         cc_log("Found -shared option, so assuming shared lib link mode\n");
710                 }
711
712                 if (!nostartfiles || !nodefaultlibs) {
713                         if (libc_libdir && (stat(libc_libdir, &st) != 0 || !S_ISDIR(st.st_mode))) {
714                                 fatal("ucfront: libc lib directory does not exist, %s", libc_libdir);
715                         }
716                 }
717
718                 /* Now we need to work out where the compilers lib directory is, because we
719                  * still need some "standard" start files
720                  */
721                 if (libtype == LIBTYPE_NONE) {
722                 }
723                 else if (libtype == LIBTYPE_LIBC) {
724                         /* This one always uses the compiler start files */
725                         if (nostartfiles) {
726                                 /*args_add(stripped_args, "XX -nostartfiles XX");*/
727                         }
728                         if (nodefaultlibs) {
729                                 args_add(stripped_args, "-nodefaultlibs");
730                         }
731                 }
732                 else {
733                         if (!nostartfiles) {
734                                 startfile = NULL;
735                                 if (!flat_executable)
736                                         startfile = find_gcc_file(compiler,
737                                                         (mode == MODE_LINK) ? "crtbegin.o" : "crtbeginS.o");
738                                 if (startfile)
739                                         args_add_prefix(stripped_args, startfile);
740                                 x_asprintf(&startfile, "%s/crti.o", libc_libdir);
741                                 args_add_prefix(stripped_args, startfile);
742                                 if (mode == MODE_LINK) {
743                                         x_asprintf(&startfile, "%s/crt1.o", libc_libdir);
744                                         args_add_prefix(stripped_args, startfile);
745                                 }
746                         }
747
748                         /* Don't use standard libs or start files */
749                         args_add_prefix(stripped_args, "-nostdlib");
750                 }
751
752                 if (libtype != LIBTYPE_NONE && !nodefaultlibs) {
753                         args_add(stripped_args, "-L");
754                         args_add(stripped_args, libc_libdir);
755
756                         x_asprintf(&rpath, "-Wl,-rpath-link,%s", libc_libdir);
757                         args_add(stripped_args, rpath);
758
759                         libpaths[num_lib_paths++] = libc_libdir;
760                 }
761
762                 /* Need to be able to find all the libs */
763                 x_asprintf(&e, "%s/lib", stagedir);
764                 args_add(stripped_args, "-L");
765                 args_add(stripped_args, e);
766
767                 x_asprintf(&rpath, "-Wl,-rpath-link,%s", e);
768                 args_add(stripped_args, rpath);
769
770                 libpaths[num_lib_paths++] = e;
771
772                 if (!nodefaultlibs) {
773                         if (id_shared_library) {
774                                 add_shared_lib(stripped_args, "-lc");
775                                 add_shared_lib(stripped_args, "-lgcc");
776                         }
777                         else {
778                                 /*
779                                  * ensure cross references are fixed
780                                  */
781                                 if (cplusplus) {
782                                         args_add_with_spaces(stripped_args, getenv("SLIBSTDCPP") ?: "-lstdc++");
783                                         args_add_with_spaces(stripped_args, getenv("CXXSUP") ?: "-lsupc++");
784                                 }
785                                 args_add(stripped_args, "-Wl,--start-group");
786                                 if (cplusplus && getenv("CONFIG_LIB_STLPORT"))
787                                         pthread = 1;
788                                 if (pthread) {
789                                         args_add(stripped_args, "-lpthread");
790                                         args_add(stripped_args, "-lm");
791                                 }
792                                 args_add(stripped_args, "-lc");
793                                 args_add(stripped_args, "-lgcc");
794                                 args_add(stripped_args, "-Wl,--end-group");
795                         }
796                 }
797
798                 if (!nostartfiles && libtype != LIBTYPE_LIBC && libtype != LIBTYPE_NONE) {
799 #if 0
800                         if (libtype == LIBTYPE_GLIBC) {
801                                 x_asprintf(&startfile, "%s/ld-linux.so.2", libc_libdir);
802                                 args_add(stripped_args, startfile);
803                         }
804 #endif
805                         startfile = NULL;
806                         if (!flat_executable)
807                                 startfile = find_gcc_file(compiler,
808                                                 (mode == MODE_LINK) ? "crtend.o" : "crtendS.o");
809                         if (startfile)
810                                 args_add(stripped_args, startfile);
811                         x_asprintf(&startfile, "%s/crtn.o", libc_libdir);
812                         args_add(stripped_args, startfile);
813                 }
814
815                 /*sh-linux-gcc -m4 -ml crt1.o crti.o <orig>/crtbegin.o -o discard discard.o <orig>/crtend.o crtn.o*/
816         }
817
818         if (!nostdinc && libtype != LIBTYPE_NONE) {
819                 char *includedir;
820
821                 /* Note that we ALWAYS add -isystem since we may be operating in combined compile/link mode */
822                 if (stat(libc_incdir, &st) != 0 || !S_ISDIR(st.st_mode)) {
823                         fprintf(stderr, "ucfront: libc include directory does not exist, %s\n", libc_incdir);
824                         exit(1);
825                 }
826
827                 /* Do this in reverse order. We use -idirafter so that user-specified include paths come first */
828                 includedir = find_gcc_file(compiler, "include");
829                 if (includedir) {
830                         args_add_prefix(stripped_args, includedir);
831                         args_add_prefix(stripped_args, "-isystem");
832                 }
833
834                 includedir = find_gcc_file(compiler, "include-fixed");
835                 if (includedir) {
836                         args_add_prefix(stripped_args, includedir);
837                         args_add_prefix(stripped_args, "-isystem");
838                 }
839
840                 args_add_prefix(stripped_args, libc_incdir);
841                 args_add_prefix(stripped_args, "-isystem");
842         }
843
844         if (cplusplus) {
845                 if (getenv("CONFIG_LIB_STLPORT")) {
846                         x_asprintf(&e, "%s/include/c++", stagedir);
847                         args_add_prefix(stripped_args, e);
848                         args_add_prefix(stripped_args, "-isystem");
849                         x_asprintf(&e, "%s", getenv("STL_INCDIR"));
850                         args_add_prefix(stripped_args, e);
851                         args_add_prefix(stripped_args, "-idirafter");
852                 }
853                 else if (getenv("CONFIG_LIB_UCLIBCXX") ||
854                                 getenv("CONFIG_LIB_UCLIBCXX_FORCE")) {
855                         x_asprintf(&e, "-I%s/lib/uClibc++/include", stagedir);
856                         args_add_prefix(stripped_args, e);
857                 }
858                 else {
859                         x_asprintf(&e, "-I%s/include/c++", stagedir);
860                         args_add_prefix(stripped_args, e);
861                 }
862         }
863
864         /* Do this in reverse order. We use -idirafter so that user-specified include paths come first */
865         x_asprintf(&e, "%s/include", stagedir);
866         args_add_prefix(stripped_args, e);
867         args_add_prefix(stripped_args, "-idirafter");
868
869         if (libtype != LIBTYPE_NONE) {
870                 /* Don't add this option since we still need some compiler-specific includes */
871                 args_add_prefix(stripped_args, "-nostdinc");
872         }
873
874         /* Now add the compiler */
875         args_add_prefix(stripped_args, compiler);
876
877         /* Any any specified prefix */
878         if ((e=getenv("UCFRONT_PREFIX"))) {
879                 char *p = find_executable(e, MYNAME);
880                 if (!p) {
881                         perror(e);
882                         exit(1);
883                 }
884                 args_add_prefix(stripped_args, p);
885         }
886
887         /* Hack by JW to allow forcing specific args right at the 
888            end of the stripped arg list.  This works around cruftiness
889            in mb-gcc-2.95.x, however it may be useful somewhere else */
890         if((e=getenv("UCFRONT_LINK_SUFFIX")) && (mode==MODE_LINK) ) 
891         {
892                 /* Break up potentially multiple words into substrings */
893                 char *p1;
894
895                 /* Duplicate the string - strtok messes with it */
896                 char *e2=(char *)malloc(strlen(e)+1);
897                 strcpy(e2,e);
898
899                 p1=strtok(e2," ");
900
901                 while(p1)
902                 {
903                         args_add(stripped_args,p1);
904                         p1=strtok(NULL," ");
905                 }
906                 free(e2);
907         }
908 }
909
910 /* the main ucfront driver function */
911 static void ucfront(int argc, char *argv[])
912 {
913         const char *pt = getenv("UCFRONT_DEBUG");
914         if (pt) {
915                 ucfront_debug = atoi(getenv("UCFRONT_DEBUG"));
916                 if (!ucfront_debug) {
917                         ucfront_debug = 1;
918                 }
919         }
920
921         /* find the real compiler */
922         find_compiler(argc, argv);
923
924         if (ucfront_debug) {
925                 cc_log("Original: ");
926                 log_args(orig_args);
927         }
928
929         if (getenv("UCFRONT_DISABLE")) {
930                 invoke_original_compiler();
931         }
932
933         /* load remapped directories */
934         parse_map_dirs(getenv("UCFRONT_MAPDIRS"));
935         
936         /* process argument list, returning a new set of arguments for pre-processing */
937         process_args(orig_args->argc, orig_args->argv);
938
939         /* Print the args for debugging */
940         if (ucfront_debug) {
941                 cc_log("Final: ");
942                 log_args(stripped_args);
943                 /* Don't log this to stderr since it can confuse configure */
944                 /*print_args(stderr, stripped_args);*/
945         }
946
947         /* Now execute the actual command */
948         execv(stripped_args->argv[0], stripped_args->argv);
949
950         perror("execv");
951
952         exit(1);
953 }
954
955
956 static void usage(void)
957 {
958         printf("ucfront, a uClinux compiler front end. Version %s\n", UCFRONT_VERSION);
959         printf("Copyright Steve Bennett, 2005\n\n");
960         
961         printf("Usage:\n");
962         printf("\tucfront [options]\n");
963         printf("\tucfront<anything> compiler [compile options]\n");
964         printf("\tcompiler [compile options]    (via symbolic link)\n");
965         printf("\nOptions:\n");
966
967         printf("-h                      this help page\n");
968         printf("-V                      print version number\n");
969 }
970
971 /* the main program when not doing a compile */
972 static int ucfront_main(int argc, char *argv[])
973 {
974         extern int optind;
975         int c;
976
977         while ((c = getopt(argc, argv, "Vh")) != -1) {
978                 switch (c) {
979                 case 'V':
980                         printf("ucfront version %s\n", UCFRONT_VERSION);
981                         printf("Copyright Steve Bennett 2005\n");
982                         printf("Released under the GNU GPL v2 or later\n");
983                         exit(0);
984
985                 case 'h':
986                         usage();
987                         exit(0);
988                         
989                 default:
990                         usage();
991                         exit(1);
992                 }
993         }
994
995         return 0;
996 }
997
998 int main(int argc, char *argv[])
999 {
1000         cache_logfile = getenv("UCFRONT_LOGFILE");
1001
1002         /* Try to find this executable on the path */
1003         argv0 = find_on_path(argv[0], getenv("PATH") ?: "") ?: argv[0];
1004
1005         /* check for c++ */
1006         if (strlen(argv[0]) > 2 && strcmp(argv[0] + strlen(argv[0]) - 2, "++") == 0)
1007                 cplusplus = 1;
1008
1009         /* check if we are being invoked as "ucfront" */
1010         if (strlen(argv[0]) >= strlen(MYNAME) &&
1011             strcmp(argv[0] + strlen(argv[0]) - strlen(MYNAME), MYNAME) == 0) {
1012                 if (argc < 2) {
1013                         usage();
1014                         exit(1);
1015                 }
1016                 /* if the first argument isn't an option, then assume we are
1017                    being passed a compiler name and options */
1018                 if (argv[1][0] == '-') {
1019                         return ucfront_main(argc, argv);
1020                 }
1021         }
1022
1023         ucfront(argc, argv);
1024         return 1;
1025 }