diff options
Diffstat (limited to 'scripts/basic')
| -rw-r--r-- | scripts/basic/.gitignore | 4 | ||||
| -rw-r--r-- | scripts/basic/Makefile | 32 | ||||
| -rw-r--r-- | scripts/basic/fixdep.c | 488 |
3 files changed, 255 insertions, 269 deletions
diff --git a/scripts/basic/.gitignore b/scripts/basic/.gitignore index a776371a3502..07c195f605a1 100644 --- a/scripts/basic/.gitignore +++ b/scripts/basic/.gitignore @@ -1 +1,3 @@ -fixdep +# SPDX-License-Identifier: GPL-2.0-only +/fixdep +/randstruct.seed diff --git a/scripts/basic/Makefile b/scripts/basic/Makefile index 4fcef87bb875..fb8e2c38fbc7 100644 --- a/scripts/basic/Makefile +++ b/scripts/basic/Makefile @@ -1,15 +1,21 @@ -### -# Makefile.basic lists the most basic programs used during the build process. -# The programs listed herein are what are needed to do the basic stuff, -# such as fix file dependencies. -# This initial step is needed to avoid files to be recompiled -# when kernel configuration changes (which is what happens when -# .config is included by main Makefile. -# --------------------------------------------------------------------------- -# fixdep: Used to generate dependency information during build process +# SPDX-License-Identifier: GPL-2.0-only +# +# fixdep: used to generate dependency information during build process -hostprogs-y := fixdep -always := $(hostprogs-y) +hostprogs-always-y += fixdep -# fixdep is needed to compile other host programs -$(addprefix $(obj)/,$(filter-out fixdep,$(always))): $(obj)/fixdep +# randstruct: the seed is needed before building the gcc-plugin or +# before running a Clang kernel build. +gen-randstruct-seed := $(srctree)/scripts/gen-randstruct-seed.sh +quiet_cmd_create_randstruct_seed = GENSEED $@ +cmd_create_randstruct_seed = \ + $(CONFIG_SHELL) $(gen-randstruct-seed) \ + $@ $(objtree)/include/generated/randstruct_hash.h +$(obj)/randstruct.seed: $(gen-randstruct-seed) FORCE + $(call if_changed,create_randstruct_seed) +always-$(CONFIG_RANDSTRUCT) += randstruct.seed + +# integer-wrap: if the .scl file changes, we need to do a full rebuild. +$(obj)/../../include/generated/integer-wrap.h: $(srctree)/scripts/integer-wrap-ignore.scl FORCE + $(call if_changed,touch) +always-$(CONFIG_UBSAN_INTEGER_WRAP) += ../../include/generated/integer-wrap.h diff --git a/scripts/basic/fixdep.c b/scripts/basic/fixdep.c index 078fe1d64e7d..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,20 +70,14 @@ * * 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. + * dependencies on include/config/MY_OPTION for every + * CONFIG_MY_OPTION encountered in any of the prerequisites. * - * 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. - * - * The algorithm to grep for "CONFIG_..." is bit unusual, but should - * be fast ;-) We don't even try to really parse the header files, but + * 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 * be picked up as well. It's not a problem with respect to * correctness, since that can only give too many dependencies, thus @@ -94,35 +88,18 @@ * (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> - -#define INT_CONF ntohl(0x434f4e46) -#define INT_ONFI ntohl(0x4f4e4649) -#define INT_NFIG ntohl(0x4e464947) -#define INT_FIG_ ntohl(0x4649475f) -char *target; -char *depfile; -char *cmdline; +#include <xalloc.h> static void usage(void) { @@ -130,23 +107,15 @@ static void usage(void) exit(1); } -/* - * Print out the commandline prefixed with cmd_<target filename> := - */ -static void print_cmdline(void) -{ - printf("cmd_%s := %s\n\n", target, cmdline); -} - 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) { @@ -159,31 +128,14 @@ 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); + struct item *aux; - if (!aux) { - perror("fixdep:malloc"); - exit(1); - } + aux = xmalloc(sizeof(*aux) + len); memcpy(aux->name, name, len); aux->len = len; aux->hash = hash; @@ -192,20 +144,23 @@ static void define_config(const char *name, int len, unsigned int hash) } /* - * Clear the set of configuration strings. + * Lookup a string in the hash table. If found, just return true. + * If not, add it to the hashtable and return false. */ -static void clear_config(void) +static bool in_hashtable(const char *name, int len, struct item *hashtab[]) { - struct item *aux, *next; - unsigned int i; + struct item *aux; + unsigned int hash = strhash(name, len); - for (i = 0; i < HASHSZ; i++) { - for (aux = hashtab[i]; aux; aux = next) { - next = aux->next; - free(aux); - } - hashtab[i] = NULL; + 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; } /* @@ -213,100 +168,89 @@ static void clear_config(void) */ static void use_config(const char *m, int slen) { - unsigned int hash = strhash(m, slen); - int c, i; - - if (is_defined_config(m, slen, hash)) - return; - - define_config(m, slen, hash); + if (in_hashtable(m, slen, config_hashtab)) + return; - 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"); + /* Print out a dependency path from a symbol name. */ + printf(" $(wildcard include/config/%.*s) \\\n", slen, m); } -static void parse_config_file(const char *map, size_t len) +/* test if s ends in sub */ +static int str_ends_with(const char *s, int slen, const char *sub) { - const int *end = (const int *) (map + len); - /* start at +1, so that p can never be < map */ - const int *m = (const int *) map + 1; - const char *p, *q; - - for (; m < end; m++) { - if (*m == INT_CONF) { p = (char *) m ; goto conf; } - if (*m == INT_ONFI) { p = (char *) m-1; goto conf; } - if (*m == INT_NFIG) { p = (char *) m-2; goto conf; } - if (*m == INT_FIG_) { p = (char *) m-3; goto conf; } - continue; - conf: - if (p > map + len - 7) - continue; - if (memcmp(p, "CONFIG_", 7)) - continue; - for (q = p + 7; q < map + len; q++) { - if (!(isalnum(*q) || *q == '_')) - goto found; - } - continue; + int sublen = strlen(sub); - found: - if (!memcmp(q - 7, "_MODULE", 7)) - q -= 7; - if( (q-p-7) < 0 ) - continue; - use_config(p+7, q-p-7); - } + if (sublen > slen) + return 0; + + return !memcmp(s + slen - sublen, sub, sublen); } -/* test is s ends in sub */ -static int strrcmp(char *s, char *sub) +static void parse_config_file(const char *p) { - int slen = strlen(s); - int sublen = strlen(sub); - - if (sublen > slen) - return 1; + const char *q, *r; + const char *start = p; - return memcmp(s + slen - sublen, sub, sublen); + while ((p = strstr(p, "CONFIG_"))) { + if (p > start && (isalnum(p[-1]) || p[-1] == '_')) { + p += 7; + continue; + } + p += 7; + q = p; + while (isalnum(*q) || *q == '_') + q++; + if (str_ends_with(p, q - p, "_MODULE")) + r = q - 7; + else + r = q; + if (r > p) + use_config(p, r - p); + p = q; + } } -static void do_config_file(const char *filename) +static void *read_file(const char *filename) { struct stat st; int fd; - void *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); } - fstat(fd, &st); - if (st.st_size == 0) { - close(fd); - return; + if (fstat(fd, &st) < 0) { + fprintf(stderr, "fixdep: error fstat'ing file: "); + perror(filename); + exit(2); } - map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); - if ((long) map == -1) { - perror("fixdep: mmap"); - close(fd); - return; + buf = xmalloc(st.st_size + 1); + if (read(fd, buf, st.st_size) != st.st_size) { + perror("fixdep: read"); + exit(2); } + buf[st.st_size] = '\0'; + close(fd); - parse_config_file(map, st.st_size); + return buf; +} - munmap(map, st.st_size); +/* Ignore certain dependencies */ +static int is_ignored_file(const char *s, int len) +{ + return str_ends_with(s, len, "include/generated/autoconf.h"); +} - close(fd); +/* 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"); } /* @@ -314,77 +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; - - clear_config(); - - 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, "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) { @@ -396,57 +407,10 @@ static void parse_dep_file(void *map, size_t len) 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); -} - -static void traps(void) -{ - static char test[] __attribute__((aligned(sizeof(int)))) = "CONF"; - int *p = (int *)test; - - if (*p != INT_CONF) { - fprintf(stderr, "fixdep: sizeof(int) != 4 or wrong endianness? %#x\n", - *p); - exit(2); - } -} - int main(int argc, char *argv[]) { - traps(); + const char *depfile, *target, *cmdline; + void *buf; if (argc != 4) usage(); @@ -455,8 +419,22 @@ int main(int argc, char *argv[]) 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; } |
