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