reduce_features.c 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. #include <tools/ccanlint/ccanlint.h>
  2. #include <tools/tools.h>
  3. #include <ccan/htable/htable_type.h>
  4. #include <ccan/foreach/foreach.h>
  5. #include <ccan/str/str.h>
  6. #include <ccan/hash/hash.h>
  7. #include <ccan/read_write_all/read_write_all.h>
  8. #include <errno.h>
  9. #include <err.h>
  10. #include <sys/types.h>
  11. #include <sys/stat.h>
  12. #include <fcntl.h>
  13. #include <unistd.h>
  14. #include "reduce_features.h"
  15. bool features_were_reduced;
  16. static const char *can_run(struct manifest *m)
  17. {
  18. if (!config_header)
  19. return tal_strdup(m, "Could not read config.h");
  20. return NULL;
  21. }
  22. static size_t option_hash(const char *name)
  23. {
  24. return hash(name, strlen(name), 0);
  25. }
  26. static const char *option_name(const char *name)
  27. {
  28. return name;
  29. }
  30. static bool option_cmp(const char *name1, const char *name2)
  31. {
  32. return streq(name1, name2);
  33. }
  34. HTABLE_DEFINE_TYPE(char, option_name, option_hash, option_cmp, htable_option);
  35. static struct htable_option *htable_option_new(void)
  36. {
  37. struct htable_option *opts = malloc(sizeof(*opts));
  38. htable_option_init(opts);
  39. return opts;
  40. }
  41. static void htable_option_free(struct htable_option *opts)
  42. {
  43. htable_option_clear(opts);
  44. free(opts);
  45. }
  46. static unsigned int add_options(struct htable_option *opts,
  47. struct pp_conditions *cond)
  48. {
  49. unsigned int num = 0;
  50. if (cond->parent)
  51. num += add_options(opts, cond->parent);
  52. if (cond->type == PP_COND_IF || cond->type == PP_COND_IFDEF) {
  53. if (strstarts(cond->symbol, "HAVE_")) {
  54. if (!htable_option_get(opts, cond->symbol)) {
  55. htable_option_add(opts, cond->symbol);
  56. num++;
  57. }
  58. }
  59. }
  60. return num;
  61. }
  62. static struct htable_option *get_used_options(struct manifest *m)
  63. {
  64. struct list_head *list;
  65. struct ccan_file *f;
  66. unsigned int i, num;
  67. struct htable_option *opts = htable_option_new();
  68. struct line_info *info;
  69. num = 0;
  70. foreach_ptr(list, &m->c_files, &m->h_files) {
  71. list_for_each(list, f, list) {
  72. info = get_ccan_line_info(f);
  73. struct pp_conditions *prev = NULL;
  74. for (i = 0; f->lines[i]; i++) {
  75. if (info[i].cond && info[i].cond != prev) {
  76. num += add_options(opts, info[i].cond);
  77. prev = info[i].cond;
  78. }
  79. }
  80. }
  81. }
  82. if (!num) {
  83. htable_option_free(opts);
  84. opts = NULL;
  85. }
  86. return opts;
  87. }
  88. static struct htable_option *get_config_options(struct manifest *m)
  89. {
  90. const char **lines = (const char **)tal_strsplit(m, config_header, "\n",
  91. STR_EMPTY_OK);
  92. unsigned int i;
  93. struct htable_option *opts = htable_option_new();
  94. for (i = 0; i < tal_count(lines) - 1; i++) {
  95. char *sym;
  96. if (!get_token(&lines[i], "#"))
  97. continue;
  98. if (!get_token(&lines[i], "define"))
  99. continue;
  100. sym = get_symbol_token(lines, &lines[i]);
  101. if (!strstarts(sym, "HAVE_"))
  102. continue;
  103. /* Don't override endian... */
  104. if (strends(sym, "_ENDIAN"))
  105. continue;
  106. /* Don't override HAVE_STRUCT_TIMESPEC. */
  107. if (streq(sym, "HAVE_STRUCT_TIMESPEC"))
  108. continue;
  109. if (!get_token(&lines[i], "1"))
  110. continue;
  111. htable_option_add(opts, sym);
  112. }
  113. return opts;
  114. }
  115. static void do_reduce_features(struct manifest *m,
  116. unsigned int *timeleft, struct score *score)
  117. {
  118. struct htable_option *options_used, *options_avail, *options;
  119. struct htable_option_iter i;
  120. int fd;
  121. const char *sym;
  122. char *hdr;
  123. /* This isn't really a test, as such. */
  124. score->total = 0;
  125. score->pass = true;
  126. options_used = get_used_options(m);
  127. if (!options_used) {
  128. return;
  129. }
  130. options_avail = get_config_options(m);
  131. options = NULL;
  132. for (sym = htable_option_first(options_used, &i);
  133. sym;
  134. sym = htable_option_next(options_used, &i)) {
  135. if (htable_option_get(options_avail, sym)) {
  136. if (!options)
  137. options = htable_option_new();
  138. htable_option_add(options, sym);
  139. }
  140. }
  141. htable_option_free(options_avail);
  142. htable_option_free(options_used);
  143. if (!options)
  144. return;
  145. /* Now make our own config.h variant, with our own options. */
  146. hdr = tal_strcat(m, "/* Modified by reduce_features */\n",
  147. config_header);
  148. for (sym = htable_option_first(options, &i);
  149. sym;
  150. sym = htable_option_next(options, &i)) {
  151. tal_append_fmt(&hdr, "#undef %s\n#define %s 0\n", sym, sym);
  152. }
  153. if (mkdir("reduced-features", 0700) != 0 && errno != EEXIST)
  154. err(1, "Creating reduced-features directory");
  155. fd = open("reduced-features/config.h", O_TRUNC|O_CREAT|O_RDWR, 0600);
  156. if (fd < 0)
  157. err(1, "Creating reduced-features/config.h");
  158. if (!write_all(fd, hdr, strlen(hdr)))
  159. err(1, "Writing reduced-features/config.h");
  160. close(fd);
  161. features_were_reduced = true;
  162. }
  163. struct ccanlint reduce_features = {
  164. .key = "reduce_features",
  165. .name = "Produce config.h with reduced features",
  166. .can_run = can_run,
  167. .check = do_reduce_features,
  168. /* We only want to compile up versions with reduced featuress once
  169. * objects for normal tests are built. */
  170. .needs = "tests_compile"
  171. };
  172. REGISTER_TEST(reduce_features);