Author: sebastien.lelong
Date: Fri Oct 3 13:36:51 2008
New Revision: 344
Added:
trunk/tools/jallib (contents, props changed)
Log:
jallib magical wrapper script. For now, handle compile and validate action
Added: trunk/tools/jallib
==============================================================================
--- (empty file)
+++ trunk/tools/jallib Fri Oct 3 13:36:51 2008
@@ -0,0 +1,414 @@
+#!/usr/bin/python
+#
+#
+# Title: jallib main wrapper script
+# Author: Sebastien Lelong, Copyright (c) 2008, all rights reserved.
+# Adapted-by:
+# Compiler:
+#
+# This file is part of jallib (http://jallib.googlecode.com)
+# Released under the BSD license
(http://www.opensource.org/licenses/bsd-license.php)
+#
+# Sources:
+#
+# Description: this script handles common tasks when using the jallib SVN
repository
+# `jallib help` for more !
+#
+#
+# Notes:
+#
+
+
+import sys, os
+import getopt
+
+
+
+################
+# MAIN TARGETS #
+################
+
+
+#---------#
+# COMPILE #
+#---------#
+
+def do_compile(args):
+
+ def _explore_dir(onedir):
+ dirs = [onedir]
+ def keepit(fd):
+ if os.path.isdir(fd):
+ dirs.extend(_explore_dir(fd))
+ for fd in os.listdir(onedir):
+ if not fd.startswith("."):
+ keepit(os.path.join(onedir,fd))
+ return dirs
+
+ # Only parse jallib arg, leave other for underlying cmd
+ def parse_args(args):
+ new_args = args
+ try:
+ opts, args = getopt.getopt(args,
ACTIONS['compile']['options'])
+ return opts,args
+ except getopt.error,e:
+ # args is replaced, now contains options with problems
+ return
parse_args(list(set(new_args).difference(set(args))))
+ if not args:
+ print "Missing arguments to jalv2 compiler..."
+ sys.exit(255)
+ jalfile = args.pop()
+ opts, args = parse_args(args)
+
+ jalv2_exec = "jalv2"
+ dirs = None
+ for o,v in opts:
+ if o == '-R':
+ dirs = []
+ gdirs = v.split(":")
+ for d in gdirs:
+ dirs = _explore_dir(d)
+ elif o == '-E':
+ jalv2_exec = v
+ else:
+ print "Wrong option %s" % o
+ # No root specified ? Try env var, else defaut to cwd
+ if not dirs:
+ v = os.environ.get('JALLIB_REPOS',os.path.curdir)
+ gdirs = v.split(":")
+ for d in gdirs:
+ dirs = _explore_dir(d)
+ cmd = "%s -s %s %s %s" %
(jalv2_exec,"\\;".join(dirs)," ".join(args),jalfile)
+ print "cmd: %s" % cmd
+ status = os.system(cmd)
+ if status != 0:
+ print "Error while compiling file (status=%s). See previous
message." %
status
+ sys.exit(status)
+ pass
+
+
+#----------#
+# VALIDATE #
+#----------#
+
+import re
+
+def content_not_empty(val):
+ return val.strip() != ''
+def compiler_version(val):
+ return re.match("^(>|<|>=|<=|=)\d+(\.\d+\w*)+\s+$",val)
+
+JALLIB = """^-- This file is part of
jallib\s+\(http://jallib.googlecode.com\)"""
+LICENSE = """^-- Released under the BSD
license\s+\(http://www.opensource.org/licenses/bsd-license.php\)"""
+
+# exceptions while checking case
+ALLOWED_MIXED_CASE = """.*(pin|port|tris|option|ancon).*"""
+
+FIELDS = [
+ {'field':"Title",'predicate' : content_not_empty,'mandatory':
True, 'multiline' :False},
+
{'field':"Author",'predicate':content_not_empty,'mandatory':True,'multiline':False},
+
{'field':"Adapted-by",'predicate':content_not_empty,'mandatory':False,'multiline':False},
+
{'field':"Compiler",'predicate':compiler_version,'mandatory':True,'multiline':False},
+
{'field':"Description",'predicate':content_not_empty,'mandatory':True,'multiline':True},
+
{'field':"Sources",'predicate':content_not_empty,'mandatory':False,'multiline':True},
+
{'field':"Notes",'predicate':content_not_empty,'mandatory':False,'multiline':True},
+ ]
+
+errors = []
+warnings = []
+
+def extract_header(content):
+ # reverse() to pop header lines (no such shift())
+ content.reverse()
+ header = []
+ while True:
+ i,l = content.pop()
+ if not l.startswith("--"):
+ # not a header line, put it again
+ content.append((i,l))
+ break
+ else:
+ header.append((i,l))
+ # remaining content: back to forward...
+ content.reverse()
+ return header
+
+def validate_field(data,field,predicate,mandatory,multiline=False):
+
+ syntax = "^-- %s:\s*(.*)" % field
+
+ def single_line_content(line):
+ return "".join(re.sub(syntax,lambda x:x.group(1),line))
+ def multi_line_content(num,line,data):
+ c = single_line_content(line)
+ for i,l in data[data.index((num,line)) + 1:]:
+ # Check no comment gap
+ if not re.match("^--\s",l):
+ errors.append("%d: cannot extract content, line
is not starting with
comment: %s" % (i,repr(l)))
+ return ""
+ # No other field within content
+ for f in [d['field'] for d in FIELDS]:
+ if l.startswith("-- %s:" % f):
+ errors.append("%d: detected field %s
within content of field %s" %
(i,f,field))
+ return ""
+ if l.strip() == '--':
+ # got end of block
+ break
+ c += re.sub("^--\s","",l)
+ else:
+ errors.append("%d: cannot find end of field content %s"
% (i,field))
+ return c
+
+ # eat header, and check fields (mandatory or not) and
+ # content (using predicate)
+ c = None
+ for i,l in data:
+ if re.match(syntax,l):
+ # got it, check field content
+ if multiline:
+ c = multi_line_content(i,l,data)
+ else:
+ c = single_line_content(l)
+ # and check with predicate
+ valid = predicate(c)
+ if mandatory and not valid:
+ errors.append("%d: content for field %s is not
valid: %s" %
(i,field,repr(c)))
+ break
+ else:
+ if mandatory:
+ errors.append("Cannot find field %s (searched for
'%s')" %
(field,syntax))
+
+ return c
+
+
+def validate_header(content):
+ header = extract_header(content)
+
+ # check stuff about jallib and license
+ jallib = False
+ license = False
+ for i,line in header:
+ if re.match(JALLIB,line):
+ jallib = True
+ if license: break
+ if re.match(LICENSE,line):
+ license = True
+ if jallib: break
+ not jallib and errors.append("Cannot find references to jallib (should
have: %s)" % repr(JALLIB))
+ not license and errors.append("Cannot find references to license
(should
have: %s)" % repr(LICENSE))
+ for field_dict in FIELDS:
+ validate_field(header,**field_dict)
+
+
+def validate_filename(filename):
+ # must be lowercase
+ filename = os.path.basename(filename)
+ if filename.lower() != filename:
+ errors.append("Filename is not lowercase: %s" % filename)
+ # should have "jal" extention
+ if not filename.endswith(".jal"):
+ warnings.append("Filename doesn't have '*.jal' extention: %s" %
filename)
+ pass
+
+
+def validate_lower_case(content):
+ # tokenize content, searching for infamous CamelCase words
+ # Also search Capitalized ones...
+ tokenizer = re.compile("\w+")
+ caps = re.compile("[A-Z]")
+ freetext = re.compile('(("|\').*("|\'))')
+ mixed = re.compile(ALLOWED_MIXED_CASE,re.IGNORECASE)
+ # no check in comments
+ codeonly = []
+ for i,line in content:
+ if line.startswith(";") or line.startswith("--"):
+ continue
+ # don't consider free text (between quotes)
+ line = freetext.sub("",line)
+ l = line.split()
+ def nocomment(l,c):
+ # dirty search comments...
+ if c in l:
+ l = l[:l.index(c)]
+ tmp = [w.startswith(c) for n,w in enumerate(l)]
+ if True in tmp:
+ l = l[:tmp.index(True)]
+ return l
+ # first check ";" *then* "--"
+ # so things like ";;;;-- Note" are not considered
+ # (else produces wrong splitting...)
+ l = nocomment(l,";")
+ l = nocomment(l,"--")
+ # propagate line number
+ codeonly.append((i," ".join(l)))
+
+ weird = {} # stores all errors
+ for i,line in codeonly:
+ for token in tokenizer.findall(line):
+ # it can be all in caps (constants)
+ if token.upper() == token:
+ continue
+ # hex definition, give up
+ if "0x" in token:
+ continue
+ # exceptions...
+ if mixed.match(token.lower()):
+ continue
+ if caps.findall(token):
+ weird.setdefault("Found Capitals in token: %s"
%
repr(token),[]).append(i)
+ # reconsitute errors, with multiple line number if error has occured
multiple time
+ for s,nums in weird.items():
+ err = "%s: %s" % (",".join(map(str,nums)),s)
+ errors.append(err)
+
+def validate_procfunc_defs(content):
+ # no () in definition
+ func_proc = re.compile("^(procedure|function)")
+ no_spaces = re.compile(".*\s+\(.*is")
+ for i,line in content:
+ # don't even check both (), not needed
+ if func_proc.match(line) and not "(" in line:
+ errors.append("%d: %s missing (). Calls must also be
explicit" %
(i,repr(line)))
+ if func_proc.match(line) and no_spaces.match(line):
+ errors.append("%d: found a space before parenthesis:
%s" %
(i,repr(line)))
+
+def validate_code(content):
+ validate_lower_case(content)
+ validate_procfunc_defs(content)
+ # ...
+
+def validate(filename):
+ validate_filename(filename)
+ # also extract line number (enumerate from 0, count from 1)
+ content = [(i + 1,l) for i,l in
enumerate(open(filename,"r").readlines())]
+ validate_header(content)
+ # remaining content has no more header
+ validate_code(content)
+
+
+def report(filename):
+ print "File: %s" % filename
+ print "%d errors found" % len(errors)
+ for err in errors:
+ print "\tERROR: %s" % err
+ print
+ print "%d warnings found" % len(warnings)
+ for warn in warnings:
+ print "\twarning: %s" % warn
+
+ if errors or warnings:
+ return True
+
+
+def do_validate(args):
+ # No jallib args (yet!) for validating
+ # args contain jal file to validate
+
+ global errors
+ global warnings
+ at_least_one_failed = False
+ for filename in args:
+ validate(filename)
+ if report(filename):
+ at_least_one_failed = True
+ errors = []
+ warnings = []
+
+ if at_least_one_failed:
+ sys.exit(1)
+ else:
+ sys.exit(0)
+
+
+
+#############
+# HELP FUNC #
+#############
+
+def generic_help():
+ print """
+Actions:
+ - compile : compile the given file, expanding one or more root
directories containing libraries
+ - validate : validate the given file, according to Jallib Style Guide
(JSG)
+
+Use 'help' with each action for more (eg. "jallib help compile")
+
+"""
+
+def compile_help():
+ print """
+ jallib compile file.jal
+
+Use this option to actually compile the given file. This script
+will the -s option of jalv2 compiler, to build an appropriate
+command line. When using libs from SVN, libs are organized within a
+directory hierarchy. Every sub-directories will then be explored so
+all are included in the command line (except hidden dir, starting
with '.').
+
+All arguments given to this action will be passed to the jalv2 compiler
+(so please, do not pass any -s option...). Also be aware the 'jalv2'
executable
+must be declare in the path (PATH) by default.
+
+Additional options:
+
+ -R path1[:path2[:path3]...] : use this option to specify one or more
root path
+ to jallib directories. Directory paths
are seperated
+ using ':' char. If no option is given,
this script will
+ also look at a JALLIB_REPOS environment
variable. If no
+ variable is set, then this script will
consider the current
+ path (cwd) as the root path.
+ Precedence: cwd < var < option
+
+ -E path : use this option to manually set the path to jalv2
executable. If not set, except
+ it to be on path.
+
+"""
+
+def validate_help():
+ print """
+ jallib validate file.jal [another_file.jal yet_anoter_file.jal ...]
+
+Use this option to validate (or not...) your file against
+the Jallib Style Guide (JSG). Not all rules are checked, so
+while validating a file, please manually have a look to it too !
+
+See: http://code.google.com/p/jallib/wiki/JallibStyleGuide for more
+"""
+
+def do_help(action_args=[]):
+ action = None
+ if action_args:
+ action = action_args[0]
+ if not action:
+ generic_help()
+ else:
+ ACTIONS[action]['help']()
+
+
+# Main action registry
+ACTIONS = {
+ 'compile' : {'callback' : do_compile, 'options' : 'R:E:',
'help' :
compile_help},
+ 'validate' : {'callback' : do_validate, 'options' : '',
'help' :
validate_help},
+ 'help' : {'callback' : do_help, 'options' : '', 'help'
: None},
+ }
+
+
+if __name__ == "__main__":
+ try:
+ # First extract the action, then parse action's args
+
+ action = sys.argv[1]
+ action_args = sys.argv[2:]
+ try:
+ callme = ACTIONS[action]['callback']
+ callme(action_args)
+ except KeyError,e:
+ if action in ('help','--help','-h'):
+ do_help(action_args)
+ sys.exit(0)
+ else:
+ print "Unknown action %s" % e
+ sys.exit(255)
+ except IndexError,e:
+ print >> sys.stderr,"Please provide an action: %s" %
repr(ACTIONS.keys())
+ sys.exit(255)
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"jallib" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at
http://groups.google.com/group/jallib?hl=en
-~----------~----~----~----~------~----~------~--~---