Commit bc6df812 authored by Masahiro Yamada's avatar Masahiro Yamada

fixdep: parse Makefile more correctly to handle comments etc.

fixdep parses dependency files (*.d) emitted by the compiler.

*.d files are Makefiles describing the dependencies of the main source
file.

fixdep understands minimal Makefile syntax. It works well enough for
GCC and Clang, but not for rustc.

This commit improves the parser a little more for better processing
comments, escape sequences, etc.

My main motivation is to drop comments. rustc may output comments
(e.g. env-dep). Currentyly, rustc build rules invoke sed to remove
comments, but it is more efficient to do it in fixdep.
Signed-off-by: default avatarMasahiro Yamada <masahiroy@kernel.org>
Acked-by: default avatarMiguel Ojeda <ojeda@kernel.org>
Tested-by: default avatarMiguel Ojeda <ojeda@kernel.org>
parent 295d8398
...@@ -94,6 +94,7 @@ ...@@ -94,6 +94,7 @@
#include <unistd.h> #include <unistd.h>
#include <fcntl.h> #include <fcntl.h>
#include <string.h> #include <string.h>
#include <stdbool.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <ctype.h> #include <ctype.h>
...@@ -251,75 +252,139 @@ static int is_ignored_file(const char *s, int len) ...@@ -251,75 +252,139 @@ static int is_ignored_file(const char *s, int len)
* assignments are parsed not only by make, but also by the rather simple * assignments are parsed not only by make, but also by the rather simple
* parser in scripts/mod/sumversion.c. * 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; bool saw_any_target = false;
int is_last, is_target; bool is_target = true;
int saw_any_target = 0; bool is_source = false;
int is_first_dep = 0; bool need_parse;
void *buf; char *q, saved_c;
while (1) { while (*p) {
/* Skip any "white space" */ /* handle some special characters first. */
while (*m == ' ' || *m == '\\' || *m == '\n') switch (*p) {
m++; 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;
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;
}
if (!*m) /* 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; break;
/* Find next "white space" */ /* escaped special characters */
p = m; if (*(q + 1) == '#' || *(q + 1) == ':') {
while (*p && *p != ' ' && *p != '\\' && *p != '\n') memmove(p + 1, p, q - p);
p++; p++;
is_last = (*p == '\0'); }
/* Is the token we found a target name? */
is_target = (*(p-1) == ':'); q++;
/* Don't write any target names into the dependency file */ }
if (*q == '\0')
break;
q++;
}
/* Just discard the target */
if (is_target) { if (is_target) {
/* The /next/ file is the first dependency */ p = q;
is_first_dep = 1; continue;
} else if (!is_ignored_file(m, p - m)) { }
*p = '\0';
saved_c = *q;
*q = '\0';
need_parse = false;
/* /*
* Do not list the source file as dependency, so that * Do not list the source file as dependency, so that kbuild is
* kbuild is not confused if a .c file is rewritten * not confused if a .c file is rewritten into .S or vice versa.
* into .S or vice versa. Storing it in source_* is * Storing it in source_* is needed for modpost to compute
* needed for modpost to compute srcversions. * srcversions.
*/ */
if (is_first_dep) { if (is_source) {
/* /*
* If processing the concatenation of multiple * The DT build rule concatenates multiple dep files.
* dependency files, only process the first * When processing them, only process the first source
* target name, which will be the original * name, which will be the original one, and ignore any
* source name, and ignore any other target * other source names, which will be intermediate
* names, which will be intermediate temporary * temporary files.
* files.
*/ */
if (!saw_any_target) { if (!saw_any_target) {
saw_any_target = 1; saw_any_target = true;
printf("source_%s := %s\n\n", printf("source_%s := %s\n\n", target, p);
target, m);
printf("deps_%s := \\\n", target); printf("deps_%s := \\\n", target);
need_parse = true;
} }
is_first_dep = 0; } else if (!is_ignored_file(p, q - p)) {
} else { printf(" %s \\\n", p);
printf(" %s \\\n", m); need_parse = true;
} }
buf = read_file(m); if (need_parse) {
void *buf;
buf = read_file(p);
parse_config_file(buf); parse_config_file(buf);
free(buf); free(buf);
} }
if (is_last) is_source = false;
break; *q = saved_c;
p = q;
/*
* Start searching for next token immediately after the first
* "whitespace" character that follows this token.
*/
m = p + 1;
} }
if (!saw_any_target) { if (!saw_any_target) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment