Commit d8c9fdc0 authored by Rusty Russell's avatar Rusty Russell

tools: split out manifest from ccanlint

Other tools will soon want to use this, so make it independent.
(eg. ccantool).
parent 83611937
...@@ -30,6 +30,7 @@ CORE_OBJS := \ ...@@ -30,6 +30,7 @@ CORE_OBJS := \
tools/compile.o \ tools/compile.o \
tools/depends.o \ tools/depends.o \
tools/doc_extract-core.o \ tools/doc_extract-core.o \
tools/manifest.o \
tools/tools.o tools/tools.o
OBJS := $(CORE_OBJS) $(TEST_OBJS) OBJS := $(CORE_OBJS) $(TEST_OBJS)
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include <ccan/autodata/autodata.h> #include <ccan/autodata/autodata.h>
#include <stdbool.h> #include <stdbool.h>
#include "../doc_extract.h" #include "../doc_extract.h"
#include "../manifest.h"
#include "licenses.h" #include "licenses.h"
AUTODATA_TYPE(ccanlint_tests, struct ccanlint); AUTODATA_TYPE(ccanlint_tests, struct ccanlint);
...@@ -20,48 +21,6 @@ AUTODATA_TYPE(ccanlint_tests, struct ccanlint); ...@@ -20,48 +21,6 @@ AUTODATA_TYPE(ccanlint_tests, struct ccanlint);
4 == Describe every action. */ 4 == Describe every action. */
extern int verbose; extern int verbose;
enum compile_type {
COMPILE_NORMAL,
COMPILE_NOFEAT,
COMPILE_COVERAGE,
COMPILE_TYPES
};
struct manifest {
char *dir;
/* The module name, ie. final element of dir name */
char *basename;
struct ccan_file *info_file;
/* Linked off deps. */
struct list_node list;
/* Where our final compiled output is */
char *compiled[COMPILE_TYPES];
struct list_head c_files;
struct list_head h_files;
struct list_head run_tests;
struct list_head api_tests;
struct list_head compile_ok_tests;
struct list_head compile_fail_tests;
struct list_head other_test_c_files;
struct list_head other_test_files;
struct list_head other_files;
struct list_head examples;
struct list_head mangled_examples;
/* From tests/check_depends_exist.c */
struct list_head deps;
/* From tests/license_exists.c */
enum license license;
};
/* Get the manifest for a given directory. */
struct manifest *get_manifest(const void *ctx, const char *dir);
/* Error in a particular file: stored off score->per_file_errors. */ /* Error in a particular file: stored off score->per_file_errors. */
struct file_error { struct file_error {
struct list_node list; struct list_node list;
...@@ -158,49 +117,6 @@ struct line_info { ...@@ -158,49 +117,6 @@ struct line_info {
struct pp_conditions *cond; struct pp_conditions *cond;
}; };
struct ccan_file {
struct list_node list;
/* Name (usually, within m->dir). */
char *name;
/* Full path name. */
char *fullname;
/* Pristine version of the original file.
* Use get_ccan_file_contents to fill this. */
const char *contents;
size_t contents_size;
/* Use get_ccan_file_lines / get_ccan_line_info to fill these. */
unsigned int num_lines;
char **lines;
struct line_info *line_info;
struct list_head *doc_sections;
/* If this file gets compiled (eg. .C file to .o file), result here. */
char *compiled[COMPILE_TYPES];
/* Filename containing output from valgrind. */
char *valgrind_log;
/* Leak output from valgrind. */
char *leak_info;
/* Simplified stream (lowercase letters and single spaces) */
char *simplified;
};
/* A new ccan_file, with the given name (talloc_steal onto returned value). */
struct ccan_file *new_ccan_file(const void *ctx, const char *dir, char *name);
/* Use this rather than accessing f->contents directly: loads on demand. */
const char *get_ccan_file_contents(struct ccan_file *f);
/* Use this rather than accessing f->lines directly: loads on demand. */
char **get_ccan_file_lines(struct ccan_file *f);
/* Use this rather than accessing f->lines directly: loads on demand. */ /* Use this rather than accessing f->lines directly: loads on demand. */
struct line_info *get_ccan_line_info(struct ccan_file *f); struct line_info *get_ccan_line_info(struct ccan_file *f);
...@@ -269,9 +185,6 @@ extern bool safe_mode; ...@@ -269,9 +185,6 @@ extern bool safe_mode;
/* Did the user want to keep all the results? */ /* Did the user want to keep all the results? */
extern bool keep_results; extern bool keep_results;
/* Where is the ccan dir? Available after first manifest. */
extern const char *ccan_dir;
/* Compiler and CFLAGS, from config.h if available. */ /* Compiler and CFLAGS, from config.h if available. */
extern const char *compiler, *cflags; extern const char *compiler, *cflags;
......
...@@ -23,47 +23,6 @@ ...@@ -23,47 +23,6 @@
#include <stdarg.h> #include <stdarg.h>
#include <assert.h> #include <assert.h>
const char *ccan_dir;
static size_t dir_hash(const char *name)
{
return hash(name, strlen(name), 0);
}
static const char *manifest_name(const struct manifest *m)
{
return m->dir;
}
static bool dir_cmp(const struct manifest *m, const char *dir)
{
return strcmp(m->dir, dir) == 0;
}
HTABLE_DEFINE_TYPE(struct manifest, manifest_name, dir_hash, dir_cmp,
htable_manifest);
static struct htable_manifest *manifests;
const char *get_ccan_file_contents(struct ccan_file *f)
{
if (!f->contents) {
f->contents = grab_file(f, f->fullname, &f->contents_size);
if (!f->contents)
err(1, "Reading file %s", f->fullname);
}
return f->contents;
}
char **get_ccan_file_lines(struct ccan_file *f)
{
if (!f->lines)
f->lines = strsplit(f, get_ccan_file_contents(f), "\n");
/* FIXME: is f->num_lines necessary? */
f->num_lines = talloc_array_length(f->lines) - 1;
return f->lines;
}
struct list_head *get_ccan_file_docs(struct ccan_file *f) struct list_head *get_ccan_file_docs(struct ccan_file *f)
{ {
if (!f->doc_sections) { if (!f->doc_sections) {
...@@ -73,223 +32,6 @@ struct list_head *get_ccan_file_docs(struct ccan_file *f) ...@@ -73,223 +32,6 @@ struct list_head *get_ccan_file_docs(struct ccan_file *f)
return f->doc_sections; return f->doc_sections;
} }
struct ccan_file *new_ccan_file(const void *ctx, const char *dir, char *name)
{
struct ccan_file *f;
unsigned int i;
assert(dir[0] == '/');
f = talloc(ctx, struct ccan_file);
f->lines = NULL;
f->line_info = NULL;
f->doc_sections = NULL;
for (i = 0; i < ARRAY_SIZE(f->compiled); i++)
f->compiled[i] = NULL;
f->name = talloc_steal(f, name);
f->fullname = talloc_asprintf(f, "%s/%s", dir, f->name);
f->contents = NULL;
f->simplified = NULL;
return f;
}
static void add_files(struct manifest *m, const char *dir)
{
DIR *d;
struct dirent *ent;
char **subs = NULL;
if (dir[0])
d = opendir(dir);
else
d = opendir(".");
if (!d)
err(1, "Opening directory %s", dir[0] ? dir : ".");
while ((ent = readdir(d)) != NULL) {
struct stat st;
struct ccan_file *f;
struct list_head *dest;
bool is_c_src;
if (ent->d_name[0] == '.')
continue;
f = new_ccan_file(m, m->dir,
talloc_asprintf(m, "%s%s",
dir, ent->d_name));
if (lstat(f->name, &st) != 0)
err(1, "lstat %s", f->name);
if (S_ISDIR(st.st_mode)) {
size_t len = talloc_array_length(subs);
subs = talloc_realloc(m, subs, char *, len+1);
subs[len] = talloc_append_string(f->name, "/");
continue;
}
if (!S_ISREG(st.st_mode)) {
talloc_free(f);
continue;
}
if (streq(f->name, "_info")) {
m->info_file = f;
continue;
}
is_c_src = strends(f->name, ".c");
if (!is_c_src && !strends(f->name, ".h")) {
dest = &m->other_files;
} else if (!strchr(f->name, '/')) {
if (is_c_src)
dest = &m->c_files;
else
dest = &m->h_files;
} else if (strstarts(f->name, "test/")) {
if (is_c_src) {
if (strstarts(f->name, "test/api"))
dest = &m->api_tests;
else if (strstarts(f->name, "test/run"))
dest = &m->run_tests;
else if (strstarts(f->name, "test/compile_ok"))
dest = &m->compile_ok_tests;
else if (strstarts(f->name, "test/compile_fail"))
dest = &m->compile_fail_tests;
else
dest = &m->other_test_c_files;
} else
dest = &m->other_test_files;
} else
dest = &m->other_files;
list_add(dest, &f->list);
}
closedir(d);
/* Before we recurse, sanity check this is a ccan module. */
if (!dir[0]) {
size_t i;
if (!m->info_file
&& list_empty(&m->c_files)
&& list_empty(&m->h_files))
errx(1, "No _info, C or H files found here!");
for (i = 0; i < talloc_array_length(subs); i++)
add_files(m, subs[i]);
}
talloc_free(subs);
}
static int cmp_names(struct ccan_file *const *a, struct ccan_file *const *b,
void *unused)
{
return strcmp((*a)->name, (*b)->name);
}
static void sort_files(struct list_head *list)
{
struct ccan_file **files = NULL, *f;
unsigned int i, num;
num = 0;
while ((f = list_top(list, struct ccan_file, list)) != NULL) {
files = talloc_realloc(NULL, files, struct ccan_file *, num+1);
files[num++] = f;
list_del(&f->list);
}
asort(files, num, cmp_names, NULL);
for (i = 0; i < num; i++)
list_add_tail(list, &files[i]->list);
talloc_free(files);
}
struct manifest *get_manifest(const void *ctx, const char *dir)
{
struct manifest *m;
char *olddir, *canon_dir;
unsigned int len;
struct list_head *list;
if (!manifests) {
manifests = talloc(NULL, struct htable_manifest);
htable_manifest_init(manifests);
}
olddir = talloc_getcwd(NULL);
if (!olddir)
err(1, "Getting current directory");
if (chdir(dir) != 0)
err(1, "Failed to chdir to %s", dir);
canon_dir = talloc_getcwd(olddir);
if (!canon_dir)
err(1, "Getting current directory");
m = htable_manifest_get(manifests, canon_dir);
if (m)
goto done;
m = talloc_linked(ctx, talloc(NULL, struct manifest));
m->info_file = NULL;
m->compiled[COMPILE_NORMAL] = m->compiled[COMPILE_NOFEAT] = NULL;
m->dir = talloc_steal(m, canon_dir);
list_head_init(&m->c_files);
list_head_init(&m->h_files);
list_head_init(&m->api_tests);
list_head_init(&m->run_tests);
list_head_init(&m->compile_ok_tests);
list_head_init(&m->compile_fail_tests);
list_head_init(&m->other_test_c_files);
list_head_init(&m->other_test_files);
list_head_init(&m->other_files);
list_head_init(&m->examples);
list_head_init(&m->mangled_examples);
list_head_init(&m->deps);
len = strlen(m->dir);
while (len && m->dir[len-1] == '/')
m->dir[--len] = '\0';
m->basename = strrchr(m->dir, '/');
if (!m->basename)
errx(1, "I don't expect to be run from the root directory");
m->basename++;
/* We expect the ccan dir to be two levels above module dir. */
if (!ccan_dir) {
char *p, *dir;
dir = talloc_strdup(NULL, m->dir);
p = strrchr(dir, '/');
if (!p)
errx(1, "I expect the ccan root directory in ../..");
*p = '\0';
p = strrchr(dir, '/');
if (!p)
errx(1, "I expect the ccan root directory in ../..");
*p = '\0';
ccan_dir = dir;
}
add_files(m, "");
/* Nicer to run tests in a predictable order. */
foreach_ptr(list, &m->api_tests, &m->run_tests, &m->compile_ok_tests,
&m->compile_fail_tests)
sort_files(list);
htable_manifest_add(manifests, m);
done:
if (chdir(olddir) != 0)
err(1, "Returning to original directory '%s'", olddir);
talloc_free(olddir);
return m;
}
/** /**
* remove_comments - strip comments from a line, return copy. * remove_comments - strip comments from a line, return copy.
......
#include "config.h"
#include "manifest.h"
#include "tools.h"
#include <ccan/talloc/talloc.h>
#include <ccan/str/str.h>
#include <ccan/str_talloc/str_talloc.h>
#include <ccan/talloc_link/talloc_link.h>
#include <ccan/hash/hash.h>
#include <ccan/htable/htable_type.h>
#include <ccan/grab_file/grab_file.h>
#include <ccan/noerr/noerr.h>
#include <ccan/foreach/foreach.h>
#include <ccan/asort/asort.h>
#include <ccan/array_size/array_size.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <err.h>
#include <errno.h>
#include <dirent.h>
#include <ctype.h>
#include <stdarg.h>
#include <assert.h>
const char *ccan_dir;
static size_t dir_hash(const char *name)
{
return hash(name, strlen(name), 0);
}
static const char *manifest_name(const struct manifest *m)
{
return m->dir;
}
static bool dir_cmp(const struct manifest *m, const char *dir)
{
return strcmp(m->dir, dir) == 0;
}
HTABLE_DEFINE_TYPE(struct manifest, manifest_name, dir_hash, dir_cmp,
htable_manifest);
static struct htable_manifest *manifests;
const char *get_ccan_file_contents(struct ccan_file *f)
{
if (!f->contents) {
f->contents = grab_file(f, f->fullname, &f->contents_size);
if (!f->contents)
err(1, "Reading file %s", f->fullname);
}
return f->contents;
}
char **get_ccan_file_lines(struct ccan_file *f)
{
if (!f->lines)
f->lines = strsplit(f, get_ccan_file_contents(f), "\n");
/* FIXME: is f->num_lines necessary? */
f->num_lines = talloc_array_length(f->lines) - 1;
return f->lines;
}
struct ccan_file *new_ccan_file(const void *ctx, const char *dir, char *name)
{
struct ccan_file *f;
unsigned int i;
assert(dir[0] == '/');
f = talloc(ctx, struct ccan_file);
f->lines = NULL;
f->line_info = NULL;
f->doc_sections = NULL;
for (i = 0; i < ARRAY_SIZE(f->compiled); i++)
f->compiled[i] = NULL;
f->name = talloc_steal(f, name);
f->fullname = talloc_asprintf(f, "%s/%s", dir, f->name);
f->contents = NULL;
f->simplified = NULL;
return f;
}
static void add_files(struct manifest *m, const char *dir)
{
DIR *d;
struct dirent *ent;
char **subs = NULL;
if (dir[0])
d = opendir(dir);
else
d = opendir(".");
if (!d)
err(1, "Opening directory %s", dir[0] ? dir : ".");
while ((ent = readdir(d)) != NULL) {
struct stat st;
struct ccan_file *f;
struct list_head *dest;
bool is_c_src;
if (ent->d_name[0] == '.')
continue;
f = new_ccan_file(m, m->dir,
talloc_asprintf(m, "%s%s",
dir, ent->d_name));
if (lstat(f->name, &st) != 0)
err(1, "lstat %s", f->name);
if (S_ISDIR(st.st_mode)) {
size_t len = talloc_array_length(subs);
subs = talloc_realloc(m, subs, char *, len+1);
subs[len] = talloc_append_string(f->name, "/");
continue;
}
if (!S_ISREG(st.st_mode)) {
talloc_free(f);
continue;
}
if (streq(f->name, "_info")) {
m->info_file = f;
continue;
}
is_c_src = strends(f->name, ".c");
if (!is_c_src && !strends(f->name, ".h")) {
dest = &m->other_files;
} else if (!strchr(f->name, '/')) {
if (is_c_src)
dest = &m->c_files;
else
dest = &m->h_files;
} else if (strstarts(f->name, "test/")) {
if (is_c_src) {
if (strstarts(f->name, "test/api"))
dest = &m->api_tests;
else if (strstarts(f->name, "test/run"))
dest = &m->run_tests;
else if (strstarts(f->name, "test/compile_ok"))
dest = &m->compile_ok_tests;
else if (strstarts(f->name, "test/compile_fail"))
dest = &m->compile_fail_tests;
else
dest = &m->other_test_c_files;
} else
dest = &m->other_test_files;
} else
dest = &m->other_files;
list_add(dest, &f->list);
}
closedir(d);
/* Before we recurse, sanity check this is a ccan module. */
if (!dir[0]) {
size_t i;
if (!m->info_file
&& list_empty(&m->c_files)
&& list_empty(&m->h_files))
errx(1, "No _info, C or H files found here!");
for (i = 0; i < talloc_array_length(subs); i++)
add_files(m, subs[i]);
}
talloc_free(subs);
}
static int cmp_names(struct ccan_file *const *a, struct ccan_file *const *b,
void *unused)
{
return strcmp((*a)->name, (*b)->name);
}
static void sort_files(struct list_head *list)
{
struct ccan_file **files = NULL, *f;
unsigned int i, num;
num = 0;
while ((f = list_top(list, struct ccan_file, list)) != NULL) {
files = talloc_realloc(NULL, files, struct ccan_file *, num+1);
files[num++] = f;
list_del(&f->list);
}
asort(files, num, cmp_names, NULL);
for (i = 0; i < num; i++)
list_add_tail(list, &files[i]->list);
talloc_free(files);
}
struct manifest *get_manifest(const void *ctx, const char *dir)
{
struct manifest *m;
char *olddir, *canon_dir;
unsigned int len;
struct list_head *list;
if (!manifests) {
manifests = talloc(NULL, struct htable_manifest);
htable_manifest_init(manifests);
}
olddir = talloc_getcwd(NULL);
if (!olddir)
err(1, "Getting current directory");
if (chdir(dir) != 0)
err(1, "Failed to chdir to %s", dir);
canon_dir = talloc_getcwd(olddir);
if (!canon_dir)
err(1, "Getting current directory");
m = htable_manifest_get(manifests, canon_dir);
if (m)
goto done;
m = talloc_linked(ctx, talloc(NULL, struct manifest));
m->info_file = NULL;
m->compiled[COMPILE_NORMAL] = m->compiled[COMPILE_NOFEAT] = NULL;
m->dir = talloc_steal(m, canon_dir);
list_head_init(&m->c_files);
list_head_init(&m->h_files);
list_head_init(&m->api_tests);
list_head_init(&m->run_tests);
list_head_init(&m->compile_ok_tests);
list_head_init(&m->compile_fail_tests);
list_head_init(&m->other_test_c_files);
list_head_init(&m->other_test_files);
list_head_init(&m->other_files);
list_head_init(&m->examples);
list_head_init(&m->mangled_examples);
list_head_init(&m->deps);
len = strlen(m->dir);
while (len && m->dir[len-1] == '/')
m->dir[--len] = '\0';
m->basename = strrchr(m->dir, '/');
if (!m->basename)
errx(1, "I don't expect to be run from the root directory");
m->basename++;
/* We expect the ccan dir to be two levels above module dir. */
if (!ccan_dir) {
char *p, *dir;
dir = talloc_strdup(NULL, m->dir);
p = strrchr(dir, '/');
if (!p)
errx(1, "I expect the ccan root directory in ../..");
*p = '\0';
p = strrchr(dir, '/');
if (!p)
errx(1, "I expect the ccan root directory in ../..");
*p = '\0';
ccan_dir = dir;
}
add_files(m, "");
/* Nicer to run tests in a predictable order. */
foreach_ptr(list, &m->api_tests, &m->run_tests, &m->compile_ok_tests,
&m->compile_fail_tests)
sort_files(list);
htable_manifest_add(manifests, m);
done:
if (chdir(olddir) != 0)
err(1, "Returning to original directory '%s'", olddir);
talloc_free(olddir);
return m;
}
#ifndef CCAN_TOOLS_MANIFEST_H
#define CCAN_TOOLS_MANIFEST_H
#include "config.h"
#include "ccanlint/licenses.h"
#include <ccan/list/list.h>
enum compile_type {
COMPILE_NORMAL,
COMPILE_NOFEAT,
COMPILE_COVERAGE,
COMPILE_TYPES
};
struct manifest {
char *dir;
/* The module name, ie. final element of dir name */
char *basename;
struct ccan_file *info_file;
/* Linked off deps. */
struct list_node list;
/* Where our final compiled output is */
char *compiled[COMPILE_TYPES];
struct list_head c_files;
struct list_head h_files;
struct list_head run_tests;
struct list_head api_tests;
struct list_head compile_ok_tests;
struct list_head compile_fail_tests;
struct list_head other_test_c_files;
struct list_head other_test_files;
struct list_head other_files;
struct list_head examples;
struct list_head mangled_examples;
/* From tests/check_depends_exist.c */
struct list_head deps;
/* From tests/license_exists.c */
enum license license;
};
/* Get the manifest for a given directory. */
struct manifest *get_manifest(const void *ctx, const char *dir);
struct ccan_file {
struct list_node list;
/* Name (usually, within m->dir). */
char *name;
/* Full path name. */
char *fullname;
/* Pristine version of the original file.
* Use get_ccan_file_contents to fill this. */
const char *contents;
size_t contents_size;
/* Use get_ccan_file_lines / get_ccan_line_info to fill these. */
unsigned int num_lines;
char **lines;
struct line_info *line_info;
struct list_head *doc_sections;
/* If this file gets compiled (eg. .C file to .o file), result here. */
char *compiled[COMPILE_TYPES];
/* Filename containing output from valgrind. */
char *valgrind_log;
/* Leak output from valgrind. */
char *leak_info;
/* Simplified stream (lowercase letters and single spaces) */
char *simplified;
};
/* A new ccan_file, with the given name (talloc_steal onto returned value). */
struct ccan_file *new_ccan_file(const void *ctx, const char *dir, char *name);
/* Use this rather than accessing f->contents directly: loads on demand. */
const char *get_ccan_file_contents(struct ccan_file *f);
/* Use this rather than accessing f->lines directly: loads on demand. */
char **get_ccan_file_lines(struct ccan_file *f);
/* Where is the ccan dir? Available after first manifest. */
extern const char *ccan_dir;
#endif /* CCAN_TOOLS_MANIFEST_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment