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/talloc/talloc.h>
  6. #include <ccan/grab_file/grab_file.h>
  7. #include <ccan/str/str.h>
  8. #include <ccan/str_talloc/str_talloc.h>
  9. #include <ccan/hash/hash.h>
  10. #include <ccan/read_write_all/read_write_all.h>
  11. #include <errno.h>
  12. #include <err.h>
  13. #include <sys/types.h>
  14. #include <sys/stat.h>
  15. #include <fcntl.h>
  16. #include <unistd.h>
  17. #include "reduce_features.h"
  18. bool features_were_reduced;
  19. static const char *can_run(struct manifest *m)
  20. {
  21. if (!config_header)
  22. return talloc_strdup(m, "Could not read config.h");
  23. return NULL;
  24. }
  25. static size_t option_hash(const char *name)
  26. {
  27. return hash(name, strlen(name), 0);
  28. }
  29. static const char *option_name(const char *name)
  30. {
  31. return name;
  32. }
  33. static bool option_cmp(const char *name1, const char *name2)
  34. {
  35. return streq(name1, name2);
  36. }
  37. HTABLE_DEFINE_TYPE(char, option_name, option_hash, option_cmp, htable_option);
  38. static struct htable_option *htable_option_new(void)
  39. {
  40. struct htable_option *opts = malloc(sizeof(*opts));
  41. htable_option_init(opts);
  42. return opts;
  43. }
  44. static void htable_option_free(struct htable_option *opts)
  45. {
  46. htable_option_clear(opts);
  47. free(opts);
  48. }
  49. static unsigned int add_options(struct htable_option *opts,
  50. struct pp_conditions *cond)
  51. {
  52. unsigned int num = 0;
  53. if (cond->parent)
  54. num += add_options(opts, cond->parent);
  55. if (cond->type == PP_COND_IF || cond->type == PP_COND_IFDEF) {
  56. if (strstarts(cond->symbol, "HAVE_")) {
  57. if (!htable_option_get(opts, cond->symbol)) {
  58. htable_option_add(opts, cond->symbol);
  59. num++;
  60. }
  61. }
  62. }
  63. return num;
  64. }
  65. static struct htable_option *get_used_options(struct manifest *m)
  66. {
  67. struct list_head *list;
  68. struct ccan_file *f;
  69. unsigned int i, num;
  70. struct htable_option *opts = htable_option_new();
  71. struct line_info *info;
  72. num = 0;
  73. foreach_ptr(list, &m->c_files, &m->h_files) {
  74. list_for_each(list, f, list) {
  75. info = get_ccan_line_info(f);
  76. struct pp_conditions *prev = NULL;
  77. for (i = 0; i < f->num_lines; i++) {
  78. if (info[i].cond && info[i].cond != prev) {
  79. num += add_options(opts, info[i].cond);
  80. prev = info[i].cond;
  81. }
  82. }
  83. }
  84. }
  85. if (!num) {
  86. htable_option_free(opts);
  87. opts = NULL;
  88. }
  89. return opts;
  90. }
  91. static struct htable_option *get_config_options(struct manifest *m)
  92. {
  93. const char **lines = (const char **)strsplit(m, config_header, "\n");
  94. unsigned int i;
  95. struct htable_option *opts = htable_option_new();
  96. for (i = 0; i < talloc_array_length(lines) - 1; i++) {
  97. char *sym;
  98. if (!get_token(&lines[i], "#"))
  99. continue;
  100. if (!get_token(&lines[i], "define"))
  101. continue;
  102. sym = get_symbol_token(lines, &lines[i]);
  103. if (!strstarts(sym, "HAVE_"))
  104. continue;
  105. /* Don't override endian... */
  106. if (strends(sym, "_ENDIAN"))
  107. continue;
  108. if (!get_token(&lines[i], "1"))
  109. continue;
  110. htable_option_add(opts, sym);
  111. }
  112. return opts;
  113. }
  114. static void do_reduce_features(struct manifest *m,
  115. unsigned int *timeleft, struct score *score)
  116. {
  117. struct htable_option *options_used, *options_avail, *options;
  118. struct htable_option_iter i;
  119. int fd;
  120. const char *sym;
  121. char *hdr;
  122. /* This isn't really a test, as such. */
  123. score->total = 0;
  124. score->pass = true;
  125. options_used = get_used_options(m);
  126. if (!options_used) {
  127. return;
  128. }
  129. options_avail = get_config_options(m);
  130. options = NULL;
  131. for (sym = htable_option_first(options_used, &i);
  132. sym;
  133. sym = htable_option_next(options_used, &i)) {
  134. if (htable_option_get(options_avail, sym)) {
  135. if (!options)
  136. options = htable_option_new();
  137. htable_option_add(options, sym);
  138. }
  139. }
  140. htable_option_free(options_avail);
  141. htable_option_free(options_used);
  142. if (!options)
  143. return;
  144. /* Now make our own config.h variant, with our own options. */
  145. hdr = talloc_strdup(m, "/* Modified by reduce_features */\n");
  146. hdr = talloc_append_string(hdr, config_header);
  147. for (sym = htable_option_first(options, &i);
  148. sym;
  149. sym = htable_option_next(options, &i)) {
  150. hdr = talloc_asprintf_append
  151. (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);