1# -*- coding: utf-8 -*- 2 3#------------------------------------------------------------------------- 4# drawElements Quality Program utilities 5# -------------------------------------- 6# 7# Copyright 2016 The Android Open Source Project 8# 9# Licensed under the Apache License, Version 2.0 (the "License"); 10# you may not use this file except in compliance with the License. 11# You may obtain a copy of the License at 12# 13# http://www.apache.org/licenses/LICENSE-2.0 14# 15# Unless required by applicable law or agreed to in writing, software 16# distributed under the License is distributed on an "AS IS" BASIS, 17# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18# See the License for the specific language governing permissions and 19# limitations under the License. 20# 21#------------------------------------------------------------------------- 22 23import sys 24import os 25import xml.etree.cElementTree as ElementTree 26import xml.dom.minidom as minidom 27 28from build_caselists import Module, getModuleByName, getBuildConfig, genCaseList, getCaseListPath, DEFAULT_BUILD_DIR, DEFAULT_TARGET, GLCTS_BIN_NAME 29 30sys.path.append(os.path.join(os.path.dirname(__file__), "..", "..", "..", "scripts")) 31 32from build.common import * 33from build.config import ANY_GENERATOR 34from build.build import build 35from fnmatch import fnmatch 36from copy import copy 37 38GENERATED_FILE_WARNING = """\ 39/* WARNING: This is auto-generated file. Do not modify, since changes will 40 * be lost! Modify the generating script instead. 41 */""" 42 43class Project: 44 def __init__ (self, name, path, incpath, devicepath, copyright = None): 45 self.name = name 46 self.path = path 47 self.incpath = incpath 48 self.devicepath = devicepath 49 self.copyright = copyright 50 51class Configuration: 52 def __init__ (self, name, filters, glconfig = None, rotation = "unspecified", surfacetype = None, surfacewidth = None, surfaceheight = None, baseseed = None, fboconfig = None, required = False, runtime = None, os = "any", skip = "none"): 53 self.name = name 54 self.glconfig = glconfig 55 self.rotation = rotation 56 self.surfacetype = surfacetype 57 self.required = required 58 self.surfacewidth = surfacewidth 59 self.surfaceheight = surfaceheight 60 self.baseseed = baseseed 61 self.fboconfig = fboconfig 62 self.filters = filters 63 self.expectedRuntime = runtime 64 self.os = os 65 self.skipPlatform = skip 66 67class Package: 68 def __init__ (self, module, configurations, useforfirsteglconfig = True): 69 self.module = module 70 self.useforfirsteglconfig = useforfirsteglconfig 71 self.configurations = configurations 72 73class Mustpass: 74 def __init__ (self, project, version, packages, isCurrent): 75 self.project = project 76 self.version = version 77 self.packages = packages 78 self.isCurrent = isCurrent 79 80class Filter: 81 TYPE_INCLUDE = 0 82 TYPE_EXCLUDE = 1 83 84 def __init__ (self, type, filename): 85 self.type = type 86 self.filename = filename 87 88def getSrcDir (mustpass): 89 return os.path.join(mustpass.project.path, mustpass.version, "src") 90 91def getTmpDir (mustpass): 92 return os.path.join(mustpass.project.path, mustpass.version, "tmp") 93 94def getModuleShorthand (module): 95 return module.api.lower() 96 97def getCaseListFileName (package, configuration): 98 return "%s-%s.txt" % (getModuleShorthand(package.module), configuration.name) 99 100def getDstDir(mustpass): 101 return os.path.join(mustpass.project.path, mustpass.version) 102 103def getDstCaseListPath (mustpass, package, configuration): 104 return os.path.join(getDstDir(mustpass), getCaseListFileName(package, configuration)) 105 106def getCommandLine (config): 107 cmdLine = "" 108 109 if config.glconfig != None: 110 cmdLine += "--deqp-gl-config-name=%s " % config.glconfig 111 112 if config.rotation != None: 113 cmdLine += "--deqp-screen-rotation=%s " % config.rotation 114 115 if config.surfacetype != None: 116 cmdLine += "--deqp-surface-type=%s " % config.surfacetype 117 118 if config.surfacewidth != None: 119 cmdLine += "--deqp-surface-width=%s " % config.surfacewidth 120 121 if config.surfaceheight != None: 122 cmdLine += "--deqp-surface-height=%s " % config.surfaceheight 123 124 if config.baseseed != None: 125 cmdLine += "--deqp-base-seed=%s " % config.baseseed 126 127 if config.fboconfig != None: 128 cmdLine += "--deqp-gl-config-name=%s --deqp-surface-type=fbo " % config.fboconfig 129 130 cmdLine += "--deqp-watchdog=disable" 131 132 return cmdLine 133 134def readCaseList (filename): 135 cases = [] 136 with open(filename, 'rt') as f: 137 for line in f: 138 if line[:6] == "TEST: ": 139 cases.append(line[6:].strip()) 140 return cases 141 142def getCaseList (buildCfg, generator, module): 143 return readCaseList(getCaseListPath(buildCfg, module, "txt")) 144 145def readPatternList (filename): 146 ptrns = [] 147 with open(filename, 'rt') as f: 148 for line in f: 149 line = line.strip() 150 if len(line) > 0 and line[0] != '#': 151 ptrns.append(line) 152 return ptrns 153 154def applyPatterns (caseList, patterns, filename, op): 155 matched = set() 156 errors = [] 157 curList = copy(caseList) 158 trivialPtrns = [p for p in patterns if p.find('*') < 0] 159 regularPtrns = [p for p in patterns if p.find('*') >= 0] 160 161 # Apply trivial (just case paths) 162 allCasesSet = set(caseList) 163 for path in trivialPtrns: 164 if path in allCasesSet: 165 if path in matched: 166 errors.append((path, "Same case specified more than once")) 167 matched.add(path) 168 else: 169 errors.append((path, "Test case not found")) 170 171 curList = [c for c in curList if c not in matched] 172 173 for pattern in regularPtrns: 174 matchedThisPtrn = set() 175 176 for case in curList: 177 if fnmatch(case, pattern): 178 matchedThisPtrn.add(case) 179 180 if len(matchedThisPtrn) == 0: 181 errors.append((pattern, "Pattern didn't match any cases")) 182 183 matched = matched | matchedThisPtrn 184 curList = [c for c in curList if c not in matched] 185 186 for pattern, reason in errors: 187 print("ERROR: %s: %s" % (reason, pattern)) 188 189 if len(errors) > 0: 190 die("Found %s invalid patterns while processing file %s" % (len(errors), filename)) 191 192 return [c for c in caseList if op(c in matched)] 193 194def applyInclude (caseList, patterns, filename): 195 return applyPatterns(caseList, patterns, filename, lambda b: b) 196 197def applyExclude (caseList, patterns, filename): 198 return applyPatterns(caseList, patterns, filename, lambda b: not b) 199 200def readPatternLists (mustpass): 201 lists = {} 202 for package in mustpass.packages: 203 for cfg in package.configurations: 204 for filter in cfg.filters: 205 if not filter.filename in lists: 206 lists[filter.filename] = readPatternList(os.path.join(getSrcDir(mustpass), filter.filename)) 207 return lists 208 209def applyFilters (caseList, patternLists, filters): 210 res = copy(caseList) 211 for filter in filters: 212 ptrnList = patternLists[filter.filename] 213 if filter.type == Filter.TYPE_INCLUDE: 214 res = applyInclude(res, ptrnList, filter.filename) 215 else: 216 assert filter.type == Filter.TYPE_EXCLUDE 217 res = applyExclude(res, ptrnList, filter.filename) 218 return res 219 220 221def include (filename): 222 return Filter(Filter.TYPE_INCLUDE, filename) 223 224def exclude (filename): 225 return Filter(Filter.TYPE_EXCLUDE, filename) 226 227def insertXMLHeaders (mustpass, doc): 228 if mustpass.project.copyright != None: 229 doc.insert(0, ElementTree.Comment(mustpass.project.copyright)) 230 doc.insert(1, ElementTree.Comment(GENERATED_FILE_WARNING)) 231 232def prettifyXML (doc): 233 uglyString = ElementTree.tostring(doc, 'utf-8') 234 reparsed = minidom.parseString(uglyString) 235 return reparsed.toprettyxml(indent='\t', encoding='utf-8') 236 237def genSpecXML (mustpass): 238 mustpassElem = ElementTree.Element("Mustpass", version = mustpass.version) 239 insertXMLHeaders(mustpass, mustpassElem) 240 241 packageElem = ElementTree.SubElement(mustpassElem, "TestPackage", name = mustpass.project.name) 242 243 for package in mustpass.packages: 244 for config in package.configurations: 245 configElem = ElementTree.SubElement(packageElem, "Configuration", 246 caseListFile = getCaseListFileName(package, config), 247 commandLine = getCommandLine(config), 248 name = config.name, 249 os = str(config.os), 250 useForFirstEGLConfig = str(package.useforfirsteglconfig) 251 ) 252 253 return mustpassElem 254 255def getIncludeGuardName (headerFile): 256 return '_' + os.path.basename(headerFile).upper().replace('.', '_') 257 258def convertToCamelcase(s): 259 return ''.join(w.capitalize() or '_' for w in s.split('_')) 260 261def getApiType(apiName): 262 if apiName == "GLES2": 263 return "glu::ApiType::es(2, 0)" 264 if apiName == "GLES3": 265 return "glu::ApiType::es(3, 0)" 266 if apiName == "GLES31": 267 return "glu::ApiType::es(3, 1)" 268 if apiName == "GLES32": 269 return "glu::ApiType::es(3, 2)" 270 if apiName == "GL46": 271 return "glu::ApiType::core(4, 6)" 272 if apiName == "GL45": 273 return "glu::ApiType::core(4, 5)" 274 if apiName == "GL44": 275 return "glu::ApiType::core(4, 4)" 276 if apiName == "GL43": 277 return "glu::ApiType::core(4, 3)" 278 if apiName == "GL42": 279 return "glu::ApiType::core(4, 2)" 280 if apiName == "GL41": 281 return "glu::ApiType::core(4, 1)" 282 if apiName == "GL40": 283 return "glu::ApiType::core(4, 0)" 284 if apiName == "GL33": 285 return "glu::ApiType::core(3, 3)" 286 if apiName == "GL32": 287 return "glu::ApiType::core(3, 2)" 288 if apiName == "GL31": 289 return "glu::ApiType::core(3, 1)" 290 if apiName == "GL30": 291 return "glu::ApiType::core(3, 0)" 292 if apiName == "EGL": 293 return "glu::ApiType()" 294 295 raise Exception("Unknown API %s" % apiName) 296 return "Unknown" 297 298def getConfigName(cfgName): 299 if cfgName == None: 300 return "DE_NULL" 301 else: 302 return '"' + cfgName + '"' 303 304def getIntBaseSeed(baseSeed): 305 if baseSeed == None: 306 return "-1" 307 else: 308 return baseSeed 309 310def genSpecCPPIncludeFile (specFilename, mustpass): 311 fileBody = "" 312 313 includeGuard = getIncludeGuardName(specFilename) 314 fileBody += "#ifndef %s\n" % includeGuard 315 fileBody += "#define %s\n" % includeGuard 316 fileBody += mustpass.project.copyright 317 fileBody += "\n\n" 318 fileBody += GENERATED_FILE_WARNING 319 fileBody += "\n\n" 320 fileBody += 'const char* mustpassDir = "' + mustpass.project.devicepath + '/' + mustpass.version + '/";\n\n' 321 322 gtf_wrapper_open = "#if defined(DEQP_GTF_AVAILABLE)\n" 323 gtf_wrapper_close = "#endif // defined(DEQP_GTF_AVAILABLE)\n" 324 android_wrapper_open = "#if DE_OS == DE_OS_ANDROID\n" 325 android_wrapper_close = "#endif // DE_OS == DE_OS_ANDROID\n" 326 skip_x11_wrapper_open = "#ifndef DEQP_SUPPORT_X11\n" 327 skip_x11_wrapper_close = "#endif // DEQP_SUPPORT_X11\n" 328 TABLE_ELEM_PATTERN = "{apiType} {configName} {glConfigName} {screenRotation} {baseSeed} {fboConfig} {surfaceWidth} {surfaceHeight}" 329 330 emitOtherCfgTbl = False 331 firstCfgDecl = "static const RunParams %s_first_cfg[] = " % mustpass.project.name.lower().replace(' ','_') 332 firstCfgTbl = "{\n" 333 334 otherCfgDecl = "static const RunParams %s_other_cfg[] = " % mustpass.project.name.lower().replace(' ','_') 335 otherCfgTbl = "{\n" 336 337 for package in mustpass.packages: 338 for config in package.configurations: 339 pApiType = getApiType(package.module.api) + ',' 340 pConfigName = '"' + config.name + '",' 341 pGLConfig = getConfigName(config.glconfig) + ',' 342 pRotation = '"' + config.rotation + '",' 343 pSeed = getIntBaseSeed(config.baseseed) + ',' 344 pFBOConfig = getConfigName(config.fboconfig) + ',' 345 pWidth = config.surfacewidth + ',' 346 pHeight = config.surfaceheight 347 elemFinal = "" 348 elemContent = TABLE_ELEM_PATTERN.format(apiType = pApiType, configName = pConfigName, glConfigName = pGLConfig, screenRotation = pRotation, baseSeed = pSeed, fboConfig = pFBOConfig, surfaceWidth = pWidth, surfaceHeight = pHeight) 349 elem = "\t{ " + elemContent + " },\n" 350 if package.module.name[:3] == "GTF": 351 elemFinal += gtf_wrapper_open 352 353 if config.os == "android": 354 elemFinal += android_wrapper_open 355 356 if config.skipPlatform == "x11": 357 elemFinal += skip_x11_wrapper_open 358 359 elemFinal += elem 360 361 if config.skipPlatform == "x11": 362 elemFinal += skip_x11_wrapper_close 363 364 if config.os == "android": 365 elemFinal += android_wrapper_close 366 367 if package.module.name[:3] == "GTF": 368 elemFinal += gtf_wrapper_close 369 370 if package.useforfirsteglconfig == True: 371 firstCfgTbl += elemFinal 372 else: 373 otherCfgTbl += elemFinal 374 emitOtherCfgTbl = True 375 376 firstCfgTbl += "};\n" 377 otherCfgTbl += "};\n" 378 379 fileBody += firstCfgDecl 380 fileBody += firstCfgTbl 381 382 if emitOtherCfgTbl == True: 383 fileBody += "\n" 384 fileBody += otherCfgDecl 385 fileBody += otherCfgTbl 386 387 fileBody += "\n" 388 fileBody += "#endif // %s\n" % includeGuard 389 return fileBody 390 391 392def genSpecCPPIncludes (mustpassLists): 393 for mustpass in mustpassLists: 394 if mustpass.isCurrent == True: 395 specFilename = os.path.join(mustpass.project.incpath, "glc%s.hpp" % convertToCamelcase(mustpass.project.name.lower().replace(' ','_'))) 396 hpp = genSpecCPPIncludeFile(specFilename, mustpass) 397 398 print(" Writing spec: " + specFilename) 399 writeFile(specFilename, hpp) 400 print("Done!") 401 402def genMustpass (mustpass, moduleCaseLists): 403 print("Generating mustpass '%s'" % mustpass.version) 404 405 patternLists = readPatternLists(mustpass) 406 407 for package in mustpass.packages: 408 allCasesInPkg = moduleCaseLists[package.module] 409 410 for config in package.configurations: 411 filtered = applyFilters(allCasesInPkg, patternLists, config.filters) 412 dstFile = getDstCaseListPath(mustpass, package, config) 413 414 print(" Writing deqp caselist: " + dstFile) 415 writeFile(dstFile, "\n".join(filtered) + "\n") 416 417 specXML = genSpecXML(mustpass) 418 specFilename = os.path.join(mustpass.project.path, mustpass.version, "mustpass.xml") 419 420 print(" Writing spec: " + specFilename) 421 writeFile(specFilename, prettifyXML(specXML).decode()) 422 423 print("Done!") 424 425def genMustpassLists (mustpassLists, generator, buildCfg): 426 moduleCaseLists = {} 427 428 # Getting case lists involves invoking build, so we want to cache the results 429 build(buildCfg, generator, [GLCTS_BIN_NAME]) 430 genCaseList(buildCfg, generator, "txt") 431 for mustpass in mustpassLists: 432 for package in mustpass.packages: 433 if not package.module in moduleCaseLists: 434 moduleCaseLists[package.module] = getCaseList(buildCfg, generator, package.module) 435 436 for mustpass in mustpassLists: 437 genMustpass(mustpass, moduleCaseLists) 438 439 440 genSpecCPPIncludes(mustpassLists) 441