forked from qt-creator/qt-creator
CMakePM: Implement BuildSystem::addDependencies
This can be triggered via C++ QuickFix or the new C++ class wizard. Change-Id: I93439fb823a2e5c359a3f6b310c1cd642201d1a1 Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
This commit is contained in:
@@ -50,3 +50,11 @@ add_qtc_plugin(CMakeProjectManager
|
||||
3rdparty/cmake/cmListFileCache.h
|
||||
3rdparty/rstparser/rstparser.cc 3rdparty/rstparser/rstparser.h
|
||||
)
|
||||
|
||||
file(GLOB_RECURSE test_cases RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} testcases/*)
|
||||
qtc_add_resources(CMakeProjectManager "testcases"
|
||||
CONDITION WITH_TESTS
|
||||
PREFIX "/cmakeprojectmanager"
|
||||
BASE "."
|
||||
FILES ${test_cases}
|
||||
)
|
||||
|
@@ -62,6 +62,11 @@
|
||||
#include <QLoggingCategory>
|
||||
#include <QPushButton>
|
||||
|
||||
#ifdef WITH_TESTS
|
||||
#include <cppeditor/cpptoolstestcase.h>
|
||||
#include <QTest>
|
||||
#endif
|
||||
|
||||
using namespace ProjectExplorer;
|
||||
using namespace TextEditor;
|
||||
using namespace Utils;
|
||||
@@ -1077,6 +1082,178 @@ void CMakeBuildSystem::buildNamedTarget(const QString &target)
|
||||
CMakeProjectManager::Internal::buildTarget(this, target);
|
||||
}
|
||||
|
||||
static Result<bool> insertDependencies(
|
||||
const QString &targetName,
|
||||
const FilePath &targetCMakeFile,
|
||||
int targetDefinitionLine,
|
||||
const QStringList &dependencies,
|
||||
int qtMajorVersion)
|
||||
{
|
||||
std::optional<cmListFile> cmakeListFile = getUncachedCMakeListFile(targetCMakeFile);
|
||||
if (!cmakeListFile)
|
||||
return ResultError("Failed to read " + targetCMakeFile.toUserOutput());
|
||||
|
||||
std::optional<cmListFileFunction> function
|
||||
= findFunction(*cmakeListFile, [targetDefinitionLine](const auto &func) {
|
||||
return func.Line() == targetDefinitionLine;
|
||||
});
|
||||
if (!function.has_value())
|
||||
return ResultError(QString("Failed to locate the target defining function at %1").arg(targetDefinitionLine));
|
||||
const int targetDefinitionLastLine = function->LineEnd();
|
||||
|
||||
//
|
||||
// find_package
|
||||
//
|
||||
const QString qtPackage = QString("Qt%1").arg(qtMajorVersion);
|
||||
function = findFunction(
|
||||
*cmakeListFile,
|
||||
[qtPackage](const auto &func) {
|
||||
return func.LowerCaseName() == "find_package" && func.Arguments().size() > 0
|
||||
&& func.Arguments()[0].Value == qtPackage;
|
||||
},
|
||||
/* reverse = */ true);
|
||||
|
||||
const QString findComponents = transform(dependencies, [](const QString &dep) {
|
||||
QTC_ASSERT(dep.size() > 3, return dep);
|
||||
return dep.mid(3);
|
||||
}).join(" ");
|
||||
QString snippet = QString("find_package(%1 REQUIRED COMPONENTS %2)\n%3")
|
||||
.arg(qtPackage)
|
||||
.arg(findComponents)
|
||||
.arg(!function ? QString("\n") : QString(""));
|
||||
|
||||
int insertionLine = function ? function->LineEnd() + 1 : targetDefinitionLine;
|
||||
Result<bool> inserted = insertSnippetSilently(targetCMakeFile, {snippet, insertionLine, 0});
|
||||
if (!inserted)
|
||||
return inserted;
|
||||
const int insertedFindPackageOffset = 2;
|
||||
|
||||
//
|
||||
// target_link_libraries
|
||||
//
|
||||
cmakeListFile = getUncachedCMakeListFile(targetCMakeFile);
|
||||
|
||||
function = findFunction(
|
||||
*cmakeListFile,
|
||||
[targetName](const auto &func) {
|
||||
return func.LowerCaseName() == "target_link_libraries" && func.Arguments().size() > 0
|
||||
&& func.Arguments()[0].Value == targetName;
|
||||
},
|
||||
/* reverse = */ true);
|
||||
|
||||
const QString targetPrefix = QString("Qt%1::").arg(qtMajorVersion);
|
||||
const QString linkLibraries
|
||||
= transform(dependencies, [targetPrefix](const QString &dep) -> QString {
|
||||
QTC_ASSERT(dep.size() > 3, return targetPrefix + dep);
|
||||
return targetPrefix + dep.mid(3);
|
||||
}).join(" ");
|
||||
snippet = QString("%1target_link_libraries(%2 PRIVATE %3)\n")
|
||||
.arg(!function ? QString("\n") : QString(""))
|
||||
.arg(targetName)
|
||||
.arg(linkLibraries);
|
||||
|
||||
insertionLine = (function ? function->LineEnd()
|
||||
: targetDefinitionLastLine + insertedFindPackageOffset)
|
||||
+ 1;
|
||||
return insertSnippetSilently(targetCMakeFile, {snippet, insertionLine, 0});
|
||||
}
|
||||
|
||||
bool CMakeBuildSystem::addDependencies(
|
||||
ProjectExplorer::Node *context, const QStringList &dependencies)
|
||||
{
|
||||
if (auto n = dynamic_cast<CMakeTargetNode *>(context)) {
|
||||
const QString targetName = n->buildKey();
|
||||
const std::optional<Link> cmakeFile = cmakeFileForBuildKey(targetName, buildTargets());
|
||||
if (!cmakeFile)
|
||||
return false;
|
||||
|
||||
int qtMajorVersion = 6;
|
||||
if (auto qt = m_findPackagesFilesHash.value("Qt5Core"); qt.hasValidTarget())
|
||||
qtMajorVersion = 5;
|
||||
|
||||
Result<bool> inserted = insertDependencies(
|
||||
targetName,
|
||||
cmakeFile->targetFilePath,
|
||||
cmakeFile->targetLine,
|
||||
dependencies,
|
||||
qtMajorVersion);
|
||||
if (!inserted) {
|
||||
qCCritical(cmakeBuildSystemLog) << inserted.error();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return BuildSystem::addDependencies(context, dependencies);
|
||||
}
|
||||
|
||||
#ifdef WITH_TESTS
|
||||
class AddDependenciesTest final : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void test()
|
||||
{
|
||||
const auto projectDir = std::make_unique<CppEditor::Tests::TemporaryCopiedDir>(
|
||||
":/cmakeprojectmanager/testcases/adddependencies");
|
||||
|
||||
QVERIFY(insertDependencies(
|
||||
"HelloQt",
|
||||
projectDir->filePath().pathAppended("existing_qt5.cmake"),
|
||||
18,
|
||||
{"Qt.Concurrent"},
|
||||
5));
|
||||
QVERIFY(insertDependencies(
|
||||
"HelloQt",
|
||||
projectDir->filePath().pathAppended("existing_qt6.cmake"),
|
||||
8,
|
||||
{"Qt.Concurrent"},
|
||||
6));
|
||||
QVERIFY(insertDependencies(
|
||||
"HelloCpp",
|
||||
projectDir->filePath().pathAppended("no_qt6.cmake"),
|
||||
8,
|
||||
{"Qt.Concurrent"},
|
||||
6));
|
||||
|
||||
// Compare files.
|
||||
static const QString suffix = "_expected.cmake";
|
||||
const FileFilter filter({"*" + suffix}, QDir::Files);
|
||||
const FilePaths expectedDocuments = projectDir->filePath().dirEntries(filter);
|
||||
QVERIFY(!expectedDocuments.isEmpty());
|
||||
for (const FilePath &expected : expectedDocuments) {
|
||||
const FilePath actual = expected.parentDir().pathAppended(
|
||||
expected.fileName().chopped(suffix.length()) + ".cmake");
|
||||
QVERIFY(actual.exists());
|
||||
const auto actualContents = actual.fileContents();
|
||||
QVERIFY(actualContents);
|
||||
const auto expectedContents = expected.fileContents();
|
||||
const QByteArrayList actualLines = actualContents->split('\n');
|
||||
const QByteArrayList expectedLines = expectedContents->split('\n');
|
||||
if (actualLines.size() != expectedLines.size()) {
|
||||
qDebug().noquote().nospace() << "---\n" << *expectedContents << "EOF";
|
||||
qDebug().noquote().nospace() << "+++\n" << *actualContents << "EOF";
|
||||
}
|
||||
QCOMPARE(actualLines.size(), expectedLines.size());
|
||||
for (int i = 0; i < actualLines.size(); ++i) {
|
||||
const QByteArray actualLine = actualLines.at(i);
|
||||
const QByteArray expectedLine = expectedLines.at(i);
|
||||
if (actualLine != expectedLine)
|
||||
qDebug() << "Unexpected content in line" << (i + 1) << "of file"
|
||||
<< actual.fileName();
|
||||
QCOMPARE(actualLine, expectedLine);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
QObject *createAddDependenciesTest()
|
||||
{
|
||||
return new AddDependenciesTest;
|
||||
}
|
||||
#endif
|
||||
|
||||
FilePaths CMakeBuildSystem::filesGeneratedFrom(const FilePath &sourceFile) const
|
||||
{
|
||||
FilePath project = projectDirectory();
|
||||
@@ -2608,3 +2785,7 @@ ExtraCompiler *CMakeBuildSystem::findExtraCompiler(const ExtraCompilerFilter &fi
|
||||
}
|
||||
|
||||
} // CMakeProjectManager::Internal
|
||||
|
||||
#ifdef WITH_TESTS
|
||||
#include <cmakebuildsystem.moc>
|
||||
#endif
|
||||
|
@@ -68,6 +68,8 @@ public:
|
||||
|
||||
Utils::FilePaths filesGeneratedFrom(const Utils::FilePath &sourceFile) const final;
|
||||
|
||||
bool addDependencies(ProjectExplorer::Node *context, const QStringList &dependencies) final;
|
||||
|
||||
// Actions:
|
||||
void runCMake();
|
||||
void runCMakeAndScanProjectTree();
|
||||
@@ -269,5 +271,9 @@ private:
|
||||
QString m_warning;
|
||||
};
|
||||
|
||||
#ifdef WITH_TESTS
|
||||
QObject *createAddDependenciesTest();
|
||||
#endif
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace CMakeProjectManager
|
||||
|
@@ -108,4 +108,10 @@ QtcPlugin {
|
||||
"rstparser/rstparser.h"
|
||||
]
|
||||
}
|
||||
|
||||
QtcTestFiles {
|
||||
name: "test data"
|
||||
files: "testcases/**/*"
|
||||
fileTags: qtc.withPluginTests ? ["qt.core.resource_data"] : []
|
||||
}
|
||||
}
|
||||
|
@@ -80,6 +80,7 @@ class CMakeProjectPlugin final : public ExtensionSystem::IPlugin
|
||||
addTestCreator(createCMakeOutputParserTest);
|
||||
addTestCreator(createCMakeAutogenParserTest);
|
||||
addTestCreator(createCMakeProjectImporterTest);
|
||||
addTestCreator(createAddDependenciesTest);
|
||||
#endif
|
||||
|
||||
FileIconProvider::registerIconOverlayForSuffix(Constants::Icons::FILE_OVERLAY, "cmake");
|
||||
|
@@ -0,0 +1,25 @@
|
||||
cmake_minimum_required(VERSION 3.1.0)
|
||||
|
||||
project(HelloQt VERSION 1.0.0 LANGUAGES CXX)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_AUTORCC ON)
|
||||
set(CMAKE_AUTOUIC ON)
|
||||
|
||||
if(CMAKE_VERSION VERSION_LESS "3.7.0")
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
endif()
|
||||
|
||||
find_package(Qt5 COMPONENTS Widgets REQUIRED)
|
||||
|
||||
add_executable(HelloQt
|
||||
mainwindow.ui
|
||||
mainwindow.cpp
|
||||
main.cpp
|
||||
resources.qrc
|
||||
)
|
||||
|
||||
target_link_libraries(HelloQt Qt5::Widgets)
|
@@ -0,0 +1,27 @@
|
||||
cmake_minimum_required(VERSION 3.1.0)
|
||||
|
||||
project(HelloQt VERSION 1.0.0 LANGUAGES CXX)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_AUTORCC ON)
|
||||
set(CMAKE_AUTOUIC ON)
|
||||
|
||||
if(CMAKE_VERSION VERSION_LESS "3.7.0")
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
endif()
|
||||
|
||||
find_package(Qt5 COMPONENTS Widgets REQUIRED)
|
||||
find_package(Qt5 REQUIRED COMPONENTS Concurrent)
|
||||
|
||||
add_executable(HelloQt
|
||||
mainwindow.ui
|
||||
mainwindow.cpp
|
||||
main.cpp
|
||||
resources.qrc
|
||||
)
|
||||
|
||||
target_link_libraries(HelloQt Qt5::Widgets)
|
||||
target_link_libraries(HelloQt PRIVATE Qt5::Concurrent)
|
@@ -0,0 +1,35 @@
|
||||
cmake_minimum_required(VERSION 3.19)
|
||||
project(HelloQt LANGUAGES CXX)
|
||||
|
||||
find_package(Qt6 6.5 REQUIRED COMPONENTS Core Widgets)
|
||||
|
||||
qt_standard_project_setup()
|
||||
|
||||
qt_add_executable(HelloQt
|
||||
WIN32 MACOSX_BUNDLE
|
||||
main.cpp
|
||||
mainwindow.cpp
|
||||
mainwindow.h
|
||||
mainwindow.ui
|
||||
)
|
||||
|
||||
target_link_libraries(HelloQt
|
||||
PRIVATE
|
||||
Qt::Core
|
||||
Qt::Widgets
|
||||
)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
|
||||
install(TARGETS HelloQt
|
||||
BUNDLE DESTINATION .
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
)
|
||||
|
||||
qt_generate_deploy_app_script(
|
||||
TARGET HelloQt
|
||||
OUTPUT_SCRIPT deploy_script
|
||||
NO_UNSUPPORTED_PLATFORM_ERROR
|
||||
)
|
||||
install(SCRIPT ${deploy_script})
|
@@ -0,0 +1,37 @@
|
||||
cmake_minimum_required(VERSION 3.19)
|
||||
project(HelloQt LANGUAGES CXX)
|
||||
|
||||
find_package(Qt6 6.5 REQUIRED COMPONENTS Core Widgets)
|
||||
find_package(Qt6 REQUIRED COMPONENTS Concurrent)
|
||||
|
||||
qt_standard_project_setup()
|
||||
|
||||
qt_add_executable(HelloQt
|
||||
WIN32 MACOSX_BUNDLE
|
||||
main.cpp
|
||||
mainwindow.cpp
|
||||
mainwindow.h
|
||||
mainwindow.ui
|
||||
)
|
||||
|
||||
target_link_libraries(HelloQt
|
||||
PRIVATE
|
||||
Qt::Core
|
||||
Qt::Widgets
|
||||
)
|
||||
target_link_libraries(HelloQt PRIVATE Qt6::Concurrent)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
|
||||
install(TARGETS HelloQt
|
||||
BUNDLE DESTINATION .
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
)
|
||||
|
||||
qt_generate_deploy_app_script(
|
||||
TARGET HelloQt
|
||||
OUTPUT_SCRIPT deploy_script
|
||||
NO_UNSUPPORTED_PLATFORM_ERROR
|
||||
)
|
||||
install(SCRIPT ${deploy_script})
|
@@ -0,0 +1,14 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
project(HelloCpp LANGUAGES CXX)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
add_executable(HelloCpp main.cpp)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
install(TARGETS HelloCpp
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
)
|
@@ -0,0 +1,18 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
project(HelloCpp LANGUAGES CXX)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
find_package(Qt6 REQUIRED COMPONENTS Concurrent)
|
||||
|
||||
add_executable(HelloCpp main.cpp)
|
||||
|
||||
target_link_libraries(HelloCpp PRIVATE Qt6::Concurrent)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
install(TARGETS HelloCpp
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
)
|
Reference in New Issue
Block a user