cmake_minimum_required(VERSION 3.13) project(wibo LANGUAGES ASM C CXX) set(CMAKE_CXX_STANDARD 20) set(WIBO_VERSION "" CACHE STRING "Version string for the wibo binary; if empty, attempts to use git describe") if(NOT "${WIBO_VERSION}" STREQUAL "") set(WIBO_VERSION_STRING "${WIBO_VERSION}") elseif(DEFINED ENV{WIBO_VERSION} AND NOT "$ENV{WIBO_VERSION}" STREQUAL "") set(WIBO_VERSION_STRING "$ENV{WIBO_VERSION}") else() find_package(Git QUIET) if(GIT_FOUND) execute_process( COMMAND ${GIT_EXECUTABLE} describe --tags --dirty --always WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} RESULT_VARIABLE WIBO_GIT_DESCRIBE_RESULT OUTPUT_VARIABLE WIBO_GIT_DESCRIBE_OUTPUT OUTPUT_STRIP_TRAILING_WHITESPACE ) if(WIBO_GIT_DESCRIBE_RESULT EQUAL 0 AND NOT "${WIBO_GIT_DESCRIBE_OUTPUT}" STREQUAL "") set(WIBO_VERSION_STRING "${WIBO_GIT_DESCRIBE_OUTPUT}") endif() endif() endif() if(NOT DEFINED WIBO_VERSION_STRING OR "${WIBO_VERSION_STRING}" STREQUAL "") set(WIBO_VERSION_STRING "unknown") endif() set(WIBO_GENERATED_HEADER_DIR ${CMAKE_CURRENT_BINARY_DIR}/generated) file(MAKE_DIRECTORY ${WIBO_GENERATED_HEADER_DIR}) configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/src/version_info.h.in ${WIBO_GENERATED_HEADER_DIR}/version_info.h @ONLY ) # Detect if we're building for 64-bit based on the target architecture # CMAKE_SIZEOF_VOID_P is set by the project() command and the toolchain if(CMAKE_SIZEOF_VOID_P EQUAL 8) set(WIBO_64 ON) message(STATUS "Detected 64-bit target architecture") elseif(CMAKE_SIZEOF_VOID_P EQUAL 4) set(WIBO_64 OFF) message(STATUS "Detected 32-bit target architecture") else() message(FATAL_ERROR "Unknown pointer size: ${CMAKE_SIZEOF_VOID_P}") endif() option(WIBO_ENABLE_FIXTURE_TESTS "Enable Win32 fixture tests (requires i686-w64-mingw32)" ON) option(WIBO_ENABLE_LIBURING "Enable liburing for asynchronous I/O" OFF) set(WIBO_ENABLE_LTO "AUTO" CACHE STRING "Enable link-time optimization (LTO)") set_property(CACHE WIBO_ENABLE_LTO PROPERTY STRINGS "AUTO" "ON" "OFF") add_compile_options( $<$:-fno-exceptions> $<$:-fno-rtti> ) if (WIBO_ENABLE_LTO STREQUAL "AUTO") if (CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "MinSizeRel") include(CheckIPOSupported) check_ipo_supported(RESULT ipo_supported OUTPUT ipo_error) if (ipo_supported) message(STATUS "Enabling LTO") set(WIBO_ENABLE_LTO ON) else() message(STATUS "LTO not supported: ${ipo_error}") set(WIBO_ENABLE_LTO OFF) endif() else() message(STATUS "Disabling LTO") set(WIBO_ENABLE_LTO OFF) endif() elseif (NOT (WIBO_ENABLE_LTO STREQUAL "ON" OR WIBO_ENABLE_LTO STREQUAL "OFF")) message(FATAL_ERROR "WIBO_ENABLE_LTO must be ON, OFF, or AUTO") endif() set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ${WIBO_ENABLE_LTO}) # Fetch and configure mimalloc set(MI_BUILD_SHARED OFF CACHE BOOL "Build shared library" FORCE) set(MI_BUILD_STATIC OFF CACHE BOOL "Build static library" FORCE) set(MI_BUILD_OBJECT ON CACHE BOOL "Build object library" FORCE) set(MI_BUILD_TESTS OFF CACHE BOOL "Build test executables" FORCE) include(FetchContent) FetchContent_Declare( mimalloc GIT_REPOSITORY https://github.com/encounter/mimalloc.git GIT_TAG 18d8537659d36ddeb320dfacc3b88fcd22a2607f # v3.1.5 + patch ) FetchContent_MakeAvailable(mimalloc) # Disable `note: the alignment of '_Atomic long long int' fields changed in GCC 11.1` target_compile_options(mimalloc-obj PRIVATE -Wno-psabi) if (CMAKE_SYSTEM_NAME STREQUAL "Linux") target_compile_definitions(mimalloc-obj PRIVATE MI_USE_BUILTIN_THREAD_POINTER=1) endif() if (WIBO_64) # Disable a noisy warning on startup target_compile_definitions(mimalloc-obj PRIVATE MI_DEBUG=0) endif() if (WIBO_ENABLE_LIBURING AND CMAKE_SYSTEM_NAME STREQUAL "Linux") FetchContent_Declare( liburing GIT_REPOSITORY https://github.com/axboe/liburing.git GIT_TAG e907d6a342e80b70874f93abd440b92b8a40b7bc # liburing-2.12 ) FetchContent_MakeAvailable(liburing) add_custom_command( OUTPUT ${liburing_SOURCE_DIR}/config-host.h ${liburing_SOURCE_DIR}/src/include/liburing/compat.h COMMAND ${CMAKE_COMMAND} -E env CC=${CMAKE_C_COMPILER} CXX=${CMAKE_CXX_COMPILER} ./configure --prefix=${liburing_BINARY_DIR} WORKING_DIRECTORY ${liburing_SOURCE_DIR} DEPENDS ${liburing_SOURCE_DIR}/configure COMMENT "Running liburing configure" VERBATIM ) add_library(liburing STATIC ${liburing_SOURCE_DIR}/config-host.h ${liburing_SOURCE_DIR}/src/nolibc.c ${liburing_SOURCE_DIR}/src/queue.c ${liburing_SOURCE_DIR}/src/register.c ${liburing_SOURCE_DIR}/src/setup.c ${liburing_SOURCE_DIR}/src/syscall.c ${liburing_SOURCE_DIR}/src/version.c ${liburing_SOURCE_DIR}/src/include/liburing/compat.h) target_compile_definitions(liburing PRIVATE _GNU_SOURCE _FILE_OFFSET_BITS=64 _TIME_BITS=64) target_compile_options(liburing PRIVATE -include config-host.h) target_include_directories(liburing PUBLIC ${liburing_SOURCE_DIR}/src/include) target_include_directories(liburing PRIVATE ${liburing_SOURCE_DIR}) endif() if (NOT DEFINED MSVCRT_DLL) set(MSVCRT_DLL ${CMAKE_CURRENT_BINARY_DIR}/vendor/msvcrt.dll) file(DOWNLOAD https://github.com/encounter/winedll/releases/download/2025-11-08/msvcrt.dll ${MSVCRT_DLL} EXPECTED_HASH SHA256=4a29890d7ee8722415b7b185321476db2cd20dbe964124189b85893f0d301e29 ) endif() function(embed_binary SRC FILE) set_source_files_properties(${SRC} PROPERTIES OBJECT_DEPENDS "${FILE}" COMPILE_DEFINITIONS "EMBED_PATH=\"${FILE}\"" ) endfunction() add_executable(wibo dll/advapi32.cpp dll/advapi32/md5.c dll/advapi32/processthreadsapi.cpp dll/advapi32/securitybaseapi.cpp dll/advapi32/winbase.cpp dll/advapi32/wincrypt.cpp dll/advapi32/winreg.cpp dll/bcrypt.cpp dll/kernel32.cpp dll/kernel32/debugapi.cpp dll/kernel32/errhandlingapi.cpp dll/kernel32/fibersapi.cpp dll/kernel32/fileapi.cpp dll/kernel32/handleapi.cpp dll/kernel32/heapapi.cpp dll/kernel32/interlockedapi.cpp dll/kernel32/ioapiset.cpp dll/kernel32/libloaderapi.cpp dll/kernel32/memoryapi.cpp dll/kernel32/namedpipeapi.cpp dll/kernel32/processenv.cpp dll/kernel32/processthreadsapi.cpp dll/kernel32/profileapi.cpp dll/kernel32/stringapiset.cpp dll/kernel32/synchapi.cpp dll/kernel32/sysinfoapi.cpp dll/kernel32/timezoneapi.cpp dll/kernel32/winbase.cpp dll/kernel32/wincon.cpp dll/kernel32/winnls.cpp dll/kernel32/winnt.cpp dll/kernel32/wow64apiset.cpp dll/lmgr.cpp dll/mscoree.cpp dll/ntdll.cpp dll/ole32.cpp dll/rpcrt4.cpp dll/user32.cpp dll/vcruntime.cpp dll/version.cpp src/access.cpp src/async_io.cpp src/errors.cpp src/files.cpp src/handles.cpp src/heap.cpp src/loader.cpp src/main.cpp src/modules.cpp src/processes_common.cpp src/resources.cpp src/setup.S src/strutil.cpp src/tls.cpp ) if ("${MSVCRT_DLL}" STREQUAL "") target_compile_definitions(wibo PRIVATE WIBO_HAS_MSVCRT=0) else() target_compile_definitions(wibo PRIVATE WIBO_HAS_MSVCRT=1) target_sources(wibo PRIVATE dll/msvcrt.cpp) embed_binary(dll/msvcrt.cpp "${MSVCRT_DLL}") endif() if (CMAKE_SYSTEM_NAME STREQUAL "Linux") target_sources(wibo PRIVATE src/async_io_epoll.cpp src/processes_linux.cpp src/setup_linux.cpp ) elseif (CMAKE_SYSTEM_NAME STREQUAL "Darwin") target_sources(wibo PRIVATE src/async_io_kqueue.cpp src/processes_darwin.cpp src/setup_darwin.cpp ) set_source_files_properties(src/setup_darwin.cpp PROPERTIES COMPILE_FLAGS "-fms-extensions") else() message(FATAL_ERROR "Unsupported platform for ProcessManager") endif() target_compile_definitions(wibo PRIVATE _GNU_SOURCE _FILE_OFFSET_BITS=64 _TIME_BITS=64) target_compile_features(wibo PRIVATE cxx_std_20) target_compile_options(wibo PRIVATE -Wall -Wextra) if (CMAKE_SYSTEM_NAME STREQUAL "Darwin") target_link_options(wibo PRIVATE -Wl,-no_pie,-no_fixup_chains -Wl,-segalign,0x1000 -Wl,-pagezero_size,0x1000 -Wl,-image_base,0x7E001000 -Wl,-segaddr,RESV32,0x1000 -Wl,-segprot,RESV32,-,- ) else() target_link_options(wibo PRIVATE -no-pie -Wl,--image-base=0x70000000) endif() if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") target_compile_options(wibo PRIVATE -maccumulate-outgoing-args) target_link_options(wibo PRIVATE -maccumulate-outgoing-args) endif() target_include_directories(wibo PRIVATE dll src ${WIBO_GENERATED_HEADER_DIR}) target_link_libraries(wibo PRIVATE mimalloc-obj) if (CMAKE_SYSTEM_NAME STREQUAL "Linux") target_link_libraries(wibo PRIVATE atomic) elseif (CMAKE_SYSTEM_NAME STREQUAL "Darwin") target_link_libraries(wibo PRIVATE c++ c++abi) endif() if (WIBO_ENABLE_LIBURING AND CMAKE_SYSTEM_NAME STREQUAL "Linux") target_compile_definitions(wibo PRIVATE WIBO_ENABLE_LIBURING=1) target_link_libraries(wibo PRIVATE liburing) target_sources(wibo PRIVATE src/async_io_uring.cpp) else() target_compile_definitions(wibo PRIVATE WIBO_ENABLE_LIBURING=0) endif() install(TARGETS wibo DESTINATION bin) find_package(Python3 COMPONENTS Interpreter REQUIRED) # Track down libclang for trampoline generation. # find_package(Clang) ends up requiring too many dependencies, # so a quick-and-dirty manual search is done here instead. if (CMAKE_SYSTEM_NAME STREQUAL "Linux") set(CLANG_ROOT "/usr" CACHE PATH "Path to Clang installation") elseif (CMAKE_SYSTEM_NAME STREQUAL "Darwin") set(CLANG_ROOT "/Library/Developer/CommandLineTools/usr/lib" CACHE PATH "Path to Clang installation") endif() set(_LLVM_MIN_VER 17) set(_LLVM_MAX_VER 25) set(_CLANG_LIB_SUFFIXES "") foreach(ver RANGE ${_LLVM_MAX_VER} ${_LLVM_MIN_VER} -1) list(APPEND _CLANG_LIB_SUFFIXES "llvm-${ver}/lib") endforeach() list(APPEND _CLANG_LIB_SUFFIXES llvm/lib lib lib64) find_library(LIBCLANG_LIBRARY NAMES libclang clang HINTS ${CLANG_ROOT} PATH_SUFFIXES ${_CLANG_LIB_SUFFIXES} ) if(NOT LIBCLANG_LIBRARY) message(FATAL_ERROR "libclang library not found!") endif() message(STATUS "Using libclang library: ${LIBCLANG_LIBRARY}") function(wibo_codegen_module) set(options) set(oneValueArgs NAME) set(multiValueArgs HEADERS) cmake_parse_arguments(PARSE_ARGV 0 module "${options}" "${oneValueArgs}" "${multiValueArgs}") set(out_asm ${WIBO_GENERATED_HEADER_DIR}/${module_NAME}_trampolines.S) set(out_hdr ${WIBO_GENERATED_HEADER_DIR}/${module_NAME}_trampolines.h) add_custom_command( OUTPUT ${out_asm} ${out_hdr} COMMAND ${CMAKE_COMMAND} -E env ${CMAKE_COMMAND} -E echo "Generating ${module_NAME} trampolines" COMMAND ${CMAKE_COMMAND} -E env LIBCLANG_PATH=${LIBCLANG_LIBRARY} ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/tools/gen_trampolines.py --dll ${module_NAME} --headers ${module_HEADERS} --namespace ${module_NAME} --arch $,x86_64,x86> --out-asm ${out_asm} --out-hdr ${out_hdr} -I ${CMAKE_CURRENT_SOURCE_DIR} -I ${CMAKE_CURRENT_SOURCE_DIR}/dll -I ${CMAKE_CURRENT_SOURCE_DIR}/src DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/tools/gen_trampolines.py ${module_HEADERS} ) target_sources(wibo PRIVATE ${out_asm}) endfunction() wibo_codegen_module(NAME advapi32 HEADERS dll/advapi32/processthreadsapi.h dll/advapi32/securitybaseapi.h dll/advapi32/winbase.h dll/advapi32/wincrypt.h dll/advapi32/winreg.h ) wibo_codegen_module(NAME bcrypt HEADERS dll/bcrypt.h) wibo_codegen_module(NAME entry HEADERS src/entry.h) wibo_codegen_module(NAME mscoree HEADERS dll/mscoree.h) wibo_codegen_module(NAME version HEADERS dll/version.h) wibo_codegen_module(NAME rpcrt4 HEADERS dll/rpcrt4.h) wibo_codegen_module(NAME vcruntime HEADERS dll/vcruntime.h) wibo_codegen_module(NAME lmgr HEADERS dll/lmgr.h) wibo_codegen_module(NAME ole32 HEADERS dll/ole32.h) wibo_codegen_module(NAME user32 HEADERS dll/user32.h) wibo_codegen_module(NAME ntdll HEADERS dll/ntdll.h) wibo_codegen_module(NAME kernel32 HEADERS dll/kernel32/debugapi.h dll/kernel32/errhandlingapi.h dll/kernel32/fibersapi.h dll/kernel32/fileapi.h dll/kernel32/handleapi.h dll/kernel32/heapapi.h dll/kernel32/interlockedapi.h dll/kernel32/ioapiset.h dll/kernel32/libloaderapi.h dll/kernel32/memoryapi.h dll/kernel32/namedpipeapi.h dll/kernel32/processenv.h dll/kernel32/processthreadsapi.h dll/kernel32/profileapi.h dll/kernel32/stringapiset.h dll/kernel32/synchapi.h dll/kernel32/sysinfoapi.h dll/kernel32/timezoneapi.h dll/kernel32/winbase.h dll/kernel32/wincon.h dll/kernel32/winnls.h dll/kernel32/winnt.h dll/kernel32/wow64apiset.h ) if (WIBO_ENABLE_FIXTURE_TESTS) include(CTest) find_program(WIBO_MINGW_CC i686-w64-mingw32-gcc) find_program(WIBO_MINGW_WINDRES i686-w64-mingw32-windres) set(WIBO_HAVE_MINGW_TOOLCHAIN FALSE) if(WIBO_MINGW_CC AND WIBO_MINGW_WINDRES) set(WIBO_HAVE_MINGW_TOOLCHAIN TRUE) endif() if(NOT WIBO_HAVE_MINGW_TOOLCHAIN) message(WARNING "MinGW toolchain not found; skipping fixture tests") else() set(WIBO_TEST_BIN_DIR ${CMAKE_CURRENT_BINARY_DIR}/test) file(MAKE_DIRECTORY ${WIBO_TEST_BIN_DIR}) function(wibo_add_fixture_dll) set(options) set(oneValueArgs NAME) set(multiValueArgs SOURCES COMPILE_OPTIONS DEPENDS) cmake_parse_arguments(PARSE_ARGV 0 fixture "${options}" "${oneValueArgs}" "${multiValueArgs}") set(fixture_OUTPUT ${WIBO_TEST_BIN_DIR}/${fixture_NAME}.dll) add_custom_command( OUTPUT ${fixture_OUTPUT} COMMAND ${WIBO_MINGW_CC} -Wall -Wextra -O2 -shared -I${CMAKE_CURRENT_SOURCE_DIR}/test -o ${fixture_OUTPUT} ${fixture_SOURCES} ${fixture_COMPILE_OPTIONS} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} DEPENDS ${fixture_SOURCES} ${fixture_DEPENDS}) list(APPEND WIBO_TEST_FIXTURES ${fixture_OUTPUT}) set(WIBO_TEST_FIXTURES ${WIBO_TEST_FIXTURES} PARENT_SCOPE) endfunction(wibo_add_fixture_dll) function(wibo_add_fixture_bin) set(options) set(oneValueArgs NAME) set(multiValueArgs SOURCES COMPILE_OPTIONS DEPENDS) cmake_parse_arguments(PARSE_ARGV 0 fixture "${options}" "${oneValueArgs}" "${multiValueArgs}") set(fixture_OUTPUT ${WIBO_TEST_BIN_DIR}/${fixture_NAME}.exe) add_custom_command( OUTPUT ${fixture_OUTPUT} COMMAND ${WIBO_MINGW_CC} -Wall -Wextra -O2 -I${CMAKE_CURRENT_SOURCE_DIR}/test -o ${fixture_OUTPUT} ${fixture_SOURCES} ${fixture_COMPILE_OPTIONS} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} DEPENDS ${fixture_SOURCES} ${fixture_DEPENDS}) list(APPEND WIBO_TEST_FIXTURES ${fixture_OUTPUT}) set(WIBO_TEST_FIXTURES ${WIBO_TEST_FIXTURES} PARENT_SCOPE) add_test(NAME wibo.${fixture_NAME} COMMAND $ ${fixture_OUTPUT}) set_tests_properties(wibo.${fixture_NAME} PROPERTIES WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/test DEPENDS wibo.build_fixtures) endfunction(wibo_add_fixture_bin) # Define fixture tests wibo_add_fixture_bin(NAME test_external_dll SOURCES test/test_external_dll.c) wibo_add_fixture_bin(NAME test_dll_attach_failure SOURCES test/test_dll_attach_failure.c) wibo_add_fixture_bin(NAME test_thread_notifications SOURCES test/test_thread_notifications.c) wibo_add_fixture_bin(NAME test_bcrypt SOURCES test/test_bcrypt.c COMPILE_OPTIONS -lbcrypt) wibo_add_fixture_bin(NAME test_resources SOURCES test/test_resources.c ${WIBO_TEST_BIN_DIR}/test_resources_res.o COMPILE_OPTIONS -lversion) wibo_add_fixture_bin(NAME test_threading SOURCES test/test_threading.c) wibo_add_fixture_bin(NAME test_tls SOURCES test/test_tls.c) wibo_add_fixture_bin(NAME test_tls_reloc SOURCES test/test_tls_reloc.c) wibo_add_fixture_bin(NAME test_handleapi SOURCES test/test_handleapi.c) wibo_add_fixture_bin(NAME test_findfile SOURCES test/test_findfile.c) wibo_add_fixture_bin(NAME test_locale SOURCES test/test_locale.c) wibo_add_fixture_bin(NAME test_critical_section SOURCES test/test_critical_section.c) wibo_add_fixture_bin(NAME test_synchapi SOURCES test/test_synchapi.c) wibo_add_fixture_bin(NAME test_processes SOURCES test/test_processes.c) wibo_add_fixture_bin(NAME test_heap SOURCES test/test_heap.c) wibo_add_fixture_bin(NAME test_actctx SOURCES test/test_actctx.c) wibo_add_fixture_bin(NAME test_overlapped_io SOURCES test/test_overlapped_io.c) wibo_add_fixture_bin(NAME test_time SOURCES test/test_time.c) wibo_add_fixture_bin(NAME test_ntdll_time SOURCES test/test_ntdll_time.c) wibo_add_fixture_bin(NAME test_virtualalloc SOURCES test/test_virtualalloc.c) wibo_add_fixture_bin(NAME test_virtualquery SOURCES test/test_virtualquery.c) wibo_add_fixture_bin(NAME test_clsids SOURCES test/test_clsids.c COMPILE_OPTIONS -lole32) wibo_add_fixture_bin(NAME test_rtl SOURCES test/test_rtl.c) wibo_add_fixture_bin(NAME test_rtl_bitmap SOURCES test/test_rtl_bitmap.c) wibo_add_fixture_bin(NAME test_ntquery SOURCES test/test_ntquery.c) wibo_add_fixture_bin(NAME test_ntqueryfile SOURCES test/test_ntqueryfile.c) wibo_add_fixture_bin(NAME test_ntreadfile SOURCES test/test_ntreadfile.c) wibo_add_fixture_bin(NAME test_ntwritefile SOURCES test/test_ntwritefile.c) wibo_add_fixture_bin(NAME test_pipe_io SOURCES test/test_pipe_io.c) wibo_add_fixture_bin(NAME test_namedpipe SOURCES test/test_namedpipe.c) wibo_add_fixture_bin(NAME test_sysdir SOURCES test/test_sysdir.c) wibo_add_fixture_bin(NAME test_srw_lock SOURCES test/test_srw_lock.c) wibo_add_fixture_bin(NAME test_init_once SOURCES test/test_init_once.c) wibo_add_fixture_bin(NAME test_wait_on_address SOURCES test/test_wait_on_address.c COMPILE_OPTIONS -lsynchronization) # DLLs for fixture tests wibo_add_fixture_dll(NAME external_exports SOURCES test/external_exports.c) wibo_add_fixture_dll(NAME dll_attach_failure SOURCES test/dll_attach_failure.c) wibo_add_fixture_dll(NAME thread_notifications SOURCES test/thread_notifications.c) wibo_add_fixture_dll(NAME tls_reloc SOURCES test/tls_reloc_dll.c) # Resources for test_resources add_custom_command( OUTPUT ${WIBO_TEST_BIN_DIR}/test_resources_res.o COMMAND ${WIBO_MINGW_WINDRES} ${CMAKE_CURRENT_SOURCE_DIR}/test/test_resources.rc -O coff -o test_resources_res.o WORKING_DIRECTORY ${WIBO_TEST_BIN_DIR} DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/test/test_resources.rc) add_custom_target(wibo_test_fixtures DEPENDS ${WIBO_TEST_FIXTURES}) if(CMAKE_CONFIGURATION_TYPES) set(_wibo_fixture_build_command ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --config $ --target wibo_test_fixtures) else() set(_wibo_fixture_build_command ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target wibo_test_fixtures) endif() add_test(NAME wibo.build_fixtures COMMAND ${_wibo_fixture_build_command}) endif() endif() if (NOT TARGET wibo_test_fixtures) add_custom_target(wibo_test_fixtures) endif()