aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib/corelib/tools
diff options
context:
space:
mode:
authorChristian Kandeler <christian.kandeler@digia.com>2014-01-09 17:50:40 +0100
committerJoerg Bornemann <joerg.bornemann@digia.com>2014-01-10 18:11:22 +0100
commit81af9acaa295a574c1cb5e6714725197dac7f530 (patch)
treecc8c94467f49a7d267e5249f624874feecc7eed4 /src/lib/corelib/tools
parent2fe25eb3f20ffb4e58cb559f2bcb9950c963290a (diff)
Move Qt profile setup into a dedicated library.
Otherwise all changes to the implementation will have to be duplicated in IDEs. Change-Id: I61e6d4fa1ee9b724eb5d9de9f233dc915a6c8bc3 Reviewed-by: Joerg Bornemann <joerg.bornemann@digia.com>
Diffstat (limited to 'src/lib/corelib/tools')
-rw-r--r--src/lib/corelib/tools/buildoptions.cpp240
-rw-r--r--src/lib/corelib/tools/buildoptions.h79
-rw-r--r--src/lib/corelib/tools/cleanoptions.cpp158
-rw-r--r--src/lib/corelib/tools/cleanoptions.h67
-rw-r--r--src/lib/corelib/tools/codelocation.cpp140
-rw-r--r--src/lib/corelib/tools/codelocation.h72
-rw-r--r--src/lib/corelib/tools/error.cpp196
-rw-r--r--src/lib/corelib/tools/error.h94
-rw-r--r--src/lib/corelib/tools/fileinfo.cpp477
-rw-r--r--src/lib/corelib/tools/fileinfo.h95
-rw-r--r--src/lib/corelib/tools/filetime.h128
-rw-r--r--src/lib/corelib/tools/filetime_unix.cpp73
-rw-r--r--src/lib/corelib/tools/filetime_win.cpp101
-rw-r--r--src/lib/corelib/tools/hostosinfo.h196
-rw-r--r--src/lib/corelib/tools/id.cpp324
-rw-r--r--src/lib/corelib/tools/id.h87
-rw-r--r--src/lib/corelib/tools/installoptions.cpp203
-rw-r--r--src/lib/corelib/tools/installoptions.h73
-rw-r--r--src/lib/corelib/tools/persistence.cpp217
-rw-r--r--src/lib/corelib/tools/persistence.h197
-rw-r--r--src/lib/corelib/tools/persistentobject.h48
-rw-r--r--src/lib/corelib/tools/preferences.cpp133
-rw-r--r--src/lib/corelib/tools/preferences.h63
-rw-r--r--src/lib/corelib/tools/processresult.cpp121
-rw-r--r--src/lib/corelib/tools/processresult.h72
-rw-r--r--src/lib/corelib/tools/processresult_p.h56
-rw-r--r--src/lib/corelib/tools/profile.cpp224
-rw-r--r--src/lib/corelib/tools/profile.h81
-rw-r--r--src/lib/corelib/tools/progressobserver.cpp95
-rw-r--r--src/lib/corelib/tools/progressobserver.h62
-rw-r--r--src/lib/corelib/tools/propertyfinder.cpp107
-rw-r--r--src/lib/corelib/tools/propertyfinder.h62
-rw-r--r--src/lib/corelib/tools/qbs_export.h44
-rw-r--r--src/lib/corelib/tools/qbsassert.cpp50
-rw-r--r--src/lib/corelib/tools/qbsassert.h59
-rw-r--r--src/lib/corelib/tools/qttools.cpp40
-rw-r--r--src/lib/corelib/tools/qttools.h40
-rw-r--r--src/lib/corelib/tools/scannerpluginmanager.cpp115
-rw-r--r--src/lib/corelib/tools/scannerpluginmanager.h66
-rw-r--r--src/lib/corelib/tools/scripttools.cpp130
-rw-r--r--src/lib/corelib/tools/scripttools.h94
-rw-r--r--src/lib/corelib/tools/settings.cpp184
-rw-r--r--src/lib/corelib/tools/settings.h72
-rw-r--r--src/lib/corelib/tools/setupprojectparameters.cpp434
-rw-r--r--src/lib/corelib/tools/setupprojectparameters.h101
-rw-r--r--src/lib/corelib/tools/tools.pri79
-rw-r--r--src/lib/corelib/tools/tst_tools.cpp178
-rw-r--r--src/lib/corelib/tools/tst_tools.h56
-rw-r--r--src/lib/corelib/tools/weakpointer.h60
49 files changed, 6143 insertions, 0 deletions
diff --git a/src/lib/corelib/tools/buildoptions.cpp b/src/lib/corelib/tools/buildoptions.cpp
new file mode 100644
index 000000000..2261124d8
--- /dev/null
+++ b/src/lib/corelib/tools/buildoptions.cpp
@@ -0,0 +1,240 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+#include "buildoptions.h"
+
+#include <QSharedData>
+#include <QThread>
+
+namespace qbs {
+namespace Internal {
+
+class BuildOptionsPrivate : public QSharedData
+{
+public:
+ BuildOptionsPrivate() : maxJobCount(0), dryRun(false), keepGoing(false), logElapsedTime(false)
+ {
+ }
+
+ QStringList changedFiles;
+ QStringList activeFileTags;
+ int maxJobCount;
+ bool dryRun;
+ bool keepGoing;
+ bool forceTimestampCheck;
+ bool logElapsedTime;
+};
+
+} // namespace Internal
+
+/*!
+ * \class BuildOptions
+ * \brief The \c BuildOptions class comprises parameters that influence the behavior of
+ * build and clean operations.
+ */
+
+/*!
+ * \brief Creates a \c BuildOptions object and initializes its members to sensible default values.
+ */
+BuildOptions::BuildOptions() : d(new Internal::BuildOptionsPrivate)
+{
+}
+
+BuildOptions::BuildOptions(const BuildOptions &other) : d(other.d)
+{
+}
+
+BuildOptions &BuildOptions::operator=(const BuildOptions &other)
+{
+ d = other.d;
+ return *this;
+}
+
+BuildOptions::~BuildOptions()
+{
+}
+
+/*!
+ * \brief If non-empty, qbs pretends that only these files have changed.
+ * By default, this list is empty.
+ */
+QStringList BuildOptions::changedFiles() const
+{
+ return d->changedFiles;
+}
+
+/*!
+ * \brief If the given list is empty, qbs will pretend only the listed files are changed.
+ * \note The list elements must be absolute file paths.
+ */
+void BuildOptions::setChangedFiles(const QStringList &changedFiles)
+{
+ d->changedFiles = changedFiles;
+}
+
+/*!
+ * \brief The list of active file tags.
+ * \sa setActiveFileTags
+ */
+QStringList BuildOptions::activeFileTags() const
+{
+ return d->activeFileTags;
+}
+
+/*!
+ * \brief Set the list of active file tags.
+ * If this list is non-empty, then every transformer with non-matching output file tags is skipped.
+ * E.g. set changed files to "foo.cpp" and activeFileTags to ["obj"] to run the compiler
+ * on foo.cpp without further processing like linking.
+ * \sa activeFileTags
+ */
+void BuildOptions::setActiveFileTags(const QStringList &fileTags)
+{
+ d->activeFileTags = fileTags;
+}
+
+/*!
+ * \brief Returns the default value for \c maxJobCount.
+ * This value will be used when \c maxJobCount has not been set explicitly.
+ */
+int BuildOptions::defaultMaxJobCount()
+{
+ return QThread::idealThreadCount();
+}
+
+/*!
+ * \brief Returns the maximum number of build commands to run concurrently.
+ * If the value is not valid (i.e. <= 0), a sensible one will be derived at build time
+ * from the number of available processor cores at build time.
+ * The default is 0.
+ * \sa BuildOptions::defaultMaxJobCount
+ */
+int BuildOptions::maxJobCount() const
+{
+ return d->maxJobCount;
+}
+
+/*!
+ * \brief Controls how many build commands can be run in parallel.
+ * A value <= 0 leaves the decision to qbs.
+ */
+void BuildOptions::setMaxJobCount(int jobCount)
+{
+ d->maxJobCount = jobCount;
+}
+
+/*!
+ * \brief Returns true iff qbs will not actually execute any commands, but just show what
+ * would happen.
+ * The default is false.
+ */
+bool BuildOptions::dryRun() const
+{
+ return d->dryRun;
+}
+
+/*!
+ * \brief Controls whether qbs will actually build something.
+ * If the argument is true, qbs will just emit information about what it would do. Otherwise,
+ * the build is actually done.
+ * \note After you build with this setting enabled, the next call to \c build() on the same
+ * \c Project object will do nothing, since the internal state needs to be updated the same way
+ * as if an actual build had happened. You'll need to create a new \c Project object to do
+ * a real build afterwards.
+ */
+void BuildOptions::setDryRun(bool dryRun)
+{
+ d->dryRun = dryRun;
+}
+
+/*!
+ * \brief Returns true iff a build will continue after an error.
+ * E.g. a failed compile command will result in a warning message being printed, instead of
+ * stopping the build process right away. However, there might still be fatal errors after which the
+ * build process cannot continue.
+ * The default is \c false.
+ */
+bool BuildOptions::keepGoing() const
+{
+ return d->keepGoing;
+}
+
+/*!
+ * \brief Controls whether a qbs will try to continue building after an error has occurred.
+ */
+void BuildOptions::setKeepGoing(bool keepGoing)
+{
+ d->keepGoing = keepGoing;
+}
+
+/*!
+ * \brief Returns true if qbs is to use physical timestamps instead of the timestamps stored in the
+ * build graph.
+ * The default is \c false.
+ */
+bool BuildOptions::forceTimestampCheck() const
+{
+ return d->forceTimestampCheck;
+}
+
+/*!
+ * \brief Controls whether qbs should use physical timestamps for up-to-date checks.
+ */
+void BuildOptions::setForceTimestampCheck(bool enabled)
+{
+ d->forceTimestampCheck = enabled;
+}
+
+/*!
+ * \brief Returns true iff the time the operation takes will be logged.
+ * The default is \c false.
+ */
+bool BuildOptions::logElapsedTime() const
+{
+ return d->logElapsedTime;
+}
+
+/*!
+ * \brief Controls whether the build time will be measured and logged.
+ */
+void BuildOptions::setLogElapsedTime(bool log)
+{
+ d->logElapsedTime = log;
+}
+
+
+bool operator==(const BuildOptions &bo1, const BuildOptions &bo2)
+{
+ return bo1.changedFiles() == bo2.changedFiles()
+ && bo1.dryRun() == bo2.dryRun()
+ && bo1.keepGoing() == bo2.keepGoing()
+ && bo1.logElapsedTime() == bo2.logElapsedTime()
+ && bo1.maxJobCount() == bo2.maxJobCount();
+}
+
+} // namespace qbs
diff --git a/src/lib/corelib/tools/buildoptions.h b/src/lib/corelib/tools/buildoptions.h
new file mode 100644
index 000000000..2c277fc3b
--- /dev/null
+++ b/src/lib/corelib/tools/buildoptions.h
@@ -0,0 +1,79 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+#ifndef QBS_BUILDOPTIONS_H
+#define QBS_BUILDOPTIONS_H
+
+#include "qbs_export.h"
+
+#include <QSharedDataPointer>
+#include <QStringList>
+
+namespace qbs {
+namespace Internal { class BuildOptionsPrivate; }
+
+class QBS_EXPORT BuildOptions
+{
+public:
+ BuildOptions();
+ BuildOptions(const BuildOptions &other);
+ BuildOptions &operator=(const BuildOptions &other);
+ ~BuildOptions();
+
+ QStringList changedFiles() const;
+ void setChangedFiles(const QStringList &changedFiles);
+
+ QStringList activeFileTags() const;
+ void setActiveFileTags(const QStringList &fileTags);
+
+ static int defaultMaxJobCount();
+ int maxJobCount() const;
+ void setMaxJobCount(int jobCount);
+
+ bool dryRun() const;
+ void setDryRun(bool dryRun);
+
+ bool keepGoing() const;
+ void setKeepGoing(bool keepGoing);
+
+ bool forceTimestampCheck() const;
+ void setForceTimestampCheck(bool enabled);
+
+ bool logElapsedTime() const;
+ void setLogElapsedTime(bool log);
+
+private:
+ QSharedDataPointer<Internal::BuildOptionsPrivate> d;
+};
+
+bool operator==(const BuildOptions &bo1, const BuildOptions &bo2);
+inline bool operator!=(const BuildOptions &bo1, const BuildOptions &bo2) { return !(bo1 == bo2); }
+
+} // namespace qbs
+
+#endif // QBS_BUILDOPTIONS_H
diff --git a/src/lib/corelib/tools/cleanoptions.cpp b/src/lib/corelib/tools/cleanoptions.cpp
new file mode 100644
index 000000000..359fd47dc
--- /dev/null
+++ b/src/lib/corelib/tools/cleanoptions.cpp
@@ -0,0 +1,158 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+#include "cleanoptions.h"
+
+#include <QSharedData>
+
+namespace qbs {
+namespace Internal {
+
+class CleanOptionsPrivate : public QSharedData
+{
+public:
+ CleanOptionsPrivate()
+ : cleanType(CleanOptions::CleanupAll), dryRun(false),
+ keepGoing(false), logElapsedTime(false)
+ { }
+
+ CleanOptions::CleanType cleanType;
+ bool dryRun;
+ bool keepGoing;
+ bool logElapsedTime;
+};
+
+}
+
+/*!
+ * \class CleanOptions
+ * \brief The \c CleanOptions class comprises parameters that influence the behavior of
+ * cleaning operations.
+ */
+
+/*!
+ * \enum CleanOptions::CleanType
+ * This enum type specifies which kind of build artifacts to remove.
+ * \value CleanupAll Indicates that all files created by the build process should be removed.
+ * \value CleanupTemporaries Indicates that only intermediate build artifacts should be removed.
+ * If, for example, the product to clean up for is a Linux shared library, the .so file
+ * would be left on the disk, but the .o files would be removed.
+ */
+
+CleanOptions::CleanOptions() : d(new Internal::CleanOptionsPrivate)
+{
+}
+
+CleanOptions::CleanOptions(const CleanOptions &other) : d(other.d)
+{
+}
+
+CleanOptions &CleanOptions::operator=(const CleanOptions &other)
+{
+ d = other.d;
+ return *this;
+}
+
+CleanOptions::~CleanOptions()
+{
+}
+
+/*!
+ * \brief Returns information about which type of artifacts will be removed.
+ */
+CleanOptions::CleanType CleanOptions::cleanType() const
+{
+ return d->cleanType;
+}
+
+/*!
+ * \brief Controls which kind of artifacts to remove.
+ * \sa CleanOptions::CleanType
+ */
+void CleanOptions::setCleanType(CleanOptions::CleanType cleanType)
+{
+ d->cleanType = cleanType;
+}
+
+/*!
+ * \brief Returns true iff qbs will not actually remove any files, but just show what would happen.
+ * The default is false.
+ */
+bool CleanOptions::dryRun() const
+{
+ return d->dryRun;
+}
+
+/*!
+ * \brief Controls whether clean-up will actually take place.
+ * If the argument is true, then qbs will emit information about which files would be removed
+ * instead of actually doing it.
+ */
+void CleanOptions::setDryRun(bool dryRun)
+{
+ d->dryRun = dryRun;
+}
+
+/*!
+ * Returns true iff clean-up will continue if an error occurs.
+ * The default is false.
+ */
+bool CleanOptions::keepGoing() const
+{
+ return d->keepGoing;
+}
+
+/*!
+ * \brief Controls whether to abort on errors.
+ * If the argument is true, then if a file cannot be removed e.g. due to a permission problem,
+ * a warning will be printed and the clean-up will continue. If the argument is false,
+ * then the clean-up will abort immediately in case of an error.
+ */
+void CleanOptions::setKeepGoing(bool keepGoing)
+{
+ d->keepGoing = keepGoing;
+}
+
+/*!
+ * \brief Returns true iff the time the operation takes will be logged.
+ * The default is false.
+ */
+bool CleanOptions::logElapsedTime() const
+{
+ return d->logElapsedTime;
+}
+
+/*!
+ * \brief Controls whether the clean-up time will be measured and logged.
+ */
+void CleanOptions::setLogElapsedTime(bool log)
+{
+ d->logElapsedTime = log;
+}
+
+} // namespace qbs
diff --git a/src/lib/corelib/tools/cleanoptions.h b/src/lib/corelib/tools/cleanoptions.h
new file mode 100644
index 000000000..0e3a0a564
--- /dev/null
+++ b/src/lib/corelib/tools/cleanoptions.h
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+#ifndef QBS_CLEANOPTIONS_H
+#define QBS_CLEANOPTIONS_H
+
+#include "qbs_export.h"
+
+#include <QSharedDataPointer>
+#include <QString>
+
+namespace qbs {
+namespace Internal { class CleanOptionsPrivate; }
+
+class QBS_EXPORT CleanOptions
+{
+public:
+ CleanOptions();
+ CleanOptions(const CleanOptions &other);
+ CleanOptions &operator=(const CleanOptions &other);
+ ~CleanOptions();
+
+ enum CleanType { CleanupAll, CleanupTemporaries };
+ CleanType cleanType() const;
+ void setCleanType(CleanType cleanType);
+
+ bool dryRun() const;
+ void setDryRun(bool dryRun);
+
+ bool keepGoing() const;
+ void setKeepGoing(bool keepGoing);
+
+ bool logElapsedTime() const;
+ void setLogElapsedTime(bool log);
+
+private:
+ QSharedDataPointer<Internal::CleanOptionsPrivate> d;
+};
+
+} // namespace qbs
+
+#endif // Include guard
diff --git a/src/lib/corelib/tools/codelocation.cpp b/src/lib/corelib/tools/codelocation.cpp
new file mode 100644
index 000000000..70a1814cd
--- /dev/null
+++ b/src/lib/corelib/tools/codelocation.cpp
@@ -0,0 +1,140 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+#include "codelocation.h"
+
+#include <QDataStream>
+#include <QDir>
+#include <QRegExp>
+#include <QSharedData>
+#include <QString>
+
+namespace qbs {
+
+class CodeLocation::CodeLocationPrivate : public QSharedData
+{
+public:
+ QString fileName;
+ int line;
+ int column;
+};
+
+CodeLocation::CodeLocation() : d(new CodeLocationPrivate)
+{
+ d->line = d->column = -1;
+}
+
+CodeLocation::CodeLocation(const QString &aFileName, int aLine, int aColumn)
+ : d(new CodeLocationPrivate)
+{
+ d->fileName = aFileName;
+ d->line = aLine;
+ d->column = aColumn;
+}
+
+CodeLocation::CodeLocation(const CodeLocation &other) : d(other.d)
+{
+}
+
+CodeLocation &CodeLocation::operator=(const CodeLocation &other)
+{
+ d = other.d;
+ return *this;
+}
+
+CodeLocation::~CodeLocation()
+{
+}
+
+QString CodeLocation::fileName() const
+{
+ return d->fileName;
+}
+
+int CodeLocation::line() const
+{
+ return d->line;
+}
+
+int CodeLocation::column() const
+{
+ return d->column;
+}
+
+bool CodeLocation::isValid() const
+{
+ return !fileName().isEmpty();
+}
+
+QString CodeLocation::toString() const
+{
+ QString str;
+ if (isValid()) {
+ str = QDir::toNativeSeparators(fileName());
+ QString lineAndColumn;
+ if (line() > 0 && !str.contains(QRegExp(QLatin1String(":[0-9]+$"))))
+ lineAndColumn += QLatin1Char(':') + QString::number(line());
+ if (column() > 0 && !str.contains(QRegExp(QLatin1String(":[0-9]+:[0-9]+$"))))
+ lineAndColumn += QLatin1Char(':') + QString::number(column());
+ str += lineAndColumn;
+ }
+ return str;
+}
+
+bool operator==(const CodeLocation &cl1, const CodeLocation &cl2)
+{
+ return cl1.fileName() == cl2.fileName() && cl1.line() == cl2.line()
+ && cl1.column() == cl2.column();
+}
+
+bool operator!=(const CodeLocation &cl1, const CodeLocation &cl2)
+{
+ return !(cl1 == cl2);
+}
+
+QDataStream &operator<<(QDataStream &s, const CodeLocation &o)
+{
+ s << o.fileName();
+ s << o.line();
+ s << o.column();
+ return s;
+}
+
+QDataStream &operator>>(QDataStream &s, CodeLocation &o)
+{
+ QString fileName;
+ int line;
+ int column;
+ s >> fileName;
+ s >> line;
+ s >> column;
+ o = CodeLocation(fileName, line, column);
+ return s;
+}
+
+} // namespace qbs
diff --git a/src/lib/corelib/tools/codelocation.h b/src/lib/corelib/tools/codelocation.h
new file mode 100644
index 000000000..204192b5a
--- /dev/null
+++ b/src/lib/corelib/tools/codelocation.h
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef QBS_SOURCELOCATION_H
+#define QBS_SOURCELOCATION_H
+
+#include "qbs_export.h"
+
+#include <QExplicitlySharedDataPointer>
+
+QT_BEGIN_NAMESPACE
+class QDataStream;
+class QString;
+QT_END_NAMESPACE
+
+namespace qbs {
+
+class QBS_EXPORT CodeLocation
+{
+public:
+ CodeLocation();
+ CodeLocation(const QString &aFileName, int aLine = -1, int aColumn = -1);
+ CodeLocation(const CodeLocation &other);
+ CodeLocation &operator=(const CodeLocation &other);
+ ~CodeLocation();
+
+ QString fileName() const;
+ int line() const;
+ int column() const;
+
+ bool isValid() const;
+ QString toString() const;
+private:
+ class CodeLocationPrivate;
+ QExplicitlySharedDataPointer<CodeLocationPrivate> d;
+};
+
+QBS_EXPORT bool operator==(const CodeLocation &cl1, const CodeLocation &cl2);
+QBS_EXPORT bool operator!=(const CodeLocation &cl1, const CodeLocation &cl2);
+
+QDataStream &operator<<(QDataStream &s, const CodeLocation &o);
+QDataStream &operator>>(QDataStream &s, CodeLocation &o);
+
+} // namespace qbs
+
+#endif // QBS_SOURCELOCATION_H
diff --git a/src/lib/corelib/tools/error.cpp b/src/lib/corelib/tools/error.cpp
new file mode 100644
index 000000000..4bcdc620c
--- /dev/null
+++ b/src/lib/corelib/tools/error.cpp
@@ -0,0 +1,196 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "error.h"
+
+#include <QSharedData>
+#include <QStringList>
+
+namespace qbs {
+
+class ErrorItem::ErrorItemPrivate : public QSharedData
+{
+public:
+ QString description;
+ CodeLocation codeLocation;
+};
+
+/*!
+ * \class ErrorData
+ * \brief The \c ErrorData class describes (part of) an error resulting from a qbs operation.
+ * It is always delivered as part of an \c Error.
+ * \sa Error
+ */
+
+ErrorItem::ErrorItem() : d(new ErrorItemPrivate)
+{
+}
+
+ErrorItem::ErrorItem(const QString &description, const CodeLocation &codeLocation)
+ : d(new ErrorItemPrivate)
+{
+ d->description = description;
+ d->codeLocation = codeLocation;
+}
+
+ErrorItem::ErrorItem(const ErrorItem &rhs) : d(rhs.d)
+{
+}
+
+ErrorItem &ErrorItem::operator=(const ErrorItem &other)
+{
+ d = other.d;
+ return *this;
+}
+
+ErrorItem::~ErrorItem()
+{
+}
+
+QString ErrorItem::description() const
+{
+ return d->description;
+}
+
+CodeLocation ErrorItem::codeLocation() const
+{
+ return d->codeLocation;
+}
+
+/*!
+ * \fn const QString &ErrorData::description() const
+ * \brief A general description of the error.
+ */
+
+ /*!
+ * \fn const QString &ErrorData::codeLocation() const
+ * \brief The location at which file in which the error occurred.
+ * \note This information might not be applicable, in which case location().isValid() returns false
+ */
+
+/*!
+ * \brief A full textual description of the error using all available information.
+ */
+QString ErrorItem::toString() const
+{
+ QString str = codeLocation().toString();
+ if (!str.isEmpty())
+ str += QLatin1Char(' ');
+ return str += description();
+}
+
+
+class ErrorInfo::ErrorInfoPrivate : public QSharedData
+{
+public:
+ ErrorInfoPrivate() : internalError(false) { }
+
+ QList<ErrorItem> items;
+ bool internalError;
+};
+
+/*!
+ * \class Error
+ * \brief Represents an error resulting from a qbs operation.
+ * It is made up of one or more \c ErrorData objects.
+ * \sa ErrorData
+ */
+
+ErrorInfo::ErrorInfo() : d(new ErrorInfoPrivate)
+{
+}
+
+ErrorInfo::ErrorInfo(const ErrorInfo &rhs) : d(rhs.d)
+{
+}
+
+ErrorInfo::ErrorInfo(const QString &description, const CodeLocation &location, bool internalError)
+ : d(new ErrorInfoPrivate)
+{
+ append(description, location);
+ d->internalError = internalError;
+}
+
+ErrorInfo &ErrorInfo::operator =(const ErrorInfo &other)
+{
+ d = other.d;
+ return *this;
+}
+
+ErrorInfo::~ErrorInfo()
+{
+}
+
+void ErrorInfo::append(const QString &description, const CodeLocation &location)
+{
+ d->items.append(ErrorItem(description, location));
+}
+
+void ErrorInfo::prepend(const QString &description, const CodeLocation &location)
+{
+ d->items.prepend(ErrorItem(description, location));
+}
+
+/*!
+ * \brief A list of concrete error description.
+ * Most often, there will be one element in this list, but there can be more e.g. to illustrate
+ * how an error condition propagates through several source files.
+ */
+QList<ErrorItem> ErrorInfo::items() const
+{
+ return d->items;
+}
+
+void ErrorInfo::clear()
+{
+ d->items.clear();
+}
+
+/*!
+ * \brief A complete textual description of the error.
+ * All "sub-errors" will be represented.
+ * \sa Error::entries()
+ */
+QString ErrorInfo::toString() const
+{
+ QStringList lines;
+ foreach (const ErrorItem &e, d->items)
+ lines.append(e.toString());
+ return lines.join(QLatin1String("\n"));
+}
+
+/*!
+ * \brief Returns true if this error represents a bug in qbs, false otherwise.
+ */
+bool ErrorInfo::isInternalError() const
+{
+ return d->internalError;
+}
+
+} // namespace qbs
diff --git a/src/lib/corelib/tools/error.h b/src/lib/corelib/tools/error.h
new file mode 100644
index 000000000..3aae9e239
--- /dev/null
+++ b/src/lib/corelib/tools/error.h
@@ -0,0 +1,94 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef QBS_ERROR
+#define QBS_ERROR
+
+#include "codelocation.h"
+
+#include <QExplicitlySharedDataPointer>
+#include <QList>
+#include <QMetaType>
+#include <QSharedDataPointer>
+
+QT_BEGIN_NAMESPACE
+class QString;
+QT_END_NAMESPACE
+
+namespace qbs {
+class CodeLocation;
+
+class QBS_EXPORT ErrorItem
+{
+ friend class ErrorInfo;
+public:
+ ErrorItem();
+ ErrorItem(const ErrorItem &rhs);
+ ErrorItem &operator=(const ErrorItem &other);
+ ~ErrorItem();
+
+ QString description() const;
+ CodeLocation codeLocation() const;
+ QString toString() const;
+
+private:
+ ErrorItem(const QString &description, const CodeLocation &codeLocation);
+
+ class ErrorItemPrivate;
+ QExplicitlySharedDataPointer<ErrorItemPrivate> d;
+};
+
+class QBS_EXPORT ErrorInfo
+{
+public:
+ ErrorInfo();
+ ErrorInfo(const ErrorInfo &rhs);
+ ErrorInfo(const QString &description, const CodeLocation &location = CodeLocation(),
+ bool internalError = false);
+ ErrorInfo &operator=(const ErrorInfo &other);
+ ~ErrorInfo();
+
+ void append(const QString &description, const CodeLocation &location = CodeLocation());
+ void prepend(const QString &description, const CodeLocation &location = CodeLocation());
+ QList<ErrorItem> items() const;
+ bool hasError() const { return !items().isEmpty(); }
+ void clear();
+ QString toString() const;
+ bool isInternalError() const;
+
+private:
+ class ErrorInfoPrivate;
+ QSharedDataPointer<ErrorInfoPrivate> d;
+};
+
+} // namespace qbs
+
+Q_DECLARE_METATYPE(qbs::ErrorInfo)
+
+#endif // QBS_ERROR
diff --git a/src/lib/corelib/tools/fileinfo.cpp b/src/lib/corelib/tools/fileinfo.cpp
new file mode 100644
index 000000000..9eca8c2ca
--- /dev/null
+++ b/src/lib/corelib/tools/fileinfo.cpp
@@ -0,0 +1,477 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "fileinfo.h"
+
+#include <logging/translator.h>
+#include <tools/hostosinfo.h>
+#include <tools/qbsassert.h>
+
+#include <QCoreApplication>
+#include <QDateTime>
+#include <QDir>
+#include <QFileInfo>
+
+#if defined(Q_OS_UNIX)
+#include <errno.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#elif defined(Q_OS_WIN)
+#include <qt_windows.h>
+#endif
+
+namespace qbs {
+namespace Internal {
+
+QString FileInfo::fileName(const QString &fp)
+{
+ int last = fp.lastIndexOf(QLatin1Char('/'));
+ if (last < 0)
+ return fp;
+ return fp.mid(last + 1);
+}
+
+QString FileInfo::baseName(const QString &fp)
+{
+ QString fn = fileName(fp);
+ int dot = fn.indexOf(QLatin1Char('.'));
+ if (dot < 0)
+ return fp;
+ return fn.mid(0, dot);
+}
+
+QString FileInfo::completeBaseName(const QString &fp)
+{
+ QString fn = fileName(fp);
+ int dot = fn.lastIndexOf(QLatin1Char('.'));
+ if (dot < 0)
+ return fp;
+ return fn.mid(0, dot);
+}
+
+QString FileInfo::path(const QString &fp)
+{
+ if (fp.isEmpty())
+ return QString();
+ if (fp.at(fp.size() - 1) == QLatin1Char('/'))
+ return fp;
+ int last = fp.lastIndexOf(QLatin1Char('/'));
+ if (last < 0)
+ return QLatin1String(".");
+ return QDir::cleanPath(fp.mid(0, last));
+}
+
+void FileInfo::splitIntoDirectoryAndFileName(const QString &filePath, QString *dirPath, QString *fileName)
+{
+ int idx = filePath.lastIndexOf(QLatin1Char('/'));
+ if (idx < 0) {
+ dirPath->clear();
+ *fileName = filePath;
+ return;
+ }
+ *dirPath = filePath.left(idx);
+ *fileName = filePath.mid(idx + 1);
+}
+
+void FileInfo::splitIntoDirectoryAndFileName(const QString &filePath, QStringRef *dirPath, QStringRef *fileName)
+{
+ int idx = filePath.lastIndexOf(QLatin1Char('/'));
+ if (idx < 0) {
+ dirPath->clear();
+ *fileName = QStringRef(&filePath);
+ return;
+ }
+ *dirPath = filePath.leftRef(idx);
+ *fileName = filePath.midRef(idx + 1);
+}
+
+bool FileInfo::exists(const QString &fp)
+{
+ return FileInfo(fp).exists();
+}
+
+// from creator/src/shared/proparser/ioutils.cpp
+bool FileInfo::isAbsolute(const QString &path)
+{
+ const int n = path.size();
+ if (n == 0)
+ return false;
+ const QChar at0 = path.at(0);
+ if (at0 == QLatin1Char('/'))
+ return true;
+ if (HostOsInfo::isWindowsHost()) {
+ if (at0 == QLatin1Char('\\'))
+ return true;
+ // Unlike QFileInfo, this won't accept a relative path with a drive letter.
+ // Such paths result in a royal mess anyway ...
+ if (n >= 3 && path.at(1) == QLatin1Char(':') && at0.isLetter()
+ && (path.at(2) == QLatin1Char('/') || path.at(2) == QLatin1Char('\\')))
+ return true;
+ }
+ return false;
+}
+
+bool FileInfo::isPattern(const QString &str)
+{
+ return isPattern(QStringRef(&str));
+}
+
+bool FileInfo::isPattern(const QStringRef &str)
+{
+ for (int i = 0; i < str.size(); ++i) {
+ const QChar ch = str.at(i);
+ if (ch == QLatin1Char('*') || ch == QLatin1Char('?')
+ || ch == QLatin1Char(']') || ch == QLatin1Char('[')) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Concatenates the paths \a base and \a rel.
+ * Base must be an absolute path.
+ * Double dots at the start of \a rel are handled.
+ * This function assumes that both paths are clean, that is they don't contain
+ * double slashes or redundant dot parts.
+ */
+QString FileInfo::resolvePath(const QString &base, const QString &rel)
+{
+ QBS_ASSERT(isAbsolute(base), return QString());
+ if (isAbsolute(rel))
+ return rel;
+ if (rel.size() == 1 && rel.at(0) == QLatin1Char('.'))
+ return base;
+ if (rel.size() == 1 && rel.at(0) == QLatin1Char('~'))
+ return QDir::homePath();
+ if (rel.startsWith(QLatin1String("~/")))
+ return QDir::homePath() + rel.mid(1);
+
+ QString r = base;
+ if (r.endsWith(QLatin1Char('/')))
+ r.chop(1);
+
+ QString s = rel;
+ while (s.startsWith(QLatin1String("../"))) {
+ s.remove(0, 3);
+ int idx = r.lastIndexOf(QLatin1Char('/'));
+ if (idx >= 0)
+ r.truncate(idx);
+ }
+
+ r.reserve(r.length() + 1 + s.length());
+ r += QLatin1Char('/');
+ r += s;
+ return r;
+}
+
+bool FileInfo::globMatches(const QRegExp &regexp, const QString &fileName)
+{
+ const QString pattern = regexp.pattern();
+ // May be it's simple wildcard, i.e. "*.cpp"?
+ if (pattern.startsWith(QLatin1Char('*')) && !isPattern(pattern.midRef(1))) {
+ // Yes, it's rather simple to just check the extension
+ return fileName.endsWith(pattern.midRef(1));
+ }
+ return regexp.exactMatch(fileName);
+}
+
+bool FileInfo::isFileCaseCorrect(const QString &filePath)
+{
+#if defined(Q_OS_WIN)
+ // QFileInfo::canonicalFilePath() does not return the real case of the file path on Windows.
+ QFileInfo fi(filePath);
+ const QString absolute = fi.absoluteFilePath();
+ WIN32_FIND_DATA fd;
+ HANDLE hFindFile = ::FindFirstFile((wchar_t*)absolute.utf16(), &fd);
+ if (hFindFile == INVALID_HANDLE_VALUE)
+ return false;
+ const QString actualFileName = QString::fromWCharArray(fd.cFileName);
+ FindClose(hFindFile);
+ return actualFileName == fi.fileName();
+#elif defined(Q_OS_DARWIN)
+ QFileInfo fi(filePath);
+ return fi.absoluteFilePath() == fi.canonicalFilePath();
+#else
+ Q_UNUSED(filePath)
+ return true;
+#endif
+}
+
+#if defined(Q_OS_WIN)
+
+#define z(x) reinterpret_cast<WIN32_FILE_ATTRIBUTE_DATA*>(const_cast<FileInfo::InternalStatType*>(&x))
+
+template<bool> struct CompileTimeAssert;
+template<> struct CompileTimeAssert<true> {};
+
+FileInfo::FileInfo(const QString &fileName)
+{
+ static CompileTimeAssert<
+ sizeof(FileInfo::InternalStatType) == sizeof(WIN32_FILE_ATTRIBUTE_DATA)
+ > internal_type_has_wrong_size;
+ Q_UNUSED(internal_type_has_wrong_size);
+ if (!GetFileAttributesEx(reinterpret_cast<const WCHAR*>(fileName.utf16()),
+ GetFileExInfoStandard, &m_stat))
+ {
+ ZeroMemory(z(m_stat), sizeof(WIN32_FILE_ATTRIBUTE_DATA));
+ z(m_stat)->dwFileAttributes = INVALID_FILE_ATTRIBUTES;
+ }
+}
+
+bool FileInfo::exists() const
+{
+ return z(m_stat)->dwFileAttributes != INVALID_FILE_ATTRIBUTES;
+}
+
+FileTime FileInfo::lastModified() const
+{
+ const FileTime::InternalType* ft_it;
+ ft_it = reinterpret_cast<const FileTime::InternalType*>(&z(m_stat)->ftLastWriteTime);
+ return FileTime(*ft_it);
+}
+
+FileTime FileInfo::lastStatusChange() const
+{
+ return lastModified();
+}
+
+bool FileInfo::isDir() const
+{
+ return z(m_stat)->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
+}
+
+static QString resolveSymlinks(const QString &fileName)
+{
+ QFileInfo fi(fileName);
+ while (fi.isSymLink())
+ fi.setFile(fi.symLinkTarget());
+ return fi.absoluteFilePath();
+}
+
+QString applicationDirPath()
+{
+ static const QString appDirPath = FileInfo::path(resolveSymlinks(QCoreApplication::applicationFilePath()));
+ return appDirPath;
+}
+
+#elif defined(Q_OS_UNIX)
+
+FileInfo::FileInfo(const QString &fileName)
+{
+ if (stat(fileName.toLocal8Bit(), &m_stat) == -1)
+ m_stat.st_mtime = 0;
+}
+
+bool FileInfo::exists() const
+{
+ return m_stat.st_mtime != 0;
+}
+
+FileTime FileInfo::lastModified() const
+{
+ return m_stat.st_mtime;
+}
+
+FileTime FileInfo::lastStatusChange() const
+{
+ return m_stat.st_ctime;
+}
+
+bool FileInfo::isDir() const
+{
+ return S_ISDIR(m_stat.st_mode);
+}
+
+#endif
+
+// adapted from qtc/plugins/vcsbase/cleandialog.cpp
+bool removeFileRecursion(const QFileInfo &f, QString *errorMessage)
+{
+ if (!f.exists())
+ return true;
+ if (f.isDir()) {
+ const QDir dir(f.absoluteFilePath());
+ foreach(const QFileInfo &fi, dir.entryInfoList(QDir::AllEntries|QDir::NoDotAndDotDot|QDir::Hidden))
+ removeFileRecursion(fi, errorMessage);
+ QDir parent = f.absoluteDir();
+ if (!parent.rmdir(f.fileName())) {
+ errorMessage->append(Tr::tr("The directory %1 could not be deleted.").
+ arg(QDir::toNativeSeparators(f.absoluteFilePath())));
+ return false;
+ }
+ } else {
+ QFile file(f.absoluteFilePath());
+ file.setPermissions(f.permissions() | QFile::WriteUser);
+ if (!file.remove()) {
+ if (!errorMessage->isEmpty())
+ errorMessage->append(QLatin1Char('\n'));
+ errorMessage->append(Tr::tr("The file %1 could not be deleted.").
+ arg(QDir::toNativeSeparators(f.absoluteFilePath())));
+ return false;
+ }
+ }
+ return true;
+}
+
+bool removeDirectoryWithContents(const QString &path, QString *errorMessage)
+{
+ QFileInfo f(path);
+ if (f.exists() && !f.isDir()) {
+ *errorMessage = Tr::tr("%1 is not a directory.").arg(QDir::toNativeSeparators(path));
+ return false;
+ }
+ return removeFileRecursion(f, errorMessage);
+}
+
+/*!
+ * Returns the stored link target of the symbolic link \a{filePath}.
+ * Unlike QFileInfo::symLinkTarget, this will not make the link target an absolute path.
+ */
+static QByteArray storedLinkTarget(const QString &filePath)
+{
+ QByteArray result;
+
+#ifdef Q_OS_UNIX
+ const QByteArray nativeFilePath = QFile::encodeName(filePath);
+ ssize_t len;
+ while (true) {
+ struct stat sb;
+ if (lstat(nativeFilePath.constData(), &sb)) {
+ qWarning("storedLinkTarget: lstat for %s failed with error code %d",
+ nativeFilePath.constData(), errno);
+ return QByteArray();
+ }
+
+ result.resize(sb.st_size);
+ len = readlink(nativeFilePath.constData(), result.data(), sb.st_size + 1);
+ if (len < 0) {
+ qWarning("storedLinkTarget: readlink for %s failed with error code %d",
+ nativeFilePath.constData(), errno);
+ return QByteArray();
+ }
+
+ if (len < sb.st_size) {
+ result.resize(len);
+ break;
+ }
+ if (len == sb.st_size)
+ break;
+ }
+#else
+ Q_UNUSED(filePath);
+#endif // Q_OS_UNIX
+
+ return result;
+}
+
+static bool createSymLink(const QByteArray &path1, const QString &path2)
+{
+#ifdef Q_OS_UNIX
+ const QByteArray newPath = QFile::encodeName(path2);
+ unlink(newPath.constData());
+ return symlink(path1.constData(), newPath.constData()) == 0;
+#else
+ Q_UNUSED(path1);
+ Q_UNUSED(path2);
+ return false;
+#endif // Q_OS_UNIX
+}
+
+/*!
+ Copies the directory specified by \a srcFilePath recursively to \a tgtFilePath.
+ \a tgtFilePath will contain the target directory, which will be created. Example usage:
+
+ \code
+ QString error;
+ book ok = Utils::FileUtils::copyRecursively("/foo/bar", "/foo/baz", &error);
+ if (!ok)
+ qDebug() << error;
+ \endcode
+
+ This will copy the contents of /foo/bar into to the baz directory under /foo,
+ which will be created in the process.
+
+ \return Whether the operation succeeded.
+ \note Function was adapted from qtc/src/libs/fileutils.cpp
+*/
+
+bool copyFileRecursion(const QString &srcFilePath, const QString &tgtFilePath,
+ bool preserveSymLinks, QString *errorMessage)
+{
+ QFileInfo srcFileInfo(srcFilePath);
+ QFileInfo tgtFileInfo(tgtFilePath);
+ const QString targetDirPath = tgtFileInfo.absoluteDir().path();
+ if (!QDir::root().mkpath(targetDirPath)) {
+ *errorMessage = Tr::tr("The directory '%1' could not be created.")
+ .arg(QDir::toNativeSeparators(targetDirPath));
+ return false;
+ }
+ if (HostOsInfo::isAnyUnixHost() && preserveSymLinks && srcFileInfo.isSymLink()) {
+ // For now, disable symlink preserving copying on Windows.
+ // MS did a good job to prevent people from using symlinks - even if they are supported.
+ if (!createSymLink(storedLinkTarget(srcFilePath), tgtFilePath)) {
+ *errorMessage = Tr::tr("The symlink '%1' could not be created.")
+ .arg(tgtFilePath);
+ return false;
+ }
+ } else if (srcFileInfo.isDir()) {
+ QDir sourceDir(srcFilePath);
+ QStringList fileNames = sourceDir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot
+ | QDir::Hidden | QDir::System);
+ foreach (const QString &fileName, fileNames) {
+ const QString newSrcFilePath = srcFilePath + QLatin1Char('/') + fileName;
+ const QString newTgtFilePath = tgtFilePath + QLatin1Char('/') + fileName;
+ if (!copyFileRecursion(newSrcFilePath, newTgtFilePath, preserveSymLinks, errorMessage))
+ return false;
+ }
+ } else {
+ if (tgtFileInfo.exists() && srcFileInfo.lastModified() <= tgtFileInfo.lastModified())
+ return true;
+ QFile file(srcFilePath);
+ QFile targetFile(tgtFilePath);
+ if (targetFile.exists()) {
+ targetFile.setPermissions(targetFile.permissions() | QFile::WriteUser);
+ if (!targetFile.remove()) {
+ *errorMessage = Tr::tr("Could not remove file '%1'. %2")
+ .arg(QDir::toNativeSeparators(tgtFilePath), targetFile.errorString());
+ }
+ }
+ if (!file.copy(tgtFilePath)) {
+ *errorMessage = Tr::tr("Could not copy file '%1' to '%2'. %3")
+ .arg(QDir::toNativeSeparators(srcFilePath), QDir::toNativeSeparators(tgtFilePath),
+ file.errorString());
+ return false;
+ }
+ }
+ return true;
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/tools/fileinfo.h b/src/lib/corelib/tools/fileinfo.h
new file mode 100644
index 000000000..b5731cedd
--- /dev/null
+++ b/src/lib/corelib/tools/fileinfo.h
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef QBS_FILEINFO_H
+#define QBS_FILEINFO_H
+
+#include "filetime.h"
+#include "qbs_export.h"
+
+#if defined(Q_OS_UNIX)
+#include <sys/stat.h>
+#endif
+
+#include <QString>
+
+QT_FORWARD_DECLARE_CLASS(QFileInfo)
+
+namespace qbs {
+namespace Internal {
+
+class FileInfo
+{
+public:
+ FileInfo(const QString &fileName);
+
+ bool exists() const;
+ FileTime lastModified() const;
+ FileTime lastStatusChange() const;
+ bool isDir() const;
+
+ static QString fileName(const QString &fp);
+ static QString baseName(const QString &fp);
+ static QString completeBaseName(const QString &fp);
+ static QString path(const QString &fp);
+ static void splitIntoDirectoryAndFileName(const QString &filePath, QString *dirPath, QString *fileName);
+ static void splitIntoDirectoryAndFileName(const QString &filePath, QStringRef *dirPath, QStringRef *fileName);
+ static bool exists(const QString &fp);
+ static bool isAbsolute(const QString &fp);
+ static bool isPattern(const QStringRef &str);
+ static bool isPattern(const QString &str);
+ static QString resolvePath(const QString &base, const QString &rel);
+ static bool globMatches(const QRegExp &pattern, const QString &subject);
+ static bool isFileCaseCorrect(const QString &filePath);
+
+private:
+#if defined(Q_OS_WIN)
+ struct InternalStatType
+ {
+ quint8 z[36];
+ };
+#elif defined(Q_OS_UNIX)
+ typedef struct stat InternalStatType;
+#else
+# error unknown platform
+#endif
+ InternalStatType m_stat;
+};
+
+bool removeFileRecursion(const QFileInfo &f, QString *errorMessage);
+
+// FIXME: Used by tests.
+bool QBS_EXPORT removeDirectoryWithContents(const QString &path, QString *errorMessage);
+bool QBS_EXPORT copyFileRecursion(const QString &sourcePath, const QString &targetPath,
+ bool preserveSymLinks, QString *errorMessage);
+
+} // namespace Internal
+} // namespace qbs
+
+#endif
diff --git a/src/lib/corelib/tools/filetime.h b/src/lib/corelib/tools/filetime.h
new file mode 100644
index 000000000..0dc0524df
--- /dev/null
+++ b/src/lib/corelib/tools/filetime.h
@@ -0,0 +1,128 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef QBS_FILETIME_H
+#define QBS_FILETIME_H
+
+#include <QDataStream>
+#include <QDebug>
+
+#if defined(Q_OS_UNIX)
+#include <time.h>
+#endif
+
+namespace qbs {
+namespace Internal {
+
+class FileTime
+{
+public:
+#if defined(Q_OS_UNIX)
+ typedef time_t InternalType;
+#elif defined(Q_OS_WIN)
+ typedef quint64 InternalType;
+#else
+# error unknown platform
+#endif
+
+ FileTime();
+ FileTime(const InternalType &ft)
+ : m_fileTime(ft)
+ { }
+
+ bool operator < (const FileTime &rhs) const;
+ bool operator > (const FileTime &rhs) const;
+ bool operator <= (const FileTime &rhs) const;
+ bool operator >= (const FileTime &rhs) const;
+ bool operator == (const FileTime &rhs) const;
+ bool operator != (const FileTime &rhs) const;
+
+ void clear();
+ bool isValid() const;
+ QString toString() const;
+
+ static FileTime currentTime();
+
+ friend class FileInfo;
+ InternalType m_fileTime;
+};
+
+inline bool FileTime::operator > (const FileTime &rhs) const
+{
+ return rhs < *this;
+}
+
+inline bool FileTime::operator <= (const FileTime &rhs) const
+{
+ return operator < (rhs) || operator == (rhs);
+}
+
+inline bool FileTime::operator >= (const FileTime &rhs) const
+{
+ return operator > (rhs) || operator == (rhs);
+}
+
+inline bool FileTime::operator == (const FileTime &rhs) const
+{
+ return m_fileTime == rhs.m_fileTime;
+}
+
+inline bool FileTime::operator != (const FileTime &rhs) const
+{
+ return !operator==(rhs);
+}
+
+} // namespace Internal
+} // namespace qbs
+
+QT_BEGIN_NAMESPACE
+
+inline QDataStream& operator>>(QDataStream &stream, qbs::Internal::FileTime &ft)
+{
+ quint64 u;
+ stream >> u;
+ ft.m_fileTime = u;
+ return stream;
+}
+
+inline QDataStream& operator<<(QDataStream &stream, const qbs::Internal::FileTime &ft)
+{
+ stream << (quint64)ft.m_fileTime;
+ return stream;
+}
+
+inline QDebug operator<<(QDebug dbg, const qbs::Internal::FileTime &t)
+{
+ dbg.nospace() << t.toString();
+ return dbg.space();
+}
+
+QT_END_NAMESPACE
+
+#endif // QBS_FILETIME_H
diff --git a/src/lib/corelib/tools/filetime_unix.cpp b/src/lib/corelib/tools/filetime_unix.cpp
new file mode 100644
index 000000000..945be8f44
--- /dev/null
+++ b/src/lib/corelib/tools/filetime_unix.cpp
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "filetime.h"
+
+#include <QDateTime>
+#include <QString>
+
+#include <time.h>
+
+namespace qbs {
+namespace Internal {
+
+FileTime::FileTime()
+ : m_fileTime(0)
+{
+}
+
+bool FileTime::operator < (const FileTime &rhs) const
+{
+ return m_fileTime < rhs.m_fileTime;
+}
+
+void FileTime::clear()
+{
+ m_fileTime = 0;
+}
+
+bool FileTime::isValid() const
+{
+ return m_fileTime != 0;
+}
+
+FileTime FileTime::currentTime()
+{
+ return time(0);
+}
+
+QString FileTime::toString() const
+{
+ QDateTime dt;
+ dt.setTime_t(m_fileTime);
+ return dt.toString();
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/tools/filetime_win.cpp b/src/lib/corelib/tools/filetime_win.cpp
new file mode 100644
index 000000000..b3a7fab67
--- /dev/null
+++ b/src/lib/corelib/tools/filetime_win.cpp
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "filetime.h"
+
+#include <QString>
+#include <qt_windows.h>
+#ifdef Q_CC_MSVC
+#include <strsafe.h>
+#endif // Q_CC_MSVC
+
+namespace qbs {
+namespace Internal {
+
+template<bool> struct CompileTimeAssert;
+template<> struct CompileTimeAssert<true> {};
+
+FileTime::FileTime()
+ : m_fileTime(0)
+{
+ static CompileTimeAssert<sizeof(FileTime::InternalType) == sizeof(FILETIME)> internal_type_has_wrong_size;
+ Q_UNUSED(internal_type_has_wrong_size);
+}
+
+bool FileTime::operator < (const FileTime &rhs) const
+{
+ const FILETIME *const t1 = reinterpret_cast<const FILETIME *>(&m_fileTime);
+ const FILETIME *const t2 = reinterpret_cast<const FILETIME *>(&rhs.m_fileTime);
+ return CompareFileTime(t1, t2) < 0;
+}
+
+void FileTime::clear()
+{
+ m_fileTime = 0;
+}
+
+bool FileTime::isValid() const
+{
+ return m_fileTime != 0;
+}
+
+FileTime FileTime::currentTime()
+{
+ FileTime result;
+ SYSTEMTIME st;
+ GetSystemTime(&st);
+ FILETIME *const ft = reinterpret_cast<FILETIME *>(&result.m_fileTime);
+ SystemTimeToFileTime(&st, ft);
+ return result;
+}
+
+QString FileTime::toString() const
+{
+ const FILETIME *const ft = reinterpret_cast<const FILETIME *>(&m_fileTime);
+ SYSTEMTIME stUTC, stLocal;
+ FileTimeToSystemTime(ft, &stUTC);
+ SystemTimeToTzSpecificLocalTime(NULL, &stUTC, &stLocal);
+#ifdef Q_CC_MSVC
+ WCHAR szString[512];
+ HRESULT hr = StringCchPrintf(szString, sizeof(szString) / sizeof(WCHAR),
+ L"%02d.%02d.%d %02d:%02d:%02d:%02d",
+ stLocal.wDay, stLocal.wMonth, stLocal.wYear,
+ stLocal.wHour, stLocal.wMinute, stLocal.wSecond,
+ stLocal.wMilliseconds);
+ return SUCCEEDED(hr) ? QString::fromWCharArray(szString) : QString();
+#else // Q_CC_MSVC
+ const QString result = QString("%1.%2.%3 %4:%5:%6")
+ .arg(stLocal.wDay, 2, 10, QLatin1Char('0')).arg(stLocal.wMonth, 2, 10, QLatin1Char('0')).arg(stLocal.wYear)
+ .arg(stLocal.wHour, 2, 10, QLatin1Char('0')).arg(stLocal.wMinute, 2, 10, QLatin1Char('0')).arg(stLocal.wSecond, 2, 10, QLatin1Char('0'));
+ return result;
+#endif // Q_CC_MSVC
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/tools/hostosinfo.h b/src/lib/corelib/tools/hostosinfo.h
new file mode 100644
index 000000000..0bf05cbfd
--- /dev/null
+++ b/src/lib/corelib/tools/hostosinfo.h
@@ -0,0 +1,196 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef QBS_HOSTOSINFO_H
+#define QBS_HOSTOSINFO_H
+
+#include "qbs_export.h"
+
+#include <QtGlobal>
+#include <QMap>
+#include <QString>
+#include <QStringList>
+
+#if defined(Q_OS_WIN)
+#define QTC_HOST_EXE_SUFFIX ".exe"
+#define QTC_HOST_DYNAMICLIB_PREFIX ""
+#define QTC_HOST_DYNAMICLIB_SUFFIX ".dll"
+#define QTC_HOST_OBJECT_SUFFIX ".obj"
+#elif defined(Q_OS_DARWIN)
+#define QTC_HOST_EXE_SUFFIX ""
+#define QTC_HOST_DYNAMICLIB_PREFIX "lib"
+#define QTC_HOST_DYNAMICLIB_SUFFIX ".dylib"
+#define QTC_HOST_OBJECT_SUFFIX ".o"
+#else
+#define QTC_HOST_EXE_SUFFIX ""
+#define QTC_HOST_DYNAMICLIB_PREFIX "lib"
+#define QTC_HOST_DYNAMICLIB_SUFFIX ".so"
+#define QTC_HOST_OBJECT_SUFFIX ".o"
+#endif // Q_OS_WIN
+
+namespace qbs {
+namespace Internal {
+
+class QBS_EXPORT HostOsInfo // Exported for use by command-line tools.
+{
+public:
+ // Add more as needed.
+ enum HostOs { HostOsWindows, HostOsLinux, HostOsOsx, HostOsOtherUnix, HostOsOther };
+
+ static inline HostOs hostOs();
+
+ static bool isWindowsHost() { return hostOs() == HostOsWindows; }
+ static bool isLinuxHost() { return hostOs() == HostOsLinux; }
+ static bool isOsxHost() { return hostOs() == HostOsOsx; }
+ static inline bool isAnyUnixHost();
+ static inline QString canonicalArchitecture(const QString &architecture);
+ static inline QString defaultEndianness(const QString &architecture);
+
+ static QString appendExecutableSuffix(const QString &executable)
+ {
+ QString finalName = executable;
+ if (isWindowsHost())
+ finalName += QLatin1String(QTC_HOST_EXE_SUFFIX);
+ return finalName;
+ }
+
+ static QString dynamicLibraryName(const QString &libraryBaseName)
+ {
+ return QLatin1String(QTC_HOST_DYNAMICLIB_PREFIX) + libraryBaseName
+ + QLatin1String(QTC_HOST_DYNAMICLIB_SUFFIX);
+ }
+
+ static QString objectName(const QString &baseName)
+ {
+ return baseName + QLatin1String(QTC_HOST_OBJECT_SUFFIX);
+ }
+
+ static Qt::CaseSensitivity fileNameCaseSensitivity()
+ {
+ return isWindowsHost() ? Qt::CaseInsensitive: Qt::CaseSensitive;
+ }
+
+ static QChar pathListSeparator()
+ {
+ return isWindowsHost() ? QLatin1Char(';') : QLatin1Char(':');
+ }
+
+ static Qt::KeyboardModifier controlModifier()
+ {
+ return isOsxHost() ? Qt::MetaModifier : Qt::ControlModifier;
+ }
+};
+
+HostOsInfo::HostOs HostOsInfo::hostOs()
+{
+#if defined(Q_OS_WIN)
+ return HostOsWindows;
+#elif defined(Q_OS_LINUX)
+ return HostOsLinux;
+#elif defined(Q_OS_DARWIN)
+ return HostOsOsx;
+#elif defined(Q_OS_UNIX)
+ return HostOsOtherUnix;
+#else
+ return HostOsOther;
+#endif
+}
+
+bool HostOsInfo::isAnyUnixHost()
+{
+#ifdef Q_OS_UNIX
+ return true;
+#else
+ return false;
+#endif
+}
+
+QString HostOsInfo::canonicalArchitecture(const QString &architecture)
+{
+ QMap<QString, QStringList> archMap;
+ archMap.insert(QLatin1String("x86"), QStringList()
+ << QLatin1String("i386")
+ << QLatin1String("i486")
+ << QLatin1String("i586")
+ << QLatin1String("i686")
+ << QLatin1String("ia32")
+ << QLatin1String("ia-32")
+ << QLatin1String("x86_32")
+ << QLatin1String("x86-32")
+ << QLatin1String("intel32"));
+
+ archMap.insert(QLatin1String("x86_64"), QStringList()
+ << QLatin1String("x86-64")
+ << QLatin1String("x64")
+ << QLatin1String("amd64")
+ << QLatin1String("ia32e")
+ << QLatin1String("em64t")
+ << QLatin1String("intel64"));
+
+ archMap.insert(QLatin1String("ia64"), QStringList()
+ << QLatin1String("ia-64")
+ << QLatin1String("itanium"));
+
+ QMapIterator<QString, QStringList> i(archMap);
+ while (i.hasNext()) {
+ i.next();
+ if (i.value().contains(architecture.toLower()))
+ return i.key();
+ }
+
+ return architecture;
+}
+
+QString HostOsInfo::defaultEndianness(const QString &architecture)
+{
+ const QString canonicalArch = canonicalArchitecture(architecture);
+
+ QStringList little = QStringList()
+ << QLatin1String("x86")
+ << QLatin1String("x86_64")
+ << QLatin1String("arm")
+ << QLatin1String("arm64");
+
+ if (little.contains(canonicalArch))
+ return QLatin1String("little");
+
+ QStringList big = QStringList()
+ << QLatin1String("ppc")
+ << QLatin1String("ppc64");
+
+ if (big.contains(canonicalArch))
+ return QLatin1String("big");
+
+ return QString();
+}
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_HOSTOSINFO_H
diff --git a/src/lib/corelib/tools/id.cpp b/src/lib/corelib/tools/id.cpp
new file mode 100644
index 000000000..a9dc07cbc
--- /dev/null
+++ b/src/lib/corelib/tools/id.cpp
@@ -0,0 +1,324 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "id.h"
+#include "qbsassert.h"
+
+#include <QByteArray>
+#include <QHash>
+#include <QVector>
+
+namespace qbs {
+namespace Internal {
+
+/*!
+ \class qbs::Internal::Id
+
+ \brief The class Id encapsulates an identifier that is unique
+ within a specific running process, using the qbs library.
+
+ \c{Id} is used as facility to identify objects of interest
+ in a more typesafe and faster manner than a plain \c QString or
+ \c QByteArray would provide.
+
+ An id is internally represented as a 32 bit integer (its \c UID)
+ and associated with a plain 7-bit-clean ASCII name used
+ for display and persistency.
+
+ This class is copied from Qt Creator.
+*/
+
+class StringHolder
+{
+public:
+ StringHolder()
+ : n(0), str(0)
+ {}
+
+ StringHolder(const char *s, int length)
+ : n(length), str(s)
+ {
+ if (!n)
+ length = n = qstrlen(s);
+ h = 0;
+ while (length--) {
+ h = (h << 4) + *s++;
+ h ^= (h & 0xf0000000) >> 23;
+ h &= 0x0fffffff;
+ }
+ }
+ int n;
+ const char *str;
+ uint h;
+};
+
+static bool operator==(const StringHolder &sh1, const StringHolder &sh2)
+{
+ // sh.n is unlikely to discriminate better than the hash.
+ return sh1.h == sh2.h && sh1.str && sh2.str && strcmp(sh1.str, sh2.str) == 0;
+}
+
+
+static uint qHash(const StringHolder &sh)
+{
+ return sh.h;
+}
+
+struct IdCache : public QHash<StringHolder, int>
+{
+#ifndef QBS_ALLOW_STATIC_LEAKS
+ ~IdCache()
+ {
+ for (IdCache::iterator it = begin(); it != end(); ++it)
+ delete[](const_cast<char *>(it.key().str));
+ }
+#endif
+};
+
+
+static int firstUnusedId = Id::IdsPerPlugin * Id::ReservedPlugins;
+
+static QHash<int, StringHolder> stringFromId;
+static IdCache idFromString;
+
+static int theId(const char *str, int n = 0)
+{
+ QBS_ASSERT(str && *str, return 0);
+ StringHolder sh(str, n);
+ int res = idFromString.value(sh, 0);
+ if (res == 0) {
+ res = firstUnusedId++;
+ sh.str = qstrdup(sh.str);
+ idFromString[sh] = res;
+ stringFromId[res] = sh;
+ }
+ return res;
+}
+
+static int theId(const QByteArray &ba)
+{
+ return theId(ba.constData(), ba.size());
+}
+
+/*!
+ \fn qbs::Internal::Id(int uid)
+
+ \brief Constructs an id given a UID.
+
+ The UID is an integer value that is unique within the running
+ process.
+
+ It is the callers responsibility to ensure the uniqueness of
+ the passed integer. The recommended approach is to use
+ \c{registerId()} with an value taken from the plugin's
+ private range.
+
+ \sa registerId()
+
+*/
+
+/*!
+ Constructs an id given its associated name. The internal
+ representation will be unspecified, but consistent within a
+ process.
+
+*/
+Id::Id(const char *name)
+ : m_id(theId(name, 0))
+{}
+
+/*!
+ \overload
+
+*/
+Id::Id(const QByteArray &name)
+ : m_id(theId(name))
+{}
+
+///*!
+// \overload
+// \deprecated
+//*/
+//Id::Id(const QString &name)
+// : m_id(theId(name.toUtf8()))
+//{}
+
+/*!
+ Returns an internal representation of the id.
+*/
+
+QByteArray Id::name() const
+{
+ return stringFromId.value(m_id).str;
+}
+
+/*!
+ Returns a string representation of the id suitable
+ for UI display.
+
+ This should not be used to create a persistent version
+ of the Id, use \c{toSetting()} instead.
+
+ \sa fromString(), toSetting()
+*/
+
+QString Id::toString() const
+{
+ return QString::fromUtf8(stringFromId.value(m_id).str);
+}
+
+/*!
+ Creates an id from a string representation.
+
+ This should not be used to handle a persistent version
+ of the Id, use \c{fromSetting()} instead.
+
+ \deprecated
+
+ \sa toString(), fromSetting()
+*/
+
+Id Id::fromString(const QString &name)
+{
+ return Id(theId(name.toUtf8()));
+}
+
+/*!
+ Creates an id from a string representation.
+
+ This should not be used to handle a persistent version
+ of the Id, use \c{fromSetting()} instead.
+
+ \deprecated
+
+ \sa toString(), fromSetting()
+*/
+
+Id Id::fromName(const QByteArray &name)
+{
+ return Id(theId(name));
+}
+
+/*!
+ Returns a persistent value representing the id which is
+ suitable to be stored in QSettings.
+
+ \sa fromSetting()
+*/
+
+QVariant Id::toSetting() const
+{
+ return QVariant(QString::fromUtf8(stringFromId.value(m_id).str));
+}
+
+/*!
+ Reconstructs an id from a persistent value.
+
+ \sa toSetting()
+*/
+
+Id Id::fromSetting(const QVariant &variant)
+{
+ const QByteArray ba = variant.toString().toUtf8();
+ if (ba.isEmpty())
+ return Id();
+ return Id(theId(ba));
+}
+
+/*!
+ Constructs a derived id.
+
+ This can be used to construct groups of ids logically
+ belonging together. The associated internal name
+ will be generated by appending \c{suffix}.
+*/
+
+Id Id::withSuffix(int suffix) const
+{
+ const QByteArray ba = name() + QByteArray::number(suffix);
+ return Id(ba.constData());
+}
+
+/*!
+ \overload
+*/
+
+Id Id::withSuffix(const char *suffix) const
+{
+ const QByteArray ba = name() + suffix;
+ return Id(ba.constData());
+}
+
+/*!
+ Constructs a derived id.
+
+ This can be used to construct groups of ids logically
+ belonging together. The associated internal name
+ will be generated by prepending \c{prefix}.
+*/
+
+Id Id::withPrefix(const char *prefix) const
+{
+ const QByteArray ba = prefix + name();
+ return Id(ba.constData());
+}
+
+
+/*!
+ Associates a id with its uid and its string
+ representation.
+
+ The uid should be taken from the plugin's private range.
+
+ \sa fromSetting()
+*/
+
+void Id::registerId(int uid, const char *name)
+{
+ StringHolder sh(name, 0);
+ idFromString[sh] = uid;
+ stringFromId[uid] = sh;
+}
+
+bool Id::operator==(const char *name) const
+{
+ const char *string = stringFromId.value(m_id).str;
+ if (string && name)
+ return strcmp(string, name) == 0;
+ else
+ return false;
+}
+
+bool Id::alphabeticallyBefore(Id other) const
+{
+ return toString().compare(other.toString(), Qt::CaseInsensitive) < 0;
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/tools/id.h b/src/lib/corelib/tools/id.h
new file mode 100644
index 000000000..097a2d89b
--- /dev/null
+++ b/src/lib/corelib/tools/id.h
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef QBS_TOOLS_ID_H
+#define QBS_TOOLS_ID_H
+
+#include <QMetaType>
+#include <QString>
+#include <QVariant>
+
+namespace qbs {
+namespace Internal {
+
+class Id
+{
+public:
+ enum { IdsPerPlugin = 10000, ReservedPlugins = 1000 };
+
+ Id() : m_id(0) {}
+ Id(int uid) : m_id(uid) {}
+ Id(const char *name);
+// explicit Id(const QString &name);
+ explicit Id(const QByteArray &name);
+
+ Id withSuffix(int suffix) const;
+ Id withSuffix(const char *name) const;
+ Id withPrefix(const char *name) const;
+
+ QByteArray name() const;
+ QString toString() const; // Avoid.
+ QVariant toSetting() const; // Good to use.
+ bool isValid() const { return m_id; }
+ bool operator==(Id id) const { return m_id == id.m_id; }
+ bool operator==(const char *name) const;
+ bool operator!=(Id id) const { return m_id != id.m_id; }
+ bool operator!=(const char *name) const { return !operator==(name); }
+ bool operator<(Id id) const { return m_id < id.m_id; }
+ bool operator>(Id id) const { return m_id > id.m_id; }
+ bool alphabeticallyBefore(Id other) const;
+ int uniqueIdentifier() const { return m_id; }
+ static Id fromUniqueIdentifier(int uid) { return Id(uid); }
+ static Id fromString(const QString &str); // FIXME: avoid.
+ static Id fromName(const QByteArray &ba); // FIXME: avoid.
+ static Id fromSetting(const QVariant &variant); // Good to use.
+ static void registerId(int uid, const char *name);
+
+private:
+ // Intentionally unimplemented
+ Id(const QLatin1String &);
+ int m_id;
+};
+
+inline uint qHash(const Id &id) { return id.uniqueIdentifier(); }
+
+} // namespace Internal
+} // namespace qbs
+
+Q_DECLARE_METATYPE(qbs::Internal::Id)
+Q_DECLARE_METATYPE(QList<qbs::Internal::Id>)
+
+#endif // QBS_TOOLS_ID_H
diff --git a/src/lib/corelib/tools/installoptions.cpp b/src/lib/corelib/tools/installoptions.cpp
new file mode 100644
index 000000000..66e82fd23
--- /dev/null
+++ b/src/lib/corelib/tools/installoptions.cpp
@@ -0,0 +1,203 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+#include "installoptions.h"
+
+#include <QDir>
+#include <QSharedData>
+
+namespace qbs {
+namespace Internal {
+
+class InstallOptionsPrivate : public QSharedData
+{
+public:
+ InstallOptionsPrivate()
+ : useSysroot(false), removeExisting(false), dryRun(false),
+ keepGoing(false), logElapsedTime(false)
+ {}
+
+ QString installRoot;
+ bool useSysroot;
+ bool removeExisting;
+ bool dryRun;
+ bool keepGoing;
+ bool logElapsedTime;
+};
+
+} // namespace Internal
+
+/*!
+ * \class InstallOptions
+ * \brief The \c InstallOptions class comprises parameters that influence the behavior of
+ * install operations.
+ */
+
+InstallOptions::InstallOptions() : d(new Internal::InstallOptionsPrivate)
+{
+}
+
+InstallOptions::InstallOptions(const InstallOptions &other) : d(other.d)
+{
+}
+
+InstallOptions &InstallOptions::operator=(const InstallOptions &other)
+{
+ d = other.d;
+ return *this;
+}
+
+InstallOptions::~InstallOptions()
+{
+}
+
+/*!
+ * \brief The default install root, relative to the build directory.
+ */
+QString InstallOptions::defaultInstallRoot()
+{
+ return QLatin1String("install-root");
+}
+
+/*!
+ * Returns the base directory for the installation.
+ * The \c qbs.installPrefix path is relative to this root. If the string is empty, either the value of
+ * qbs.sysroot or "<build dir>/install-root" will be used, depending on what \c installIntoSysroot()
+ * returns.
+ * The default is empty.
+ */
+QString InstallOptions::installRoot() const
+{
+ return d->installRoot;
+}
+
+/*!
+ * \brief Sets the base directory for the installation.
+ * \note The argument must either be an empty string or an absolute path to a directory
+ * (which might not yet exist, in which case it will be created).
+ */
+void InstallOptions::setInstallRoot(const QString &installRoot)
+{
+ d->installRoot = installRoot;
+ if (!QDir(installRoot).isRoot()) {
+ while (d->installRoot.endsWith(QLatin1Char('/')))
+ d->installRoot.chop(1);
+ }
+}
+
+/*!
+ * Returns whether to use the sysroot as the default install root.
+ * The default is false.
+ */
+bool InstallOptions::installIntoSysroot() const
+{
+ return d->useSysroot;
+}
+
+void InstallOptions::setInstallIntoSysroot(bool useSysroot)
+{
+ d->useSysroot = useSysroot;
+}
+
+/*!
+ * \brief Returns true iff an existing installation will be removed prior to installing.
+ * The default is false.
+ */
+bool InstallOptions::removeExistingInstallation() const
+{
+ return d->removeExisting;
+}
+
+/*!
+ * Controls whether to remove an existing installation before installing.
+ * \note qbs may do some safety checks and refuse to remove certain directories such as
+ * a user's home directory. You should still be careful with this option, since it
+ * deletes recursively.
+ */
+void InstallOptions::setRemoveExistingInstallation(bool removeExisting)
+{
+ d->removeExisting = removeExisting;
+}
+
+/*!
+ * \brief Returns true iff qbs will not actually copy any files, but just show what would happen.
+ * The default is false.
+ */
+bool InstallOptions::dryRun() const
+{
+ return d->dryRun;
+}
+
+/*!
+ * \brief Controls whether installation will actually take place.
+ * If the argument is true, then qbs will emit information about which files would be copied
+ * instead of actually doing it.
+ */
+void InstallOptions::setDryRun(bool dryRun)
+{
+ d->dryRun = dryRun;
+}
+
+/*!
+ * Returns true iff installation will continue if an error occurs.
+ * The default is false.
+ */
+bool InstallOptions::keepGoing() const
+{
+ return d->keepGoing;
+}
+
+/*!
+ * \brief Controls whether to abort on errors.
+ * If the argument is true, then if a file cannot be copied e.g. due to a permission problem,
+ * a warning will be printed and the installation will continue. If the argument is false,
+ * then the installation will abort immediately in case of an error.
+ */
+void InstallOptions::setKeepGoing(bool keepGoing)
+{
+ d->keepGoing = keepGoing;
+}
+
+/*!
+ * \brief Returns true iff the time the operation takes will be logged.
+ * The default is false.
+ */
+bool InstallOptions::logElapsedTime() const
+{
+ return d->logElapsedTime;
+}
+
+/*!
+ * \brief Controls whether the installation time will be measured and logged.
+ */
+void InstallOptions::setLogElapsedTime(bool logElapsedTime)
+{
+ d->logElapsedTime = logElapsedTime;
+}
+
+} // namespace qbs
diff --git a/src/lib/corelib/tools/installoptions.h b/src/lib/corelib/tools/installoptions.h
new file mode 100644
index 000000000..2b6194fb0
--- /dev/null
+++ b/src/lib/corelib/tools/installoptions.h
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+#ifndef QBS_INSTALLOPTIONS_H
+#define QBS_INSTALLOPTIONS_H
+
+#include "qbs_export.h"
+
+#include <QSharedDataPointer>
+#include <QString>
+
+namespace qbs {
+namespace Internal { class InstallOptionsPrivate; }
+
+class QBS_EXPORT InstallOptions
+{
+public:
+ InstallOptions();
+ InstallOptions(const InstallOptions &other);
+ InstallOptions &operator=(const InstallOptions &other);
+ ~InstallOptions();
+
+ static QString defaultInstallRoot();
+ QString installRoot() const;
+ void setInstallRoot(const QString &installRoot);
+
+ bool installIntoSysroot() const;
+ void setInstallIntoSysroot(bool useSysroot);
+
+ bool removeExistingInstallation() const;
+ void setRemoveExistingInstallation(bool removeExisting);
+
+ bool dryRun() const;
+ void setDryRun(bool dryRun);
+
+ bool keepGoing() const;
+ void setKeepGoing(bool keepGoing);
+
+ bool logElapsedTime() const;
+ void setLogElapsedTime(bool logElapsedTime);
+
+private:
+ QSharedDataPointer<Internal::InstallOptionsPrivate> d;
+};
+
+} // namespace qbs
+
+#endif // QBS_INSTALLOPTIONS_H
diff --git a/src/lib/corelib/tools/persistence.cpp b/src/lib/corelib/tools/persistence.cpp
new file mode 100644
index 000000000..aa519cab2
--- /dev/null
+++ b/src/lib/corelib/tools/persistence.cpp
@@ -0,0 +1,217 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "persistence.h"
+
+#include "fileinfo.h"
+#include <logging/translator.h>
+#include <tools/error.h>
+#include <tools/qbsassert.h>
+
+#include <QDir>
+#include <QScopedPointer>
+
+namespace qbs {
+namespace Internal {
+
+static const char QBS_PERSISTENCE_MAGIC[] = "QBSPERSISTENCE-60";
+
+PersistentPool::PersistentPool(const Logger &logger) : m_logger(logger)
+{
+ m_stream.setVersion(QDataStream::Qt_4_8);
+}
+
+PersistentPool::~PersistentPool()
+{
+ closeStream();
+}
+
+void PersistentPool::load(const QString &filePath)
+{
+ QScopedPointer<QFile> file(new QFile(filePath));
+ if (!file->exists())
+ throw ErrorInfo(Tr::tr("No build graph exists yet for this configuration."));
+ if (!file->open(QFile::ReadOnly)) {
+ throw ErrorInfo(Tr::tr("Could not open open build graph file '%1': %2")
+ .arg(filePath, file->errorString()));
+ }
+
+ m_stream.setDevice(file.data());
+ QByteArray magic;
+ m_stream >> magic;
+ if (magic != QBS_PERSISTENCE_MAGIC) {
+ file->close();
+ file->remove();
+ m_stream.setDevice(0);
+ throw ErrorInfo(Tr::tr("Cannot use stored build graph at '%1': Incompatible file format. "
+ "Expected magic token '%2', got '%3'.")
+ .arg(filePath, QString::fromLatin1(QBS_PERSISTENCE_MAGIC),
+ QString::fromLatin1(magic)));
+ }
+
+ m_stream >> m_headData.projectConfig;
+ file.take();
+ m_loadedRaw.clear();
+ m_loaded.clear();
+ m_storageIndices.clear();
+ m_stringStorage.clear();
+ m_inverseStringStorage.clear();
+}
+
+void PersistentPool::setupWriteStream(const QString &filePath)
+{
+ QString dirPath = FileInfo::path(filePath);
+ if (!FileInfo::exists(dirPath) && !QDir().mkpath(dirPath)) {
+ throw ErrorInfo(Tr::tr("Failure storing build graph: Cannot create directory '%1'.")
+ .arg(dirPath));
+ }
+
+ if (QFile::exists(filePath) && !QFile::remove(filePath)) {
+ throw ErrorInfo(Tr::tr("Failure storing build graph: Cannot remove old file '%1'")
+ .arg(filePath));
+ }
+ QBS_CHECK(!QFile::exists(filePath));
+ QScopedPointer<QFile> file(new QFile(filePath));
+ if (!file->open(QFile::WriteOnly)) {
+ throw ErrorInfo(Tr::tr("Failure storing build graph: "
+ "Cannot open file '%1' for writing: %2").arg(filePath, file->errorString()));
+ }
+
+ m_stream.setDevice(file.take());
+ m_stream << QByteArray(QBS_PERSISTENCE_MAGIC) << m_headData.projectConfig;
+ m_lastStoredObjectId = 0;
+ m_lastStoredStringId = 0;
+}
+
+void PersistentPool::closeStream()
+{
+ delete m_stream.device();
+ m_stream.setDevice(0);
+}
+
+void PersistentPool::store(const PersistentObject *object)
+{
+ if (!object) {
+ m_stream << -1;
+ return;
+ }
+ PersistentObjectId id = m_storageIndices.value(object, -1);
+ if (id < 0) {
+ id = m_lastStoredObjectId++;
+ m_storageIndices.insert(object, id);
+ m_stream << id;
+ object->store(*this);
+ } else {
+ m_stream << id;
+ }
+}
+
+void PersistentPool::clear()
+{
+ m_loaded.clear();
+ m_storageIndices.clear();
+ m_stringStorage.clear();
+ m_inverseStringStorage.clear();
+}
+
+QDataStream &PersistentPool::stream()
+{
+ return m_stream;
+}
+
+void PersistentPool::storeString(const QString &t)
+{
+ int id = m_inverseStringStorage.value(t, -1);
+ if (id < 0) {
+ id = m_lastStoredStringId++;
+ m_inverseStringStorage.insert(t, id);
+ m_stream << id << t;
+ } else {
+ m_stream << id;
+ }
+}
+
+QString PersistentPool::loadString(int id)
+{
+ QBS_CHECK(id >= 0);
+
+ if (id >= m_stringStorage.count()) {
+ QString s;
+ m_stream >> s;
+ m_stringStorage.resize(id + 1);
+ m_stringStorage[id] = s;
+ return s;
+ }
+
+ return m_stringStorage.at(id);
+}
+
+QString PersistentPool::idLoadString()
+{
+ int id;
+ m_stream >> id;
+ return loadString(id);
+}
+
+void PersistentPool::storeStringSet(const QSet<QString> &t)
+{
+ m_stream << t.count();
+ foreach (const QString &s, t)
+ storeString(s);
+}
+
+QSet<QString> PersistentPool::idLoadStringSet()
+{
+ int i;
+ m_stream >> i;
+ QSet<QString> result;
+ for (; --i >= 0;)
+ result += idLoadString();
+ return result;
+}
+
+void PersistentPool::storeStringList(const QStringList &t)
+{
+ m_stream << t.count();
+ foreach (const QString &s, t)
+ storeString(s);
+}
+
+QStringList PersistentPool::idLoadStringList()
+{
+ int i;
+ m_stream >> i;
+ QStringList result;
+ for (; --i >= 0;)
+ result += idLoadString();
+ return result;
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/tools/persistence.h b/src/lib/corelib/tools/persistence.h
new file mode 100644
index 000000000..1afea6d80
--- /dev/null
+++ b/src/lib/corelib/tools/persistence.h
@@ -0,0 +1,197 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef QBS_PERSISTENCE
+#define QBS_PERSISTENCE
+
+#include "persistentobject.h"
+#include <logging/logger.h>
+
+#include <QDataStream>
+#include <QSharedPointer>
+#include <QString>
+#include <QVariantMap>
+#include <QVector>
+
+namespace qbs {
+namespace Internal {
+
+class PersistentPool
+{
+public:
+ PersistentPool(const Logger &logger);
+ ~PersistentPool();
+
+ class HeadData
+ {
+ public:
+ QVariantMap projectConfig;
+ };
+
+ void load(const QString &filePath);
+ void setupWriteStream(const QString &filePath);
+ void closeStream();
+ void clear();
+ QDataStream &stream();
+
+ template <typename T> T *idLoad();
+ template <typename T> void loadContainer(T &container);
+ template <class T> QSharedPointer<T> idLoadS();
+ template <typename T> void loadContainerS(T &container);
+
+ void store(const QSharedPointer<const PersistentObject> &ptr) { store(ptr.data()); }
+ void store(const PersistentObject *object);
+ template <typename T> void storeContainer(T &container);
+
+ void storeString(const QString &t);
+ QString loadString(int id);
+ QString idLoadString();
+
+ void storeStringSet(const QSet<QString> &t);
+ QSet<QString> loadStringSet(const QList<int> &id);
+ QSet<QString> idLoadStringSet();
+
+ void storeStringList(const QStringList &t);
+ QStringList loadStringList(const QList<int> &ids);
+ QStringList idLoadStringList();
+
+ const HeadData &headData() const { return m_headData; }
+ void setHeadData(const HeadData &hd) { m_headData = hd; }
+
+private:
+ typedef int PersistentObjectId;
+
+ template<typename T> struct RemovePointer { typedef T Type; };
+ template<typename T> struct RemovePointer<T*> { typedef T Type; };
+ template <class T> struct RemoveConst { typedef T Type; };
+ template <class T> struct RemoveConst<const T> { typedef T Type; };
+
+ template <class T> T *loadRaw(PersistentObjectId id);
+ template <class T> QSharedPointer<T> load(PersistentObjectId id);
+
+ QDataStream m_stream;
+ HeadData m_headData;
+ QVector<PersistentObject *> m_loadedRaw;
+ QVector<QSharedPointer<PersistentObject> > m_loaded;
+ QHash<const PersistentObject *, int> m_storageIndices;
+ PersistentObjectId m_lastStoredObjectId;
+
+ QVector<QString> m_stringStorage;
+ QHash<QString, int> m_inverseStringStorage;
+ PersistentObjectId m_lastStoredStringId;
+ Logger m_logger;
+};
+
+template <typename T> inline T *PersistentPool::idLoad()
+{
+ PersistentObjectId id;
+ stream() >> id;
+ return loadRaw<T>(id);
+}
+
+template <typename T> inline void PersistentPool::loadContainer(T &container)
+{
+ int count;
+ stream() >> count;
+ container.clear();
+ container.reserve(count);
+ for (int i = count; --i >= 0;)
+ container += idLoad<typename RemovePointer<typename T::value_type>::Type>();
+}
+
+template <class T> inline QSharedPointer<T> PersistentPool::idLoadS()
+{
+ PersistentObjectId id;
+ m_stream >> id;
+ return load<T>(id);
+}
+
+template <typename T> inline void PersistentPool::loadContainerS(T &container)
+{
+ int count;
+ stream() >> count;
+ container.clear();
+ container.reserve(count);
+ for (int i = count; --i >= 0;)
+ container += idLoadS<typename RemoveConst<typename T::value_type::value_type>::Type>();
+}
+
+template <typename T> inline void PersistentPool::storeContainer(T &container)
+{
+ stream() << container.count();
+ typename T::const_iterator it = container.constBegin();
+ const typename T::const_iterator itEnd = container.constEnd();
+ for (; it != itEnd; ++it)
+ store(*it);
+}
+
+template <class T> inline T *PersistentPool::loadRaw(PersistentObjectId id)
+{
+ if (id < 0)
+ return 0;
+
+ if (id < m_loadedRaw.count()) {
+ PersistentObject *obj = m_loadedRaw.value(id);
+ return dynamic_cast<T*>(obj);
+ }
+
+ int i = m_loadedRaw.count();
+ m_loadedRaw.resize(id + 1);
+ for (; i < m_loadedRaw.count(); ++i)
+ m_loadedRaw[i] = 0;
+
+ T * const t = new T;
+ PersistentObject * const po = t;
+ m_loadedRaw[id] = po;
+ po->load(*this);
+ return t;
+}
+
+template <class T> inline QSharedPointer<T> PersistentPool::load(PersistentObjectId id)
+{
+ if (id < 0)
+ return QSharedPointer<T>();
+
+ if (id < m_loaded.count()) {
+ QSharedPointer<PersistentObject> obj = m_loaded.value(id);
+ return obj.dynamicCast<T>();
+ }
+
+ m_loaded.resize(id + 1);
+ const QSharedPointer<T> t = T::create();
+ m_loaded[id] = t;
+ PersistentObject * const po = t.data();
+ po->load(*this);
+ return t;
+}
+
+} // namespace Internal
+} // namespace qbs
+
+#endif
diff --git a/src/lib/corelib/tools/persistentobject.h b/src/lib/corelib/tools/persistentobject.h
new file mode 100644
index 000000000..0d9b588c0
--- /dev/null
+++ b/src/lib/corelib/tools/persistentobject.h
@@ -0,0 +1,48 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+#ifndef QBS_PERSISTENTOBJECT_H
+#define QBS_PERSISTENTOBJECT_H
+
+namespace qbs {
+namespace Internal {
+
+class PersistentPool;
+
+class PersistentObject
+{
+public:
+ virtual ~PersistentObject() {}
+ virtual void load(PersistentPool &) = 0;
+ virtual void store(PersistentPool &) const = 0;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_PERSISTENTOBJECT_H
diff --git a/src/lib/corelib/tools/preferences.cpp b/src/lib/corelib/tools/preferences.cpp
new file mode 100644
index 000000000..bdff7c67b
--- /dev/null
+++ b/src/lib/corelib/tools/preferences.cpp
@@ -0,0 +1,133 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+#include "preferences.h"
+
+#include "buildoptions.h"
+#include "hostosinfo.h"
+#include "profile.h"
+#include "settings.h"
+
+namespace qbs {
+
+/*!
+ * \class Preferences
+ * \brief The \c Preferences class gives access to all general qbs preferences.
+ * If a non-empty \c profileName is given, the profile's preferences take precedence over global
+ * ones. Otherwise, the global preferences are used.
+ */
+Preferences::Preferences(Settings *settings, const QString &profileName)
+ : m_settings(settings), m_profile(profileName)
+{
+}
+
+
+/*!
+ * \brief Returns true <=> colored output should be used for printing messages.
+ * This is only relevant for command-line frontends.
+ */
+bool Preferences::useColoredOutput() const
+{
+ return getPreference(QLatin1String("useColoredOutput"), true).toBool();
+}
+
+/*!
+ * \brief Returns the number of parallel jobs to use for building.
+ * Uses a sensible default value if there is no such setting.
+ */
+int Preferences::jobs() const
+{
+ return getPreference(QLatin1String("jobs"), BuildOptions::defaultMaxJobCount()).toInt();
+}
+
+/*!
+ * \brief Returns the shell to use for the "qbs shell" command.
+ * This is only relevant for command-line frontends.
+ */
+QString Preferences::shell() const
+{
+ return getPreference(QLatin1String("shell")).toString();
+}
+
+/*!
+ * \brief Returns the default build directory used by Qbs if none is specified.
+ */
+QString Preferences::defaultBuildDirectory() const
+{
+ return getPreference(QLatin1String("defaultBuildDirectory")).toString();
+}
+
+/*!
+ * \brief Returns the list of paths where qbs looks for module definitions and such.
+ * If there is no such setting, \c qbsRootPath will be used to look up a fallback location.
+ */
+QStringList Preferences::searchPaths(const QString &qbsRootPath) const
+{
+ const QStringList searchPaths = pathList(QLatin1String("qbsSearchPaths"),
+ qbsRootPath + QLatin1String("/share/qbs"));
+
+ // TODO: Remove in 1.2.
+ const QStringList deprecatedSearchPaths = getPreference(QLatin1String("qbsPath")).toString()
+ .split(Internal::HostOsInfo::pathListSeparator(), QString::SkipEmptyParts);
+ if (!deprecatedSearchPaths.isEmpty()) {
+ qDebug("Warning: preferences.qbsPath is deprecated, "
+ "use preferences.qbsSearchPaths instead.");
+ }
+ return deprecatedSearchPaths + searchPaths;
+}
+
+/*!
+ * \brief Returns the list of paths where qbs looks for plugins.
+ * If there is no such setting, \c qbsRootPath will be used to look up a fallback location.
+ */
+QStringList Preferences::pluginPaths(const QString &qbsRootPath) const
+{
+ return pathList(QLatin1String("pluginsPath"), qbsRootPath + QLatin1String("/lib/qbs/plugins"));
+}
+
+QVariant Preferences::getPreference(const QString &key, const QVariant &defaultValue) const
+{
+ const QString fullKey = QLatin1String("preferences.") + key;
+ if (!m_profile.isEmpty()) {
+ const QVariant value = Profile(m_profile, m_settings).value(fullKey);
+ if (value.isValid())
+ return value;
+ }
+
+ return m_settings->value(fullKey, defaultValue);
+}
+
+QStringList Preferences::pathList(const QString &key, const QString &defaultValue) const
+{
+ QStringList paths = getPreference(key).toString().split(
+ Internal::HostOsInfo::pathListSeparator(), QString::SkipEmptyParts);
+ paths << defaultValue;
+ return paths;
+}
+
+} // namespace qbs
diff --git a/src/lib/corelib/tools/preferences.h b/src/lib/corelib/tools/preferences.h
new file mode 100644
index 000000000..e5e6c9bb6
--- /dev/null
+++ b/src/lib/corelib/tools/preferences.h
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+#ifndef QBS_PREFERENCES_H
+#define QBS_PREFERENCES_H
+
+#include "qbs_export.h"
+
+#include <QStringList>
+#include <QVariant>
+
+namespace qbs {
+class Settings;
+
+class QBS_EXPORT Preferences
+{
+public:
+ explicit Preferences(Settings *settings, const QString &profileName = QString());
+
+ bool useColoredOutput() const;
+ int jobs() const;
+ QString shell() const;
+ QString defaultBuildDirectory() const;
+ QStringList searchPaths(const QString &qbsRootPath = QString()) const;
+ QStringList pluginPaths(const QString &qbsRootPath = QString()) const;
+
+private:
+ QVariant getPreference(const QString &key, const QVariant &defaultValue = QVariant()) const;
+ QStringList pathList(const QString &key, const QString &defaultValue) const;
+
+ Settings *m_settings;
+ QString m_profile;
+};
+
+} // namespace qbs
+
+
+#endif // Header guard
diff --git a/src/lib/corelib/tools/processresult.cpp b/src/lib/corelib/tools/processresult.cpp
new file mode 100644
index 000000000..885d7ffee
--- /dev/null
+++ b/src/lib/corelib/tools/processresult.cpp
@@ -0,0 +1,121 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+#include "processresult.h"
+#include "processresult_p.h"
+
+/*!
+ * \class SetupProjectParameters
+ * \brief The \c ProcessResult class describes a finished qbs process command.
+ */
+
+namespace qbs {
+
+ProcessResult::ProcessResult() : d(new Internal::ProcessResultPrivate)
+{
+}
+
+ProcessResult::ProcessResult(const ProcessResult &other) : d(other.d)
+{
+}
+
+ProcessResult &ProcessResult::operator=(const ProcessResult &other)
+{
+ d = other.d;
+ return *this;
+}
+
+ProcessResult::~ProcessResult()
+{
+}
+
+/*!
+ * \brief Returns true iff the command finished successfully.
+ */
+bool ProcessResult::success() const
+{
+ return d->success;
+}
+
+/*!
+ * \brief Returns the file path of the executable that was run.
+ */
+QString ProcessResult::executableFilePath() const
+{
+ return d->executableFilePath;
+}
+
+/*!
+ * \brief Returns the command-line arguments with which the command was invoked.
+ */
+QStringList ProcessResult::arguments() const
+{
+ return d->arguments;
+}
+
+/*!
+ * \brief Returns the working directory of the invoked command.
+ */
+QString ProcessResult::workingDirectory() const
+{
+ return d->workingDirectory;
+}
+
+/*!
+ * \brief Returns the exit status of the command.
+ */
+QProcess::ExitStatus ProcessResult::exitStatus() const
+{
+ return d->exitStatus;
+}
+
+/*!
+ * \brief Returns the exit code of the command.
+ */
+int ProcessResult::exitCode() const
+{
+ return d->exitCode;
+}
+
+/*!
+ * \brief Returns the data the command wrote to the standard output channel.
+ */
+QStringList ProcessResult::stdOut() const
+{
+ return d->stdOut;
+}
+
+/*!
+ * \brief Returns the data the command wrote to the standard error channel.
+ */
+QStringList ProcessResult::stdErr() const
+{
+ return d->stdErr;
+}
+
+} // namespace qbs
diff --git a/src/lib/corelib/tools/processresult.h b/src/lib/corelib/tools/processresult.h
new file mode 100644
index 000000000..b8de9ddd2
--- /dev/null
+++ b/src/lib/corelib/tools/processresult.h
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+#ifndef QBS_PROCESSRESULT_H
+#define QBS_PROCESSRESULT_H
+
+#include "qbs_export.h"
+
+#include <QExplicitlySharedDataPointer>
+#include <QMetaType>
+#include <QProcess>
+#include <QString>
+#include <QStringList>
+
+namespace qbs {
+namespace Internal {
+class ProcessCommandExecutor;
+class ProcessResultPrivate;
+}
+
+class QBS_EXPORT ProcessResult
+{
+ friend class qbs::Internal::ProcessCommandExecutor;
+public:
+ ProcessResult();
+ ProcessResult(const ProcessResult &other);
+ ProcessResult &operator=(const ProcessResult &other);
+ ~ProcessResult();
+
+ bool success() const;
+ QString executableFilePath() const;
+ QStringList arguments() const;
+ QString workingDirectory() const;
+ QProcess::ExitStatus exitStatus() const;
+ int exitCode() const;
+ QStringList stdOut() const;
+ QStringList stdErr() const;
+
+private:
+ QExplicitlySharedDataPointer<Internal::ProcessResultPrivate> d;
+};
+
+} // namespace qbs
+
+Q_DECLARE_METATYPE(qbs::ProcessResult)
+
+#endif // QBS_PROCESSRESULT_H
diff --git a/src/lib/corelib/tools/processresult_p.h b/src/lib/corelib/tools/processresult_p.h
new file mode 100644
index 000000000..db2475b44
--- /dev/null
+++ b/src/lib/corelib/tools/processresult_p.h
@@ -0,0 +1,56 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+#ifndef QBS_PROCESSRESULT_P_H
+#define QBS_PROCESSRESULT_P_H
+
+#include <QSharedData>
+#include <QStringList>
+#include <QProcess>
+
+namespace qbs {
+namespace Internal {
+class ProcessResultPrivate : public QSharedData
+{
+public:
+ bool success;
+
+ QString executableFilePath;
+ QStringList arguments;
+ QString workingDirectory;
+
+ QProcess::ExitStatus exitStatus;
+ int exitCode;
+ QStringList stdOut;
+ QStringList stdErr;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // Include guard.
diff --git a/src/lib/corelib/tools/profile.cpp b/src/lib/corelib/tools/profile.cpp
new file mode 100644
index 000000000..cb3186ae4
--- /dev/null
+++ b/src/lib/corelib/tools/profile.cpp
@@ -0,0 +1,224 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+#include "profile.h"
+#include "qbsassert.h"
+#include "settings.h"
+
+#include <logging/translator.h>
+#include <tools/error.h>
+
+namespace qbs {
+
+/*!
+ * \class Profile
+ * \brief The \c Profile class gives access to the settings of a given profile.
+ */
+
+ /*!
+ * \enum Profile::KeySelection
+ * This enum type specifies whether to enumerate keys recursively.
+ * \value KeySelectionRecursive Indicates that key enumeration should happen recursively, i.e.
+ * it should go up the base profile chain.
+ * \value KeySelectionNonRecursive Indicates that only keys directly attached to a profile
+ * should be listed.
+ */
+
+/*!
+ * \brief Creates an object giving access to the settings for profile \c name.
+ */
+Profile::Profile(const QString &name, Settings *settings) : m_name(name), m_settings(settings)
+{
+ QBS_ASSERT(name == cleanName(name), return);
+}
+
+bool Profile::exists() const
+{
+ return !m_settings->allKeysWithPrefix(profileKey()).isEmpty();
+}
+
+/*!
+ * \brief Returns the value for property \c key in this profile.
+ */
+QVariant Profile::value(const QString &key, const QVariant &defaultValue) const
+{
+ return possiblyInheritedValue(key, defaultValue, QStringList());
+}
+
+/*!
+ * \brief Gives value \c value to the property \c key in this profile.
+ */
+void Profile::setValue(const QString &key, const QVariant &value)
+{
+ m_settings->setValue(fullyQualifiedKey(key), value);
+
+ if (key == baseProfileKey()) {
+ QBS_ASSERT(value.toString() == cleanName(value.toString()), return);
+ }
+}
+
+/*!
+ * \brief Removes a key and the associated value from this profile.
+ */
+void Profile::remove(const QString &key)
+{
+ m_settings->remove(fullyQualifiedKey(key));
+}
+
+/*!
+ * \brief Returns the name of this profile.
+ */
+QString Profile::name() const
+{
+ return m_name;
+}
+
+/*!
+ * \brief Returns all property keys in this profile.
+ * If and only if selection is Profile::KeySelectionRecursive, this will also list keys defined
+ * in base profiles.
+ */
+QStringList Profile::allKeys(KeySelection selection) const
+{
+ return allKeysInternal(selection, QStringList());
+}
+
+/*!
+ * \brief Returns the name of this profile's base profile.
+ * The returned value is empty if the profile does not have a base profile.
+ */
+QString Profile::baseProfile() const
+{
+ return localValue(baseProfileKey()).toString();
+}
+
+/*!
+ * \brief Sets a new base profile for this profile.
+ */
+void Profile::setBaseProfile(const QString &baseProfile)
+{
+ setValue(baseProfileKey(), baseProfile);
+}
+
+/*!
+ * \brief Removes this profile's base profile setting.
+ */
+void Profile::removeBaseProfile()
+{
+ remove(baseProfileKey());
+}
+
+/*!
+ * \brief Removes this profile from the settings.
+ */
+void Profile::removeProfile()
+{
+ m_settings->remove(profileKey());
+}
+
+/*!
+ * \brief Returns a string suitiable as a profile name.
+ * Removes all dots and replaces them with hyphens.
+ */
+QString Profile::cleanName(const QString &name)
+{
+ QString newName = name;
+ return newName.replace(QLatin1Char('.'), QLatin1Char('-'));
+}
+
+QString Profile::profileKey() const
+{
+ return QLatin1String("profiles.") + m_name;
+}
+
+QString Profile::baseProfileKey()
+{
+ return QLatin1String("baseProfile");
+}
+
+void Profile::checkBaseProfileExistence(const Profile &baseProfile) const
+{
+ if (!baseProfile.exists())
+ throw ErrorInfo(Internal::Tr::tr("Profile \"%1\" has a non-existent base profile \"%2\".").arg(
+ name(), baseProfile.name()));
+}
+
+QVariant Profile::localValue(const QString &key) const
+{
+ return m_settings->value(fullyQualifiedKey(key));
+}
+
+QString Profile::fullyQualifiedKey(const QString &key) const
+{
+ return profileKey() + QLatin1Char('.') + key;
+}
+
+QVariant Profile::possiblyInheritedValue(const QString &key, const QVariant &defaultValue,
+ QStringList profileChain) const
+{
+ extendAndCheckProfileChain(profileChain);
+ const QVariant v = localValue(key);
+ if (v.isValid())
+ return v;
+ const QString baseProfileName = baseProfile();
+ if (baseProfileName.isEmpty())
+ return defaultValue;
+ Profile parentProfile(baseProfileName, m_settings);
+ checkBaseProfileExistence(parentProfile);
+ return parentProfile.possiblyInheritedValue(key, defaultValue, profileChain);
+}
+
+QStringList Profile::allKeysInternal(Profile::KeySelection selection,
+ QStringList profileChain) const
+{
+ extendAndCheckProfileChain(profileChain);
+ QStringList keys = m_settings->allKeysWithPrefix(profileKey());
+ if (selection == KeySelectionNonRecursive)
+ return keys;
+ const QString baseProfileName = baseProfile();
+ if (baseProfileName.isEmpty())
+ return keys;
+ Profile parentProfile(baseProfileName, m_settings);
+ checkBaseProfileExistence(parentProfile);
+ keys += parentProfile.allKeysInternal(KeySelectionRecursive, profileChain);
+ keys.removeDuplicates();
+ keys.removeOne(baseProfileKey());
+ keys.sort();
+ return keys;
+}
+
+void Profile::extendAndCheckProfileChain(QStringList &chain) const
+{
+ chain << m_name;
+ if (Q_UNLIKELY(chain.count(m_name) > 1)) {
+ throw ErrorInfo(Internal::Tr::tr("Circular profile inheritance. Cycle is '%1'.")
+ .arg(chain.join(QLatin1String(" -> "))));
+ }
+}
+
+} // namespace qbs
diff --git a/src/lib/corelib/tools/profile.h b/src/lib/corelib/tools/profile.h
new file mode 100644
index 000000000..c3b0bbfe8
--- /dev/null
+++ b/src/lib/corelib/tools/profile.h
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+#ifndef QBS_PROFILE_H
+#define QBS_PROFILE_H
+
+#include "qbs_export.h"
+
+#include <QString>
+#include <QStringList>
+#include <QVariant>
+
+namespace qbs {
+class Settings;
+
+class QBS_EXPORT Profile
+{
+public:
+ explicit Profile(const QString &name, Settings *settings);
+
+ bool exists() const;
+ QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const;
+ void setValue(const QString &key, const QVariant &value);
+ void remove(const QString &key);
+
+ QString name() const;
+
+ QString baseProfile() const;
+ void setBaseProfile(const QString &baseProfile);
+ void removeBaseProfile();
+
+ void removeProfile();
+
+ enum KeySelection { KeySelectionRecursive, KeySelectionNonRecursive };
+ QStringList allKeys(KeySelection selection) const;
+
+ static QString cleanName(const QString &name);
+
+private:
+ static QString baseProfileKey();
+ void checkBaseProfileExistence(const Profile &baseProfile) const;
+ QString profileKey() const;
+ QVariant localValue(const QString &key) const;
+ QString fullyQualifiedKey(const QString &key) const;
+ QVariant possiblyInheritedValue(const QString &key, const QVariant &defaultValue,
+ QStringList profileChain) const;
+ QStringList allKeysInternal(KeySelection selection, QStringList profileChain) const;
+ void extendAndCheckProfileChain(QStringList &chain) const;
+
+ QString m_name;
+ Settings *m_settings;
+};
+
+} // namespace qbs
+
+#endif // Header guard
diff --git a/src/lib/corelib/tools/progressobserver.cpp b/src/lib/corelib/tools/progressobserver.cpp
new file mode 100644
index 000000000..f18aefcfb
--- /dev/null
+++ b/src/lib/corelib/tools/progressobserver.cpp
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+#include "progressobserver.h"
+
+namespace qbs {
+namespace Internal {
+
+/*!
+ * \class ProgressObserver
+ * The \c ProgressObserver class is used in long running qbs operations. It serves two purposes:
+ * Firstly, it allows operations to indicate progress to a client. Secondly, a client can
+ * signal to an operation that is should exit prematurely.
+ * Clients of the qbs library are supposed to subclass this class and implement the virtual
+ * functions in a way that lets users know about the current operation and its progress.
+ */
+
+/*!
+ * \fn virtual void initialize(const QString &task, int maximum) = 0
+ * \brief Indicates that a new operation is starting.
+ * Library code calls this function to indicate that it is starting a new task.
+ * The \a task parameter is a textual description of that task suitable for presentation to a user.
+ * The \a maximum parameter is an estimate of the maximum effort the operation is going to take.
+ * This is helpful if the client wants to set up some sort of progress bar showing the
+ * percentage of the work already done.
+ */
+
+/*!
+ * \fn virtual void setProgressValue(int value) = 0
+ * \brief Sets the new progress value.
+ * Library code calls this function to indicate that the current operation has progressed.
+ * It will try hard to ensure that \a value will not exceed \c maximum().
+ * \sa ProgressObserver::maximum().
+ */
+
+/*!
+ * \fn virtual int progressValue() = 0
+ * \brief The current progress value.
+ * Will typically reflect the \a value from the last call to \c setProgressValue() and should not
+ * exceed \c maximum().
+ * \sa setProgressvalue()
+ * \sa maximum()
+ */
+
+void ProgressObserver::incrementProgressValue(int increment)
+{
+ setProgressValue(progressValue() + increment);
+}
+
+/*!
+ * \fn virtual bool canceled() const = 0
+ * \brief Indicates whether the current operation should be canceled.
+ * Library code will periodically call this function and abort the current operation
+ * if it returns true.
+ */
+
+/*!
+ * \fn virtual int maximum() const = 0
+ * \brief The expected maximum progress value.
+ * This will typically be the value of \c maximum passed to \c initialize().
+ * \sa ProgressObserver::initialize()
+ */
+
+void ProgressObserver::setFinished()
+{
+ setProgressValue(maximum());
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/tools/progressobserver.h b/src/lib/corelib/tools/progressobserver.h
new file mode 100644
index 000000000..66d552a02
--- /dev/null
+++ b/src/lib/corelib/tools/progressobserver.h
@@ -0,0 +1,62 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+#ifndef QBS_PROGRESSOBSERVER_H
+#define QBS_PROGRESSOBSERVER_H
+
+#include <QtGlobal>
+
+QT_BEGIN_NAMESPACE
+class QString;
+QT_END_NAMESPACE
+
+namespace qbs {
+namespace Internal {
+
+class ProgressObserver
+{
+public:
+ virtual ~ProgressObserver() { }
+
+ virtual void initialize(const QString &task, int maximum) = 0;
+ virtual void setProgressValue(int value) = 0;
+ virtual int progressValue() = 0;
+ virtual bool canceled() const = 0;
+ virtual void setMaximum(int maximum) = 0;
+ virtual int maximum() const = 0;
+
+ void incrementProgressValue(int increment = 1);
+
+ // Call this to ensure that the progress bar always goes to 100%.
+ void setFinished();
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_PROGRESSOBSERVER_H
diff --git a/src/lib/corelib/tools/propertyfinder.cpp b/src/lib/corelib/tools/propertyfinder.cpp
new file mode 100644
index 000000000..96c081f01
--- /dev/null
+++ b/src/lib/corelib/tools/propertyfinder.cpp
@@ -0,0 +1,107 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+#include "propertyfinder.h"
+
+#include "qbsassert.h"
+
+namespace qbs {
+namespace Internal {
+
+QVariantList PropertyFinder::propertyValues(const QVariantMap &properties,
+ const QString &moduleName, const QString &key, MergeType mergeType)
+{
+ m_moduleName = moduleName;
+ m_key = key;
+ m_values.clear();
+ findModuleValues(properties, true);
+ if (mergeType == DoMergeLists)
+ mergeLists(&m_values);
+ return m_values;
+}
+
+QVariant PropertyFinder::propertyValue(const QVariantMap &properties, const QString &moduleName,
+ const QString &key)
+{
+ m_moduleName = moduleName;
+ m_key = key;
+ m_values.clear();
+ findModuleValues(properties, false);
+
+ QBS_ASSERT(m_values.count() <= 1, return QVariant());
+ return m_values.isEmpty() ? QVariant() : m_values.first();
+}
+
+void PropertyFinder::findModuleValues(const QVariantMap &properties, bool searchRecursively)
+{
+ QVariantMap moduleProperties = properties.value(QLatin1String("modules")).toMap();
+
+ // Direct hits come first.
+ const QVariantMap::Iterator modIt = moduleProperties.find(m_moduleName);
+ if (modIt != moduleProperties.end()) {
+ const QVariantMap moduleMap = modIt->toMap();
+ const QVariant property = moduleMap.value(m_key);
+ addToList(property);
+ moduleProperties.erase(modIt);
+ }
+
+ if (!searchRecursively)
+ return;
+
+ // These are the non-matching modules.
+ for (QVariantMap::ConstIterator it = moduleProperties.constBegin();
+ it != moduleProperties.constEnd(); ++it) {
+ findModuleValues(it->toMap(), true);
+ }
+}
+
+void PropertyFinder::addToList(const QVariant &value)
+{
+ // Note: This means that manually setting a property to "null" will not lead to a "hit".
+ if (!value.isNull() && !m_values.contains(value))
+ m_values << value;
+}
+
+void PropertyFinder::mergeLists(QVariantList *values)
+{
+ QVariantList::iterator it = values->begin();
+ while (it != values->end()) {
+ if (it->canConvert<QVariantList>()) {
+ QVariantList sublist = it->toList();
+ mergeLists(&sublist);
+ it = values->erase(it);
+ for (int k = sublist.count(); --k >= 0;)
+ it = values->insert(it, sublist.at(k));
+ } else {
+ ++it;
+ }
+ }
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/tools/propertyfinder.h b/src/lib/corelib/tools/propertyfinder.h
new file mode 100644
index 000000000..0db0ba39f
--- /dev/null
+++ b/src/lib/corelib/tools/propertyfinder.h
@@ -0,0 +1,62 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+#ifndef QBS_PROPERTY_FINDER_H
+#define QBS_PROPERTY_FINDER_H
+
+#include <QVariantList>
+#include <QVariantMap>
+
+namespace qbs {
+namespace Internal {
+
+class PropertyFinder
+{
+public:
+ enum MergeType { DoMergeLists, DoNotMergeLists };
+ QVariantList propertyValues(const QVariantMap &properties, const QString &moduleName,
+ const QString &key, MergeType mergeType = DoMergeLists);
+
+ // Note that this can still be a list if the property type itself is one.
+ QVariant propertyValue(const QVariantMap &properties, const QString &moduleName,
+ const QString &key);
+
+private:
+ void findModuleValues(const QVariantMap &properties, bool searchRecursively);
+ void addToList(const QVariant &value);
+ static void mergeLists(QVariantList *values);
+
+ QString m_moduleName;
+ QString m_key;
+ QVariantList m_values;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // Include guard
diff --git a/src/lib/corelib/tools/qbs_export.h b/src/lib/corelib/tools/qbs_export.h
new file mode 100644
index 000000000..da6779088
--- /dev/null
+++ b/src/lib/corelib/tools/qbs_export.h
@@ -0,0 +1,44 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+#ifndef QBS_EXPORT_H
+#define QBS_EXPORT_H
+
+#include <qglobal.h>
+
+#ifdef QBS_STATIC_LIB
+# define QBS_EXPORT
+#else
+# ifdef QBS_LIBRARY
+# define QBS_EXPORT Q_DECL_EXPORT
+# else
+# define QBS_EXPORT Q_DECL_IMPORT
+# endif
+#endif
+
+#endif // Include guard.
diff --git a/src/lib/corelib/tools/qbsassert.cpp b/src/lib/corelib/tools/qbsassert.cpp
new file mode 100644
index 000000000..46ea58b83
--- /dev/null
+++ b/src/lib/corelib/tools/qbsassert.cpp
@@ -0,0 +1,50 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "qbsassert.h"
+#include "error.h"
+
+#include <QString>
+
+namespace qbs {
+namespace Internal {
+
+void writeAssertLocation(const char *condition, const char *file, int line)
+{
+ qDebug("SOFT ASSERT: %s in %s:%d", condition, file, line);
+}
+
+void throwAssertLocation(const char *condition, const char *file, int line)
+{
+ throw ErrorInfo(QString(QLatin1String("ASSERT: %1")).arg(condition),
+ CodeLocation(QString::fromLocal8Bit(file), line), true);
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/tools/qbsassert.h b/src/lib/corelib/tools/qbsassert.h
new file mode 100644
index 000000000..25c00ae1a
--- /dev/null
+++ b/src/lib/corelib/tools/qbsassert.h
@@ -0,0 +1,59 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef QBS_QBSASSERT_H
+#define QBS_QBSASSERT_H
+
+#include "qbs_export.h"
+
+namespace qbs {
+namespace Internal {
+
+void QBS_EXPORT writeAssertLocation(const char *condition, const char *file, int line);
+void QBS_EXPORT throwAssertLocation(const char *condition, const char *file, int line);
+
+} // namespace Internal
+} // namespace qbs
+
+#define QBS_ASSERT(cond, action)\
+ if (Q_LIKELY(cond)) {} else {\
+ ::qbs::Internal::writeAssertLocation(#cond, __FILE__, __LINE__); action;\
+ } do {} while (0)
+
+// The do {} while (0) is here to enforce the use of a semicolon after QBS_ASSERT.
+// action can also be continue or break. Copied from qtcassert.h in Qt Creator.
+
+#define QBS_CHECK(cond)\
+ do {\
+ if (Q_LIKELY(cond)) {} else {\
+ ::qbs::Internal::throwAssertLocation(#cond, __FILE__, __LINE__);\
+ }\
+ } while (0)
+
+#endif // QBS_QBSASSERT_H
diff --git a/src/lib/corelib/tools/qttools.cpp b/src/lib/corelib/tools/qttools.cpp
new file mode 100644
index 000000000..21fef9b8f
--- /dev/null
+++ b/src/lib/corelib/tools/qttools.cpp
@@ -0,0 +1,40 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "qttools.h"
+
+QT_BEGIN_NAMESPACE
+uint qHash(const QStringList &list)
+{
+ uint s = 0;
+ foreach (const QString &n, list)
+ s ^= qHash(n) + 0x9e3779b9 + (s << 6) + (s >> 2);
+ return s;
+}
+QT_END_NAMESPACE
diff --git a/src/lib/corelib/tools/qttools.h b/src/lib/corelib/tools/qttools.h
new file mode 100644
index 000000000..bef545c0e
--- /dev/null
+++ b/src/lib/corelib/tools/qttools.h
@@ -0,0 +1,40 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef QBSQTTOOLS_H
+#define QBSQTTOOLS_H
+
+#include <QHash>
+#include <QStringList>
+
+QT_BEGIN_NAMESPACE
+uint qHash(const QStringList &list);
+QT_END_NAMESPACE
+
+#endif // QBSQTTOOLS_H
diff --git a/src/lib/corelib/tools/scannerpluginmanager.cpp b/src/lib/corelib/tools/scannerpluginmanager.cpp
new file mode 100644
index 000000000..3c5dad702
--- /dev/null
+++ b/src/lib/corelib/tools/scannerpluginmanager.cpp
@@ -0,0 +1,115 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "scannerpluginmanager.h"
+
+#include <logging/logger.h>
+#include <logging/translator.h>
+#include <tools/hostosinfo.h>
+
+#include <QCoreApplication>
+#include <QDirIterator>
+#include <QLibrary>
+
+namespace qbs {
+namespace Internal {
+
+ScannerPluginManager::~ScannerPluginManager()
+{
+ foreach (QLibrary * const lib, m_libs) {
+ lib->unload();
+ delete lib;
+ }
+}
+
+ScannerPluginManager *ScannerPluginManager::instance()
+{
+ static ScannerPluginManager scannerPlugin;
+ return &scannerPlugin;
+}
+
+ScannerPluginManager::ScannerPluginManager()
+{
+}
+
+QList<ScannerPlugin *> ScannerPluginManager::scannersForFileTag(const FileTag &fileTag)
+{
+ return instance()->m_scannerPlugins.value(fileTag);
+}
+
+void ScannerPluginManager::loadPlugins(const QStringList &pluginPaths, const Logger &logger)
+{
+ QStringList filters;
+
+ if (HostOsInfo::isWindowsHost())
+ filters << "*.dll";
+ else if (HostOsInfo::isOsxHost())
+ filters << "*.dylib";
+ else
+ filters << "*.so";
+
+ foreach (const QString &pluginPath, pluginPaths) {
+ logger.qbsTrace() << QString::fromLocal8Bit("pluginmanager: loading plugins from '%1'.")
+ .arg(QDir::toNativeSeparators(pluginPath));
+ QDirIterator it(pluginPath, filters, QDir::Files);
+ while (it.hasNext()) {
+ const QString fileName = it.next();
+ QScopedPointer<QLibrary> lib(new QLibrary(fileName));
+ if (!lib->load()) {
+ logger.qbsWarning() << Tr::tr("pluginmanager: couldn't load plugin '%1': %2")
+ .arg(QDir::toNativeSeparators(fileName), lib->errorString());
+ continue;
+ }
+
+ getScanners_f getScanners = reinterpret_cast<getScanners_f>(lib->resolve("getScanners"));
+ if (!getScanners) {
+ logger.qbsWarning() << Tr::tr("pluginmanager: couldn't resolve "
+ "symbol in '%1'.").arg(QDir::toNativeSeparators(fileName));
+ continue;
+ }
+
+ ScannerPlugin **plugins = getScanners();
+ if (plugins == 0) {
+ logger.qbsWarning() << Tr::tr("pluginmanager: no scanners "
+ "returned from '%1'.").arg(QDir::toNativeSeparators(fileName));
+ continue;
+ }
+
+ logger.qbsTrace() << QString::fromLocal8Bit("pluginmanager: scanner plugin '%1' "
+ "loaded.").arg(QDir::toNativeSeparators(fileName));
+
+ for (int i = 0; plugins[i] != 0; ++i)
+ m_scannerPlugins[FileTag(plugins[i]->fileTag)] += plugins[i];
+ m_libs.append(lib.take());
+ }
+ }
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/tools/scannerpluginmanager.h b/src/lib/corelib/tools/scannerpluginmanager.h
new file mode 100644
index 000000000..ce1ab2d2d
--- /dev/null
+++ b/src/lib/corelib/tools/scannerpluginmanager.h
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef QBS_PLUGINS_H
+#define QBS_PLUGINS_H
+
+#include <language/filetags.h>
+#include <plugins/scanner/scanner.h>
+
+#include <QHash>
+#include <QString>
+
+QT_BEGIN_NAMESPACE
+class QLibrary;
+QT_END_NAMESPACE
+
+namespace qbs {
+namespace Internal {
+class Logger;
+
+class ScannerPluginManager
+{
+public:
+ ~ScannerPluginManager();
+ static ScannerPluginManager *instance();
+ static QList<ScannerPlugin *> scannersForFileTag(const FileTag &fileTag);
+ void loadPlugins(const QStringList &paths, const Logger &logger);
+
+private:
+ ScannerPluginManager();
+
+private:
+ QList<QLibrary *> m_libs;
+ QHash<FileTag, QList<ScannerPlugin*> > m_scannerPlugins;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif
diff --git a/src/lib/corelib/tools/scripttools.cpp b/src/lib/corelib/tools/scripttools.cpp
new file mode 100644
index 000000000..c87898da5
--- /dev/null
+++ b/src/lib/corelib/tools/scripttools.cpp
@@ -0,0 +1,130 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "scripttools.h"
+
+#include <QScriptEngine>
+#include <QScriptValueIterator>
+
+QT_BEGIN_NAMESPACE
+
+QDataStream &operator<< (QDataStream &s, const QScriptProgram &script)
+{
+ s << script.sourceCode()
+ << script.fileName()
+ << script.firstLineNumber();
+ return s;
+}
+
+QDataStream &operator>> (QDataStream &s, QScriptProgram &script)
+{
+ QString fileName, sourceCode;
+ int lineNumber;
+ s >> sourceCode
+ >> fileName
+ >> lineNumber;
+ script = QScriptProgram(sourceCode, fileName, lineNumber);
+ return s;
+}
+
+QT_END_NAMESPACE
+
+namespace qbs {
+namespace Internal {
+
+void setConfigProperty(QVariantMap &cfg, const QStringList &name, const QVariant &value)
+{
+ if (name.length() == 1) {
+ cfg.insert(name.first(), value);
+ } else {
+ QVariant &subCfg = cfg[name.first()];
+ QVariantMap subCfgMap = subCfg.toMap();
+ setConfigProperty(subCfgMap, name.mid(1), value);
+ subCfg = subCfgMap;
+ }
+}
+
+QVariant getConfigProperty(const QVariantMap &cfg, const QStringList &name)
+{
+ if (name.length() == 1)
+ return cfg.value(name.first());
+ else
+ return getConfigProperty(cfg.value(name.first()).toMap(), name.mid(1));
+}
+
+QString toJSLiteral(const bool b)
+{
+ return b ? "true" : "false";
+}
+
+QString toJSLiteral(const QString &str)
+{
+ QString js = str;
+ js.replace(QRegExp("([\\\\\"])"), "\\\\1");
+ js.prepend('"');
+ js.append('"');
+ return js;
+}
+
+QString toJSLiteral(const QStringList &strs)
+{
+ QString js = "[";
+ for (int i = 0; i < strs.count(); ++i) {
+ if (i != 0)
+ js.append(", ");
+ js.append(toJSLiteral(strs.at(i)));
+ }
+ js.append(']');
+ return js;
+}
+
+QString toJSLiteral(const QVariant &val)
+{
+ if (!val.isValid()) {
+ return "undefined";
+ } else if (val.type() == QVariant::List || val.type() == QVariant::StringList) {
+ QString res;
+ foreach (const QVariant &child, val.toList()) {
+ if (res.length()) res.append(", ");
+ res.append(toJSLiteral(child));
+ }
+ res.prepend("[");
+ res.append("]");
+ return res;
+ } else if (val.type() == QVariant::Bool) {
+ return val.toBool() ? "true" : "false";
+ } else if (val.canConvert(QVariant::String)) {
+ return QLatin1Char('"') + val.toString() + QLatin1Char('"');
+ } else {
+ return QString("Unconvertible type %1").arg(val.typeName());
+ }
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/tools/scripttools.h b/src/lib/corelib/tools/scripttools.h
new file mode 100644
index 000000000..4230c898c
--- /dev/null
+++ b/src/lib/corelib/tools/scripttools.h
@@ -0,0 +1,94 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef QBS_SCRIPTTOOLS_H
+#define QBS_SCRIPTTOOLS_H
+
+#include <tools/qbs_export.h>
+
+#include <QScriptEngine>
+#include <QScriptProgram>
+#include <QScriptValue>
+#include <QSet>
+#include <QStringList>
+#include <QVariantMap>
+
+QT_BEGIN_NAMESPACE
+
+QDataStream &operator<< (QDataStream &s, const QScriptProgram &script);
+QDataStream &operator>> (QDataStream &s, QScriptProgram &script);
+
+QT_END_NAMESPACE
+
+namespace qbs {
+namespace Internal {
+
+template <typename C>
+QScriptValue toScriptValue(QScriptEngine *scriptEngine, const C &container)
+{
+ QScriptValue v = scriptEngine->newArray(container.count());
+ int i = 0;
+ foreach (const typename C::value_type &item, container)
+ v.setProperty(i++, scriptEngine->toScriptValue(item));
+ return v;
+}
+
+void setConfigProperty(QVariantMap &cfg, const QStringList &name, const QVariant &value);
+QVariant getConfigProperty(const QVariantMap &cfg, const QStringList &name);
+
+QString toJSLiteral(const bool b);
+QString toJSLiteral(const QString &str);
+QString toJSLiteral(const QStringList &strs);
+QString toJSLiteral(const QVariant &val);
+
+/**
+ * @brief push/pop a QScriptEngine's context the RAII way.
+ */
+class ScriptEngineContextPusher
+{
+public:
+ ScriptEngineContextPusher(QScriptEngine *scriptEngine)
+ : m_scriptEngine(scriptEngine)
+ {
+ m_scriptEngine->pushContext();
+ }
+
+ ~ScriptEngineContextPusher()
+ {
+ m_scriptEngine->popContext();
+ }
+
+private:
+ QScriptEngine *m_scriptEngine;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_SCRIPTTOOLS_H
diff --git a/src/lib/corelib/tools/settings.cpp b/src/lib/corelib/tools/settings.cpp
new file mode 100644
index 000000000..95d27ba24
--- /dev/null
+++ b/src/lib/corelib/tools/settings.cpp
@@ -0,0 +1,184 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "settings.h"
+
+#include "error.h"
+#include <logging/translator.h>
+#include <tools/hostosinfo.h>
+
+#include <QSettings>
+
+#include <algorithm>
+
+namespace qbs {
+using namespace Internal;
+
+static QSettings::Format format()
+{
+ return HostOsInfo::isWindowsHost() ? QSettings::IniFormat : QSettings::NativeFormat;
+}
+
+static void migrateValue(QSettings *settings, const QString &key)
+{
+ const QVariant v = settings->value(key);
+ if (!v.isValid())
+ return;
+ settings->setValue(QLatin1String("org/qt-project/qbs/") + key, v);
+ settings->remove(key);
+}
+
+static void migrateGroup(QSettings *settings, const QString &group)
+{
+ QStringList fullKeys;
+ settings->beginGroup(group);
+ foreach (const QString &key, settings->allKeys())
+ fullKeys += group + QLatin1Char('/') + key;
+ settings->endGroup();
+ foreach (const QString &key, fullKeys)
+ migrateValue(settings, key);
+}
+
+Settings::Settings(const QString &organization, const QString &application)
+ : m_settings(new QSettings(format(), QSettings::UserScope, organization, application))
+{
+ if (HostOsInfo::isOsxHost()) {
+ // Migrate settings to internal group.
+ // ### remove in qbs 1.3
+ if (!m_settings->childGroups().contains(QLatin1String("org/qt-project/qbs"))) {
+ migrateValue(m_settings, QLatin1String("defaultProfile"));
+ migrateGroup(m_settings, QLatin1String("profiles"));
+ migrateGroup(m_settings, QLatin1String("preferences"));
+ }
+ // Actual qbs settings are stored within a group, because QSettings sees extra system global
+ // settings on OS X we're not interested in.
+ m_settings->beginGroup(QLatin1String("org/qt-project/qbs"));
+ }
+}
+
+Settings::~Settings()
+{
+ delete m_settings;
+}
+
+QVariant Settings::value(const QString &key, const QVariant &defaultValue) const
+{
+ return m_settings->value(internalRepresentation(key), defaultValue);
+}
+
+QStringList Settings::allKeys() const
+{
+ QStringList keys = m_settings->allKeys();
+ fixupKeys(keys);
+ return keys;
+}
+
+QStringList Settings::directChildren(const QString &parentGroup)
+{
+ m_settings->beginGroup(internalRepresentation(parentGroup));
+ QStringList children = m_settings->childGroups();
+ children << m_settings->childKeys();
+ m_settings->endGroup();
+ fixupKeys(children);
+ return children;
+}
+
+QStringList Settings::allKeysWithPrefix(const QString &group) const
+{
+ m_settings->beginGroup(internalRepresentation(group));
+ QStringList keys = m_settings->allKeys();
+ m_settings->endGroup();
+ fixupKeys(keys);
+ return keys;
+}
+
+void Settings::setValue(const QString &key, const QVariant &value)
+{
+ m_settings->setValue(internalRepresentation(key), value);
+ checkStatus();
+}
+
+void Settings::remove(const QString &key)
+{
+ m_settings->remove(internalRepresentation(key));
+ checkStatus();
+}
+
+void Settings::clear()
+{
+ m_settings->clear();
+}
+
+QString Settings::defaultProfile() const
+{
+ return value(QLatin1String("defaultProfile")).toString();
+}
+
+QStringList Settings::profiles() const
+{
+ m_settings->beginGroup(QLatin1String("profiles"));
+ QStringList result = m_settings->childGroups();
+ m_settings->endGroup();
+ return result;
+}
+
+QString Settings::internalRepresentation(const QString &externalKey) const
+{
+ QString internalKey = externalKey;
+ return internalKey.replace(QLatin1Char('.'), QLatin1Char('/'));
+}
+
+QString Settings::externalRepresentation(const QString &internalKey) const
+{
+ QString externalKey = internalKey;
+ return externalKey.replace(QLatin1Char('/'), QLatin1Char('.'));
+}
+
+void Settings::fixupKeys(QStringList &keys) const
+{
+ keys.sort();
+ keys.removeDuplicates();
+ for (QStringList::Iterator it = keys.begin(); it != keys.end(); ++it)
+ *it = externalRepresentation(*it);
+}
+
+void Settings::checkStatus()
+{
+ m_settings->sync();
+ switch (m_settings->status()) {
+ case QSettings::NoError:
+ break;
+ case QSettings::AccessError:
+ throw ErrorInfo(Tr::tr("%1 is not accessible.").arg(m_settings->fileName()));
+ case QSettings::FormatError:
+ throw ErrorInfo(Tr::tr("Format error in %1.").arg(m_settings->fileName()));
+ }
+}
+
+} // namespace qbs
diff --git a/src/lib/corelib/tools/settings.h b/src/lib/corelib/tools/settings.h
new file mode 100644
index 000000000..2542df4fb
--- /dev/null
+++ b/src/lib/corelib/tools/settings.h
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef QBS_SETTINGS_H
+#define QBS_SETTINGS_H
+
+#include "qbs_export.h"
+
+#include <QStringList>
+#include <QVariant>
+
+QT_BEGIN_NAMESPACE
+class QSettings;
+QT_END_NAMESPACE
+
+namespace qbs {
+
+class QBS_EXPORT Settings
+{
+public:
+ Settings(const QString &organization, const QString &application);
+ ~Settings();
+
+ QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const;
+ QStringList allKeys() const;
+ QStringList directChildren(const QString &parentGroup); // Keys and groups.
+ QStringList allKeysWithPrefix(const QString &group) const;
+ void setValue(const QString &key, const QVariant &value);
+ void remove(const QString &key);
+ void clear();
+
+ QString defaultProfile() const;
+ QStringList profiles() const;
+
+private:
+ QString internalRepresentation(const QString &externalKey) const;
+ QString externalRepresentation(const QString &internalKey) const;
+ void fixupKeys(QStringList &keys) const;
+ void checkStatus();
+
+ QSettings * const m_settings;
+};
+
+} // namespace qbs
+
+#endif // QBS_SETTINGS_H
diff --git a/src/lib/corelib/tools/setupprojectparameters.cpp b/src/lib/corelib/tools/setupprojectparameters.cpp
new file mode 100644
index 000000000..c3b00f3c4
--- /dev/null
+++ b/src/lib/corelib/tools/setupprojectparameters.cpp
@@ -0,0 +1,434 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+#include "setupprojectparameters.h"
+
+#include <logging/translator.h>
+#include <tools/profile.h>
+#include <tools/qbsassert.h>
+#include <tools/scripttools.h>
+#include <tools/settings.h>
+
+namespace qbs {
+namespace Internal {
+
+/*!
+ * \class SetupProjectParameters
+ * \brief The \c SetupProjectParameters class comprises data required to set up a qbs project.
+ */
+
+class SetupProjectParametersPrivate : public QSharedData
+{
+public:
+ SetupProjectParametersPrivate()
+ : ignoreDifferentProjectFilePath(false)
+ , dryRun(false)
+ , logElapsedTime(false)
+ , restoreBehavior(SetupProjectParameters::RestoreAndTrackChanges)
+ , environment(QProcessEnvironment::systemEnvironment())
+ {
+ }
+
+ QString projectFilePath;
+ QString buildRoot;
+ QStringList searchPaths;
+ QStringList pluginPaths;
+ QVariantMap overriddenValues;
+ QVariantMap buildConfiguration;
+ mutable QVariantMap overriddenValuesTree;
+ mutable QVariantMap buildConfigurationTree;
+ mutable QVariantMap finalBuildConfigtree;
+ bool ignoreDifferentProjectFilePath;
+ bool dryRun;
+ bool logElapsedTime;
+ SetupProjectParameters::RestoreBehavior restoreBehavior;
+ QProcessEnvironment environment;
+};
+
+} // namespace Internal
+
+SetupProjectParameters::SetupProjectParameters() : d(new Internal::SetupProjectParametersPrivate)
+{
+}
+
+SetupProjectParameters::SetupProjectParameters(const SetupProjectParameters &other) : d(other.d)
+{
+}
+
+SetupProjectParameters::~SetupProjectParameters()
+{
+}
+
+SetupProjectParameters &SetupProjectParameters::operator=(const SetupProjectParameters &other)
+{
+ d = other.d;
+ return *this;
+}
+
+/*!
+ * \brief Returns the absolute path to the qbs project file.
+ * This file typically has a ".qbs" suffix.
+ */
+QString SetupProjectParameters::projectFilePath() const
+{
+ return d->projectFilePath;
+}
+
+/*!
+ * \brief Sets the path to the main project file.
+ * \note The argument must be an absolute file path.
+ */
+void SetupProjectParameters::setProjectFilePath(const QString &projectFilePath)
+{
+ d->projectFilePath = projectFilePath;
+}
+
+/*!
+ * \brief Returns the base path of where to put the build artifacts and store the build graph.
+ */
+QString SetupProjectParameters::buildRoot() const
+{
+ return d->buildRoot;
+}
+
+/*!
+ * \brief Sets the base path of where to put the build artifacts and store the build graph.
+ * The same base path can be used for several build profiles of the same project without them
+ * interfering with each other.
+ * It might look as if this parameter would not be needed at the time of setting up the project,
+ * but keep in mind that the project information could already exist on disk, in which case
+ * loading it will be much faster than setting up the project from scratch.
+ * \note The argument must be an absolute path to a directory.
+ */
+void SetupProjectParameters::setBuildRoot(const QString &buildRoot)
+{
+ d->buildRoot = buildRoot;
+}
+
+/*!
+ * \brief Where to look for modules and items to import.
+ */
+QStringList SetupProjectParameters::searchPaths() const
+{
+ return d->searchPaths;
+}
+
+/*!
+ * \brief Sets the information about where to look for modules and items to import.
+ * \note The elements of the list must be absolute paths to directories.
+ */
+void SetupProjectParameters::setSearchPaths(const QStringList &searchPaths)
+{
+ d->searchPaths = searchPaths;
+}
+
+/*!
+ * \brief Where to look for plugins.
+ */
+QStringList SetupProjectParameters::pluginPaths() const
+{
+ return d->pluginPaths;
+}
+
+/*!
+ * \brief Sets the information about where to look for plugins.
+ * \note The elements of the list must be absolute paths to directories.
+ */
+void SetupProjectParameters::setPluginPaths(const QStringList &pluginPaths)
+{
+ d->pluginPaths = pluginPaths;
+}
+
+/*!
+ * Returns the overridden values of the build configuration.
+ */
+QVariantMap SetupProjectParameters::overriddenValues() const
+{
+ return d->overriddenValues;
+}
+
+/*!
+ * Set the overridden values of the build configuration.
+ */
+void SetupProjectParameters::setOverriddenValues(const QVariantMap &values)
+{
+ // warn if somebody tries to set a build configuration tree:
+ for (QVariantMap::const_iterator i = values.constBegin();
+ i != values.constEnd(); ++i) {
+ QBS_ASSERT(i.value().type() != QVariant::Map, return);
+ }
+ d->overriddenValues = values;
+ d->overriddenValuesTree.clear();
+ d->finalBuildConfigtree.clear();
+}
+
+static void provideValuesTree(const QVariantMap &values, QVariantMap *valueTree)
+{
+ if (!valueTree->isEmpty() || values.isEmpty())
+ return;
+
+ valueTree->clear();
+ for (QVariantMap::const_iterator it = values.constBegin(); it != values.constEnd(); ++it) {
+ QStringList nameElements = it.key().split(QLatin1Char('.'));
+ if (nameElements.count() > 2) { // ### workaround for submodules being represented internally as a single module of name "module/submodule" rather than two nested modules "module" and "submodule"
+ const QString last = nameElements.takeLast();
+ nameElements = QStringList(nameElements.join(QLatin1String("/")));
+ nameElements.append(last);
+ }
+ Internal::setConfigProperty(*valueTree, nameElements, it.value());
+ }
+}
+
+QVariantMap SetupProjectParameters::overriddenValuesTree() const
+{
+ provideValuesTree(d->overriddenValues, &d->overriddenValuesTree);
+ return d->overriddenValuesTree;
+}
+
+/*!
+ * \brief The collection of properties to use for resolving the project.
+ */
+QVariantMap SetupProjectParameters::buildConfiguration() const
+{
+ return d->buildConfiguration;
+}
+
+/*!
+ * Sets the collection of properties to use for resolving the project.
+ *
+ * Keys are expected to be in dotted syntax (e.g. Qt.declarative.qmlDebugging) that is
+ * used by "qbs config".
+ */
+void SetupProjectParameters::setBuildConfiguration(const QVariantMap &buildConfiguration)
+{
+ // warn if somebody tries to set a build configuration tree:
+ for (QVariantMap::const_iterator i = buildConfiguration.constBegin();
+ i != buildConfiguration.constEnd(); ++i) {
+ QBS_ASSERT(i.value().type() != QVariant::Map, return);
+ }
+ d->buildConfiguration = buildConfiguration;
+ d->buildConfigurationTree.clear();
+ d->finalBuildConfigtree.clear();
+}
+
+/*!
+ * \brief Returns the build configuration in tree form.
+ * \return the tree form of the build configuration.
+ */
+QVariantMap SetupProjectParameters::buildConfigurationTree() const
+{
+ provideValuesTree(d->buildConfiguration, &d->buildConfigurationTree);
+ return d->buildConfigurationTree;
+}
+
+/*!
+ * \brief Expands the build configuration based on the given settings.
+ *
+ * Expansion is the process by which the build configuration is completed based on the given
+ * settings. E.g. the information configured in a profile is filled into the build
+ * configuration by this step.
+ *
+ * This method returns an Error. The list of entries in this error will be empty is the
+ * expansion was successful.
+ */
+ErrorInfo SetupProjectParameters::expandBuildConfiguration(Settings *settings)
+{
+ ErrorInfo err;
+
+ // Generates a full build configuration from user input, using the settings.
+ QVariantMap expandedConfig = d->buildConfiguration;
+
+ const QString buildVariant = expandedConfig.value(QLatin1String("qbs.buildVariant")).toString();
+ if (buildVariant.isEmpty())
+ return ErrorInfo(Internal::Tr::tr("No build variant set."));
+ if (buildVariant != QLatin1String("debug") && buildVariant != QLatin1String("release")) {
+ err.append(Internal::Tr::tr("Invalid build variant '%1'. Must be 'debug' or 'release'.")
+ .arg(buildVariant));
+ return err;
+ }
+
+ // Fill in buildCfg in this order (making sure not to overwrite a key already set by a previous stage)
+ // 1) Things specified on command line (already in buildCfg at this point)
+ // 2) Everything from the profile key
+ QString profileName = expandedConfig.value("qbs.profile").toString();
+ if (profileName.isNull()) {
+ profileName = settings->defaultProfile();
+ if (profileName.isNull()) {
+ const QString profileNames = settings->profiles().join(QLatin1String(", "));
+ err.append(Internal::Tr::tr("No profile given and no default profile set.\n"
+ "Either set the configuration value 'defaultProfile' to a "
+ "valid profile name\n"
+ "or specify the profile with the command line parameter "
+ "'profile:name'.\n"
+ "The following profiles are available:\n%1")
+ .arg(profileNames));
+ return err;
+ }
+ expandedConfig.insert("qbs.profile", profileName);
+ }
+
+ // (2)
+ const Profile profile(profileName, settings);
+ const QStringList profileKeys = profile.allKeys(Profile::KeySelectionRecursive);
+ if (profileKeys.isEmpty()) {
+ err.append(Internal::Tr::tr("Unknown or empty profile '%1'.").arg(profileName));
+ return err;
+ }
+ foreach (const QString &profileKey, profileKeys) {
+ if (!expandedConfig.contains(profileKey))
+ expandedConfig.insert(profileKey, profile.value(profileKey));
+ }
+
+ if (d->buildConfiguration != expandedConfig) {
+ d->buildConfigurationTree.clear();
+ d->buildConfiguration = expandedConfig;
+ }
+ return err;
+}
+
+/*!
+ * \brief Returns the build configuration in tree form, with overridden values taken into account.
+ */
+QVariantMap SetupProjectParameters::finalBuildConfigurationTree() const
+{
+ if (d->finalBuildConfigtree.isEmpty()) {
+ QVariantMap finalMap = d->buildConfiguration;
+ for (QVariantMap::ConstIterator it = d->overriddenValues.constBegin();
+ it != d->overriddenValues.constEnd(); ++it) {
+ finalMap.insert(it.key(), it.value());
+ }
+ provideValuesTree(finalMap, &d->finalBuildConfigtree);
+ }
+ return d->finalBuildConfigtree;
+}
+
+/*!
+ * \variable SetupProjectParameters::ignoreDifferentProjectFilePath
+ * \brief Returns true iff the saved build graph should be used even if its path to the
+ * project file is different from \c SetupProjectParameters::projectFilePath()
+ */
+bool SetupProjectParameters::ignoreDifferentProjectFilePath() const
+{
+ return d->ignoreDifferentProjectFilePath;
+}
+
+/*!
+ * \brief Controls whether the path to the main project file may be different from the one
+ * stored in a possible build graph file.
+ * The default is false.
+ */
+void SetupProjectParameters::setIgnoreDifferentProjectFilePath(bool doIgnore)
+{
+ d->ignoreDifferentProjectFilePath = doIgnore;
+}
+
+ /*!
+ * \brief if true, qbs will not store the build graph of the resolved project.
+ */
+bool SetupProjectParameters::dryRun() const
+{
+ return d->dryRun;
+}
+
+ /*!
+ * \brief Controls whether the build graph will be stored.
+ * If the argument is true, qbs will not store the build graph after resolving the project.
+ * The default is false.
+ */
+void SetupProjectParameters::setDryRun(bool dryRun)
+{
+ d->dryRun = dryRun;
+}
+
+ /*!
+ * \brief Returns true iff the time the operation takes should be logged
+ */
+bool SetupProjectParameters::logElapsedTime() const
+{
+ return d->logElapsedTime;
+}
+
+/*!
+ * Controls whether to log the time taken up for resolving the project.
+ * The default is false.
+ */
+void SetupProjectParameters::setLogElapsedTime(bool logElapsedTime)
+{
+ d->logElapsedTime = logElapsedTime;
+}
+
+/*!
+ * \brief Gets the environment used while resolving the project.
+ */
+QProcessEnvironment SetupProjectParameters::environment() const
+{
+ return d->environment;
+}
+
+/*!
+ * \brief Sets the environment used while resolving the project.
+ */
+void SetupProjectParameters::setEnvironment(const QProcessEnvironment &env)
+{
+ d->environment = env;
+}
+
+
+/*!
+ * \enum SetupProjectParamaters::RestoreBehavior
+ * This enum type specifies how to deal with existing on-disk build information.
+ * \value RestoreOnly Indicates that a stored build graph is to be loaded and the information
+ * therein assumed to be up to date. It is then considered an error if no
+ * such build graph exists.
+ * \value ResolveOnly Indicates that no attempt should be made to restore an existing build graph.
+ * Instead, the project is to be resolved from scratch.
+ * \value RestoreAndTrackChanges Indicates that the build graph should be restored from disk
+ * if possible and otherwise set up from scratch. In the first case,
+ * (parts of) the project might still be re-resolved if certain
+ * parameters have changed (e.g. environment variables used in the
+ * project files).
+ */
+
+
+/*!
+ * Returns information about how restored build data will be handled.
+ */
+SetupProjectParameters::RestoreBehavior SetupProjectParameters::restoreBehavior() const
+{
+ return d->restoreBehavior;
+}
+
+/*!
+ * Controls how restored build data will be handled.
+ */
+void SetupProjectParameters::setRestoreBehavior(SetupProjectParameters::RestoreBehavior behavior)
+{
+ d->restoreBehavior = behavior;
+}
+
+} // namespace qbs
diff --git a/src/lib/corelib/tools/setupprojectparameters.h b/src/lib/corelib/tools/setupprojectparameters.h
new file mode 100644
index 000000000..d6d8bf88c
--- /dev/null
+++ b/src/lib/corelib/tools/setupprojectparameters.h
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+#ifndef QBS_SETUPPROJECTPARAMETERS_H
+#define QBS_SETUPPROJECTPARAMETERS_H
+
+#include "qbs_export.h"
+
+#include <tools/error.h>
+
+#include <QProcessEnvironment>
+#include <QSharedDataPointer>
+#include <QStringList>
+#include <QVariantMap>
+
+namespace qbs {
+
+class Settings;
+
+namespace Internal { class SetupProjectParametersPrivate; }
+
+class QBS_EXPORT SetupProjectParameters
+{
+public:
+ SetupProjectParameters();
+ SetupProjectParameters(const SetupProjectParameters &other);
+ ~SetupProjectParameters();
+
+ SetupProjectParameters &operator=(const SetupProjectParameters &other);
+
+ QString projectFilePath() const;
+ void setProjectFilePath(const QString &projectFilePath);
+
+ QString buildRoot() const;
+ void setBuildRoot(const QString &buildRoot);
+
+ QStringList searchPaths() const;
+ void setSearchPaths(const QStringList &searchPaths);
+
+ QStringList pluginPaths() const;
+ void setPluginPaths(const QStringList &pluginPaths);
+
+ QVariantMap overriddenValues() const;
+ void setOverriddenValues(const QVariantMap &values);
+ QVariantMap overriddenValuesTree() const;
+
+ QVariantMap buildConfiguration() const;
+ void setBuildConfiguration(const QVariantMap &buildConfiguration);
+ QVariantMap buildConfigurationTree() const;
+ ErrorInfo expandBuildConfiguration(Settings *settings);
+
+ QVariantMap finalBuildConfigurationTree() const;
+
+ bool ignoreDifferentProjectFilePath() const;
+ void setIgnoreDifferentProjectFilePath(bool doIgnore);
+
+ bool dryRun() const;
+ void setDryRun(bool dryRun);
+
+ bool logElapsedTime() const;
+ void setLogElapsedTime(bool logElapsedTime);
+
+ QProcessEnvironment environment() const;
+ void setEnvironment(const QProcessEnvironment &env);
+
+ enum RestoreBehavior { RestoreOnly, ResolveOnly, RestoreAndTrackChanges };
+ RestoreBehavior restoreBehavior() const;
+ void setRestoreBehavior(RestoreBehavior behavior);
+
+private:
+ QSharedDataPointer<Internal::SetupProjectParametersPrivate> d;
+};
+
+} // namespace qbs
+
+#endif // Include guard
diff --git a/src/lib/corelib/tools/tools.pri b/src/lib/corelib/tools/tools.pri
new file mode 100644
index 000000000..fca3d8f4b
--- /dev/null
+++ b/src/lib/corelib/tools/tools.pri
@@ -0,0 +1,79 @@
+INCLUDEPATH += $$PWD/../.. # for plugins
+
+HEADERS += \
+ $$PWD/codelocation.h \
+ $$PWD/error.h \
+ $$PWD/fileinfo.h \
+ $$PWD/filetime.h \
+ $$PWD/id.h \
+ $$PWD/persistence.h \
+ $$PWD/scannerpluginmanager.h \
+ $$PWD/scripttools.h \
+ $$PWD/settings.h \
+ $$PWD/preferences.h \
+ $$PWD/profile.h \
+ $$PWD/processresult.h \
+ $$PWD/processresult_p.h \
+ $$PWD/progressobserver.h \
+ $$PWD/propertyfinder.h \
+ $$PWD/hostosinfo.h \
+ $$PWD/buildoptions.h \
+ $$PWD/installoptions.h \
+ $$PWD/cleanoptions.h \
+ $$PWD/setupprojectparameters.h \
+ $$PWD/persistentobject.h \
+ $$PWD/weakpointer.h \
+ $$PWD/qbs_export.h \
+ $$PWD/qbsassert.h \
+ $$PWD/qttools.h
+
+SOURCES += \
+ $$PWD/codelocation.cpp \
+ $$PWD/error.cpp \
+ $$PWD/fileinfo.cpp \
+ $$PWD/id.cpp \
+ $$PWD/persistence.cpp \
+ $$PWD/scannerpluginmanager.cpp \
+ $$PWD/scripttools.cpp \
+ $$PWD/settings.cpp \
+ $$PWD/preferences.cpp \
+ $$PWD/processresult.cpp \
+ $$PWD/profile.cpp \
+ $$PWD/progressobserver.cpp \
+ $$PWD/propertyfinder.cpp \
+ $$PWD/buildoptions.cpp \
+ $$PWD/installoptions.cpp \
+ $$PWD/cleanoptions.cpp \
+ $$PWD/setupprojectparameters.cpp \
+ $$PWD/qbsassert.cpp \
+ $$PWD/qttools.cpp
+
+win32 {
+ SOURCES += $$PWD/filetime_win.cpp
+}
+
+unix {
+ SOURCES += $$PWD/filetime_unix.cpp
+}
+
+all_tests {
+ HEADERS += $$PWD/tst_tools.h
+ SOURCES += $$PWD/tst_tools.cpp
+}
+
+!qbs_no_dev_install {
+ tools_headers.files = \
+ $$PWD/cleanoptions.h \
+ $$PWD/codelocation.h \
+ $$PWD/error.h \
+ $$PWD/settings.h \
+ $$PWD/preferences.h \
+ $$PWD/profile.h \
+ $$PWD/processresult.h \
+ $$PWD/qbs_export.h \
+ $$PWD/buildoptions.h \
+ $$PWD/installoptions.h \
+ $$PWD/setupprojectparameters.h
+ tools_headers.path = $${QBS_INSTALL_PREFIX}/include/qbs/tools
+ INSTALLS += tools_headers
+}
diff --git a/src/lib/corelib/tools/tst_tools.cpp b/src/lib/corelib/tools/tst_tools.cpp
new file mode 100644
index 000000000..c1af58f70
--- /dev/null
+++ b/src/lib/corelib/tools/tst_tools.cpp
@@ -0,0 +1,178 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+#include "tst_tools.h"
+
+#include <tools/buildoptions.h>
+#include <tools/error.h>
+#include <tools/fileinfo.h>
+#include <tools/hostosinfo.h>
+#include <tools/profile.h>
+#include <tools/settings.h>
+#include <tools/setupprojectparameters.h>
+
+#include <QFileInfo>
+#include <QTemporaryFile>
+#include <QTest>
+
+namespace qbs {
+namespace Internal {
+
+TestTools::TestTools(Settings *settings) : m_settings(settings)
+{
+}
+
+void TestTools::testFileInfo()
+{
+ QCOMPARE(FileInfo::fileName("C:/waffl/copter.exe"), QString("copter.exe"));
+ QCOMPARE(FileInfo::baseName("C:/waffl/copter.exe.lib"), QString("copter"));
+ QCOMPARE(FileInfo::completeBaseName("C:/waffl/copter.exe.lib"), QString("copter.exe"));
+ QCOMPARE(FileInfo::path("abc"), QString("."));
+ QCOMPARE(FileInfo::path("/abc/lol"), QString("/abc"));
+ QVERIFY(!FileInfo::isAbsolute("bla/lol"));
+ QVERIFY(FileInfo::isAbsolute("/bla/lol"));
+ if (HostOsInfo::isWindowsHost())
+ QVERIFY(FileInfo::isAbsolute("C:\\bla\\lol"));
+ QCOMPARE(FileInfo::resolvePath("/abc/lol", "waffl"), QString("/abc/lol/waffl"));
+ QCOMPARE(FileInfo::resolvePath("/abc/def/ghi/jkl/", "../foo/bar"), QString("/abc/def/ghi/foo/bar"));
+ QCOMPARE(FileInfo::resolvePath("/abc/def/ghi/jkl/", "../../foo/bar"), QString("/abc/def/foo/bar"));
+ QCOMPARE(FileInfo::resolvePath("/abc", "../../../foo/bar"), QString("/foo/bar"));
+ QCOMPARE(FileInfo("/does/not/exist").lastModified(), FileTime());
+}
+
+void TestTools::fileCaseCheck()
+{
+ QTemporaryFile tempFile(QLatin1String("CamelCase"));
+ QVERIFY(tempFile.open());
+ QFileInfo tempFileInfo(tempFile.fileName());
+ const QString lowerFilePath = tempFileInfo.absolutePath() + QLatin1Char('/')
+ + tempFileInfo.fileName().toLower();
+ const QString upperFilePath = tempFileInfo.absolutePath() + QLatin1Char('/')
+ + tempFileInfo.fileName().toUpper();
+ QVERIFY(FileInfo::isFileCaseCorrect(tempFileInfo.absoluteFilePath()));
+ if (QFile::exists(lowerFilePath))
+ QVERIFY(!FileInfo::isFileCaseCorrect(lowerFilePath));
+ if (QFile::exists(upperFilePath))
+ QVERIFY(!FileInfo::isFileCaseCorrect(upperFilePath));
+}
+
+void TestTools::testProfiles()
+{
+ bool exceptionCaught;
+ Profile parentProfile("parent", m_settings);
+ Profile childProfile("child", m_settings);
+ try {
+ parentProfile.removeBaseProfile();
+ parentProfile.remove("testKey");
+ QCOMPARE(parentProfile.value("testKey", "none").toString(), QLatin1String("none"));
+ parentProfile.setValue("testKey", "testValue");
+ QCOMPARE(parentProfile.value("testKey").toString(), QLatin1String("testValue"));
+
+ childProfile.remove("testKey");
+ childProfile.removeBaseProfile();
+ QCOMPARE(childProfile.value("testKey", "none").toString(), QLatin1String("none"));
+ childProfile.setBaseProfile("parent");
+ QCOMPARE(childProfile.value("testKey").toString(), QLatin1String("testValue"));
+
+ // Change base profile and check if the inherited value also changes.
+ Profile fooProfile("foo", m_settings);
+ fooProfile.setValue("testKey", "gnampf");
+ childProfile.setBaseProfile("foo");
+ QCOMPARE(childProfile.value("testKey", "none").toString(), QLatin1String("gnampf"));
+ exceptionCaught = false;
+ } catch (ErrorInfo &) {
+ exceptionCaught = true;
+ }
+ QVERIFY(!exceptionCaught);
+
+ try {
+ childProfile.setBaseProfile("SmurfAlongWithMe");
+ childProfile.value("blubb");
+ exceptionCaught = false;
+ } catch (ErrorInfo &) {
+ exceptionCaught = true;
+ }
+ QVERIFY(exceptionCaught);
+
+ try {
+ childProfile.setBaseProfile("parent");
+ parentProfile.setBaseProfile("child");
+ QVERIFY(!childProfile.value("blubb").isValid());
+ exceptionCaught = false;
+ } catch (ErrorInfo &) {
+ exceptionCaught = true;
+ }
+ QVERIFY(exceptionCaught);
+
+ try {
+ QVERIFY(!childProfile.allKeys(Profile::KeySelectionNonRecursive).isEmpty());
+ exceptionCaught = false;
+ } catch (ErrorInfo &) {
+ exceptionCaught = true;
+ }
+ QVERIFY(!exceptionCaught);
+
+ try {
+ QVERIFY(!childProfile.allKeys(Profile::KeySelectionRecursive).isEmpty());
+ exceptionCaught = false;
+ } catch (ErrorInfo &) {
+ exceptionCaught = true;
+ }
+ QVERIFY(exceptionCaught);
+}
+
+void TestTools::testBuildConfigMerging()
+{
+ QVariantMap buildConfigMap;
+ buildConfigMap.insert(QLatin1String("topLevelKey"), QLatin1String("topLevelValue"));
+ buildConfigMap.insert(QLatin1String("qbs.toolchain"), QLatin1String("gcc"));
+ buildConfigMap.insert(QLatin1String("qbs.architecture"),
+ QLatin1String("Jean-Claude Pillemann"));
+ buildConfigMap.insert(QLatin1String("cpp.treatWarningsAsErrors"), true);
+ QVariantMap overrideMap;
+ overrideMap.insert(QLatin1String("qbs.toolchain"), QLatin1String("clang"));
+ SetupProjectParameters params;
+ params.setBuildConfiguration(buildConfigMap);
+ params.setOverriddenValues(overrideMap);
+ const QVariantMap finalMap = params.finalBuildConfigurationTree();
+ QCOMPARE(finalMap.count(), 3);
+ QCOMPARE(finalMap.value(QLatin1String("topLevelKey")).toString(),
+ QString::fromLatin1("topLevelValue"));
+ const QVariantMap finalQbsMap = finalMap.value(QLatin1String("qbs")).toMap();
+ QCOMPARE(finalQbsMap.count(), 2);
+ QCOMPARE(finalQbsMap.value(QLatin1String("toolchain")).toString(),
+ QString::fromLatin1("clang"));
+ QCOMPARE(finalQbsMap.value(QLatin1String("architecture")).toString(),
+ QString::fromLatin1("Jean-Claude Pillemann"));
+ const QVariantMap finalCppMap = finalMap.value(QLatin1String("cpp")).toMap();
+ QCOMPARE(finalCppMap.count(), 1);
+ QCOMPARE(finalCppMap.value(QLatin1String("treatWarningsAsErrors")).toBool(), true);
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/tools/tst_tools.h b/src/lib/corelib/tools/tst_tools.h
new file mode 100644
index 000000000..c7168a686
--- /dev/null
+++ b/src/lib/corelib/tools/tst_tools.h
@@ -0,0 +1,56 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+#include "qbs_export.h"
+
+#include <QObject>
+
+namespace qbs {
+class Settings;
+
+namespace Internal {
+
+class QBS_EXPORT TestTools : public QObject
+{
+ Q_OBJECT
+
+public:
+ TestTools(Settings *settings);
+
+private slots:
+ void testFileInfo();
+ void fileCaseCheck();
+ void testProfiles();
+ void testBuildConfigMerging();
+
+private:
+ Settings * const m_settings;
+};
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/tools/weakpointer.h b/src/lib/corelib/tools/weakpointer.h
new file mode 100644
index 000000000..3da1abd6d
--- /dev/null
+++ b/src/lib/corelib/tools/weakpointer.h
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+#ifndef QBS_WEAKPOINTER_H
+#define QBS_WEAKPOINTER_H
+
+#include <QWeakPointer>
+
+namespace qbs {
+namespace Internal {
+
+template<typename T> class WeakPointer : public QWeakPointer<T>
+{
+public:
+ WeakPointer() : QWeakPointer<T>() {}
+ WeakPointer(const QSharedPointer<T> &sharedPointer) : QWeakPointer<T>(sharedPointer) {}
+ template <class X> WeakPointer(const QSharedPointer<X> &sp) : QWeakPointer<T>(sp) { }
+
+
+ operator T*() const { return checkedData(); }
+ T *operator->() const { return checkedData(); }
+ T operator*() const { return *checkedData(); }
+
+private:
+ T *checkedData() const {
+ T * const d = QWeakPointer<T>::data();
+ Q_ASSERT(d); // Calling code is not expecting this situation.
+ return d;
+ }
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_WEAKPOINTER_H