File indexing completed on 2024-05-05 16:46:16
0001 /* 0002 SPDX-License-Identifier: GPL-2.0-or-later 0003 */ 0004 0005 #include "qmakeconfig.h" 0006 0007 #include <QDir> 0008 #include <QMutex> 0009 #include <QFileInfo> 0010 #include <QStandardPaths> 0011 0012 #include <KConfigGroup> 0013 #include <KProcess> 0014 0015 #include <interfaces/iproject.h> 0016 #include <util/path.h> 0017 #include <debug.h> 0018 0019 const char QMakeConfig::CONFIG_GROUP[] = "QMake_Builder"; 0020 0021 // TODO: migrate to more generic & consistent key term "QMake_Executable" 0022 const char QMakeConfig::QMAKE_EXECUTABLE[] = "QMake_Binary"; 0023 const char QMakeConfig::BUILD_FOLDER[] = "Build_Folder"; 0024 const char QMakeConfig::INSTALL_PREFIX[] = "Install_Prefix"; 0025 const char QMakeConfig::EXTRA_ARGUMENTS[] = "Extra_Arguments"; 0026 const char QMakeConfig::BUILD_TYPE[] = "Build_Type"; 0027 const char QMakeConfig::ALL_BUILDS[] = "All_Builds"; 0028 0029 using namespace KDevelop; 0030 0031 /// NOTE: KConfig is not thread safe 0032 QMutex s_buildDirMutex; 0033 0034 bool QMakeConfig::isConfigured(const IProject* project) 0035 { 0036 QMutexLocker lock(&s_buildDirMutex); 0037 KConfigGroup cg(project->projectConfiguration(), CONFIG_GROUP); 0038 return cg.exists() && cg.hasKey(QMAKE_EXECUTABLE) && cg.hasKey(BUILD_FOLDER); 0039 } 0040 0041 Path QMakeConfig::buildDirFromSrc(const IProject* project, const Path& srcDir) 0042 { 0043 QMutexLocker lock(&s_buildDirMutex); 0044 KConfigGroup cg(project->projectConfiguration(), QMakeConfig::CONFIG_GROUP); 0045 Path buildDir = Path(cg.readEntry(QMakeConfig::BUILD_FOLDER, QString())); 0046 lock.unlock(); 0047 0048 if (buildDir.isValid()) { 0049 buildDir.addPath(project->path().relativePath(srcDir)); 0050 } 0051 return buildDir; 0052 } 0053 0054 QString QMakeConfig::qmakeExecutable(const IProject* project) 0055 { 0056 QMutexLocker lock(&s_buildDirMutex); 0057 QString exe; 0058 if (project) { 0059 KSharedConfig::Ptr cfg = project->projectConfiguration(); 0060 KConfigGroup group(cfg.data(), CONFIG_GROUP); 0061 if (group.hasKey(QMAKE_EXECUTABLE)) { 0062 exe = group.readEntry(QMAKE_EXECUTABLE, QString()); 0063 QFileInfo info(exe); 0064 if (!info.exists() || !info.isExecutable()) { 0065 qCWarning(KDEV_QMAKE) << "bad QMake configured for project " << project->path().toUrl() << ":" << exe; 0066 exe.clear(); 0067 } 0068 } 0069 } 0070 if (exe.isEmpty()) { 0071 exe = QStandardPaths::findExecutable(QStringLiteral("qmake")); 0072 } 0073 if (exe.isEmpty()) { 0074 exe = QStandardPaths::findExecutable(QStringLiteral("qmake-qt5")); 0075 } 0076 if (exe.isEmpty()) { 0077 exe = QStandardPaths::findExecutable(QStringLiteral("qmake-qt4")); 0078 } 0079 Q_ASSERT(!exe.isEmpty()); 0080 return exe; 0081 } 0082 0083 QHash<QString, QString> QMakeConfig::queryQMake(const QString& qmakeExecutable, const QStringList& args) 0084 { 0085 QHash<QString, QString> hash; 0086 KProcess p; 0087 p.setOutputChannelMode(KProcess::OnlyStdoutChannel); 0088 p << qmakeExecutable << QStringLiteral("-query") << args; 0089 0090 const int rc = p.execute(); 0091 if (rc != 0) { 0092 qCWarning(KDEV_QMAKE) << "failed to execute qmake query " << p.program().join(QLatin1Char(' ')) << "return code was:" << rc; 0093 return QHash<QString, QString>(); 0094 } 0095 0096 // TODO: Qt 5.5: Use QTextStream::readLineInto 0097 QTextStream stream(&p); 0098 while (!stream.atEnd()) { 0099 const QString line = stream.readLine(); 0100 const int colon = line.indexOf(QLatin1Char(':')); 0101 if (colon == -1) { 0102 continue; 0103 } 0104 0105 const auto key = line.left(colon); 0106 const auto value = line.mid(colon + 1); 0107 hash.insert(key, value); 0108 } 0109 qCDebug(KDEV_QMAKE) << "Ran qmake (" << p.program().join(QLatin1Char(' ')) << "), found:" << hash; 0110 return hash; 0111 } 0112 0113 QString QMakeConfig::findBasicMkSpec(const QHash<QString, QString>& qmakeVars) 0114 { 0115 QStringList paths; 0116 if (qmakeVars.contains(QStringLiteral("QMAKE_MKSPECS"))) { 0117 // qt4 0118 const auto mkspecDirs = qmakeVars[QStringLiteral("QMAKE_MKSPECS")].split(QDir::listSeparator()); 0119 for (const auto& dir : mkspecDirs) { 0120 paths << dir + QLatin1String("/default/qmake.conf"); 0121 } 0122 } else if (!qmakeVars.contains(QStringLiteral("QMAKE_MKSPECS")) && qmakeVars.contains(QStringLiteral("QMAKE_SPEC"))) { 0123 QString path; 0124 // qt5 doesn't have the MKSPECS nor default anymore 0125 // let's try to look up the mkspec path ourselves, 0126 // see QMakeEvaluator::updateMkspecPaths() in QMake source code as reference 0127 if (qmakeVars.contains(QStringLiteral("QT_HOST_DATA/src"))) { 0128 // >=qt5.2: since 0d463c05fc4f2e79e5a4e5a5382a1e6ed5d2615e (in Qt5 qtbase repository) 0129 // mkspecs are no longer copied to the build directory. 0130 // instead, we have to look them up in the source directory. 0131 // this commit also introduced the key 'QT_HOST_DATA/src' which we use here 0132 path = qmakeVars[QStringLiteral("QT_HOST_DATA/src")]; 0133 } else if (qmakeVars.contains(QStringLiteral("QT_HOST_DATA"))) { 0134 // cross compilation 0135 path = qmakeVars[QStringLiteral("QT_HOST_DATA")]; 0136 } else { 0137 Q_ASSERT(qmakeVars.contains(QStringLiteral("QT_INSTALL_PREFIX"))); 0138 path = qmakeVars[QStringLiteral("QT_INSTALL_PREFIX")]; 0139 } 0140 path += QLatin1String("/mkspecs/") + qmakeVars[QStringLiteral("QMAKE_SPEC")] + QLatin1String("/qmake.conf"); 0141 paths << path; 0142 } 0143 0144 for (const auto& path : std::as_const(paths)) { 0145 QFileInfo fi(path); 0146 if (fi.exists()) 0147 return fi.absoluteFilePath(); 0148 } 0149 0150 return QString(); 0151 }