/* something went badly wrong - just execute the real compiler */ static void failed(void) { char *e; /* delete intermediate pre-processor file if needed */ if (i_tmpfile) { if (!direct_i_file) { unlink(i_tmpfile); } free(i_tmpfile); i_tmpfile = NULL; } /* delete the cpp stderr file if necessary */ if (cpp_stderr) { unlink(cpp_stderr); free(cpp_stderr); cpp_stderr = NULL; } /* strip any local args */ args_strip(orig_args, "--ccache-"); if ((e=getenv("CCACHE_PREFIX"))) { char *p = find_executable(e, MYNAME); if (!p) { perror(e); exit(1); } args_add_prefix(orig_args, p); } execv(orig_args->argv[0], orig_args->argv); cc_log("execv returned (%s)!\n", strerror(errno)); perror(orig_args->argv[0]); exit(1); }
/* process the compiler options to form the correct set of options for obtaining the preprocessor output */ static void process_args(int argc, char **argv) { int i; int found_c_opt = 0; int found_S_opt = 0; struct stat st; char *e; /* is gcc being asked to output dependencies? */ int generating_dependencies = 0; /* is the dependency makefile name overridden with -MF? */ int dependency_filename_specified = 0; /* is the dependency makefile target name specified with -MQ or -MF? */ int dependency_target_specified = 0; stripped_args = args_init(0, NULL); args_add(stripped_args, argv[0]); /* -c not required for SWIG */ if (swig) { found_c_opt = 1; } for (i=1; i<argc; i++) { /* some options will never work ... */ if (strcmp(argv[i], "-E") == 0) { failed(); } /* these are too hard */ if (strcmp(argv[i], "-fbranch-probabilities")==0 || strcmp(argv[i], "-fprofile-arcs") == 0 || strcmp(argv[i], "-ftest-coverage") == 0 || strcmp(argv[i], "--coverage") == 0 || strcmp(argv[i], "-M") == 0 || strcmp(argv[i], "-MM") == 0 || strcmp(argv[i], "-x") == 0) { cc_log("argument %s is unsupported\n", argv[i]); stats_update(STATS_UNSUPPORTED); failed(); continue; } /* we must have -c */ if (strcmp(argv[i], "-c") == 0) { if (!strip_c_option) { args_add(stripped_args, argv[i]); } found_c_opt = 1; continue; } /* -S changes the default extension */ if (strcmp(argv[i], "-S") == 0) { args_add(stripped_args, argv[i]); found_S_opt = 1; continue; } /* we need to work out where the output was meant to go */ if (strcmp(argv[i], "-o") == 0) { if (i == argc-1) { cc_log("missing argument to %s\n", argv[i]); stats_update(STATS_ARGS); failed(); } output_file = argv[i+1]; i++; continue; } /* alternate form of -o, with no space */ if (!swig) { /* some of SWIG's arguments begin with -o */ if (strncmp(argv[i], "-o", 2) == 0) { output_file = &argv[i][2]; continue; } } /* debugging is handled specially, so that we know if we can strip line number info */ if (strncmp(argv[i], "-g", 2) == 0) { args_add(stripped_args, argv[i]); if (strcmp(argv[i], "-g0") != 0) { enable_unify = 0; } continue; } /* The user knows best: just swallow the next arg */ if (strcmp(argv[i], "--ccache-skip") == 0) { i++; if (i == argc) { failed(); } args_add(stripped_args, argv[i]); continue; } /* These options require special handling, because they behave differently with gcc -E, when the output file is not specified. */ if (strcmp(argv[i], "-MD") == 0 || strcmp(argv[i], "-MMD") == 0) { generating_dependencies = 1; } else if (strcmp(argv[i], "-MF") == 0) { dependency_filename_specified = 1; } else if (strcmp(argv[i], "-MQ") == 0 || strcmp(argv[i], "-MT") == 0) { dependency_target_specified = 1; } /* the input file is already preprocessed */ if (swig && strcmp(argv[i], "-nopreprocess") == 0) { direct_i_file = 1; continue; } /* options that take an argument */ { const char *opts[] = {"-I", "-include", "-imacros", "-iprefix", "-iwithprefix", "-iwithprefixbefore", "-L", "-D", "-U", "-x", "-MF", "-MT", "-MQ", "-isystem", "-aux-info", "--param", "-A", "-Xlinker", "-u", "-idirafter", NULL}; int j; for (j=0;opts[j];j++) { if (strcmp(argv[i], opts[j]) == 0) { if (i == argc-1) { cc_log("missing argument to %s\n", argv[i]); stats_update(STATS_ARGS); failed(); } args_add(stripped_args, argv[i]); args_add(stripped_args, argv[i+1]); i++; break; } } if (opts[j]) continue; } /* other options */ if (argv[i][0] == '-') { args_add(stripped_args, argv[i]); continue; } /* if an argument isn't a plain file then assume its an option, not an input file. This allows us to cope better with unusual compiler options */ if (stat(argv[i], &st) != 0 || !S_ISREG(st.st_mode)) { args_add(stripped_args, argv[i]); continue; } if (input_file) { if (check_extension(argv[i], NULL)) { cc_log("multiple input files (%s and %s)\n", input_file, argv[i]); stats_update(STATS_MULTIPLE); } else if (!found_c_opt) { cc_log("called for link with %s\n", argv[i]); if (strstr(argv[i], "conftest.")) { stats_update(STATS_CONFTEST); } else { stats_update(STATS_LINK); } } else { cc_log("non C/C++ file %s\n", argv[i]); stats_update(STATS_NOTC); } failed(); } input_file = argv[i]; } if (!input_file) { cc_log("No input file found\n"); stats_update(STATS_NOINPUT); failed(); } if (swig) { i_extension = check_extension(input_file, NULL); } else { i_extension = check_extension(input_file, &direct_i_file); } if (i_extension == NULL) { cc_log("Not a C/C++ file - %s\n", input_file); stats_update(STATS_NOTC); failed(); } if (!found_c_opt) { cc_log("No -c option found for %s\n", input_file); /* I find that having a separate statistic for autoconf tests is useful, as they are the dominant form of "called for link" in many cases */ if (strstr(input_file, "conftest.")) { stats_update(STATS_CONFTEST); } else { stats_update(STATS_LINK); } failed(); } /* don't try to second guess the compilers heuristics for stdout handling */ if (output_file && strcmp(output_file, "-") == 0) { stats_update(STATS_OUTSTDOUT); failed(); } if (!swig && !output_file) { char *p; output_file = x_strdup(input_file); if ((p = strrchr(output_file, '/'))) { output_file = p+1; } p = strrchr(output_file, '.'); if (!p || !p[1]) { cc_log("badly formed output_file %s\n", output_file); stats_update(STATS_ARGS); failed(); } p[1] = found_S_opt ? 's' : 'o'; p[2] = 0; } /* If dependencies are generated, configure the preprocessor */ if (generating_dependencies && output_file) { if (!dependency_filename_specified) { char *default_depfile_name = x_strdup(output_file); char *p = strrchr(default_depfile_name, '.'); if (p) { if (strlen(p) < 2) { cc_log("badly formed dependency file %s\n", output_file); stats_update(STATS_ARGS); failed(); return; } *p = 0; } else { int len = p - default_depfile_name; p = x_malloc(len + 3); strncpy(default_depfile_name, p, len - 1); free(default_depfile_name); default_depfile_name = p; } strcat(default_depfile_name, ".d"); args_add(stripped_args, "-MF"); args_add(stripped_args, default_depfile_name); } if (!dependency_target_specified) { args_add(stripped_args, "-MT"); args_add(stripped_args, output_file); } } /* cope with -o /dev/null */ if (output_file && strcmp(output_file,"/dev/null") != 0 && stat(output_file, &st) == 0 && !S_ISREG(st.st_mode)) { cc_log("Not a regular file %s\n", output_file); stats_update(STATS_DEVICE); failed(); } if ((e=getenv("CCACHE_PREFIX"))) { char *p = find_executable(e, MYNAME); if (!p) { cc_log("could not find executable (%s)\n", e); stats_update(STATS_ENVIRONMMENT); perror(e); exit(1); } args_add_prefix(stripped_args, p); } }
/* process the compiler options to form the correct set of options for obtaining the preprocessor output */ static void process_args(int argc, char **argv) { int i; int found_c_opt = 0; int found_S_opt = 0; struct stat st; char *e; stripped_args = args_init(0, NULL); args_add(stripped_args, argv[0]); for (i=1; i<argc; i++) { /* some options will never work ... */ if (strcmp(argv[i], "-E") == 0) { failed(); } /* these are too hard */ if (strcmp(argv[i], "-fbranch-probabilities")==0 || strcmp(argv[i], "-M") == 0 || strcmp(argv[i], "-MM") == 0 || strcmp(argv[i], "-x") == 0) { cc_log("argument %s is unsupported\n", argv[i]); stats_update(STATS_UNSUPPORTED); failed(); continue; } /* we must have -c */ if (strcmp(argv[i], "-c") == 0) { args_add(stripped_args, argv[i]); found_c_opt = 1; continue; } /* -S changes the default extension */ if (strcmp(argv[i], "-S") == 0) { args_add(stripped_args, argv[i]); found_S_opt = 1; continue; } /* we need to work out where the output was meant to go */ if (strcmp(argv[i], "-o") == 0) { if (i == argc-1) { cc_log("missing argument to %s\n", argv[i]); stats_update(STATS_ARGS); failed(); } output_file = argv[i+1]; i++; continue; } /* alternate form of -o, with no space */ if (strncmp(argv[i], "-o", 2) == 0) { output_file = &argv[i][2]; continue; } /* debugging is handled specially, so that we know if we can strip line number info */ if (strncmp(argv[i], "-g", 2) == 0) { args_add(stripped_args, argv[i]); if (strcmp(argv[i], "-g0") != 0) { enable_unify = 0; } continue; } /* The user knows best: just swallow the next arg */ if (strcmp(argv[i], "--ccache-skip") == 0) { i++; if (i == argc) { failed(); } args_add(stripped_args, argv[i]); continue; } /* options that take an argument */ { const char *opts[] = {"-I", "-include", "-imacros", "-iprefix", "-iwithprefix", "-iwithprefixbefore", "-L", "-D", "-U", "-x", "-MF", "-MT", "-MQ", "-isystem", "-aux-info", "--param", "-A", "-Xlinker", "-u", "-idirafter", NULL}; int j; for (j=0;opts[j];j++) { if (strcmp(argv[i], opts[j]) == 0) { if (i == argc-1) { cc_log("missing argument to %s\n", argv[i]); stats_update(STATS_ARGS); failed(); } args_add(stripped_args, argv[i]); args_add(stripped_args, argv[i+1]); i++; break; } } if (opts[j]) continue; } /* other options */ if (argv[i][0] == '-') { args_add(stripped_args, argv[i]); continue; } /* if an argument isn't a plain file then assume its an option, not an input file. This allows us to cope better with unusual compiler options */ if (stat(argv[i], &st) != 0 || !S_ISREG(st.st_mode)) { args_add(stripped_args, argv[i]); continue; } if (input_file) { if (check_extension(argv[i], NULL)) { cc_log("multiple input files (%s and %s)\n", input_file, argv[i]); stats_update(STATS_MULTIPLE); } else if (!found_c_opt) { cc_log("called for link with %s\n", argv[i]); if (strstr(argv[i], "conftest.")) { stats_update(STATS_CONFTEST); } else { stats_update(STATS_LINK); } } else { cc_log("non C/C++ file %s\n", argv[i]); stats_update(STATS_NOTC); } failed(); } input_file = argv[i]; } if (!input_file) { cc_log("No input file found\n"); stats_update(STATS_NOINPUT); failed(); } i_extension = check_extension(input_file, &direct_i_file); if (i_extension == NULL) { cc_log("Not a C/C++ file - %s\n", input_file); stats_update(STATS_NOTC); failed(); } if (!found_c_opt) { cc_log("No -c option found for %s\n", input_file); /* I find that having a separate statistic for autoconf tests is useful, as they are the dominant form of "called for link" in many cases */ if (strstr(input_file, "conftest.")) { stats_update(STATS_CONFTEST); } else { stats_update(STATS_LINK); } failed(); } /* don't try to second guess the compilers heuristics for stdout handling */ if (output_file && strcmp(output_file, "-") == 0) { stats_update(STATS_OUTSTDOUT); failed(); } if (!output_file) { char *p; output_file = x_strdup(input_file); if ((p = strrchr(output_file, '/'))) { output_file = p+1; } p = strrchr(output_file, '.'); if (!p || !p[1]) { cc_log("badly formed output_file %s\n", output_file); stats_update(STATS_ARGS); failed(); } p[1] = found_S_opt ? 's' : 'o'; p[2] = 0; } /* cope with -o /dev/null */ if (strcmp(output_file,"/dev/null") != 0 && stat(output_file, &st) == 0 && !S_ISREG(st.st_mode)) { cc_log("Not a regular file %s\n", output_file); stats_update(STATS_DEVICE); failed(); } if ((e=getenv("CCACHE_PREFIX"))) { char *p = find_executable(e, MYNAME); if (!p) { perror(e); exit(1); } args_add_prefix(stripped_args, p); } }
/* something went badly wrong - just execute the real compiler */ static void failed(void) { char *e; /* delete intermediate pre-processor file if needed */ if (i_tmpfile) { if (!direct_i_file) { unlink(i_tmpfile); } free(i_tmpfile); i_tmpfile = NULL; } /* delete the cpp stderr file if necessary */ if (cpp_stderr) { unlink(cpp_stderr); free(cpp_stderr); cpp_stderr = NULL; } /* strip any local args */ args_strip(orig_args, "--ccache-"); if ((e=getenv("CCACHE_PREFIX"))) { char *p = find_executable(e, MYNAME); if (!p) { cc_log("could not find executable (%s)\n", e); perror(e); exit(1); } args_add_prefix(orig_args, p); } if (ccache_verbose) { display_execute_args(orig_args->argv); } if (swig) { putenv("CCACHE_OUTFILES"); } #ifndef _WIN32 execv(orig_args->argv[0], orig_args->argv); cc_log("execv returned (%s)!\n", strerror(errno)); perror(orig_args->argv[0]); exit(1); #else /* execv on Windows causes the 'non-regular' testcase to fail, so use Win32 API instead */ { PROCESS_INFORMATION pinfo; STARTUPINFO sinfo; BOOL ret; DWORD exitcode; char *args; ZeroMemory(&pinfo, sizeof(PROCESS_INFORMATION)); ZeroMemory(&sinfo, sizeof(STARTUPINFO)); sinfo.cb = sizeof(STARTUPINFO); args = argvtos(orig_args->argv); ret = CreateProcessA(orig_args->argv[0], args, NULL, NULL, TRUE, 0, NULL, NULL, &sinfo, &pinfo); if (!ret) { exitcode = 1; cc_log("CreateProcessA failed starting %s\n", orig_args->argv[0]); perror_win32(orig_args->argv[0]); } else { WaitForSingleObject(pinfo.hProcess, INFINITE); GetExitCodeProcess(pinfo.hProcess, &exitcode); CloseHandle(pinfo.hProcess); CloseHandle(pinfo.hThread); } free(args); exit(exitcode); } #endif }