[ccache] [PATCH] Support Solaris Studio compiler

Erik Joelsson erik at joelsson.info
Fri Jan 30 10:20:32 MST 2015


Hello,

I've added some new configuration options that will enable ccache to work
with the Solaris Studio compiler. With these changes I'm able to use ccache
successfully for building OpenJDK 9 on Solaris.

I have added tests and updated the documentation with the new options.
Please look there for more information.

/Erik
-------------- next part --------------
diff --git a/MANUAL.txt b/MANUAL.txt
index 4ac113e..6996d52 100644
--- a/MANUAL.txt
+++ b/MANUAL.txt
@@ -349,6 +349,12 @@ WRAPPERS>>.
     the hash sum that idetifies the build. The list separator is semicolon on
     Windows systems and colon on other systems.
 
+*extra_hash_extensions* (*CCACHE_EXTRAHASHEXT*)::
+
+    If an argument file matches any of these extensions, it is considered an
+    additional input file to be hashed. The list is space separated and default
+    is empty.
+
 *hard_link* (*CCACHE_HARDLINK*) [boolean]::
 
     If true, ccache will attempt to use hard links from the cache directory
@@ -429,6 +435,13 @@ WRAPPERS>>.
     problems with compiling the preprocessed output, in which case this option
     could allow ccache to be used anyway.
 
+*cpp_generates_deps* (*CCACHE_CPPGENSDEPS*) [boolean]::
+
+    If true, ccache will not send the dependency file arguments to the
+    preprocessor but only to the compiler. This helps if the preprocessor cannot
+    handle dependency files. It is recommended to also set run_second_cpp
+    together with this. 
+
 *sloppiness* (*CCACHE_SLOPPINESS*)::
 
     By default, ccache tries to give as few false cache hits as possible.
@@ -494,6 +507,33 @@ NOTE: In previous versions of ccache, *CCACHE_TEMPDIR* had to be on the same
     compiler warning messages and expansions of the *\_\_LINE__* macro. Also
     note that enabling the unifier implies turning off the direct mode.
 
+*x_means_lang* (*CCACHE_XMEANSLANG*) [boolean]::
+
+    If false, the -x flag will not be interpreted as explicitly setting the
+    compilation language. This enables usage of compiler options starting with
+    -x without having to prefix them all with with --ccache-skip. If the
+    dependency arguments for the compiler starts with -x, then using this option
+    is required for ccache to properly handle dependency files. Defaults to
+    true.
+
+*solaris_studio* (*CCACHE_SOLARISSTUDIO*) [boolean]::
+
+    A meta option that when set to true changes other options to setup ccache to
+    specifically work with the Solaris Studio compiler. It sets:
++
+--
+*x_means_lang*::
+    False since many compiler options, including dependency options, start with
+    *-x*.
+*run_second_cpp*::
+    True since the compiler will not always work with cpp input.
+*cpp_generates_deps*::
+    False since the preprocessor for C++ will not work with the dependency flags
+    set.
+*extra_hash_extensions*::
+    To *.il* to support inline template files.
+--
+
 
 Cache size management
 ---------------------
diff --git a/ccache.c b/ccache.c
index 0b6643b..85e2165 100644
--- a/ccache.c
+++ b/ccache.c
@@ -192,6 +192,14 @@ enum fromcache_call_mode {
 	FROMCACHE_COMPILED_MODE
 };
 
+struct extra_input_file {
+	char *file;
+	struct extra_input_file *next;
+};
+
+/* Extra input files from the command line matching the extra hash extension. */
+static struct extra_input_file *extra_input_files = NULL;
+
 struct pending_tmp_file {
 	char *path;
 	struct pending_tmp_file *next;
@@ -276,6 +284,15 @@ temp_dir()
 }
 
 static void
+add_extra_input_file(const char *file)
+{
+	struct extra_input_file *f = x_malloc(sizeof(*f));
+	f->file = x_strdup(file);
+	f->next = extra_input_files;
+	extra_input_files = f;
+}
+
+static void
 add_pending_tmp_file(const char *path)
 {
 	struct pending_tmp_file *e = x_malloc(sizeof(*e));
@@ -360,6 +377,31 @@ get_current_working_dir(void)
 }
 
 /*
+ * Checks if the extension of the given file matches one of the extra hash extensions
+ */
+static bool
+matches_extra_extensions(const char *file)
+{
+	bool ret = false;
+	if (!str_eq(conf->extra_hash_extensions, "")) {
+		char *p, *q;
+		char *e;
+		char *saveptr = NULL;
+
+		p = x_strdup(conf->extra_hash_extensions);
+		q = p;
+		while ((e = strtok_r(q, " ", &saveptr))) {
+			if (str_endswith(file, e)) {
+				ret = true;
+			}
+			q = NULL;
+		}
+		free(p);
+        }
+	return ret;
+}
+
+/*
  * Transform a name to a full path into the cache directory, creating needed
  * sublevels if needed. Caller frees.
  */
@@ -928,6 +970,11 @@ to_cache(struct args *args)
 	}
 
 	put_file_in_cache(output_obj, cached_obj);
+
+	if (!conf->cpp_generates_deps && generating_dependencies) {
+		put_file_in_cache(output_dep, cached_dep);
+	}
+
 	stats_update(STATS_TOCACHE);
 
 	/* Make sure we have a CACHEDIR.TAG in the cache part of cache_dir. This can
@@ -1192,6 +1239,18 @@ calculate_common_hash(struct args *args, struct mdfour *hash)
 		}
 	}
 
+	if (extra_input_files != NULL) {
+		struct extra_input_file *f;
+		for (f = extra_input_files; f != NULL; f = f->next) {
+			cc_log("Hashing extra input file %s", f->file);
+			hash_delimiter(hash, "extrainputfile");
+			if (!hash_file(hash, f->file)) {
+				stats_update(STATS_BADEXTRAFILE);
+				failed();
+			}
+		}
+	}
+
 	if (!str_eq(conf->extra_files_to_hash, "")) {
 		char *path, *p, *q, *saveptr = NULL;
 		p = x_strdup(conf->extra_files_to_hash);
@@ -1303,6 +1362,18 @@ calculate_object_hash(struct args *args, struct mdfour *hash, int direct_mode)
 					i++;
 				}
 				continue;
+			} else if (str_startswith(args->argv[i], "-xMF")) {
+				bool separate_argument = (strlen(args->argv[i]) == 4);
+
+				/* In either case, hash the "-xMF" part. */
+				hash_string_length(hash, args->argv[i], 4);
+
+				if (separate_argument) {
+					/* Next argument is dependency name, so
+					 * skip it. */
+					i++;
+				}
+				continue;
 			}
 		}
 
@@ -1438,7 +1509,7 @@ calculate_object_hash(struct args *args, struct mdfour *hash, int direct_mode)
 	} else {
 		object_hash = get_object_name_from_cpp(args, hash);
 		cc_log("Got object file hash from preprocessor");
-		if (generating_dependencies) {
+		if (generating_dependencies && conf->cpp_generates_deps) {
 			cc_log("Preprocessor created %s", output_dep);
 		}
 	}
@@ -1488,7 +1559,8 @@ from_cache(enum fromcache_call_mode mode, bool put_object_in_manifest)
 	 * (If mode != FROMCACHE_DIRECT_MODE, the dependency file is created by
 	 * gcc.)
 	 */
-	produce_dep_file = generating_dependencies && mode == FROMCACHE_DIRECT_MODE;
+	produce_dep_file = generating_dependencies
+		&& (mode == FROMCACHE_DIRECT_MODE || !conf->cpp_generates_deps);
 
 	/* If the dependency file should be in the cache, check that it is. */
 	if (produce_dep_file && stat(cached_dep, &st) != 0) {
@@ -1517,7 +1589,7 @@ from_cache(enum fromcache_call_mode mode, bool put_object_in_manifest)
 		update_mtime(cached_dia);
 	}
 
-	if (generating_dependencies && mode != FROMCACHE_DIRECT_MODE) {
+	if (conf->cpp_generates_deps && generating_dependencies && mode != FROMCACHE_DIRECT_MODE) {
 		put_file_in_cache(output_dep, cached_dep);
 	}
 
@@ -1760,24 +1832,26 @@ cc_process_args(struct args *args, struct args **preprocessor_args,
 		 * Special handling for -x: remember the last specified language before the
 		 * input file and strip all -x options from the arguments.
 		 */
-		if (str_eq(argv[i], "-x")) {
-			if (i == argc-1) {
-				cc_log("Missing argument to %s", argv[i]);
-				stats_update(STATS_ARGS);
-				result = false;
-				goto out;
-			}
-			if (!input_file) {
-				explicit_language = argv[i+1];
+		if (conf->x_means_lang) {
+			if (str_eq(argv[i], "-x")) {
+				if (i == argc-1) {
+					cc_log("Missing argument to %s", argv[i]);
+					stats_update(STATS_ARGS);
+					result = false;
+					goto out;
+				}
+				if (!input_file) {
+					explicit_language = argv[i+1];
+				}
+				i++;
+				continue;
 			}
-			i++;
-			continue;
-		}
-		if (str_startswith(argv[i], "-x")) {
-			if (!input_file) {
-				explicit_language = &argv[i][2];
+			if (str_startswith(argv[i], "-x")) {
+				if (!input_file) {
+					explicit_language = &argv[i][2];
+				}
+				continue;
 			}
-			continue;
 		}
 
 		/* we need to work out where the output was meant to go */
@@ -1822,7 +1896,8 @@ cc_process_args(struct args *args, struct args **preprocessor_args,
 		/* These options require special handling, because they
 		   behave differently with gcc -E, when the output
 		   file is not specified. */
-		if (str_eq(argv[i], "-MD") || str_eq(argv[i], "-MMD")) {
+		if (str_eq(argv[i], "-MD") || str_eq(argv[i], "-MMD") 
+			|| (str_eq(argv[i], "-xMD") || str_eq(argv[i], "-xMMD"))) {
 			generating_dependencies = true;
 			args_add(dep_args, argv[i]);
 			continue;
@@ -1858,6 +1933,37 @@ cc_process_args(struct args *args, struct args **preprocessor_args,
 			}
 			continue;
 		}
+		if (str_startswith(argv[i], "-xMF")) {
+			char *arg;
+			bool separate_argument = (strlen(argv[i]) == 4);
+			dependency_filename_specified = true;
+			free(output_dep);
+			if (separate_argument) {
+				/* -xMF arg */
+				if (i >= argc - 1) {
+					cc_log("Missing argument to %s", argv[i]);
+					stats_update(STATS_ARGS);
+					result = false;
+					goto out;
+				}
+				arg = argv[i + 1];
+				i++;
+			} else {
+				/* -MFarg */
+				arg = &argv[i][4];
+			}
+			output_dep = make_relative_path(x_strdup(arg));
+			/* Keep the format of the args the same */
+			if (separate_argument) {
+				args_add(dep_args, "-xMF");
+				args_add(dep_args, output_dep);
+			} else {
+				char *option = format("-xMF%s", output_dep);
+				args_add(dep_args, option);
+				free(option);
+			}
+			continue;
+		}
 		if (str_startswith(argv[i], "-MQ") || str_startswith(argv[i], "-MT")) {
 			char *relpath;
 			dependency_target_specified = true;
@@ -2186,6 +2292,12 @@ cc_process_args(struct args *args, struct args **preprocessor_args,
 			goto out;
 		}
 
+		if (matches_extra_extensions(argv[i])) {
+			args_add(stripped_args, make_relative_path(x_strdup(argv[i])));
+			add_extra_input_file(argv[i]);
+			continue;
+		}
+
 		/* Rewrite to relative to increase hit rate. */
 		input_file = make_relative_path(x_strdup(argv[i]));
 	}
@@ -2354,7 +2466,7 @@ cc_process_args(struct args *args, struct args **preprocessor_args,
 	/*
 	 * Add flags for dependency generation only to the preprocessor command line.
 	 */
-	if (generating_dependencies) {
+	if (conf->cpp_generates_deps && generating_dependencies) {
 		if (!dependency_filename_specified) {
 			char *default_depfile_name;
 			char *base_name;
@@ -2363,7 +2475,7 @@ cc_process_args(struct args *args, struct args **preprocessor_args,
 			default_depfile_name = format("%s.d", base_name);
 			free(base_name);
 			args_add(dep_args, "-MF");
-			args_add(dep_args, default_depfile_name);
+ 			args_add(dep_args, default_depfile_name);
 			output_dep = make_relative_path(x_strdup(default_depfile_name));
 		}
 
@@ -2397,7 +2509,11 @@ cc_process_args(struct args *args, struct args **preprocessor_args,
 	 * compiler doesn't produce a correct .d file when compiling preprocessed
 	 * source.
 	 */
-	args_extend(cpp_args, dep_args);
+	if (conf->cpp_generates_deps) {
+		args_extend(cpp_args, dep_args);
+	} else {
+		args_extend(*compiler_args, dep_args);
+	}
 
 	*preprocessor_args = args_copy(stripped_args);
 	args_extend(*preprocessor_args, cpp_args);
@@ -2517,6 +2633,19 @@ initialize(void)
 	exitfn_add_nullary(stats_flush);
 	exitfn_add_nullary(clean_up_pending_tmp_files);
 
+	if (conf->solaris_studio) {
+		conf->run_second_cpp = true;
+		conf->cpp_generates_deps = false;
+		conf->x_means_lang = false;
+		if (str_eq(conf->extra_hash_extensions, "")) {
+			conf->extra_hash_extensions = x_strdup(".il");
+		} else {
+			size_t n = strlen(conf->extra_hash_extensions);
+			conf->extra_hash_extensions = x_realloc(conf->extra_hash_extensions, n + 4);
+			strcpy(conf->extra_hash_extensions + n, " .il");
+		}
+	}
+
 	cc_log("=== CCACHE %s STARTED =========================================",
 	       CCACHE_VERSION);
 
diff --git a/ccache.h b/ccache.h
index 9d35a55..6866648 100644
--- a/ccache.h
+++ b/ccache.h
@@ -170,6 +170,7 @@ char *x_readlink(const char *path);
 bool read_file(const char *path, size_t size_hint, char **data, size_t *size);
 char *read_text_file(const char *path, size_t size_hint);
 char *subst_env_in_string(const char *str, char **errmsg);
+bool str_endswith(const char *str, const char *suffix);
 
 /* ------------------------------------------------------------------------- */
 /* stats.c */
diff --git a/conf.c b/conf.c
index 92c70c0..6064f1d 100644
--- a/conf.c
+++ b/conf.c
@@ -308,9 +308,11 @@ conf_create(void)
 	conf->compression = false;
 	conf->compression_level = 6;
 	conf->cpp_extension = x_strdup("");
+	conf->cpp_generates_deps = true;
 	conf->direct_mode = true;
 	conf->disable = false;
 	conf->extra_files_to_hash = x_strdup("");
+	conf->extra_hash_extensions = x_strdup("");
 	conf->hard_link = false;
 	conf->hash_dir = false;
 	conf->log_file = x_strdup("");
@@ -323,10 +325,12 @@ conf_create(void)
 	conf->recache = false;
 	conf->run_second_cpp = false;
 	conf->sloppiness = 0;
+	conf->solaris_studio = false;
 	conf->stats = true;
 	conf->temporary_dir = x_strdup("");
 	conf->umask = UINT_MAX; /* default: don't set umask */
 	conf->unify = false;
+	conf->x_means_lang = true;
 	conf->item_origins = x_malloc(CONFITEMS_TOTAL_KEYWORDS * sizeof(char *));
 	for (i = 0; i < CONFITEMS_TOTAL_KEYWORDS; ++i) {
 		conf->item_origins[i] = "default";
@@ -346,6 +350,7 @@ conf_free(struct conf *conf)
 	free(conf->compiler_check);
 	free(conf->cpp_extension);
 	free(conf->extra_files_to_hash);
+	free(conf->extra_hash_extensions);
 	free(conf->log_file);
 	free(conf->path);
 	free(conf->prefix_command);
@@ -548,6 +553,9 @@ conf_print_items(struct conf *conf,
 	reformat(&s, "cpp_extension = %s", conf->cpp_extension);
 	printer(s, conf->item_origins[find_conf("cpp_extension")->number], context);
 
+	reformat(&s, "cpp_generates_deps = %s", bool_to_string(conf->cpp_generates_deps));
+	printer(s, conf->item_origins[find_conf("cpp_generates_deps")->number], context);
+
 	reformat(&s, "direct_mode = %s", bool_to_string(conf->direct_mode));
 	printer(s, conf->item_origins[find_conf("direct_mode")->number], context);
 
@@ -558,6 +566,10 @@ conf_print_items(struct conf *conf,
 	printer(s, conf->item_origins[find_conf("extra_files_to_hash")->number],
 	        context);
 
+	reformat(&s, "extra_hash_extensions = %s", conf->extra_hash_extensions);
+	printer(s, conf->item_origins[find_conf("extra_hash_extensions")->number],
+	        context);
+
 	reformat(&s, "hard_link = %s", bool_to_string(conf->hard_link));
 	printer(s, conf->item_origins[find_conf("hard_link")->number], context);
 
@@ -616,6 +628,9 @@ conf_print_items(struct conf *conf,
 	}
 	printer(s, conf->item_origins[find_conf("sloppiness")->number], context);
 
+	reformat(&s, "solaris_studio = %s", bool_to_string(conf->solaris_studio));
+	printer(s, conf->item_origins[find_conf("solaris_studio")->number], context);
+
 	reformat(&s, "stats = %s", bool_to_string(conf->stats));
 	printer(s, conf->item_origins[find_conf("stats")->number], context);
 
@@ -632,6 +647,9 @@ conf_print_items(struct conf *conf,
 	reformat(&s, "unify = %s", bool_to_string(conf->unify));
 	printer(s, conf->item_origins[find_conf("unify")->number], context);
 
+	reformat(&s, "x_means_lang = %s", bool_to_string(conf->x_means_lang));
+	printer(s, conf->item_origins[find_conf("x_means_lang")->number], context);
+
 	free(s);
 	return true;
 }
diff --git a/conf.h b/conf.h
index 0e30028..524ea2a 100644
--- a/conf.h
+++ b/conf.h
@@ -12,9 +12,11 @@ struct conf {
 	bool compression;
 	unsigned compression_level;
 	char *cpp_extension;
+	bool cpp_generates_deps;
 	bool direct_mode;
 	bool disable;
 	char *extra_files_to_hash;
+	char *extra_hash_extensions;
 	bool hard_link;
 	bool hash_dir;
 	char *log_file;
@@ -27,10 +29,12 @@ struct conf {
 	bool recache;
 	bool run_second_cpp;
 	unsigned sloppiness;
+	bool solaris_studio;
 	bool stats;
 	char *temporary_dir;
 	unsigned umask;
 	bool unify;
+	bool x_means_lang;
 
 	const char **item_origins;
 };
diff --git a/confitems.gperf b/confitems.gperf
index 6fb21ef..f67dba8 100644
--- a/confitems.gperf
+++ b/confitems.gperf
@@ -7,30 +7,34 @@
 %define initializer-suffix ,0,NULL,0,NULL
 struct conf_item;
 %%
-base_dir,             0, ITEM_V(base_dir, env_string, absolute_path)
-cache_dir,            1, ITEM(cache_dir, env_string)
-cache_dir_levels,     2, ITEM_V(cache_dir_levels, unsigned, dir_levels)
-compiler,             3, ITEM(compiler, string)
-compiler_check,       4, ITEM(compiler_check, string)
-compression,          5, ITEM(compression, bool)
-compression_level,    6, ITEM(compression_level, unsigned)
-cpp_extension,        7, ITEM(cpp_extension, string)
-direct_mode,          8, ITEM(direct_mode, bool)
-disable,              9, ITEM(disable, bool)
-extra_files_to_hash, 10, ITEM(extra_files_to_hash, env_string)
-hard_link,           11, ITEM(hard_link, bool)
-hash_dir,            12, ITEM(hash_dir, bool)
-log_file,            13, ITEM(log_file, env_string)
-max_files,           14, ITEM(max_files, unsigned)
-max_size,            15, ITEM(max_size, size)
-path,                16, ITEM(path, env_string)
-prefix_command,      17, ITEM(prefix_command, env_string)
-read_only,           18, ITEM(read_only, bool)
-read_only_direct,    19, ITEM(read_only_direct, bool)
-recache,             20, ITEM(recache, bool)
-run_second_cpp,      21, ITEM(run_second_cpp, bool)
-sloppiness,          22, ITEM(sloppiness, sloppiness)
-stats,               23, ITEM(stats, bool)
-temporary_dir,       24, ITEM(temporary_dir, env_string)
-umask,               25, ITEM(umask, umask)
-unify,               26, ITEM(unify, bool)
+base_dir,               0, ITEM_V(base_dir, env_string, absolute_path)
+cache_dir,              1, ITEM(cache_dir, env_string)
+cache_dir_levels,       2, ITEM_V(cache_dir_levels, unsigned, dir_levels)
+compiler,               3, ITEM(compiler, string)
+compiler_check,         4, ITEM(compiler_check, string)
+compression,            5, ITEM(compression, bool)
+compression_level,      6, ITEM(compression_level, unsigned)
+cpp_extension,          7, ITEM(cpp_extension, string)
+cpp_generates_deps,     8, ITEM(cpp_generates_deps, bool)
+direct_mode,            9, ITEM(direct_mode, bool)
+disable,               10, ITEM(disable, bool)
+extra_files_to_hash,   11, ITEM(extra_files_to_hash, env_string)
+extra_hash_extensions, 12, ITEM(extra_hash_extensions, env_string)
+hard_link,             13, ITEM(hard_link, bool)
+hash_dir,              14, ITEM(hash_dir, bool)
+log_file,              15, ITEM(log_file, env_string)
+max_files,             16, ITEM(max_files, unsigned)
+max_size,              17, ITEM(max_size, size)
+path,                  18, ITEM(path, env_string)
+prefix_command,        19, ITEM(prefix_command, env_string)
+read_only,             20, ITEM(read_only, bool)
+read_only_direct,      21, ITEM(read_only_direct, bool)
+recache,               22, ITEM(recache, bool)
+run_second_cpp,        23, ITEM(run_second_cpp, bool)
+sloppiness,            24, ITEM(sloppiness, sloppiness)
+solaris_studio,        25, ITEM(solaris_studio, bool)
+stats,                 26, ITEM(stats, bool)
+temporary_dir,         27, ITEM(temporary_dir, env_string)
+umask,                 28, ITEM(umask, umask)
+unify,                 29, ITEM(unify, bool)
+x_means_lang,          30, ITEM(x_means_lang, bool)
diff --git a/confitems_lookup.c b/confitems_lookup.c
index 2642dd6..708f4fc 100644
--- a/confitems_lookup.c
+++ b/confitems_lookup.c
@@ -31,7 +31,7 @@
 
 #line 8 "confitems.gperf"
 struct conf_item;
-/* maximum key range = 45, duplicates = 0 */
+/* maximum key range = 62, duplicates = 0 */
 
 #ifdef __GNUC__
 __inline
@@ -45,32 +45,32 @@ confitems_hash (register const char *str, register unsigned int len)
 {
   static const unsigned char asso_values[] =
     {
-      50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
-      50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
-      50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
-      50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
-      50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
-      50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
-      50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
-      50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
-      50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
-      50, 50, 50, 50, 50, 50, 50, 20,  5,  0,
-      10,  0, 50, 50, 15,  5, 50, 50, 20, 10,
-       0,  0, 10, 50,  0,  0,  0,  5, 50, 50,
-      30, 50, 50, 50, 50, 50, 50, 50, 50, 50,
-      50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
-      50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
-      50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
-      50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
-      50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
-      50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
-      50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
-      50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
-      50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
-      50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
-      50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
-      50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
-      50, 50, 50, 50, 50, 50
+      66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
+      66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
+      66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
+      66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
+      66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
+      66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
+      66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
+      66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
+      66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
+      66, 66, 66, 66, 66, 15, 66,  0, 30,  0,
+      15, 10, 66, 66, 18, 15, 66, 66, 25, 20,
+       0,  0,  0, 66,  5, 30,  0, 20, 66, 66,
+       5, 66, 66, 66, 66, 66, 66, 66, 66, 66,
+      66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
+      66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
+      66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
+      66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
+      66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
+      66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
+      66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
+      66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
+      66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
+      66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
+      66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
+      66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
+      66, 66, 66, 66, 66, 66
     };
   return len + asso_values[(unsigned char)str[1]] + asso_values[(unsigned char)str[0]];
 }
@@ -86,86 +86,98 @@ confitems_get (register const char *str, register unsigned int len)
 {
   enum
     {
-      TOTAL_KEYWORDS = 27,
+      TOTAL_KEYWORDS = 31,
       MIN_WORD_LENGTH = 4,
-      MAX_WORD_LENGTH = 19,
-      MIN_HASH_VALUE = 5,
-      MAX_HASH_VALUE = 49
+      MAX_WORD_LENGTH = 21,
+      MIN_HASH_VALUE = 4,
+      MAX_HASH_VALUE = 65
     };
 
   static const struct conf_item wordlist[] =
     {
       {"",0,NULL,0,NULL}, {"",0,NULL,0,NULL},
       {"",0,NULL,0,NULL}, {"",0,NULL,0,NULL},
+#line 28 "confitems.gperf"
+      {"path",                  18, ITEM(path, env_string)},
+      {"",0,NULL,0,NULL}, {"",0,NULL,0,NULL},
       {"",0,NULL,0,NULL},
-#line 33 "confitems.gperf"
-      {"stats",               23, ITEM(stats, bool)},
-      {"",0,NULL,0,NULL},
-#line 30 "confitems.gperf"
-      {"recache",             20, ITEM(recache, bool)},
 #line 13 "confitems.gperf"
-      {"compiler",             3, ITEM(compiler, string)},
-#line 28 "confitems.gperf"
-      {"read_only",           18, ITEM(read_only, bool)},
-#line 36 "confitems.gperf"
-      {"unify",               26, ITEM(unify, bool)},
+      {"compiler",               3, ITEM(compiler, string)},
+#line 11 "confitems.gperf"
+      {"cache_dir",              1, ITEM(cache_dir, env_string)},
+      {"",0,NULL,0,NULL},
 #line 15 "confitems.gperf"
-      {"compression",          5, ITEM(compression, bool)},
+      {"compression",            5, ITEM(compression, bool)},
       {"",0,NULL,0,NULL},
-#line 34 "confitems.gperf"
-      {"temporary_dir",       24, ITEM(temporary_dir, env_string)},
+#line 17 "confitems.gperf"
+      {"cpp_extension",          7, ITEM(cpp_extension, string)},
 #line 14 "confitems.gperf"
-      {"compiler_check",       4, ITEM(compiler_check, string)},
+      {"compiler_check",         4, ITEM(compiler_check, string)},
       {"",0,NULL,0,NULL},
-#line 29 "confitems.gperf"
-      {"read_only_direct",    19, ITEM(read_only_direct, bool)},
+#line 12 "confitems.gperf"
+      {"cache_dir_levels",       2, ITEM_V(cache_dir_levels, unsigned, dir_levels)},
 #line 16 "confitems.gperf"
-      {"compression_level",    6, ITEM(compression_level, unsigned)},
-      {"",0,NULL,0,NULL},
-#line 31 "confitems.gperf"
-      {"run_second_cpp",      21, ITEM(run_second_cpp, bool)},
-#line 35 "confitems.gperf"
-      {"umask",               25, ITEM(umask, umask)},
-      {"",0,NULL,0,NULL},
-#line 19 "confitems.gperf"
-      {"disable",              9, ITEM(disable, bool)},
-#line 17 "confitems.gperf"
-      {"cpp_extension",        7, ITEM(cpp_extension, string)},
-#line 27 "confitems.gperf"
-      {"prefix_command",      17, ITEM(prefix_command, env_string)},
-      {"",0,NULL,0,NULL},
+      {"compression_level",      6, ITEM(compression_level, unsigned)},
 #line 18 "confitems.gperf"
-      {"direct_mode",          8, ITEM(direct_mode, bool)},
-      {"",0,NULL,0,NULL},
-#line 23 "confitems.gperf"
-      {"log_file",            13, ITEM(log_file, env_string)},
-#line 11 "confitems.gperf"
-      {"cache_dir",            1, ITEM(cache_dir, env_string)},
-#line 32 "confitems.gperf"
-      {"sloppiness",          22, ITEM(sloppiness, sloppiness)},
+      {"cpp_generates_deps",     8, ITEM(cpp_generates_deps, bool)},
+#line 29 "confitems.gperf"
+      {"prefix_command",        19, ITEM(prefix_command, env_string)},
       {"",0,NULL,0,NULL}, {"",0,NULL,0,NULL},
-#line 10 "confitems.gperf"
-      {"base_dir",             0, ITEM_V(base_dir, env_string, absolute_path)},
+#line 32 "confitems.gperf"
+      {"recache",               22, ITEM(recache, bool)},
+#line 37 "confitems.gperf"
+      {"temporary_dir",         27, ITEM(temporary_dir, env_string)},
+#line 30 "confitems.gperf"
+      {"read_only",             20, ITEM(read_only, bool)},
+#line 39 "confitems.gperf"
+      {"unify",                 29, ITEM(unify, bool)},
+#line 24 "confitems.gperf"
+      {"hash_dir",              14, ITEM(hash_dir, bool)},
+#line 23 "confitems.gperf"
+      {"hard_link",             13, ITEM(hard_link, bool)},
+#line 27 "confitems.gperf"
+      {"max_size",              17, ITEM(max_size, size)},
 #line 26 "confitems.gperf"
-      {"path",                16, ITEM(path, env_string)},
-      {"",0,NULL,0,NULL},
-#line 12 "confitems.gperf"
-      {"cache_dir_levels",     2, ITEM_V(cache_dir_levels, unsigned, dir_levels)},
+      {"max_files",             16, ITEM(max_files, unsigned)},
       {"",0,NULL,0,NULL},
+#line 31 "confitems.gperf"
+      {"read_only_direct",      21, ITEM(read_only_direct, bool)},
+#line 40 "confitems.gperf"
+      {"x_means_lang",          30, ITEM(x_means_lang, bool)},
 #line 25 "confitems.gperf"
-      {"max_size",            15, ITEM(max_size, size)},
-#line 24 "confitems.gperf"
-      {"max_files",           14, ITEM(max_files, unsigned)},
-      {"",0,NULL,0,NULL}, {"",0,NULL,0,NULL},
-      {"",0,NULL,0,NULL},
-#line 22 "confitems.gperf"
-      {"hash_dir",            12, ITEM(hash_dir, bool)},
+      {"log_file",              15, ITEM(log_file, env_string)},
 #line 21 "confitems.gperf"
-      {"hard_link",           11, ITEM(hard_link, bool)},
+      {"extra_files_to_hash",   11, ITEM(extra_files_to_hash, env_string)},
+#line 36 "confitems.gperf"
+      {"stats",                 26, ITEM(stats, bool)},
+#line 22 "confitems.gperf"
+      {"extra_hash_extensions", 12, ITEM(extra_hash_extensions, env_string)},
+#line 20 "confitems.gperf"
+      {"disable",               10, ITEM(disable, bool)},
+#line 10 "confitems.gperf"
+      {"base_dir",               0, ITEM_V(base_dir, env_string, absolute_path)},
+#line 33 "confitems.gperf"
+      {"run_second_cpp",        23, ITEM(run_second_cpp, bool)},
+      {"",0,NULL,0,NULL},
+#line 19 "confitems.gperf"
+      {"direct_mode",            9, ITEM(direct_mode, bool)},
       {"",0,NULL,0,NULL}, {"",0,NULL,0,NULL},
+#line 35 "confitems.gperf"
+      {"solaris_studio",        25, ITEM(solaris_studio, bool)},
+#line 38 "confitems.gperf"
+      {"umask",                 28, ITEM(umask, umask)},
       {"",0,NULL,0,NULL}, {"",0,NULL,0,NULL},
-#line 20 "confitems.gperf"
-      {"extra_files_to_hash", 10, ITEM(extra_files_to_hash, env_string)}
+      {"",0,NULL,0,NULL}, {"",0,NULL,0,NULL},
+      {"",0,NULL,0,NULL}, {"",0,NULL,0,NULL},
+      {"",0,NULL,0,NULL}, {"",0,NULL,0,NULL},
+      {"",0,NULL,0,NULL}, {"",0,NULL,0,NULL},
+      {"",0,NULL,0,NULL}, {"",0,NULL,0,NULL},
+      {"",0,NULL,0,NULL}, {"",0,NULL,0,NULL},
+      {"",0,NULL,0,NULL}, {"",0,NULL,0,NULL},
+      {"",0,NULL,0,NULL}, {"",0,NULL,0,NULL},
+      {"",0,NULL,0,NULL},
+#line 34 "confitems.gperf"
+      {"sloppiness",            24, ITEM(sloppiness, sloppiness)}
     };
 
   if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
@@ -182,4 +194,4 @@ confitems_get (register const char *str, register unsigned int len)
     }
   return 0;
 }
-static const size_t CONFITEMS_TOTAL_KEYWORDS = 27;
+static const size_t CONFITEMS_TOTAL_KEYWORDS = 31;
diff --git a/envtoconfitems.gperf b/envtoconfitems.gperf
index f623c45..da197ad 100644
--- a/envtoconfitems.gperf
+++ b/envtoconfitems.gperf
@@ -14,11 +14,13 @@ COMPILERCHECK, "compiler_check"
 COMPRESS, "compression"
 COMPRESSLEVEL, "compression_level"
 CPP2, "run_second_cpp"
+CPPGENSDEPS, "cpp_generates_deps"
 DIR, "cache_dir"
 DIRECT, "direct_mode"
 DISABLE, "disable"
 EXTENSION, "cpp_extension"
 EXTRAFILES, "extra_files_to_hash"
+EXTRAHASHEXT, "extra_hash_extensions"
 HARDLINK, "hard_link"
 HASHDIR, "hash_dir"
 LOGFILE, "log_file"
@@ -31,7 +33,9 @@ READONLY, "read_only"
 READONLY_DIRECT, "read_only_direct"
 RECACHE, "recache"
 SLOPPINESS, "sloppiness"
+SOLARISSTUDIO, "solaris_studio"
 STATS, "stats"
 TEMPDIR, "temporary_dir"
 UMASK, "umask"
 UNIFY, "unify"
+XMEANSLANG, "x_means_lang"
diff --git a/envtoconfitems_lookup.c b/envtoconfitems_lookup.c
index 2bfd2f8..107eec1 100644
--- a/envtoconfitems_lookup.c
+++ b/envtoconfitems_lookup.c
@@ -31,7 +31,7 @@
 
 #line 9 "envtoconfitems.gperf"
 struct env_to_conf_item;
-/* maximum key range = 41, duplicates = 0 */
+/* maximum key range = 40, duplicates = 0 */
 
 #ifdef __GNUC__
 __inline
@@ -51,9 +51,9 @@ envtoconfitems_hash (register const char *str, register unsigned int len)
       43, 43, 43, 43, 43, 43, 43, 43, 43, 43,
       43, 43, 43, 43, 43, 43, 43, 43, 43, 43,
       43, 43, 43, 43, 43, 43, 43, 43, 43, 43,
-      43, 43, 43, 43, 43, 43, 20,  0,  0, 10,
-      20, 43, 15, 43, 10, 43,  5, 10, 15,  0,
-       5, 10,  5,  0,  0,  0, 43, 43, 43, 43,
+      43, 43, 43, 43, 43, 43, 15, 10,  0,  0,
+       0, 43,  3, 43,  5, 43, 30, 20,  9,  0,
+       0, 28,  0,  0, 30,  5, 43, 43, 20, 43,
       10, 43, 43, 43, 43, 43, 43, 43, 43, 43,
       43, 43, 43, 43, 43, 43, 43, 43, 43, 43,
       43, 43, 43, 43, 43, 43, 43, 43, 43, 43,
@@ -100,79 +100,87 @@ envtoconfitems_get (register const char *str, register unsigned int len)
 {
   enum
     {
-      TOTAL_KEYWORDS = 27,
+      TOTAL_KEYWORDS = 31,
       MIN_WORD_LENGTH = 2,
       MAX_WORD_LENGTH = 15,
-      MIN_HASH_VALUE = 2,
+      MIN_HASH_VALUE = 3,
       MAX_HASH_VALUE = 42
     };
 
   static const struct env_to_conf_item wordlist[] =
     {
-      {"",""}, {"",""},
+      {"",""}, {"",""}, {"",""},
+#line 18 "envtoconfitems.gperf"
+      {"DIR", "cache_dir"},
+#line 30 "envtoconfitems.gperf"
+      {"PATH", "path"},
+      {"",""},
+#line 19 "envtoconfitems.gperf"
+      {"DIRECT", "direct_mode"},
+#line 34 "envtoconfitems.gperf"
+      {"RECACHE", "recache"},
+#line 32 "envtoconfitems.gperf"
+      {"READONLY", "read_only"},
+#line 21 "envtoconfitems.gperf"
+      {"EXTENSION", "cpp_extension"},
+#line 25 "envtoconfitems.gperf"
+      {"HASHDIR", "hash_dir"},
+#line 31 "envtoconfitems.gperf"
+      {"PREFIX", "prefix_command"},
 #line 12 "envtoconfitems.gperf"
       {"CC", "compiler"},
-#line 17 "envtoconfitems.gperf"
-      {"DIR", "cache_dir"},
+#line 36 "envtoconfitems.gperf"
+      {"SOLARISSTUDIO", "solaris_studio"},
 #line 16 "envtoconfitems.gperf"
       {"CPP2", "run_second_cpp"},
-#line 34 "envtoconfitems.gperf"
-      {"STATS", "stats"},
-#line 18 "envtoconfitems.gperf"
-      {"DIRECT", "direct_mode"},
-#line 19 "envtoconfitems.gperf"
+#line 33 "envtoconfitems.gperf"
+      {"READONLY_DIRECT", "read_only_direct"},
+#line 29 "envtoconfitems.gperf"
+      {"NLEVELS", "cache_dir_levels"},
+#line 20 "envtoconfitems.gperf"
       {"DISABLE", "disable"},
 #line 14 "envtoconfitems.gperf"
       {"COMPRESS", "compression"},
-#line 28 "envtoconfitems.gperf"
-      {"PATH", "path"},
-#line 36 "envtoconfitems.gperf"
-      {"UMASK", "umask"},
       {"",""},
-#line 32 "envtoconfitems.gperf"
-      {"RECACHE", "recache"},
+#line 40 "envtoconfitems.gperf"
+      {"UNIFY", "unify"},
+#line 17 "envtoconfitems.gperf"
+      {"CPPGENSDEPS", "cpp_generates_deps"},
+#line 11 "envtoconfitems.gperf"
+      {"BASEDIR", "base_dir"},
 #line 15 "envtoconfitems.gperf"
       {"COMPRESSLEVEL", "compression_level"},
       {"",""},
-#line 37 "envtoconfitems.gperf"
-      {"UNIFY", "unify"},
+#line 22 "envtoconfitems.gperf"
+      {"EXTRAFILES", "extra_files_to_hash"},
       {"",""},
-#line 35 "envtoconfitems.gperf"
-      {"TEMPDIR", "temporary_dir"},
-#line 30 "envtoconfitems.gperf"
-      {"READONLY", "read_only"},
-#line 20 "envtoconfitems.gperf"
-      {"EXTENSION", "cpp_extension"},
-#line 33 "envtoconfitems.gperf"
-      {"SLOPPINESS", "sloppiness"},
-#line 29 "envtoconfitems.gperf"
-      {"PREFIX", "prefix_command"},
-#line 24 "envtoconfitems.gperf"
-      {"LOGFILE", "log_file"},
+#line 23 "envtoconfitems.gperf"
+      {"EXTRAHASHEXT", "extra_hash_extensions"},
 #line 13 "envtoconfitems.gperf"
       {"COMPILERCHECK", "compiler_check"},
       {"",""},
-#line 31 "envtoconfitems.gperf"
-      {"READONLY_DIRECT", "read_only_direct"},
-      {"",""},
-#line 26 "envtoconfitems.gperf"
+#line 41 "envtoconfitems.gperf"
+      {"XMEANSLANG", "x_means_lang"},
+#line 24 "envtoconfitems.gperf"
+      {"HARDLINK", "hard_link"},
+#line 28 "envtoconfitems.gperf"
       {"MAXSIZE", "max_size"},
-#line 25 "envtoconfitems.gperf"
+#line 27 "envtoconfitems.gperf"
       {"MAXFILES", "max_files"},
-      {"",""}, {"",""}, {"",""},
-#line 23 "envtoconfitems.gperf"
-      {"HASHDIR", "hash_dir"},
-#line 22 "envtoconfitems.gperf"
-      {"HARDLINK", "hard_link"},
-      {"",""}, {"",""}, {"",""},
-#line 11 "envtoconfitems.gperf"
-      {"BASEDIR", "base_dir"},
-      {"",""}, {"",""},
-#line 21 "envtoconfitems.gperf"
-      {"EXTRAFILES", "extra_files_to_hash"},
       {"",""},
-#line 27 "envtoconfitems.gperf"
-      {"NLEVELS", "cache_dir_levels"}
+#line 37 "envtoconfitems.gperf"
+      {"STATS", "stats"},
+      {"",""},
+#line 38 "envtoconfitems.gperf"
+      {"TEMPDIR", "temporary_dir"},
+#line 35 "envtoconfitems.gperf"
+      {"SLOPPINESS", "sloppiness"},
+      {"",""},
+#line 39 "envtoconfitems.gperf"
+      {"UMASK", "umask"},
+      {"",""},
+#line 26 "envtoconfitems.gperf"
+      {"LOGFILE", "log_file"}
     };
 
   if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
@@ -189,4 +197,4 @@ envtoconfitems_get (register const char *str, register unsigned int len)
     }
   return 0;
 }
-static const size_t ENVTOCONFITEMS_TOTAL_KEYWORDS = 27;
+static const size_t ENVTOCONFITEMS_TOTAL_KEYWORDS = 31;
diff --git a/test.sh b/test.sh
index 0fde545..b15bf98 100755
--- a/test.sh
+++ b/test.sh
@@ -23,11 +23,13 @@ unset CCACHE_BASEDIR
 unset CCACHE_CC
 unset CCACHE_COMPILERCHECK
 unset CCACHE_COMPRESS
+unset CCACHE_NOCPPGENSDEPS
 unset CCACHE_CPP2
 unset CCACHE_DIR
 unset CCACHE_DISABLE
 unset CCACHE_EXTENSION
 unset CCACHE_EXTRAFILES
+unset CCACHE_EXTRAHASHEXT
 unset CCACHE_HARDLINK
 unset CCACHE_HASHDIR
 unset CCACHE_LOGFILE
@@ -40,9 +42,11 @@ unset CCACHE_READONLY
 unset CCACHE_READONLY_DIRECT
 unset CCACHE_RECACHE
 unset CCACHE_SLOPPINESS
+unset CCACHE_SOLARISSTUDIO
 unset CCACHE_TEMPDIR
 unset CCACHE_UMASK
 unset CCACHE_UNIFY
+unset CCACHE_NOXMEANSLANG
 unset GCC_COLORS
 
 # Many tests backdate files, which updates their ctimes.  In those tests, we
@@ -697,6 +701,62 @@ nlevels1_suite() {
     unset CCACHE_NLEVELS
 }
 
+nocppgensdeps_suite() {
+    unset CCACHE_NODIRECT
+    CCACHE_CPP2=1
+    export CCACHE_CPP2
+    CCACHE_NOCPPGENSDEPS=1
+    export CCACHE_NOCPPGENSDEPS
+
+    ##################################################################
+    # Create some code to compile.
+    echo "int test;" >test.c
+    $COMPILER -c -Wp,-MD,expected.d test.c
+    expected_d_content=`cat expected.d`
+
+    testname="nocppgensdeps-MF"
+    $CCACHE -C >/dev/null
+    $CCACHE -z >/dev/null
+    $CCACHE $COMPILER -c -MD -MF other.d test.c
+    checkstat 'cache hit (direct)' 0
+    checkstat 'cache hit (preprocessed)' 0
+    checkstat 'cache miss' 1
+    checkfile other.d "$expected_d_content"
+    CCACHE_DISABLE=1 $COMPILER -c -MD -MF other.d test.c -o reference_test.o
+    compare_file reference_test.o test.o
+
+    rm -f other.d
+
+    $CCACHE $COMPILER -c -MD -MF other.d test.c
+    checkstat 'cache hit (direct)' 1
+    checkstat 'cache hit (preprocessed)' 0
+    checkstat 'cache miss' 1
+    checkfile other.d "$expected_d_content"
+    compare_file reference_test.o test.o
+
+    $CCACHE $COMPILER -c -MD -MF different_name.d test.c
+    checkstat 'cache hit (direct)' 2
+    checkstat 'cache hit (preprocessed)' 0
+    checkstat 'cache miss' 1
+    checkfile different_name.d "$expected_d_content"
+    compare_file reference_test.o test.o
+
+    rm -f different_name.d
+
+    $CCACHE $COMPILER -c -MD -MFthird_name.d test.c
+    checkstat 'cache hit (direct)' 3
+    checkstat 'cache hit (preprocessed)' 0
+    checkstat 'cache miss' 1
+    checkfile third_name.d "$expected_d_content"
+    compare_file reference_test.o test.o
+
+    rm -f third_name.d
+    rm -f test.c
+
+    unset CCACHE_CPP2
+    unset CCACHE_NOCPPGENSDEPS
+}
+
 direct_suite() {
     unset CCACHE_NODIRECT
 
@@ -2416,6 +2476,7 @@ all_suites="
 base
 link          !win32
 hardlink
+nocppgensdeps
 nlevels4
 nlevels1
 basedir       !win32
diff --git a/test/test_argument_processing.c b/test/test_argument_processing.c
index e0a4479..6f3cfeb 100644
--- a/test/test_argument_processing.c
+++ b/test/test_argument_processing.c
@@ -57,13 +57,35 @@ TEST(dependency_flags_should_only_be_sent_to_the_preprocessor)
 {
 #define CMD \
 	"cc -MD -MMD -MP -MF foo.d -MT mt1 -MT mt2 -MQ mq1 -MQ mq2" \
-	" -Wp,-MD,wpmd -Wp,-MMD,wpmmd"
+	" -Wp,-MD,wpmd -Wp,-MMD,wpmmd -xMD -xMMD -xMF foo.d"
 	struct args *orig = args_init_from_string(CMD " -c foo.c -o foo.o");
 	struct args *exp_cpp = args_init_from_string(CMD);
 #undef CMD
 	struct args *exp_cc = args_init_from_string("cc -c");
 	struct args *act_cpp = NULL, *act_cc = NULL;
 	create_file("foo.c", "");
+	conf->x_means_lang = false;
+
+	CHECK(cc_process_args(orig, &act_cpp, &act_cc));
+	CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp);
+	CHECK_ARGS_EQ_FREE12(exp_cc, act_cc);
+
+	args_free(orig);
+}
+
+TEST(dependency_flags_should_only_be_sent_compiler_if_cpp_generates_deps_false)
+{
+#define CMD \
+	" -MD -MMD -MP -MF foo.d -MT mt1 -MT mt2 -MQ mq1 -MQ mq2" \
+	" -Wp,-MD,wpmd -Wp,-MMD,wpmmd -xMD -xMMD -xMF foo.d"
+	struct args *orig = args_init_from_string("cc" CMD " -c foo.c -o foo.o");
+	struct args *exp_cpp = args_init_from_string("cc");
+	struct args *exp_cc = args_init_from_string("cc -c" CMD);
+#undef CMD
+	struct args *act_cpp = NULL, *act_cc = NULL;
+	create_file("foo.c", "");
+	conf->x_means_lang = false;
+	conf->cpp_generates_deps = false;
 
 	CHECK(cc_process_args(orig, &act_cpp, &act_cc));
 	CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp);
@@ -98,12 +120,13 @@ TEST(preprocessor_only_flags_should_only_be_sent_to_the_preprocessor)
 TEST(dependency_flags_that_take_an_argument_should_not_require_space_delimiter)
 {
 	struct args *orig = args_init_from_string(
-		"cc -c -MMD -MFfoo.d -MT mt -MTmt -MQmq foo.c -o foo.o");
+		"cc -c -MMD -MFfoo.d -MT mt -MTmt -MQmq -xMFfoo.d foo.c -o foo.o");
 	struct args *exp_cpp = args_init_from_string(
-		"cc -MMD -MFfoo.d -MT mt -MTmt -MQmq");
+		"cc -MMD -MFfoo.d -MT mt -MTmt -MQmq -xMFfoo.d");
 	struct args *exp_cc = args_init_from_string("cc -c");
 	struct args *act_cpp = NULL, *act_cc = NULL;
 	create_file("foo.c", "");
+	conf->x_means_lang = false;
 
 	CHECK(cc_process_args(orig, &act_cpp, &act_cc));
 	CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp);
@@ -297,4 +320,35 @@ TEST(fprofile_flag_with_nonexisting_dir_not_be_rewritten)
 	args_free(orig);
 }
 
+TEST(set_lang_with_x)
+{
+	struct args *orig = args_init_from_string("cc -c -x c foo.c");
+	struct args *exp_cpp = args_init_from_string("cc -x c");
+	struct args *exp_cc = args_init_from_string("cc -x cpp-output -c");
+	struct args *act_cpp = NULL, *act_cc = NULL;
+	create_file("foo.c", "");
+
+	CHECK(cc_process_args(orig, &act_cpp, &act_cc));
+	CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp);
+	CHECK_ARGS_EQ_FREE12(exp_cc, act_cc);
+
+	args_free(orig);
+}
+
+TEST(x_means_lang_false)
+{
+	struct args *orig = args_init_from_string("cc -c -xFOO foo.c");
+	struct args *exp_cpp = args_init_from_string("cc -xFOO");
+	struct args *exp_cc = args_init_from_string("cc -xFOO -c");
+	struct args *act_cpp = NULL, *act_cc = NULL;
+	create_file("foo.c", "");
+	conf->x_means_lang = false;
+
+	CHECK(cc_process_args(orig, &act_cpp, &act_cc));
+	CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp);
+	CHECK_ARGS_EQ_FREE12(exp_cc, act_cc);
+
+	args_free(orig);
+}
+
 TEST_SUITE_END
diff --git a/test/test_conf.c b/test/test_conf.c
index 46be780..24a1fa8 100644
--- a/test/test_conf.c
+++ b/test/test_conf.c
@@ -20,7 +20,7 @@
 #include "test/framework.h"
 #include "test/util.h"
 
-#define N_CONFIG_ITEMS 27
+#define N_CONFIG_ITEMS 31
 static struct {
 	char *descr;
 	const char *origin;
@@ -351,9 +351,11 @@ TEST(conf_print_items)
 		true,
 		8,
 		"ce",
+		true,
 		false,
 		true,
 		"efth",
+		"ehe",
 		true,
 		true,
 		"lf",
@@ -369,9 +371,11 @@ TEST(conf_print_items)
 		  SLOPPY_INCLUDE_FILE_CTIME|SLOPPY_TIME_MACROS|
 		  SLOPPY_FILE_STAT_MATCHES,
 		false,
+		false,
 		"td",
 		022,
 		true,
+		true,
 		NULL
 	};
 	size_t n = 0;
@@ -391,9 +395,11 @@ TEST(conf_print_items)
 	CHECK_STR_EQ("compression = true", received_conf_items[n++].descr);
 	CHECK_STR_EQ("compression_level = 8", received_conf_items[n++].descr);
 	CHECK_STR_EQ("cpp_extension = ce", received_conf_items[n++].descr);
+	CHECK_STR_EQ("cpp_generates_deps = true", received_conf_items[n++].descr);
 	CHECK_STR_EQ("direct_mode = false", received_conf_items[n++].descr);
 	CHECK_STR_EQ("disable = true", received_conf_items[n++].descr);
 	CHECK_STR_EQ("extra_files_to_hash = efth", received_conf_items[n++].descr);
+	CHECK_STR_EQ("extra_hash_extensions = ehe", received_conf_items[n++].descr);
 	CHECK_STR_EQ("hard_link = true", received_conf_items[n++].descr);
 	CHECK_STR_EQ("hash_dir = true", received_conf_items[n++].descr);
 	CHECK_STR_EQ("log_file = lf", received_conf_items[n++].descr);
@@ -408,10 +414,12 @@ TEST(conf_print_items)
 	CHECK_STR_EQ("sloppiness = file_macro, include_file_mtime,"
 	             " include_file_ctime, time_macros, file_stat_matches",
 	             received_conf_items[n++].descr);
+	CHECK_STR_EQ("solaris_studio = false", received_conf_items[n++].descr);
 	CHECK_STR_EQ("stats = false", received_conf_items[n++].descr);
 	CHECK_STR_EQ("temporary_dir = td", received_conf_items[n++].descr);
 	CHECK_STR_EQ("umask = 022", received_conf_items[n++].descr);
 	CHECK_STR_EQ("unify = true", received_conf_items[n++].descr);
+	CHECK_STR_EQ("x_means_lang = true", received_conf_items[n++].descr);
 
 	for (i = 0; i < N_CONFIG_ITEMS; ++i) {
 		char *expected = format("origin%zu", i);
diff --git a/util.c b/util.c
index 4b8dd15..bee09ca 100644
--- a/util.c
+++ b/util.c
@@ -1615,3 +1615,21 @@ subst_env_in_string(const char *str, char **errmsg)
 	reformat(&result, "%s%.*s", result, (int)(q - p), p);
 	return result;
 }
+
+bool
+str_endswith(const char *str, const char *suffix)
+{
+	if (!str || !suffix) {
+		return false;
+	}
+	size_t lenstr = strlen(str);
+	size_t lensuffix = strlen(suffix);
+	if (lensuffix > lenstr) {
+		return false;
+	}
+	if (strncmp(str + lenstr - lensuffix, suffix, lensuffix) == 0) {
+		return true;
+	} else {
+		return false;
+	}
+}


More information about the ccache mailing list