ccanlint.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716
  1. /*
  2. * ccanlint: assorted checks and advice for a ccan package
  3. * Copyright (C) 2008 Rusty Russell, Idris Soule
  4. * Copyright (C) 2010 Rusty Russell, Idris Soule
  5. *
  6. * This program is free software; you can redistribute it and/or modify it
  7. * under the terms of the GNU General Public License as published by the Free
  8. * Software Foundation; either version 2 of the License, or (at your option)
  9. * any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful, but
  12. * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  13. * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
  14. * more details.
  15. *
  16. * You should have received a copy of the GNU General Public License along with
  17. * this program; if not, write to the Free Software Foundation, Inc., 51
  18. * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  19. */
  20. #include "ccanlint.h"
  21. #include "../tools.h"
  22. #include "../read_config_header.h"
  23. #include <unistd.h>
  24. #include <stdio.h>
  25. #include <stdlib.h>
  26. #include <string.h>
  27. #include <err.h>
  28. #include <ctype.h>
  29. #include <ccan/str/str.h>
  30. #include <ccan/str_talloc/str_talloc.h>
  31. #include <ccan/talloc/talloc.h>
  32. #include <ccan/opt/opt.h>
  33. #include <ccan/foreach/foreach.h>
  34. #include <ccan/grab_file/grab_file.h>
  35. #include <ccan/cast/cast.h>
  36. #include <ccan/tlist/tlist.h>
  37. #include <ccan/strmap/strmap.h>
  38. struct ccanlint_map {
  39. STRMAP_MEMBERS(struct ccanlint *);
  40. };
  41. int verbose = 0;
  42. static struct ccanlint_map tests;
  43. bool safe_mode = false;
  44. bool keep_results = false;
  45. static bool targeting = false;
  46. static unsigned int timeout;
  47. /* These are overridden at runtime if we can find config.h */
  48. const char *compiler = NULL;
  49. const char *cflags = NULL;
  50. const char *config_header;
  51. const char *ccan_dir;
  52. #if 0
  53. static void indent_print(const char *string)
  54. {
  55. while (*string) {
  56. unsigned int line = strcspn(string, "\n");
  57. printf("\t%.*s", line, string);
  58. if (string[line] == '\n') {
  59. printf("\n");
  60. line++;
  61. }
  62. string += line;
  63. }
  64. }
  65. #endif
  66. bool ask(const char *question)
  67. {
  68. char reply[80];
  69. printf("%s ", question);
  70. fflush(stdout);
  71. return fgets(reply, sizeof(reply), stdin) != NULL
  72. && toupper(reply[0]) == 'Y';
  73. }
  74. /* Skip, but don't remove. */
  75. static bool skip_test(struct dgraph_node *node, const char *why)
  76. {
  77. struct ccanlint *c = container_of(node, struct ccanlint, node);
  78. c->skip = why;
  79. return true;
  80. }
  81. static const char *dep_failed(struct manifest *m)
  82. {
  83. return "dependency couldn't run";
  84. }
  85. static bool cannot_run(struct dgraph_node *node, void *unused)
  86. {
  87. struct ccanlint *c = container_of(node, struct ccanlint, node);
  88. c->can_run = dep_failed;
  89. return true;
  90. }
  91. struct run_info {
  92. bool quiet;
  93. unsigned int score, total;
  94. struct manifest *m;
  95. const char *prefix;
  96. bool pass;
  97. };
  98. static bool run_test(struct dgraph_node *n, struct run_info *run)
  99. {
  100. struct ccanlint *i = container_of(n, struct ccanlint, node);
  101. unsigned int timeleft;
  102. struct score *score;
  103. if (i->done)
  104. return true;
  105. score = talloc(run->m, struct score);
  106. list_head_init(&score->per_file_errors);
  107. score->error = NULL;
  108. score->pass = false;
  109. score->score = 0;
  110. score->total = 1;
  111. /* We can see skipped things in two cases:
  112. * (1) _info excluded them (presumably because they fail).
  113. * (2) A prerequisite failed.
  114. */
  115. if (i->skip) {
  116. if (verbose)
  117. printf("%s%s: skipped (%s)\n",
  118. run->prefix, i->name, i->skip);
  119. /* Pass us up to the test which failed, not us. */
  120. score->pass = true;
  121. goto out;
  122. }
  123. if (i->can_run) {
  124. i->skip = i->can_run(run->m);
  125. if (i->skip) {
  126. /* Test doesn't apply, or can't run? That's OK. */
  127. if (verbose > 1)
  128. printf("%s%s: skipped (%s)\n",
  129. run->prefix, i->name, i->skip);
  130. /* Mark our dependencies to skip. */
  131. dgraph_traverse_from(&i->node, cannot_run, NULL);
  132. score->pass = true;
  133. score->total = 0;
  134. goto out;
  135. }
  136. }
  137. timeleft = timeout ? timeout : default_timeout_ms;
  138. i->check(run->m, &timeleft, score);
  139. if (timeout && timeleft == 0) {
  140. i->skip = "timeout";
  141. if (verbose)
  142. printf("%s%s: skipped (%s)\n",
  143. run->prefix, i->name, i->skip);
  144. /* Mark our dependencies to skip. */
  145. dgraph_traverse_from(&i->node, skip_test,
  146. "dependency timed out");
  147. score->pass = true;
  148. score->total = 0;
  149. goto out;
  150. }
  151. assert(score->score <= score->total);
  152. if ((!score->pass && !run->quiet)
  153. || (score->score < score->total && verbose)
  154. || verbose > 1) {
  155. printf("%s%s (%s): %s",
  156. run->prefix, i->name, i->key,
  157. score->pass ? "PASS" : "FAIL");
  158. if (score->total > 1)
  159. printf(" (+%u/%u)", score->score, score->total);
  160. printf("\n");
  161. }
  162. if ((!run->quiet && !score->pass) || verbose) {
  163. if (score->error) {
  164. printf("%s%s", score->error,
  165. strends(score->error, "\n") ? "" : "\n");
  166. }
  167. }
  168. if (!run->quiet && score->score < score->total && i->handle)
  169. i->handle(run->m, score);
  170. if (!score->pass) {
  171. /* Skip any tests which depend on this one. */
  172. dgraph_traverse_from(&i->node, skip_test, "dependency failed");
  173. }
  174. out:
  175. run->score += score->score;
  176. run->total += score->total;
  177. /* FIXME: Free score. */
  178. run->pass &= score->pass;
  179. i->done = true;
  180. if (!score->pass && i->compulsory) {
  181. warnx("%s%s failed", run->prefix, i->name);
  182. run->score = 0;
  183. return false;
  184. }
  185. return true;
  186. }
  187. static void register_test(struct ccanlint *test)
  188. {
  189. if (!strmap_add(&tests, test->key, test))
  190. err(1, "Adding test %s", test->key);
  191. test->options = talloc_array(NULL, char *, 1);
  192. test->options[0] = NULL;
  193. dgraph_init_node(&test->node);
  194. }
  195. static bool get_test(const char *member, struct ccanlint *i,
  196. struct ccanlint **ret)
  197. {
  198. if (tlist_empty(&i->node.edge[DGRAPH_TO])) {
  199. *ret = i;
  200. return false;
  201. }
  202. return true;
  203. }
  204. /**
  205. * get_next_test - retrieves the next test to be processed
  206. **/
  207. static inline struct ccanlint *get_next_test(void)
  208. {
  209. struct ccanlint *i = NULL;
  210. strmap_iterate(&tests, get_test, &i);
  211. if (i)
  212. return i;
  213. if (strmap_empty(&tests))
  214. return NULL;
  215. errx(1, "Can't make process; test dependency cycle");
  216. }
  217. static struct ccanlint *find_test(const char *key)
  218. {
  219. return strmap_get(&tests, key);
  220. }
  221. bool is_excluded(const char *name)
  222. {
  223. return find_test(name)->skip != NULL;
  224. }
  225. static bool init_deps(const char *member, struct ccanlint *c, void *unused)
  226. {
  227. char **deps = strsplit(NULL, c->needs, " ");
  228. unsigned int i;
  229. for (i = 0; deps[i]; i++) {
  230. struct ccanlint *dep;
  231. dep = find_test(deps[i]);
  232. if (!dep)
  233. errx(1, "BUG: unknown dep '%s' for %s",
  234. deps[i], c->key);
  235. dgraph_add_edge(&dep->node, &c->node);
  236. }
  237. talloc_free(deps);
  238. return true;
  239. }
  240. static bool check_names(const char *member, struct ccanlint *c,
  241. struct ccanlint_map *names)
  242. {
  243. if (!strmap_add(names, c->name, c))
  244. err(1, "Duplicate name %s", c->name);
  245. return true;
  246. }
  247. static void init_tests(void)
  248. {
  249. struct ccanlint_map names;
  250. struct ccanlint **table;
  251. size_t i, num;
  252. strmap_init(&tests);
  253. table = autodata_get(ccanlint_tests, &num);
  254. for (i = 0; i < num; i++)
  255. register_test(table[i]);
  256. autodata_free(table);
  257. strmap_iterate(&tests, init_deps, NULL);
  258. /* Check for duplicate names. */
  259. strmap_init(&names);
  260. strmap_iterate(&tests, check_names, &names);
  261. strmap_clear(&names);
  262. }
  263. static bool reset_test(struct dgraph_node *node, void *unused)
  264. {
  265. struct ccanlint *c = container_of(node, struct ccanlint, node);
  266. c->skip = NULL;
  267. c->done = false;
  268. return true;
  269. }
  270. static void reset_tests(struct dgraph_node *all)
  271. {
  272. dgraph_traverse_to(all, reset_test, NULL);
  273. }
  274. static bool print_deps(const char *member, struct ccanlint *c, void *unused)
  275. {
  276. if (!tlist_empty(&c->node.edge[DGRAPH_FROM])) {
  277. struct dgraph_edge *e;
  278. printf("These depend on %s:\n", c->key);
  279. dgraph_for_each_edge(&c->node, e, DGRAPH_FROM) {
  280. struct ccanlint *to = container_of(e->n[DGRAPH_TO],
  281. struct ccanlint,
  282. node);
  283. printf("\t%s\n", to->key);
  284. }
  285. }
  286. return true;
  287. }
  288. static void print_test_depends(void)
  289. {
  290. printf("Tests:\n");
  291. strmap_iterate(&tests, print_deps, NULL);
  292. }
  293. static int show_tmpdir(const char *dir)
  294. {
  295. printf("You can find ccanlint working files in '%s'\n", dir);
  296. return 0;
  297. }
  298. static char *keep_tests(void *unused)
  299. {
  300. keep_results = true;
  301. /* Don't automatically destroy temporary dir. */
  302. talloc_set_destructor(temp_dir(NULL), show_tmpdir);
  303. return NULL;
  304. }
  305. static bool remove_test(struct dgraph_node *node, const char *why)
  306. {
  307. struct ccanlint *c = container_of(node, struct ccanlint, node);
  308. c->skip = why;
  309. dgraph_clear_node(node);
  310. return true;
  311. }
  312. static char *exclude_test(const char *testname, void *unused)
  313. {
  314. struct ccanlint *i = find_test(testname);
  315. if (!i)
  316. return talloc_asprintf(NULL, "No test %s to --exclude",
  317. testname);
  318. /* Remove this, and everything which depends on it. */
  319. dgraph_traverse_from(&i->node, remove_test, "excluded on command line");
  320. remove_test(&i->node, "excluded on command line");
  321. return NULL;
  322. }
  323. static void skip_test_and_deps(struct ccanlint *c, const char *why)
  324. {
  325. /* Skip this, and everything which depends on us. */
  326. dgraph_traverse_from(&c->node, skip_test, why);
  327. skip_test(&c->node, why);
  328. }
  329. static char *list_tests(void *arg)
  330. {
  331. struct ccanlint *i;
  332. printf("Tests:\n");
  333. /* This makes them print in topological order. */
  334. while ((i = get_next_test()) != NULL) {
  335. printf(" %-25s %s\n", i->key, i->name);
  336. dgraph_clear_node(&i->node);
  337. strmap_del(&tests, i->key, NULL);
  338. }
  339. exit(0);
  340. }
  341. static bool draw_test(const char *member, struct ccanlint *c, const char *style)
  342. {
  343. /*
  344. * todo: escape labels in case ccanlint test keys have
  345. * characters interpreted as GraphViz syntax.
  346. */
  347. printf("\t\"%p\" [label=\"%s\"%s]\n", c, c->key, style);
  348. return true;
  349. }
  350. static void test_dgraph_vertices(const char *style)
  351. {
  352. strmap_iterate(&tests, draw_test, style);
  353. }
  354. static bool draw_edges(const char *member, struct ccanlint *c, void *unused)
  355. {
  356. struct dgraph_edge *e;
  357. dgraph_for_each_edge(&c->node, e, DGRAPH_FROM) {
  358. struct ccanlint *to = container_of(e->n[DGRAPH_TO],
  359. struct ccanlint,
  360. node);
  361. printf("\t\"%p\" -> \"%p\"\n", c->name, to->name);
  362. }
  363. return true;
  364. }
  365. static void test_dgraph_edges(void)
  366. {
  367. strmap_iterate(&tests, draw_edges, NULL);
  368. }
  369. static char *test_dependency_graph(void *arg)
  370. {
  371. puts("digraph G {");
  372. test_dgraph_vertices("");
  373. test_dgraph_edges();
  374. puts("}");
  375. exit(0);
  376. }
  377. /* Remove empty lines. */
  378. static char **collapse(char **lines, unsigned int *nump)
  379. {
  380. unsigned int i, j;
  381. for (i = j = 0; lines[i]; i++) {
  382. if (lines[i][0])
  383. lines[j++] = lines[i];
  384. }
  385. lines[j] = NULL;
  386. if (nump)
  387. *nump = j;
  388. return lines;
  389. }
  390. static void add_options(struct ccanlint *test, char **options,
  391. unsigned int num_options)
  392. {
  393. unsigned int num;
  394. if (!test->options)
  395. num = 0;
  396. else
  397. /* -1, because last one is NULL. */
  398. num = talloc_array_length(test->options) - 1;
  399. test->options = talloc_realloc(NULL, test->options,
  400. char *,
  401. num + num_options + 1);
  402. memcpy(&test->options[num], options, (num_options + 1)*sizeof(char *));
  403. }
  404. void add_info_options(struct ccan_file *info)
  405. {
  406. struct doc_section *d;
  407. unsigned int i;
  408. struct ccanlint *test;
  409. list_for_each(get_ccan_file_docs(info), d, list) {
  410. if (!streq(d->type, "ccanlint"))
  411. continue;
  412. for (i = 0; i < d->num_lines; i++) {
  413. unsigned int num_words;
  414. char **words = collapse(strsplit(d, d->lines[i], " \t"),
  415. &num_words);
  416. if (num_words == 0)
  417. continue;
  418. if (strncmp(words[0], "//", 2) == 0)
  419. continue;
  420. test = find_test(words[0]);
  421. if (!test) {
  422. warnx("%s: unknown ccanlint test '%s'",
  423. info->fullname, words[0]);
  424. continue;
  425. }
  426. if (!words[1]) {
  427. warnx("%s: no argument to test '%s'",
  428. info->fullname, words[0]);
  429. continue;
  430. }
  431. /* Known failure? */
  432. if (strcasecmp(words[1], "FAIL") == 0) {
  433. if (!targeting)
  434. skip_test_and_deps(test,
  435. "excluded in _info"
  436. " file");
  437. } else {
  438. if (!test->takes_options)
  439. warnx("%s: %s doesn't take options",
  440. info->fullname, words[0]);
  441. add_options(test, words+1, num_words-1);
  442. }
  443. }
  444. }
  445. }
  446. /* If options are of form "filename:<option>" they only apply to that file */
  447. char **per_file_options(const struct ccanlint *test, struct ccan_file *f)
  448. {
  449. char **ret;
  450. unsigned int i, j = 0;
  451. /* Fast path. */
  452. if (!test->options[0])
  453. return test->options;
  454. ret = talloc_array(f, char *, talloc_array_length(test->options));
  455. for (i = 0; test->options[i]; i++) {
  456. char *optname;
  457. if (!test->options[i] || !strchr(test->options[i], ':')) {
  458. optname = test->options[i];
  459. } else if (strstarts(test->options[i], f->name)
  460. && test->options[i][strlen(f->name)] == ':') {
  461. optname = test->options[i] + strlen(f->name) + 1;
  462. } else
  463. continue;
  464. /* FAIL overrides anything else. */
  465. if (streq(optname, "FAIL")) {
  466. ret = talloc_array(f, char *, 2);
  467. ret[0] = (char *)"FAIL";
  468. ret[1] = NULL;
  469. return ret;
  470. }
  471. ret[j++] = optname;
  472. }
  473. ret[j] = NULL;
  474. /* Shrink it to size so talloc_array_length() works as expected. */
  475. return talloc_realloc(NULL, ret, char *, j + 1);
  476. }
  477. static char *opt_set_const_charp(const char *arg, const char **p)
  478. {
  479. return opt_set_charp(arg, cast_const2(char **, p));
  480. }
  481. static char *opt_set_target(const char *arg, struct dgraph_node *all)
  482. {
  483. struct ccanlint *t = find_test(arg);
  484. if (!t)
  485. return talloc_asprintf(NULL, "unknown --target %s", arg);
  486. targeting = true;
  487. dgraph_add_edge(&t->node, all);
  488. return NULL;
  489. }
  490. static bool run_tests(struct dgraph_node *all,
  491. bool summary,
  492. struct manifest *m,
  493. const char *prefix)
  494. {
  495. struct run_info run;
  496. run.quiet = summary;
  497. run.m = m;
  498. run.prefix = prefix;
  499. run.score = run.total = 0;
  500. run.pass = true;
  501. dgraph_traverse_to(all, run_test, &run);
  502. printf("%sTotal score: %u/%u\n", prefix, run.score, run.total);
  503. return run.pass;
  504. }
  505. static bool add_to_all(const char *member, struct ccanlint *c,
  506. struct dgraph_node *all)
  507. {
  508. /* If we're excluded on cmdline, don't add. */
  509. if (!c->skip)
  510. dgraph_add_edge(&c->node, all);
  511. return true;
  512. }
  513. int main(int argc, char *argv[])
  514. {
  515. bool summary = false, pass = true;
  516. unsigned int i;
  517. struct manifest *m;
  518. const char *prefix = "";
  519. char *dir = talloc_getcwd(NULL), *base_dir = dir, *testlink;
  520. struct dgraph_node all;
  521. /* Empty graph node to which we attach everything else. */
  522. dgraph_init_node(&all);
  523. opt_register_early_noarg("--verbose|-v", opt_inc_intval, &verbose,
  524. "verbose mode (up to -vvvv)");
  525. opt_register_noarg("-n|--safe-mode", opt_set_bool, &safe_mode,
  526. "do not compile anything");
  527. opt_register_noarg("-l|--list-tests", list_tests, NULL,
  528. "list tests ccanlint performs (and exit)");
  529. opt_register_noarg("--test-dep-graph", test_dependency_graph, NULL,
  530. "print dependency graph of tests in Graphviz .dot format");
  531. opt_register_noarg("-k|--keep", keep_tests, NULL,
  532. "do not delete ccanlint working files");
  533. opt_register_noarg("--summary|-s", opt_set_bool, &summary,
  534. "simply give one line summary");
  535. opt_register_arg("-x|--exclude <testname>", exclude_test, NULL, NULL,
  536. "exclude <testname> (can be used multiple times)");
  537. opt_register_arg("--timeout <milleseconds>", opt_set_uintval,
  538. NULL, &timeout,
  539. "ignore (terminate) tests that are slower than this");
  540. opt_register_arg("-t|--target <testname>", opt_set_target, NULL, &all,
  541. "only run one test (and its prerequisites)");
  542. opt_register_arg("--compiler <compiler>", opt_set_const_charp,
  543. NULL, &compiler, "set the compiler");
  544. opt_register_arg("--cflags <flags>", opt_set_const_charp,
  545. NULL, &cflags, "set the compiler flags");
  546. opt_register_noarg("-?|-h|--help", opt_usage_and_exit,
  547. "\nA program for checking and guiding development"
  548. " of CCAN modules.",
  549. "This usage message");
  550. /* Do verbose before anything else... */
  551. opt_early_parse(argc, argv, opt_log_stderr_exit);
  552. /* We move into temporary directory, so gcov dumps its files there. */
  553. if (chdir(temp_dir(talloc_autofree_context())) != 0)
  554. err(1, "Error changing to %s temporary dir", temp_dir(NULL));
  555. init_tests();
  556. if (verbose >= 3) {
  557. compile_verbose = true;
  558. print_test_depends();
  559. }
  560. if (verbose >= 4)
  561. tools_verbose = true;
  562. opt_parse(&argc, argv, opt_log_stderr_exit);
  563. if (!targeting)
  564. strmap_iterate(&tests, add_to_all, &all);
  565. /* This links back to the module's test dir. */
  566. testlink = talloc_asprintf(NULL, "%s/test", temp_dir(NULL));
  567. /* Defaults to pwd. */
  568. if (argc == 1) {
  569. i = 1;
  570. goto got_dir;
  571. }
  572. for (i = 1; i < argc; i++) {
  573. dir = argv[i];
  574. if (dir[0] != '/')
  575. dir = talloc_asprintf_append(NULL, "%s/%s",
  576. base_dir, dir);
  577. while (strends(dir, "/"))
  578. dir[strlen(dir)-1] = '\0';
  579. got_dir:
  580. /* We assume there's a ccan/ in there somewhere... */
  581. if (i == 1) {
  582. ccan_dir = find_ccan_dir(dir);
  583. if (!ccan_dir)
  584. errx(1, "Cannot find ccan/ base directory in %s",
  585. dir);
  586. config_header = read_config_header(ccan_dir,
  587. &compiler, &cflags,
  588. verbose > 1);
  589. }
  590. if (dir != base_dir)
  591. prefix = talloc_append_string(talloc_basename(NULL,dir),
  592. ": ");
  593. m = get_manifest(talloc_autofree_context(), dir);
  594. /* Create a symlink from temp dir back to src dir's
  595. * test directory. */
  596. unlink(testlink);
  597. if (symlink(talloc_asprintf(m, "%s/test", dir), testlink) != 0)
  598. err(1, "Creating test symlink in %s", temp_dir(NULL));
  599. if (!run_tests(&all, summary, m, prefix))
  600. pass = false;
  601. reset_tests(&all);
  602. }
  603. return pass ? 0 : 1;
  604. }