/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qt Creator. ** ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ****************************************************************************/ #include "defaultpropertyprovider.h" #include "qbskitinformation.h" #include "qbsprojectmanagerconstants.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace ProjectExplorer; namespace QbsProjectManager { using namespace Constants; namespace Internal { using namespace ProjectExplorer::Constants; using namespace Ios::Constants; using namespace WinRt::Internal::Constants; static QString extractToolchainPrefix(QString *compilerName) { QString prefix; const QStringList candidates = {QLatin1String("g++"), QLatin1String("clang++"), QLatin1String("gcc"), QLatin1String("clang")}; for (const QString &candidate : candidates) { const QString suffix = QLatin1Char('-') + candidate; const int suffixIndex = compilerName->lastIndexOf(suffix); if (suffixIndex == -1) continue; prefix = compilerName->left(suffixIndex + 1); compilerName->remove(0, suffixIndex + 1); break; } return prefix; } static QString targetPlatform(const ProjectExplorer::Abi &abi, const ProjectExplorer::Kit *k) { const Utils::Id device = ProjectExplorer::DeviceTypeKitAspect::deviceTypeId(k); switch (abi.os()) { case ProjectExplorer::Abi::WindowsOS: return QLatin1String("windows"); case ProjectExplorer::Abi::DarwinOS: if (device == DESKTOP_DEVICE_TYPE) return QLatin1String("macos"); if (device == IOS_DEVICE_TYPE) return QLatin1String("ios"); if (device == IOS_SIMULATOR_TYPE) return QLatin1String("ios-simulator"); return QLatin1String("darwin"); case ProjectExplorer::Abi::LinuxOS: if (abi.osFlavor() == ProjectExplorer::Abi::AndroidLinuxFlavor) return QLatin1String("android"); return QLatin1String("linux"); case ProjectExplorer::Abi::BsdOS: switch (abi.osFlavor()) { case ProjectExplorer::Abi::FreeBsdFlavor: return QLatin1String("freebsd"); case ProjectExplorer::Abi::NetBsdFlavor: return QLatin1String("netbsd"); case ProjectExplorer::Abi::OpenBsdFlavor: return QLatin1String("openbsd"); default: break; } return QLatin1String("bsd"); case ProjectExplorer::Abi::QnxOS: return QLatin1String("qnx"); case ProjectExplorer::Abi::UnixOS: if (abi.osFlavor() == ProjectExplorer::Abi::SolarisUnixFlavor) return QLatin1String("solaris"); return QLatin1String("unix"); case ProjectExplorer::Abi::VxWorks: return QLatin1String("vxworks"); case ProjectExplorer::Abi::BareMetalOS: case ProjectExplorer::Abi::UnknownOS: break; } return QString(); } static QStringList toolchainList(const ProjectExplorer::ToolChain *tc) { QStringList list; if (tc->typeId() == ProjectExplorer::Constants::CLANG_TOOLCHAIN_TYPEID || (tc->typeId() == Android::Constants::ANDROID_TOOLCHAIN_TYPEID && tc->compilerCommand().toString().contains("clang"))) { list << QLatin1String("clang") << QLatin1String("llvm") << QLatin1String("gcc"); } else if (tc->typeId() == ProjectExplorer::Constants::GCC_TOOLCHAIN_TYPEID || tc->typeId() == Android::Constants::ANDROID_TOOLCHAIN_TYPEID) { list << QLatin1String("gcc"); // TODO: Detect llvm-gcc } else if (tc->typeId() == ProjectExplorer::Constants::MINGW_TOOLCHAIN_TYPEID) { list << QLatin1String("mingw") << QLatin1String("gcc"); } else if (tc->typeId() == ProjectExplorer::Constants::CLANG_CL_TOOLCHAIN_TYPEID) { list << QLatin1String("clang-cl") << QLatin1String("msvc"); } else if (tc->typeId() == ProjectExplorer::Constants::MSVC_TOOLCHAIN_TYPEID) { list << QLatin1String("msvc"); } else if (tc->typeId() == BareMetal::Constants::IAREW_TOOLCHAIN_TYPEID) { list << QLatin1String("iar"); } else if (tc->typeId() == BareMetal::Constants::KEIL_TOOLCHAIN_TYPEID) { list << QLatin1String("keil"); } else if (tc->typeId() == BareMetal::Constants::SDCC_TOOLCHAIN_TYPEID) { list << QLatin1String("sdcc"); } return list; } static QString architecture(const ProjectExplorer::Abi &targetAbi) { if (targetAbi.architecture() != ProjectExplorer::Abi::UnknownArchitecture) { QString architecture = ProjectExplorer::Abi::toString(targetAbi.architecture()); if (targetAbi.osFlavor() == ProjectExplorer::Abi::AndroidLinuxFlavor) { switch (targetAbi.architecture()) { case ProjectExplorer::Abi::X86Architecture: if (targetAbi.wordWidth() == 64) architecture += "_64"; return architecture; case ProjectExplorer::Abi::ArmArchitecture: if (targetAbi.wordWidth() == 64) architecture += "64"; else architecture += "v7a"; return architecture; default: break; } } // We have to be conservative tacking on suffixes to arch names because an arch that is // already 64-bit may get an incorrect name as a result (i.e. Itanium) if (targetAbi.wordWidth() == 64) { switch (targetAbi.architecture()) { case ProjectExplorer::Abi::X86Architecture: architecture.append(QLatin1Char('_')); // fall through case ProjectExplorer::Abi::ArmArchitecture: // ARM sub-architectures are currently not handled, which is kind of problematic case ProjectExplorer::Abi::MipsArchitecture: case ProjectExplorer::Abi::PowerPCArchitecture: architecture.append(QString::number(targetAbi.wordWidth())); break; default: break; } } return architecture; } return QString(); } static bool isMultiTargetingToolchain(const ProjectExplorer::ToolChain *tc) { // Clang and QCC are multi-targeting compilers; others (GCC/MinGW, MSVC, ICC) are not return tc->targetAbi().os() == ProjectExplorer::Abi::QnxOS || tc->typeId() == ProjectExplorer::Constants::CLANG_TOOLCHAIN_TYPEID; } static QStringList architectures(const ProjectExplorer::ToolChain *tc) { // For platforms which can have builds for multiple architectures in a single configuration // (Darwin, Android), regardless of whether the toolchain is multi-targeting or not (Clang // always is, but Android GCC is not), let qbs automatically determine the list of architectures // to build for by default. Similarly, if the underlying toolchain only targets a single // architecture there's no reason to duplicate the detection logic here. // Handles: GCC/MinGW, ICC, MSVC, Clang (Darwin, Android) if (tc->targetAbi().os() == ProjectExplorer::Abi::DarwinOS || tc->targetAbi().osFlavor() == ProjectExplorer::Abi::AndroidLinuxFlavor || !isMultiTargetingToolchain(tc)) return { }; // This attempts to use the preferred architecture for toolchains which are multi-targeting. // Handles: Clang (Linux/UNIX), QCC const auto arch = architecture(tc->targetAbi()); if (!arch.isEmpty()) return { arch }; return { }; } QVariantMap DefaultPropertyProvider::properties(const ProjectExplorer::Kit *k, const QVariantMap &defaultData) const { QTC_ASSERT(k, return defaultData); QVariantMap data = autoGeneratedProperties(k, defaultData); const QVariantMap customProperties = QbsKitAspect::properties(k); for (QVariantMap::ConstIterator it = customProperties.constBegin(); it != customProperties.constEnd(); ++it) { data.insert(it.key(), it.value()); } return data; } static void filterCompilerLinkerFlags(const ProjectExplorer::Abi &targetAbi, QStringList &flags) { for (int i = 0; i < flags.size(); ) { if (targetAbi.architecture() != ProjectExplorer::Abi::UnknownArchitecture && flags[i] == QStringLiteral("-arch") && i + 1 < flags.size()) { flags.removeAt(i); flags.removeAt(i); } else { ++i; } } } QVariantMap DefaultPropertyProvider::autoGeneratedProperties(const ProjectExplorer::Kit *k, const QVariantMap &defaultData) const { QVariantMap data = defaultData; const QString sysroot = SysRootKitAspect::sysRoot(k).toUserOutput(); if (!sysroot.isEmpty()) data.insert(QLatin1String(QBS_SYSROOT), sysroot); ToolChain *tcC = ToolChainKitAspect::cToolChain(k); ToolChain *tcCxx = ToolChainKitAspect::cxxToolChain(k); if (!tcC && !tcCxx) return data; ToolChain *mainTc = tcCxx ? tcCxx : tcC; Abi targetAbi = mainTc->targetAbi(); auto archs = architectures(mainTc); if (!archs.isEmpty()) data.insert(QLatin1String(QBS_ARCHITECTURES), archs); if (mainTc->targetAbi() != Abi::abiFromTargetTriplet(mainTc->originalTargetTriple()) || targetAbi.osFlavor() == Abi::AndroidLinuxFlavor) { data.insert(QLatin1String(QBS_ARCHITECTURE), architecture(mainTc->targetAbi())); } else if (archs.count() == 1) { data.insert(QLatin1String(QBS_ARCHITECTURE), archs.first()); } data.insert(QLatin1String(QBS_TARGETPLATFORM), targetPlatform(targetAbi, k)); QStringList toolchain = toolchainList(mainTc); if (targetAbi.osFlavor() == Abi::AndroidLinuxFlavor) { const IDevice::ConstPtr dev = DeviceKitAspect::device(k); if (dev) { const QString sdkDir = k->value(Android::Constants::ANDROID_KIT_SDK).toString(); if (!sdkDir.isEmpty()) data.insert("Android.sdk.sdkDir", sdkDir); const QString ndkDir = k->value(Android::Constants::ANDROID_KIT_NDK).toString(); if (!ndkDir.isEmpty()) { data.insert("Android.sdk.ndkDir", ndkDir); data.insert("Android.ndk.ndkDir", ndkDir); } } QtSupport::BaseQtVersion *qtVersion = QtSupport::QtKitAspect::qtVersion(k); if (qtVersion) { data.remove(QBS_ARCHITECTURES); data.remove(QBS_ARCHITECTURE); QStringList abis; for (const auto &abi : qtVersion->qtAbis()) abis << architecture(abi); if (abis.size() == 1) data.insert(QLatin1String(QBS_ARCHITECTURE), abis.first()); else data.insert(QLatin1String(QBS_ARCHITECTURES), abis); } } else { Utils::FilePath cCompilerPath; if (tcC) cCompilerPath = tcC->compilerCommand(); Utils::FilePath cxxCompilerPath; if (tcCxx) cxxCompilerPath = tcCxx->compilerCommand(); const QFileInfo cFileInfo = cCompilerPath.toFileInfo(); const QFileInfo cxxFileInfo = cxxCompilerPath.toFileInfo(); QString cCompilerName = cFileInfo.fileName(); QString cxxCompilerName = cxxFileInfo.fileName(); const QString cToolchainPrefix = extractToolchainPrefix(&cCompilerName); const QString cxxToolchainPrefix = extractToolchainPrefix(&cxxCompilerName); QFileInfo mainFileInfo; QString mainCompilerName; QString mainToolchainPrefix; if (tcCxx) { mainFileInfo = cxxFileInfo; mainCompilerName = cxxCompilerName; mainToolchainPrefix = cxxToolchainPrefix; } else { mainFileInfo = cFileInfo; mainCompilerName = cCompilerName; mainToolchainPrefix = cToolchainPrefix; } if (!mainToolchainPrefix.isEmpty()) data.insert(QLatin1String(CPP_TOOLCHAINPREFIX), mainToolchainPrefix); if (toolchain.contains(QLatin1String("clang-cl"))) { data.insert(QLatin1String(CPP_COMPILERNAME), mainCompilerName); const auto clangClToolchain = static_cast(mainTc); data.insert(QLatin1String(CPP_VCVARSALLPATH), clangClToolchain->varsBat()); } else if (toolchain.contains(QLatin1String("msvc"))) { data.insert(QLatin1String(CPP_COMPILERNAME), mainCompilerName); } else { if (!mainCompilerName.isEmpty()) data.insert(QLatin1String(CPP_COMPILERNAME), mainCompilerName); if (!cCompilerName.isEmpty()) data.insert(QLatin1String(CPP_CCOMPILERNAME), cCompilerName); if (!cxxCompilerName.isEmpty()) data.insert(QLatin1String(CPP_CXXCOMPILERNAME), cxxCompilerName); } if (tcC && tcCxx && !cCompilerPath.isEmpty() && !cxxCompilerPath.isEmpty() && cFileInfo.absolutePath() != cxxFileInfo.absolutePath()) { Core::MessageManager::writeFlashing( tr("C and C++ compiler paths differ. C compiler may not work.")); } data.insert(QLatin1String(CPP_TOOLCHAINPATH), mainFileInfo.absolutePath()); if (auto gcc = dynamic_cast(mainTc)) { QStringList compilerFlags = gcc->platformCodeGenFlags(); filterCompilerLinkerFlags(targetAbi, compilerFlags); data.insert(QLatin1String(CPP_PLATFORMCOMMONCOMPILERFLAGS), compilerFlags); QStringList linkerFlags = gcc->platformLinkerFlags(); filterCompilerLinkerFlags(targetAbi, linkerFlags); data.insert(QLatin1String(CPP_PLATFORMLINKERFLAGS), linkerFlags); } if (targetAbi.os() == ProjectExplorer::Abi::DarwinOS) { // Reverse engineer the Xcode developer path from the compiler path const QRegularExpression compilerRe( QStringLiteral("^(?.*)/Toolchains/(?:.+)\\.xctoolchain/usr/bin$")); const QRegularExpressionMatch compilerReMatch = compilerRe.match(cxxFileInfo.absolutePath()); if (compilerReMatch.hasMatch()) { const QString developerPath = compilerReMatch.captured(QStringLiteral("developerpath")); data.insert(QLatin1String(XCODE_DEVELOPERPATH), developerPath); toolchain.insert(0, QStringLiteral("xcode")); // If the sysroot is part of this developer path, set the canonical SDK name const QDir sysrootdir(QDir::cleanPath(sysroot)); const QString sysrootAbs = sysrootdir.absolutePath(); const QSettings sdkSettings( sysrootdir.absoluteFilePath(QStringLiteral("SDKSettings.plist")), QSettings::NativeFormat); const QString version( sdkSettings.value(QStringLiteral("Version")).toString()); QString canonicalName( sdkSettings.value(QStringLiteral("CanonicalName")).toString()); canonicalName.chop(version.size()); if (!canonicalName.isEmpty() && !version.isEmpty() && sysrootAbs.startsWith(developerPath)) { if (sysrootAbs.endsWith(QStringLiteral("/%1.sdk").arg(canonicalName + version), Qt::CaseInsensitive)) { data.insert(QLatin1String(XCODE_SDK), QString(canonicalName + version)); } if (sysrootAbs.endsWith(QStringLiteral("/%1.sdk").arg(canonicalName), Qt::CaseInsensitive)) { data.insert(QLatin1String(XCODE_SDK), canonicalName); } } } } } if (!toolchain.isEmpty()) data.insert(QLatin1String(QBS_TOOLCHAIN), toolchain); return data; } } // namespace Internal } // namespace QbsProjectManager