forked from qt-creator/qt-creator
Add AutotoolsProjectManager plugin
Change-Id: Icbc8d105a3ffea2bf705d18e3413f6b3487ccfd3 Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@nokia.com> Reviewed-by: hjk <qthjk@ovi.com> Reviewed-by: Leena Miettinen <riitta-leena.miettinen@nokia.com>
This commit is contained in:
committed by
hjk
parent
442f2d6972
commit
6445c99ad2
454
src/plugins/autotoolsprojectmanager/makefileparser.cpp
Normal file
454
src/plugins/autotoolsprojectmanager/makefileparser.cpp
Normal file
@@ -0,0 +1,454 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2010-2011 Openismus GmbH.
|
||||
** Authors: Peter Penz (ppenz@openismus.com)
|
||||
** Patricia Santana Cruz (patriciasantanacruz@gmail.com)
|
||||
**
|
||||
** Contact: Nokia Corporation (info@qt.nokia.com)
|
||||
**
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
**
|
||||
** This file may be used under the terms of the GNU Lesser General Public
|
||||
** License version 2.1 as published by the Free Software Foundation and
|
||||
** appearing in the file LICENSE.LGPL included in the packaging of this file.
|
||||
** Please review the following information to ensure the GNU Lesser General
|
||||
** Public License version 2.1 requirements will be met:
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** Other Usage
|
||||
**
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at info@qt.nokia.com.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#include "makefileparser.h"
|
||||
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
#include <QtCore/QFile>
|
||||
#include <QtCore/QFileInfoList>
|
||||
#include <QtCore/QMutexLocker>
|
||||
|
||||
using namespace AutotoolsProjectManager::Internal;
|
||||
|
||||
MakefileParser::MakefileParser(const QString &makefile) :
|
||||
QObject(),
|
||||
m_success(false),
|
||||
m_cancel(false),
|
||||
m_mutex(),
|
||||
m_makefile(makefile),
|
||||
m_executable(),
|
||||
m_sources(),
|
||||
m_makefiles(),
|
||||
m_includePaths(),
|
||||
m_line(),
|
||||
m_textStream()
|
||||
{
|
||||
}
|
||||
|
||||
MakefileParser::~MakefileParser()
|
||||
{
|
||||
}
|
||||
|
||||
bool MakefileParser::parse()
|
||||
{
|
||||
m_mutex.lock();
|
||||
m_cancel = false;
|
||||
m_mutex.unlock(),
|
||||
|
||||
m_success = true;
|
||||
m_executable.clear();
|
||||
m_sources.clear();
|
||||
m_makefiles.clear();
|
||||
|
||||
QFile *file = new QFile(m_makefile);
|
||||
if (!file->open(QIODevice::ReadOnly | QIODevice::Text))
|
||||
return false;
|
||||
|
||||
QFileInfo info(m_makefile);
|
||||
m_makefiles.append(info.fileName());
|
||||
|
||||
emit status(tr("Parsing %1 in directory %2").arg(info.fileName()).arg(info.absolutePath()));
|
||||
|
||||
m_textStream.setDevice(file);
|
||||
|
||||
do {
|
||||
m_line = m_textStream.readLine();
|
||||
switch (topTarget()) {
|
||||
case AmDefaultSourceExt: parseDefaultSourceExtensions(); break;
|
||||
case BinPrograms: parseBinPrograms(); break;
|
||||
case BuiltSources: break; // TODO: Add to m_sources?
|
||||
case Sources: parseSources(); break;
|
||||
case SubDirs: parseSubDirs(); break;
|
||||
case Undefined:
|
||||
default: break;
|
||||
}
|
||||
} while (!m_line.isNull());
|
||||
|
||||
parseIncludePaths();
|
||||
|
||||
return m_success;
|
||||
}
|
||||
|
||||
QStringList MakefileParser::sources() const
|
||||
{
|
||||
return m_sources;
|
||||
}
|
||||
|
||||
QStringList MakefileParser::makefiles() const
|
||||
{
|
||||
return m_makefiles;
|
||||
}
|
||||
|
||||
QString MakefileParser::executable() const
|
||||
{
|
||||
return m_executable;
|
||||
}
|
||||
|
||||
QStringList MakefileParser::includePaths() const
|
||||
{
|
||||
return m_includePaths;
|
||||
}
|
||||
|
||||
void MakefileParser::cancel()
|
||||
{
|
||||
QMutexLocker locker(&m_mutex);
|
||||
m_cancel = true;
|
||||
}
|
||||
|
||||
bool MakefileParser::isCanceled() const
|
||||
{
|
||||
QMutexLocker locker(&m_mutex);
|
||||
return m_cancel;
|
||||
}
|
||||
|
||||
MakefileParser::TopTarget MakefileParser::topTarget() const
|
||||
{
|
||||
TopTarget topTarget = Undefined;
|
||||
|
||||
const QString line = m_line.simplified();
|
||||
if (!line.isEmpty() && !line.startsWith(QChar('#'))) {
|
||||
// TODO: Check how many fixed strings like AM_DEFAULT_SOURCE_EXT will
|
||||
// be needed vs. variable strings like _SOURCES. Dependend on this a
|
||||
// more clever way than this (expensive) if-cascading might be done.
|
||||
if (line.startsWith(QLatin1String("AM_DEFAULT_SOURCE_EXT =")))
|
||||
topTarget = AmDefaultSourceExt;
|
||||
else if (line.startsWith(QLatin1String("bin_PROGRAMS =")))
|
||||
topTarget = BinPrograms;
|
||||
else if (line.startsWith(QLatin1String("BUILT_SOURCES =")))
|
||||
topTarget = BuiltSources;
|
||||
else if (line.contains(QLatin1String("SUBDIRS =")))
|
||||
topTarget = SubDirs;
|
||||
else if (line.contains(QLatin1String("_SOURCES =")))
|
||||
topTarget = Sources;
|
||||
}
|
||||
|
||||
return topTarget;
|
||||
}
|
||||
|
||||
void MakefileParser::parseBinPrograms()
|
||||
{
|
||||
QTC_ASSERT(m_line.contains(QLatin1String("bin_PROGRAMS")), return);
|
||||
const QStringList binPrograms = targetValues();
|
||||
|
||||
// TODO: are multiple values possible?
|
||||
if (binPrograms.size() == 1) {
|
||||
QFileInfo info(binPrograms.first());
|
||||
m_executable = info.fileName();
|
||||
}
|
||||
}
|
||||
|
||||
void MakefileParser::parseSources()
|
||||
{
|
||||
QTC_ASSERT(m_line.contains(QLatin1String("_SOURCES")), return);
|
||||
|
||||
bool hasVariables = false;
|
||||
m_sources.append(targetValues(&hasVariables));
|
||||
|
||||
// Skip parsing of Makefile.am for getting the sub directories,
|
||||
// as variables have been used. As fallback all sources will be added.
|
||||
if (hasVariables)
|
||||
addAllSources();
|
||||
|
||||
// Duplicates might be possible in combination with 'AM_DEFAULT_SOURCE_EXT ='
|
||||
m_sources.removeDuplicates();
|
||||
|
||||
// TODO: Definitions like "SOURCES = ../src.cpp" are ignored currently.
|
||||
// This case must be handled correctly in MakefileParser::parseSubDirs(),
|
||||
// where the current sub directory must be shortened.
|
||||
QStringList::iterator it = m_sources.begin();
|
||||
while (it != m_sources.end()) {
|
||||
if ((*it).startsWith(QLatin1String("..")))
|
||||
it = m_sources.erase(it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
void MakefileParser::parseDefaultSourceExtensions()
|
||||
{
|
||||
QTC_ASSERT(m_line.contains(QLatin1String("AM_DEFAULT_SOURCE_EXT")), return);
|
||||
const QStringList extensions = targetValues();
|
||||
if (extensions.isEmpty()) {
|
||||
m_success = false;
|
||||
return;
|
||||
}
|
||||
|
||||
QFileInfo info(m_makefile);
|
||||
const QString dirName = info.absolutePath();
|
||||
m_sources.append(directorySources(dirName, extensions));
|
||||
|
||||
// Duplicates might be possible in combination with '_SOURCES ='
|
||||
m_sources.removeDuplicates();
|
||||
}
|
||||
|
||||
void MakefileParser::parseSubDirs()
|
||||
{
|
||||
QTC_ASSERT(m_line.contains(QLatin1String("SUBDIRS")), return);
|
||||
if (isCanceled()) {
|
||||
m_success = false;
|
||||
return;
|
||||
}
|
||||
|
||||
QFileInfo info(m_makefile);
|
||||
const QString path = info.absolutePath();
|
||||
const QString makefileName = info.fileName();
|
||||
|
||||
bool hasVariables = false;
|
||||
QStringList subDirs = targetValues(&hasVariables);
|
||||
if (hasVariables) {
|
||||
// Skip parsing of Makefile.am for getting the sub directories,
|
||||
// as variables have been used. As fallback all sources will be added.
|
||||
addAllSources();
|
||||
return;
|
||||
}
|
||||
|
||||
// If the SUBDIRS values contain a '.' or a variable like $(test),
|
||||
// all the sub directories of the current folder must get parsed.
|
||||
bool hasDotSubDir = false;
|
||||
QStringList::iterator it = subDirs.begin();
|
||||
while (it != subDirs.end()) {
|
||||
// Erase all entries that represent a '.'
|
||||
if ((*it) == QChar('.')) {
|
||||
hasDotSubDir = true;
|
||||
it = subDirs.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
if (hasDotSubDir) {
|
||||
// Add all sub directories of the current folder
|
||||
QDir dir(path);
|
||||
dir.setFilter(QDir::Dirs | QDir::NoDotAndDotDot);
|
||||
foreach (const QFileInfo& info, dir.entryInfoList()) {
|
||||
subDirs.append(info.fileName());
|
||||
}
|
||||
}
|
||||
subDirs.removeDuplicates();
|
||||
|
||||
// Delegate the parsing of all sub directories to a local
|
||||
// makefile parser and merge the results
|
||||
foreach (const QString& subDir, subDirs) {
|
||||
const QChar slash('/');
|
||||
const QString subDirMakefile = path + slash + subDir
|
||||
+ slash + makefileName;
|
||||
|
||||
// Parse sub directory
|
||||
QFile *file = new QFile(subDirMakefile);
|
||||
|
||||
// Don't try to parse a file, that might not exist (e. g.
|
||||
// if SUBDIRS specifies a 'po' directory).
|
||||
if (!file->exists())
|
||||
continue;
|
||||
|
||||
MakefileParser parser(subDirMakefile);
|
||||
connect(&parser, SIGNAL(status(QString)), this, SIGNAL(status(QString)));
|
||||
const bool success = parser.parse();
|
||||
|
||||
// Don't return, try to parse as many sub directories
|
||||
// as possible
|
||||
if (!success)
|
||||
m_success = false;
|
||||
|
||||
m_makefiles.append(subDir + slash + makefileName);
|
||||
|
||||
// Append the sources of the sub directory to the
|
||||
// current sources
|
||||
foreach (const QString& source, parser.sources())
|
||||
m_sources.append(subDir + slash + source);
|
||||
|
||||
// Duplicates might be possible in combination with several
|
||||
// "..._SUBDIRS" targets
|
||||
m_makefiles.removeDuplicates();
|
||||
m_sources.removeDuplicates();
|
||||
}
|
||||
|
||||
if (subDirs.isEmpty())
|
||||
m_success = false;
|
||||
}
|
||||
|
||||
QStringList MakefileParser::directorySources(const QString &directory,
|
||||
const QStringList &extensions)
|
||||
{
|
||||
if (isCanceled()) {
|
||||
m_success = false;
|
||||
return QStringList();
|
||||
}
|
||||
|
||||
emit status(tr("Parsing directory %1").arg(directory));
|
||||
|
||||
QStringList list; // return value
|
||||
|
||||
QDir dir(directory);
|
||||
dir.setFilter(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
|
||||
|
||||
const QFileInfoList infos = dir.entryInfoList();
|
||||
foreach (const QFileInfo& info, infos) {
|
||||
if (info.isDir()) {
|
||||
// Append recursively sources from the sub directory
|
||||
const QStringList subDirSources = directorySources(info.absoluteFilePath(),
|
||||
extensions);
|
||||
const QString dirPath = info.fileName();
|
||||
foreach (const QString& subDirSource, subDirSources)
|
||||
list.append(dirPath + QChar('/') + subDirSource);
|
||||
} else {
|
||||
// Check whether the file matches to an extension
|
||||
foreach (const QString& extension, extensions) {
|
||||
if (info.fileName().endsWith(extension)) {
|
||||
list.append(info.fileName());
|
||||
appendHeader(list, dir, info.baseName());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
QStringList MakefileParser::targetValues(bool *hasVariables)
|
||||
{
|
||||
QStringList values;
|
||||
if (hasVariables != 0)
|
||||
*hasVariables = false;
|
||||
|
||||
const int index = m_line.indexOf(QChar('='));
|
||||
if (index < 0) {
|
||||
m_success = false;
|
||||
return QStringList();
|
||||
}
|
||||
|
||||
m_line.remove(0, index + 1); // remove the 'target = ' prefix
|
||||
|
||||
bool endReached = false;
|
||||
do {
|
||||
m_line = m_line.simplified();
|
||||
|
||||
// Get all values of a line separated by spaces.
|
||||
// Values representing a variable like $(value) get
|
||||
// removed currently.
|
||||
QStringList lineValues = m_line.split(QChar(' '));
|
||||
QStringList::iterator it = lineValues.begin();
|
||||
while (it != lineValues.end()) {
|
||||
if ((*it).startsWith(QLatin1String("$("))) {
|
||||
it = lineValues.erase(it);
|
||||
if (hasVariables != 0)
|
||||
*hasVariables = true;
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
endReached = lineValues.isEmpty();
|
||||
if (!endReached) {
|
||||
const QChar backSlash('\\');
|
||||
QString last = lineValues.last();
|
||||
if (last.endsWith(backSlash)) {
|
||||
// The last value contains a backslash. Remove the
|
||||
// backslash and replace the last value.
|
||||
lineValues.pop_back();
|
||||
last.remove(backSlash);
|
||||
if (!last.isEmpty())
|
||||
lineValues.push_back(last);
|
||||
|
||||
values.append(lineValues);
|
||||
m_line = m_textStream.readLine();
|
||||
endReached = m_line.isNull();
|
||||
} else {
|
||||
values.append(lineValues);
|
||||
endReached = true;
|
||||
}
|
||||
}
|
||||
} while (!endReached);
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
void MakefileParser::appendHeader(QStringList &list, const QDir &dir, const QString &fileName)
|
||||
{
|
||||
const char *const headerExtensions[] = { ".h", ".hh", ".hg", ".hxx", ".hpp", 0 };
|
||||
int i = 0;
|
||||
while (headerExtensions[i] != 0) {
|
||||
const QString headerFile = fileName + QLatin1String(headerExtensions[i]);
|
||||
QFileInfo fileInfo(dir, headerFile);
|
||||
if (fileInfo.exists())
|
||||
list.append(headerFile);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
void MakefileParser::addAllSources()
|
||||
{
|
||||
QStringList extensions;
|
||||
extensions << QLatin1String(".c")
|
||||
<< QLatin1String(".cpp")
|
||||
<< QLatin1String(".cc")
|
||||
<< QLatin1String(".cxx")
|
||||
<< QLatin1String(".ccg");
|
||||
QFileInfo info(m_makefile);
|
||||
m_sources.append(directorySources(info.absolutePath(), extensions));
|
||||
m_sources.removeDuplicates();
|
||||
}
|
||||
|
||||
void MakefileParser::parseIncludePaths()
|
||||
{
|
||||
QFileInfo info(m_makefile);
|
||||
const QString dirName = info.absolutePath();
|
||||
|
||||
QFile *file = new QFile(dirName + QLatin1String("/Makefile"));
|
||||
if (!file->open(QIODevice::ReadOnly | QIODevice::Text))
|
||||
return;
|
||||
|
||||
// TODO: The parsing is done very poor. Comments are ignored and targets
|
||||
// are ignored too. Whether it is worth to improve this, depends on whether
|
||||
// we want to parse the generated Makefile at all or whether we want to
|
||||
// improve the Makefile.am parsing to be aware of variables.
|
||||
QTextStream textStream(file);
|
||||
QString line;
|
||||
do {
|
||||
line = textStream.readLine();
|
||||
QStringList terms = line.split(QLatin1Char(' '), QString::SkipEmptyParts);
|
||||
foreach (const QString &term, terms) {
|
||||
if (term.startsWith(QLatin1String("-I"))) {
|
||||
QString includePath = term.right(term.length() - 2); // remove the "-I"
|
||||
if (includePath == QLatin1String("."))
|
||||
includePath = dirName;
|
||||
if (!includePath.isEmpty())
|
||||
m_includePaths += includePath;
|
||||
}
|
||||
}
|
||||
} while (!line.isNull());
|
||||
|
||||
m_includePaths.removeDuplicates();
|
||||
}
|
||||
Reference in New Issue
Block a user