History

fixdep: check return value of printf() and putchar()

When there is not enough space on your storage device, the build will fail with 'No space left on device' error message. The reason is obvious from the message, so you will free up some disk space, then you will resume the build. However, sometimes you may still see a mysterious error message: unterminated call to function 'wildcard': missing ')'. If you run out of the disk space, fixdep may end up with generating incomplete .*.cmd files. For example, if the disk-full error occurs while fixdep is running print_dep(), the .*.cmd might be truncated like this: $(wildcard include/config/ When you run 'make' next time, this broken .*.cmd will be included, then Make will terminate parsing since it is a wrong syntax. Once this happens, you need to run 'make clean' or delete the broken .*.cmd file manually. Even if you do not see any error message, the .*.cmd files after any error could be potentially incomplete, and unreliable. You may miss the re-compilation due to missing header dependency. If printf() cannot output the string for disk shortage or whatever reason, it returns a negative value, but currently fixdep does not check it at all. Consequently, fixdep *successfully* generates a broken .*.cmd file. Make never notices that since fixdep exits with 0, which means success. Given the intended usage of fixdep, it must respect the return value of not only malloc(), but also printf() and putchar(). This seems a long-standing issue since the introduction of fixdep. In old days, Kbuild tried to provide an extra safety by letting fixdep output to a temporary file and renaming it after everything is done: scripts/basic/fixdep $(depfile) $@ '$(make-cmd)' > $(dot-target).tmp;\ rm -f $(depfile); \ mv -f $(dot-target).tmp $(dot-target).cmd) It was no help to avoid the current issue; fixdep successfully created a truncated tmp file, which would be renamed to a .*.cmd file. This problem should be fixed by propagating the error status to the build system because: [1] Since commit 9c2af1c7377a ("kbuild: add .DELETE_ON_ERROR special target"), Make will delete the target automatically on any failure in the recipe. [2] Since commit 392885ee82d3 ("kbuild: let fixdep directly write to .*.cmd files"), .*.cmd file is included only when the corresponding target already exists.

Masahiro Yamada committed 7 years ago

Commit 6f9ac9f1 parent 68980b41 file · +41 −10Browse files

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

Unified · Split

scripts/basic/fixdep.c+41−10
@@ -99,6 +99,7 @@ · 98 unmodified lines
99#include <unistd.h>99#include <unistd.h>
100#include <fcntl.h>100#include <fcntl.h>
101#include <string.h>101#include <string.h>
102#include <stdarg.h>
102#include <stdlib.h>103#include <stdlib.h>
103#include <stdio.h>104#include <stdio.h>
104#include <ctype.h>105#include <ctype.h>
@@ -109,6 +110,36 @@ static void usage(void) · 4 unmodified lines
109 exit(1);110 exit(1);
110}111}
111 112
113/*
114 * In the intended usage of this program, the stdout is redirected to .*.cmd
115 * files. The return value of printf() and putchar() must be checked to catch
116 * any error, e.g. "No space left on device".
117 */
118static void xprintf(const char *format, ...)
119{
120 va_list ap;
121 int ret;
122
123 va_start(ap, format);
124 ret = vprintf(format, ap);
125 if (ret < 0) {
126 perror("fixdep");
127 exit(1);
128 }
129 va_end(ap);
130}
131
132static void xputchar(int c)
133{
134 int ret;
135
136 ret = putchar(c);
137 if (ret == EOF) {
138 perror("fixdep");
139 exit(1);
140 }
141}
142
112/*143/*
113 * Print out a dependency path from a symbol name144 * Print out a dependency path from a symbol name
114 */145 */
@@ -116,7 +147,7 @@ static void print_dep(const char *m, int slen, const char *dir) · 1 unmodified line
116{147{
117 int c, prev_c = '/', i;148 int c, prev_c = '/', i;
118 149
119 printf(" $(wildcard %s/", dir);150 xprintf(" $(wildcard %s/", dir);
120 for (i = 0; i < slen; i++) {151 for (i = 0; i < slen; i++) {
121 c = m[i];152 c = m[i];
122 if (c == '_')153 if (c == '_')
@@ -124,10 +155,10 @@ static void print_dep(const char *m, int slen, const char *dir) · 1 unmodified line
124 else155 else
125 c = tolower(c);156 c = tolower(c);
126 if (c != '/' || prev_c != '/')157 if (c != '/' || prev_c != '/')
127 putchar(c);158 xputchar(c);
128 prev_c = c;159 prev_c = c;
129 }160 }
130 printf(".h) \\\n");161 xprintf(".h) \\\n");
131}162}
132 163
133struct item {164struct item {
@@ -324,13 +355,13 @@ static void parse_dep_file(char *m, const char *target) · 190 unmodified lines
324 */355 */
325 if (!saw_any_target) {356 if (!saw_any_target) {
326 saw_any_target = 1;357 saw_any_target = 1;
327 printf("source_%s := %s\n\n",358 xprintf("source_%s := %s\n\n",
328 target, m);359 target, m);
329 printf("deps_%s := \\\n", target);360 xprintf("deps_%s := \\\n", target);
330 }361 }
331 is_first_dep = 0;362 is_first_dep = 0;
332 } else {363 } else {
333 printf(" %s \\\n", m);364 xprintf(" %s \\\n", m);
334 }365 }
335 366
336 buf = read_file(m);367 buf = read_file(m);
@@ -353,8 +384,8 @@ static void parse_dep_file(char *m, const char *target) · 16 unmodified lines
353 exit(1);384 exit(1);
354 }385 }
355 386
356 printf("\n%s: $(deps_%s)\n\n", target, target);387 xprintf("\n%s: $(deps_%s)\n\n", target, target);
357 printf("$(deps_%s):\n", target);388 xprintf("$(deps_%s):\n", target);
358}389}
359 390
360int main(int argc, char *argv[])391int main(int argc, char *argv[])
@@ -369,7 +400,7 @@ int main(int argc, char *argv[]) · 8 unmodified lines
369 target = argv[2];400 target = argv[2];
370 cmdline = argv[3];401 cmdline = argv[3];
371 402
372 printf("cmd_%s := %s\n\n", target, cmdline);403 xprintf("cmd_%s := %s\n\n", target, cmdline);
373 404
374 buf = read_file(depfile);405 buf = read_file(depfile);
375 parse_dep_file(buf, target);406 parse_dep_file(buf, target);