📄 ccache.c
字号:
} /* get rid of the intermediate preprocessor file */ if (i_tmpfile) { if (!direct_i_file) { unlink(i_tmpfile); } free(i_tmpfile); i_tmpfile = NULL; } /* send the cpp stderr, if applicable */ fd_cpp_stderr = open(cpp_stderr, O_RDONLY); if (fd_cpp_stderr != -1) { copy_fd(fd_cpp_stderr, 2); close(fd_cpp_stderr); unlink(cpp_stderr); free(cpp_stderr); cpp_stderr = NULL; } /* send the stderr */ copy_fd(fd_stderr, 2); close(fd_stderr); /* and exit with the right status code */ if (first) { cc_log("got cached result for %s\n", output_file); stats_update(STATS_CACHED); } exit(0);}/* find the real compiler. We just search the PATH to find a executable of the same name that isn't a link to ourselves */static void find_compiler(int argc, char **argv){ char *base; char *path; orig_args = args_init(argc, argv); base = str_basename(argv[0]); /* we might be being invoked like "ccache gcc -c foo.c" */ if (strcmp(base, MYNAME) == 0) { args_remove_first(orig_args); free(base); if (strchr(argv[1],'/')) { /* a full path was given */ return; } base = str_basename(argv[1]); } /* support user override of the compiler */ if ((path=getenv("CCACHE_CC"))) { base = strdup(path); } orig_args->argv[0] = find_executable(base, MYNAME); /* can't find the compiler! */ if (!orig_args->argv[0]) { stats_update(STATS_COMPILER); perror(base); exit(1); }}/* check a filename for C/C++ extension. Return the pre-processor extension */static const char *check_extension(const char *fname, int *direct_i){ int i; const char *p; if (direct_i) { *direct_i = 0; } p = strrchr(fname, '.'); if (!p) return NULL; p++; for (i=0; extensions[i].extension; i++) { if (strcmp(p, extensions[i].extension) == 0) { if (direct_i && strcmp(p, extensions[i].i_extension) == 0) { *direct_i = 1; } p = getenv("CCACHE_EXTENSION"); if (p) return p; return extensions[i].i_extension; } } return NULL;}/* 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); }}/* the main ccache driver function */static void ccache(int argc, char *argv[]){ /* find the real compiler */ find_compiler(argc, argv); /* we might be disabled */ if (getenv("CCACHE_DISABLE")) { cc_log("ccache is disabled\n"); failed(); } if (getenv("CCACHE_UNIFY")) { enable_unify = 1; } /* process argument list, returning a new set of arguments for pre-processing */ process_args(orig_args->argc, orig_args->argv); /* run with -E to find the hash */ find_hash(stripped_args); /* if we can return from cache at this point then do */ from_cache(1); /* run real compiler, sending output to cache */ to_cache(stripped_args); /* return from cache */ from_cache(0); /* oh oh! */ cc_log("secondary from_cache failed!\n"); stats_update(STATS_ERROR); failed();}static void usage(void){ printf("ccache, a compiler cache. Version %s\n", CCACHE_VERSION); printf("Copyright Andrew Tridgell, 2002\n\n"); printf("Usage:\n"); printf("\tccache [options]\n"); printf("\tccache compiler [compile options]\n"); printf("\tcompiler [compile options] (via symbolic link)\n"); printf("\nOptions:\n"); printf("-s show statistics summary\n"); printf("-z zero statistics\n"); printf("-c run a cache cleanup\n"); printf("-C clear the cache completely\n"); printf("-F <maxfiles> set maximum files in cache\n"); printf("-M <maxsize> set maximum size of cache (use G, M or K)\n"); printf("-h this help page\n"); printf("-V print version number\n");}/* the main program when not doing a compile */static int ccache_main(int argc, char *argv[]){ extern int optind; int c; size_t v; while ((c = getopt(argc, argv, "hszcCF:M:V")) != -1) { switch (c) { case 'V': printf("ccache version %s\n", CCACHE_VERSION); printf("Copyright Andrew Tridgell 2002\n"); printf("Released under the GNU GPL v2 or later\n"); exit(0); case 'h': usage(); exit(0); case 's': stats_summary(); break; case 'c': cleanup_all(cache_dir); printf("Cleaned cache\n"); break; case 'C': wipe_all(cache_dir); printf("Cleared cache\n"); break; case 'z': stats_zero(); printf("Statistics cleared\n"); break; case 'F': v = atoi(optarg); stats_set_limits(v, -1); printf("Set cache file limit to %u\n", (unsigned)v); break; case 'M': v = value_units(optarg); stats_set_limits(-1, v); printf("Set cache size limit to %uk\n", (unsigned)v); break; default: usage(); exit(1); } } return 0;}/* Make a copy of stderr that will not be cached, so things like distcc can send networking errors to it. */static void setup_uncached_err(void){ char *buf; int uncached_fd; uncached_fd = dup(2); if (uncached_fd == -1) { cc_log("dup(2) failed\n"); failed(); } /* leak a pointer to the environment */ x_asprintf(&buf, "UNCACHED_ERR_FD=%d", uncached_fd); if (putenv(buf) == -1) { cc_log("putenv failed\n"); failed(); }}int main(int argc, char *argv[]){ char *p; cache_dir = getenv("CCACHE_DIR"); if (!cache_dir) { x_asprintf(&cache_dir, "%s/.ccache", getenv("HOME")); } cache_logfile = getenv("CCACHE_LOGFILE"); setup_uncached_err(); /* the user might have set CCACHE_UMASK */ p = getenv("CCACHE_UMASK"); if (p) { mode_t mask; errno = 0; mask = strtol(p, NULL, 8); if (errno == 0) { umask(mask); } } /* check if we are being invoked as "ccache" */ if (strlen(argv[0]) >= strlen(MYNAME) && strcmp(argv[0] + strlen(argv[0]) - strlen(MYNAME), MYNAME) == 0) { if (argc < 2) { usage(); exit(1); } /* if the first argument isn't an option, then assume we are being passed a compiler name and options */ if (argv[1][0] == '-') { return ccache_main(argc, argv); } } /* make sure the cache dir exists */ if (create_dir(cache_dir) != 0) { fprintf(stderr,"ccache: failed to create %s (%s)\n", cache_dir, strerror(errno)); exit(1); } ccache(argc, argv); return 1;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -