examples_compile.c 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  1. #include <tools/ccanlint/ccanlint.h>
  2. #include <tools/tools.h>
  3. #include <ccan/talloc/talloc.h>
  4. #include <sys/types.h>
  5. #include <sys/stat.h>
  6. #include <fcntl.h>
  7. #include <stdint.h>
  8. #include <string.h>
  9. #include <unistd.h>
  10. #include <ctype.h>
  11. static const char *can_run(struct manifest *m)
  12. {
  13. if (safe_mode)
  14. return "Safe mode enabled";
  15. return NULL;
  16. }
  17. static char *obj_list(const struct manifest *m)
  18. {
  19. char *list = talloc_strdup(m, "");
  20. struct ccan_file *i;
  21. /* Object files for this module. */
  22. list_for_each(&m->c_files, i, list)
  23. list = talloc_asprintf_append(list, " %s", i->compiled);
  24. /* Other ccan modules we depend on. */
  25. list_for_each(&m->dep_dirs, i, list) {
  26. if (i->compiled)
  27. list = talloc_asprintf_append(list, " %s", i->compiled);
  28. }
  29. return list;
  30. }
  31. static char *lib_list(const struct manifest *m)
  32. {
  33. unsigned int i, num;
  34. char **libs = get_libs(m, ".", &num, &m->info_file->compiled);
  35. char *ret = talloc_strdup(m, "");
  36. for (i = 0; i < num; i++)
  37. ret = talloc_asprintf_append(ret, "-l%s ", libs[i]);
  38. return ret;
  39. }
  40. static char *compile(const void *ctx,
  41. struct manifest *m,
  42. struct ccan_file *file,
  43. bool keep)
  44. {
  45. char *errmsg;
  46. file->compiled = maybe_temp_file(ctx, "", keep, file->fullname);
  47. errmsg = compile_and_link(ctx, file->fullname, ccan_dir,
  48. obj_list(m), "", lib_list(m), file->compiled);
  49. if (errmsg) {
  50. talloc_free(file->compiled);
  51. return errmsg;
  52. }
  53. return NULL;
  54. }
  55. struct score {
  56. unsigned int score;
  57. char *errors;
  58. };
  59. static char *start_main(char *ret)
  60. {
  61. return talloc_asprintf_append(ret,
  62. "/* Fake function wrapper inserted */\n"
  63. "int main(int argc, char *argv[])\n"
  64. "{\n");
  65. }
  66. /* We only handle simple function definitions here. */
  67. static char *add_func(char *others, const char *line)
  68. {
  69. const char *p, *end = strchr(line, '(') - 1;
  70. while (isblank(*end)) {
  71. end--;
  72. if (end == line)
  73. return others;
  74. }
  75. for (p = end; isalnum(*p) || *p == '_'; p--) {
  76. if (p == line)
  77. return others;
  78. }
  79. return talloc_asprintf_append(others, "printf(\"%%p\", %.*s);\n",
  80. end - p + 1, p);
  81. }
  82. static bool looks_internal(const char *p)
  83. {
  84. return (strncmp(p, "#", 1) != 0
  85. && strncmp(p, "static", 6) != 0
  86. && strncmp(p, "struct", 6) != 0
  87. && strncmp(p, "union", 5) != 0);
  88. }
  89. static void strip_leading_whitespace(char **lines, unsigned prefix_len)
  90. {
  91. unsigned int i;
  92. for (i = 0; lines[i]; i++)
  93. if (strlen(lines[i]) >= prefix_len)
  94. lines[i] += prefix_len;
  95. }
  96. /* Examples will often build on prior ones. Try combining them. */
  97. static char **combine(char **lines, char **prev)
  98. {
  99. unsigned int i, lines_total, prev_total, count;
  100. char **ret;
  101. if (!prev)
  102. return NULL;
  103. /* If it looks internal, put prev at start. */
  104. if (lines[0]
  105. && isblank(lines[0][0])
  106. && looks_internal(lines[0] + strspn(lines[0], " \t"))) {
  107. count = 0;
  108. } else {
  109. /* Try inserting in first elided position */
  110. for (count = 0; lines[count]; count++) {
  111. if (strcmp(lines[count], "...") == 0)
  112. break;
  113. }
  114. if (!lines[count])
  115. return NULL;
  116. count++;
  117. }
  118. for (i = 0; lines[i]; i++);
  119. lines_total = i;
  120. for (i = 0; prev[i]; i++);
  121. prev_total = i;
  122. ret = talloc_array(lines, char *, lines_total + prev_total + 1);
  123. memcpy(ret, lines, count * sizeof(ret[0]));
  124. memcpy(ret + count, prev, prev_total * sizeof(ret[0]));
  125. memcpy(ret + count + prev_total, lines + count,
  126. (lines_total - count + 1) * sizeof(ret[0]));
  127. return ret;
  128. }
  129. static char *mangle(struct manifest *m, char **lines)
  130. {
  131. char *ret, *use_funcs = NULL;
  132. bool in_function = false, fake_function = false, has_main = false;
  133. unsigned int i;
  134. ret = talloc_strdup(m, "/* Prepend a heap of headers. */\n"
  135. "#include <assert.h>\n"
  136. "#include <err.h>\n"
  137. "#include <fcntl.h>\n"
  138. "#include <stdbool.h>\n"
  139. "#include <stdint.h>\n"
  140. "#include <stdio.h>\n"
  141. "#include <stdlib.h>\n"
  142. "#include <string.h>\n"
  143. "#include <sys/stat.h>\n"
  144. "#include <sys/types.h>\n"
  145. "#include <unistd.h>\n");
  146. ret = talloc_asprintf_append(ret, "/* Include header from module. */\n"
  147. "#include <ccan/%s/%s.h>\n",
  148. m->basename, m->basename);
  149. ret = talloc_asprintf_append(ret, "/* Useful dummy functions. */\n"
  150. "int somefunc(void);\n"
  151. "int somefunc(void) { return 0; }\n");
  152. /* Starts indented? */
  153. if (lines[0] && isblank(lines[0][0])) {
  154. unsigned prefix = strspn(lines[0], " \t");
  155. if (looks_internal(lines[0] + prefix)) {
  156. /* Wrap it all in main(). */
  157. ret = start_main(ret);
  158. fake_function = true;
  159. in_function = true;
  160. has_main = true;
  161. } else
  162. strip_leading_whitespace(lines, prefix);
  163. }
  164. /* Primitive, very primitive. */
  165. for (i = 0; lines[i]; i++) {
  166. /* } at start of line ends a function. */
  167. if (in_function) {
  168. if (lines[i][0] == '}')
  169. in_function = false;
  170. } else {
  171. /* Character at start of line, with ( and no ;
  172. * == function start. */
  173. if (!isblank(lines[i][0])
  174. && strchr(lines[i], '(')
  175. && !strchr(lines[i], ';')) {
  176. in_function = true;
  177. if (strncmp(lines[i], "int main", 8) == 0)
  178. has_main = true;
  179. if (strncmp(lines[i], "static", 6) == 0) {
  180. use_funcs = add_func(use_funcs,
  181. lines[i]);
  182. }
  183. }
  184. }
  185. /* ... means elided code. If followed by spaced line, means
  186. * next part is supposed to be inside a function. */
  187. if (strcmp(lines[i], "...") == 0) {
  188. if (!in_function
  189. && lines[i+1]
  190. && isblank(lines[i+1][0])) {
  191. /* This implies we start a function here. */
  192. ret = start_main(ret);
  193. has_main = true;
  194. fake_function = true;
  195. in_function = true;
  196. }
  197. ret = talloc_asprintf_append(ret,
  198. "/* ... removed */\n");
  199. continue;
  200. }
  201. ret = talloc_asprintf_append(ret, "%s\n", lines[i]);
  202. }
  203. /* Need a main to link successfully. */
  204. if (!has_main) {
  205. ret = talloc_asprintf_append(ret, "int main(void)\n{\n");
  206. fake_function = true;
  207. }
  208. /* Get rid of unused warnings by printing addresses of static funcs. */
  209. if (use_funcs) {
  210. if (!fake_function) {
  211. ret = talloc_asprintf_append(ret,
  212. "int use_funcs(void);\n"
  213. "int use_funcs(void) {\n");
  214. fake_function = true;
  215. }
  216. ret = talloc_asprintf_append(ret, " %s\n", use_funcs);
  217. }
  218. if (fake_function)
  219. ret = talloc_asprintf_append(ret, "return 0;\n"
  220. "}\n");
  221. return ret;
  222. }
  223. static struct ccan_file *mangle_example(struct manifest *m,
  224. struct ccan_file *example,
  225. char **lines,
  226. bool keep)
  227. {
  228. char *name, *contents;
  229. int fd;
  230. struct ccan_file *f;
  231. name = maybe_temp_file(example, ".c", keep,
  232. talloc_asprintf(m, "%s/mangled-%s",
  233. m->dir, example->name));
  234. f = new_ccan_file(example,
  235. talloc_dirname(example, name),
  236. talloc_basename(example, name));
  237. talloc_steal(f, name);
  238. fd = open(f->fullname, O_WRONLY | O_CREAT | O_EXCL, 0600);
  239. if (fd < 0)
  240. return NULL;
  241. contents = mangle(m, lines);
  242. if (write(fd, contents, strlen(contents)) != strlen(contents)) {
  243. close(fd);
  244. return NULL;
  245. }
  246. close(fd);
  247. return f;
  248. }
  249. static void *build_examples(struct manifest *m, bool keep,
  250. unsigned int *timeleft)
  251. {
  252. struct ccan_file *i;
  253. struct score *score = talloc(m, struct score);
  254. struct ccan_file *mangle;
  255. char **prev = NULL;
  256. score->score = 0;
  257. score->errors = NULL;
  258. list_for_each(&m->examples, i, list) {
  259. char *ret;
  260. examples_compile.total_score++;
  261. ret = compile(score, m, i, keep);
  262. if (!ret) {
  263. prev = get_ccan_file_lines(i);
  264. score->score++;
  265. continue;
  266. }
  267. talloc_free(ret);
  268. mangle = mangle_example(m, i, get_ccan_file_lines(i), keep);
  269. ret = compile(score, m, mangle, keep);
  270. if (!ret) {
  271. prev = get_ccan_file_lines(i);
  272. score->score++;
  273. continue;
  274. }
  275. /* Try combining with previous (successful) example... */
  276. prev = combine(get_ccan_file_lines(i), prev);
  277. if (prev) {
  278. talloc_free(ret);
  279. /* We're going to replace this failure. */
  280. if (keep)
  281. unlink(mangle->fullname);
  282. talloc_free(mangle);
  283. mangle = mangle_example(m, i, prev, keep);
  284. ret = compile(score, m, mangle, keep);
  285. if (!ret) {
  286. score->score++;
  287. continue;
  288. }
  289. }
  290. if (!score->errors)
  291. score->errors = ret;
  292. else {
  293. score->errors = talloc_append_string(score->errors,
  294. ret);
  295. talloc_free(ret);
  296. }
  297. /* This didn't work, so not a candidate for combining. */
  298. talloc_free(prev);
  299. prev = NULL;
  300. }
  301. return score;
  302. }
  303. static unsigned int score_examples(struct manifest *m, void *check_result)
  304. {
  305. struct score *score = check_result;
  306. return score->score;
  307. }
  308. static const char *describe(struct manifest *m, void *check_result)
  309. {
  310. struct score *score = check_result;
  311. if (verbose >= 2 && score->errors)
  312. return talloc_asprintf(m, "Compile errors building examples:\n"
  313. "%s", score->errors);
  314. return NULL;
  315. }
  316. struct ccanlint examples_compile = {
  317. .key = "examples-compile",
  318. .name = "Module examples compile",
  319. .score = score_examples,
  320. .check = build_examples,
  321. .describe = describe,
  322. .can_run = can_run,
  323. };
  324. REGISTER_TEST(examples_compile, &has_examples, NULL);