History

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.

Masahiro Yamada committed 3 years ago

Commit bc6df811 parent 295d8391 file · +119 −54Browse files

Showing the changes to scripts/basic (1 of 1 file). Show all

Unified · Split

scripts/basic/fixdep.c+119−54
@@ -94,6 +94,7 @@ · 93 unmodified lines
9494 #include <unistd.h>
9595 #include <fcntl.h>
9696 #include <string.h>
97+#include <stdbool.h>
9798 #include <stdlib.h>
9899 #include <stdio.h>
99100 #include <ctype.h>
@@ -251,75 +252,139 @@ static int is_ignored_file(const char *s, int len) · 151 unmodified lines
251252 * assignments are parsed not only by make, but also by the rather simple
252253 * parser in scripts/mod/sumversion.c.
253254 */
254−static void parse_dep_file(char *m, const char *target)
255+static void parse_dep_file(char *p, const char *target)
255256 {
256− char *p;
257− int is_last, is_target;
258− int saw_any_target = 0;
259− int is_first_dep = 0;
260− void *buf;
261−
262− while (1) {
263− /* Skip any "white space" */
264− while (*m == ' ' || *m == '\\' || *m == '\n')
265− m++;
266−
267− if (!*m)
257+ bool saw_any_target = false;
258+ bool is_target = true;
259+ bool is_source = false;
260+ bool need_parse;
261+ char *q, saved_c;
262+
263+ while (*p) {
264+ /* handle some special characters first. */
265+ switch (*p) {
266+ case '#':
267+ /*
268+ * skip comments.
269+ * rustc may emit comments to dep-info.
270+ */
271+ p++;
272+ while (*p != '\0' && *p != '\n') {
273+ /*
274+ * escaped newlines continue the comment across
275+ * multiple lines.
276+ */
277+ if (*p == '\\')
278+ p++;
279+ p++;
280+ }
281+ continue;
282+ case ' ':
283+ case '\t':
284+ /* skip whitespaces */
285+ p++;
286+ continue;
287+ case '\\':
288+ /*
289+ * backslash/newline combinations continue the
290+ * statement. Skip it just like a whitespace.
291+ */
292+ if (*(p + 1) == '\n') {
293+ p += 2;
294+ continue;
295+ }
268296 break;
269−
270− /* Find next "white space" */
271− p = m;
272− while (*p && *p != ' ' && *p != '\\' && *p != '\n')
297+ case '\n':
298+ /*
299+ * Makefiles use a line-based syntax, where the newline
300+ * is the end of a statement. After seeing a newline,
301+ * we expect the next token is a target.
302+ */
273303 p++;
274− is_last = (*p == '\0');
275− /* Is the token we found a target name? */
276− is_target = (*(p-1) == ':');
277− /* Don't write any target names into the dependency file */
278− if (is_target) {
279− /* The /next/ file is the first dependency */
280− is_first_dep = 1;
281− } else if (!is_ignored_file(m, p - m)) {
282− *p = '\0';
283−
304+ is_target = true;
305+ continue;
306+ case ':':
284307 /*
285− * Do not list the source file as dependency, so that
286− * kbuild is not confused if a .c file is rewritten
287− * into .S or vice versa. Storing it in source_* is
288− * needed for modpost to compute srcversions.
308+ * assume the first dependency after a colon as the
309+ * source file.
289310 */
290− if (is_first_dep) {
311+ p++;
312+ is_target = false;
313+ is_source = true;
314+ continue;
315+ }
316+
317+ /* find the end of the token */
318+ q = p;
319+ while (*q != ' ' && *q != '\t' && *q != '\n' && *q != '#' && *q != ':') {
320+ if (*q == '\\') {
291321 /*
292− * If processing the concatenation of multiple
293− * dependency files, only process the first
294− * target name, which will be the original
295− * source name, and ignore any other target
296− * names, which will be intermediate temporary
297− * files.
322+ * backslash/newline combinations work like as
323+ * a whitespace, so this is the end of token.
298324 */
299− if (!saw_any_target) {
300− saw_any_target = 1;
301− printf("source_%s := %s\n\n",
302− target, m);
303− printf("deps_%s := \\\n", target);
325+ if (*(q + 1) == '\n')
326+ break;
327+
328+ /* escaped special characters */
329+ if (*(q + 1) == '#' || *(q + 1) == ':') {
330+ memmove(p + 1, p, q - p);
331+ p++;
304332 }
305− is_first_dep = 0;
306− } else {
307− printf(" %s \\\n", m);
333+
334+ q++;
308335 }
309336
310− buf = read_file(m);
311− parse_config_file(buf);
312− free(buf);
337+ if (*q == '\0')
338+ break;
339+ q++;
313340 }
314341
315− if (is_last)
316− break;
342+ /* Just discard the target */
343+ if (is_target) {
344+ p = q;
345+ continue;
346+ }
347+
348+ saved_c = *q;
349+ *q = '\0';
350+ need_parse = false;
317351
318352 /*
319− * Start searching for next token immediately after the first
320− * "whitespace" character that follows this token.
353+ * Do not list the source file as dependency, so that kbuild is
354+ * not confused if a .c file is rewritten into .S or vice versa.
355+ * Storing it in source_* is needed for modpost to compute
356+ * srcversions.
321357 */
322− m = p + 1;
358+ if (is_source) {
359+ /*
360+ * The DT build rule concatenates multiple dep files.
361+ * When processing them, only process the first source
362+ * name, which will be the original one, and ignore any
363+ * other source names, which will be intermediate
364+ * temporary files.
365+ */
366+ if (!saw_any_target) {
367+ saw_any_target = true;
368+ printf("source_%s := %s\n\n", target, p);
369+ printf("deps_%s := \\\n", target);
370+ need_parse = true;
371+ }
372+ } else if (!is_ignored_file(p, q - p)) {
373+ printf(" %s \\\n", p);
374+ need_parse = true;
375+ }
376+
377+ if (need_parse) {
378+ void *buf;
379+
380+ buf = read_file(p);
381+ parse_config_file(buf);
382+ free(buf);
383+ }
384+
385+ is_source = false;
386+ *q = saved_c;
387+ p = q;
323388 }
324389
325390 if (!saw_any_target) {