diff options
author | Christian Kandeler <christian.kandeler@digia.com> | 2014-02-12 18:07:34 +0100 |
---|---|---|
committer | Christian Kandeler <christian.kandeler@digia.com> | 2014-02-13 12:05:31 +0100 |
commit | 0764d2aff60b2c822c81e5e58b4e56055e316efd (patch) | |
tree | 6f5ad4d027666bf8c84a26362806ac22896fffcc /src/app/qbs-setup-toolchains | |
parent | 032a1c4b4b37814594fbb01a0452ea5efa09dfcf (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.cpp | 123 | ||||
-rw-r--r-- | src/app/qbs-setup-toolchains/commandlineparser.h | 62 | ||||
-rw-r--r-- | src/app/qbs-setup-toolchains/main.cpp | 72 | ||||
-rw-r--r-- | src/app/qbs-setup-toolchains/msvcprobe.cpp | 223 | ||||
-rw-r--r-- | src/app/qbs-setup-toolchains/msvcprobe.h | 42 | ||||
-rw-r--r-- | src/app/qbs-setup-toolchains/probe.cpp | 253 | ||||
-rw-r--r-- | src/app/qbs-setup-toolchains/probe.h | 46 | ||||
-rw-r--r-- | src/app/qbs-setup-toolchains/qbs-setup-toolchains.exe.manifest | 13 | ||||
-rw-r--r-- | src/app/qbs-setup-toolchains/qbs-setup-toolchains.pro | 10 | ||||
-rw-r--r-- | src/app/qbs-setup-toolchains/qbs-setup-toolchains.qbs | 19 | ||||
-rw-r--r-- | src/app/qbs-setup-toolchains/qbs-setup-toolchains.rc | 4 | ||||
-rw-r--r-- | src/app/qbs-setup-toolchains/xcodeprobe.cpp | 362 | ||||
-rw-r--r-- | src/app/qbs-setup-toolchains/xcodeprobe.h | 42 |
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 |