aboutsummaryrefslogtreecommitdiffstats
path: root/scripts/basic/fixdep.c
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/basic/fixdep.c')
-rw-r--r--scripts/basic/fixdep.c315
1 files changed, 178 insertions, 137 deletions
diff --git a/scripts/basic/fixdep.c b/scripts/basic/fixdep.c
index d98540552941..84b6efa849f4 100644
--- a/scripts/basic/fixdep.c
+++ b/scripts/basic/fixdep.c
@@ -34,7 +34,7 @@
* the config symbols are rebuilt.
*
* So if the user changes his CONFIG_HIS_DRIVER option, only the objects
- * which depend on "include/config/his/driver.h" will be rebuilt,
+ * which depend on "include/config/HIS_DRIVER" will be rebuilt,
* so most likely only his driver ;-)
*
* The idea above dates, by the way, back to Michael E Chastain, AFAIK.
@@ -70,11 +70,11 @@
*
* It first generates a line
*
- * cmd_<target> = <cmdline>
+ * savedcmd_<target> = <cmdline>
*
* and then basically copies the .<target>.d file to stdout, in the
* process filtering out the dependency on autoconf.h and adding
- * dependencies on include/config/my/option.h for every
+ * dependencies on include/config/MY_OPTION for every
* CONFIG_MY_OPTION encountered in any of the prerequisites.
*
* We don't even try to really parse the header files, but
@@ -94,7 +94,7 @@
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
-#include <stdarg.h>
+#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
@@ -105,57 +105,6 @@ static void usage(void)
exit(1);
}
-/*
- * In the intended usage of this program, the stdout is redirected to .*.cmd
- * files. The return value of printf() and putchar() must be checked to catch
- * any error, e.g. "No space left on device".
- */
-static void xprintf(const char *format, ...)
-{
- va_list ap;
- int ret;
-
- va_start(ap, format);
- ret = vprintf(format, ap);
- if (ret < 0) {
- perror("fixdep");
- exit(1);
- }
- va_end(ap);
-}
-
-static void xputchar(int c)
-{
- int ret;
-
- ret = putchar(c);
- if (ret == EOF) {
- perror("fixdep");
- exit(1);
- }
-}
-
-/*
- * Print out a dependency path from a symbol name
- */
-static void print_dep(const char *m, int slen, const char *dir)
-{
- int c, prev_c = '/', i;
-
- xprintf(" $(wildcard %s/", dir);
- for (i = 0; i < slen; i++) {
- c = m[i];
- if (c == '_')
- c = '/';
- else
- c = tolower(c);
- if (c != '/' || prev_c != '/')
- xputchar(c);
- prev_c = c;
- }
- xprintf(".h) \\\n");
-}
-
struct item {
struct item *next;
unsigned int len;
@@ -164,7 +113,7 @@ struct item {
};
#define HASHSZ 256
-static struct item *hashtab[HASHSZ];
+static struct item *config_hashtab[HASHSZ], *file_hashtab[HASHSZ];
static unsigned int strhash(const char *str, unsigned int sz)
{
@@ -177,24 +126,10 @@ static unsigned int strhash(const char *str, unsigned int sz)
}
/*
- * Lookup a value in the configuration string.
- */
-static int is_defined_config(const char *name, int len, unsigned int hash)
-{
- struct item *aux;
-
- for (aux = hashtab[hash % HASHSZ]; aux; aux = aux->next) {
- if (aux->hash == hash && aux->len == len &&
- memcmp(aux->name, name, len) == 0)
- return 1;
- }
- return 0;
-}
-
-/*
* Add a new value to the configuration string.
*/
-static void define_config(const char *name, int len, unsigned int hash)
+static void add_to_hashtable(const char *name, int len, unsigned int hash,
+ struct item *hashtab[])
{
struct item *aux = malloc(sizeof(*aux) + len);
@@ -210,17 +145,35 @@ static void define_config(const char *name, int len, unsigned int hash)
}
/*
+ * Lookup a string in the hash table. If found, just return true.
+ * If not, add it to the hashtable and return false.
+ */
+static bool in_hashtable(const char *name, int len, struct item *hashtab[])
+{
+ struct item *aux;
+ unsigned int hash = strhash(name, len);
+
+ for (aux = hashtab[hash % HASHSZ]; aux; aux = aux->next) {
+ if (aux->hash == hash && aux->len == len &&
+ memcmp(aux->name, name, len) == 0)
+ return true;
+ }
+
+ add_to_hashtable(name, len, hash, hashtab);
+
+ return false;
+}
+
+/*
* Record the use of a CONFIG_* word.
*/
static void use_config(const char *m, int slen)
{
- unsigned int hash = strhash(m, slen);
-
- if (is_defined_config(m, slen, hash))
- return;
+ if (in_hashtable(m, slen, config_hashtab))
+ return;
- define_config(m, slen, hash);
- print_dep(m, slen, "include/config");
+ /* Print out a dependency path from a symbol name. */
+ printf(" $(wildcard include/config/%.*s) \\\n", slen, m);
}
/* test if s ends in sub */
@@ -293,8 +246,16 @@ static void *read_file(const char *filename)
/* Ignore certain dependencies */
static int is_ignored_file(const char *s, int len)
{
- return str_ends_with(s, len, "include/generated/autoconf.h") ||
- str_ends_with(s, len, "include/generated/autoksyms.h");
+ return str_ends_with(s, len, "include/generated/autoconf.h");
+}
+
+/* Do not parse these files */
+static int is_no_parse_file(const char *s, int len)
+{
+ /* rustc may list binary files in dep-info */
+ return str_ends_with(s, len, ".rlib") ||
+ str_ends_with(s, len, ".rmeta") ||
+ str_ends_with(s, len, ".so");
}
/*
@@ -302,75 +263,144 @@ static int is_ignored_file(const char *s, int len)
* assignments are parsed not only by make, but also by the rather simple
* parser in scripts/mod/sumversion.c.
*/
-static void parse_dep_file(char *m, const char *target)
+static void parse_dep_file(char *p, const char *target)
{
- char *p;
- int is_last, is_target;
- int saw_any_target = 0;
- int is_first_dep = 0;
- void *buf;
-
- while (1) {
- /* Skip any "white space" */
- while (*m == ' ' || *m == '\\' || *m == '\n')
- m++;
-
- if (!*m)
+ bool saw_any_target = false;
+ bool is_target = true;
+ bool is_source = false;
+ bool need_parse;
+ char *q, saved_c;
+
+ while (*p) {
+ /* handle some special characters first. */
+ switch (*p) {
+ case '#':
+ /*
+ * skip comments.
+ * rustc may emit comments to dep-info.
+ */
+ p++;
+ while (*p != '\0' && *p != '\n') {
+ /*
+ * escaped newlines continue the comment across
+ * multiple lines.
+ */
+ if (*p == '\\')
+ p++;
+ p++;
+ }
+ continue;
+ case ' ':
+ case '\t':
+ /* skip whitespaces */
+ p++;
+ continue;
+ case '\\':
+ /*
+ * backslash/newline combinations continue the
+ * statement. Skip it just like a whitespace.
+ */
+ if (*(p + 1) == '\n') {
+ p += 2;
+ continue;
+ }
break;
-
- /* Find next "white space" */
- p = m;
- while (*p && *p != ' ' && *p != '\\' && *p != '\n')
+ case '\n':
+ /*
+ * Makefiles use a line-based syntax, where the newline
+ * is the end of a statement. After seeing a newline,
+ * we expect the next token is a target.
+ */
p++;
- is_last = (*p == '\0');
- /* Is the token we found a target name? */
- is_target = (*(p-1) == ':');
- /* Don't write any target names into the dependency file */
- if (is_target) {
- /* The /next/ file is the first dependency */
- is_first_dep = 1;
- } else if (!is_ignored_file(m, p - m)) {
- *p = '\0';
-
+ is_target = true;
+ continue;
+ case ':':
/*
- * Do not list the source file as dependency, so that
- * kbuild is not confused if a .c file is rewritten
- * into .S or vice versa. Storing it in source_* is
- * needed for modpost to compute srcversions.
+ * assume the first dependency after a colon as the
+ * source file.
*/
- if (is_first_dep) {
+ p++;
+ is_target = false;
+ is_source = true;
+ continue;
+ }
+
+ /* find the end of the token */
+ q = p;
+ while (*q != ' ' && *q != '\t' && *q != '\n' && *q != '#' && *q != ':') {
+ if (*q == '\\') {
/*
- * If processing the concatenation of multiple
- * dependency files, only process the first
- * target name, which will be the original
- * source name, and ignore any other target
- * names, which will be intermediate temporary
- * files.
+ * backslash/newline combinations work like as
+ * a whitespace, so this is the end of token.
*/
- if (!saw_any_target) {
- saw_any_target = 1;
- xprintf("source_%s := %s\n\n",
- target, m);
- xprintf("deps_%s := \\\n", target);
+ if (*(q + 1) == '\n')
+ break;
+
+ /* escaped special characters */
+ if (*(q + 1) == '#' || *(q + 1) == ':') {
+ memmove(p + 1, p, q - p);
+ p++;
}
- is_first_dep = 0;
- } else {
- xprintf(" %s \\\n", m);
+
+ q++;
}
- buf = read_file(m);
- parse_config_file(buf);
- free(buf);
+ if (*q == '\0')
+ break;
+ q++;
}
- if (is_last)
- break;
+ /* Just discard the target */
+ if (is_target) {
+ p = q;
+ continue;
+ }
+
+ saved_c = *q;
+ *q = '\0';
+ need_parse = false;
/*
- * Start searching for next token immediately after the first
- * "whitespace" character that follows this token.
+ * Do not list the source file as dependency, so that kbuild is
+ * not confused if a .c file is rewritten into .S or vice versa.
+ * Storing it in source_* is needed for modpost to compute
+ * srcversions.
*/
- m = p + 1;
+ if (is_source) {
+ /*
+ * The DT build rule concatenates multiple dep files.
+ * When processing them, only process the first source
+ * name, which will be the original one, and ignore any
+ * other source names, which will be intermediate
+ * temporary files.
+ *
+ * rustc emits the same dependency list for each
+ * emission type. It is enough to list the source name
+ * just once.
+ */
+ if (!saw_any_target) {
+ saw_any_target = true;
+ printf("source_%s := %s\n\n", target, p);
+ printf("deps_%s := \\\n", target);
+ need_parse = true;
+ }
+ } else if (!is_ignored_file(p, q - p) &&
+ !in_hashtable(p, q - p, file_hashtab)) {
+ printf(" %s \\\n", p);
+ need_parse = true;
+ }
+
+ if (need_parse && !is_no_parse_file(p, q - p)) {
+ void *buf;
+
+ buf = read_file(p);
+ parse_config_file(buf);
+ free(buf);
+ }
+
+ is_source = false;
+ *q = saved_c;
+ p = q;
}
if (!saw_any_target) {
@@ -378,8 +408,8 @@ static void parse_dep_file(char *m, const char *target)
exit(1);
}
- xprintf("\n%s: $(deps_%s)\n\n", target, target);
- xprintf("$(deps_%s):\n", target);
+ printf("\n%s: $(deps_%s)\n\n", target, target);
+ printf("$(deps_%s):\n", target);
}
int main(int argc, char *argv[])
@@ -394,11 +424,22 @@ int main(int argc, char *argv[])
target = argv[2];
cmdline = argv[3];
- xprintf("cmd_%s := %s\n\n", target, cmdline);
+ printf("savedcmd_%s := %s\n\n", target, cmdline);
buf = read_file(depfile);
parse_dep_file(buf, target);
free(buf);
+ fflush(stdout);
+
+ /*
+ * In the intended usage, the stdout is redirected to .*.cmd files.
+ * Call ferror() to catch errors such as "No space left on device".
+ */
+ if (ferror(stdout)) {
+ fprintf(stderr, "fixdep: not all data was written to the output\n");
+ exit(1);
+ }
+
return 0;
}