llama.cpp 外部安装与 CMake 包消费验证:详解 examples/test-cmake 的最小可运行示例
【免费下载链接】llama.cppLLM inference in C/C++项目地址: https://gitcode.com/GitHub_Trending/ll/llama.cpp
examples/test-cmake是 llama.cpp 仓库中一个专门用于手动验证本机安装结果的最小工程:先把 llama.cpp 本身配置、构建并安装到一个本地目录,再把这个目录当作一个标准的 CMake 依赖包,用find_package(llama)的方式编译并链接一个独立的 C++ 程序。读完后,你将掌握如何把一个已安装的 llama.cpp 作为第三方库接入自己的项目,并能通过两个脚本build-install.sh/build.sh快速排查安装配置(install layout、共享库、动态后端加载)是否正确。
这个示例解决什么问题
README 对它的定位非常明确:
This is just for manually testing/developing of a llama.cpp installation to enable troubleshooting issues and exploration. The idea is that this can be used after making changes to llama.cpp installation cmake configuration and then verify it locally.
也就是说,当你修改了 llama.cpp 的安装相关 CMake 配置(例如库安装目录、共享/静态库开关、后端动态加载方式)之后,可以用这个工程在本地“自测”:安装产物是否完整、CMake config 文件是否能被下游find_package正确找到、运行期能否从自定义目录加载动态后端。整个示例由 5 个文件组成:
- README.md:用法说明;
- build-install.sh:以“被依赖库”的身份构建并安装 llama.cpp;
- build.sh:以“下游消费者”的身份配置、编译、运行 test-cmake;
- CMakeLists.txt:消费者工程的最小 CMake 脚本;
- test-cmake.cpp:只做版本打印与后端初始化的最小 C++ 程序;
- .gitignore:忽略
install、build、llama-build-install三个本地产物目录。
第一步:构建并安装 llama.cpp 到本地目录
build-install.sh 的完整内容只有三行核心逻辑(清目录、配置、构建、安装):
#!/bin/bash set -e rm -rf llama-build-install install cmake --fresh -S ../../. -B llama-build-install -DCMAKE_BUILD_TYPE=Release \ -DBUILD_SHARED_LIBS=ON \ -DGGML_BACKEND_DL=ON \ -DGGML_CPU_ALL_VARIANTS=ON \ -DLLAMA_TESTS_INSTALL=OFF \ -DCMAKE_INSTALL_PREFIX="${PWD}/install" \ -DGGML_BACKEND_DIR="${PWD}/install/lib/llama.cpp" \ -DGGML_LIB_INSTALL_DIR="${PWD}/install/lib/llama.cpp" \ -DLLAMA_LIB_INSTALL_DIR="${PWD}/install/lib/llama.cpp" \ -DLLAMA_TOOLS_INSTALL=OFF cmake --build llama-build-install --parallel 12 cmake --install llama-build-install注意它的-S ../../.指向的是 llama.cpp 仓库根目录,即“先把自己安装成一个库”。逐个参数解释如下,这些开关在仓库的 CMake 源码中都可以找到定义:
| 参数 | 作用 | 源码依据 |
|---|---|---|
CMAKE_BUILD_TYPE=Release | 以 Release 模式构建被安装的主库 | CMake 常规 |
BUILD_SHARED_LIBS=ON | 构建共享库(.so/.dylib/.dll),是下游动态链接的前提 | cmake/llama-config.cmake.in中LLAMA_SHARED_LIB即取自该开关 |
GGML_BACKEND_DL=ON | 将 ggml 各计算后端(CPU、CUDA、Metal 等)构建成可动态加载的.so,运行期再加载,而非静态编入主库 | ggml/CMakeLists.txt:option(GGML_BACKEND_DL "ggml: build backends as dynamic libraries (requires BUILD_SHARED_LIBS)" OFF),且 ggml/src/CMakeLists.txt 明确GGML_BACKEND_DL依赖BUILD_SHARED_LIBS |
GGML_CPU_ALL_VARIANTS=ON | 一次构建所有 CPU 微架构变体(如libggml-cpu-alderlake.so、haswell等),运行期按机器能力挑选加载 | ggml/CMakeLists.txt:option(GGML_CPU_ALL_VARIANTS "ggml: build all variants of the CPU backend (requires GGML_BACKEND_DL)" OFF) |
GGML_BACKEND_DIR | 指定动态后端.so的安装目标目录 | ggml/CMakeLists.txt 定义为CACHE PATH;ggml/src/CMakeLists.txt 会在GGML_BACKEND_DL开启时把它编进ggml的宏定义GGML_BACKEND_DIR="...",并在 ggml/src/CMakeLists.txt 把各后端install(TARGETS ... LIBRARY DESTINATION ${GGML_BACKEND_DIR}) |
GGML_LIB_INSTALL_DIR/LLAMA_LIB_INSTALL_DIR | ggml 与 llama 两层库的安装目录 | ggml/CMakeLists.txt 与 CMakeLists.txt 中均定义为CACHE PATH |
CMAKE_INSTALL_PREFIX | 整个安装的根前缀,这里落到当前目录下的install/ | CMake 常规 |
LLAMA_TESTS_INSTALL=OFF/LLAMA_TOOLS_INSTALL=OFF | 只安装库与头文件,不安装测试程序和 CLI 工具,缩小安装产物 | CMakeLists.txt 中两个option(...)的定义 |
安装完成后,install/lib/下的产物在 README 中给出了一个真实示例(不同平台文件名会不同):
(venv) $ ls install/lib/ cmake libggml.so libllama-common.so.0 libllama.so.0.1.0 llama.cpp libggml-base.so libggml.so.0 libllama-common.so.0.1.0 libmtmd.so pkgconfig libggml-base.so.0 libggml.so.0.19.0 libllama.so libmtmd.so.0 libggml-base.so.0.19.0 libllama-common.so libllama.so.0 libmtmd.so.0.1.0可以观察到三个关键点:
- 出现了
libggml、libggml-base、libllama、libllama-common、libmtmd多组“无版本主链 + 带版本实体 + 带版本符号链接”的组合,说明主库确实按共享库方式安装,且 soname 机制完整; cmake/子目录存放的是供下游find_package使用的 CMake package config 文件(即由 cmake/llama-config.cmake.in 生成的产物);llama.cpp/子目录是GGML_BACKEND_DIR/GGML_LIB_INSTALL_DIR/LLAMA_LIB_INSTALL_DIR指向的目标位置,动态后端.so会落在里面——这解释了为什么运行期需要把install/lib/llama.cpp加进LD_LIBRARY_PATH。
第二步:把安装目录当作 CMake 包来消费
build.sh 只有三行,但完整演示了“下游消费者”的标准流程:
#!/bin/bash set -e cmake -S . -B build -DCMAKE_PREFIX_PATH="${PWD}/install" cmake --build build LD_LIBRARY_PATH="${PWD}/install/lib/llama.cpp:${PWD}/install/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" ./build/test-cmake三步分别对应:
- 配置:
-DCMAKE_PREFIX_PATH="${PWD}/install"告诉 CMake 到本地安装前缀里找find_package需要的 config 文件; - 构建:正常
cmake --build; - 运行:把
install/lib/llama.cpp(动态后端目录)与install/lib(共享库目录)前置注入LD_LIBRARY_PATH,保证动态链接器能在运行期找到libllama.so、libggml.so以及libggml-cpu-*.so等动态后端。
消费者工程 examples/test-cmake/CMakeLists.txt 是接入 llama.cpp 安装的最小模板,全文如下:
cmake_minimum_required(VERSION 3.14) project(llama-simple) set(CMAKE_CXX_STANDARD 17) find_package(llama 0.1.0 REQUIRED) add_executable(test-cmake test-cmake.cpp) target_link_libraries(test-cmake PRIVATE llama) target_compile_definitions(test-cmake PRIVATE LLAMA_BUILD_NUMBER=${LLAMA_BUILD_NUMBER} LLAMA_BUILD_COMMIT="${LLAMA_BUILD_COMMIT}" )这里有两点值得展开。
find_package(llama)背后发生了什么
llama.cpp 通过 cmake/llama-config.cmake.in 提供标准 CMake package config,其关键片段是:
set(LLAMA_VERSION @LLAMA_VERSION@) set(LLAMA_BUILD_COMMIT @LLAMA_BUILD_COMMIT@) set(LLAMA_BUILD_NUMBER @LLAMA_BUILD_NUMBER@) set(LLAMA_SHARED_LIB @BUILD_SHARED_LIBS@) @PACKAGE_INIT@ set_and_check(LLAMA_INCLUDE_DIR "@PACKAGE_LLAMA_INCLUDE_INSTALL_DIR@") set_and_check(LLAMA_LIB_DIR "@PACKAGE_LLAMA_LIB_INSTALL_DIR@") find_package(ggml REQUIRED HINTS ${LLAMA_LIB_DIR}/cmake) find_library(llama_LIBRARY llama REQUIRED HINTS ${LLAMA_LIB_DIR} NO_CMAKE_FIND_ROOT_PATH) add_library(llama UNKNOWN IMPORTED) set_target_properties(llama PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${LLAMA_INCLUDE_DIR}" INTERFACE_LINK_LIBRARIES "ggml::ggml;ggml::ggml-base;" IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" IMPORTED_LOCATION "${llama_LIBRARY}" INTERFACE_COMPILE_FEATURES c_std_90 POSITION_INDEPENDENT_CODE ON)由此可以确认:
- config 文件本身把
LLAMA_VERSION、LLAMA_BUILD_COMMIT、LLAMA_BUILD_NUMBER暴露为普通 CMake 变量——这就是 examples/test-cmake/CMakeLists.txt 里能直接引用${LLAMA_BUILD_NUMBER}/${LLAMA_BUILD_COMMIT}的原因,它们并不是find_package自动生成的,而是llama-config.cmake显式set出来的; - 该 config 又通过
find_package(ggml REQUIRED HINTS ${LLAMA_LIB_DIR}/cmake)级联找到 ggml 的 package config,并把导入目标llama的INTERFACE_LINK_LIBRARIES指向ggml::ggml;ggml::ggml-base,所以下游只需要target_link_libraries(... PRIVATE llama)就能同时拿到 llama 与 ggml 两套符号; INTERFACE_INCLUDE_DIRECTORIES指向安装前缀里的头文件目录,因此消费者不需要再手工include_directories。
这也解释了为什么build-install.sh中必须显式指定GGML_LIB_INSTALL_DIR与LLAMA_LIB_INSTALL_DIR:config 文件里HINTS ${LLAMA_LIB_DIR}/cmake与HINTS ${LLAMA_LIB_DIR}都依赖这个路径才能找到 ggml 的 cmake 子目录和libllama.so实体。
test-cmake.cpp:只做“初始化即成功”
test-cmake.cpp 的完整源码:
#include "llama.h" #include <cstdio> int main(void) { printf("[test-cmake] version: %s, build: %d (%s)\n", llama_version(), LLAMA_BUILD_NUMBER, LLAMA_BUILD_COMMIT); printf("[test-cmake] Initializing backend...\n"); llama_backend_init(); printf("[test-cmake] Backend initialized.\n"); llama_backend_free(); return 0; }它只验证四件事:
- 头文件
llama.h能被编译器找到(include 目录配置正确); llama_version()、LLAMA_BUILD_NUMBER、LLAMA_BUILD_COMMIT这些运行期/编译期版本信息可读(config 文件变量传递正确);llama_backend_init()能成功加载后端——在GGML_BACKEND_DL=ON+GGML_CPU_ALL_VARIANTS=ON下,这一步会真正从GGML_BACKEND_DIR指向的目录 dlopen 具体的libggml-cpu-<variant>.so;llama_backend_free()能正常释放,无运行期崩溃。
完整流程与预期输出
在examples/test-cmake/目录内按 README 顺序执行:
./build-install.sh会创建install/目录,并在其lib/下生成上节列出的共享库与cmake/、pkgconfig/子目录。然后:
./build.shREADME 给出的实际运行输出是:
-- Configuring done (0.0s) -- Generating done (0.0s) -- Build files have been written to: /path/to/llama.cpp/examples/test-cmake/build [100%] Built target test-cmake [test-cmake] Using llama.cpp version 0.1.0-dev-b10335 [test-cmake] Initializing backend... load_backend: loaded CPU backend from /path/to/llama.cpp/examples/test-cmake/install/lib/llama.cpp/libggml-cpu-alderlake.so [test-cmake] Backend initialized.其中load_backend: loaded CPU backend from .../install/lib/llama.cpp/libggml-cpu-alderlake.so这一行是判断GGML_BACKEND_DL与GGML_BACKEND_DIR是否配置成功的直接证据:它明确说明运行期是动态加载了位于install/lib/llama.cpp/的 alderlake 变体 CPU 后端,而不是静态链接进libggml.so。
适用前提与排查要点
在使用这个示例前需要明确的限制:
- 依赖平台:示例脚本使用
LD_LIBRARY_PATH注入动态库路径,属于 Linux/macOS 风格;Windows 用户需要改用 PATH 或将.dll放入可执行文件目录,脚本需自行调整。 - 依赖共享库:
GGML_BACKEND_DL=ON在 ggml/src/CMakeLists.txt 中要求BUILD_SHARED_LIBS=ON,两者必须同时开启; - CPU 变体与 native 互斥:ggml/src/ggml-cpu/CMakeLists.txt 明确
GGML_NATIVE与GGML_BACKEND_DL不兼容,需要改用GGML_CPU_ALL_VARIANTS,这也正是build-install.sh的选法; - 安装前缀:
CMAKE_INSTALL_PREFIX硬编码为${PWD}/install,若仓库不允许写入(如根目录只读),需先修改脚本里的路径; - 产物目录:
install/、build/、llama-build-install/均已被 .gitignore 忽略,可放心在本地重复执行。
当你的项目修改了安装相关 CMake 配置(例如调整LLAMA_LIB_INSTALL_DIR、切换GGML_BACKEND_DL)后,最直接的验证路径就是本示例的“安装 →find_package→llama_backend_init”闭环:build-install.sh验证安装 layout,build.sh验证下游可发现性与运行期后端加载,两者合起来即等价于一次轻量化的集成回归。
【免费下载链接】llama.cppLLM inference in C/C++项目地址: https://gitcode.com/GitHub_Trending/ll/llama.cpp
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考