In build systems, this is used to automatically collect target dependencies, e.g. ---- 8< (hello.c) ---- #include "hello.h" #include <stdio.h> int main() { printf("Hello World!\n"); return 0; } $ tcc -MD -c hello.c # -> hello.o, hello.d $ cat hello.d hello.o : \ hello.c \ hello.h \ /usr/include/stdio.h \ /usr/include/features.h \ /usr/include/bits/predefs.h \ /usr/include/sys/cdefs.h \ /usr/include/bits/wordsize.h \ /usr/include/gnu/stubs.h \ /usr/include/bits/wordsize.h \ /usr/include/gnu/stubs-32.h \ /home/kirr/local/tcc/lib/tcc/include/stddef.h \ /usr/include/bits/types.h \ /usr/include/bits/wordsize.h \ /usr/include/bits/typesizes.h \ /usr/include/libio.h \ /usr/include/_G_config.h \ /usr/include/wchar.h \ /home/kirr/local/tcc/lib/tcc/include/stdarg.h \ /usr/include/bits/stdio_lim.h \ /usr/include/bits/sys_errlist.h \ NOTE: gcc supports -MD only for .c -> .o, but in tcc, we generate dependencies for whatever action is being taken. E.g. for .c -> exe, the result will be: $ tcc -MD -o hello hello.c # -> hello, hello.d hello: \ /usr/lib/crt1.o \ /usr/lib/crti.o \ hello.c \ hello.h \ /usr/include/stdio.h \ /usr/include/features.h \ /usr/include/bits/predefs.h \ /usr/include/sys/cdefs.h \ /usr/include/bits/wordsize.h \ /usr/include/gnu/stubs.h \ /usr/include/bits/wordsize.h \ /usr/include/gnu/stubs-32.h \ /home/kirr/local/tcc/lib/tcc/include/stddef.h \ /usr/include/bits/types.h \ /usr/include/bits/wordsize.h \ /usr/include/bits/typesizes.h \ /usr/include/libio.h \ /usr/include/_G_config.h \ /usr/include/wchar.h \ /home/kirr/local/tcc/lib/tcc/include/stdarg.h \ /usr/include/bits/stdio_lim.h \ /usr/include/bits/sys_errlist.h \ /usr/lib/libc.so \ /lib/libc.so.6 \ /usr/lib/ld-linux.so.2 \ /lib/ld-linux.so.2 \ /usr/lib/libc_nonshared.a \ /lib/libc.so.6 \ /usr/lib/libc_nonshared.a \ /home/kirr/local/tcc/lib/tcc/libtcc1.a \ /usr/lib/crtn.o \ So tcc dependency generator is a bit more clever than one used in gcc :) Also, I've updated TODO and Changelog (in not-yet-released section). v2: (Taking inputs from grischka and me myself) - put code to generate deps file into a function. - used tcc_fileextension() instead of open-coding - generate deps only when compilation/preprocessing was successful v3: - use pstrcpy instead of snprintf(buf, sizeof(buf), "%s", ...)
Unified · Split
| @@ -1,3 +1,7 @@ | ||
| 1 | +not released: | |
| 2 | + | |
| 3 | +- Add support for -MD/-MF (automatically generate dependencies for make) | |
| 4 | + | |
| 1 | 5 | version 0.9.25: |
| 2 | 6 | |
| 3 | 7 | - first support for x86-64 target (Shinichiro Hamaji) |
| @@ -46,7 +46,6 @@ Missing features: · 45 unmodified lines | ||
| 46 | 46 | - disable-asm and disable-bcheck options |
| 47 | 47 | - __builtin_expect() |
| 48 | 48 | - improve '-E' option. |
| 49 | −- add '-MD' option | |
| 50 | 49 | - atexit (Nigel Horne) |
| 51 | 50 | - packed attribute |
| 52 | 51 | - C99: add variable size arrays (gcc 3.2 testsuite issue) |
| @@ -1041,6 +1041,7 @@ LIBTCCAPI void tcc_delete(TCCState *s1) · 1040 unmodified lines | ||
| 1041 | 1041 | |
| 1042 | 1042 | dynarray_reset(&s1->input_files, &s1->nb_input_files); |
| 1043 | 1043 | dynarray_reset(&s1->input_libs, &s1->nb_input_libs); |
| 1044 | + dynarray_reset(&s1->target_deps, &s1->nb_target_deps); | |
| 1044 | 1045 | |
| 1045 | 1046 | #ifdef HAVE_SELINUX |
| 1046 | 1047 | munmap (s1->write_mem, s1->mem_size); |
| @@ -1092,6 +1093,10 @@ ST_FUNC int tcc_add_file_internal(TCCState *s1, const char *filename, int flags) · 45 unmodified lines | ||
| 1092 | 1093 | goto the_end; |
| 1093 | 1094 | } |
| 1094 | 1095 | |
| 1096 | + /* update target deps */ | |
| 1097 | + dynarray_add((void ***)&s1->target_deps, &s1->nb_target_deps, | |
| 1098 | + tcc_strdup(filename)); | |
| 1099 | + | |
| 1095 | 1100 | if (flags & AFF_PREPROCESS) { |
| 1096 | 1101 | ret = tcc_preprocess(s1); |
| 1097 | 1102 | goto the_end; |
| @@ -1585,3 +1590,37 @@ LIBTCCAPI const char *tcc_default_target(TCCState *s) · 487 unmodified lines | ||
| 1585 | 1590 | |
| 1586 | 1591 | return outfile_default; |
| 1587 | 1592 | } |
| 1593 | + | |
| 1594 | + | |
| 1595 | +LIBTCCAPI void tcc_gen_makedeps(TCCState *s, const char *target, const char *filename) | |
| 1596 | +{ | |
| 1597 | + FILE *depout; | |
| 1598 | + char buf[1024], *ext; | |
| 1599 | + int i; | |
| 1600 | + | |
| 1601 | + if (!target) | |
| 1602 | + target = tcc_default_target(s); | |
| 1603 | + | |
| 1604 | + if (!filename) { | |
| 1605 | + /* compute filename automatically | |
| 1606 | + * dir/file.o -> dir/file.d */ | |
| 1607 | + pstrcpy(buf, sizeof(buf), target); | |
| 1608 | + ext = tcc_fileextension(buf); | |
| 1609 | + pstrcpy(ext, sizeof(buf) - (ext-buf), ".d"); | |
| 1610 | + filename = buf; | |
| 1611 | + } | |
| 1612 | + | |
| 1613 | + if (s->verbose) | |
| 1614 | + printf("<- %s\n", filename); | |
| 1615 | + | |
| 1616 | + /* XXX return err codes instead of error() ? */ | |
| 1617 | + depout = fopen(filename, "w"); | |
| 1618 | + if (!depout) | |
| 1619 | + error("could not open '%s'", filename); | |
| 1620 | + | |
| 1621 | + fprintf(depout, "%s : \\\n", target); | |
| 1622 | + for (i=0; i<s->nb_target_deps; ++i) | |
| 1623 | + fprintf(depout, "\t%s \\\n", s->target_deps[i]); | |
| 1624 | + fprintf(depout, "\n"); | |
| 1625 | + fclose(depout); | |
| 1626 | +} | |
| @@ -110,6 +110,13 @@ LIBTCCAPI void tcc_set_lib_path(TCCState *s, const char *path); · 109 unmodified lines | ||
| 110 | 110 | /* Get default target filename for this compilation */ |
| 111 | 111 | LIBTCCAPI const char *tcc_default_target(TCCState *s); |
| 112 | 112 | |
| 113 | +/* Generate make dependencies for target and store them into file | |
| 114 | + * | |
| 115 | + * !target - use default target name | |
| 116 | + * !filename - use (target.o -> target.d) | |
| 117 | + */ | |
| 118 | +LIBTCCAPI void tcc_gen_makedeps(TCCState *s, const char *target, const char *filename); | |
| 119 | + | |
| 113 | 120 | #ifdef __cplusplus |
| 114 | 121 | } |
| 115 | 122 | #endif |
| @@ -32,6 +32,8 @@ static int output_type; · 31 unmodified lines | ||
| 32 | 32 | static int reloc_output; |
| 33 | 33 | static const char *outfile; |
| 34 | 34 | static int do_bench = 0; |
| 35 | +static int gen_deps; | |
| 36 | +static const char *deps_outfile; | |
| 35 | 37 | |
| 36 | 38 | #define TCC_OPTION_HAS_ARG 0x0001 |
| 37 | 39 | #define TCC_OPTION_NOSEP 0x0002 /* cannot have space before option and arg */ |
| @@ -74,6 +76,9 @@ static void help(void) · 36 unmodified lines | ||
| 74 | 76 | #ifdef CONFIG_TCC_BACKTRACE |
| 75 | 77 | " -bt N show N callers in stack traces\n" |
| 76 | 78 | #endif |
| 79 | + "Misc options:\n" | |
| 80 | + " -MD generate target dependencies for make\n" | |
| 81 | + " -MF depfile put generated dependencies here\n" | |
| 77 | 82 | ); |
| 78 | 83 | } |
| 79 | 84 | |
| @@ -115,6 +120,8 @@ enum { · 35 unmodified lines | ||
| 115 | 120 | TCC_OPTION_w, |
| 116 | 121 | TCC_OPTION_pipe, |
| 117 | 122 | TCC_OPTION_E, |
| 123 | + TCC_OPTION_MD, | |
| 124 | + TCC_OPTION_MF, | |
| 118 | 125 | TCC_OPTION_x, |
| 119 | 126 | }; |
| 120 | 127 | |
| @@ -154,6 +161,8 @@ static const TCCOption tcc_options[] = { · 33 unmodified lines | ||
| 154 | 161 | { "w", TCC_OPTION_w, 0 }, |
| 155 | 162 | { "pipe", TCC_OPTION_pipe, 0}, |
| 156 | 163 | { "E", TCC_OPTION_E, 0}, |
| 164 | + { "MD", TCC_OPTION_MD, 0}, | |
| 165 | + { "MF", TCC_OPTION_MF, TCC_OPTION_HAS_ARG }, | |
| 157 | 166 | { "x", TCC_OPTION_x, TCC_OPTION_HAS_ARG }, |
| 158 | 167 | { NULL }, |
| 159 | 168 | }; |
| @@ -374,6 +383,12 @@ static int parse_args(TCCState *s, int argc, char **argv) · 214 unmodified lines | ||
| 374 | 383 | case TCC_OPTION_E: |
| 375 | 384 | output_type = TCC_OUTPUT_PREPROCESS; |
| 376 | 385 | break; |
| 386 | + case TCC_OPTION_MD: | |
| 387 | + gen_deps = 1; | |
| 388 | + break; | |
| 389 | + case TCC_OPTION_MF: | |
| 390 | + deps_outfile = optarg; | |
| 391 | + break; | |
| 377 | 392 | case TCC_OPTION_x: |
| 378 | 393 | break; |
| 379 | 394 | default: |
| @@ -479,14 +494,20 @@ int main(int argc, char **argv) · 99 unmodified lines | ||
| 479 | 494 | if (do_bench) |
| 480 | 495 | tcc_print_stats(s, getclock_us() - start_time); |
| 481 | 496 | |
| 482 | − if (s->output_type == TCC_OUTPUT_PREPROCESS) { | |
| 483 | − if (outfile) | |
| 484 | − fclose(s->outfile); | |
| 485 | − } else if (s->output_type == TCC_OUTPUT_MEMORY) | |
| 497 | + if (s->output_type == TCC_OUTPUT_MEMORY) | |
| 486 | 498 | ret = tcc_run(s, argc - optind, argv + optind); |
| 487 | 499 | else { |
| 488 | − ret = tcc_output_file(s, outfile ? outfile : tcc_default_target(s)); | |
| 489 | − ret = ret ? 1 : 0; | |
| 500 | + if (s->output_type == TCC_OUTPUT_PREPROCESS) { | |
| 501 | + if (outfile) | |
| 502 | + fclose(s->outfile); | |
| 503 | + } else { | |
| 504 | + ret = tcc_output_file(s, outfile ? outfile : tcc_default_target(s)); | |
| 505 | + ret = ret ? 1 : 0; | |
| 506 | + } | |
| 507 | + | |
| 508 | + /* dump collected dependencies */ | |
| 509 | + if (gen_deps && !ret) | |
| 510 | + tcc_gen_makedeps(s, outfile, deps_outfile); | |
| 490 | 511 | } |
| 491 | 512 | } |
| 492 | 513 | |
| @@ -525,6 +525,10 @@ struct TCCState { · 524 unmodified lines | ||
| 525 | 525 | char **input_libs; |
| 526 | 526 | int nb_input_libs; |
| 527 | 527 | |
| 528 | + /* automatically collected dependencies for this compilation */ | |
| 529 | + char **target_deps; | |
| 530 | + int nb_target_deps; | |
| 531 | + | |
| 528 | 532 | /* for tcc_relocate */ |
| 529 | 533 | int runtime_added; |
| 530 | 534 | void *runtime_mem; |
| @@ -1491,6 +1491,9 @@ ST_FUNC void preprocess(int is_bof) · 1490 unmodified lines | ||
| 1491 | 1491 | #ifdef INC_DEBUG |
| 1492 | 1492 | printf("%s: including %s\n", file->filename, buf1); |
| 1493 | 1493 | #endif |
| 1494 | + /* update target deps */ | |
| 1495 | + dynarray_add((void ***)&s1->target_deps, &s1->nb_target_deps, | |
| 1496 | + tcc_strdup(buf1)); | |
| 1494 | 1497 | |
| 1495 | 1498 | /* XXX: fix current line init */ |
| 1496 | 1499 | /* push current file in stack */ |