HEX
Server: LiteSpeed
System: Linux s1049.use1.mysecurecloudhost.com 4.18.0-477.27.2.lve.el8.x86_64 #1 SMP Wed Oct 11 12:32:56 UTC 2023 x86_64
User: xedaptot (3356)
PHP: 8.3.31
Disabled: NONE
Upload Files
File: //opt/saltstack/salt/lib/python3.10/site-packages/relenv/build/linux.py
# Copyright 2025-2026 Broadcom.
# SPDX-License-Identifier: Apache-2
# mypy: ignore-errors
"""
The linux build process.
"""

from __future__ import annotations

import glob
import io
import os
import pathlib
import shutil
import tarfile
import tempfile
import time
import urllib.request
from collections.abc import MutableMapping
from typing import IO

from ..common import LINUX, Version, arches, runcmd
from .common import (
    Dirs,
    build_openssl,
    build_openssl_fips,
    build_sqlite,
    builds,
    finalize,
    get_dependency_version,
    update_sbom_checksums,
)

ARCHES = arches[LINUX]

EnvMapping = MutableMapping[str, str]

# Patch for Python's setup.py
PATCH = """--- ./setup.py
+++ ./setup.py
@@ -664,6 +664,7 @@
             self.failed.append(ext.name)

     def add_multiarch_paths(self):
+        return
         # Debian/Ubuntu multiarch support.
         # https://wiki.ubuntu.com/MultiarchSpec
         tmpfile = os.path.join(self.build_temp, 'multiarch')
"""


def populate_env(env: EnvMapping, dirs: Dirs) -> None:
    """
    Make sure we have the correct environment variables set.

    :param env: The environment dictionary
    :type env: dict
    :param dirs: The working directories
    :type dirs: ``relenv.build.common.Dirs``
    """
    # CC and CXX need to be to have the full path to the executable
    env["CC"] = f"{dirs.toolchain}/bin/{env['RELENV_HOST']}-gcc"
    env["CXX"] = f"{dirs.toolchain}/bin/{env['RELENV_HOST']}-g++"
    # Add our toolchain binaries to the path. We also add the bin directory of
    # our prefix so that libtirpc can find krb5-config
    env["PATH"] = f"{dirs.toolchain}/bin/:{dirs.prefix}/bin/:{env['PATH']}"
    ldflags = [
        "-Wl,--build-id=sha1",
        f"-Wl,--rpath={dirs.prefix}/lib",
        f"-L{dirs.prefix}/lib",
        f"-L{dirs.toolchain}/{env['RELENV_HOST']}/sysroot/lib",
    ]
    env["LDFLAGS"] = " ".join(ldflags)
    cflags = [
        "-g",
        f"-I{dirs.prefix}/include",
        f"-I{dirs.prefix}/include/readline",
        f"-I{dirs.prefix}/include/ncursesw",
        f"-I{dirs.toolchain}/{env['RELENV_HOST']}/sysroot/usr/include",
    ]
    env["CFLAGS"] = " ".join(cflags)
    # CPPFLAGS are needed for Python's setup.py to find the 'nessicery bits'
    # for things like zlib and sqlite.
    cpplags = [
        f"-I{dirs.prefix}/include",
        f"-I{dirs.prefix}/include/readline",
        f"-I{dirs.prefix}/include/ncursesw",
        f"-I{dirs.toolchain}/{env['RELENV_HOST']}/sysroot/usr/include",
    ]
    # env["CXXFLAGS"] = " ".join(cpplags)
    env["CPPFLAGS"] = " ".join(cpplags)
    env["PKG_CONFIG_PATH"] = f"{dirs.prefix}/lib/pkgconfig"


def build_bzip2(env: EnvMapping, dirs: Dirs, logfp: IO[str]) -> None:
    """
    Build bzip2.

    :param env: The environment dictionary
    :type env: dict
    :param dirs: The working directories
    :type dirs: ``relenv.build.common.Dirs``
    :param logfp: A handle for the log file
    :type logfp: file
    """
    runcmd(
        [
            "make",
            "-j8",
            f"PREFIX={dirs.prefix}",
            f"LDFLAGS={env['LDFLAGS']}",
            "CFLAGS=-fPIC",
            f"CC={env['CC']}",
            "BUILD=x86_64-linux-gnu",
            f"HOST={env['RELENV_HOST']}",
            "install",
        ],
        env=env,
        stderr=logfp,
        stdout=logfp,
    )
    runcmd(
        [
            "make",
            "-f",
            "Makefile-libbz2_so",
            f"CC={env['CC']}",
            f"LDFLAGS={env['LDFLAGS']}",
            "BUILD=x86_64-linux-gnu",
            f"HOST={env['RELENV_HOST']}",
        ],
        env=env,
        stderr=logfp,
        stdout=logfp,
    )
    shutil.copy2("libbz2.so.1.0.8", os.path.join(dirs.prefix, "lib"))


def build_libxcrypt(env: EnvMapping, dirs: Dirs, logfp: IO[str]) -> None:
    """
    Build libxcrypt.

    :param env: The environment dictionary
    :type env: dict
    :param dirs: The working directories
    :type dirs: ``relenv.build.common.Dirs``
    :param logfp: A handle for the log file
    :type logfp: file
    """
    runcmd(
        [
            "./configure",
            f"--prefix={dirs.prefix}",
            # "--enable-libgdbm-compat",
            f"--build={env['RELENV_BUILD']}",
            f"--host={env['RELENV_HOST']}",
        ],
        env=env,
        stderr=logfp,
        stdout=logfp,
    )
    runcmd(["make", "-j8"], env=env, stderr=logfp, stdout=logfp)
    runcmd(["make", "install"], env=env, stderr=logfp, stdout=logfp)


def build_gdbm(env: EnvMapping, dirs: Dirs, logfp: IO[str]) -> None:
    """
    Build gdbm.

    :param env: The environment dictionary
    :type env: dict
    :param dirs: The working directories
    :type dirs: ``relenv.build.common.Dirs``
    :param logfp: A handle for the log file
    :type logfp: file
    """
    runcmd(
        [
            "./configure",
            f"--prefix={dirs.prefix}",
            "--enable-libgdbm-compat",
            f"--build={env['RELENV_BUILD']}",
            f"--host={env['RELENV_HOST']}",
        ],
        env=env,
        stderr=logfp,
        stdout=logfp,
    )
    runcmd(["make", "-j8"], env=env, stderr=logfp, stdout=logfp)
    runcmd(["make", "install"], env=env, stderr=logfp, stdout=logfp)


def build_ncurses(env: EnvMapping, dirs: Dirs, logfp: IO[str]) -> None:
    """
    Build ncurses.

    :param env: The environment dictionary
    :type env: dict
    :param dirs: The working directories
    :type dirs: ``relenv.build.common.Dirs``
    :param logfp: A handle for the log file
    :type logfp: file
    """
    configure = pathlib.Path(dirs.source) / "configure"
    if env["RELENV_BUILD_ARCH"] == "aarch64" or env["RELENV_HOST_ARCH"] == "aarch64":
        os.chdir(dirs.tmpbuild)
        runcmd([str(configure)], stderr=logfp, stdout=logfp)
        runcmd(["make", "-C", "include"], stderr=logfp, stdout=logfp)
        runcmd(["make", "-C", "progs", "tic"], stderr=logfp, stdout=logfp)
    os.chdir(dirs.source)

    # Configure with a prefix of '/' so things will be installed to '/lib'
    # instead of '/usr/local/lib'. The root of the install will be specified
    # via the DESTDIR make argument.
    runcmd(
        [
            str(configure),
            "--prefix=/",
            "--with-shared",
            "--enable-termcap",
            "--with-termlib",
            "--without-cxx-shared",
            "--without-static",
            "--without-cxx",
            "--enable-widec",
            "--without-normal",
            "--disable-stripping",
            f"--with-pkg-config={dirs.prefix}/lib/pkgconfig",
            "--enable-pc-files",
            f"--build={env['RELENV_BUILD']}",
            f"--host={env['RELENV_HOST']}",
        ],
        env=env,
        stderr=logfp,
        stdout=logfp,
    )
    runcmd(["make", "-j8"], env=env, stderr=logfp, stdout=logfp)
    ticdir = str(pathlib.Path(dirs.tmpbuild) / "progs" / "tic")
    runcmd(
        [
            "make",
            f"DESTDIR={dirs.prefix}",
            f"TIC_PATH={ticdir}",
            "install",
        ],
        env=env,
        stderr=logfp,
        stdout=logfp,
    )


def build_readline(env: EnvMapping, dirs: Dirs, logfp: IO[str]) -> None:
    """
    Build readline library.

    :param env: The environment dictionary
    :type env: dict
    :param dirs: The working directories
    :type dirs: ``relenv.build.common.Dirs``
    :param logfp: A handle for the log file
    :type logfp: file
    """
    env["LDFLAGS"] = f"{env['LDFLAGS']} -ltinfow"
    cmd = [
        "./configure",
        f"--prefix={dirs.prefix}",
    ]
    if env["RELENV_HOST"].find("linux") > -1:
        cmd += [
            f"--build={env['RELENV_BUILD']}",
            f"--host={env['RELENV_HOST']}",
        ]
    runcmd(cmd, env=env, stderr=logfp, stdout=logfp)
    runcmd(["make", "-j8"], env=env, stderr=logfp, stdout=logfp)
    runcmd(["make", "install"], env=env, stderr=logfp, stdout=logfp)


def build_libffi(env: EnvMapping, dirs: Dirs, logfp: IO[str]) -> None:
    """
    Build libffi.

    :param env: The environment dictionary
    :type env: dict
    :param dirs: The working directories
    :type dirs: ``relenv.build.common.Dirs``
    :param logfp: A handle for the log file
    :type logfp: file
    """
    runcmd(
        [
            "./configure",
            f"--prefix={dirs.prefix}",
            "--disable-multi-os-directory",
            f"--build={env['RELENV_BUILD']}",
            f"--host={env['RELENV_HOST']}",
        ],
        env=env,
        stderr=logfp,
        stdout=logfp,
    )
    # libffi doens't want to honor libdir, force install to lib instead of lib64
    runcmd(["sed", "-i", "s/lib64/lib/g", "Makefile"], env=env, stderr=logfp, stdout=logfp)
    runcmd(["make", "-j8"], env=env, stderr=logfp, stdout=logfp)
    runcmd(["make", "install"], env=env, stderr=logfp, stdout=logfp)


def build_zlib(env: EnvMapping, dirs: Dirs, logfp: IO[str]) -> None:
    """
    Build zlib.

    :param env: The environment dictionary
    :type env: dict
    :param dirs: The working directories
    :type dirs: ``relenv.build.common.Dirs``
    :param logfp: A handle for the log file
    :type logfp: file
    """
    env["CFLAGS"] = f"-fPIC {env['CFLAGS']}"
    runcmd(
        [
            "./configure",
            f"--prefix={dirs.prefix}",
            f"--libdir={dirs.prefix}/lib",
            "--shared",
        ],
        env=env,
        stderr=logfp,
        stdout=logfp,
    )
    runcmd(["make", "-no-pie", "-j8"], env=env, stderr=logfp, stdout=logfp)
    runcmd(["make", "install"], env=env, stderr=logfp, stdout=logfp)


def build_zstd(env: EnvMapping, dirs: Dirs, logfp: IO[str]) -> None:
    """
    Build zstd.

    :param env: The environment dictionary
    :type env: dict
    :param dirs: The working directories
    :type dirs: ``relenv.build.common.Dirs``
    :param logfp: A handle for the log file
    :type logfp: file
    """
    os.chdir(pathlib.Path(dirs.source) / "lib")
    # Build static + shared so curl and friends can resolve ZSTD_* via libzstd.so.
    # LDFLAGS is left to the lib Makefile because a command-line override would
    # clobber its target-specific `LDFLAGS += -shared` and the .so link would
    # then pull Scrt1.o and fail on a missing `main`.
    runcmd(
        [
            "make",
            "-j8",
            f"PREFIX={dirs.prefix}",
            f"CC={env['CC']}",
            f"CFLAGS={env['CFLAGS']} -fPIC -pthread",
            f"LDFLAGS_DYNLIB={env['LDFLAGS']} -pthread",
            "lib",
        ],
        env=env,
        stderr=logfp,
        stdout=logfp,
    )
    runcmd(
        [
            "make",
            "install-static",
            "install-shared",
            "install-pc",
            "install-includes",
            f"PREFIX={dirs.prefix}",
            f"CC={env['CC']}",
        ],
        env=env,
        stderr=logfp,
        stdout=logfp,
    )


def build_krb(env: EnvMapping, dirs: Dirs, logfp: IO[str]) -> None:
    """
    Build kerberos.

    :param env: The environment dictionary
    :type env: dict
    :param dirs: The working directories
    :type dirs: ``relenv.build.common.Dirs``
    :param logfp: A handle for the log file
    :type logfp: file
    """
    if env["RELENV_BUILD_ARCH"] != env["RELENV_HOST_ARCH"]:
        env["krb5_cv_attr_constructor_destructor"] = "yes,yes"
        env["ac_cv_func_regcomp"] = "yes"
        env["ac_cv_printf_positional"] = "yes"
    os.chdir(dirs.source / "src")
    # krb5 1.22+ and some 1.21 tarballs are missing getdate.c, which normally
    # triggers a yacc call when building the kadmin CLI. We skip building the
    # CLI by removing it from the Makefile.
    runcmd(["sed", "-i", "s/ kadmin / /g", "Makefile.in"], stderr=logfp, stdout=logfp)
    runcmd(
        [
            "./configure",
            f"--prefix={dirs.prefix}",
            "--without-system-verto",
            "--without-libedit",
            f"--build={env['RELENV_BUILD']}",
            f"--host={env['RELENV_HOST']}",
        ],
        env=env,
        stderr=logfp,
        stdout=logfp,
    )
    runcmd(["make", "-j8"], env=env, stderr=logfp, stdout=logfp)
    runcmd(["make", "install"], env=env, stderr=logfp, stdout=logfp)


def update_expat(dirs: Dirs, env: EnvMapping) -> None:
    """
    Update the bundled expat library to the latest version.

    Python ships with an older bundled expat. This function updates it
    to the latest version for security and bug fixes.
    """
    # Get version from JSON
    expat_info = get_dependency_version("expat", "linux")
    if not expat_info:
        # No update needed, use bundled version
        return

    version = expat_info["version"]
    version_tag = version.replace(".", "_")
    url = f"https://github.com/libexpat/libexpat/releases/download/R_{version_tag}/expat-{version}.tar.xz"

    expat_dir = pathlib.Path(dirs.source) / "Modules" / "expat"
    if not expat_dir.exists():
        # No expat directory, skip
        return

    # Download expat tarball
    tmpbuild = pathlib.Path(dirs.tmpbuild)
    tarball_path = tmpbuild / f"expat-{version}.tar.xz"
    urllib.request.urlretrieve(url, str(tarball_path))

    # Extract tarball
    with tarfile.open(tarball_path) as tar:
        tar.extractall(path=str(tmpbuild))

    # Copy source files to Modules/expat/
    expat_source_dir = tmpbuild / f"expat-{version}" / "lib"
    updated_files = []
    for source_file in ["*.h", "*.c"]:
        for file_path in glob.glob(str(expat_source_dir / source_file)):
            target_file = expat_dir / pathlib.Path(file_path).name
            # Remove old file if it exists
            if target_file.exists():
                target_file.unlink()
            shutil.copy2(file_path, str(expat_dir))
            updated_files.append(target_file)

    # Touch all updated files to ensure make rebuilds them
    # (The tarball may contain files with newer timestamps)
    now = time.time()
    for target_file in updated_files:
        os.utime(target_file, (now, now))

    # For expat >= 2.8.0, new entropy source files are required but not compiled
    # by Python's build system. Include them directly in xmlparse.c.
    xmlparse_c = expat_dir / "xmlparse.c"
    if xmlparse_c.exists():
        with open(str(xmlparse_c), "a") as f:
            f.write("\n/* Relenv: include new entropy sources for expat >= 2.8.0 */\n")
            f.write('#if defined(_WIN32)\n#include "random_rand_s.c"\n#endif\n')
            f.write('#if defined(HAVE_GETENTROPY)\n#include "random_getentropy.c"\n#endif\n')
            f.write("#if defined(HAVE_GETRANDOM) || defined(HAVE_SYSCALL_GETRANDOM)\n")
            f.write('#include "random_getrandom.c"\n#endif\n')
            f.write('#if defined(HAVE_ARC4RANDOM_BUF)\n#include "random_arc4random_buf.c"\n#endif\n')
            f.write('#if defined(HAVE_ARC4RANDOM)\n#include "random_arc4random.c"\n#endif\n')
            f.write('#if !defined(_WIN32) && defined(XML_DEV_URANDOM)\n#include "random_dev_urandom.c"\n#endif\n')

    # Update SBOM with correct checksums for updated expat files
    files_to_update = {}
    for target_file in updated_files:
        # SBOM uses relative paths from Python source root
        relative_path = f"Modules/expat/{target_file.name}"
        files_to_update[relative_path] = target_file

    update_sbom_checksums(dirs.source, files_to_update)


def build_python(env: EnvMapping, dirs: Dirs, logfp: IO[str]) -> None:
    """
    Run the commands to build Python.

    :param env: The environment dictionary
    :type env: dict
    :param dirs: The working directories
    :type dirs: ``relenv.build.common.Dirs``
    :param logfp: A handle for the log file
    :type logfp: file
    """
    # Update bundled expat to latest version
    update_expat(dirs, env)

    ldflagopt = f"-Wl,--rpath={dirs.prefix}/lib"
    if ldflagopt not in env["LDFLAGS"]:
        env["LDFLAGS"] = f"{ldflagopt} {env['LDFLAGS']}"

    # Needed when using a toolchain even if build and host match.
    runcmd(
        [
            "sed",
            "-i",
            "s/ac_cv_buggy_getaddrinfo=yes/ac_cv_buggy_getaddrinfo=no/g",
            "configure",
        ]
    )
    runcmd(
        [
            "sed",
            "-i",
            (
                "s/ac_cv_enable_implicit_function_declaration_error=yes/"
                "ac_cv_enable_implicit_function_declaration_error=no/g"
            ),
            "configure",
        ]
    )

    if pathlib.Path("setup.py").exists():
        with tempfile.NamedTemporaryFile(mode="w", suffix="_patch") as p_file:
            p_file.write(PATCH)
            p_file.flush()
            runcmd(
                ["patch", "-p0", "-i", p_file.name],
                env=env,
                stderr=logfp,
                stdout=logfp,
            )

    env["OPENSSL_CFLAGS"] = f"-I{dirs.prefix}/include  -Wno-coverage-mismatch"
    env["OPENSSL_LDFLAGS"] = f"-L{dirs.prefix}/lib"
    env["CFLAGS"] = f"-Wno-coverage-mismatch {env['CFLAGS']}"

    runcmd(
        [
            "sed",
            "-i",
            "s/#readline readline.c -lreadline -ltermcap/readline readline.c -lreadline -ltinfow/g",
            "Modules/Setup",
        ]
    )
    if Version.parse_string(env["RELENV_PY_MAJOR_VERSION"]) <= Version.parse_string("3.10"):
        runcmd(
            [
                "sed",
                "-i",
                (
                    "s/#_curses -lncurses -lncursesw -ltermcap _cursesmodule.c"
                    "/_curses -lncursesw -ltinfow _cursesmodule.c/g"
                ),
                "Modules/Setup",
            ]
        )
        runcmd(
            [
                "sed",
                "-i",
                (
                    "s/#_curses_panel _curses_panel.c -lpanel -lncurses"
                    "/_curses_panel _curses_panel.c -lpanelw -lncursesw/g"
                ),
                "Modules/Setup",
            ]
        )
    else:
        env["CURSES_LIBS"] = "-lncursesw -ltinfow"
        env["PANEL_LIBS"] = "-lpanelw"

    cmd = [
        "./configure",
        "-v",
        f"--prefix={dirs.prefix}",
        f"--with-openssl={dirs.prefix}",
        "--enable-optimizations",
        "--with-ensurepip=no",
        f"--build={env['RELENV_BUILD']}",
        f"--host={env['RELENV_HOST']}",
        "--disable-test-modules",
        "--with-ssl-default-suites=openssl",
        "--with-builtin-hashlib-hashes=blake2,md5,sha1,sha2,sha3",
        "--with-readline=readline",
        "--with-pkg-config=yes",
        "--with-zstd=yes",
    ]

    if env["RELENV_HOST_ARCH"] != env["RELENV_BUILD_ARCH"]:
        # env["RELENV_CROSS"] = dirs.prefix
        cmd += [
            f"--with-build-python={env['RELENV_NATIVE_PY']}",
        ]
    # Needed when using a toolchain even if build and host match.
    cmd += [
        "ac_cv_file__dev_ptmx=yes",
        "ac_cv_file__dev_ptc=no",
    ]

    runcmd(cmd, env=env, stderr=logfp, stdout=logfp)

    with open("Modules/Setup", "a+") as fp:
        fp.seek(0, io.SEEK_END)
        fp.write("*disabled*\n_tkinter\nnsl\nnis\n")
    for _ in ["LDFLAGS", "CFLAGS", "CPPFLAGS", "CXX", "CC"]:
        env.pop(_)
    runcmd(["make", "-j8"], env=env, stderr=logfp, stdout=logfp)
    runcmd(["make", "install"], env=env, stderr=logfp, stdout=logfp)

    # RELENVCROSS=relenv/_build/aarch64-linux-gnu  relenv/_build/x86_64-linux-gnu/bin/python3 -m ensurepip
    # python = dirs.prefix / "bin" / "python3"
    # if env["RELENV_BUILD_ARCH"] == "aarch64":
    #    python = env["RELENV_NATIVE_PY"]
    # env["PYTHONUSERBASE"] = dirs.prefix
    # runcmd([str(python), "-m", "ensurepip", "-U"], env=env, stderr=logfp, stdout=logfp)


build = builds.add("linux", populate_env=populate_env)

# Get dependency versions from JSON (with fallback to hardcoded values)
openssl_info = get_dependency_version("openssl", "linux")
if openssl_info:
    openssl_version = openssl_info["version"]
    openssl_url = openssl_info["url"]
    openssl_checksum = openssl_info["sha256"]
else:
    # Fallback to hardcoded values
    openssl_version = "3.5.4"
    openssl_url = "https://github.com/openssl/openssl/releases/download/openssl-{version}/openssl-{version}.tar.gz"
    openssl_checksum = "b75daac8e10f189abe28a076ba5905d363e4801f"

build.add(
    "openssl",
    build_func=build_openssl,
    download={
        "url": openssl_url,
        "version": openssl_version,
        "checksum": openssl_checksum,
    },
)

# If openssl-fips-module runs before openssl we get an error installing openssl
# becuase <prefix>/lib/ossl-modules exists.
build.add(
    "openssl-fips-module",
    wait_on=["openssl"],
    build_func=build_openssl_fips,
    download={
        "url": "https://www.openssl.org/source/openssl-{version}.tar.gz",
        "version": "3.1.2",
        "checksum": "206036c21264e53f0196f715d81d905742e6245b",
    },
)


# Get libxcrypt version from JSON
libxcrypt_info = get_dependency_version("libxcrypt", "linux")
if libxcrypt_info:
    libxcrypt_version = libxcrypt_info["version"]
    libxcrypt_url = libxcrypt_info["url"]
    libxcrypt_checksum = libxcrypt_info["sha256"]
else:
    libxcrypt_version = "4.4.38"
    libxcrypt_url = "https://github.com/besser82/libxcrypt/releases/download/v{version}/libxcrypt-{version}.tar.xz"
    libxcrypt_checksum = "9aa2fa261be6144af492e9b6bfd03bfaa47f7159"

build.add(
    "libxcrypt",
    download={
        "url": libxcrypt_url,
        "version": libxcrypt_version,
        "checksum": libxcrypt_checksum,
    },
)

# Get XZ version from JSON
xz_info = get_dependency_version("xz", "linux")
if xz_info:
    xz_version = xz_info["version"]
    xz_url = xz_info["url"]
    xz_checksum = xz_info["sha256"]
else:
    # Fallback to hardcoded values
    xz_version = "5.8.1"
    xz_url = "http://tukaani.org/xz/xz-{version}.tar.gz"
    xz_checksum = "ed4d5589c4cfe84e1697bd02a9954b76af336931"

build.add(
    "XZ",
    download={
        "url": xz_url,
        "version": xz_version,
        "checksum": xz_checksum,
    },
)

# Get SQLite version from JSON
sqlite_info = get_dependency_version("sqlite", "linux")
if sqlite_info:
    sqlite_url = sqlite_info["url"]
    sqlite_checksum = sqlite_info["sha256"]
    # SQLite uses a special 7-digit version number
    sqlite_version_num = sqlite_info.get("sqliteversion", "3500400")
else:
    # Fallback to hardcoded values
    sqlite_version_num = "3500400"
    sqlite_url = "https://sqlite.org/2025/sqlite-autoconf-{version}.tar.gz"
    sqlite_checksum = "145048005c777796dd8494aa1cfed304e8c34283"

build.add(
    name="SQLite",
    build_func=build_sqlite,
    download={
        "url": sqlite_url,
        "version": sqlite_version_num,
        "checksum": sqlite_checksum,
    },
)

# Get bzip2 version from JSON
bzip2_info = get_dependency_version("bzip2", "linux")
if bzip2_info:
    bzip2_version = bzip2_info["version"]
    bzip2_url = bzip2_info["url"]
    bzip2_checksum = bzip2_info["sha256"]
else:
    bzip2_version = "1.0.8"
    bzip2_url = "https://sourceware.org/pub/bzip2/bzip2-{version}.tar.gz"
    bzip2_checksum = "bf7badf7e248e0ecf465d33c2f5aeec774209227"

build.add(
    name="bzip2",
    build_func=build_bzip2,
    download={
        "url": bzip2_url,
        "version": bzip2_version,
        "checksum": bzip2_checksum,
    },
)

# Get gdbm version from JSON
gdbm_info = get_dependency_version("gdbm", "linux")
if gdbm_info:
    gdbm_version = gdbm_info["version"]
    gdbm_url = gdbm_info["url"]
    gdbm_checksum = gdbm_info["sha256"]
else:
    gdbm_version = "1.26"
    gdbm_url = "https://mirrors.ocf.berkeley.edu/gnu/gdbm/gdbm-{version}.tar.gz"
    gdbm_checksum = "6cee3657de948e691e8df26509157be950cef4d4"

build.add(
    name="gdbm",
    build_func=build_gdbm,
    download={
        "url": gdbm_url,
        "version": gdbm_version,
        "checksum": gdbm_checksum,
    },
)

# Get ncurses version from JSON
ncurses_info = get_dependency_version("ncurses", "linux")
if ncurses_info:
    ncurses_version = ncurses_info["version"]
    ncurses_url = ncurses_info["url"]
    ncurses_checksum = ncurses_info["sha256"]
else:
    ncurses_version = "6.5"
    ncurses_url = "https://mirrors.ocf.berkeley.edu/gnu/ncurses/ncurses-{version}.tar.gz"
    ncurses_checksum = "cde3024ac3f9ef21eaed6f001476ea8fffcaa381"

build.add(
    name="ncurses",
    build_func=build_ncurses,
    download={
        "url": ncurses_url,
        "version": ncurses_version,
        "checksum": ncurses_checksum,
    },
)

# Get libffi version from JSON
libffi_info = get_dependency_version("libffi", "linux")
if libffi_info:
    libffi_version = libffi_info["version"]
    libffi_url = libffi_info["url"]
    libffi_checksum = libffi_info["sha256"]
else:
    libffi_version = "3.5.2"
    libffi_url = "https://github.com/libffi/libffi/releases/download/v{version}/libffi-{version}.tar.gz"
    libffi_checksum = "2bd35b135b0eeb5c631e02422c9dbe786ddb626a"

build.add(
    "libffi",
    build_libffi,
    download={
        "url": libffi_url,
        "version": libffi_version,
        "checksum": libffi_checksum,
    },
)

# Get zlib version from JSON
zlib_info = get_dependency_version("zlib", "linux")
if zlib_info:
    zlib_version = zlib_info["version"]
    zlib_url = zlib_info["url"]
    zlib_checksum = zlib_info["sha256"]
else:
    zlib_version = "1.3.1"
    zlib_url = "https://zlib.net/fossils/zlib-{version}.tar.gz"
    zlib_checksum = "f535367b1a11e2f9ac3bec723fb007fbc0d189e5"

build.add(
    "zlib",
    build_zlib,
    download={
        "url": zlib_url,
        "version": zlib_version,
        "checksum": zlib_checksum,
    },
)

# Get uuid version from JSON
uuid_info = get_dependency_version("uuid", "linux")
if uuid_info:
    uuid_ver = uuid_info["version"]
    uuid_url = uuid_info["url"]
    uuid_checksum = uuid_info["sha256"]
else:
    uuid_ver = "1.0.3"
    uuid_url = "https://sourceforge.net/projects/libuuid/files/libuuid-{version}.tar.gz"
    uuid_checksum = "46eaedb875ae6e63677b51ec583656199241d597"

build.add(
    "uuid",
    download={
        "url": uuid_url,
        "version": uuid_ver,
        "checksum": uuid_checksum,
    },
)

# Get krb5 version from JSON
krb5_info = get_dependency_version("krb5", "linux")
if krb5_info:
    krb5_version = krb5_info["version"]
    krb5_url = krb5_info["url"]
    krb5_checksum = krb5_info["sha256"]
else:
    krb5_version = "1.22"
    krb5_url = "https://kerberos.org/dist/krb5/{version}/krb5-{version}.tar.gz"
    krb5_checksum = "3ad930ab036a8dc3678356fbb9de9246567e7984"

build.add(
    "krb5",
    build_func=build_krb,
    wait_on=["openssl"],
    download={
        "url": krb5_url,
        "fallback_url": krb5_url.replace(
            "https://kerberos.org/dist/",
            "https://web.mit.edu/kerberos/dist/",
        ),
        "version": krb5_version,
        "checksum": krb5_checksum,
    },
)

# Get readline version from JSON
readline_info = get_dependency_version("readline", "linux")
if readline_info:
    readline_version = readline_info["version"]
    readline_url = readline_info["url"]
    readline_checksum = readline_info["sha256"]
else:
    readline_version = "8.3"
    readline_url = "https://mirrors.ocf.berkeley.edu/gnu/readline/readline-{version}.tar.gz"
    readline_checksum = "2c05ae9350b695f69d70b47f17f092611de2081f"

build.add(
    "readline",
    build_func=build_readline,
    wait_on=["ncurses"],
    download={
        "url": readline_url,
        "version": readline_version,
        "checksum": readline_checksum,
    },
)

# Get zstd version from JSON
zstd_info = get_dependency_version("zstd", "linux")
if zstd_info:
    zstd_version = zstd_info["version"]
    zstd_url = zstd_info["url"]
    zstd_checksum = zstd_info["sha256"]
else:
    zstd_version = "1.5.7"
    zstd_url = "https://github.com/facebook/zstd/releases/download/v{version}/zstd-{version}.tar.gz"
    zstd_checksum = "eb33e51f49a15e023950cd7825ca74a4a2b43db8354825ac24fc1b7ee09e6fa3"

build.add(
    "zstd",
    build_func=build_zstd,
    download={
        "url": zstd_url,
        "version": zstd_version,
        "checksum": zstd_checksum,
    },
)

# Get tirpc version from JSON
tirpc_info = get_dependency_version("tirpc", "linux")
if tirpc_info:
    tirpc_version = tirpc_info["version"]
    tirpc_url = tirpc_info["url"]
    tirpc_checksum = tirpc_info["sha256"]
else:
    tirpc_version = "1.3.4"
    tirpc_url = "https://sourceforge.net/projects/libtirpc/files/libtirpc-{version}.tar.bz2"
    tirpc_checksum = "63c800f81f823254d2706637bab551dec176b99b"

build.add(
    "tirpc",
    wait_on=[
        "krb5",
    ],
    download={
        "url": tirpc_url,
        # "url": "https://downloads.sourceforge.net/projects/libtirpc/files/libtirpc-{version}.tar.bz2",
        "version": tirpc_version,
        "checksum": tirpc_checksum,
    },
)

build.add(
    "python",
    build_func=build_python,
    wait_on=[
        "openssl",
        "libxcrypt",
        "XZ",
        "SQLite",
        "bzip2",
        "gdbm",
        "ncurses",
        "libffi",
        "zlib",
        "uuid",
        "krb5",
        "readline",
        "zstd",
        "tirpc",
    ],
    download={
        "url": "https://www.python.org/ftp/python/{version}/Python-{version}.tar.xz",
        "version": build.version,
        "checksum": None,
    },
)


build.add(
    "relenv-finalize",
    build_func=finalize,
    wait_on=[
        "python",
        "openssl-fips-module",
    ],
)