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
94#include <unistd.h>94#include <unistd.h>
95#include <fcntl.h>95#include <fcntl.h>
96#include <string.h>96#include <string.h>
97#include <stdbool.h>
97#include <stdlib.h>98#include <stdlib.h>
98#include <stdio.h>99#include <stdio.h>
99#include <ctype.h>100#include <ctype.h>
@@ -251,75 +252,139 @@ static int is_ignored_file(const char *s, int len) · 151 unmodified lines
251 * assignments are parsed not only by make, but also by the rather simple252 * assignments are parsed not only by make, but also by the rather simple
252 * parser in scripts/mod/sumversion.c.253 * parser in scripts/mod/sumversion.c.
253 */254 */
254static void parse_dep_file(char *m, const char *target)255static void parse_dep_file(char *p, const char *target)
255{256{
256 char *p;257 bool saw_any_target = false;
257 int is_last, is_target;258 bool is_target = true;
258 int saw_any_target = 0;259 bool is_source = false;
259 int is_first_dep = 0;260 bool need_parse;
260 void *buf;261 char *q, saved_c;
261 262
262 while (1) {263 while (*p) {
263 /* Skip any "white space" */264 /* handle some special characters first. */
264 while (*m == ' ' || *m == '\\' || *m == '\n')265 switch (*p) {
265 m++;266 case '#':
266 267 /*
267 if (!*m)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 }
268 break;296 break;
269 297 case '\n':
270 /* Find next "white space" */298 /*
271 p = m;299 * Makefiles use a line-based syntax, where the newline
272 while (*p && *p != ' ' && *p != '\\' && *p != '\n')300 * is the end of a statement. After seeing a newline,
301 * we expect the next token is a target.
302 */
273 p++;303 p++;
274 is_last = (*p == '\0');304 is_target = true;
275 /* Is the token we found a target name? */305 continue;
276 is_target = (*(p-1) == ':');306 case ':':
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
284 /*307 /*
285 * Do not list the source file as dependency, so that308 * assume the first dependency after a colon as the
286 * kbuild is not confused if a .c file is rewritten309 * source file.
287 * into .S or vice versa. Storing it in source_* is
288 * needed for modpost to compute srcversions.
289 */310 */
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 == '\\') {
291 /*321 /*
292 * If processing the concatenation of multiple322 * backslash/newline combinations work like as
293 * dependency files, only process the first323 * a whitespace, so this is the end of token.
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.
298 */324 */
299 if (!saw_any_target) {325 if (*(q + 1) == '\n')
300 saw_any_target = 1;326 break;
301 printf("source_%s := %s\n\n",327
302 target, m);328 /* escaped special characters */
303 printf("deps_%s := \\\n", target);329 if (*(q + 1) == '#' || *(q + 1) == ':') {
330 memmove(p + 1, p, q - p);
331 p++;
304 }332 }
305 is_first_dep = 0;333
306 } else {334 q++;
307 printf(" %s \\\n", m);
308 }335 }
309 336
310 buf = read_file(m);337 if (*q == '\0')
311 parse_config_file(buf);338 break;
312 free(buf);339 q++;
313 }340 }
314 341
315 if (is_last)342 /* Just discard the target */
316 break;343 if (is_target) {
344 p = q;
345 continue;
346 }
347
348 saved_c = *q;
349 *q = '\0';
350 need_parse = false;
317 351
318 /*352 /*
319 * Start searching for next token immediately after the first353 * Do not list the source file as dependency, so that kbuild is
320 * "whitespace" character that follows this token.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.
321 */357 */
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;
323 }388 }
324 389
325 if (!saw_any_target) {390 if (!saw_any_target) {