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 if apiName == "GL42-COMPAT": 295 return "glu::ApiType::compatibility(4, 2)" 296 297 raise Exception("Unknown API %s" % apiName) 298 return "Unknown" 299 300def getConfigName(cfgName): 301 if cfgName == None: 302 return "DE_NULL" 303 else: 304 return '"' + cfgName + '"' 305 306def getIntBaseSeed(baseSeed): 307 if baseSeed == None: 308 return "-1" 309 else: 310 return baseSeed 311 312def genSpecCPPIncludeFile (specFilename, mustpass): 313 fileBody = "" 314 315 includeGuard = getIncludeGuardName(specFilename) 316 fileBody += "#ifndef %s\n" % includeGuard 317 fileBody += "#define %s\n" % includeGuard 318 fileBody += mustpass.project.copyright 319 fileBody += "\n\n" 320 fileBody += GENERATED_FILE_WARNING 321 fileBody += "\n\n" 322 fileBody += 'const char* mustpassDir = "' + mustpass.project.devicepath + '/' + mustpass.version + '/";\n\n' 323 324 gtf_wrapper_open = "#if defined(DEQP_GTF_AVAILABLE)\n" 325 gtf_wrapper_close = "#endif // defined(DEQP_GTF_AVAILABLE)\n" 326 android_wrapper_open = "#if DE_OS == DE_OS_ANDROID\n" 327 android_wrapper_close = "#endif // DE_OS == DE_OS_ANDROID\n" 328 skip_x11_wrapper_open = "#ifndef DEQP_SUPPORT_X11\n" 329 skip_x11_wrapper_close = "#endif // DEQP_SUPPORT_X11\n" 330 TABLE_ELEM_PATTERN = "{apiType} {configName} {glConfigName} {screenRotation} {baseSeed} {fboConfig} {surfaceWidth} {surfaceHeight}" 331 332 emitOtherCfgTbl = False 333 firstCfgDecl = "static const RunParams %s_first_cfg[] = " % mustpass.project.name.lower().replace(' ','_') 334 firstCfgTbl = "{\n" 335 336 otherCfgDecl = "static const RunParams %s_other_cfg[] = " % mustpass.project.name.lower().replace(' ','_') 337 otherCfgTbl = "{\n" 338 339 for package in mustpass.packages: 340 for config in package.configurations: 341 pApiType = getApiType(package.module.api) + ',' 342 pConfigName = '"' + config.name + '",' 343 pGLConfig = getConfigName(config.glconfig) + ',' 344 pRotation = '"' + config.rotation + '",' 345 pSeed = getIntBaseSeed(config.baseseed) + ',' 346 pFBOConfig = getConfigName(config.fboconfig) + ',' 347 pWidth = config.surfacewidth + ',' 348 pHeight = config.surfaceheight 349 elemFinal = "" 350 elemContent = TABLE_ELEM_PATTERN.format(apiType = pApiType, configName = pConfigName, glConfigName = pGLConfig, screenRotation = pRotation, baseSeed = pSeed, fboConfig = pFBOConfig, surfaceWidth = pWidth, surfaceHeight = pHeight) 351 elem = "\t{ " + elemContent + " },\n" 352 if package.module.name[:3] == "GTF": 353 elemFinal += gtf_wrapper_open 354 355 if config.os == "android": 356 elemFinal += android_wrapper_open 357 358 if config.skipPlatform == "x11": 359 elemFinal += skip_x11_wrapper_open 360 361 elemFinal += elem 362 363 if config.skipPlatform == "x11": 364 elemFinal += skip_x11_wrapper_close 365 366 if config.os == "android": 367 elemFinal += android_wrapper_close 368 369 if package.module.name[:3] == "GTF": 370 elemFinal += gtf_wrapper_close 371 372 if package.useforfirsteglconfig == True: 373 firstCfgTbl += elemFinal 374 else: 375 otherCfgTbl += elemFinal 376 emitOtherCfgTbl = True 377 378 firstCfgTbl += "};\n" 379 otherCfgTbl += "};\n" 380 381 fileBody += firstCfgDecl 382 fileBody += firstCfgTbl 383 384 if emitOtherCfgTbl == True: 385 fileBody += "\n" 386 fileBody += otherCfgDecl 387 fileBody += otherCfgTbl 388 389 fileBody += "\n" 390 fileBody += "#endif // %s\n" % includeGuard 391 return fileBody 392 393 394def genSpecCPPIncludes (mustpassLists): 395 for mustpass in mustpassLists: 396 if mustpass.isCurrent == True: 397 specFilename = os.path.join(mustpass.project.incpath, "glc%s.hpp" % convertToCamelcase(mustpass.project.name.lower().replace(' ','_'))) 398 hpp = genSpecCPPIncludeFile(specFilename, mustpass) 399 400 print(" Writing spec: " + specFilename) 401 writeFile(specFilename, hpp) 402 print("Done!") 403 404def genMustpass (mustpass, moduleCaseLists): 405 print("Generating mustpass '%s'" % mustpass.version) 406 407 patternLists = readPatternLists(mustpass) 408 409 for package in mustpass.packages: 410 allCasesInPkg = moduleCaseLists[package.module] 411 412 for config in package.configurations: 413 filtered = applyFilters(allCasesInPkg, patternLists, config.filters) 414 dstFile = getDstCaseListPath(mustpass, package, config) 415 416 print(" Writing deqp caselist: " + dstFile) 417 writeFile(dstFile, "\n".join(filtered) + "\n") 418 419 specXML = genSpecXML(mustpass) 420 specFilename = os.path.join(mustpass.project.path, mustpass.version, "mustpass.xml") 421 422 print(" Writing spec: " + specFilename) 423 writeFile(specFilename, prettifyXML(specXML).decode()) 424 425 print("Done!") 426 427def genMustpassLists (mustpassLists, generator, buildCfg): 428 moduleCaseLists = {} 429 430 # Getting case lists involves invoking build, so we want to cache the results 431 build(buildCfg, generator, [GLCTS_BIN_NAME]) 432 genCaseList(buildCfg, generator, "txt") 433 for mustpass in mustpassLists: 434 for package in mustpass.packages: 435 if not package.module in moduleCaseLists: 436 moduleCaseLists[package.module] = getCaseList(buildCfg, generator, package.module) 437 438 for mustpass in mustpassLists: 439 genMustpass(mustpass, moduleCaseLists) 440 441 442 genSpecCPPIncludes(mustpassLists) 443