2002-03-26 14:46:43 +00:00
|
|
|
/*
|
|
|
|
a re-implementation of the compilercache scripts in C
|
|
|
|
|
|
|
|
The idea is based on the shell-script compilercache by Erik Thiele <erikyyy@erikyyy.de>
|
2002-03-26 23:58:31 +00:00
|
|
|
|
|
|
|
Copyright (C) Andrew Tridgell 2002
|
2003-09-24 07:10:32 +00:00
|
|
|
Copyright (C) Martin Pool 2003
|
2002-03-26 23:58:31 +00:00
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
2002-03-26 14:46:43 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "ccache.h"
|
2009-11-01 18:39:58 +00:00
|
|
|
#include <getopt.h>
|
2002-03-26 14:46:43 +00:00
|
|
|
|
2002-04-07 13:20:53 +00:00
|
|
|
/* the base cache directory */
|
2002-03-30 10:43:26 +00:00
|
|
|
char *cache_dir = NULL;
|
2002-04-07 13:20:53 +00:00
|
|
|
|
2004-09-10 05:05:14 +00:00
|
|
|
/* the directory for temporary files */
|
|
|
|
static char *temp_dir = NULL;
|
|
|
|
|
2002-04-07 13:20:53 +00:00
|
|
|
/* the debug logfile name, if set */
|
2002-03-26 23:58:31 +00:00
|
|
|
char *cache_logfile = NULL;
|
2002-04-07 13:20:53 +00:00
|
|
|
|
|
|
|
/* the argument list after processing */
|
2002-03-26 14:46:43 +00:00
|
|
|
static ARGS *stripped_args;
|
2002-04-07 13:20:53 +00:00
|
|
|
|
|
|
|
/* the original argument list */
|
2002-03-26 14:46:43 +00:00
|
|
|
static ARGS *orig_args;
|
2002-04-07 13:20:53 +00:00
|
|
|
|
|
|
|
/* the output filename being compiled to */
|
2002-03-26 14:46:43 +00:00
|
|
|
static char *output_file;
|
2002-04-07 13:20:53 +00:00
|
|
|
|
|
|
|
/* the source file */
|
2002-04-02 04:09:56 +00:00
|
|
|
static char *input_file;
|
2002-04-07 13:20:53 +00:00
|
|
|
|
|
|
|
/* the name of the file containing the cached object code */
|
2002-03-26 14:46:43 +00:00
|
|
|
static char *hashname;
|
2002-04-07 13:20:53 +00:00
|
|
|
|
|
|
|
/* the extension of the file after pre-processing */
|
2003-01-06 02:10:15 +00:00
|
|
|
static const char *i_extension;
|
2002-04-07 13:20:53 +00:00
|
|
|
|
|
|
|
/* the name of the temporary pre-processor file */
|
|
|
|
static char *i_tmpfile;
|
|
|
|
|
2003-09-25 05:41:17 +00:00
|
|
|
/* are we compiling a .i or .ii file directly? */
|
|
|
|
static int direct_i_file;
|
|
|
|
|
2002-05-12 09:51:36 +00:00
|
|
|
/* the name of the cpp stderr file */
|
|
|
|
static char *cpp_stderr;
|
|
|
|
|
2002-04-07 13:20:53 +00:00
|
|
|
/* the name of the statistics file */
|
2002-03-30 10:43:26 +00:00
|
|
|
char *stats_file = NULL;
|
2002-04-07 13:20:53 +00:00
|
|
|
|
2003-09-25 05:41:17 +00:00
|
|
|
/* can we safely use the unification hashing backend? */
|
|
|
|
static int enable_unify;
|
2002-03-26 14:46:43 +00:00
|
|
|
|
2002-04-07 13:20:53 +00:00
|
|
|
/* a list of supported file extensions, and the equivalent
|
|
|
|
extension for code that has been through the pre-processor
|
|
|
|
*/
|
|
|
|
static struct {
|
|
|
|
char *extension;
|
|
|
|
char *i_extension;
|
|
|
|
} extensions[] = {
|
|
|
|
{"c", "i"},
|
|
|
|
{"C", "ii"},
|
|
|
|
{"m", "mi"},
|
|
|
|
{"cc", "ii"},
|
|
|
|
{"CC", "ii"},
|
|
|
|
{"cpp", "ii"},
|
|
|
|
{"CPP", "ii"},
|
|
|
|
{"cxx", "ii"},
|
|
|
|
{"CXX", "ii"},
|
2002-04-24 13:13:05 +00:00
|
|
|
{"c++", "ii"},
|
|
|
|
{"C++", "ii"},
|
2003-09-25 05:41:17 +00:00
|
|
|
{"i", "i"},
|
|
|
|
{"ii", "ii"},
|
2002-04-07 13:20:53 +00:00
|
|
|
{NULL, NULL}};
|
|
|
|
|
2002-03-26 23:58:31 +00:00
|
|
|
/*
|
|
|
|
something went badly wrong - just execute the real compiler
|
|
|
|
*/
|
2002-03-26 14:46:43 +00:00
|
|
|
static void failed(void)
|
|
|
|
{
|
2003-02-16 02:28:35 +00:00
|
|
|
char *e;
|
|
|
|
|
2002-04-07 13:20:53 +00:00
|
|
|
/* delete intermediate pre-processor file if needed */
|
|
|
|
if (i_tmpfile) {
|
2003-09-25 05:41:17 +00:00
|
|
|
if (!direct_i_file) {
|
|
|
|
unlink(i_tmpfile);
|
|
|
|
}
|
2002-04-07 13:20:53 +00:00
|
|
|
free(i_tmpfile);
|
|
|
|
i_tmpfile = NULL;
|
|
|
|
}
|
2002-05-12 09:51:36 +00:00
|
|
|
|
|
|
|
/* delete the cpp stderr file if necessary */
|
|
|
|
if (cpp_stderr) {
|
|
|
|
unlink(cpp_stderr);
|
|
|
|
free(cpp_stderr);
|
|
|
|
cpp_stderr = NULL;
|
|
|
|
}
|
|
|
|
|
2003-02-08 05:49:22 +00:00
|
|
|
/* strip any local args */
|
|
|
|
args_strip(orig_args, "--ccache-");
|
|
|
|
|
2003-02-16 02:28:35 +00:00
|
|
|
if ((e=getenv("CCACHE_PREFIX"))) {
|
|
|
|
char *p = find_executable(e, MYNAME);
|
|
|
|
if (!p) {
|
|
|
|
perror(e);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
args_add_prefix(orig_args, p);
|
|
|
|
}
|
|
|
|
|
2002-03-26 14:46:43 +00:00
|
|
|
execv(orig_args->argv[0], orig_args->argv);
|
|
|
|
cc_log("execv returned (%s)!\n", strerror(errno));
|
2002-04-03 14:45:51 +00:00
|
|
|
perror(orig_args->argv[0]);
|
2002-03-26 14:46:43 +00:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
2002-05-29 12:22:21 +00:00
|
|
|
|
|
|
|
/* return a string to be used to distinguish temporary files
|
|
|
|
this also tries to cope with NFS by adding the local hostname
|
|
|
|
*/
|
|
|
|
static const char *tmp_string(void)
|
|
|
|
{
|
|
|
|
static char *ret;
|
|
|
|
|
|
|
|
if (!ret) {
|
|
|
|
char hostname[200];
|
|
|
|
strcpy(hostname, "unknown");
|
2003-03-07 12:09:19 +00:00
|
|
|
#if HAVE_GETHOSTNAME
|
2002-05-29 12:22:21 +00:00
|
|
|
gethostname(hostname, sizeof(hostname)-1);
|
2003-03-07 12:09:19 +00:00
|
|
|
#endif
|
2002-05-29 12:22:21 +00:00
|
|
|
hostname[sizeof(hostname)-1] = 0;
|
|
|
|
asprintf(&ret, "%s.%u", hostname, (unsigned)getpid());
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-03-26 14:46:43 +00:00
|
|
|
/* run the real compiler and put the result in cache */
|
|
|
|
static void to_cache(ARGS *args)
|
|
|
|
{
|
2002-03-27 00:39:06 +00:00
|
|
|
char *path_stderr;
|
|
|
|
char *tmp_stdout, *tmp_stderr, *tmp_hashname;
|
2002-03-30 10:43:26 +00:00
|
|
|
struct stat st1, st2;
|
2002-03-27 00:39:06 +00:00
|
|
|
int status;
|
2002-03-26 14:46:43 +00:00
|
|
|
|
2006-07-17 01:09:30 +00:00
|
|
|
x_asprintf(&tmp_stdout, "%s.tmp.stdout.%s", hashname, tmp_string());
|
|
|
|
x_asprintf(&tmp_stderr, "%s.tmp.stderr.%s", hashname, tmp_string());
|
|
|
|
x_asprintf(&tmp_hashname, "%s.tmp.%s", hashname, tmp_string());
|
2002-03-26 14:46:43 +00:00
|
|
|
|
|
|
|
args_add(args, "-o");
|
2002-03-27 00:39:06 +00:00
|
|
|
args_add(args, tmp_hashname);
|
2003-09-24 07:10:32 +00:00
|
|
|
|
|
|
|
/* Turn off DEPENDENCIES_OUTPUT when running cc1, because
|
|
|
|
* otherwise it will emit a line like
|
|
|
|
*
|
|
|
|
* tmp.stdout.vexed.732.o: /home/mbp/.ccache/tmp.stdout.vexed.732.i
|
|
|
|
*
|
|
|
|
* unsetenv() is on BSD and Linux but not portable. */
|
|
|
|
putenv("DEPENDENCIES_OUTPUT");
|
2002-04-07 13:20:53 +00:00
|
|
|
|
|
|
|
if (getenv("CCACHE_CPP2")) {
|
|
|
|
args_add(args, input_file);
|
|
|
|
} else {
|
|
|
|
args_add(args, i_tmpfile);
|
|
|
|
}
|
2002-03-27 00:39:06 +00:00
|
|
|
status = execute(args->argv, tmp_stdout, tmp_stderr);
|
2002-04-07 13:20:53 +00:00
|
|
|
args_pop(args, 3);
|
2002-03-26 14:46:43 +00:00
|
|
|
|
2002-03-30 10:43:26 +00:00
|
|
|
if (stat(tmp_stdout, &st1) != 0 || st1.st_size != 0) {
|
2002-03-27 01:29:53 +00:00
|
|
|
cc_log("compiler produced stdout for %s\n", output_file);
|
2002-03-30 10:43:26 +00:00
|
|
|
stats_update(STATS_STDOUT);
|
2002-03-27 00:39:06 +00:00
|
|
|
unlink(tmp_stdout);
|
|
|
|
unlink(tmp_stderr);
|
|
|
|
unlink(tmp_hashname);
|
|
|
|
failed();
|
|
|
|
}
|
2002-03-27 01:29:53 +00:00
|
|
|
unlink(tmp_stdout);
|
2002-03-26 14:46:43 +00:00
|
|
|
|
2002-03-27 01:29:53 +00:00
|
|
|
if (status != 0) {
|
|
|
|
int fd;
|
|
|
|
cc_log("compile of %s gave status = %d\n", output_file, status);
|
2002-03-30 10:43:26 +00:00
|
|
|
stats_update(STATS_STATUS);
|
2002-03-27 01:29:53 +00:00
|
|
|
|
2004-09-06 12:24:05 +00:00
|
|
|
fd = open(tmp_stderr, O_RDONLY | O_BINARY);
|
2002-04-08 03:53:14 +00:00
|
|
|
if (fd != -1) {
|
|
|
|
if (strcmp(output_file, "/dev/null") == 0 ||
|
2009-11-01 18:39:58 +00:00
|
|
|
move_file(tmp_hashname, output_file) == 0 || errno == ENOENT) {
|
2002-05-12 09:51:36 +00:00
|
|
|
if (cpp_stderr) {
|
|
|
|
/* we might have some stderr from cpp */
|
2004-09-06 12:24:05 +00:00
|
|
|
int fd2 = open(cpp_stderr, O_RDONLY | O_BINARY);
|
2002-05-12 09:51:36 +00:00
|
|
|
if (fd2 != -1) {
|
|
|
|
copy_fd(fd2, 2);
|
|
|
|
close(fd2);
|
|
|
|
unlink(cpp_stderr);
|
|
|
|
cpp_stderr = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* we can use a quick method of
|
|
|
|
getting the failed output */
|
2002-04-08 03:53:14 +00:00
|
|
|
copy_fd(fd, 2);
|
|
|
|
close(fd);
|
|
|
|
unlink(tmp_stderr);
|
2003-09-25 05:41:17 +00:00
|
|
|
if (i_tmpfile && !direct_i_file) {
|
2002-04-08 03:53:14 +00:00
|
|
|
unlink(i_tmpfile);
|
|
|
|
}
|
|
|
|
exit(status);
|
2002-04-07 14:03:15 +00:00
|
|
|
}
|
2002-03-27 01:29:53 +00:00
|
|
|
}
|
|
|
|
|
2002-03-27 00:39:06 +00:00
|
|
|
unlink(tmp_stderr);
|
|
|
|
unlink(tmp_hashname);
|
|
|
|
failed();
|
|
|
|
}
|
2002-03-26 14:46:43 +00:00
|
|
|
|
2002-03-27 00:39:06 +00:00
|
|
|
x_asprintf(&path_stderr, "%s.stderr", hashname);
|
|
|
|
|
2002-03-30 10:43:26 +00:00
|
|
|
if (stat(tmp_stderr, &st1) != 0 ||
|
2009-11-01 18:39:58 +00:00
|
|
|
stat(tmp_hashname, &st2) != 0 ||
|
|
|
|
move_file(tmp_hashname, hashname) != 0 ||
|
|
|
|
move_file(tmp_stderr, path_stderr) != 0) {
|
2002-06-03 03:22:17 +00:00
|
|
|
cc_log("failed to rename tmp files - %s\n", strerror(errno));
|
2002-03-30 10:43:26 +00:00
|
|
|
stats_update(STATS_ERROR);
|
2002-03-26 14:46:43 +00:00
|
|
|
failed();
|
|
|
|
}
|
|
|
|
|
2009-11-01 18:39:58 +00:00
|
|
|
#if ENABLE_ZLIB
|
|
|
|
/* do an extra stat on the cache files for
|
|
|
|
the size statistics */
|
|
|
|
if (stat(path_stderr, &st1) != 0 ||
|
|
|
|
stat(hashname, &st2) != 0) {
|
|
|
|
cc_log("failed to stat cache files - %s\n", strerror(errno));
|
|
|
|
stats_update(STATS_ERROR);
|
|
|
|
failed();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2002-03-26 14:46:43 +00:00
|
|
|
cc_log("Placed %s into cache\n", output_file);
|
2002-03-30 10:43:26 +00:00
|
|
|
stats_tocache(file_size(&st1) + file_size(&st2));
|
2002-03-27 00:39:06 +00:00
|
|
|
|
|
|
|
free(tmp_hashname);
|
|
|
|
free(tmp_stderr);
|
|
|
|
free(tmp_stdout);
|
|
|
|
free(path_stderr);
|
2002-03-26 14:46:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* find the hash for a command. The hash includes all argument lists,
|
|
|
|
plus the output from running the compiler with -E */
|
|
|
|
static void find_hash(ARGS *args)
|
|
|
|
{
|
|
|
|
int i;
|
2002-03-27 00:39:06 +00:00
|
|
|
char *path_stdout, *path_stderr;
|
2002-03-26 15:11:48 +00:00
|
|
|
char *hash_dir;
|
|
|
|
char *s;
|
2002-03-26 22:25:14 +00:00
|
|
|
struct stat st;
|
2002-03-27 00:39:06 +00:00
|
|
|
int status;
|
2002-04-03 14:45:51 +00:00
|
|
|
int nlevels = 2;
|
2003-09-24 06:39:27 +00:00
|
|
|
char *input_base;
|
|
|
|
char *tmp;
|
|
|
|
|
2002-04-03 14:45:51 +00:00
|
|
|
if ((s = getenv("CCACHE_NLEVELS"))) {
|
|
|
|
nlevels = atoi(s);
|
|
|
|
if (nlevels < 1) nlevels = 1;
|
|
|
|
if (nlevels > 8) nlevels = 8;
|
|
|
|
}
|
2002-03-26 14:46:43 +00:00
|
|
|
|
|
|
|
hash_start();
|
|
|
|
|
2002-04-23 02:10:22 +00:00
|
|
|
/* when we are doing the unifying tricks we need to include
|
|
|
|
the input file name in the hash to get the warnings right */
|
2003-09-25 05:41:17 +00:00
|
|
|
if (enable_unify) {
|
2002-04-23 02:10:22 +00:00
|
|
|
hash_string(input_file);
|
|
|
|
}
|
|
|
|
|
2003-09-25 05:49:50 +00:00
|
|
|
/* we have to hash the extension, as a .i file isn't treated the same
|
|
|
|
by the compiler as a .ii file */
|
|
|
|
hash_string(i_extension);
|
|
|
|
|
2002-03-26 14:46:43 +00:00
|
|
|
/* first the arguments */
|
2002-04-02 03:29:05 +00:00
|
|
|
for (i=1;i<args->argc;i++) {
|
2002-03-27 06:30:31 +00:00
|
|
|
/* some arguments don't contribute to the hash. The
|
|
|
|
theory is that these arguments will change the
|
|
|
|
output of -E if they are going to have any effect
|
|
|
|
at all, or they only affect linking */
|
|
|
|
if (i < args->argc-1) {
|
|
|
|
if (strcmp(args->argv[i], "-I") == 0 ||
|
|
|
|
strcmp(args->argv[i], "-include") == 0 ||
|
|
|
|
strcmp(args->argv[i], "-L") == 0 ||
|
|
|
|
strcmp(args->argv[i], "-D") == 0 ||
|
2003-09-27 12:30:55 +00:00
|
|
|
strcmp(args->argv[i], "-idirafter") == 0 ||
|
2002-03-27 06:30:31 +00:00
|
|
|
strcmp(args->argv[i], "-isystem") == 0) {
|
|
|
|
i++;
|
|
|
|
continue;
|
|
|
|
}
|
2002-04-08 01:32:07 +00:00
|
|
|
}
|
|
|
|
if (strncmp(args->argv[i], "-I", 2) == 0 ||
|
|
|
|
strncmp(args->argv[i], "-L", 2) == 0 ||
|
|
|
|
strncmp(args->argv[i], "-D", 2) == 0 ||
|
2003-09-27 12:30:55 +00:00
|
|
|
strncmp(args->argv[i], "-idirafter", 10) == 0 ||
|
2002-04-08 01:32:07 +00:00
|
|
|
strncmp(args->argv[i], "-isystem", 8) == 0) {
|
|
|
|
continue;
|
2002-03-27 06:30:31 +00:00
|
|
|
}
|
2002-04-09 03:39:20 +00:00
|
|
|
|
|
|
|
if (strncmp(args->argv[i], "--specs=", 8) == 0 &&
|
|
|
|
stat(args->argv[i]+8, &st) == 0) {
|
|
|
|
/* if given a explicit specs file, then hash that file, but
|
|
|
|
don't include the path to it in the hash */
|
|
|
|
hash_file(args->argv[i]+8);
|
|
|
|
continue;
|
|
|
|
}
|
2002-04-09 03:47:29 +00:00
|
|
|
|
|
|
|
/* all other arguments are included in the hash */
|
2002-03-26 14:46:43 +00:00
|
|
|
hash_string(args->argv[i]);
|
|
|
|
}
|
|
|
|
|
2002-03-26 22:25:14 +00:00
|
|
|
/* the compiler driver size and date. This is a simple minded way
|
|
|
|
to try and detect compiler upgrades. It is not 100% reliable */
|
|
|
|
if (stat(args->argv[0], &st) != 0) {
|
2003-02-16 02:28:35 +00:00
|
|
|
cc_log("Couldn't stat the compiler!? (argv[0]='%s')\n", args->argv[0]);
|
2002-03-30 10:43:26 +00:00
|
|
|
stats_update(STATS_COMPILER);
|
2002-03-26 22:25:14 +00:00
|
|
|
failed();
|
|
|
|
}
|
2004-09-13 10:19:06 +00:00
|
|
|
|
|
|
|
/* also include the hash of the compiler name - as some compilers
|
|
|
|
use hard links and behave differently depending on the real name */
|
|
|
|
if (st.st_nlink > 1) {
|
|
|
|
hash_string(str_basename(args->argv[0]));
|
|
|
|
}
|
|
|
|
|
2005-11-24 21:54:09 +00:00
|
|
|
if (getenv("CCACHE_HASH_COMPILER")) {
|
|
|
|
hash_file(args->argv[0]);
|
|
|
|
} else if (!getenv("CCACHE_NOHASH_SIZE_MTIME")) {
|
2005-11-24 21:10:08 +00:00
|
|
|
hash_int(st.st_size);
|
|
|
|
hash_int(st.st_mtime);
|
|
|
|
}
|
2002-03-26 22:25:14 +00:00
|
|
|
|
2003-01-07 05:19:00 +00:00
|
|
|
/* possibly hash the current working directory */
|
|
|
|
if (getenv("CCACHE_HASHDIR")) {
|
|
|
|
char *cwd = gnu_getcwd();
|
|
|
|
if (cwd) {
|
|
|
|
hash_string(cwd);
|
|
|
|
free(cwd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-09-24 06:39:27 +00:00
|
|
|
/* ~/hello.c -> tmp.hello.123.i
|
|
|
|
limit the basename to 10
|
|
|
|
characters in order to cope with filesystem with small
|
|
|
|
maximum filename length limits */
|
2003-09-25 06:11:28 +00:00
|
|
|
input_base = str_basename(input_file);
|
2003-09-24 06:39:27 +00:00
|
|
|
tmp = strchr(input_base, '.');
|
|
|
|
if (tmp != NULL) {
|
|
|
|
*tmp = 0;
|
|
|
|
}
|
|
|
|
if (strlen(input_base) > 10) {
|
|
|
|
input_base[10] = 0;
|
|
|
|
}
|
|
|
|
|
2002-03-26 14:46:43 +00:00
|
|
|
/* now the run */
|
2004-09-10 05:05:14 +00:00
|
|
|
x_asprintf(&path_stdout, "%s/%s.tmp.%s.%s", temp_dir,
|
2003-09-24 06:39:27 +00:00
|
|
|
input_base, tmp_string(),
|
2002-04-07 13:20:53 +00:00
|
|
|
i_extension);
|
2004-09-10 05:05:14 +00:00
|
|
|
x_asprintf(&path_stderr, "%s/tmp.cpp_stderr.%s", temp_dir, tmp_string());
|
2002-03-26 14:46:43 +00:00
|
|
|
|
2003-09-25 05:41:17 +00:00
|
|
|
if (!direct_i_file) {
|
|
|
|
/* run cpp on the input file to obtain the .i */
|
|
|
|
args_add(args, "-E");
|
|
|
|
args_add(args, input_file);
|
|
|
|
status = execute(args->argv, path_stdout, path_stderr);
|
|
|
|
args_pop(args, 2);
|
|
|
|
} else {
|
2003-09-25 05:49:50 +00:00
|
|
|
/* we are compiling a .i or .ii file - that means we
|
|
|
|
can skip the cpp stage and directly form the
|
|
|
|
correct i_tmpfile */
|
2003-09-25 05:41:17 +00:00
|
|
|
path_stdout = input_file;
|
|
|
|
if (create_empty_file(path_stderr) != 0) {
|
|
|
|
stats_update(STATS_ERROR);
|
|
|
|
cc_log("failed to create empty stderr file\n");
|
|
|
|
failed();
|
|
|
|
}
|
|
|
|
status = 0;
|
|
|
|
}
|
2002-03-26 14:46:43 +00:00
|
|
|
|
2002-03-27 00:39:06 +00:00
|
|
|
if (status != 0) {
|
2003-09-25 05:41:17 +00:00
|
|
|
if (!direct_i_file) {
|
|
|
|
unlink(path_stdout);
|
|
|
|
}
|
2002-03-27 00:39:06 +00:00
|
|
|
unlink(path_stderr);
|
|
|
|
cc_log("the preprocessor gave %d\n", status);
|
2002-03-30 10:43:26 +00:00
|
|
|
stats_update(STATS_PREPROCESSOR);
|
2002-03-27 00:39:06 +00:00
|
|
|
failed();
|
|
|
|
}
|
|
|
|
|
2003-09-25 05:41:17 +00:00
|
|
|
/* if the compilation is with -g then we have to include the whole of the
|
2002-03-26 22:25:14 +00:00
|
|
|
preprocessor output, which means we are sensitive to line number
|
|
|
|
information. Otherwise we can discard line number info, which makes
|
|
|
|
us less sensitive to reformatting changes
|
2002-04-29 09:12:46 +00:00
|
|
|
|
|
|
|
Note! I have now disabled the unification code by default
|
|
|
|
as it gives the wrong line numbers for warnings. Pity.
|
2002-03-26 22:25:14 +00:00
|
|
|
*/
|
2003-09-25 05:41:17 +00:00
|
|
|
if (!enable_unify) {
|
2002-03-26 14:46:43 +00:00
|
|
|
hash_file(path_stdout);
|
|
|
|
} else {
|
2002-04-01 00:23:31 +00:00
|
|
|
if (unify_hash(path_stdout) != 0) {
|
2002-04-18 10:26:47 +00:00
|
|
|
stats_update(STATS_ERROR);
|
2002-04-01 00:23:31 +00:00
|
|
|
failed();
|
|
|
|
}
|
2002-03-26 14:46:43 +00:00
|
|
|
}
|
|
|
|
hash_file(path_stderr);
|
|
|
|
|
2002-04-07 13:20:53 +00:00
|
|
|
i_tmpfile = path_stdout;
|
2002-05-12 09:51:36 +00:00
|
|
|
|
|
|
|
if (!getenv("CCACHE_CPP2")) {
|
|
|
|
/* if we are using the CPP trick then we need to remember this stderr
|
|
|
|
data and output it just before the main stderr from the compiler
|
|
|
|
pass */
|
|
|
|
cpp_stderr = path_stderr;
|
|
|
|
} else {
|
|
|
|
unlink(path_stderr);
|
|
|
|
free(path_stderr);
|
|
|
|
}
|
2002-03-26 14:46:43 +00:00
|
|
|
|
2002-04-03 14:45:51 +00:00
|
|
|
/* we use a N level subdir for the cache path to reduce the impact
|
2002-03-26 23:58:31 +00:00
|
|
|
on filesystems which are slow for large directories
|
|
|
|
*/
|
2002-03-26 15:11:48 +00:00
|
|
|
s = hash_result();
|
2002-04-03 14:45:51 +00:00
|
|
|
x_asprintf(&hash_dir, "%s/%c", cache_dir, s[0]);
|
|
|
|
x_asprintf(&stats_file, "%s/stats", hash_dir);
|
|
|
|
for (i=1; i<nlevels; i++) {
|
|
|
|
char *p;
|
|
|
|
if (create_dir(hash_dir) != 0) {
|
|
|
|
cc_log("failed to create %s\n", hash_dir);
|
|
|
|
failed();
|
|
|
|
}
|
|
|
|
x_asprintf(&p, "%s/%c", hash_dir, s[i]);
|
|
|
|
free(hash_dir);
|
|
|
|
hash_dir = p;
|
2002-04-02 05:04:49 +00:00
|
|
|
}
|
2002-03-26 22:25:14 +00:00
|
|
|
if (create_dir(hash_dir) != 0) {
|
2002-04-02 05:04:49 +00:00
|
|
|
cc_log("failed to create %s\n", hash_dir);
|
2002-03-26 22:25:14 +00:00
|
|
|
failed();
|
|
|
|
}
|
2002-04-03 14:45:51 +00:00
|
|
|
x_asprintf(&hashname, "%s/%s", hash_dir, s+nlevels);
|
2002-03-26 15:11:48 +00:00
|
|
|
free(hash_dir);
|
2002-03-26 14:46:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
try to return the compile result from cache. If we can return from
|
|
|
|
cache then this function exits with the correct status code,
|
|
|
|
otherwise it returns */
|
|
|
|
static void from_cache(int first)
|
|
|
|
{
|
2002-05-12 09:51:36 +00:00
|
|
|
int fd_stderr, fd_cpp_stderr;
|
2002-03-28 04:22:40 +00:00
|
|
|
char *stderr_file;
|
2002-03-27 00:39:06 +00:00
|
|
|
int ret;
|
2002-03-30 10:43:26 +00:00
|
|
|
struct stat st;
|
2002-03-26 14:46:43 +00:00
|
|
|
|
2002-03-28 04:22:40 +00:00
|
|
|
x_asprintf(&stderr_file, "%s.stderr", hashname);
|
2004-09-06 12:24:05 +00:00
|
|
|
fd_stderr = open(stderr_file, O_RDONLY | O_BINARY);
|
2002-03-26 14:46:43 +00:00
|
|
|
if (fd_stderr == -1) {
|
2002-03-27 00:39:06 +00:00
|
|
|
/* it isn't in cache ... */
|
2002-03-28 04:22:40 +00:00
|
|
|
free(stderr_file);
|
2002-03-26 14:46:43 +00:00
|
|
|
return;
|
|
|
|
}
|
2002-03-30 10:43:26 +00:00
|
|
|
|
|
|
|
/* make sure the output is there too */
|
|
|
|
if (stat(hashname, &st) != 0) {
|
|
|
|
close(fd_stderr);
|
|
|
|
unlink(stderr_file);
|
|
|
|
free(stderr_file);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2003-01-06 00:20:54 +00:00
|
|
|
/* the user might be disabling cache hits */
|
2009-11-01 18:39:58 +00:00
|
|
|
#ifndef ENABLE_ZLIB
|
|
|
|
/* if the cache file is compressed we must recache */
|
|
|
|
if ((first && getenv("CCACHE_RECACHE")) ||
|
|
|
|
test_if_compressed(hashname) == 1) {
|
|
|
|
#else
|
2003-01-06 00:20:54 +00:00
|
|
|
if (first && getenv("CCACHE_RECACHE")) {
|
2009-11-01 18:39:58 +00:00
|
|
|
#endif
|
2003-01-06 00:20:54 +00:00
|
|
|
close(fd_stderr);
|
|
|
|
unlink(stderr_file);
|
|
|
|
free(stderr_file);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2002-03-28 04:22:40 +00:00
|
|
|
utime(stderr_file, NULL);
|
2002-03-26 14:46:43 +00:00
|
|
|
|
2002-04-08 03:53:14 +00:00
|
|
|
if (strcmp(output_file, "/dev/null") == 0) {
|
|
|
|
ret = 0;
|
2002-04-01 06:58:31 +00:00
|
|
|
} else {
|
2002-04-08 03:53:14 +00:00
|
|
|
unlink(output_file);
|
2009-11-01 18:39:58 +00:00
|
|
|
/* only make a hardlink if the cache file is uncompressed */
|
|
|
|
if (getenv("CCACHE_HARDLINK") &&
|
|
|
|
test_if_compressed(hashname) == 0) {
|
2002-04-08 03:53:14 +00:00
|
|
|
ret = link(hashname, output_file);
|
2003-02-17 00:46:15 +00:00
|
|
|
} else {
|
|
|
|
ret = copy_file(hashname, output_file);
|
2002-04-08 03:53:14 +00:00
|
|
|
}
|
2002-04-01 06:58:31 +00:00
|
|
|
}
|
2002-03-28 05:48:39 +00:00
|
|
|
|
|
|
|
/* the hash file might have been deleted by some external process */
|
|
|
|
if (ret == -1 && errno == ENOENT) {
|
|
|
|
cc_log("hashfile missing for %s\n", output_file);
|
2002-03-30 10:43:26 +00:00
|
|
|
stats_update(STATS_MISSING);
|
2002-03-28 05:48:39 +00:00
|
|
|
close(fd_stderr);
|
|
|
|
unlink(stderr_file);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
free(stderr_file);
|
|
|
|
|
2002-03-28 21:38:41 +00:00
|
|
|
if (ret == -1) {
|
2002-03-27 05:40:28 +00:00
|
|
|
ret = copy_file(hashname, output_file);
|
2002-03-28 21:38:41 +00:00
|
|
|
if (ret == -1) {
|
2002-03-27 05:40:28 +00:00
|
|
|
cc_log("failed to copy %s -> %s (%s)\n",
|
|
|
|
hashname, output_file, strerror(errno));
|
2002-03-30 10:43:26 +00:00
|
|
|
stats_update(STATS_ERROR);
|
2002-03-27 05:40:28 +00:00
|
|
|
failed();
|
|
|
|
}
|
2002-03-26 22:25:14 +00:00
|
|
|
}
|
|
|
|
if (ret == 0) {
|
2002-03-27 09:46:40 +00:00
|
|
|
/* update the mtime on the file so that make doesn't get confused */
|
2002-03-26 22:25:14 +00:00
|
|
|
utime(output_file, NULL);
|
2002-03-26 14:46:43 +00:00
|
|
|
}
|
|
|
|
|
2002-04-07 13:20:53 +00:00
|
|
|
/* get rid of the intermediate preprocessor file */
|
|
|
|
if (i_tmpfile) {
|
2003-09-25 05:41:17 +00:00
|
|
|
if (!direct_i_file) {
|
|
|
|
unlink(i_tmpfile);
|
|
|
|
}
|
2002-04-07 13:20:53 +00:00
|
|
|
free(i_tmpfile);
|
|
|
|
i_tmpfile = NULL;
|
|
|
|
}
|
|
|
|
|
2002-05-12 09:51:36 +00:00
|
|
|
/* send the cpp stderr, if applicable */
|
2004-09-06 12:24:05 +00:00
|
|
|
fd_cpp_stderr = open(cpp_stderr, O_RDONLY | O_BINARY);
|
2002-05-12 09:51:36 +00:00
|
|
|
if (fd_cpp_stderr != -1) {
|
|
|
|
copy_fd(fd_cpp_stderr, 2);
|
|
|
|
close(fd_cpp_stderr);
|
|
|
|
unlink(cpp_stderr);
|
|
|
|
free(cpp_stderr);
|
|
|
|
cpp_stderr = NULL;
|
|
|
|
}
|
|
|
|
|
2002-03-26 14:46:43 +00:00
|
|
|
/* send the stderr */
|
|
|
|
copy_fd(fd_stderr, 2);
|
|
|
|
close(fd_stderr);
|
|
|
|
|
|
|
|
/* and exit with the right status code */
|
|
|
|
if (first) {
|
2002-03-27 00:39:06 +00:00
|
|
|
cc_log("got cached result for %s\n", output_file);
|
2002-03-30 10:43:26 +00:00
|
|
|
stats_update(STATS_CACHED);
|
2002-03-26 15:11:48 +00:00
|
|
|
}
|
|
|
|
|
2002-03-27 00:39:06 +00:00
|
|
|
exit(0);
|
2002-03-26 14:46:43 +00:00
|
|
|
}
|
|
|
|
|
2002-03-26 23:58:31 +00:00
|
|
|
/* find the real compiler. We just search the PATH to find a executable of the
|
|
|
|
same name that isn't a link to ourselves */
|
2002-03-30 00:24:23 +00:00
|
|
|
static void find_compiler(int argc, char **argv)
|
2002-03-26 14:46:43 +00:00
|
|
|
{
|
|
|
|
char *base;
|
2003-02-16 02:28:35 +00:00
|
|
|
char *path;
|
2002-03-26 22:39:25 +00:00
|
|
|
|
2003-02-16 02:28:35 +00:00
|
|
|
orig_args = args_init(argc, argv);
|
2002-03-27 01:29:53 +00:00
|
|
|
|
2003-09-25 06:11:28 +00:00
|
|
|
base = str_basename(argv[0]);
|
2002-03-30 00:24:23 +00:00
|
|
|
|
|
|
|
/* we might be being invoked like "ccache gcc -c foo.c" */
|
|
|
|
if (strcmp(base, MYNAME) == 0) {
|
2003-02-16 02:28:35 +00:00
|
|
|
args_remove_first(orig_args);
|
2002-03-30 00:24:23 +00:00
|
|
|
free(base);
|
2002-04-03 14:45:51 +00:00
|
|
|
if (strchr(argv[1],'/')) {
|
|
|
|
/* a full path was given */
|
|
|
|
return;
|
|
|
|
}
|
2003-09-25 06:11:28 +00:00
|
|
|
base = str_basename(argv[1]);
|
2002-03-26 14:46:43 +00:00
|
|
|
}
|
|
|
|
|
2003-01-06 04:25:13 +00:00
|
|
|
/* support user override of the compiler */
|
|
|
|
if ((path=getenv("CCACHE_CC"))) {
|
|
|
|
base = strdup(path);
|
|
|
|
}
|
|
|
|
|
2003-02-16 02:28:35 +00:00
|
|
|
orig_args->argv[0] = find_executable(base, MYNAME);
|
2002-03-30 00:24:23 +00:00
|
|
|
|
|
|
|
/* can't find the compiler! */
|
2003-02-16 13:31:13 +00:00
|
|
|
if (!orig_args->argv[0]) {
|
|
|
|
stats_update(STATS_COMPILER);
|
2003-02-16 02:28:35 +00:00
|
|
|
perror(base);
|
|
|
|
exit(1);
|
|
|
|
}
|
2002-03-26 14:46:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-04-07 13:20:53 +00:00
|
|
|
/* check a filename for C/C++ extension. Return the pre-processor
|
|
|
|
extension */
|
2003-09-25 05:41:17 +00:00
|
|
|
static const char *check_extension(const char *fname, int *direct_i)
|
2002-03-31 14:15:06 +00:00
|
|
|
{
|
|
|
|
int i;
|
2003-01-06 02:10:15 +00:00
|
|
|
const char *p;
|
2002-03-31 14:15:06 +00:00
|
|
|
|
2003-09-25 05:41:17 +00:00
|
|
|
if (direct_i) {
|
|
|
|
*direct_i = 0;
|
|
|
|
}
|
|
|
|
|
2002-03-31 14:15:06 +00:00
|
|
|
p = strrchr(fname, '.');
|
2002-04-07 13:20:53 +00:00
|
|
|
if (!p) return NULL;
|
2002-03-31 14:15:06 +00:00
|
|
|
p++;
|
2002-04-07 13:20:53 +00:00
|
|
|
for (i=0; extensions[i].extension; i++) {
|
|
|
|
if (strcmp(p, extensions[i].extension) == 0) {
|
2003-09-25 05:41:17 +00:00
|
|
|
if (direct_i && strcmp(p, extensions[i].i_extension) == 0) {
|
|
|
|
*direct_i = 1;
|
|
|
|
}
|
2002-06-03 03:51:17 +00:00
|
|
|
p = getenv("CCACHE_EXTENSION");
|
|
|
|
if (p) return p;
|
2002-04-07 13:20:53 +00:00
|
|
|
return extensions[i].i_extension;
|
|
|
|
}
|
2002-03-31 14:15:06 +00:00
|
|
|
}
|
2002-04-07 13:20:53 +00:00
|
|
|
return NULL;
|
2002-03-31 14:15:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-03-26 23:58:31 +00:00
|
|
|
/*
|
|
|
|
process the compiler options to form the correct set of options
|
|
|
|
for obtaining the preprocessor output
|
|
|
|
*/
|
2002-03-26 14:46:43 +00:00
|
|
|
static void process_args(int argc, char **argv)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int found_c_opt = 0;
|
2002-03-27 04:53:05 +00:00
|
|
|
int found_S_opt = 0;
|
2002-03-28 06:21:20 +00:00
|
|
|
struct stat st;
|
2003-02-16 02:28:35 +00:00
|
|
|
char *e;
|
2009-11-01 18:39:58 +00:00
|
|
|
/* 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;
|
|
|
|
|
2002-03-26 14:46:43 +00:00
|
|
|
|
2003-02-16 02:28:35 +00:00
|
|
|
stripped_args = args_init(0, NULL);
|
2002-03-26 14:46:43 +00:00
|
|
|
|
|
|
|
args_add(stripped_args, argv[0]);
|
|
|
|
|
|
|
|
for (i=1; i<argc; i++) {
|
2002-03-27 07:31:55 +00:00
|
|
|
/* some options will never work ... */
|
2003-01-06 03:45:06 +00:00
|
|
|
if (strcmp(argv[i], "-E") == 0) {
|
2002-03-26 14:46:43 +00:00
|
|
|
failed();
|
|
|
|
}
|
|
|
|
|
2003-02-17 01:09:12 +00:00
|
|
|
/* these are too hard */
|
2003-09-24 05:57:54 +00:00
|
|
|
if (strcmp(argv[i], "-fbranch-probabilities")==0 ||
|
2007-02-28 04:24:04 +00:00
|
|
|
strcmp(argv[i], "-coverage") == 0 ||
|
|
|
|
strcmp(argv[i], "-ftest-coverage") == 0 ||
|
2003-09-28 04:47:59 +00:00
|
|
|
strcmp(argv[i], "-M") == 0 ||
|
|
|
|
strcmp(argv[i], "-MM") == 0 ||
|
2003-02-17 01:09:12 +00:00
|
|
|
strcmp(argv[i], "-x") == 0) {
|
2002-04-24 12:07:37 +00:00
|
|
|
cc_log("argument %s is unsupported\n", argv[i]);
|
|
|
|
stats_update(STATS_UNSUPPORTED);
|
|
|
|
failed();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2002-03-26 23:58:31 +00:00
|
|
|
/* we must have -c */
|
2002-03-26 14:46:43 +00:00
|
|
|
if (strcmp(argv[i], "-c") == 0) {
|
|
|
|
args_add(stripped_args, argv[i]);
|
|
|
|
found_c_opt = 1;
|
|
|
|
continue;
|
|
|
|
}
|
2002-03-27 04:53:05 +00:00
|
|
|
|
|
|
|
/* -S changes the default extension */
|
|
|
|
if (strcmp(argv[i], "-S") == 0) {
|
|
|
|
args_add(stripped_args, argv[i]);
|
|
|
|
found_S_opt = 1;
|
|
|
|
continue;
|
|
|
|
}
|
2002-03-26 23:58:31 +00:00
|
|
|
|
|
|
|
/* we need to work out where the output was meant to go */
|
2002-03-26 14:46:43 +00:00
|
|
|
if (strcmp(argv[i], "-o") == 0) {
|
|
|
|
if (i == argc-1) {
|
|
|
|
cc_log("missing argument to %s\n", argv[i]);
|
2002-03-30 10:43:26 +00:00
|
|
|
stats_update(STATS_ARGS);
|
2002-03-26 14:46:43 +00:00
|
|
|
failed();
|
|
|
|
}
|
|
|
|
output_file = argv[i+1];
|
|
|
|
i++;
|
|
|
|
continue;
|
|
|
|
}
|
2002-04-10 07:22:59 +00:00
|
|
|
|
|
|
|
/* alternate form of -o, with no space */
|
|
|
|
if (strncmp(argv[i], "-o", 2) == 0) {
|
|
|
|
output_file = &argv[i][2];
|
|
|
|
continue;
|
|
|
|
}
|
2002-03-26 14:46:43 +00:00
|
|
|
|
2002-03-26 23:58:31 +00:00
|
|
|
/* debugging is handled specially, so that we know if we
|
|
|
|
can strip line number info
|
|
|
|
*/
|
2002-03-26 14:46:43 +00:00
|
|
|
if (strncmp(argv[i], "-g", 2) == 0) {
|
|
|
|
args_add(stripped_args, argv[i]);
|
|
|
|
if (strcmp(argv[i], "-g0") != 0) {
|
2003-09-25 05:41:17 +00:00
|
|
|
enable_unify = 0;
|
2002-03-26 14:46:43 +00:00
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2003-02-08 04:27:02 +00:00
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
2009-11-01 18:39:58 +00:00
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
2002-03-26 14:46:43 +00:00
|
|
|
/* options that take an argument */
|
2002-06-03 04:34:56 +00:00
|
|
|
{
|
2003-01-09 21:40:05 +00:00
|
|
|
const char *opts[] = {"-I", "-include", "-imacros", "-iprefix",
|
2002-06-03 04:34:56 +00:00
|
|
|
"-iwithprefix", "-iwithprefixbefore",
|
2003-01-06 00:20:54 +00:00
|
|
|
"-L", "-D", "-U", "-x", "-MF",
|
2002-06-03 04:34:56 +00:00
|
|
|
"-MT", "-MQ", "-isystem", "-aux-info",
|
|
|
|
"--param", "-A", "-Xlinker", "-u",
|
2003-09-27 12:30:55 +00:00
|
|
|
"-idirafter",
|
2002-06-03 04:34:56 +00:00
|
|
|
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();
|
|
|
|
}
|
2002-03-26 14:46:43 +00:00
|
|
|
|
2002-06-03 04:34:56 +00:00
|
|
|
args_add(stripped_args, argv[i]);
|
|
|
|
args_add(stripped_args, argv[i+1]);
|
|
|
|
i++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (opts[j]) continue;
|
2002-03-26 14:46:43 +00:00
|
|
|
}
|
2002-03-26 23:58:31 +00:00
|
|
|
|
|
|
|
/* other options */
|
2002-03-26 14:46:43 +00:00
|
|
|
if (argv[i][0] == '-') {
|
|
|
|
args_add(stripped_args, argv[i]);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2002-03-28 06:21:20 +00:00
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
2002-03-26 14:46:43 +00:00
|
|
|
if (input_file) {
|
2003-09-25 05:41:17 +00:00
|
|
|
if (check_extension(argv[i], NULL)) {
|
2002-04-08 00:43:58 +00:00
|
|
|
cc_log("multiple input files (%s and %s)\n",
|
|
|
|
input_file, argv[i]);
|
|
|
|
stats_update(STATS_MULTIPLE);
|
2002-04-09 12:21:19 +00:00
|
|
|
} else if (!found_c_opt) {
|
|
|
|
cc_log("called for link with %s\n", argv[i]);
|
2002-04-10 08:31:12 +00:00
|
|
|
if (strstr(argv[i], "conftest.")) {
|
|
|
|
stats_update(STATS_CONFTEST);
|
|
|
|
} else {
|
|
|
|
stats_update(STATS_LINK);
|
|
|
|
}
|
2002-04-08 00:43:58 +00:00
|
|
|
} else {
|
|
|
|
cc_log("non C/C++ file %s\n", argv[i]);
|
|
|
|
stats_update(STATS_NOTC);
|
|
|
|
}
|
2002-03-26 14:46:43 +00:00
|
|
|
failed();
|
|
|
|
}
|
|
|
|
|
|
|
|
input_file = argv[i];
|
|
|
|
}
|
|
|
|
|
2002-03-26 15:11:48 +00:00
|
|
|
if (!input_file) {
|
|
|
|
cc_log("No input file found\n");
|
2002-04-01 02:59:06 +00:00
|
|
|
stats_update(STATS_NOINPUT);
|
2002-03-26 14:46:43 +00:00
|
|
|
failed();
|
|
|
|
}
|
|
|
|
|
2003-09-25 05:41:17 +00:00
|
|
|
i_extension = check_extension(input_file, &direct_i_file);
|
2002-04-07 13:20:53 +00:00
|
|
|
if (i_extension == NULL) {
|
2002-03-31 14:15:06 +00:00
|
|
|
cc_log("Not a C/C++ file - %s\n", input_file);
|
2002-04-01 02:59:06 +00:00
|
|
|
stats_update(STATS_NOTC);
|
2002-03-31 14:15:06 +00:00
|
|
|
failed();
|
|
|
|
}
|
|
|
|
|
2002-03-26 15:11:48 +00:00
|
|
|
if (!found_c_opt) {
|
|
|
|
cc_log("No -c option found for %s\n", input_file);
|
2002-04-10 08:02:34 +00:00
|
|
|
/* 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 */
|
2002-04-10 07:52:52 +00:00
|
|
|
if (strstr(input_file, "conftest.")) {
|
|
|
|
stats_update(STATS_CONFTEST);
|
|
|
|
} else {
|
|
|
|
stats_update(STATS_LINK);
|
|
|
|
}
|
2002-03-26 14:46:43 +00:00
|
|
|
failed();
|
|
|
|
}
|
|
|
|
|
2003-01-07 05:06:07 +00:00
|
|
|
|
|
|
|
/* don't try to second guess the compilers heuristics for stdout handling */
|
2003-01-07 21:16:50 +00:00
|
|
|
if (output_file && strcmp(output_file, "-") == 0) {
|
2003-01-07 05:06:07 +00:00
|
|
|
stats_update(STATS_OUTSTDOUT);
|
|
|
|
failed();
|
|
|
|
}
|
|
|
|
|
2002-03-26 14:46:43 +00:00
|
|
|
if (!output_file) {
|
|
|
|
char *p;
|
2002-03-26 23:58:31 +00:00
|
|
|
output_file = x_strdup(input_file);
|
2002-03-26 14:46:43 +00:00
|
|
|
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);
|
2002-03-30 10:43:26 +00:00
|
|
|
stats_update(STATS_ARGS);
|
2002-03-26 14:46:43 +00:00
|
|
|
failed();
|
|
|
|
}
|
2002-03-27 04:53:05 +00:00
|
|
|
p[1] = found_S_opt ? 's' : 'o';
|
2002-03-26 14:46:43 +00:00
|
|
|
p[2] = 0;
|
|
|
|
}
|
2002-03-31 02:57:44 +00:00
|
|
|
|
2009-11-01 18:39:58 +00:00
|
|
|
/* 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) {
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-03-31 02:57:44 +00:00
|
|
|
/* cope with -o /dev/null */
|
2002-04-08 03:53:14 +00:00
|
|
|
if (strcmp(output_file,"/dev/null") != 0 && stat(output_file, &st) == 0 && !S_ISREG(st.st_mode)) {
|
2002-03-31 02:57:44 +00:00
|
|
|
cc_log("Not a regular file %s\n", output_file);
|
2002-04-01 02:59:06 +00:00
|
|
|
stats_update(STATS_DEVICE);
|
2002-03-31 02:57:44 +00:00
|
|
|
failed();
|
|
|
|
}
|
2003-02-16 02:28:35 +00:00
|
|
|
|
|
|
|
if ((e=getenv("CCACHE_PREFIX"))) {
|
|
|
|
char *p = find_executable(e, MYNAME);
|
|
|
|
if (!p) {
|
|
|
|
perror(e);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
args_add_prefix(stripped_args, p);
|
|
|
|
}
|
2002-03-26 14:46:43 +00:00
|
|
|
}
|
|
|
|
|
2002-03-26 23:58:31 +00:00
|
|
|
/* the main ccache driver function */
|
2002-03-26 14:46:43 +00:00
|
|
|
static void ccache(int argc, char *argv[])
|
|
|
|
{
|
|
|
|
/* find the real compiler */
|
2002-03-30 00:24:23 +00:00
|
|
|
find_compiler(argc, argv);
|
2009-11-01 18:39:58 +00:00
|
|
|
|
|
|
|
/* use the real compiler if HOME is not set */
|
|
|
|
if (!cache_dir) {
|
|
|
|
cc_log("Unable to determine home directory\n");
|
|
|
|
cc_log("ccache is disabled\n");
|
|
|
|
failed();
|
|
|
|
}
|
2002-03-30 00:24:23 +00:00
|
|
|
|
|
|
|
/* we might be disabled */
|
2002-03-27 06:15:32 +00:00
|
|
|
if (getenv("CCACHE_DISABLE")) {
|
|
|
|
cc_log("ccache is disabled\n");
|
|
|
|
failed();
|
|
|
|
}
|
|
|
|
|
2003-09-25 05:41:17 +00:00
|
|
|
if (getenv("CCACHE_UNIFY")) {
|
|
|
|
enable_unify = 1;
|
|
|
|
}
|
|
|
|
|
2002-03-26 14:46:43 +00:00
|
|
|
/* process argument list, returning a new set of arguments for pre-processing */
|
2002-03-30 00:24:23 +00:00
|
|
|
process_args(orig_args->argc, orig_args->argv);
|
2002-03-26 14:46:43 +00:00
|
|
|
|
|
|
|
/* 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);
|
2004-09-10 05:05:14 +00:00
|
|
|
|
|
|
|
if (getenv("CCACHE_READONLY")) {
|
|
|
|
cc_log("read-only set - doing real compile\n");
|
|
|
|
failed();
|
|
|
|
}
|
2002-03-26 14:46:43 +00:00
|
|
|
|
2002-05-12 09:51:36 +00:00
|
|
|
/* run real compiler, sending output to cache */
|
2002-03-26 14:46:43 +00:00
|
|
|
to_cache(stripped_args);
|
|
|
|
|
|
|
|
/* return from cache */
|
|
|
|
from_cache(0);
|
|
|
|
|
|
|
|
/* oh oh! */
|
|
|
|
cc_log("secondary from_cache failed!\n");
|
2002-03-30 10:43:26 +00:00
|
|
|
stats_update(STATS_ERROR);
|
2002-03-26 14:46:43 +00:00
|
|
|
failed();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-03-30 00:24:23 +00:00
|
|
|
static void usage(void)
|
|
|
|
{
|
2002-03-30 12:35:26 +00:00
|
|
|
printf("ccache, a compiler cache. Version %s\n", CCACHE_VERSION);
|
2002-03-30 10:43:26 +00:00
|
|
|
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");
|
|
|
|
|
2009-11-01 18:39:58 +00:00
|
|
|
printf("-s, --show-stats show statistics summary\n");
|
|
|
|
printf("-z, --zero-stats zero statistics\n");
|
|
|
|
printf("-c, --cleanup run a cache cleanup\n");
|
|
|
|
printf("-C, --clear clear the cache completely\n");
|
|
|
|
printf("-F <n>, --max-files=<n> set maximum files in cache\n");
|
|
|
|
printf("-M <n>, --max-size=<n> set maximum size of cache (use G, M or K)\n");
|
|
|
|
printf("-h, --help this help page\n");
|
|
|
|
printf("-V, --version print version number\n");
|
2002-03-30 00:24:23 +00:00
|
|
|
}
|
|
|
|
|
2009-11-01 18:39:58 +00:00
|
|
|
static void check_cache_dir(void)
|
|
|
|
{
|
|
|
|
if (!cache_dir) {
|
|
|
|
fatal("Unable to determine home directory");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-03-30 10:43:26 +00:00
|
|
|
/* the main program when not doing a compile */
|
2002-03-30 00:24:23 +00:00
|
|
|
static int ccache_main(int argc, char *argv[])
|
|
|
|
{
|
2002-03-30 10:43:26 +00:00
|
|
|
int c;
|
|
|
|
size_t v;
|
|
|
|
|
2009-11-01 18:39:58 +00:00
|
|
|
static struct option long_options[] =
|
|
|
|
{
|
|
|
|
{"show-stats", no_argument, 0, 's'},
|
|
|
|
{"zero-stats", no_argument, 0, 'z'},
|
|
|
|
{"cleanup", no_argument, 0, 'c'},
|
|
|
|
{"clear", no_argument, 0, 'C'},
|
|
|
|
{"max-files", required_argument, 0, 'F'},
|
|
|
|
{"max-size", required_argument, 0, 'M'},
|
|
|
|
{"help", no_argument, 0, 'h'},
|
|
|
|
{"version", no_argument, 0, 'V'},
|
|
|
|
{0, 0, 0, 0}
|
|
|
|
};
|
|
|
|
int option_index = 0;
|
|
|
|
|
|
|
|
while ((c = getopt_long(argc, argv, "hszcCF:M:V", long_options, &option_index)) != -1) {
|
2002-03-30 10:43:26 +00:00
|
|
|
switch (c) {
|
2002-03-30 12:35:26 +00:00
|
|
|
case 'V':
|
|
|
|
printf("ccache version %s\n", CCACHE_VERSION);
|
2002-03-31 09:30:27 +00:00
|
|
|
printf("Copyright Andrew Tridgell 2002\n");
|
|
|
|
printf("Released under the GNU GPL v2 or later\n");
|
2002-03-30 12:35:26 +00:00
|
|
|
exit(0);
|
|
|
|
|
2002-03-30 10:43:26 +00:00
|
|
|
case 'h':
|
|
|
|
usage();
|
|
|
|
exit(0);
|
|
|
|
|
|
|
|
case 's':
|
2009-11-01 18:39:58 +00:00
|
|
|
check_cache_dir();
|
2002-03-30 10:43:26 +00:00
|
|
|
stats_summary();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'c':
|
2009-11-01 18:39:58 +00:00
|
|
|
check_cache_dir();
|
2002-03-30 10:43:26 +00:00
|
|
|
cleanup_all(cache_dir);
|
2003-02-16 13:31:13 +00:00
|
|
|
printf("Cleaned cache\n");
|
2002-03-30 10:43:26 +00:00
|
|
|
break;
|
|
|
|
|
2002-06-03 03:22:17 +00:00
|
|
|
case 'C':
|
2009-11-01 18:39:58 +00:00
|
|
|
check_cache_dir();
|
2002-06-03 03:22:17 +00:00
|
|
|
wipe_all(cache_dir);
|
|
|
|
printf("Cleared cache\n");
|
|
|
|
break;
|
|
|
|
|
2002-03-30 10:43:26 +00:00
|
|
|
case 'z':
|
2009-11-01 18:39:58 +00:00
|
|
|
check_cache_dir();
|
2002-03-30 10:43:26 +00:00
|
|
|
stats_zero();
|
|
|
|
printf("Statistics cleared\n");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'F':
|
2009-11-01 18:39:58 +00:00
|
|
|
check_cache_dir();
|
2002-03-30 10:43:26 +00:00
|
|
|
v = atoi(optarg);
|
|
|
|
stats_set_limits(v, -1);
|
|
|
|
printf("Set cache file limit to %u\n", (unsigned)v);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'M':
|
2009-11-01 18:39:58 +00:00
|
|
|
check_cache_dir();
|
2002-03-30 10:43:26 +00:00
|
|
|
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;
|
2002-03-30 00:24:23 +00:00
|
|
|
}
|
|
|
|
|
2003-09-24 07:26:15 +00:00
|
|
|
|
|
|
|
/* Make a copy of stderr that will not be cached, so things like
|
2003-09-25 05:41:17 +00:00
|
|
|
distcc can send networking errors to it. */
|
|
|
|
static void setup_uncached_err(void)
|
2003-09-24 07:26:15 +00:00
|
|
|
{
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-03-26 14:46:43 +00:00
|
|
|
int main(int argc, char *argv[])
|
|
|
|
{
|
2003-09-27 11:34:50 +00:00
|
|
|
char *p;
|
|
|
|
|
2002-03-26 23:58:31 +00:00
|
|
|
cache_dir = getenv("CCACHE_DIR");
|
2002-03-27 03:53:22 +00:00
|
|
|
if (!cache_dir) {
|
2009-11-01 18:39:58 +00:00
|
|
|
const char *home_directory = get_home_directory();
|
|
|
|
if (home_directory) {
|
|
|
|
x_asprintf(&cache_dir, "%s/.ccache", home_directory);
|
|
|
|
}
|
2002-03-27 03:53:22 +00:00
|
|
|
}
|
2002-03-26 23:58:31 +00:00
|
|
|
|
2004-09-10 05:05:14 +00:00
|
|
|
temp_dir = getenv("CCACHE_TEMPDIR");
|
|
|
|
if (!temp_dir) {
|
|
|
|
temp_dir = cache_dir;
|
|
|
|
}
|
|
|
|
|
2002-03-26 23:58:31 +00:00
|
|
|
cache_logfile = getenv("CCACHE_LOGFILE");
|
|
|
|
|
2003-09-24 07:26:15 +00:00
|
|
|
setup_uncached_err();
|
2003-09-27 11:34:50 +00:00
|
|
|
|
|
|
|
|
|
|
|
/* 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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-09-24 07:26:15 +00:00
|
|
|
|
2002-03-30 00:24:23 +00:00
|
|
|
/* 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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-03-26 23:58:31 +00:00
|
|
|
/* make sure the cache dir exists */
|
2009-11-01 18:39:58 +00:00
|
|
|
if (cache_dir && (create_dir(cache_dir) != 0)) {
|
2002-03-26 22:25:14 +00:00
|
|
|
fprintf(stderr,"ccache: failed to create %s (%s)\n",
|
2002-03-26 14:46:43 +00:00
|
|
|
cache_dir, strerror(errno));
|
|
|
|
exit(1);
|
|
|
|
}
|
2002-03-26 23:58:31 +00:00
|
|
|
|
2002-03-26 14:46:43 +00:00
|
|
|
ccache(argc, argv);
|
|
|
|
return 1;
|
|
|
|
}
|