From d63fbfe794b62990086043fa9e05fd6e8367b4d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrien=20Gallou=C3=ABt?= Date: Fri, 30 Oct 2026 10:56:53 +0100 Subject: [PATCH] cmake : embed an app manifest in all Windows exes (#31329) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * cmake : embed an app manifest in all Windows exes Signed-off-by: Adrien Gallouët * Remove LLAMA_WIN_APP_CMAKE Signed-off-by: Adrien Gallouët --------- Signed-off-by: Adrien Gallouët --- CMakeLists.txt | 3 ++ cmake/win-app.cmake | 66 +++++++++++++++++++++++++++++++++++ cmake/win-app.rc.in | 3 ++ common/win-app.manifest | 9 +++++ tools/server/server-tools.cpp | 24 ++++++++++--- 5 files changed, 101 insertions(+), 4 deletions(-) create mode 100644 cmake/win-app.cmake create mode 100644 cmake/win-app.rc.in create mode 100644 common/win-app.manifest 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 "$") + 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 @@ + + + + + UTF-8 + true + + + 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(&value), sizeof(value) / sizeof(WCHAR)); + if (ret > 0 && value && value != CP_OEMCP) { + return static_cast(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);