Commit d63fbfe79 for llama.cpp
commit d63fbfe794b62990086043fa9e05fd6e8367b4d6
Author: Adrien Gallouët <angt@huggingface.co>
Date: Fri Oct 30 10:56:53 2026 +0100
cmake : embed an app manifest in all Windows exes (#31329)
* cmake : embed an app manifest in all Windows exes
Signed-off-by: Adrien Gallouët <angt@huggingface.co>
* Remove LLAMA_WIN_APP_CMAKE
Signed-off-by: Adrien Gallouët <angt@huggingface.co>
---------
Signed-off-by: Adrien Gallouët <angt@huggingface.co>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 0cf2dc815..e37c8aa4c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -150,6 +150,7 @@ option(LLAMA_LLGUIDANCE "llama-common: include LLGuidance library for structured
# Required for relocatable CMake package
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/build-info.cmake)
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/common.cmake)
+include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/win-app.cmake)
if (NOT DEFINED LLAMA_BUILD_NUMBER)
set(LLAMA_BUILD_NUMBER ${BUILD_NUMBER})
@@ -269,6 +270,8 @@ if (LLAMA_BUILD_MTMD AND NOT (LLAMA_BUILD_COMMON AND LLAMA_BUILD_TOOLS))
add_subdirectory(tools/mtmd)
endif()
+llama_apply_app_manifests()
+
#
# install
#
diff --git a/cmake/win-app.cmake b/cmake/win-app.cmake
new file mode 100644
index 000000000..5456e48fd
--- /dev/null
+++ b/cmake/win-app.cmake
@@ -0,0 +1,66 @@
+set(LLAMA_WIN_APP_MANIFEST "${CMAKE_CURRENT_LIST_DIR}/../common/win-app.manifest")
+
+if(WIN32)
+ if(MSVC OR CMAKE_CXX_SIMULATE_ID STREQUAL "MSVC")
+ set(LLAMA_WIN_APP_MSVC_ABI TRUE)
+ else()
+ set(LLAMA_WIN_APP_MSVC_ABI FALSE)
+ endif()
+
+ if(NOT LLAMA_WIN_APP_MSVC_ABI)
+ if(CMAKE_VERSION VERSION_LESS 3.21)
+ message(FATAL_ERROR "CMake 3.21 or newer is required to build for Windows with the GNU ABI")
+ endif()
+ enable_language(RC)
+ set(LLAMA_WIN_APP_MANIFEST_RC "${CMAKE_CURRENT_BINARY_DIR}/win-app-manifest.rc")
+ configure_file("${CMAKE_CURRENT_LIST_DIR}/win-app.rc.in" "${LLAMA_WIN_APP_MANIFEST_RC}" @ONLY)
+ set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${LLAMA_WIN_APP_MANIFEST}")
+ endif()
+endif()
+
+function(llama_add_app_manifest target)
+ if(NOT WIN32)
+ return()
+ endif()
+ get_target_property(_aliased ${target} ALIASED_TARGET)
+ if(_aliased)
+ return()
+ endif()
+ get_target_property(_type ${target} TYPE)
+ if(NOT _type STREQUAL "EXECUTABLE")
+ return()
+ endif()
+ if(LLAMA_WIN_APP_MSVC_ABI)
+ target_sources(${target} PRIVATE "${LLAMA_WIN_APP_MANIFEST}")
+ else()
+ if(NOT TARGET llama-win-app-manifest-res)
+ add_library(llama-win-app-manifest-res OBJECT ${LLAMA_WIN_APP_MANIFEST_RC})
+ set_source_files_properties(${LLAMA_WIN_APP_MANIFEST_RC} PROPERTIES OBJECT_DEPENDS "${LLAMA_WIN_APP_MANIFEST}")
+ endif()
+ target_link_libraries(${target} PRIVATE "$<TARGET_OBJECTS:llama-win-app-manifest-res>")
+ endif()
+endfunction()
+
+function(llama_apply_app_manifests_dir dir)
+ get_property(_targets DIRECTORY "${dir}" PROPERTY BUILDSYSTEM_TARGETS)
+ foreach(_target IN LISTS _targets)
+ llama_add_app_manifest(${_target})
+ endforeach()
+ get_property(_subdirs DIRECTORY "${dir}" PROPERTY SUBDIRECTORIES)
+ foreach(_subdir IN LISTS _subdirs)
+ llama_apply_app_manifests_dir("${_subdir}")
+ endforeach()
+endfunction()
+
+function(llama_apply_app_manifests)
+ if(NOT WIN32)
+ return()
+ endif()
+ get_property(_subdirs DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" PROPERTY SUBDIRECTORIES)
+ foreach(_dir IN LISTS _subdirs)
+ get_filename_component(_name "${_dir}" NAME)
+ if(_name STREQUAL "app" OR _name STREQUAL "examples" OR _name STREQUAL "pocs" OR _name STREQUAL "tests" OR _name STREQUAL "tools")
+ llama_apply_app_manifests_dir("${_dir}")
+ endif()
+ endforeach()
+endfunction()
diff --git a/cmake/win-app.rc.in b/cmake/win-app.rc.in
new file mode 100644
index 000000000..38288a5f8
--- /dev/null
+++ b/cmake/win-app.rc.in
@@ -0,0 +1,3 @@
+// generated from cmake/win-app.rc.in - do not edit
+// RT_MANIFEST (24), ID 1: the application manifest shared by all llama.cpp executables
+1 24 "@LLAMA_WIN_APP_MANIFEST@"
diff --git a/common/win-app.manifest b/common/win-app.manifest
new file mode 100644
index 000000000..540f6ed81
--- /dev/null
+++ b/common/win-app.manifest
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+ <application xmlns="urn:schemas-microsoft-com:asm.v3">
+ <windowsSettings>
+ <activeCodePage xmlns="http://schemas.microsoft.com/SMI/2019/WindowsSettings">UTF-8</activeCodePage>
+ <longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware>
+ </windowsSettings>
+ </application>
+</assembly>
diff --git a/tools/server/server-tools.cpp b/tools/server/server-tools.cpp
index 12e9dbb8c..ad6dd1d78 100644
--- a/tools/server/server-tools.cpp
+++ b/tools/server/server-tools.cpp
@@ -39,9 +39,25 @@ namespace fs = std::filesystem;
// internal helpers
//
-// a child process writes in the OEM code page, so accented output would reach
-// the JSON layer as invalid bytes. run() spawns without a console, so the
-// console code page never applies
+#if defined(_WIN32)
+// code page of the system locale, read once at first use
+static UINT legacy_oem_code_page() {
+ static const UINT cp = [] {
+ DWORD value = 0;
+ const int ret = GetLocaleInfoEx(LOCALE_NAME_SYSTEM_DEFAULT, LOCALE_IDEFAULTCODEPAGE | LOCALE_RETURN_NUMBER,
+ reinterpret_cast<LPWSTR>(&value), sizeof(value) / sizeof(WCHAR));
+ if (ret > 0 && value && value != CP_OEMCP) {
+ return static_cast<UINT>(value);
+ }
+ return GetOEMCP();
+ }();
+ return cp;
+}
+#endif
+
+// a child process writes in the legacy console code page, so accented output would reach
+// the JSON layer as invalid bytes. run() spawns without a console, so the console
+// code page never applies
static std::string console_output_to_utf8(const std::string & text) {
#if defined(_WIN32)
// a chunk can end mid sequence, so the incomplete tail is dropped first
@@ -50,7 +66,7 @@ static std::string console_output_to_utf8(const std::string & text) {
return text;
}
- const UINT cp = GetOEMCP();
+ const UINT cp = legacy_oem_code_page();
// fail rather than emit replacement characters when the code page is wrong
const int wide_len = MultiByteToWideChar(cp, MB_ERR_INVALID_CHARS, text.data(), (int) text.size(), nullptr, 0);