summaryrefslogtreecommitdiff
path: root/scripts/basic/fixdep.c
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/basic/fixdep.c')
-rw-r--r--scripts/basic/fixdep.c461
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;
}