#!/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())