svn commit: r241560 - in user/crees: . rclint
Chris Rees
crees at FreeBSD.org
Sun Oct 14 22:28:57 UTC 2012
Author: crees (ports committer)
Date: Sun Oct 14 22:28:56 2012
New Revision: 241560
URL: http://svn.freebsd.org/changeset/base/241560
Log:
Add the framework for an RC script testing tool.
It will mostly be used by ports developers, but a base mode is possible
Added:
user/crees/
user/crees/rclint/
user/crees/rclint/errors.en
user/crees/rclint/rclint.py (contents, props changed)
Added: user/crees/rclint/errors.en
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ user/crees/rclint/errors.en Sun Oct 14 22:28:56 2012 (r241560)
@@ -0,0 +1,15 @@
+# Rigid format of this file; colon-separated tuples to define
+# error messages and explanations.
+# format as follows:
+error_id:message:explanation
+name_missing:name is set late or not at all:Directly following the sourcing of scripts must follow setting of the variable name.
+name_quoted:name is quoted:Do not quote the value of name. If it has spaces, use underscores.
+rc_subr_late:rc.subr sourcing late or nonexistent:The first non-comment non-blank line in any rc file must be sourcing /etc/rc.subr.
+rcorder_keyword_freebsd:Do not include FreeBSD in the KEYWORD rcorder line:Historically FreeBSD scripts were marked in the KEYWORD section. This is no longer necessary.
+rcorder_missing:Missing rcorder block:Following the FreeBSD RCSId keyword must be nothing but empty comment lines and an rcorder block.
+rcorder_order:rcorder block in the wrong order; should be PROVIDE/REQUIRE/BEFORE/KEYWORD:See the article on RC scripting.
+rcvar_incorrect:rcvar is not set correctly:rcvar must be directly set to name_enable. Do not quote, and do not use indirection; ${name}_enable is slower than example_enable.
+rcvar_missing:rcvar is set late or not at all:Setting rcvar must be done straight after setting name.
+rcvar_quoted:rcvar is quoted:Do not quote the value of rcvar.
+rcsid:Missing FreeBSD RCSId keyword:All rc scripts must contain a line beginning # $FreeBSD$. Do not include blank lines without comment markers at the beginning (#) until the script begins.
+shebang:Incorrect shebang used:All rc scripts must start with the correct shebang; #!/bin/sh.
Added: user/crees/rclint/rclint.py
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ user/crees/rclint/rclint.py Sun Oct 14 22:28:56 2012 (r241560)
@@ -0,0 +1,161 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2012 The FreeBSD Project. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE PROJECT ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE PROJECT BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# $FreeBSD$
+#
+
+major = 0
+minor = 0
+micro = 0
+
+import argparse
+import logging
+import re
+
+def error_explain(error):
+ if verbosity > 0: print error['explanation']
+
+def error(type, line_number, filename):
+ logging.error("[%d]%s: %s " % (line_number, filename, errors[type]['message']))
+ error_explain(errors[type])
+
+def check_quoted(string):
+ return True if string[0] == '"' or string[0] == "'" else False
+
+def get_value(var, line):
+ n = re.match('%s=(\S*)$' % var, line)
+ if n:
+ value=n.group(1)
+ return value
+ else:
+ return False
+
+def do_rclint(filename):
+ logging.debug('Suck in file %s' % filename)
+ lines=[line.rstrip('\n') for line in open(filename)]
+ num = 0
+
+ # Basic order; shebang, copyright, RCSId, gap, rcorder
+
+ logging.debug('Check shebang')
+ if lines[num] != '#!/bin/sh':
+ error('shebang', num, filename)
+
+ logging.debug('Skipping license')
+ num += 1
+ while (not re.match('# \$FreeBSD[:$]', lines[num]) and
+ (lines[num] == '' or lines[num][0] == '#')):
+ num += 1
+
+ logging.debug('Checking for RCSId')
+ if not re.match('# \$FreeBSD[:$]', lines[num]):
+ error('rcsid', num, filename)
+
+ num += 1
+ while lines[num] == '#' or lines[num] == '': num += 1
+
+ logging.debug('Checking rcorder order and sucking in names')
+ if not re.match('# [PRBK]', lines[num]):
+ error('rcorder_missing', num, filename)
+ orders = ['provide', 'require', 'before', 'keyword']
+ index = 0
+ rcorder = { o: [] for o in orders }
+ while index < 4:
+ order = orders[index]
+ try:
+ for result in re.match('# %s: (.*)' % order.upper(),
+ lines[num]).group(1).split(' '):
+ rcorder[order].append(result)
+ num += 1
+ except:
+ index += 1
+
+ if 'FreeBSD' in rcorder['keyword']:
+ error('rcorder_keyword_freebsd', num, filename)
+
+ if re.match('# [PRBK]', lines[num]):
+ error('rcorder_order', num, filename)
+
+ documentation_line = num
+
+ logging.debug('Checking sourcing lines')
+ while lines[num] == '' or lines[num][0] == '#':
+ num += 1
+
+ if lines[num] != '. /etc/rc.subr':
+ error('rc_subr_late', num, filename)
+
+ logging.debug('Checking name assignment')
+ while lines[num] == '' or lines[num][0] == '#' or lines[num][0] == '.':
+ num += 1
+
+ name = get_value('name', lines[num])
+ if not name:
+ error('name_missing', num, filename)
+ elif check_quoted(name):
+ error('name_quoted', num, filename)
+ else:
+ logging.debug('name discovered as %s' % name)
+ num += 1
+
+ logging.debug('Checking rcvar')
+ rcvar = get_value('rcvar', lines[num])
+ logging.debug('rcvar discovered as %s' % rcvar if rcvar else 'rcvar not discovered')
+ if not rcvar:
+ error('rcvar_missing', num, filename)
+ elif check_quoted(rcvar):
+ error('rcvar_quoted', num, filename)
+ elif rcvar != '%s_enable' % name:
+ error('rcvar_incorrect', num, filename)
+ else:
+ num += 1
+
+
+parser = argparse.ArgumentParser()
+parser.add_argument('filenames', nargs = '+')
+parser.add_argument('--language', nargs = 1, type=str, default = ['en'], help = 'sets the language that errors are reported in')
+parser.add_argument('-v', action='count', help='raises debug level; provides detailed explanations of errors')
+
+args = parser.parse_args()
+
+verbosity = args.v
+errordb = 'errors.%s' % args.language[0]
+
+logging.basicConfig(level=(logging.DEBUG if verbosity > 1 else logging.ERROR))
+
+try:
+ with open(errordb) as f:
+ logging.debug('Sucking in error database')
+ errors = {}
+ for e in f.readlines():
+ if e[0] == '#' or len(e) == 1:
+ continue
+ e = e.split(':')
+ errors[e[0]] = { 'message': e[1], 'explanation': e[2] }
+except:
+ logging.error('Cannot open database for language %s' % errordb)
+ exit()
+
+for file in args.filenames:
+ do_rclint(file)
More information about the svn-src-user
mailing list