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


"""
The Onefile scons file for the bootstrap binary, check out Backend.scons for
where the actual compiled binaries are build. If you have Scons or Windows
platform knowledge, please be especially invited and contribute improvements.

This file is used to build a bootstrap binary that only unpacks the distribution
contents to have a launch it.
"""

# Make nuitka package importable from calling installation

import sys
import os
import types

sys.modules["nuitka"] = types.ModuleType("nuitka")
sys.modules["nuitka"].__path__ = [os.environ["NUITKA_PACKAGE_DIR"]]

# We are in the build.build package really.
import nuitka.build  # pylint: disable=unused-import

__package__ = "nuitka.build"  # pylint: disable=redefined-builtin

# isort:start

from nuitka.Tracing import scons_details_logger, scons_logger
from nuitka.utils.FileOperations import getExternalUsePath

from .SconsCompilerSettings import (
    createNuitkaSconsEnvironment,
    reportCCompiler,
    setupCCompiler,
)
from .SconsProgress import setSconsProgressBarTotal
from .SconsSpawn import enableSpawnMonitoring
from .SconsUtils import (
    changeKeyboardInterruptToErrorExit,
    createDefinitionsFile,
    getArgumentBool,
    getArgumentDefaulted,
    getArgumentList,
    getArgumentRequired,
    makeResultPathFileSystemEncodable,
    scanSourceDir,
    writeSconsReport,
)

# spell-checker: ignore ccflags,cppdefines,cpppath
# spell-checker: ignore libpath

# Set up the basic stuff.
env = createNuitkaSconsEnvironment()
assert env.low_memory is False  # Never a concern in this case.

# The directory containing the C files generated by Nuitka to be built using
# scons. They are referred to as sources from here on.

static_src = os.path.join(env.source_dir, "static")

# The name of executable that we produce
result_exe = getArgumentDefaulted("result_exe", None)


# Module mode: Create a Python extension module, create an executable otherwise.
module_mode = getArgumentBool("module_mode", False)
# Store it in env for later use
env.module_mode = module_mode
env.dll_mode = False  # Onefile is never dll mode in this context?? Onefile uses exe_target=True later.
# Wait, Onefile.scons usually builds an executable.
# Line 259: exe_target=True.

# Debug mode: Less optimizations, debug information in the resulting binary.
# Note: env.debug_mode is already set by createNuitkaSconsEnvironment
debug_mode = env.debug_mode

# Target arch for some decisions
target_arch = env.target_arch


# Home of Python to be compiled against, used to find tools like clcache and
# ccache, no linking against Python is needed here.
python_prefix = getArgumentRequired("python_prefix")

python_prefix_external = getExternalUsePath(python_prefix)


# Preprocessor defines and link libraries from plugins
cpp_defines = getArgumentList("cpp_defines", "")
cpp_include_dirs = getArgumentList("cpp_include_dirs", "")
link_dirs = getArgumentList("link_dirs", "")
link_libraries = getArgumentList("link_libraries", "")


# Onefile bootstrap is optional.
onefile_splash_screen = getArgumentBool("onefile_splash_screen", False)

# Report the C compiler used.
reportCCompiler(env, "Onefile", output_func=scons_logger.info)


# Set up C compiler settings.
setupCCompiler(
    env=env,
    pgo_mode="no",  # Bootstrap doesn't use PGO
    exe_target=True,
    onefile_compile=True,
)


if debug_mode:
    if env.msvc_mode:
        # Disable warnings that system headers already show.
        env.Append(
            CCFLAGS=[
                "/W4",
                "/wd4505",
                "/wd4127",
                "/wd4100",
                "/wd4702",
                "/wd4189",
                "/wd4211",
                "/WX",
            ]
        )

        # Disable warnings, the first 3 are for Python headers, maybe not needed.
        env.Append(CCFLAGS=["/wd4512", "/wd4510", "/wd4610", "/wd4996"])

if debug_mode:
    env.Append(CPPDEFINES=["_NUITKA_DEBUG"])

if env.trace_mode:
    env.Append(CPPDEFINES=["_NUITKA_TRACE"])

# The static include files reside in Nuitka installation, which may be where
# the "nuitka.build" package lives.
nuitka_include = os.path.join(env.nuitka_src, "include")

if not os.path.exists(os.path.join(nuitka_include, "nuitka", "prelude.h")):
    sys.exit(
        "Error, cannot locate Nuitka includes at '%s', broken installation."
        % nuitka_include
    )

# We have include files in the build directory and the static include directory
# that is located inside Nuitka installation, as well as an inline copy of zstd
# that should be found.
env.Append(
    CPPPATH=[
        env.source_dir,
        nuitka_include,
        os.path.join(env.nuitka_src, "static_src"),
        os.path.join(env.nuitka_src, "inline_copy", "zstd"),
    ],
)

# Indicate, that the common code is used to build an executable.
env.Append(CPPDEFINES=["_NUITKA_EXE_MODE"])


def discoverSourceFiles():
    result = []

    # Scan for Nuitka created source files, and add them too.
    result.extend(scanSourceDir(env=env, dirname=env.source_dir, plugins=False))
    result.extend(
        scanSourceDir(
            env=env,
            dirname=os.path.join(env.source_dir, "plugins"),
            plugins=True,
        )
    )
    result.extend(
        scanSourceDir(
            env=env,
            dirname=os.path.join(env.source_dir, "static_src"),
            plugins=False,
        )
    )

    return result


source_files = discoverSourceFiles()

# Remove the target file to avoid cases where it falsely doesn't get rebuild and
# then lingers from previous builds, and also workaround for MinGW64 not
# supporting unicode result paths for "-o" basename.
result_exe = makeResultPathFileSystemEncodable(env=env, result_exe=result_exe)

# For scons internal use, we use Python native paths as MSYS2 does wrong things otherwise.
target = env.Program(os.path.normpath(result_exe), source_files)


# Remove the target file to avoid cases where it falsely doesn't get rebuild
# and then lingers from previous builds,
if os.path.exists(target[0].abspath):
    os.unlink(target[0].abspath)


# Plugin contributed C defines should be used too.
env.Append(CPPDEFINES=cpp_defines)
# Plugin contributed C include directories should be used too.
env.Append(CPPPATH=cpp_include_dirs)
# Plugin contributed link dirs should be used too.
env.Append(LIBPATH=link_dirs)
# Plugin contributed link libraries should be used too.
env.Append(LIBS=link_libraries)

# Work around windows bugs and use watchdogs to track progress of compilation.
enableSpawnMonitoring(
    env=env,
    source_files=source_files,
)

# Before we go, also lets turn KeyboardInterrupt into a mere error exit as the
# scons traceback is not going to be very interesting to us.
changeKeyboardInterruptToErrorExit()


def createBuildDefinitionsFile():
    onefile_definitions = {}

    # TODO: Do this via a plugin too, so it can provide these externally.
    if onefile_splash_screen:
        onefile_definitions["_NUITKA_ONEFILE_SPLASH_SCREEN"] = 1

        # spell-checker: ignore Windowscodecs,Shlwapi
        env.Append(LIBS=["Ole32", "Windowscodecs", "User32", "Gdi32", "Shlwapi"])

    if env.onefile_dll_mode and os.name == "nt":
        env.Append(LIBS=["PsApi"])

    createDefinitionsFile(env.source_dir, "onefile_definitions.h", onefile_definitions)


createBuildDefinitionsFile()


writeSconsReport(env=env, target=target)

setSconsProgressBarTotal(name="Onefile", total=len(source_files))

scons_details_logger.info("Launching Scons target: %s" % target)
env.Default(target)

#     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.
