--- /dev/null
+# -*- coding: utf-8 -*-
+
+import os
+import re
+import sys
+import copy
+import zlib
+import time
+import shlex
+import shutil
+import fnmatch
+import tarfile
+import argparse
+import platform
+import datetime
+import tempfile
+import posixpath
+import subprocess
+
+from build.common import *
+from build.config import *
+from build.build import *
+
+def die (msg):
+ print msg
+ sys.exit(-1)
+
+def removeLeadingPath (path, basePath):
+ # Both inputs must be normalized already
+ assert os.path.normpath(path) == path
+ assert os.path.normpath(basePath) == basePath
+ return path[len(basePath) + 1:]
+
+def findFile (candidates):
+ for file in candidates:
+ if os.path.exists(file):
+ return file
+ return None
+
+def getFileList (basePath):
+ allFiles = []
+ basePath = os.path.normpath(basePath)
+ for root, dirs, files in os.walk(basePath):
+ for file in files:
+ relPath = removeLeadingPath(os.path.normpath(os.path.join(root, file)), basePath)
+ allFiles.append(relPath)
+ return allFiles
+
+def toDatetime (dateTuple):
+ Y, M, D = dateTuple
+ return datetime.datetime(Y, M, D)
+
+class PackageBuildInfo:
+ def __init__ (self, releaseConfig, srcBasePath, dstBasePath, tmpBasePath):
+ self.releaseConfig = releaseConfig
+ self.srcBasePath = srcBasePath
+ self.dstBasePath = dstBasePath
+ self.tmpBasePath = tmpBasePath
+
+ def getReleaseConfig (self):
+ return self.releaseConfig
+
+ def getReleaseVersion (self):
+ return self.releaseConfig.getVersion()
+
+ def getReleaseId (self):
+ # Release id is crc32(releaseConfig + release)
+ return zlib.crc32(self.releaseConfig.getName() + self.releaseConfig.getVersion()) & 0xffffffff
+
+ def getSrcBasePath (self):
+ return self.srcBasePath
+
+ def getTmpBasePath (self):
+ return self.tmpBasePath
+
+class DstFile (object):
+ def __init__ (self, dstFile):
+ self.dstFile = dstFile
+
+ def makeDir (self):
+ dirName = os.path.dirname(self.dstFile)
+ if not os.path.exists(dirName):
+ os.makedirs(dirName)
+
+ def make (self, packageBuildInfo):
+ assert False # Should not be called
+
+class CopyFile (DstFile):
+ def __init__ (self, srcFile, dstFile):
+ super(CopyFile, self).__init__(dstFile)
+ self.srcFile = srcFile
+
+ def make (self, packageBuildInfo):
+ self.makeDir()
+ if os.path.exists(self.dstFile):
+ die("%s already exists" % self.dstFile)
+ shutil.copyfile(self.srcFile, self.dstFile)
+
+class GenInfoFile (DstFile):
+ def __init__ (self, srcFile, dstFile):
+ super(GenInfoFile, self).__init__(dstFile)
+ self.srcFile = srcFile
+
+ def make (self, packageBuildInfo):
+ self.makeDir()
+ print " GenInfoFile: %s" % removeLeadingPath(self.dstFile, packageBuildInfo.dstBasePath)
+ src = readFile(self.srcFile)
+
+ for define, value in [("DEQP_RELEASE_NAME", "\"%s\"" % packageBuildInfo.getReleaseVersion()),
+ ("DEQP_RELEASE_ID", "0x%08x" % packageBuildInfo.getReleaseId())]:
+ src = re.sub('(#\s*define\s+%s\s+)[^\n\r]+' % re.escape(define), r'\1 %s' % value, src)
+
+ writeFile(self.dstFile, src)
+
+class GenCMake (DstFile):
+ def __init__ (self, srcFile, dstFile, replaceVars):
+ super(GenCMake, self).__init__(dstFile)
+ self.srcFile = srcFile
+ self.replaceVars = replaceVars
+
+ def make (self, packageBuildInfo):
+ self.makeDir()
+ print " GenCMake: %s" % removeLeadingPath(self.dstFile, packageBuildInfo.dstBasePath)
+ src = readFile(self.srcFile)
+ for var, value in self.replaceVars:
+ src = re.sub('set\(%s\s+"[^"]*"' % re.escape(var),
+ 'set(%s "%s"' % (var, value), src)
+ writeFile(self.dstFile, src)
+
+def createFileTargets (srcBasePath, dstBasePath, files, filters):
+ usedFiles = set() # Files that are already included by other filters
+ targets = []
+
+ for isMatch, createFileObj in filters:
+ # Build list of files that match filter
+ matchingFiles = []
+ for file in files:
+ if not file in usedFiles and isMatch(file):
+ matchingFiles.append(file)
+
+ # Build file objects, add to used set
+ for file in matchingFiles:
+ usedFiles.add(file)
+ targets.append(createFileObj(os.path.join(srcBasePath, file), os.path.join(dstBasePath, file)))
+
+ return targets
+
+# Generates multiple file targets based on filters
+class FileTargetGroup:
+ def __init__ (self, srcBasePath, dstBasePath, filters, srcBasePathFunc=PackageBuildInfo.getSrcBasePath):
+ self.srcBasePath = srcBasePath
+ self.dstBasePath = dstBasePath
+ self.filters = filters
+ self.getSrcBasePath = srcBasePathFunc
+
+ def make (self, packageBuildInfo):
+ fullSrcPath = os.path.normpath(os.path.join(self.getSrcBasePath(packageBuildInfo), self.srcBasePath))
+ fullDstPath = os.path.normpath(os.path.join(packageBuildInfo.dstBasePath, self.dstBasePath))
+
+ allFiles = getFileList(fullSrcPath)
+ targets = createFileTargets(fullSrcPath, fullDstPath, allFiles, self.filters)
+
+ # Make all file targets
+ for file in targets:
+ file.make(packageBuildInfo)
+
+# Single file target
+class SingleFileTarget:
+ def __init__ (self, srcFile, dstFile, makeTarget):
+ self.srcFile = srcFile
+ self.dstFile = dstFile
+ self.makeTarget = makeTarget
+
+ def make (self, packageBuildInfo):
+ fullSrcPath = os.path.normpath(os.path.join(packageBuildInfo.srcBasePath, self.srcFile))
+ fullDstPath = os.path.normpath(os.path.join(packageBuildInfo.dstBasePath, self.dstFile))
+
+ target = self.makeTarget(fullSrcPath, fullDstPath)
+ target.make(packageBuildInfo)
+
+class BuildTarget:
+ def __init__ (self, baseConfig, generator, targets = None):
+ self.baseConfig = baseConfig
+ self.generator = generator
+ self.targets = targets
+
+ def make (self, packageBuildInfo):
+ print " Building %s" % self.baseConfig.getBuildDir()
+
+ # Create config with full build dir path
+ config = BuildConfig(os.path.join(packageBuildInfo.getTmpBasePath(), self.baseConfig.getBuildDir()),
+ self.baseConfig.getBuildType(),
+ self.baseConfig.getArgs(),
+ srcPath = os.path.join(packageBuildInfo.dstBasePath, "src"))
+
+ assert not os.path.exists(config.getBuildDir())
+ build(config, self.generator, self.targets)
+
+class BuildAndroidTarget:
+ def __init__ (self, dstFile):
+ self.dstFile = dstFile
+
+ def make (self, packageBuildInfo):
+ print " Building Android binary"
+
+ buildRoot = os.path.join(packageBuildInfo.tmpBasePath, "android-build")
+
+ assert not os.path.exists(buildRoot)
+ os.makedirs(buildRoot)
+
+ # Execute build script
+ scriptPath = os.path.normpath(os.path.join(packageBuildInfo.dstBasePath, "src", "android", "scripts", "build.py"))
+ execute([
+ "python",
+ "-B", # no .py[co]
+ scriptPath,
+ "--build-root=%s" % buildRoot,
+ ])
+
+ srcFile = os.path.normpath(os.path.join(buildRoot, "package", "bin", "dEQP-debug.apk"))
+ dstFile = os.path.normpath(os.path.join(packageBuildInfo.dstBasePath, self.dstFile))
+
+ CopyFile(srcFile, dstFile).make(packageBuildInfo)
+
+class FetchExternalSourcesTarget:
+ def __init__ (self):
+ pass
+
+ def make (self, packageBuildInfo):
+ scriptPath = os.path.normpath(os.path.join(packageBuildInfo.dstBasePath, "src", "external", "fetch_sources.py"))
+ execute([
+ "python",
+ "-B", # no .py[co]
+ scriptPath,
+ ])
+
+class RemoveSourcesTarget:
+ def __init__ (self):
+ pass
+
+ def make (self, packageBuildInfo):
+ shutil.rmtree(os.path.join(packageBuildInfo.dstBasePath, "src"), ignore_errors=False)
+
+class Module:
+ def __init__ (self, name, targets):
+ self.name = name
+ self.targets = targets
+
+ def make (self, packageBuildInfo):
+ for target in self.targets:
+ target.make(packageBuildInfo)
+
+class ReleaseConfig:
+ def __init__ (self, name, version, modules, sources = True):
+ self.name = name
+ self.version = version
+ self.modules = modules
+ self.sources = sources
+
+ def getName (self):
+ return self.name
+
+ def getVersion (self):
+ return self.version
+
+ def getModules (self):
+ return self.modules
+
+ def packageWithSources (self):
+ return self.sources
+
+def matchIncludeExclude (includePatterns, excludePatterns, filename):
+ components = os.path.normpath(filename).split(os.sep)
+ for pattern in excludePatterns:
+ for component in components:
+ if fnmatch.fnmatch(component, pattern):
+ return False
+
+ for pattern in includePatterns:
+ for component in components:
+ if fnmatch.fnmatch(component, pattern):
+ return True
+
+ return False
+
+def copyFileFilter (includePatterns, excludePatterns=[]):
+ return (lambda f: matchIncludeExclude(includePatterns, excludePatterns, f),
+ lambda s, d: CopyFile(s, d))
+
+def makeFileCopyGroup (srcDir, dstDir, includePatterns, excludePatterns=[]):
+ return FileTargetGroup(srcDir, dstDir, [copyFileFilter(includePatterns, excludePatterns)])
+
+def makeTmpFileCopyGroup (srcDir, dstDir, includePatterns, excludePatterns=[]):
+ return FileTargetGroup(srcDir, dstDir, [copyFileFilter(includePatterns, excludePatterns)], PackageBuildInfo.getTmpBasePath)
+
+def makeFileCopy (srcFile, dstFile):
+ return SingleFileTarget(srcFile, dstFile, lambda s, d: CopyFile(s, d))
+
+def getReleaseFileName (configName, releaseName):
+ today = datetime.date.today()
+ return "dEQP-%s-%04d-%02d-%02d-%s" % (releaseName, today.year, today.month, today.day, configName)
+
+def getTempDir ():
+ dirName = os.path.join(tempfile.gettempdir(), "dEQP-Releases")
+ if not os.path.exists(dirName):
+ os.makedirs(dirName)
+ return dirName
+
+def makeRelease (releaseConfig):
+ releaseName = getReleaseFileName(releaseConfig.getName(), releaseConfig.getVersion())
+ tmpPath = getTempDir()
+ srcBasePath = DEQP_DIR
+ dstBasePath = os.path.join(tmpPath, releaseName)
+ tmpBasePath = os.path.join(tmpPath, releaseName + "-tmp")
+ packageBuildInfo = PackageBuildInfo(releaseConfig, srcBasePath, dstBasePath, tmpBasePath)
+ dstArchiveName = releaseName + ".tar.bz2"
+
+ print "Creating release %s to %s" % (releaseName, tmpPath)
+
+ # Remove old temporary dirs
+ for path in [dstBasePath, tmpBasePath]:
+ if os.path.exists(path):
+ shutil.rmtree(path, ignore_errors=False)
+
+ # Make all modules
+ for module in releaseConfig.getModules():
+ print " Processing module %s" % module.name
+ module.make(packageBuildInfo)
+
+ # Remove sources?
+ if not releaseConfig.packageWithSources():
+ shutil.rmtree(os.path.join(dstBasePath, "src"), ignore_errors=False)
+
+ # Create archive
+ print "Creating %s" % dstArchiveName
+ archive = tarfile.open(dstArchiveName, 'w:bz2')
+ archive.add(dstBasePath, arcname=releaseName)
+ archive.close()
+
+ # Remove tmp dirs
+ for path in [dstBasePath, tmpBasePath]:
+ if os.path.exists(path):
+ shutil.rmtree(path, ignore_errors=False)
+
+ print "Done!"
+
+# Module declarations
+
+SRC_FILE_PATTERNS = ["*.h", "*.hpp", "*.c", "*.cpp", "*.m", "*.mm", "*.inl", "*.java", "*.aidl", "CMakeLists.txt", "LICENSE.txt", "*.cmake"]
+TARGET_PATTERNS = ["*.cmake", "*.h", "*.lib", "*.dll", "*.so", "*.txt"]
+
+BASE = Module("Base", [
+ makeFileCopy ("LICENSE", "src/LICENSE"),
+ makeFileCopy ("CMakeLists.txt", "src/CMakeLists.txt"),
+ makeFileCopyGroup ("targets", "src/targets", TARGET_PATTERNS),
+ makeFileCopyGroup ("execserver", "src/execserver", SRC_FILE_PATTERNS),
+ makeFileCopyGroup ("executor", "src/executor", SRC_FILE_PATTERNS),
+ makeFileCopy ("modules/CMakeLists.txt", "src/modules/CMakeLists.txt"),
+ makeFileCopyGroup ("external", "src/external", ["CMakeLists.txt", "*.py"]),
+
+ # Stylesheet for displaying test logs on browser
+ makeFileCopyGroup ("doc/testlog-stylesheet", "doc/testlog-stylesheet", ["*"]),
+
+ # Non-optional parts of framework
+ # \note qpInfo.c must be processed!
+ FileTargetGroup ("framework", "src/framework", [
+ # If file is qpInfo.c use GenInfoFile
+ (lambda f: f[-8:] == "qpInfo.c", lambda s, d: GenInfoFile(s, d)),
+ # Otherwise just CopyFile targets
+ copyFileFilter(SRC_FILE_PATTERNS, ["imagedifftester", "randomshaders", "simplereference", "referencerenderer"])
+ ]),
+
+ # Main modules CMakeLists.txt
+
+ # android sources
+ makeFileCopyGroup ("android/package/src", "src/android/package/src", SRC_FILE_PATTERNS),
+ makeFileCopy ("android/package/AndroidManifest.xml", "src/android/package/AndroidManifest.xml"),
+ makeFileCopyGroup ("android/package/res", "src/android/package/res", ["*.png", "*.xml"]),
+ makeFileCopyGroup ("android/scripts", "src/android/scripts", [
+ "common.py",
+ "build.py",
+ "resources.py",
+ "install.py",
+ "launch.py",
+ "debug.py"
+ ]),
+])
+
+DOCUMENTATION = Module("Documentation", [
+ makeFileCopyGroup ("doc/pdf", "doc", ["*.pdf"]),
+ makeFileCopyGroup ("doc", "doc", ["porting_layer_changes_*.txt"]),
+])
+
+GLSHARED = Module("Shared GL Tests", [
+ # Optional framework components
+ makeFileCopyGroup ("framework/randomshaders", "src/framework/randomshaders", SRC_FILE_PATTERNS),
+ makeFileCopyGroup ("framework/opengl/simplereference", "src/framework/opengl/simplereference", SRC_FILE_PATTERNS),
+ makeFileCopyGroup ("framework/referencerenderer", "src/framework/referencerenderer", SRC_FILE_PATTERNS),
+
+ makeFileCopyGroup ("modules/glshared", "src/modules/glshared", SRC_FILE_PATTERNS),
+])
+
+GLES2 = Module("GLES2", [
+ makeFileCopyGroup ("modules/gles2", "src/modules/gles2", SRC_FILE_PATTERNS),
+ makeFileCopyGroup ("data/gles2", "src/data/gles2", ["*.*"]),
+ makeFileCopyGroup ("doc/testspecs/GLES2", "doc/testspecs/GLES2", ["*.txt"])
+])
+
+GLES3 = Module("GLES3", [
+ makeFileCopyGroup ("modules/gles3", "src/modules/gles3", SRC_FILE_PATTERNS),
+ makeFileCopyGroup ("data/gles3", "src/data/gles3", ["*.*"]),
+ makeFileCopyGroup ("doc/testspecs/GLES3", "doc/testspecs/GLES3", ["*.txt"])
+])
+
+GLES31 = Module("GLES31", [
+ makeFileCopyGroup ("modules/gles31", "src/modules/gles31", SRC_FILE_PATTERNS),
+ makeFileCopyGroup ("data/gles31", "src/data/gles31", ["*.*"]),
+ makeFileCopyGroup ("doc/testspecs/GLES31", "doc/testspecs/GLES31", ["*.txt"])
+])
+
+EGL = Module("EGL", [
+ makeFileCopyGroup ("modules/egl", "src/modules/egl", SRC_FILE_PATTERNS)
+])
+
+INTERNAL = Module("Internal", [
+ makeFileCopyGroup ("modules/internal", "src/modules/internal", SRC_FILE_PATTERNS),
+ makeFileCopyGroup ("data/internal", "src/data/internal", ["*.*"]),
+])
+
+EXTERNAL_SRCS = Module("External sources", [
+ FetchExternalSourcesTarget()
+])
+
+ANDROID_BINARIES = Module("Android Binaries", [
+ BuildAndroidTarget ("bin/android/dEQP.apk"),
+ makeFileCopyGroup ("targets/android", "bin/android", ["*.bat", "*.sh"]),
+])
+
+COMMON_BUILD_ARGS = ['-DPNG_SRC_PATH=%s' % os.path.realpath(os.path.join(DEQP_DIR, '..', 'libpng'))]
+NULL_X32_CONFIG = BuildConfig('null-x32', 'Release', ['-DDEQP_TARGET=null', '-DCMAKE_C_FLAGS=-m32', '-DCMAKE_CXX_FLAGS=-m32'] + COMMON_BUILD_ARGS)
+NULL_X64_CONFIG = BuildConfig('null-x64', 'Release', ['-DDEQP_TARGET=null', '-DCMAKE_C_FLAGS=-m64', '-DCMAKE_CXX_FLAGS=-m64'] + COMMON_BUILD_ARGS)
+GLX_X32_CONFIG = BuildConfig('glx-x32', 'Release', ['-DDEQP_TARGET=x11_glx', '-DCMAKE_C_FLAGS=-m32', '-DCMAKE_CXX_FLAGS=-m32'] + COMMON_BUILD_ARGS)
+GLX_X64_CONFIG = BuildConfig('glx-x64', 'Release', ['-DDEQP_TARGET=x11_glx', '-DCMAKE_C_FLAGS=-m64', '-DCMAKE_CXX_FLAGS=-m64'] + COMMON_BUILD_ARGS)
+
+EXCLUDE_BUILD_FILES = ["CMakeFiles", "*.a", "*.cmake"]
+
+LINUX_X32_COMMON_BINARIES = Module("Linux x32 Common Binaries", [
+ BuildTarget (NULL_X32_CONFIG, ANY_UNIX_GENERATOR),
+ makeTmpFileCopyGroup(NULL_X32_CONFIG.getBuildDir() + "/execserver", "bin/linux32", ["*"], EXCLUDE_BUILD_FILES),
+ makeTmpFileCopyGroup(NULL_X32_CONFIG.getBuildDir() + "/executor", "bin/linux32", ["*"], EXCLUDE_BUILD_FILES),
+])
+
+LINUX_X64_COMMON_BINARIES = Module("Linux x64 Common Binaries", [
+ BuildTarget (NULL_X64_CONFIG, ANY_UNIX_GENERATOR),
+ makeTmpFileCopyGroup(NULL_X64_CONFIG.getBuildDir() + "/execserver", "bin/linux64", ["*"], EXCLUDE_BUILD_FILES),
+ makeTmpFileCopyGroup(NULL_X64_CONFIG.getBuildDir() + "/executor", "bin/linux64", ["*"], EXCLUDE_BUILD_FILES),
+])
+
+# Special module to remove src dir, for example after binary build
+REMOVE_SOURCES = Module("Remove sources from package", [
+ RemoveSourcesTarget()
+])
+
+# Release configuration
+
+ALL_MODULES = [
+ BASE,
+ DOCUMENTATION,
+ GLSHARED,
+ GLES2,
+ GLES3,
+ GLES31,
+ EGL,
+ INTERNAL,
+ EXTERNAL_SRCS,
+]
+
+ALL_BINARIES = [
+ LINUX_X64_COMMON_BINARIES,
+ ANDROID_BINARIES,
+]
+
+RELEASE_CONFIGS = {
+ "src": ALL_MODULES,
+ "src-bin": ALL_MODULES + ALL_BINARIES,
+ "bin": ALL_MODULES + ALL_BINARIES + [REMOVE_SOURCES],
+}
+
+def parseArgs ():
+ parser = argparse.ArgumentParser(description = "Build release package")
+ parser.add_argument("-c",
+ "--config",
+ dest="config",
+ choices=RELEASE_CONFIGS.keys(),
+ required=True,
+ help="Release configuration")
+ parser.add_argument("-n",
+ "--name",
+ dest="name",
+ required=True,
+ help="Package-specific name")
+ parser.add_argument("-v",
+ "--version",
+ dest="version",
+ required=True,
+ help="Version code")
+ return parser.parse_args()
+
+if __name__ == "__main__":
+ args = parseArgs()
+ config = ReleaseConfig(args.name, args.version, RELEASE_CONFIGS[args.config])
+ makeRelease(config)