xref: /aosp_15_r20/external/deqp/scripts/check_build_sanity.py (revision 35238bce31c2a825756842865a792f8cf7f89930)
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