From 0b3015df3a5c45aa2feb735bc6679783db461aae Mon Sep 17 00:00:00 2001 From: SchwartzKamel Date: Tue, 2 Jun 2026 19:44:00 +0000 Subject: [PATCH 1/2] Include on Windows for struct _stat / _stat yep_set_fs_times() uses `struct _stat` and `_stat()` on the _WIN32 branch, but was only included in the non-Windows branch. With MinGW 15.x, `_stat` is a macro expanding to `_stat64i32`, whose declaration lives in . Without that include the Windows build fails: error: implicit declaration of function '_stat64i32' Add to the _WIN32 include block so the packer builds under MinGW. --- src/yepfs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/yepfs.c b/src/yepfs.c index deb38c7..219ddb2 100644 --- a/src/yepfs.c +++ b/src/yepfs.c @@ -19,6 +19,7 @@ #ifdef _WIN32 #include + #include #include #include #else From be2ed7d66c8af45e02251b1452f56b0f5f4907fd Mon Sep 17 00:00:00 2001 From: SchwartzKamel Date: Tue, 2 Jun 2026 19:55:25 +0000 Subject: [PATCH 2/2] Build a host-runnable packer when cross-compiling pack_resources() runs the `yep` packer at build time to produce .yep archives. When the consuming project cross-compiles (e.g. Linux host -> Windows target), the in-tree `yep` target is built for the target platform, so the resource-packing step tried to execute a target binary (e.g. a Windows PE) on the build host and failed with "Exec format error". Add a host/target split: - yep's CMakeLists.txt now publishes, via global properties, which packer pack_resources() should invoke: * native build -> the in-tree `yep` target (unchanged behavior) * cross build -> a host-runnable packer Global properties are used so the choice is visible from the consumer scope (FetchContent adds yep in a child scope) and is recomputed on every configure (so it stays correct across reconfigures). - When cross-compiling and no packer is supplied, build one for the host via ExternalProject_Add. The nested CMake invocation does not inherit the cross toolchain, so it configures for the host; being non-cross, it does not recurse. The host binary is emitted to a deterministic path (robust for multi-config generators). - Consumers may instead set YEP_HOST_EXECUTABLE to a prebuilt host packer (validated to exist), and YEP_HOST_C_COMPILER / YEP_HOST_CXX_COMPILER to override host compiler detection. Verified end-to-end with a mingw-w64 cross build: the host ELF packer is built and run, producing resources.yep, while the in-tree target remains a Windows PE. Native (non-cross) builds are unchanged. --- CMakeLists.txt | 76 +++++++++++++++++++++++++++++++++++++++++++++++++ cmake/yep.cmake | 16 +++++++++-- 2 files changed, 90 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 65704a4..2ce8120 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,6 +12,11 @@ include(FetchContent) option(YEP_BUILD_BIN "Build the yep binary" ON) +# Optional: a prebuilt, host-runnable yep packer to use during cross builds. +# Leave empty to have this build produce one automatically (see below). +set(YEP_HOST_EXECUTABLE "" CACHE FILEPATH + "Prebuilt host-runnable yep packer to use when cross-compiling (optional)") + # libyep add_library(libyep STATIC) target_sources(libyep PRIVATE src/yepfs.c src/libyep.c) @@ -25,6 +30,77 @@ if(YEP_BUILD_BIN) # we need libyep if we are building the binary set(YEP_BUILD_LIBYEP ON CACHE BOOL "Build the libyep library" FORCE) + + # Decide which packer pack_resources() should run, and publish the choice via + # global properties. These are visible from the consumer scope (FetchContent + # adds yep in a child scope, so a plain variable would not propagate up) and, + # unlike a cache entry, they are recomputed every configure so reconfigures + # stay consistent. + if(NOT CMAKE_CROSSCOMPILING) + # Host == target: just run the in-tree binary. + set_property(GLOBAL PROPERTY YEP_PACKER_COMMAND "$") + set_property(GLOBAL PROPERTY YEP_PACKER_DEPEND yep) + elseif(YEP_HOST_EXECUTABLE) + # Consumer supplied a prebuilt host packer. + if(NOT EXISTS "${YEP_HOST_EXECUTABLE}") + message(FATAL_ERROR + "YEP_HOST_EXECUTABLE does not exist: ${YEP_HOST_EXECUTABLE}") + endif() + set_property(GLOBAL PROPERTY YEP_PACKER_COMMAND "${YEP_HOST_EXECUTABLE}") + set_property(GLOBAL PROPERTY YEP_PACKER_DEPEND "") + else() + # Cross-compiling: the `yep` target above is built for the *target* and + # cannot run on the build host. pack_resources() runs the packer at build + # time, so build a host-runnable copy via a nested host build. + # + # ExternalProject_Add launches a fresh CMake that does NOT inherit our + # cross toolchain, so it configures for the host; because that nested + # build is not cross-compiling, it does not recurse here. If the host + # compiler cannot be autodetected (e.g. CC/CXX point at the cross + # compiler), set YEP_HOST_C_COMPILER / YEP_HOST_CXX_COMPILER. + include(ExternalProject) + + if(CMAKE_HOST_WIN32) + set(_yep_host_exe_suffix ".exe") + else() + set(_yep_host_exe_suffix "") + endif() + + # Explicit, generator-independent output location so the host binary + # lands at a known path (multi-config generators otherwise nest it under + # a per-config subdirectory). + set(_yep_host_dir "${CMAKE_CURRENT_BINARY_DIR}/yep_host_tool") + set(_yep_host_exe "${_yep_host_dir}/bin/yep${_yep_host_exe_suffix}") + + set(_yep_host_compiler_args "") + if(YEP_HOST_C_COMPILER) + list(APPEND _yep_host_compiler_args "-DCMAKE_C_COMPILER=${YEP_HOST_C_COMPILER}") + endif() + if(YEP_HOST_CXX_COMPILER) + list(APPEND _yep_host_compiler_args "-DCMAKE_CXX_COMPILER=${YEP_HOST_CXX_COMPILER}") + endif() + + ExternalProject_Add(yep_host_tool + SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}" + BINARY_DIR "${_yep_host_dir}/build" + CMAKE_ARGS + "-DCMAKE_TOOLCHAIN_FILE:FILEPATH=" # drop any inherited/leaked cross toolchain + "-DCMAKE_BUILD_TYPE=Release" + "-DYEP_BUILD_BIN=ON" + "-DCMAKE_RUNTIME_OUTPUT_DIRECTORY=${_yep_host_dir}/bin" + "-DCMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG=${_yep_host_dir}/bin" + "-DCMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE=${_yep_host_dir}/bin" + "-DCMAKE_RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO=${_yep_host_dir}/bin" + "-DCMAKE_RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL=${_yep_host_dir}/bin" + ${_yep_host_compiler_args} + BUILD_ALWAYS ON + INSTALL_COMMAND "" + BUILD_BYPRODUCTS "${_yep_host_exe}" + ) + + set_property(GLOBAL PROPERTY YEP_PACKER_COMMAND "${_yep_host_exe}") + set_property(GLOBAL PROPERTY YEP_PACKER_DEPEND yep_host_tool) + endif() endif() ############### diff --git a/cmake/yep.cmake b/cmake/yep.cmake index 3fdb356..8b5b40e 100644 --- a/cmake/yep.cmake +++ b/cmake/yep.cmake @@ -10,10 +10,22 @@ function(pack_resources INPUT_DIR OUTPUT_FILE TARGET_NAME) "${INPUT_DIR}/*" ) + # The packer to run is published by yep's CMakeLists.txt via global + # properties: the in-tree `yep` target for native builds, or a host-runnable + # copy when cross-compiling (the in-tree binary would be a target-platform + # executable that cannot run on the build host). + get_property(_yep_packer GLOBAL PROPERTY YEP_PACKER_COMMAND) + get_property(_yep_packer_dep GLOBAL PROPERTY YEP_PACKER_DEPEND) + if(NOT _yep_packer) + # Fallback (e.g. yep.cmake included without yep's CMakeLists setup). + set(_yep_packer "$") + set(_yep_packer_dep yep) + endif() + add_custom_command( OUTPUT "${OUTPUT_FILE}" - COMMAND $ "${INPUT_DIR}" "${OUTPUT_FILE}" - DEPENDS yep ${RESOURCE_INPUT_FILES} + COMMAND "${_yep_packer}" "${INPUT_DIR}" "${OUTPUT_FILE}" + DEPENDS ${_yep_packer_dep} ${RESOURCE_INPUT_FILES} COMMENT "Packing resources from ${INPUT_DIR} to ${OUTPUT_FILE}" VERBATIM )