diff --git a/src/plugins/qt4projectmanager/qt-maemo/maemopackagecontents.cpp b/src/plugins/qt4projectmanager/qt-maemo/maemopackagecontents.cpp index 774f52a2811c54bb2d8c7b803aa9d2991c0a3f4b..a0c08a3d78420195003dc08cab67750bc90b4bca 100644 --- a/src/plugins/qt4projectmanager/qt-maemo/maemopackagecontents.cpp +++ b/src/plugins/qt4projectmanager/qt-maemo/maemopackagecontents.cpp @@ -32,22 +32,31 @@ #include "maemopackagecreationstep.h" #include "maemotoolchain.h" +#include <qt4projectmanager/profilereader.h> #include <qt4projectmanager/qt4buildconfiguration.h> #include <qt4projectmanager/qt4project.h> #include <qt4projectmanager/qt4target.h> -#include <QtCore/QDir> +#include <profile.h> +#include <prowriter.h> + +#include <QtCore/QCryptographicHash> +#include <QtCore/QFile> #include <QtCore/QFileInfo> namespace { - const char * const MODIFIED_KEY - = "Qt4ProjectManager.BuildStep.MaemoPackage.Modified"; - const char * const REMOTE_EXE_DIR_KEY - = "Qt4ProjectManager.BuildStep.MaemoPackage.RemoteExeDir"; - const char * const LOCAL_FILES_KEY - = "Qt4ProjectManager.BuildStep.MaemoPackage.LocalFiles"; - const char * const REMOTE_DIRS_KEY - = "Qt4ProjectManager.BuildStep.MaemoPackage.RemoteDirs"; + QString pathVar(const QString &var) + { + return var + QLatin1String(".path"); + } + + QString filesVar(const QString &var) + { + return var + QLatin1String(".files"); + } + + const QLatin1String InstallsVar("INSTALLS"); + const QLatin1String TargetVar("target"); } namespace Qt4ProjectManager { @@ -56,43 +65,161 @@ namespace Internal { MaemoPackageContents::MaemoPackageContents(MaemoPackageCreationStep *packageStep) : QAbstractTableModel(packageStep), m_packageStep(packageStep), - m_modified(true) + m_proFileOption(new ProFileOption), + m_proFileReader(new ProFileReader(m_proFileOption.data())), + m_modified(false), + m_proFile(0) +{ +} + +MaemoPackageContents::~MaemoPackageContents() {} + +bool MaemoPackageContents::init() +{ + return m_proFile ? true : buildModel(); +} + +bool MaemoPackageContents::buildModel() const { + m_deployables.clear(); + const Qt4ProFileNode * const proFileNode = m_packageStep + ->qt4BuildConfiguration()->qt4Target()->qt4Project()->rootProjectNode(); + if (m_proFileName.isEmpty()) { + m_proFileName = proFileNode->path(); + m_proDir = QFileInfo(m_proFileName).dir(); + } + + resetProFileContents(); + if (!m_proFile) + return false; + + const QStringList elemList = m_proFileReader->values(InstallsVar, m_proFile); + bool targetFound = false; + foreach (const QString &elem, elemList) { + const QStringList paths + = m_proFileReader->values(pathVar(elem), m_proFile); + if (paths.count() != 1) { + qWarning("Error: Variable %s has %d values.", + qPrintable(pathVar(elem)), paths.count()); + continue; + } + + const QStringList files + = m_proFileReader->values(filesVar(elem), m_proFile); + if (files.isEmpty() && elem != TargetVar) { + qWarning("Error: Variable %s has no RHS.", + qPrintable(filesVar(elem))); + continue; + } + + if (elem == TargetVar) { + m_deployables.prepend(Deployable(m_packageStep->localExecutableFilePath(), + paths.first())); + targetFound = true; + } else { + foreach (const QString &file, files) + m_deployables << Deployable(cleanPath(file), paths.first()); + } + } + + if (!targetFound) { + const QString remoteDir = proFileNode->projectType() == LibraryTemplate + ? QLatin1String("/usr/local/lib") + : QLatin1String("/usr/local/bin"); + m_deployables.prepend(Deployable(m_packageStep->localExecutableFilePath(), + remoteDir)); + QString errorString; + if (!readProFileContents(&errorString)) { + qWarning("Error reading .pro file: %s", qPrintable(errorString)); + return false; + } + addValueToProFile(pathVar(TargetVar), remoteDir); + addValueToProFile(InstallsVar, TargetVar); + if (!writeProFileContents(&errorString)) { + qWarning("Error writing .pro file: %s", qPrintable(errorString)); + return false; + } + } + + m_modified = true; + return true; } MaemoPackageContents::Deployable MaemoPackageContents::deployableAt(int row) const { Q_ASSERT(row >= 0 && row < rowCount()); - return row == 0 - ? Deployable(m_packageStep->localExecutableFilePath(), - remoteExecutableDir()) - : m_deployables.at(row - 1); + return m_deployables.at(row); } -bool MaemoPackageContents::addDeployable(const Deployable &deployable) +bool MaemoPackageContents::addDeployable(const Deployable &deployable, + QString *error) { if (m_deployables.contains(deployable) || deployableAt(0) == deployable) return false; + if (!readProFileContents(error)) + return false; + + QCryptographicHash elemHash(QCryptographicHash::Md5); + elemHash.addData(deployable.localFilePath.toUtf8()); + const QString elemName = QString::fromAscii(elemHash.result().toHex()); + addFileToProFile(filesVar(elemName), deployable.localFilePath); + addValueToProFile(pathVar(elemName), deployable.remoteDir); + addValueToProFile(InstallsVar, elemName); + + if (!writeProFileContents(error)) + return false; + beginInsertRows(QModelIndex(), rowCount(), rowCount()); m_deployables << deployable; endInsertRows(); - m_modified = true; return true; } -void MaemoPackageContents::removeDeployableAt(int row) +bool MaemoPackageContents::removeDeployableAt(int row, QString *error) { Q_ASSERT(row > 0 && row < rowCount()); + + const Deployable &deployable = deployableAt(row); + const QString elemToRemove = findInstallsElem(deployable); + if (elemToRemove.isEmpty()) { + *error = tr("Inconsistent model: Deployable not found in .pro file."); + return false; + } + + if (!readProFileContents(error)) + return false; + + const QString filesVarName = filesVar(elemToRemove); + const bool isOnlyElem + = m_proFileReader->values(filesVarName, m_proFile).count() == 1; + bool success + = removeFileFromProFile(filesVarName, deployable.localFilePath); + if (success && isOnlyElem) { + success = removeValueFromProFile(pathVar(elemToRemove), + deployable.remoteDir); + if (success) + success = removeValueFromProFile(InstallsVar, elemToRemove); + } + if (!success) { + *error = tr("Could not remove deployable from .pro file."); + return false; + } + + if (!writeProFileContents(error)) + return false; + beginRemoveRows(QModelIndex(), row, row); m_deployables.removeAt(row - 1); endRemoveRows(); - m_modified = true; + return true; } int MaemoPackageContents::rowCount(const QModelIndex &parent) const { - return parent.isValid() ? 0 : m_deployables.count() + 1; + if (!m_proFile) + buildModel(); + return parent.isValid() ? 0 : m_deployables.count(); } int MaemoPackageContents::columnCount(const QModelIndex &parent) const @@ -128,12 +255,33 @@ bool MaemoPackageContents::setData(const QModelIndex &index, || role != Qt::EditRole) return false; - const QString &remoteDir = value.toString(); - if (index.row() == 0) - m_remoteExecutableDir = remoteDir; - else - m_deployables[index.row() - 1].remoteDir = remoteDir; - m_modified = true; + QString error; + if (!readProFileContents(&error)) { + qWarning(qPrintable(error)); + return false; + } + + Deployable &deployable = m_deployables[index.row()]; + const QString elemToChange = findInstallsElem(deployable); + if (elemToChange.isEmpty()) { + qWarning("Error: Inconsistent model. " + "INSTALLS element not found in .pro file"); + return false; + } + const QString pathElem = pathVar(elemToChange); + if (!removeValueFromProFile(pathElem, deployable.remoteDir)) { + qWarning("Error: Could not change remote path in .pro file."); + return false; + } + const QString &newRemoteDir = value.toString(); + addValueToProFile(pathElem, newRemoteDir); + + if (!writeProFileContents(&error)) { + qWarning(qPrintable(error)); + return false; + } + + deployable.remoteDir = newRemoteDir; emit dataChanged(index, index); return true; } @@ -146,57 +294,146 @@ QVariant MaemoPackageContents::headerData(int section, return section == 0 ? tr("Local File Path") : tr("Remote Directory"); } -QVariantMap MaemoPackageContents::toMap() const +QString MaemoPackageContents::remoteExecutableFilePath() const +{ + if (!m_proFile) + buildModel(); + return deployableAt(0).remoteDir + '/' + m_packageStep->executableFileName(); +} + +bool MaemoPackageContents::readProFileContents(QString *error) const +{ + if (!m_proFileLines.isEmpty()) + return true; + + QFile proFileOnDisk(m_proFileName); + if (!proFileOnDisk.open(QIODevice::ReadOnly)) { + *error = tr("Project file '%1' could not be opened for reading.") + .arg(m_proFileName); + return false; + } + const QString proFileContents + = QString::fromLatin1(proFileOnDisk.readAll()); + if (proFileOnDisk.error() != QFile::NoError) { + *error = tr("Project file '%1' could not be read.") + .arg(m_proFileName); + return false; + } + m_proFileLines = proFileContents.split('\n'); + return true; +} + +bool MaemoPackageContents::writeProFileContents(QString *error) const { - QVariantMap map; - map.insert(MODIFIED_KEY, m_modified); - map.insert(REMOTE_EXE_DIR_KEY, m_remoteExecutableDir); + QFile proFileOnDisk(m_proFileName); + if (!proFileOnDisk.open(QIODevice::WriteOnly)) { + *error = tr("Project file '%1' could not be opened for writing.") + .arg(m_proFileName); + resetProFileContents(); + return false; + } - QDir dir; - QStringList localFiles; - QStringList remoteDirs; - foreach (const Deployable &p, m_deployables) { - localFiles << dir.fromNativeSeparators(p.localFilePath); - remoteDirs << p.remoteDir; + // TODO: Disconnect and reconnect FS watcher here. + proFileOnDisk.write(m_proFileLines.join("\n").toLatin1()); + proFileOnDisk.close(); + if (proFileOnDisk.error() != QFile::NoError) { + *error = tr("Project file '%1' could not be written.") + .arg(m_proFileName); + resetProFileContents(); + return false; } - map.insert(LOCAL_FILES_KEY, localFiles); - map.insert(REMOTE_DIRS_KEY, remoteDirs); - return map; + m_modified = true; + return true; } -void MaemoPackageContents::fromMap(const QVariantMap &map) +QString MaemoPackageContents::cleanPath(const QString &relFileName) const { - m_modified = map.value(MODIFIED_KEY).toBool(); - m_remoteExecutableDir = map.value(REMOTE_EXE_DIR_KEY).toString(); - const QStringList localFiles = map.value(LOCAL_FILES_KEY).toStringList(); - const QStringList remoteDirs = map.value(REMOTE_DIRS_KEY).toStringList(); - if (localFiles.count() != remoteDirs.count()) - qWarning("%s: serialized data inconsistent", Q_FUNC_INFO); + // I'd rather use QDir::cleanPath(), but that doesn't work well + // enough for redundant ".." dirs. + return QFileInfo(m_proFile->directoryName() + '/' + + relFileName).canonicalFilePath(); +} - QDir dir; - const int count = qMin(localFiles.count(), remoteDirs.count()); - for (int i = 0; i < count; ++i) { - m_deployables << Deployable(dir.toNativeSeparators(localFiles.at(i)), - remoteDirs.at(i)); +QString MaemoPackageContents::findInstallsElem(const Deployable &deployable) const +{ + const QStringList elems = m_proFileReader->values(InstallsVar, m_proFile); + foreach (const QString &elem, elems) { + const QStringList elemPaths + = m_proFileReader->values(pathVar(elem), m_proFile); + if (elemPaths.count() != 1 || elemPaths.first() != deployable.remoteDir) + continue; + if (elem == TargetVar) + return elem; + const QStringList elemFiles + = m_proFileReader->values(filesVar(elem), m_proFile); + foreach (const QString &file, elemFiles) { + if (cleanPath(file) == deployable.localFilePath) + return elem; + } } + return QString(); } -QString MaemoPackageContents::remoteExecutableDir() const +void MaemoPackageContents::addFileToProFile(const QString &var, + const QString &absFilePath) { - if (m_remoteExecutableDir.isEmpty()) { - const Qt4ProjectType projectType - = m_packageStep->qt4BuildConfiguration()->qt4Target()->qt4Project() - ->rootProjectNode()->projectType(); - m_remoteExecutableDir = projectType == LibraryTemplate - ? QLatin1String("/usr/local/lib") - : QLatin1String("/usr/local/bin"); + ProWriter::addFiles(m_proFile, &m_proFileLines, m_proDir, + QStringList(absFilePath), var); + parseProFile(ParseFromLines); +} + +void MaemoPackageContents::addValueToProFile(const QString &var, + const QString &value) const +{ + ProWriter::addVarValues(m_proFile, &m_proFileLines, m_proDir, + QStringList(value), var); + parseProFile(ParseFromLines); +} + +bool MaemoPackageContents::removeFileFromProFile(const QString &var, + const QString &absFilePath) +{ + const bool success = ProWriter::removeFiles(m_proFile, &m_proFileLines, + m_proDir, QStringList(absFilePath), + QStringList(var)).isEmpty(); + if (success) + parseProFile(ParseFromLines); + else + resetProFileContents(); + return success; +} + +bool MaemoPackageContents::removeValueFromProFile(const QString &var, + const QString &value) +{ + const bool success = ProWriter::removeVarValues(m_proFile, + &m_proFileLines, m_proDir, QStringList(value), + QStringList(var)).isEmpty(); + if (success) + parseProFile(ParseFromLines); + else + resetProFileContents(); + return success; +} + +void MaemoPackageContents::parseProFile(ParseType type) const +{ + if (type == ParseFromLines) { + m_proFile = m_proFileReader->parsedProFile(m_proFileName, + m_proFileLines.join("\n")); + } else { + m_proFile = m_proFileReader->readProFile(m_proFileName) + ? m_proFileReader->proFileFor(m_proFileName) : 0; } - return m_remoteExecutableDir; } -QString MaemoPackageContents::remoteExecutableFilePath() const +void MaemoPackageContents::resetProFileContents() const { - return remoteExecutableDir() + '/' + m_packageStep->executableFileName(); + m_proFileLines.clear(); + parseProFile(ParseFromFile); + if (!m_proFile) + qWarning("Fatal: Could not parse .pro file '%s'.", + qPrintable(m_proFileName)); } } // namespace Qt4ProjectManager diff --git a/src/plugins/qt4projectmanager/qt-maemo/maemopackagecontents.h b/src/plugins/qt4projectmanager/qt-maemo/maemopackagecontents.h index b4f05cb15ff09dc8999a66583e9bb3a07c70a325..d096cb66ad1cf891ea26f54a60588afbe78e55b8 100644 --- a/src/plugins/qt4projectmanager/qt-maemo/maemopackagecontents.h +++ b/src/plugins/qt4projectmanager/qt-maemo/maemopackagecontents.h @@ -31,14 +31,21 @@ #define MAEMOPACKAGECONTENTS_H #include <QtCore/QAbstractTableModel> +#include <QtCore/QDir> #include <QtCore/QList> +#include <QtCore/QScopedPointer> #include <QtCore/QString> -#include <QtCore/QVariantMap> +#include <QtCore/QStringList> + +QT_BEGIN_NAMESPACE +class ProFile; +class ProFileOption; +QT_END_NAMESPACE namespace Qt4ProjectManager { namespace Internal { - class MaemoPackageCreationStep; +class ProFileReader; class MaemoPackageContents : public QAbstractTableModel { @@ -60,15 +67,15 @@ public: }; MaemoPackageContents(MaemoPackageCreationStep *packageStep); + ~MaemoPackageContents(); - virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; + bool init(); - QVariantMap toMap() const; - void fromMap(const QVariantMap &map); + virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; Deployable deployableAt(int row) const; - bool addDeployable(const Deployable &deployable); - void removeDeployableAt(int row); + bool addDeployable(const Deployable &deployable, QString *error); + bool removeDeployableAt(int row, QString *error); bool isModified() const { return m_modified; } void setUnModified() { m_modified = false; } QString remoteExecutableFilePath() const; @@ -83,13 +90,31 @@ private: virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); - QString remoteExecutableDir() const; + bool buildModel() const; + void resetProFileContents() const; + bool readProFileContents(QString *error) const; + bool writeProFileContents(QString *error) const; + + QString cleanPath(const QString &relFileName) const; + + QString findInstallsElem(const Deployable &deployable) const; + void addFileToProFile(const QString &var, const QString &absFilePath); + void addValueToProFile(const QString &var, const QString &value) const; + bool removeFileFromProFile(const QString &var, const QString &absFilePath); + bool removeValueFromProFile(const QString &var, const QString &value); + + enum ParseType { ParseFromFile, ParseFromLines }; + void parseProFile(ParseType type) const; -private: const MaemoPackageCreationStep * const m_packageStep; - QList<Deployable> m_deployables; - bool m_modified; - mutable QString m_remoteExecutableDir; + QScopedPointer<ProFileOption> m_proFileOption; + QScopedPointer<ProFileReader> m_proFileReader; + mutable QList<Deployable> m_deployables; + mutable bool m_modified; + mutable ProFile *m_proFile; + mutable QStringList m_proFileLines; // TODO: FS watcher + mutable QString m_proFileName; + mutable QDir m_proDir; }; } // namespace Qt4ProjectManager diff --git a/src/plugins/qt4projectmanager/qt-maemo/maemopackagecreationstep.cpp b/src/plugins/qt4projectmanager/qt-maemo/maemopackagecreationstep.cpp index 0b0eeee54286a8220cf332be578df97cfed998b5..3095352c589b8c42d48288c6ed17566e5ddf2a27 100644 --- a/src/plugins/qt4projectmanager/qt-maemo/maemopackagecreationstep.cpp +++ b/src/plugins/qt4projectmanager/qt-maemo/maemopackagecreationstep.cpp @@ -95,12 +95,11 @@ QVariantMap MaemoPackageCreationStep::toMap() const { QVariantMap map(ProjectExplorer::BuildStep::toMap()); map.insert(PackagingEnabledKey, m_packagingEnabled); - return map.unite(m_packageContents->toMap()); + return map; } bool MaemoPackageCreationStep::fromMap(const QVariantMap &map) { - m_packageContents->fromMap(map); m_packagingEnabled = map.value(PackagingEnabledKey, true).toBool(); return ProjectExplorer::BuildStep::fromMap(map); } diff --git a/src/plugins/qt4projectmanager/qt-maemo/maemopackagecreationwidget.cpp b/src/plugins/qt4projectmanager/qt-maemo/maemopackagecreationwidget.cpp index 5a51e1e6307179b7323fb70237aaa66373b3d6c8..2b5b181f56347d740365fea391f87d541aaf7d8f 100644 --- a/src/plugins/qt4projectmanager/qt-maemo/maemopackagecreationwidget.cpp +++ b/src/plugins/qt4projectmanager/qt-maemo/maemopackagecreationwidget.cpp @@ -109,9 +109,9 @@ void MaemoPackageCreationWidget::addFile() deployable(QDir::toNativeSeparators(QFileInfo(localFile).absoluteFilePath()), "/"); MaemoPackageContents * const contents = m_step->packageContents(); - if (!contents->addDeployable(deployable)) { - QMessageBox::information(this, tr("File already in package"), - tr("You have already added this file.")); + QString errorString; + if (!contents->addDeployable(deployable, &errorString)) { + QMessageBox::information(this, tr("Error adding file"), errorString); } else { const QModelIndex newIndex = contents->index(contents->rowCount() - 1, 1); @@ -128,8 +128,13 @@ void MaemoPackageCreationWidget::removeFile() if (selectedRows.isEmpty()) return; const int row = selectedRows.first().row(); - if (row != 0) - m_step->packageContents()->removeDeployableAt(row); + if (row != 0) { + QString errorString; + if (!m_step->packageContents()->removeDeployableAt(row, &errorString)) { + QMessageBox::information(this, tr("Error removing file"), + errorString); + } + } } void MaemoPackageCreationWidget::enableOrDisableRemoveButton()