[PATCH 3/4] fixdep: sync with Linux
Ahmad Fatoum
a.fatoum at pengutronix.de
Mon Nov 25 07:09:44 PST 2024
It's been quite a while since we updated fixdep, but we don't have any
barebox-specifics in fixdep, so let's sync now with Linux.
Signed-off-by: Ahmad Fatoum <a.fatoum at pengutronix.de>
---
Makefile | 4 +-
scripts/Kbuild.include | 31 ++--
scripts/Makefile.build | 8 +-
scripts/basic/Makefile | 2 +
scripts/basic/fixdep.c | 297 ++++++++++++++++++++++++---------------
scripts/compiler.h | 20 +--
scripts/include/xalloc.h | 34 +++++
scripts/mod/sumversion.c | 19 ++-
8 files changed, 265 insertions(+), 150 deletions(-)
create mode 100644 scripts/include/xalloc.h
diff --git a/Makefile b/Makefile
index 991e11bd0dee..b17452ed2cb1 100644
--- a/Makefile
+++ b/Makefile
@@ -846,7 +846,7 @@ quiet_cmd_sysmap = SYSMAP System.map
define rule_barebox__
$(if $(CONFIG_KALLSYMS),,+$(call cmd,barebox_version))
$(call cmd,barebox__)
- $(Q)echo 'cmd_$@ := $(cmd_barebox__)' > $(@D)/.$(@F).cmd
+ $(Q)echo 'savedcmd_$@ := $(cmd_barebox__)' > $(@D)/.$(@F).cmd
$(call cmd,prelink__)
$(call cmd,sysmap)
endef
@@ -893,7 +893,7 @@ cmd_ksym_ld = $(cmd_barebox__)
define rule_ksym_ld
+$(call cmd,barebox_version)
$(call cmd,barebox__)
- $(Q)echo 'cmd_$@ := $(cmd_barebox__)' > $(@D)/.$(@F).cmd
+ $(Q)echo 'savedcmd_$@ := $(cmd_barebox__)' > $(@D)/.$(@F).cmd
endef
# Generate .S file with all kernel symbols
diff --git a/scripts/Kbuild.include b/scripts/Kbuild.include
index eeb459f8fad3..315e50a2f709 100644
--- a/scripts/Kbuild.include
+++ b/scripts/Kbuild.include
@@ -183,10 +183,14 @@ cmd = @set -e; $(echo-cmd) $(cmd_$(1))
ifneq ($(KBUILD_NOCMDDEP),1)
# Check if both commands are the same including their order. Result is empty
# string if equal. User may override this check using make KBUILD_NOCMDDEP=1
-cmd-check = $(filter-out $(subst $(space),$(space_escape),$(strip $(cmd_$@))), \
+# If the target does not exist, the *.cmd file should not be included so
+# $(savedcmd_$@) gets empty. Then, target will be built even if $(newer-prereqs)
+# happens to become empty.
+cmd-check = $(filter-out $(subst $(space),$(space_escape),$(strip $(savedcmd_$@))), \
$(subst $(space),$(space_escape),$(strip $(cmd_$1))))
else
-cmd-check = $(if $(strip $(cmd_$@)),,1)
+# We still need to detect missing targets.
+cmd-check = $(if $(strip $(savedcmd_$@)),,1)
endif
# Replace >$< with >$$< to preserve $ when reloading the .cmd file
@@ -198,19 +202,26 @@ endif
make-cmd = $(call escsq,$(subst $(pound),$$(pound),$(subst $$,$$$$,$(cmd_$(1)))))
# Find any prerequisites that are newer than target or that do not exist.
-# (This is not true for now; $? should contain any non-existent prerequisites,
-# but it does not work as expected when .SECONDARY is present. This seems a bug
-# of GNU Make.)
# PHONY targets skipped in both cases.
+# If there is no prerequisite other than phony targets, $(newer-prereqs) becomes
+# empty even if the target does not exist. cmd-check saves this corner case.
newer-prereqs = $(filter-out $(PHONY),$?)
+# It is a typical mistake to forget the FORCE prerequisite. Check it here so
+# no more breakage will slip in.
+check-FORCE = $(if $(filter FORCE, $^),,$(warning FORCE prerequisite is missing))
+
+if-changed-cond = $(newer-prereqs)$(cmd-check)$(check-FORCE)
+
# Execute command if command has changed or prerequisite(s) are updated.
-if_changed = $(if $(newer-prereqs)$(cmd-check), \
+if_changed = $(if $(if-changed-cond),$(cmd_and_savecmd),@:)
+
+cmd_and_savecmd = \
$(cmd); \
- printf '%s\n' 'cmd_$@ := $(make-cmd)' > $(dot-target).cmd, @:)
+ printf '%s\n' 'savedcmd_$@ := $(make-cmd)' > $(dot-target).cmd
# Execute the command and also postprocess generated .d dependencies file.
-if_changed_dep = $(if $(newer-prereqs)$(cmd-check),$(cmd_and_fixdep),@:)
+if_changed_dep = $(if $(if-changed-cond),$(cmd_and_fixdep),@:)
cmd_and_fixdep = \
$(cmd); \
@@ -220,7 +231,7 @@ cmd_and_fixdep = \
# Usage: $(call if_changed_rule,foo)
# Will check if $(cmd_foo) or any of the prerequisites changed,
# and if so will execute $(rule_foo).
-if_changed_rule = $(if $(newer-prereqs)$(cmd-check),$(rule_$(1)),@:)
+if_changed_rule = $(if $(if-changed-cond),$(rule_$(1)),@:)
###
# why - tell why a target got built
@@ -247,7 +258,7 @@ why = \
$(if $(wildcard $@), \
$(if $(newer-prereqs),- due to: $(newer-prereqs), \
$(if $(cmd-check), \
- $(if $(cmd_$@),- due to command line change, \
+ $(if $(savedcmd_$@),- due to command line change, \
$(if $(filter $@, $(targets)), \
- due to missing .cmd file, \
- due to $(notdir $@) not in $$(targets) \
diff --git a/scripts/Makefile.build b/scripts/Makefile.build
index 25347eee0108..6b67ad17634b 100644
--- a/scripts/Makefile.build
+++ b/scripts/Makefile.build
@@ -130,6 +130,10 @@ define rule_cc_o_c
$(call cmd_and_fixdep,cc_o_c)
endef
+define rule_as_o_S
+ $(call cmd_and_fixdep,as_o_S)
+endef
+
# Built-in and composite module parts
%.pbl.o: %.c FORCE
@@ -168,10 +172,10 @@ quiet_cmd_as_o_S = AS $(quiet_modtag) $@
cmd_as_o_S = $(CC) $(a_flags) -c -o $@ $<
%.pbl.o: %.S FORCE
- $(call if_changed_dep,as_o_S)
+ $(call if_changed_rule,as_o_S)
%.o: %.S FORCE
- $(call if_changed_dep,as_o_S)
+ $(call if_changed_rule,as_o_S)
targets += $(filter-out $(subdir-obj-y), $(real-obj-y)) $(real-obj-m) $(lib-y)
targets += $(pbl-y)
diff --git a/scripts/basic/Makefile b/scripts/basic/Makefile
index eeb6a38c5551..e48754e29924 100644
--- a/scripts/basic/Makefile
+++ b/scripts/basic/Makefile
@@ -3,3 +3,5 @@
# fixdep: used to generate dependency information during build process
hostprogs-always-y += fixdep
+
+KBUILD_HOSTCFLAGS += -I$(srctree)/scripts/include/
diff --git a/scripts/basic/fixdep.c b/scripts/basic/fixdep.c
index 44e887cff49b..cdd5da7e009b 100644
--- a/scripts/basic/fixdep.c
+++ b/scripts/basic/fixdep.c
@@ -70,7 +70,7 @@
*
* 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
@@ -94,36 +94,19 @@
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
-#include <stdarg.h>
+#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
+#include <xalloc.h>
+
static void usage(void)
{
fprintf(stderr, "Usage: fixdep <depfile> <target> <cmdline>\n");
exit(1);
}
-/*
- * In the intended usage of this program, the stdout is redirected to .*.cmd
- * files. The return value of printf() 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);
-}
-
struct item {
struct item *next;
unsigned int len;
@@ -132,7 +115,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)
{
@@ -145,31 +128,14 @@ 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;
-}
-
-/*
- * Add a new value to the configuration string.
- */
-static void define_config(const char *name, int len, unsigned int hash)
-{
- struct item *aux = malloc(sizeof(*aux) + len);
-
- if (!aux) {
- perror("fixdep:malloc");
- exit(1);
- }
+ aux = xmalloc(sizeof(*aux) + len);
memcpy(aux->name, name, len);
aux->len = len;
aux->hash = hash;
@@ -177,19 +143,36 @@ static void define_config(const char *name, int len, unsigned int hash)
hashtab[hash % HASHSZ] = aux;
}
+/*
+ * 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 (in_hashtable(m, slen, config_hashtab))
+ return;
- if (is_defined_config(m, slen, hash))
- return;
-
- define_config(m, slen, hash);
/* Print out a dependency path from a symbol name. */
- xprintf(" $(wildcard include/config/%.*s) \\\n", slen, m);
+ printf(" $(wildcard include/config/%.*s) \\\n", slen, m);
}
/* test if s ends in sub */
@@ -244,11 +227,7 @@ static void *read_file(const char *filename)
perror(filename);
exit(2);
}
- buf = malloc(st.st_size + 1);
- if (!buf) {
- perror("fixdep: malloc");
- exit(2);
- }
+ buf = xmalloc(st.st_size + 1);
if (read(fd, buf, st.st_size) != st.st_size) {
perror("fixdep: read");
exit(2);
@@ -262,8 +241,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");
}
/*
@@ -271,75 +258,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)
- break;
-
- /* Find next "white space" */
- p = m;
- while (*p && *p != ' ' && *p != '\\' && *p != '\n')
- 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';
+ 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 '#':
/*
- * 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.
+ * skip comments.
+ * rustc may emit comments to dep-info.
*/
- if (is_first_dep) {
+ p++;
+ while (*p != '\0' && *p != '\n') {
/*
- * 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.
+ * escaped newlines continue the comment across
+ * multiple lines.
*/
- if (!saw_any_target) {
- saw_any_target = 1;
- xprintf("source_%s := %s\n\n",
- target, m);
- xprintf("deps_%s := \\\n", target);
+ 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 == '\\') {
+ /*
+ * backslash/newline combinations work like as
+ * a whitespace, so this is the end of token.
+ */
+ 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);
+ if (*q == '\0')
+ break;
+ q++;
+ }
+
+ /* Just discard the target */
+ if (is_target) {
+ p = q;
+ continue;
+ }
+
+ saved_c = *q;
+ *q = '\0';
+ need_parse = false;
+
+ /*
+ * 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.
+ */
+ 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);
}
- if (is_last)
- break;
-
- /*
- * Start searching for next token immediately after the first
- * "whitespace" character that follows this token.
- */
- m = p + 1;
+ is_source = false;
+ *q = saved_c;
+ p = q;
}
if (!saw_any_target) {
@@ -347,8 +403,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[])
@@ -363,11 +419,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;
}
diff --git a/scripts/compiler.h b/scripts/compiler.h
index 925cad21b6cf..d8d0e1b906df 100644
--- a/scripts/compiler.h
+++ b/scripts/compiler.h
@@ -6,6 +6,7 @@
#define __COMPILER_H__
#include <stddef.h>
+#include <xalloc.h>
#if defined(__BEOS__) || \
defined(__NetBSD__) || \
@@ -167,23 +168,4 @@ typedef uint32_t __u32;
_min1 < _min2 ? _min1 : _min2; })
#endif
-static inline void *xmalloc(size_t size)
-{
- void *p = NULL;
-
- if (!(p = malloc(size))) {
- printf("ERROR: out of memory\n");
- exit(1);
- }
-
- return p;
-}
-
-static inline void *xzalloc(size_t size)
-{
- void *p = xmalloc(size);
- memset(p, 0, size);
- return p;
-}
-
#endif
diff --git a/scripts/include/xalloc.h b/scripts/include/xalloc.h
new file mode 100644
index 000000000000..597d81ee2f6f
--- /dev/null
+++ b/scripts/include/xalloc.h
@@ -0,0 +1,34 @@
+/*
+ * Keep all the ugly #ifdef for system stuff here
+ */
+
+#ifndef __XALLOC_H__
+#define __XALLOC_H__
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+__attribute__ ((__returns_nonnull__))
+static inline void *xmalloc(size_t size)
+{
+ void *p = NULL;
+
+ if (!(p = malloc(size))) {
+ printf("ERROR: out of memory\n");
+ exit(1);
+ }
+
+ return p;
+}
+
+__attribute__ ((__returns_nonnull__))
+static inline void *xzalloc(size_t size)
+{
+ void *p = xmalloc(size);
+ memset(p, 0, size);
+ return p;
+}
+
+#endif
diff --git a/scripts/mod/sumversion.c b/scripts/mod/sumversion.c
index b5f1824a6934..20b6ee2aefe6 100644
--- a/scripts/mod/sumversion.c
+++ b/scripts/mod/sumversion.c
@@ -287,8 +287,8 @@ static int parse_file(const char *fname, struct md4_ctx *md)
return 1;
}
-/* We have dir/file.o. Open dir/.file.o.cmd, look for deps_ line to
- * figure out source file. */
+/* We have dir/file.o. Open dir/.file.o.cmd, look for source_ and deps_ line
+ * to figure out source file. */
static int parse_source_files(const char *objfile, struct md4_ctx *md)
{
char *cmd, *file, *line, *dir;
@@ -329,6 +329,21 @@ static int parse_source_files(const char *objfile, struct md4_ctx *md)
*/
while ((line = get_next_line(&pos, file, flen)) != NULL) {
char* p = line;
+
+ if (strncmp(line, "source_", sizeof("source_")-1) == 0) {
+ p = strrchr(line, ' ');
+ if (!p) {
+ warn("malformed line: %s\n", line);
+ goto out_file;
+ }
+ p++;
+ if (!parse_file(p, md)) {
+ warn("could not open %s: %s\n",
+ p, strerror(errno));
+ goto out_file;
+ }
+ continue;
+ }
if (strncmp(line, "deps_", sizeof("deps_")-1) == 0) {
check_files = 1;
continue;
--
2.39.5
More information about the barebox
mailing list