2 A front end for gcc which can work with a custom uClibc or glibc library
4 Steve Bennett <steveb@snapgear.com>
6 Argument processing code based on ccache
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.
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.
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.
25 #define MYNAME "ucfront"
27 /* the debug logfile name, if set */
28 char *cache_logfile = NULL;
30 /* the argument list after processing */
31 static ARGS *stripped_args;
33 /* contains just some basic args needed when running -print-file-name
34 * such as -mbig-endian
36 static ARGS *basic_args;
38 /* the original argument list */
39 static ARGS *orig_args;
41 /* Are we in link mode? */
49 /* Are we using uClibc or glibc? */
55 } libtype = LIBTYPE_NONE;
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
60 static int flat_executable = 0;
62 static int pthread = 0;
64 static int cplusplus = 0;
66 static int ucfront_debug = 0;
68 static const char *libpaths[100];
69 static int num_lib_paths = 0;
75 static int num_map_dirs = 0;
78 static char *stagedir;
79 static const char *argv0;
80 static char *libc_libdir = 0;
81 static char *libc_incdir = 0;
84 /* Print the given args */
85 static void print_args(FILE *fh, ARGS *args)
88 for (i = 0; i < args->argc; i++) {
92 fputs(args->argv[i], fh);
99 static void log_args(ARGS *args)
102 for (i = 0; i < args->argc; i++) {
106 cc_log("%s", args->argv[i]);
113 * Invoke the compiler with the original arguments (minus --ucfront- prefixes)
115 static void invoke_original_compiler(void)
119 /* strip any local args */
120 args_strip(orig_args, "--ucfront-");
122 if ((e=getenv("UCFRONT_PREFIX"))) {
123 char *p = find_executable(e, MYNAME);
128 args_add_prefix(orig_args, p);
136 execv(orig_args->argv[0], orig_args->argv);
137 cc_log("execv returned (%s)!\n", strerror(errno));
138 perror(orig_args->argv[0]);
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)
149 orig_args = args_init(argc, argv);
151 base = str_basename(argv[0]);
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);
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]);
162 base = str_basename(argv[1]);
164 cc_log("Found base compiler name afer removing myname: %s\n", base);
167 /* support user override of the compiler */
168 if ((path=getenv("UCFRONT_CC"))) {
171 cc_log("Using explicit UCFRONT_CC=%s compiler\n", base);
174 orig_args->argv[0] = find_executable(base, MYNAME);
176 cc_log("Searching for executable %s gave %s\n", base, orig_args->argv[0]);
178 /* can't find the compiler! */
179 if (!orig_args->argv[0]) {
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
190 static char *find_gcc_file(const char *path, const char *filename)
201 fatal("Failed to create pipe");
206 fatal("Failed to fork");
212 args_add_prefix(basic_args, path);
214 x_asprintf(&arg, "-print-file-name=%s", filename);
215 args_add(basic_args, arg);
221 exit(execv(path, basic_args->argv));
226 if (waitpid(pid, &status, 0) != pid) {
227 fatal("waitpid failed");
230 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
231 fatal("gcc -print-file-name failed");
234 ret = read(fds[0], buf, sizeof(buf) - 1);
238 fatal("gcc -print-file-name=%s failed to return a result", filename);
243 pt = strchr(buf, '\n');
248 if (buf[0] != '/' && strcmp(buf, filename) == 0)
255 static void parse_map_dirs(const char *mapping)
265 /* skip whitespace */
266 if (*cp == ' ' || *cp == '\t') {
271 ep = strchr(cp, '=');
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);
283 * 'lib' is something like -labc
286 static void add_shared_lib(ARGS *args, const char *lib)
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
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);
301 if (stat(e, &st) == 0 && S_ISREG(st.st_mode)) {
304 x_asprintf(&e, "-Wl,-R,%s/lib%s.gdb", libpaths[j], lib + 2);
312 /* Search for an executable 'prog' along the given path.
313 * Returns the full path to the executable as an allocated string,
316 * If 'prog' is already an absolute path, just returns it.
318 const char *find_on_path(const char *prog, const char *path)
330 pathstr = strdup(path);
334 for (pt = pathstr; pt; pt = next) {
338 next = strchr(pt, ':');
343 x_asprintf(&fullpath, "%s/%s", pt, prog);
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 */
362 * Determines the environment in the following ways:
364 * 1. If environment variables such as $ROOTDIR, $CONFIG_DEFAULTS_LIBC_UCLIBC and $CONFIG_LIBCDIR
365 * are set, works out libc from there.
367 * 2. If $ROOTDIR is set, but not the others, look in $ROOTDIR/.config for settings
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).
372 static void find_lib_env(void)
374 char *config_libcdir = getenv("CONFIG_LIBCDIR");
376 rootdir = getenv("FAKE_ROOTDIR");
378 rootdir = getenv("ROOTDIR");
383 rootdir = strdup(argv0);
385 pt = strstr(rootdir, "/tools/");
387 fatal("Could not determine ROOTDIR from argv[0]=%s\n", argv0);
392 stagedir = getenv("STAGEDIR");
394 x_asprintf(&stagedir, "%s/staging", rootdir);
396 if (!config_libcdir) {
397 /* Not set, so read $ROOTDIR/.config and set the environment */
402 x_asprintf(&dot_config, "%s/.config", rootdir);
404 fh = fopen(dot_config, "r");
406 fatal("Failed to open %s\n", dot_config);
409 while (fgets(buf, sizeof(buf), fh) != 0) {
415 pt = strchr(buf, '\n');
422 pt = strchr(buf, '=');
429 config_libcdir = getenv("CONFIG_LIBCDIR");
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);
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);
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);
453 else if (getenv("CONFIG_DEFAULTS_LIBC_NONE")) {
454 if (config_libcdir) {
455 libtype = LIBTYPE_NONE;
461 fatal("Could not determine libc. Are $CONFIG_DEFAULTS_LIBC_... and $CONFIG_LIBCDIR set correctly?");
466 * Process the compiler options, to determine what mode
467 * we were called in and insert extra arguments as necessary.
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
475 static void process_args(int argc, char **argv)
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",
485 static const char *includes[] = {
487 "-isystem", "-idirafter",
496 const char *compiler = argv[0];
497 int nostartfiles = 0;
499 int nodefaultlibs = 0;
500 int id_shared_library = 0;
504 stripped_args = args_init(0, NULL);
505 basic_args = args_init(0, NULL);
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]);
515 /* -shared changes the mode */
516 if (strcmp(argv[i], "-shared") == 0) {
517 args_add(stripped_args, argv[i]);
518 mode = MODE_LINK_SHARED;
522 if (strcmp(argv[i], "-nostartfiles") == 0) {
527 if (strcmp(argv[i], "-nodefaultlibs") == 0) {
532 if (strcmp(argv[i], "-nostdlib") == 0) {
538 if (strcmp(argv[i], "-nostdinc") == 0) {
543 if (strcmp(argv[i], "-mid-shared-library") == 0) {
544 args_add(stripped_args, argv[i]);
545 id_shared_library = 1;
549 /* Need to remember lib paths */
550 if (strncmp(argv[i], "-L", 2) == 0) {
551 if (strcmp(argv[i], "-L") == 0) {
553 fatal("missing argument to -L\n");
560 for (k = 0; k < num_map_dirs; k++) {
562 int n = strlen(map_dirs[k].old);
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)]);
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);
579 libpaths[num_lib_paths++] = e;
580 args_add(stripped_args, "-L");
581 args_add(stripped_args, e);
585 /* The user knows best: just swallow the next arg */
586 if (strcmp(argv[i], "--ucfront-skip") == 0) {
589 invoke_original_compiler();
591 args_add(stripped_args, argv[i]);
595 /* include processing */
596 for (j=0;includes[j];j++) {
597 if (strcmp(argv[i], includes[j]) == 0) {
599 fatal("missing argument to %s\n", includes[j]);
602 } else if (strncmp(argv[i],includes[j],strlen(includes[j])) == 0) {
603 e = argv[i] + strlen(includes[j]);
607 for (k = 0; k < num_map_dirs; k++)
608 if (strncmp(map_dirs[k].old, e, strlen(map_dirs[k].old)) == 0) {
610 x_asprintf(&newval, "%s%s",
611 map_dirs[k].new, &e[strlen(map_dirs[k].old)]);
615 args_add(stripped_args, includes[j]);
616 args_add(stripped_args, e);
623 /* options that take an argument */
624 for (j=0;opts[j];j++) {
625 if (strcmp(argv[i], opts[j]) == 0) {
627 fatal("missing argument to %s\n", argv[i]);
630 args_add(stripped_args, argv[i]);
631 args_add(stripped_args, argv[i+1]);
640 if (strncmp(argv[i], "-M", 2) == 0) {
641 /* These are for dependency generation. */
642 args_add(stripped_args, argv[i]);
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]);
654 if (id_shared_library && strncmp(argv[i], "-l", 2) == 0) {
655 add_shared_lib(stripped_args, argv[i]);
659 if (strstr(argv[i], "-pthread")) {
661 args_add(stripped_args, argv[i]);
665 if (strstr(argv[i], "-elf2flt")) {
667 args_add(stripped_args, argv[i]);
672 if (argv[i][0] == '-') {
673 args_add(stripped_args, argv[i]);
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]);
685 /* Not an option, so this as an input file */
686 args_add(stripped_args, argv[i]);
690 if (mode != MODE_DEPEND && !input_files) {
691 cc_log("No input files found\n");
692 invoke_original_compiler();
695 if (mode == MODE_COMPILE) {
696 cc_log("Found -c or -S option, so assuming compile mode\n");
698 else if (mode == MODE_DEPEND) {
699 cc_log("Found -M option, so assuming dependency mode\n");
705 if (mode == MODE_LINK) {
706 cc_log("Assuming link mode\n");
709 cc_log("Found -shared option, so assuming shared lib link mode\n");
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);
718 /* Now we need to work out where the compilers lib directory is, because we
719 * still need some "standard" start files
721 if (libtype == LIBTYPE_NONE) {
723 else if (libtype == LIBTYPE_LIBC) {
724 /* This one always uses the compiler start files */
726 /*args_add(stripped_args, "XX -nostartfiles XX");*/
729 args_add(stripped_args, "-nodefaultlibs");
735 if (!flat_executable)
736 startfile = find_gcc_file(compiler,
737 (mode == MODE_LINK) ? "crtbegin.o" : "crtbeginS.o");
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);
748 /* Don't use standard libs or start files */
749 args_add_prefix(stripped_args, "-nostdlib");
752 if (libtype != LIBTYPE_NONE && !nodefaultlibs) {
753 args_add(stripped_args, "-L");
754 args_add(stripped_args, libc_libdir);
756 x_asprintf(&rpath, "-Wl,-rpath-link,%s", libc_libdir);
757 args_add(stripped_args, rpath);
759 libpaths[num_lib_paths++] = libc_libdir;
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);
767 x_asprintf(&rpath, "-Wl,-rpath-link,%s", e);
768 args_add(stripped_args, rpath);
770 libpaths[num_lib_paths++] = e;
772 if (!nodefaultlibs) {
773 if (id_shared_library) {
774 add_shared_lib(stripped_args, "-lc");
775 add_shared_lib(stripped_args, "-lgcc");
779 * ensure cross references are fixed
782 args_add_with_spaces(stripped_args, getenv("SLIBSTDCPP") ?: "-lstdc++");
783 args_add_with_spaces(stripped_args, getenv("CXXSUP") ?: "-lsupc++");
785 args_add(stripped_args, "-Wl,--start-group");
786 if (cplusplus && getenv("CONFIG_LIB_STLPORT"))
789 args_add(stripped_args, "-lpthread");
790 args_add(stripped_args, "-lm");
792 args_add(stripped_args, "-lc");
793 args_add(stripped_args, "-lgcc");
794 args_add(stripped_args, "-Wl,--end-group");
798 if (!nostartfiles && libtype != LIBTYPE_LIBC && libtype != LIBTYPE_NONE) {
800 if (libtype == LIBTYPE_GLIBC) {
801 x_asprintf(&startfile, "%s/ld-linux.so.2", libc_libdir);
802 args_add(stripped_args, startfile);
806 if (!flat_executable)
807 startfile = find_gcc_file(compiler,
808 (mode == MODE_LINK) ? "crtend.o" : "crtendS.o");
810 args_add(stripped_args, startfile);
811 x_asprintf(&startfile, "%s/crtn.o", libc_libdir);
812 args_add(stripped_args, startfile);
815 /*sh-linux-gcc -m4 -ml crt1.o crti.o <orig>/crtbegin.o -o discard discard.o <orig>/crtend.o crtn.o*/
818 if (!nostdinc && libtype != LIBTYPE_NONE) {
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);
827 /* Do this in reverse order. We use -idirafter so that user-specified include paths come first */
828 includedir = find_gcc_file(compiler, "include");
830 args_add_prefix(stripped_args, includedir);
831 args_add_prefix(stripped_args, "-isystem");
834 includedir = find_gcc_file(compiler, "include-fixed");
836 args_add_prefix(stripped_args, includedir);
837 args_add_prefix(stripped_args, "-isystem");
840 args_add_prefix(stripped_args, libc_incdir);
841 args_add_prefix(stripped_args, "-isystem");
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");
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);
859 x_asprintf(&e, "-I%s/include/c++", stagedir);
860 args_add_prefix(stripped_args, e);
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");
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");
874 /* Now add the compiler */
875 args_add_prefix(stripped_args, compiler);
877 /* Any any specified prefix */
878 if ((e=getenv("UCFRONT_PREFIX"))) {
879 char *p = find_executable(e, MYNAME);
884 args_add_prefix(stripped_args, p);
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) )
892 /* Break up potentially multiple words into substrings */
895 /* Duplicate the string - strtok messes with it */
896 char *e2=(char *)malloc(strlen(e)+1);
903 args_add(stripped_args,p1);
910 /* the main ucfront driver function */
911 static void ucfront(int argc, char *argv[])
913 const char *pt = getenv("UCFRONT_DEBUG");
915 ucfront_debug = atoi(getenv("UCFRONT_DEBUG"));
916 if (!ucfront_debug) {
921 /* find the real compiler */
922 find_compiler(argc, argv);
925 cc_log("Original: ");
929 if (getenv("UCFRONT_DISABLE")) {
930 invoke_original_compiler();
933 /* load remapped directories */
934 parse_map_dirs(getenv("UCFRONT_MAPDIRS"));
936 /* process argument list, returning a new set of arguments for pre-processing */
937 process_args(orig_args->argc, orig_args->argv);
939 /* Print the args for debugging */
942 log_args(stripped_args);
943 /* Don't log this to stderr since it can confuse configure */
944 /*print_args(stderr, stripped_args);*/
947 /* Now execute the actual command */
948 execv(stripped_args->argv[0], stripped_args->argv);
956 static void usage(void)
958 printf("ucfront, a uClinux compiler front end. Version %s\n", UCFRONT_VERSION);
959 printf("Copyright Steve Bennett, 2005\n\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");
967 printf("-h this help page\n");
968 printf("-V print version number\n");
971 /* the main program when not doing a compile */
972 static int ucfront_main(int argc, char *argv[])
977 while ((c = getopt(argc, argv, "Vh")) != -1) {
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");
998 int main(int argc, char *argv[])
1000 cache_logfile = getenv("UCFRONT_LOGFILE");
1002 /* Try to find this executable on the path */
1003 argv0 = find_on_path(argv[0], getenv("PATH") ?: "") ?: argv[0];
1006 if (strlen(argv[0]) > 2 && strcmp(argv[0] + strlen(argv[0]) - 2, "++") == 0)
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) {
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);
1023 ucfront(argc, argv);