2016-08-25 14:33:44 +02:00
|
|
|
/****************************************************************************
|
|
|
|
|
**
|
|
|
|
|
** Copyright (C) 2016 The Qt Company Ltd.
|
|
|
|
|
** Contact: https://www.qt.io/licensing/
|
|
|
|
|
**
|
|
|
|
|
** This file is part of Qt Creator.
|
|
|
|
|
**
|
|
|
|
|
** Commercial License Usage
|
|
|
|
|
** Licensees holding valid commercial Qt licenses may use this file in
|
|
|
|
|
** accordance with the commercial license agreement provided with the
|
|
|
|
|
** Software or, alternatively, in accordance with the terms contained in
|
|
|
|
|
** a written agreement between you and The Qt Company. For licensing terms
|
|
|
|
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
|
|
|
|
** information use the contact form at https://www.qt.io/contact-us.
|
|
|
|
|
**
|
|
|
|
|
** GNU General Public License Usage
|
|
|
|
|
** Alternatively, this file may be used under the terms of the GNU
|
|
|
|
|
** General Public License version 3 as published by the Free Software
|
|
|
|
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
|
|
|
|
** included in the packaging of this file. Please review the following
|
|
|
|
|
** information to ensure the GNU General Public License requirements will
|
|
|
|
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
|
|
|
|
**
|
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
|
|
#include "cmakeprojectimporter.h"
|
|
|
|
|
|
|
|
|
|
#include "cmakebuildconfiguration.h"
|
2020-04-24 10:29:43 +02:00
|
|
|
#include "cmakebuildsystem.h"
|
2016-08-25 14:33:44 +02:00
|
|
|
#include "cmakekitinformation.h"
|
2020-12-02 19:16:40 +01:00
|
|
|
#include "cmakeprojectconstants.h"
|
2016-08-25 14:33:44 +02:00
|
|
|
#include "cmaketoolmanager.h"
|
|
|
|
|
|
2020-12-02 19:16:40 +01:00
|
|
|
#include <coreplugin/messagemanager.h>
|
|
|
|
|
|
2019-01-29 16:51:17 +01:00
|
|
|
#include <projectexplorer/buildinfo.h>
|
2020-04-17 15:30:05 +02:00
|
|
|
#include <projectexplorer/kitinformation.h>
|
2016-08-25 14:33:44 +02:00
|
|
|
#include <projectexplorer/projectexplorerconstants.h>
|
2020-12-09 12:41:11 +01:00
|
|
|
#include <projectexplorer/toolchainmanager.h>
|
2016-08-25 14:33:44 +02:00
|
|
|
|
|
|
|
|
#include <qtsupport/qtkitinformation.h>
|
|
|
|
|
|
|
|
|
|
#include <utils/algorithm.h>
|
|
|
|
|
#include <utils/qtcassert.h>
|
2020-08-18 11:34:44 +01:00
|
|
|
#include <utils/stringutils.h>
|
2016-08-25 14:33:44 +02:00
|
|
|
|
|
|
|
|
#include <QDir>
|
|
|
|
|
#include <QLoggingCategory>
|
|
|
|
|
|
|
|
|
|
using namespace ProjectExplorer;
|
|
|
|
|
using namespace QtSupport;
|
2019-05-17 13:28:38 +02:00
|
|
|
using namespace Utils;
|
2016-08-25 14:33:44 +02:00
|
|
|
|
2021-01-04 13:48:21 +01:00
|
|
|
namespace CMakeProjectManager {
|
|
|
|
|
namespace Internal {
|
2016-08-25 14:33:44 +02:00
|
|
|
|
2020-01-15 14:39:23 +01:00
|
|
|
static Q_LOGGING_CATEGORY(cmInputLog, "qtc.cmake.import", QtWarningMsg);
|
2016-08-25 14:33:44 +02:00
|
|
|
|
|
|
|
|
struct DirectoryData
|
|
|
|
|
{
|
|
|
|
|
// Project Stuff:
|
|
|
|
|
QByteArray cmakeBuildType;
|
2021-01-04 13:48:21 +01:00
|
|
|
FilePath buildDirectory;
|
2020-12-02 19:16:40 +01:00
|
|
|
FilePath cmakeHomeDirectory;
|
2016-08-25 14:33:44 +02:00
|
|
|
|
|
|
|
|
// Kit Stuff
|
2021-01-04 13:48:21 +01:00
|
|
|
FilePath cmakeBinary;
|
2021-07-06 09:50:01 +02:00
|
|
|
QString generator;
|
|
|
|
|
QString extraGenerator;
|
|
|
|
|
QString platform;
|
|
|
|
|
QString toolset;
|
|
|
|
|
FilePath sysroot;
|
2016-08-25 14:33:44 +02:00
|
|
|
QtProjectImporter::QtVersionData qt;
|
2019-07-02 11:31:12 +02:00
|
|
|
QVector<ToolChainDescription> toolChains;
|
2016-08-25 14:33:44 +02:00
|
|
|
};
|
|
|
|
|
|
2021-09-09 08:27:28 +02:00
|
|
|
static QStringList scanDirectory(const FilePath &path, const QString &prefix)
|
2016-08-25 14:33:44 +02:00
|
|
|
{
|
|
|
|
|
QStringList result;
|
2020-06-18 14:21:09 +02:00
|
|
|
qCDebug(cmInputLog) << "Scanning for directories matching" << prefix << "in" << path;
|
2016-08-25 14:33:44 +02:00
|
|
|
|
2021-09-09 08:27:28 +02:00
|
|
|
foreach (const FilePath &entry, path.dirEntries({prefix + ".*"}, QDir::Dirs | QDir::NoDotAndDotDot)) {
|
|
|
|
|
QTC_ASSERT(entry.isDir(), continue);
|
|
|
|
|
result.append(entry.toString());
|
2016-08-25 14:33:44 +02:00
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-04 13:48:21 +01:00
|
|
|
static QString baseCMakeToolDisplayName(CMakeTool &tool)
|
2020-08-18 11:34:44 +01:00
|
|
|
{
|
2021-01-04 13:48:21 +01:00
|
|
|
CMakeTool::Version version = tool.version();
|
2020-08-18 11:34:44 +01:00
|
|
|
return QString("CMake %1.%2.%3").arg(version.major).arg(version.minor).arg(version.patch);
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-04 13:48:21 +01:00
|
|
|
static QString uniqueCMakeToolDisplayName(CMakeTool &tool)
|
2020-08-18 11:34:44 +01:00
|
|
|
{
|
|
|
|
|
QString baseName = baseCMakeToolDisplayName(tool);
|
|
|
|
|
|
|
|
|
|
QStringList existingNames;
|
2021-01-04 13:48:21 +01:00
|
|
|
for (const CMakeTool *t : CMakeToolManager::cmakeTools())
|
2020-08-18 11:34:44 +01:00
|
|
|
existingNames << t->displayName();
|
|
|
|
|
return Utils::makeUniquelyNumbered(baseName, existingNames);
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-04 13:48:21 +01:00
|
|
|
// CMakeProjectImporter
|
2016-08-25 14:33:44 +02:00
|
|
|
|
2021-01-04 13:48:21 +01:00
|
|
|
CMakeProjectImporter::CMakeProjectImporter(const FilePath &path) : QtProjectImporter(path)
|
2016-08-25 14:33:44 +02:00
|
|
|
{
|
2019-02-06 12:50:51 +01:00
|
|
|
useTemporaryKitAspect(CMakeKitAspect::id(),
|
2016-08-25 14:33:44 +02:00
|
|
|
[this](Kit *k, const QVariantList &vl) { cleanupTemporaryCMake(k, vl); },
|
|
|
|
|
[this](Kit *k, const QVariantList &vl) { persistTemporaryCMake(k, vl); });
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QStringList CMakeProjectImporter::importCandidates()
|
|
|
|
|
{
|
|
|
|
|
QStringList candidates;
|
|
|
|
|
|
2021-09-09 08:27:28 +02:00
|
|
|
candidates << scanDirectory(projectFilePath().absolutePath(), "build");
|
2016-08-25 14:33:44 +02:00
|
|
|
|
|
|
|
|
foreach (Kit *k, KitManager::kits()) {
|
2021-09-09 08:27:28 +02:00
|
|
|
FilePath shadowBuildDirectory
|
|
|
|
|
= CMakeBuildConfiguration::shadowBuildDirectory(projectFilePath(),
|
|
|
|
|
k,
|
|
|
|
|
QString(),
|
|
|
|
|
BuildConfiguration::Unknown);
|
|
|
|
|
candidates << scanDirectory(shadowBuildDirectory.absolutePath(), QString());
|
2016-08-25 14:33:44 +02:00
|
|
|
}
|
|
|
|
|
const QStringList finalists = Utils::filteredUnique(candidates);
|
2020-06-18 14:21:09 +02:00
|
|
|
qCInfo(cmInputLog) << "import candidates:" << finalists;
|
2016-08-25 14:33:44 +02:00
|
|
|
return finalists;
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-04 13:48:21 +01:00
|
|
|
static FilePath qmakeFromCMakeCache(const CMakeConfig &config)
|
2016-08-25 14:33:44 +02:00
|
|
|
{
|
|
|
|
|
// Qt4 way to define things (more convenient for us, so try this first;-)
|
2021-07-06 10:29:29 +02:00
|
|
|
const FilePath qmake = config.filePathValueOf("QT_QMAKE_EXECUTABLE");
|
2020-06-18 14:21:09 +02:00
|
|
|
qCDebug(cmInputLog) << "QT_QMAKE_EXECUTABLE=" << qmake.toUserOutput();
|
2016-08-25 14:33:44 +02:00
|
|
|
if (!qmake.isEmpty())
|
|
|
|
|
return qmake;
|
|
|
|
|
|
|
|
|
|
// Check Qt5 settings: oh, the horror!
|
2021-01-04 13:48:21 +01:00
|
|
|
const FilePath qtCMakeDir = [config] {
|
2021-07-06 10:29:29 +02:00
|
|
|
FilePath tmp = config.filePathValueOf("Qt5Core_DIR");
|
|
|
|
|
if (tmp.isEmpty())
|
|
|
|
|
tmp = config.filePathValueOf("Qt6Core_DIR");
|
2020-06-17 10:45:09 +02:00
|
|
|
return tmp;
|
|
|
|
|
}();
|
2020-06-18 14:21:09 +02:00
|
|
|
qCDebug(cmInputLog) << "QtXCore_DIR=" << qtCMakeDir.toUserOutput();
|
2021-01-04 13:48:21 +01:00
|
|
|
const FilePath canQtCMakeDir = FilePath::fromString(qtCMakeDir.toFileInfo().canonicalFilePath());
|
2020-06-18 14:21:09 +02:00
|
|
|
qCInfo(cmInputLog) << "QtXCore_DIR (canonical)=" << canQtCMakeDir.toUserOutput();
|
2016-08-25 14:33:44 +02:00
|
|
|
if (qtCMakeDir.isEmpty())
|
2021-01-04 13:48:21 +01:00
|
|
|
return FilePath();
|
|
|
|
|
const FilePath baseQtDir = canQtCMakeDir.parentDir().parentDir().parentDir(); // Up 3 levels...
|
2020-06-18 14:21:09 +02:00
|
|
|
qCDebug(cmInputLog) << "BaseQtDir:" << baseQtDir.toUserOutput();
|
2016-08-25 14:33:44 +02:00
|
|
|
|
2020-12-31 14:08:16 +01:00
|
|
|
// Run a CMake project that would do qmake probing
|
|
|
|
|
TemporaryDirectory qtcQMakeProbeDir("qtc-cmake-qmake-probe-XXXXXXXX");
|
2016-08-25 14:33:44 +02:00
|
|
|
|
2021-07-01 09:58:48 +02:00
|
|
|
QFile cmakeListTxt(qtcQMakeProbeDir.filePath("CMakeLists.txt").toString());
|
2020-12-31 14:08:16 +01:00
|
|
|
if (!cmakeListTxt.open(QIODevice::WriteOnly)) {
|
|
|
|
|
return FilePath();
|
|
|
|
|
}
|
2021-04-28 11:23:56 +02:00
|
|
|
// FIXME replace by raw string when gcc 8+ is minimum
|
|
|
|
|
cmakeListTxt.write(QByteArray(
|
|
|
|
|
"cmake_minimum_required(VERSION 3.15)\n"
|
|
|
|
|
"\n"
|
|
|
|
|
"project(qmake-probe LANGUAGES NONE)\n"
|
|
|
|
|
"\n"
|
|
|
|
|
"# Bypass Qt6's usage of find_dependency, which would require compiler\n"
|
|
|
|
|
"# and source code probing, which slows things unnecessarily"
|
|
|
|
|
"file(WRITE \"${CMAKE_SOURCE_DIR}/CMakeFindDependencyMacro.cmake\"\n"
|
|
|
|
|
"[=["
|
|
|
|
|
" macro(find_dependency dep)\n"
|
|
|
|
|
" endmacro()\n"
|
|
|
|
|
"]=])\n"
|
|
|
|
|
"set(CMAKE_MODULE_PATH \"${CMAKE_SOURCE_DIR}\")\n"
|
|
|
|
|
"\n"
|
|
|
|
|
"find_package(QT NAMES Qt6 Qt5 COMPONENTS Core REQUIRED)\n"
|
|
|
|
|
"find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core REQUIRED)\n"
|
|
|
|
|
"\n"
|
|
|
|
|
"if (CMAKE_CROSSCOMPILING)\n"
|
|
|
|
|
" find_program(qmake_binary\n"
|
|
|
|
|
" NAMES qmake qmake.bat\n"
|
|
|
|
|
" PATHS \"${Qt${QT_VERSION_MAJOR}_DIR}/../../../bin\"\n"
|
|
|
|
|
" NO_DEFAULT_PATH)\n"
|
|
|
|
|
" file(WRITE \"${CMAKE_SOURCE_DIR}/qmake-location.txt\" \"${qmake_binary}\")\n"
|
|
|
|
|
"else()\n"
|
|
|
|
|
" file(GENERATE\n"
|
|
|
|
|
" OUTPUT \"${CMAKE_SOURCE_DIR}/qmake-location.txt\"\n"
|
|
|
|
|
" CONTENT \"$<TARGET_PROPERTY:Qt${QT_VERSION_MAJOR}::qmake,IMPORTED_LOCATION>\")\n"
|
|
|
|
|
"endif()\n"
|
|
|
|
|
));
|
2020-12-31 14:08:16 +01:00
|
|
|
cmakeListTxt.close();
|
|
|
|
|
|
2021-06-22 04:33:47 +02:00
|
|
|
QtcProcess cmake;
|
2020-12-31 14:08:16 +01:00
|
|
|
cmake.setTimeoutS(5);
|
2021-05-05 15:23:34 +02:00
|
|
|
cmake.setDisableUnixTerminal();
|
2020-12-31 14:08:16 +01:00
|
|
|
Environment env = Environment::systemEnvironment();
|
2021-05-20 11:57:21 +02:00
|
|
|
env.setupEnglishOutput();
|
2021-04-30 18:26:57 +02:00
|
|
|
cmake.setEnvironment(env);
|
2020-12-31 14:08:16 +01:00
|
|
|
cmake.setTimeOutMessageBoxEnabled(false);
|
|
|
|
|
|
2021-07-06 10:29:29 +02:00
|
|
|
QString cmakeGenerator = config.stringValueOf(QByteArray("CMAKE_GENERATOR"));
|
|
|
|
|
FilePath cmakeExecutable = config.filePathValueOf(QByteArray("CMAKE_COMMAND"));
|
|
|
|
|
FilePath cmakeMakeProgram = config.filePathValueOf(QByteArray("CMAKE_MAKE_PROGRAM"));
|
|
|
|
|
FilePath toolchainFile = config.filePathValueOf(QByteArray("CMAKE_TOOLCHAIN_FILE"));
|
|
|
|
|
FilePath hostPath = config.filePathValueOf(QByteArray("QT_HOST_PATH"));
|
2020-12-31 14:08:16 +01:00
|
|
|
|
|
|
|
|
QStringList args;
|
|
|
|
|
args.push_back("-S");
|
2021-07-01 09:58:48 +02:00
|
|
|
args.push_back(qtcQMakeProbeDir.path().path());
|
2020-12-31 14:08:16 +01:00
|
|
|
args.push_back("-B");
|
2021-07-01 09:58:48 +02:00
|
|
|
args.push_back(qtcQMakeProbeDir.filePath("build").path());
|
2020-12-31 14:08:16 +01:00
|
|
|
args.push_back("-G");
|
|
|
|
|
args.push_back(cmakeGenerator);
|
|
|
|
|
|
|
|
|
|
if (!cmakeMakeProgram.isEmpty()) {
|
|
|
|
|
args.push_back(QStringLiteral("-DCMAKE_MAKE_PROGRAM=%1").arg(cmakeMakeProgram.toString()));
|
|
|
|
|
}
|
|
|
|
|
if (toolchainFile.isEmpty()) {
|
|
|
|
|
args.push_back(QStringLiteral("-DCMAKE_PREFIX_PATH=%1").arg(baseQtDir.toString()));
|
|
|
|
|
} else {
|
|
|
|
|
args.push_back(QStringLiteral("-DCMAKE_FIND_ROOT_PATH=%1").arg(baseQtDir.toString()));
|
|
|
|
|
args.push_back(QStringLiteral("-DCMAKE_TOOLCHAIN_FILE=%1").arg(toolchainFile.toString()));
|
|
|
|
|
}
|
|
|
|
|
if (!hostPath.isEmpty()) {
|
|
|
|
|
args.push_back(QStringLiteral("-DQT_HOST_PATH=%1").arg(hostPath.toString()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
qCDebug(cmInputLog) << "CMake probing for qmake path: " << cmakeExecutable.toUserOutput() << args;
|
2021-05-17 12:02:42 +02:00
|
|
|
cmake.setCommand({cmakeExecutable, args});
|
|
|
|
|
cmake.runBlocking();
|
2020-12-31 14:08:16 +01:00
|
|
|
|
2021-07-01 09:58:48 +02:00
|
|
|
QFile qmakeLocationTxt(qtcQMakeProbeDir.filePath("qmake-location.txt").path());
|
2020-12-31 14:08:16 +01:00
|
|
|
if (!qmakeLocationTxt.open(QIODevice::ReadOnly)) {
|
|
|
|
|
return FilePath();
|
2016-08-25 14:33:44 +02:00
|
|
|
}
|
2021-02-15 00:49:15 +01:00
|
|
|
FilePath qmakeLocation = FilePath::fromUtf8(qmakeLocationTxt.readLine().constData());
|
2020-12-31 14:08:16 +01:00
|
|
|
qCDebug(cmInputLog) << "qmake location: " << qmakeLocation.toUserOutput();
|
2016-08-25 14:33:44 +02:00
|
|
|
|
2020-12-31 14:08:16 +01:00
|
|
|
return qmakeLocation;
|
2016-08-25 14:33:44 +02:00
|
|
|
}
|
|
|
|
|
|
2019-07-02 11:31:12 +02:00
|
|
|
static QVector<ToolChainDescription> extractToolChainsFromCache(const CMakeConfig &config)
|
2016-08-25 14:33:44 +02:00
|
|
|
{
|
2019-07-02 11:31:12 +02:00
|
|
|
QVector<ToolChainDescription> result;
|
2021-02-01 19:19:45 +01:00
|
|
|
bool haveCCxxCompiler = false;
|
2016-08-25 14:33:44 +02:00
|
|
|
for (const CMakeConfigItem &i : config) {
|
|
|
|
|
if (!i.key.startsWith("CMAKE_") || !i.key.endsWith("_COMPILER"))
|
|
|
|
|
continue;
|
|
|
|
|
const QByteArray language = i.key.mid(6, i.key.count() - 6 - 9); // skip "CMAKE_" and "_COMPILER"
|
2021-01-04 13:48:21 +01:00
|
|
|
Id languageId;
|
2021-02-01 19:19:45 +01:00
|
|
|
if (language == "CXX") {
|
|
|
|
|
haveCCxxCompiler = true;
|
2019-07-02 11:31:12 +02:00
|
|
|
languageId = ProjectExplorer::Constants::CXX_LANGUAGE_ID;
|
2021-02-01 19:19:45 +01:00
|
|
|
}
|
|
|
|
|
else if (language == "C") {
|
|
|
|
|
haveCCxxCompiler = true;
|
2019-07-02 11:31:12 +02:00
|
|
|
languageId = ProjectExplorer::Constants::C_LANGUAGE_ID;
|
2021-02-01 19:19:45 +01:00
|
|
|
}
|
2019-07-02 11:31:12 +02:00
|
|
|
else
|
2021-01-04 13:48:21 +01:00
|
|
|
languageId = Id::fromName(language);
|
|
|
|
|
result.append({FilePath::fromUtf8(i.value), languageId});
|
2016-08-25 14:33:44 +02:00
|
|
|
}
|
2021-02-01 19:19:45 +01:00
|
|
|
|
|
|
|
|
if (!haveCCxxCompiler) {
|
2021-07-06 10:29:29 +02:00
|
|
|
const QByteArray generator = config.valueOf("CMAKE_GENERATOR");
|
2021-02-01 19:19:45 +01:00
|
|
|
QString cCompilerName;
|
|
|
|
|
QString cxxCompilerName;
|
|
|
|
|
if (generator.contains("Visual Studio")) {
|
|
|
|
|
cCompilerName = "cl.exe";
|
|
|
|
|
cxxCompilerName = "cl.exe";
|
|
|
|
|
} else if (generator.contains("Xcode")) {
|
|
|
|
|
cCompilerName = "clang";
|
|
|
|
|
cxxCompilerName = "clang++";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!cCompilerName.isEmpty() && !cxxCompilerName.isEmpty()) {
|
2021-07-06 10:29:29 +02:00
|
|
|
const FilePath linker = config.filePathValueOf("CMAKE_LINKER");
|
2021-02-01 19:19:45 +01:00
|
|
|
if (!linker.isEmpty()) {
|
|
|
|
|
const FilePath compilerPath = linker.parentDir();
|
|
|
|
|
result.append({compilerPath.pathAppended(cCompilerName),
|
|
|
|
|
ProjectExplorer::Constants::C_LANGUAGE_ID});
|
|
|
|
|
result.append({compilerPath.pathAppended(cxxCompilerName),
|
|
|
|
|
ProjectExplorer::Constants::CXX_LANGUAGE_ID});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-25 14:33:44 +02:00
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-02 19:16:40 +01:00
|
|
|
QList<void *> CMakeProjectImporter::examineDirectory(const FilePath &importPath,
|
|
|
|
|
QString *warningMessage) const
|
2016-08-25 14:33:44 +02:00
|
|
|
{
|
2020-06-18 14:21:09 +02:00
|
|
|
qCInfo(cmInputLog) << "Examining directory:" << importPath.toUserOutput();
|
2019-05-28 13:49:26 +02:00
|
|
|
const FilePath cacheFile = importPath.pathAppended("CMakeCache.txt");
|
2016-08-25 14:33:44 +02:00
|
|
|
|
|
|
|
|
if (!cacheFile.exists()) {
|
2020-06-18 14:21:09 +02:00
|
|
|
qCDebug(cmInputLog) << cacheFile.toUserOutput() << "does not exist, returning.";
|
2016-08-25 14:33:44 +02:00
|
|
|
return { };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString errorMessage;
|
2020-04-24 10:29:43 +02:00
|
|
|
const CMakeConfig config = CMakeBuildSystem::parseCMakeCacheDotTxt(cacheFile, &errorMessage);
|
2016-08-25 14:33:44 +02:00
|
|
|
if (config.isEmpty() || !errorMessage.isEmpty()) {
|
2020-06-18 14:21:09 +02:00
|
|
|
qCDebug(cmInputLog) << "Failed to read configuration from" << cacheFile << errorMessage;
|
2016-08-25 14:33:44 +02:00
|
|
|
return { };
|
|
|
|
|
}
|
2021-01-14 16:38:55 +01:00
|
|
|
|
2021-07-06 10:29:29 +02:00
|
|
|
QByteArrayList buildConfigurationTypes = {config.valueOf("CMAKE_BUILD_TYPE")};
|
2021-01-14 16:38:55 +01:00
|
|
|
if (buildConfigurationTypes.front().isEmpty()) {
|
2021-07-06 10:29:29 +02:00
|
|
|
QByteArray buildConfigurationTypesString = config.valueOf("CMAKE_CONFIGURATION_TYPES");
|
|
|
|
|
if (!buildConfigurationTypesString.isEmpty())
|
2021-01-14 16:38:55 +01:00
|
|
|
buildConfigurationTypes = buildConfigurationTypesString.split(';');
|
2016-08-25 14:33:44 +02:00
|
|
|
}
|
|
|
|
|
|
2021-01-14 16:38:55 +01:00
|
|
|
QList<void *> result;
|
|
|
|
|
for (auto const &buildType: qAsConst(buildConfigurationTypes)) {
|
|
|
|
|
auto data = std::make_unique<DirectoryData>();
|
|
|
|
|
|
2021-07-06 09:50:01 +02:00
|
|
|
data->cmakeHomeDirectory =
|
2021-07-06 10:29:29 +02:00
|
|
|
FilePath::fromUserInput(config.stringValueOf("CMAKE_HOME_DIRECTORY"))
|
2021-07-06 09:50:01 +02:00
|
|
|
.canonicalPath();
|
2021-01-14 16:38:55 +01:00
|
|
|
const FilePath canonicalProjectDirectory = projectDirectory().canonicalPath();
|
|
|
|
|
if (data->cmakeHomeDirectory != canonicalProjectDirectory) {
|
|
|
|
|
*warningMessage = tr("Unexpected source directory \"%1\", expected \"%2\". "
|
|
|
|
|
"This can be correct in some situations, for example when "
|
|
|
|
|
"importing a standalone Qt test, but usually this is an error. "
|
|
|
|
|
"Import the build anyway?")
|
|
|
|
|
.arg(data->cmakeHomeDirectory.toUserOutput(),
|
|
|
|
|
canonicalProjectDirectory.toUserOutput());
|
|
|
|
|
}
|
2016-08-25 14:33:44 +02:00
|
|
|
|
2021-01-14 16:38:55 +01:00
|
|
|
data->buildDirectory = importPath;
|
|
|
|
|
data->cmakeBuildType = buildType;
|
2016-08-25 14:33:44 +02:00
|
|
|
|
2021-07-06 10:29:29 +02:00
|
|
|
data->cmakeBinary = config.filePathValueOf("CMAKE_COMMAND");
|
|
|
|
|
data->generator = config.stringValueOf("CMAKE_GENERATOR");
|
|
|
|
|
data->extraGenerator = config.stringValueOf("CMAKE_EXTRA_GENERATOR");
|
|
|
|
|
data->platform = config.stringValueOf("CMAKE_GENERATOR_PLATFORM");
|
|
|
|
|
data->toolset = config.stringValueOf("CMAKE_GENERATOR_TOOLSET");
|
|
|
|
|
data->sysroot = config.filePathValueOf("CMAKE_SYSROOT");
|
2016-08-25 14:33:44 +02:00
|
|
|
|
2021-01-14 16:38:55 +01:00
|
|
|
// Qt:
|
|
|
|
|
const FilePath qmake = qmakeFromCMakeCache(config);
|
|
|
|
|
if (!qmake.isEmpty())
|
|
|
|
|
data->qt = findOrCreateQtVersion(qmake);
|
2016-08-25 14:33:44 +02:00
|
|
|
|
2021-01-14 16:38:55 +01:00
|
|
|
// ToolChains:
|
|
|
|
|
data->toolChains = extractToolChainsFromCache(config);
|
|
|
|
|
|
|
|
|
|
qCInfo(cmInputLog) << "Offering to import" << importPath.toUserOutput();
|
|
|
|
|
result.push_back(static_cast<void *>(data.release()));
|
|
|
|
|
}
|
|
|
|
|
return result;
|
2016-08-25 14:33:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CMakeProjectImporter::matchKit(void *directoryData, const Kit *k) const
|
|
|
|
|
{
|
|
|
|
|
const DirectoryData *data = static_cast<DirectoryData *>(directoryData);
|
|
|
|
|
|
2019-02-06 12:50:51 +01:00
|
|
|
CMakeTool *cm = CMakeKitAspect::cmakeTool(k);
|
2016-08-25 14:33:44 +02:00
|
|
|
if (!cm || cm->cmakeExecutable() != data->cmakeBinary)
|
|
|
|
|
return false;
|
|
|
|
|
|
2021-07-06 09:50:01 +02:00
|
|
|
if (CMakeGeneratorKitAspect::generator(k) != data->generator
|
|
|
|
|
|| CMakeGeneratorKitAspect::extraGenerator(k) != data->extraGenerator
|
|
|
|
|
|| CMakeGeneratorKitAspect::platform(k) != data->platform
|
|
|
|
|
|| CMakeGeneratorKitAspect::toolset(k) != data->toolset)
|
2016-08-25 14:33:44 +02:00
|
|
|
return false;
|
|
|
|
|
|
2021-07-06 09:50:01 +02:00
|
|
|
if (SysRootKitAspect::sysRoot(k) != data->sysroot)
|
2016-08-25 14:33:44 +02:00
|
|
|
return false;
|
|
|
|
|
|
2019-02-06 12:50:51 +01:00
|
|
|
if (data->qt.qt && QtSupport::QtKitAspect::qtVersionId(k) != data->qt.qt->uniqueId())
|
2016-08-25 14:33:44 +02:00
|
|
|
return false;
|
|
|
|
|
|
2020-12-09 12:41:11 +01:00
|
|
|
const QList<Id> allLanguages = ToolChainManager::allLanguages();
|
2019-07-02 11:31:12 +02:00
|
|
|
for (const ToolChainDescription &tcd : data->toolChains) {
|
2020-12-09 12:41:11 +01:00
|
|
|
if (!Utils::contains(allLanguages, [&tcd](const Id& language) {return language == tcd.language;}))
|
|
|
|
|
continue;
|
2019-07-02 11:31:12 +02:00
|
|
|
ToolChain *tc = ToolChainKitAspect::toolChain(k, tcd.language);
|
2021-02-01 19:19:45 +01:00
|
|
|
if (!tc
|
|
|
|
|
|| !Utils::Environment::systemEnvironment()
|
|
|
|
|
.isSameExecutable(tc->compilerCommand().toString(),
|
|
|
|
|
tcd.compilerPath.toString())) {
|
2016-08-25 14:33:44 +02:00
|
|
|
return false;
|
2021-02-01 19:19:45 +01:00
|
|
|
}
|
2016-08-25 14:33:44 +02:00
|
|
|
}
|
|
|
|
|
|
2020-06-18 14:21:09 +02:00
|
|
|
qCDebug(cmInputLog) << k->displayName()
|
2016-08-25 14:33:44 +02:00
|
|
|
<< "matches directoryData for" << data->buildDirectory.toUserOutput();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Kit *CMakeProjectImporter::createKit(void *directoryData) const
|
|
|
|
|
{
|
|
|
|
|
const DirectoryData *data = static_cast<DirectoryData *>(directoryData);
|
|
|
|
|
|
|
|
|
|
return QtProjectImporter::createTemporaryKit(data->qt, [&data, this](Kit *k) {
|
|
|
|
|
const CMakeToolData cmtd = findOrCreateCMakeTool(data->cmakeBinary);
|
|
|
|
|
QTC_ASSERT(cmtd.cmakeTool, return);
|
|
|
|
|
if (cmtd.isTemporary)
|
2019-02-06 12:50:51 +01:00
|
|
|
addTemporaryData(CMakeKitAspect::id(), cmtd.cmakeTool->id().toSetting(), k);
|
2020-08-18 11:34:44 +01:00
|
|
|
CMakeKitAspect::setCMakeTool(k, cmtd.cmakeTool->id());
|
2016-08-25 14:33:44 +02:00
|
|
|
|
2021-07-06 09:50:01 +02:00
|
|
|
CMakeGeneratorKitAspect::setGenerator(k, data->generator);
|
|
|
|
|
CMakeGeneratorKitAspect::setExtraGenerator(k, data->extraGenerator);
|
|
|
|
|
CMakeGeneratorKitAspect::setPlatform(k, data->platform);
|
|
|
|
|
CMakeGeneratorKitAspect::setToolset(k, data->toolset);
|
2016-08-25 14:33:44 +02:00
|
|
|
|
2021-07-06 09:50:01 +02:00
|
|
|
SysRootKitAspect::setSysRoot(k, data->sysroot);
|
2016-08-25 14:33:44 +02:00
|
|
|
|
2019-07-02 11:31:12 +02:00
|
|
|
for (const ToolChainDescription &cmtcd : data->toolChains) {
|
|
|
|
|
const ToolChainData tcd = findOrCreateToolChains(cmtcd);
|
2016-08-25 14:33:44 +02:00
|
|
|
QTC_ASSERT(!tcd.tcs.isEmpty(), continue);
|
|
|
|
|
|
|
|
|
|
if (tcd.areTemporary) {
|
|
|
|
|
for (ToolChain *tc : tcd.tcs)
|
2019-02-06 12:50:51 +01:00
|
|
|
addTemporaryData(ToolChainKitAspect::id(), tc->id(), k);
|
2016-08-25 14:33:44 +02:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 12:50:51 +01:00
|
|
|
ToolChainKitAspect::setToolChain(k, tcd.tcs.at(0));
|
2016-08-25 14:33:44 +02:00
|
|
|
}
|
|
|
|
|
|
2020-06-18 14:21:09 +02:00
|
|
|
qCInfo(cmInputLog) << "Temporary Kit created.";
|
2016-08-25 14:33:44 +02:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-09 18:42:28 +01:00
|
|
|
const QList<BuildInfo> CMakeProjectImporter::buildInfoList(void *directoryData) const
|
2016-08-25 14:33:44 +02:00
|
|
|
{
|
2018-11-04 23:09:41 +01:00
|
|
|
auto data = static_cast<const DirectoryData *>(directoryData);
|
2016-08-25 14:33:44 +02:00
|
|
|
|
|
|
|
|
// create info:
|
2020-01-09 18:42:28 +01:00
|
|
|
BuildInfo info = CMakeBuildConfigurationFactory::createBuildInfo(
|
|
|
|
|
CMakeBuildConfigurationFactory::buildTypeFromByteArray(data->cmakeBuildType));
|
2019-01-29 16:51:17 +01:00
|
|
|
info.buildDirectory = data->buildDirectory;
|
|
|
|
|
info.displayName = info.typeName;
|
2016-08-25 14:33:44 +02:00
|
|
|
|
2020-12-02 19:16:40 +01:00
|
|
|
QVariantMap config;
|
|
|
|
|
config.insert(Constants::CMAKE_HOME_DIR, data->cmakeHomeDirectory.toString());
|
|
|
|
|
info.extraInfo = config;
|
|
|
|
|
|
2020-06-18 14:21:09 +02:00
|
|
|
qCDebug(cmInputLog) << "BuildInfo configured.";
|
2019-01-30 12:10:24 +01:00
|
|
|
return {info};
|
2016-08-25 14:33:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CMakeProjectImporter::CMakeToolData
|
2021-01-04 13:48:21 +01:00
|
|
|
CMakeProjectImporter::findOrCreateCMakeTool(const FilePath &cmakeToolPath) const
|
2016-08-25 14:33:44 +02:00
|
|
|
{
|
|
|
|
|
CMakeToolData result;
|
|
|
|
|
result.cmakeTool = CMakeToolManager::findByCommand(cmakeToolPath);
|
|
|
|
|
if (!result.cmakeTool) {
|
2020-06-18 14:21:09 +02:00
|
|
|
qCDebug(cmInputLog) << "Creating temporary CMakeTool for" << cmakeToolPath.toUserOutput();
|
2020-08-18 11:34:44 +01:00
|
|
|
|
|
|
|
|
UpdateGuard guard(*this);
|
|
|
|
|
|
|
|
|
|
auto newTool = std::make_unique<CMakeTool>(CMakeTool::ManualDetection, CMakeTool::createId());
|
|
|
|
|
newTool->setFilePath(cmakeToolPath);
|
|
|
|
|
newTool->setDisplayName(uniqueCMakeToolDisplayName(*newTool));
|
|
|
|
|
|
|
|
|
|
result.cmakeTool = newTool.get();
|
2016-08-25 14:33:44 +02:00
|
|
|
result.isTemporary = true;
|
2020-08-18 11:34:44 +01:00
|
|
|
CMakeToolManager::registerCMakeTool(std::move(newTool));
|
2016-08-25 14:33:44 +02:00
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CMakeProjectImporter::deleteDirectoryData(void *directoryData) const
|
|
|
|
|
{
|
|
|
|
|
delete static_cast<DirectoryData *>(directoryData);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CMakeProjectImporter::cleanupTemporaryCMake(Kit *k, const QVariantList &vl)
|
|
|
|
|
{
|
|
|
|
|
if (vl.isEmpty())
|
|
|
|
|
return; // No temporary CMake
|
|
|
|
|
QTC_ASSERT(vl.count() == 1, return);
|
2021-01-04 13:48:21 +01:00
|
|
|
CMakeKitAspect::setCMakeTool(k, Id()); // Always mark Kit as not using this Qt
|
|
|
|
|
CMakeToolManager::deregisterCMakeTool(Id::fromSetting(vl.at(0)));
|
2020-06-18 14:21:09 +02:00
|
|
|
qCDebug(cmInputLog) << "Temporary CMake tool cleaned up.";
|
2016-08-25 14:33:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CMakeProjectImporter::persistTemporaryCMake(Kit *k, const QVariantList &vl)
|
|
|
|
|
{
|
|
|
|
|
if (vl.isEmpty())
|
|
|
|
|
return; // No temporary CMake
|
|
|
|
|
QTC_ASSERT(vl.count() == 1, return);
|
|
|
|
|
const QVariant data = vl.at(0);
|
2021-01-04 13:48:21 +01:00
|
|
|
CMakeTool *tmpCmake = CMakeToolManager::findById(Id::fromSetting(data));
|
2019-02-06 12:50:51 +01:00
|
|
|
CMakeTool *actualCmake = CMakeKitAspect::cmakeTool(k);
|
2016-08-25 14:33:44 +02:00
|
|
|
|
|
|
|
|
// User changed Kit away from temporary CMake that was set up:
|
|
|
|
|
if (tmpCmake && actualCmake != tmpCmake)
|
|
|
|
|
CMakeToolManager::deregisterCMakeTool(tmpCmake->id());
|
|
|
|
|
|
2020-06-18 14:21:09 +02:00
|
|
|
qCDebug(cmInputLog) << "Temporary CMake tool made persistent.";
|
2016-08-25 14:33:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace Internal
|
|
|
|
|
} // namespace CMakeProjectManager
|
|
|
|
|
|
|
|
|
|
#ifdef WITH_TESTS
|
|
|
|
|
|
|
|
|
|
#include "cmakeprojectplugin.h"
|
|
|
|
|
|
|
|
|
|
#include <QTest>
|
|
|
|
|
|
|
|
|
|
namespace CMakeProjectManager {
|
|
|
|
|
namespace Internal {
|
|
|
|
|
|
|
|
|
|
void CMakeProjectPlugin::testCMakeProjectImporterQt_data()
|
|
|
|
|
{
|
|
|
|
|
QTest::addColumn<QStringList>("cache");
|
|
|
|
|
QTest::addColumn<QString>("expectedQmake");
|
|
|
|
|
|
|
|
|
|
QTest::newRow("Empty input")
|
|
|
|
|
<< QStringList() << QString();
|
|
|
|
|
|
|
|
|
|
QTest::newRow("Qt4")
|
2017-02-22 15:09:35 +01:00
|
|
|
<< QStringList({QString::fromLatin1("QT_QMAKE_EXECUTABLE=/usr/bin/xxx/qmake")})
|
2016-08-25 14:33:44 +02:00
|
|
|
<< "/usr/bin/xxx/qmake";
|
|
|
|
|
|
|
|
|
|
// Everything else will require Qt installations!
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CMakeProjectPlugin::testCMakeProjectImporterQt()
|
|
|
|
|
{
|
|
|
|
|
QFETCH(QStringList, cache);
|
|
|
|
|
QFETCH(QString, expectedQmake);
|
|
|
|
|
|
|
|
|
|
CMakeConfig config;
|
|
|
|
|
foreach (const QString &c, cache) {
|
|
|
|
|
const int pos = c.indexOf('=');
|
|
|
|
|
Q_ASSERT(pos > 0);
|
|
|
|
|
const QString key = c.left(pos);
|
|
|
|
|
const QString value = c.mid(pos + 1);
|
|
|
|
|
config.append(CMakeConfigItem(key.toUtf8(), value.toUtf8()));
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-04 13:48:21 +01:00
|
|
|
FilePath realQmake = qmakeFromCMakeCache(config);
|
2016-08-25 14:33:44 +02:00
|
|
|
QCOMPARE(realQmake.toString(), expectedQmake);
|
|
|
|
|
}
|
|
|
|
|
void CMakeProjectPlugin::testCMakeProjectImporterToolChain_data()
|
|
|
|
|
{
|
|
|
|
|
QTest::addColumn<QStringList>("cache");
|
|
|
|
|
QTest::addColumn<QByteArrayList>("expectedLanguages");
|
|
|
|
|
QTest::addColumn<QStringList>("expectedToolChains");
|
|
|
|
|
|
|
|
|
|
QTest::newRow("Empty input")
|
|
|
|
|
<< QStringList() << QByteArrayList() << QStringList();
|
|
|
|
|
|
|
|
|
|
QTest::newRow("Unrelated input")
|
|
|
|
|
<< QStringList("CMAKE_SOMETHING_ELSE=/tmp") << QByteArrayList() << QStringList();
|
|
|
|
|
QTest::newRow("CXX compiler")
|
2017-02-22 15:09:35 +01:00
|
|
|
<< QStringList({"CMAKE_CXX_COMPILER=/usr/bin/g++"})
|
2019-07-02 11:31:12 +02:00
|
|
|
<< QByteArrayList({"Cxx"})
|
2017-02-22 15:09:35 +01:00
|
|
|
<< QStringList({"/usr/bin/g++"});
|
2016-08-25 14:33:44 +02:00
|
|
|
QTest::newRow("CXX compiler, C compiler")
|
2017-02-22 15:09:35 +01:00
|
|
|
<< QStringList({"CMAKE_CXX_COMPILER=/usr/bin/g++", "CMAKE_C_COMPILER=/usr/bin/clang"})
|
2019-07-02 11:31:12 +02:00
|
|
|
<< QByteArrayList({"Cxx", "C"})
|
2017-02-22 15:09:35 +01:00
|
|
|
<< QStringList({"/usr/bin/g++", "/usr/bin/clang"});
|
2016-08-25 14:33:44 +02:00
|
|
|
QTest::newRow("CXX compiler, C compiler, strange compiler")
|
2017-02-22 15:09:35 +01:00
|
|
|
<< QStringList({"CMAKE_CXX_COMPILER=/usr/bin/g++",
|
2016-08-25 14:33:44 +02:00
|
|
|
"CMAKE_C_COMPILER=/usr/bin/clang",
|
2017-02-22 15:09:35 +01:00
|
|
|
"CMAKE_STRANGE_LANGUAGE_COMPILER=/tmp/strange/compiler"})
|
2019-07-02 11:31:12 +02:00
|
|
|
<< QByteArrayList({"Cxx", "C", "STRANGE_LANGUAGE"})
|
2017-02-22 15:09:35 +01:00
|
|
|
<< QStringList({"/usr/bin/g++", "/usr/bin/clang", "/tmp/strange/compiler"});
|
2016-08-25 14:33:44 +02:00
|
|
|
QTest::newRow("CXX compiler, C compiler, strange compiler (with junk)")
|
2017-02-22 15:09:35 +01:00
|
|
|
<< QStringList({"FOO=test",
|
2016-08-25 14:33:44 +02:00
|
|
|
"CMAKE_CXX_COMPILER=/usr/bin/g++",
|
|
|
|
|
"CMAKE_BUILD_TYPE=debug",
|
|
|
|
|
"CMAKE_C_COMPILER=/usr/bin/clang",
|
|
|
|
|
"SOMETHING_COMPILER=/usr/bin/something",
|
|
|
|
|
"CMAKE_STRANGE_LANGUAGE_COMPILER=/tmp/strange/compiler",
|
2017-02-22 15:09:35 +01:00
|
|
|
"BAR=more test"})
|
2019-07-02 11:31:12 +02:00
|
|
|
<< QByteArrayList({"Cxx", "C", "STRANGE_LANGUAGE"})
|
2017-02-22 15:09:35 +01:00
|
|
|
<< QStringList({"/usr/bin/g++", "/usr/bin/clang", "/tmp/strange/compiler"});
|
2016-08-25 14:33:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CMakeProjectPlugin::testCMakeProjectImporterToolChain()
|
|
|
|
|
{
|
|
|
|
|
QFETCH(QStringList, cache);
|
|
|
|
|
QFETCH(QByteArrayList, expectedLanguages);
|
|
|
|
|
QFETCH(QStringList, expectedToolChains);
|
|
|
|
|
|
|
|
|
|
QCOMPARE(expectedLanguages.count(), expectedToolChains.count());
|
|
|
|
|
|
|
|
|
|
CMakeConfig config;
|
|
|
|
|
foreach (const QString &c, cache) {
|
|
|
|
|
const int pos = c.indexOf('=');
|
|
|
|
|
Q_ASSERT(pos > 0);
|
|
|
|
|
const QString key = c.left(pos);
|
|
|
|
|
const QString value = c.mid(pos + 1);
|
|
|
|
|
config.append(CMakeConfigItem(key.toUtf8(), value.toUtf8()));
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-02 11:31:12 +02:00
|
|
|
const QVector<ToolChainDescription> tcs = extractToolChainsFromCache(config);
|
2016-08-25 14:33:44 +02:00
|
|
|
QCOMPARE(tcs.count(), expectedLanguages.count());
|
|
|
|
|
for (int i = 0; i < tcs.count(); ++i) {
|
2019-07-02 11:31:12 +02:00
|
|
|
QCOMPARE(tcs.at(i).language, expectedLanguages.at(i));
|
2016-08-25 14:33:44 +02:00
|
|
|
QCOMPARE(tcs.at(i).compilerPath.toString(), expectedToolChains.at(i));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace Internal
|
|
|
|
|
} // namespace CMakeProjectManager
|
|
|
|
|
|
|
|
|
|
#endif
|