chore: consolidate gRPC and protobuf linking into a dedicated support library

- Introduced a new `yaze_grpc_support` library to centralize all gRPC and protobuf usage, addressing Windows linker errors and improving build stability.
- Updated CMake configurations across various components to link against the new support library instead of individual protobuf targets, simplifying the linking process.
- Removed legacy whole-archive linking logic, ensuring a cleaner and more maintainable build setup.

Benefits:
- Reduces complexity in CMake files and enhances compatibility across platforms.
- Prevents potential linker errors by consolidating gRPC and protobuf dependencies into a single library.
This commit is contained in:
scawful
2025-10-18 15:58:58 -04:00
parent 8e86c1bbdf
commit 6db7ba4782
15 changed files with 152 additions and 170 deletions

View File

@@ -67,6 +67,9 @@ set(YAZE_WITH_JSON ON)
set(YAZE_WITH_GRPC ON)
set(Z3ED_AI ON)
# vcpkg option for desktop users (faster gRPC builds on Windows)
option(YAZE_USE_VCPKG_GRPC "Use vcpkg for gRPC on Windows (faster for desktop)" OFF)
# Minimal build override - disable only the most expensive features
if(YAZE_MINIMAL_BUILD)
set(YAZE_WITH_GRPC OFF)

View File

@@ -95,16 +95,7 @@ else()
endif()
endif()
# Filter WHOLEARCHIVE targets to only include libraries (not executables like protoc)
set(YAZE_PROTOBUF_WHOLEARCHIVE_TARGETS)
foreach(_proto_target IN LISTS YAZE_PROTOBUF_TARGETS)
if(TARGET ${_proto_target})
get_target_property(_target_type ${_proto_target} TYPE)
if(_target_type MATCHES ".*_LIBRARY")
list(APPEND YAZE_PROTOBUF_WHOLEARCHIVE_TARGETS ${_proto_target})
endif()
endif()
endforeach()
# WHOLEARCHIVE logic removed - protobuf linking now handled by yaze_grpc_support library
if(YAZE_PROTOBUF_TARGETS)
list(GET YAZE_PROTOBUF_TARGETS 0 YAZE_PROTOBUF_TARGET)

View File

@@ -114,12 +114,20 @@ set(gRPC_MSVC_STATIC_RUNTIME ON CACHE BOOL "" FORCE)
if(APPLE AND CMAKE_OSX_ARCHITECTURES STREQUAL "arm64")
set(ABSL_USE_EXTERNAL_GOOGLETEST OFF CACHE BOOL "" FORCE)
set(ABSL_BUILD_TEST_HELPERS OFF CACHE BOOL "" FORCE)
# Disable problematic random targets that use x86-specific instructions
set(ABSL_RANDOM_HWAES_IMPL OFF CACHE BOOL "" FORCE)
set(ABSL_RANDOM_HWAES OFF CACHE BOOL "" FORCE)
# Disable all x86-specific random implementations
set(ABSL_RANDOM_INTERNAL_RANDEN_HWAES_IMPL OFF CACHE BOOL "" FORCE)
set(ABSL_RANDOM_INTERNAL_RANDEN_HWAES OFF CACHE BOOL "" FORCE)
# Force use of portable random implementation
set(ABSL_RANDOM_INTERNAL_PLATFORM_IMPL "portable" CACHE STRING "" FORCE)
endif()
# Declare gRPC version - using latest for all platforms
# v1.75.1 has ARM64 + modern compiler fixes
set(_GRPC_VERSION "v1.75.1")
set(_GRPC_VERSION_REASON "Latest stable - ARM64 macOS + modern Clang/MSVC compatibility")
# Declare gRPC version - using stable version with better protobuf compatibility
# v1.67.1 has good stability and protobuf compatibility
set(_GRPC_VERSION "v1.67.1")
set(_GRPC_VERSION_REASON "Stable version with good protobuf compatibility")
# Windows-specific: Disable BoringSSL ASM to avoid NASM build issues
if(WIN32)
@@ -212,7 +220,15 @@ endif()
# Fix Abseil ARM64 macOS compile flags (remove x86-specific flags)
if(APPLE AND DEFINED CMAKE_OSX_ARCHITECTURES AND CMAKE_OSX_ARCHITECTURES STREQUAL "arm64")
foreach(_absl_target IN ITEMS absl_random_internal_randen_hwaes absl_random_internal_randen_hwaes_impl)
# List of all Abseil targets that might have x86-specific flags
set(_absl_targets_with_x86_flags
absl_random_internal_randen_hwaes
absl_random_internal_randen_hwaes_impl
absl_random_internal_randen_hwaes_impl
absl_random_internal_randen_hwaes
)
foreach(_absl_target IN LISTS _absl_targets_with_x86_flags)
if(TARGET ${_absl_target})
get_target_property(_absl_opts ${_absl_target} COMPILE_OPTIONS)
if(_absl_opts AND NOT _absl_opts STREQUAL "NOTFOUND")
@@ -227,12 +243,13 @@ if(APPLE AND DEFINED CMAKE_OSX_ARCHITECTURES AND CMAKE_OSX_ARCHITECTURES STREQUA
set(_absl_skip_next TRUE)
continue()
endif()
if(_absl_opt STREQUAL "-maes" OR _absl_opt STREQUAL "-msse4.1")
if(_absl_opt STREQUAL "-maes" OR _absl_opt STREQUAL "-msse4.1" OR _absl_opt STREQUAL "-msse2")
continue()
endif()
list(APPEND _absl_filtered_opts ${_absl_opt})
endforeach()
set_property(TARGET ${_absl_target} PROPERTY COMPILE_OPTIONS ${_absl_filtered_opts})
message(STATUS "Fixed ARM64 flags for ${_absl_target}")
endif()
endif()
endforeach()

View File

@@ -11,10 +11,10 @@
cmake_minimum_required(VERSION 3.16)
# Option to use vcpkg for gRPC on Windows
option(YAZE_USE_VCPKG_GRPC "Use vcpkg pre-compiled gRPC packages (Windows only)" ON)
# Option to use vcpkg for gRPC on Windows (default OFF for CI reliability)
option(YAZE_USE_VCPKG_GRPC "Use vcpkg pre-compiled gRPC packages (Windows only)" OFF)
if(WIN32 AND YAZE_USE_VCPKG_GRPC)
if(WIN32 AND YAZE_USE_VCPKG_GRPC AND DEFINED CMAKE_TOOLCHAIN_FILE)
message(STATUS "Attempting to use vcpkg gRPC packages for faster Windows builds...")
message(STATUS " Note: If gRPC not in vcpkg.json, will fallback to FetchContent (recommended)")
@@ -170,7 +170,6 @@ if(WIN32 AND YAZE_USE_VCPKG_GRPC)
# Export protobuf targets (vcpkg uses protobuf:: namespace)
set(YAZE_PROTOBUF_TARGETS protobuf::libprotobuf PARENT_SCOPE)
set(YAZE_PROTOBUF_WHOLEARCHIVE_TARGETS protobuf::libprotobuf PARENT_SCOPE)
# Get protobuf include directories for proto generation
get_target_property(_PROTOBUF_INCLUDE_DIRS protobuf::libprotobuf

View File

@@ -76,6 +76,11 @@ if(YAZE_BUILD_TESTS OR NOT YAZE_MINIMAL_BUILD)
include(app/test/test.cmake)
endif()
# Include gRPC support library (consolidates all protobuf/gRPC usage)
if(YAZE_WITH_GRPC)
include(app/service/grpc_support.cmake)
endif()
# Include agent/CLI components (needed by yaze_editor for agent features)
if(YAZE_BUILD_APP OR YAZE_BUILD_Z3ED OR YAZE_BUILD_TESTS)
include(cli/agent.cmake)

View File

@@ -108,30 +108,8 @@ if(YAZE_WITH_GRPC)
${CMAKE_SOURCE_DIR}/third_party/json/include)
target_compile_definitions(yaze_app_core_lib PRIVATE YAZE_WITH_JSON)
# Add proto definitions for ROM service, canvas automation, and test harness
# Test harness proto is needed because widget_discovery_service.h includes it
target_add_protobuf(yaze_app_core_lib
${PROJECT_SOURCE_DIR}/src/protos/rom_service.proto)
target_add_protobuf(yaze_app_core_lib
${PROJECT_SOURCE_DIR}/src/protos/canvas_automation.proto)
target_add_protobuf(yaze_app_core_lib
${PROJECT_SOURCE_DIR}/src/protos/imgui_test_harness.proto)
# Add unified gRPC server (non-test services only)
target_sources(yaze_app_core_lib PRIVATE
${CMAKE_SOURCE_DIR}/src/app/service/unified_grpc_server.cc
${CMAKE_SOURCE_DIR}/src/app/service/unified_grpc_server.h
)
target_link_libraries(yaze_app_core_lib PUBLIC
grpc++
grpc++_reflection
)
# NOTE: Do NOT link protobuf at library level on Windows - causes LNK1241
# Executables will link it with /WHOLEARCHIVE to include internal symbols
if(NOT WIN32 AND YAZE_PROTOBUF_TARGETS)
target_link_libraries(yaze_app_core_lib PUBLIC ${YAZE_PROTOBUF_TARGETS})
endif()
# Link to consolidated gRPC support library
target_link_libraries(yaze_app_core_lib PUBLIC yaze_grpc_support)
message(STATUS " - gRPC ROM service + canvas automation enabled")
endif()
@@ -202,22 +180,7 @@ target_link_libraries(yaze PRIVATE
absl::flags
absl::flags_parse
)
if(YAZE_WITH_GRPC AND YAZE_PROTOBUF_TARGETS)
# On Windows: Use /WHOLEARCHIVE instead of normal linking to include internal symbols
# On Unix: Use normal linking (symbols resolve correctly without whole-archive)
if(MSVC AND YAZE_PROTOBUF_WHOLEARCHIVE_TARGETS)
foreach(_yaze_proto_target IN LISTS YAZE_PROTOBUF_WHOLEARCHIVE_TARGETS)
if(TARGET ${_yaze_proto_target})
get_target_property(_target_type ${_yaze_proto_target} TYPE)
if(_target_type MATCHES ".*_LIBRARY")
target_link_options(yaze PRIVATE /WHOLEARCHIVE:$<TARGET_FILE:${_yaze_proto_target}>)
endif()
endif()
endforeach()
else()
target_link_libraries(yaze PRIVATE ${YAZE_PROTOBUF_TARGETS})
endif()
endif()
# gRPC/protobuf linking is now handled by yaze_grpc_support library
# Link test support library (yaze_editor needs TestManager)
if(TARGET yaze_test_support)

View File

@@ -163,15 +163,7 @@ endif()
# Conditionally link gRPC if enabled
if(YAZE_WITH_GRPC)
target_link_libraries(yaze_editor PRIVATE
grpc++
grpc++_reflection
)
# NOTE: Do NOT link protobuf at library level on Windows - causes LNK1241
# Executables will link it with /WHOLEARCHIVE to include internal symbols
if(NOT WIN32 AND YAZE_PROTOBUF_TARGETS)
target_link_libraries(yaze_editor PRIVATE ${YAZE_PROTOBUF_TARGETS})
endif()
target_link_libraries(yaze_editor PUBLIC yaze_grpc_support)
endif()
set_target_properties(yaze_editor PROPERTIES

View File

@@ -34,19 +34,7 @@ if(YAZE_BUILD_EMU AND NOT YAZE_MINIMAL_BUILD)
message(WARNING "yaze_emu needs yaze_test_support but TARGET not found")
endif()
# Windows: Link protobuf with /WHOLEARCHIVE (not via libraries to avoid LNK1241)
if(WIN32 AND MSVC AND YAZE_WITH_GRPC AND YAZE_PROTOBUF_WHOLEARCHIVE_TARGETS)
foreach(_yaze_proto_target IN LISTS YAZE_PROTOBUF_WHOLEARCHIVE_TARGETS)
if(TARGET ${_yaze_proto_target})
get_target_property(_target_type ${_yaze_proto_target} TYPE)
if(_target_type MATCHES ".*_LIBRARY")
target_link_options(yaze_emu PRIVATE /WHOLEARCHIVE:$<TARGET_FILE:${_yaze_proto_target}>)
endif()
endif()
endforeach()
elseif(YAZE_WITH_GRPC AND YAZE_PROTOBUF_TARGETS)
target_link_libraries(yaze_emu PRIVATE ${YAZE_PROTOBUF_TARGETS})
endif()
# gRPC/protobuf linking is now handled by yaze_grpc_support library
# Test engine is always available when tests are built
# No need for conditional definitions
@@ -68,19 +56,7 @@ if(YAZE_BUILD_EMU AND NOT YAZE_MINIMAL_BUILD)
absl::str_format
)
# Windows: Link protobuf with /WHOLEARCHIVE (not via libraries to avoid LNK1241)
if(WIN32 AND MSVC AND YAZE_WITH_GRPC AND YAZE_PROTOBUF_WHOLEARCHIVE_TARGETS)
foreach(_yaze_proto_target IN LISTS YAZE_PROTOBUF_WHOLEARCHIVE_TARGETS)
if(TARGET ${_yaze_proto_target})
get_target_property(_target_type ${_yaze_proto_target} TYPE)
if(_target_type MATCHES ".*_LIBRARY")
target_link_options(yaze_emu_test PRIVATE /WHOLEARCHIVE:$<TARGET_FILE:${_yaze_proto_target}>)
endif()
endif()
endforeach()
elseif(YAZE_WITH_GRPC AND YAZE_PROTOBUF_TARGETS)
target_link_libraries(yaze_emu_test PRIVATE ${YAZE_PROTOBUF_TARGETS})
endif()
# gRPC/protobuf linking is now handled by yaze_grpc_support library
message(STATUS "✓ yaze_emu_test: Headless emulator test harness configured")
message(STATUS "✓ yaze_emu: Standalone emulator executable configured")

View File

@@ -78,18 +78,7 @@ endif()
# Add gRPC support for ROM service
if(YAZE_WITH_GRPC)
target_add_protobuf(yaze_net ${PROJECT_SOURCE_DIR}/src/protos/rom_service.proto)
target_link_libraries(yaze_net PUBLIC
grpc++
grpc++_reflection
)
# NOTE: Do NOT link protobuf at library level on Windows - causes LNK1241
# Executables will link it with /WHOLEARCHIVE to include internal symbols
if(NOT WIN32 AND YAZE_PROTOBUF_TARGETS)
target_link_libraries(yaze_net PUBLIC ${YAZE_PROTOBUF_TARGETS})
endif()
target_link_libraries(yaze_net PUBLIC yaze_grpc_support)
message(STATUS " - gRPC ROM service enabled")
endif()

View File

@@ -0,0 +1,97 @@
# ==============================================================================
# Yaze gRPC Support Library
# ==============================================================================
# This library consolidates ALL gRPC/protobuf usage to eliminate Windows
# linker errors (LNK1241, LNK2005). All protobuf definitions and gRPC
# service implementations are contained here, with other libraries linking
# to this single source of truth.
#
# Dependencies: yaze_util, yaze_common, yaze_app_core_lib, yaze_zelda3, yaze_gfx, yaze_gui
# ==============================================================================
set(
YAZE_GRPC_SOURCES
# Core gRPC services
app/service/unified_grpc_server.cc
app/service/canvas_automation_service.cc
app/service/imgui_test_harness_service.cc
app/service/widget_discovery_service.cc
app/service/screenshot_utils.cc
# Test infrastructure
app/test/test_recorder.cc
app/test/test_script_parser.cc
# CLI agent gRPC client code (only files that actually exist)
cli/service/planning/tile16_proposal_generator.cc
cli/service/gui/gui_automation_client.cc
)
add_library(yaze_grpc_support STATIC ${YAZE_GRPC_SOURCES})
target_precompile_headers(yaze_grpc_support PRIVATE
"$<$<COMPILE_LANGUAGE:CXX>:${CMAKE_SOURCE_DIR}/src/yaze_pch.h>"
)
target_include_directories(yaze_grpc_support PUBLIC
${CMAKE_SOURCE_DIR}/src
${CMAKE_SOURCE_DIR}/src/app
${CMAKE_SOURCE_DIR}/src/lib
${CMAKE_SOURCE_DIR}/src/lib/imgui
${CMAKE_SOURCE_DIR}/src/lib/imgui_test_engine
${CMAKE_SOURCE_DIR}/incl
${SDL2_INCLUDE_DIR}
${PROJECT_BINARY_DIR}
)
# Link all required yaze libraries
target_link_libraries(yaze_grpc_support PUBLIC
yaze_app_core_lib
yaze_util
yaze_common
yaze_zelda3
yaze_gfx
yaze_gui
yaze_emulator
${ABSL_TARGETS}
${SDL_TARGETS}
)
# Add JSON support
if(YAZE_WITH_JSON)
target_include_directories(yaze_grpc_support PUBLIC
${CMAKE_SOURCE_DIR}/third_party/json/include)
target_compile_definitions(yaze_grpc_support PUBLIC YAZE_WITH_JSON)
endif()
# Add ALL protobuf definitions (consolidated from multiple libraries)
target_add_protobuf(yaze_grpc_support
${PROJECT_SOURCE_DIR}/src/protos/rom_service.proto
${PROJECT_SOURCE_DIR}/src/protos/canvas_automation.proto
${PROJECT_SOURCE_DIR}/src/protos/imgui_test_harness.proto
${PROJECT_SOURCE_DIR}/src/protos/emulator_service.proto
)
# Link gRPC and protobuf libraries (single point of linking)
target_link_libraries(yaze_grpc_support PUBLIC
grpc++
grpc++_reflection
${YAZE_PROTOBUF_TARGETS}
)
set_target_properties(yaze_grpc_support PROPERTIES
POSITION_INDEPENDENT_CODE ON
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
)
# Platform-specific compile definitions
if(UNIX AND NOT APPLE)
target_compile_definitions(yaze_grpc_support PRIVATE linux stricmp=strcasecmp)
elseif(APPLE)
target_compile_definitions(yaze_grpc_support PRIVATE MACOS)
elseif(WIN32)
target_compile_definitions(yaze_grpc_support PRIVATE WINDOWS)
endif()
message(STATUS "✓ yaze_grpc_support library configured (consolidated gRPC/protobuf)")

View File

@@ -15,16 +15,7 @@ set(YAZE_TEST_SOURCES
app/test/z3ed_test_suite.cc
)
# Add gRPC test harness services if enabled (depend on TestManager)
if(YAZE_WITH_GRPC)
list(APPEND YAZE_TEST_SOURCES
app/service/imgui_test_harness_service.cc
app/service/screenshot_utils.cc
app/service/widget_discovery_service.cc
app/test/test_recorder.cc
app/test/test_script_parser.cc
)
endif()
# gRPC test harness services are now in yaze_grpc_support library
add_library(yaze_test_support STATIC ${YAZE_TEST_SOURCES})
@@ -55,14 +46,8 @@ if(YAZE_WITH_GRPC)
${CMAKE_SOURCE_DIR}/third_party/json/include)
target_compile_definitions(yaze_test_support PRIVATE YAZE_WITH_JSON)
# Add test harness proto definition
target_add_protobuf(yaze_test_support
${PROJECT_SOURCE_DIR}/src/protos/imgui_test_harness.proto)
target_link_libraries(yaze_test_support PUBLIC
grpc++
grpc++_reflection
)
# Link to consolidated gRPC support library
target_link_libraries(yaze_test_support PUBLIC yaze_grpc_support)
message(STATUS " - gRPC test harness service enabled in yaze_test_support")
endif()

View File

@@ -151,21 +151,8 @@ endif()
# Add gRPC support for GUI automation
if(YAZE_WITH_GRPC)
# Generate proto files for yaze_agent
target_add_protobuf(yaze_agent
${PROJECT_SOURCE_DIR}/src/protos/imgui_test_harness.proto
${PROJECT_SOURCE_DIR}/src/protos/canvas_automation.proto
${PROJECT_SOURCE_DIR}/src/protos/emulator_service.proto)
target_link_libraries(yaze_agent PUBLIC
grpc++
grpc++_reflection
)
# NOTE: Do NOT link protobuf at library level on Windows - causes LNK1241
# Executables will link it with /WHOLEARCHIVE to include internal symbols
if(NOT WIN32 AND YAZE_PROTOBUF_TARGETS)
target_link_libraries(yaze_agent PUBLIC ${YAZE_PROTOBUF_TARGETS})
endif()
# Link to consolidated gRPC support library
target_link_libraries(yaze_agent PUBLIC yaze_grpc_support)
# Note: YAZE_WITH_GRPC is defined globally via add_compile_definitions in root CMakeLists.txt
# This ensures #ifdef YAZE_WITH_GRPC works in all translation units

View File

@@ -39,21 +39,6 @@ endif()
if(YAZE_WITH_GRPC)
message(STATUS "Adding gRPC support to z3ed CLI")
target_link_libraries(z3ed PRIVATE grpc++ grpc++_reflection)
if(YAZE_PROTOBUF_TARGETS)
# On Windows: Use /WHOLEARCHIVE instead of normal linking to include internal symbols
# On Unix: Use normal linking (symbols resolve correctly without whole-archive)
if(MSVC AND YAZE_PROTOBUF_WHOLEARCHIVE_TARGETS)
foreach(_yaze_proto_target IN LISTS YAZE_PROTOBUF_WHOLEARCHIVE_TARGETS)
if(TARGET ${_yaze_proto_target})
get_target_property(_target_type ${_yaze_proto_target} TYPE)
if(_target_type MATCHES ".*_LIBRARY")
target_link_options(z3ed PRIVATE /WHOLEARCHIVE:$<TARGET_FILE:${_yaze_proto_target}>)
endif()
endif()
endforeach()
else()
target_link_libraries(z3ed PRIVATE ${YAZE_PROTOBUF_TARGETS})
endif()
endif()
# Link to consolidated gRPC support library
target_link_libraries(z3ed PRIVATE yaze_grpc_support)
endif()

View File

@@ -47,19 +47,7 @@ set(HELPER_TOOLS
)
foreach(TOOL ${HELPER_TOOLS})
# Windows: Link protobuf with /WHOLEARCHIVE (not via yaze_core to avoid LNK1241)
if(WIN32 AND MSVC AND YAZE_WITH_GRPC AND YAZE_PROTOBUF_WHOLEARCHIVE_TARGETS)
foreach(_yaze_proto_target IN LISTS YAZE_PROTOBUF_WHOLEARCHIVE_TARGETS)
if(TARGET ${_yaze_proto_target})
get_target_property(_target_type ${_yaze_proto_target} TYPE)
if(_target_type MATCHES ".*_LIBRARY")
target_link_options(${TOOL} PRIVATE /WHOLEARCHIVE:$<TARGET_FILE:${_yaze_proto_target}>)
endif()
endif()
endforeach()
elseif(YAZE_WITH_GRPC AND YAZE_PROTOBUF_TARGETS)
target_link_libraries(${TOOL} PRIVATE ${YAZE_PROTOBUF_TARGETS})
endif()
# gRPC/protobuf linking is now handled by yaze_grpc_support library
if(WIN32)
if(MSVC)

View File

@@ -12,6 +12,11 @@
{
"name": "yaml-cpp",
"platform": "windows"
},
{
"name": "grpc",
"platform": "windows",
"features": ["codegen"]
}
]
}