diff options
Diffstat (limited to 'tools/binarycreator/binarycreator.cpp')
-rw-r--r-- | tools/binarycreator/binarycreator.cpp | 1043 |
1 files changed, 0 insertions, 1043 deletions
diff --git a/tools/binarycreator/binarycreator.cpp b/tools/binarycreator/binarycreator.cpp deleted file mode 100644 index f6662615e..000000000 --- a/tools/binarycreator/binarycreator.cpp +++ /dev/null @@ -1,1043 +0,0 @@ -/************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the Qt Installer Framework. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -**************************************************************************/ -#include "common/repositorygen.h" - -#include <qtpatch.h> - -#include <binarycontent.h> -#include <binaryformat.h> -#include <errors.h> -#include <fileio.h> -#include <fileutils.h> -#include <init.h> -#include <repository.h> -#include <settings.h> -#include <utils.h> - -#include <QDateTime> -#include <QDirIterator> -#include <QDomDocument> -#include <QProcess> -#include <QRegExp> -#include <QSettings> -#include <QTemporaryFile> -#include <QTemporaryDir> - -#include <iostream> - -#ifdef Q_OS_MACOS -#include <QtCore/QtEndian> -#include <QtCore/QFile> -#include <QtCore/QVersionNumber> - -#include <mach-o/fat.h> -#include <mach-o/loader.h> -#endif - -using namespace QInstaller; - -struct Input { - QString outputPath; - QString installerExePath; - QInstallerTools::PackageInfoVector packages; - QInstaller::ResourceCollectionManager manager; -}; - -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; -}; - -#ifndef Q_OS_WIN -static void chmod755(const QString &absolutFilePath) -{ - QFile::setPermissions(absolutFilePath, QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner - | QFile::ReadGroup | QFile::ExeGroup | QFile::ReadOther | QFile::ExeOther); -} -#endif - -#ifdef Q_OS_MACOS -template <typename T = uint32_t> T readInt(QIODevice *ioDevice, bool *ok, - bool swap, bool peek = false) { - const auto bytes = peek - ? ioDevice->peek(sizeof(T)) - : ioDevice->read(sizeof(T)); - if (bytes.size() != sizeof(T)) { - if (ok) - *ok = false; - return T(); - } - if (ok) - *ok = true; - T n = *reinterpret_cast<const T *>(bytes.constData()); - return swap ? qbswap(n) : n; -} - -static QVersionNumber readMachOMinimumSystemVersion(QIODevice *device) -{ - bool ok; - std::vector<qint64> machoOffsets; - - qint64 pos = device->pos(); - uint32_t magic = readInt(device, &ok, false); - if (magic == FAT_MAGIC || magic == FAT_CIGAM || - magic == FAT_MAGIC_64 || magic == FAT_CIGAM_64) { - bool swap = magic == FAT_CIGAM || magic == FAT_CIGAM_64; - uint32_t nfat = readInt(device, &ok, swap); - if (!ok) - return QVersionNumber(); - - for (uint32_t n = 0; n < nfat; ++n) { - const bool is64bit = magic == FAT_MAGIC_64 || magic == FAT_CIGAM_64; - fat_arch_64 fat_arch; - fat_arch.cputype = static_cast<cpu_type_t>(readInt(device, &ok, swap)); - if (!ok) - return QVersionNumber(); - - fat_arch.cpusubtype = static_cast<cpu_subtype_t>(readInt(device, &ok, swap)); - if (!ok) - return QVersionNumber(); - - fat_arch.offset = is64bit - ? readInt<uint64_t>(device, &ok, swap) : readInt(device, &ok, swap); - if (!ok) - return QVersionNumber(); - - fat_arch.size = is64bit - ? readInt<uint64_t>(device, &ok, swap) : readInt(device, &ok, swap); - if (!ok) - return QVersionNumber(); - - fat_arch.align = readInt(device, &ok, swap); - if (!ok) - return QVersionNumber(); - - fat_arch.reserved = is64bit ? readInt(device, &ok, swap) : 0; - if (!ok) - return QVersionNumber(); - - machoOffsets.push_back(static_cast<qint64>(fat_arch.offset)); - } - } else if (!ok) { - return QVersionNumber(); - } - - // Wasn't a fat file, so we just read a thin Mach-O from the original offset - if (machoOffsets.empty()) - machoOffsets.push_back(pos); - - std::vector<QVersionNumber> versions; - - for (const auto &offset : machoOffsets) { - if (!device->seek(offset)) - return QVersionNumber(); - - bool swap = false; - mach_header_64 header; - header.magic = readInt(device, nullptr, swap); - switch (header.magic) { - case MH_CIGAM: - case MH_CIGAM_64: - swap = true; - break; - case MH_MAGIC: - case MH_MAGIC_64: - break; - default: - return QVersionNumber(); - } - - header.cputype = static_cast<cpu_type_t>(readInt(device, &ok, swap)); - if (!ok) - return QVersionNumber(); - - header.cpusubtype = static_cast<cpu_subtype_t>(readInt(device, &ok, swap)); - if (!ok) - return QVersionNumber(); - - header.filetype = readInt(device, &ok, swap); - if (!ok) - return QVersionNumber(); - - header.ncmds = readInt(device, &ok, swap); - if (!ok) - return QVersionNumber(); - - header.sizeofcmds = readInt(device, &ok, swap); - if (!ok) - return QVersionNumber(); - - header.flags = readInt(device, &ok, swap); - if (!ok) - return QVersionNumber(); - - header.reserved = header.magic == MH_MAGIC_64 || header.magic == MH_CIGAM_64 - ? readInt(device, &ok, swap) : 0; - if (!ok) - return QVersionNumber(); - - for (uint32_t i = 0; i < header.ncmds; ++i) { - const uint32_t cmd = readInt(device, nullptr, swap); - const uint32_t cmdsize = readInt(device, nullptr, swap); - if (cmd == 0 || cmdsize == 0) - return QVersionNumber(); - - switch (cmd) { - case LC_VERSION_MIN_MACOSX: - case LC_VERSION_MIN_IPHONEOS: - case LC_VERSION_MIN_TVOS: - case LC_VERSION_MIN_WATCHOS: - const uint32_t version = readInt(device, &ok, swap, true); - if (!ok) - return QVersionNumber(); - - versions.push_back(QVersionNumber( - static_cast<int>(version >> 16), - static_cast<int>((version >> 8) & 0xff), - static_cast<int>(version & 0xff))); - break; - } - - const qint64 remaining = static_cast<qint64>(cmdsize - sizeof(cmd) - sizeof(cmdsize)); - if (device->read(remaining).size() != remaining) - return QVersionNumber(); - } - } - - std::sort(versions.begin(), versions.end()); - return !versions.empty() ? versions.front() : QVersionNumber(); -} -#endif - -static int assemble(Input input, const QInstaller::Settings &settings, const QString &signingIdentity) -{ -#ifdef Q_OS_MACOS - if (QInstaller::isInBundle(input.installerExePath)) { - 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); - - QString minimumSystemVersion; - QFile file(input.installerExePath); - if (file.open(QIODevice::ReadOnly)) - minimumSystemVersion = readMachOMinimumSystemVersion(&file).normalized().toString(); - - const QString contentsResourcesPath = fi.filePath() + QLatin1String("/Contents/Resources/"); - - QInstaller::mkpath(fi.filePath() + QLatin1String("/Contents/MacOS")); - QInstaller::mkpath(contentsResourcesPath); - - { - QFile pkgInfo(fi.filePath() + QLatin1String("/Contents/PkgInfo")); - pkgInfo.open(QIODevice::WriteOnly); - QTextStream pkgInfoStream(&pkgInfo); - pkgInfoStream << QLatin1String("APPL????") << endl; - } - - QString iconFile; - if (QFile::exists(settings.installerApplicationIcon())) { - iconFile = settings.installerApplicationIcon(); - } else { - iconFile = QString::fromLatin1(":/resources/default_icon_mac.icns"); - } - - const QString iconTargetFile = fi.completeBaseName() + QLatin1String(".icns"); - QFile::copy(iconFile, contentsResourcesPath + iconTargetFile); - if (QDir(qApp->applicationDirPath() + QLatin1String("/qt_menu.nib")).exists()) { - copyDirectoryContents(qApp->applicationDirPath() + QLatin1String("/qt_menu.nib"), - contentsResourcesPath + QLatin1String("/qt_menu.nib")); - } - - 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 PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" " - "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">") << endl; - plistStream << QLatin1String("<plist version=\"1.0\">") << endl; - plistStream << QLatin1String("<dict>") << endl; - plistStream << QLatin1String("\t<key>CFBundleIconFile</key>") << endl; - plistStream << QLatin1String("\t<string>") << iconTargetFile << QLatin1String("</string>") - << endl; - plistStream << QLatin1String("\t<key>CFBundlePackageType</key>") << endl; - plistStream << QLatin1String("\t<string>APPL</string>") << endl; -#define QUOTE_(x) #x -#define QUOTE(x) QUOTE_(x) - plistStream << QLatin1String("\t<key>CFBundleShortVersionString</key>") << endl; - plistStream << QLatin1String("\t<string>") << QLatin1String(QUOTE(IFW_VERSION_STR)) << ("</string>") - << endl; - plistStream << QLatin1String("\t<key>CFBundleVersion</key>") << endl; - plistStream << QLatin1String("\t<string>") << QLatin1String(QUOTE(IFW_VERSION_STR)) << ("</string>") - << endl; -#undef QUOTE -#undef QUOTE_ - plistStream << QLatin1String("\t<key>CFBundleSignature</key>") << endl; - plistStream << QLatin1String("\t<string>\?\?\?\?</string>") << endl; - plistStream << QLatin1String("\t<key>CFBundleExecutable</key>") << endl; - plistStream << QLatin1String("\t<string>") << fi.completeBaseName() << QLatin1String("</string>") - << endl; - plistStream << QLatin1String("\t<key>CFBundleIdentifier</key>") << endl; - plistStream << QLatin1String("\t<string>com.yourcompany.installerbase</string>") << endl; - plistStream << QLatin1String("\t<key>NOTE</key>") << endl; - plistStream << QLatin1String("\t<string>This file was generated by Qt Installer Framework.</string>") - << endl; - plistStream << QLatin1String("\t<key>NSPrincipalClass</key>") << endl; - plistStream << QLatin1String("\t<string>NSApplication</string>") << endl; - if (!minimumSystemVersion.isEmpty()) { - plistStream << QLatin1String("\t<key>LSMinimumSystemVersion</key>") << endl; - plistStream << QLatin1String("\t<string>") << minimumSystemVersion << QLatin1String("</string>") << endl; - } - plistStream << QLatin1String("</dict>") << endl; - plistStream << QLatin1String("</plist>") << endl; - - input.outputPath = QString::fromLatin1("%1/Contents/MacOS/%2").arg(input.outputPath) - .arg(fi.completeBaseName()); - } -#elif defined(Q_OS_LINUX) - Q_UNUSED(settings) -#endif - - QTemporaryFile file(input.outputPath); - if (!file.open()) { - throw Error(QString::fromLatin1("Cannot 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(QString::fromLatin1("Cannot copy %1 to %2: %3").arg(instExe.fileName(), - tempFile, instExe.errorString())); - } - - QtPatch::patchBinaryFile(tempFile, QByteArray("MY_InstallerCreateDateTime_MY"), - QDateTime::currentDateTime().toString(QLatin1String("yyyy-MM-dd - HH:mm:ss")).toLatin1()); - - - 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.installerApplicationIcon())) { - // no error handling as this is not fatal - setApplicationIcon(tempFile, settings.installerApplicationIcon()); - } -#elif defined(Q_OS_MACOS) - 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(-1); - QFile::rename(input.outputPath, tempFile); - QFile::remove(copyscript); - } -#endif - - QFile out(generateTemporaryFileName()); - - QString targetName = input.outputPath; -#ifdef Q_OS_MACOS - QDir resourcePath(QFileInfo(input.outputPath).dir()); - resourcePath.cdUp(); - resourcePath.cd(QLatin1String("Resources")); - targetName = resourcePath.filePath(QLatin1String("installer.dat")); -#endif - - { - QFile target(targetName); - if (target.exists() && !target.remove()) { - qCritical("Cannot remove target %s: %s", qPrintable(target.fileName()), - qPrintable(target.errorString())); - QFile::remove(tempFile); - return EXIT_FAILURE; - } - } - - try { - QInstaller::openForWrite(&out); - QFile exe(input.installerExePath); - -#ifdef Q_OS_MACOS - if (!exe.copy(input.outputPath)) { - throw Error(QString::fromLatin1("Cannot copy %1 to %2: %3").arg(exe.fileName(), - input.outputPath, exe.errorString())); - } -#else - QInstaller::openForRead(&exe); - QInstaller::appendData(&out, &exe, exe.size()); -#endif - - foreach (const QInstallerTools::PackageInfo &info, input.packages) { - QInstaller::ResourceCollection collection; - collection.setName(info.name.toUtf8()); - - qDebug() << "Creating resource archive for" << info.name; - foreach (const QString &file, info.copiedFiles) { - const QSharedPointer<Resource> resource(new Resource(file)); - qDebug().nospace() << "Appending " << file << " (" << humanReadableSize(resource->size()) << ")"; - collection.appendResource(resource); - } - input.manager.insertCollection(collection); - } - - const QList<QInstaller::OperationBlob> operations; - BinaryContent::writeBinaryContent(&out, operations, input.manager, - BinaryContent::MagicInstallerMarker, BinaryContent::MagicCookie); - } catch (const Error &e) { - qCritical("Error occurred while assembling the installer: %s", qPrintable(e.message())); - QFile::remove(tempFile); - return EXIT_FAILURE; - } - - if (!out.rename(targetName)) { - qCritical("Cannot write installer to %s: %s", targetName.toUtf8().constData(), - out.errorString().toUtf8().constData()); - QFile::remove(tempFile); - return EXIT_FAILURE; - } - -#ifndef Q_OS_WIN - chmod755(out.fileName()); -#endif - QFile::remove(tempFile); - -#ifdef Q_OS_MACOS - if (isBundle && !signingIdentity.isEmpty()) { - qDebug() << "Signing .app bundle..."; - - QProcess p; - p.start(QLatin1String("codesign"), - QStringList() << QLatin1String("--force") - << QLatin1String("--deep") - << QLatin1String("--sign") << signingIdentity - << bundle); - - if (!p.waitForFinished(-1)) { - qCritical("Failed to sign app bundle: error while running '%s %s': %s", - p.program().toUtf8().constData(), - p.arguments().join(QLatin1Char(' ')).toUtf8().constData(), - p.errorString().toUtf8().constData()); - return EXIT_FAILURE; - } - - if (p.exitStatus() == QProcess::NormalExit) { - if (p.exitCode() != 0) { - qCritical("Failed to sign app bundle: running codesign failed " - "with exit code %d: %s", p.exitCode(), - p.readAllStandardError().constData()); - return EXIT_FAILURE; - } - } - - qDebug() << "done."; - } - - bundleBackup.release(); - - if (createDMG) { - qDebug() << "creating a DMG disk image..."; - - const QString volumeName = QFileInfo(input.outputPath).fileName(); - const QString imagePath = QString::fromLatin1("%1/%2.dmg") - .arg(QFileInfo(bundle).path()) - .arg(volumeName); - - // no error handling as this is not fatal - QProcess p; - p.start(QLatin1String("/usr/bin/hdiutil"), - QStringList() << QLatin1String("create") - << imagePath - << QLatin1String("-srcfolder") - << bundle - << QLatin1String("-ov") - << QLatin1String("-volname") - << volumeName - << QLatin1String("-fs") - << QLatin1String("HFS+")); - qDebug() << "running " << p.program() << p.arguments(); - p.waitForFinished(-1); - qDebug() << "removing" << bundle; - QDir(bundle).removeRecursively(); - qDebug() << "done."; - } -#else - Q_UNUSED(signingIdentity) -#endif - return EXIT_SUCCESS; -} - -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, nullptr); - for (int i = 0; i < argc; ++i) - argv[i] = qstrdup(qPrintable(args[i])); - - // Note: this does not run the rcc provided by Qt, this one is using the compiled in binarycreator - // version. If it happens that resource mapping fails, we might need to adapt the code here... - 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 QSharedPointer<QInstaller::Resource> createDefaultResourceFile(const QString &directory, - const QString &binaryName) -{ - QTemporaryFile projectFile(directory + QLatin1String("/rccprojectXXXXXX.qrc")); - if (!projectFile.open()) - throw Error(QString::fromLatin1("Cannot create temporary file for generated rcc project file")); - projectFile.close(); - - const WorkingDirectoryChange wd(directory); - const QString projectFileName = QFileInfo(projectFile.fileName()).absoluteFilePath(); - - // 1. create the .qrc file - if (runRcc(QStringList() << QLatin1String("rcc") << QLatin1String("-project") << QLatin1String("-o") - << projectFileName) != EXIT_SUCCESS) { - throw Error(QString::fromLatin1("Cannot create rcc project file.")); - } - - // 2. create the binary resource file from the .qrc file - if (runRcc(QStringList() << QLatin1String("rcc") << QLatin1String("-binary") << QLatin1String("-o") - << binaryName << projectFileName) != EXIT_SUCCESS) { - throw Error(QString::fromLatin1("Cannot compile rcc project file.")); - } - - return QSharedPointer<QInstaller::Resource>(new QInstaller::Resource(binaryName, binaryName - .toUtf8())); -} - -static -QList<QSharedPointer<QInstaller::Resource> > createBinaryResourceFiles(const QStringList &resources) -{ - QList<QSharedPointer<QInstaller::Resource> > 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) - continue; - - result.append(QSharedPointer<QInstaller::Resource> (new QInstaller::Resource(binaryName, - binaryName.toUtf8()))); - } - } - return result; -} - -static void printUsage() -{ - QString suffix; -#ifdef Q_OS_WIN - suffix = QLatin1String(".exe"); -#endif - 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 << " -c|--config file The file containing the installer configuration" << std::endl; - - std::cout << " -n|--online-only Do not add any package into the installer" << std::endl; - std::cout << " (for online only installers)" << std::endl; - - std::cout << " -f|--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 << " -v|--verbose Verbose output" << std::endl; - std::cout << " -rcc|--compile-resource Compiles the default resource and outputs the result into" - << std::endl; - std::cout << " 'update.rcc' in the current path." << std::endl; -#ifdef Q_OS_MACOS - std::cout << " -s|--sign identity Sign generated app bundle using the given code " << std::endl; - std::cout << " signing identity" << std::endl; -#endif - std::cout << 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; - char sep = QDir::separator().toLatin1(); - std::cout << " " << appName << " --offline-only -c installer-config" << sep << "config.xml -p " - "packages-directory -t installerbase" << suffix << " SDKInstaller" << suffix << 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" << sep << "config.xml -p packages-directory " - "-e org.qt-project.sdk.qt,org.qt-project.qtcreator -t installerbase" << suffix << " SDKInstaller" - << suffix << 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; - std::cout << "Example update.rcc:" << std::endl; - std::cout << " " << appName << " -c installer-config" << sep << "config.xml -p packages-directory " - "-rcc" << std::endl; -} - -void copyConfigData(const QString &configFile, const QString &targetDir) -{ - qDebug() << "Begin to copy configuration file and data."; - - const QString sourceConfigFile = QFileInfo(configFile).absoluteFilePath(); - const QString targetConfigFile = targetDir + QLatin1String("/config.xml"); - QInstallerTools::copyWithException(sourceConfigFile, targetConfigFile, QLatin1String("configuration")); - - QFile configXml(targetConfigFile); - QInstaller::openForRead(&configXml); - - QDomDocument dom; - dom.setContent(&configXml); - configXml.close(); - - // iterate over all child elements, searching for relative file names - const QDomNodeList children = dom.documentElement().childNodes(); - const QString sourceConfigFilePath = QFileInfo(sourceConfigFile).absolutePath(); - for (int i = 0; i < children.count(); ++i) { - QDomElement domElement = children.at(i).toElement(); - if (domElement.isNull()) - continue; - - const QString tagName = domElement.tagName(); - const QString elementText = domElement.text(); - qDebug().noquote() << QString::fromLatin1("Read dom element: <%1>%2</%1>.").arg(tagName, elementText); - - if (tagName == QLatin1String("ProductImages")) { - const QDomNodeList childNodes = domElement.childNodes(); - for (int i = 0; i < childNodes.count(); ++i) { - const QDomElement childElement = childNodes.at(i).toElement(); - const QString childName = childElement.tagName(); - if (childName != QLatin1String("Image")) - continue; - - const QString targetFile = targetDir + QLatin1Char('/') + childElement.text(); - const QFileInfo childFileInfo = QFileInfo(sourceConfigFilePath, childElement.text()); - QInstallerTools::copyWithException(childFileInfo.absoluteFilePath(), targetFile, childName); - } - continue; - } - - QString newName = domElement.text().replace(QRegExp(QLatin1String("\\\\|/|\\.|:")), - QLatin1String("_")); - - QString targetFile; - QFileInfo elementFileInfo; - if (tagName == QLatin1String("Icon") || tagName == QLatin1String("InstallerApplicationIcon")) { -#if defined(Q_OS_MACOS) - const QString suffix = QLatin1String(".icns"); -#elif defined(Q_OS_WIN) - const QString suffix = QLatin1String(".ico"); -#else - const QString suffix = QLatin1String(".png"); -#endif - elementFileInfo = QFileInfo(sourceConfigFilePath, elementText + suffix); - targetFile = targetDir + QLatin1Char('/') + newName + suffix; - } else { - elementFileInfo = QFileInfo(sourceConfigFilePath, elementText); - const QString suffix = elementFileInfo.completeSuffix(); - if (!suffix.isEmpty()) - newName.append(QLatin1Char('.') + suffix); - targetFile = targetDir + QLatin1Char('/') + newName; - } - if (!elementFileInfo.exists() || elementFileInfo.isDir()) - continue; - - domElement.replaceChild(dom.createTextNode(newName), domElement.firstChild()); - QInstallerTools::copyWithException(elementFileInfo.absoluteFilePath(), targetFile, tagName); - } - - QInstaller::openForWrite(&configXml); - QTextStream stream(&configXml); - dom.save(stream, 4); - - qDebug() << "done.\n"; -} - -static int printErrorAndUsageAndExit(const QString &err) -{ - std::cerr << qPrintable(err) << std::endl << std::endl; - printUsage(); - return EXIT_FAILURE; -} - -int main(int argc, char **argv) -{ -// increase maximum numbers of file descriptors -#if defined (Q_OS_MACOS) - struct rlimit rl; - getrlimit(RLIMIT_NOFILE, &rl); - rl.rlim_cur = qMin(static_cast<rlim_t>(OPEN_MAX), rl.rlim_max); - setrlimit(RLIMIT_NOFILE, &rl); -#endif - QCoreApplication app(argc, argv); - - QInstaller::init(); - - QString templateBinary = QLatin1String("installerbase"); - QString suffix; -#ifdef Q_OS_WIN - suffix = QLatin1String(".exe"); - templateBinary = templateBinary + suffix; -#endif - if (!QFileInfo(templateBinary).exists()) - templateBinary = QString::fromLatin1("%1/%2").arg(qApp->applicationDirPath(), templateBinary); - - QString target; - QString configFile; - QStringList packagesDirectories; - QStringList repositoryDirectories; - bool onlineOnly = false; - bool offlineOnly = false; - QStringList resources; - QStringList filteredPackages; - QInstallerTools::FilterType ftype = QInstallerTools::Exclude; - bool compileResource = false; - QString signingIdentity; - - 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(QString::fromLatin1("Error: Packages parameter missing argument.")); - } - if (!QFileInfo(*it).exists()) { - return printErrorAndUsageAndExit(QString::fromLatin1("Error: Package directory not found at the " - "specified location.")); - } - packagesDirectories.append(*it); - } else if (*it == QLatin1String("--repository")) { - ++it; - if (it == args.end()) { - return printErrorAndUsageAndExit(QString::fromLatin1("Error: Repository parameter missing argument.")); - } - if (QFileInfo(*it).exists()) { - repositoryDirectories.append(*it); - } else { - return printErrorAndUsageAndExit(QString::fromLatin1("Error: Only local filesystem repositories now supported.")); - } - } else if (*it == QLatin1String("-e") || *it == QLatin1String("--exclude")) { - ++it; - if (!filteredPackages.isEmpty()) - return printErrorAndUsageAndExit(QString::fromLatin1("Error: --include and --exclude are mutually " - "exclusive. Use either one or the other.")); - if (it == args.end() || it->startsWith(QLatin1String("-"))) - return printErrorAndUsageAndExit(QString::fromLatin1("Error: Package to exclude missing.")); - filteredPackages = it->split(QLatin1Char(',')); - } else if (*it == QLatin1String("-i") || *it == QLatin1String("--include")) { - ++it; - if (!filteredPackages.isEmpty()) - return printErrorAndUsageAndExit(QString::fromLatin1("Error: --include and --exclude are mutually " - "exclusive. Use either one or the other.")); - if (it == args.end() || it->startsWith(QLatin1String("-"))) - return printErrorAndUsageAndExit(QString::fromLatin1("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("--online-only")) { - if (!filteredPackages.isEmpty()) { - return printErrorAndUsageAndExit(QString::fromLatin1("Error: 'online-only' option cannot be used " - "in conjunction with the 'include' or 'exclude' option. An 'online-only' installer will never " - "contain any components apart from the root component.")); - } - onlineOnly = true; - } else if (*it == QLatin1String("-f") || *it == QLatin1String("--offline-only")) { - offlineOnly = true; - } else if (*it == QLatin1String("-t") || *it == QLatin1String("--template")) { - ++it; - if (it == args.end()) { - return printErrorAndUsageAndExit(QString::fromLatin1("Error: Template parameter missing argument.")); - } - templateBinary = *it; -#ifdef Q_OS_WIN - if (!templateBinary.endsWith(suffix)) - templateBinary = templateBinary + suffix; -#endif - if (!QFileInfo(templateBinary).exists()) { - return printErrorAndUsageAndExit(QString::fromLatin1("Error: Template not found at the specified " - "location.")); - } - } else if (*it == QLatin1String("-c") || *it == QLatin1String("--config")) { - ++it; - if (it == args.end()) - return printErrorAndUsageAndExit(QString::fromLatin1("Error: Config parameter missing argument.")); - const QFileInfo fi(*it); - if (!fi.exists()) { - return printErrorAndUsageAndExit(QString::fromLatin1("Error: Config file %1 not found at the " - "specified location.").arg(*it)); - } - if (!fi.isFile()) { - return printErrorAndUsageAndExit(QString::fromLatin1("Error: Configuration %1 is not a file.") - .arg(*it)); - } - if (!fi.isReadable()) { - return printErrorAndUsageAndExit(QString::fromLatin1("Error: Config file %1 is not readable.") - .arg(*it)); - } - configFile = *it; - } else if (*it == QLatin1String("-r") || *it == QLatin1String("--resources")) { - ++it; - if (it == args.end() || it->startsWith(QLatin1String("-"))) - return printErrorAndUsageAndExit(QString::fromLatin1("Error: Resource files to include are missing.")); - resources = it->split(QLatin1Char(',')); - } else if (*it == QLatin1String("--ignore-translations") - || *it == QLatin1String("--ignore-invalid-packages")) { - continue; - } else if (*it == QLatin1String("-rcc") || *it == QLatin1String("--compile-resource")) { - compileResource = true; -#ifdef Q_OS_MACOS - } else if (*it == QLatin1String("-s") || *it == QLatin1String("--sign")) { - ++it; - if (it == args.end() || it->startsWith(QLatin1String("-"))) - return printErrorAndUsageAndExit(QString::fromLatin1("Error: No code signing identity specified.")); - signingIdentity = *it; -#endif - } else { - if (it->startsWith(QLatin1String("-"))) { - return printErrorAndUsageAndExit(QString::fromLatin1("Error: Unknown option \"%1\" used. Maybe you " - "are using an old syntax.").arg(*it)); - } else if (target.isEmpty()) { - target = *it; -#ifdef Q_OS_WIN - if (!target.endsWith(suffix)) - target = target + suffix; -#endif - } else { - return printErrorAndUsageAndExit(QString::fromLatin1("Error: You are using an old syntax please add the " - "component name with the include option") - .arg(*it)); - } - } - } - - if (onlineOnly && offlineOnly) { - return printErrorAndUsageAndExit(QString::fromLatin1("You cannot use --online-only and " - "--offline-only at the same time.")); - } - - if (onlineOnly) { - filteredPackages.append(QLatin1String("X_fake_filter_component_for_online_only_installer_X")); - ftype = QInstallerTools::Include; - } - - if (target.isEmpty() && !compileResource) - return printErrorAndUsageAndExit(QString::fromLatin1("Error: Target parameter missing.")); - - if (configFile.isEmpty()) - return printErrorAndUsageAndExit(QString::fromLatin1("Error: No configuration file selected.")); - - if (packagesDirectories.isEmpty() && repositoryDirectories.isEmpty()) - return printErrorAndUsageAndExit(QString::fromLatin1("Error: Both Package directory and Repository parameters missing.")); - - qDebug() << "Parsed arguments, ok."; - - Input input; - int exitCode = EXIT_FAILURE; - QTemporaryDir tmp; - tmp.setAutoRemove(false); - const QString tmpMetaDir = tmp.path(); - QTemporaryDir tmp2; - tmp2.setAutoRemove(false); - const QString tmpRepoDir = tmp2.path(); - try { - const Settings settings = Settings::fromFileAndPrefix(configFile, QFileInfo(configFile) - .absolutePath()); - - // Note: the order here is important - - QInstallerTools::PackageInfoVector packages; - - // 1; update the list of available compressed packages - if (!repositoryDirectories.isEmpty()) { - // 1.1; search packages - QInstallerTools::PackageInfoVector precompressedPackages = QInstallerTools::createListOfRepositoryPackages(repositoryDirectories, - &filteredPackages, ftype); - // 1.2; add to common vector - packages.append(precompressedPackages); - } - - // 2; update the list of available prepared packages - if (!packagesDirectories.isEmpty()) { - // 2.1; search packages - QInstallerTools::PackageInfoVector preparedPackages = QInstallerTools::createListOfPackages(packagesDirectories, - &filteredPackages, ftype); - // 2.2; copy the packages data and setup the packages vector with the files we copied, - // must happen before copying meta data because files will be compressed if - // needed and meta data generation relies on this - QInstallerTools::copyComponentData(packagesDirectories, tmpRepoDir, &preparedPackages); - // 2.3; add to common vector - packages.append(preparedPackages); - } - - // 3; copy the meta data of the available packages, generate Updates.xml - QInstallerTools::copyMetaData(tmpMetaDir, tmpRepoDir, packages, settings - .applicationName(), settings.version(), QStringList()); - - // 4; copy the configuration file and and icons etc. - copyConfigData(configFile, tmpMetaDir + QLatin1String("/installer-config")); - { - QSettings confInternal(tmpMetaDir + QLatin1String("/config/config-internal.ini") - , QSettings::IniFormat); - // assume offline installer if there are no repositories and no - //--online-only not set - offlineOnly = offlineOnly | settings.repositories().isEmpty(); - if (onlineOnly) - offlineOnly = !onlineOnly; - confInternal.setValue(QLatin1String("offlineOnly"), offlineOnly); - } - -#ifdef Q_OS_MACOS - // on mac, we enforce building a bundle - if (!target.endsWith(QLatin1String(".app")) && !target.endsWith(QLatin1String(".dmg"))) - target += QLatin1String(".app"); -#endif - if (!compileResource) { - // 5; put the copied resources into a resource file - QInstaller::ResourceCollection metaCollection("QResources"); - metaCollection.appendResource(createDefaultResourceFile(tmpMetaDir, - generateTemporaryFileName())); - metaCollection.appendResources(createBinaryResourceFiles(resources)); - input.manager.insertCollection(metaCollection); - - input.packages = packages; - input.outputPath = target; - input.installerExePath = templateBinary; - - qDebug() << "Creating the binary"; - exitCode = assemble(input, settings, signingIdentity); - } else { - createDefaultResourceFile(tmpMetaDir, QDir::currentPath() + QLatin1String("/update.rcc")); - exitCode = EXIT_SUCCESS; - } - } catch (const Error &e) { - QFile::remove(input.outputPath); - std::cerr << "Caught exception: " << e.message() << std::endl; - } catch (...) { - QFile::remove(input.outputPath); - std::cerr << "Unknown exception caught" << std::endl; - } - - qDebug() << "Cleaning up..."; - const QInstaller::ResourceCollection collection = input.manager.collectionByName("QResources"); - foreach (const QSharedPointer<QInstaller::Resource> &resource, collection.resources()) - QFile::remove(QString::fromUtf8(resource->name())); - QInstaller::removeDirectory(tmpMetaDir, true); - QInstaller::removeDirectory(tmpRepoDir, true); - - return exitCode; -} |