[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