diff --git a/check_blender_release/check_module_enabled.py b/check_blender_release/check_module_enabled.py new file mode 100644 index 0000000000000000000000000000000000000000..158a5386a9ddb7d9848133a58a7fe85c40dbae18 --- /dev/null +++ b/check_blender_release/check_module_enabled.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 + +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# Contributor(s): Sergey Sharybin +# +# #**** END GPL LICENSE BLOCK #**** + +# <pep8 compliant> + +import unittest + +from check_utils import (sliceCommandLineArguments, + SceiptUnitTesting) + + +class UnitTesting(SceiptUnitTesting): + def test_modulesEnabled(self): + self.checkScript("modules_enabled") + + +def main(): + # Slice command line arguments by '--' + unittest_args, parser_args = sliceCommandLineArguments() + # Construct and run unit tests. + unittest.main(argv=unittest_args) + + +if __name__ == "__main__": + main() diff --git a/check_blender_release/check_module_numpy.py b/check_blender_release/check_module_numpy.py new file mode 100644 index 0000000000000000000000000000000000000000..36c90006807531cdb44b0ca5441ed042a6b0511f --- /dev/null +++ b/check_blender_release/check_module_numpy.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 + +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# Contributor(s): Sergey Sharybin +# +# #**** END GPL LICENSE BLOCK #**** + +# <pep8 compliant> + +import unittest + +from check_utils import (sliceCommandLineArguments, + SceiptUnitTesting) + + +class UnitTesting(SceiptUnitTesting): + def test_numpyImports(self): + self.checkScript("numpy_import") + + def test_numpyBasicOperation(self): + self.checkScript("numpy_basic_operation") + + +def main(): + # Slice command line arguments by '--' + unittest_args, parser_args = sliceCommandLineArguments() + # Construct and run unit tests. + unittest.main(argv=unittest_args) + + +if __name__ == "__main__": + main() diff --git a/check_blender_release/check_module_requests.py b/check_blender_release/check_module_requests.py new file mode 100644 index 0000000000000000000000000000000000000000..af8f6e3b8e43f03e17fda167d6a3f974a7445e8f --- /dev/null +++ b/check_blender_release/check_module_requests.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 + +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# Contributor(s): Sergey Sharybin +# +# #**** END GPL LICENSE BLOCK #**** + +# <pep8 compliant> + +import unittest + +from check_utils import (sliceCommandLineArguments, + SceiptUnitTesting) + + +class UnitTesting(SceiptUnitTesting): + def test_requestsImports(self): + self.checkScript("requests_import") + + def test_requestsBasicAccess(self): + self.checkScript("requests_basic_access") + + +def main(): + # Slice command line arguments by '--' + unittest_args, parser_args = sliceCommandLineArguments() + # Construct and run unit tests. + unittest.main(argv=unittest_args) + + +if __name__ == "__main__": + main() diff --git a/check_blender_release/check_release.py b/check_blender_release/check_release.py new file mode 100755 index 0000000000000000000000000000000000000000..6275f79b0833160135ccc2f7e1b8fad63c2669fb --- /dev/null +++ b/check_blender_release/check_release.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 + +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# Contributor(s): Sergey Sharybin +# +# #**** END GPL LICENSE BLOCK #**** + +# <pep8 compliant> + +# Usage: ./check_release.py -- ../path/to/release/folder + + +import os +import sys +import unittest + +import check_module_enabled +import check_module_numpy +import check_module_requests +import check_static_binaries +from check_utils import sliceCommandLineArguments + + +def load_tests(loader, standard_tests, pattern): + standard_tests.addTests(loader.loadTestsFromTestCase( + check_module_enabled.UnitTesting)) + standard_tests.addTests(loader.loadTestsFromTestCase( + check_module_numpy.UnitTesting)) + standard_tests.addTests(loader.loadTestsFromTestCase( + check_module_requests.UnitTesting)) + standard_tests.addTests(loader.loadTestsFromTestCase( + check_static_binaries.UnitTesting)) + return standard_tests + + +def main(): + # Slice command line arguments by '--' + unittest_args, parser_args = sliceCommandLineArguments() + # Construct and run unit tests. + unittest.main(argv=unittest_args) + + +if __name__ == "__main__": + main() diff --git a/check_blender_release/check_static_binaries.py b/check_blender_release/check_static_binaries.py new file mode 100644 index 0000000000000000000000000000000000000000..198796bce1863776925bdd2d7aff220f4d4d57c9 --- /dev/null +++ b/check_blender_release/check_static_binaries.py @@ -0,0 +1,200 @@ +#!/usr/bin/env python3 + +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# Contributor(s): Sergey Sharybin +# +# #**** END GPL LICENSE BLOCK #**** + +# <pep8 compliant> + +import os +from pathlib import Path +import re +import subprocess +import sys +import unittest + +from check_utils import (sliceCommandLineArguments, + parseArguments) + + +ALLOWED_LIBS = ( + # Core C/C++ libraries + "ld-linux.so", + "ld-linux-x86-64.so", + "libc.so", + "libm.so", + "libstdc++.so", + "libdl.so", + "libpthread.so", + "libgcc_s.so", + "librt.so", + "libutil.so", + + # X11 libraries we don't link statically, + "libX11.so", + "libXext.so", + "libXrender.so", + "libXxf86vm.so", + "libXi.so", + + # OpenGL libraries. + "libGL.so", + "libGLU.so", + + # Own dependencies we don't link statically. + "libfreetype.so", +) + +IGNORE_FILES = ("blender-softwaregl", ) +IGNORE_EXTENSION = (".sh", ".py", ) + + +# Library dependencies. + +def getNeededLibrariesLDD(binary_filepath): + """ + This function uses ldd to collect libraries which binary depends on. + + Not totally safe since ldd might actually execute the binary to get it's + symbols and will also collect indirect dependnecies which might not be + desired. + + Has advantage of telling that some dependnecy library is not found. + """ + ldd_command = ("ldd", str(binary_filepath)) + ldd_output = subprocess.check_output(ldd_command, stderr=subprocess.STDOUT) + lines = ldd_output.decode().split("\n") + libraries = [] + for line in lines: + line = line.strip() + if not line: + continue + lib_name = line.split("=>")[0] + lib_name = lib_name.split(" (")[0].strip() + lib_file_name = os.path.basename(lib_name) + libraries.append(lib_file_name) + return libraries + + +def getNeededLibrariesOBJDUMP(binary_filepath): + """ + This function uses objdump to get direct dependencies of a given binary. + + Totally safe, but will require manual check over libraries which are not + found on the system. + """ + objdump_command = ("objdump", "-p", str(binary_filepath)) + objdump_output = subprocess.check_output(objdump_command, + stderr=subprocess.STDOUT) + lines = objdump_output.decode().split("\n") + libraries = [] + for line in lines: + line = line.strip() + if not line: + continue + if not line.startswith("NEEDED"): + continue + lib_name = line[6:].strip() + libraries.append(lib_name) + return libraries + + +def getNeededLibraries(binary_filepath): + """ + Get all libraries given binary depends on. + """ + if False: + return getNeededLibrariesLDD(binary_filepath) + else: + return getNeededLibrariesOBJDUMP(binary_filepath) + + +def stripLibraryABI(lib_name): + """ + Strip ABI suffix from .so file + + Example; libexample.so.1.0 => libexample.so + """ + lib_name_no_abi = lib_name + # TOOD(sergey): Optimize this! + while True: + no_abi = re.sub(r"\.[0-9]+$", "", lib_name_no_abi) + if lib_name_no_abi == no_abi: + break + lib_name_no_abi = no_abi + return lib_name_no_abi + + +class UnitTesting(unittest.TestCase): + def checkBinary(self, binary_filepath): + """ + Check given binary file to be a proper static self-sufficient. + """ + + libraries = getNeededLibraries(binary_filepath) + for lib_name in libraries: + lib_name_no_abi = stripLibraryABI(lib_name) + self.assertTrue(lib_name_no_abi in ALLOWED_LIBS, + "Error detected in {}: library used {}" . format( + binary_filepath, lib_name)) + + def checkDirectory(self, directory): + """ + Recursively traverse directory and check every binary in in. + """ + + for path in Path(directory).rglob("*"): + # Ignore any checks on directory. + if path.is_dir(): + continue + # Ignore script files. + if path.name in IGNORE_FILES: + continue + if path.suffix in IGNORE_EXTENSION: + continue + # Check any executable binary, + if path.stat().st_mode & 0o111 != 0: + self.checkBinary(path) + # Check all dynamic libraries. + elif path.suffix == ".so": + self.checkBinary(path) + + def test_directoryIsStatic(self): + # Parse arguments which are not handled by unit testing framework. + args = parseArguments() + # Do some sanity checks first. + self.assertTrue(os.path.exists(args.directory), + "Given directory does not exist: {}" . + format(args.directory)) + self.assertTrue(os.path.isdir(args.directory), + "Given path is not a directory: {}" . + format(args.directory)) + # Perform actual test, + self.checkDirectory(args.directory) + + +def main(): + # Slice command line arguments by '--' + unittest_args, parser_args = sliceCommandLineArguments() + # Construct and run unit tests. + unittest.main(argv=unittest_args) + + +if __name__ == "__main__": + main() diff --git a/check_blender_release/check_utils.py b/check_blender_release/check_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..42679cd16ec420b530b6b7f04b9c6f2a2e9e9b7c --- /dev/null +++ b/check_blender_release/check_utils.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python3 + +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# Contributor(s): Sergey Sharybin +# +# #**** END GPL LICENSE BLOCK #**** + +# <pep8 compliant> + + +import unittest + + +def sliceCommandLineArguments(): + """ + Slice command line arguments by -- argument. + """ + + import sys + + try: + double_shasl_index = sys.argv.index("--") + except ValueError: + unittest_args = sys.argv[:] + parser_args = [] + else: + unittest_args = sys.argv[:double_shasl_index] + parser_args = sys.argv[double_shasl_index + 1:] + + return unittest_args, parser_args + + +def parseArguments(): + import argparse + + # Construct argument parser. + parser = argparse.ArgumentParser(description="Static binary checker") + parser.add_argument('directory', help='Directories to check') + # Parse arguments which are not handled by unit testing framework. + unittest_args, parser_args = sliceCommandLineArguments() + args = parser.parse_args(args=parser_args) + # TODO(sergey): Run some checks here? + return args + + +def runScriptInBlender(blender_directory, script): + """ + Run given script inside Blender and check non-zero exit code + """ + + import os + import subprocess + + blender = os.path.join(blender_directory, "blender") + python = os.path.join(os.path.dirname(__file__), "scripts", script) + ".py" + + command = (blender, + "-b", + "--factory-startup", + "--python-exit-code", "1", + "--python", python) + + process = subprocess.Popen(command, + shell=False, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + output, error = process.communicate() + return process.returncode == 0 + + +class SceiptUnitTesting(unittest.TestCase): + def checkScript(self, script): + # Parse arguments which are not handled by unit testing framework. + args = parseArguments() + # Perform actual test, + self.assertTrue(runScriptInBlender(args.directory, script), + "Failed to run script {}" . format(script)) diff --git a/check_blender_release/scripts/modules_enabled.py b/check_blender_release/scripts/modules_enabled.py new file mode 100644 index 0000000000000000000000000000000000000000..1a83ec6b22643c633d1556fab6ec809794846fa4 --- /dev/null +++ b/check_blender_release/scripts/modules_enabled.py @@ -0,0 +1,5 @@ +import _sha1 +import _sha256 +import _md5 +import ssl +import multiprocessing.synchronize diff --git a/check_blender_release/scripts/numpy_basic_operation.py b/check_blender_release/scripts/numpy_basic_operation.py new file mode 100644 index 0000000000000000000000000000000000000000..54c581bbf6551f0404b3b53e9552d9f60ec8eb44 --- /dev/null +++ b/check_blender_release/scripts/numpy_basic_operation.py @@ -0,0 +1,6 @@ +# This code tests bug reported in T50703 + +import numpy + +a = numpy.array([[3, 2, 0], [3, 1, 0]], dtype=numpy.int32) +a[0] diff --git a/check_blender_release/scripts/numpy_import.py b/check_blender_release/scripts/numpy_import.py new file mode 100644 index 0000000000000000000000000000000000000000..c2e5936fe3786bc72d6bf8f7278f733571c09e48 --- /dev/null +++ b/check_blender_release/scripts/numpy_import.py @@ -0,0 +1 @@ +import numpy diff --git a/check_blender_release/scripts/requests_basic_access.py b/check_blender_release/scripts/requests_basic_access.py new file mode 100644 index 0000000000000000000000000000000000000000..09d6f61796cccdaba15a17b39f2dc1923b623bca --- /dev/null +++ b/check_blender_release/scripts/requests_basic_access.py @@ -0,0 +1,8 @@ +import requests + +r = requests.get("https://blender.org/", verify=True) + +assert(r.status_code == 200) +assert(r.reason == "OK") +assert(True if r.ok else False) +assert(len(r.content) > 256) diff --git a/check_blender_release/scripts/requests_import.py b/check_blender_release/scripts/requests_import.py new file mode 100644 index 0000000000000000000000000000000000000000..20b15530dd259d47a077bafde4e99ff35e656c80 --- /dev/null +++ b/check_blender_release/scripts/requests_import.py @@ -0,0 +1 @@ +import requests