diff --git a/src/plugins/qt4projectmanager/qt-maemo/maemopackagecreationstep.cpp b/src/plugins/qt4projectmanager/qt-maemo/maemopackagecreationstep.cpp index 573ca699b10..e83976a07e4 100644 --- a/src/plugins/qt4projectmanager/qt-maemo/maemopackagecreationstep.cpp +++ b/src/plugins/qt4projectmanager/qt-maemo/maemopackagecreationstep.cpp @@ -121,7 +121,7 @@ void AbstractMaemoPackageCreationStep::run(QFutureInterface &fi) connect(buildProc, SIGNAL(readyReadStandardError()), this, SLOT(handleBuildOutput())); emit addOutput(tr("Creating package file ..."), MessageOutput); - const bool success = createPackage(buildProc); + const bool success = createPackage(buildProc, fi); disconnect(buildProc, 0, this, 0); buildProc->deleteLater(); if (success) { @@ -332,8 +332,10 @@ void MaemoDebianPackageCreationStep::ctor() setDefaultDisplayName(tr("Create Debian Package")); } -bool MaemoDebianPackageCreationStep::createPackage(QProcess *buildProc) +bool MaemoDebianPackageCreationStep::createPackage(QProcess *buildProc, + const QFutureInterface &fi) { + Q_UNUSED(fi); checkProjectName(); const QString projectDir = buildConfiguration()->target()->project()->projectDirectory(); @@ -601,8 +603,10 @@ void MaemoRpmPackageCreationStep::ctor() setDefaultDisplayName(tr("Create RPM Package")); } -bool MaemoRpmPackageCreationStep::createPackage(QProcess *buildProc) +bool MaemoRpmPackageCreationStep::createPackage(QProcess *buildProc, + const QFutureInterface &fi) { + Q_UNUSED(fi); const QStringList args = QStringList() << QLatin1String("rrpmbuild") << QLatin1String("-bb") << rpmBasedMaemoTarget()->specFilePath(); if (!callPackagingCommand(buildProc, args)) @@ -661,6 +665,29 @@ private: const MaemoTarPackageCreationStep * const m_step; }; +namespace { +const int TarBlockSize = 512; +struct TarFileHeader { + char fileName[100]; + char fileMode[8]; + char uid[8]; + char gid[8]; + char length[12]; + char mtime[12]; + char chksum[8]; + char typeflag; + char linkname[100]; + char magic[6]; + char version[2]; + char uname[32]; + char gname[32]; + char devmajor[8]; + char devminor[8]; + char fileNamePrefix[155]; + char padding[12]; +}; +} // Anonymous namespace. + MaemoTarPackageCreationStep::MaemoTarPackageCreationStep(BuildStepList *bsl) : AbstractMaemoPackageCreationStep(bsl, CreatePackageId) { @@ -679,15 +706,165 @@ void MaemoTarPackageCreationStep::ctor() setDefaultDisplayName(tr("Create tar ball")); } -bool MaemoTarPackageCreationStep::createPackage(QProcess *buildProc) +bool MaemoTarPackageCreationStep::createPackage(QProcess *buildProc, + const QFutureInterface &fi) { Q_UNUSED(buildProc); - // TODO: Copy files one by one into tar file. + // TODO: Optimization: Only package changed files (needs refactoring in upper level; worth the effort?) + QFile tarFile(packageFilePath()); + if (!tarFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { + raiseError(tr("Error: tar file %1 cannot be opened (%2).") + .arg(QDir::toNativeSeparators(packageFilePath()), + tarFile.errorString())); + return false; + } + const QSharedPointer deployables = deployConfig()->deployables(); + for (int i = 0; i < deployables->deployableCount(); ++i) { + const MaemoDeployable &d = deployables->deployableAt(i); + QFileInfo fileInfo(d.localFilePath); + if (!appendFile(tarFile, fileInfo, d.remoteDir + QLatin1Char('/') + + fileInfo.fileName(), fi)) { + return false; + } + } return true; } +bool MaemoTarPackageCreationStep::appendFile(QFile &tarFile, + const QFileInfo &fileInfo, const QString &remoteFilePath, + const QFutureInterface &fi) +{ + if (!writeHeader(tarFile, fileInfo, remoteFilePath)) + return false; + if (fileInfo.isDir()) { + QDir dir(fileInfo.absoluteFilePath()); + foreach (const QString &fileName, + dir.entryList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot)) { + const QString thisLocalFilePath + = dir.path() + QLatin1Char('/') + fileName; + const QString thisRemoteFilePath = remoteFilePath + + QLatin1Char('/') + fileName; + if (!appendFile(tarFile, QFileInfo(thisLocalFilePath), + thisRemoteFilePath, fi)) { + return false; + } + } + } else { + const QString nativePath + = QDir::toNativeSeparators(fileInfo.filePath()); + QFile file(fileInfo.filePath()); + if (!file.open(QIODevice::ReadOnly)) { + raiseError(tr("Error reading file '%1': %2.") + .arg(nativePath, file.errorString())); + return false; + } + + const int chunkSize = 1024*1024; + + // TODO: Wasteful. Work with fixed-size buffer. + while (!file.atEnd() && !file.error() != QFile::NoError + && !tarFile.error() != QFile::NoError) { + const QByteArray data = file.read(chunkSize); + tarFile.write(data); + if (fi.isCanceled()) + return false; + } + if (file.error() != QFile::NoError) { + raiseError(tr("Error reading file '%1': %2.") + .arg(nativePath, file.errorString())); + return false; + } + + const int blockModulo = file.size() % TarBlockSize; + if (blockModulo != 0) { + tarFile.write(QByteArray(TarBlockSize - blockModulo, 0)); + } + + if (tarFile.error() != QFile::NoError) { + raiseError(tr("Error writing tar file '%1': %2.") + .arg(QDir::toNativeSeparators(tarFile.fileName()), + tarFile.errorString())); + return false; + } + } + return true; +} + +bool MaemoTarPackageCreationStep::writeHeader(QFile &tarFile, + const QFileInfo &fileInfo, const QString &remoteFilePath) +{ + TarFileHeader header; + qMemSet(&header, '\0', sizeof header); + const QByteArray &filePath = remoteFilePath.toUtf8(); + const int maxFilePathLength = sizeof header.fileNamePrefix + + sizeof header.fileName; + if (filePath.count() > maxFilePathLength) { + raiseError(tr("Cannot tar file '%1': path is too long.") + .arg(QDir::toNativeSeparators(filePath))); + return false; + } + + const int fileNameBytesToWrite + = qMin(filePath.length(), sizeof header.fileName); + const int fileNameOffset = filePath.length() - fileNameBytesToWrite; + qMemCopy(&header.fileName, filePath.data() + fileNameOffset, + fileNameBytesToWrite); + if (fileNameOffset > 0) + qMemCopy(&header.fileNamePrefix, filePath.data(), fileNameOffset); + int permissions = (0400 * fileInfo.permission(QFile::ReadOwner)) + | (0200 * fileInfo.permission(QFile::WriteOwner)) + | (0100 * fileInfo.permission(QFile::ExeOwner)) + | (040 * fileInfo.permission(QFile::ReadGroup)) + | (020 * fileInfo.permission(QFile::WriteGroup)) + | (010 * fileInfo.permission(QFile::ExeGroup)) + | (04 * fileInfo.permission(QFile::ReadOther)) + | (02 * fileInfo.permission(QFile::WriteOther)) + | (01 * fileInfo.permission(QFile::ExeOther)); + const QByteArray permissionString = QString("%1").arg(permissions, + sizeof header.fileMode - 1, 8, QLatin1Char('0')).toAscii(); + qMemCopy(&header.fileMode, permissionString.data(), + permissionString.length()); + const QByteArray uidString = QString("%1").arg(fileInfo.ownerId(), + sizeof header.uid - 1, 8, QLatin1Char('0')).toAscii(); + qMemCopy(&header.uid, uidString.data(), uidString.length()); + const QByteArray gidString = QString("%1").arg(fileInfo.groupId(), + sizeof header.gid - 1, 8, QLatin1Char('0')).toAscii(); + qMemCopy(&header.gid, gidString.data(), gidString.length()); + const QByteArray sizeString = QString("%1").arg(fileInfo.size(), + sizeof header.length - 1, 8, QLatin1Char('0')).toAscii(); + qMemCopy(&header.length, sizeString.data(), sizeString.length()); + const QByteArray mtimeString = QString("%1").arg(fileInfo.lastModified().toTime_t(), + sizeof header.mtime - 1, 8, QLatin1Char('0')).toAscii(); + qMemCopy(&header.mtime, mtimeString.data(), mtimeString.length()); + if (fileInfo.isDir()) + header.typeflag = '5'; + qMemCopy(&header.magic, "ustar", sizeof "ustar"); + qMemCopy(&header.version, "00", 2); + const QByteArray &owner = fileInfo.owner().toUtf8(); + qMemCopy(&header.uname, owner.data(), + qMin(owner.length(), sizeof header.uname - 1)); + const QByteArray &group = fileInfo.group().toUtf8(); + qMemCopy(&header.gname, group.data(), + qMin(group.length(), sizeof header.gname - 1)); + qMemSet(&header.chksum, ' ', sizeof header.chksum); + quint64 checksum = 0; + for (size_t i = 0; i < sizeof header; ++i) + checksum += reinterpret_cast(&header)[i]; + const QByteArray checksumString = QString("%1").arg(checksum, + sizeof header.chksum - 1, 8, QLatin1Char('0')).toAscii(); + qMemCopy(&header.chksum, checksumString.data(), checksumString.length()); + header.chksum[sizeof header.chksum-1] = 0; + if (!tarFile.write(reinterpret_cast(&header), sizeof header)) { + raiseError(tr("Error writing tar file '%1': %2") + .arg(QDir::toNativeSeparators(packageFilePath()), + tarFile.errorString())); + return false; + } + return true; +} + bool MaemoTarPackageCreationStep::isMetaDataNewerThan(const QDateTime &packageDate) const { Q_UNUSED(packageDate); diff --git a/src/plugins/qt4projectmanager/qt-maemo/maemopackagecreationstep.h b/src/plugins/qt4projectmanager/qt-maemo/maemopackagecreationstep.h index ddc25a14a77..a2f0255c979 100644 --- a/src/plugins/qt4projectmanager/qt-maemo/maemopackagecreationstep.h +++ b/src/plugins/qt4projectmanager/qt-maemo/maemopackagecreationstep.h @@ -47,6 +47,7 @@ QT_BEGIN_NAMESPACE class QDateTime; class QFile; +class QFileInfo; class QProcess; QT_END_NAMESPACE @@ -109,7 +110,7 @@ private: virtual void run(QFutureInterface &fi); virtual ProjectExplorer::BuildStepConfigWidget *createConfigWidget(); - virtual bool createPackage(QProcess *buildProc)=0; + virtual bool createPackage(QProcess *buildProc, const QFutureInterface &fi)=0; virtual bool isMetaDataNewerThan(const QDateTime &packageDate) const=0; static QString nativePath(const QFile &file); @@ -132,7 +133,7 @@ private: MaemoDebianPackageCreationStep(ProjectExplorer::BuildStepList *buildConfig, MaemoDebianPackageCreationStep *other); - virtual bool createPackage(QProcess *buildProc); + virtual bool createPackage(QProcess *buildProc, const QFutureInterface &fi); virtual bool isMetaDataNewerThan(const QDateTime &packageDate) const; void ctor(); @@ -159,7 +160,7 @@ public: MaemoRpmPackageCreationStep(ProjectExplorer::BuildStepList *bsl); private: - virtual bool createPackage(QProcess *buildProc); + virtual bool createPackage(QProcess *buildProc, const QFutureInterface &fi); virtual bool isMetaDataNewerThan(const QDateTime &packageDate) const; MaemoRpmPackageCreationStep(ProjectExplorer::BuildStepList *buildConfig, @@ -180,7 +181,7 @@ public: virtual QString packageFilePath() const; private: - virtual bool createPackage(QProcess *buildProc); + virtual bool createPackage(QProcess *buildProc, const QFutureInterface &fi); virtual bool isMetaDataNewerThan(const QDateTime &packageDate) const; virtual ProjectExplorer::BuildStepConfigWidget *createConfigWidget(); @@ -188,6 +189,10 @@ private: MaemoTarPackageCreationStep *other); void ctor(); + bool appendFile(QFile &tarFile, const QFileInfo &fileInfo, + const QString &remoteFilePath, const QFutureInterface &fi); + bool writeHeader(QFile &tarFile, const QFileInfo &fileInfo, + const QString &remoteFilePath); static const QString CreatePackageId; }; diff --git a/src/plugins/qt4projectmanager/qt-maemo/maemopackageinstaller.cpp b/src/plugins/qt4projectmanager/qt-maemo/maemopackageinstaller.cpp index aefb2d1213f..7e2793af6e4 100644 --- a/src/plugins/qt4projectmanager/qt-maemo/maemopackageinstaller.cpp +++ b/src/plugins/qt4projectmanager/qt-maemo/maemopackageinstaller.cpp @@ -205,7 +205,7 @@ QString MaemoTarPackageInstaller::installCommand() const QStringList MaemoTarPackageInstaller::installCommandArguments() const { - return QStringList() << QLatin1String("xvf"); + return QStringList() << QLatin1String("--absolute-names -xvf"); } } // namespace Internal