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

View File

@@ -187,9 +187,16 @@ void CMakeTargetNode::setConfig(const CMakeConfig &config)
m_config = config;
}
void CMakeTargetNode::setVisibleAfterAddFileAction(bool visibleAfterAddFileAction)
{
m_visibleAfterAddFileAction = visibleAfterAddFileAction;
}
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()

View File

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