bool compile = false;
bool recurse = true;
bool ccan = true;
+ char *dirname, *basename;
if (argv[1] && streq(argv[1], "--direct")) {
argv++;
if (!ccan && !compile)
errx(1, "--non-ccan needs --compile");
+ dirname = talloc_dirname(NULL, argv[1]);
+ basename = talloc_basename(NULL, argv[1]);
+
if (compile)
- deps = get_deps(talloc_autofree_context(), argv[1], recurse);
+ deps = get_deps(talloc_autofree_context(),
+ dirname, basename, recurse);
else
- deps = get_safe_ccan_deps(talloc_autofree_context(), argv[1],
- recurse);
+ deps = get_safe_ccan_deps(talloc_autofree_context(),
+ dirname, basename, recurse);
for (i = 0; deps[i]; i++)
if (strstarts(deps[i], "ccan/") == ccan)
CORE_OBJS := tools/ccanlint/ccanlint.o \
tools/ccanlint/file_analysis.o \
tools/doc_extract-core.o \
+ tools/depends.o \
ccan/str_talloc/str_talloc.o ccan/grab_file/grab_file.o \
ccan/talloc/talloc.o ccan/noerr/noerr.o
#include <string.h>
#include <err.h>
#include <ctype.h>
+#include <ccan/talloc/talloc.h>
static unsigned int verbose = 0;
static LIST_HEAD(compulsory_tests);
static LIST_HEAD(normal_tests);
static LIST_HEAD(finished_tests);
+bool safe_mode = false;
static void usage(const char *name)
{
- fprintf(stderr, "Usage: %s [-s] [-v] [-d <dirname>]\n"
+ fprintf(stderr, "Usage: %s [-s] [-n] [-v] [-d <dirname>]\n"
" -v: verbose mode\n"
" -s: simply give one line per FAIL and total score\n"
- " -d: use this directory instead of the current one\n",
+ " -d: use this directory instead of the current one\n"
+ " -n: do not compile anything\n",
name);
exit(1);
}
/* I'd love to use long options, but that's not standard. */
/* FIXME: getopt_long ccan package? */
- while ((c = getopt(argc, argv, "sd:v")) != -1) {
+ while ((c = getopt(argc, argv, "sd:vn")) != -1) {
switch (c) {
case 'd':
if (chdir(optarg) != 0)
case 'v':
verbose++;
break;
+ case 'n':
+ safe_mode = true;
+ break;
default:
usage(argv[0]);
}
if (optind < argc)
usage(argv[0]);
- m = get_manifest();
+ m = get_manifest(talloc_autofree_context());
init_tests();
run_test(i, summary, &score, &total_score, m);
}
printf("Total score: %u/%u\n", score, total_score);
-
return 0;
}
#define REGISTER_TEST(name, ...)
struct manifest {
+ /* The module name, ie. final element of dir name */
char *basename;
struct ccan_file *info_file;
struct list_head other_test_files;
struct list_head other_files;
+
+ /* From tests/check_depends.c */
+ struct list_head dep_obj_files;
};
-struct manifest *get_manifest(void);
+struct manifest *get_manifest(const void *ctx);
struct ccanlint {
struct list_node list;
struct list_head *doc_sections;
};
+/* A new ccan_file, with the given name (talloc_steal onto returned value). */
+struct ccan_file *new_ccan_file(const void *ctx, char *name);
+
/* Use this rather than accessing f->lines directly: loads on demand. */
char **get_ccan_file_lines(struct ccan_file *f);
struct ccanlint *dependent;
};
+/* Are we happy to compile stuff, or just non-intrusive tests? */
+extern bool safe_mode;
+
#endif /* CCAN_LINT_H */
return f->doc_sections;
}
+struct ccan_file *new_ccan_file(const void *ctx, char *name)
+{
+ struct ccan_file *f;
+
+ f = talloc(ctx, struct ccan_file);
+ f->lines = NULL;
+ f->line_info = NULL;
+ f->doc_sections = NULL;
+ f->name = talloc_steal(f, name);
+ return f;
+}
+
static void add_files(struct manifest *m, const char *dir)
{
DIR *d;
if (ent->d_name[0] == '.')
continue;
- f = talloc(m, struct ccan_file);
- f->lines = NULL;
- f->line_info = NULL;
- f->doc_sections = NULL;
- f->name = talloc_asprintf(f, "%s%s", dir, ent->d_name);
+ f = new_ccan_file(m, talloc_asprintf(m, "%s%s",
+ dir, ent->d_name));
if (lstat(f->name, &st) != 0)
err(1, "lstat %s", f->name);
return sofar;
}
-struct manifest *get_manifest(void)
+struct manifest *get_manifest(const void *ctx)
{
- struct manifest *m = talloc(NULL, struct manifest);
+ struct manifest *m = talloc(ctx, struct manifest);
unsigned int len;
m->info_file = NULL;
list_head_init(&m->other_test_files);
list_head_init(&m->other_files);
- /* *This* is why people hate C. */
- len = 32;
- m->basename = talloc_array(m, char, len);
- while (!getcwd(m->basename, len)) {
- if (errno != ERANGE)
- err(1, "Getting current directory");
- m->basename = talloc_realloc(m, m->basename, char, len *= 2);
- }
-
+ m->basename = talloc_getcwd(m);
+ if (!m->basename)
+ err(1, "Getting current directory");
len = strlen(m->basename);
while (len && m->basename[len-1] == '/')
m->basename[--len] = '\0';
--- /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);
#include <err.h>
#include <stdbool.h>
#include <unistd.h>
+#include <errno.h>
static char ** __attribute__((format(printf, 3, 4)))
lines_from_cmd(const void *ctx, unsigned int *num, char *format, ...)
}
/* Be careful about trying to compile over running programs (parallel make) */
-static char *compile_info(const void *ctx, const char *dir)
+static char *compile_info(const void *ctx, const char *dir, const char *name)
{
char *infofile = talloc_asprintf(ctx, "%s/info.%u", dir, getpid());
- char *cmd = talloc_asprintf(ctx, "cc " CFLAGS " -o %s -x c %s/_info",
- infofile, dir);
+ char *cmd = talloc_asprintf(ctx, "cc " CFLAGS
+ " -o %s -x c %s/%s/_info",
+ infofile, dir, name);
talloc_set_destructor(infofile, unlink_info);
if (system(cmd) != 0)
return NULL;
return infofile;
}
-static char **get_one_deps(const void *ctx, const char *dir, unsigned int *num)
+static char **get_one_deps(const void *ctx, const char *dir,
+ const char *name, unsigned int *num)
{
char **deps, *cmd, *infofile;
- infofile = compile_info(ctx, dir);
+ infofile = compile_info(ctx, dir, name);
if (!infofile)
- errx(1, "Could not compile _info for '%s'", dir);
+ errx(1, "Could not compile _info for '%s'", name);
cmd = talloc_asprintf(ctx, "%s depends", infofile);
deps = lines_from_cmd(cmd, num, "%s", cmd);
/* This is a terrible hack. We scan for ccan/ strings. */
static char **get_one_safe_deps(const void *ctx,
- const char *dir, unsigned int *num)
+ const char *dir, const char *name,
+ unsigned int *num)
{
char **deps, **lines, *raw, *fname;
unsigned int i, n = 0;
- fname = talloc_asprintf(ctx, "%s/_info", dir);
+ fname = talloc_asprintf(ctx, "%s/%s/_info", dir, name);
raw = grab_file(fname, fname, NULL);
if (!raw)
errx(1, "Could not open %s", fname);
/* Gets all the dependencies, recursively. */
static char **
-get_all_deps(const void *ctx, const char *dir,
- char **(*get_one)(const void *, const char *, unsigned int *))
+get_all_deps(const void *ctx, const char *dir, const char *name,
+ char **(*get_one)(const void *, const char *, const char *,
+ unsigned int *))
{
char **deps;
unsigned int i, num;
- deps = get_one(ctx, dir, &num);
+ deps = get_one(ctx, dir, name, &num);
for (i = 0; i < num; i++) {
char **newdeps;
unsigned int j, newnum;
if (!strstarts(deps[i], "ccan/"))
continue;
- newdeps = get_one(ctx, deps[i], &newnum);
+ newdeps = get_one(ctx, dir, deps[i] + strlen("ccan/"),
+ &newnum);
/* Should be short, so brute-force out dups. */
for (j = 0; j < newnum; j++) {
return deps;
}
-char **get_deps(const void *ctx, const char *dir, bool recurse)
+char **get_deps(const void *ctx, const char *dir, const char *name,
+ bool recurse)
{
if (!recurse) {
unsigned int num;
- return get_one_deps(ctx, dir, &num);
+ return get_one_deps(ctx, dir, name, &num);
}
- return get_all_deps(ctx, dir, get_one_deps);
+ return get_all_deps(ctx, dir, name, get_one_deps);
}
-char **get_safe_ccan_deps(const void *ctx, const char *dir, bool recurse)
+char **get_safe_ccan_deps(const void *ctx, const char *dir,
+ const char *name, bool recurse)
{
if (!recurse) {
unsigned int num;
- return get_one_safe_deps(ctx, dir, &num);
+ return get_one_safe_deps(ctx, dir, name, &num);
}
- return get_all_deps(ctx, dir, get_one_safe_deps);
+ 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;
+}
+
*repl = new;
}
-static char *basename(const void *ctx, const char *dir)
-{
- char *p = strrchr(dir, '/');
-
- if (!p)
- return (char *)dir;
- return talloc_strdup(ctx, p+1);
-}
-
static void look_for_macros(char *contents, struct replace **repl)
{
char *p;
char *hdr, *contents;
/* Get hold of header, assume that's it. */
- hdr = talloc_asprintf(dir, "%s/%s.h", dir, basename(dir, dir));
+ hdr = talloc_asprintf(dir, "%s/%s.h", dir, talloc_basename(dir, dir));
contents = grab_file(dir, hdr, NULL);
if (!contents)
err(1, "Reading %s", hdr);
return repl;
}
-static char *parent_dir(const void *ctx, const char *dir)
-{
- char *parent, *slash;
-
- parent = talloc_strdup(ctx, dir);
- slash = strrchr(parent, '/');
- if (slash)
- *slash = '\0';
- else
- parent = talloc_strdup(ctx, ".");
- return parent;
-}
-
static void adjust_dir(const char *dir)
{
- char *parent = parent_dir(talloc_autofree_context(), dir);
+ char *parent = talloc_dirname(talloc_autofree_context(), dir);
char **deps;
verbose("Adjusting %s\n", dir);
verbose_indent();
- for (deps = get_deps(parent, dir, false); *deps; deps++) {
+ for (deps = get_deps(parent, parent, talloc_basename(parent, dir),
+ false);
+ *deps; deps++) {
char *depdir;
struct adjusted *adj = NULL;
struct replace *repl;
static void adjust_dependents(const char *dir)
{
- char *parent = parent_dir(NULL, dir);
- char *base = basename(parent, dir);
+ char *parent = talloc_dirname(NULL, dir);
+ char *base = talloc_basename(parent, dir);
char **file;
verbose("Looking for dependents in %s\n", parent);
char *info, **deps;
bool isdep = false;
- if (basename(*file, *file)[0] == '.')
+ if (talloc_basename(*file, *file)[0] == '.')
continue;
info = talloc_asprintf(*file, "%s/_info", *file);
if (access(info, R_OK) != 0)
continue;
- for (deps = get_deps(*file, *file, false); *deps; deps++) {
- if (streq(*deps, base))
+ for (deps = get_deps(*file, talloc_dirname(*file, *file),
+ talloc_basename(*file, *file), false);
+ *deps; deps++) {
+ if (!strstarts(*deps, "ccan/"))
+ continue;
+ if (streq(*deps + strlen("ccan/"), base))
isdep = true;
}
if (isdep)
#define SPACE_CHARS " \f\n\r\t\v"
-#define CFLAGS "-O3 -Wall -Wundef -Wstrict-prototypes -Wold-style-definition -Wmissing-prototypes -Wmissing-declarations -Werror -Iccan/ -I."
+/* FIXME: Remove some -I */
+#define CFLAGS "-O3 -Wall -Wundef -Wstrict-prototypes -Wold-style-definition -Wmissing-prototypes -Wmissing-declarations -Werror -Iccan/ -I. -I../.."
/* This actually compiles and runs the info file to get dependencies. */
-char **get_deps(const void *ctx, const char *dir, bool recurse);
+char **get_deps(const void *ctx, const char *dir, const char *name,
+ bool recurse);
/* This is safer: just looks for ccan/ strings in info */
-char **get_safe_ccan_deps(const void *ctx, const char *dir, bool recurse);
+char **get_safe_ccan_deps(const void *ctx, const char *dir, const char *name,
+ bool recurse);
+char *talloc_basename(const void *ctx, const char *dir);
+char *talloc_dirname(const void *ctx, const char *dir);
+char *talloc_getcwd(const void *ctx);
#endif /* CCAN_TOOLS_H */
-