From e67de75f0d64096956ad40ecb09462dac6fc5f03 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 21 Jul 2011 13:02:27 +0930 Subject: [PATCH] ccanlint: add simple check for comment referring to LICENSE file. After discussion with various developers (particularly the Samba team), there's a consensus that a reference to the license in each source file is useful. Since CCAN modules are designed to be cut and paste, this helps avoid any confusion should the LICENSE file go missing. We also detect standard boilerplates, in which case a one-line summary isn't necessary. --- tools/ccanlint/Makefile | 1 + tools/ccanlint/ccanlint.h | 22 ++--- tools/ccanlint/file_analysis.c | 1 + tools/ccanlint/licenses.c | 113 +++++++++++++++++++++++++ tools/ccanlint/licenses.h | 34 ++++++++ tools/ccanlint/tests/license_comment.c | 69 +++++++++++++++ 6 files changed, 225 insertions(+), 15 deletions(-) create mode 100644 tools/ccanlint/licenses.c create mode 100644 tools/ccanlint/licenses.h create mode 100644 tools/ccanlint/tests/license_comment.c diff --git a/tools/ccanlint/Makefile b/tools/ccanlint/Makefile index 3772267e..6bec8c4a 100644 --- a/tools/ccanlint/Makefile +++ b/tools/ccanlint/Makefile @@ -4,6 +4,7 @@ TEST_OBJS := $(NORMAL_TEST_CFILES:.c=.o) $(COMPULSORY_TEST_CFILES:.c=.o) CORE_OBJS := tools/ccanlint/ccanlint.o \ tools/ccanlint/file_analysis.o \ + tools/ccanlint/licenses.o \ tools/doc_extract-core.o \ tools/depends.o \ tools/tools.o \ diff --git a/tools/ccanlint/ccanlint.h b/tools/ccanlint/ccanlint.h index b9965f75..aec75ad9 100644 --- a/tools/ccanlint/ccanlint.h +++ b/tools/ccanlint/ccanlint.h @@ -4,6 +4,7 @@ #include #include #include "../doc_extract.h" +#include "licenses.h" #define REGISTER_TEST(name, ...) extern struct ccanlint name @@ -16,21 +17,6 @@ 4 == Describe every action. */ extern int verbose; -enum license { - LICENSE_LGPLv2_PLUS, - LICENSE_LGPLv2, - LICENSE_LGPLv3, - LICENSE_LGPL, - LICENSE_GPLv2_PLUS, - LICENSE_GPLv2, - LICENSE_GPLv3, - LICENSE_GPL, - LICENSE_BSD, - LICENSE_MIT, - LICENSE_PUBLIC_DOMAIN, - LICENSE_UNKNOWN -}; - struct manifest { char *dir; /* The module name, ie. final element of dir name */ @@ -195,6 +181,9 @@ struct ccan_file { /* 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). */ @@ -209,6 +198,9 @@ char **get_ccan_file_lines(struct ccan_file *f); /* Use this rather than accessing f->lines directly: loads on demand. */ struct line_info *get_ccan_line_info(struct ccan_file *f); +/* Use this rather than accessing f->simplified directly: loads on demand. */ +const char *get_ccan_simplified(struct ccan_file *f); + enum line_compiled { NOT_COMPILED, COMPILED, diff --git a/tools/ccanlint/file_analysis.c b/tools/ccanlint/file_analysis.c index 7ce65479..c2021167 100644 --- a/tools/ccanlint/file_analysis.c +++ b/tools/ccanlint/file_analysis.c @@ -86,6 +86,7 @@ struct ccan_file *new_ccan_file(const void *ctx, const char *dir, char *name) f->fullname = talloc_asprintf(f, "%s/%s", dir, f->name); f->contents = NULL; f->cov_compiled = NULL; + f->simplified = NULL; return f; } diff --git a/tools/ccanlint/licenses.c b/tools/ccanlint/licenses.c new file mode 100644 index 00000000..9bfa1d2b --- /dev/null +++ b/tools/ccanlint/licenses.c @@ -0,0 +1,113 @@ +#include "licenses.h" +#include "ccanlint.h" +#include +#include + +const struct license_info licenses[] = { + { "LGPLv2+", "LGPL", + { "gnu lesser general public license", + "version 2", + "or at your option any later version" + } + }, + { "LGPLv2", "LGPL", + { "gnu lesser general public license", + "version 2", + NULL + } + }, + { "LGPLv3", "LGPL", + { "gnu lesser general public license", + "version 3", + NULL + } + }, + { "LGPL", "LGPL", + { "gnu lesser general public license", + NULL, + NULL + } + }, + { "GPLv2+", "GPL", + { "gnu general public license", + "version 2", + "or at your option any later version" + } + }, + { "GPLv2", "GPL", + { "gnu general public license", + "version 2", + NULL + } + }, + { "GPLv3", "GPL", + { "gnu general public license", + "version 3", + NULL + } + }, + { "GPL", "GPL", + { "gnu general public license", + NULL, + NULL + } + }, + { "BSD-3CLAUSE", "BSD", + { "redistributions of source code must retain", + "redistributions in binary form must reproduce", + "endorse or promote" + } + }, + { "BSD-MIT", "MIT", + { "without restriction", + "above copyright notice", + "without warranty" + } + }, + { "Public domain", "Public domain", + { NULL, NULL, NULL } + }, + { "Unknown license", "Unknown license", + { NULL, NULL, NULL } + }, +}; + +const char *get_ccan_simplified(struct ccan_file *f) +{ + if (!f->simplified) { + unsigned int i, j; + + /* Simplify for easy matching: only alnum and single spaces. */ + f->simplified = talloc_strdup(f, get_ccan_file_contents(f)); + for (i = 0, j = 0; f->simplified[i]; i++) { + if (cisupper(f->simplified[i])) + f->simplified[j++] = tolower(f->simplified[i]); + else if (cislower(f->simplified[i])) + f->simplified[j++] = f->simplified[i]; + else if (cisdigit(f->simplified[i])) + f->simplified[j++] = f->simplified[i]; + else if (cisspace(f->simplified[i])) { + if (j != 0 && f->simplified[j-1] != ' ') + f->simplified[j++] = ' '; + } + } + f->simplified[j] = '\0'; + } + return f->simplified; +} + +bool find_boilerplate(struct ccan_file *f, enum license license) +{ + unsigned int i; + + for (i = 0; i < NUM_CLAUSES; i++) { + if (!licenses[license].clause[i]) + break; + + if (!strstr(get_ccan_simplified(f), + licenses[license].clause[i])) { + return false; + } + } + return true; +} diff --git a/tools/ccanlint/licenses.h b/tools/ccanlint/licenses.h new file mode 100644 index 00000000..7b70bfac --- /dev/null +++ b/tools/ccanlint/licenses.h @@ -0,0 +1,34 @@ +#ifndef CCANLINT_LICENSES_H +#define CCANLINT_LICENSES_H +#include + +enum license { + LICENSE_LGPLv2_PLUS, + LICENSE_LGPLv2, + LICENSE_LGPLv3, + LICENSE_LGPL, + LICENSE_GPLv2_PLUS, + LICENSE_GPLv2, + LICENSE_GPLv3, + LICENSE_GPL, + LICENSE_BSD, + LICENSE_MIT, + LICENSE_PUBLIC_DOMAIN, + LICENSE_UNKNOWN +}; + +#define NUM_CLAUSES 3 + +struct license_info { + const char *name; + const char *shortname; + /* Edit distance is expensive, and this works quite well. */ + const char *clause[NUM_CLAUSES]; +}; + +extern const struct license_info licenses[]; + +struct ccan_file; +bool find_boilerplate(struct ccan_file *f, enum license license); + +#endif /* CCANLINT_LICENSES_H */ diff --git a/tools/ccanlint/tests/license_comment.c b/tools/ccanlint/tests/license_comment.c new file mode 100644 index 00000000..f1902126 --- /dev/null +++ b/tools/ccanlint/tests/license_comment.c @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void check_license_comment(struct manifest *m, + bool keep, + unsigned int *timeleft, struct score *score) +{ + struct list_head *list; + + /* No requirements on public domain. */ + if (m->license == LICENSE_PUBLIC_DOMAIN + || m->license == LICENSE_UNKNOWN) { + score->pass = true; + score->score = score->total; + return; + } + + foreach_ptr(list, &m->c_files, &m->h_files) { + struct ccan_file *f; + + list_for_each(list, f, list) { + unsigned int i; + char **lines = get_ccan_file_lines(f); + struct line_info *info = get_ccan_line_info(f); + bool found_license = false, found_flavor = false; + + for (i = 0; lines[i]; i++) { + if (info[i].type == CODE_LINE) + break; + if (strstr(lines[i], "LICENSE")) + found_license = true; + if (strstr(lines[i], + licenses[m->license].shortname)) + found_flavor = true; + } + if ((!found_license || !found_flavor) + && !find_boilerplate(f, m->license)) { + score_file_error(score, f, lines[i] ? i : 0, + "No reference to license" + " found"); + } + } + } + + if (list_empty(&score->per_file_errors)) { + score->pass = true; + score->score = score->total; + } +} + +struct ccanlint license_comment = { + .key = "license_comment", + .name = "Source and header files refer to LICENSE", + .check = check_license_comment, + .needs = "license_exists" +}; +REGISTER_TEST(license_comment); -- 2.39.2