[ccache] [PATCH v5] add support for '@' parameters

Andrew Boie andrew.p.boie at intel.com
Mon Aug 6 17:30:55 MDT 2012


From: "Boie, Andrew P" <andrew.p.boie at intel.com>

These indicate to the compiler that additional command line options
should be read from a text file. If encountered, read the file,
tokenize any arguments, and if any are found, do an in-place replacement
of the '@' parameter with the arguments within the file.

args_insert() added to insert a set of arguments into a position within
another set of arguments.

args_init_from_gcc_atfile() reads and processes the argument files using the
quoting/escape conventions that GCC expects.

Signed-off-by: Andrew Boie <andrew.p.boie at intel.com>
---
 args.c           |  125 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 ccache.c         |   21 ++++++++-
 ccache.h         |    2 +
 test/test_args.c |   65 ++++++++++++++++++++++++++++
 4 files changed, 212 insertions(+), 1 deletion(-)

diff --git a/args.c b/args.c
index 13a3d37..068338c 100644
--- a/args.c
+++ b/args.c
@@ -52,11 +52,136 @@ args_init_from_string(const char *command)
 }
 
 struct args *
+args_init_from_gcc_atfile(const char *filename)
+{
+	struct args *args;
+	char *pos, *argtext, *argpos, *argbuf;
+	char quoting;
+
+	/* Used to track quoting state; if \0, we're not
+	 * inside quotes. Otherwise stores the quoting character
+	 * that started it, for matching the end quote */
+	quoting = '\0';
+
+	if (!(argtext = read_text_file(filename, 0)))
+		return NULL;
+
+	args = args_init(0, NULL);
+	pos = argtext;
+	argbuf = x_malloc(strlen(argtext) + 1);
+	argpos = argbuf;
+
+	while (1) {
+		switch (*pos) {
+		case '\\':
+			pos++;
+			if (*pos == '\0')
+				continue;
+			break;
+
+		case '\"': case '\'':
+			if (quoting != '\0') {
+				if (quoting == *pos) {
+					quoting = '\0';
+					pos++;
+					continue;
+				} else
+					break;
+			} else {
+				quoting = *pos;
+				pos++;
+				continue;
+			}
+		case '\n': case '\t': case ' ':
+			if (quoting)
+				break;
+			/* Fall through */
+		case '\0':
+			/* end of token */
+			*argpos = '\0';
+			if (argbuf[0] != '\0')
+				args_add(args, argbuf);
+			argpos = argbuf;
+			if (*pos == '\0')
+				goto out;
+			else {
+				pos++;
+				continue;
+			}
+		}
+		*argpos = *pos;
+		pos++;
+		argpos++;
+	}
+out:
+	free(argbuf);
+	free(argtext);
+	return args;
+}
+
+struct args *
 args_copy(struct args *args)
 {
 	return args_init(args->argc, args->argv);
 }
 
+/* Insert all arguments in src into dest at position index.
+ * If replace is true, the element at dest->argv[index] is replaced
+ * with the contents of src and everything past it is shifted.
+ * Otherwise, dest->argv[index] is also shifted.
+ *
+ * src is consumed by this operation and should not be freed or used
+ * again by the caller */
+void
+args_insert(struct args *dest, int index, struct args *src, bool replace)
+{
+	int offset;
+	int j;
+
+	/* Adjustments made if we are replacing or shifting the element
+	 * currently at dest->argv[index] */
+	offset = replace ? 1 : 0;
+
+	if (replace)
+		free(dest->argv[index]);
+
+	if (src->argc == 0) {
+		if (replace) {
+			/* Have to shift everything down by 1 since
+			 * we replaced with an empty list */
+			for (j = index; j < dest->argc; j++)
+				dest->argv[j] = dest->argv[j + 1];
+			dest->argc--;
+		}
+		args_free(src);
+		return;
+	}
+
+	if (src->argc == 1 && replace) {
+		/* Trivial case; replace with 1 element */
+		dest->argv[index] = src->argv[0];
+		src->argc = 0;
+		args_free(src);
+		return;
+	}
+
+	dest->argv = (char**)x_realloc(dest->argv,
+			(src->argc + dest->argc + 1 - offset) *
+			sizeof(char *));
+
+	/* Shift arguments over */
+	for (j = dest->argc; j >= index + offset; j--)
+		dest->argv[j + src->argc - offset] = dest->argv[j];
+
+	/* Copy the new arguments into place */
+	for (j = 0; j < src->argc; j++)
+		dest->argv[j + index] = src->argv[j];
+
+	dest->argc += src->argc - offset;
+	src->argc = 0;
+	args_free(src);
+}
+
 void
 args_free(struct args *args)
 {
diff --git a/ccache.c b/ccache.c
index 8e36bdd..5792482 100644
--- a/ccache.c
+++ b/ccache.c
@@ -1445,9 +1445,28 @@ cc_process_args(struct args *orig_args, struct args **preprocessor_args,
 			goto out;
 		}
 
+		if (str_startswith(argv[i], "@")) {
+			char *argpath = argv[i] + 1;
+			struct args *file_args;
+
+			file_args = args_init_from_gcc_atfile(argpath);
+			if (!file_args) {
+				cc_log("Coudln't read arg file %s", argpath);
+				stats_update(STATS_ARGS);
+				result = false;
+				goto out;
+			}
+
+			args_insert(orig_args, i, file_args, true);
+
+			argc = orig_args->argc;
+			argv = orig_args->argv;
+			i--;
+			continue;
+		}
+
 		/* These are always too hard. */
 		if (compopt_too_hard(argv[i])
-		    || str_startswith(argv[i], "@")
 		    || str_startswith(argv[i], "-fdump-")) {
 			cc_log("Compiler option %s is unsupported", argv[i]);
 			stats_update(STATS_UNSUPPORTED);
diff --git a/ccache.h b/ccache.h
index 7e25883..a0a82aa 100644
--- a/ccache.h
+++ b/ccache.h
@@ -70,11 +70,13 @@ struct args {
 
 struct args *args_init(int, char **);
 struct args *args_init_from_string(const char *);
+struct args *args_init_from_gcc_atfile(const char *filename);
 struct args *args_copy(struct args *args);
 void args_free(struct args *args);
 void args_add(struct args *args, const char *s);
 void args_add_prefix(struct args *args, const char *s);
 void args_extend(struct args *args, struct args *to_append);
+void args_insert(struct args *dest, int index, struct args *src, bool replace);
 void args_pop(struct args *args, int n);
 void args_set(struct args *args, int index, const char *value);
 void args_strip(struct args *args, const char *prefix);
diff --git a/test/test_args.c b/test/test_args.c
index 50608fc..8da83bd 100644
--- a/test/test_args.c
+++ b/test/test_args.c
@@ -59,6 +59,31 @@ TEST(args_init_from_string)
 	args_free(args);
 }
 
+TEST(args_init_from_gcc_atfile)
+{
+	int fd;
+	struct args *args;
+	const char *argtext = "first sec\\\tond\tthi\\\\rd\nfourth  \tfif\\ th \"si'x\\\" th\" 'seve\nth'\\";
+
+	fd = open("gcc_atfile", O_CREAT | O_WRONLY, 0600);
+	CHECK(fd >= 0);
+	CHECK(write(fd, argtext, strlen(argtext)) == (ssize_t)strlen(argtext));
+	close(fd);
+
+	args = args_init_from_gcc_atfile("gcc_atfile");
+	CHECK(args);
+	CHECK_INT_EQ(7, args->argc);
+	CHECK_STR_EQ("first", args->argv[0]);
+	CHECK_STR_EQ("sec\tond", args->argv[1]);
+	CHECK_STR_EQ("thi\\rd", args->argv[2]);
+	CHECK_STR_EQ("fourth", args->argv[3]);
+	CHECK_STR_EQ("fif th", args->argv[4]);
+	CHECK_STR_EQ("si'x\" th", args->argv[5]);
+	CHECK_STR_EQ("seve\nth", args->argv[6]);
+	CHECK(!args->argv[7]);
+	args_free(args);
+}
+
 TEST(args_copy)
 {
 	struct args *args1 = args_init_from_string("foo");
@@ -144,4 +169,44 @@ TEST(args_to_string)
 	args_free(args);
 }
 
+TEST(args_insert)
+{
+	struct args *args = args_init_from_string("first second third fourth fifth");
+
+	struct args *src1 = args_init_from_string("alpha beta gamma");
+	struct args *src2 = args_init_from_string("one");
+	struct args *src3 = args_init_from_string("");
+	struct args *src4 = args_init_from_string("alpha beta gamma");
+	struct args *src5 = args_init_from_string("one");
+	struct args *src6 = args_init_from_string("");
+
+	args_insert(args, 2, src1, true);
+	CHECK_STR_EQ_FREE2("first second alpha beta gamma fourth fifth",
+			args_to_string(args));
+	CHECK_INT_EQ(7, args->argc);
+	args_insert(args, 2, src2, true);
+	CHECK_STR_EQ_FREE2("first second one beta gamma fourth fifth",
+			args_to_string(args));
+	CHECK_INT_EQ(7, args->argc);
+	args_insert(args, 2, src3, true);
+	CHECK_STR_EQ_FREE2("first second beta gamma fourth fifth",
+			args_to_string(args));
+	CHECK_INT_EQ(6, args->argc);
+
+	args_insert(args, 1, src4, false);
+	CHECK_STR_EQ_FREE2("first alpha beta gamma second beta gamma fourth fifth",
+			args_to_string(args));
+	CHECK_INT_EQ(9, args->argc);
+	args_insert(args, 1, src5, false);
+	CHECK_STR_EQ_FREE2("first one alpha beta gamma second beta gamma fourth fifth",
+			args_to_string(args));
+	CHECK_INT_EQ(10, args->argc);
+	args_insert(args, 1, src6, false);
+	CHECK_STR_EQ_FREE2("first one alpha beta gamma second beta gamma fourth fifth",
+			args_to_string(args));
+	CHECK_INT_EQ(10, args->argc);
+
+	args_free(args);
+}
+
 TEST_SUITE_END
-- 
1.7.9.5



More information about the ccache mailing list