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 itertools 24import os 25import argparse 26import tempfile 27import sys 28 29from ctsbuild.common import * 30from ctsbuild.build import * 31 32pythonExecutable = sys.executable or "python" 33 34class Environment: 35 def __init__ (self, srcDir, tmpDir, verbose): 36 self.srcDir = srcDir 37 self.tmpDir = tmpDir 38 self.verbose = verbose 39 40class BuildTestStep: 41 def getName (self): 42 return "<unknown>" 43 44 def isAvailable (self, env): 45 return True 46 47 def run (self, env): 48 raise Exception("Not implemented") 49 50class RunScript(BuildTestStep): 51 def __init__ (self, scriptPath, getExtraArgs = None): 52 self.scriptPath = scriptPath 53 self.getExtraArgs = getExtraArgs 54 55 def getName (self): 56 return self.scriptPath 57 58 def run (self, env): 59 args = [pythonExecutable, os.path.join(env.srcDir, self.scriptPath)] 60 61 if self.getExtraArgs != None: 62 args += self.getExtraArgs(env) 63 64 execute(args) 65 66 def __repr__(self): 67 return "RunScript:%s" % (self.scriptPath) 68 69def makeCflagsArgs (cflags): 70 cflagsStr = " ".join(cflags) 71 return ["-DCMAKE_C_FLAGS=%s" % cflagsStr, "-DCMAKE_CXX_FLAGS=%s" % cflagsStr] 72 73def makeBuildArgs (target, cc, cpp, cflags): 74 return ["-DDEQP_TARGET=%s" % target, "-DCMAKE_C_COMPILER=%s" % cc, "-DCMAKE_CXX_COMPILER=%s" % cpp] + makeCflagsArgs(cflags) 75 76class BuildConfigGen: 77 def isAvailable (self, env): 78 return True 79 80class UnixConfig(BuildConfigGen): 81 def __init__ (self, target, buildType, cc, cpp, cflags): 82 self.target = target 83 self.buildType = buildType 84 self.cc = cc 85 self.cpp = cpp 86 self.cflags = cflags 87 88 def isAvailable (self, env): 89 return which(self.cc) != None and which(self.cpp) != None 90 91 def getBuildConfig (self, env, buildDir): 92 args = makeBuildArgs(self.target, self.cc, self.cpp, self.cflags) 93 return BuildConfig(buildDir, self.buildType, args, env.srcDir) 94 95class VSConfig(BuildConfigGen): 96 def __init__ (self, buildType): 97 self.buildType = buildType 98 99 def getBuildConfig (self, env, buildDir): 100 args = ["-DCMAKE_C_FLAGS=/WX -DCMAKE_CXX_FLAGS=/WX"] 101 return BuildConfig(buildDir, self.buildType, args, env.srcDir) 102 103class Build(BuildTestStep): 104 def __init__ (self, buildDir, configGen, generator): 105 self.buildDir = buildDir 106 self.configGen = configGen 107 self.generator = generator 108 109 def getName (self): 110 return self.buildDir 111 112 def isAvailable (self, env): 113 return self.configGen.isAvailable(env) and self.generator != None and self.generator.isAvailable() 114 115 def run (self, env): 116 # specialize config for env 117 buildDir = os.path.join(env.tmpDir, self.buildDir) 118 curConfig = self.configGen.getBuildConfig(env, buildDir) 119 120 build(curConfig, self.generator) 121 122class CheckSrcChanges(BuildTestStep): 123 def getName (self): 124 return "check for changes" 125 126 def run (self, env): 127 pushWorkingDir(env.srcDir) 128 execute(["git", "diff", "--exit-code"]) 129 popWorkingDir() 130 131def getClangVersion (): 132 knownVersions = ["4.0", "3.9", "3.8", "3.7", "3.6", "3.5"] 133 for version in knownVersions: 134 if which("clang-" + version) != None: 135 return "-" + version 136 return "" 137 138def runSteps (steps): 139 for step in steps: 140 if step.isAvailable(env): 141 print("Run: %s" % step.getName()) 142 step.run(env) 143 else: 144 print("Skip: %s" % step.getName()) 145 146COMMON_CFLAGS = ["-Werror", "-Wno-error=unused-function"] 147COMMON_GCC_CFLAGS = COMMON_CFLAGS + ["-Wno-error=array-bounds", "-Wno-error=address", "-Wno-error=nonnull"] 148COMMON_CLANG_CFLAGS = COMMON_CFLAGS + ["-Wno-error=unused-command-line-argument"] 149GCC_32BIT_CFLAGS = COMMON_GCC_CFLAGS + ["-m32"] 150CLANG_32BIT_CFLAGS = COMMON_CLANG_CFLAGS + ["-m32"] 151GCC_64BIT_CFLAGS = COMMON_GCC_CFLAGS + ["-m64"] 152CLANG_64BIT_CFLAGS = COMMON_CLANG_CFLAGS + ["-m64"] 153CLANG_VERSION = getClangVersion() 154 155# Always ran before any receipe 156PREREQUISITES = [ 157 RunScript(os.path.join("external", "fetch_sources.py"), lambda env: ["--force"] + (["--verbose"] if env.verbose else [])) 158] 159 160# Always ran after any receipe 161POST_CHECKS = [ 162 CheckSrcChanges() 163] 164 165# Optional step to clean up external resources after finishing receipe 166POST_CLEANUP = [ 167 RunScript(os.path.join("external", "fetch_sources.py"), lambda env: ["--clean"]) 168] 169 170BUILD_TARGETS = [ 171 Build("clang-64-debug", 172 UnixConfig("null", 173 "Debug", 174 "clang" + CLANG_VERSION, 175 "clang++" + CLANG_VERSION, 176 CLANG_64BIT_CFLAGS), 177 ANY_UNIX_GENERATOR), 178 Build("gcc-32-debug", 179 UnixConfig("null", 180 "Debug", 181 "gcc", 182 "g++", 183 GCC_32BIT_CFLAGS), 184 ANY_UNIX_GENERATOR), 185 Build("gcc-64-release", 186 UnixConfig("null", 187 "Release", 188 "gcc", 189 "g++", 190 GCC_64BIT_CFLAGS), 191 ANY_UNIX_GENERATOR), 192 Build("vs-64-debug", 193 VSConfig("Debug"), 194 ANY_VS_X64_GENERATOR), 195] 196 197EARLY_SPECIAL_RECIPES = [ 198 ('gen-inl-files', [ 199 RunScript(os.path.join("scripts", "gen_egl.py")), 200 RunScript(os.path.join("scripts", "opengl", "gen_all.py")), 201 RunScript(os.path.join("external", "vulkancts", "scripts", "gen_framework.py"), lambda env: (["--verbose"] if env.verbose else []) ), 202 RunScript(os.path.join("external", "vulkancts", "scripts", "gen_framework_c.py"), lambda env: (["--verbose"] if env.verbose else []) ), 203 RunScript(os.path.join("external", "vulkancts", "scripts", "gen_framework.py"), lambda env: ["--api", "SC"] ), 204 RunScript(os.path.join("external", "vulkancts", "scripts", "gen_framework_c.py"), lambda env: ["--api", "SC"] ), 205 RunScript(os.path.join("scripts", "gen_android_bp.py")), 206 RunScript(os.path.join("scripts", "gen_khronos_cts_bp.py")) 207 ]), 208] 209 210LATE_SPECIAL_RECIPES = [ 211 ('android-mustpass', [ 212 RunScript(os.path.join("scripts", "build_android_mustpass.py"), 213 lambda env: ["--build-dir", os.path.join(env.tmpDir, "android-mustpass")] + (["--verbose"] if env.verbose else [])), 214 ]), 215 ('vulkan-mustpass', [ 216 RunScript(os.path.join("external", "vulkancts", "scripts", "build_mustpass.py"), 217 lambda env: ["--build-dir", os.path.join(env.tmpDir, "vulkan-mustpass")] + (["--verbose"] if env.verbose else [])), 218 ]), 219 ('spirv-binaries', [ 220 RunScript(os.path.join("external", "vulkancts", "scripts", "build_spirv_binaries.py"), 221 lambda env: ["--build-type", "Release", 222 "--build-dir", os.path.join(env.tmpDir, "spirv-binaries"), 223 "--dst-path", os.path.join(env.tmpDir, "spirv-binaries")]), 224 ]), 225 ('amber-verify', [ 226 RunScript(os.path.join("external", "vulkancts", "scripts", "amber_verify.py"), 227 lambda env: ["--build-type", "Release", 228 "--build-dir", os.path.join(env.tmpDir, "amber-verify"), 229 "--dst-path", os.path.join(env.tmpDir, "amber-verify")]), 230 ]), 231 ('check-all', [ 232 RunScript(os.path.join("scripts", "src_util", "check_all.py")), 233 ]) 234] 235 236def getBuildRecipes (): 237 return [(b.getName(), [b]) for b in BUILD_TARGETS] 238 239def getAllRecipe (recipes): 240 allSteps = {} 241 for name, steps in recipes: 242 allSteps[name] = steps 243 return allSteps 244 245def getRecipes (): 246 recipes = EARLY_SPECIAL_RECIPES + getBuildRecipes() + LATE_SPECIAL_RECIPES 247 return recipes 248 249def getRecipesByName (recipes, recipeNames): 250 selectedRecipes = {} 251 for recipeName in recipeNames: 252 for curName, steps in recipes: 253 logging.debug("Evaluating %s against %s" % (recipeName, curName)) 254 if curName == recipeName: 255 selectedRecipes[curName] = steps 256 return selectedRecipes 257 258RECIPES = getRecipes() 259 260def parseArgs (): 261 parser = argparse.ArgumentParser(description = "Build and test source", 262 formatter_class=argparse.ArgumentDefaultsHelpFormatter) 263 parser.add_argument("-s", 264 "--src-dir", 265 dest="srcDir", 266 default=DEQP_DIR, 267 help="Source directory") 268 parser.add_argument("-t", 269 "--tmp-dir", 270 dest="tmpDir", 271 default=os.path.join(tempfile.gettempdir(), "deqp-build-test"), 272 help="Temporary directory") 273 parser.add_argument("-r", 274 "--recipe", 275 dest="recipes", 276 nargs='+', 277 choices=[n for n, s in RECIPES] + ["all"], 278 default="all", 279 help="Build / test recipe") 280 parser.add_argument("-d", 281 "--dump-recipes", 282 dest="dumpRecipes", 283 action="store_true", 284 help="Print out recipes that have any available actions") 285 parser.add_argument("--skip-prerequisites", 286 dest="skipPrerequisites", 287 action="store_true", 288 help="Skip external dependency fetch") 289 parser.add_argument("--skip-post-checks", 290 dest="skipPostCheck", 291 action="store_true", 292 help="Skip post recipe checks") 293 parser.add_argument("--apply-post-external-cleanup", 294 dest="applyPostExternalDependencyCleanup", 295 action="store_true", 296 help="skip external dependency clean up") 297 parser.add_argument("-v", "--verbose", 298 dest="verbose", 299 action="store_true", 300 help="Enable verbose logging") 301 302 return parser.parse_args() 303 304if __name__ == "__main__": 305 args = parseArgs() 306 env = Environment(args.srcDir, args.tmpDir, args.verbose) 307 initializeLogger(args.verbose) 308 309 if args.dumpRecipes: 310 for name, steps in RECIPES: 311 for step in steps: 312 if step.isAvailable(env): 313 print(name) 314 break 315 else: 316 selectedRecipes = getAllRecipe(RECIPES) if args.recipes == "all" \ 317 else getRecipesByName(RECIPES, args.recipes) 318 319 print("Running %s" % ','.join(selectedRecipes.keys())) 320 selectedSteps = list(itertools.chain.from_iterable(selectedRecipes.values())) 321 allSteps = (PREREQUISITES if (args.skipPrerequisites == False) else []) + selectedSteps + (POST_CHECKS if (args.skipPostCheck == False) else []) + (POST_CLEANUP if (args.applyPostExternalDependencyCleanup == True) else []) 322 runSteps(allSteps) 323 324 print("All steps completed successfully") 325