#!/usr/bin/env python3
# Locate an LLVM install that ships the LLDB headers, mirroring the search order
# the old premake5.lua used. Meson calls this at configure time and parses the
# KEY=value lines below; run it by hand to see what would be picked.
#
# Precedence: -Dllvm_prefix (argv[1]) -> $LLVM_PREFIX -> vendored ext/llvm ->
# `llvm-config --prefix` -> per-platform well-known locations. A candidate only
# counts if it actually contains include/lldb.
#
# Output (stdout), only when a usable prefix is found:
# prefix=
# incdir=/include
# libdir=/lib
# lib=
# dll= (zero or more, Windows runtime libraries only)
# Exit status is non-zero when nothing usable was found.
import glob
import os
import platform
import subprocess
import sys
REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
VENDORED = os.path.join(REPO_ROOT, "ext", "llvm")
def norm(path):
# forward slashes so the value can be baked into a C string literal without
# a backslash being read as an escape (matters for HSDBG_LLVM_PREFIX)
return os.path.abspath(path).replace("\\", "/")
def usable(candidate):
if not candidate:
return None
candidate = candidate.strip()
if not candidate:
return None
prefix = norm(candidate)
if os.path.isdir(os.path.join(prefix, "include", "lldb")):
return prefix
return None
def command_output(*args):
try:
out = subprocess.run(
args, capture_output=True, text=True, check=False
)
except OSError:
return None
if out.returncode != 0:
return None
return out.stdout.strip() or None
def newest_of(pattern):
matches = sorted(glob.glob(pattern))
for candidate in reversed(matches):
prefix = usable(candidate)
if prefix:
return prefix
return None
def find_prefix(override):
prefix = usable(override) or usable(os.environ.get("LLVM_PREFIX"))
if prefix:
return prefix
prefix = usable(VENDORED)
if prefix:
return prefix
prefix = usable(command_output("llvm-config", "--prefix"))
if prefix:
return prefix
host = platform.system()
if host == "Windows":
program_files = os.environ.get("ProgramFiles")
if program_files:
prefix = usable(os.path.join(program_files, "LLVM"))
if prefix:
return prefix
return newest_of("C:/Program Files/LLVM*")
if host == "Darwin":
return usable(command_output("brew", "--prefix", "llvm"))
if host == "Linux":
return newest_of("/usr/lib/llvm-*") or newest_of("/usr/local/llvm*")
return None
def lldb_library(libdir):
host = platform.system()
if host == "Windows":
names = ["liblldb.lib"]
elif host == "Darwin":
names = ["liblldb.dylib"]
else:
names = ["liblldb.so"]
for name in names:
candidate = os.path.join(libdir, name)
if os.path.isfile(candidate):
return norm(candidate)
versioned = sorted(glob.glob(os.path.join(libdir, "liblldb.so.*")))
if versioned:
return norm(versioned[-1])
# nothing on disk yet (e.g. a cross setup); hand back the expected path so
# the caller gets a clear link-time error rather than a silent misconfigure
return norm(os.path.join(libdir, names[0]))
def windows_dlls(prefix):
bindir = os.path.join(prefix, "bin")
runtimes = glob.glob(os.path.join(bindir, "liblldb.dll"))
runtimes += glob.glob(os.path.join(bindir, "python*.dll"))
if not runtimes:
runtimes = [os.path.join(bindir, "liblldb.dll")]
return [norm(path) for path in runtimes]
def main():
override = sys.argv[1] if len(sys.argv) > 1 else ""
prefix = find_prefix(override)
if not prefix:
sys.stderr.write("no llvm with lldb headers found\n")
return 1
incdir = norm(os.path.join(prefix, "include"))
libdir = norm(os.path.join(prefix, "lib"))
print("prefix=" + prefix)
print("incdir=" + incdir)
print("libdir=" + libdir)
print("lib=" + lldb_library(libdir))
if platform.system() == "Windows":
for dll in windows_dlls(prefix):
print("dll=" + dll)
return 0
if __name__ == "__main__":
sys.exit(main())