Dependency checking (make sure .o files exist, prereq to building)
authorRusty Russell <rusty@rustcorp.com.au>
Tue, 8 Sep 2009 11:42:03 +0000 (21:12 +0930)
committerRusty Russell <rusty@rustcorp.com.au>
Tue, 8 Sep 2009 11:42:03 +0000 (21:12 +0930)
tools/ccan_depends.c
tools/ccanlint/Makefile
tools/ccanlint/ccanlint.c
tools/ccanlint/ccanlint.h
tools/ccanlint/file_analysis.c
tools/ccanlint/tests/check_depends.c [new file with mode: 0644]
tools/depends.c
tools/namespacize.c
tools/tools.h

index 0c46173f06a7987a72e6f5b376d2e96d7269b89e..1baff828c267899364bd06ee0500fb1ab875d45c 100644 (file)
@@ -12,6 +12,7 @@ int main(int argc, char *argv[])
        bool compile = false;
        bool recurse = true;
        bool ccan = true;
+       char *dirname, *basename;
 
        if (argv[1] && streq(argv[1], "--direct")) {
                argv++;
@@ -36,11 +37,15 @@ int main(int argc, char *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)
index 16c4bfd2b3324daab2c6462af18341d7528601a8..e40b59decf9e5e3f5d48f984364ebb76cc2d169b 100644 (file)
@@ -5,6 +5,7 @@ TEST_OBJS := $(TEST_CFILES:.c=.o)
 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
 
index 1164c314ec3446637bfab88b69c972753b3fdab7..9b252735f8187c3f2c3fb554bccf160f61f2a3bc 100644 (file)
 #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);
 }
@@ -208,7 +211,7 @@ int main(int argc, char *argv[])
 
        /* 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)
@@ -220,6 +223,9 @@ int main(int argc, char *argv[])
                case 'v':
                        verbose++;
                        break;
+               case 'n':
+                       safe_mode = true;
+                       break;
                default:
                        usage(argv[0]);
                }
@@ -228,7 +234,7 @@ int main(int argc, char *argv[])
        if (optind < argc)
                usage(argv[0]);
 
-       m = get_manifest();
+       m = get_manifest(talloc_autofree_context());
 
        init_tests();
 
@@ -249,6 +255,5 @@ int main(int argc, char *argv[])
                        run_test(i, summary, &score, &total_score, m);
        }
        printf("Total score: %u/%u\n", score, total_score);
-
        return 0;
 }
index ee12ce04fb81083c228e2ac47604c27ed39e25b8..9644185f5af8fba4191bdc1570800535ef6bdd30 100644 (file)
@@ -11,6 +11,7 @@
 #define REGISTER_TEST(name, ...) 
 
 struct manifest {
+       /* The module name, ie. final element of dir name */
        char *basename;
        struct ccan_file *info_file;
 
@@ -24,9 +25,12 @@ struct manifest {
        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;
@@ -113,6 +117,9 @@ struct ccan_file {
        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);
 
@@ -155,4 +162,7 @@ struct dependent {
        struct ccanlint *dependent;
 };
 
+/* Are we happy to compile stuff, or just non-intrusive tests? */
+extern bool safe_mode;
+
 #endif /* CCAN_LINT_H */
index 2a66a545846e37e119ffc5921395f3309a0f049f..3c8930f3d847b10b5dd6ad1ad5a4eca19af47bdf 100644 (file)
@@ -32,6 +32,18 @@ struct list_head *get_ccan_file_docs(struct ccan_file *f)
        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;
@@ -53,11 +65,8 @@ static void add_files(struct manifest *m, const char *dir)
                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);
 
@@ -141,9 +150,9 @@ char *report_on_lines(struct list_head *files,
        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;
@@ -156,15 +165,9 @@ struct manifest *get_manifest(void)
        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';
diff --git a/tools/ccanlint/tests/check_depends.c b/tools/ccanlint/tests/check_depends.c
new file mode 100644 (file)
index 0000000..fecce0b
--- /dev/null
@@ -0,0 +1,109 @@
+#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);
index c9cf88164cd81e37b4981e1e7da1f5a51756609d..1360cc3931de3767415fa7c6021ec2b8935abc1f 100644 (file)
@@ -6,6 +6,7 @@
 #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, ...)
@@ -37,11 +38,12 @@ static int unlink_info(char *infofile)
 }
 
 /* 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;
@@ -49,13 +51,14 @@ static char *compile_info(const void *ctx, const char *dir)
        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);
@@ -93,12 +96,13 @@ static char *replace(const void *ctx, const char *src,
 
 /* 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);
@@ -150,13 +154,14 @@ static bool have_dep(char **deps, unsigned int num, const char *dep)
 
 /* 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;
@@ -164,7 +169,8 @@ get_all_deps(const void *ctx, const char *dir,
                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++) {
@@ -179,21 +185,59 @@ get_all_deps(const void *ctx, const char *dir,
        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;
+}
+
index 0ed36aa6acf8f7ae8ae2b28636b6c80af3cdeba0..2e6dcbfb9b3f5293b1cf2026bfc6c4930da128c4 100644 (file)
@@ -102,15 +102,6 @@ static void add_replace_tok(struct replace **repl, const char *s)
        *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;
@@ -269,7 +260,7 @@ static void analyze_headers(const char *dir, struct replace **repl)
        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);
@@ -459,27 +450,16 @@ static struct replace *read_replacement_file(const char *depdir)
        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;
@@ -500,8 +480,8 @@ static void adjust_dir(const char *dir)
 
 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);
@@ -510,15 +490,19 @@ static void adjust_dependents(const char *dir)
                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)
index 4bdb8077e26c82b3f6934877a2b7f82706ccf0e0..6ac4e66fcbc78b865e655d0182f4a4e7ead6cb0f 100644 (file)
@@ -8,13 +8,18 @@
 
 #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 */
-