diff --git a/.github/workflows/ci-cell.yml b/.github/workflows/ci-cell.yml index 7b4d3b19..57c0fb8c 100644 --- a/.github/workflows/ci-cell.yml +++ b/.github/workflows/ci-cell.yml @@ -23,6 +23,10 @@ on: required: false type: boolean default: false + with-mfc: + required: false + type: boolean + default: false jobs: build: @@ -145,9 +149,16 @@ jobs: else ACE_FLAG="-DNO_ACE=ON" fi + # Default MSVC cells suppress MFC discovery (keeps non-MFC cells lean). + # **with-mfc** cells leave FindMFC enabled so **MFCSTL** tests build. + if [ "${{ inputs.c-compiler }}" = "cl" ] && [ "${{ inputs.with-mfc }}" != "true" ]; then + MFC_FLAG="-DCMAKE_DISABLE_FIND_PACKAGE_MFC=TRUE" + else + MFC_FLAG="" + fi if [ "${{ inputs.c-compiler }}" = "cl" ]; then cmake -B build -S . \ - -DCMAKE_DISABLE_FIND_PACKAGE_MFC=TRUE \ + $MFC_FLAG \ -DCMAKE_PREFIX_PATH="${RUNNER_TEMP}/sis-deps" \ -DBUILD_EXAMPLES=ON \ -DBUILD_TESTING=ON \ @@ -196,6 +207,42 @@ jobs: echo "ACE_LIBRARY=${ACE_LIB}" echo "ACE_INCLUDE_DIR=${ACE_INC}" + - name: Verify MFC discovered + if: inputs.with-mfc + shell: bash + run: | + set -euo pipefail + MFC_HAVE="$(grep -E '^MFC_HAVE_MFC:INTERNAL=' build/CMakeCache.txt | head -1 | cut -d= -f2- || true)" + MFC_FOUND="$(grep -E '^MFC_FOUND:(BOOL|INTERNAL|STRING)=' build/CMakeCache.txt | head -1 | cut -d= -f2- || true)" + if [ "${MFC_HAVE}" != "1" ] && [ "${MFC_FOUND}" != "YES" ] && [ "${MFC_FOUND}" != "TRUE" ] && [ "${MFC_FOUND}" != "ON" ]; then + echo "with-mfc requested but MFC was not found:" >&2 + grep -i '^MFC_' build/CMakeCache.txt || true + exit 1 + fi + echo "MFC_HAVE_MFC=${MFC_HAVE}" + echo "MFC_FOUND=${MFC_FOUND}" + grep -E '^CMAKE_MFC_FLAG:' build/CMakeCache.txt || true + + - name: Verify ACE discovered + if: inputs.with-ace + shell: bash + run: | + set -euo pipefail + ACE_LIB="$(grep -E '^ACE_LIBRARY:(FILEPATH|PATH)=' build/CMakeCache.txt | head -1 | cut -d= -f2-)" + ACE_INC="$(grep -E '^ACE_INCLUDE_DIR:(FILEPATH|PATH)=' build/CMakeCache.txt | head -1 | cut -d= -f2-)" + if [ -z "${ACE_LIB}" ] || [[ "${ACE_LIB}" == *NOTFOUND* ]]; then + echo "with-ace requested but ACE_LIBRARY was not found:" >&2 + grep -i '^ACE_' build/CMakeCache.txt || true + exit 1 + fi + if [ -z "${ACE_INC}" ] || [[ "${ACE_INC}" == *NOTFOUND* ]]; then + echo "with-ace requested but ACE_INCLUDE_DIR was not found:" >&2 + grep -i '^ACE_' build/CMakeCache.txt || true + exit 1 + fi + echo "ACE_LIBRARY=${ACE_LIB}" + echo "ACE_INCLUDE_DIR=${ACE_INC}" + - name: Build shell: bash run: | diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 86b76b3f..75493e90 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -63,6 +63,7 @@ jobs: cpp-compiler: ${{ matrix.cpp_compiler }} build-type: Release with-ace: false + with-mfc: false secrets: inherit cell-ace: @@ -83,6 +84,28 @@ jobs: cpp-compiler: ${{ matrix.cpp_compiler }} build-type: Release with-ace: true + with-mfc: false + secrets: inherit + + cell-mfc: + name: CI MFC (${{ matrix.id }}) + strategy: + fail-fast: false + matrix: + include: + - id: windows-cl-mfc + os: windows-latest + c_compiler: cl + cpp_compiler: cl + uses: ./.github/workflows/ci-cell.yml + with: + cell-id: ${{ matrix.id }} + os: ${{ matrix.os }} + c-compiler: ${{ matrix.c_compiler }} + cpp-compiler: ${{ matrix.cpp_compiler }} + build-type: Release + with-ace: false + with-mfc: true secrets: inherit install-smoke: diff --git a/CMakeLists.txt b/CMakeLists.txt index 51ee7adf..a79785a4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -100,9 +100,30 @@ endif(WIN32) if(MFC_FOUND) - add_compile_definitions(STLSOFT_HAS_MFC) + # Shared MFC (/MD + _AFXDLL) unless the project is configured for the + # static CRT (**MSVC_USE_MT**), in which case static MFC is required. + # + # **CMAKE_MFC_FLAG** / **_AFXDLL** are applied only to MFC-consuming + # targets (see **test/unit/mfcstl** and **test.unit.versions**). Setting + # them project-wide would force every subsequent MSVC executable to link + # MFC. Without them on those targets, `find_package(MFC)` can succeed + # while mfcstl tests fail to link — the historical GHA failure mode. + + if(MSVC_USE_MT) + + set(STLSOFT_MFC_FLAG 1) + set(STLSOFT_MFC_SHARED OFF) + + message("-- using Microsoft Foundation Classes (static; MSVC_USE_MT)") + else(MSVC_USE_MT) - message("-- using Microsoft Foundation Classes") + set(STLSOFT_MFC_FLAG 2) + set(STLSOFT_MFC_SHARED ON) + + message("-- using Microsoft Foundation Classes (shared)") + endif(MSVC_USE_MT) + + add_compile_definitions(STLSOFT_HAS_MFC) endif(MFC_FOUND) diff --git a/TODO.md b/TODO.md index 6b929679..0d8affee 100644 --- a/TODO.md +++ b/TODO.md @@ -29,7 +29,7 @@ * [ ] Remove **WTL**; * [x] ~~~ACESTL: implement `invoke_ACE_OS_snprintf` in terms of `stlsoft_C_snprintf()`~~~ - ✅; -* [ ] CI: exercise **MFCSTL** on a Windows cell (drop / gate `CMAKE_DISABLE_FIND_PACKAGE_MFC`; confirm MSVC+MFC on the runner); +* [x] ~~~CI: exercise **MFCSTL** on a Windows cell (drop / gate `CMAKE_DISABLE_FIND_PACKAGE_MFC`; confirm MSVC+MFC on the runner)~~~ - ✅; * [ ] Resolve finally the structure and semantics of the top-level execution scripts (**run_all_unit_tests.sh**, **run_all_scratch_tests.sh**, **run_all_examples.sh**, **execute_performance_tests.sh**, and Windows `.cmd` counterparts) on all platforms — discovery globs, execute-bit / artifact restore, zero-match failure, verbosity, and CI wiring; diff --git a/cmake/TargetMacros.cmake b/cmake/TargetMacros.cmake index 5f2b320c..07dc987a 100644 --- a/cmake/TargetMacros.cmake +++ b/cmake/TargetMacros.cmake @@ -1,5 +1,28 @@ + +# Apply MFC link settings for targets created in the current directory +# scope. Call before define_automated_test_program / +# define_example_program when the target includes MFC headers. +# **CMAKE_MFC_FLAG** is read at add_executable() time (Visual Studio +# generators); **_AFXDLL** is required for shared MFC on Make/Ninja+cl +# as well. +macro(stlsoft_prepare_mfc_target) + + if(NOT MFC_FOUND) + + message(FATAL_ERROR "stlsoft_prepare_mfc_target() requires MFC_FOUND") + endif() + + set(CMAKE_MFC_FLAG ${STLSOFT_MFC_FLAG}) + + if(STLSOFT_MFC_SHARED) + + add_compile_definitions(_AFXDLL) + endif() +endmacro(stlsoft_prepare_mfc_target) + + function(define_automated_test_program program_name entry_point_source_name) add_executable(${program_name} diff --git a/test/unit/mfcstl/CMakeLists.txt b/test/unit/mfcstl/CMakeLists.txt index 99c39215..c0b88ac7 100644 --- a/test/unit/mfcstl/CMakeLists.txt +++ b/test/unit/mfcstl/CMakeLists.txt @@ -1,2 +1,12 @@ -# SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten +# MFC link settings must be in scope before leaf targets call +# define_automated_test_program() (see stlsoft_prepare_mfc_target). +# **CMAKE_MFC_FLAG** is a normal variable and would otherwise leak to later +# sibling directories after this **add_subdirectory** returns. +set(_stlsoft_cmake_mfc_flag_save "${CMAKE_MFC_FLAG}") + +stlsoft_prepare_mfc_target() + add_subdirectory(memory) + +set(CMAKE_MFC_FLAG "${_stlsoft_cmake_mfc_flag_save}") +unset(_stlsoft_cmake_mfc_flag_save) diff --git a/test/unit/mfcstl/memory/test.unit.mfcstl.memory.afx_allocator/CMakeLists.txt b/test/unit/mfcstl/memory/test.unit.mfcstl.memory.afx_allocator/CMakeLists.txt index 7e92a536..87050360 100644 --- a/test/unit/mfcstl/memory/test.unit.mfcstl.memory.afx_allocator/CMakeLists.txt +++ b/test/unit/mfcstl/memory/test.unit.mfcstl.memory.afx_allocator/CMakeLists.txt @@ -1,2 +1 @@ -# SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten define_automated_test_program(test.unit.mfcstl.memory.afx_allocator entry.cpp) diff --git a/test/unit/test.unit.versions/CMakeLists.txt b/test/unit/test.unit.versions/CMakeLists.txt index 9ee86a0b..b17a7d22 100644 --- a/test/unit/test.unit.versions/CMakeLists.txt +++ b/test/unit/test.unit.versions/CMakeLists.txt @@ -1,5 +1,21 @@ +if(MFC_FOUND) + + # **test.unit.versions** includes when + # **STLSOFT_HAS_MFC**. Save/restore **CMAKE_MFC_FLAG** so later + # directories are unaffected. + set(_stlsoft_cmake_mfc_flag_save "${CMAKE_MFC_FLAG}") + + stlsoft_prepare_mfc_target() +endif(MFC_FOUND) + define_automated_test_program(test.unit.versions entry.cpp) +if(MFC_FOUND) + + set(CMAKE_MFC_FLAG "${_stlsoft_cmake_mfc_flag_save}") + unset(_stlsoft_cmake_mfc_flag_save) +endif(MFC_FOUND) + if(HAS_ACE) target_link_libraries(test.unit.versions diff --git a/test/unit/test.unit.versions/entry.cpp b/test/unit/test.unit.versions/entry.cpp index fd615684..4b43cf80 100644 --- a/test/unit/test.unit.versions/entry.cpp +++ b/test/unit/test.unit.versions/entry.cpp @@ -161,7 +161,7 @@ static void TEST__WINSTL_VER() static void TEST__MFCSTL_VER() { - TEST_INT_EQ(_MFCSTL_VER_1_6_1, MFCSTL_VER); + TEST_INT_EQ(_MFCSTL_VER_1_6_1, _MFCSTL_VER); } #endif