#14584 Stop Windows build race copying Qt DLLs into the shared build root - #14585
Merged
magnesj merged 2 commits intoAug 22, 2026
Merged
Conversation
extract-projectfile-versions linked into the shared build root and copied Qt6Core.dll and Qt6Sql.dll next to its executable as a POST_BUILD step. The generated_classes.py edge runs ResInsight.exe out of that same directory with those DLLs loaded, and nothing orders the two edges relative to each other, so Ninja was free to schedule them concurrently. Overwriting a mapped DLL is a sharing violation on Windows, which failed the copy and took the build down. Give the target its own RUNTIME_OUTPUT_DIRECTORY. It is a standalone developer tool with no reason to share the application output directory, so moving it removes the conflict instead of narrowing it. Also switch the copy to copy_if_different, matching the equivalent step in the unit test target.
ResInsight-tests linked into the build root and copied its full runtime DLL set there as a POST_BUILD step, the same pattern that made extract-projectfile-versions race against the generated_classes.py edge. The set is a superset of the one that failed, covering Qt6Gui, Qt6Widgets and the rest, and nothing orders it against the code generation edge either. It fires rarely only because copy_if_different compares content, so windeployqt having already placed identical DLLs makes it a no-op. On a cold build where the copy wins that race it genuinely writes. Give the target its own RUNTIME_OUTPUT_DIRECTORY, and point the companion PreBuildFileCopyTest target at the same directory so the ODB, OpenVDS and HDF5 runtime files stay next to the executable. That target runs before the link creates the directory, so create it first. Update the three workflows that run the test executable by path.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #14584
Problem
extract-projectfile-versionslinks into the shared build root and copiesQt6Core.dllandQt6Sql.dllnext to its executable as aPOST_BUILDstep. Thegenerated_classes.pyedge runsResInsight.exeout of that same directory with those DLLs loaded, and nothing orders the two edges relative to each other, so Ninja is free to schedule them concurrently. Overwriting a mapped DLL is a sharing violation on Windows, which fails the copy and takes the build down.Fix
Give the target its own
RUNTIME_OUTPUT_DIRECTORY. It is a standalone developer tool with no reason to share the application output directory, so moving it removes the conflict rather than narrowing it. The copy also becomescopy_if_different, matching the equivalent step in the unit test target.Ordering the two edges with a dependency was considered. It closes this specific race, but it is one hand-written edge per racer, it puts an unrelated ThirdParty tool on the critical path to the Python bindings, and nothing stops the next target that lands in the build root from reintroducing the same bug.
ResInsight-tests
While fixing the reported target,
ResInsight-teststurned out to be in the same position:CMakeLists.txt:62pointsCMAKE_RUNTIME_OUTPUT_DIRECTORYat the build root for everything, and the test target copies its full runtime DLL set there with the same unorderedPOST_BUILDpattern. That set is a superset of the one that failed, covering Qt6Gui, Qt6Widgets, arrow, protobuf, abseil and the rest. It fires rarely only becausecopy_if_differentcompares content, so windeployqt having already placed identical DLLs makes it a no-op — on a cold build where the copy wins that race it genuinely writes.It is moved out of the build root the same way. The companion
PreBuildFileCopyTesttarget hardcoded${CMAKE_RUNTIME_OUTPUT_DIRECTORY}, so it is pointed at the new directory as well to keep the ODB, OpenVDS and HDF5 runtime files next to the executable; it runs before the link creates that directory, so the directory is created first.The three workflows that run the test executable by path are updated:
ResInsightWithCache.yml(three matrix entries),ResInsightMac.ymlandrhel8-unit-tests.yml.Dockerfile.rhel8and the rhel8 build step reference only the target name and are unaffected.Install packages are unchanged
Packaging on every platform goes through
cmake --build --target install. Diffing every generatedcmake_install.cmakein the tree from pristinedevto this branch, excluding FetchContent_deps/*-subbuildscaffolding, the entire diff is one line — the source path of the tool executable. TheDESTINATIONis identical, sinceinstall(TARGETS ...)tracksRUNTIME_OUTPUT_DIRECTORYautomatically.ResInsight-testshas no install rule and contributes nothing.Qt6Sql.dllwas checked specifically, since losing it from the package would have been a real regression:RiaProjectBackupTools.cppusesQSqlDatabaseand bothApplicationExeCodeandApplicationLibCodelinkQt6::Sql, so it is a genuineResInsight.exedependency deployed byqt_generate_deploy_app_script(TARGET ResInsight)and byRUNTIME_DEPENDENCIESon Windows — never by the copy being moved here.Verification
Verified locally on Windows (MSVC 19.51, Ninja, Qt 6.10.1):
extract-projectfile-versions.exestill runs from its new location.RiaConsoleApplicationderives fromQCoreApplication, so no GUI plugin is required — and test data resolves through the absolute paths baked intoRiaTestDataDirectory.h.cmake.cmake-formatrun withcmake/cmake-format.pyon theApplicationLibCodefile covered by the format workflow.Not verified locally: the Linux and macOS legs. They move the test binary too, and while
RI_FILENAMESthere is the OpenVDS shared libraries plusSEGYImport, all copied along to the new directory, shared-library resolution on those platforms goes through RPATH rather than the executable directory. CI is the real confirmation.One cosmetic note for existing build trees: a stale
extract-projectfile-versions.exeandResInsight-testsremain in the build root, since Ninja does not remove outputs of retired edges. Harmless, cleared byninja -t cleandeador a fresh configure directory.