mirror of
https://github.com/qgis/QGIS.git
synced 2025-03-07 00:02:15 -05:00
433 lines
16 KiB
Python
433 lines
16 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
"""
|
|
***************************************************************************
|
|
OTBTester.py
|
|
---------------------
|
|
Copyright : (C) 2013 by CS Systemes d'information (CS SI)
|
|
Email : otb at c-s dot fr (CS SI)
|
|
Contributors : Julien Malik (CS SI)
|
|
Oscar Picas (CS SI)
|
|
***************************************************************************
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
***************************************************************************
|
|
"""
|
|
__author__ = 'Julien Malik, Oscar Picas'
|
|
__copyright__ = '(C) 2013, CS Systemes d\'information (CS SI)'
|
|
# This will get replaced with a git SHA1 when you do a git archive
|
|
__revision__ = '$Format:%H$'
|
|
|
|
from parsing import parse
|
|
|
|
from string import Template
|
|
import os
|
|
import traceback
|
|
|
|
from ConfigParser import SafeConfigParser
|
|
|
|
from processing.otb.OTBHelper import get_OTB_log
|
|
|
|
|
|
class LowerTemplate(Template):
|
|
|
|
def safe_substitute(self, param):
|
|
ret = super(LowerTemplate, self).safe_substitute(param).lower()
|
|
return ret
|
|
|
|
|
|
class MakefileParser(object):
|
|
|
|
def __init__(self):
|
|
self.maxDiff = None
|
|
self.parser = SafeConfigParser()
|
|
self.parser.read('otbcfg.ini')
|
|
if not os.path.exists('otbcfg.ini'):
|
|
raise Exception("OTB_SOURCE_DIR and OTB_BINARY_DIR must be specified in the file otbcfg.ini")
|
|
|
|
self.root_dir = self.parser.get('otb', 'checkout_dir')
|
|
if not os.path.exists(self.root_dir):
|
|
raise Exception("Check otbcfg.ini : OTB_SOURCE_DIR and OTB_BINARY_DIR must be specified there")
|
|
self.build_dir = self.parser.get('otb', 'build_dir')
|
|
if not os.path.exists(self.build_dir):
|
|
raise Exception("Check otbcfg.ini : OTB_SOURCE_DIR and OTB_BINARY_DIR must be specified there")
|
|
self.logger = get_OTB_log()
|
|
|
|
def test_CMakelists(self):
|
|
provided = {}
|
|
provided["OTB_SOURCE_DIR"] = self.root_dir
|
|
provided["OTB_BINARY_DIR"] = self.build_dir
|
|
provided["OTB_DATA_LARGEINPUT_ROOT"] = os.path.normpath(os.path.join(self.root_dir, "../OTB-Data/Input"))
|
|
|
|
try:
|
|
with open(os.path.join(self.root_dir, "CMakeLists.txt")) as file_input:
|
|
content = file_input.read()
|
|
output = parse(content)
|
|
|
|
defined_paths = [each for each in output if 'Command' in unicode(type(each)) and "FIND_PATH" in each.name]
|
|
the_paths = {key.body[0].contents: [thing.contents for thing in key.body[1:]] for key in defined_paths}
|
|
|
|
the_sets = [each for each in output if 'Command' in unicode(type(each)) and "SET" in each.name.upper()]
|
|
the_sets = {key.body[0].contents: [thing.contents for thing in key.body[1:]] for key in the_sets}
|
|
the_sets = {key: " ".join(the_sets[key]) for key in the_sets}
|
|
|
|
the_strings = set([each.body[-1].contents for each in output if 'Command' in unicode(type(each)) and "STRING" in each.name.upper()])
|
|
|
|
def mini_clean(item):
|
|
if item.startswith('"') and item.endswith('"') and " " not in item:
|
|
return item[1:-1]
|
|
return item
|
|
|
|
the_sets = {key: mini_clean(the_sets[key]) for key in the_sets}
|
|
|
|
def templatize(item):
|
|
if "$" in item:
|
|
return Template(item)
|
|
return item
|
|
|
|
for key in the_sets:
|
|
if key in the_strings:
|
|
the_sets[key] = the_sets[key].lower()
|
|
|
|
the_sets = {key: templatize(the_sets[key]) for key in the_sets}
|
|
|
|
for path in the_paths:
|
|
target_file = the_paths[path][1]
|
|
suggested_paths = []
|
|
if len(the_paths[path]) > 2:
|
|
suggested_paths = the_paths[path][2:]
|
|
|
|
try:
|
|
provided[path] = find_file(target_file)
|
|
except Exception as e:
|
|
for each in suggested_paths:
|
|
st = Template(each)
|
|
pac = os.path.abspath(st.safe_substitute(provided))
|
|
if os.path.exists(pac):
|
|
provided[path] = pac
|
|
break
|
|
|
|
resolve_dict(provided, the_sets)
|
|
provided.update(the_sets)
|
|
|
|
return provided
|
|
except Exception as e:
|
|
traceback.print_exc()
|
|
self.fail(e.message)
|
|
|
|
def add_make(self, previous_context, new_file):
|
|
input = open(new_file).read()
|
|
output = parse(input)
|
|
apps = [each for each in output if 'Command' in unicode(type(each))]
|
|
setcommands = [each for each in apps if 'SET' in each.name.upper()]
|
|
stringcommands = [each for each in apps if 'STRING' in each.name.upper()]
|
|
|
|
environment = previous_context
|
|
|
|
def mini_clean(item):
|
|
if item.startswith('"') and item.endswith('"') and " " not in item:
|
|
return item[1:-1]
|
|
return item
|
|
|
|
new_env = {}
|
|
for command in setcommands:
|
|
key = command.body[0].contents
|
|
ct = " ".join([item.contents for item in command.body[1:]])
|
|
ct = mini_clean(ct)
|
|
|
|
if "$" in ct:
|
|
values = Template(ct)
|
|
else:
|
|
values = ct
|
|
|
|
new_env[key] = values
|
|
|
|
for stringcommand in stringcommands:
|
|
key = stringcommand.body[-1].contents
|
|
ct = stringcommand.body[-2].contents
|
|
ct = mini_clean(ct.lower())
|
|
|
|
if "$" in ct:
|
|
values = LowerTemplate(ct)
|
|
else:
|
|
values = ct
|
|
new_env[key] = values
|
|
|
|
resolve_dict(environment, new_env)
|
|
environment.update(new_env)
|
|
|
|
return environment
|
|
|
|
def get_apps(self, the_makefile, the_dict):
|
|
input = open(the_makefile).read()
|
|
output = parse(input)
|
|
apps = [each for each in output if 'Command' in unicode(type(each))]
|
|
otb_apps = [each for each in apps if 'OTB_TEST_APPLICATION' in each.name.upper()]
|
|
return otb_apps
|
|
|
|
def get_tests(self, the_makefile, the_dict):
|
|
input = open(the_makefile).read()
|
|
output = parse(input)
|
|
apps = [each for each in output if 'Command' in unicode(type(each))]
|
|
otb_tests = [each for each in apps if 'ADD_TEST' in each.name.upper()]
|
|
return otb_tests
|
|
|
|
def get_apps_with_context(self, the_makefile, the_dict):
|
|
input = open(the_makefile).read()
|
|
output = parse(input)
|
|
|
|
def is_a_command(item):
|
|
return 'Command' in unicode(type(item))
|
|
|
|
appz = []
|
|
context = []
|
|
for each in output:
|
|
if is_a_command(each):
|
|
if 'FOREACH' in each.name and 'ENDFOREACH' not in each.name:
|
|
args = [item.contents for item in each.body]
|
|
context.append(args)
|
|
elif 'ENDFOREACH' in each.name:
|
|
context.pop()
|
|
elif 'OTB_TEST_APPLICATION' in each.name.upper():
|
|
appz.append((each, context[:]))
|
|
return appz
|
|
|
|
def get_name_line(self, the_list, the_dict):
|
|
items = ('NAME', 'APP', 'OPTIONS', 'TESTENVOPTIONS', 'VALID')
|
|
itemz = [[], [], [], [], []]
|
|
last_index = 0
|
|
for each in the_list:
|
|
if each.contents in items:
|
|
last_index = items.index(each.contents)
|
|
else:
|
|
itemz[last_index].append(each.contents)
|
|
result = itemz[0][0]
|
|
the_string = Template(result).safe_substitute(the_dict)
|
|
|
|
if '$' in the_string:
|
|
neo_dict = the_dict
|
|
the_string = Template(the_string).safe_substitute(neo_dict)
|
|
while '$' in the_string:
|
|
try:
|
|
the_string = Template(the_string).substitute(neo_dict)
|
|
except KeyError as e:
|
|
self.logger.warning("Key %s is not found in makefiles" % e.message)
|
|
neo_dict[e.message] = ""
|
|
|
|
if 'string.Template' in the_string:
|
|
raise Exception("Unexpected toString call in %s" % the_string)
|
|
|
|
return the_string
|
|
|
|
def get_command_line(self, the_list, the_dict):
|
|
items = ('NAME', 'APP', 'OPTIONS', 'TESTENVOPTIONS', 'VALID')
|
|
itemz = [[], [], [], [], []]
|
|
last_index = 0
|
|
for each in the_list:
|
|
if each.contents in items:
|
|
last_index = items.index(each.contents)
|
|
else:
|
|
itemz[last_index].append(each.contents)
|
|
result = []
|
|
result.extend(["otbcli_%s" % each for each in itemz[1]])
|
|
|
|
if len(result[0]) == 7:
|
|
raise Exception("App name is empty !")
|
|
|
|
result.extend(itemz[2])
|
|
result.append("-testenv")
|
|
result.extend(itemz[3])
|
|
the_string = Template(" ".join(result)).safe_substitute(the_dict)
|
|
|
|
if '$' in the_string:
|
|
neo_dict = the_dict
|
|
the_string = Template(" ".join(result)).safe_substitute(neo_dict)
|
|
while '$' in the_string:
|
|
try:
|
|
the_string = Template(the_string).substitute(neo_dict)
|
|
except KeyError as e:
|
|
self.logger.warning("Key %s is not found in makefiles" % e.message)
|
|
neo_dict[e.message] = ""
|
|
|
|
if 'string.Template' in the_string:
|
|
raise Exception("Unexpected toString call in %s" % the_string)
|
|
|
|
return the_string
|
|
|
|
def get_test(self, the_list, the_dict):
|
|
items = ('NAME', 'APP', 'OPTIONS', 'TESTENVOPTIONS', 'VALID')
|
|
itemz = [[], [], [], [], []]
|
|
last_index = 0
|
|
for each in the_list:
|
|
if each.contents in items:
|
|
last_index = items.index(each.contents)
|
|
else:
|
|
itemz[last_index].append(each.contents)
|
|
result = ["otbTestDriver"]
|
|
result.extend(itemz[4])
|
|
|
|
if len(result) == 1:
|
|
return ""
|
|
|
|
the_string = Template(" ".join(result)).safe_substitute(the_dict)
|
|
|
|
if '$' in the_string:
|
|
neo_dict = the_dict
|
|
the_string = Template(" ".join(result)).safe_substitute(neo_dict)
|
|
while '$' in the_string:
|
|
try:
|
|
the_string = Template(the_string).substitute(neo_dict)
|
|
except KeyError as e:
|
|
self.logger.warning("Key %s is not found in makefiles" % e.message)
|
|
neo_dict[e.message] = ""
|
|
|
|
if 'string.Template' in the_string:
|
|
raise Exception("Unexpected toString call in %s" % the_string)
|
|
|
|
return the_string
|
|
|
|
def test_algos(self):
|
|
tests = {}
|
|
|
|
algos_dir = os.path.join(self.root_dir, "Testing/Applications")
|
|
makefiles = find_files("CMakeLists.txt", algos_dir)
|
|
to_be_excluded = os.path.join(self.root_dir, "Testing/Applications/CMakeLists.txt")
|
|
if to_be_excluded in makefiles:
|
|
makefiles.remove(to_be_excluded)
|
|
|
|
resolve_algos = {}
|
|
for makefile in makefiles:
|
|
intermediate_makefiles = []
|
|
path = makefile.split(os.sep)[len(self.root_dir.split(os.sep)):-1]
|
|
for ind in range(len(path)):
|
|
tmp_path = path[:ind + 1]
|
|
tmp_path.append("CMakeLists.txt")
|
|
tmp_path = os.sep.join(tmp_path)
|
|
candidate_makefile = os.path.join(self.root_dir, tmp_path)
|
|
if os.path.exists(candidate_makefile):
|
|
intermediate_makefiles.append(candidate_makefile)
|
|
resolve_algos[makefile] = intermediate_makefiles
|
|
|
|
dict_for_algo = {}
|
|
for makefile in makefiles:
|
|
basic = self.test_CMakelists()
|
|
last_context = self.add_make(basic, os.path.join(self.root_dir, "Testing/Utilities/CMakeLists.txt"))
|
|
for intermediate_makefile in resolve_algos[makefile]:
|
|
last_context = self.add_make(last_context, intermediate_makefile)
|
|
dict_for_algo[makefile] = last_context
|
|
|
|
for makefile in makefiles:
|
|
appz = self.get_apps_with_context(makefile, dict_for_algo[makefile])
|
|
|
|
for app, context in appz:
|
|
if len(context) == 0:
|
|
import copy
|
|
ddi = copy.deepcopy(dict_for_algo[makefile])
|
|
tk_dict = autoresolve(ddi)
|
|
tk_dict = autoresolve(tk_dict)
|
|
|
|
name_line = self.get_name_line(app.body, tk_dict)
|
|
command_line = self.get_command_line(app.body, tk_dict)
|
|
test_line = self.get_test(app.body, tk_dict)
|
|
|
|
if '$' in test_line or '$' in command_line:
|
|
if '$' in command_line:
|
|
self.logger.error(command_line)
|
|
if '$' in test_line:
|
|
self.logger.warning(test_line)
|
|
else:
|
|
tests[name_line] = (command_line, test_line)
|
|
else:
|
|
contexts = {}
|
|
for iteration in context:
|
|
key = iteration[0]
|
|
values = [each[1:-1].lower() for each in iteration[1:]]
|
|
contexts[key] = values
|
|
|
|
keyorder = contexts.keys()
|
|
import itertools
|
|
pool = [each for each in itertools.product(*contexts.values())]
|
|
|
|
import copy
|
|
for poolinstance in pool:
|
|
neo_dict = copy.deepcopy(dict_for_algo[makefile])
|
|
zipped = zip(keyorder, poolinstance)
|
|
for each in zipped:
|
|
neo_dict[each[0]] = each[1]
|
|
|
|
ak_dict = autoresolve(neo_dict)
|
|
ak_dict = autoresolve(ak_dict)
|
|
ak_dict = autoresolve(ak_dict)
|
|
|
|
ddi = ak_dict
|
|
|
|
name_line = self.get_name_line(app.body, ddi)
|
|
command_line = self.get_command_line(app.body, ddi)
|
|
test_line = self.get_test(app.body, ddi)
|
|
|
|
if '$' in command_line or '$' not in test_line:
|
|
if '$' in command_line:
|
|
self.logger.error(command_line)
|
|
if '$' in test_line:
|
|
self.logger.warning(test_line)
|
|
else:
|
|
tests[name_line] = (command_line, test_line)
|
|
|
|
return tests
|
|
|
|
|
|
def autoresolve(a_dict):
|
|
def as_template(item, b_dict):
|
|
if hasattr(item, 'safe_substitute'):
|
|
return item.safe_substitute(b_dict)
|
|
ate = Template(item)
|
|
return ate.safe_substitute(b_dict)
|
|
templatized = {key: as_template(a_dict[key], a_dict) for key in a_dict.keys()}
|
|
return templatized
|
|
|
|
|
|
def find_file(file_name, base_dir=os.curdir):
|
|
import os
|
|
for root, dirs, files in os.walk(base_dir, topdown=False):
|
|
for name in files:
|
|
if name == file_name:
|
|
return os.path.join(root, name)
|
|
raise Exception("File not found %s" % file_name)
|
|
|
|
|
|
def find_files(file_name, base_dir=os.curdir):
|
|
import os
|
|
result = []
|
|
for root, dirs, files in os.walk(base_dir, topdown=False):
|
|
for name in files:
|
|
if name == file_name:
|
|
result.append(os.path.join(root, name))
|
|
return result
|
|
|
|
|
|
def resolve_dict(adia, adib):
|
|
init = len(adia)
|
|
fin = len(adia) + 1
|
|
|
|
def _resolve_dict(dia, dib):
|
|
for key in dib:
|
|
cand_value = dib[key]
|
|
if hasattr(cand_value, 'safe_substitute'):
|
|
value = cand_value.safe_substitute(dia)
|
|
if isinstance(value, str) and "$" not in value:
|
|
dia[key] = value
|
|
else:
|
|
dia[key] = cand_value
|
|
for key in dia:
|
|
if key in dib:
|
|
del dib[key]
|
|
|
|
while(init != fin):
|
|
init = len(adia)
|
|
_resolve_dict(adia, adib)
|
|
fin = len(adia)
|