cmake_minimum_required(VERSION 3.16) project(libmodbus_win C) option(MODBUS_ENABLE_RTU "Build RTU backend (modbus-rtu.c)" ON) option(MODBUS_ENABLE_TCP "Build TCP backend (modbus-tcp.c)" ON) option(MODBUS_FIX_MSVC_SOCKOPT_WARNINGS "Fix MSVC winsock C4133 warnings" ON) set(SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/src") # --- 1) Prepare shim include dir (build-only includes live here) --- set(SHIM_INC_DIR "${CMAKE_CURRENT_BINARY_DIR}/include") file(MAKE_DIRECTORY "${SHIM_INC_DIR}") # We expect config.h to exist in ${SRC_DIR}/config.h (you generate it via src/win32/configure.js) if(NOT EXISTS "${SRC_DIR}/win32/config.h") message(FATAL_ERROR "config.h not found at ${SRC_DIR}/win32/config.h. " "Run src/win32/configure.js and copy config.h into src/ before configuring with CMake.") endif() # --- 2) Generate shim modbus.h (static-friendly MODBUS_API) --- file(READ "${SRC_DIR}/modbus.h" MODBUS_H_TXT) set(MODBUS_API_BLOCK "#if defined(_MSC_VER) # if defined(DLLBUILD) /* define DLLBUILD when building the DLL */ # define MODBUS_API __declspec(dllexport) # else # define MODBUS_API __declspec(dllimport) # endif #else # define MODBUS_API #endif") set(MODBUS_API_BLOCK_REPL "#if defined(_MSC_VER) /* Static library: do NOT use dllimport/dllexport in public headers */ # if defined(DLLBUILD) # define MODBUS_API __declspec(dllexport) # else # define MODBUS_API # endif #else # define MODBUS_API #endif") string(REPLACE "${MODBUS_API_BLOCK}" "${MODBUS_API_BLOCK_REPL}" MODBUS_H_TXT "${MODBUS_H_TXT}") file(WRITE "${SHIM_INC_DIR}/modbus.h" "${MODBUS_H_TXT}") # Copy remaining headers needed to compile (build-only set includes private/config) file(COPY "${SRC_DIR}/modbus-tcp.h" DESTINATION "${SHIM_INC_DIR}") file(COPY "${SRC_DIR}/modbus-rtu.h" DESTINATION "${SHIM_INC_DIR}") file(COPY "${SRC_DIR}/modbus-version.h" DESTINATION "${SHIM_INC_DIR}") # Build-only headers required by .c files file(COPY "${SRC_DIR}/win32/config.h" DESTINATION "${SHIM_INC_DIR}") file(COPY "${SRC_DIR}/modbus-private.h" DESTINATION "${SHIM_INC_DIR}") file(COPY "${SRC_DIR}/modbus-tcp-private.h" DESTINATION "${SHIM_INC_DIR}") file(COPY "${SRC_DIR}/modbus-rtu-private.h" DESTINATION "${SHIM_INC_DIR}") # --- 3) Generate shim sources (do not modify upstream sources on disk) --- set(SHIM_SRC_DIR "${CMAKE_CURRENT_BINARY_DIR}/shim_src") file(MAKE_DIRECTORY "${SHIM_SRC_DIR}") function(make_shim_source in_file out_file) file(READ "${in_file}" TXT) # Force includes via include paths (so our shim headers win) string(REPLACE "#include \"modbus.h\"" "#include " TXT "${TXT}") string(REPLACE "#include \"modbus-tcp.h\"" "#include " TXT "${TXT}") string(REPLACE "#include \"modbus-rtu.h\"" "#include " TXT "${TXT}") string(REPLACE "#include \"modbus-version.h\"" "#include " TXT "${TXT}") # Some files include private/config with quotes; route those too string(REPLACE "#include \"config.h\"" "#include " TXT "${TXT}") string(REPLACE "#include \"modbus-private.h\"" "#include " TXT "${TXT}") string(REPLACE "#include \"modbus-tcp-private.h\"" "#include " TXT "${TXT}") string(REPLACE "#include \"modbus-rtu-private.h\"" "#include " TXT "${TXT}") # Optional: silence MSVC winsock setsockopt warnings (C4133) # On Windows setsockopt expects const char* optval; libmodbus passes &int. if (MSVC AND MODBUS_FIX_MSVC_SOCKOPT_WARNINGS) string(REPLACE ", &option, sizeof(int))" ", (const char*)&option, sizeof(int))" TXT "${TXT}") endif() file(WRITE "${out_file}" "${TXT}") endfunction() # Base sources make_shim_source("${SRC_DIR}/modbus.c" "${SHIM_SRC_DIR}/modbus.c") make_shim_source("${SRC_DIR}/modbus-data.c" "${SHIM_SRC_DIR}/modbus-data.c") set(MODBUS_SOURCES "${SHIM_SRC_DIR}/modbus.c" "${SHIM_SRC_DIR}/modbus-data.c" ) if(MODBUS_ENABLE_TCP) make_shim_source("${SRC_DIR}/modbus-tcp.c" "${SHIM_SRC_DIR}/modbus-tcp.c") list(APPEND MODBUS_SOURCES "${SHIM_SRC_DIR}/modbus-tcp.c") endif() if(MODBUS_ENABLE_RTU) make_shim_source("${SRC_DIR}/modbus-rtu.c" "${SHIM_SRC_DIR}/modbus-rtu.c") list(APPEND MODBUS_SOURCES "${SHIM_SRC_DIR}/modbus-rtu.c") endif() add_library(modbus STATIC ${MODBUS_SOURCES}) # --- MSVC: silence WinSock signature warning (setsockopt wants const char*) --- if (MSVC) target_compile_options(modbus PRIVATE /wd4133) endif() # Use shim headers for build + consumers (PUBLIC), but we will INSTALL only public headers explicitly target_include_directories(modbus PUBLIC "${SHIM_INC_DIR}" ) if (WIN32) target_link_libraries(modbus PUBLIC ws2_32) endif() # --- Install: only public headers (no config.h, no *-private.h) --- include(GNUInstallDirs) install(TARGETS modbus ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" ) set(PUBLIC_HEADERS "${SHIM_INC_DIR}/modbus.h" "${SHIM_INC_DIR}/modbus-version.h" ) if(MODBUS_ENABLE_TCP) list(APPEND PUBLIC_HEADERS "${SHIM_INC_DIR}/modbus-tcp.h") endif() if(MODBUS_ENABLE_RTU) list(APPEND PUBLIC_HEADERS "${SHIM_INC_DIR}/modbus-rtu.h") endif() install(FILES ${PUBLIC_HEADERS} DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" )