Skip to content

Install Libclang

CLI tool for installing the libclang system dependency.

CLI Usage

headerkit install-libclang [--version VERSION] [--skip-verify]
python -m headerkit.install_libclang [--version VERSION] [--skip-verify]

See the Installing libclang guide for detailed usage, platform behavior, and CI examples.

API Reference

install_libclang

Install libclang for the current platform.

Usage::

python -m headerkit.install_libclang [--version VERSION]

This module installs the libclang shared library so that headerkit's libclang backend can function. It handles platform-specific installation:

  • Linux (RHEL/Fedora/AlmaLinux): dnf install clang-libs (falls back to clang-devel)
  • Linux (Debian/Ubuntu): apt-get install libclang-dev
  • Linux (Alpine): apk add clang-dev
  • macOS: brew install llvm (via Homebrew)
  • Windows x64: choco install llvm (via Chocolatey)
  • Windows ARM64: Downloads native woa64 installer from LLVM GitHub releases

After installation, verifies that libclang is loadable by headerkit.

install_linux

install_linux(*, quiet=False)

Install libclang on Linux using the available package manager.

Source code in headerkit/install_libclang.py
def install_linux(*, quiet: bool = False) -> bool:
    """Install libclang on Linux using the available package manager."""
    # Try dnf first (RHEL/Fedora/AlmaLinux/manylinux_2_28)
    if _is_command_available("dnf"):
        _log_or_print("Detected dnf package manager (RHEL/Fedora/AlmaLinux)", quiet=quiet)
        # Try clang-libs first (lighter, provides just libclang.so)
        result = _run(["dnf", "install", "-y", "clang-libs"], check=False, quiet=quiet)
        if result.returncode == 0:
            return True
        # Fall back to clang-devel (heavier but also provides libclang.so)
        result = _run(["dnf", "install", "-y", "clang-devel"], check=False, quiet=quiet)
        if result.returncode == 0:
            return True

    # Try apt-get (Debian/Ubuntu)
    if _is_command_available("apt-get"):
        _log_or_print("Detected apt-get package manager (Debian/Ubuntu)", quiet=quiet)
        _run(["apt-get", "update", "-qq"], check=False, quiet=quiet)
        result = _run(["apt-get", "install", "-y", "libclang-dev"], check=False, quiet=quiet)
        if result.returncode == 0:
            return True

    # Try apk (Alpine)
    if _is_command_available("apk"):
        _log_or_print("Detected apk package manager (Alpine)", quiet=quiet)
        result = _run(["apk", "add", "clang-dev"], check=False, quiet=quiet)
        if result.returncode == 0:
            return True

    _log_or_print("ERROR: No supported package manager found (tried dnf, apt-get, apk)", quiet=quiet)
    return False

install_macos

install_macos(*, quiet=False)

Install libclang on macOS using Homebrew.

Source code in headerkit/install_libclang.py
def install_macos(*, quiet: bool = False) -> bool:
    """Install libclang on macOS using Homebrew."""
    if not _is_command_available("brew"):
        _log_or_print("ERROR: Homebrew not found. Install it from https://brew.sh/", quiet=quiet)
        return False

    _log_or_print("Installing LLVM via Homebrew...", quiet=quiet)
    result = _run(["brew", "install", "llvm"], check=False, quiet=quiet)
    return result.returncode == 0

install_windows

install_windows(llvm_version, *, quiet=False)

Install libclang on Windows.

On ARM64, downloads the native woa64 installer from LLVM GitHub releases. On x64, uses Chocolatey with the same pinned LLVM version.

Source code in headerkit/install_libclang.py
def install_windows(llvm_version: str, *, quiet: bool = False) -> bool:
    """Install libclang on Windows.

    On ARM64, downloads the native woa64 installer from LLVM GitHub releases.
    On x64, uses Chocolatey with the same pinned LLVM version.
    """
    arch = os.environ.get("PROCESSOR_ARCHITECTURE", "").upper()

    if arch == "ARM64":
        return _install_windows_arm64(llvm_version, quiet=quiet)
    else:
        return _install_windows_x64(llvm_version, quiet=quiet)

verify_libclang

verify_libclang(*, quiet=False)

Verify that libclang is now loadable by headerkit.

Source code in headerkit/install_libclang.py
def verify_libclang(*, quiet: bool = False) -> bool:
    """Verify that libclang is now loadable by headerkit."""
    try:
        from headerkit.backends.libclang import is_system_libclang_available

        if is_system_libclang_available():
            if not quiet:
                print("Verification: libclang is available and loadable.")
            return True
        else:
            msg = "libclang was installed but could not be loaded by headerkit."
            if quiet:
                logger.warning(msg)
            else:
                print(f"WARNING: {msg}")
                print("You may need to set your library path or restart your shell.")
            return False
    except (ImportError, OSError, RuntimeError) as e:
        if quiet:
            logger.warning("Could not verify libclang: %s", e)
        else:
            print(f"WARNING: Could not verify libclang: {e}")
        return False

auto_install

auto_install()

Quietly auto-install libclang if not already available.

This is the non-interactive counterpart to main(). It is called by generate() when the libclang backend is needed but unavailable.

  • Idempotent: returns True immediately if libclang is already loadable.
  • Suppresses stdout from the underlying install functions so that callers (e.g. generate()) never see unexpected print output.
  • Logs progress via the headerkit.install logger instead of printing.
  • Always verifies after installation (no --skip-verify).

Returns:

Type Description
bool

True if libclang is available after this call, False otherwise.

Source code in headerkit/install_libclang.py
def auto_install() -> bool:
    """Quietly auto-install libclang if not already available.

    This is the non-interactive counterpart to ``main()``. It is called by
    ``generate()`` when the libclang backend is needed but unavailable.

    - Idempotent: returns True immediately if libclang is already loadable.
    - Suppresses stdout from the underlying install functions so that
      callers (e.g. ``generate()``) never see unexpected print output.
    - Logs progress via the ``headerkit.install`` logger instead of printing.
    - Always verifies after installation (no ``--skip-verify``).

    :returns: True if libclang is available after this call, False otherwise.
    """
    if verify_libclang(quiet=True):
        return True

    logger.info("libclang not found; attempting automatic installation")
    logger.info("Platform: %s (%s)", sys.platform, platform.machine())

    ok: bool
    if sys.platform == "linux":
        ok = install_linux(quiet=True)
    elif sys.platform == "darwin":
        ok = install_macos(quiet=True)
    elif sys.platform == "win32":
        ok = install_windows(DEFAULT_LLVM_VERSION, quiet=True)
    else:
        logger.warning("Unsupported platform for auto-install: %s", sys.platform)
        return False

    if ok and verify_libclang(quiet=True):
        logger.info("libclang auto-installed successfully")
        return True

    # Platform install failed or libclang not loadable after install
    # Try pip install libclang as universal fallback
    if _try_pip_install_libclang(quiet=True):
        if verify_libclang(quiet=True):
            logger.info("libclang installed via pip")
            return True

    logger.warning("libclang installation failed")
    return False