def runScons(options, quiet): with _setupSconsEnvironment(): if Options.shallCompileWithoutBuildDirectory(): # Make sure we become non-local, by changing all paths to be # absolute, but ones that can be resolved by any program # externally, as the Python of Scons may not be good at unicode. options = copy.deepcopy(options) source_dir = options["source_dir"] options["source_dir"] = "." options["result_name"] = getExternalUsePath( options["result_name"], only_dirname=True ) options["nuitka_src"] = getExternalUsePath(options["nuitka_src"]) if "result_exe" in options: options["result_exe"] = getExternalUsePath( options["result_exe"], only_dirname=True ) if "icon_path" in options: options["icon_path"] = getExternalUsePath( options["icon_path"], only_dirname=True ) else: source_dir = None scons_command = _buildSconsCommand(quiet, options) if Options.isShowScons(): Tracing.printLine("Scons command:", " ".join(scons_command)) Tracing.flushStdout() return subprocess.call(scons_command, shell=False, cwd=source_dir) == 0
def detectDLLsWithDependencyWalker(binary_filename, scan_dirs): dwp_filename = binary_filename + ".dwp" output_filename = binary_filename + ".depends" # User query should only happen once if at all. with withFileLock( "Finding out dependency walker path and creating DWP file for %s" % binary_filename): depends_exe = getDependsExePath() # Note: Do this under lock to avoid forked processes to hold # a copy of the file handle on Windows. with open(dwp_filename, "w") as dwp_file: dwp_file.write( """\ %(scan_dirs)s SxS """ % { "scan_dirs": "\n".join("UserDir %s" % getExternalUsePath(dirname) for dirname in scan_dirs) }) # Starting the process while locked, so file handles are not duplicated. depends_exe_process = subprocess.Popen( ( depends_exe, "-c", "-ot%s" % output_filename, "-d:%s" % dwp_filename, "-f1", "-pa1", "-ps1", binary_filename, ), stdin=getNullInput(), cwd=getExternalUsePath(os.getcwd()), ) # TODO: Exit code should be checked. depends_exe_process.wait() if not os.path.exists(output_filename): inclusion_logger.sysexit( "Error, depends.exe failed to produce expected output.") # Opening the result under lock, so it is not getting locked by new processes. # Note: Do this under lock to avoid forked processes to hold # a copy of the file handle on Windows. result = _parseDependsExeOutput(output_filename) deleteFile(output_filename, must_exist=True) deleteFile(dwp_filename, must_exist=True) return result
def _getSconsBinaryCall(): """Return a way to execute Scons. Using potentially in-line copy if no system Scons is available or if we are on Windows, there it is mandatory. """ inline_path = os.path.join(_getSconsInlinePath(), "bin", "scons.py") if os.path.exists(inline_path): return [ _getPythonForSconsExePath(), "-W", "ignore", # Disable Python warnings in case of debug Python. getExternalUsePath(inline_path), ] else: scons_path = Execution.getExecutablePath("scons") if scons_path is not None: return [scons_path] else: Tracing.scons_logger.sysexit( "Error, the inline copy of scons is not present, nor a scons binary in the PATH." )
def enableClcache(the_compiler, env, source_dir): importFromInlineCopy("atomicwrites", must_exist=True) importFromInlineCopy("clcache", must_exist=True) # Avoid importing this in threads, triggers CPython 3.9 importing bugs at least, # do it now, so it's not a race issue. import concurrent.futures.thread # pylint: disable=I0021,unused-import,unused-variable cl_binary = getExecutablePath(the_compiler, env) # The compiler is passed via environment. setEnvironmentVariable(env, "CLCACHE_CL", cl_binary) env["CXX"] = env["CC"] = "<clcache>" setEnvironmentVariable(env, "CLCACHE_HIDE_OUTPUTS", "1") # The clcache stats filename needs absolute path, otherwise it will not work. clcache_stats_filename = os.path.abspath( os.path.join(source_dir, "clcache-stats.%d.txt" % os.getpid()) ) setEnvironmentVariable(env, "CLCACHE_STATS", clcache_stats_filename) env["CLCACHE_STATS"] = clcache_stats_filename # Unless asked to do otherwise, store ccache files in our own directory. if "CLCACHE_DIR" not in os.environ: clcache_dir = os.path.join(getCacheDir(), "clcache") makePath(clcache_dir) clcache_dir = getExternalUsePath(clcache_dir) setEnvironmentVariable(env, "CLCACHE_DIR", clcache_dir) env["CLCACHE_DIR"] = clcache_dir scons_details_logger.info( "Using inline copy of clcache with %r cl binary." % cl_binary )
def enableClcache(the_compiler, env, source_dir): importFromInlineCopy("atomicwrites", must_exist=True) importFromInlineCopy("clcache", must_exist=True) cl_binary = getExecutablePath(the_compiler, env) # The compiler is passed via environment. setEnvironmentVariable(env, "CLCACHE_CL", cl_binary) env["CXX"] = env["CC"] = "<clcache>" setEnvironmentVariable(env, "CLCACHE_HIDE_OUTPUTS", "1") # The clcache stats filename needs absolute path, otherwise it will not work. clcache_stats_filename = os.path.abspath( os.path.join(source_dir, "clcache-stats.%d.txt" % os.getpid())) setEnvironmentVariable(env, "CLCACHE_STATS", clcache_stats_filename) env["CLCACHE_STATS"] = clcache_stats_filename # Unless asked to do otherwise, store ccache files in our own directory. if "CLCACHE_DIR" not in os.environ: clcache_dir = os.path.join(getCacheDir(), "clcache") makePath(clcache_dir) clcache_dir = getExternalUsePath(clcache_dir) setEnvironmentVariable(env, "CLCACHE_DIR", clcache_dir) env["CLCACHE_DIR"] = clcache_dir scons_details_logger.info( "Using inline copy of clcache with %r cl binary." % cl_binary) # Do not consider scons cache anymore. return True
def detectDLLsWithDependencyWalker(binary_filename, scan_dirs): dwp_filename = binary_filename + ".dwp" output_filename = binary_filename + ".depends" # User query should only happen once if at all. with withFileLock( "Finding out dependency walker path and creating DWP file for %s" % binary_filename ): depends_exe = getDependsExePath() # Note: Do this under lock to avoid forked processes to hold # a copy of the file handle on Windows. putTextFileContents( dwp_filename, contents="""\ %(scan_dirs)s SxS """ % { "scan_dirs": "\n".join( "UserDir %s" % getExternalUsePath(dirname) for dirname in scan_dirs ) }, ) # Starting the process while locked, so file handles are not duplicated. # TODO: At least exit code should be checked, output goes to a filename, # but errors might be interesting potentially. _stdout, _stderr, _exit_code = executeProcess( command=( depends_exe, "-c", "-ot%s" % output_filename, "-d:%s" % dwp_filename, "-f1", "-pa1", "-ps1", binary_filename, ), external_cwd=True, ) if not os.path.exists(output_filename): inclusion_logger.sysexit( "Error, 'depends.exe' failed to produce expected output." ) # Opening the result under lock, so it is not getting locked by new processes. # Note: Do this under lock to avoid forked processes to hold # a copy of the file handle on Windows. result = parseDependsExeOutput(output_filename) deleteFile(output_filename, must_exist=True) deleteFile(dwp_filename, must_exist=True) return result
def runScons(options, quiet, scons_filename): with _setupSconsEnvironment(): if Options.shallCompileWithoutBuildDirectory(): # Make sure we become non-local, by changing all paths to be # absolute, but ones that can be resolved by any program # externally, as the Python of Scons may not be good at unicode. options = copy.deepcopy(options) source_dir = options["source_dir"] options["source_dir"] = "." options["result_name"] = getExternalUsePath(options["result_name"], only_dirname=True) options["nuitka_src"] = getExternalUsePath(options["nuitka_src"]) if "result_exe" in options: options["result_exe"] = getExternalUsePath( options["result_exe"], only_dirname=True) if "compiled_exe" in options: options["compiled_exe"] = getExternalUsePath( options["compiled_exe"], only_dirname=True) else: source_dir = None scons_command = _buildSconsCommand(quiet=quiet, options=options, scons_filename=scons_filename) if Options.isShowScons(): Tracing.printLine("Scons command:", " ".join(scons_command)) Tracing.flushStandardOutputs() # Call scons, make sure to pass on quiet setting. with Execution.withEnvironmentVarOverridden( "NUITKA_QUIET", "1" if Tracing.is_quiet else "0"): result = subprocess.call(scons_command, shell=False, cwd=source_dir) flushSconsReports() if result == 0: checkCachingSuccess(source_dir or options["source_dir"]) return result == 0
def _runPgoBinary(): pgo_executable = OutputDirectories.getPgoRunExecutable() if not os.path.isfile(pgo_executable): general.sysexit("Error, failed to produce PGO binary '%s'" % pgo_executable) return callProcess( [getExternalUsePath(pgo_executable)] + Options.getPgoArgs(), shell=False, )
def _buildSconsCommand(quiet, options, scons_filename): """Build the scons command to run. The options are a dictionary to be passed to scons as a command line, and other scons stuff is set. """ scons_command = _getSconsBinaryCall() if quiet: scons_command.append("--quiet") scons_command += [ # The scons file "-f", getExternalUsePath(os.path.join(getSconsDataPath(), scons_filename)), # Parallel compilation. "--jobs", str(Options.getJobLimit()), # Do not warn about deprecation from Scons "--warn=no-deprecated", # Don't load "site_scons" at all. "--no-site-dir", ] if Options.isShowScons(): scons_command.append("--debug=stacktrace") # Python2, encoding unicode values def encode(value): if str is bytes and type(value) is unicode: return value.encode("utf8") else: return value # Option values to provide to scons. Find these in the caller. for key, value in options.items(): if value is None: Tracing.scons_logger.sysexit( "Error, failure to provide argument for '%s', please report bug." % key) scons_command.append(key + "=" + encode(value)) # Python2, make argument encoding recognizable. if str is bytes: scons_command.append("arg_encoding=utf8") return scons_command
def enableCcache( the_compiler, env, source_dir, python_prefix, show_scons_mode, assume_yes_for_downloads, ): # The ccache needs absolute path, otherwise it will not work. ccache_logfile = os.path.abspath( os.path.join(source_dir, "ccache-%d.txt" % os.getpid())) setEnvironmentVariable(env, "CCACHE_LOGFILE", ccache_logfile) env["CCACHE_LOGFILE"] = ccache_logfile # Unless asked to do otherwise, store ccache files in our own directory. if "CCACHE_DIR" not in os.environ: ccache_dir = os.path.join(getCacheDir(), "ccache") makePath(ccache_dir) ccache_dir = getExternalUsePath(ccache_dir) setEnvironmentVariable(env, "CCACHE_DIR", ccache_dir) env["CCACHE_DIR"] = ccache_dir # First check if it's not already supposed to be a ccache, then do nothing. cc_path = getExecutablePath(the_compiler, env=env) cc_is_link, cc_link_path = getLinkTarget(cc_path) if cc_is_link and os.path.basename(cc_link_path) == "ccache": if show_scons_mode: scons_logger.info( "Chosen compiler %s is pointing to ccache %s already." % (cc_path, cc_link_path)) return True return _injectCcache( the_compiler=the_compiler, cc_path=cc_path, env=env, python_prefix=python_prefix, show_scons_mode=show_scons_mode, assume_yes_for_downloads=assume_yes_for_downloads, )
def detectBinaryPathDLLsWindowsDependencyWalker( is_main_executable, source_dir, original_dir, binary_filename, package_name, use_cache, update_cache, ): # This is complex, as it also includes the caching mechanism # pylint: disable=too-many-locals result = set() if use_cache or update_cache: cache_filename = _getCacheFilename( dependency_tool="depends.exe", is_main_executable=is_main_executable, source_dir=source_dir, original_dir=original_dir, binary_filename=binary_filename, ) if use_cache: with withFileLock(): if not os.path.exists(cache_filename): use_cache = False if use_cache: for line in getFileContentByLine(cache_filename): line = line.strip() result.add(line) return result if Options.isShowProgress(): info("Analysing dependencies of '%s'." % binary_filename) scan_dirs = getScanDirectories(package_name, original_dir) dwp_filename = binary_filename + ".dwp" output_filename = binary_filename + ".depends" # User query should only happen once if at all. with _withLock( "Finding out dependency walker path and creating DWP file for %s" % binary_filename ): depends_exe = getDependsExePath() # Note: Do this under lock to avoid forked processes to hold # a copy of the file handle on Windows. with open(dwp_filename, "w") as dwp_file: dwp_file.write( """\ %(scan_dirs)s SxS """ % { "scan_dirs": "\n".join( "UserDir %s" % dirname for dirname in scan_dirs ) } ) # Starting the process while locked, so file handles are not duplicated. depends_exe_process = subprocess.Popen( ( depends_exe, "-c", "-ot%s" % output_filename, "-d:%s" % dwp_filename, "-f1", "-pa1", "-ps1", binary_filename, ), cwd=getExternalUsePath(os.getcwd()), ) # TODO: Exit code should be checked. depends_exe_process.wait() if not os.path.exists(output_filename): sys.exit("Error, depends.exe failed to product output.") # Opening the result under lock, so it is not getting locked by new processes. # Note: Do this under lock to avoid forked processes to hold # a copy of the file handle on Windows. _parseDependsExeOutput(output_filename, result) deleteFile(output_filename, must_exist=True) deleteFile(dwp_filename, must_exist=True) if update_cache: with open(cache_filename, "w") as cache_file: for dll_filename in result: print(dll_filename, file=cache_file) return result
def executePostProcessing(): """Postprocessing of the resulting binary. These are in part required steps, not usable after failure. """ result_filename = OutputDirectories.getResultFullpath(onefile=False) if not os.path.exists(result_filename): postprocessing_logger.sysexit( "Error, scons failed to create the expected file %r. " % result_filename) if isWin32Windows(): if not Options.shallMakeModule(): if python_version < 0x300: # Copy the Windows manifest from the CPython binary to the created # executable, so it finds "MSCRT.DLL". This is needed for Python2 # only, for Python3 newer MSVC doesn't hide the C runtime. manifest = getWindowsExecutableManifest(sys.executable) else: manifest = None executePostProcessingResources(manifest=manifest, onefile=False) source_dir = OutputDirectories.getSourceDirectoryPath() # Attach the binary blob as a Windows resource. addResourceToFile( target_filename=result_filename, data=getFileContents(getConstantBlobFilename(source_dir), "rb"), resource_kind=RT_RCDATA, res_name=3, lang_id=0, logger=postprocessing_logger, ) # On macOS, we update the executable path for searching the "libpython" # library. if (isMacOS() and not Options.shallMakeModule() and not Options.shallUseStaticLibPython()): python_abi_version = python_version_str + getPythonABI() python_dll_filename = "libpython" + python_abi_version + ".dylib" python_lib_path = os.path.join(sys.prefix, "lib") # Note: For CPython and potentially others, the rpath for the Python # library needs to be set. callInstallNameTool( filename=result_filename, mapping=( ( python_dll_filename, os.path.join(python_lib_path, python_dll_filename), ), ( "@rpath/Python3.framework/Versions/%s/Python3" % python_version_str, os.path.join(python_lib_path, python_dll_filename), ), ), id_path=None, rpath=python_lib_path, ) if Options.shallCreateAppBundle(): createPlistInfoFile(logger=postprocessing_logger, onefile=False) # Modules should not be executable, but Scons creates them like it, fix # it up here. if not isWin32Windows() and Options.shallMakeModule(): removeFileExecutablePermission(result_filename) if isWin32Windows() and Options.shallMakeModule(): candidate = os.path.join( os.path.dirname(result_filename), "lib" + os.path.basename(result_filename)[:-4] + ".a", ) if os.path.exists(candidate): os.unlink(candidate) # Might have to create a CMD file, potentially with debugger run. if Options.shallCreateCmdFileForExecution(): dll_directory = getExternalUsePath( os.path.dirname(getTargetPythonDLLPath())) cmd_filename = OutputDirectories.getResultRunFilename(onefile=False) cmd_contents = """ @echo off rem This script was created by Nuitka to execute '%(exe_filename)s' with Python DLL being found. set PATH=%(dll_directory)s;%%PATH%% set PYTHONHOME=%(dll_directory)s %(debugger_call)s"%%~dp0.\\%(exe_filename)s" %%* """ % { "debugger_call": (" ".join(wrapCommandForDebuggerForExec()) + " ") if Options.shallRunInDebugger() else "", "dll_directory": dll_directory, "exe_filename": os.path.basename(result_filename), } putTextFileContents(cmd_filename, cmd_contents) # Create a ".pyi" file for created modules if Options.shallMakeModule() and Options.shallCreatePyiFile(): pyi_filename = OutputDirectories.getResultBasepath() + ".pyi" putTextFileContents( filename=pyi_filename, contents="""\ # This file was generated by Nuitka and describes the types of the # created shared library. # At this time it lists only the imports made and can be used by the # tools that bundle libraries, including Nuitka itself. For instance # standalone mode usage of the created library will need it. # In the future, this will also contain type information for values # in the module, so IDEs will use this. Therefore please include it # when you make software releases of the extension module that it # describes. %(imports)s # This is not Python source even if it looks so. Make it clear for # now. This was decided by PEP 484 designers. __name__ = ... """ % { "imports": "\n".join("import %s" % module_name for module_name in getImportedNames()) }, )
def main(): # Complex stuff, even more should become common code though. # pylint: disable=too-many-branches,too-many-locals,too-many-statements python_version = setup(needs_io_encoding=True) search_mode = createSearchMode() for filename in sorted(os.listdir(".")): if not filename.endswith(".py"): continue if not decideFilenameVersionSkip(filename): continue active = search_mode.consider(dirname=None, filename=filename) if not active: test_logger.info("Skipping %s" % filename) continue extra_flags = [ "expect_success", "--standalone", "remove_output", # Cache the CPython results for re-use, they will normally not change. "cpython_cache", # To understand what is slow. "timing", # TODO: This plugin probably ought to be on by default. "plugin_enable:pkg-resources", ] # skip each test if their respective requirements are not met requirements_met, error_message = checkRequirements(filename) if not requirements_met: reportSkip(error_message, ".", filename) continue # catch error if filename == "Boto3Using.py": reportSkip("boto3 test not fully working yet", ".", filename) continue if "Idna" in filename: # For the warnings of Python2. if python_version < (3, ): extra_flags.append("ignore_stderr") if filename == "CtypesUsing.py": extra_flags.append("plugin_disable:pylint-warnings") if filename == "GtkUsing.py": # Don't test on platforms not supported by current Debian testing, and # which should be considered irrelevant by now. if python_version < (2, 7): reportSkip("irrelevant Python version", ".", filename) continue # For the warnings. extra_flags.append("ignore_warnings") if filename.startswith("Win"): if os.name != "nt": reportSkip("Windows only test", ".", filename) continue if filename == "TkInterUsing.py": if getOS() == "Darwin": reportSkip("Not working macOS yet", ".", filename) continue if getOS() == "Windows": reportSkip("Can hang on Windows CI.", ".", filename) continue # For the plug-in information. extra_flags.append("plugin_enable:tk-inter") if filename == "FlaskUsing.py": # For the warnings. extra_flags.append("ignore_warnings") if filename == "NumpyUsing.py": extra_flags.append("plugin_enable:numpy") # TODO: Disabled for now. reportSkip("numpy.test not fully working yet", ".", filename) continue if filename == "PandasUsing.py": extra_flags.append("plugin_enable:numpy") extra_flags.append("plugin_disable:pylint-warnings") extra_flags.append("plugin_disable:qt-plugins") extra_flags.append("plugin_disable:pyside2") extra_flags.append("plugin_disable:pyside6") if filename == "PmwUsing.py": extra_flags.append("plugin_enable:pmw-freezer") if filename == "OpenGLUsing.py": # For the warnings. extra_flags.append("ignore_warnings") if filename == "GlfwUsing.py": # For the warnings. extra_flags.append("plugin_enable:glfw") extra_flags.append("plugin_enable:numpy") if filename == "PasslibUsing.py": # For the warnings. extra_flags.append("ignore_warnings") if filename == "Win32ComUsing.py": # For the warnings. extra_flags.append("ignore_warnings") if filename.startswith(("PySide6", "PyQt5", "PyQt6")): # Don't test on platforms not supported by current Debian testing, and # which should be considered irrelevant by now. if python_version < (2, 7) or ((3, ) <= python_version < (3, 7)): reportSkip("irrelevant Python version", ".", filename) continue # For the plug-in information if filename.startswith("PySide6"): extra_flags.append("plugin_enable:pyside6") elif filename.startswith("PyQt5"): extra_flags.append("plugin_enable:qt-plugins") elif filename.startswith("PyQt6"): extra_flags.append("plugin_enable:pyqt6") test_logger.info( "Consider output of standalone mode compiled program: %s" % filename) # First compare so we know the program behaves identical. compareWithCPython( dirname=None, filename=filename, extra_flags=extra_flags, search_mode=search_mode, needs_2to3=False, on_error=displayError, ) # Second check if glibc libraries haven't been accidentally # shipped with the standalone executable found_glibc_libs = [] for dist_filename in os.listdir(os.path.join(filename[:-3] + ".dist")): if os.path.basename(dist_filename).startswith(( "ld-linux-x86-64.so", "libc.so.", "libpthread.so.", "libm.so.", "libdl.so.", "libBrokenLocale.so.", "libSegFault.so", "libanl.so.", "libcidn.so.", "libcrypt.so.", "libmemusage.so", "libmvec.so.", "libnsl.so.", "libnss_compat.so.", "libnss_db.so.", "libnss_dns.so.", "libnss_files.so.", "libnss_hesiod.so.", "libnss_nis.so.", "libnss_nisplus.so.", "libpcprofile.so", "libresolv.so.", "librt.so.", "libthread_db-1.0.so", "libthread_db.so.", "libutil.so.", )): found_glibc_libs.append(dist_filename) if found_glibc_libs: test_logger.warning( "Should not ship glibc libraries with the standalone executable (found %s)" % found_glibc_libs) sys.exit(1) binary_filename = os.path.join( filename[:-3] + ".dist", filename[:-3] + (".exe" if os.name == "nt" else "")) # Then use "strace" on the result. with TimerReport("Determining run time loaded files took %.2f", logger=test_logger): loaded_filenames = getRuntimeTraceOfLoadedFiles( logger=test_logger, path=binary_filename) current_dir = os.path.normpath(os.getcwd()) current_dir = os.path.normcase(current_dir) current_dir_ext = os.path.normcase(getExternalUsePath(current_dir)) illegal_access = False for loaded_filename in loaded_filenames: loaded_filename = os.path.normpath(loaded_filename) loaded_filename = os.path.normcase(loaded_filename) loaded_basename = os.path.basename(loaded_filename) if os.name == "nt": if areSamePaths( os.path.dirname(loaded_filename), os.path.normpath( os.path.join(os.environ["SYSTEMROOT"], "System32")), ): continue if areSamePaths( os.path.dirname(loaded_filename), os.path.normpath( os.path.join(os.environ["SYSTEMROOT"], "SysWOW64")), ): continue if r"windows\winsxs" in loaded_filename: continue # Github actions have these in PATH overriding SYSTEMROOT if r"windows performance toolkit" in loaded_filename: continue if r"powershell" in loaded_filename: continue if r"azure dev spaces cli" in loaded_filename: continue if r"tortoisesvn" in loaded_filename: continue if loaded_filename.startswith(current_dir): continue if loaded_filename.startswith(os.path.abspath(current_dir)): continue if loaded_filename.startswith(current_dir_ext): continue if loaded_filename.startswith("/etc/"): continue if loaded_filename.startswith("/usr/etc/"): continue if loaded_filename.startswith( "/proc/") or loaded_filename == "/proc": continue if loaded_filename.startswith("/dev/"): continue if loaded_filename.startswith("/tmp/"): continue if loaded_filename.startswith("/run/"): continue if loaded_filename.startswith("/usr/lib/locale/"): continue if loaded_filename.startswith("/usr/share/locale/"): continue if loaded_filename.startswith("/usr/share/X11/locale/"): continue # Themes may of course be loaded. if loaded_filename.startswith("/usr/share/themes"): continue if "gtk" in loaded_filename and "/engines/" in loaded_filename: continue if loaded_filename in ( "/usr", "/usr/local", "/usr/local/lib", "/usr/share", "/usr/local/share", "/usr/lib64", ): continue # TCL/tk for tkinter for non-Windows is OK. if loaded_filename.startswith(( "/usr/lib/tcltk/", "/usr/share/tcltk/", "/usr/lib/tcl/", "/usr/lib64/tcl/", )): continue if loaded_filename in ( "/usr/lib/tcltk", "/usr/share/tcltk", "/usr/lib/tcl", "/usr/lib64/tcl", ): continue if loaded_filename in ( "/lib", "/lib64", "/lib/sse2", "/lib/tls", "/lib64/tls", "/usr/lib/sse2", "/usr/lib/tls", "/usr/lib64/tls", ): continue if loaded_filename in ("/usr/share/tcl8.6", "/usr/share/tcl8.5"): continue if loaded_filename in ( "/usr/share/tcl8.6/init.tcl", "/usr/share/tcl8.5/init.tcl", ): continue if loaded_filename in ( "/usr/share/tcl8.6/encoding", "/usr/share/tcl8.5/encoding", ): continue # System SSL config on Linux. TODO: Should this not be included and # read from dist folder. if loaded_basename == "openssl.cnf": continue # Taking these from system is harmless and desirable if loaded_basename.startswith(("libz.so", "libgcc_s.so")): continue # System C libraries are to be expected. if loaded_basename.startswith(( "ld-linux-x86-64.so", "libc.so.", "libpthread.so.", "libm.so.", "libdl.so.", "libBrokenLocale.so.", "libSegFault.so", "libanl.so.", "libcidn.so.", "libcrypt.so.", "libmemusage.so", "libmvec.so.", "libnsl.so.", "libnss_compat.so.", "libnss_db.so.", "libnss_dns.so.", "libnss_files.so.", "libnss_hesiod.so.", "libnss_nis.so.", "libnss_nisplus.so.", "libpcprofile.so", "libresolv.so.", "librt.so.", "libthread_db-1.0.so", "libthread_db.so.", "libutil.so.", )): continue # Loaded by C library potentially for DNS lookups. if loaded_basename.startswith(( "libnss_", "libnsl", # Some systems load a lot more, this is CentOS 7 on OBS "libattr.so.", "libbz2.so.", "libcap.so.", "libdw.so.", "libelf.so.", "liblzma.so.", # Some systems load a lot more, this is Fedora 26 on OBS "libselinux.so.", "libpcre.so.", # And this is Fedora 29 on OBS "libblkid.so.", "libmount.so.", "libpcre2-8.so.", # CentOS 8 on OBS "libuuid.so.", )): continue # Loaded by dtruss on macOS X. if loaded_filename.startswith("/usr/lib/dtrace/"): continue # Loaded by cowbuilder and pbuilder on Debian if loaded_basename == ".ilist": continue if "cowdancer" in loaded_filename: continue if "eatmydata" in loaded_filename: continue # Loading from home directories is OK too. if (loaded_filename.startswith("/home/") or loaded_filename.startswith("/data/") or loaded_filename.startswith("/root/") or loaded_filename in ("/home", "/data", "/root")): continue # For Debian builders, /build is OK too. if loaded_filename.startswith( "/build/") or loaded_filename == "/build": continue # TODO: Unclear, loading gconv from filesystem of installed system # may be OK or not. I think it should be. if loaded_basename == "gconv-modules.cache": continue if "/gconv/" in loaded_filename: continue if loaded_basename.startswith("libicu"): continue if loaded_filename.startswith("/usr/share/icu/"): continue # Loading from caches is OK. if loaded_filename.startswith("/var/cache/"): continue lib_prefix_dir = "/usr/lib/python%d.%s" % ( python_version[0], python_version[1], ) # PySide accesses its directory. if loaded_filename == os.path.join(lib_prefix_dir, "dist-packages/PySide"): continue # GTK accesses package directories only. if loaded_filename == os.path.join(lib_prefix_dir, "dist-packages/gtk-2.0/gtk"): continue if loaded_filename == os.path.join(lib_prefix_dir, "dist-packages/glib"): continue if loaded_filename == os.path.join(lib_prefix_dir, "dist-packages/gtk-2.0/gio"): continue if loaded_filename == os.path.join(lib_prefix_dir, "dist-packages/gobject"): continue # PyQt5 seems to do this, but won't use contents then. if loaded_filename in ( "/usr/lib/qt5/plugins", "/usr/lib/qt5", "/usr/lib64/qt5/plugins", "/usr/lib64/qt5", "/usr/lib/x86_64-linux-gnu/qt5/plugins", "/usr/lib/x86_64-linux-gnu/qt5", "/usr/lib/x86_64-linux-gnu", "/usr/lib", ): continue # Can look at the interpreters of the system. if loaded_basename in "python3": continue if loaded_basename in ("python%s" + supported_version for supported_version in ( getSupportedPythonVersions() + getPartiallySupportedPythonVersions())): continue # Current Python executable can actually be a symlink and # the real executable which it points to will be on the # loaded_filenames list. This is all fine, let's ignore it. # Also, because the loaded_filename can be yet another symlink # (this is weird, but it's true), let's better resolve its real # path too. if os.path.realpath(loaded_filename) == os.path.realpath( sys.executable): continue # Accessing SE-Linux is OK. if loaded_filename in ("/sys/fs/selinux", "/selinux"): continue # Looking at device is OK. if loaded_filename.startswith("/sys/devices/"): continue # Allow reading time zone info of local system. if loaded_filename.startswith("/usr/share/zoneinfo/"): continue # The access to .pth files has no effect. if loaded_filename.endswith(".pth"): continue # Looking at site-package dir alone is alone. if loaded_filename.endswith(("site-packages", "dist-packages")): continue # QtNetwork insist on doing this it seems. if loaded_basename.startswith(("libcrypto.so", "libssl.so")): continue # macOS uses these: if loaded_basename in ( "libcrypto.1.0.0.dylib", "libssl.1.0.0.dylib", "libcrypto.1.1.dylib", ): continue # MSVC run time DLLs, seem to sometimes come from system. if loaded_basename.upper() in ("MSVCRT.DLL", "MSVCR90.DLL"): continue test_logger.warning("Should not access '%s'." % loaded_filename) illegal_access = True if illegal_access: if os.name != "nt": displayError(None, filename) displayRuntimeTraces(test_logger, binary_filename) search_mode.onErrorDetected(1) removeDirectory(filename[:-3] + ".dist", ignore_errors=True) search_mode.finish()