def makeMsiCompatibleFilename(filename): filename = filename[:-4] for supported_version in getSupportedPythonVersions(): filename = filename.replace("-py" + supported_version, "") filename = filename.replace("Nuitka32", "Nuitka") filename = filename.replace("Nuitka64", "Nuitka") parts = [filename, "py" + sys.version[:3].replace(".", ""), "msi"] return ".".join(parts)
def makeMsiCompatibleFilename(filename): filename = filename[:-4] for supported_version in getSupportedPythonVersions(): filename = filename.replace("-py" + supported_version, "") filename = filename.replace("Nuitka32", "Nuitka") filename = filename.replace("Nuitka64", "Nuitka") parts = [filename, "py" + sys.version[:3].replace(".", ""), "msi"] return ".".join(parts)
def makeMsiCompatibleFilename(filename): filename = filename[:-4] for supported_version in (getSupportedPythonVersions() + getPartiallySupportedPythonVersions()): filename = filename.replace("-py" + supported_version, "") # Python 3.10 does funny things filename = filename.replace("-py3.1", "") filename = filename.replace("Nuitka32", "Nuitka") filename = filename.replace("Nuitka64", "Nuitka") parts = [ filename, "py%s%s" % (sys.version_info[0], sys.version_info[1]), "msi" ] return ".".join(parts)
def handleSyntaxError(e): # Syntax or indentation errors, output them to the user and abort. If # we are not in full compat, and user has not specified the Python # versions he wants, tell him about the potential version problem. error_message = SyntaxErrors.formatOutput(e) if not Options.isFullCompat(): if python_version < 300: suggested_python_version_str = getSupportedPythonVersions()[-1] else: suggested_python_version_str = "2.7" error_message += """ Nuitka is very syntax compatible with standard Python. It is currently running with Python version '%s', you might want to specify more clearly with the use of the precise Python interpreter binary and '-m nuitka', e.g. use this 'python%s -m nuitka' option, if that's not the one the program expects. """ % (python_version_str, suggested_python_version_str) sys.exit(error_message)
def handleSyntaxError(e): # Syntax or indentation errors, output them to the user and abort. If # we are not in full compat, and user has not specified the Python # versions he wants, tell him about the potential version problem. error_message = SyntaxErrors.formatOutput(e) if not Options.isFullCompat(): if python_version < 300: suggested_python_version_str = getSupportedPythonVersions()[-1] else: suggested_python_version_str = "2.7" error_message += """ Nuitka is very syntax compatible with standard Python. It is currently running with Python version '%s', you might want to specify more clearly with the use of the precise Python interpreter binary and '-m nuitka', e.g. use this 'python%s -m nuitka' option, if that's not the one the program expects. """ % ( python_version_str, suggested_python_version_str, ) sys.exit(error_message)
action = "store_false", dest = "freeze_stdlib", default = True, help = """\ In standalone mode by default all modules of standard library will be frozen as bytecode. This compiles them all and as a result compilation time will increase very much. """, ) parser.add_option( "--python-version", action = "store", dest = "python_version", choices = getSupportedPythonVersions(), default = None, help = """\ Major version of Python to be used, one of %s. Defaults to what you run Nuitka with (currently %s)""" % ( getSupportedPythonVersionStr(), python_version_str ) ) parser.add_option( "--python-debug", "--python-dbg", action = "store_true", dest = "python_debug", default = None, help = """\
parser.add_option( "--nofreeze-stdlib", action="store_false", dest="freeze_stdlib", default=True, help="""\ In standalone mode by default all modules of standard library will be frozen as bytecode. This compiles them all and as a result compilation time will increase very much. """, ) parser.add_option("--python-version", action="store", dest="python_version", choices=getSupportedPythonVersions(), default=None, help="""\ Major version of Python to be used, one of %s. Defaults to what you run Nuitka with (currently %s)""" % (getSupportedPythonVersionStr(), python_version_str)) parser.add_option("--python-debug", "--python-dbg", action="store_true", dest="python_debug", default=None, help="""\ Use debug version or not. Default uses what you are using to run Nuitka, most likely a non-debug version.""")
def commentArgs(): """Comment on options, where we know something is not having the intended effect. :meta private: """ # A ton of cases to consider, pylint: disable=too-many-branches,too-many-statements # Inform the user about potential issues with the running version. e.g. unsupported # version. if python_version_str not in getSupportedPythonVersions(): # Do not disturb run of automatic tests with, detected from the presence of # that environment variable. if "PYTHON" not in os.environ: Tracing.general.warning( "The version %r is not currently supported. Expect problems." % python_version_str, ) default_reference_mode = ("runtime" if shallMakeModule() or isStandaloneMode() else "original") if getFileReferenceMode() is None: options.file_reference_mode = default_reference_mode else: if options.file_reference_mode != default_reference_mode: Tracing.options_logger.warning( "Using non-default file reference mode '%s' rather than '%s' may cause runtime issues." % (getFileReferenceMode(), default_reference_mode)) else: Tracing.options_logger.info( "Using default file reference mode '%s' need not be specified." % default_reference_mode) default_mode_name_mode = "runtime" if shallMakeModule() else "original" if getModuleNameMode() is None: options.module_name_mode = default_mode_name_mode elif getModuleNameMode() == default_mode_name_mode: Tracing.options_logger.info( "Using module name mode '%s' need not be specified." % default_mode_name_mode) # TODO: Not all of these are usable with MSYS2 really, split those off. if getOS() != "Windows": # Too many Windows specific options clearly if (getWindowsIconExecutablePath() or shallAskForWindowsAdminRights() or shallAskForWindowsUIAccessRights() or getWindowsCompanyName() or getWindowsProductName() or getWindowsProductVersion() or getWindowsFileVersion() or getForcedStderrPath() # not yet for other platforms or getForcedStdoutPath() or getWindowsSplashScreen()): Tracing.options_logger.warning( "Using Windows specific options has no effect on other platforms." ) if options.mingw64 or options.msvc_version: Tracing.options_logger.warning( "Requesting Windows specific compilers has no effect on other platforms." ) if isMingw64() and getMsvcVersion(): Tracing.options_logger.sysexit( "Requesting both Windows specific compilers makes no sense.") if getMsvcVersion() and getMsvcVersion() not in ("list", "latest"): if getMsvcVersion().count(".") != 1 or not all( x.isdigit() for x in getMsvcVersion().split(".")): Tracing.options_logger.sysexit( "For --msvc only values 'latest', 'info', and 'X.Y' values are allowed, but not %r." % getMsvcVersion()) if isOnefileMode(): standalone_mode = "onefile" elif isStandaloneMode(): standalone_mode = "standalone" else: standalone_mode = None if standalone_mode and not hasStandaloneSupportedOS(): Tracing.options_logger.warning( "Standalone mode on %s is not known to be supported, might fail to work." % getOS()) if options.follow_all and shallMakeModule(): Tracing.optimization_logger.sysexit("""\ In module mode you must follow modules more selectively, and e.g. should \ not include standard library or all foreign modules or else it will fail \ to work. You can selectively add them with '--follow-import-to=name' though.""" ) if options.follow_all and standalone_mode: Tracing.options_logger.info( "Following all imports is the default for %s mode and need not be specified." % standalone_mode) if options.follow_none and standalone_mode: Tracing.options_logger.warning( "Following no imports is unlikely to work for %s mode and should not be specified." % standalone_mode) if options.follow_stdlib and not standalone_mode: Tracing.options_logger.warning( "Following imports to stdlib is unlikely to work without --standalone/--onefile and should not be specified." ) if (not shallDumpBuiltTreeXML() and not standalone_mode and not options.follow_all and not options.follow_none and not options.follow_modules and not options.follow_stdlib and not options.include_modules and not options.include_packages and not options.include_extra and not options.follow_not_modules): Tracing.options_logger.warning( """You did not specify to follow or include anything but main %s. Check options and \ make sure that is intended.""" % ("module" if shallMakeModule() else "program")) if options.dependency_tool: Tracing.options_logger.warning( "Using removed option '--windows-dependency-tool' is deprecated and has no impact anymore." ) if shallMakeModule() and options.static_libpython == "yes": Tracing.options_logger.warning( "In module mode, providing '--static-libpython' has no effect, it's not used." ) options.static_libpython = "no" if (not isPgoMode() and not isPythonPgoMode() and (getPgoArgs() or getPgoExecutable())): Tracing.optimization_logger.warning( "Providing PGO arguments without enabling PGO mode has no effect.") if isPgoMode(): if isStandaloneMode(): Tracing.optimization_logger.warning( "Using PGO with standalone/onefile mode is not currently working. Expect errors." ) if shallMakeModule(): Tracing.optimization_logger.warning( "Using PGO with module mode is not currently working. Expect errors." ) if (options.static_libpython == "auto" and not shallMakeModule() and not shallDumpBuiltTreeXML() and not shallUseStaticLibPython() and getSystemStaticLibPythonPath() is not None): Tracing.options_logger.info( """Detected static libpython to exist, consider '--static-libpython=yes' for better performance, \ but errors may happen.""") if not shallExecuteImmediately(): if shallRunInDebugger(): Tracing.options_logger.warning( "The '--debugger' option has no effect outside of '--debug' without '--run' option." ) if not shallClearPythonPathEnvironment(): Tracing.options_logger.warning( "The '--execute-with-pythonpath' option has no effect without '--run' option." )
def main(): # PyLint for Python3 thinks we import from ourselves if we really # import from package, pylint:disable=I0021,no-name-in-module # Also high complexity. # pylint: disable=too-many-branches,too-many-locals,too-many-statements if "NUITKA_BINARY_NAME" in os.environ: sys.argv[0] = os.environ["NUITKA_BINARY_NAME"] if "NUITKA_PYTHONPATH" in os.environ: # Restore the PYTHONPATH gained from the site module, that we chose not # to have imported. pylint: disable=eval-used sys.path = eval(os.environ["NUITKA_PYTHONPATH"]) del os.environ["NUITKA_PYTHONPATH"] else: # Remove path element added for being called via "__main__.py", this can # only lead to trouble, having e.g. a "distutils" in sys.path that comes # from "nuitka.distutils". sys.path = [ path_element for path_element in sys.path if os.path.dirname(os.path.abspath(__file__)) != path_element ] # For re-execution, we might not have done this. from nuitka import Options # isort:skip Options.parseArgs() import logging # isort:skip logging.basicConfig(format="Nuitka:%(levelname)s:%(message)s") # We don't care, and these are triggered by run time calculations of "range" and # others, while on python2.7 they are disabled by default. warnings.simplefilter("ignore", DeprecationWarning) # We will run with the Python configuration as specified by the user, if it does # not match, we restart ourselves with matching configuration. needs_reexec = False current_version = "%d.%d" % (sys.version_info[0], sys.version_info[1]) if sys.flags.no_site == 0: needs_reexec = True # The hash randomization totally changes the created source code created, # changing it every single time Nuitka is run. This kills any attempt at # caching it, and comparing generated source code. While the created binary # actually may still use it, during compilation we don't want to. So lets # disable it. if os.environ.get("PYTHONHASHSEED", "-1") != '0': needs_reexec = True # In case we need to re-execute. if needs_reexec: if not Options.isAllowedToReexecute(): sys.exit( "Error, not allowed to re-execute, but that would be needed.") our_filename = sys.modules[__name__].__file__ # Execute with full path as the process name, so it can find itself and its # libraries. args = [sys.executable, sys.executable] if current_version >= "3.7" and sys.flags.utf8_mode: args += ["-X", "utf8"] args += [ "-S", our_filename, ] os.environ["NUITKA_BINARY_NAME"] = sys.modules["__main__"].__file__ if Options.is_nuitka_run: args.append("--run") # Same arguments as before. args += sys.argv[1:] + list(Options.getMainArgs()) os.environ["NUITKA_PYTHONPATH"] = repr(sys.path) from nuitka.importing.PreloadedPackages import detectPreLoadedPackagePaths, detectPthImportedPackages os.environ["NUITKA_NAMESPACES"] = repr(detectPreLoadedPackagePaths()) if "site" in sys.modules: os.environ["NUITKA_SITE_FILENAME"] = sys.modules["site"].__file__ os.environ["NUITKA_PTH_IMPORTED"] = repr( detectPthImportedPackages()) os.environ["NUITKA_SITE_FLAG"] = str(sys.flags.no_site) \ if "no_site" not in Options.getPythonFlags() \ else '1' os.environ["PYTHONHASHSEED"] = '0' from nuitka.utils import Execution # isort:skip Execution.callExec(args) if Options.isShowMemory(): from nuitka.utils import MemoryUsage MemoryUsage.startMemoryTracing() # Inform the user about potential issues. from nuitka.PythonVersions import getSupportedPythonVersions if current_version not in getSupportedPythonVersions(): # Do not disturb run of automatic tests, detected from the presence of # that environment variable. if "PYTHON" not in os.environ: logging.warning( "The version '%s' is not currently supported. Expect problems.", current_version) if "NUITKA_NAMESPACES" in os.environ: # Restore the detected name space packages, that were force loaded in # site.py, and will need a free pass later on. pylint: disable=eval-used from nuitka.importing.PreloadedPackages import setPreloadedPackagePaths setPreloadedPackagePaths(eval(os.environ["NUITKA_NAMESPACES"])) del os.environ["NUITKA_NAMESPACES"] if "NUITKA_PTH_IMPORTED" in os.environ: # Restore the packages that the ".pth" files asked to import. # pylint: disable=eval-used from nuitka.importing.PreloadedPackages import setPthImportedPackages setPthImportedPackages(eval(os.environ["NUITKA_PTH_IMPORTED"])) del os.environ["NUITKA_PTH_IMPORTED"] # Now the real main program of Nuitka can take over. from nuitka import MainControl # isort:skip MainControl.main() if Options.isShowMemory(): MemoryUsage.showMemoryTrace()
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", ] # 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 # 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") 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 == "PasslibUsing.py": # For the warnings. extra_flags.append("ignore_warnings") if filename == "PySideUsing.py": # TODO: Disabled due to lack of upstream support. reportSkip("PySide not supported yet", ".", filename) continue if filename == "Win32ComUsing.py": # For the warnings. extra_flags.append("ignore_warnings") if filename.startswith(("PySide", "PyQt")): # 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 plug-in information. if getPythonVendor() != "Anaconda": extra_flags.append("plugin_enable:qt-plugins") else: # For the plug-in not used information. extra_flags.append("ignore_warnings") 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) 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 loaded_filename.startswith(current_dir): continue if loaded_filename.startswith(os.path.abspath(current_dir)): 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) if search_mode.abortIfExecuted(): break search_mode.finish()
def main(): """ Main program flow of Nuitka At this point, options will be parsed already, Nuitka will be executing in the desired version of Python with desired flags, and we just get to execute the task assigned. We might be asked to only re-compile generated C++, dump only an XML representation of the internal node tree after optimization, etc. """ # Main has to fulfill many options, leading to many branches and statements # to deal with them. pylint: disable=too-many-branches filename = Options.getPositionalArgs()[0] # Inform the importing layer about the main script directory, so it can use # it when attempting to follow imports. Importing.setMainScriptDirectory( main_dir=os.path.dirname(os.path.abspath(filename))) # Detect to be frozen modules if any, so we can consider to not recurse # to them. if Options.isStandaloneMode(): for module in detectEarlyImports(): ModuleRegistry.addUncompiledModule(module) if module.getName() == "site": origin_prefix_filename = os.path.join( os.path.dirname(module.getCompileTimeFilename()), "orig-prefix.txt") if os.path.isfile(origin_prefix_filename): data_files.append((filename, "orig-prefix.txt")) # Turn that source code into a node tree structure. try: main_module = createNodeTree(filename=filename) except (SyntaxError, IndentationError) as e: # Syntax or indentation errors, output them to the user and abort. If # we are not in full compat, and user has not specified the Python # versions he wants, tell him about the potential version problem. error_message = SyntaxErrors.formatOutput(e) if not Options.isFullCompat() and \ Options.getIntendedPythonVersion() is None: if python_version < 300: suggested_python_version_str = getSupportedPythonVersions()[-1] else: suggested_python_version_str = "2.7" error_message += """ Nuitka is very syntax compatible with standard Python. It is currently running with Python version '%s', you might want to specify more clearly with the use of e.g. '--python-version=%s' option, if that's not the one expected. """ % (python_version_str, suggested_python_version_str) sys.exit(error_message) if Options.shallDumpBuiltTreeXML(): for module in ModuleRegistry.getDoneModules(): dumpTreeXML(module) elif Options.shallDisplayBuiltTree(): displayTree(main_module) else: result, options = compileTree(main_module=main_module) # Exit if compilation failed. if not result: sys.exit(1) if Options.shallNotDoExecCppCall(): sys.exit(0) # Remove the source directory (now build directory too) if asked to. if Options.isRemoveBuildDir(): removeDirectory(path=getSourceDirectoryPath(main_module), ignore_errors=False) if Options.isStandaloneMode(): binary_filename = options["result_name"] + ".exe" standalone_entry_points.insert(0, (None, binary_filename, None)) dist_dir = getStandaloneDirectoryPath(main_module) for module in ModuleRegistry.getDoneUserModules(): standalone_entry_points.extend( Plugins.considerExtraDlls(dist_dir, module)) for module in ModuleRegistry.getUncompiledModules(): standalone_entry_points.extend( Plugins.considerExtraDlls(dist_dir, module)) copyUsedDLLs(dist_dir=dist_dir, standalone_entry_points=standalone_entry_points) for module in ModuleRegistry.getDoneModules(): data_files.extend(Plugins.considerDataFiles(module)) for source_filename, target_filename in data_files: target_filename = os.path.join( getStandaloneDirectoryPath(main_module), target_filename) makePath(os.path.dirname(target_filename)) shutil.copy2(source_filename, target_filename) # Modules should not be executable, but Scons creates them like it, fix # it up here. if Utils.getOS() != "Windows" and Options.shallMakeModule(): subprocess.call(("chmod", "-x", getResultFullpath(main_module))) # Execute the module immediately if option was given. if Options.shallExecuteImmediately(): if Options.shallMakeModule(): executeModule( tree=main_module, clean_path=Options.shallClearPythonPathEnvironment()) else: executeMain( binary_filename=getResultFullpath(main_module), clean_path=Options.shallClearPythonPathEnvironment())
def commentArgs(): """Comment on options, where we know something is not having the intended effect.""" # A ton of cases to consider, pylint: disable=too-many-boolean-expressions,too-many-branches # Inform the user about potential issues with the running version. e.g. unsupported # version. if python_version_str not in getSupportedPythonVersions(): # Do not disturb run of automatic tests with, detected from the presence of # that environment variable. if "PYTHON" not in os.environ: Tracing.general.warning( "The version %r is not currently supported. Expect problems." % python_version_str, ) default_reference_mode = ("runtime" if shallMakeModule() or isStandaloneMode() else "original") if getFileReferenceMode() is None: options.file_reference_mode = default_reference_mode else: if options.file_reference_mode != default_reference_mode: Tracing.options_logger.warning( "Using non-default file reference mode %r rather than %r may cause runtime issues." % (getFileReferenceMode(), default_reference_mode)) else: Tracing.options_logger.info( "Using default file reference mode %r need not be specified." % default_reference_mode) if getOS() != "Windows": if (getWindowsIconExecutablePath() or shallAskForWindowsAdminRights() or shallAskForWindowsUIAccessRights() or getWindowsCompanyName() or getWindowsProductName() or getWindowsProductVersion() or getWindowsFileVersion() or isWindowsOnefileTempDirMode() or getWindowsOnefileTempDirSpec(use_default=False) or getForcedStderrPath() # not yet for other platforms or getForcedStdoutPath()): Tracing.options_logger.warning( "Using Windows specific options has no effect on other platforms." ) if not isOnefileMode() and ( isWindowsOnefileTempDirMode() or getWindowsOnefileTempDirSpec(use_default=False)): Tracing.options_logger.warning( "Using onefile specific option for Windows without --onefile enabled." ) if isOnefileMode(): standalone_mode = "onefile" elif isStandaloneMode(): standalone_mode = "standalone" else: standalone_mode = None if standalone_mode and getOS() == "NetBSD": Tracing.options_logger.warning( "Standalone mode on NetBSD is not functional, due to $ORIGIN linkage not being supported." ) if options.recurse_all and standalone_mode: if standalone_mode: Tracing.options_logger.info( "Following all imports is the default for %s mode and need not be specified." % standalone_mode) if options.recurse_none and standalone_mode: if standalone_mode: Tracing.options_logger.warning( "Following no imports is unlikely to work for %s mode and should not be specified." % standalone_mode) if options.dependency_tool: Tracing.options_logger.warning( "Using removed option '--windows-dependency-tool' is deprecated and has no impact anymore." )
def main(): # PyLint for Python3 thinks we import from ourselves if we really # import from package, pylint:disable=I0021,no-name-in-module # Also high complexity. # pylint: disable=too-many-branches,too-many-locals,too-many-statements if "NUITKA_BINARY_NAME" in os.environ: sys.argv[0] = os.environ["NUITKA_BINARY_NAME"] if "NUITKA_PYTHONPATH" in os.environ: # Restore the PYTHONPATH gained from the site module, that we chose not # to have imported. pylint: disable=eval-used sys.path = eval(os.environ["NUITKA_PYTHONPATH"]) del os.environ["NUITKA_PYTHONPATH"] else: # Remove path element added for being called via "__main__.py", this can # only lead to trouble, having e.g. a "distutils" in sys.path that comes # from "nuitka.distutils". sys.path = [ path_element for path_element in sys.path if os.path.dirname(os.path.abspath(__file__)) != path_element ] # For re-execution, we might not have done this. from nuitka import Options # isort:skip Options.parseArgs() import logging # isort:skip logging.basicConfig(format="Nuitka:%(levelname)s:%(message)s") # We don't care, and these are triggered by run time calculations of "range" and # others, while on python2.7 they are disabled by default. warnings.simplefilter("ignore", DeprecationWarning) # We will run with the Python configuration as specified by the user, if it does # not match, we restart ourselves with matching configuration. needs_reexec = False current_version = "%d.%d" % (sys.version_info[0], sys.version_info[1]) if sys.flags.no_site == 0: needs_reexec = True # The hash randomization totally changes the created source code created, # changing it every single time Nuitka is run. This kills any attempt at # caching it, and comparing generated source code. While the created binary # actually may still use it, during compilation we don't want to. So lets # disable it. if os.environ.get("PYTHONHASHSEED", "-1") != "0": needs_reexec = True # In case we need to re-execute. if needs_reexec: if not Options.isAllowedToReexecute(): sys.exit("Error, not allowed to re-execute, but that would be needed.") our_filename = sys.modules[__name__].__file__ # Execute with full path as the process name, so it can find itself and its # libraries. args = [sys.executable, sys.executable] if current_version >= "3.7" and sys.flags.utf8_mode: args += ["-X", "utf8"] args += ["-S", our_filename] os.environ["NUITKA_BINARY_NAME"] = sys.modules["__main__"].__file__ if Options.is_nuitka_run: args.append("--run") # Same arguments as before. args += sys.argv[1:] + list(Options.getMainArgs()) os.environ["NUITKA_PYTHONPATH"] = repr(sys.path) from nuitka.importing.PreloadedPackages import ( detectPreLoadedPackagePaths, detectPthImportedPackages, ) os.environ["NUITKA_NAMESPACES"] = repr(detectPreLoadedPackagePaths()) if "site" in sys.modules: os.environ["NUITKA_SITE_FILENAME"] = sys.modules["site"].__file__ os.environ["NUITKA_PTH_IMPORTED"] = repr(detectPthImportedPackages()) os.environ["NUITKA_SITE_FLAG"] = ( str(sys.flags.no_site) if "no_site" not in Options.getPythonFlags() else "1" ) os.environ["PYTHONHASHSEED"] = "0" from nuitka.utils import Execution # isort:skip Execution.callExec(args) if Options.isShowMemory(): from nuitka.utils import MemoryUsage MemoryUsage.startMemoryTracing() # Inform the user about potential issues. from nuitka.PythonVersions import getSupportedPythonVersions if current_version not in getSupportedPythonVersions(): # Do not disturb run of automatic tests, detected from the presence of # that environment variable. if "PYTHON" not in os.environ: logging.warning( "The version '%s' is not currently supported. Expect problems.", current_version, ) if "NUITKA_NAMESPACES" in os.environ: # Restore the detected name space packages, that were force loaded in # site.py, and will need a free pass later on. pylint: disable=eval-used from nuitka.importing.PreloadedPackages import setPreloadedPackagePaths setPreloadedPackagePaths(eval(os.environ["NUITKA_NAMESPACES"])) del os.environ["NUITKA_NAMESPACES"] if "NUITKA_PTH_IMPORTED" in os.environ: # Restore the packages that the ".pth" files asked to import. # pylint: disable=eval-used from nuitka.importing.PreloadedPackages import setPthImportedPackages setPthImportedPackages(eval(os.environ["NUITKA_PTH_IMPORTED"])) del os.environ["NUITKA_PTH_IMPORTED"] # Now the real main program of Nuitka can take over. from nuitka import MainControl # isort:skip MainControl.main() if Options.isShowMemory(): MemoryUsage.showMemoryTrace()