Ninja: broken build statements on empty custom commands
I have a problem running ninja
on the target all_qmllint
generated for a Qt 6.5.0 project. The build aborts with error CreateProcess failed: The system cannot find the file specified
. I could patch ninja to show the failing command, and it printed:
CreateProcess failed: The system cannot find the file 'cd /D C:\Src\MyQtGUIComponents\MyQtModels' specified.
In turn I could trace this in build.ninja
to a block that reads:
# Custom command for MyQtModels\CMakeFiles\MyQtModelsForQml_qmllint
build MyQtModels\CMakeFiles\MyQtModelsForQml_qmllint | ${cmake_ninja_workdir}MyQtModels\CMakeFiles\MyQtModelsForQml_qmllint: CUSTOM_COMMAND C$:\data\usr-dst-C10Skc1935322161c1507\Debug\bin\qmllint.exe || MyQtModels\MyQtModelsForQml.dll MyQtModels\MyQtModelsForQml_autogen MyQtModels\MyQtModelsForQml_autogen_timestamp_deps MyQtModels\MyQtModelsForQml_qmltyperegistration MyQtModels\all_qmltyperegistrations MyQtQuick\MyQtQuick_autogen_timestamp_deps MyQtQuick\MyQtQuick_qmltyperegistration
COMMAND = cd /D C:\Src\MyQtGUIComponents\MyQtModels
I am under the impression that cmake has a bug there, because it adds a cd
command, but nothing else. Looking at cmake sources in https://gitlab.kitware.com/cmake/cmake/-/blob/v3.26.3/Source/cmLocalNinjaGenerator.cxx#L553, the logic seems slightly flawed: If commands exist, but all of them are empty, then no commands are emitted, except the cd
-command, which would be useless, and seems to break ninja.
I have changed the logic to:
diff --git a/Source/cmLocalNinjaGenerator.cxx b/Source/cmLocalNinjaGenerator.cxx
index 3443cd31b3..49287ae544 100644
--- a/Source/cmLocalNinjaGenerator.cxx
+++ b/Source/cmLocalNinjaGenerator.cxx
@@ -550,7 +550,16 @@ void cmLocalNinjaGenerator::AppendCustomCommandLines(
{
auto* gg = this->GetGlobalNinjaGenerator();
- if (ccg.GetNumberOfCommands() > 0) {
+ int numNonEmptyCommands = 0;
+ for (unsigned i = 0; i != ccg.GetNumberOfCommands(); ++i) {
+ if (!ccg.GetCommand(i).empty()) {
+ ++numNonEmptyCommands;
+ }
+ }
+
+ if (numNonEmptyCommands == 0) {
+ return;
+ } else {
std::string wd = ccg.GetWorkingDirectory();
if (wd.empty()) {
wd = this->GetCurrentBinaryDirectory();
Then the build works. The new generated block in build.ninja
becomes:
# Phony custom command for MyQtModels\CMakeFiles\MyQtModelsForQml_qmllint
build MyQtModels\CMakeFiles\MyQtModelsForQml_qmllint | ${cmake_ninja_workdir}MyQtModels\CMakeFiles\MyQtModelsForQml_qmllint: phony C$:\data\usr-dst-C10Skc1935322161c1507\Debug\bin\qmllint.exe || MyQtModels\MyQtModelsForQml.dll MyQtModels\MyQtModelsForQml_autogen MyQtModels\MyQtModelsForQml_autogen_timestamp_deps MyQtModels\MyQtModelsForQml_qmltyperegistration MyQtModels\all_qmltyperegistrations MyQtQuick\MyQtQuick_autogen_timestamp_deps MyQtQuick\MyQtQuick_qmltyperegistration
I am not providing this as a PR because probably the same problem exists in other generators too?! Possibly this would be better fixed directly in GetNumberOfCommands()
, but that may have wide implications, so I better not touch that myself.