# -*- coding: utf-8 -*-
#-------------------------------------------------------------------------
# drawElements Quality Program utilities
# --------------------------------------
#
# Copyright 2015 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
#-------------------------------------------------------------------------
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 GenReleaseInfoFileTarget (DstFile):
def __init__ (self, dstFile):
super(GenReleaseInfoFileTarget, self).__init__(dstFile)
def make (self, packageBuildInfo):
self.makeDir()
scriptPath = os.path.normpath(os.path.join(packageBuildInfo.srcBasePath, "framework", "qphelper", "gen_release_info.py"))
execute([
"python",
"-B", # no .py[co]
scriptPath,
"--name=%s" % packageBuildInfo.getReleaseVersion(),
"--id=0x%08x" % packageBuildInfo.getReleaseId(),
"--out=%s" % self.dstFile
])
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
makeFileCopy ("framework/CMakeLists.txt", "src/framework/CMakeLists.txt"),
makeFileCopyGroup ("framework/delibs", "src/framework/delibs", SRC_FILE_PATTERNS),
makeFileCopyGroup ("framework/common", "src/framework/common", SRC_FILE_PATTERNS),
makeFileCopyGroup ("framework/qphelper", "src/framework/qphelper", SRC_FILE_PATTERNS),
makeFileCopyGroup ("framework/platform", "src/framework/platform", SRC_FILE_PATTERNS),
makeFileCopyGroup ("framework/opengl", "src/framework/opengl", SRC_FILE_PATTERNS, ["simplereference"]),
makeFileCopyGroup ("framework/egl", "src/framework/egl", SRC_FILE_PATTERNS),
# 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"
]),
# Release info
GenReleaseInfoFileTarget("src/framework/qphelper/qpReleaseInfo.inl")
])
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)