diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index 112637b9f1..67fc843a33 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -145,6 +145,17 @@ jobs: BUILD_TYPE: ${{ matrix.RELEASE && 'RelWithDebInfo' || 'Debug' }} TEST_USE_ROCKSDB: ${{ matrix.BACKEND == 'rocksdb' && '1' || '0' }} DEADLINE_SCALE_FACTOR: ${{ matrix.BACKEND == 'rocksdb' && '2' || '1' }} + # The Visual Studio generator ignores compiler launchers, so caching requires Ninja. + CMAKE_GENERATOR: Ninja + CMAKE_C_COMPILER_LAUNCHER: sccache + CMAKE_CXX_COMPILER_LAUNCHER: sccache + SCCACHE_GHA_ENABLED: "true" + # Both backends compile identically, so they share a namespace. + SCCACHE_GHA_VERSION: windows-${{ matrix.RELEASE }} + # TEMPORARY - REVERT BEFORE MERGING TO DEVELOP. + # Forced READ_WRITE so this branch can populate and exercise the cache. + # Restore: ${{ github.ref == 'refs/heads/develop' && 'READ_WRITE' || 'READ_ONLY' }} + SCCACHE_GHA_RW_MODE: READ_WRITE if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository steps: - name: Checkout @@ -155,11 +166,22 @@ jobs: - name: Prepare run: ci/prepare/windows/prepare.ps1 + # Ninja invokes the compiler directly, so cl.exe must be on PATH. + - name: Setup MSVC + uses: ilammy/msvc-dev-cmd@v1 + + - name: Setup sccache + uses: mozilla-actions/sccache-action@v0.0.9 + - name: Build Tests id: build run: ci/build-tests.sh shell: bash + - name: Build cache stats + if: always() + run: sccache --show-stats + - name: Setup Core Dumps if: steps.build.outcome == 'success' && (success() || failure()) run: ci/tests/setup-core-dumps.sh diff --git a/CMakeLists.txt b/CMakeLists.txt index c3ff042201..2bed52c045 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,6 +41,26 @@ if(MSVC) add_definitions(/MP) add_definitions( -D_SILENCE_STDEXT_ARR_ITERS_DEPRECATION_WARNING) # Suppress iterator warning + + # RocksDB enables the generic ASM language, which otherwise claims the .asm + # extension ahead of MASM and leaves Boost.Context's sources without a compile + # rule, as CMake has no generic ASM support for MSVC. The Visual Studio + # generator hides this; Ninja fails to generate. Nothing here assembles .s + # files on Windows, so narrow the generic language to leave .asm to MASM. + set(CMAKE_ASM_SOURCE_FILE_EXTENSIONS s;S) + + # Compiler caches cannot handle /Zi, which writes debug info to a PDB shared + # by every translation unit of a target. Embed the debug info in the object + # files instead so each compilation is a self-contained, cacheable artifact. + if(CMAKE_C_COMPILER_LAUNCHER OR CMAKE_CXX_COMPILER_LAUNCHER) + message(STATUS "Compiler launcher in use - embedding MSVC debug info (/Z7)") + foreach(flags_var + CMAKE_C_FLAGS_DEBUG CMAKE_CXX_FLAGS_DEBUG + CMAKE_C_FLAGS_RELWITHDEBINFO CMAKE_CXX_FLAGS_RELWITHDEBINFO) + string(REPLACE "/ZI" "/Z7" ${flags_var} "${${flags_var}}") + string(REPLACE "/Zi" "/Z7" ${flags_var} "${${flags_var}}") + endforeach() + endif() endif() set(CPACK_PACKAGE_VENDOR "Nano Currency") @@ -519,6 +539,9 @@ else() 1 CACHE BOOL "" FORCE) endif() +# Runs inside RocksDB's scope to drop MSVC flags that defeat compiler caching +set(CMAKE_PROJECT_rocksdb_INCLUDE + ${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules/RocksDBMsvcCache.cmake) add_subdirectory(submodules/rocksdb EXCLUDE_FROM_ALL) # cpptoml diff --git a/ci/build.sh b/ci/build.sh index c913344136..507f530036 100755 --- a/ci/build.sh +++ b/ci/build.sh @@ -77,17 +77,8 @@ number_of_processors() { esac } -parallel_build_flag() { - case "$(uname -s)" in - CYGWIN*|MINGW32*|MSYS*|MINGW*) - echo "-- -m" - ;; - *) - echo "--parallel $(number_of_processors)" - ;; - esac -} - -cmake --build ${PWD} ${BUILD_TARGET} $(parallel_build_flag) +# Works for every generator: CMake forwards it as /m:N to MSBuild and -j N to +# Ninja and Make. +cmake --build ${PWD} ${BUILD_TARGET} --parallel $(number_of_processors) popd diff --git a/ci/prepare/windows/prepare.ps1 b/ci/prepare/windows/prepare.ps1 index 7ed8f7e7ab..cbd20d66d9 100644 --- a/ci/prepare/windows/prepare.ps1 +++ b/ci/prepare/windows/prepare.ps1 @@ -1,4 +1,5 @@ $ErrorActionPreference = "Stop" & "$PSScriptRoot\disable-defender.ps1" -& "$PSScriptRoot\install-qt.ps1" \ No newline at end of file +& "$PSScriptRoot\install-qt.ps1" +& "$PSScriptRoot\remove-git-link.ps1" \ No newline at end of file diff --git a/ci/prepare/windows/remove-git-link.ps1 b/ci/prepare/windows/remove-git-link.ps1 new file mode 100644 index 0000000000..2a40c8cc60 --- /dev/null +++ b/ci/prepare/windows/remove-git-link.ps1 @@ -0,0 +1,9 @@ +$ErrorActionPreference = "Continue" + +# Git ships a coreutils `link.exe` that shadows the MSVC linker of the same name +# whenever CMake configures from a bash shell, because bash puts its own /usr/bin +# ahead of the MSVC toolchain on PATH. +$gitLink = "C:\Program Files\Git\usr\bin\link.exe" +if (Test-Path $gitLink) { + Remove-Item $gitLink -Force +} diff --git a/cmake/Modules/RocksDBMsvcCache.cmake b/cmake/Modules/RocksDBMsvcCache.cmake new file mode 100644 index 0000000000..9eb16d41f5 --- /dev/null +++ b/cmake/Modules/RocksDBMsvcCache.cmake @@ -0,0 +1,39 @@ +# Included via CMAKE_PROJECT_rocksdb_INCLUDE, so this runs inside RocksDB's own +# directory scope right after its project() call. +# +# RocksDB unconditionally appends /Zi and /d2Zi+ to CMAKE_CXX_FLAGS for MSVC, and +# both defeat compiler caching. /Zi points every translation unit at a single +# PDB shared by the whole target, which cannot be cached per compilation, and the +# undocumented /d2Zi+ is misparsed by sccache badly enough that it rejects the +# command line as having "multiple input files". Together they account for a +# third of the object files in a Windows build. +# +# The flags are added well after this file is included, so schedule the cleanup +# to run once RocksDB has finished with its directory. Debug info is not lost: +# /Z7 embeds it in the object files instead. + +if(NOT MSVC) + return() +endif() + +if(NOT CMAKE_C_COMPILER_LAUNCHER AND NOT CMAKE_CXX_COMPILER_LAUNCHER) + return() +endif() + +# cmake_language(DEFER) requires CMake 3.19; without it RocksDB simply stays +# uncached rather than failing to build. +if(CMAKE_VERSION VERSION_LESS 3.19) + message( + STATUS "RocksDB: CMake < 3.19, leaving MSVC debug flags uncacheable") + return() +endif() + +function(nano_strip_rocksdb_uncacheable_flags) + foreach(flags_var CMAKE_C_FLAGS CMAKE_CXX_FLAGS) + string(REPLACE "/d2Zi+" "" ${flags_var} "${${flags_var}}") + string(REPLACE "/Zi" "" ${flags_var} "${${flags_var}}") + set(${flags_var} "${${flags_var}}" PARENT_SCOPE) + endforeach() +endfunction() + +cmake_language(DEFER CALL nano_strip_rocksdb_uncacheable_flags)