def main(): parser = OptionParser() parser.add_option( "--mode", action="store", dest="mode", default=None, help="""\ The mode of update, prerelease, hotfix, release, auto (default auto determines from branch).""", ) options, positional_args = parser.parse_args() if positional_args: parser.print_help() sys.exit("\nError, no positional argument allowed.") # Go its own directory, to have it easy with path knowledge. goHome() with openTextFile("nuitka/Version.py", "r") as f: option_lines = f.readlines() (version_line,) = [line for line in option_lines if line.startswith("Nuitka V")] old_version = version_line[8:].rstrip() mode = options.mode branch_name = getBranchName() if mode is None: if branch_name.startswith("hotfix/"): mode = "hotfix" elif branch_name == "main" or branch_name.startswith("release/"): mode = "release" elif branch_name == "develop": mode = "prerelease" else: sys.exit("Error, cannot detect mode from branch name '%s'." % branch_name) new_version = getBumpedVersion(mode, old_version) my_print("Bumped %s '%s' -> '%s'." % (mode, old_version, new_version)) with openTextFile("nuitka/Version.py", "w") as options_file: for line in option_lines: if line.startswith("Nuitka V"): line = "Nuitka V" + new_version + "\n" options_file.write(line) # Debian is currently in not freeze, change to "experimental" once that changes. updateDebianChangelog(old_version, new_version, "unstable")
def writeSconsReport(env, source_dir): with openTextFile(os.path.join(source_dir, "scons-report.txt"), "w") as report_file: # We are friends to get at this debug info, pylint: disable=protected-access for key, value in sorted(env._dict.items()): if type(value) is list and all( isinstance(v, basestring) for v in value): value = repr(value) if not isinstance(value, basestring): continue if key.startswith(("_", "CONFIGURE")): continue if key in ("MSVSSCONS", "BUILD_DIR", "IDLSUFFIXES", "DSUFFIXES"): continue # TODO: For these kinds of prints, maybe have our own method of doing them # rather than print, or maybe just json or something similar. print(key + "=" + value, file=report_file) print("gcc_mode=%s" % env.gcc_mode, file=report_file) print("clang_mode=%s" % env.clang_mode, file=report_file) print("msvc_mode=%s" % env.msvc_mode, file=report_file) print("mingw_mode=%s" % env.mingw_mode, file=report_file) print("clangcl_mode=%s" % env.clangcl_mode, file=report_file) print("PATH=%s" % os.environ["PATH"], file=report_file)
def writeConstantsDataSource(): with openTextFile(constants_generated_filename, "w") as output: if not env.c11_mode: output.write('extern "C" {') output.write(""" // Constant data for the program. #if !defined(_NUITKA_EXPERIMENTAL_WRITEABLE_CONSTANTS) const #endif unsigned char constant_bin_data[] =\n{\n """) with open(constants_bin_filename, "rb") as f: content = f.read() for count, stream_byte in enumerate(content): if count % 16 == 0: if count > 0: output.write("\n") output.write(" ") if str is bytes: stream_byte = ord(stream_byte) output.write(" 0x%02x," % stream_byte) output.write("\n};\n") if not env.c11_mode: output.write("}")
def writeImportedModulesNamesToCache(module_name, source_code, used_modules): cache_name = makeCacheName(module_name, source_code) with openTextFile(_getCacheFilename(cache_name, "txt"), "w") as modules_cache_file: for used_module_name, _filename in used_modules: modules_cache_file.write(used_module_name.asString() + "\n")
def createDefinitionsFile(source_dir, filename, definitions): build_definitions_filename = os.path.join(source_dir, filename) with openTextFile(build_definitions_filename, "w") as f: for key, value in sorted(definitions.items()): if type(value) is int: f.write("#define %s %s\n" % (key, value)) else: f.write("#define %s %s\n" % (key, makeCLiteral(value)))
def putFileHashContent(filename): with openTextFile(filename, "r") as input_file: new_hash = check_output(["git", "hash-object", "-w", "--stdin"], stdin=input_file) if str is not bytes: new_hash = new_hash.decode("utf8") assert new_hash return new_hash.rstrip()
def _discardDebianChangelogLastEntry(): changelog_lines = getFileContents("debian/changelog").splitlines() with openTextFile("debian/changelog", "w") as output: first = True for line in changelog_lines[1:]: if line.startswith("nuitka") and first: first = False if not first: output.write(line + "\n")
def makeManpage(python, suffix): cmd = [ "help2man", "-n", "the Python compiler", "--no-discard-stderr", "--no-info", "--include", "doc/nuitka-man-include.txt", "%s ./bin/nuitka" % python, ] with openTextFile("doc/nuitka%s.1" % suffix, "wb") as output: check_call(cmd, stdout=output) cmd[-1] += "-run" with openTextFile("doc/nuitka%s-run.1" % suffix, "wb") as output: check_call(cmd, stdout=output) for manpage in ("doc/nuitka%s.1" % suffix, "doc/nuitka%s-run.1" % suffix): manpage_contents = getFileContents(manpage).splitlines() new_contents = [] mark = False for count, line in enumerate(manpage_contents): if mark: line = ".SS " + line + ".BR\n" mark = False elif line == ".IP\n" and manpage_contents[count + 1].endswith(":\n"): mark = True continue if line == r"\fB\-\-g\fR++\-only" + "\n": line = r"\fB\-\-g\++\-only\fR" + "\n" new_contents.append(line) putTextFileContents(manpage, contents=new_contents)
def updateDebianChangelog(old_version, new_version, distribution): debian_version = new_version.replace("rc", "~rc") + "+ds-1" os.environ["DEBEMAIL"] = "Kay Hayen <*****@*****.**>" if "rc" in new_version: if "rc" in old_version: # Initial final release after pre-releases. _discardDebianChangelogLastEntry() message = "New upstream pre-release." if checkNuitkaChangelog() != "draft": changelog = getFileContents("Changelog.rst") title = "Nuitka Release " + new_version[:-3] + " (Draft)" found = False with openTextFile("Changelog.rst", "w") as changelog_file: for line in changelog.splitlines(): if not found: if line.startswith("***") and line.endswith("***"): found = True marker = "*" * (len(title) + 2) changelog_file.write( marker + "\n " + title + "\n" + marker + "\n\n" ) changelog_file.write("This release is not done yet.\n\n") changelog_file.write(line + "\n") continue changelog_file.write(line + "\n") assert found else: if "rc" in old_version: # Initial final release after pre-releases. _discardDebianChangelogLastEntry() message = "New upstream release." else: # Initial final release after pre-releases. # Hotfix release after previous final or hotfix release. message = "New upstream hotfix release." _callDebchange("--newversion=%s" % debian_version, message) _callDebchange("-r", "--distribution", distribution, "")
def publishCoverageData(): def copyToGlobalCoverageData(source, target): coverage_dir = os.environ.get("COVERAGE_DIR") if coverage_dir is None: return check_call(("scp", source, os.path.join(coverage_dir, target))) if os.name == "nt": suffix = "win" else: import platform suffix = platform.uname()[0] + "." + platform.uname()[4] with openTextFile("data.coverage", "w") as data_file: source_dir = os.path.abspath(os.path.dirname( os.path.dirname(__file__))) with withDirectoryChange(source_dir): nuitka_id = check_output("git rev-parse HEAD".split()) nuitka_id = nuitka_id.strip() if sys.version_info > (3, ): nuitka_id = nuitka_id.decode() data_file.write("NUITKA_SOURCE_DIR=%r\n" % source_dir) data_file.write("NUITKA_COMMIT=%r\n" % nuitka_id) copyToGlobalCoverageData("data.coverage", "meta.coverage." + suffix) def makeCoverageRelative(filename): """Normalize coverage data.""" data = getFileContents(filename) data = data.replace( (os.path.abspath(".") + os.path.sep).replace("\\", "\\\\"), "") if os.path.sep != "/": data.replace(os.path.sep, "/") putTextFileContents(filename, contents=data) coverage_file = os.environ.get("COVERAGE_FILE", ".coverage") makeCoverageRelative(coverage_file) copyToGlobalCoverageData(coverage_file, "data.coverage." + suffix)
def checkNuitkaChangelog(): with openTextFile("Changelog.rst", "r") as f: # First paragraph doesn't count while True: line = f.readline().strip() if line.startswith("***") and line.endswith("***"): break # Second line is the actual title. line = f.readline() if "(Draft)" in line: return "draft" else: return "final"
def makeGccUseLinkerFile(source_dir, source_files, env): tmp_linker_filename = os.path.join(source_dir, "@link_input.txt") env["LINKCOM"] = env["LINKCOM"].replace( "$SOURCES", "@%s" % env.get("ESCAPE", lambda x: x)(tmp_linker_filename) ) with openTextFile(tmp_linker_filename, "w") as tmpfile: for filename in source_files: filename = ".".join(filename.split(".")[:-1]) + ".o" if os.name == "nt": filename = filename.replace(os.path.sep, "/") tmpfile.write('"%s"\n' % filename) tmpfile.write(env.subst("$SOURCES"))
def __init__(self, filename): self.count = 0 filename = os.path.join(OutputDirectories.getSourceDirectoryPath(), filename) self.file = openTextFile(filename, "wb") if python_version < 0x300: self.pickle = pickle.Pickler(self.file, -1) else: self.pickle = pickle._Pickler( # pylint: disable=I0021,protected-access self.file, -1) self.pickle.dispatch[type] = _pickleAnonValues self.pickle.dispatch[type(Ellipsis)] = _pickleAnonValues self.pickle.dispatch[type(NotImplemented)] = _pickleAnonValues if type(sys.version_info) is not tuple: self.pickle.dispatch[type(sys.version_info)] = _pickleAnonValues # Standard pickling doesn't work with our necessary wrappers. if python_version >= 0x3A0: self.pickle.dispatch[UnionType] = _pickeUnionType
def withFileOpenedAndAutoformatted(filename): my_print("Creating %r ..." % filename) tmp_filename = filename + ".tmp" with openTextFile(tmp_filename, "w") as output: yield output autoformat(filename=tmp_filename, git_stage=None, effective_filename=filename, trace=False) # No idea why, but this helps. if os.name == "nt": autoformat( filename=tmp_filename, git_stage=None, effective_filename=filename, trace=False, ) shutil.copy(tmp_filename, filename) os.unlink(tmp_filename)
def action(stage_dir, root, path): command = [ sys.executable, os.path.join("..", "..", "bin", "nuitka"), "--stand", "--run", "--output-dir=%s" % stage_dir, "--remove-output", ] filename = os.path.join(stage_dir, "importer.py") assert path.startswith(root) module_name = path[len(root) + 1:] module_name = module_name.split(".")[0] module_name = module_name.replace(os.path.sep, ".") module_name = ModuleName(module_name) with openTextFile(filename, "w") as output: plugin_names = set(["pylint-warnings", "anti-bloat"]) if module_name.hasNamespace("PySide2"): plugin_names.add("pyside2") elif module_name.hasNamespace("PySide6"): plugin_names.add("pyside2") elif module_name.hasNamespace("PyQt5"): plugin_names.add("pyqt5") else: # TODO: We do not have a noqt plugin yet. plugin_names.add("pyqt5") for plugin_name in plugin_names: output.write("# nuitka-project: --enable-plugin=%s\n" % plugin_name) # Make it an error to find unwanted bloat compiled in. output.write("# nuitka-project: --noinclude-default-mode=error\n") output.write("import " + module_name.asString() + "\n") output.write("print('OK.')") command += os.environ.get("NUITKA_EXTRA_OPTIONS", "").split() command.append(filename) if checkSucceedsWithCPython(filename): try: output = check_output(command).splitlines() except Exception: # only trying to check for no exception, pylint: disable=try-except-raise raise else: assert os.path.exists(filename[:-3] + ".dist") binary_filename = os.path.join( filename[:-3] + ".dist", "importer.exe" if os.name == "nt" else "importer", ) loaded_filenames = getRuntimeTraceOfLoadedFiles( logger=test_logger, command=[binary_filename], ) outside_accesses = checkLoadedFileAccesses( loaded_filenames=loaded_filenames, current_dir=os.getcwd()) if outside_accesses: displayError(None, filename) displayRuntimeTraces(test_logger, binary_filename) test_logger.warning( "Should not access these file(s): '%r'." % outside_accesses) search_mode.onErrorDetected(1) if output[-1] != b"OK.": my_print(" ".join(command)) my_print(filename) my_print(output) test_logger.sysexit("FAIL.") my_print("OK.") assert not outside_accesses, outside_accesses shutil.rmtree(filename[:-3] + ".dist") else: my_print("SKIP (does not work with CPython)")
def parseArgs(will_reexec): # singleton with many cases checking the options right away. # pylint: disable=global-statement,too-many-branches,too-many-locals,too-many-statements global is_nuitka_run, options, positional_args, extra_args, is_debug, is_nondebug, is_fullcompat if os.name == "nt": # Windows store Python's don't allow looking at the python, catch that. try: with open(sys.executable): pass except OSError: Tracing.general.sysexit( "Error, the Python from Windows store is not supported, check the User Manual of Nuitka ." ) is_nuitka_run, options, positional_args, extra_args = parseOptions( logger=Tracing.options_logger) if options.quiet or int(os.environ.get("NUITKA_QUIET", "0")): Tracing.setQuiet() if not will_reexec and not shallDumpBuiltTreeXML(): Tracing.options_logger.info("Used command line options: %s" % " ".join(sys.argv[1:])) if options.progress_bar and not will_reexec: Progress.enableProgressBar() if options.verbose_output and not will_reexec: Tracing.optimization_logger.setFileHandle( # Can only have unbuffered binary IO in Python3, therefore not disabling buffering here. openTextFile(options.verbose_output, "w", encoding="utf8")) options.verbose = True Tracing.optimization_logger.is_quiet = not options.verbose if options.show_inclusion_output and not will_reexec: Tracing.inclusion_logger.setFileHandle( # Can only have unbuffered binary IO in Python3, therefore not disabling buffering here. openTextFile(options.show_inclusion_output, "w", encoding="utf8")) options.show_inclusion = True Tracing.progress_logger.is_quiet = not options.show_progress # Onefile implies standalone build. if options.is_onefile: options.is_standalone = True # Provide a tempdir spec implies onefile tempdir. if options.windows_onefile_tempdir_spec: options.is_windows_onefile_tempdir = True # Standalone mode implies an executable, not importing "site" module, which is # only for this machine, recursing to all modules, and even including the # standard library. if options.is_standalone: if not options.executable: Tracing.options_logger.sysexit("""\ Error, conflicting options, cannot make standalone module, only executable. Modules are supposed to be imported to an existing Python installation, therefore it makes no sense to include a Python runtime.""") for any_case_module in getShallFollowModules(): if any_case_module.startswith("."): bad = True else: for char in "/\\:": if char in any_case_module: bad = True break else: bad = False if bad: Tracing.options_logger.sysexit("""\ Error, '--follow-import-to' takes only module names, not directory path '%s'.""" % any_case_module) for no_case_module in getShallFollowInNoCase(): if no_case_module.startswith("."): bad = True else: for char in "/\\:": if char in no_case_module: bad = True break else: bad = False if bad: Tracing.options_logger.sysexit("""\ Error, '--nofollow-import-to' takes only module names, not directory path '%s'.""" % no_case_module) scons_python = getPythonPathForScons() if scons_python is not None and not os.path.isfile(scons_python): Tracing.options_logger.sysexit( "Error, no such Python binary %r, should be full path." % scons_python) if options.output_filename is not None and ( (isStandaloneMode() and not isOnefileMode()) or shallMakeModule()): Tracing.options_logger.sysexit("""\ Error, may only specify output filename for acceleration and onefile mode, but not for module mode where filenames are mandatory, and not for standalone where there is a sane default used inside the dist folder.""") if getOS() == "Linux": if len(getIconPaths()) > 1: Tracing.options_logger.sysexit( "Error, can only use one icon on Linux.") for icon_path in getIconPaths(): if "#" in icon_path and isWin32Windows(): icon_path, icon_index = icon_path.rsplit("#", 1) if not icon_index.isdigit() or int(icon_index) < 0: Tracing.options_logger.sysexit( "Error, icon number in %r not valid." % (icon_path + "#" + icon_index)) if not os.path.exists(icon_path): Tracing.options_logger.sysexit( "Error, icon path %r does not exist." % icon_path) if getWindowsIconExecutablePath(): Tracing.options_logger.sysexit( "Error, can only use icons from template executable or from icon files, but not both." ) icon_exe_path = getWindowsIconExecutablePath() if icon_exe_path is not None and not os.path.exists(icon_exe_path): Tracing.options_logger.sysexit("Error, icon path %r does not exist." % icon_exe_path) try: file_version = getWindowsFileVersion() except Exception: # Catch all the things, don't want any interface, pylint: disable=broad-except Tracing.options_logger.sysexit( "Error, file version must be a tuple of up to 4 integer values.") try: product_version = getWindowsProductVersion() except Exception: # Catch all the things, don't want any interface, pylint: disable=broad-except Tracing.options_logger.sysexit( "Error, product version must be a tuple of up to 4 integer values." ) if getWindowsCompanyName() == "": Tracing.options_logger.sysexit( """Error, empty string is not an acceptable company name.""") if getWindowsProductName() == "": Tracing.options_logger.sysexit( """Error, empty string is not an acceptable product name.""") if file_version or product_version or getWindowsVersionInfoStrings(): if not (file_version or product_version) and getWindowsCompanyName(): Tracing.options_logger.sysexit( "Error, company name and file or product version need to be given when any version information is given." ) if isOnefileMode() and not hasOnefileSupportedOS(): Tracing.options_logger.sysexit("Error, unsupported OS for onefile %r" % getOS()) if isOnefileMode() and os.name == "nt": if not getWindowsCompanyName() and not isWindowsOnefileTempDirMode(): Tracing.options_logger.sysexit( """Error, onefile on Windows needs more options. It requires either company name and product version to be given or the selection of onefile temp directory mode. Check --help output.""") if options.recurse_none and options.recurse_all: Tracing.options_logger.sysexit( "Conflicting options '--follow-imports' and '--nofollow-imports' given." ) if getShallIncludePackageData() and not isStandaloneMode(): Tracing.options_logger.sysexit( "Error, package data files are only included in standalone or onefile mode." ) for module_pattern in getShallIncludePackageData(): if (module_pattern.startswith("-") or "/" in module_pattern or "\\" in module_pattern): Tracing.options_logger.sysexit( "Error, '--include-package-data' needs module name or pattern as an argument, not %r." % module_pattern) for module_pattern in getShallFollowModules(): if (module_pattern.startswith("-") or "/" in module_pattern or "\\" in module_pattern): Tracing.options_logger.sysexit( "Error, '--follow-import-to' options needs module name or pattern as an argument, not %r." % module_pattern) for module_pattern in getShallFollowInNoCase(): if (module_pattern.startswith("-") or "/" in module_pattern or "\\" in module_pattern): Tracing.options_logger.sysexit( "Error, '--nofollow-import-to' options needs module name or pattern as an argument, not %r." % module_pattern) for data_file in options.data_files: if "=" not in data_file: Tracing.options_logger.sysexit( "Error, malformed data file description, must specify relative target path with =." ) src, dst = data_file.split("=", 1) if os.path.isabs(dst): Tracing.options_logger.sysexit( "Error, must specify relative target path for data file, not %r." % data_file) if not resolveShellPatternToFilenames(src): Tracing.options_logger.sysexit( "Error, %r does not match any files." % src) for data_dir in options.data_dirs: if "=" not in data_dir: Tracing.options_logger.sysexit( "Error, malformed data dir description, must specify relative target path with '=' separating it." ) src, dst = data_dir.split("=", 1) if os.path.isabs(dst): Tracing.options_logger.sysexit( "Error, must specify relative target path for data dir, not %r as in %r." % (dst, data_dir)) if not os.path.isdir(src): Tracing.options_logger.sysexit( "Error, must specify existing source data directory, not %r as in %r." % (dst, data_dir)) if (options.data_files or options.data_dirs) and not isStandaloneMode(): Tracing.options_logger.sysexit( "Error, data files are only included in standalone or onefile mode." ) for pattern in getShallFollowExtraFilePatterns(): if os.path.isdir(pattern): Tracing.options_logger.sysexit( "Error, pattern %r given to '--include-plugin-files' cannot be a directory name." % pattern) if options.static_libpython == "yes" and getSystemStaticLibPythonPath( ) is None: Tracing.options_logger.sysexit( "Error, static libpython is not found for this Python installation." ) is_debug = _isDebug() is_nondebug = not is_debug is_fullcompat = _isFullCompat()
def main(): setup(suite="test-runners") os.chdir("subject") nuitka_main_path = os.path.join("..", "..", "..", "bin", "nuitka") tmp_dir = getTempDir() command = [ os.environ["PYTHON"], nuitka_main_path, "--enable-plugin=pylint-warnings", "--output-dir=%s" % tmp_dir, "--include-package=package", "--nofollow-import-to=*.tests", "--debug", "--module", "package", ] result = subprocess.call(command) if result != 0: sys.exit(result) os.makedirs(os.path.join(tmp_dir, "package.ext")) copyTree("package", os.path.join(tmp_dir, "package.ext/package")) os.chdir(tmp_dir) # We compile the package non-closed, so we can smuggle in tests # and user code. This is going to be the example code. with openTextFile("package.ext/package/user_provided.py", "w") as output: # TODO: Maybe assert that the type name of a local function and one from # the package are not the same, i.e. we are running inside the compiled # package. output.write(""" from __future__ import print_function import package print("__name__:", package.__name__) print("__package__:", package.__package__) print("__path__:", package.__path__) print("__file__:", package.__file__) # print("__loader__:", package.__loader__) import package.sub_package1 print("__name__:", package.sub_package1.__name__) print("__package__:", package.sub_package1.__package__) print("__path__:", package.sub_package1.__path__) print("__file__:", package.sub_package1.__file__) # print("__loader__:", package.sub_package1.__loader__) import package.sub_package1.tests; print("__name__:", package.sub_package1.tests.__name__) print("__package__:", package.sub_package1.tests.__package__) print("__path__:", package.sub_package1.tests.__path__) print("__file__:", package.sub_package1.tests.__file__) # print("__loader__:", package.sub_package1.tests.__loader__) """) os.makedirs("nose") with openTextFile("nose/usage.txt", "w") as output: pass os.system("find | sort") # Inform about the extra path, format is NUITKA_PACKAGE_fullname where # dots become "_" and should point to the directory where external code # to be loaded will live under. Probably should be an absolute path, but # we avoid it here. os.environ["NUITKA_PACKAGE_package"] = "./package.ext/package" # Lets make sure these to not work. These will be used in the compiled # form only. for module_path in ("__init__.py", "sub_package1__init__.py"): with openTextFile(os.path.join("./package.ext/package", module_path), "w") as output: output.write("assert False") # Check the compiled package is functional for importing. my_print("Running package as basic test:") command = [os.environ["PYTHON"], "-c", "import package"] result = subprocess.call(command) if result != 0: sys.exit(result) my_print("Running nose tests:") # assert os.system(os.environ["PYTHON"] + " -m nose --first-package-wins -s package.sub_package1.tests") == 0 my_print("Running py.test tests:") command = [ os.environ["PYTHON"], "-m", "pytest", "-v", "--pyargs", "package.sub_package1.tests", ] result = subprocess.call(command) if result != 0: sys.exit(result)
def packDistFolderToOnefileLinux(onefile_output_filename, dist_dir, binary_filename): """Pack to onefile binary on Linux. Notes: This is mostly a wrapper around AppImage, which does all the heavy lifting. """ if not locateDLL("fuse"): postprocessing_logger.sysexit("""\ Error, the fuse library (libfuse.so.x from fuse2, *not* fuse3) must be installed for onefile creation to work on Linux.""") # This might be possible to avoid being done with --runtime-file. apprun_filename = os.path.join(dist_dir, "AppRun") putTextFileContents( apprun_filename, contents="""\ #!/bin/bash exec -a $ARGV0 $APPDIR/%s \"$@\"""" % os.path.basename(binary_filename), ) addFileExecutablePermission(apprun_filename) binary_basename = os.path.basename(getResultBasepath()) icon_paths = getIconPaths() assert icon_paths extension = os.path.splitext(icon_paths[0])[1].lower() copyFile(icon_paths[0], getResultBasepath() + extension) putTextFileContents( getResultBasepath() + ".desktop", contents="""\ [Desktop Entry] Name=%(binary_basename)s Exec=%(binary_filename)s Icon=%(binary_basename)s Type=Application Categories=Utility;""" % { "binary_basename": binary_basename, "binary_filename": os.path.basename(binary_filename), }, ) postprocessing_logger.info( "Creating single file from dist folder, this may take a while.") stdout_filename = binary_filename + ".appimage.stdout.txt" stderr_filename = binary_filename + ".appimage.stderr.txt" stdout_file = openTextFile(stdout_filename, "wb") stderr_file = openTextFile(stderr_filename, "wb") command = ( _getAppImageToolPath(for_operation=True, assume_yes_for_downloads=assumeYesForDownloads()), dist_dir, "--comp", getAppImageCompression(), "-n", onefile_output_filename, ) stderr_file.write(b"Executed %r\n" % " ".join(command)) # Starting the process while locked, so file handles are not duplicated, we # need fine grained control over process here, therefore we cannot use the # Execution.executeProcess() function without making it too complex and not # all Python versions allow using with, pylint: disable=consider-using-with # pylint: disable appimagetool_process = subprocess.Popen( command, shell=False, stdin=getNullInput(), stdout=stdout_file, stderr=stderr_file, ) result = appimagetool_process.wait() stdout_file.close() stderr_file.close() if result != 0: # Useless result if there were errors, so now remove it. deleteFile(onefile_output_filename, must_exist=False) stderr = getFileContents(stderr_filename, mode="rb") if b"Text file busy" in stderr: postprocessing_logger.sysexit( "Error, error exit from AppImage because target file is locked." ) if b"modprobe fuse" in stderr: postprocessing_logger.sysexit( "Error, error exit from AppImage because fuse kernel module was not loaded." ) postprocessing_logger.sysexit( "Error, error exit from AppImage, check its outputs '%s' and '%s'." % (stdout_filename, stderr_filename)) if not os.path.exists(onefile_output_filename): postprocessing_logger.sysexit( "Error, expected output file %r not created by AppImage, check its outputs '%s' and '%s'." % (onefile_output_filename, stdout_filename, stderr_filename)) deleteFile(stdout_filename, must_exist=True) deleteFile(stderr_filename, must_exist=True) postprocessing_logger.info("Completed onefile creation.")
def parseArgs(): """Parse the command line arguments :meta private: """ # singleton with many cases checking the options right away. # pylint: disable=global-statement,too-many-branches,too-many-locals,too-many-statements global is_nuitka_run, options, positional_args, extra_args, is_debug, is_nondebug global is_fullcompat, is_report_missing, is_verbose if os.name == "nt": # Windows store Python's don't allow looking at the python, catch that. try: with openTextFile(sys.executable, "rb"): pass except OSError: Tracing.general.sysexit( "Error, the Python from Windows store is not supported, check the User Manual of Nuitka ." ) is_nuitka_run, options, positional_args, extra_args = parseOptions( logger=Tracing.options_logger) is_debug = _isDebug() is_nondebug = not is_debug is_fullcompat = _isFullCompat() # TODO: Have dedicated option for it. is_report_missing = is_debug if options.quiet or int(os.environ.get("NUITKA_QUIET", "0")): Tracing.setQuiet() if not shallDumpBuiltTreeXML(): Tracing.options_logger.info("Used command line options: %s" % " ".join(sys.argv[1:])) if os.environ.get("NUITKA_REEXECUTION") and not isAllowedToReexecute(): Tracing.general.sysexit( "Error, not allowed to re-execute, but that has happened.") if options.progress_bar: Progress.enableProgressBar() if options.verbose_output: Tracing.optimization_logger.setFileHandle( # Can only have unbuffered binary IO in Python3, therefore not disabling buffering here. openTextFile(options.verbose_output, "w", encoding="utf8")) options.verbose = True is_verbose = options.verbose Tracing.optimization_logger.is_quiet = not options.verbose if options.show_inclusion_output: Tracing.inclusion_logger.setFileHandle( # Can only have unbuffered binary IO in Python3, therefore not disabling buffering here. openTextFile(options.show_inclusion_output, "w", encoding="utf8")) options.show_inclusion = True Tracing.progress_logger.is_quiet = not options.show_progress # Onefile implies standalone build. if options.is_onefile: options.is_standalone = True # Standalone implies no_site build if options.is_standalone: options.python_flags.insert(0, "no_site") # Provide a tempdir spec implies onefile tempdir, even on Linux. if options.onefile_tempdir_spec: options.is_onefile_tempdir = True if os.path.normpath(options.onefile_tempdir_spec) == ".": Tracing.options_logger.sysexit("""\ Error, using '.' as a value for '--onefile-tempdir-spec' is not supported, you cannot unpack the onefile payload into the same directory as the binary, as that would overwrite it and cause locking issues as well.""") if options.onefile_tempdir_spec.count("%") % 2 != 0: Tracing.options_logger.warning( """Unmatched '%%' is suspicious for '--onefile-tempdir-spec' and may not do what you want it to do: '%s'""" % options.onefile_tempdir_spec) if options.onefile_tempdir_spec.count("%") == 0: Tracing.options_logger.warning( """Not using any variables for '--onefile-tempdir-spec' should only be done if your program absolutely needs to be in the same path always: '%s'""" % options.onefile_tempdir_spec) if os.path.isabs(options.onefile_tempdir_spec): Tracing.options_logger.warning( """Using an absolute path should be avoided unless you are targeting a very well known environment: '%s'""" % options.onefile_tempdir_spec) elif relpath(options.onefile_tempdir_spec): Tracing.options_logger.warning( """Using an relative path above the executable should be avoided unless you are targeting a very well known environment: '%s'""" % options.onefile_tempdir_spec) # Standalone mode implies an executable, not importing "site" module, which is # only for this machine, recursing to all modules, and even including the # standard library. if options.is_standalone: if options.module_mode: Tracing.options_logger.sysexit("""\ Error, conflicting options, cannot make standalone module, only executable. Modules are supposed to be imported to an existing Python installation, therefore it makes no sense to include a Python runtime.""") for any_case_module in getShallFollowModules(): if any_case_module.startswith("."): bad = True else: for char in "/\\:": if char in any_case_module: bad = True break else: bad = False if bad: Tracing.options_logger.sysexit("""\ Error, '--follow-import-to' takes only module names or patterns, not directory path '%s'.""" % any_case_module) for no_case_module in getShallFollowInNoCase(): if no_case_module.startswith("."): bad = True else: for char in "/\\:": if char in no_case_module: bad = True break else: bad = False if bad: Tracing.options_logger.sysexit("""\ Error, '--nofollow-import-to' takes only module names or patterns, not directory path '%s'.""" % no_case_module) scons_python = getPythonPathForScons() if scons_python is not None and not os.path.isfile(scons_python): Tracing.options_logger.sysexit( "Error, no such Python binary %r, should be full path." % scons_python) if options.output_filename is not None and ( (isStandaloneMode() and not isOnefileMode()) or shallMakeModule()): Tracing.options_logger.sysexit("""\ Error, may only specify output filename for acceleration and onefile mode, but not for module mode where filenames are mandatory, and not for standalone where there is a sane default used inside the dist folder.""") if isLinux(): if len(getIconPaths()) > 1: Tracing.options_logger.sysexit( "Error, can only use one icon file on Linux.") if isMacOS(): if len(getIconPaths()) > 1: Tracing.options_logger.sysexit( "Error, can only use one icon file on macOS.") for icon_path in getIconPaths(): if "#" in icon_path and isWin32Windows(): icon_path, icon_index = icon_path.rsplit("#", 1) if not icon_index.isdigit() or int(icon_index) < 0: Tracing.options_logger.sysexit( "Error, icon number in %r not valid." % (icon_path + "#" + icon_index)) if not os.path.exists(icon_path): Tracing.options_logger.sysexit( "Error, icon path %r does not exist." % icon_path) if getWindowsIconExecutablePath(): Tracing.options_logger.sysexit( "Error, can only use icons from template executable or from icon files, but not both." ) icon_exe_path = getWindowsIconExecutablePath() if icon_exe_path is not None and not os.path.exists(icon_exe_path): Tracing.options_logger.sysexit("Error, icon path %r does not exist." % icon_exe_path) try: file_version = getWindowsFileVersion() except Exception: # Catch all the things, don't want any interface, pylint: disable=broad-except Tracing.options_logger.sysexit( "Error, file version must be a tuple of up to 4 integer values.") try: product_version = getWindowsProductVersion() except Exception: # Catch all the things, don't want any interface, pylint: disable=broad-except Tracing.options_logger.sysexit( "Error, product version must be a tuple of up to 4 integer values." ) if getWindowsCompanyName() == "": Tracing.options_logger.sysexit( """Error, empty string is not an acceptable company name.""") if getWindowsProductName() == "": Tracing.options_logger.sysexit( """Error, empty string is not an acceptable product name.""") splash_screen_filename = getWindowsSplashScreen() if splash_screen_filename is not None: if not os.path.isfile(splash_screen_filename): Tracing.options_logger.sysexit( "Error, specified splash screen image '%s' does not exist." % splash_screen_filename) if file_version or product_version or getWindowsVersionInfoStrings(): if not (file_version or product_version) and getWindowsCompanyName(): Tracing.options_logger.sysexit( "Error, company name and file or product version need to be given when any version information is given." ) if isOnefileMode() and not hasOnefileSupportedOS(): Tracing.options_logger.sysexit("Error, unsupported OS for onefile %r" % getOS()) if options.follow_none and options.follow_all: Tracing.options_logger.sysexit( "Conflicting options '--follow-imports' and '--nofollow-imports' given." ) for module_pattern in getShallIncludePackageData(): if (module_pattern.startswith("-") or "/" in module_pattern or "\\" in module_pattern): Tracing.options_logger.sysexit( "Error, '--include-package-data' needs module name or pattern as an argument, not %r." % module_pattern) for module_pattern in getShallFollowModules(): if (module_pattern.startswith("-") or "/" in module_pattern or "\\" in module_pattern): Tracing.options_logger.sysexit( "Error, '--follow-import-to' options needs module name or pattern as an argument, not %r." % module_pattern) for module_pattern in getShallFollowInNoCase(): if (module_pattern.startswith("-") or "/" in module_pattern or "\\" in module_pattern): Tracing.options_logger.sysexit( "Error, '--nofollow-import-to' options needs module name or pattern as an argument, not %r." % module_pattern) for data_file in options.data_files: if "=" not in data_file: Tracing.options_logger.sysexit( "Error, malformed data file description, must specify relative target path separated with '='." ) if data_file.count("=") == 1: src, dst = data_file.split("=", 1) filenames = resolveShellPatternToFilenames(src) if len(filenames) > 1 and not dst.endswith(("/", os.path.sep)): Tracing.options_logger.sysexit( "Error, pattern '%s' matches more than one file, but target has no trailing slash, not a directory." % src) else: src, dst, pattern = data_file.split("=", 2) filenames = resolveShellPatternToFilenames( os.path.join(src, pattern)) if not filenames: Tracing.options_logger.sysexit( "Error, '%s' does not match any files." % src) if os.path.isabs(dst): Tracing.options_logger.sysexit( "Error, must specify relative target path for data file, not absolute path '%s'." % data_file) for data_dir in options.data_dirs: if "=" not in data_dir: Tracing.options_logger.sysexit( "Error, malformed data dir description, must specify relative target path with '=' separating it." ) src, dst = data_dir.split("=", 1) if os.path.isabs(dst): Tracing.options_logger.sysexit( "Error, must specify relative target path for data dir, not %r as in %r." % (dst, data_dir)) if not os.path.isdir(src): Tracing.options_logger.sysexit( "Error, must specify existing source data directory, not %r as in %r." % (dst, data_dir)) for pattern in getShallFollowExtraFilePatterns(): if os.path.isdir(pattern): Tracing.options_logger.sysexit( "Error, pattern %r given to '--include-plugin-files' cannot be a directory name." % pattern) if options.static_libpython == "yes" and getSystemStaticLibPythonPath( ) is None: Tracing.options_logger.sysexit( "Error, static libpython is not found or not supported for this Python installation." ) if shallUseStaticLibPython() and getSystemStaticLibPythonPath() is None: Tracing.options_logger.sysexit( """Error, usable static libpython is not found for this Python installation. You \ might be missing required packages. Disable with --static-libpython=no" if you don't \ want to install it.""") if isApplePython(): if isStandaloneMode(): Tracing.options_logger.sysexit( "Error, for standalone mode, Apple Python from macOS is not supported, use e.g. CPython instead." ) if str is bytes: Tracing.options_logger.sysexit( "Error, Apple Python 2.7 from macOS is not usable as per Apple decision, use e.g. CPython 2.7 instead." ) if isStandaloneMode() and isLinux( ) and getExecutablePath("patchelf") is None: Tracing.options_logger.sysexit( "Error, standalone mode on Linux requires 'patchelf' to be installed. Use 'apt/dnf/yum install patchelf' first." ) pgo_executable = getPgoExecutable() if pgo_executable and not isPathExecutable(pgo_executable): Tracing.options_logger.sysexit( "Error, path '%s' to binary to use for PGO is not executable." % pgo_executable)