CoreValidation: Moved common parts of command line build script to Utilities.
diff --git a/CMSIS/CoreValidation/Examples/MDK-FVP/build.py b/CMSIS/CoreValidation/Examples/MDK-FVP/build.py
index 9045f8e..2a8a5ef 100644
--- a/CMSIS/CoreValidation/Examples/MDK-FVP/build.py
+++ b/CMSIS/CoreValidation/Examples/MDK-FVP/build.py
@@ -1,38 +1,52 @@
 #! python
 
-from subprocess import call
-from xml.etree import ElementTree
-import os.path
+import sys
+from argparse import ArgumentParser
+from datetime import datetime
 
-print "Build CMSIS-Core Validation using MDK"
+sys.path.append('../../../Utilities/buildutils') 
 
-TARGET_FVP = 'FVP'
+from uv4cmd import Uv4Cmd 
+from fvpcmd import FvpCmd 
+from testresult import TestResult
+
+DEVICE_CM0  = 'Cortex-M0'
+DEVICE_CM3  = 'Cortex-M3'
+DEVICE_CM4f = 'Cortex-M4f'
+DEVICE_CM7  = 'Cortex-M7'
+DEVICE_CM23 = 'Cortex-M23'
+DEVICE_CM33 = 'Cortex-M33'
+
 CC_AC6 = 'AC6'
 CC_AC5 = 'AC5'
 CC_GCC = 'GCC'
 
-UV4 = "UV4.exe"
-PRJ = "CMSIS_CV.uvprojx"
-# DEVICES = [ 'Cortex-M0', 'Cortex-M3', 'Cortex-M4f', 'Cortex-M7', 'Cortex-M23', 'Cortex-M33' ]
-# DEVICES = [ 'Cortex-M0', 'Cortex-M3', 'Cortex-M4f', 'Cortex-M7' ]
-DEVICES = [ 'Cortex-M4f' ]
+TARGET_FVP = 'FVP'
+
+DEVICES = [ DEVICE_CM0, DEVICE_CM3, DEVICE_CM4f, DEVICE_CM7 ]
 COMPILERS = [ CC_AC5, CC_AC6, CC_GCC ]
 TARGETS = [ TARGET_FVP ]
 
-FVP_MODELS = { 
-    'Cortex-M0'  : [ "fvp_mps2_cortex-m0.exe", "--cyclelimit",   "2000000", "" ],
-    'Cortex-M3'  : [ "fvp_mps2_cortex-m3.exe", "--cyclelimit",   "2000000", "" ],
-    'Cortex-M4f' : [ "fvp_mps2_cortex-m4.exe", "--cyclelimit",   "5000000", "" ],
-    'Cortex-M7'  : [ "fvp_mps2_cortex-m7.exe", "--cyclelimit",   "5000000", "" ],
-    'Cortex-M23' : [ "fvp_mps2_cortex-m23.exe", "--cyclelimit", "10000000", "-f", "ARMCM23_TZ_config.txt", "-a", "cpu0=" ],
-    'Cortex-M33' : [ "fvp_mps2_cortex-m33.exe", "--cyclelimit", "10000000", "-f", "ARMCM33_DSP_FP_TZ_config.txt", "-a", "cpu0=" ]
-  } 
-
 SKIP = [ 
-    ['Cortex-M23', CC_GCC, None ], 
-    ['Cortex-M33', CC_GCC, None ] 
+    [ DEVICE_CM23, CC_GCC, None ],
+    [ DEVICE_CM33, CC_GCC, None ]
   ]
 
+APP_FORMAT = {
+  CC_AC6: "axf",
+  CC_AC5: "axf",
+  CC_GCC: "elf"
+}
+  
+FVP_MODELS = { 
+    DEVICE_CM0  : { 'cmd': "fvp_mps2_cortex-m0.exe",  'args': { 'limit': "2000000" } },
+    DEVICE_CM3  : { 'cmd': "fvp_mps2_cortex-m3.exe",  'args': { 'limit': "2000000" } },
+    DEVICE_CM4f : { 'cmd': "fvp_mps2_cortex-m4.exe",  'args': { 'limit': "5000000" } },
+    DEVICE_CM7  : { 'cmd': "fvp_mps2_cortex-m7.exe",  'args': { 'limit': "5000000" } },
+    DEVICE_CM23 : { 'cmd': "fvp_mps2_cortex-m23.exe", 'args': { 'limit': "5000000", 'config': "ARMCM23_TZ_config.txt",        'target': "cpu0" } },
+    DEVICE_CM33 : { 'cmd': "fvp_mps2_cortex-m33.exe", 'args': { 'limit': "5000000", 'config': "ARMCM33_DSP_FP_TZ_config.txt", 'target': "cpu0" } }
+  }
+
 def isSkipped(dev, cc, target):
   for skip in SKIP:
     skipDev = (skip[0] == None or skip[0] == dev)
@@ -42,95 +56,67 @@
       return True
   return False
 
-def ret2result(ret):
-  if ret == 0:
-    return "successfull"
-  elif ret == 1:
-    return "successfull with warnings"
-  else:
-    return "failed!"
+def prepare(steps, args):
+  for dev in args.devices:
+    for cc in args.compilers:
+      for target in args.targets:
+        if not isSkipped(dev, cc, target):
+          config = "{dev} ({cc}, {target})".format(dev = dev, cc = cc, target = target)
+          prefix = "{dev}_{cc}_{target}".format(dev = dev, cc = cc, target = target)
+          if args.execute_only:
+            build = None
+          else:
+            build = Uv4Cmd("CMSIS_CV.uvprojx", config)
+          if args.build_only:
+            test = None
+          else:
+            test = FvpCmd(FVP_MODELS[dev]['cmd'], "Objects\CMSIS_CV."+APP_FORMAT[cc], **FVP_MODELS[dev]['args'])
+          steps += [ { 'name': config, 'prefix': prefix, 'build': build, 'test': test } ]
 
-def binary(cc):
-  if cc == CC_GCC:
-    return 'Objects/CMSIS_CV.elf'
-  else:
-    return 'Objects/CMSIS_CV.axf'
-    
-def build(dev, cc, target):
-  print "Building..."
-  config = "{dev} ({cc}, {target})".format(dev = dev, cc = cc, target = target)
-  log = "build_{dev}_{cc}_{target}.log".format(dev = dev, cc = cc, target = target)
-  print "{cmd} -t {config} -r {prj} -j0 -o {log}".format(cmd = UV4, config = config, prj = PRJ, log = log)
-  try:
-    ret = call([UV4, "-t", config, "-r", PRJ, "-j0", "-o", log])
-    print open(log, "r").read()
-    print "Build " + ret2result(ret)
-    return (ret <= 1)
-  except:
-    print "Build failed!"
-    return False
-
-def run(dev, cc, target):
-  print "Running..."
-  config = "{dev} ({cc}, {target})".format(dev = dev, cc = cc, target = target)
-  log = "run_{dev}_{cc}_{target}.log".format(dev = dev, cc = cc, target = target)
-  xml = "result_{dev}_{cc}_{target}.xml".format(dev = dev, cc = cc, target = target)
-  
-  if target == TARGET_FVP:
-    model = FVP_MODELS[dev][:]
-    app = binary(cc)
-    model[-1] = model[-1] + app
-    print ' '.join(model)
-    logfile = open(log, "w")
-    call(model, stdout=logfile)
-    
-  logfile = open(log, "r")
-  xmlfile = open(xml, "w")
-  dump = False
-  for line in logfile:
-    if dump:
-      xmlfile.write(line)
-      if line.strip() == "</report>":
-        dump = False
+def execute(steps):
+  for step in steps:
+    print step['name']
+    if step['build']:
+      step['build'].run()
     else:
-      if line.strip() == "Simulation is started":
-        dump = True
-      print line,
+      print "Skipping build"
+      
+    if (not step['build']) or step['build'].isSuccess():
+      step['test'].run()
+      step['result'] = TestResult(step['test'].getOutput())
+      step['result'].saveXml("result_{0}_{1}.xml".format(step['prefix'], datetime.now().strftime("%Y%m%d%H%M%S")))
+    else:
+      print "Skipping test"
+      
+def printSummary(steps):
+  print ""
+  print "Test Summary"
+  print "============"
+  print
+  print "Test run                       Total Exec  Pass  Fail  "
+  print "-------------------------------------------------------"
+  for step in steps:
+    try:
+      print "{0:30} {1:>4}  {2:>4}  {3:>4}  {4:>4}".format(step['name'], *step['result'].getSummary())
+    except:
+      print "{0:30} ------ NO RESULTS ------".format(step['name'])
+
+def main(argv):
+  parser = ArgumentParser()
+  parser.add_argument('-b', '--build-only', action='store_true')
+  parser.add_argument('-e', '--execute-only', action='store_true')
+  parser.add_argument('-d', '--devices', nargs='*', choices=DEVICES, default=DEVICES, help = 'Devices to be considered.')
+  parser.add_argument('-c', '--compilers', nargs='*', choices=COMPILERS, default=COMPILERS, help = 'Compilers to be considered.')
+  parser.add_argument('-t', '--targets', nargs='*', choices=TARGETS, default=TARGETS, help = 'Targets to be considered.')
+  args = parser.parse_args()
+    
+  steps = []
+
+  prepare(steps, args)
   
-  return True
-
-for dev in DEVICES:
-  for cc in COMPILERS:
-    for target in TARGETS:
-      if not isSkipped(dev, cc, target):
-        print ""
-        print "{dev} with {cc} on {target}".format(dev = dev, cc = cc, target = target)
-        success = build(dev, cc, target)
-        if success:
-          run(dev, cc, target)
-
-# Test Summary 
-print ""
-print "Test Summary"
-print "============"
-print
-print "Test run                       Total Exec  Pass  Fail  "
-print "-------------------------------------------------------"
-for dev in DEVICES:
-  for cc in COMPILERS:
-    for target in TARGETS:
-      name = "{dev} ({cc}, {target})".format(dev = dev, cc = cc, target = target)
-      if isSkipped(dev, cc, target):
-        print "{0:30} ------- skipped --------".format(name)
-      else:
-        try:
-          xml = "result_{dev}_{cc}_{target}.xml".format(dev = dev, cc = cc, target = target)
-          report = ElementTree.parse(xml).getroot()
-          summary = report[0].findall('summary')[0]
-          tests = summary.find('tcnt').text
-          executed = summary.find('exec').text
-          passed = summary.find('pass').text
-          failed = summary.find('fail').text
-          print "{0:30} {1:>4}  {2:>4}  {3:>4}  {4:>4}".format(name, tests, executed, passed, failed)
-        except:
-          print "{0:30} ------ NO RESULTS ------".format(name)
+  execute(steps)
+  
+  printSummary(steps)
+  
+if __name__ == "__main__":
+  main(sys.argv[1:])
\ No newline at end of file
diff --git a/CMSIS/Utilities/buildutils/.gitignore b/CMSIS/Utilities/buildutils/.gitignore
new file mode 100644
index 0000000..7e99e36
--- /dev/null
+++ b/CMSIS/Utilities/buildutils/.gitignore
@@ -0,0 +1 @@
+*.pyc
\ No newline at end of file
diff --git a/CMSIS/Utilities/buildutils/__init__.py b/CMSIS/Utilities/buildutils/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/CMSIS/Utilities/buildutils/__init__.py
diff --git a/CMSIS/Utilities/buildutils/buildcmd.py b/CMSIS/Utilities/buildutils/buildcmd.py
new file mode 100644
index 0000000..ceabe64
--- /dev/null
+++ b/CMSIS/Utilities/buildutils/buildcmd.py
@@ -0,0 +1,43 @@
+#! python
+
+from subprocess import call, Popen
+from tempfile import TemporaryFile
+
+class BuildCmd:
+  def __init__(self):
+    self._result = -1
+    self._output = TemporaryFile(mode="r+")
+  
+  def getCommand(self):
+    raise NotImplementedError
+    
+  def getArguments(self):
+    return []
+    
+  def getOutput(self):
+    return self._output
+
+  def getLog(self):
+    return None
+    
+  def isSuccess(self):
+    return self._output == 0
+
+  def run(self):  
+    cmd = [ self.getCommand() ] + self.getArguments()
+    print "Running: " + ' '.join(cmd)
+    try:
+      self._result = call(cmd, stdout = self._output)
+    except:
+      print "Fatal error!"
+    self._output.seek(0)
+    print self._output.read()
+    
+    logfile = self.getLog()
+    if logfile != None:
+      print logfile.read()
+      
+    print "Command returned: {0}".format(self._result)
+      
+    return self._result
+    
\ No newline at end of file
diff --git a/CMSIS/Utilities/buildutils/dircmd.py b/CMSIS/Utilities/buildutils/dircmd.py
new file mode 100644
index 0000000..5a0f211
--- /dev/null
+++ b/CMSIS/Utilities/buildutils/dircmd.py
@@ -0,0 +1,13 @@
+#! python
+
+from buildcmd import BuildCmd
+
+class DirCmd(BuildCmd):
+
+  def __init__(self):
+    BuildCmd.__init__(self)
+    
+  def getCommand(self):
+    return "dir"
+
+    
\ No newline at end of file
diff --git a/CMSIS/Utilities/buildutils/fvpcmd.py b/CMSIS/Utilities/buildutils/fvpcmd.py
new file mode 100644
index 0000000..d3a4d94
--- /dev/null
+++ b/CMSIS/Utilities/buildutils/fvpcmd.py
@@ -0,0 +1,25 @@
+#! python
+
+from buildcmd import BuildCmd
+
+class FvpCmd(BuildCmd):
+
+  def __init__(self, model, app, **args):
+    BuildCmd.__init__(self)
+    self._model = model
+    self._app = app
+    self._args = args
+    
+  def getCommand(self):
+    return self._model
+    
+  def getArguments(self):
+    args = []
+    if self._args.has_key('limit'):  args += [ "--cyclelimit", self._args['limit'] ] 
+    if self._args.has_key('config'): args += [ "-f", self._args['config'] ]
+    if self._args.has_key('target'):
+      args += [ "-a", "{0}={1}".format(self._args['target'], self._app ) ]
+    else:
+      args += [ self._app ]
+    return args
+    
\ No newline at end of file
diff --git a/CMSIS/Utilities/buildutils/testresult.py b/CMSIS/Utilities/buildutils/testresult.py
new file mode 100644
index 0000000..6e24d6d
--- /dev/null
+++ b/CMSIS/Utilities/buildutils/testresult.py
@@ -0,0 +1,40 @@
+#! python
+
+import shutil
+from StringIO import StringIO
+from xml.etree import ElementTree
+
+class TestResult:
+
+  def _extractXml(self, log, xml):
+    dump = False
+    log.seek(0)
+    for line in log:
+      if dump:
+        xml.write(line)
+        if line.strip() == '</report>':
+          dump = False
+      else:
+        if line.strip() == '<?xml version="1.0"?>':
+          dump = True
+          xml.write(line)
+
+  def __init__(self, log):
+    self._xml = StringIO()
+    self._extractXml(log, self._xml)
+    self._xml.seek(0)
+  
+    report = ElementTree.parse(self._xml).getroot()
+    summary = report[0].findall('summary')[0]
+    self._tests = summary.find('tcnt').text
+    self._executed = summary.find('exec').text
+    self._passed = summary.find('pass').text
+    self._failed = summary.find('fail').text
+          
+  def saveXml(self, filename):
+    with open(filename, "w") as file:
+      self._xml.seek(0)
+      shutil.copyfileobj(self._xml, file)
+    
+  def getSummary(self):
+    return self._tests, self._executed, self._passed, self._failed
\ No newline at end of file
diff --git a/CMSIS/Utilities/buildutils/uv4cmd.py b/CMSIS/Utilities/buildutils/uv4cmd.py
new file mode 100644
index 0000000..8c7f932
--- /dev/null
+++ b/CMSIS/Utilities/buildutils/uv4cmd.py
@@ -0,0 +1,29 @@
+#! python
+
+from buildcmd import BuildCmd
+from string import maketrans
+from datetime import datetime
+import mmap
+
+class Uv4Cmd(BuildCmd):
+
+  def __init__(self, project, config):
+    BuildCmd.__init__(self)
+    self._project = project
+    self._config = config
+    self._log = "UV4_{0}_{1}.log".format(self._config.translate(maketrans(" ", "_"), "()[],"), datetime.now().strftime("%Y%m%d%H%M%S"))
+    
+  def getCommand(self):
+    return "UV4.exe"
+    
+  def getArguments(self):
+    return [ "-t", self._config, "-cr", self._project, "-j0", "-o", self._log ]
+  
+  def isSuccess(self):
+    return self._result <= 1
+
+  def getLog(self):
+    try:
+      return open(self._log, "r")
+    except:
+      return None