More sophisticated skipping: skip dependencies when one fails as well.
Allow tests to change their total_score; only access it after running (other than to check it's non-zero).
.PHONY: tools
tools: $(ALL_TOOLS)
-tools/ccan_depends: tools/ccan_depends.o tools/depends.o ccan/str_talloc/str_talloc.o ccan/grab_file/grab_file.o ccan/talloc/talloc.o ccan/noerr/noerr.o
+tools/ccan_depends: tools/ccan_depends.o tools/depends.o tools/tools.o ccan/str_talloc/str_talloc.o ccan/grab_file/grab_file.o ccan/talloc/talloc.o ccan/noerr/noerr.o
tools/run_tests: tools/run_tests.o tools/depends.o ccan/str_talloc/str_talloc.o ccan/grab_file/grab_file.o ccan/tap/tap.o ccan/noerr/noerr.o ccan/talloc/talloc.o
tools/doc_extract: tools/doc_extract.o tools/doc_extract-core.o ccan/str_talloc/str_talloc.o ccan/grab_file/grab_file.o ccan/noerr/noerr.o ccan/talloc/talloc.o
-tools/namespacize: tools/namespacize.o tools/depends.o ccan/str_talloc/str_talloc.o ccan/grab_file/grab_file.o ccan/noerr/noerr.o ccan/talloc/talloc.o
+tools/namespacize: tools/namespacize.o tools/depends.o tools/tools.o ccan/str_talloc/str_talloc.o ccan/grab_file/grab_file.o ccan/noerr/noerr.o ccan/talloc/talloc.o
tools/run_tests.o tools/namespacize.o tools/depends.o: tools/tools.h
tools/ccanlint/file_analysis.o \
tools/doc_extract-core.o \
tools/depends.o \
+ tools/tools.o \
ccan/str_talloc/str_talloc.o ccan/grab_file/grab_file.o \
ccan/talloc/talloc.o ccan/noerr/noerr.o
&& toupper(reply[0]) == 'Y';
}
+static const char *should_skip(struct manifest *m, struct ccanlint *i)
+{
+ if (i->skip_fail)
+ return "dependency failed";
+
+ if (i->skip)
+ return "dependency was skipped";
+
+ if (i->can_run)
+ return i->can_run(m);
+ return NULL;
+}
+
static bool run_test(struct ccanlint *i,
bool summary,
unsigned int *score,
void *result;
unsigned int this_score;
const struct dependent *d;
+ const char *skip;
- if (i->total_score)
- *total_score += i->total_score;
//one less test to run through
list_for_each(&i->dependencies, d, node)
d->dependent->num_depends--;
+
+ skip = should_skip(m, i);
+ if (skip) {
+ if (verbose)
+ printf(" %s: skipped (%s)\n", i->name, skip);
+
+ /* If we're skipping this because a prereq failed, we fail. */
+ if (i->skip_fail)
+ *total_score += i->total_score;
+
+ list_del(&i->list);
+ list_add_tail(&finished_tests, &i->list);
+ list_for_each(&i->dependencies, d, node) {
+ d->dependent->skip = true;
+ d->dependent->skip_fail = i->skip_fail;
+ }
+ return true;
+ }
+
result = i->check(m);
if (!result) {
if (verbose)
printf(" %s: OK\n", i->name);
- if (i->total_score)
+ if (i->total_score) {
*score += i->total_score;
+ *total_score += i->total_score;
+ }
list_del(&i->list);
list_add_tail(&finished_tests, &i->list);
list_del(&i->list);
list_add_tail(&finished_tests, &i->list);
+ *total_score += i->total_score;
*score += this_score;
if (summary) {
printf("%s FAILED (%u/%u)\n",
/* Skip any tests which depend on this one. */
list_for_each(&i->dependencies, d, node) {
- list_del(&d->dependent->list);
- list_add(&finished_tests, &d->dependent->list);
- if (verbose)
- printf(" -> skipping %s\n", d->dependent->name);
- *total_score += d->dependent->total_score;
+ d->dependent->skip = true;
+ d->dependent->skip_fail = true;
}
return false;
struct list_head other_files;
- /* From tests/check_depends.c */
- struct list_head dep_obj_files;
+ /* From tests/check_depends_exist.c */
+ struct list_head dep_dirs;
+ /* From tests/check_depends_built.c */
+ struct list_head dep_objs;
};
struct manifest *get_manifest(const void *ctx);
/* Total score that this test is worth. 0 means compulsory tests. */
unsigned int total_score;
+ /* Can we run this test? Return string explaining why, if not. */
+ const char *(*can_run)(struct manifest *m);
+
/* If this returns non-NULL, it means the check failed. */
void *(*check)(struct manifest *m);
struct list_head dependencies;
/* How many things do we (still) depend on? */
unsigned int num_depends;
+ /* Did we skip a dependency? If so, must skip this, too. */
+ bool skip;
+ /* Did we fail a dependency? If so, skip and mark as fail. */
+ bool skip_fail;
};
/* Ask the user a yes/no question: the answer is NO if there's an error. */
list_head_init(&m->compile_fail_tests);
list_head_init(&m->other_test_files);
list_head_init(&m->other_files);
+ list_head_init(&m->dep_dirs);
+ list_head_init(&m->dep_objs);
m->basename = talloc_getcwd(m);
if (!m->basename)
--- /dev/null
+#include <tools/ccanlint/ccanlint.h>
+#include <tools/tools.h>
+#include <ccan/talloc/talloc.h>
+#include <ccan/str/str.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <err.h>
+#include <string.h>
+#include <ctype.h>
+
+static const char *can_build(struct manifest *m)
+{
+ if (list_empty(&m->c_files))
+ return "No C files in module";
+ if (safe_mode)
+ return "Safe mode enabled";
+ return NULL;
+}
+
+static char *obj_list(const struct manifest *m)
+{
+ char *list = talloc_strdup(m, "");
+ struct ccan_file *i;
+
+ /* Object from all the C files. */
+ list_for_each(&m->c_files, i, list)
+ list = talloc_asprintf_append(list, "%.*s.o ",
+ strlen(i->name) - 2, i->name);
+
+ return list;
+}
+
+/* We leave this object file around after ccanlint runs, all built. */
+static void *do_build(struct manifest *m)
+{
+ return run_command(m, "ld -r -o ../%s.o %s", m->basename, obj_list(m));
+}
+
+static const char *describe_build(struct manifest *m, void *check_result)
+{
+ return talloc_asprintf(check_result,
+ "The object file for the module didn't build:\n"
+ "%s", (char *)check_result);
+}
+
+struct ccanlint build = {
+ .name = "Module can be built",
+ .total_score = 1,
+ .check = do_build,
+ .describe = describe_build,
+ .can_run = can_build,
+};
+
+REGISTER_TEST(build, &depends_built, NULL);
--- /dev/null
+#include <tools/ccanlint/ccanlint.h>
+#include <tools/tools.h>
+#include <ccan/talloc/talloc.h>
+#include <ccan/str/str.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <err.h>
+#include <string.h>
+#include <ctype.h>
+
+static const char *can_build(struct manifest *m)
+{
+ if (list_empty(&m->c_files))
+ return "No C files in module";
+ if (safe_mode)
+ return "Safe mode enabled";
+ return NULL;
+}
+
+static bool compile_obj(struct ccan_file *c_file, char *objfile, char **report)
+{
+ char *contents;
+
+ contents = run_command(objfile, "cc " CFLAGS " -o %s -c %s",
+ objfile, c_file->name);
+ if (contents) {
+ if (*report)
+ *report = talloc_append_string(*report, contents);
+ else
+ *report = contents;
+ return false;
+ }
+ return true;
+}
+
+static int cleanup_obj(char *objfile)
+{
+ unlink(objfile);
+ return 0;
+}
+
+static void *check_objs_build(struct manifest *m)
+{
+ char *report = NULL;
+ struct ccan_file *i;
+
+ /* One point for each obj file. */
+ build_objs.total_score = 0;
+ list_for_each(&m->c_files, i, list)
+ build_objs.total_score++;
+
+ list_for_each(&m->c_files, i, list) {
+ char *objfile = talloc_strdup(m, i->name);
+ objfile[strlen(objfile)-1] = 'o';
+
+ if (compile_obj(i, objfile, &report))
+ talloc_set_destructor(objfile, cleanup_obj);
+ }
+ return report;
+}
+
+static const char *describe_objs_build(struct manifest *m, void *check_result)
+{
+ return talloc_asprintf(check_result,
+ "%s", (char *)check_result);
+}
+
+struct ccanlint build_objs = {
+ .name = "Module object files can be built",
+ .total_score = 1,
+ .check = check_objs_build,
+ .describe = describe_objs_build,
+ .can_run = can_build,
+};
+
+REGISTER_TEST(build_objs, &depends_exist, NULL);
--- /dev/null
+#include <tools/ccanlint/ccanlint.h>
+#include <tools/tools.h>
+#include <ccan/talloc/talloc.h>
+#include <ccan/str/str.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <err.h>
+#include <string.h>
+#include <ctype.h>
+
+static const char *can_build(struct manifest *m)
+{
+ if (safe_mode)
+ return "Safe mode enabled";
+ return NULL;
+}
+
+static int cleanup_testfile(char *testfile)
+{
+ unlink(testfile);
+ return 0;
+}
+
+static char *obj_list(const struct manifest *m)
+{
+ char *list = talloc_strdup(m, "");
+ struct ccan_file *i;
+
+ /* Other CCAN deps. */
+ list_for_each(&m->dep_objs, i, list)
+ list = talloc_asprintf_append(list, "%s ", i->name);
+
+ return list;
+}
+
+static char *lib_list(const struct manifest *m)
+{
+ unsigned int i, num;
+ char **libs = get_libs(m, ".", ".", &num);
+ char *ret = talloc_strdup(m, "");
+
+ for (i = 0; i < num; i++)
+ ret = talloc_asprintf_append(ret, "-l %s ", libs[i]);
+ return ret;
+}
+
+static void *check_use_build(struct manifest *m)
+{
+ char *contents;
+ char *tmpfile, *outfile;
+ int fd;
+
+ tmpfile = talloc_strdup(m, tempnam("/tmp", "ccanlint"));
+ talloc_set_destructor(tmpfile, cleanup_testfile);
+ outfile = talloc_strdup(m, tempnam("/tmp", "ccanlint"));
+ talloc_set_destructor(outfile, cleanup_testfile);
+
+ fd = open(tmpfile, O_WRONLY | O_CREAT | O_EXCL, 0600);
+ if (fd < 0)
+ return talloc_asprintf(m, "Creating temporary file: %s",
+ strerror(errno));
+
+ contents = talloc_asprintf(tmpfile,
+ "#include <ccan/%s/%s.h>\n"
+ "int main(void)\n"
+ "{\n"
+ " return 0;\n"
+ "}\n",
+ m->basename, m->basename);
+ if (write(fd, contents, strlen(contents)) != strlen(contents)) {
+ close(fd);
+ return "Failure writing to temporary file";
+ }
+ close(fd);
+
+ return run_command(m, "cc " CFLAGS " -o %s -x c %s -x none %s %s",
+ outfile, tmpfile, obj_list(m), lib_list(m));
+}
+
+static const char *describe_use_build(struct manifest *m, void *check_result)
+{
+ return talloc_asprintf(check_result,
+ "Linking against module:\n"
+ "%s", (char *)check_result);
+}
+
+struct ccanlint check_build = {
+ .name = "Module can be used",
+ .total_score = 1,
+ .check = check_use_build,
+ .describe = describe_use_build,
+ .can_run = can_build,
+};
+
+REGISTER_TEST(check_build, &build, NULL);
+++ /dev/null
-#include <tools/ccanlint/ccanlint.h>
-#include <tools/tools.h>
-#include <ccan/talloc/talloc.h>
-#include <ccan/str/str.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <limits.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <err.h>
-#include <string.h>
-#include <ctype.h>
-
-static bool expect_obj_file(const char *dir)
-{
- char *olddir;
- struct manifest *dep_man;
- bool has_c_files;
-
- olddir = talloc_getcwd(dir);
- if (!olddir)
- err(1, "Getting current directory");
-
- /* We will fail below if this doesn't exist. */
- if (chdir(dir) != 0)
- return false;
-
- dep_man = get_manifest(dir);
- if (chdir(olddir) != 0)
- err(1, "Returning to original directory '%s'", olddir);
- talloc_free(olddir);
-
- /* If it has C files, we expect an object file built from them. */
- has_c_files = !list_empty(&dep_man->c_files);
- talloc_free(dep_man);
- return has_c_files;
-}
-
-/* FIXME: recursive ccanlint if they ask for it. */
-static char *add_dep(char *sofar, struct manifest *m, const char *dep)
-{
- char *file, *dir;
- struct stat st;
- bool need_obj;
-
- dir = talloc_asprintf(m, "../%s", dep);
- need_obj = expect_obj_file(dir);
-
- if (need_obj) {
- file = talloc_asprintf(m, "../%s.o", dep);
- if (stat(file, &st) == 0) {
- struct ccan_file *f = new_ccan_file(m, file);
- list_add_tail(&m->dep_obj_files, &f->list);
- return sofar;
- }
- }
-
- if (stat(dir, &st) == 0) {
- if (!need_obj)
- return sofar;
-
- return talloc_asprintf_append(sofar,
- "ccan/%s: isn't built (no %s)\n",
- dep, file);
- }
-
- return talloc_asprintf_append(sofar,
- "ccan/%s: could not find directory %s\n",
- dep, dir);
-}
-
-static void *check_depends(struct manifest *m)
-{
- unsigned int i;
- char *report = NULL;
- char **deps;
-
- if (safe_mode)
- deps = get_safe_ccan_deps(m, "..", m->basename, true);
- else
- deps = get_deps(m, "..", m->basename, true);
-
- for (i = 0; deps[i]; i++) {
- if (!strstarts(deps[i], "ccan/"))
- continue;
-
- report = add_dep(report, m, deps[i] + strlen("ccan/"));
- }
- return report;
-}
-
-static const char *describe_depends(struct manifest *m, void *check_result)
-{
- return talloc_asprintf(check_result,
- "The following dependencies are needed:\n"
- "%s\n", (char *)check_result);
-}
-
-struct ccanlint depends = {
- .name = "CCAN dependencies are built",
- .total_score = 1,
- .check = check_depends,
- .describe = describe_depends,
-};
-
-REGISTER_TEST(depends, NULL);
--- /dev/null
+#include <tools/ccanlint/ccanlint.h>
+#include <tools/tools.h>
+#include <ccan/talloc/talloc.h>
+#include <ccan/str/str.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <err.h>
+#include <string.h>
+#include <ctype.h>
+
+static const char *can_build(struct manifest *m)
+{
+ if (safe_mode)
+ return "Safe mode enabled";
+ return NULL;
+}
+
+/* FIXME: recursive ccanlint if they ask for it. */
+static bool expect_obj_file(const char *dir)
+{
+ char *olddir;
+ struct manifest *dep_man;
+ bool has_c_files;
+
+ olddir = talloc_getcwd(dir);
+ if (!olddir)
+ err(1, "Getting current directory");
+
+ /* We will fail below if this doesn't exist. */
+ if (chdir(dir) != 0)
+ return false;
+
+ dep_man = get_manifest(dir);
+ if (chdir(olddir) != 0)
+ err(1, "Returning to original directory '%s'", olddir);
+ talloc_free(olddir);
+
+ /* If it has C files, we expect an object file built from them. */
+ has_c_files = !list_empty(&dep_man->c_files);
+ talloc_free(dep_man);
+ return has_c_files;
+}
+
+static void *check_depends_built(struct manifest *m)
+{
+ struct ccan_file *i;
+ char *report = NULL;
+
+ list_for_each(&m->dep_dirs, i, list) {
+ char *objfile;
+ struct stat st;
+
+ if (!expect_obj_file(i->name))
+ continue;
+
+ objfile = talloc_asprintf(m, "%s.o", i->name);
+ if (stat(objfile, &st) != 0) {
+ report = talloc_asprintf_append(report,
+ "object file %s\n",
+ objfile);
+ } else {
+ struct ccan_file *f = new_ccan_file(m, objfile);
+ list_add_tail(&m->dep_objs, &f->list);
+ }
+
+ }
+ return talloc_steal(m, report);
+}
+
+static const char *describe_depends_built(struct manifest *m,
+ void *check_result)
+{
+ return talloc_asprintf(check_result,
+ "The following dependencies are not built:\n"
+ "%s", (char *)check_result);
+}
+
+struct ccanlint depends_built = {
+ .name = "CCAN dependencies are built",
+ .total_score = 1,
+ .check = check_depends_built,
+ .describe = describe_depends_built,
+ .can_run = can_build,
+};
+
+REGISTER_TEST(depends_built, &depends_exist, NULL);
--- /dev/null
+#include <tools/ccanlint/ccanlint.h>
+#include <tools/tools.h>
+#include <ccan/talloc/talloc.h>
+#include <ccan/str/str.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <err.h>
+#include <string.h>
+#include <ctype.h>
+
+static char *add_dep(char *sofar, struct manifest *m, const char *dep)
+{
+ char *dir;
+ struct stat st;
+ struct ccan_file *f;
+
+ dir = talloc_asprintf(m, "../%s", dep);
+ if (stat(dir, &st) != 0) {
+ return talloc_asprintf_append(sofar,
+ "ccan/%s: expected it in"
+ " directory %s\n",
+ dep, dir);
+ }
+
+ f = new_ccan_file(m, dir);
+ list_add_tail(&m->dep_dirs, &f->list);
+ return sofar;
+}
+
+static void *check_depends_exist(struct manifest *m)
+{
+ unsigned int i;
+ char *report = NULL;
+ char **deps;
+
+ if (safe_mode)
+ deps = get_safe_ccan_deps(m, "..", m->basename, true);
+ else
+ deps = get_deps(m, "..", m->basename, true);
+
+ for (i = 0; deps[i]; i++) {
+ if (!strstarts(deps[i], "ccan/"))
+ continue;
+
+ report = add_dep(report, m, deps[i] + strlen("ccan/"));
+ }
+ return report;
+}
+
+static const char *describe_depends_exist(struct manifest *m,
+ void *check_result)
+{
+ return talloc_asprintf(check_result,
+ "The following dependencies are are expected:\n"
+ "%s", (char *)check_result);
+}
+
+struct ccanlint depends_exist = {
+ .name = "CCAN dependencies are present",
+ .total_score = 1,
+ .check = check_depends_exist,
+ .describe = describe_depends_exist,
+};
+
+REGISTER_TEST(depends_exist, NULL);
--- /dev/null
+#include <tools/ccanlint/ccanlint.h>
+#include <tools/tools.h>
+#include <ccan/talloc/talloc.h>
+#include <ccan/grab_file/grab_file.h>
+#include <ccan/str/str.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <err.h>
+#include <string.h>
+#include <ctype.h>
+
+static const char *can_build(struct manifest *m)
+{
+ if (safe_mode)
+ return "Safe mode enabled";
+ return NULL;
+}
+
+static int cleanup_testfile(char *testfile)
+{
+ unlink(testfile);
+ return 0;
+}
+
+static void *check_includes_build(struct manifest *m)
+{
+ char *contents;
+ char *tmpfile, *objfile;
+ int fd;
+
+ tmpfile = talloc_strdup(m, tempnam("/tmp", "ccanlint"));
+ talloc_set_destructor(tmpfile, cleanup_testfile);
+ objfile = talloc_strdup(m, tempnam("/tmp", "ccanlint"));
+ talloc_set_destructor(objfile, cleanup_testfile);
+
+ fd = open(tmpfile, O_WRONLY | O_CREAT | O_EXCL, 0600);
+ if (fd < 0)
+ return talloc_asprintf(m, "Creating temporary file: %s",
+ strerror(errno));
+
+ contents = talloc_asprintf(tmpfile, "#include <ccan/%s/%s.h>\n",
+ m->basename, m->basename);
+ if (write(fd, contents, strlen(contents)) != strlen(contents)) {
+ close(fd);
+ return "Failure writing to temporary file";
+ }
+ close(fd);
+
+ return run_command(m, "cc " CFLAGS " -o %s -c -x c %s",
+ objfile, tmpfile);
+}
+
+static const char *describe_includes_build(struct manifest *m,
+ void *check_result)
+{
+ return talloc_asprintf(check_result,
+ "#include of the main header file:\n"
+ "%s", (char *)check_result);
+}
+
+struct ccanlint includes_build = {
+ .name = "Can compile against main header",
+ .total_score = 1,
+ .check = check_includes_build,
+ .describe = describe_includes_build,
+ .can_run = can_build,
+};
+
+REGISTER_TEST(includes_build, &depends_exist, NULL);
return deps;
}
+char **get_libs(const void *ctx, const char *dir,
+ const char *name, unsigned int *num)
+{
+ char **libs, *cmd, *infofile;
+
+ infofile = compile_info(ctx, dir, name);
+ if (!infofile)
+ errx(1, "Could not compile _info for '%s'", name);
+
+ cmd = talloc_asprintf(ctx, "%s libs", infofile);
+ libs = lines_from_cmd(cmd, num, "%s", cmd);
+ if (!libs)
+ err(1, "Could not run '%s'", cmd);
+ return libs;
+}
+
char **get_deps(const void *ctx, const char *dir, const char *name,
bool recurse)
{
}
return get_all_deps(ctx, dir, name, get_one_safe_deps);
}
-
-char *talloc_basename(const void *ctx, const char *dir)
-{
- char *p = strrchr(dir, '/');
-
- if (!p)
- return (char *)dir;
- return talloc_strdup(ctx, p+1);
-}
-
-char *talloc_dirname(const void *ctx, const char *dir)
-{
- char *p = strrchr(dir, '/');
-
- if (!p)
- return talloc_strdup(ctx, ".");
- return talloc_strndup(ctx, dir, p - dir);
-}
-
-char *talloc_getcwd(const void *ctx)
-{
- unsigned int len;
- char *cwd;
-
- /* *This* is why people hate C. */
- len = 32;
- cwd = talloc_array(ctx, char, len);
- while (!getcwd(cwd, len)) {
- if (errno != ERANGE) {
- talloc_free(cwd);
- return NULL;
- }
- cwd = talloc_realloc(ctx, cwd, char, len *= 2);
- }
- return cwd;
-}
-
--- /dev/null
+#include <ccan/talloc/talloc.h>
+#include <ccan/grab_file/grab_file.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <errno.h>
+#include "tools.h"
+
+char *talloc_basename(const void *ctx, const char *dir)
+{
+ char *p = strrchr(dir, '/');
+
+ if (!p)
+ return (char *)dir;
+ return talloc_strdup(ctx, p+1);
+}
+
+char *talloc_dirname(const void *ctx, const char *dir)
+{
+ char *p = strrchr(dir, '/');
+
+ if (!p)
+ return talloc_strdup(ctx, ".");
+ return talloc_strndup(ctx, dir, p - dir);
+}
+
+char *talloc_getcwd(const void *ctx)
+{
+ unsigned int len;
+ char *cwd;
+
+ /* *This* is why people hate C. */
+ len = 32;
+ cwd = talloc_array(ctx, char, len);
+ while (!getcwd(cwd, len)) {
+ if (errno != ERANGE) {
+ talloc_free(cwd);
+ return NULL;
+ }
+ cwd = talloc_realloc(ctx, cwd, char, len *= 2);
+ }
+ return cwd;
+}
+
+char *run_command(const void *ctx, const char *fmt, ...)
+{
+ va_list ap;
+ char *cmd, *contents;
+ FILE *pipe;
+
+ va_start(ap, fmt);
+ cmd = talloc_vasprintf(ctx, fmt, ap);
+ va_end(ap);
+
+ /* Ensure stderr gets to us too. */
+ cmd = talloc_asprintf_append(cmd, " 2>&1");
+
+ pipe = popen(cmd, "r");
+ if (!pipe)
+ return talloc_asprintf(ctx, "Failed to run '%s'", cmd);
+
+ contents = grab_fd(cmd, fileno(pipe), NULL);
+ if (pclose(pipe) != 0)
+ return talloc_asprintf(ctx, "Running '%s':\n%s",
+ cmd, contents);
+
+ talloc_free(cmd);
+ return NULL;
+}
char **get_safe_ccan_deps(const void *ctx, const char *dir, const char *name,
bool recurse);
+/* This also needs to compile the info file. */
+char **get_libs(const void *ctx, const char *dir,
+ const char *name, unsigned int *num);
+
+/* From tools.c */
char *talloc_basename(const void *ctx, const char *dir);
char *talloc_dirname(const void *ctx, const char *dir);
char *talloc_getcwd(const void *ctx);
+char *run_command(const void *ctx, const char *fmt, ...);
#endif /* CCAN_TOOLS_H */