File indexing completed on 2024-05-05 16:46:16

0001 /*
0002     SPDX-FileCopyrightText: 2007 Andreas Pakulat <apaku@gmx.de>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "qmakefile.h"
0008 
0009 #include <QDir>
0010 #include <QFileInfo>
0011 
0012 #include <debug.h>
0013 #include "parser/ast.h"
0014 #include "qmakedriver.h"
0015 
0016 #define ifDebug(x)
0017 
0018 //@TODO: Make the globbing stuff work with drives on win32
0019 
0020 void resolveShellGlobbingInternal(QStringList& entries, const QStringList& segments, const QFileInfo& match, QDir& dir,
0021                                   int offset);
0022 
0023 QStringList resolveShellGlobbingInternal(const QStringList& segments, QDir& dir, int offset = 0)
0024 {
0025     if (offset >= segments.size()) {
0026         return QStringList();
0027     }
0028 
0029     const QString& pathPattern = segments.at(offset);
0030 
0031     QStringList entries;
0032     if (pathPattern.contains(QLatin1Char('*')) ||
0033         pathPattern.contains(QLatin1Char('?')) ||
0034         pathPattern.contains(QLatin1Char('['))) {
0035         // pattern contains globbing chars
0036         const auto dirEntries =
0037             dir.entryInfoList(QStringList() << pathPattern, QDir::AllEntries | QDir::NoDotAndDotDot, QDir::Unsorted);
0038         for (const auto& match : dirEntries) {
0039             resolveShellGlobbingInternal(entries, segments, match, dir, offset);
0040         }
0041     } else {
0042         // pattern is "simple" hence be fast, but make sure the file exists
0043         QFileInfo info(dir.filePath(pathPattern));
0044         if (info.exists()) {
0045             resolveShellGlobbingInternal(entries, segments, info, dir, offset);
0046         }
0047     }
0048 
0049     return entries;
0050 }
0051 
0052 void resolveShellGlobbingInternal(QStringList& entries, const QStringList& segments, const QFileInfo& match, QDir& dir,
0053                                   int offset)
0054 {
0055     if (match.isDir() && offset + 1 < segments.size()) {
0056         dir.cd(match.fileName());
0057         entries += resolveShellGlobbingInternal(segments, dir, offset + 1);
0058         dir.cdUp();
0059     } else {
0060         entries << match.canonicalFilePath();
0061     }
0062 }
0063 
0064 QStringList resolveShellGlobbingInternal(const QString& pattern, const QString& dir)
0065 {
0066     if (pattern.isEmpty()) {
0067         return QStringList();
0068     }
0069 
0070     QDir dir_(pattern.startsWith(QLatin1Char('/')) ? QStringLiteral("/") : dir);
0071 
0072     // break up pattern into path segments
0073     return resolveShellGlobbingInternal(pattern.split(QLatin1Char('/'), Qt::SkipEmptyParts), dir_);
0074 }
0075 
0076 QMakeFile::QMakeFile(QString file)
0077     : m_ast(nullptr)
0078     , m_projectFile(std::move(file))
0079     , m_project(nullptr)
0080 {
0081     Q_ASSERT(!m_projectFile.isEmpty());
0082 }
0083 
0084 bool QMakeFile::read()
0085 {
0086     Q_ASSERT(!m_projectFile.isEmpty());
0087     QFileInfo fi(m_projectFile);
0088     ifDebug(qCDebug(KDEV_QMAKE) << "Is" << m_projectFile << "a dir?" << fi.isDir();) if (fi.isDir())
0089     {
0090         QDir dir(m_projectFile);
0091         QStringList l = dir.entryList(QStringList() << QStringLiteral("*.pro"));
0092 
0093         QString projectfile;
0094 
0095         if (!l.count() || (l.count() && l.indexOf(fi.baseName() + QLatin1String(".pro")) != -1)) {
0096             projectfile = fi.baseName() + QLatin1String(".pro");
0097         } else {
0098             projectfile = l.first();
0099         }
0100         m_projectFile += QLatin1Char('/') + projectfile;
0101     }
0102     QMake::Driver d;
0103     d.readFile(m_projectFile);
0104 
0105     if (!d.parse(&m_ast)) {
0106         qCWarning(KDEV_QMAKE) << "Couldn't parse project:" << m_projectFile;
0107         delete m_ast;
0108         m_ast = nullptr;
0109         m_projectFile = QString();
0110         return false;
0111     } else {
0112         ifDebug(qCDebug(KDEV_QMAKE) << "found ast:" << m_ast->statements.count();) QMakeFileVisitor visitor(this, this);
0113         /// TODO: cleanup, re-use m_variableValues directly in the visitor
0114         visitor.setVariables(m_variableValues);
0115         m_variableValues = visitor.visitFile(m_ast);
0116         ifDebug(qCDebug(KDEV_QMAKE) << "Variables found:" << m_variableValues;)
0117     }
0118     return true;
0119 }
0120 
0121 QMakeFile::~QMakeFile()
0122 {
0123     delete m_ast;
0124     m_ast = nullptr;
0125 }
0126 
0127 QString QMakeFile::absoluteDir() const
0128 {
0129     return QFileInfo(m_projectFile).absoluteDir().canonicalPath();
0130 }
0131 
0132 QString QMakeFile::absoluteFile() const
0133 {
0134     return m_projectFile;
0135 }
0136 
0137 QMake::ProjectAST* QMakeFile::ast() const
0138 {
0139     return m_ast;
0140 }
0141 
0142 QStringList QMakeFile::variables() const
0143 {
0144     return m_variableValues.keys();
0145 }
0146 
0147 QStringList QMakeFile::variableValues(const QString& variable) const
0148 {
0149     return m_variableValues.value(variable, QStringList());
0150 }
0151 
0152 bool QMakeFile::containsVariable(const QString& variable) const
0153 {
0154     return m_variableValues.contains(variable);
0155 }
0156 
0157 QMakeFile::VariableMap QMakeFile::variableMap() const
0158 {
0159     return m_variableValues;
0160 }
0161 
0162 QStringList QMakeFile::resolveVariable(const QString& variable, VariableInfo::VariableType type) const
0163 {
0164     if (type == VariableInfo::QMakeVariable) {
0165         const auto variableValueIt = m_variableValues.find(variable);
0166         if (variableValueIt != m_variableValues.end()) {
0167             return *variableValueIt;
0168         }
0169     }
0170 
0171     qCWarning(KDEV_QMAKE) << "unresolved variable:" << variable << "type:" << type;
0172     return QStringList();
0173 }
0174 
0175 QStringList QMakeFile::resolveShellGlobbing(const QString& pattern, const QString& base) const
0176 {
0177     return resolveShellGlobbingInternal(pattern, base.isEmpty() ? absoluteDir() : base);
0178 }
0179 
0180 QString QMakeFile::resolveToSingleFileName(const QString& file, const QString& base) const
0181 {
0182     QStringList l = resolveFileName(file, base);
0183     if (l.isEmpty())
0184         return QString();
0185     else
0186         return l.first();
0187 }
0188 
0189 QStringList QMakeFile::resolveFileName(const QString& file, const QString& base) const
0190 {
0191     return resolveShellGlobbing(file, base);
0192 }
0193 
0194 void QMakeFile::setProject(KDevelop::IProject* project)
0195 {
0196     m_project = project;
0197 }
0198 
0199 KDevelop::IProject* QMakeFile::project() const
0200 {
0201     return m_project;
0202 }