CMakePM: Do not add sources to globbed targets

If a class is being added to a target that has a variable resulted from
a `file(GLOB|_RECURSE)` call, we skip adding the files to the target.

Do not add the files, just run CMake which would gather the new sources
into the project.

Adding sources to a globbed target will no longer open the
CMakeLists.txt file, since nothing has changed.

CMake will be run after adding, renaming and removing of sources files.

Fixes: QTCREATORBUG-30445
Change-Id: I82e126737789f215a6400f5a43f303dcc57de005
Reviewed-by: Alessandro Portale <alessandro.portale@qt.io>
This commit is contained in:
Cristian Adam
2024-03-01 14:21:10 +01:00
parent 313ed327be
commit 030e3c2cd5
3 changed files with 95 additions and 60 deletions

View File

@@ -642,6 +642,25 @@ bool CMakeBuildSystem::addTsFiles(Node *context, const FilePaths &filePaths, Fil
return false; return false;
} }
static bool isGlobbingFunction(const cmListFile &cmakeListFile, const cmListFileFunction &func)
{
// Check if the filename is part of globbing variable result
const auto globFunctions = std::get<0>(
Utils::partition(cmakeListFile.Functions, [](const auto &f) {
return f.LowerCaseName() == "file" && f.Arguments().size() > 2
&& (f.Arguments().front().Value == "GLOB"
|| f.Arguments().front().Value == "GLOB_RECURSE");
}));
const auto globVariables = Utils::transform<QSet>(globFunctions, [](const auto &func) {
return std::string("${") + func.Arguments()[1].Value + "}";
});
return Utils::anyOf(func.Arguments(), [globVariables](const auto &arg) {
return globVariables.contains(arg.Value);
});
}
bool CMakeBuildSystem::addSrcFiles(Node *context, const FilePaths &filePaths, FilePaths *notAdded) bool CMakeBuildSystem::addSrcFiles(Node *context, const FilePaths &filePaths, FilePaths *notAdded)
{ {
if (notAdded) if (notAdded)
@@ -670,26 +689,32 @@ bool CMakeBuildSystem::addSrcFiles(Node *context, const FilePaths &filePaths, Fi
return false; return false;
} }
const std::string target_name = function->Arguments().front().Value; const bool haveGlobbing = isGlobbingFunction(cmakeListFile.value(), function.value());
auto qtAddModule = [target_name](const auto &func) { n->setVisibleAfterAddFileAction(!haveGlobbing);
return (func.LowerCaseName() == "qt_add_qml_module" if (haveGlobbing && settings(project()).autorunCMake()) {
|| func.LowerCaseName() == "qt6_add_qml_module") runCMake();
&& func.Arguments().front().Value == target_name; } else {
}; const std::string target_name = function->Arguments().front().Value;
// Special case: when qt_add_executable and qt_add_qml_module use the same target name auto qtAddModule = [target_name](const auto &func) {
// then qt_add_qml_module function should be used return (func.LowerCaseName() == "qt_add_qml_module"
function = findFunction(*cmakeListFile, qtAddModule).value_or(*function); || func.LowerCaseName() == "qt6_add_qml_module")
&& func.Arguments().front().Value == target_name;
};
// Special case: when qt_add_executable and qt_add_qml_module use the same target name
// then qt_add_qml_module function should be used
function = findFunction(*cmakeListFile, qtAddModule).value_or(*function);
const QString newSourceFiles = newFilesForFunction(function->LowerCaseName(), const QString newSourceFiles = newFilesForFunction(function->LowerCaseName(),
filePaths, filePaths,
n->filePath().canonicalPath()); n->filePath().canonicalPath());
const SnippetAndLocation snippetLocation = generateSnippetAndLocationForSources( const SnippetAndLocation snippetLocation = generateSnippetAndLocationForSources(
newSourceFiles, *cmakeListFile, *function, targetName); newSourceFiles, *cmakeListFile, *function, targetName);
expected_str<bool> inserted = insertSnippetSilently(targetCMakeFile, snippetLocation); expected_str<bool> inserted = insertSnippetSilently(targetCMakeFile, snippetLocation);
if (!inserted) { if (!inserted) {
qCCritical(cmakeBuildSystemLog) << inserted.error(); qCCritical(cmakeBuildSystemLog) << inserted.error();
return false; return false;
}
} }
if (notAdded) if (notAdded)
@@ -770,22 +795,7 @@ CMakeBuildSystem::projectFileArgumentPosition(const QString &targetName, const Q
return ProjectFileArgumentPosition{filePathArgument, targetCMakeFile, fileName}; return ProjectFileArgumentPosition{filePathArgument, targetCMakeFile, fileName};
} else { } else {
// Check if the filename is part of globbing variable result // Check if the filename is part of globbing variable result
const auto globFunctions = std::get<0>( const auto haveGlobbing = isGlobbingFunction(cmakeListFile.value(), func.value());
Utils::partition(cmakeListFile->Functions, [](const auto &f) {
return f.LowerCaseName() == "file" && f.Arguments().size() > 2
&& (f.Arguments().front().Value == "GLOB"
|| f.Arguments().front().Value == "GLOB_RECURSE");
}));
const auto globVariables = Utils::transform<QSet>(globFunctions, [](const auto &func) {
return std::string("${") + func.Arguments()[1].Value + "}";
});
const auto haveGlobbing = Utils::anyOf(func->Arguments(),
[globVariables](const auto &arg) {
return globVariables.contains(arg.Value);
});
if (haveGlobbing) { if (haveGlobbing) {
return ProjectFileArgumentPosition{filePathArgument, return ProjectFileArgumentPosition{filePathArgument,
targetCMakeFile, targetCMakeFile,
@@ -833,6 +843,7 @@ RemovedFilesFromProject CMakeBuildSystem::removeFiles(Node *context,
const FilePath projDir = n->filePath().canonicalPath(); const FilePath projDir = n->filePath().canonicalPath();
const QString targetName = n->buildKey(); const QString targetName = n->buildKey();
bool haveGlobbing = false;
for (const auto &file : filePaths) { for (const auto &file : filePaths) {
const QString fileName const QString fileName
= file.canonicalPath().relativePathFrom(projDir).cleanPath().toString(); = file.canonicalPath().relativePathFrom(projDir).cleanPath().toString();
@@ -847,6 +858,11 @@ RemovedFilesFromProject CMakeBuildSystem::removeFiles(Node *context,
continue; continue;
} }
if (filePos.value().fromGlobbing) {
haveGlobbing = true;
continue;
}
BaseTextEditor *editor = qobject_cast<BaseTextEditor *>( BaseTextEditor *editor = qobject_cast<BaseTextEditor *>(
Core::EditorManager::openEditorAt({filePos.value().cmakeFile, Core::EditorManager::openEditorAt({filePos.value().cmakeFile,
static_cast<int>(filePos.value().argumentPosition.Line), static_cast<int>(filePos.value().argumentPosition.Line),
@@ -869,8 +885,7 @@ RemovedFilesFromProject CMakeBuildSystem::removeFiles(Node *context,
if (filePos->argumentPosition.Delim == cmListFileArgument::Quoted) if (filePos->argumentPosition.Delim == cmListFileArgument::Quoted)
extraChars = 2; extraChars = 2;
if (!filePos.value().fromGlobbing) editor->replace(filePos.value().relativeFileName.length() + extraChars, "");
editor->replace(filePos.value().relativeFileName.length() + extraChars, "");
editor->editorWidget()->autoIndent(); editor->editorWidget()->autoIndent();
if (!Core::DocumentManager::saveDocument(editor->document())) { if (!Core::DocumentManager::saveDocument(editor->document())) {
@@ -889,6 +904,9 @@ RemovedFilesFromProject CMakeBuildSystem::removeFiles(Node *context,
if (notRemoved && !badFiles.isEmpty()) if (notRemoved && !badFiles.isEmpty())
*notRemoved = badFiles; *notRemoved = badFiles;
if (haveGlobbing && settings(project()).autorunCMake())
runCMake();
return badFiles.isEmpty() ? RemovedFilesFromProject::Ok : RemovedFilesFromProject::Error; return badFiles.isEmpty() ? RemovedFilesFromProject::Ok : RemovedFilesFromProject::Error;
} }
@@ -949,40 +967,47 @@ bool CMakeBuildSystem::renameFile(Node *context,
return false; return false;
} }
bool haveGlobbing = false;
do { do {
BaseTextEditor *editor = qobject_cast<BaseTextEditor *>( if (!fileToRename->fromGlobbing) {
Core::EditorManager::openEditorAt( BaseTextEditor *editor = qobject_cast<BaseTextEditor *>(
{fileToRename->cmakeFile, Core::EditorManager::openEditorAt(
static_cast<int>(fileToRename->argumentPosition.Line), {fileToRename->cmakeFile,
static_cast<int>(fileToRename->argumentPosition.Column - 1)}, static_cast<int>(fileToRename->argumentPosition.Line),
Constants::CMAKE_EDITOR_ID, static_cast<int>(fileToRename->argumentPosition.Column - 1)},
Core::EditorManager::DoNotMakeVisible)); Constants::CMAKE_EDITOR_ID,
if (!editor) { Core::EditorManager::DoNotMakeVisible));
qCCritical(cmakeBuildSystemLog).noquote() if (!editor) {
<< "BaseTextEditor cannot be obtained for" << fileToRename->cmakeFile.path() qCCritical(cmakeBuildSystemLog).noquote()
<< fileToRename->argumentPosition.Line << "BaseTextEditor cannot be obtained for" << fileToRename->cmakeFile.path()
<< int(fileToRename->argumentPosition.Column); << fileToRename->argumentPosition.Line
return false; << int(fileToRename->argumentPosition.Column);
} return false;
}
// If quotes were used for the source file, skip the starting quote // If quotes were used for the source file, skip the starting quote
if (fileToRename->argumentPosition.Delim == cmListFileArgument::Quoted) if (fileToRename->argumentPosition.Delim == cmListFileArgument::Quoted)
editor->setCursorPosition(editor->position() + 1); editor->setCursorPosition(editor->position() + 1);
if (!fileToRename->fromGlobbing)
editor->replace(fileToRename->relativeFileName.length(), newRelPathName); editor->replace(fileToRename->relativeFileName.length(), newRelPathName);
editor->editorWidget()->autoIndent(); editor->editorWidget()->autoIndent();
if (!Core::DocumentManager::saveDocument(editor->document())) { if (!Core::DocumentManager::saveDocument(editor->document())) {
qCCritical(cmakeBuildSystemLog).noquote() qCCritical(cmakeBuildSystemLog).noquote()
<< "Changes to" << fileToRename->cmakeFile.path() << "could not be saved."; << "Changes to" << fileToRename->cmakeFile.path() << "could not be saved.";
return false; return false;
}
} else {
haveGlobbing = true;
} }
// Try the next occurrence. This can happen if set_source_file_properties is used // Try the next occurrence. This can happen if set_source_file_properties is used
fileToRename = projectFileArgumentPosition(targetName, fileToRename->relativeFileName); fileToRename = projectFileArgumentPosition(targetName, fileToRename->relativeFileName);
} while (fileToRename && !fileToRename->fromGlobbing); } while (fileToRename && !fileToRename->fromGlobbing);
if (haveGlobbing && settings(project()).autorunCMake())
runCMake();
return true; return true;
} }

View File

@@ -187,9 +187,16 @@ void CMakeTargetNode::setConfig(const CMakeConfig &config)
m_config = config; m_config = config;
} }
void CMakeTargetNode::setVisibleAfterAddFileAction(bool visibleAfterAddFileAction)
{
m_visibleAfterAddFileAction = visibleAfterAddFileAction;
}
std::optional<FilePath> CMakeTargetNode::visibleAfterAddFileAction() const std::optional<FilePath> CMakeTargetNode::visibleAfterAddFileAction() const
{ {
return filePath().pathAppended(Constants::CMAKE_LISTS_TXT); if (m_visibleAfterAddFileAction)
return filePath().pathAppended(Constants::CMAKE_LISTS_TXT);
return std::nullopt;
} }
void CMakeTargetNode::build() void CMakeTargetNode::build()

View File

@@ -57,11 +57,14 @@ public:
QVariant data(Utils::Id role) const override; QVariant data(Utils::Id role) const override;
void setConfig(const CMakeConfig &config); void setConfig(const CMakeConfig &config);
void setVisibleAfterAddFileAction(bool visibleAfterAddFileAction);
private: private:
QString m_tooltip; QString m_tooltip;
Utils::FilePath m_buildDirectory; Utils::FilePath m_buildDirectory;
Utils::FilePath m_artifact; Utils::FilePath m_artifact;
CMakeConfig m_config; CMakeConfig m_config;
bool m_visibleAfterAddFileAction = true;
}; };
} // CMakeProjectManager::Internal } // CMakeProjectManager::Internal