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 | version 0.9.25: | 5 | version 0.9.25: |
| 2 | 6 | ||
| 3 | - first support for x86-64 target (Shinichiro Hamaji) | 7 | - first support for x86-64 target (Shinichiro Hamaji) |
| @@ -46,7 +46,6 @@ Missing features: · 45 unmodified lines | |||
| 46 | - disable-asm and disable-bcheck options | 46 | - disable-asm and disable-bcheck options |
| 47 | - __builtin_expect() | 47 | - __builtin_expect() |
| 48 | - improve '-E' option. | 48 | - improve '-E' option. |
| 49 | - add '-MD' option | ||
| 50 | - atexit (Nigel Horne) | 49 | - atexit (Nigel Horne) |
| 51 | - packed attribute | 50 | - packed attribute |
| 52 | - C99: add variable size arrays (gcc 3.2 testsuite issue) | 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 | dynarray_reset(&s1->input_files, &s1->nb_input_files); | 1042 | dynarray_reset(&s1->input_files, &s1->nb_input_files); |
| 1043 | dynarray_reset(&s1->input_libs, &s1->nb_input_libs); | 1043 | dynarray_reset(&s1->input_libs, &s1->nb_input_libs); |
| 1044 | dynarray_reset(&s1->target_deps, &s1->nb_target_deps); | ||
| 1044 | 1045 | ||
| 1045 | #ifdef HAVE_SELINUX | 1046 | #ifdef HAVE_SELINUX |
| 1046 | munmap (s1->write_mem, s1->mem_size); | 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 | goto the_end; | 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 | if (flags & AFF_PREPROCESS) { | 1100 | if (flags & AFF_PREPROCESS) { |
| 1096 | ret = tcc_preprocess(s1); | 1101 | ret = tcc_preprocess(s1); |
| 1097 | goto the_end; | 1102 | goto the_end; |
| @@ -1585,3 +1590,37 @@ LIBTCCAPI const char *tcc_default_target(TCCState *s) · 487 unmodified lines | |||
| 1585 | 1590 | ||
| 1586 | return outfile_default; | 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 | /* Get default target filename for this compilation */ | 110 | /* Get default target filename for this compilation */ |
| 111 | LIBTCCAPI const char *tcc_default_target(TCCState *s); | 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 | #ifdef __cplusplus | 120 | #ifdef __cplusplus |
| 114 | } | 121 | } |
| 115 | #endif | 122 | #endif |
| @@ -32,6 +32,8 @@ static int output_type; · 31 unmodified lines | |||
| 32 | static int reloc_output; | 32 | static int reloc_output; |
| 33 | static const char *outfile; | 33 | static const char *outfile; |
| 34 | static int do_bench = 0; | 34 | static int do_bench = 0; |
| 35 | static int gen_deps; | ||
| 36 | static const char *deps_outfile; | ||
| 35 | 37 | ||
| 36 | #define TCC_OPTION_HAS_ARG 0x0001 | 38 | #define TCC_OPTION_HAS_ARG 0x0001 |
| 37 | #define TCC_OPTION_NOSEP 0x0002 /* cannot have space before option and arg */ | 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 | #ifdef CONFIG_TCC_BACKTRACE | 76 | #ifdef CONFIG_TCC_BACKTRACE |
| 75 | " -bt N show N callers in stack traces\n" | 77 | " -bt N show N callers in stack traces\n" |
| 76 | #endif | 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 | TCC_OPTION_w, | 120 | TCC_OPTION_w, |
| 116 | TCC_OPTION_pipe, | 121 | TCC_OPTION_pipe, |
| 117 | TCC_OPTION_E, | 122 | TCC_OPTION_E, |
| 123 | TCC_OPTION_MD, | ||
| 124 | TCC_OPTION_MF, | ||
| 118 | TCC_OPTION_x, | 125 | TCC_OPTION_x, |
| 119 | }; | 126 | }; |
| 120 | 127 | ||
| @@ -154,6 +161,8 @@ static const TCCOption tcc_options[] = { · 33 unmodified lines | |||
| 154 | { "w", TCC_OPTION_w, 0 }, | 161 | { "w", TCC_OPTION_w, 0 }, |
| 155 | { "pipe", TCC_OPTION_pipe, 0}, | 162 | { "pipe", TCC_OPTION_pipe, 0}, |
| 156 | { "E", TCC_OPTION_E, 0}, | 163 | { "E", TCC_OPTION_E, 0}, |
| 164 | { "MD", TCC_OPTION_MD, 0}, | ||
| 165 | { "MF", TCC_OPTION_MF, TCC_OPTION_HAS_ARG }, | ||
| 157 | { "x", TCC_OPTION_x, TCC_OPTION_HAS_ARG }, | 166 | { "x", TCC_OPTION_x, TCC_OPTION_HAS_ARG }, |
| 158 | { NULL }, | 167 | { NULL }, |
| 159 | }; | 168 | }; |
| @@ -374,6 +383,12 @@ static int parse_args(TCCState *s, int argc, char **argv) · 214 unmodified lines | |||
| 374 | case TCC_OPTION_E: | 383 | case TCC_OPTION_E: |
| 375 | output_type = TCC_OUTPUT_PREPROCESS; | 384 | output_type = TCC_OUTPUT_PREPROCESS; |
| 376 | break; | 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 | case TCC_OPTION_x: | 392 | case TCC_OPTION_x: |
| 378 | break; | 393 | break; |
| 379 | default: | 394 | default: |
| @@ -479,14 +494,20 @@ int main(int argc, char **argv) · 99 unmodified lines | |||
| 479 | if (do_bench) | 494 | if (do_bench) |
| 480 | tcc_print_stats(s, getclock_us() - start_time); | 495 | tcc_print_stats(s, getclock_us() - start_time); |
| 481 | 496 | ||
| 482 | if (s->output_type == TCC_OUTPUT_PREPROCESS) { | 497 | if (s->output_type == TCC_OUTPUT_MEMORY) |
| 483 | if (outfile) | ||
| 484 | fclose(s->outfile); | ||
| 485 | } else if (s->output_type == TCC_OUTPUT_MEMORY) | ||
| 486 | ret = tcc_run(s, argc - optind, argv + optind); | 498 | ret = tcc_run(s, argc - optind, argv + optind); |
| 487 | else { | 499 | else { |
| 488 | ret = tcc_output_file(s, outfile ? outfile : tcc_default_target(s)); | 500 | if (s->output_type == TCC_OUTPUT_PREPROCESS) { |
| 489 | ret = ret ? 1 : 0; | 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 | char **input_libs; | 525 | char **input_libs; |
| 526 | int nb_input_libs; | 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 | /* for tcc_relocate */ | 532 | /* for tcc_relocate */ |
| 529 | int runtime_added; | 533 | int runtime_added; |
| 530 | void *runtime_mem; | 534 | void *runtime_mem; |
| @@ -1491,6 +1491,9 @@ ST_FUNC void preprocess(int is_bof) · 1490 unmodified lines | |||
| 1491 | #ifdef INC_DEBUG | 1491 | #ifdef INC_DEBUG |
| 1492 | printf("%s: including %s\n", file->filename, buf1); | 1492 | printf("%s: including %s\n", file->filename, buf1); |
| 1493 | #endif | 1493 | #endif |
| 1494 | /* update target deps */ | ||
| 1495 | dynarray_add((void ***)&s1->target_deps, &s1->nb_target_deps, | ||
| 1496 | tcc_strdup(buf1)); | ||
| 1494 | 1497 | ||
| 1495 | /* XXX: fix current line init */ | 1498 | /* XXX: fix current line init */ |
| 1496 | /* push current file in stack */ | 1499 | /* push current file in stack */ |