diff options
Diffstat (limited to 'scripts/basic/fixdep.c')
| -rw-r--r-- | scripts/basic/fixdep.c | 461 |
1 files changed, 226 insertions, 235 deletions
diff --git a/scripts/basic/fixdep.c b/scripts/basic/fixdep.c index fff818b92acb..cdd5da7e009b 100644 --- a/scripts/basic/fixdep.c +++ b/scripts/basic/fixdep.c @@ -25,7 +25,7 @@ * * So we play the same trick that "mkdep" played before. We replace * the dependency on autoconf.h by a dependency on every config - * option which is mentioned in any of the listed prequisites. + * option which is mentioned in any of the listed prerequisites. * * kconfig populates a tree in include/config/ with an empty file * for each config symbol and when the configuration is updated @@ -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/linux/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,17 +70,12 @@ * * 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 - * CONFIG_MY_OPTION encountered in any of the prequisites. - * - * It will also filter out all the dependencies on *.ver. We need - * to make sure that the generated version checksum are globally up - * to date before even starting the recursive build, so it's too late - * at this point anyway. + * 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 * merely grep, i.e. if CONFIG_FOO is mentioned in a comment, it will @@ -93,90 +88,34 @@ * (Note: it'd be easy to port over the complete mkdep state machine, * but I don't think the added complexity is worth it) */ -/* - * Note 2: if somebody writes HELLO_CONFIG_BOOM in a file, it will depend onto - * CONFIG_BOOM. This could seem a bug (not too hard to fix), but please do not - * fix it! Some UserModeLinux files (look at arch/um/) call CONFIG_BOOM as - * UML_CONFIG_BOOM, to avoid conflicts with /usr/include/linux/autoconf.h, - * through arch/um/include/uml-config.h; this fixdep "bug" makes sure that - * those files will have correct dependencies. - */ #include <sys/types.h> #include <sys/stat.h> -#include <sys/mman.h> #include <unistd.h> #include <fcntl.h> #include <string.h> +#include <stdbool.h> #include <stdlib.h> #include <stdio.h> -#include <limits.h> #include <ctype.h> -#include <arpa/inet.h> -int insert_extra_deps; -char *target; -char *depfile; -char *cmdline; +#include <xalloc.h> static void usage(void) { - fprintf(stderr, "Usage: fixdep [-e] <depfile> <target> <cmdline>\n"); - fprintf(stderr, " -e insert extra dependencies given on stdin\n"); + fprintf(stderr, "Usage: fixdep <depfile> <target> <cmdline>\n"); exit(1); } -/* - * Print out the commandline prefixed with cmd_<target filename> := - */ -static void print_cmdline(void) -{ - printf("cmd_%s := %s\n\n", target, cmdline); -} - -/* - * Print out a dependency path from a symbol name - */ -static void print_config(const char *m, int slen) -{ - int c, i; - - printf(" $(wildcard include/config/"); - for (i = 0; i < slen; i++) { - c = m[i]; - if (c == '_') - c = '/'; - else - c = tolower(c); - putchar(c); - } - printf(".h) \\\n"); -} - -static void do_extra_deps(void) -{ - if (insert_extra_deps) { - char buf[80]; - while(fgets(buf, sizeof(buf), stdin)) { - int len = strlen(buf); - if (len < 2 || buf[len-1] != '\n') { - fprintf(stderr, "fixdep: bad data on stdin\n"); - exit(1); - } - print_config(buf, len-1); - } - } -} - struct item { struct item *next; unsigned int len; unsigned int hash; - char name[0]; + char name[]; }; #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) { @@ -189,36 +128,39 @@ static unsigned int strhash(const char *str, unsigned int sz) } /* - * Lookup a value in the configuration string. + * Add a new value to the configuration string. */ -static int is_defined_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; - 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; + aux = xmalloc(sizeof(*aux) + len); + memcpy(aux->name, name, len); + aux->len = len; + aux->hash = hash; + aux->next = hashtab[hash % HASHSZ]; + hashtab[hash % HASHSZ] = aux; } /* - * Add a new value to the configuration string. + * Lookup a string in the hash table. If found, just return true. + * If not, add it to the hashtable and return false. */ -static void define_config(const char *name, int len, unsigned int hash) +static bool in_hashtable(const char *name, int len, struct item *hashtab[]) { - struct item *aux = malloc(sizeof(*aux) + len); + struct item *aux; + unsigned int hash = strhash(name, len); - if (!aux) { - perror("fixdep:malloc"); - exit(1); + for (aux = hashtab[hash % HASHSZ]; aux; aux = aux->next) { + if (aux->hash == hash && aux->len == len && + memcmp(aux->name, name, len) == 0) + return true; } - memcpy(aux->name, name, len); - aux->len = len; - aux->hash = hash; - aux->next = hashtab[hash % HASHSZ]; - hashtab[hash % HASHSZ] = aux; + + add_to_hashtable(name, len, hash, hashtab); + + return false; } /* @@ -226,25 +168,39 @@ static void define_config(const char *name, int len, unsigned int hash) */ static void use_config(const char *m, int slen) { - unsigned int hash = strhash(m, slen); + if (in_hashtable(m, slen, config_hashtab)) + return; + + /* Print out a dependency path from a symbol name. */ + printf(" $(wildcard include/config/%.*s) \\\n", slen, m); +} + +/* test if s ends in sub */ +static int str_ends_with(const char *s, int slen, const char *sub) +{ + int sublen = strlen(sub); - if (is_defined_config(m, slen, hash)) - return; + if (sublen > slen) + return 0; - define_config(m, slen, hash); - print_config(m, slen); + return !memcmp(s + slen - sublen, sub, sublen); } static void parse_config_file(const char *p) { const char *q, *r; + const char *start = p; while ((p = strstr(p, "CONFIG_"))) { + if (p > start && (isalnum(p[-1]) || p[-1] == '_')) { + p += 7; + continue; + } p += 7; q = p; - while (*q && (isalnum(*q) || *q == '_')) + while (isalnum(*q) || *q == '_') q++; - if (memcmp(q - 7, "_MODULE", 7) == 0) + if (str_ends_with(p, q - p, "_MODULE")) r = q - 7; else r = q; @@ -254,56 +210,47 @@ static void parse_config_file(const char *p) } } -/* test if s ends in sub */ -static int strrcmp(const char *s, const char *sub) -{ - int slen = strlen(s); - int sublen = strlen(sub); - - if (sublen > slen) - return 1; - - return memcmp(s + slen - sublen, sub, sublen); -} - -static void do_config_file(const char *filename) +static void *read_file(const char *filename) { struct stat st; int fd; - char *map; + char *buf; fd = open(filename, O_RDONLY); if (fd < 0) { - fprintf(stderr, "fixdep: error opening config file: "); + fprintf(stderr, "fixdep: error opening file: "); perror(filename); exit(2); } if (fstat(fd, &st) < 0) { - fprintf(stderr, "fixdep: error fstat'ing config file: "); + fprintf(stderr, "fixdep: error fstat'ing file: "); perror(filename); exit(2); } - if (st.st_size == 0) { - close(fd); - return; - } - map = malloc(st.st_size + 1); - if (!map) { - perror("fixdep: malloc"); - close(fd); - return; - } - if (read(fd, map, st.st_size) != st.st_size) { + buf = xmalloc(st.st_size + 1); + if (read(fd, buf, st.st_size) != st.st_size) { perror("fixdep: read"); - close(fd); - return; + exit(2); } - map[st.st_size] = '\0'; + buf[st.st_size] = '\0'; close(fd); - parse_config_file(map); + return buf; +} + +/* Ignore certain dependencies */ +static int is_ignored_file(const char *s, int len) +{ + return str_ends_with(s, len, "include/generated/autoconf.h"); +} - free(map); +/* 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"); } /* @@ -311,76 +258,144 @@ static void do_config_file(const char *filename) * assignments are parsed not only by make, but also by the rather simple * parser in scripts/mod/sumversion.c. */ -static void parse_dep_file(void *map, size_t len) +static void parse_dep_file(char *p, const char *target) { - char *m = map; - char *end = m + len; - char *p; - char s[PATH_MAX]; - int is_target; - int saw_any_target = 0; - int is_first_dep = 0; - - while (m < end) { - /* Skip any "white space" */ - while (m < end && (*m == ' ' || *m == '\\' || *m == '\n')) - m++; - /* Find next "white space" */ - p = m; - while (p < end && *p != ' ' && *p != '\\' && *p != '\n') + 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++; - /* 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 { - /* Save this token/filename */ - memcpy(s, m, p-m); - s[p - m] = 0; - - /* Ignore certain dependencies */ - if (strrcmp(s, "include/generated/autoconf.h") && - strrcmp(s, "include/generated/autoksyms.h") && - strrcmp(s, "arch/um/include/uml-config.h") && - strrcmp(s, "include/linux/kconfig.h") && - strrcmp(s, ".ver")) { + 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; + 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_target = true; + continue; + case ':': + /* + * assume the first dependency after a colon as the + * source file. + */ + 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 == '\\') { /* - * 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. + * backslash/newline combinations work like as + * a whitespace, so this is the end of token. */ - if (is_first_dep) { - /* - * 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. - */ - if (!saw_any_target) { - saw_any_target = 1; - printf("source_%s := %s\n\n", - target, s); - printf("deps_%s := \\\n", - target); - } - is_first_dep = 0; - } else - printf(" %s \\\n", s); - do_config_file(s); + if (*(q + 1) == '\n') + break; + + /* escaped special characters */ + if (*(q + 1) == '#' || *(q + 1) == ':') { + memmove(p + 1, p, q - p); + p++; + } + + q++; } + + if (*q == '\0') + break; + q++; + } + + /* 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) { @@ -388,62 +403,38 @@ static void parse_dep_file(void *map, size_t len) exit(1); } - do_extra_deps(); - printf("\n%s: $(deps_%s)\n\n", target, target); printf("$(deps_%s):\n", target); } -static void print_deps(void) -{ - struct stat st; - int fd; - void *map; - - fd = open(depfile, O_RDONLY); - if (fd < 0) { - fprintf(stderr, "fixdep: error opening depfile: "); - perror(depfile); - exit(2); - } - if (fstat(fd, &st) < 0) { - fprintf(stderr, "fixdep: error fstat'ing depfile: "); - perror(depfile); - exit(2); - } - if (st.st_size == 0) { - fprintf(stderr,"fixdep: %s is empty\n",depfile); - close(fd); - return; - } - map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); - if ((long) map == -1) { - perror("fixdep: mmap"); - close(fd); - return; - } - - parse_dep_file(map, st.st_size); - - munmap(map, st.st_size); - - close(fd); -} - int main(int argc, char *argv[]) { - if (argc == 5 && !strcmp(argv[1], "-e")) { - insert_extra_deps = 1; - argv++; - } else if (argc != 4) + const char *depfile, *target, *cmdline; + void *buf; + + if (argc != 4) usage(); depfile = argv[1]; target = argv[2]; cmdline = argv[3]; - print_cmdline(); - print_deps(); + 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; } |
