reduce_features.c 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  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 UNNEEDED,
  117. struct score *score)
  118. {
  119. struct htable_option *options_used, *options_avail, *options;
  120. struct htable_option_iter i;
  121. int fd;
  122. const char *sym;
  123. char *hdr;
  124. /* This isn't really a test, as such. */
  125. score->total = 0;
  126. score->pass = true;
  127. options_used = get_used_options(m);
  128. if (!options_used) {
  129. return;
  130. }
  131. options_avail = get_config_options(m);
  132. options = NULL;
  133. for (sym = htable_option_first(options_used, &i);
  134. sym;
  135. sym = htable_option_next(options_used, &i)) {
  136. if (htable_option_get(options_avail, sym)) {
  137. if (!options)
  138. options = htable_option_new();
  139. htable_option_add(options, sym);
  140. }
  141. }
  142. htable_option_free(options_avail);
  143. htable_option_free(options_used);
  144. if (!options)
  145. return;
  146. /* Now make our own config.h variant, with our own options. */
  147. hdr = tal_strcat(m, "/* Modified by reduce_features */\n",
  148. config_header);
  149. for (sym = htable_option_first(options, &i);
  150. sym;
  151. sym = htable_option_next(options, &i)) {
  152. tal_append_fmt(&hdr, "#undef %s\n#define %s 0\n", sym, sym);
  153. }
  154. if (mkdir("reduced-features", 0700) != 0 && errno != EEXIST)
  155. err(1, "Creating reduced-features directory");
  156. fd = open("reduced-features/config.h", O_TRUNC|O_CREAT|O_RDWR, 0600);
  157. if (fd < 0)
  158. err(1, "Creating reduced-features/config.h");
  159. if (!write_all(fd, hdr, strlen(hdr)))
  160. err(1, "Writing reduced-features/config.h");
  161. close(fd);
  162. features_were_reduced = true;
  163. }
  164. struct ccanlint reduce_features = {
  165. .key = "reduce_features",
  166. .name = "Produce config.h with reduced features",
  167. .can_run = can_run,
  168. .check = do_reduce_features,
  169. /* We only want to compile up versions with reduced featuress once
  170. * objects for normal tests are built. */
  171. .needs = "tests_compile"
  172. };
  173. REGISTER_TEST(reduce_features);