From 9ab270f47d68198dee29780a346294a728087105 Mon Sep 17 00:00:00 2001 From: Alexey Edelev Date: Mon, 19 Aug 2024 21:10:18 +0200 Subject: cmSystemTools: Add GetComspec method to get cmd on Windows The function attempts to read the path to cmd executable from the COMSPEC environment variable. Falls back to cmd.exe if the respective environment variable is not set or path doesn't exist. --- Source/cmGlobalNinjaGenerator.cxx | 8 +------- Source/cmSystemTools.cxx | 11 +++++++++++ Source/cmSystemTools.h | 3 +++ 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx index 7d62fa8..b051d67 100644 --- a/Source/cmGlobalNinjaGenerator.cxx +++ b/Source/cmGlobalNinjaGenerator.cxx @@ -573,13 +573,7 @@ cmGlobalNinjaGenerator::cmGlobalNinjaGenerator(cmake* cm) cm->GetState()->SetWindowsShell(true); // Attempt to use full path to COMSPEC, default "cmd.exe" - std::string comspec; - if (cmSystemTools::GetEnv("COMSPEC", comspec) && - cmSystemTools::FileIsFullPath(comspec)) { - this->Comspec = comspec; - } else { - this->Comspec = "cmd.exe"; - } + this->Comspec = cmSystemTools::GetComspec(); #endif cm->GetState()->SetNinja(true); this->FindMakeProgramFile = "CMakeNinjaFindMake.cmake"; diff --git a/Source/cmSystemTools.cxx b/Source/cmSystemTools.cxx index 8e59a35..05cc85a 100644 --- a/Source/cmSystemTools.cxx +++ b/Source/cmSystemTools.cxx @@ -970,6 +970,17 @@ cmSystemTools::WindowsVersion cmSystemTools::GetWindowsVersion() result.dwBuildNumber = osviex.dwBuildNumber; return result; } + +std::string cmSystemTools::GetComspec() +{ + std::string comspec; + if (!cmSystemTools::GetEnv("COMSPEC", comspec) || + !cmSystemTools::FileIsFullPath(comspec)) { + comspec = "cmd.exe"; + } + return comspec; +} + #endif std::string cmSystemTools::GetRealPathResolvingWindowsSubst( diff --git a/Source/cmSystemTools.h b/Source/cmSystemTools.h index a07c749..d0b6046 100644 --- a/Source/cmSystemTools.h +++ b/Source/cmSystemTools.h @@ -585,6 +585,9 @@ public: unsigned int dwBuildNumber; }; static WindowsVersion GetWindowsVersion(); + + /** Attempt to get full path to COMSPEC, default "cmd.exe" */ + static std::string GetComspec(); #endif /** Get the real path for a given path, removing all symlinks. -- cgit v0.12 From 8d1803d463adac8ebc63bb0d1a9ef55b6c56472a Mon Sep 17 00:00:00 2001 From: Alexey Edelev Date: Mon, 19 Aug 2024 21:12:03 +0200 Subject: AutoGen: Run batch scripts using cmd.exe on windows platforms explicitly The CreateProcessW function requires to use the cmd.exe when attempting to execute batch scripts. AutoMoc RunProcess relies on CreateProcessW in it's internals. Currently the cmd.exe run happens implicitly for batch files(perhaps by luck), but this is not documented anywhere. This replaces the .bat files in the AutoGen related command lines with explicit 'cmd.exe /c' call. Also 'cmd.exe /c' has own limitation related to the arguments. The very first argument shouldn't be quoted otherwise this lead to the parsing issues. So for the .bat files that contain spaces in their paths use short name conversion. Fixes #26208 --- Source/cmQtAutoMocUic.cxx | 56 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/Source/cmQtAutoMocUic.cxx b/Source/cmQtAutoMocUic.cxx index 408a22c..c07f361 100644 --- a/Source/cmQtAutoMocUic.cxx +++ b/Source/cmQtAutoMocUic.cxx @@ -345,6 +345,8 @@ public: void MaybeWriteResponseFile(std::string const& outputFile, std::vector& cmd) const; + static void MaybePrependCmdExe(std::vector& cmd); + /** @brief Run an external process. Use only during Process() call! */ bool RunProcess(GenT genType, cmWorkerPool::ProcessResultT& result, std::vector const& command, @@ -848,6 +850,54 @@ void cmQtAutoMocUicT::JobT::MaybeWriteResponseFile( #endif } +/* + * According to the CreateProcessW documentation which is the underlying + * function for all RunProcess calls: + * + * "To run a batch file, you must start the command interpreter; set" + * "lpApplicationName to cmd.exe and set lpCommandLine to the following" + * "arguments: /c plus the name of the batch file." + * + * we should to take care of the correctness of the command line when + * attempting to execute the batch files. + * + * Also cmd.exe is unable to parse batch file names correctly if they + * contain spaces. This function uses cmSystemTools::GetShortPath conversion + * to suppress this behavior. + * + * The function is noop on platforms different from the pure WIN32 one. + */ +void cmQtAutoMocUicT::JobT::MaybePrependCmdExe( + std::vector& cmdLine) +{ +#if defined(_WIN32) && !defined(__CYGWIN__) + if (!cmdLine.empty()) { + const auto& applicationName = cmdLine.at(0); + if (cmSystemTools::StringEndsWith(applicationName, ".bat") || + cmSystemTools::StringEndsWith(applicationName, ".cmd")) { + std::vector output; + output.reserve(cmdLine.size() + 2); + output.emplace_back(cmSystemTools::GetComspec()); + output.emplace_back("/c"); + std::string tmpShortPath; + if (applicationName.find(' ') != std::string::npos && + cmSystemTools::GetShortPath(applicationName, tmpShortPath)) { + // If the batch file name contains spaces convert it to the windows + // short path. Otherwise it might cause issue when running cmd.exe. + output.emplace_back(tmpShortPath); + } else { + output.push_back(applicationName); + } + std::move(cmdLine.begin() + 1, cmdLine.end(), + std::back_inserter(output)); + cmdLine = std::move(output); + } + } +#else + static_cast(cmdLine); +#endif +} + bool cmQtAutoMocUicT::JobT::RunProcess(GenT genType, cmWorkerPool::ProcessResultT& result, std::vector const& command, @@ -891,6 +941,9 @@ void cmQtAutoMocUicT::JobMocPredefsT::Process() cm::append(cmd, this->MocConst().OptionsIncludes); // Check if response file is necessary MaybeWriteResponseFile(this->MocConst().PredefsFileAbs, cmd); + + MaybePrependCmdExe(cmd); + // Execute command if (!this->RunProcess(GenT::MOC, result, cmd, reason.get())) { this->LogCommandError(GenT::MOC, @@ -2090,6 +2143,7 @@ void cmQtAutoMocUicT::JobCompileMocT::Process() cmd.push_back(sourceFile); MaybeWriteResponseFile(outputFile, cmd); + MaybePrependCmdExe(cmd); } // Execute moc command @@ -2156,6 +2210,8 @@ void cmQtAutoMocUicT::JobCompileUicT::Process() cmd.emplace_back(outputFile); cmd.emplace_back(sourceFile); + MaybePrependCmdExe(cmd); + cmWorkerPool::ProcessResultT result; if (this->RunProcess(GenT::UIC, result, cmd, this->Reason.get())) { // Uic command success -- cgit v0.12