summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorkh1 <karsten.heimrich@nokia.com>2012-03-15 14:53:47 +0100
committerKarsten Heimrich <karsten.heimrich@nokia.com>2012-03-19 16:14:04 +0100
commitbe3b47d0d504a3409ce66bd77bb8c0acff87c4f5 (patch)
tree09dfb02d484a4f395991972b828da71400fb761a /tools
parent9fd62353cf7f973d78cd2093328ac15b5c4980b6 (diff)
Reorganize the tree, have better ifw.pri. Shadow build support.
Change-Id: I01fb12537f863ed0744979973c7e4153889cc5cb Reviewed-by: Tim Jenssen <tim.jenssen@nokia.com>
Diffstat (limited to 'tools')
-rw-r--r--tools/archivegen/archive.cpp75
-rw-r--r--tools/archivegen/archivegen.pro17
-rw-r--r--tools/binarycreator/binarycreator.cpp711
-rw-r--r--tools/binarycreator/binarycreator.pro20
-rw-r--r--tools/binarycreator/binarycreator.qrc8
-rw-r--r--tools/binarycreator/rcc/qcorecmdlineargs_p.h72
-rw-r--r--tools/binarycreator/rcc/rcc.cpp958
-rw-r--r--tools/binarycreator/rcc/rcc.h144
-rw-r--r--tools/binarycreator/rcc/rccmain.cpp248
-rw-r--r--tools/binarycreator/resources/copylibsintobundle.sh161
-rw-r--r--tools/binarycreator/resources/default_icon_mac.icnsbin0 -> 118992 bytes
-rw-r--r--tools/binarycreator/resources/mkdmg.sh37
-rw-r--r--tools/common/repositorygen.cpp622
-rw-r--r--tools/common/repositorygen.h77
-rw-r--r--tools/extractbinarydata/extractbinarydata.pro14
-rw-r--r--tools/maddehelper/maddehelper.pro16
-rw-r--r--tools/repocompare/repocompare.pro20
-rw-r--r--tools/repogen/repogen.cpp221
-rw-r--r--tools/repogen/repogen.pro17
-rw-r--r--tools/repogenfromonlinerepo/repogenfromonlinerepo.pro2
-rw-r--r--tools/tools.pro10
21 files changed, 3420 insertions, 30 deletions
diff --git a/tools/archivegen/archive.cpp b/tools/archivegen/archive.cpp
new file mode 100644
index 000000000..2d1257ae7
--- /dev/null
+++ b/tools/archivegen/archive.cpp
@@ -0,0 +1,75 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@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 qt-info@nokia.com.
+**
+**************************************************************************/
+#include "common/repositorygen.h"
+
+#include <errors.h>
+#include <init.h>
+#include <lib7z_facade.h>
+#include <utils.h>
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QFileInfo>
+#include <QtCore/QStringList>
+
+#include <iostream>
+
+using namespace Lib7z;
+using namespace QInstaller;
+
+static void printUsage()
+{
+ std::cout << "Usage: " << QFileInfo(QCoreApplication::applicationFilePath()).fileName()
+ << " directory.7z directories" << std::endl;
+}
+
+int main(int argc, char *argv[])
+{
+ try {
+ QCoreApplication app(argc, argv);
+
+ if (app.arguments().count() < 3) {
+ printUsage();
+ return EXIT_FAILURE;
+ }
+
+ QInstaller::init();
+ QInstaller::setVerbose(true);
+ const QStringList sourceDirectories = app.arguments().mid(2);
+ QInstallerTools::compressDirectory(sourceDirectories, app.arguments().at(1));
+ return EXIT_SUCCESS;
+ } catch (const Lib7z::SevenZipException &e) {
+ std::cerr << e.message() << std::endl;
+ } catch (const QInstaller::Error &e) {
+ std::cerr << e.message() << std::endl;
+ }
+ return EXIT_FAILURE;
+}
diff --git a/tools/archivegen/archivegen.pro b/tools/archivegen/archivegen.pro
new file mode 100644
index 000000000..9d778ee98
--- /dev/null
+++ b/tools/archivegen/archivegen.pro
@@ -0,0 +1,17 @@
+TEMPLATE = app
+TARGET = archivegen
+DEPENDPATH += . .. ../common
+INCLUDEPATH += . .. ../common
+
+include(../../installerfw.pri)
+
+QT -= gui
+LIBS += -linstaller
+
+CONFIG += console
+CONFIG -= app_bundle
+DESTDIR = $$IFW_APP_PATH
+
+SOURCES += archive.cpp \
+ repositorygen.cpp
+HEADERS += repositorygen.h
diff --git a/tools/binarycreator/binarycreator.cpp b/tools/binarycreator/binarycreator.cpp
new file mode 100644
index 000000000..c749fb276
--- /dev/null
+++ b/tools/binarycreator/binarycreator.cpp
@@ -0,0 +1,711 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@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 qt-info@nokia.com.
+**
+**************************************************************************/
+#include "common/repositorygen.h"
+
+#include <binaryformat.h>
+#include <errors.h>
+#include <fileutils.h>
+#include <init.h>
+#include <settings.h>
+#include <utils.h>
+
+#include <kdsavefile.h>
+
+#include <QtCore/QDirIterator>
+#include <QtCore/QProcess>
+#include <QtCore/QSettings>
+#include <QtCore/QTemporaryFile>
+
+#include <iostream>
+
+using namespace QInstaller;
+using namespace QInstallerCreator;
+
+struct Input {
+ QString outputPath;
+ QString installerExePath;
+ ComponentIndex components;
+ QString binaryResourcePath;
+ QStringList binaryResources;
+
+ Range<qint64> operationsPos;
+ QVector<Range<qint64> > resourcePos;
+ Range<qint64> componentIndexSegment;
+};
+
+class BundleBackup
+{
+public:
+ explicit BundleBackup(const QString &bundle = QString())
+ : bundle(bundle)
+ {
+ if (!bundle.isEmpty() && QFileInfo(bundle).exists()) {
+ backup = generateTemporaryFileName(bundle);
+ QFile::rename(bundle, backup);
+ }
+ }
+
+ ~BundleBackup()
+ {
+ if (!backup.isEmpty()) {
+ removeDirectory(bundle);
+ QFile::rename(backup, bundle);
+ }
+ }
+
+ void release() const
+ {
+ if (!backup.isEmpty())
+ removeDirectory(backup);
+ backup.clear();
+ }
+
+private:
+ const QString bundle;
+ mutable QString backup;
+};
+
+static void chmod755(const QString &absolutFilePath)
+{
+ QFile::setPermissions(absolutFilePath, QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner
+ | QFile::ReadGroup | QFile::ExeGroup | QFile::ReadOther | QFile::ExeOther);
+}
+
+static int assemble(Input input, const QString &configdir)
+{
+ const QString configfile = QFileInfo(configdir, QLatin1String("config.xml")).absoluteFilePath();
+ const QInstaller::Settings &settings = QInstaller::Settings::fromFileAndPrefix(configfile, configdir);
+
+#ifdef Q_OS_LINUX
+Q_UNUSED(settings)
+#endif
+
+#ifdef Q_OS_MAC
+ if (QFileInfo(input.installerExePath).isBundle()) {
+ const QString bundle = input.installerExePath;
+ // if the input file was a bundle
+ const QSettings s(QString::fromLatin1("%1/Contents/Info.plist").arg(input.installerExePath),
+ QSettings::NativeFormat);
+ input.installerExePath = QString::fromLatin1("%1/Contents/MacOS/%2").arg(bundle)
+ .arg(s.value(QLatin1String("CFBundleExecutable"),
+ QFileInfo(input.installerExePath).completeBaseName()).toString());
+ }
+
+ const bool createDMG = input.outputPath.endsWith(QLatin1String(".dmg"));
+ if (createDMG)
+ input.outputPath.replace(input.outputPath.length() - 4, 4, QLatin1String(".app"));
+
+ const bool isBundle = input.outputPath.endsWith(QLatin1String(".app"));
+ const QString bundle = isBundle ? input.outputPath : QString();
+ const BundleBackup bundleBackup(bundle);
+
+ if (isBundle) {
+ // output should be a bundle
+ const QFileInfo fi(input.outputPath);
+ QInstaller::mkpath(fi.filePath() + QLatin1String("/Contents/MacOS"));
+ QInstaller::mkpath(fi.filePath() + QLatin1String("/Contents/Resources"));
+
+ {
+ QFile pkgInfo(fi.filePath() + QLatin1String("/Contents/PkgInfo"));
+ pkgInfo.open(QIODevice::WriteOnly);
+ QTextStream pkgInfoStream(&pkgInfo);
+ pkgInfoStream << QLatin1String("APPL????") << endl;
+ }
+
+ const QString iconFile = QFile::exists(settings.icon()) ? settings.icon()
+ : QString::fromLatin1(":/resources/default_icon_mac.icns");
+ const QString iconTargetFile = fi.completeBaseName() + QLatin1String(".icns");
+ QFile::copy(iconFile, fi.filePath() + QLatin1String("/Contents/Resources/") + iconTargetFile);
+
+ QFile infoPList(fi.filePath() + QLatin1String("/Contents/Info.plist"));
+ infoPList.open(QIODevice::WriteOnly);
+ QTextStream plistStream(&infoPList);
+ plistStream << QLatin1String("<?xml version=\"1.0\" encoding=\"UTF-8\"?>") << endl;
+ plistStream << QLatin1String("<!DOCTYPE plist SYSTEM \"file://localhost/System/Library/DTDs"
+ "/PropertyList.dtd\">") << endl;
+ plistStream << QLatin1String("<plist version=\"0.9\">") << endl;
+ plistStream << QLatin1String("<dict>") << endl;
+ plistStream << QLatin1String(" <key>CFBundleIconFile</key>") << endl;
+ plistStream << QLatin1String(" <string>") << iconTargetFile << QLatin1String("</string>")
+ << endl;
+ plistStream << QLatin1String(" <key>CFBundlePackageType</key>") << endl;
+ plistStream << QLatin1String(" <string>APPL</string>") << endl;
+ plistStream << QLatin1String(" <key>CFBundleGetInfoString</key>") << endl;
+ plistStream << QLatin1String(" <string>Created by Qt/QMake</string>") << endl;
+ plistStream << QLatin1String(" <key>CFBundleSignature</key>") << endl;
+ plistStream << QLatin1String(" <string> ???? </string>") << endl;
+ plistStream << QLatin1String(" <key>CFBundleExecutable</key>") << endl;
+ plistStream << QLatin1String(" <string>") << fi.completeBaseName() << QLatin1String("</string>")
+ << endl;
+ plistStream << QLatin1String(" <key>CFBundleIdentifier</key>") << endl;
+ plistStream << QLatin1String(" <string>com.yourcompany.installerbase</string>") << endl;
+ plistStream << QLatin1String(" <key>NOTE</key>") << endl;
+ plistStream << QLatin1String(" <string>This file was generated by Qt/QMake.</string>")
+ << endl;
+ plistStream << QLatin1String("</dict>") << endl;
+ plistStream << QLatin1String("</plist>") << endl;
+
+ input.outputPath = QString::fromLatin1("%1/Contents/MacOS/%2").arg(input.outputPath)
+ .arg(fi.completeBaseName());
+ }
+#endif
+
+ QTemporaryFile file(input.outputPath);
+ if (!file.open()) {
+ throw Error(QObject::tr("Could not copy %1 to %2: %3").arg(input.installerExePath,
+ input.outputPath, file.errorString()));
+ }
+
+ const QString tempFile = file.fileName();
+ file.close();
+ file.remove();
+
+ QFile instExe(input.installerExePath);
+ if (!instExe.copy(tempFile)) {
+ throw Error(QObject::tr("Could not copy %1 to %2: %3").arg(instExe.fileName(), tempFile,
+ instExe.errorString()));
+ }
+ input.installerExePath = tempFile;
+
+#if defined(Q_OS_WIN)
+ // setting the windows icon must happen before we append our binary data - otherwise they get lost :-/
+ if (QFile::exists(settings.icon())) {
+ // no error handling as this is not fatal
+ setApplicationIcon(tempFile, settings.icon());
+ }
+#elif defined(Q_OS_MAC)
+ if (isBundle) {
+ // no error handling as this is not fatal
+ const QString copyscript = QDir::temp().absoluteFilePath(QLatin1String("copylibsintobundle.sh"));
+ QFile::copy(QLatin1String(":/resources/copylibsintobundle.sh"), copyscript);
+ QFile::rename(tempFile, input.outputPath);
+ chmod755(copyscript);
+ QProcess p;
+ p.start(copyscript, QStringList() << bundle);
+ p.waitForFinished();
+ QFile::rename(input.outputPath, tempFile);
+ QFile::remove(copyscript);
+ }
+#endif
+
+ KDSaveFile out(input.outputPath);
+ try {
+ openForWrite(&out, input.outputPath);
+
+ QFile exe(input.installerExePath);
+ openForRead(&exe, exe.fileName());
+ appendFileData(&out, &exe);
+
+ const qint64 dataBlockStart = out.pos();
+ qDebug() << "Data block starts at" << dataBlockStart;
+
+ // append our self created resource file
+ QFile res(input.binaryResourcePath);
+ openForRead(&res, res.fileName());
+ appendFileData(&out, &res);
+ input.resourcePos.append(Range<qint64>::fromStartAndEnd(out.pos() - res.size(), out.pos())
+ .moved(-dataBlockStart));
+
+ // append given resource files
+ foreach (const QString &resource, input.binaryResources) {
+ QFile res(resource);
+ openForRead(&res, res.fileName());
+ appendFileData(&out, &res);
+ input.resourcePos.append(Range<qint64>::fromStartAndEnd(out.pos() - res.size(), out.pos())
+ .moved(-dataBlockStart));
+ }
+
+ // zero operations cause we are not the uninstaller
+ const qint64 operationsStart = out.pos();
+ appendInt64(&out, 0);
+ appendInt64(&out, 0);
+ input.operationsPos = Range<qint64>::fromStartAndEnd(operationsStart, out.pos())
+ .moved(-dataBlockStart);
+
+ // component index:
+ input.components.writeComponentData(&out, -dataBlockStart);
+ const qint64 compIndexStart = out.pos() - dataBlockStart;
+ input.components.writeIndex(&out, -dataBlockStart);
+ input.componentIndexSegment = Range<qint64>::fromStartAndEnd(compIndexStart, out.pos()
+ - dataBlockStart);
+
+ qDebug("Component index: [%llu:%llu]", input.componentIndexSegment.start(),
+ input.componentIndexSegment.end());
+ appendInt64Range(&out, input.componentIndexSegment);
+ foreach (const Range<qint64> &range, input.resourcePos)
+ appendInt64Range(&out, range);
+ appendInt64Range(&out, input.operationsPos);
+ appendInt64(&out, input.resourcePos.count());
+
+ //data block size, from end of .exe to end of file
+ appendInt64(&out, out.pos() + 3 * sizeof(qint64) - dataBlockStart);
+ appendInt64(&out, QInstaller::MagicInstallerMarker);
+ appendInt64(&out, QInstaller::MagicCookie);
+
+ } catch (const Error &e) {
+ qCritical("Error occurred while assembling the installer: %s", qPrintable(e.message()));
+ QFile::remove(tempFile);
+ return 1;
+ }
+
+ if (!out.commit(KDSaveFile::OverwriteExistingFile)) {
+ qCritical("Could not write installer to %s: %s", qPrintable(out.fileName()),
+ qPrintable(out.errorString()));
+ QFile::remove(tempFile);
+ return 1;
+ }
+#ifndef Q_OS_WIN
+ chmod755(out.fileName());
+#endif
+ QFile::remove(tempFile);
+
+#ifdef Q_OS_MAC
+ bundleBackup.release();
+
+ if (createDMG) {
+ qDebug() << "creating a DMG disk image...";
+ // no error handling as this is not fatal
+ const QString mkdmgscript = QDir::temp().absoluteFilePath(QLatin1String("mkdmg.sh"));
+ QFile::copy(QLatin1String(":/resources/mkdmg.sh"), mkdmgscript);
+ chmod755(mkdmgscript);
+
+ QProcess p;
+ p.start(mkdmgscript, QStringList() << QFileInfo(out.fileName()).fileName() << bundle);
+ p.waitForFinished();
+ QFile::remove(mkdmgscript);
+ qDebug() << "done." << mkdmgscript;
+ }
+#endif
+ return 0;
+}
+
+QT_BEGIN_NAMESPACE
+int runRcc(int argc, char *argv[]);
+QT_END_NAMESPACE
+
+static int runRcc(const QStringList &args)
+{
+ const int argc = args.count();
+ QVector<char*> argv(argc, 0);
+ for (int i = 0; i < argc; ++i)
+ argv[i] = qstrdup(qPrintable(args[i]));
+
+ const int result = runRcc(argc, argv.data());
+
+ foreach (char *arg, argv)
+ delete [] arg;
+
+ return result;
+}
+
+class WorkingDirectoryChange
+{
+public:
+ explicit WorkingDirectoryChange(const QString &path)
+ : oldPath(QDir::currentPath())
+ {
+ QDir::setCurrent(path);
+ }
+
+ virtual ~WorkingDirectoryChange()
+ {
+ QDir::setCurrent(oldPath);
+ }
+
+private:
+ const QString oldPath;
+};
+
+static QString createBinaryResourceFile(const QString &directory)
+{
+ QTemporaryFile projectFile(directory + QLatin1String("/rccprojectXXXXXX.qrc"));
+ if (!projectFile.open())
+ throw Error(QObject::tr("Could not create temporary file for generated rcc project file"));
+ projectFile.close();
+
+ const WorkingDirectoryChange wd(directory);
+ const QString binaryName = generateTemporaryFileName();
+ const QString projectFileName = QFileInfo(projectFile.fileName()).absoluteFilePath();
+
+ // 1. create the .qrc file
+ runRcc(QStringList() << QLatin1String("rcc") << QLatin1String("-project")
+ << QLatin1String("-o") << projectFileName);
+
+ // 2. create the binary resource file from the .qrc file
+ runRcc(QStringList() << QLatin1String("rcc") << QLatin1String("-binary")
+ << QLatin1String("-o") << binaryName << projectFileName);
+
+ return binaryName;
+}
+
+static QStringList createBinaryResourceFiles(const QStringList &resources)
+{
+ QStringList result;
+ foreach (const QString &resource, resources) {
+ QFile file(resource);
+ if (file.exists()) {
+ const QString binaryName = generateTemporaryFileName();
+ const QString fileName = QFileInfo(file.fileName()).absoluteFilePath();
+ const int status = runRcc(QStringList() << QLatin1String("rcc")
+ << QLatin1String("-binary") << QLatin1String("-o") << binaryName << fileName);
+ if (status == EXIT_SUCCESS)
+ result.append(binaryName);
+ }
+ }
+ return result;
+}
+
+static void printUsage()
+{
+ const QString appName = QFileInfo(QCoreApplication::applicationFilePath()).fileName();
+ std::cout << "Usage: " << appName << " [options] target" << std::endl;
+ std::cout << std::endl;
+ std::cout << "Options:" << std::endl;
+
+ std::cout << " -t|--template file Use file as installer template binary" << std::endl;
+ std::cout << " If this parameter is not given, the template used" << std::endl;
+ std::cout << " defaults to installerbase." << std::endl;
+
+ QInstallerTools::printRepositoryGenOptions();
+
+ std::cout << " -n|--nodeps Don't add dependencies of package1...n into the " << std::endl;
+ std::cout << " installer (for online installers)" << std::endl;
+
+ std::cout << " --offline-only Forces the installer to act as an offline installer, " << std::endl;
+ std::cout << " i.e. never access online repositories" << std::endl;
+
+ std::cout << " -r|--resources r1,.,rn include the given resource files into the binary" << std::endl;
+ std::cout << std::endl;
+ std::cout << " -v|--verbose Verbose output" << std::endl;
+ std::cout << "Packages are to be found in the current working directory and get listed as "
+ "their names" << std::endl << std::endl;
+ std::cout << "Example (offline installer):" << std::endl;
+ std::cout << " " << appName << " --offline-only -c installer-config -p packages-directory -t "
+ "installerbase SDKInstaller.exe" << std::endl;
+ std::cout << "Creates an offline installer for the SDK, containing all dependencies." << std::endl;
+ std::cout << std::endl;
+ std::cout << "Example (online installer):" << std::endl;
+ std::cout << " " << appName << " -c installer-config -p packages-directory -e com.nokia.sdk.qt,"
+ "com.nokia.qtcreator -t installerbase SDKInstaller.exe" << std::endl;
+ std::cout << std::endl;
+ std::cout << "Creates an installer for the SDK without qt and qt creator." << std::endl;
+ std::cout << std::endl;
+}
+
+static QString createMetaDataDirectory(const QInstallerTools::PackageInfoVector &packages,
+ const QString &packagesDir, const QString &configdir)
+{
+ const QString configfile = QFileInfo(configdir, QLatin1String("config.xml")).absoluteFilePath();
+ const QInstaller::Settings &settings = QInstaller::Settings::fromFileAndPrefix(configfile, QString());
+
+ const QString metapath = createTemporaryDirectory();
+ generateMetaDataDirectory(metapath, packagesDir, packages, settings.applicationName(),
+ settings.applicationVersion());
+
+ const QString configCopy = metapath + QLatin1String("/installer-config");
+ QInstaller::mkdir(configCopy);
+ QString absoluteConfigPath = QFileInfo(configdir).absoluteFilePath();
+
+ QDirIterator it(absoluteConfigPath, QDir::Files | QDir::NoDotAndDotDot, QDirIterator::Subdirectories);
+ while (it.hasNext()) {
+ const QString next = it.next();
+ if (next.contains(QLatin1String("/."))) // skip files that are in directories starting with a point
+ continue;
+
+ qDebug() << "\tFound configuration file: " << next;
+ const QFileInfo sourceFileInfo(next);
+ const QString source = sourceFileInfo.absoluteFilePath();
+ const QFileInfo targetFileInfo(configCopy, QFileInfo(next).fileName());
+ const QDir targetDir = targetFileInfo.dir();
+ if (!targetDir.exists())
+ QInstaller::mkpath(targetFileInfo.absolutePath());
+ const QString target = targetFileInfo.absoluteFilePath();
+
+ if (!QFile::copy(source, target))
+ throw Error(QObject::tr("Could not copy %1.").arg(source));
+
+ if (sourceFileInfo.fileName().toLower() == QLatin1String("config.xml")) {
+ // if we just copied the config.xml, make sure to remove the RSA private key from it :-o
+ QFile configXml(targetDir.filePath(QLatin1String("config.xml")));
+ configXml.open(QIODevice::ReadOnly);
+ QDomDocument dom;
+ dom.setContent(&configXml);
+ configXml.close();
+
+ // iterate over all child elements, searching for relative file names
+ const QDomNodeList children = dom.documentElement().childNodes();
+ for (int i = 0; i < children.count(); ++i) {
+ QDomElement el = children.at(i).toElement();
+ if (el.isNull())
+ continue;
+
+ QFileInfo fi(absoluteConfigPath, el.text());
+#if defined(Q_OS_MAC)
+ const QFileInfo fiIcon(absoluteConfigPath, el.text() + QLatin1String(".icns"));
+#elif defined(Q_OS_WIN)
+ const QFileInfo fiIcon(absoluteConfigPath, el.text() + QLatin1String(".ico"));
+#else
+ const QFileInfo fiIcon(absoluteConfigPath, el.text() + QLatin1String(".png"));
+#endif
+ if (!fi.exists() && fiIcon.exists())
+ fi = fiIcon;
+
+ if (!fi.exists() || fi.absolutePath() == QFileInfo(configdir).dir().absolutePath())
+ continue;
+
+ if (fi.isDir())
+ continue;
+
+ const QString newName = el.text().replace(QRegExp(QLatin1String("\\\\|/|\\.")),
+ QLatin1String("_"));
+
+ if (!QFile::exists(targetDir.absoluteFilePath(newName))) {
+ if (!QFile::copy(fi.absoluteFilePath(), targetDir.absoluteFilePath(newName)))
+ throw Error(QObject::tr("Could not copy %1.").arg(el.text()));
+ }
+ el.removeChild(el.firstChild());
+ el.appendChild(dom.createTextNode(newName));
+ }
+
+ openForWrite(&configXml, configXml.fileName());
+ QTextStream stream(&configXml);
+ dom.save(stream, 4);
+ qDebug() << "\tdone.";
+ }
+ }
+ return metapath;
+}
+
+static int printErrorAndUsageAndExit(const QString &err)
+{
+ std::cerr << qPrintable(err) << std::endl << std::endl;
+ printUsage();
+ return EXIT_FAILURE;
+}
+
+/*!
+ Usage:
+ binarycreator: [--help|-h] [-p|--packages packages directory] [-t|--template binary]
+ -c|--config confdir target component ...
+ template defaults to installerbase[.exe] in the same directory
+*/
+int main(int argc, char **argv)
+{
+ QCoreApplication app(argc, argv);
+
+ QInstaller::init();
+
+ QString templateBinary = QLatin1String("installerbase");
+#ifdef Q_OS_WIN
+ templateBinary += QLatin1String(".exe");
+#endif
+ if (!QFileInfo(templateBinary).exists())
+ templateBinary = QString::fromLatin1("%1/%2").arg(qApp->applicationDirPath(), templateBinary);
+
+ QString target;
+ QString configDir;
+ QString packagesDirectory = QDir::currentPath();
+ bool nodeps = false;
+ bool offlineOnly = false;
+ QStringList resources;
+ QStringList components;
+ QStringList filteredPackages;
+ QInstallerTools::FilterType ftype = QInstallerTools::Exclude;
+
+ const QStringList args = app.arguments().mid(1);
+ for (QStringList::const_iterator it = args.begin(); it != args.end(); ++it) {
+ if (*it == QLatin1String("-h") || *it == QLatin1String("--help")) {
+ printUsage();
+ return 0;
+ } else if (*it == QLatin1String("-p") || *it == QLatin1String("--packages")) {
+ ++it;
+ if (it == args.end()) {
+ return printErrorAndUsageAndExit(QObject::tr("Error: Packages parameter missing argument."));
+ }
+ if (!QFileInfo(*it).exists()) {
+ return printErrorAndUsageAndExit(QObject::tr("Error: Package directory not found at the "
+ "specified location."));
+ }
+ packagesDirectory = *it;
+ } else if (*it == QLatin1String("-e") || *it == QLatin1String("--exclude")) {
+ ++it;
+ if (!filteredPackages.isEmpty())
+ return printErrorAndUsageAndExit(QObject::tr("Error: --include and --exclude are mutually "
+ "exclusive. Use either one or the other."));
+ if (it == args.end() || it->startsWith(QLatin1String("-")))
+ return printErrorAndUsageAndExit(QObject::tr("Error: Package to exclude missing."));
+ filteredPackages = it->split(QLatin1Char(','));
+ } else if (*it == QLatin1String("-i") || *it == QLatin1String("--include")) {
+ ++it;
+ if (!filteredPackages.isEmpty())
+ return printErrorAndUsageAndExit(QObject::tr("Error: --include and --exclude are mutually "
+ "exclusive. Use either one or the other."));
+ if (it == args.end() || it->startsWith(QLatin1String("-")))
+ return printErrorAndUsageAndExit(QObject::tr("Error: Package to include missing."));
+ filteredPackages = it->split(QLatin1Char(','));
+ ftype = QInstallerTools::Include;
+ }
+ else if (*it == QLatin1String("-v") || *it == QLatin1String("--verbose")) {
+ QInstaller::setVerbose(true);
+ } else if (*it == QLatin1String("-n") || *it == QLatin1String("--nodeps")) {
+ if (!filteredPackages.isEmpty())
+ return printErrorAndUsageAndExit(QObject::tr("for the --include and --exclude case you also "
+ "have to ensure that nopdeps==false"));
+ nodeps = true;
+ } else if (*it == QLatin1String("--offline-only")) {
+ offlineOnly = true;
+ } else if (*it == QLatin1String("-t") || *it == QLatin1String("--template")) {
+ ++it;
+ if (it == args.end()) {
+ return printErrorAndUsageAndExit(QObject::tr("Error: Template parameter missing argument."));
+ }
+ if (!QFileInfo(*it).exists()) {
+ return printErrorAndUsageAndExit(QObject::tr("Error: Template not found at the specified "
+ "location."));
+ }
+ templateBinary = *it;
+ } else if (*it == QLatin1String("-c") || *it == QLatin1String("--config")) {
+ ++it;
+ if (it == args.end())
+ return printErrorAndUsageAndExit(QObject::tr("Error: Config parameter missing argument."));
+ const QFileInfo fi(*it);
+ if (!fi.exists()) {
+ return printErrorAndUsageAndExit(QObject::tr("Error: Config directory %1 not found at the "
+ "specified location.").arg(*it));
+ }
+ if (!fi.isDir()) {
+ return printErrorAndUsageAndExit(QObject::tr("Error: Configuration %1 is not a directory.")
+ .arg(*it));
+ }
+ if (!fi.isReadable()) {
+ return printErrorAndUsageAndExit(QObject::tr("Error: Config directory %1 is not readable.")
+ .arg(*it));
+ }
+ configDir = *it;
+ } else if (*it == QLatin1String("-r") || *it == QLatin1String("--resources")) {
+ ++it;
+ if (it == args.end() || it->startsWith(QLatin1String("-")))
+ return printErrorAndUsageAndExit(QObject::tr("Error: Resource files to include missing."));
+ resources = it->split(QLatin1Char(','));
+ } else if (*it == QLatin1String("--ignore-translations")
+ || *it == QLatin1String("--ignore-invalid-packages")) {
+ continue;
+ } else {
+ if (target.isEmpty())
+ target = *it;
+ else
+ components.append(*it);
+ }
+ }
+
+ if (!components.isEmpty()) {
+ std::cout << "Package names at the end of the command are deprecated"
+ " - please use --include or --exclude" << std::endl;
+ if (nodeps) {
+ filteredPackages.append(components);
+ ftype = QInstallerTools::Include;
+ }
+ }
+
+ if (target.isEmpty())
+ return printErrorAndUsageAndExit(QObject::tr("Error: Target parameter missing."));
+
+ if (configDir.isEmpty())
+ return printErrorAndUsageAndExit(QObject::tr("Error: No configuration directory selected."));
+
+ qDebug() << "Parsed arguments, ok.";
+
+ try {
+ QInstallerTools::PackageInfoVector packages = createListOfPackages(packagesDirectory,
+ filteredPackages, ftype);
+ const QString metaDir = createMetaDataDirectory(packages, packagesDirectory, configDir);
+ {
+ QSettings confInternal(metaDir + QLatin1String("/config/config-internal.ini")
+ , QSettings::IniFormat);
+ confInternal.setValue(QLatin1String("offlineOnly"), offlineOnly);
+ }
+
+#if defined(Q_OS_MAC)
+ // on mac, we enforce building a bundle
+ if (!target.endsWith(QLatin1String(".app")) && !target.endsWith(QLatin1String(".dmg"))) {
+ target += QLatin1String(".app");
+ }
+#elif defined(Q_OS_WIN)
+ // on windows, we add .exe
+ if (!target.endsWith(QLatin1String(".exe")))
+ target += QLatin1String(".exe");
+#endif
+ int result = EXIT_FAILURE;
+ {
+ Input input;
+ input.outputPath = target;
+ input.installerExePath = templateBinary;
+ input.binaryResourcePath = createBinaryResourceFile(metaDir);
+ input.binaryResources = createBinaryResourceFiles(resources);
+
+ QInstallerTools::copyComponentData(packagesDirectory, metaDir, packages);
+
+ // now put the packages into the components section of the binary
+ foreach (const QInstallerTools::PackageInfo &info, packages) {
+ Component comp;
+ comp.setName(info.name.toUtf8());
+
+ qDebug() << "Creating component info for" << info.name;
+ foreach (const QString &archive, info.copiedArchives) {
+ const QSharedPointer<Archive> arch(new Archive(archive));
+ qDebug() << QString::fromLatin1("\tAppending %1 (%2 bytes)").arg(archive,
+ QString::number(arch->size()));
+ comp.appendArchive(arch);
+ }
+ input.components.insertComponent(comp);
+ }
+
+ qDebug() << "Creating the binary";
+ result = assemble(input, configDir);
+
+ // cleanup
+ qDebug() << "Cleaning up...";
+ QFile::remove(input.binaryResourcePath);
+ foreach (const QString &resource, input.binaryResources)
+ QFile::remove(resource);
+ }
+ removeDirectory(metaDir);
+ return result;
+ } catch (const Error &e) {
+ std::cerr << e.message() << std::endl;
+ return EXIT_FAILURE;
+ } catch (...) {
+ std::cerr << "Unknown exception caught" << std::endl;
+ return EXIT_FAILURE;
+ }
+ return EXIT_FAILURE;
+}
diff --git a/tools/binarycreator/binarycreator.pro b/tools/binarycreator/binarycreator.pro
new file mode 100644
index 000000000..b6f164b4d
--- /dev/null
+++ b/tools/binarycreator/binarycreator.pro
@@ -0,0 +1,20 @@
+TEMPLATE = app
+TARGET = binarycreator
+DEPENDPATH += . .. rcc ../common
+INCLUDEPATH += . .. rcc ../common
+
+include(../../installerfw.pri)
+
+QT -= gui
+LIBS += -linstaller
+
+CONFIG += console
+CONFIG -= app_bundle
+DESTDIR = $$IFW_APP_PATH
+
+SOURCES = binarycreator.cpp \
+ rcc.cpp \
+ rccmain.cpp \
+ repositorygen.cpp
+HEADERS = rcc.h
+RESOURCES += binarycreator.qrc \ No newline at end of file
diff --git a/tools/binarycreator/binarycreator.qrc b/tools/binarycreator/binarycreator.qrc
new file mode 100644
index 000000000..a32a88e27
--- /dev/null
+++ b/tools/binarycreator/binarycreator.qrc
@@ -0,0 +1,8 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource>
+ <file>resources/default_icon_mac.icns</file>
+ <file>resources/copylibsintobundle.sh</file>
+ <file>resources/mkdmg.sh</file>
+ <file alias="resources/installerbase.ico">../../src/sdk/installerbase.ico</file>
+</qresource>
+</RCC>
diff --git a/tools/binarycreator/rcc/qcorecmdlineargs_p.h b/tools/binarycreator/rcc/qcorecmdlineargs_p.h
new file mode 100644
index 000000000..0f6c48dbb
--- /dev/null
+++ b/tools/binarycreator/rcc/qcorecmdlineargs_p.h
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial Usage
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCORECMDLINEARGS_P_H
+#define QCORECMDLINEARGS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtCore/qstring.h"
+#include "QtCore/qstringlist.h"
+
+QT_BEGIN_NAMESPACE
+
+static inline QStringList qCmdLineArgs(int argc, char *argv[])
+{
+ QStringList args;
+ for (int i = 0; i != argc; ++i)
+ args += QString::fromLocal8Bit(argv[i]);
+ return args;
+}
+
+
+QT_END_NAMESPACE
+
+#endif // QCORECMDLINEARGS_WIN_P_H
diff --git a/tools/binarycreator/rcc/rcc.cpp b/tools/binarycreator/rcc/rcc.cpp
new file mode 100644
index 000000000..de1cfc566
--- /dev/null
+++ b/tools/binarycreator/rcc/rcc.cpp
@@ -0,0 +1,958 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2009-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@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 qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "rcc.h"
+
+#include <QtCore/QByteArray>
+#include <QtCore/QDateTime>
+#include <QtCore/QDebug>
+#include <QtCore/QDir>
+#include <QtCore/QDirIterator>
+#include <QtCore/QFile>
+#include <QtCore/QIODevice>
+#include <QtCore/QLocale>
+#include <QtCore/QStack>
+
+#include <QtXml/QDomDocument>
+
+QT_BEGIN_NAMESPACE
+
+enum {
+ CONSTANT_USENAMESPACE = 1,
+ CONSTANT_COMPRESSLEVEL_DEFAULT = -1,
+ CONSTANT_COMPRESSTHRESHOLD_DEFAULT = 70
+};
+
+
+#define writeString(s) write(s, sizeof(s))
+
+void RCCResourceLibrary::write(const char *str, int len)
+{
+ --len; // trailing \0 on string literals...
+ int n = m_out.size();
+ m_out.resize(n + len);
+ memcpy(m_out.data() + n, str, len);
+}
+
+void RCCResourceLibrary::writeByteArray(const QByteArray &other)
+{
+ m_out.append(other);
+}
+
+static inline QString msgOpenReadFailed(const QString &fname, const QString &why)
+{
+ return QString::fromUtf8("Unable to open %1 for reading: %2\n").arg(fname).arg(why);
+}
+
+
+///////////////////////////////////////////////////////////
+//
+// RCCFileInfo
+//
+///////////////////////////////////////////////////////////
+
+class RCCFileInfo
+{
+public:
+ enum Flags
+ {
+ NoFlags = 0x00,
+ Compressed = 0x01,
+ Directory = 0x02
+ };
+
+ RCCFileInfo(const QString &name = QString(), const QFileInfo &fileInfo = QFileInfo(),
+ QLocale::Language language = QLocale::C,
+ QLocale::Country country = QLocale::AnyCountry,
+ uint flags = NoFlags,
+ int compressLevel = CONSTANT_COMPRESSLEVEL_DEFAULT,
+ int compressThreshold = CONSTANT_COMPRESSTHRESHOLD_DEFAULT);
+ ~RCCFileInfo();
+
+ QString resourceName() const;
+
+public:
+ qint64 writeDataBlob(RCCResourceLibrary &lib, qint64 offset, QString *errorMessage);
+ qint64 writeDataName(RCCResourceLibrary &, qint64 offset);
+ void writeDataInfo(RCCResourceLibrary &lib);
+
+ int m_flags;
+ QString m_name;
+ QLocale::Language m_language;
+ QLocale::Country m_country;
+ QFileInfo m_fileInfo;
+ RCCFileInfo *m_parent;
+ QHash<QString, RCCFileInfo*> m_children;
+ int m_compressLevel;
+ int m_compressThreshold;
+
+ qint64 m_nameOffset;
+ qint64 m_dataOffset;
+ qint64 m_childOffset;
+};
+
+RCCFileInfo::RCCFileInfo(const QString &name, const QFileInfo &fileInfo,
+ QLocale::Language language, QLocale::Country country, uint flags,
+ int compressLevel, int compressThreshold)
+{
+ m_name = name;
+ m_fileInfo = fileInfo;
+ m_language = language;
+ m_country = country;
+ m_flags = flags;
+ m_parent = 0;
+ m_nameOffset = 0;
+ m_dataOffset = 0;
+ m_childOffset = 0;
+ m_compressLevel = compressLevel;
+ m_compressThreshold = compressThreshold;
+}
+
+RCCFileInfo::~RCCFileInfo()
+{
+ qDeleteAll(m_children);
+}
+
+QString RCCFileInfo::resourceName() const
+{
+ QString resource = m_name;
+ for (RCCFileInfo *p = m_parent; p; p = p->m_parent)
+ resource = resource.prepend(p->m_name + QLatin1Char('/'));
+ return QLatin1Char(':') + resource;
+}
+
+void RCCFileInfo::writeDataInfo(RCCResourceLibrary &lib)
+{
+ const bool text = (lib.m_format == RCCResourceLibrary::C_Code);
+ //some info
+ if (text) {
+ if (m_language != QLocale::C) {
+ lib.writeString(" // ");
+ lib.writeByteArray(resourceName().toLocal8Bit());
+ lib.writeString(" [");
+ lib.writeByteArray(QByteArray::number(m_country));
+ lib.writeString("::");
+ lib.writeByteArray(QByteArray::number(m_language));
+ lib.writeString("[\n ");
+ } else {
+ lib.writeString(" // ");
+ lib.writeByteArray(resourceName().toLocal8Bit());
+ lib.writeString("\n ");
+ }
+ }
+
+ //pointer data
+ if (m_flags & RCCFileInfo::Directory) {
+ // name offset
+ lib.writeNumber4(m_nameOffset);
+
+ // flags
+ lib.writeNumber2(m_flags);
+
+ // child count
+ lib.writeNumber4(m_children.size());
+
+ // first child offset
+ lib.writeNumber4(m_childOffset);
+ } else {
+ // name offset
+ lib.writeNumber4(m_nameOffset);
+
+ // flags
+ lib.writeNumber2(m_flags);
+
+ // locale
+ lib.writeNumber2(m_country);
+ lib.writeNumber2(m_language);
+
+ //data offset
+ lib.writeNumber4(m_dataOffset);
+ }
+ if (text)
+ lib.writeChar('\n');
+}
+
+qint64 RCCFileInfo::writeDataBlob(RCCResourceLibrary &lib, qint64 offset,
+ QString *errorMessage)
+{
+ const bool text = (lib.m_format == RCCResourceLibrary::C_Code);
+
+ //capture the offset
+ m_dataOffset = offset;
+
+ //find the data to be written
+ QFile file(m_fileInfo.absoluteFilePath());
+ if (!file.open(QFile::ReadOnly)) {
+ *errorMessage = msgOpenReadFailed(m_fileInfo.absoluteFilePath(), file.errorString());
+ return 0;
+ }
+ QByteArray data = file.readAll();
+
+#ifndef QT_NO_COMPRESS
+ // Check if compression is useful for this file
+ if (m_compressLevel != 0 && data.size() != 0) {
+ QByteArray compressed =
+ qCompress(reinterpret_cast<uchar *>(data.data()), data.size(), m_compressLevel);
+
+ int compressRatio = int(100.0 * (data.size() - compressed.size()) / data.size());
+ if (compressRatio >= m_compressThreshold) {
+ data = compressed;
+ m_flags |= Compressed;
+ }
+ }
+#endif // QT_NO_COMPRESS
+
+ // some info
+ if (text) {
+ lib.writeString(" // ");
+ lib.writeByteArray(m_fileInfo.absoluteFilePath().toLocal8Bit());
+ lib.writeString("\n ");
+ }
+
+ // write the length
+
+ lib.writeNumber4(data.size());
+ if (text)
+ lib.writeString("\n ");
+ offset += 4;
+
+ // write the payload
+ const char *p = data.constData();
+ if (text) {
+ for (int i = data.size(), j = 0; --i >= 0; --j) {
+ lib.writeHex(*p++);
+ if (j == 0) {
+ lib.writeString("\n ");
+ j = 16;
+ }
+ }
+ } else {
+ for (int i = data.size(); --i >= 0; )
+ lib.writeChar(*p++);
+ }
+ offset += data.size();
+
+ // done
+ if (text)
+ lib.writeString("\n ");
+ return offset;
+}
+
+qint64 RCCFileInfo::writeDataName(RCCResourceLibrary &lib, qint64 offset)
+{
+ const bool text = (lib.m_format == RCCResourceLibrary::C_Code);
+
+ // capture the offset
+ m_nameOffset = offset;
+
+ // some info
+ if (text) {
+ lib.writeString(" // ");
+ lib.writeByteArray(m_name.toLocal8Bit());
+ lib.writeString("\n ");
+ }
+
+ // write the length
+ lib.writeNumber2(m_name.length());
+ if (text)
+ lib.writeString("\n ");
+ offset += 2;
+
+ // write the hash
+ lib.writeNumber4(qHash(m_name));
+ if (text)
+ lib.writeString("\n ");
+ offset += 4;
+
+ // write the m_name
+ const QChar *unicode = m_name.unicode();
+ for (int i = 0; i < m_name.length(); ++i) {
+ lib.writeNumber2(unicode[i].unicode());
+ if (text && i % 16 == 0)
+ lib.writeString("\n ");
+ }
+ offset += m_name.length()*2;
+
+ // done
+ if (text)
+ lib.writeString("\n ");
+ return offset;
+}
+
+
+///////////////////////////////////////////////////////////
+//
+// RCCResourceLibrary
+//
+///////////////////////////////////////////////////////////
+
+RCCResourceLibrary::Strings::Strings() :
+ TAG_RCC(QLatin1String("RCC")),
+ TAG_RESOURCE(QLatin1String("qresource")),
+ TAG_FILE(QLatin1String("file")),
+ ATTRIBUTE_LANG(QLatin1String("lang")),
+ ATTRIBUTE_PREFIX(QLatin1String("prefix")),
+ ATTRIBUTE_ALIAS(QLatin1String("alias")),
+ ATTRIBUTE_THRESHOLD(QLatin1String("threshold")),
+ ATTRIBUTE_COMPRESS(QLatin1String("compress"))
+{
+}
+
+RCCResourceLibrary::RCCResourceLibrary()
+ : m_root(0),
+ m_format(C_Code),
+ m_verbose(false),
+ m_compressLevel(CONSTANT_COMPRESSLEVEL_DEFAULT),
+ m_compressThreshold(CONSTANT_COMPRESSTHRESHOLD_DEFAULT),
+ m_treeOffset(0),
+ m_namesOffset(0),
+ m_dataOffset(0),
+ m_useNameSpace(CONSTANT_USENAMESPACE),
+ m_errorDevice(0)
+{
+ m_out.reserve(30 * 1000 * 1000);
+}
+
+RCCResourceLibrary::~RCCResourceLibrary()
+{
+ delete m_root;
+}
+
+bool RCCResourceLibrary::interpretResourceFile(QIODevice *inputDevice,
+ const QString &fname, QString currentPath, bool ignoreErrors)
+{
+ Q_ASSERT(m_errorDevice);
+ const QChar slash = QLatin1Char('/');
+ if (!currentPath.isEmpty() && !currentPath.endsWith(slash))
+ currentPath += slash;
+
+ QDomDocument document;
+ {
+ QString errorMsg;
+ int errorLine = 0;
+ int errorColumn = 0;
+ if (!document.setContent(inputDevice, &errorMsg, &errorLine, &errorColumn)) {
+ if (ignoreErrors)
+ return true;
+ const QString msg = QString::fromUtf8("RCC Parse Error: '%1' Line: %2 Column: %3 [%4]\n").arg(fname).arg(errorLine).arg(errorColumn).arg(errorMsg);
+ m_errorDevice->write(msg.toUtf8());
+ return false;
+ }
+ }
+
+ QDomElement domRoot = document.firstChildElement(m_strings.TAG_RCC).toElement();
+ if (!domRoot.isNull() && domRoot.tagName() == m_strings.TAG_RCC) {
+ for (QDomNode node = domRoot.firstChild(); !node.isNull(); node = node.nextSibling()) {
+ if (!node.isElement())
+ continue;
+
+ QDomElement child = node.toElement();
+ if (!child.isNull() && child.tagName() == m_strings.TAG_RESOURCE) {
+ QLocale::Language language = QLocale::c().language();
+ QLocale::Country country = QLocale::c().country();
+
+ if (child.hasAttribute(m_strings.ATTRIBUTE_LANG)) {
+ QString attribute = child.attribute(m_strings.ATTRIBUTE_LANG);
+ QLocale lang = QLocale(attribute);
+ language = lang.language();
+ if (2 == attribute.length()) {
+ // Language only
+ country = QLocale::AnyCountry;
+ } else {
+ country = lang.country();
+ }
+ }
+
+ QString prefix;
+ if (child.hasAttribute(m_strings.ATTRIBUTE_PREFIX))
+ prefix = child.attribute(m_strings.ATTRIBUTE_PREFIX);
+ if (!prefix.startsWith(slash))
+ prefix.prepend(slash);
+ if (!prefix.endsWith(slash))
+ prefix += slash;
+
+ for (QDomNode res = child.firstChild(); !res.isNull(); res = res.nextSibling()) {
+ if (res.isElement() && res.toElement().tagName() == m_strings.TAG_FILE) {
+
+ QString fileName(res.firstChild().toText().data());
+ if (fileName.isEmpty()) {
+ const QString msg = QString::fromUtf8("RCC: Warning: Null node in XML of '%1'\n").arg(fname);
+ m_errorDevice->write(msg.toUtf8());
+ }
+ QString alias;
+ if (res.toElement().hasAttribute(m_strings.ATTRIBUTE_ALIAS))
+ alias = res.toElement().attribute(m_strings.ATTRIBUTE_ALIAS);
+ else
+ alias = fileName;
+
+ int compressLevel = m_compressLevel;
+ if (res.toElement().hasAttribute(m_strings.ATTRIBUTE_COMPRESS))
+ compressLevel = res.toElement().attribute(m_strings.ATTRIBUTE_COMPRESS).toInt();
+ int compressThreshold = m_compressThreshold;
+ if (res.toElement().hasAttribute(m_strings.ATTRIBUTE_THRESHOLD))
+ compressThreshold = res.toElement().attribute(m_strings.ATTRIBUTE_THRESHOLD).toInt();
+
+ // Special case for -no-compress. Overrides all other settings.
+ if (m_compressLevel == -2)
+ compressLevel = 0;
+
+ alias = QDir::cleanPath(alias);
+ while (alias.startsWith(QLatin1String("../")))
+ alias.remove(0, 3);
+ alias = QDir::cleanPath(m_resourceRoot) + prefix + alias;
+
+ QString absFileName = fileName;
+ if (QDir::isRelativePath(absFileName))
+ absFileName.prepend(currentPath);
+ QFileInfo file(absFileName);
+ if (!file.exists()) {
+ m_failedResources.push_back(absFileName);
+ const QString msg = QString::fromUtf8("RCC: Error in '%1': Cannot find file '%2'\n").arg(fname).arg(fileName);
+ m_errorDevice->write(msg.toUtf8());
+ if (ignoreErrors)
+ continue;
+ else
+ return false;
+ } else if (file.isFile()) {
+ const bool arc = addFile(alias, RCCFileInfo(alias.section(slash, -1), file, language, country,
+ RCCFileInfo::NoFlags, compressLevel, compressThreshold));
+ if (!arc)
+ m_failedResources.push_back(absFileName);
+ } else {
+ QDir dir;
+ if (file.isDir()) {
+ dir.setPath(file.filePath());
+ } else {
+ dir.setPath(file.path());
+ dir.setNameFilters(QStringList(file.fileName()));
+ if (alias.endsWith(file.fileName()))
+ alias = alias.left(alias.length()-file.fileName().length());
+ }
+ if (!alias.endsWith(slash))
+ alias += slash;
+ QDirIterator it(dir, QDirIterator::FollowSymlinks|QDirIterator::Subdirectories);
+ while (it.hasNext()) {
+ it.next();
+ QFileInfo child(it.fileInfo());
+ if (child.fileName() != QLatin1String(".") && child.fileName() != QLatin1String("..")) {
+ const bool arc = addFile(alias + child.fileName(),
+ RCCFileInfo(child.fileName(), child, language, country,
+ RCCFileInfo::NoFlags, compressLevel, compressThreshold));
+ if (!arc)
+ m_failedResources.push_back(child.fileName());
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ if (m_root == 0) {
+ const QString msg = QString::fromUtf8("RCC: Warning: No resources in '%1'.\n").arg(fname);
+ m_errorDevice->write(msg.toUtf8());
+ if (!ignoreErrors && m_format == Binary) {
+ // create dummy entry, otherwise loading qith QResource will crash
+ m_root = new RCCFileInfo(QString(), QFileInfo(),
+ QLocale::C, QLocale::AnyCountry, RCCFileInfo::Directory);
+ }
+ }
+
+ return true;
+}
+
+bool RCCResourceLibrary::addFile(const QString &alias, const RCCFileInfo &file)
+{
+ Q_ASSERT(m_errorDevice);
+ if (file.m_fileInfo.size() > 0xffffffff) {
+ const QString msg = QString::fromUtf8("File too big: %1\n").arg(file.m_fileInfo.absoluteFilePath());
+ m_errorDevice->write(msg.toUtf8());
+ return false;
+ }
+ if (!m_root)
+ m_root = new RCCFileInfo(QString(), QFileInfo(), QLocale::C, QLocale::AnyCountry, RCCFileInfo::Directory);
+
+ RCCFileInfo *parent = m_root;
+ const QStringList nodes = alias.split(QLatin1Char('/'));
+ for (int i = 1; i < nodes.size()-1; ++i) {
+ const QString node = nodes.at(i);
+ if (node.isEmpty())
+ continue;
+ if (!parent->m_children.contains(node)) {
+ RCCFileInfo *s = new RCCFileInfo(node, QFileInfo(), QLocale::C, QLocale::AnyCountry, RCCFileInfo::Directory);
+ s->m_parent = parent;
+ parent->m_children.insert(node, s);
+ parent = s;
+ } else {
+ parent = parent->m_children[node];
+ }
+ }
+
+ const QString filename = nodes.at(nodes.size()-1);
+ RCCFileInfo *s = new RCCFileInfo(file);
+ s->m_parent = parent;
+ parent->m_children.insertMulti(filename, s);
+ return true;
+}
+
+void RCCResourceLibrary::reset()
+{
+ if (m_root) {
+ delete m_root;
+ m_root = 0;
+ }
+ m_errorDevice = 0;
+ m_failedResources.clear();
+}
+
+
+bool RCCResourceLibrary::readFiles(bool ignoreErrors, QIODevice &errorDevice)
+{
+ reset();
+ m_errorDevice = &errorDevice;
+ //read in data
+ if (m_verbose) {
+ const QString msg = QString::fromUtf8("Processing %1 files [%2]\n")
+ .arg(m_fileNames.size()).arg(static_cast<int>(ignoreErrors));
+ m_errorDevice->write(msg.toUtf8());
+ }
+ for (int i = 0; i < m_fileNames.size(); ++i) {
+ QFile fileIn;
+ QString fname = m_fileNames.at(i);
+ QString pwd;
+ if (fname == QLatin1String("-")) {
+ fname = QLatin1String("(stdin)");
+ pwd = QDir::currentPath();
+ fileIn.setFileName(fname);
+ if (!fileIn.open(stdin, QIODevice::ReadOnly)) {
+ m_errorDevice->write(msgOpenReadFailed(fname, fileIn.errorString()).toUtf8());
+ return false;
+ }
+ } else {
+ pwd = QFileInfo(fname).path();
+ fileIn.setFileName(fname);
+ if (!fileIn.open(QIODevice::ReadOnly)) {
+ m_errorDevice->write(msgOpenReadFailed(fname, fileIn.errorString()).toUtf8());
+ return false;
+ }
+ }
+ if (m_verbose) {
+ const QString msg = QString::fromUtf8("Interpreting %1\n").arg(fname);
+ m_errorDevice->write(msg.toUtf8());
+ }
+
+ if (!interpretResourceFile(&fileIn, fname, pwd, ignoreErrors))
+ return false;
+ }
+ return true;
+}
+
+QStringList RCCResourceLibrary::dataFiles() const
+{
+ QStringList ret;
+ QStack<RCCFileInfo*> pending;
+
+ if (!m_root)
+ return ret;
+ pending.push(m_root);
+ while (!pending.isEmpty()) {
+ RCCFileInfo *file = pending.pop();
+ for (QHash<QString, RCCFileInfo*>::iterator it = file->m_children.begin();
+ it != file->m_children.end(); ++it) {
+ RCCFileInfo *child = it.value();
+ if (child->m_flags & RCCFileInfo::Directory)
+ pending.push(child);
+ ret.append(child->m_fileInfo.filePath());
+ }
+ }
+ return ret;
+}
+
+// Determine map of resource identifier (':/newPrefix/images/p1.png') to file via recursion
+static void resourceDataFileMapRecursion(const RCCFileInfo *m_root, const QString &path, RCCResourceLibrary::ResourceDataFileMap &m)
+{
+ typedef QHash<QString, RCCFileInfo*>::const_iterator ChildConstIterator;
+ const QChar slash = QLatin1Char('/');
+ const ChildConstIterator cend = m_root->m_children.constEnd();
+ for (ChildConstIterator it = m_root->m_children.constBegin(); it != cend; ++it) {
+ const RCCFileInfo *child = it.value();
+ QString childName = path;
+ childName += slash;
+ childName += child->m_name;
+ if (child->m_flags & RCCFileInfo::Directory) {
+ resourceDataFileMapRecursion(child, childName, m);
+ } else {
+ m.insert(childName, child->m_fileInfo.filePath());
+ }
+ }
+}
+
+RCCResourceLibrary::ResourceDataFileMap RCCResourceLibrary::resourceDataFileMap() const
+{
+ ResourceDataFileMap rc;
+ if (m_root)
+ resourceDataFileMapRecursion(m_root, QString(QLatin1Char(':')), rc);
+ return rc;
+}
+
+bool RCCResourceLibrary::output(QIODevice &outDevice, QIODevice &errorDevice)
+{
+ m_errorDevice = &errorDevice;
+ //write out
+ if (m_verbose)
+ m_errorDevice->write("Outputting code\n");
+ if (!writeHeader()) {
+ m_errorDevice->write("Could not write header\n");
+ return false;
+ }
+ if (m_root) {
+ if (!writeDataBlobs()) {
+ m_errorDevice->write("Could not write data blobs.\n");
+ return false;
+ }
+ if (!writeDataNames()) {
+ m_errorDevice->write("Could not write file names\n");
+ return false;
+ }
+ if (!writeDataStructure()) {
+ m_errorDevice->write("Could not write data tree\n");
+ return false;
+ }
+ }
+ if (!writeInitializer()) {
+ m_errorDevice->write("Could not write footer\n");
+ return false;
+ }
+ outDevice.write(m_out, m_out.size());
+ return true;
+}
+
+void RCCResourceLibrary::writeHex(quint8 tmp)
+{
+ const char * const digits = "0123456789abcdef";
+ writeChar('0');
+ writeChar('x');
+ if (tmp < 16) {
+ writeChar(digits[tmp]);
+ } else {
+ writeChar(digits[tmp >> 4]);
+ writeChar(digits[tmp & 0xf]);
+ }
+ writeChar(',');
+}
+
+void RCCResourceLibrary::writeNumber2(quint16 number)
+{
+ if (m_format == RCCResourceLibrary::Binary) {
+ writeChar(number >> 8);
+ writeChar(number);
+ } else {
+ writeHex(number >> 8);
+ writeHex(number);
+ }
+}
+
+void RCCResourceLibrary::writeNumber4(quint32 number)
+{
+ if (m_format == RCCResourceLibrary::Binary) {
+ writeChar(number >> 24);
+ writeChar(number >> 16);
+ writeChar(number >> 8);
+ writeChar(number);
+ } else {
+ writeHex(number >> 24);
+ writeHex(number >> 16);
+ writeHex(number >> 8);
+ writeHex(number);
+ }
+}
+
+bool RCCResourceLibrary::writeHeader()
+{
+ if (m_format == C_Code) {
+ writeString("/****************************************************************************\n");
+ writeString("** Resource object code\n");
+ writeString("**\n");
+ writeString("** Created: ");
+ writeByteArray(QDateTime::currentDateTime().toString().toLatin1());
+ writeString("\n** by: The Resource Compiler for Qt version ");
+ writeByteArray(QT_VERSION_STR);
+ writeString("\n**\n");
+ writeString("** WARNING! All changes made in this file will be lost!\n");
+ writeString( "*****************************************************************************/\n\n");
+ writeString("#include <QtCore/qglobal.h>\n\n");
+ } else if (m_format == Binary) {
+ writeString("qres");
+ writeNumber4(0);
+ writeNumber4(0);
+ writeNumber4(0);
+ writeNumber4(0);
+ }
+ return true;
+}
+
+bool RCCResourceLibrary::writeDataBlobs()
+{
+ Q_ASSERT(m_errorDevice);
+ if (m_format == C_Code)
+ writeString("static const unsigned char qt_resource_data[] = {\n");
+ else if (m_format == Binary)
+ m_dataOffset = m_out.size();
+ QStack<RCCFileInfo*> pending;
+
+ if (!m_root)
+ return false;
+
+ pending.push(m_root);
+ qint64 offset = 0;
+ QString errorMessage;
+ while (!pending.isEmpty()) {
+ RCCFileInfo *file = pending.pop();
+ for (QHash<QString, RCCFileInfo*>::iterator it = file->m_children.begin();
+ it != file->m_children.end(); ++it) {
+ RCCFileInfo *child = it.value();
+ if (child->m_flags & RCCFileInfo::Directory)
+ pending.push(child);
+ else {
+ offset = child->writeDataBlob(*this, offset, &errorMessage);
+ if (offset == 0)
+ m_errorDevice->write(errorMessage.toUtf8());
+ }
+ }
+ }
+ if (m_format == C_Code)
+ writeString("\n};\n\n");
+ return true;
+}
+
+bool RCCResourceLibrary::writeDataNames()
+{
+ if (m_format == C_Code)
+ writeString("static const unsigned char qt_resource_name[] = {\n");
+ else if (m_format == Binary)
+ m_namesOffset = m_out.size();
+
+ QHash<QString, int> names;
+ QStack<RCCFileInfo*> pending;
+
+ if (!m_root)
+ return false;
+
+ pending.push(m_root);
+ qint64 offset = 0;
+ while (!pending.isEmpty()) {
+ RCCFileInfo *file = pending.pop();
+ for (QHash<QString, RCCFileInfo*>::iterator it = file->m_children.begin();
+ it != file->m_children.end(); ++it) {
+ RCCFileInfo *child = it.value();
+ if (child->m_flags & RCCFileInfo::Directory)
+ pending.push(child);
+ if (names.contains(child->m_name)) {
+ child->m_nameOffset = names.value(child->m_name);
+ } else {
+ names.insert(child->m_name, offset);
+ offset = child->writeDataName(*this, offset);
+ }
+ }
+ }
+ if (m_format == C_Code)
+ writeString("\n};\n\n");
+ return true;
+}
+
+static bool qt_rcc_compare_hash(const RCCFileInfo *left, const RCCFileInfo *right)
+{
+ return qHash(left->m_name) < qHash(right->m_name);
+}
+
+bool RCCResourceLibrary::writeDataStructure()
+{
+ if (m_format == C_Code)
+ writeString("static const unsigned char qt_resource_struct[] = {\n");
+ else if (m_format == Binary)
+ m_treeOffset = m_out.size();
+ QStack<RCCFileInfo*> pending;
+
+ if (!m_root)
+ return false;
+
+ //calculate the child offsets (flat)
+ pending.push(m_root);
+ int offset = 1;
+ while (!pending.isEmpty()) {
+ RCCFileInfo *file = pending.pop();
+ file->m_childOffset = offset;
+
+ //sort by hash value for binary lookup
+ QList<RCCFileInfo*> m_children = file->m_children.values();
+ qSort(m_children.begin(), m_children.end(), qt_rcc_compare_hash);
+
+ //write out the actual data now
+ for (int i = 0; i < m_children.size(); ++i) {
+ RCCFileInfo *child = m_children.at(i);
+ ++offset;
+ if (child->m_flags & RCCFileInfo::Directory)
+ pending.push(child);
+ }
+ }
+
+ //write out the structure (ie iterate again!)
+ pending.push(m_root);
+ m_root->writeDataInfo(*this);
+ while (!pending.isEmpty()) {
+ RCCFileInfo *file = pending.pop();
+
+ //sort by hash value for binary lookup
+ QList<RCCFileInfo*> m_children = file->m_children.values();
+ qSort(m_children.begin(), m_children.end(), qt_rcc_compare_hash);
+
+ //write out the actual data now
+ for (int i = 0; i < m_children.size(); ++i) {
+ RCCFileInfo *child = m_children.at(i);
+ child->writeDataInfo(*this);
+ if (child->m_flags & RCCFileInfo::Directory)
+ pending.push(child);
+ }
+ }
+ if (m_format == C_Code)
+ writeString("\n};\n\n");
+
+ return true;
+}
+
+void RCCResourceLibrary::writeMangleNamespaceFunction(const QByteArray &name)
+{
+ if (m_useNameSpace) {
+ writeString("QT_MANGLE_NAMESPACE(");
+ writeByteArray(name);
+ writeChar(')');
+ } else {
+ writeByteArray(name);
+ }
+}
+
+void RCCResourceLibrary::writeAddNamespaceFunction(const QByteArray &name)
+{
+ if (m_useNameSpace) {
+ writeString("QT_PREPEND_NAMESPACE(");
+ writeByteArray(name);
+ writeChar(')');
+ } else {
+ writeByteArray(name);
+ }
+}
+
+bool RCCResourceLibrary::writeInitializer()
+{
+ if (m_format == C_Code) {
+ //write("\nQT_BEGIN_NAMESPACE\n");
+ QString initName = m_initName;
+ if (!initName.isEmpty()) {
+ initName.prepend(QLatin1Char('_'));
+ initName.replace(QRegExp(QLatin1String("[^a-zA-Z0-9_]")), QLatin1String("_"));
+ }
+
+ //init
+ if (m_useNameSpace)
+ writeString("QT_BEGIN_NAMESPACE\n\n");
+ if (m_root) {
+ writeString("extern bool qRegisterResourceData\n "
+ "(int, const unsigned char *, "
+ "const unsigned char *, const unsigned char *);\n\n");
+ writeString("extern bool qUnregisterResourceData\n "
+ "(int, const unsigned char *, "
+ "const unsigned char *, const unsigned char *);\n\n");
+ }
+ if (m_useNameSpace)
+ writeString("QT_END_NAMESPACE\n\n\n");
+ QString initResources = QLatin1String("qInitResources");
+ initResources += initName;
+ writeString("int ");
+ writeMangleNamespaceFunction(initResources.toLatin1());
+ writeString("()\n{\n");
+
+ if (m_root) {
+ writeString(" ");
+ writeAddNamespaceFunction("qRegisterResourceData");
+ writeString("\n (0x01, qt_resource_struct, "
+ "qt_resource_name, qt_resource_data);\n");
+ }
+ writeString(" return 1;\n");
+ writeString("}\n\n");
+ writeString("Q_CONSTRUCTOR_FUNCTION(");
+ writeMangleNamespaceFunction(initResources.toLatin1());
+ writeString(")\n\n");
+
+ //cleanup
+ QString cleanResources = QLatin1String("qCleanupResources");
+ cleanResources += initName;
+ writeString("int ");
+ writeMangleNamespaceFunction(cleanResources.toLatin1());
+ writeString("()\n{\n");
+ if (m_root) {
+ writeString(" ");
+ writeAddNamespaceFunction("qUnregisterResourceData");
+ writeString("\n (0x01, qt_resource_struct, "
+ "qt_resource_name, qt_resource_data);\n");
+ }
+ writeString(" return 1;\n");
+ writeString("}\n\n");
+ writeString("Q_DESTRUCTOR_FUNCTION(");
+ writeMangleNamespaceFunction(cleanResources.toLatin1());
+ writeString(")\n\n");
+ } else if (m_format == Binary) {
+ int i = 4;
+ char *p = m_out.data();
+ p[i++] = 0; // 0x01
+ p[i++] = 0;
+ p[i++] = 0;
+ p[i++] = 1;
+
+ p[i++] = (m_treeOffset >> 24) & 0xff;
+ p[i++] = (m_treeOffset >> 16) & 0xff;
+ p[i++] = (m_treeOffset >> 8) & 0xff;
+ p[i++] = (m_treeOffset >> 0) & 0xff;
+
+ p[i++] = (m_dataOffset >> 24) & 0xff;
+ p[i++] = (m_dataOffset >> 16) & 0xff;
+ p[i++] = (m_dataOffset >> 8) & 0xff;
+ p[i++] = (m_dataOffset >> 0) & 0xff;
+
+ p[i++] = (m_namesOffset >> 24) & 0xff;
+ p[i++] = (m_namesOffset >> 16) & 0xff;
+ p[i++] = (m_namesOffset >> 8) & 0xff;
+ p[i++] = (m_namesOffset >> 0) & 0xff;
+ }
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/tools/binarycreator/rcc/rcc.h b/tools/binarycreator/rcc/rcc.h
new file mode 100644
index 000000000..205d1a8e9
--- /dev/null
+++ b/tools/binarycreator/rcc/rcc.h
@@ -0,0 +1,144 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2009-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@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 qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef RCC_H
+#define RCC_H
+
+#include <QtCore/QStringList>
+#include <QtCore/QHash>
+#include <QtCore/QString>
+
+QT_BEGIN_NAMESPACE
+
+class RCCFileInfo;
+class QIODevice;
+class QTextStream;
+
+
+class RCCResourceLibrary
+{
+ RCCResourceLibrary(const RCCResourceLibrary &);
+ RCCResourceLibrary &operator=(const RCCResourceLibrary &);
+
+public:
+ RCCResourceLibrary();
+ ~RCCResourceLibrary();
+
+ bool output(QIODevice &out, QIODevice &errorDevice);
+
+ bool readFiles(bool ignoreErrors, QIODevice &errorDevice);
+
+ enum Format { Binary, C_Code };
+ void setFormat(Format f) { m_format = f; }
+ Format format() const { return m_format; }
+
+ void setInputFiles(const QStringList &files) { m_fileNames = files; }
+ QStringList inputFiles() const { return m_fileNames; }
+
+ QStringList dataFiles() const;
+
+ // Return a map of resource identifier (':/newPrefix/images/p1.png') to file.
+ typedef QHash<QString, QString> ResourceDataFileMap;
+ ResourceDataFileMap resourceDataFileMap() const;
+
+ void setVerbose(bool b) { m_verbose = b; }
+ bool verbose() const { return m_verbose; }
+
+ void setInitName(const QString &name) { m_initName = name; }
+ QString initName() const { return m_initName; }
+
+ void setCompressLevel(int c) { m_compressLevel = c; }
+ int compressLevel() const { return m_compressLevel; }
+
+ void setCompressThreshold(int t) { m_compressThreshold = t; }
+ int compressThreshold() const { return m_compressThreshold; }
+
+ void setResourceRoot(const QString &root) { m_resourceRoot = root; }
+ QString resourceRoot() const { return m_resourceRoot; }
+
+ void setUseNameSpace(bool v) { m_useNameSpace = v; }
+ bool useNameSpace() const { return m_useNameSpace; }
+
+ QStringList failedResources() const { return m_failedResources; }
+
+private:
+ struct Strings {
+ Strings();
+ const QString TAG_RCC;
+ const QString TAG_RESOURCE;
+ const QString TAG_FILE;
+ const QString ATTRIBUTE_LANG;
+ const QString ATTRIBUTE_PREFIX;
+ const QString ATTRIBUTE_ALIAS;
+ const QString ATTRIBUTE_THRESHOLD;
+ const QString ATTRIBUTE_COMPRESS;
+ };
+ friend class RCCFileInfo;
+ void reset();
+ bool addFile(const QString &alias, const RCCFileInfo &file);
+ bool interpretResourceFile(QIODevice *inputDevice, const QString &file,
+ QString currentPath = QString(), bool ignoreErrors = false);
+ bool writeHeader();
+ bool writeDataBlobs();
+ bool writeDataNames();
+ bool writeDataStructure();
+ bool writeInitializer();
+ void writeMangleNamespaceFunction(const QByteArray &name);
+ void writeAddNamespaceFunction(const QByteArray &name);
+ void writeHex(quint8 number);
+ void writeNumber2(quint16 number);
+ void writeNumber4(quint32 number);
+ void writeChar(char c) { m_out.append(c); }
+ void writeByteArray(const QByteArray &);
+ void write(const char *, int len);
+
+ const Strings m_strings;
+ RCCFileInfo *m_root;
+ QStringList m_fileNames;
+ QString m_resourceRoot;
+ QString m_initName;
+ Format m_format;
+ bool m_verbose;
+ int m_compressLevel;
+ int m_compressThreshold;
+ int m_treeOffset;
+ int m_namesOffset;
+ int m_dataOffset;
+ bool m_useNameSpace;
+ QStringList m_failedResources;
+ QIODevice *m_errorDevice;
+ QByteArray m_out;
+};
+
+QT_END_NAMESPACE
+
+#endif // RCC_H
diff --git a/tools/binarycreator/rcc/rccmain.cpp b/tools/binarycreator/rcc/rccmain.cpp
new file mode 100644
index 000000000..4256d5db4
--- /dev/null
+++ b/tools/binarycreator/rcc/rccmain.cpp
@@ -0,0 +1,248 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2009-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@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 qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include <rcc.h>
+#include "qcorecmdlineargs_p.h"
+
+#include <QDebug>
+#include <QDir>
+#include <QFile>
+#include <QFileInfo>
+#include <QTextStream>
+
+QT_BEGIN_NAMESPACE
+
+void showHelp(const QString &argv0, const QString &error)
+{
+ fprintf(stderr, "Qt resource compiler\n");
+ if (!error.isEmpty())
+ fprintf(stderr, "%s: %s\n", qPrintable(argv0), qPrintable(error));
+ fprintf(stderr, "Usage: %s [options] <inputs>\n\n"
+ "Options:\n"
+ " -o file write output to file rather than stdout\n"
+ " -name name create an external initialization function with name\n"
+ " -threshold level threshold to consider compressing files\n"
+ " -compress level compress input files by level\n"
+ " -root path prefix resource access path with root path\n"
+ " -no-compress disable all compression\n"
+ " -binary output a binary file for use as a dynamic resource\n"
+ " -namespace turn off namespace macros\n"
+ " -project Output a resource file containing all\n"
+ " files from the current directory\n"
+ " -version display version\n"
+ " -help display this information\n",
+ qPrintable(argv0));
+}
+
+void dumpRecursive(const QDir &dir, QTextStream &out)
+{
+ QFileInfoList entries = dir.entryInfoList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot
+ | QDir::NoSymLinks);
+ foreach (QFileInfo entry, entries) {
+ if (entry.isDir()) {
+ dumpRecursive(entry.filePath(), out);
+ } else {
+ out << QLatin1String("<file>")
+ << entry.filePath()
+ << QLatin1String("</file>\n");
+ }
+ }
+}
+
+int createProject(const QString &outFileName)
+{
+ QDir currentDir = QDir::current();
+ QString currentDirName = currentDir.dirName();
+ if (currentDirName.isEmpty())
+ currentDirName = QLatin1String("root");
+
+ QFile file;
+ bool isOk = false;
+ if (outFileName.isEmpty()) {
+ isOk = file.open(stdout, QFile::WriteOnly | QFile::Text);
+ } else {
+ file.setFileName(outFileName);
+ isOk = file.open(QFile::WriteOnly | QFile::Text);
+ }
+ if (!isOk) {
+ fprintf(stderr, "Unable to open %s: %s\n",
+ outFileName.isEmpty() ? qPrintable(outFileName) : "standard output",
+ qPrintable(file.errorString()));
+ return 1;
+ }
+
+ QTextStream out(&file);
+ out << QLatin1String("<!DOCTYPE RCC><RCC version=\"1.0\">\n"
+ "<qresource>\n");
+
+ // use "." as dir to get relative file pathes
+ dumpRecursive(QDir(QLatin1String(".")), out);
+
+ out << QLatin1String("</qresource>\n"
+ "</RCC>\n");
+
+ return 0;
+}
+
+int runRcc(int argc, char *argv[])
+{
+ QString outFilename;
+ bool helpRequested = false;
+ bool list = false;
+ bool projectRequested = false;
+ QStringList filenamesIn;
+
+ QStringList args = qCmdLineArgs(argc, argv);
+
+ RCCResourceLibrary library;
+
+ //parse options
+ QString errorMsg;
+ for (int i = 1; i < args.count() && errorMsg.isEmpty(); i++) {
+ if (args[i].isEmpty())
+ continue;
+ if (args[i][0] == QLatin1Char('-')) { // option
+ QString opt = args[i];
+ if (opt == QLatin1String("-o")) {
+ if (!(i < argc-1)) {
+ errorMsg = QLatin1String("Missing output name");
+ break;
+ }
+ outFilename = args[++i];
+ } else if (opt == QLatin1String("-name")) {
+ if (!(i < argc-1)) {
+ errorMsg = QLatin1String("Missing target name");
+ break;
+ }
+ library.setInitName(args[++i]);
+ } else if (opt == QLatin1String("-root")) {
+ if (!(i < argc-1)) {
+ errorMsg = QLatin1String("Missing root path");
+ break;
+ }
+ library.setResourceRoot(QDir::cleanPath(args[++i]));
+ if (library.resourceRoot().isEmpty()
+ || library.resourceRoot().at(0) != QLatin1Char('/'))
+ errorMsg = QLatin1String("Root must start with a /");
+ } else if (opt == QLatin1String("-compress")) {
+ if (!(i < argc-1)) {
+ errorMsg = QLatin1String("Missing compression level");
+ break;
+ }
+ library.setCompressLevel(args[++i].toInt());
+ } else if (opt == QLatin1String("-threshold")) {
+ if (!(i < argc-1)) {
+ errorMsg = QLatin1String("Missing compression threshold");
+ break;
+ }
+ library.setCompressThreshold(args[++i].toInt());
+ } else if (opt == QLatin1String("-binary")) {
+ library.setFormat(RCCResourceLibrary::Binary);
+ } else if (opt == QLatin1String("-namespace")) {
+ library.setUseNameSpace(!library.useNameSpace());
+ } else if (opt == QLatin1String("-verbose")) {
+ library.setVerbose(true);
+ } else if (opt == QLatin1String("-list")) {
+ list = true;
+ } else if (opt == QLatin1String("-version") || opt == QLatin1String("-v")) {
+ fprintf(stderr, "Qt Resource Compiler version %s\n", QT_VERSION_STR);
+ return 1;
+ } else if (opt == QLatin1String("-help") || opt == QLatin1String("-h")) {
+ helpRequested = true;
+ } else if (opt == QLatin1String("-no-compress")) {
+ library.setCompressLevel(-2);
+ } else if (opt == QLatin1String("-project")) {
+ projectRequested = true;
+ } else {
+ errorMsg = QString::fromLatin1("Unknown option: '%1'").arg(args[i]);
+ }
+ } else {
+ if (!QFile::exists(args[i])) {
+ qWarning("%s: File does not exist '%s'",
+ qPrintable(args[0]), qPrintable(args[i]));
+ return 1;
+ }
+ filenamesIn.append(args[i]);
+ }
+ }
+
+ if (projectRequested && !helpRequested) {
+ return createProject(outFilename);
+ }
+
+ if (!filenamesIn.size() || !errorMsg.isEmpty() || helpRequested) {
+ showHelp(args[0], errorMsg);
+ return 1;
+ }
+ QFile errorDevice;
+ errorDevice.open(stderr, QIODevice::WriteOnly|QIODevice::Text);
+
+ if (library.verbose())
+ errorDevice.write("Qt resource compiler\n");
+
+ library.setInputFiles(filenamesIn);
+
+ if (!library.readFiles(list, errorDevice))
+ return 1;
+
+ // open output
+ QFile out;
+ QIODevice::OpenMode mode = QIODevice::WriteOnly;
+ if (library.format() == RCCResourceLibrary::C_Code)
+ mode |= QIODevice::Text;
+
+ if (outFilename.isEmpty() || outFilename == QLatin1String("-")) {
+ // using this overload close() only flushes.
+ out.open(stdout, mode);
+ } else {
+ out.setFileName(outFilename);
+ if (!out.open(mode)) {
+ const QString msg = QString::fromUtf8("Unable to open %1 for writing: %2\n").arg(outFilename).arg(out.errorString());
+ errorDevice.write(msg.toUtf8());
+ return 1;
+ }
+ }
+
+ // do the task
+ if (list) {
+ const QStringList data = library.dataFiles();
+ for (int i = 0; i < data.size(); ++i) {
+ out.write(qPrintable(QDir::cleanPath(data.at(i))));
+ out.write("\n");
+ }
+ return 0;
+ }
+
+ return library.output(out, errorDevice) ? 0 : 1;
+}
+
+QT_END_NAMESPACE
diff --git a/tools/binarycreator/resources/copylibsintobundle.sh b/tools/binarycreator/resources/copylibsintobundle.sh
new file mode 100644
index 000000000..2b997f9b5
--- /dev/null
+++ b/tools/binarycreator/resources/copylibsintobundle.sh
@@ -0,0 +1,161 @@
+#!/bin/sh
+
+# this script puts all libs directly needed by the bundle into it
+
+QTDIR=""
+IS_DEBUG=0
+HAVE_CORE=0
+HAVE_SVG=0
+HAVE_PHONON=0
+HAVE_SCRIPT=0
+HAVE_SQL=0
+HAVE_WEBKIT=0
+
+function handleFile()
+{
+ local FILE=$1
+ local BUNDLE=$2
+
+ # all dynamic libs directly needed by the bundle, which are not in /System/Library or in /usr/lib (which are system default libs, which we don't want)
+ local LIBS=`otool -L $FILE | grep -v 'executable_path' | grep -v '/System/Library' | grep -v '/usr/lib' | grep '/' | sed -ne 's,^ *\(.*\) (.*,\1,p'`
+
+ local lib
+ for lib in $LIBS; do
+ local NAME=`basename $lib`
+
+ if echo $NAME | grep 'QtCore' >/dev/null; then
+ HAVE_CORE=1
+ QTDIR=`echo $lib | sed -ne 's,^\(.*\)/lib/[^/]*QtCore.*$,\1,p'`
+ if echo $NAME | grep 'debug' >/dev/null; then
+ IS_DEBUG=1
+ fi
+ elif echo $NAME | grep 'QtSvg' >/dev/null; then
+ HAVE_SVG=1
+ elif echo $NAME | grep 'phonon' >/dev/null; then
+ HAVE_PHONON=1
+ elif echo $NAME | grep 'QtScript' >/dev/null; then
+ HAVE_SCRIPT=1
+ elif echo $NAME | grep 'QtSql' >/dev/null; then
+ HAVE_SQL=1
+ elif echo $NAME | grep 'QtWebKit' >/dev/null; then
+ HAVE_WEBKIT=1
+ fi
+
+ if [ `basename $FILE` != $NAME ]; then
+
+ # this part handles libraries which are Mac OS X frameworks
+ if echo $lib | grep '\.framework' >/dev/null; then
+ local FRAMEWORKPATH=`echo $lib | sed -ne 's,\(.*\.framework\).*,\1,p'`
+ local FRAMEWORKNAME=`basename $FRAMEWORKPATH`
+ local NEWFRAMEWORKPATH=`echo $lib | sed -ne "s,.*\($FRAMEWORKNAME\),\1,p"`
+
+ # Qt installed via the precompled binaries...
+ if [ $FRAMEWORKPATH = $FRAMEWORKNAME ]; then
+ FRAMEWORKPATH="/Library/Frameworks/$FRAMEWORKNAME"
+ if [ ! -e "$FRAMEWORKPATH" ]; then
+ echo "Framework $FRAMEWORKNAME not found."
+ exit 1
+ fi
+ fi
+
+ if [ ! -e "$BUNDLE/Contents/Frameworks/$NEWFRAMEWORKPATH" ]; then
+ echo Embedding framework $FRAMEWORKNAME
+
+
+ # copy the framework into the bundle
+ cp -R $FRAMEWORKPATH $BUNDLE/Contents/Frameworks
+ # remove debug libs we've copied
+ find $BUNDLE/Contents/Frameworks/$FRAMEWORKNAME -regex '.*_debug\(\.dSYM\)*' | xargs rm -rf
+
+ handleFile "$BUNDLE/Contents/Frameworks/$NEWFRAMEWORKPATH" "$BUNDLE"
+ fi
+ # and inform the dynamic linker about this
+ install_name_tool -change $lib @executable_path/../Frameworks/$NEWFRAMEWORKPATH $FILE
+
+
+ # this part handles 'normal' dynamic libraries (.dylib)
+ else
+ if [ ! -e "$BUNDLE/Contents/Frameworks/$NAME" ]; then
+ echo Embedding library $NAME
+
+ # Qt installed via the precompled binaries...
+ if [ $lib = $NAME ]; then
+ lib="/Library/Frameworks/$NAME"
+ if [ ! -e "$lib" ]; then
+ lib="/usr/lib/$NAME"
+ fi
+ if [ ! -e "$lib" ]; then
+ echo "Library $NAME not found."
+ exit 1
+ fi
+ fi
+
+ # copy the lib into the bundle
+ cp $lib $BUNDLE/Contents/Frameworks
+ handleFile "$BUNDLE/Contents/Frameworks/$NAME" "$BUNDLE"
+ fi
+
+ # and inform the dynamic linker about this
+ install_name_tool -change $lib @executable_path/../Frameworks/$NAME $FILE
+ fi
+
+ fi
+ done
+}
+
+function handleQtPlugins()
+{
+ local PLUGINPATH=$QTDIR/plugins
+
+ # QTDIR was not found, then we're using /Developer/Applications/Qt
+ if [ "$PLUGINPATH" = "/plugins" ]; then
+ PLUGINPATH="/Developer/Applications/Qt/plugins"
+ fi
+
+ CLASS=$1
+ EXECUTABLE=$2
+ BUNDLE=$3
+ mkdir -p $BUNDLE/Contents/plugins/$CLASS
+ echo Add $CLASS plugins
+ for plugin in `ls $PLUGINPATH/$CLASS/*`; do
+ plugin=`basename $plugin`
+ if echo $plugin | grep 'debug' >/dev/null; then
+ #if [ $IS_DEBUG -eq 1 ]; then
+ cp "$PLUGINPATH/$CLASS/$plugin" $BUNDLE/Contents/plugins/$CLASS
+ install_name_tool -change $plugin @executable_path/../plugins/$CLASS/$plugin $EXECUTABLE
+ handleFile $BUNDLE/Contents/plugins/$CLASS/$plugin $BUNDLE
+ #fi
+ else
+ #if [ $IS_DEBUG -eq 0 ]; then
+ cp "$PLUGINPATH/$CLASS/$plugin" $BUNDLE/Contents/plugins/$CLASS
+ install_name_tool -change $plugin @executable_path/../plugins/$CLASS/$plugin $EXECUTABLE
+ handleFile $BUNDLE/Contents/plugins/$CLASS/$plugin $BUNDLE
+ #fi
+ fi
+ done
+}
+
+# the app bundle we're working with
+BUNDLE=$1
+# the executable inside of the bundle
+EXECUTABLE=$BUNDLE/Contents/MacOS/`xargs < $BUNDLE/Contents/Info.plist | sed -ne 's,.*<key>CFBundleExecutable</key> <string>\([^<]*\)</string>.*,\1,p'`
+
+mkdir -p $BUNDLE/Contents/Frameworks
+
+handleFile $EXECUTABLE $BUNDLE
+
+if [ $HAVE_CORE -eq 1 ]; then
+ handleQtPlugins "imageformats" "$EXECUTABLE" "$BUNDLE"
+fi
+if [ $HAVE_SVG -eq 1 ]; then
+ handleQtPlugins "iconengines" "$EXECUTABLE" "$BUNDLE"
+fi
+if [ $HAVE_PHONON -eq 1 ]; then
+ handleQtPlugins "phonon_backend" "$EXECUTABLE" "$BUNDLE"
+fi
+if [ $HAVE_SQL -eq 1 ]; then
+ handleQtPlugins "sqldrivers" "$EXECUTABLE" "$BUNDLE"
+fi
+if [ $HAVE_WEBKIT -eq 1 ]; then
+ handleQtPlugins "codecs" "$EXECUTABLE" "$BUNDLE"
+fi
diff --git a/tools/binarycreator/resources/default_icon_mac.icns b/tools/binarycreator/resources/default_icon_mac.icns
new file mode 100644
index 000000000..8d870d649
--- /dev/null
+++ b/tools/binarycreator/resources/default_icon_mac.icns
Binary files differ
diff --git a/tools/binarycreator/resources/mkdmg.sh b/tools/binarycreator/resources/mkdmg.sh
new file mode 100644
index 000000000..46606c125
--- /dev/null
+++ b/tools/binarycreator/resources/mkdmg.sh
@@ -0,0 +1,37 @@
+#!/bin/sh
+#
+# Creates a disk image (dmg) on Mac OS X from the command line.
+# usage:
+# mkdmg <volname> <vers> <srcdir>
+#
+# Where <volname> is the name to use for the mounted image, <vers> is the version
+# number of the volume and <srcdir> is where the contents to put on the dmg are.
+#
+# The result will be a file called <volname>-<vers>.dmg
+
+if [ $# != 2 ]; then
+ echo "usage: mkdmg.sh volname srcdir"
+ exit 0
+fi
+
+VOL="$1"
+FILES="$2"
+PATHNAME=`dirname $FILES`
+
+DMG=`mktemp "/tmp/$VOL.XXXXXX.dmg"`
+
+# create temporary disk image and format, ejecting when done
+SIZE=`du -sk ${FILES} | sed -n 's,^\([0-9]*\).*,\1,p'`
+SIZE=$((${SIZE}/1000+1))
+hdiutil create "$DMG" -megabytes ${SIZE} -ov -volname "$VOL" -type UDIF -fs HFS+ >/dev/null
+DISK=`hdid "$DMG" | sed -ne 's,^\(.*\) *Apple_H.*,\1,p'`
+MOUNT=`hdid "$DMG" | sed -ne 's,^.*Apple_HFS[^/]*\(/.*\)$,\1,p'`
+
+# mount and copy files onto volume
+cp -R "$PATHNAME/`basename $FILES`" "$MOUNT"
+hdiutil eject $DISK >/dev/null
+
+# convert to compressed image, delete temp image
+rm -f "$PATHNAME/${VOL}.dmg"
+hdiutil convert "$DMG" -format UDZO -o "$PATHNAME/${VOL}.dmg" >/dev/null
+rm -f "$DMG"
diff --git a/tools/common/repositorygen.cpp b/tools/common/repositorygen.cpp
new file mode 100644
index 000000000..84ed0e658
--- /dev/null
+++ b/tools/common/repositorygen.cpp
@@ -0,0 +1,622 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@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 qt-info@nokia.com.
+**
+**************************************************************************/
+#include "repositorygen.h"
+
+#include <fileutils.h>
+#include <errors.h>
+#include <lib7z_facade.h>
+#include <settings.h>
+
+#include <kdupdater.h>
+
+#include <QtCore/QCryptographicHash>
+#include <QtCore/QDirIterator>
+
+#include <QtXml/QDomDocument>
+
+#include <iostream>
+
+using namespace QInstallerTools;
+
+void QInstallerTools::printRepositoryGenOptions()
+{
+ std::cout << " -c|--config dir The directory containing the installer configuration" << std::endl;
+
+ std::cout << " -p|--packages dir The directory containing the available packages." << std::endl;
+ std::cout << " Defaults to the current working directory." << std::endl;
+
+ std::cout << " -e|--exclude p1,...,pn Exclude the given packages." << std::endl;
+ std::cout << " -i|--include p1,...,pn Include the given packages and their dependencies from the "
+ << "repository." << std::endl;
+ std::cout << " --ignore-translations Don't use any translation" << std::endl;
+ std::cout << " --ignore-invalid-packages Ignore all invalid packages instead of aborting." << std::endl;
+}
+
+void QInstallerTools::compressDirectory(const QStringList &paths, const QString &archivePath)
+{
+ foreach (const QString &path, paths) {
+ if (!QFileInfo(path).exists())
+ throw QInstaller::Error(QObject::tr("Folder %1 does not exist.").arg(path));
+ }
+
+ QFile archive(archivePath);
+ QInstaller::openForWrite(&archive, archivePath);
+ Lib7z::createArchive(&archive, paths);
+}
+
+void QInstallerTools::compressMetaDirectories(const QString &repoDir)
+{
+ QDir dir(repoDir);
+ const QStringList sub = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
+ foreach (const QString &i, sub) {
+ QDir sd(dir);
+ sd.cd(i);
+ const QString absPath = sd.absolutePath();
+ const QString fn = QLatin1String("meta.7z");
+ const QString tmpTarget = repoDir + QLatin1String("/") +fn;
+ compressDirectory(QStringList() << absPath, tmpTarget);
+ QFile tmp(tmpTarget);
+ const QString finalTarget = absPath + QLatin1String("/") + fn;
+ if (!tmp.rename(finalTarget)) {
+ throw QInstaller::Error(QObject::tr("Could not move %1 to %2").arg(tmpTarget,
+ finalTarget));
+ }
+ }
+}
+
+void QInstallerTools::generateMetaDataDirectory(const QString &outDir, const QString &dataDir,
+ const PackageInfoVector &packages, const QString &appName, const QString &appVersion,
+ const QString &redirectUpdateUrl)
+{
+ QString metapath = outDir;
+ if (QFileInfo(metapath).isRelative())
+ metapath = QDir::cleanPath(QDir::current().absoluteFilePath(metapath));
+ qDebug() << "Generating meta data...";
+
+ if (!QFile::exists(metapath))
+ QInstaller::mkpath(metapath);
+
+ QDomDocument doc;
+ QDomElement root;
+ // use existing Updates.xml, if any
+ QFile existingUpdatesXml(QFileInfo(dataDir, QLatin1String("Updates.xml")).absoluteFilePath());
+ if (!existingUpdatesXml.open(QIODevice::ReadOnly) || !doc.setContent(&existingUpdatesXml)) {
+ root = doc.createElement(QLatin1String("Updates"));
+ root.appendChild(doc.createElement(QLatin1String("ApplicationName"))).appendChild(
+ doc.createTextNode(appName));
+ root.appendChild(doc.createElement(QLatin1String("ApplicationVersion"))).appendChild(
+ doc.createTextNode(appVersion));
+ root.appendChild(doc.createElement(QLatin1String("Checksum"))).appendChild(
+ doc.createTextNode(QLatin1String("true")));
+ if (!redirectUpdateUrl.isEmpty()) {
+ root.appendChild(doc.createElement(QLatin1String("RedirectUpdateUrl"))).appendChild(
+ doc.createTextNode(redirectUpdateUrl));
+ }
+ } else {
+ root = doc.documentElement();
+ }
+
+ for (PackageInfoVector::const_iterator it = packages.begin(); it != packages.end(); ++it) {
+ const QString packageXmlPath = QString::fromLatin1("%1/meta/package.xml").arg(it->directory);
+ qDebug() << QString::fromLatin1("\tGenerating meta data for package %1 using %2.").arg(
+ it->name, packageXmlPath);
+
+ // remove existing entry for this component from existing Updates.xml
+ const QDomNodeList packageNodes = root.childNodes();
+ for (int i = 0; i < packageNodes.count(); ++i) {
+ const QDomNode node = packageNodes.at(i);
+ if (node.nodeName() != QLatin1String("PackageUpdate"))
+ continue;
+ if (node.firstChildElement(QLatin1String("Name")).text() != it->name)
+ continue;
+ root.removeChild(node);
+ --i;
+ }
+
+ QDomDocument packageXml;
+ QFile file(packageXmlPath);
+ QInstaller::openForRead(&file, packageXmlPath);
+ QString errMsg;
+ int col = 0;
+ int line = 0;
+ if (!packageXml.setContent(&file, &errMsg, &line, &col)) {
+ throw QInstaller::Error(QObject::tr("Could not parse %1: line: %2, column: %3: %4 (%5)")
+ .arg(packageXmlPath, QString::number(line), QString::number(col), errMsg, it->name));
+ }
+ const QDomNode package = packageXml.firstChildElement(QLatin1String("Package"));
+
+ QDomElement update = doc.createElement(QLatin1String("PackageUpdate"));
+
+ const QDomNodeList childNodes = package.childNodes();
+ for (int i = 0; i < childNodes.count(); ++i) {
+ const QDomNode node = childNodes.at(i);
+ // just skip the comments...
+ if (node.isComment())
+ continue;
+ const QString key = node.nodeName();
+ if (key == QLatin1String("UserInterfaces"))
+ continue;
+ if (key == QLatin1String("Translations"))
+ continue;
+ if (key == QLatin1String("Licenses"))
+ continue;
+ const QString value = node.toElement().text();
+ QDomElement element = doc.createElement(key);
+ for (int i = 0; i < node.attributes().size(); i++) {
+ element.setAttribute(node.attributes().item(i).toAttr().name(),
+ node.attributes().item(i).toAttr().value());
+ }
+ update.appendChild(element).appendChild(doc.createTextNode(value));
+ }
+
+ // get the size of the data
+ quint64 componentSize = 0;
+ quint64 compressedComponentSize = 0;
+
+ const QString cmpDataDir = QString::fromLatin1("%1/%2").arg(dataDir, it->name);
+ const QFileInfoList entries = !QDir(cmpDataDir + QLatin1String("/data")).exists()
+ ? QDir(cmpDataDir).entryInfoList(QDir::Files | QDir::NoDotAndDotDot)
+ : QDir(cmpDataDir + QLatin1String("/data")).entryInfoList(QDir::Files
+ | QDir::Dirs | QDir::NoDotAndDotDot);
+
+ foreach (const QFileInfo &fi, entries) {
+ if (fi.isHidden())
+ continue;
+
+ try {
+ if (fi.isDir()) {
+ QDirIterator recursDirIt(fi.filePath(), QDirIterator::Subdirectories);
+ while (recursDirIt.hasNext()) {
+ componentSize += QFile(recursDirIt.next()).size();
+ compressedComponentSize += QFile(recursDirIt.next()).size();
+ }
+ } else if (Lib7z::isSupportedArchive(fi.filePath())) {
+ // if it's an archive already, list its files and sum the uncompressed sizes
+ QFile archive(fi.filePath());
+ compressedComponentSize += archive.size();
+ archive.open(QIODevice::ReadOnly);
+ const QVector< Lib7z::File > files = Lib7z::listArchive(&archive);
+ for (QVector< Lib7z::File >::const_iterator fileIt = files.begin();
+ fileIt != files.end(); ++fileIt) {
+ componentSize += fileIt->uncompressedSize;
+ }
+ } else {
+ // otherwise just add its size
+ componentSize += fi.size();
+ compressedComponentSize += fi.size();
+ }
+ } catch(...) {
+ // ignore, that's just about the sizes - and size doesn't matter, you know?
+ }
+ }
+
+ // add fake update files
+ const QStringList platforms = QStringList() << QLatin1String("Windows") << QLatin1String("MacOSX")
+ << QLatin1String("Linux");
+ foreach (const QString &platform, platforms) {
+ QDomElement file = doc.createElement(QLatin1String("UpdateFile"));
+ file.setAttribute(QLatin1String("OS"), platform);
+ file.setAttribute(QLatin1String("UncompressedSize"), componentSize);
+ file.setAttribute(QLatin1String("CompressedSize"), compressedComponentSize);
+ file.appendChild(doc.createTextNode(QLatin1String("(null)")));
+ update.appendChild(file);
+ }
+
+ root.appendChild(update);
+
+ if (!QDir(metapath).mkpath(it->name))
+ throw QInstaller::Error(QObject::tr("Could not create directory %1.").arg(it->name));
+
+ // copy scripts
+ const QString script = package.firstChildElement(QLatin1String("Script")).text();
+ if (!script.isEmpty()) {
+
+ QFile scriptFile(script);
+ QString scriptContent;
+ if (scriptFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ QTextStream in(&scriptFile);
+ scriptContent = in.readAll();
+ }
+
+ // added the xml tag RequiresAdminRights to the xml if somewhere addElevatedOperation is used
+ if (scriptContent.contains(QLatin1String("addElevatedOperation"))) {
+ QDomElement requiresAdminRightsElement =
+ doc.createElement(QLatin1String("RequiresAdminRights"));
+ requiresAdminRightsElement.appendChild(doc.createTextNode(QLatin1String("true")));
+ }
+
+ qDebug() << "\tCopying associated script" << script << "into the meta package...";
+ QString fromLocation(QString::fromLatin1("%1/meta/%2").arg(it->directory, script));
+ QString toLocation(QString::fromLatin1("%1/%2/%3").arg(metapath, it->name, script));
+ if (!QFile::copy(fromLocation, toLocation)) {
+ qDebug() << "failed!";
+ throw QInstaller::Error(QObject::tr("Could not copy the script %1 to its target location %2.")
+ .arg(fromLocation, toLocation));
+ } else {
+ qDebug() << "\tdone.";
+ }
+ }
+
+ // copy user interfaces
+ const QDomNodeList uiNodes = package.firstChildElement(QLatin1String("UserInterfaces")).childNodes();
+ QStringList userinterfaces;
+ for (int i = 0; i < uiNodes.count(); ++i) {
+ const QDomNode node = uiNodes.at(i);
+ if (node.nodeName() != QLatin1String("UserInterface"))
+ continue;
+
+ const QDir dir(QString::fromLatin1("%1/meta").arg(it->directory));
+ const QStringList uis = dir.entryList(QStringList(node.toElement().text()), QDir::Files);
+ if (uis.isEmpty()) {
+ throw QInstaller::Error(QObject::tr("Couldn't find any user interface matching %1 while "
+ "copying user interfaces of %2.").arg(node.toElement().text(), it->name));
+ }
+
+ for (QStringList::const_iterator ui = uis.begin(); ui != uis.end(); ++ui) {
+ qDebug() << "\tCopying associated user interface " << *ui << " into the meta "
+ "package...";
+ userinterfaces.push_back(*ui);
+ if (!QFile::copy(QString::fromLatin1("%1/meta/%2").arg(it->directory, *ui),
+ QString::fromLatin1("%1/%2/%3").arg(metapath, it->name, *ui))) {
+ qDebug() << "failed!";
+ throw QInstaller::Error(QObject::tr("Could not copy the UI file %1 to its target "
+ "location %2.").arg(*ui, it->name));
+ } else {
+ qDebug() << "done";
+ }
+ }
+ }
+
+ if (!userinterfaces.isEmpty()) {
+ update.appendChild(doc.createElement(QLatin1String("UserInterfaces")))
+ .appendChild(doc.createTextNode(userinterfaces.join(QChar::fromLatin1(','))));
+ }
+
+ // copy translations
+ const QDomNodeList qmNodes = package.firstChildElement(QLatin1String("Translations")).childNodes();
+ QStringList translations;
+ if (!qApp->arguments().contains(QString::fromLatin1("--ignore-translations"))) {
+ for (int i = 0; i < qmNodes.count(); ++i) {
+ const QDomNode node = qmNodes.at(i);
+ if (node.nodeName() != QLatin1String("Translation"))
+ continue;
+
+ const QDir dir(QString::fromLatin1("%1/meta").arg(it->directory));
+ const QStringList qms = dir.entryList(QStringList(node.toElement().text()), QDir::Files);
+ if (qms.isEmpty()) {
+ throw QInstaller::Error(QObject::tr("Could not find any translation file matching %1 "
+ "while copying translations of %2.").arg(node.toElement().text(), it->name));
+ }
+
+ for (QStringList::const_iterator qm = qms.begin(); qm != qms.end(); ++qm) {
+ qDebug() << "\tCopying associated translation " << *qm << " into the meta "
+ "package...";
+ translations.push_back(*qm);
+ if (!QFile::copy(QString::fromLatin1("%1/meta/%2").arg(it->directory, *qm),
+ QString::fromLatin1("%1/%2/%3").arg(metapath, it->name, *qm))) {
+ qDebug() << "failed!";
+ throw QInstaller::Error(QObject::tr("Could not copy the translation %1 to its "
+ "target location %2.").arg(*qm, it->name));
+ } else {
+ qDebug() << "done";
+ }
+ }
+ }
+
+ if (!translations.isEmpty()) {
+ update.appendChild(doc.createElement(QLatin1String("Translations")))
+ .appendChild(doc.createTextNode(translations.join(QChar::fromLatin1(','))));
+ }
+
+ }
+
+ // copy license files
+ const QDomNodeList licenseNodes = package.firstChildElement(QLatin1String("Licenses")).childNodes();
+ for (int i = 0; i < licenseNodes.count(); ++i) {
+ const QDomNode licenseNode = licenseNodes.at(i);
+ if (licenseNode.nodeName() == QLatin1String("License")) {
+ const QString &licenseFile =
+ licenseNode.toElement().attributeNode(QLatin1String("file")).value();
+ const QString &sourceFile =
+ QString::fromLatin1("%1/meta/%2").arg(it->directory).arg(licenseFile);
+ if (!QFile::exists(sourceFile)) {
+ throw QInstaller::Error(QObject::tr("Could not find any license matching %1 while "
+ "copying license files of %2.").arg(licenseFile, it->name));
+ }
+
+ qDebug() << "\tCopying associated license file " << licenseFile << " into "
+ "the meta package...";
+ if (!QFile::copy(sourceFile, QString::fromLatin1("%1/%2/%3")
+ .arg(metapath, it->name, licenseFile))) {
+ qDebug() << "failed!";
+ throw QInstaller::Error(QObject::tr("Could not copy the license file %1 to its "
+ "target location %2.").arg(licenseFile, it->name));
+ } else {
+ qDebug() << "done.";
+ }
+
+ // Translated License files
+ for (int j = 0; j < translations.size(); ++j) {
+ QFileInfo translationFile(translations.at(j));
+ QFileInfo untranslated(licenseFile);
+ const QString &translatedLicenseFile =
+ QString::fromLatin1("%2_%3.%4").arg(untranslated.baseName(),
+ translationFile.baseName(), untranslated.completeSuffix());
+ const QString &translatedSourceFile =
+ QString::fromLatin1("%1/meta/%2").arg(it->directory).arg(translatedLicenseFile);
+ if (!QFile::exists(translatedSourceFile)) {
+ qDebug() << "Could not find translated license file" << translatedSourceFile;
+ continue;
+ }
+
+ qDebug() << "\tCopying associated license file" << translatedLicenseFile
+ << "into the meta package...";
+
+ if (!QFile::copy(translatedSourceFile, QString::fromLatin1("%1/%2/%3")
+ .arg(metapath, it->name, translatedLicenseFile))) {
+ qDebug() << "\tfailed!";
+ } else {
+ qDebug() << "\tdone.";
+ }
+ }
+ }
+ }
+
+ if (licenseNodes.count() > 0)
+ update.appendChild(package.firstChildElement(QLatin1String("Licenses")).cloneNode());
+ }
+
+ doc.appendChild(root);
+
+ const QString updatesXmlFile = QFileInfo(metapath, QLatin1String("Updates.xml")).absoluteFilePath();
+ QFile updatesXml(updatesXmlFile);
+
+ QInstaller::openForWrite(&updatesXml, updatesXmlFile);
+ QInstaller::blockingWrite(&updatesXml, doc.toByteArray());
+}
+
+PackageInfoVector QInstallerTools::createListOfPackages(const QString &packagesDirectory,
+ const QStringList &filteredPackages, FilterType filterType)
+{
+ qDebug() << "Collecting information about available packages...";
+
+ bool ignoreInvalidPackages = qApp->arguments().contains(QString::fromLatin1("--ignore-invalid-packages"));
+
+ PackageInfoVector dict;
+ const QFileInfoList entries = QDir(packagesDirectory)
+ .entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
+ for (QFileInfoList::const_iterator it = entries.begin(); it != entries.end(); ++it) {
+ if (filterType == Exclude) {
+ if (filteredPackages.contains(it->fileName()))
+ continue;
+ } else {
+ if (!filteredPackages.contains(it->fileName()))
+ continue;
+ }
+ qDebug() << QString::fromLatin1("\tfound subdirectory %1").arg(it->fileName());
+ // because the filter is QDir::Dirs - filename means the name of the subdirectory
+ if (it->fileName().contains(QLatin1Char('-'))) {
+ if (ignoreInvalidPackages)
+ continue;
+ throw QInstaller::Error(QObject::tr("Component %1 can't contain '-'. This is not allowed, because "
+ "it is used as the separator between the component name and the version number internally.")
+ .arg(it->fileName()));
+ }
+
+ QFile file(QString::fromLatin1("%1/meta/package.xml").arg(it->filePath()));
+ if (!file.exists()) {
+ if (ignoreInvalidPackages)
+ continue;
+ throw QInstaller::Error(QObject::tr("Component %1 does not contain a package "
+ "description(meta/package.xml is missing).").arg(it->fileName()));
+ }
+
+ file.open(QIODevice::ReadOnly);
+
+ QDomDocument doc;
+ QString error;
+ int errorLine = 0;
+ int errorColumn = 0;
+ if (!doc.setContent(&file, &error, &errorLine, &errorColumn)) {
+ if (ignoreInvalidPackages)
+ continue;
+ throw QInstaller::Error(QObject::tr("Component package description for %1 is invalid. "
+ "Error at line: %2, column: %3 -> %4").arg(it->fileName(), QString::number(errorLine),
+ QString::number(errorColumn), error));
+ }
+
+ const QString name = doc.firstChildElement(QLatin1String("Package"))
+ .firstChildElement(QLatin1String("Name")).text();
+ if (name != it->fileName()) {
+ if (ignoreInvalidPackages)
+ continue;
+ throw QInstaller::Error(QObject::tr("Component folder name must match component name: "
+ "%1 in %2/").arg(name, it->fileName()));
+ }
+
+ PackageInfo info;
+ info.name = name;
+ info.version = doc.firstChildElement(QLatin1String("Package")).
+ firstChildElement(QLatin1String("Version")).text();
+ if (!QRegExp(QLatin1String("[0-9]+((\\.|-)[0-9]+)*")).exactMatch(info.version)) {
+ if (ignoreInvalidPackages)
+ continue;
+ throw QInstaller::Error(QObject::tr("Component version for %1 is invalid! <Version>%2</version>")
+ .arg(it->fileName(), info.version));
+ }
+ info.dependencies = doc.firstChildElement(QLatin1String("Package")).
+ firstChildElement(QLatin1String("Dependencies")).text().split(QRegExp(QLatin1String("\\b(,|, )\\b")),
+ QString::SkipEmptyParts);
+ info.directory = it->filePath();
+ dict.push_back(info);
+
+ qDebug() << QString::fromLatin1("\t- it provides the package %1 - %2").arg(name, info.version);
+ }
+
+ if (dict.isEmpty())
+ qDebug() << "No available packages found at the specified location.";
+
+ return dict;
+}
+
+QMap<QString, QString> QInstallerTools::buildPathToVersionMap(const PackageInfoVector &info)
+{
+ QMap<QString, QString> map;
+ foreach (const PackageInfo &inf, info)
+ map[inf.name] = inf.version;
+ return map;
+}
+
+static void writeSHA1ToNodeWithName(QDomDocument &doc, QDomNodeList &list, const QByteArray &sha1sum,
+ const QString &nodename)
+{
+ qDebug() << "searching sha1sum node for" << nodename;
+ for (int i = 0; i < list.size(); ++i) {
+ QDomNode curNode = list.at(i);
+ QDomNode nameTag = curNode.firstChildElement(QLatin1String("Name"));
+ if (!nameTag.isNull() && nameTag.toElement().text() == nodename) {
+ QDomNode sha1Node = doc.createElement(QLatin1String("SHA1"));
+ sha1Node.appendChild(doc.createTextNode(QString::fromLatin1(sha1sum.toHex().constData())));
+ curNode.appendChild(sha1Node);
+ }
+ }
+}
+
+void QInstallerTools::compressMetaDirectories(const QString &repoDir, const QString &baseDir,
+ const QMap<QString, QString> &versionMapping)
+{
+ QDomDocument doc;
+ QDomElement root;
+ // use existing Updates.xml, if any
+ QFile existingUpdatesXml(QFileInfo(QDir(repoDir), QLatin1String("Updates.xml")).absoluteFilePath());
+ if (!existingUpdatesXml.open(QIODevice::ReadOnly) || !doc.setContent(&existingUpdatesXml)) {
+ qDebug() << "Could not find Updates.xml";
+ } else {
+ root = doc.documentElement();
+ }
+ existingUpdatesXml.close();
+
+ QDir dir(repoDir);
+ const QStringList sub = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
+ QDomNodeList elements = doc.elementsByTagName(QLatin1String("PackageUpdate"));
+ foreach (const QString &i, sub) {
+ QDir sd(dir);
+ sd.cd(i);
+ const QString path = QString(i).remove(baseDir);
+ const QString versionPrefix = versionMapping[path];
+ if (path.isNull())
+ continue;
+ const QString absPath = sd.absolutePath();
+ const QString fn = QLatin1String(versionPrefix.toLatin1() + "meta.7z");
+ const QString tmpTarget = repoDir + QLatin1String("/") +fn;
+ compressDirectory(QStringList() << absPath, tmpTarget);
+
+ // remove the files that got compressed
+ QInstaller::removeFiles(absPath, true);
+
+ QFile tmp(tmpTarget);
+ tmp.open(QFile::ReadOnly);
+ QByteArray fileToCheck = tmp.readAll();
+ QByteArray sha1Sum = QCryptographicHash::hash(fileToCheck, QCryptographicHash::Sha1);
+ writeSHA1ToNodeWithName(doc, elements, sha1Sum, path);
+ const QString finalTarget = absPath + QLatin1String("/") + fn;
+ if (!tmp.rename(finalTarget))
+ throw QInstaller::Error(QObject::tr("Could not move %1 to %2").arg(tmpTarget, finalTarget));
+ }
+
+ QInstaller::openForWrite(&existingUpdatesXml, existingUpdatesXml.fileName());
+ QInstaller::blockingWrite(&existingUpdatesXml, doc.toByteArray());
+ existingUpdatesXml.close();
+}
+
+void QInstallerTools::copyComponentData(const QString &packageDir, const QString &repoDir,
+ PackageInfoVector &infos)
+{
+ for (int i = 0; i < infos.count(); ++i) {
+ const PackageInfo info = infos.at(i);
+ const QString name = info.name;
+ qDebug() << "Copying component data for" << name;
+ const QString dataDirPath = QString::fromLatin1("%1/%2/data").arg(packageDir, name);
+ const QDir dataDir(dataDirPath);
+ if (!QDir().mkpath(QString::fromLatin1("%1/%2").arg(repoDir, name))) {
+ throw QInstaller::Error(QObject::tr("Could not create repository folder for component %1")
+ .arg(name));
+ }
+
+ const QStringList entries = dataDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::Files);
+ foreach (const QString &entry, entries) {
+ QString target;
+ QFileInfo fileInfo(dataDir.absoluteFilePath(entry));
+ if (fileInfo.isFile()) {
+ target = QString::fromLatin1("%1/%2/%4%3").arg(repoDir, name, entry, info.version);
+ QFile tmp(dataDir.absoluteFilePath(entry));
+ qDebug() << QString::fromLatin1("Copying archive from %1 to %2").arg(tmp.fileName(), target);
+ QInstaller::openForRead(&tmp, tmp.fileName());
+ if (!tmp.copy(target)) {
+ throw QInstaller::Error(QObject::tr("Could not copy %1 to %2: %3").arg(tmp.fileName(),
+ target, tmp.errorString()));
+ }
+ } else if (fileInfo.isDir()) {
+ qDebug() << "Compressing data directory" << entry;
+ target = QString::fromLatin1("%1/%2/%4%3.7z").arg(repoDir, name, entry, info.version);
+ QInstallerTools::compressDirectory(QStringList() << dataDir.absoluteFilePath(entry), target);
+ } else {
+ continue;
+ }
+ infos[i].copiedArchives.append(target);
+
+ QFile archiveFile(target);
+ QFile archiveHashFile(archiveFile.fileName() + QLatin1String(".sha1"));
+
+ qDebug() << "Hash is stored in" << archiveHashFile.fileName();
+ qDebug() << "Creating hash of archive" << archiveFile.fileName();
+
+ try {
+ QInstaller::openForRead(&archiveFile, archiveFile.fileName());
+ const QByteArray archiveData = archiveFile.readAll();
+ archiveFile.close();
+
+ QInstaller::openForWrite(&archiveHashFile, archiveHashFile.fileName());
+ const QByteArray hashOfArchiveData = QCryptographicHash::hash(archiveData,
+ QCryptographicHash::Sha1).toHex();
+ archiveHashFile.write(hashOfArchiveData);
+ qDebug() << "Generated sha1 hash:" << hashOfArchiveData;
+ infos[i].copiedArchives.append(archiveHashFile.fileName());
+ archiveHashFile.close();
+ } catch (const QInstaller::Error &/*e*/) {
+ archiveFile.close();
+ archiveHashFile.close();
+ throw;
+ }
+ }
+ }
+}
diff --git a/tools/common/repositorygen.h b/tools/common/repositorygen.h
new file mode 100644
index 000000000..faa7a8083
--- /dev/null
+++ b/tools/common/repositorygen.h
@@ -0,0 +1,77 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@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 qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef QINSTALLER_REPOSITORYGEN_H
+#define QINSTALLER_REPOSITORYGEN_H
+
+#include <QtCore/QString>
+#include <QtCore/QStringList>
+#include <QtCore/QVector>
+
+namespace QInstallerTools {
+
+void printRepositoryGenOptions();
+
+struct PackageInfo
+{
+ QString name;
+ QString version;
+ QString directory;
+ QStringList dependencies;
+ QStringList copiedArchives;
+};
+typedef QVector<PackageInfo> PackageInfoVector;
+
+enum FilterType {
+ Include,
+ Exclude
+};
+
+QMap<QString, QString> buildPathToVersionMap(const PackageInfoVector &info);
+
+void compressMetaDirectories(const QString &repoDir);
+void compressDirectory(const QStringList &paths, const QString &archivePath);
+void compressMetaDirectories(const QString &repoDir, const QString &baseDir,
+ const QMap<QString, QString> &versionMapping);
+
+void copyComponentData(const QString &packageDir, const QString &repoDir, PackageInfoVector &infos);
+
+void generateMetaDataDirectory(const QString &outDir, const QString &dataDir,
+ const PackageInfoVector &packages, const QString &appName,
+ const QString& appVersion, const QString &redirectUpdateUrl = QString());
+
+PackageInfoVector createListOfPackages(const QString &packagesDirectory, const QStringList &filteredPackages,
+ FilterType ftype);
+
+} // namespace QInstallerTools
+
+#endif // QINSTALLER_REPOSITORYGEN_H
diff --git a/tools/extractbinarydata/extractbinarydata.pro b/tools/extractbinarydata/extractbinarydata.pro
index c821a857d..056975fce 100644
--- a/tools/extractbinarydata/extractbinarydata.pro
+++ b/tools/extractbinarydata/extractbinarydata.pro
@@ -1,17 +1,15 @@
TEMPLATE = app
DEPENDPATH += . ..
INCLUDEPATH += . ..
+TARGET = extractbinarydata
-DESTDIR = ../../installerbuilder/bin
+include(../../installerfw.pri)
+
+QT -= gui
+LIBS += -linstaller
CONFIG += console
CONFIG -= app_bundle
+DESTDIR = $$IFW_APP_PATH
-include(../../installerbuilder/libinstaller/libinstaller.pri)
-
-# Input
SOURCES += main.cpp
-
-HEADERS +=
-
-LIBS = -L../../installerbuilder/lib -linstaller $$LIBS
diff --git a/tools/maddehelper/maddehelper.pro b/tools/maddehelper/maddehelper.pro
index df9954eba..bc05bb32e 100644
--- a/tools/maddehelper/maddehelper.pro
+++ b/tools/maddehelper/maddehelper.pro
@@ -1,12 +1,14 @@
-QT += core
-
-QT -= gui
-
+TEMPLATE = app
+DEPENDPATH += . ..
+INCLUDEPATH += . ..
TARGET = maddehelper
-CONFIG += console
-CONFIG -= app_bundle
-TEMPLATE = app
+include(../../installerfw.pri)
+
+QT -= gui
+CONFIG += console
+CONFIG -= app_bundle
+DESTDIR = $$IFW_APP_PATH
SOURCES += main.cpp
diff --git a/tools/repocompare/repocompare.pro b/tools/repocompare/repocompare.pro
index 3e576322a..e8672634b 100644
--- a/tools/repocompare/repocompare.pro
+++ b/tools/repocompare/repocompare.pro
@@ -1,20 +1,18 @@
-#-------------------------------------------------
-#
-# Project created by QtCreator 2011-04-04T09:43:46
-#
-#-------------------------------------------------
-
-QT += core gui network
-
-TARGET = repocompare
TEMPLATE = app
+DEPENDPATH += . ..
+INCLUDEPATH += . ..
+TARGET = repocompare
+
+include(../../installerfw.pri)
+QT += network
+DESTDIR = $$IFW_APP_PATH
SOURCES += main.cpp\
mainwindow.cpp \
repositorymanager.cpp
-HEADERS += mainwindow.h \
+HEADERS += mainwindow.h \
repositorymanager.h
-FORMS += mainwindow.ui
+FORMS += mainwindow.ui
diff --git a/tools/repogen/repogen.cpp b/tools/repogen/repogen.cpp
new file mode 100644
index 000000000..c38018e6d
--- /dev/null
+++ b/tools/repogen/repogen.cpp
@@ -0,0 +1,221 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@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 qt-info@nokia.com.
+**
+**************************************************************************/
+#include "common/repositorygen.h"
+
+#include <errors.h>
+#include <fileutils.h>
+#include <init.h>
+#include <settings.h>
+#include <utils.h>
+#include <lib7z_facade.h>
+
+#include <QtCore/QDir>
+#include <QtCore/QFileInfo>
+
+#include <iostream>
+
+using namespace Lib7z;
+using namespace QInstaller;
+
+static void printUsage()
+{
+ const QString appName = QFileInfo(QCoreApplication::applicationFilePath()).fileName();
+ std::cout << "Usage: " << appName << " [options] repository-dir" << std::endl;
+ std::cout << std::endl;
+ std::cout << "Options:" << std::endl;
+
+ QInstallerTools::printRepositoryGenOptions();
+
+ std::cout << " -u|--updateurl url instructs clients to receive updates from a " << std::endl;
+ std::cout << " different location" << std::endl;
+
+ std::cout << " --update Update a set of existing components (defined by --include "
+ << std::endl;
+ std::cout << " or --exclude) in the repository" << std::endl;
+
+ std::cout << " -v|--verbose Verbose output" << std::endl;
+
+ std::cout << std::endl;
+ std::cout << "Example:" << std::endl;
+ std::cout << " " << appName << " -p ../examples/packages -c ../examples/config -u "
+ "http://www.some-server.com:8080 repository/ com.nokia.sdk" << std::endl;
+}
+
+static int printErrorAndUsageAndExit(const QString &err)
+{
+ std::cerr << qPrintable(err) << std::endl << std::endl;
+ printUsage();
+ return 1;
+}
+
+static QString makeAbsolute(const QString &path)
+{
+ QFileInfo fi(path);
+ if (fi.isAbsolute())
+ return path;
+ return QDir::current().absoluteFilePath(path);
+}
+
+int main(int argc, char** argv)
+{
+ try {
+ QCoreApplication app(argc, argv);
+
+ QInstaller::init();
+
+ QStringList args = app.arguments().mid(1);
+
+ QStringList filteredPackages;
+ bool updateExistingRepository = false;
+ QString packagesDir;
+ QString configDir;
+ QString redirectUpdateUrl;
+ QInstallerTools::FilterType filterType = QInstallerTools::Exclude;
+
+ //TODO: use a for loop without removing values from args like it is in binarycreator.cpp
+ //for (QStringList::const_iterator it = args.begin(); it != args.end(); ++it) {
+ while (!args.isEmpty() && args.first().startsWith(QLatin1Char('-'))) {
+ if (args.first() == QLatin1String("--verbose") || args.first() == QLatin1String("-v")) {
+ args.removeFirst();
+ setVerbose(true);
+ } else if (args.first() == QLatin1String("--exclude") || args.first() == QLatin1String("-e")) {
+ args.removeFirst();
+ if (!filteredPackages.isEmpty())
+ return printErrorAndUsageAndExit(QObject::tr("Error: --include and --exclude are mutually "
+ "exclusive. Use either one or the other."));
+ if (args.isEmpty() || args.first().startsWith(QLatin1Char('-')))
+ return printErrorAndUsageAndExit(QObject::tr("Error: Package to exclude missing"));
+ filteredPackages = args.first().split(QLatin1Char(','));
+ args.removeFirst();
+ } else if (args.first() == QLatin1String("--include") || args.first() == QLatin1String("-i")) {
+ args.removeFirst();
+ if (!filteredPackages.isEmpty())
+ return printErrorAndUsageAndExit(QObject::tr("Error: --include and --exclude are mutual "
+ "exclusive options. Use either one or the other."));
+ if (args.isEmpty() || args.first().startsWith(QLatin1Char('-')))
+ return printErrorAndUsageAndExit(QObject::tr("Error: Package to include missing"));
+ filteredPackages = args.first().split(QLatin1Char(','));
+ args.removeFirst();
+ filterType = QInstallerTools::Include;
+ } else if (args.first() == QLatin1String("--single") || args.first() == QLatin1String("--update")) {
+ args.removeFirst();
+ updateExistingRepository = true;
+ } else if (args.first() == QLatin1String("-p") || args.first() == QLatin1String("--packages")) {
+ args.removeFirst();
+ if (args.isEmpty()) {
+ return printErrorAndUsageAndExit(QObject::tr("Error: Packages parameter missing "
+ "argument"));
+ }
+ if (!QFileInfo(args.first()).exists()) {
+ return printErrorAndUsageAndExit(QObject::tr("Error: Package directory not found "
+ "at the specified location"));
+ }
+ packagesDir = args.first();
+ args.removeFirst();
+ } else if (args.first() == QLatin1String("-c") || args.first() == QLatin1String("--config")) {
+ args.removeFirst();
+ if (args.isEmpty())
+ return printErrorAndUsageAndExit(QObject::tr("Error: Config parameter missing argument"));
+ const QFileInfo fi(args.first());
+ if (!fi.exists()) {
+ return printErrorAndUsageAndExit(QObject::tr("Error: Config directory %1 not found "
+ "at the specified location").arg(args.first()));
+ }
+ if (!fi.isDir()) {
+ return printErrorAndUsageAndExit(QObject::tr("Error: Configuration %1 is not a "
+ "directory").arg(args.first()));
+ }
+ if (!fi.isReadable()) {
+ return printErrorAndUsageAndExit(QObject::tr("Error: Config directory %1 is not "
+ "readable").arg(args.first()));
+ }
+ configDir = args.first();
+ args.removeFirst();
+ } else if (args.first() == QLatin1String("-u") || args.first() == QLatin1String("--updateurl")) {
+ args.removeFirst();
+ if (args.isEmpty())
+ return printErrorAndUsageAndExit(QObject::tr("Error: Config parameter missing argument"));
+ redirectUpdateUrl = args.first();
+ args.removeFirst();
+ } else if (args.first() == QLatin1String("--ignore-translations")
+ || args.first() == QLatin1String("--ignore-invalid-packages")) {
+ args.removeFirst();
+ } else {
+ printUsage();
+ return 1;
+ }
+ }
+
+ if ((packagesDir.isEmpty() || configDir.isEmpty() || args.count() != 1)) {
+ printUsage();
+ return 1;
+ }
+
+ const QString repositoryDir = makeAbsolute(args.first());
+
+ if (!updateExistingRepository && QFile::exists(repositoryDir)) {
+ throw QInstaller::Error(QObject::tr("Repository target folder %1 already exists!")
+ .arg(repositoryDir));
+ }
+
+ QInstallerTools::PackageInfoVector packages = QInstallerTools::createListOfPackages(packagesDir,
+ filteredPackages, filterType);
+ QMap<QString, QString> pathToVersionMapping = buildPathToVersionMap(packages);
+
+ foreach (const QInstallerTools::PackageInfo &package, packages) {
+ const QFileInfo fi(repositoryDir, package.name);
+ if (fi.exists())
+ removeDirectory(fi.absoluteFilePath());
+ }
+
+ copyComponentData(packagesDir, repositoryDir, packages);
+
+ TempDirDeleter tmpDeleter;
+ const QString metaTmp = createTemporaryDirectory();
+ tmpDeleter.add(metaTmp);
+
+ const Settings &settings = Settings::fromFileAndPrefix(configDir + QLatin1String("/config.xml"),
+ configDir);
+ generateMetaDataDirectory(metaTmp, repositoryDir, packages, settings.applicationName(),
+ settings.applicationVersion(), redirectUpdateUrl);
+ QInstallerTools::compressMetaDirectories(metaTmp, metaTmp, pathToVersionMapping);
+
+ QFile::remove(QFileInfo(repositoryDir, QLatin1String("Updates.xml")).absoluteFilePath());
+ moveDirectoryContents(metaTmp, repositoryDir);
+ return 0;
+ } catch (const Lib7z::SevenZipException &e) {
+ std::cerr << e.message() << std::endl;
+ } catch (const QInstaller::Error &e) {
+ std::cerr << e.message() << std::endl;
+ }
+ return 1;
+}
diff --git a/tools/repogen/repogen.pro b/tools/repogen/repogen.pro
new file mode 100644
index 000000000..e9652b25a
--- /dev/null
+++ b/tools/repogen/repogen.pro
@@ -0,0 +1,17 @@
+TEMPLATE = app
+TARGET = repogen
+DEPENDPATH += . .. ../common
+INCLUDEPATH += . .. ../common
+
+include(../../installerfw.pri)
+
+QT -= gui
+LIBS += -linstaller
+
+CONFIG += console
+CONFIG -= app_bundle
+DESTDIR = $$IFW_APP_PATH
+
+SOURCES += repogen.cpp \
+ repositorygen.cpp
+HEADERS += repositorygen.h
diff --git a/tools/repogenfromonlinerepo/repogenfromonlinerepo.pro b/tools/repogenfromonlinerepo/repogenfromonlinerepo.pro
index f34c58f08..8b3a01124 100644
--- a/tools/repogenfromonlinerepo/repogenfromonlinerepo.pro
+++ b/tools/repogenfromonlinerepo/repogenfromonlinerepo.pro
@@ -6,7 +6,7 @@ TARGET = repogenfromonlinerepo
include(../../installerfw.pri)
QT -= gui
-QT += xml network
+QT += network
CONFIG += console
CONFIG -= app_bundle
diff --git a/tools/tools.pro b/tools/tools.pro
index 627a6eeaa..963d4a2da 100644
--- a/tools/tools.pro
+++ b/tools/tools.pro
@@ -1,7 +1,11 @@
CONFIG += ordered
TEMPLATE = subdirs
-SUBDIRS += extractbinarydata \
+
+SUBDIRS += \
+ archivegen \
+ binarycreator \
+ extractbinarydata \
repocompare \
+ repogen \
repogenfromonlinerepo
-win32:SUBDIRS += maddehelper
-
+win32:SUBDIRS += maddehelper \ No newline at end of file