#     Copyright 2026, Kay Hayen, mailto:kay.hayen@gmail.com find license text at end of file


"""Ability to restart Nuitka, needed for removing site module effects and using Python PGO after compile.

Note: This avoids imports at all costs, such that initial startup doesn't do more
than necessary.

spell-checker: ignore execl, Popen
"""

import os
import sys

from .Execution import expandProcessCallForWindows


def callExecProcess(args, shell):
    """Do exec in a portable way preserving exit code.

    On Windows, unfortunately there is no real exec, so we have to spawn
    a new process instead.
    """

    # We better flush these, "os.execl" won't do it anymore.
    sys.stdout.flush()
    sys.stderr.flush()

    # On Windows "os.execl" does not work properly
    if os.name == "nt":
        import subprocess

        args = list(args)
        del args[1]

        args = expandProcessCallForWindows(command=args, shell=shell)

        try:
            # Context manager is not available on all Python versions, pylint: disable=consider-using-with
            process = subprocess.Popen(args=args, shell=shell)
            process.communicate()
            # No point in cleaning up, just exit the hard way.
            try:
                os._exit(process.returncode)
            except OverflowError:
                # Seems negative values go wrong otherwise,
                # see https://bugs.python.org/issue28474
                os._exit(process.returncode - 2**32)
        except KeyboardInterrupt:
            # There was a more relevant stack trace already, so abort this
            # right here.
            os._exit(2)
        except OSError as e:
            print("Error, executing: %s" % e)
            os._exit(2)

    else:
        # The star arguments is the API of execl
        os.execl(*args)


def setLaunchingNuitkaProcessEnvironmentValue(environment_variable_name, value):
    # spell-checker: ignore getpid
    os.environ[environment_variable_name] = str(os.getpid()) + ":" + value


def reExecuteNuitka(pgo_filename):
    # Execute with full path as the process name, so it can find itself and its
    # libraries.
    args = [sys.executable, sys.executable]

    if sys.version_info >= (3, 7) and sys.flags.utf8_mode:
        args += ["-X", "utf8"]

    if sys.version_info >= (3, 11):
        args += ["-X", "frozen_modules=off"]

    if "nuitka.__main__" in sys.modules:
        our_filename = sys.modules["nuitka.__main__"].__file__
    else:
        our_filename = sys.modules["__main__"].__file__

    args += ["-S", our_filename]

    setLaunchingNuitkaProcessEnvironmentValue(
        "NUITKA_BINARY_NAME", sys.modules["__main__"].__file__
    )
    setLaunchingNuitkaProcessEnvironmentValue(
        "NUITKA_PACKAGE_HOME",
        os.path.dirname(os.path.abspath(sys.modules["nuitka"].__path__[0])),
    )

    if pgo_filename is not None:
        args.append("--pgo-python-input=%s" % pgo_filename)
    else:
        setLaunchingNuitkaProcessEnvironmentValue("NUITKA_SYS_PREFIX", sys.prefix)

    # Same arguments as before.
    args += sys.argv[1:]

    from nuitka.importing.PreloadedPackages import (
        detectPreLoadedPackagePaths,
        detectPthImportedPackages,
    )

    os.environ["NUITKA_NAMESPACES"] = repr(detectPreLoadedPackagePaths())

    if "site" in sys.modules:
        site_filename = sys.modules["site"].__file__
        if site_filename.endswith(".pyc"):
            site_filename = site_filename[:-4] + ".py"

        setLaunchingNuitkaProcessEnvironmentValue("NUITKA_SITE_FILENAME", site_filename)

        # Note: As side effect, this might modify the "sys.path" too.
        os.environ["NUITKA_PTH_IMPORTED"] = repr(detectPthImportedPackages())

        user_site = getattr(sys.modules["site"], "USER_SITE", None)
        if user_site is not None:
            os.environ["NUITKA_USER_SITE"] = repr(user_site)

    setLaunchingNuitkaProcessEnvironmentValue("NUITKA_PYTHONPATH", repr(sys.path))

    # In some environments, initial "sys.path" does not contain enough to load
    # "ast" module, which however we use to decode "NUITKA_PYTHONPATH", this
    # helps solve the chicken and egg problem.
    import ast

    setLaunchingNuitkaProcessEnvironmentValue(
        "NUITKA_PYTHONPATH_AST", os.path.dirname(ast.__file__)
    )

    if sys.flags.no_site:
        os.environ["NUITKA_NOSITE_FLAG"] = "1"

    os.environ["PYTHONHASHSEED"] = "0"

    setLaunchingNuitkaProcessEnvironmentValue("NUITKA_RE_EXECUTION", "1")

    # Does not return:
    callExecProcess(args, shell=False)


#     Part of "Nuitka", an optimizing Python compiler that is compatible and
#     integrates with CPython, but also works on its own.
#
#     Licensed under the GNU Affero General Public License, Version 3 (the "License");
#     you may not use this file except in compliance with the License.
#     You may obtain a copy of the License at
#
#        https://www.gnu.org/licenses/agpl-3.0.txt
#
#     See also: "Nuitka Runtime Library Exception, Version 1.0" in file
#     "LICENSE-RUNTIME.txt" for additional permissions granted under Section 7.
#
#     Unless required by applicable law or agreed to in writing, software
#     distributed under the License is distributed on an "AS IS" BASIS,
#     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#     See the License for the specific language governing permissions and
#     limitations under the License.
