Browse Source

ccanlint: check license and LICENSE symlink.

Michael Ellerman noted that ccan/opt has no symlink, so add ccanlint test.
Rusty Russell 15 years ago
parent
commit
1925eb421d
1 changed files with 155 additions and 0 deletions
  1. 155 0
      tools/ccanlint/tests/license.c

+ 155 - 0
tools/ccanlint/tests/license.c

@@ -0,0 +1,155 @@
+#include <tools/ccanlint/ccanlint.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 <ccan/talloc/talloc.h>
+#include <ccan/str/str.h>
+
+static struct doc_section *find_license(const struct manifest *m)
+{
+	struct doc_section *d;
+
+	list_for_each(m->info_file->doc_sections, d, list) {
+		if (!streq(d->function, m->basename))
+			continue;
+		if (streq(d->type, "license"))
+			return d;
+	}
+	return NULL;
+}
+
+static const char *expected_link(const struct manifest *m,
+				 struct doc_section *d)
+{
+	if (streq(d->lines[0], "GPL")
+	    || streq(d->lines[0], "GPLv3")
+	    || streq(d->lines[0], "GPLv3 or later")
+	    || streq(d->lines[0], "GPLv3 (or later)")
+	    || streq(d->lines[0], "GPL (3 or any later version)"))
+		return "../../licenses/GPL-3";
+	if (streq(d->lines[0], "GPLv2")
+	    || streq(d->lines[0], "GPLv2 or later")
+	    || streq(d->lines[0], "GPLv2 (or later)")
+	    || streq(d->lines[0], "GPL (2 or any later version)"))
+		return "../../licenses/GPL-3";
+	if (streq(d->lines[0], "LGPL")
+	    || streq(d->lines[0], "LGPLv3")
+	    || streq(d->lines[0], "LGPLv3 or later")
+	    || streq(d->lines[0], "LGPLv3 (or later)")
+	    || streq(d->lines[0], "LGPL (3 or any later version)"))
+		return "../../licenses/LGPL-3";
+	if (streq(d->lines[0], "LGPLv2")
+	    || streq(d->lines[0], "LGPLv2 or later")
+	    || streq(d->lines[0], "LGPLv2 (or later)")
+	    || streq(d->lines[0], "LGPL (2 or any later version)"))
+		return "../../licenses/LGPL-2.1";
+	if (streq(d->lines[0], "BSD")
+	    || streq(d->lines[0], "MIT"))
+		return "../../licenses/BSD-MIT";
+	return NULL;
+}
+
+static void handle_license_link(struct manifest *m, struct score *score)
+{
+	const char *link = talloc_asprintf(m, "%s/LICENSE", m->dir);
+	struct doc_section *d = find_license(m);
+	const char *ldest = expected_link(m, d);
+	char *q;
+
+	printf(
+	"Most modules want a copy of their license, so usually we create a\n"
+	"LICENSE symlink into ../../licenses to avoid too many copies.\n");
+
+	/* FIXME: make ask printf-like */
+	q = talloc_asprintf(m, "Set up link to %s (license is %s)?",
+			    ldest, d->lines[0]);
+	if (ask(q)) {
+		if (symlink(ldest, link) != 0)
+			err(1, "Creating symlink %s -> %s", link, ldest);
+	}
+}
+
+static void check_has_license(struct manifest *m,
+			      bool keep,
+			      unsigned int *timeleft, struct score *score)
+{
+	char buf[PATH_MAX];
+	ssize_t len;
+	char *license = talloc_asprintf(m, "%s/LICENSE", m->dir);
+	const char *expected;
+	struct doc_section *d;
+
+	d = find_license(m);
+	if (!d) {
+		score->error = "No License: tag in _info";
+		return;
+	}
+	expected = expected_link(m, d);
+
+	len = readlink(license, buf, sizeof(buf));
+	if (len < 0) {
+		/* Could be a real file... OK if not a standard license. */
+		if (errno == EINVAL) {
+			if (!expected) {
+				score->pass = true;
+				return;
+			}
+			score->error
+				= talloc_asprintf(score,
+					  "License in _info is '%s',"
+					  " expect LICENSE symlink '%s'",
+					  d->lines[0], expected);
+			return;
+		}
+		if (errno == ENOENT) {
+			score->error = "LICENSE does not exist";
+			if (expected)
+				has_license.handle = handle_license_link;
+			return;
+		}
+		err(1, "readlink on %s", license);
+	}
+	if (len >= sizeof(buf))
+		errx(1, "Reading symlink %s gave huge result", license);
+
+	buf[len] = '\0';
+
+	if (!strstarts(buf, "../../licenses/")) {
+		score->error = talloc_asprintf(score,
+					       "Expected symlink to"
+					       " ../../licenses/..."
+					       " not %s", buf);
+		return;
+	}
+
+	if (!expected) {
+		score->error = talloc_asprintf(score,
+					  "License in _info is unknown '%s',"
+					  " but LICENSE symlink is '%s'",
+					  d->lines[0], buf);
+		return;
+	}
+
+	if (!streq(buf, expected)) {
+		score->error = talloc_asprintf(score,
+				       "Expected symlink to %s not %s",
+				       expected, buf);
+		return;
+	}
+	score->pass = true;
+	score->score = score->total;
+}
+
+struct ccanlint has_license = {
+	.key = "has-license",
+	.name = "Module has license",
+	.check = check_has_license,
+};
+
+REGISTER_TEST(has_license, &has_info, NULL);