Skip to content

Commit 59e8ff4

Browse files
Merge PR #470: Windows build support for runanywhere-commons (rebase of #383)
Adds first-class Windows (MSVC/VS 2022) build support: POSIX compat shim, CMake presets, bundled zlib, ONNX Runtime Windows fetch, .bat build scripts, plus cross-platform bug fixes surfaced during review. Original author: @peilinok (via sanchitmonga22 revival) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2 parents 26ba2b8 + d48fc0a commit 59e8ff4

28 files changed

Lines changed: 1279 additions & 43 deletions
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Windows batch files MUST use CRLF line endings. cmd.exe's parser can
2+
# misbehave at 512-byte boundaries with LF-only line endings (label parsing
3+
# breaks, labels with delayed expansion fail intermittently). Keep these
4+
# files as CRLF even on non-Windows checkouts.
5+
*.bat text eol=crlf
6+
*.cmd text eol=crlf
7+
8+
# Shell scripts must stay LF.
9+
*.sh text eol=lf

sdk/runanywhere-commons/CMakeLists.txt

Lines changed: 145 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ option(RAC_BUILD_SERVER "Build OpenAI-compatible HTTP server (runanywhere-server
4646
# C++ CONFIGURATION
4747
# =============================================================================
4848

49+
# C++20 is required because MSVC does not implement C++17 designated initializers
50+
# (e.g. `struct S s = {.field = value};`) — the codebase uses them in several
51+
# places (service vtable registration, config structs). All supported compilers
52+
# (GCC 10+, Clang 11+, MSVC 19.28+ / VS 2019 16.8+) support C++20.
4953
set(CMAKE_CXX_STANDARD 20)
5054
set(CMAKE_CXX_STANDARD_REQUIRED ON)
5155
set(CMAKE_CXX_EXTENSIONS OFF)
@@ -90,6 +94,9 @@ elseif(APPLE)
9094
elseif(UNIX)
9195
set(RAC_PLATFORM_LINUX TRUE)
9296
set(RAC_PLATFORM_NAME "Linux")
97+
elseif(WIN32)
98+
set(RAC_PLATFORM_WINDOWS TRUE)
99+
set(RAC_PLATFORM_NAME "Windows")
93100
else()
94101
set(RAC_PLATFORM_NAME "Unknown")
95102
endif()
@@ -125,6 +132,57 @@ if(NOT DEFINED LIBARCHIVE_VERSION)
125132
set(LIBARCHIVE_VERSION "3.8.1")
126133
endif()
127134

135+
# -----------------------------------------------------------------------------
136+
# Zlib: Bundle from source when system zlib is not available.
137+
# Windows (MSVC) and some cross-compilation targets don't ship zlib.
138+
# We try system first; if not found, build from source so libarchive gets it.
139+
# -----------------------------------------------------------------------------
140+
find_package(ZLIB QUIET)
141+
if(NOT ZLIB_FOUND)
142+
message(STATUS "System zlib not found — bundling from source...")
143+
FetchContent_Declare(
144+
zlib_src
145+
GIT_REPOSITORY https://github.com/madler/zlib.git
146+
GIT_TAG v1.3.1
147+
GIT_SHALLOW TRUE
148+
)
149+
# Prevent zlib from installing or building examples
150+
set(ZLIB_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
151+
set(SKIP_INSTALL_ALL ON CACHE BOOL "" FORCE)
152+
set(SKIP_INSTALL_LIBRARIES ON CACHE BOOL "" FORCE)
153+
set(SKIP_INSTALL_HEADERS ON CACHE BOOL "" FORCE)
154+
set(SKIP_INSTALL_FILES ON CACHE BOOL "" FORCE)
155+
# Save and restore BUILD_SHARED_LIBS
156+
set(_SAVED_BSL_ZLIB ${BUILD_SHARED_LIBS})
157+
set(BUILD_SHARED_LIBS OFF)
158+
FetchContent_MakeAvailable(zlib_src)
159+
set(BUILD_SHARED_LIBS ${_SAVED_BSL_ZLIB})
160+
161+
# Set cache variables so libarchive's find_package(ZLIB) picks up our build.
162+
# For multi-config generators (Visual Studio), we point to the static lib
163+
# output. The actual path is resolved at build time via target_link_libraries.
164+
set(ZLIB_INCLUDE_DIR "${zlib_src_SOURCE_DIR};${zlib_src_BINARY_DIR}" CACHE PATH "" FORCE)
165+
set(ZLIB_INCLUDE_DIRS "${zlib_src_SOURCE_DIR};${zlib_src_BINARY_DIR}" CACHE PATH "" FORCE)
166+
# Use the CMake target name directly - this works because we link manually below
167+
set(ZLIB_LIBRARY zlibstatic CACHE STRING "" FORCE)
168+
set(ZLIB_LIBRARIES zlibstatic CACHE STRING "" FORCE)
169+
set(ZLIB_FOUND TRUE CACHE BOOL "" FORCE)
170+
# Create an imported ZLIB::ZLIB target that points to our zlibstatic
171+
if(NOT TARGET ZLIB::ZLIB)
172+
add_library(ZLIB::ZLIB ALIAS zlibstatic)
173+
endif()
174+
# On MSVC, set zlibstatic output to a known location so the linker can find it
175+
if(MSVC AND TARGET zlibstatic)
176+
set_target_properties(zlibstatic PROPERTIES
177+
ARCHIVE_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/lib"
178+
ARCHIVE_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/lib"
179+
)
180+
endif()
181+
message(STATUS "Bundled zlib ready (v1.3.1)")
182+
else()
183+
message(STATUS "Using system zlib: ${ZLIB_LIBRARIES}")
184+
endif()
185+
128186
# -----------------------------------------------------------------------------
129187
# BZip2: Bundle from source for cross-compilation targets
130188
# Android NDK and Emscripten don't ship libbz2. macOS/iOS have it in the SDK.
@@ -156,6 +214,13 @@ if(NOT BZIP2_FOUND)
156214
set(BZIP2_INCLUDE_DIR "${bzip2_src_SOURCE_DIR}" CACHE PATH "" FORCE)
157215
set(BZIP2_LIBRARIES bz2_bundled CACHE STRING "" FORCE)
158216
set(BZIP2_FOUND TRUE CACHE BOOL "" FORCE)
217+
# On MSVC, set output to a known location so the linker can find it
218+
if(MSVC AND TARGET bz2_bundled)
219+
set_target_properties(bz2_bundled PROPERTIES
220+
ARCHIVE_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/lib"
221+
ARCHIVE_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/lib"
222+
)
223+
endif()
159224
message(STATUS "Bundled BZip2 ready (v1.0.8)")
160225
else()
161226
message(STATUS "Using system BZip2: ${BZIP2_LIBRARIES}")
@@ -182,7 +247,16 @@ set(ENABLE_LIBXML2 OFF CACHE BOOL "" FORCE)
182247
set(ENABLE_EXPAT OFF CACHE BOOL "" FORCE)
183248
set(ENABLE_PCREPOSIX OFF CACHE BOOL "" FORCE)
184249
set(ENABLE_PCRE2POSIX OFF CACHE BOOL "" FORCE)
185-
set(ENABLE_LIBGCC OFF CACHE BOOL "" FORCE)
250+
# libarchive's ENABLE_LIBGCC gates a `find_library(GCC_LIBRARY gcc)` / `-lgcc`
251+
# link. Needed on MSVC for the bundled runtime; a no-op on Apple/Clang (no
252+
# libgcc); on Linux+GCC keep it OFF so we don't add an explicit -lgcc to the
253+
# link line (it's pulled in implicitly and adding it can cause duplicate-symbol
254+
# warnings in static configurations).
255+
if(MSVC)
256+
set(ENABLE_LIBGCC ON CACHE BOOL "" FORCE)
257+
else()
258+
set(ENABLE_LIBGCC OFF CACHE BOOL "" FORCE)
259+
endif()
186260
set(ENABLE_CNG OFF CACHE BOOL "" FORCE)
187261
set(ENABLE_TAR OFF CACHE BOOL "" FORCE) # Don't build bsdtar binary
188262
set(ENABLE_CPIO OFF CACHE BOOL "" FORCE) # Don't build bsdcpio binary
@@ -203,6 +277,32 @@ if(TARGET bz2_bundled AND TARGET archive_static)
203277
target_link_libraries(archive_static bz2_bundled)
204278
endif()
205279

280+
# Link bundled zlib to libarchive if we built it from source
281+
if(TARGET zlibstatic AND TARGET archive_static)
282+
target_link_libraries(archive_static zlibstatic)
283+
target_include_directories(archive_static PRIVATE ${zlib_src_SOURCE_DIR} ${zlib_src_BINARY_DIR})
284+
endif()
285+
286+
# On MSVC multi-config generators, libarchive references "zlibstatic.lib" and
287+
# "bz2_bundled.lib" by bare filename. The linker can't find them because they
288+
# are built into <config>/ subdirectories. Fix: redirect their output to a
289+
# single known directory and add it to the global linker search path.
290+
if(MSVC)
291+
set(RAC_BUNDLED_LIB_DIR "${CMAKE_BINARY_DIR}/_bundled_libs")
292+
foreach(_cfg Release Debug RelWithDebInfo MinSizeRel)
293+
string(TOUPPER "${_cfg}" _CFG)
294+
if(TARGET zlibstatic)
295+
set_target_properties(zlibstatic PROPERTIES
296+
ARCHIVE_OUTPUT_DIRECTORY_${_CFG} "${RAC_BUNDLED_LIB_DIR}")
297+
endif()
298+
if(TARGET bz2_bundled)
299+
set_target_properties(bz2_bundled PROPERTIES
300+
ARCHIVE_OUTPUT_DIRECTORY_${_CFG} "${RAC_BUNDLED_LIB_DIR}")
301+
endif()
302+
endforeach()
303+
link_directories("${RAC_BUNDLED_LIB_DIR}")
304+
endif()
305+
206306
message(STATUS "libarchive ready (v${LIBARCHIVE_VERSION})")
207307

208308
# =============================================================================
@@ -234,6 +334,22 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU")
234334
add_compile_options(-fvisibility=hidden)
235335
add_compile_options(-ffunction-sections -fdata-sections)
236336
endif()
337+
elseif(MSVC)
338+
# /W3 = reasonable warning level, /utf-8 = source/execution charset
339+
# /Zc:__cplusplus = report correct __cplusplus value
340+
# /EHsc = standard C++ exception handling
341+
add_compile_options(/W3 /utf-8 /Zc:__cplusplus /EHsc)
342+
# Suppress common harmless warnings in third-party code
343+
add_compile_options(/wd4244 /wd4267 /wd4996)
344+
add_compile_definitions(_CRT_SECURE_NO_WARNINGS NOMINMAX WIN32_LEAN_AND_MEAN)
345+
# Use generator expressions instead of CMAKE_BUILD_TYPE so Release flags
346+
# apply correctly with multi-config generators (Visual Studio), where
347+
# CMAKE_BUILD_TYPE is empty at configure time and the config is selected
348+
# at build time via --config Release.
349+
add_compile_options(
350+
$<$<CONFIG:Release>:/O2>
351+
$<$<CONFIG:Release>:/DNDEBUG>
352+
)
237353
endif()
238354

239355
# =============================================================================
@@ -352,8 +468,11 @@ set(RAC_FEATURES_SOURCES
352468
src/features/embeddings/embeddings_component.cpp
353469
# Voice Agent
354470
src/features/voice_agent/voice_agent.cpp
355-
# Result memory management
356-
src/features/result_free.cpp
471+
# Result memory management (weak symbol fallbacks)
472+
# On MSVC, each service .cpp already provides strong definitions,
473+
# so result_free.cpp (which uses __attribute__((weak)) on GCC/Clang)
474+
# must be excluded to avoid LNK2005 duplicate symbol errors.
475+
$<$<NOT:$<CXX_COMPILER_ID:MSVC>>:src/features/result_free.cpp>
357476
)
358477

359478
# Platform services (Apple Foundation Models + System TTS + CoreML Diffusion)
@@ -421,7 +540,20 @@ if(RAC_BUILD_SHARED)
421540
endif()
422541

423542
# libarchive - native archive extraction
424-
target_link_libraries(rac_commons PRIVATE archive_static)
543+
# PUBLIC on Windows because MSVC static libs don't propagate transitive deps
544+
if(WIN32)
545+
target_link_libraries(rac_commons PUBLIC archive_static)
546+
# Tell libarchive headers we're using the static library (not DLL)
547+
target_compile_definitions(rac_commons PRIVATE LIBARCHIVE_STATIC)
548+
if(TARGET zlibstatic)
549+
target_link_libraries(rac_commons PUBLIC zlibstatic)
550+
endif()
551+
if(TARGET bz2_bundled)
552+
target_link_libraries(rac_commons PUBLIC bz2_bundled)
553+
endif()
554+
else()
555+
target_link_libraries(rac_commons PRIVATE archive_static)
556+
endif()
425557
target_include_directories(rac_commons PRIVATE ${libarchive_SOURCE_DIR}/libarchive ${libarchive_BINARY_DIR})
426558

427559
# Platform-specific linking
@@ -441,6 +573,12 @@ if(RAC_PLATFORM_ANDROID)
441573
target_link_libraries(rac_commons PUBLIC log)
442574
endif()
443575

576+
if(RAC_PLATFORM_WINDOWS)
577+
target_compile_definitions(rac_commons PRIVATE RAC_PLATFORM_WINDOWS=1)
578+
# ws2_32 = Winsock, shlwapi = path utilities
579+
target_link_libraries(rac_commons PUBLIC ws2_32 shlwapi)
580+
endif()
581+
444582
set_target_properties(rac_commons PROPERTIES
445583
CXX_STANDARD 20
446584
CXX_STANDARD_REQUIRED ON
@@ -588,6 +726,9 @@ message(STATUS " Services: LLM, STT, TTS, VAD interfaces")
588726
if(APPLE AND RAC_BUILD_PLATFORM)
589727
message(STATUS " Platform: Apple Foundation Models, System TTS")
590728
endif()
729+
if(RAC_PLATFORM_WINDOWS)
730+
message(STATUS " Platform: Windows (MSVC)")
731+
endif()
591732
if(RAC_BUILD_BACKENDS)
592733
message(STATUS " Backends: LlamaCPP=${RAC_BACKEND_LLAMACPP}, ONNX=${RAC_BACKEND_ONNX}, WhisperCPP=${RAC_BACKEND_WHISPERCPP}, WhisperKitCoreML=${RAC_BACKEND_WHISPERKIT_COREML}")
593734
if(RAC_BACKEND_RAG)

sdk/runanywhere-commons/CMakePresets.json

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,42 @@
2727
"CMAKE_BUILD_TYPE": "Debug",
2828
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON"
2929
}
30+
},
31+
{
32+
"name": "windows-msvc",
33+
"displayName": "Windows MSVC (all backends)",
34+
"description": "Windows build with MSVC, all backends enabled",
35+
"binaryDir": "${sourceDir}/build/windows",
36+
"generator": "Visual Studio 17 2022",
37+
"architecture": {
38+
"value": "x64",
39+
"strategy": "set"
40+
},
41+
"cacheVariables": {
42+
"RAC_BUILD_BACKENDS": "ON",
43+
"RAC_BACKEND_LLAMACPP": "ON",
44+
"RAC_BACKEND_ONNX": "ON",
45+
"RAC_BACKEND_RAG": "OFF",
46+
"RAC_BACKEND_WHISPERCPP": "OFF",
47+
"RAC_BUILD_PLATFORM": "OFF",
48+
"RAC_BUILD_TESTS": "ON"
49+
}
50+
},
51+
{
52+
"name": "windows-msvc-core",
53+
"displayName": "Windows MSVC (core only)",
54+
"description": "Windows build with MSVC, core library only (no backends). Build config is selected at build time via --config Release (Visual Studio generators are multi-config and ignore CMAKE_BUILD_TYPE).",
55+
"binaryDir": "${sourceDir}/build/windows-core",
56+
"generator": "Visual Studio 17 2022",
57+
"architecture": {
58+
"value": "x64",
59+
"strategy": "set"
60+
},
61+
"cacheVariables": {
62+
"RAC_BUILD_BACKENDS": "OFF",
63+
"RAC_BUILD_PLATFORM": "OFF",
64+
"RAC_BUILD_TESTS": "ON"
65+
}
3066
}
3167
]
3268
}

sdk/runanywhere-commons/VERSIONS

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ ONNX_VERSION_MACOS=1.23.2
4949
# Linux version (can use latest)
5050
ONNX_VERSION_LINUX=1.23.2
5151

52+
# Windows version (can use latest)
53+
ONNX_VERSION_WINDOWS=1.23.2
54+
5255
# =============================================================================
5356
# Sherpa-ONNX (via runanywhere-core)
5457
# =============================================================================
@@ -69,6 +72,9 @@ SHERPA_ONNX_VERSION_MACOS=1.12.18
6972
# Linux version
7073
SHERPA_ONNX_VERSION_LINUX=1.12.23
7174

75+
# Windows version
76+
SHERPA_ONNX_VERSION_WINDOWS=1.12.23
77+
7278
# =============================================================================
7379
# llama.cpp (LLM inference)
7480
# =============================================================================

sdk/runanywhere-commons/cmake/FetchONNXRuntime.cmake

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,39 @@ elseif(UNIX)
265265

266266
message(STATUS "ONNX Runtime Linux library: ${onnxruntime_SOURCE_DIR}/lib/libonnxruntime.so")
267267

268+
elseif(WIN32)
269+
# Windows: Download Windows binaries
270+
if(NOT DEFINED ONNX_VERSION_WINDOWS OR "${ONNX_VERSION_WINDOWS}" STREQUAL "")
271+
message(FATAL_ERROR "ONNX_VERSION_WINDOWS not defined in VERSIONS file")
272+
endif()
273+
274+
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
275+
set(ONNX_URL "https://github.com/microsoft/onnxruntime/releases/download/v${ONNX_VERSION_WINDOWS}/onnxruntime-win-x64-${ONNX_VERSION_WINDOWS}.zip")
276+
else()
277+
set(ONNX_URL "https://github.com/microsoft/onnxruntime/releases/download/v${ONNX_VERSION_WINDOWS}/onnxruntime-win-x86-${ONNX_VERSION_WINDOWS}.zip")
278+
endif()
279+
280+
FetchContent_Declare(
281+
onnxruntime
282+
URL ${ONNX_URL}
283+
DOWNLOAD_EXTRACT_TIMESTAMP TRUE
284+
)
285+
286+
FetchContent_MakeAvailable(onnxruntime)
287+
288+
add_library(onnxruntime SHARED IMPORTED GLOBAL)
289+
290+
set_target_properties(onnxruntime PROPERTIES
291+
IMPORTED_IMPLIB "${onnxruntime_SOURCE_DIR}/lib/onnxruntime.lib"
292+
IMPORTED_LOCATION "${onnxruntime_SOURCE_DIR}/lib/onnxruntime.dll"
293+
)
294+
295+
target_include_directories(onnxruntime INTERFACE
296+
"${onnxruntime_SOURCE_DIR}/include"
297+
)
298+
299+
message(STATUS "ONNX Runtime Windows library: ${onnxruntime_SOURCE_DIR}/lib/onnxruntime.lib")
300+
268301
else()
269302
message(FATAL_ERROR "Unsupported platform for ONNX Runtime")
270303
endif()

sdk/runanywhere-commons/cmake/LoadVersions.cmake

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,13 @@ message(STATUS " ONNX_VERSION_IOS: ${RAC_ONNX_VERSION_IOS}")
5656
message(STATUS " ONNX_VERSION_ANDROID: ${RAC_ONNX_VERSION_ANDROID}")
5757
message(STATUS " ONNX_VERSION_MACOS: ${RAC_ONNX_VERSION_MACOS}")
5858
message(STATUS " ONNX_VERSION_LINUX: ${RAC_ONNX_VERSION_LINUX}")
59+
message(STATUS " ONNX_VERSION_WINDOWS: ${RAC_ONNX_VERSION_WINDOWS}")
5960
message(STATUS " Sherpa-ONNX:")
6061
message(STATUS " SHERPA_ONNX_VERSION_IOS: ${RAC_SHERPA_ONNX_VERSION_IOS}")
6162
message(STATUS " SHERPA_ONNX_VERSION_ANDROID: ${RAC_SHERPA_ONNX_VERSION_ANDROID}")
6263
message(STATUS " SHERPA_ONNX_VERSION_MACOS: ${RAC_SHERPA_ONNX_VERSION_MACOS}")
64+
message(STATUS " SHERPA_ONNX_VERSION_LINUX: ${RAC_SHERPA_ONNX_VERSION_LINUX}")
65+
message(STATUS " SHERPA_ONNX_VERSION_WINDOWS: ${RAC_SHERPA_ONNX_VERSION_WINDOWS}")
6366
message(STATUS " Other:")
6467
message(STATUS " LLAMACPP_VERSION: ${RAC_LLAMACPP_VERSION}")
6568
message(STATUS " NLOHMANN_JSON_VERSION: ${RAC_NLOHMANN_JSON_VERSION}")

0 commit comments

Comments
 (0)