summaryrefslogtreecommitdiffstats
path: root/tools/binarycreator/binarycreator.cpp
diff options
context:
space:
mode:
authorArttu Tarkiainen <arttu.tarkiainen@qt.io>2020-11-20 17:09:30 +0200
committerArttu Tarkiainen <arttu.tarkiainen@qt.io>2020-12-02 10:25:18 +0000
commit885a41d1667ac8ccf59a4de76cb8449074a466ac (patch)
tree7e0909c5b06754f458c6b6e7d3a77a4ffd994be7 /tools/binarycreator/binarycreator.cpp
parentf053b9a627921b03529b4f797a97b582675fbe71 (diff)
Tools: refactor to move general purpose functionality to installer lib
This makes it possible to utilize parts of our existing tooling in the offline installer from online installer generation process. Task-number: QTIFW-2048 Change-Id: I7ee605be75541cc83a3b6909089bda45f0835bcf Reviewed-by: Katja Marttila <katja.marttila@qt.io>
Diffstat (limited to 'tools/binarycreator/binarycreator.cpp')
-rw-r--r--tools/binarycreator/binarycreator.cpp1043
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;
-}