aboutsummaryrefslogtreecommitdiffstats
path: root/src/app/qbs-setup-toolchains
diff options
context:
space:
mode:
authorChristian Kandeler <christian.kandeler@digia.com>2014-02-12 18:07:34 +0100
committerChristian Kandeler <christian.kandeler@digia.com>2014-02-13 12:05:31 +0100
commit0764d2aff60b2c822c81e5e58b4e56055e316efd (patch)
tree6f5ad4d027666bf8c84a26362806ac22896fffcc /src/app/qbs-setup-toolchains
parent032a1c4b4b37814594fbb01a0452ea5efa09dfcf (diff)
Let detect-toolchains be used like setup-qt.
Namely, let people specify a specific compiler and profile name instead of offering only auto-detection. Also rename to setup-toolchains for even more consistency. Task-number: QBS-496 Change-Id: I8c144d7b4dd26e38652f10c1fff56af4daf3cecb Reviewed-by: Joerg Bornemann <joerg.bornemann@digia.com>
Diffstat (limited to 'src/app/qbs-setup-toolchains')
-rw-r--r--src/app/qbs-setup-toolchains/commandlineparser.cpp123
-rw-r--r--src/app/qbs-setup-toolchains/commandlineparser.h62
-rw-r--r--src/app/qbs-setup-toolchains/main.cpp72
-rw-r--r--src/app/qbs-setup-toolchains/msvcprobe.cpp223
-rw-r--r--src/app/qbs-setup-toolchains/msvcprobe.h42
-rw-r--r--src/app/qbs-setup-toolchains/probe.cpp253
-rw-r--r--src/app/qbs-setup-toolchains/probe.h46
-rw-r--r--src/app/qbs-setup-toolchains/qbs-setup-toolchains.exe.manifest13
-rw-r--r--src/app/qbs-setup-toolchains/qbs-setup-toolchains.pro10
-rw-r--r--src/app/qbs-setup-toolchains/qbs-setup-toolchains.qbs19
-rw-r--r--src/app/qbs-setup-toolchains/qbs-setup-toolchains.rc4
-rw-r--r--src/app/qbs-setup-toolchains/xcodeprobe.cpp362
-rw-r--r--src/app/qbs-setup-toolchains/xcodeprobe.h42
13 files changed, 1271 insertions, 0 deletions
diff --git a/src/app/qbs-setup-toolchains/commandlineparser.cpp b/src/app/qbs-setup-toolchains/commandlineparser.cpp
new file mode 100644
index 000000000..dd8a1e6c9
--- /dev/null
+++ b/src/app/qbs-setup-toolchains/commandlineparser.cpp
@@ -0,0 +1,123 @@
+/****************************************************************************
+**
+** 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 "commandlineparser.h"
+
+#include <logging/translator.h>
+#include <tools/error.h>
+
+#include <QFileInfo>
+
+using qbs::Internal::Tr;
+
+static QString helpOptionShort() { return QLatin1String("-h"); }
+static QString helpOptionLong() { return QLatin1String("--help"); }
+static QString detectOption() { return QLatin1String("--detect"); }
+static QString typeOption() { return QLatin1String("--type"); }
+
+void CommandLineParser::parse(const QStringList &commandLine)
+{
+ m_commandLine = commandLine;
+ Q_ASSERT(!m_commandLine.isEmpty());
+ m_command = QFileInfo(m_commandLine.takeFirst()).fileName();
+ m_helpRequested = false;
+ m_autoDetectionMode = false;
+ m_compilerPath.clear();
+ m_toolchainType.clear();
+ m_profileName.clear();
+
+ if (m_commandLine.isEmpty())
+ throwError(Tr::tr("No command-line arguments provided."));
+
+ const QString &arg = m_commandLine.first();
+ if (arg == helpOptionShort() || arg == helpOptionLong()) {
+ m_commandLine.removeFirst();
+ m_helpRequested = true;
+ } else if (arg == detectOption()) {
+ m_autoDetectionMode = true;
+ m_commandLine.removeFirst();
+ } else if (arg == typeOption()) {
+ m_commandLine.removeFirst();
+ assignOptionArgument(typeOption(), m_toolchainType);
+ }
+
+ if (m_helpRequested || m_autoDetectionMode) {
+ if (!m_commandLine.isEmpty())
+ complainAboutExtraArguments();
+ return;
+ }
+
+ switch (m_commandLine.count()) {
+ case 0:
+ case 1:
+ throwError(Tr::tr("Not enough command-line arguments provided."));
+ case 2:
+ m_compilerPath = m_commandLine.at(0);
+ m_profileName = m_commandLine.at(1);
+ break;
+ default:
+ complainAboutExtraArguments();
+ }
+}
+
+void CommandLineParser::throwError(const QString &message)
+{
+ qbs::ErrorInfo error(Tr::tr("Syntax error: %1").arg(message));
+ error.append(usageString());
+ throw error;
+}
+
+QString CommandLineParser::usageString() const
+{
+ QString s = Tr::tr("Usage:\n");
+ s += Tr::tr(" %1 %2\n").arg(m_command, detectOption());
+ s += Tr::tr(" %1 [%2 <toolchain type>] <compiler path> <profile name>\n")
+ .arg(m_command, typeOption());
+ s += Tr::tr(" %1 %2|%3\n").arg(m_command, helpOptionShort(), helpOptionLong());
+ s += Tr::tr("The first form tries to auto-detect all known toolchains, looking them up "
+ "via the PATH environment variable.\n");
+ s += Tr::tr("The second form creates one profile for one toolchain. It will attempt "
+ "to find out the toolchain type automatically.\nIn case the compiler has "
+ "an unusual file name, you may need to provide the '--type' option.");
+ return s;
+}
+
+void CommandLineParser::assignOptionArgument(const QString &option, QString &argument)
+{
+ if (m_commandLine.isEmpty())
+ throwError(Tr::tr("Option '%1' needs an argument.").arg(option));
+ argument = m_commandLine.takeFirst();
+ if (argument.isEmpty())
+ throwError(Tr::tr("Argument for option '%1' must not be empty.").arg(option));
+}
+
+void CommandLineParser::complainAboutExtraArguments()
+{
+ throwError(Tr::tr("Extraneous command-line arguments '%1'.")
+ .arg(m_commandLine.join(QLatin1String(" "))));
+}
diff --git a/src/app/qbs-setup-toolchains/commandlineparser.h b/src/app/qbs-setup-toolchains/commandlineparser.h
new file mode 100644
index 000000000..6f6cd4f18
--- /dev/null
+++ b/src/app/qbs-setup-toolchains/commandlineparser.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_SETUPTOOLCHAINS_COMMANDLINEPARSER_H
+#define QBS_SETUPTOOLCHAINS_COMMANDLINEPARSER_H
+
+#include <QStringList>
+
+class CommandLineParser
+{
+public:
+ void parse(const QStringList &commandLine);
+
+ bool helpRequested() const { return m_helpRequested; }
+ bool autoDetectionMode() const { return m_autoDetectionMode; }
+
+ QString compilerPath() const { return m_compilerPath; }
+ QString toolchainType() const { return m_toolchainType; }
+ QString profileName() const { return m_profileName; }
+
+ QString usageString() const;
+
+private:
+ void throwError(const QString &message);
+ void assignOptionArgument(const QString &option, QString &argument);
+ void complainAboutExtraArguments();
+
+ bool m_helpRequested;
+ bool m_autoDetectionMode;
+ QString m_compilerPath;
+ QString m_toolchainType;
+ QString m_profileName;
+ QStringList m_commandLine;
+ QString m_command;
+};
+
+#endif // Include guard
diff --git a/src/app/qbs-setup-toolchains/main.cpp b/src/app/qbs-setup-toolchains/main.cpp
new file mode 100644
index 000000000..cc41baa7e
--- /dev/null
+++ b/src/app/qbs-setup-toolchains/main.cpp
@@ -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.
+**
+****************************************************************************/
+
+#include "commandlineparser.h"
+#include "probe.h"
+#include "../shared/qbssettings.h"
+
+#include <logging/translator.h>
+#include <tools/error.h>
+
+#include <QCoreApplication>
+
+#include <cstdlib>
+#include <iostream>
+
+using qbs::Internal::Tr;
+
+static void printUsage(const QString &usageString)
+{
+ std::cout << qPrintable(usageString) << std::endl;
+}
+
+int main(int argc, char **argv)
+{
+ QCoreApplication app(argc, argv);
+
+ SettingsPtr settings = qbsSettings();
+
+ CommandLineParser clParser;
+ try {
+ clParser.parse(app.arguments());
+ if (clParser.helpRequested()) {
+ printUsage(clParser.usageString());
+ return EXIT_SUCCESS;
+ }
+ if (clParser.autoDetectionMode()) {
+ probe(settings.data());
+ return EXIT_SUCCESS;
+ }
+ createProfile(clParser.profileName(), clParser.toolchainType(), clParser.compilerPath(),
+ settings.data());
+ } catch (const qbs::ErrorInfo &e) {
+ std::cerr << qPrintable(e.toString()) << std::endl;
+ return EXIT_FAILURE;
+ }
+}
diff --git a/src/app/qbs-setup-toolchains/msvcprobe.cpp b/src/app/qbs-setup-toolchains/msvcprobe.cpp
new file mode 100644
index 000000000..fe2ac9bfd
--- /dev/null
+++ b/src/app/qbs-setup-toolchains/msvcprobe.cpp
@@ -0,0 +1,223 @@
+/****************************************************************************
+**
+** 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 "msvcprobe.h"
+
+#include "probe.h"
+#include "../shared/logging/consolelogger.h"
+
+#include <logging/translator.h>
+#include <tools/hostosinfo.h>
+#include <tools/profile.h>
+#include <tools/settings.h>
+
+#include <QDir>
+#include <QFileInfo>
+#include <QSettings>
+#include <QStringList>
+#include <QVector>
+
+using namespace qbs;
+using Internal::Tr;
+
+class MSVC
+{
+public:
+ QString version;
+ QString installPath;
+ QStringList architectures;
+};
+
+class WinSDK : public MSVC
+{
+public:
+ bool isDefault;
+};
+
+QT_BEGIN_NAMESPACE
+Q_DECLARE_TYPEINFO(WinSDK, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(MSVC, Q_MOVABLE_TYPE);
+QT_END_NAMESPACE
+
+static void addMSVCPlatform(const MSVC &msvc, Settings *settings, QList<Profile> &profiles,
+ QString name, const QString &installPath, const QString &winSDKPath,
+ const QString &architecture)
+{
+ name.append(QLatin1Char('_') + architecture);
+ qbsInfo() << Tr::tr("Setting up profile '%1'.").arg(name);
+ Profile p(name, settings);
+ p.removeProfile();
+ p.setValue(QLatin1String("qbs.targetOS"), QStringList(QLatin1String("windows")));
+ p.setValue(QLatin1String("cpp.toolchainInstallPath"), installPath);
+ p.setValue(QLatin1String("qbs.toolchain"), QStringList(QLatin1String("msvc")));
+ p.setValue(QLatin1String("cpp.windowsSDKPath"), winSDKPath);
+ p.setValue(QLatin1String("qbs.architecture"),
+ Internal::HostOsInfo::canonicalArchitecture(architecture));
+ p.setValue(QLatin1String("qbs.endianness"),
+ Internal::HostOsInfo::defaultEndianness(architecture));
+ if (msvc.version.toInt() >= 2013) {
+ const QStringList flags(QLatin1String("/FS"));
+ p.setValue(QLatin1String("cpp.platformCFlags"), flags);
+ p.setValue(QLatin1String("cpp.platformCxxFlags"), flags);
+ }
+ profiles << p;
+}
+
+static void findSupportedArchitectures(MSVC *msvc, const QString &pathPrefix = QString())
+{
+ if (QFile::exists(msvc->installPath + pathPrefix + QLatin1String("/cl.exe")))
+ msvc->architectures += QLatin1String("x86");
+ if (QFile::exists(msvc->installPath + pathPrefix + QLatin1String("/amd64/cl.exe")))
+ msvc->architectures += QLatin1String("x86_64");
+}
+
+static QString wow6432Key()
+{
+#ifdef Q_OS_WIN64
+ return QLatin1String("\\Wow6432Node");
+#else
+ return QString();
+#endif
+}
+
+void msvcProbe(Settings *settings, QList<Profile> &profiles)
+{
+ qbsInfo() << Tr::tr("Detecting MSVC toolchains...");
+
+ // 1) Installed SDKs preferred over standalone Visual studio
+ QVector<WinSDK> winSDKs;
+ WinSDK defaultWinSDK;
+
+ const QSettings sdkRegistry(QLatin1String("HKEY_LOCAL_MACHINE\\SOFTWARE") + wow6432Key()
+ + QLatin1String("\\Microsoft\\Microsoft SDKs\\Windows"),
+ QSettings::NativeFormat);
+ const QString defaultSdkPath = sdkRegistry.value(QLatin1String("CurrentInstallFolder")).toString();
+ if (!defaultSdkPath.isEmpty()) {
+ foreach (const QString &sdkKey, sdkRegistry.childGroups()) {
+ WinSDK sdk;
+ sdk.version = sdkKey;
+ sdk.installPath = sdkRegistry.value(sdkKey + QLatin1String("/InstallationFolder")).toString();
+ sdk.isDefault = (sdk.installPath == defaultSdkPath);
+ if (sdk.installPath.isEmpty())
+ continue;
+ if (sdk.installPath.endsWith(QLatin1Char('\\')))
+ sdk.installPath.chop(1);
+ findSupportedArchitectures(&sdk, QLatin1String("/bin"));
+ if (sdk.isDefault)
+ defaultWinSDK = sdk;
+ winSDKs += sdk;
+ }
+ }
+
+ foreach (const WinSDK &sdk, winSDKs) {
+ qbsInfo() << Tr::tr(" Windows SDK %1 detected:\n"
+ " installed in %2").arg(sdk.version, sdk.installPath);
+ if (!sdk.architectures.isEmpty())
+ qbsInfo() << Tr::tr(" This SDK contains C++ compiler(s).");
+ if (sdk.isDefault)
+ qbsInfo() << Tr::tr(" This is the default SDK on this machine.");
+ }
+
+ // 2) Installed MSVCs
+ QVector<MSVC> msvcs;
+ const QSettings vsRegistry(
+ QLatin1String("HKEY_LOCAL_MACHINE\\SOFTWARE") + wow6432Key()
+ + QLatin1String("\\Microsoft\\VisualStudio\\SxS\\VC7"),
+ QSettings::NativeFormat);
+ foreach (const QString &vsName, vsRegistry.allKeys()) {
+ // Scan for version major.minor
+ const int dotPos = vsName.indexOf(QLatin1Char('.'));
+ if (dotPos == -1)
+ continue;
+
+ MSVC msvc;
+ msvc.installPath = vsRegistry.value(vsName).toString();
+ if (!msvc.installPath.endsWith(QLatin1Char('\\')))
+ msvc.installPath += QLatin1Char('\\');
+ msvc.installPath += QLatin1String("bin");
+ findSupportedArchitectures(&msvc);
+
+ int nVersion = vsName.left(dotPos).toInt();
+ switch (nVersion) {
+ case 8:
+ msvc.version = QLatin1String("2005");
+ break;
+ case 9:
+ msvc.version = QLatin1String("2008");
+ break;
+ case 10:
+ msvc.version = QLatin1String("2010");
+ break;
+ case 11:
+ msvc.version = QLatin1String("2012");
+ break;
+ case 12:
+ msvc.version = QLatin1String("2013");
+ break;
+ }
+
+ if (msvc.version.isEmpty()) {
+ qbsInfo() << Tr::tr(" Unknown MSVC version %1 found.").arg(nVersion);
+ continue;
+ }
+
+ // Check existence of various install scripts
+ const QString vcvars32bat = msvc.installPath + QLatin1String("/vcvars32.bat");
+ if (!QFileInfo(vcvars32bat).isFile())
+ continue;
+
+ msvcs += msvc;
+ }
+
+ foreach (const MSVC &msvc, msvcs) {
+ qbsInfo() << Tr::tr(" MSVC detected:\n"
+ " version %1\n"
+ " installed in %2").arg(msvc.version, msvc.installPath);
+ }
+
+ if (winSDKs.isEmpty() && msvcs.isEmpty()) {
+ qbsInfo() << Tr::tr("Could not detect an installation of "
+ "the Windows SDK or Visual Studio.");
+ return;
+ }
+
+ foreach (const WinSDK &sdk, winSDKs) {
+ foreach (const QString &arch, sdk.architectures) {
+ addMSVCPlatform(sdk, settings, profiles, QLatin1String("WinSDK") + sdk.version,
+ sdk.installPath + QLatin1String("\\bin"), defaultWinSDK.installPath, arch);
+ }
+ }
+
+ foreach (const MSVC &msvc, msvcs) {
+ foreach (const QString &arch, msvc.architectures) {
+ addMSVCPlatform(msvc, settings, profiles, QLatin1String("MSVC") + msvc.version,
+ msvc.installPath, defaultWinSDK.installPath, arch);
+ }
+ }
+}
diff --git a/src/app/qbs-setup-toolchains/msvcprobe.h b/src/app/qbs-setup-toolchains/msvcprobe.h
new file mode 100644
index 000000000..c32570b05
--- /dev/null
+++ b/src/app/qbs-setup-toolchains/msvcprobe.h
@@ -0,0 +1,42 @@
+/****************************************************************************
+**
+** 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 MSVCPROBE_H
+#define MSVCPROBE_H
+
+#include <QList>
+
+namespace qbs {
+class Profile;
+class Settings;
+}
+
+void msvcProbe(qbs::Settings *settings, QList<qbs::Profile> &profiles);
+
+#endif // MSVCPROBE_H
diff --git a/src/app/qbs-setup-toolchains/probe.cpp b/src/app/qbs-setup-toolchains/probe.cpp
new file mode 100644
index 000000000..3d05da638
--- /dev/null
+++ b/src/app/qbs-setup-toolchains/probe.cpp
@@ -0,0 +1,253 @@
+/****************************************************************************
+**
+** 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 "probe.h"
+
+#include "msvcprobe.h"
+#include "xcodeprobe.h"
+
+#include <logging/translator.h>
+#include <tools/error.h>
+#include <tools/hostosinfo.h>
+#include <tools/profile.h>
+#include <tools/settings.h>
+
+#include <QDir>
+#include <QFileInfo>
+#include <QProcess>
+#include <QStringList>
+#include <QTextStream>
+
+#include <cstdio>
+
+using namespace qbs;
+using Internal::HostOsInfo;
+using Internal::Tr;
+
+static QTextStream qStdout(stdout);
+static QTextStream qStderr(stderr);
+
+static QString findExecutable(const QString &fileName)
+{
+ const QString path = QString::fromLocal8Bit(qgetenv("PATH"));
+ foreach (const QString &ppath, path.split(HostOsInfo::pathListSeparator())) {
+ const QString fullPath = ppath + QLatin1Char('/') + fileName;
+ if (QFileInfo(fullPath).exists())
+ return QDir::cleanPath(fullPath);
+ }
+ return QString();
+}
+
+static QString qsystem(const QString &exe, const QStringList &args = QStringList())
+{
+ QProcess p;
+ p.setProcessChannelMode(QProcess::MergedChannels);
+ p.start(exe, args);
+ if (!p.waitForStarted()) {
+ throw qbs::ErrorInfo(Tr::tr("Failed to start compiler '%1': %2")
+ .arg(exe, p.errorString()));
+ }
+ if (!p.waitForFinished() || p.exitCode() != 0)
+ throw qbs::ErrorInfo(Tr::tr("Failed to run compiler '%1': %2").arg(exe, p.errorString()));
+ return QString::fromLocal8Bit(p.readAll());
+}
+
+static QStringList completeToolchainList(const QString &toolchainName)
+{
+ QStringList toolchains(toolchainName);
+ if (toolchainName == QLatin1String("clang"))
+ toolchains << QLatin1String("llvm") << QLatin1String("gcc");
+ else if (toolchainName == QLatin1String("mingw"))
+ toolchains << QLatin1String("gcc");
+ return toolchains;
+}
+
+static QStringList toolchainTypeFromCompilerName(const QString &compilerName)
+{
+ if (compilerName.contains(QLatin1String("clang")))
+ return completeToolchainList(QLatin1String("clang"));
+ if (compilerName.contains(QLatin1String("llvm")))
+ return QStringList() << QLatin1String("llvm") << QLatin1String("gcc");
+ if (compilerName.contains(QLatin1String("gcc")))
+ return QStringList() << QLatin1String("gcc");
+ if (compilerName == QLatin1String("cl.exe"))
+ return QStringList() << QLatin1String("msvc");
+ return QStringList();
+}
+
+static QString actualCompilerFilePath(const QString &compilerFilePath)
+{
+ const QFileInfo cfi(compilerFilePath);
+ QString compilerFileName = cfi.fileName();
+
+ // People usually want to compile C++. Since we currently link via
+ // the compiler executable, using the plain C compiler will likely lead to linking problems.
+ compilerFileName.replace(QLatin1String("gcc"), QLatin1String("g++"));
+ compilerFileName.replace(QLatin1String("clang"), QLatin1String("clang++"));
+ return cfi.absolutePath() + QLatin1Char('/') + compilerFileName;
+}
+
+static QString gccMachineName(const QString &compilerFilePath)
+{
+ return qsystem(compilerFilePath, QStringList() << QLatin1String("-dumpmachine")).trimmed();
+}
+
+static void setCommonProperties(Profile &profile, const QString &compilerFilePath,
+ const QStringList &toolchainTypes, const QString &architecture)
+{
+ QFileInfo cfi(compilerFilePath);
+ profile.setValue(QLatin1String("cpp.toolchainInstallPath"), cfi.absolutePath());
+ profile.setValue(QLatin1String("cpp.compilerName"), cfi.fileName());
+ profile.setValue(QLatin1String("qbs.toolchain"), toolchainTypes);
+ profile.setValue(QLatin1String("qbs.architecture"),
+ HostOsInfo::canonicalArchitecture(architecture));
+ profile.setValue(QLatin1String("qbs.endianness"),
+ HostOsInfo::defaultEndianness(architecture));
+}
+
+static Profile createMingwProfile(const QString &_compilerFilePath, Settings *settings,
+ const QString &profileName = QString())
+{
+ const QString compilerFilePath = actualCompilerFilePath(_compilerFilePath);
+ const QString machineName = gccMachineName(compilerFilePath);
+ const QStringList validMinGWMachines = QStringList() << QLatin1String("mingw32")
+ << QLatin1String("mingw64") << QLatin1String("i686-w64-mingw32")
+ << QLatin1String("x86_64-w64-mingw32");
+ if (!validMinGWMachines.contains(machineName)) {
+ throw qbs::ErrorInfo(Tr::tr("Detected gcc platform '%1' is not supported.")
+ .arg(machineName));
+ }
+
+ QString architecture = machineName.split(QLatin1Char('-')).first();
+ if (architecture == QLatin1String("mingw32"))
+ architecture = QLatin1String("x86");
+ else if (architecture == QLatin1String("mingw64"))
+ architecture = QLatin1String("x86_64");
+
+ Profile profile(!profileName.isEmpty() ? profileName : machineName, settings);
+ profile.removeProfile();
+ profile.setValue(QLatin1String("qbs.targetOS"), QStringList(QLatin1String("windows")));
+ setCommonProperties(profile, compilerFilePath, completeToolchainList(QLatin1String("mingw")),
+ architecture);
+ qStdout << Tr::tr("Profile '%1' created for '%2'.").arg(profile.name(), compilerFilePath)
+ << endl;
+ return profile;
+}
+
+static Profile createGccProfile(const QString &_compilerFilePath, Settings *settings,
+ const QStringList &toolchainTypes, const QString &profileName)
+{
+ const QString compilerFilePath = actualCompilerFilePath(_compilerFilePath);
+ const QString machineName = gccMachineName(compilerFilePath);
+ const QStringList compilerTriplet = machineName.split(QLatin1Char('-'));
+ if (compilerTriplet.count() < 2) {
+ throw qbs::ErrorInfo(Tr::tr("Architecture '%1' of compiler at '%1' not understood.")
+ .arg(compilerFilePath, machineName));
+ }
+ Profile profile(profileName, settings);
+ setCommonProperties(profile, compilerFilePath, toolchainTypes, compilerTriplet.first());
+ const QString compilerName = QFileInfo(compilerFilePath).fileName();
+ if (compilerName.contains(QLatin1Char('-'))) {
+ QStringList nameParts = compilerName.split(QLatin1Char('-'));
+ profile.setValue(QLatin1String("cpp.compilerName"), nameParts.takeLast());
+ profile.setValue(QLatin1String("cpp.toolchainPrefix"),
+ nameParts.join(QLatin1String("-")) + QLatin1Char('-'));
+ }
+ qStdout << Tr::tr("Profile '%1' created for '%2'.").arg(profile.name(), compilerFilePath)
+ << endl;
+ return profile;
+}
+
+static void gccProbe(Settings *settings, QList<Profile> &profiles, const QString &compilerName)
+{
+ qStdout << Tr::tr("Trying to detect %1...").arg(compilerName) << endl;
+
+ const QString crossCompilePrefix = QString::fromLocal8Bit(qgetenv("CROSS_COMPILE"));
+ const QString compilerFilePath = findExecutable(crossCompilePrefix + compilerName);
+ if (!QFileInfo(compilerFilePath).exists()) {
+ qStderr << Tr::tr("%1 not found.").arg(compilerName) << endl;
+ return;
+ }
+ const QString profileName = QFileInfo(compilerFilePath).completeBaseName();
+ const QStringList toolchainTypes = toolchainTypeFromCompilerName(compilerName);
+ profiles << createGccProfile(compilerFilePath, settings, toolchainTypes, profileName);
+}
+
+static void mingwProbe(Settings *settings, QList<Profile> &profiles)
+{
+ const QString gccPath
+ = findExecutable(HostOsInfo::appendExecutableSuffix(QLatin1String("gcc")));
+ if (!gccPath.isEmpty())
+ profiles << createMingwProfile(gccPath, settings);
+}
+
+void probe(Settings *settings)
+{
+ QList<Profile> profiles;
+ if (HostOsInfo::isWindowsHost()) {
+ msvcProbe(settings, profiles);
+ mingwProbe(settings, profiles);
+ } else if (HostOsInfo::isOsxHost()) {
+ xcodeProbe(settings, profiles);
+ gccProbe(settings, profiles, QLatin1String("gcc"));
+ gccProbe(settings, profiles, QLatin1String("clang"));
+ } else {
+ gccProbe(settings, profiles, QLatin1String("gcc"));
+ gccProbe(settings, profiles, QLatin1String("clang"));
+ }
+
+ if (profiles.isEmpty()) {
+ qStderr << Tr::tr("Could not detect any toolchains. No profile created.") << endl;
+ } else if (profiles.count() == 1 && settings->defaultProfile().isEmpty()) {
+ const QString profileName = profiles.first().name();
+ qStdout << Tr::tr("Making profile '%1' the default.").arg(profileName) << endl;
+ settings->setValue(QLatin1String("defaultProfile"), profileName);
+ }
+}
+
+void createProfile(const QString &profileName, const QString &toolchainType,
+ const QString &compilerFilePath, Settings *settings)
+{
+ QStringList toolchainTypes;
+ if (toolchainType.isEmpty())
+ toolchainTypes = toolchainTypeFromCompilerName(QFileInfo(compilerFilePath).fileName());
+ else
+ toolchainTypes = completeToolchainList(toolchainType);
+
+ if (toolchainTypes.contains(QLatin1String("msvc"))) {
+ throw qbs::ErrorInfo(Tr::tr("Cannot create profile: MSVC toolchains can only be created "
+ "via the auto-detection mechanism."));
+ }
+
+ if (toolchainTypes.contains(QLatin1String("mingw")))
+ createMingwProfile(compilerFilePath, settings, profileName);
+ else if (toolchainTypes.contains(QLatin1String("gcc")))
+ createGccProfile(compilerFilePath, settings, toolchainTypes, profileName);
+ else
+ throw qbs::ErrorInfo(Tr::tr("Cannot create profile: Unknown toolchain type."));
+}
diff --git a/src/app/qbs-setup-toolchains/probe.h b/src/app/qbs-setup-toolchains/probe.h
new file mode 100644
index 000000000..21f1eb151
--- /dev/null
+++ b/src/app/qbs-setup-toolchains/probe.h
@@ -0,0 +1,46 @@
+/****************************************************************************
+**
+** 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_PROBE_H
+#define QBS_PROBE_H
+
+#include <QtGlobal>
+
+QT_BEGIN_NAMESPACE
+class QString;
+class QStringList;
+QT_END_NAMESPACE
+
+namespace qbs { class Settings; }
+
+void createProfile(const QString &profileName, const QString &toolchainType,
+ const QString &compilerFilePath, qbs::Settings *settings);
+
+void probe(qbs::Settings *settings);
+
+#endif // Header guard
diff --git a/src/app/qbs-setup-toolchains/qbs-setup-toolchains.exe.manifest b/src/app/qbs-setup-toolchains/qbs-setup-toolchains.exe.manifest
new file mode 100644
index 000000000..c2114cc84
--- /dev/null
+++ b/src/app/qbs-setup-toolchains/qbs-setup-toolchains.exe.manifest
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+ <!-- Make sure Windows UAC does not believe qbs-setup-toolchains is an installer. -->
+ <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
+ <security>
+ <requestedPrivileges>
+ <requestedExecutionLevel
+ level="asInvoker"
+ uiAccess="false"/>
+ </requestedPrivileges>
+ </security>
+ </trustInfo>
+</assembly>
diff --git a/src/app/qbs-setup-toolchains/qbs-setup-toolchains.pro b/src/app/qbs-setup-toolchains/qbs-setup-toolchains.pro
new file mode 100644
index 000000000..89e346b86
--- /dev/null
+++ b/src/app/qbs-setup-toolchains/qbs-setup-toolchains.pro
@@ -0,0 +1,10 @@
+include(../app.pri)
+
+TARGET = qbs-setup-toolchains
+
+HEADERS += commandlineparser.h probe.h msvcprobe.h xcodeprobe.h ../shared/qbssettings.h
+SOURCES += commandlineparser.cpp main.cpp probe.cpp msvcprobe.cpp xcodeprobe.cpp
+
+mingw {
+ RC_FILE = qbs-setup-toolchains.rc
+}
diff --git a/src/app/qbs-setup-toolchains/qbs-setup-toolchains.qbs b/src/app/qbs-setup-toolchains/qbs-setup-toolchains.qbs
new file mode 100644
index 000000000..d8cf9285e
--- /dev/null
+++ b/src/app/qbs-setup-toolchains/qbs-setup-toolchains.qbs
@@ -0,0 +1,19 @@
+import qbs 1.0
+import "../apptemplate.qbs" as QbsApp
+
+QbsApp {
+ name: "qbs-setup-toolchains"
+ files: [
+ "../shared/qbssettings.h",
+ "commandlineparser.cpp",
+ "commandlineparser.h",
+ "main.cpp",
+ "msvcprobe.cpp",
+ "msvcprobe.h",
+ "probe.cpp",
+ "probe.h",
+ "xcodeprobe.cpp",
+ "xcodeprobe.h"
+ ]
+}
+
diff --git a/src/app/qbs-setup-toolchains/qbs-setup-toolchains.rc b/src/app/qbs-setup-toolchains/qbs-setup-toolchains.rc
new file mode 100644
index 000000000..0f53403f3
--- /dev/null
+++ b/src/app/qbs-setup-toolchains/qbs-setup-toolchains.rc
@@ -0,0 +1,4 @@
+#define RT_MANIFEST 24
+#define CREATEPROCESS_MANIFEST_RESOURCE_ID 1
+
+CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "qbs-setup-toolchains.exe.manifest"
diff --git a/src/app/qbs-setup-toolchains/xcodeprobe.cpp b/src/app/qbs-setup-toolchains/xcodeprobe.cpp
new file mode 100644
index 000000000..362a45e77
--- /dev/null
+++ b/src/app/qbs-setup-toolchains/xcodeprobe.cpp
@@ -0,0 +1,362 @@
+/****************************************************************************
+**
+** 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 "probe.h"
+#include "xcodeprobe.h"
+
+#include "../shared/logging/consolelogger.h"
+
+#include <logging/translator.h>
+#include <tools/profile.h>
+#include <tools/settings.h>
+#include <tools/hostosinfo.h>
+
+#include <QStringList>
+#include <QProcess>
+#include <QByteArray>
+#include <QFileInfo>
+#include <QDir>
+#include <QSettings>
+#include <QRegExp>
+
+using namespace qbs;
+using Internal::Tr;
+using Internal::HostOsInfo;
+
+namespace {
+static QString qsystem(const QString &exe, const QStringList &args = QStringList())
+{
+ QProcess p;
+ p.setProcessChannelMode(QProcess::MergedChannels);
+ p.start(exe, args);
+ p.waitForFinished();
+ return QString::fromLocal8Bit(p.readAll());
+}
+
+class XcodeProbe
+{
+public:
+ XcodeProbe(qbs::Settings *settings, QList<qbs::Profile> &profiles)
+ : settings(settings), profiles(profiles)
+ { }
+
+ static int compareVersions(const QString &v1, const QString &v2);
+ bool addDeveloperPath(const QString &path);
+ void detectDeveloperPaths();
+ void setArch(Profile *profile, const QString &pathToGcc, const QStringList &extraFlags);
+ void setupDefaultToolchains(const QString &devPath, const QString &xCodeName);
+ void detectAll();
+private:
+ qbs::Settings *settings;
+ QList<qbs::Profile> &profiles;
+ QStringList developerPaths;
+};
+
+int XcodeProbe::compareVersions(const QString &v1, const QString &v2)
+{
+ QStringList v1L = v1.split(QLatin1Char('.'));
+ QStringList v2L = v2.split(QLatin1Char('.'));
+ int i = 0;
+ while (v1.length() > i && v1.length() > i) {
+ bool n1Ok, n2Ok;
+ int n1 = v1L.value(i).toInt(&n1Ok);
+ int n2 = v2L.value(i).toInt(&n2Ok);
+ if (!(n1Ok && n2Ok)) {
+ qbsInfo() << Tr::tr("Failed to compare version %1 and %2").arg(v1,v2);
+ return 0;
+ }
+ if (n1 > n2)
+ return -1;
+ else if (n1 < n2)
+ return 1;
+ ++i;
+ }
+ if (v1.length() > v2.length())
+ return -1;
+ if (v1.length() < v2.length())
+ return 1;
+ return 0;
+}
+
+bool XcodeProbe::addDeveloperPath(const QString &path)
+{
+ if (path.isEmpty())
+ return false;
+ QFileInfo pInfo(path);
+ if (!pInfo.exists() || !pInfo.isDir())
+ return false;
+ if (developerPaths.contains(path))
+ return false;
+ developerPaths.append(path);
+ qbsInfo() << Tr::tr("Added developer path %1").arg(path);
+ return true;
+}
+
+void XcodeProbe::detectDeveloperPaths()
+{
+ QProcess selectedXcode;
+ QString program = QLatin1String("/usr/bin/xcode-select");
+ QStringList arguments(QLatin1String("--print-path"));
+ selectedXcode.start(program, arguments, QProcess::ReadOnly);
+ if (!selectedXcode.waitForFinished() || selectedXcode.exitCode()) {
+ qbsInfo() << Tr::tr("Could not detect selected xcode with /usr/bin/xcode-select");
+ } else {
+ QString path = QString::fromLocal8Bit(selectedXcode.readAllStandardOutput());
+ addDeveloperPath(path);
+ }
+ addDeveloperPath(QLatin1String("/Applications/Xcode.app/Contents/Developer"));
+}
+
+void XcodeProbe::setArch(Profile *profile, const QString &pathToGcc, const QStringList &extraFlags)
+{
+ if (!extraFlags.isEmpty()) {
+ profile->setValue(QLatin1String("cpp.platformCommonCompilerFlags"), extraFlags);
+ profile->setValue(QLatin1String("cpp.platformLinkerFlags"), extraFlags);
+ }
+ // setting architecture and endianness only here, bercause the same compiler
+ // can support several ones
+ QStringList flags(extraFlags);
+ flags << QLatin1String("-dumpmachine");
+ QString compilerTriplet = qsystem(pathToGcc, flags).simplified();
+ QStringList compilerTripletl = compilerTriplet.split(QLatin1Char('-'));
+ if (compilerTripletl.count() < 2) {
+ qbsError() << QString::fromLocal8Bit("Detected '%1', but I don't understand "
+ "its architecture '%2'.")
+ .arg(pathToGcc, compilerTriplet);
+ return;
+ }
+
+ const QString architecture = compilerTripletl.at(0);
+
+ qbsInfo() << Tr::tr(" Toolchain %1 detected:\n"
+ " binary: %2\n"
+ " triplet: %3\n"
+ " arch: %4").arg(profile->name(), pathToGcc, compilerTriplet,
+ architecture);
+
+ profile->setValue(QLatin1String("qbs.endianness"),
+ HostOsInfo::defaultEndianness(architecture));
+ profile->setValue(QLatin1String("qbs.architecture"),
+ HostOsInfo::canonicalArchitecture(architecture));
+}
+
+void XcodeProbe::setupDefaultToolchains(const QString &devPath, const QString &xCodeName)
+{
+ qbsInfo() << Tr::tr("Setting up profile '%1'.").arg(xCodeName);
+ QString indent = QLatin1String(" ");
+
+ // detect clang (default toolchain)
+ QFileInfo clangFileInfo(devPath
+ + QLatin1String("/Toolchains/XcodeDefault.xctoolchain/usr/bin")
+ + QLatin1String("/clang++"));
+ bool hasClang = clangFileInfo.exists();
+ if (!hasClang)
+ qbsInfo() << indent << Tr::tr("Default toolchain %1 not found.")
+ .arg(clangFileInfo.canonicalFilePath());
+
+ // Platforms
+ QDir platformsDir(devPath + QLatin1String("/Platforms"));
+ QFileInfoList platforms = platformsDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
+ foreach (const QFileInfo &fInfo, platforms) {
+ if (fInfo.isDir() && fInfo.suffix() == QLatin1String("platform")) {
+ qbsInfo() << indent << Tr::tr("Setting up %1").arg(fInfo.fileName());
+ QSettings infoSettings(fInfo.absoluteFilePath() + QLatin1String("/Info.plist")
+ , QSettings::NativeFormat);
+ if (!infoSettings.contains(QLatin1String("Name"))) {
+ qbsInfo() << indent << Tr::tr("Missing platform name in Info.plist of %1")
+ .arg(fInfo.absoluteFilePath());
+ continue;
+ }
+ QStringList targetOS;
+ targetOS << QLatin1String("darwin") << QLatin1String("unix");
+ QString name = infoSettings.value(QLatin1String("Name")).toString();
+ if (name == QLatin1String("macosx")) {
+ targetOS << QLatin1String("osx");
+ } else if (name == QLatin1String("iphoneos")) {
+ targetOS << QLatin1String("ios");
+ } else if (name == QLatin1String("iphonesimulator")) {
+ targetOS << QLatin1String("ios") << QLatin1String("ios-simulator");
+ } else {
+ qbsInfo() << indent << Tr::tr("Skipping unknown platform %1").arg(name);
+ continue;
+ }
+
+ // prepare default platform properties
+ QVariantMap defaultProp = infoSettings.value(QLatin1String("DefaultProperties"))
+ .toMap();
+ QVariantMap overrideProp = infoSettings.value(QLatin1String("OverrideProperties"))
+ .toMap();
+ QMapIterator<QString, QVariant> i(overrideProp);
+ while (i.hasNext()) {
+ i.next();
+ // use unite? might lead to double insertions...
+ defaultProp[i.key()] = i.value();
+ }
+
+ QString clangFullName = xCodeName + QLatin1Char('-') + name + QLatin1String("-clang");
+ // detect gcc
+ QFileInfo gccFileInfo(fInfo.absoluteFilePath() + QLatin1String("/Developer/usr/bin/g++"));
+ QString gccFullName = xCodeName + QLatin1Char('-') + name + QLatin1String("-gcc");
+ if (!gccFileInfo.exists())
+ gccFileInfo = QFileInfo(devPath + QLatin1String("/usr/bin/g++"));
+ bool hasGcc = gccFileInfo.exists();
+
+ // set SDKs/sysroot
+ QString sysRoot;
+ {
+ QString sdkName;
+ if (defaultProp.contains(QLatin1String("SDKROOT")))
+ sdkName = defaultProp.value(QLatin1String("SDKROOT")).toString();
+ QString sdkPath;
+ QDir sdks(fInfo.absoluteFilePath() + QLatin1String("/Developer/SDKs"));
+ QString maxVersion;
+ foreach (const QFileInfo &sdkDirInfo, sdks.entryInfoList(QDir::Dirs
+ | QDir::NoDotAndDotDot)) {
+ indent = QLatin1String(" ");
+ QSettings sdkInfo(sdkDirInfo.absoluteFilePath()
+ + QLatin1String("/SDKSettings.plist")
+ , QSettings::NativeFormat);
+ QString versionStr = sdkInfo.value(QLatin1String("Version")).toString();
+ QVariant currentSdkName = sdkInfo.value(QLatin1String("CanonicalName"));
+ bool isBaseSdk = sdkInfo.value((QLatin1String("isBaseSDK"))).toString()
+ .toLower() != QLatin1String("no");
+ if (!isBaseSdk) {
+ qbsInfo() << indent << Tr::tr("Skipping non base Sdk %1").arg(currentSdkName.toString());
+ continue;
+ }
+ QString safeName = currentSdkName.toString()
+ .replace(QRegExp(QLatin1String("[^-a-zA-Z0-9]")),QLatin1String("-"));
+ if (sdkName.isEmpty()) {
+ if (compareVersions(maxVersion,versionStr) > 0) {
+ maxVersion = versionStr;
+ sdkPath = sdkDirInfo.canonicalFilePath();
+ }
+ } else if (currentSdkName == sdkName) {
+ sdkPath = sdkDirInfo.canonicalFilePath();
+ }
+ if (hasClang){
+ Profile pSdk(xCodeName + QLatin1Char('-') + safeName
+ + QLatin1String("-clang"), settings);
+ pSdk.removeProfile();
+ pSdk.setBaseProfile(clangFullName);
+ pSdk.setValue(QLatin1String("qbs.sysroot"), sdkDirInfo.canonicalFilePath());
+ pSdk.setValue(QLatin1String("cpp.xcodeSdkName"), currentSdkName.toString());
+ pSdk.setValue(QLatin1String("cpp.xcodeSdkVersion"), versionStr);
+ qbsInfo() << indent << Tr::tr("* adding profile %1").arg(pSdk.name());
+ profiles << pSdk;
+ }
+ if (hasGcc) {
+ Profile pSdk(xCodeName + QLatin1Char('-') + safeName
+ + QLatin1String("-gcc"), settings);
+ pSdk.removeProfile();
+ pSdk.setBaseProfile(gccFullName);
+ pSdk.setValue(QLatin1String("qbs.sysroot"), sdkDirInfo.canonicalFilePath());
+ pSdk.setValue(QLatin1String("cpp.xcodeSdkName"), currentSdkName.toString());
+ pSdk.setValue(QLatin1String("cpp.xcodeSdkVersion"), versionStr);
+ qbsInfo() << indent << Tr::tr("* adding profile %1").arg(pSdk.name());
+ profiles << pSdk;
+ }
+ }
+ if (!sdkPath.isEmpty())
+ sysRoot = sdkPath;
+ else if (!sdkName.isEmpty())
+ qbsInfo() << indent << Tr::tr("Failed to find sysroot %1").arg(sdkName);
+ }
+ if (hasClang) {
+ Profile clangProfile(clangFullName, settings);
+ clangProfile.removeProfile();
+ clangProfile.setValue(QLatin1String("qbs.targetOS"), targetOS);
+ clangProfile.setValue(QLatin1String("qbs.toolchain"),
+ QStringList() << QLatin1String("clang")
+ << QLatin1String("llvm")
+ << QLatin1String("gcc"));
+ QStringList extraFlags;
+ if (defaultProp.contains(QLatin1String("ARCHS"))) {
+ QString arch = defaultProp.value(QLatin1String("ARCHS")).toString();
+ if (arch == QLatin1String("$(NATIVE_ARCH_32_BIT)"))
+ extraFlags << QLatin1String("-arch") << QLatin1String("i386");
+ }
+ if (defaultProp.contains(QLatin1String("NATIVE_ARCH"))) {
+ QString arch = defaultProp.value(QLatin1String("NATIVE_ARCH")).toString();
+ if (!arch.startsWith(QLatin1String("arm")))
+ qbsInfo() << indent << Tr::tr("Expected arm architecture, not %1").arg(arch);
+ extraFlags << QLatin1String("-arch") << arch;
+ }
+ if (!sysRoot.isEmpty())
+ clangProfile.setValue(QLatin1String("qbs.sysroot"), sysRoot);
+ clangProfile.setValue(QLatin1String("cpp.platformPath"), fInfo.canonicalFilePath());
+ clangProfile.setValue(QLatin1String("cpp.compilerName"), clangFileInfo.fileName());
+ clangProfile.setValue(QLatin1String("cpp.toolchainInstallPath"),
+ clangFileInfo.canonicalPath());
+ setArch(&clangProfile, clangFileInfo.canonicalFilePath(), extraFlags);
+ qbsInfo() << indent << Tr::tr("* adding profile %1").arg(clangProfile.name());
+ profiles << clangProfile;
+ }
+ if (hasGcc) {
+ Profile gccProfile(gccFullName, settings);
+ gccProfile.removeProfile();
+ // use the arm-apple-darwin10-llvm-* variant if available???
+ gccProfile.setValue(QLatin1String("qbs.targetOS"), targetOS);
+ QStringList toolchainTypes;
+ toolchainTypes << QLatin1String("gcc");
+ if (gccFullName.contains(QLatin1String("llvm")))
+ toolchainTypes << QLatin1String("llvm");
+ gccProfile.setValue(QLatin1String("qbs.toolchain"), toolchainTypes);
+ if (!sysRoot.isEmpty())
+ gccProfile.setValue(QLatin1String("qbs.sysroot"), sysRoot);
+ gccProfile.setValue(QLatin1String("cpp.platformPath"),fInfo.canonicalFilePath());
+ gccProfile.setValue(QLatin1String("cpp.compilerName"), gccFileInfo.fileName());
+ gccProfile.setValue(QLatin1String("cpp.toolchainInstallPath"),
+ gccFileInfo.canonicalPath());
+ setArch(&gccProfile, gccFileInfo.canonicalFilePath(), QStringList());
+ qbsInfo() << indent << Tr::tr("* adding profile %1").arg(gccProfile.name());
+ profiles << gccProfile;
+ }
+ }
+ indent = QLatin1String(" ");
+ }
+}
+
+void XcodeProbe::detectAll()
+{
+ detectDeveloperPaths();
+ QString xcodeName = QLatin1String("xcode");
+ for (int iXcode = 0; iXcode < developerPaths.count(); ++iXcode) {
+ setupDefaultToolchains(developerPaths.value(iXcode), xcodeName);
+ xcodeName = QString::fromLatin1("xcode%1").arg(iXcode + 2);
+ }
+}
+} // end anonymous namespace
+
+void xcodeProbe(qbs::Settings *settings, QList<qbs::Profile> &profiles)
+{
+ XcodeProbe probe(settings, profiles);
+ probe.detectAll();
+}
+
diff --git a/src/app/qbs-setup-toolchains/xcodeprobe.h b/src/app/qbs-setup-toolchains/xcodeprobe.h
new file mode 100644
index 000000000..930ed1154
--- /dev/null
+++ b/src/app/qbs-setup-toolchains/xcodeprobe.h
@@ -0,0 +1,42 @@
+/****************************************************************************
+**
+** 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 XCODEPROBE_H
+#define XCODEPROBE_H
+
+#include <QList>
+
+namespace qbs {
+class Profile;
+class Settings;
+}
+
+void xcodeProbe(qbs::Settings *settings, QList<qbs::Profile> &profiles);
+
+#endif // XCODEPROBE_H